Loading cmds/installd/InstalldNativeService.cpp +160 −32 Original line number Diff line number Diff line Loading @@ -41,6 +41,7 @@ #include <android-base/logging.h> #include <android-base/properties.h> #include <android-base/scopeguard.h> #include <android-base/stringprintf.h> #include <android-base/strings.h> #include <android-base/unique_fd.h> Loading Loading @@ -746,6 +747,162 @@ binder::Status InstalldNativeService::fixupAppData(const std::unique_ptr<std::st return ok(); } static int32_t copy_directory_recursive(const char* from, const char* to) { char *argv[] = { (char*) kCpPath, (char*) "-F", /* delete any existing destination file first (--remove-destination) */ (char*) "-p", /* preserve timestamps, ownership, and permissions */ (char*) "-R", /* recurse into subdirectories (DEST must be a directory) */ (char*) "-P", /* Do not follow symlinks [default] */ (char*) "-d", /* don't dereference symlinks */ (char*) from, (char*) to }; LOG(DEBUG) << "Copying " << from << " to " << to; return android_fork_execvp(ARRAY_SIZE(argv), argv, nullptr, false, true); } // TODO(narayan): We should pass through the ceDataInode so that we can call // clearAppData(FLAG_CLEAR_CACHE_ONLY | FLAG_CLEAR_CODE_CACHE before we commence // the copy. // // TODO(narayan): For snapshotAppData as well as restoreAppDataSnapshot, we // should validate that volumeUuid is either nullptr or TEST, we won't support // anything else. // // TODO(narayan): We need to be clearer about the expected behaviour for the // case where a snapshot already exists. We either need to clear the contents // of the snapshot directory before we make a copy, or we need to ensure that // the caller always clears it before requesting a snapshot. binder::Status InstalldNativeService::snapshotAppData( const std::unique_ptr<std::string>& volumeUuid, const std::string& packageName, int32_t user, int32_t storageFlags) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(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(); binder::Status res = ok(); bool clear_ce_on_exit = false; bool clear_de_on_exit = false; auto deleter = [&clear_ce_on_exit, &clear_de_on_exit, &volume_uuid, &user, &package_name] { if (clear_de_on_exit) { auto to = create_data_misc_de_rollback_package_path(volume_uuid, user, package_name); if (delete_dir_contents(to.c_str(), 1, nullptr) != 0) { LOG(WARNING) << "Failed to delete app data snapshot: " << to; } } if (clear_ce_on_exit) { auto to = create_data_misc_ce_rollback_package_path(volume_uuid, user, package_name); if (delete_dir_contents(to.c_str(), 1, nullptr) != 0) { LOG(WARNING) << "Failed to delete app data snapshot: " << to; } } }; auto scope_guard = android::base::make_scope_guard(deleter); // The app may not have any data at all, in which case it's OK to skip here. auto from_ce = create_data_user_ce_package_path(volume_uuid, user, package_name); if (access(from_ce.c_str(), F_OK) != 0) { LOG(INFO) << "Missing source " << from_ce; return ok(); } if (storageFlags & FLAG_STORAGE_DE) { auto from = create_data_user_de_package_path(volume_uuid, user, package_name); auto to = create_data_misc_de_rollback_path(volume_uuid, user); int rc = copy_directory_recursive(from.c_str(), to.c_str()); if (rc != 0) { res = error(rc, "Failed copying " + from + " to " + to); clear_de_on_exit = true; return res; } } if (storageFlags & FLAG_STORAGE_CE) { auto from = create_data_user_ce_package_path(volume_uuid, user, package_name); auto to = create_data_misc_ce_rollback_path(volume_uuid, user); int rc = copy_directory_recursive(from.c_str(), to.c_str()); if (rc != 0) { res = error(rc, "Failed copying " + from + " to " + to); clear_ce_on_exit = true; return res; } } return res; } binder::Status InstalldNativeService::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) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(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(); 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, user, package_name); const bool needs_ce_rollback = (storageFlags & FLAG_STORAGE_CE) && (access(from_ce.c_str(), F_OK) == 0); const bool needs_de_rollback = (storageFlags & FLAG_STORAGE_DE) && (access(from_de.c_str(), F_OK) == 0); if (!needs_ce_rollback && !needs_de_rollback) { return ok(); } // We know we're going to rollback one of the CE or DE data, so we clear // application data first. Note that it's possible that we're asked to // restore both CE & DE data but that one of the restores fail. Leaving the // app with no data in those cases is arguably better than leaving the app // with mismatched / stale data. LOG(INFO) << "Clearing app data for " << packageName << " to restore snapshot."; binder::Status res = clearAppData(volumeUuid, packageName, user, storageFlags, ceDataInode); if (!res.isOk()) { return res; } if (needs_ce_rollback) { auto to_ce = create_data_user_ce_path(volume_uuid, user); int rc = copy_directory_recursive(from_ce.c_str(), to_ce.c_str()); if (rc != 0) { res = error(rc, "Failed copying " + from_ce + " to " + to_ce); return res; } } if (needs_de_rollback) { auto to_de = create_data_user_de_path(volume_uuid, user); int rc = copy_directory_recursive(from_de.c_str(), to_de.c_str()); if (rc != 0) { // TODO(narayan): Should we clear clear the rolled back CE data if // something goes wrong here ? We're choosing between leaving the // app devoid of all its data or with just its ce data installed. res = error(rc, "Failed copying " + from_de + " to " + to_de); return res; } } // Finally, restore the SELinux label on the app data. return restoreconAppData(volumeUuid, packageName, user, storageFlags, appId, seInfo); } 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 @@ -770,19 +927,7 @@ binder::Status InstalldNativeService::moveCompleteApp(const std::unique_ptr<std: auto to = create_data_app_package_path(to_uuid, data_app_name); auto to_parent = create_data_app_path(to_uuid); char *argv[] = { (char*) kCpPath, (char*) "-F", /* delete any existing destination file first (--remove-destination) */ (char*) "-p", /* preserve timestamps, ownership, and permissions */ (char*) "-R", /* recurse into subdirectories (DEST must be a directory) */ (char*) "-P", /* Do not follow symlinks [default] */ (char*) "-d", /* don't dereference symlinks */ (char*) from.c_str(), (char*) to_parent.c_str() }; LOG(DEBUG) << "Copying " << from << " to " << to; int rc = android_fork_execvp(ARRAY_SIZE(argv), argv, nullptr, false, true); int rc = copy_directory_recursive(from.c_str(), to_parent.c_str()); if (rc != 0) { res = error(rc, "Failed copying " + from + " to " + to); goto fail; Loading Loading @@ -810,25 +955,11 @@ binder::Status InstalldNativeService::moveCompleteApp(const std::unique_ptr<std: goto fail; } char *argv[] = { (char*) kCpPath, (char*) "-F", /* delete any existing destination file first (--remove-destination) */ (char*) "-p", /* preserve timestamps, ownership, and permissions */ (char*) "-R", /* recurse into subdirectories (DEST must be a directory) */ (char*) "-P", /* Do not follow symlinks [default] */ (char*) "-d", /* don't dereference symlinks */ nullptr, nullptr }; { auto from = create_data_user_de_package_path(from_uuid, user, package_name); auto to = create_data_user_de_path(to_uuid, user); argv[6] = (char*) from.c_str(); argv[7] = (char*) to.c_str(); LOG(DEBUG) << "Copying " << from << " to " << to; int rc = android_fork_execvp(ARRAY_SIZE(argv), argv, nullptr, false, true); int rc = copy_directory_recursive(from.c_str(), to.c_str()); if (rc != 0) { res = error(rc, "Failed copying " + from + " to " + to); goto fail; Loading @@ -837,11 +968,8 @@ binder::Status InstalldNativeService::moveCompleteApp(const std::unique_ptr<std: { auto from = create_data_user_ce_package_path(from_uuid, user, package_name); auto to = create_data_user_ce_path(to_uuid, user); argv[6] = (char*) from.c_str(); argv[7] = (char*) to.c_str(); LOG(DEBUG) << "Copying " << from << " to " << to; int rc = android_fork_execvp(ARRAY_SIZE(argv), argv, nullptr, false, true); int rc = copy_directory_recursive(from.c_str(), to.c_str()); if (rc != 0) { res = error(rc, "Failed copying " + from + " to " + to); goto fail; Loading cmds/installd/InstalldNativeService.h +6 −0 Original line number Diff line number Diff line Loading @@ -60,6 +60,12 @@ 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); 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 getAppSize(const std::unique_ptr<std::string>& uuid, const std::vector<std::string>& packageNames, int32_t userId, int32_t flags, int32_t appId, const std::vector<int64_t>& ceDataInodes, Loading cmds/installd/binder/android/os/IInstalld.aidl +12 −0 Original line number Diff line number Diff line Loading @@ -104,4 +104,16 @@ interface IInstalld { boolean prepareAppProfile(@utf8InCpp String packageName, int userId, int appId, @utf8InCpp String profileName, @utf8InCpp String codePath, @nullable @utf8InCpp String dexMetadata); void 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); const int FLAG_STORAGE_DE = 0x1; const int FLAG_STORAGE_CE = 0x2; } cmds/installd/tests/installd_service_test.cpp +177 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,8 @@ #include <sys/xattr.h> #include <android-base/logging.h> #include <android-base/file.h> #include <android-base/scopeguard.h> #include <android-base/stringprintf.h> #include <cutils/properties.h> #include <gtest/gtest.h> Loading Loading @@ -240,5 +242,180 @@ TEST_F(ServiceTest, CalculateCache) { EXPECT_EQ("/data/dalvik-cache/isa/path@to@file.apk@classes.dex", std::string(buf)); } static bool mkdirs(const std::string& path, mode_t mode) { struct stat sb; if (stat(path.c_str(), &sb) != -1 && S_ISDIR(sb.st_mode)) { return true; } if (!mkdirs(android::base::Dirname(path), mode)) { return false; } return (::mkdir(path.c_str(), mode) != -1); } TEST_F(ServiceTest, CreateAppDataSnapshot) { 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_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 */)); // Request a snapshot of the CE content but not the DE content. ASSERT_TRUE(service->snapshotAppData(std::make_unique<std::string>("TEST"), "com.foo", 0, FLAG_STORAGE_CE).isOk()); std::string ce_content, de_content; // At this point, we should have the CE content but not the DE content. ASSERT_TRUE(android::base::ReadFileToString( rollback_ce_dir + "/com.foo/file1", &ce_content, false /* follow_symlinks */)); ASSERT_FALSE(android::base::ReadFileToString( rollback_de_dir + "/com.foo/file1", &de_content, false /* follow_symlinks */)); ASSERT_EQ("TEST_CONTENT_CE", ce_content); // Modify the CE content, so we can assert later that it's reflected // in the snapshot. ASSERT_TRUE(android::base::WriteStringToFile( "TEST_CONTENT_CE_MODIFIED", fake_package_ce_path + "/file1", 0700, 10000, 20000, false /* follow_symlinks */)); // Request a snapshot of the DE content but not the CE content. ASSERT_TRUE(service->snapshotAppData(std::make_unique<std::string>("TEST"), "com.foo", 0, FLAG_STORAGE_DE).isOk()); // At this point, both the CE as well as the DE content should be fully // populated. ASSERT_TRUE(android::base::ReadFileToString( rollback_ce_dir + "/com.foo/file1", &ce_content, false /* follow_symlinks */)); ASSERT_TRUE(android::base::ReadFileToString( rollback_de_dir + "/com.foo/file1", &de_content, false /* follow_symlinks */)); ASSERT_EQ("TEST_CONTENT_CE", ce_content); ASSERT_EQ("TEST_CONTENT_DE", de_content); // Modify the DE content, so we can assert later that it's reflected // in our final snapshot. ASSERT_TRUE(android::base::WriteStringToFile( "TEST_CONTENT_DE_MODIFIED", fake_package_de_path + "/file1", 0700, 10000, 20000, false /* follow_symlinks */)); // 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).isOk()); ASSERT_TRUE(android::base::ReadFileToString( rollback_ce_dir + "/com.foo/file1", &ce_content, false /* follow_symlinks */)); ASSERT_TRUE(android::base::ReadFileToString( rollback_de_dir + "/com.foo/file1", &de_content, false /* follow_symlinks */)); ASSERT_EQ("TEST_CONTENT_CE_MODIFIED", ce_content); ASSERT_EQ("TEST_CONTENT_DE_MODIFIED", de_content); } TEST_F(ServiceTest, CreateAppDataSnapshot_AppDataAbsent) { 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 deleter = [&rollback_ce_dir, &rollback_de_dir]() { delete_dir_contents(rollback_ce_dir, true); delete_dir_contents(rollback_de_dir, true); rmdir(rollback_ce_dir.c_str()); rmdir(rollback_de_dir.c_str()); }; auto scope_guard = android::base::make_scope_guard(deleter); ASSERT_TRUE(service->snapshotAppData(std::make_unique<std::string>("TEST"), "com.foo", 0, FLAG_STORAGE_CE).isOk()); ASSERT_TRUE(service->snapshotAppData(std::make_unique<std::string>("TEST"), "com.foo", 0, FLAG_STORAGE_DE).isOk()); // The snapshot calls must succeed but there should be no snapshot // created. 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)); } TEST_F(ServiceTest, RestoreAppDataSnapshot) { 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); // Write contents to the rollback location. We'll write the same files to the // app data location and make sure the restore has overwritten them. 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(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 */)); ASSERT_TRUE(service->restoreAppDataSnapshot(std::make_unique<std::string>("TEST"), "com.foo", 10000, -1, "", 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE).isOk()); std::string ce_content, de_content; ASSERT_TRUE(android::base::ReadFileToString( fake_package_ce_path + "/file1", &ce_content, false /* follow_symlinks */)); ASSERT_TRUE(android::base::ReadFileToString( fake_package_de_path + "/file1", &de_content, false /* follow_symlinks */)); ASSERT_EQ("CE_RESTORE_CONTENT", ce_content); ASSERT_EQ("DE_RESTORE_CONTENT", de_content); } } // namespace installd } // namespace android cmds/installd/tests/installd_utils_test.cpp +30 −0 Original line number Diff line number Diff line Loading @@ -544,5 +544,35 @@ TEST_F(UtilsTest, MatchExtension_Invalid) { EXPECT_EQ(0, MatchExtension("docx")); } TEST_F(UtilsTest, TestRollbackPaths) { EXPECT_EQ("/data/misc_ce/0/rollback/com.foo", create_data_misc_ce_rollback_package_path(nullptr, 0, "com.foo")); EXPECT_EQ("/data/misc_ce/10/rollback/com.foo", create_data_misc_ce_rollback_package_path(nullptr, 10, "com.foo")); EXPECT_EQ("/data/misc_de/0/rollback/com.foo", create_data_misc_de_rollback_package_path(nullptr, 0, "com.foo")); EXPECT_EQ("/data/misc_de/10/rollback/com.foo", create_data_misc_de_rollback_package_path(nullptr, 10, "com.foo")); EXPECT_EQ("/data/misc_ce/0/rollback", create_data_misc_ce_rollback_path(nullptr, 0)); EXPECT_EQ("/data/misc_ce/10/rollback", create_data_misc_ce_rollback_path(nullptr, 10)); EXPECT_EQ("/data/misc_de/0/rollback", create_data_misc_de_rollback_path(nullptr, 0)); EXPECT_EQ("/data/misc_de/10/rollback", create_data_misc_de_rollback_path(nullptr, 10)); // 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. EXPECT_EQ("/mnt/expand/57f8f4bc-abf4-655f-bf67-946fc0f9f25b/misc_ce/0/rollback/com.example", create_data_misc_ce_rollback_package_path("57f8f4bc-abf4-655f-bf67-946fc0f9f25b", 0, "com.example")); EXPECT_EQ("/mnt/expand/57f8f4bc-abf4-655f-bf67-946fc0f9f25b/misc_de/0/rollback/com.example", create_data_misc_de_rollback_package_path("57f8f4bc-abf4-655f-bf67-946fc0f9f25b", 0, "com.example")); } } // namespace installd } // namespace android Loading
cmds/installd/InstalldNativeService.cpp +160 −32 Original line number Diff line number Diff line Loading @@ -41,6 +41,7 @@ #include <android-base/logging.h> #include <android-base/properties.h> #include <android-base/scopeguard.h> #include <android-base/stringprintf.h> #include <android-base/strings.h> #include <android-base/unique_fd.h> Loading Loading @@ -746,6 +747,162 @@ binder::Status InstalldNativeService::fixupAppData(const std::unique_ptr<std::st return ok(); } static int32_t copy_directory_recursive(const char* from, const char* to) { char *argv[] = { (char*) kCpPath, (char*) "-F", /* delete any existing destination file first (--remove-destination) */ (char*) "-p", /* preserve timestamps, ownership, and permissions */ (char*) "-R", /* recurse into subdirectories (DEST must be a directory) */ (char*) "-P", /* Do not follow symlinks [default] */ (char*) "-d", /* don't dereference symlinks */ (char*) from, (char*) to }; LOG(DEBUG) << "Copying " << from << " to " << to; return android_fork_execvp(ARRAY_SIZE(argv), argv, nullptr, false, true); } // TODO(narayan): We should pass through the ceDataInode so that we can call // clearAppData(FLAG_CLEAR_CACHE_ONLY | FLAG_CLEAR_CODE_CACHE before we commence // the copy. // // TODO(narayan): For snapshotAppData as well as restoreAppDataSnapshot, we // should validate that volumeUuid is either nullptr or TEST, we won't support // anything else. // // TODO(narayan): We need to be clearer about the expected behaviour for the // case where a snapshot already exists. We either need to clear the contents // of the snapshot directory before we make a copy, or we need to ensure that // the caller always clears it before requesting a snapshot. binder::Status InstalldNativeService::snapshotAppData( const std::unique_ptr<std::string>& volumeUuid, const std::string& packageName, int32_t user, int32_t storageFlags) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(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(); binder::Status res = ok(); bool clear_ce_on_exit = false; bool clear_de_on_exit = false; auto deleter = [&clear_ce_on_exit, &clear_de_on_exit, &volume_uuid, &user, &package_name] { if (clear_de_on_exit) { auto to = create_data_misc_de_rollback_package_path(volume_uuid, user, package_name); if (delete_dir_contents(to.c_str(), 1, nullptr) != 0) { LOG(WARNING) << "Failed to delete app data snapshot: " << to; } } if (clear_ce_on_exit) { auto to = create_data_misc_ce_rollback_package_path(volume_uuid, user, package_name); if (delete_dir_contents(to.c_str(), 1, nullptr) != 0) { LOG(WARNING) << "Failed to delete app data snapshot: " << to; } } }; auto scope_guard = android::base::make_scope_guard(deleter); // The app may not have any data at all, in which case it's OK to skip here. auto from_ce = create_data_user_ce_package_path(volume_uuid, user, package_name); if (access(from_ce.c_str(), F_OK) != 0) { LOG(INFO) << "Missing source " << from_ce; return ok(); } if (storageFlags & FLAG_STORAGE_DE) { auto from = create_data_user_de_package_path(volume_uuid, user, package_name); auto to = create_data_misc_de_rollback_path(volume_uuid, user); int rc = copy_directory_recursive(from.c_str(), to.c_str()); if (rc != 0) { res = error(rc, "Failed copying " + from + " to " + to); clear_de_on_exit = true; return res; } } if (storageFlags & FLAG_STORAGE_CE) { auto from = create_data_user_ce_package_path(volume_uuid, user, package_name); auto to = create_data_misc_ce_rollback_path(volume_uuid, user); int rc = copy_directory_recursive(from.c_str(), to.c_str()); if (rc != 0) { res = error(rc, "Failed copying " + from + " to " + to); clear_ce_on_exit = true; return res; } } return res; } binder::Status InstalldNativeService::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) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(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(); 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, user, package_name); const bool needs_ce_rollback = (storageFlags & FLAG_STORAGE_CE) && (access(from_ce.c_str(), F_OK) == 0); const bool needs_de_rollback = (storageFlags & FLAG_STORAGE_DE) && (access(from_de.c_str(), F_OK) == 0); if (!needs_ce_rollback && !needs_de_rollback) { return ok(); } // We know we're going to rollback one of the CE or DE data, so we clear // application data first. Note that it's possible that we're asked to // restore both CE & DE data but that one of the restores fail. Leaving the // app with no data in those cases is arguably better than leaving the app // with mismatched / stale data. LOG(INFO) << "Clearing app data for " << packageName << " to restore snapshot."; binder::Status res = clearAppData(volumeUuid, packageName, user, storageFlags, ceDataInode); if (!res.isOk()) { return res; } if (needs_ce_rollback) { auto to_ce = create_data_user_ce_path(volume_uuid, user); int rc = copy_directory_recursive(from_ce.c_str(), to_ce.c_str()); if (rc != 0) { res = error(rc, "Failed copying " + from_ce + " to " + to_ce); return res; } } if (needs_de_rollback) { auto to_de = create_data_user_de_path(volume_uuid, user); int rc = copy_directory_recursive(from_de.c_str(), to_de.c_str()); if (rc != 0) { // TODO(narayan): Should we clear clear the rolled back CE data if // something goes wrong here ? We're choosing between leaving the // app devoid of all its data or with just its ce data installed. res = error(rc, "Failed copying " + from_de + " to " + to_de); return res; } } // Finally, restore the SELinux label on the app data. return restoreconAppData(volumeUuid, packageName, user, storageFlags, appId, seInfo); } 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 @@ -770,19 +927,7 @@ binder::Status InstalldNativeService::moveCompleteApp(const std::unique_ptr<std: auto to = create_data_app_package_path(to_uuid, data_app_name); auto to_parent = create_data_app_path(to_uuid); char *argv[] = { (char*) kCpPath, (char*) "-F", /* delete any existing destination file first (--remove-destination) */ (char*) "-p", /* preserve timestamps, ownership, and permissions */ (char*) "-R", /* recurse into subdirectories (DEST must be a directory) */ (char*) "-P", /* Do not follow symlinks [default] */ (char*) "-d", /* don't dereference symlinks */ (char*) from.c_str(), (char*) to_parent.c_str() }; LOG(DEBUG) << "Copying " << from << " to " << to; int rc = android_fork_execvp(ARRAY_SIZE(argv), argv, nullptr, false, true); int rc = copy_directory_recursive(from.c_str(), to_parent.c_str()); if (rc != 0) { res = error(rc, "Failed copying " + from + " to " + to); goto fail; Loading Loading @@ -810,25 +955,11 @@ binder::Status InstalldNativeService::moveCompleteApp(const std::unique_ptr<std: goto fail; } char *argv[] = { (char*) kCpPath, (char*) "-F", /* delete any existing destination file first (--remove-destination) */ (char*) "-p", /* preserve timestamps, ownership, and permissions */ (char*) "-R", /* recurse into subdirectories (DEST must be a directory) */ (char*) "-P", /* Do not follow symlinks [default] */ (char*) "-d", /* don't dereference symlinks */ nullptr, nullptr }; { auto from = create_data_user_de_package_path(from_uuid, user, package_name); auto to = create_data_user_de_path(to_uuid, user); argv[6] = (char*) from.c_str(); argv[7] = (char*) to.c_str(); LOG(DEBUG) << "Copying " << from << " to " << to; int rc = android_fork_execvp(ARRAY_SIZE(argv), argv, nullptr, false, true); int rc = copy_directory_recursive(from.c_str(), to.c_str()); if (rc != 0) { res = error(rc, "Failed copying " + from + " to " + to); goto fail; Loading @@ -837,11 +968,8 @@ binder::Status InstalldNativeService::moveCompleteApp(const std::unique_ptr<std: { auto from = create_data_user_ce_package_path(from_uuid, user, package_name); auto to = create_data_user_ce_path(to_uuid, user); argv[6] = (char*) from.c_str(); argv[7] = (char*) to.c_str(); LOG(DEBUG) << "Copying " << from << " to " << to; int rc = android_fork_execvp(ARRAY_SIZE(argv), argv, nullptr, false, true); int rc = copy_directory_recursive(from.c_str(), to.c_str()); if (rc != 0) { res = error(rc, "Failed copying " + from + " to " + to); goto fail; Loading
cmds/installd/InstalldNativeService.h +6 −0 Original line number Diff line number Diff line Loading @@ -60,6 +60,12 @@ 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); 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 getAppSize(const std::unique_ptr<std::string>& uuid, const std::vector<std::string>& packageNames, int32_t userId, int32_t flags, int32_t appId, const std::vector<int64_t>& ceDataInodes, Loading
cmds/installd/binder/android/os/IInstalld.aidl +12 −0 Original line number Diff line number Diff line Loading @@ -104,4 +104,16 @@ interface IInstalld { boolean prepareAppProfile(@utf8InCpp String packageName, int userId, int appId, @utf8InCpp String profileName, @utf8InCpp String codePath, @nullable @utf8InCpp String dexMetadata); void 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); const int FLAG_STORAGE_DE = 0x1; const int FLAG_STORAGE_CE = 0x2; }
cmds/installd/tests/installd_service_test.cpp +177 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,8 @@ #include <sys/xattr.h> #include <android-base/logging.h> #include <android-base/file.h> #include <android-base/scopeguard.h> #include <android-base/stringprintf.h> #include <cutils/properties.h> #include <gtest/gtest.h> Loading Loading @@ -240,5 +242,180 @@ TEST_F(ServiceTest, CalculateCache) { EXPECT_EQ("/data/dalvik-cache/isa/path@to@file.apk@classes.dex", std::string(buf)); } static bool mkdirs(const std::string& path, mode_t mode) { struct stat sb; if (stat(path.c_str(), &sb) != -1 && S_ISDIR(sb.st_mode)) { return true; } if (!mkdirs(android::base::Dirname(path), mode)) { return false; } return (::mkdir(path.c_str(), mode) != -1); } TEST_F(ServiceTest, CreateAppDataSnapshot) { 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_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 */)); // Request a snapshot of the CE content but not the DE content. ASSERT_TRUE(service->snapshotAppData(std::make_unique<std::string>("TEST"), "com.foo", 0, FLAG_STORAGE_CE).isOk()); std::string ce_content, de_content; // At this point, we should have the CE content but not the DE content. ASSERT_TRUE(android::base::ReadFileToString( rollback_ce_dir + "/com.foo/file1", &ce_content, false /* follow_symlinks */)); ASSERT_FALSE(android::base::ReadFileToString( rollback_de_dir + "/com.foo/file1", &de_content, false /* follow_symlinks */)); ASSERT_EQ("TEST_CONTENT_CE", ce_content); // Modify the CE content, so we can assert later that it's reflected // in the snapshot. ASSERT_TRUE(android::base::WriteStringToFile( "TEST_CONTENT_CE_MODIFIED", fake_package_ce_path + "/file1", 0700, 10000, 20000, false /* follow_symlinks */)); // Request a snapshot of the DE content but not the CE content. ASSERT_TRUE(service->snapshotAppData(std::make_unique<std::string>("TEST"), "com.foo", 0, FLAG_STORAGE_DE).isOk()); // At this point, both the CE as well as the DE content should be fully // populated. ASSERT_TRUE(android::base::ReadFileToString( rollback_ce_dir + "/com.foo/file1", &ce_content, false /* follow_symlinks */)); ASSERT_TRUE(android::base::ReadFileToString( rollback_de_dir + "/com.foo/file1", &de_content, false /* follow_symlinks */)); ASSERT_EQ("TEST_CONTENT_CE", ce_content); ASSERT_EQ("TEST_CONTENT_DE", de_content); // Modify the DE content, so we can assert later that it's reflected // in our final snapshot. ASSERT_TRUE(android::base::WriteStringToFile( "TEST_CONTENT_DE_MODIFIED", fake_package_de_path + "/file1", 0700, 10000, 20000, false /* follow_symlinks */)); // 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).isOk()); ASSERT_TRUE(android::base::ReadFileToString( rollback_ce_dir + "/com.foo/file1", &ce_content, false /* follow_symlinks */)); ASSERT_TRUE(android::base::ReadFileToString( rollback_de_dir + "/com.foo/file1", &de_content, false /* follow_symlinks */)); ASSERT_EQ("TEST_CONTENT_CE_MODIFIED", ce_content); ASSERT_EQ("TEST_CONTENT_DE_MODIFIED", de_content); } TEST_F(ServiceTest, CreateAppDataSnapshot_AppDataAbsent) { 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 deleter = [&rollback_ce_dir, &rollback_de_dir]() { delete_dir_contents(rollback_ce_dir, true); delete_dir_contents(rollback_de_dir, true); rmdir(rollback_ce_dir.c_str()); rmdir(rollback_de_dir.c_str()); }; auto scope_guard = android::base::make_scope_guard(deleter); ASSERT_TRUE(service->snapshotAppData(std::make_unique<std::string>("TEST"), "com.foo", 0, FLAG_STORAGE_CE).isOk()); ASSERT_TRUE(service->snapshotAppData(std::make_unique<std::string>("TEST"), "com.foo", 0, FLAG_STORAGE_DE).isOk()); // The snapshot calls must succeed but there should be no snapshot // created. 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)); } TEST_F(ServiceTest, RestoreAppDataSnapshot) { 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); // Write contents to the rollback location. We'll write the same files to the // app data location and make sure the restore has overwritten them. 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(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 */)); ASSERT_TRUE(service->restoreAppDataSnapshot(std::make_unique<std::string>("TEST"), "com.foo", 10000, -1, "", 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE).isOk()); std::string ce_content, de_content; ASSERT_TRUE(android::base::ReadFileToString( fake_package_ce_path + "/file1", &ce_content, false /* follow_symlinks */)); ASSERT_TRUE(android::base::ReadFileToString( fake_package_de_path + "/file1", &de_content, false /* follow_symlinks */)); ASSERT_EQ("CE_RESTORE_CONTENT", ce_content); ASSERT_EQ("DE_RESTORE_CONTENT", de_content); } } // namespace installd } // namespace android
cmds/installd/tests/installd_utils_test.cpp +30 −0 Original line number Diff line number Diff line Loading @@ -544,5 +544,35 @@ TEST_F(UtilsTest, MatchExtension_Invalid) { EXPECT_EQ(0, MatchExtension("docx")); } TEST_F(UtilsTest, TestRollbackPaths) { EXPECT_EQ("/data/misc_ce/0/rollback/com.foo", create_data_misc_ce_rollback_package_path(nullptr, 0, "com.foo")); EXPECT_EQ("/data/misc_ce/10/rollback/com.foo", create_data_misc_ce_rollback_package_path(nullptr, 10, "com.foo")); EXPECT_EQ("/data/misc_de/0/rollback/com.foo", create_data_misc_de_rollback_package_path(nullptr, 0, "com.foo")); EXPECT_EQ("/data/misc_de/10/rollback/com.foo", create_data_misc_de_rollback_package_path(nullptr, 10, "com.foo")); EXPECT_EQ("/data/misc_ce/0/rollback", create_data_misc_ce_rollback_path(nullptr, 0)); EXPECT_EQ("/data/misc_ce/10/rollback", create_data_misc_ce_rollback_path(nullptr, 10)); EXPECT_EQ("/data/misc_de/0/rollback", create_data_misc_de_rollback_path(nullptr, 0)); EXPECT_EQ("/data/misc_de/10/rollback", create_data_misc_de_rollback_path(nullptr, 10)); // 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. EXPECT_EQ("/mnt/expand/57f8f4bc-abf4-655f-bf67-946fc0f9f25b/misc_ce/0/rollback/com.example", create_data_misc_ce_rollback_package_path("57f8f4bc-abf4-655f-bf67-946fc0f9f25b", 0, "com.example")); EXPECT_EQ("/mnt/expand/57f8f4bc-abf4-655f-bf67-946fc0f9f25b/misc_de/0/rollback/com.example", create_data_misc_de_rollback_package_path("57f8f4bc-abf4-655f-bf67-946fc0f9f25b", 0, "com.example")); } } // namespace installd } // namespace android