diff options
-rw-r--r-- | cmds/installd/InstalldNativeService.cpp | 192 | ||||
-rw-r--r-- | cmds/installd/InstalldNativeService.h | 6 | ||||
-rw-r--r-- | cmds/installd/binder/android/os/IInstalld.aidl | 9 | ||||
-rw-r--r-- | cmds/installd/tests/installd_service_test.cpp | 177 | ||||
-rw-r--r-- | cmds/installd/tests/installd_utils_test.cpp | 30 | ||||
-rw-r--r-- | cmds/installd/utils.cpp | 21 | ||||
-rw-r--r-- | cmds/installd/utils.h | 7 |
7 files changed, 410 insertions, 32 deletions
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp index e494e9cc85..51f30da9c4 100644 --- a/cmds/installd/InstalldNativeService.cpp +++ b/cmds/installd/InstalldNativeService.cpp @@ -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> @@ -784,6 +785,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, @@ -808,19 +965,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; @@ -848,25 +993,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; @@ -875,11 +1006,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; diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h index d482472605..098a0c2985 100644 --- a/cmds/installd/InstalldNativeService.h +++ b/cmds/installd/InstalldNativeService.h @@ -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, diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl index dfd62bf743..3d093b8883 100644 --- a/cmds/installd/binder/android/os/IInstalld.aidl +++ b/cmds/installd/binder/android/os/IInstalld.aidl @@ -105,6 +105,15 @@ 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, + 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; diff --git a/cmds/installd/tests/installd_service_test.cpp b/cmds/installd/tests/installd_service_test.cpp index 9dad99563d..e6017cb761 100644 --- a/cmds/installd/tests/installd_service_test.cpp +++ b/cmds/installd/tests/installd_service_test.cpp @@ -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> @@ -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 diff --git a/cmds/installd/tests/installd_utils_test.cpp b/cmds/installd/tests/installd_utils_test.cpp index ad7a5ae6a2..ce99fff27a 100644 --- a/cmds/installd/tests/installd_utils_test.cpp +++ b/cmds/installd/tests/installd_utils_test.cpp @@ -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 diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp index bbf14cb5f7..24f5eab132 100644 --- a/cmds/installd/utils.cpp +++ b/cmds/installd/utils.cpp @@ -194,6 +194,27 @@ std::string create_data_user_de_path(const char* volume_uuid, userid_t userid) { return StringPrintf("%s/user_de/%u", data.c_str(), userid); } + +std::string create_data_misc_ce_rollback_path(const char* volume_uuid, userid_t user) { + return StringPrintf("%s/misc_ce/%u/rollback", create_data_path(volume_uuid).c_str(), user); +} + +std::string create_data_misc_de_rollback_path(const char* volume_uuid, userid_t user) { + return StringPrintf("%s/misc_de/%u/rollback", create_data_path(volume_uuid).c_str(), user); +} + +std::string create_data_misc_ce_rollback_package_path(const char* volume_uuid, + userid_t user, const char* package_name) { + return StringPrintf("%s/%s", + create_data_misc_ce_rollback_path(volume_uuid, user).c_str(), package_name); +} + +std::string create_data_misc_de_rollback_package_path(const char* volume_uuid, + userid_t user, const char* package_name) { + return StringPrintf("%s/%s", + create_data_misc_de_rollback_path(volume_uuid, user).c_str(), package_name); +} + /** * Create the path name for media for a certain userid. */ diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h index 206ad4158a..430f515980 100644 --- a/cmds/installd/utils.h +++ b/cmds/installd/utils.h @@ -61,6 +61,13 @@ std::string create_data_user_de_package_path(const char* volume_uuid, std::string create_data_user_ce_package_path_as_user_link( const char* volume_uuid, userid_t userid, const char* package_name); +std::string create_data_misc_ce_rollback_path(const char* volume_uuid, userid_t user); +std::string create_data_misc_de_rollback_path(const char* volume_uuid, userid_t user); +std::string create_data_misc_ce_rollback_package_path(const char* volume_uuid, + userid_t user, const char* package_name); +std::string create_data_misc_de_rollback_package_path(const char* volume_uuid, + userid_t user, const char* package_name); + std::string create_data_media_path(const char* volume_uuid, userid_t userid); std::string create_data_media_obb_path(const char* volume_uuid, const char* package_name); std::string create_data_media_package_path(const char* volume_uuid, userid_t userid, |