Loading cmds/installd/InstalldNativeService.cpp +67 −11 Original line number Diff line number Diff line Loading @@ -167,6 +167,15 @@ binder::Status checkArgumentUuid(const std::unique_ptr<std::string>& uuid) { } } binder::Status checkArgumentUuidTestOrNull(const std::unique_ptr<std::string>& uuid) { if (!uuid || strcmp(uuid->c_str(), kTestUuid) == 0) { return ok(); } else { return exception(binder::Status::EX_ILLEGAL_ARGUMENT, StringPrintf("UUID must be null or \"%s\", got: %s", kTestUuid, uuid->c_str())); } } binder::Status checkArgumentPackageName(const std::string& packageName) { if (is_valid_package_name(packageName.c_str())) { return ok(); Loading Loading @@ -219,6 +228,13 @@ binder::Status checkArgumentPath(const std::unique_ptr<std::string>& path) { } \ } #define CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(uuid) { \ auto status = checkArgumentUuidTestOrNull(uuid); \ if (!status.isOk()) { \ return status; \ } \ } \ #define CHECK_ARGUMENT_PACKAGE_NAME(packageName) { \ binder::Status status = \ checkArgumentPackageName((packageName)); \ Loading Loading @@ -768,20 +784,21 @@ static int32_t copy_directory_recursive(const char* from, const char* to) { binder::Status InstalldNativeService::snapshotAppData( const std::unique_ptr<std::string>& volumeUuid, const std::string& packageName, int32_t user, int32_t storageFlags) { const std::string& packageName, int32_t user, int32_t storageFlags, int64_t* _aidl_return) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(volumeUuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); std::lock_guard<std::recursive_mutex> lock(mLock); const char* volume_uuid = volumeUuid ? volumeUuid->c_str() : nullptr; const char* package_name = packageName.c_str(); if (volume_uuid && strcmp(volume_uuid, kTestUuid)) { return exception(binder::Status::EX_ILLEGAL_ARGUMENT, StringPrintf("volumeUuid must be null or \"%s\", got: %s", kTestUuid, volume_uuid)); } binder::Status res = ok(); // Default result to 0, it will be populated with inode of ce data snapshot // if FLAG_STORAGE_CE has been passed. if (_aidl_return != nullptr) *_aidl_return = 0; bool clear_ce_on_exit = false; bool clear_de_on_exit = false; Loading Loading @@ -862,6 +879,16 @@ binder::Status InstalldNativeService::snapshotAppData( clear_ce_on_exit = true; return res; } if (_aidl_return != nullptr) { auto ce_snapshot_path = create_data_misc_ce_rollback_package_path(volume_uuid, user, package_name); rc = get_path_inode(ce_snapshot_path, reinterpret_cast<ino_t*>(_aidl_return)); if (rc != 0) { res = error(rc, "Failed to get_path_inode for " + ce_snapshot_path); clear_ce_on_exit = true; return res; } } } return res; Loading @@ -872,17 +899,13 @@ binder::Status InstalldNativeService::restoreAppDataSnapshot( const int32_t appId, const int64_t ceDataInode, const std::string& seInfo, const int32_t user, int32_t storageFlags) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(volumeUuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); std::lock_guard<std::recursive_mutex> lock(mLock); const char* volume_uuid = volumeUuid ? volumeUuid->c_str() : nullptr; const char* package_name = packageName.c_str(); if (volume_uuid && strcmp(volume_uuid, kTestUuid)) { return exception(binder::Status::EX_ILLEGAL_ARGUMENT, StringPrintf("volumeUuid must be null or \"%s\", got: %s", kTestUuid, volume_uuid)); } auto from_ce = create_data_misc_ce_rollback_package_path(volume_uuid, user, package_name); auto from_de = create_data_misc_de_rollback_package_path(volume_uuid, Loading Loading @@ -933,6 +956,39 @@ binder::Status InstalldNativeService::restoreAppDataSnapshot( return restoreconAppData(volumeUuid, packageName, user, storageFlags, appId, seInfo); } binder::Status InstalldNativeService::destroyAppDataSnapshot( const std::unique_ptr<std::string> &volumeUuid, const std::string& packageName, const int32_t user, const int64_t ceSnapshotInode, int32_t storageFlags) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(volumeUuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); std::lock_guard<std::recursive_mutex> lock(mLock); const char* volume_uuid = volumeUuid ? volumeUuid->c_str() : nullptr; const char* package_name = packageName.c_str(); if (storageFlags & FLAG_STORAGE_DE) { auto de_snapshot_path = create_data_misc_de_rollback_package_path(volume_uuid, user, package_name); int res = delete_dir_contents_and_dir(de_snapshot_path, true /* ignore_if_missing */); if (res != 0) { return error(res, "Failed clearing snapshot " + de_snapshot_path); } } if (storageFlags & FLAG_STORAGE_CE) { auto ce_snapshot_path = create_data_misc_ce_rollback_package_path(volume_uuid, user, package_name, ceSnapshotInode); int res = delete_dir_contents_and_dir(ce_snapshot_path, true /* ignore_if_missing */); if (res != 0) { return error(res, "Failed clearing snapshot " + ce_snapshot_path); } } return ok(); } binder::Status InstalldNativeService::moveCompleteApp(const std::unique_ptr<std::string>& fromUuid, const std::unique_ptr<std::string>& toUuid, const std::string& packageName, const std::string& dataAppName, int32_t appId, const std::string& seInfo, Loading cmds/installd/InstalldNativeService.h +5 −1 Original line number Diff line number Diff line Loading @@ -61,10 +61,14 @@ public: binder::Status fixupAppData(const std::unique_ptr<std::string>& uuid, int32_t flags); binder::Status snapshotAppData(const std::unique_ptr<std::string>& volumeUuid, const std::string& packageName, const int32_t user, int32_t storageFlags); const std::string& packageName, const int32_t user, int32_t storageFlags, int64_t* _aidl_return); binder::Status restoreAppDataSnapshot(const std::unique_ptr<std::string>& volumeUuid, const std::string& packageName, const int32_t appId, const int64_t ceDataInode, const std::string& seInfo, const int32_t user, int32_t storageFlags); binder::Status destroyAppDataSnapshot(const std::unique_ptr<std::string> &volumeUuid, const std::string& packageName, const int32_t user, const int64_t ceSnapshotInode, int32_t storageFlags); binder::Status getAppSize(const std::unique_ptr<std::string>& uuid, const std::vector<std::string>& packageNames, int32_t userId, int32_t flags, Loading cmds/installd/binder/android/os/IInstalld.aidl +3 −5 Original line number Diff line number Diff line Loading @@ -105,14 +105,12 @@ interface IInstalld { int userId, int appId, @utf8InCpp String profileName, @utf8InCpp String codePath, @nullable @utf8InCpp String dexMetadata); void snapshotAppData(@nullable @utf8InCpp String uuid, in @utf8InCpp String packageName, long snapshotAppData(@nullable @utf8InCpp String uuid, in @utf8InCpp String packageName, int userId, int storageFlags); void restoreAppDataSnapshot(@nullable @utf8InCpp String uuid, in @utf8InCpp String packageName, int appId, long ceDataInode, @utf8InCpp String seInfo, int user, int storageflags); // TODO(narayan) we need an API to delete the app data snapshot as well. // void destroyAppDataSnapshot(@nullable @utf8InCpp String uuid, // in @utf8InCpp String packageName, int userId, int storageFlags); void destroyAppDataSnapshot(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName, int userId, long ceSnapshotInode, int storageFlags); const int FLAG_STORAGE_DE = 0x1; const int FLAG_STORAGE_CE = 0x2; Loading cmds/installd/tests/installd_service_test.cpp +145 −8 Original line number Diff line number Diff line Loading @@ -292,8 +292,13 @@ TEST_F(ServiceTest, CreateAppDataSnapshot) { 0700, 10000, 20000, false /* follow_symlinks */)); // Request a snapshot of the CE content but not the DE content. int64_t ce_snapshot_inode; ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"), "com.foo", 0, FLAG_STORAGE_CE)); "com.foo", 0, FLAG_STORAGE_CE, &ce_snapshot_inode)); struct stat buf; memset(&buf, 0, sizeof(buf)); ASSERT_EQ(0, stat((rollback_ce_dir + "/com.foo").c_str(), &buf)); ASSERT_EQ(ce_snapshot_inode, (int64_t) buf.st_ino); std::string ce_content, de_content; // At this point, we should have the CE content but not the DE content. Loading @@ -311,7 +316,9 @@ TEST_F(ServiceTest, CreateAppDataSnapshot) { // Request a snapshot of the DE content but not the CE content. ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"), "com.foo", 0, FLAG_STORAGE_DE)); "com.foo", 0, FLAG_STORAGE_DE, &ce_snapshot_inode)); // Only DE content snapshot was requested. ASSERT_EQ(ce_snapshot_inode, 0); // At this point, both the CE as well as the DE content should be fully // populated. Loading @@ -330,7 +337,7 @@ TEST_F(ServiceTest, CreateAppDataSnapshot) { // Request a snapshot of both the CE as well as the DE content. ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"), "com.foo", 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE)); "com.foo", 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE, nullptr)); ASSERT_TRUE(android::base::ReadFileToString( rollback_ce_dir + "/com.foo/file1", &ce_content, false /* follow_symlinks */)); Loading @@ -356,10 +363,13 @@ TEST_F(ServiceTest, CreateAppDataSnapshot_AppDataAbsent) { auto scope_guard = android::base::make_scope_guard(deleter); int64_t ce_snapshot_inode; ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"), "com.foo", 0, FLAG_STORAGE_CE)); "com.foo", 0, FLAG_STORAGE_CE, &ce_snapshot_inode)); ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"), "com.foo", 0, FLAG_STORAGE_DE)); "com.foo", 0, FLAG_STORAGE_DE, nullptr)); // No CE content snapshot was performed. ASSERT_EQ(ce_snapshot_inode, 0); // The snapshot calls must succeed but there should be no snapshot // created. Loading Loading @@ -409,7 +419,7 @@ TEST_F(ServiceTest, CreateAppDataSnapshot_ClearsExistingSnapshot) { 0700, 10000, 20000, false /* follow_symlinks */)); ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"), "com.foo", 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE)); "com.foo", 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE, nullptr)); // Previous snapshot (with data for file1) must be cleared. struct stat sb; Loading @@ -435,7 +445,7 @@ TEST_F(ServiceTest, SnapshotAppData_WrongVolumeUuid) { auto scope_guard = android::base::make_scope_guard(deleter); EXPECT_BINDER_FAIL(service->snapshotAppData(std::make_unique<std::string>("FOO"), "com.foo", 0, FLAG_STORAGE_DE)); "com.foo", 0, FLAG_STORAGE_DE, nullptr)); } TEST_F(ServiceTest, CreateAppDataSnapshot_ClearsCache) { Loading Loading @@ -484,7 +494,7 @@ TEST_F(ServiceTest, CreateAppDataSnapshot_ClearsCache) { "TEST_CONTENT_DE", fake_package_de_code_cache_path + "/file1", 0700, 10000, 20000, false /* follow_symlinks */)); ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"), "com.foo", 0, FLAG_STORAGE_CE | FLAG_STORAGE_DE)); "com.foo", 0, FLAG_STORAGE_CE | FLAG_STORAGE_DE, nullptr)); // The snapshot call must clear cache. struct stat sb; ASSERT_EQ(-1, stat((fake_package_ce_cache_path + "/file1").c_str(), &sb)); Loading Loading @@ -546,6 +556,133 @@ TEST_F(ServiceTest, RestoreAppDataSnapshot) { ASSERT_EQ("DE_RESTORE_CONTENT", de_content); } TEST_F(ServiceTest, CreateSnapshotThenDestroyIt) { auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0); auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0); ASSERT_TRUE(mkdirs(rollback_ce_dir, 700)); ASSERT_TRUE(mkdirs(rollback_de_dir, 700)); auto fake_package_ce_path = create_data_user_ce_package_path("TEST", 0, "com.foo"); auto fake_package_de_path = create_data_user_de_package_path("TEST", 0, "com.foo"); ASSERT_TRUE(mkdirs(fake_package_ce_path, 700)); ASSERT_TRUE(mkdirs(fake_package_de_path, 700)); auto deleter = [&rollback_ce_dir, &rollback_de_dir, &fake_package_ce_path, &fake_package_de_path]() { delete_dir_contents(rollback_ce_dir, true); delete_dir_contents(rollback_de_dir, true); delete_dir_contents(fake_package_ce_path, true); delete_dir_contents(fake_package_de_path, true); rmdir(rollback_ce_dir.c_str()); rmdir(rollback_de_dir.c_str()); }; auto scope_guard = android::base::make_scope_guard(deleter); // Prepare data for snapshot. ASSERT_TRUE(android::base::WriteStringToFile( "TEST_CONTENT_CE", fake_package_ce_path + "/file1", 0700, 10000, 20000, false /* follow_symlinks */)); ASSERT_TRUE(android::base::WriteStringToFile( "TEST_CONTENT_DE", fake_package_de_path + "/file1", 0700, 10000, 20000, false /* follow_symlinks */)); int64_t ce_snapshot_inode; // Request a snapshot of both the CE as well as the DE content. ASSERT_TRUE(service->snapshotAppData(std::make_unique<std::string>("TEST"), "com.foo", 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE, &ce_snapshot_inode).isOk()); // Because CE data snapshot was requested, ce_snapshot_inode can't be null. ASSERT_NE(0, ce_snapshot_inode); // Check snapshot is there. struct stat sb; ASSERT_EQ(0, stat((rollback_ce_dir + "/com.foo").c_str(), &sb)); ASSERT_EQ(0, stat((rollback_de_dir + "/com.foo").c_str(), &sb)); ASSERT_TRUE(service->destroyAppDataSnapshot(std::make_unique<std::string>("TEST"), "com.foo", 0, ce_snapshot_inode, FLAG_STORAGE_DE | FLAG_STORAGE_CE).isOk()); // Check snapshot is deleted. ASSERT_EQ(-1, stat((rollback_ce_dir + "/com.foo").c_str(), &sb)); ASSERT_EQ(-1, stat((rollback_de_dir + "/com.foo").c_str(), &sb)); } TEST_F(ServiceTest, DestroyAppDataSnapshot_CeSnapshotInodeIsZero) { auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0); auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0); ASSERT_TRUE(mkdirs(rollback_ce_dir, 700)); ASSERT_TRUE(mkdirs(rollback_de_dir, 700)); auto fake_package_ce_path = create_data_user_ce_package_path("TEST", 0, "com.foo"); auto fake_package_de_path = create_data_user_de_package_path("TEST", 0, "com.foo"); ASSERT_TRUE(mkdirs(fake_package_ce_path, 700)); ASSERT_TRUE(mkdirs(fake_package_de_path, 700)); auto deleter = [&rollback_ce_dir, &rollback_de_dir, &fake_package_ce_path, &fake_package_de_path]() { delete_dir_contents(rollback_ce_dir, true); delete_dir_contents(rollback_de_dir, true); delete_dir_contents(fake_package_ce_path, true); delete_dir_contents(fake_package_de_path, true); rmdir(rollback_ce_dir.c_str()); rmdir(rollback_de_dir.c_str()); }; auto scope_guard = android::base::make_scope_guard(deleter); // Create a snapshot ASSERT_TRUE(mkdirs(rollback_ce_dir + "/com.foo/", 700)); ASSERT_TRUE(mkdirs(rollback_de_dir + "/com.foo/", 700)); ASSERT_TRUE(android::base::WriteStringToFile( "CE_RESTORE_CONTENT", rollback_ce_dir + "/com.foo/file1", 0700, 10000, 20000, false /* follow_symlinks */)); ASSERT_TRUE(android::base::WriteStringToFile( "DE_RESTORE_CONTENT", rollback_de_dir + "/com.foo/file1", 0700, 10000, 20000, false /* follow_symlinks */)); ASSERT_TRUE(service->destroyAppDataSnapshot(std::make_unique<std::string>("TEST"), "com.foo", 0, 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE).isOk()); // Check snapshot is deleted. struct stat sb; ASSERT_EQ(-1, stat((rollback_ce_dir + "/com.foo").c_str(), &sb)); ASSERT_EQ(-1, stat((rollback_de_dir + "/com.foo").c_str(), &sb)); // Check that deleting already deleted snapshot is no-op. ASSERT_TRUE(service->destroyAppDataSnapshot(std::make_unique<std::string>("TEST"), "com.foo", 0, 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE).isOk()); } TEST_F(ServiceTest, DestroyAppDataSnapshot_WrongVolumeUuid) { // Setup rollback data to make sure that test fails due to wrong volumeUuid // being passed, not because of some other reason. auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0); auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0); ASSERT_TRUE(mkdirs(rollback_ce_dir, 700)); ASSERT_TRUE(mkdirs(rollback_de_dir, 700)); auto fake_package_ce_path = create_data_user_ce_package_path("TEST", 0, "com.foo"); auto fake_package_de_path = create_data_user_de_package_path("TEST", 0, "com.foo"); ASSERT_TRUE(mkdirs(fake_package_ce_path, 700)); ASSERT_TRUE(mkdirs(fake_package_de_path, 700)); auto deleter = [&rollback_ce_dir, &rollback_de_dir, &fake_package_ce_path, &fake_package_de_path]() { delete_dir_contents(rollback_ce_dir, true); delete_dir_contents(rollback_de_dir, true); delete_dir_contents(fake_package_ce_path, true); delete_dir_contents(fake_package_de_path, true); rmdir(rollback_ce_dir.c_str()); rmdir(rollback_de_dir.c_str()); }; auto scope_guard = android::base::make_scope_guard(deleter); ASSERT_FALSE(service->destroyAppDataSnapshot(std::make_unique<std::string>("BAR"), "com.foo", 0, 0, FLAG_STORAGE_DE).isOk()); } TEST_F(ServiceTest, RestoreAppDataSnapshot_WrongVolumeUuid) { // Setup rollback data to make sure that fails due to wrong volumeUuid being Loading cmds/installd/tests/installd_utils_test.cpp +24 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ #include <string.h> #include <android-base/logging.h> #include <android-base/scopeguard.h> #include <gtest/gtest.h> #include "InstalldNativeService.h" Loading Loading @@ -565,6 +566,29 @@ TEST_F(UtilsTest, TestRollbackPaths) { EXPECT_EQ("/data/misc_de/10/rollback", create_data_misc_de_rollback_path(nullptr, 10)); EXPECT_EQ("/data/misc_ce/0/rollback/com.foo", create_data_misc_ce_rollback_package_path(nullptr, 0, "com.foo", 0)); EXPECT_EQ("/data/misc_ce/0/rollback/com.foo", create_data_misc_ce_rollback_package_path(nullptr, 0, "com.foo", 239)); auto rollback_ce_package_path = create_data_misc_ce_rollback_package_path(nullptr, 0, "com.foo"); auto deleter = [&rollback_ce_package_path]() { delete_dir_contents_and_dir(rollback_ce_package_path, true /* ignore_if_missing */); }; auto scope_guard = android::base::make_scope_guard(deleter); ASSERT_NE(-1, mkdir(rollback_ce_package_path.c_str(), 700)); ino_t ce_data_inode; ASSERT_EQ(0, get_path_inode(rollback_ce_package_path, &ce_data_inode)); EXPECT_EQ("/data/misc_ce/0/rollback/com.foo", create_data_misc_ce_rollback_package_path(nullptr, 0, "com.foo", ce_data_inode)); // Check that path defined by inode is picked even if it's not the same as // the fallback one. EXPECT_EQ("/data/misc_ce/0/rollback/com.foo", create_data_misc_ce_rollback_package_path(nullptr, 0, "com.bar", ce_data_inode)); // These last couple of cases are never exercised in production because we // only snapshot apps in the primary data partition. Exercise them here for // the sake of completeness. Loading Loading
cmds/installd/InstalldNativeService.cpp +67 −11 Original line number Diff line number Diff line Loading @@ -167,6 +167,15 @@ binder::Status checkArgumentUuid(const std::unique_ptr<std::string>& uuid) { } } binder::Status checkArgumentUuidTestOrNull(const std::unique_ptr<std::string>& uuid) { if (!uuid || strcmp(uuid->c_str(), kTestUuid) == 0) { return ok(); } else { return exception(binder::Status::EX_ILLEGAL_ARGUMENT, StringPrintf("UUID must be null or \"%s\", got: %s", kTestUuid, uuid->c_str())); } } binder::Status checkArgumentPackageName(const std::string& packageName) { if (is_valid_package_name(packageName.c_str())) { return ok(); Loading Loading @@ -219,6 +228,13 @@ binder::Status checkArgumentPath(const std::unique_ptr<std::string>& path) { } \ } #define CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(uuid) { \ auto status = checkArgumentUuidTestOrNull(uuid); \ if (!status.isOk()) { \ return status; \ } \ } \ #define CHECK_ARGUMENT_PACKAGE_NAME(packageName) { \ binder::Status status = \ checkArgumentPackageName((packageName)); \ Loading Loading @@ -768,20 +784,21 @@ static int32_t copy_directory_recursive(const char* from, const char* to) { binder::Status InstalldNativeService::snapshotAppData( const std::unique_ptr<std::string>& volumeUuid, const std::string& packageName, int32_t user, int32_t storageFlags) { const std::string& packageName, int32_t user, int32_t storageFlags, int64_t* _aidl_return) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(volumeUuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); std::lock_guard<std::recursive_mutex> lock(mLock); const char* volume_uuid = volumeUuid ? volumeUuid->c_str() : nullptr; const char* package_name = packageName.c_str(); if (volume_uuid && strcmp(volume_uuid, kTestUuid)) { return exception(binder::Status::EX_ILLEGAL_ARGUMENT, StringPrintf("volumeUuid must be null or \"%s\", got: %s", kTestUuid, volume_uuid)); } binder::Status res = ok(); // Default result to 0, it will be populated with inode of ce data snapshot // if FLAG_STORAGE_CE has been passed. if (_aidl_return != nullptr) *_aidl_return = 0; bool clear_ce_on_exit = false; bool clear_de_on_exit = false; Loading Loading @@ -862,6 +879,16 @@ binder::Status InstalldNativeService::snapshotAppData( clear_ce_on_exit = true; return res; } if (_aidl_return != nullptr) { auto ce_snapshot_path = create_data_misc_ce_rollback_package_path(volume_uuid, user, package_name); rc = get_path_inode(ce_snapshot_path, reinterpret_cast<ino_t*>(_aidl_return)); if (rc != 0) { res = error(rc, "Failed to get_path_inode for " + ce_snapshot_path); clear_ce_on_exit = true; return res; } } } return res; Loading @@ -872,17 +899,13 @@ binder::Status InstalldNativeService::restoreAppDataSnapshot( const int32_t appId, const int64_t ceDataInode, const std::string& seInfo, const int32_t user, int32_t storageFlags) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(volumeUuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); std::lock_guard<std::recursive_mutex> lock(mLock); const char* volume_uuid = volumeUuid ? volumeUuid->c_str() : nullptr; const char* package_name = packageName.c_str(); if (volume_uuid && strcmp(volume_uuid, kTestUuid)) { return exception(binder::Status::EX_ILLEGAL_ARGUMENT, StringPrintf("volumeUuid must be null or \"%s\", got: %s", kTestUuid, volume_uuid)); } auto from_ce = create_data_misc_ce_rollback_package_path(volume_uuid, user, package_name); auto from_de = create_data_misc_de_rollback_package_path(volume_uuid, Loading Loading @@ -933,6 +956,39 @@ binder::Status InstalldNativeService::restoreAppDataSnapshot( return restoreconAppData(volumeUuid, packageName, user, storageFlags, appId, seInfo); } binder::Status InstalldNativeService::destroyAppDataSnapshot( const std::unique_ptr<std::string> &volumeUuid, const std::string& packageName, const int32_t user, const int64_t ceSnapshotInode, int32_t storageFlags) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(volumeUuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); std::lock_guard<std::recursive_mutex> lock(mLock); const char* volume_uuid = volumeUuid ? volumeUuid->c_str() : nullptr; const char* package_name = packageName.c_str(); if (storageFlags & FLAG_STORAGE_DE) { auto de_snapshot_path = create_data_misc_de_rollback_package_path(volume_uuid, user, package_name); int res = delete_dir_contents_and_dir(de_snapshot_path, true /* ignore_if_missing */); if (res != 0) { return error(res, "Failed clearing snapshot " + de_snapshot_path); } } if (storageFlags & FLAG_STORAGE_CE) { auto ce_snapshot_path = create_data_misc_ce_rollback_package_path(volume_uuid, user, package_name, ceSnapshotInode); int res = delete_dir_contents_and_dir(ce_snapshot_path, true /* ignore_if_missing */); if (res != 0) { return error(res, "Failed clearing snapshot " + ce_snapshot_path); } } return ok(); } binder::Status InstalldNativeService::moveCompleteApp(const std::unique_ptr<std::string>& fromUuid, const std::unique_ptr<std::string>& toUuid, const std::string& packageName, const std::string& dataAppName, int32_t appId, const std::string& seInfo, Loading
cmds/installd/InstalldNativeService.h +5 −1 Original line number Diff line number Diff line Loading @@ -61,10 +61,14 @@ public: binder::Status fixupAppData(const std::unique_ptr<std::string>& uuid, int32_t flags); binder::Status snapshotAppData(const std::unique_ptr<std::string>& volumeUuid, const std::string& packageName, const int32_t user, int32_t storageFlags); const std::string& packageName, const int32_t user, int32_t storageFlags, int64_t* _aidl_return); binder::Status restoreAppDataSnapshot(const std::unique_ptr<std::string>& volumeUuid, const std::string& packageName, const int32_t appId, const int64_t ceDataInode, const std::string& seInfo, const int32_t user, int32_t storageFlags); binder::Status destroyAppDataSnapshot(const std::unique_ptr<std::string> &volumeUuid, const std::string& packageName, const int32_t user, const int64_t ceSnapshotInode, int32_t storageFlags); binder::Status getAppSize(const std::unique_ptr<std::string>& uuid, const std::vector<std::string>& packageNames, int32_t userId, int32_t flags, Loading
cmds/installd/binder/android/os/IInstalld.aidl +3 −5 Original line number Diff line number Diff line Loading @@ -105,14 +105,12 @@ interface IInstalld { int userId, int appId, @utf8InCpp String profileName, @utf8InCpp String codePath, @nullable @utf8InCpp String dexMetadata); void snapshotAppData(@nullable @utf8InCpp String uuid, in @utf8InCpp String packageName, long snapshotAppData(@nullable @utf8InCpp String uuid, in @utf8InCpp String packageName, int userId, int storageFlags); void restoreAppDataSnapshot(@nullable @utf8InCpp String uuid, in @utf8InCpp String packageName, int appId, long ceDataInode, @utf8InCpp String seInfo, int user, int storageflags); // TODO(narayan) we need an API to delete the app data snapshot as well. // void destroyAppDataSnapshot(@nullable @utf8InCpp String uuid, // in @utf8InCpp String packageName, int userId, int storageFlags); void destroyAppDataSnapshot(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName, int userId, long ceSnapshotInode, int storageFlags); const int FLAG_STORAGE_DE = 0x1; const int FLAG_STORAGE_CE = 0x2; Loading
cmds/installd/tests/installd_service_test.cpp +145 −8 Original line number Diff line number Diff line Loading @@ -292,8 +292,13 @@ TEST_F(ServiceTest, CreateAppDataSnapshot) { 0700, 10000, 20000, false /* follow_symlinks */)); // Request a snapshot of the CE content but not the DE content. int64_t ce_snapshot_inode; ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"), "com.foo", 0, FLAG_STORAGE_CE)); "com.foo", 0, FLAG_STORAGE_CE, &ce_snapshot_inode)); struct stat buf; memset(&buf, 0, sizeof(buf)); ASSERT_EQ(0, stat((rollback_ce_dir + "/com.foo").c_str(), &buf)); ASSERT_EQ(ce_snapshot_inode, (int64_t) buf.st_ino); std::string ce_content, de_content; // At this point, we should have the CE content but not the DE content. Loading @@ -311,7 +316,9 @@ TEST_F(ServiceTest, CreateAppDataSnapshot) { // Request a snapshot of the DE content but not the CE content. ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"), "com.foo", 0, FLAG_STORAGE_DE)); "com.foo", 0, FLAG_STORAGE_DE, &ce_snapshot_inode)); // Only DE content snapshot was requested. ASSERT_EQ(ce_snapshot_inode, 0); // At this point, both the CE as well as the DE content should be fully // populated. Loading @@ -330,7 +337,7 @@ TEST_F(ServiceTest, CreateAppDataSnapshot) { // Request a snapshot of both the CE as well as the DE content. ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"), "com.foo", 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE)); "com.foo", 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE, nullptr)); ASSERT_TRUE(android::base::ReadFileToString( rollback_ce_dir + "/com.foo/file1", &ce_content, false /* follow_symlinks */)); Loading @@ -356,10 +363,13 @@ TEST_F(ServiceTest, CreateAppDataSnapshot_AppDataAbsent) { auto scope_guard = android::base::make_scope_guard(deleter); int64_t ce_snapshot_inode; ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"), "com.foo", 0, FLAG_STORAGE_CE)); "com.foo", 0, FLAG_STORAGE_CE, &ce_snapshot_inode)); ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"), "com.foo", 0, FLAG_STORAGE_DE)); "com.foo", 0, FLAG_STORAGE_DE, nullptr)); // No CE content snapshot was performed. ASSERT_EQ(ce_snapshot_inode, 0); // The snapshot calls must succeed but there should be no snapshot // created. Loading Loading @@ -409,7 +419,7 @@ TEST_F(ServiceTest, CreateAppDataSnapshot_ClearsExistingSnapshot) { 0700, 10000, 20000, false /* follow_symlinks */)); ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"), "com.foo", 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE)); "com.foo", 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE, nullptr)); // Previous snapshot (with data for file1) must be cleared. struct stat sb; Loading @@ -435,7 +445,7 @@ TEST_F(ServiceTest, SnapshotAppData_WrongVolumeUuid) { auto scope_guard = android::base::make_scope_guard(deleter); EXPECT_BINDER_FAIL(service->snapshotAppData(std::make_unique<std::string>("FOO"), "com.foo", 0, FLAG_STORAGE_DE)); "com.foo", 0, FLAG_STORAGE_DE, nullptr)); } TEST_F(ServiceTest, CreateAppDataSnapshot_ClearsCache) { Loading Loading @@ -484,7 +494,7 @@ TEST_F(ServiceTest, CreateAppDataSnapshot_ClearsCache) { "TEST_CONTENT_DE", fake_package_de_code_cache_path + "/file1", 0700, 10000, 20000, false /* follow_symlinks */)); ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"), "com.foo", 0, FLAG_STORAGE_CE | FLAG_STORAGE_DE)); "com.foo", 0, FLAG_STORAGE_CE | FLAG_STORAGE_DE, nullptr)); // The snapshot call must clear cache. struct stat sb; ASSERT_EQ(-1, stat((fake_package_ce_cache_path + "/file1").c_str(), &sb)); Loading Loading @@ -546,6 +556,133 @@ TEST_F(ServiceTest, RestoreAppDataSnapshot) { ASSERT_EQ("DE_RESTORE_CONTENT", de_content); } TEST_F(ServiceTest, CreateSnapshotThenDestroyIt) { auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0); auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0); ASSERT_TRUE(mkdirs(rollback_ce_dir, 700)); ASSERT_TRUE(mkdirs(rollback_de_dir, 700)); auto fake_package_ce_path = create_data_user_ce_package_path("TEST", 0, "com.foo"); auto fake_package_de_path = create_data_user_de_package_path("TEST", 0, "com.foo"); ASSERT_TRUE(mkdirs(fake_package_ce_path, 700)); ASSERT_TRUE(mkdirs(fake_package_de_path, 700)); auto deleter = [&rollback_ce_dir, &rollback_de_dir, &fake_package_ce_path, &fake_package_de_path]() { delete_dir_contents(rollback_ce_dir, true); delete_dir_contents(rollback_de_dir, true); delete_dir_contents(fake_package_ce_path, true); delete_dir_contents(fake_package_de_path, true); rmdir(rollback_ce_dir.c_str()); rmdir(rollback_de_dir.c_str()); }; auto scope_guard = android::base::make_scope_guard(deleter); // Prepare data for snapshot. ASSERT_TRUE(android::base::WriteStringToFile( "TEST_CONTENT_CE", fake_package_ce_path + "/file1", 0700, 10000, 20000, false /* follow_symlinks */)); ASSERT_TRUE(android::base::WriteStringToFile( "TEST_CONTENT_DE", fake_package_de_path + "/file1", 0700, 10000, 20000, false /* follow_symlinks */)); int64_t ce_snapshot_inode; // Request a snapshot of both the CE as well as the DE content. ASSERT_TRUE(service->snapshotAppData(std::make_unique<std::string>("TEST"), "com.foo", 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE, &ce_snapshot_inode).isOk()); // Because CE data snapshot was requested, ce_snapshot_inode can't be null. ASSERT_NE(0, ce_snapshot_inode); // Check snapshot is there. struct stat sb; ASSERT_EQ(0, stat((rollback_ce_dir + "/com.foo").c_str(), &sb)); ASSERT_EQ(0, stat((rollback_de_dir + "/com.foo").c_str(), &sb)); ASSERT_TRUE(service->destroyAppDataSnapshot(std::make_unique<std::string>("TEST"), "com.foo", 0, ce_snapshot_inode, FLAG_STORAGE_DE | FLAG_STORAGE_CE).isOk()); // Check snapshot is deleted. ASSERT_EQ(-1, stat((rollback_ce_dir + "/com.foo").c_str(), &sb)); ASSERT_EQ(-1, stat((rollback_de_dir + "/com.foo").c_str(), &sb)); } TEST_F(ServiceTest, DestroyAppDataSnapshot_CeSnapshotInodeIsZero) { auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0); auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0); ASSERT_TRUE(mkdirs(rollback_ce_dir, 700)); ASSERT_TRUE(mkdirs(rollback_de_dir, 700)); auto fake_package_ce_path = create_data_user_ce_package_path("TEST", 0, "com.foo"); auto fake_package_de_path = create_data_user_de_package_path("TEST", 0, "com.foo"); ASSERT_TRUE(mkdirs(fake_package_ce_path, 700)); ASSERT_TRUE(mkdirs(fake_package_de_path, 700)); auto deleter = [&rollback_ce_dir, &rollback_de_dir, &fake_package_ce_path, &fake_package_de_path]() { delete_dir_contents(rollback_ce_dir, true); delete_dir_contents(rollback_de_dir, true); delete_dir_contents(fake_package_ce_path, true); delete_dir_contents(fake_package_de_path, true); rmdir(rollback_ce_dir.c_str()); rmdir(rollback_de_dir.c_str()); }; auto scope_guard = android::base::make_scope_guard(deleter); // Create a snapshot ASSERT_TRUE(mkdirs(rollback_ce_dir + "/com.foo/", 700)); ASSERT_TRUE(mkdirs(rollback_de_dir + "/com.foo/", 700)); ASSERT_TRUE(android::base::WriteStringToFile( "CE_RESTORE_CONTENT", rollback_ce_dir + "/com.foo/file1", 0700, 10000, 20000, false /* follow_symlinks */)); ASSERT_TRUE(android::base::WriteStringToFile( "DE_RESTORE_CONTENT", rollback_de_dir + "/com.foo/file1", 0700, 10000, 20000, false /* follow_symlinks */)); ASSERT_TRUE(service->destroyAppDataSnapshot(std::make_unique<std::string>("TEST"), "com.foo", 0, 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE).isOk()); // Check snapshot is deleted. struct stat sb; ASSERT_EQ(-1, stat((rollback_ce_dir + "/com.foo").c_str(), &sb)); ASSERT_EQ(-1, stat((rollback_de_dir + "/com.foo").c_str(), &sb)); // Check that deleting already deleted snapshot is no-op. ASSERT_TRUE(service->destroyAppDataSnapshot(std::make_unique<std::string>("TEST"), "com.foo", 0, 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE).isOk()); } TEST_F(ServiceTest, DestroyAppDataSnapshot_WrongVolumeUuid) { // Setup rollback data to make sure that test fails due to wrong volumeUuid // being passed, not because of some other reason. auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0); auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0); ASSERT_TRUE(mkdirs(rollback_ce_dir, 700)); ASSERT_TRUE(mkdirs(rollback_de_dir, 700)); auto fake_package_ce_path = create_data_user_ce_package_path("TEST", 0, "com.foo"); auto fake_package_de_path = create_data_user_de_package_path("TEST", 0, "com.foo"); ASSERT_TRUE(mkdirs(fake_package_ce_path, 700)); ASSERT_TRUE(mkdirs(fake_package_de_path, 700)); auto deleter = [&rollback_ce_dir, &rollback_de_dir, &fake_package_ce_path, &fake_package_de_path]() { delete_dir_contents(rollback_ce_dir, true); delete_dir_contents(rollback_de_dir, true); delete_dir_contents(fake_package_ce_path, true); delete_dir_contents(fake_package_de_path, true); rmdir(rollback_ce_dir.c_str()); rmdir(rollback_de_dir.c_str()); }; auto scope_guard = android::base::make_scope_guard(deleter); ASSERT_FALSE(service->destroyAppDataSnapshot(std::make_unique<std::string>("BAR"), "com.foo", 0, 0, FLAG_STORAGE_DE).isOk()); } TEST_F(ServiceTest, RestoreAppDataSnapshot_WrongVolumeUuid) { // Setup rollback data to make sure that fails due to wrong volumeUuid being Loading
cmds/installd/tests/installd_utils_test.cpp +24 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ #include <string.h> #include <android-base/logging.h> #include <android-base/scopeguard.h> #include <gtest/gtest.h> #include "InstalldNativeService.h" Loading Loading @@ -565,6 +566,29 @@ TEST_F(UtilsTest, TestRollbackPaths) { EXPECT_EQ("/data/misc_de/10/rollback", create_data_misc_de_rollback_path(nullptr, 10)); EXPECT_EQ("/data/misc_ce/0/rollback/com.foo", create_data_misc_ce_rollback_package_path(nullptr, 0, "com.foo", 0)); EXPECT_EQ("/data/misc_ce/0/rollback/com.foo", create_data_misc_ce_rollback_package_path(nullptr, 0, "com.foo", 239)); auto rollback_ce_package_path = create_data_misc_ce_rollback_package_path(nullptr, 0, "com.foo"); auto deleter = [&rollback_ce_package_path]() { delete_dir_contents_and_dir(rollback_ce_package_path, true /* ignore_if_missing */); }; auto scope_guard = android::base::make_scope_guard(deleter); ASSERT_NE(-1, mkdir(rollback_ce_package_path.c_str(), 700)); ino_t ce_data_inode; ASSERT_EQ(0, get_path_inode(rollback_ce_package_path, &ce_data_inode)); EXPECT_EQ("/data/misc_ce/0/rollback/com.foo", create_data_misc_ce_rollback_package_path(nullptr, 0, "com.foo", ce_data_inode)); // Check that path defined by inode is picked even if it's not the same as // the fallback one. EXPECT_EQ("/data/misc_ce/0/rollback/com.foo", create_data_misc_ce_rollback_package_path(nullptr, 0, "com.bar", ce_data_inode)); // These last couple of cases are never exercised in production because we // only snapshot apps in the primary data partition. Exercise them here for // the sake of completeness. Loading