diff options
-rw-r--r-- | cmds/installd/Android.bp | 5 | ||||
-rw-r--r-- | cmds/installd/InstalldNativeService.cpp | 34 | ||||
-rw-r--r-- | cmds/installd/InstalldNativeService.h | 2 | ||||
-rw-r--r-- | cmds/installd/binder/android/os/IInstalld.aidl | 2 | ||||
-rw-r--r-- | cmds/installd/tests/Android.bp | 76 | ||||
-rw-r--r-- | cmds/installd/tests/installd_service_test.cpp | 168 | ||||
-rw-r--r-- | cmds/installd/utils.cpp | 88 | ||||
-rw-r--r-- | cmds/installd/utils.h | 5 |
8 files changed, 308 insertions, 72 deletions
diff --git a/cmds/installd/Android.bp b/cmds/installd/Android.bp index 00babc3346..fd38ddfc57 100644 --- a/cmds/installd/Android.bp +++ b/cmds/installd/Android.bp @@ -10,6 +10,7 @@ package { cc_defaults { name: "installd_defaults", + cpp_std: "c++2a", cflags: [ "-Wall", "-Werror", @@ -41,6 +42,7 @@ cc_defaults { "libbinder", "libcrypto", "libcutils", + "libext2_uuid", "liblog", "liblogwrap", "libprocessgroup", @@ -239,6 +241,8 @@ cc_library_static { cc_binary { name: "otapreopt", + + cpp_std: "c++2a", cflags: [ "-Wall", "-Werror", @@ -268,6 +272,7 @@ cc_binary { "libbase", "libcrypto", "libcutils", + "libext2_uuid", "liblog", "liblogwrap", "libprocessgroup", diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp index c3256fcfff..89d4868b09 100644 --- a/cmds/installd/InstalldNativeService.cpp +++ b/cmds/installd/InstalldNativeService.cpp @@ -968,13 +968,13 @@ binder::Status InstalldNativeService::destroyAppData(const std::optional<std::st binder::Status res = ok(); if (flags & FLAG_STORAGE_CE) { auto path = create_data_user_ce_package_path(uuid_, userId, pkgname, ceDataInode); - if (delete_dir_contents_and_dir(path) != 0) { + if (rename_delete_dir_contents_and_dir(path) != 0) { res = error("Failed to delete " + path); } } if (flags & FLAG_STORAGE_DE) { auto path = create_data_user_de_package_path(uuid_, userId, pkgname); - if (delete_dir_contents_and_dir(path) != 0) { + if (rename_delete_dir_contents_and_dir(path) != 0) { res = error("Failed to delete " + path); } if ((flags & FLAG_CLEAR_APP_DATA_KEEP_ART_PROFILES) == 0) { @@ -1005,16 +1005,15 @@ binder::Status InstalldNativeService::destroyAppData(const std::optional<std::st } auto path = StringPrintf("%s/Android/data/%s", extPath.c_str(), pkgname); - if (delete_dir_contents_and_dir(path, true) != 0) { + if (rename_delete_dir_contents_and_dir(path, true) != 0) { res = error("Failed to delete contents of " + path); } - path = StringPrintf("%s/Android/media/%s", extPath.c_str(), pkgname); - if (delete_dir_contents_and_dir(path, true) != 0) { + if (rename_delete_dir_contents_and_dir(path, true) != 0) { res = error("Failed to delete contents of " + path); } path = StringPrintf("%s/Android/obb/%s", extPath.c_str(), pkgname); - if (delete_dir_contents_and_dir(path, true) != 0) { + if (rename_delete_dir_contents_and_dir(path, true) != 0) { res = error("Failed to delete contents of " + path); } } @@ -1562,27 +1561,27 @@ binder::Status InstalldNativeService::destroyUserData(const std::optional<std::s binder::Status res = ok(); if (flags & FLAG_STORAGE_DE) { auto path = create_data_user_de_path(uuid_, userId); - if (delete_dir_contents_and_dir(path, true) != 0) { + if (rename_delete_dir_contents_and_dir(path, true) != 0) { res = error("Failed to delete " + path); } if (uuid_ == nullptr) { path = create_data_misc_legacy_path(userId); - if (delete_dir_contents_and_dir(path, true) != 0) { + if (rename_delete_dir_contents_and_dir(path, true) != 0) { res = error("Failed to delete " + path); } path = create_primary_cur_profile_dir_path(userId); - if (delete_dir_contents_and_dir(path, true) != 0) { + if (rename_delete_dir_contents_and_dir(path, true) != 0) { res = error("Failed to delete " + path); } } } if (flags & FLAG_STORAGE_CE) { auto path = create_data_user_ce_path(uuid_, userId); - if (delete_dir_contents_and_dir(path, true) != 0) { + if (rename_delete_dir_contents_and_dir(path, true) != 0) { res = error("Failed to delete " + path); } path = findDataMediaPath(uuid, userId); - if (delete_dir_contents_and_dir(path, true) != 0) { + if (rename_delete_dir_contents_and_dir(path, true) != 0) { res = error("Failed to delete " + path); } } @@ -3324,5 +3323,18 @@ binder::Status InstalldNativeService::migrateLegacyObbData() { return ok(); } +binder::Status InstalldNativeService::cleanupDeletedDirs(const std::optional<std::string>& uuid) { + const char* uuid_cstr = uuid ? uuid->c_str() : nullptr; + const auto users = get_known_users(uuid_cstr); + for (auto userId : users) { + auto ce_path = create_data_user_ce_path(uuid_cstr, userId); + auto de_path = create_data_user_de_path(uuid_cstr, userId); + + find_and_delete_renamed_deleted_dirs_under_path(ce_path); + find_and_delete_renamed_deleted_dirs_under_path(de_path); + } + return ok(); +} + } // namespace installd } // namespace android diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h index 09581bb544..7822810d38 100644 --- a/cmds/installd/InstalldNativeService.h +++ b/cmds/installd/InstalldNativeService.h @@ -188,6 +188,8 @@ public: binder::Status migrateLegacyObbData(); + binder::Status cleanupDeletedDirs(const std::optional<std::string>& uuid); + private: std::recursive_mutex mLock; std::unordered_map<userid_t, std::weak_ptr<std::shared_mutex>> mUserIdLock; diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl index 637a9f2171..7fab822ec0 100644 --- a/cmds/installd/binder/android/os/IInstalld.aidl +++ b/cmds/installd/binder/android/os/IInstalld.aidl @@ -129,6 +129,8 @@ interface IInstalld { void migrateLegacyObbData(); + void cleanupDeletedDirs(@nullable @utf8InCpp String uuid); + const int FLAG_STORAGE_DE = 0x1; const int FLAG_STORAGE_CE = 0x2; const int FLAG_STORAGE_EXTERNAL = 0x4; diff --git a/cmds/installd/tests/Android.bp b/cmds/installd/tests/Android.bp index 51f7716d3a..a16587ec99 100644 --- a/cmds/installd/tests/Android.bp +++ b/cmds/installd/tests/Android.bp @@ -8,46 +8,47 @@ package { default_applicable_licenses: ["frameworks_native_license"], } -cc_test { - name: "installd_utils_test", +cc_defaults { + name: "installd_tests_defaults", test_suites: ["device-tests"], clang: true, - srcs: ["installd_utils_test.cpp"], + cpp_std: "c++2a", cflags: [ "-Wall", "-Werror", ], shared_libs: [ "libbase", - "libutils", "libcutils", + "libext2_uuid", + "libutils", ], static_libs: [ + "liblog", + ], +} + +cc_test { + name: "installd_utils_test", + defaults: ["installd_tests_defaults"], + srcs: ["installd_utils_test.cpp"], + static_libs: [ "libasync_safe", "libdiskusage", "libinstalld", - "liblog", ], test_config: "installd_utils_test.xml", } cc_test { name: "installd_cache_test", - test_suites: ["device-tests"], - clang: true, + defaults: ["installd_tests_defaults"], srcs: ["installd_cache_test.cpp"], - cflags: [ - "-Wall", - "-Werror", - ], shared_libs: [ - "libbase", "libbinder", "libcrypto", - "libcutils", "libprocessgroup", "libselinux", - "libutils", "server_configurable_flags", ], static_libs: [ @@ -55,7 +56,6 @@ cc_test { "libdiskusage", "libinstalld", "libziparchive", - "liblog", "liblogwrap", ], test_config: "installd_cache_test.xml", @@ -78,21 +78,13 @@ cc_test { cc_test { name: "installd_service_test", - test_suites: ["device-tests"], - clang: true, + defaults: ["installd_tests_defaults"], srcs: ["installd_service_test.cpp"], - cflags: [ - "-Wall", - "-Werror", - ], shared_libs: [ - "libbase", "libbinder", "libcrypto", - "libcutils", "libprocessgroup", "libselinux", - "libutils", "packagemanager_aidl-cpp", "server_configurable_flags", ], @@ -101,7 +93,6 @@ cc_test { "libdiskusage", "libinstalld", "libziparchive", - "liblog", "liblogwrap", ], test_config: "installd_service_test.xml", @@ -124,28 +115,19 @@ cc_test { cc_test { name: "installd_dexopt_test", - test_suites: ["device-tests"], - clang: true, + defaults: ["installd_tests_defaults"], srcs: ["installd_dexopt_test.cpp"], - cflags: [ - "-Wall", - "-Werror", - ], shared_libs: [ - "libbase", "libbinder", "libcrypto", - "libcutils", "libprocessgroup", "libselinux", - "libutils", "server_configurable_flags", ], static_libs: [ "libasync_safe", "libdiskusage", "libinstalld", - "liblog", "liblogwrap", "libziparchive", "libz", @@ -170,41 +152,21 @@ cc_test { cc_test { name: "installd_otapreopt_test", - test_suites: ["device-tests"], - clang: true, + defaults: ["installd_tests_defaults"], srcs: ["installd_otapreopt_test.cpp"], - cflags: [ - "-Wall", - "-Werror", - ], shared_libs: [ - "libbase", - "libcutils", - "libutils", "server_configurable_flags", ], static_libs: [ - "liblog", "libotapreoptparameters", ], } cc_test { name: "installd_file_test", - test_suites: ["device-tests"], - clang: true, + defaults: ["installd_tests_defaults"], srcs: ["installd_file_test.cpp"], - cflags: [ - "-Wall", - "-Werror", - ], - shared_libs: [ - "libbase", - "libcutils", - "libutils", - ], static_libs: [ "libinstalld", - "liblog", ], } diff --git a/cmds/installd/tests/installd_service_test.cpp b/cmds/installd/tests/installd_service_test.cpp index b831515b94..69166c3939 100644 --- a/cmds/installd/tests/installd_service_test.cpp +++ b/cmds/installd/tests/installd_service_test.cpp @@ -75,6 +75,7 @@ std::string get_package_name(uid_t uid) { namespace installd { constexpr const char* kTestUuid = "TEST"; +constexpr const char* kTestPath = "/data/local/tmp/user/0"; #define FLAG_FORCE InstalldNativeService::FLAG_FORCE @@ -97,7 +98,7 @@ bool create_cache_path(char path[PKG_PATH_MAX], const char *src, const char *ins } static std::string get_full_path(const char* path) { - return StringPrintf("/data/local/tmp/user/0/%s", path); + return StringPrintf("%s/%s", kTestPath, path); } static void mkdir(const char* path, uid_t owner, gid_t group, mode_t mode) { @@ -107,12 +108,16 @@ static void mkdir(const char* path, uid_t owner, gid_t group, mode_t mode) { EXPECT_EQ(::chmod(fullPath.c_str(), mode), 0); } -static void touch(const char* path, uid_t owner, gid_t group, mode_t mode) { +static int create(const char* path, uid_t owner, gid_t group, mode_t mode) { int fd = ::open(get_full_path(path).c_str(), O_RDWR | O_CREAT, mode); EXPECT_NE(fd, -1); EXPECT_EQ(::fchown(fd, owner, group), 0); EXPECT_EQ(::fchmod(fd, mode), 0); - EXPECT_EQ(::close(fd), 0); + return fd; +} + +static void touch(const char* path, uid_t owner, gid_t group, mode_t mode) { + EXPECT_EQ(::close(create(path, owner, group, mode)), 0); } static int stat_gid(const char* path) { @@ -127,6 +132,35 @@ static int stat_mode(const char* path) { return buf.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO | S_ISGID); } +static bool exists(const char* path) { + return ::access(get_full_path(path).c_str(), F_OK) == 0; +} + +template <class Pred> +static bool find_file(const char* path, Pred&& pred) { + bool result = false; + auto d = opendir(path); + if (d == nullptr) { + return result; + } + struct dirent* de; + while ((de = readdir(d))) { + const char* name = de->d_name; + if (pred(name, de->d_type == DT_DIR)) { + result = true; + break; + } + } + closedir(d); + return result; +} + +static bool exists_renamed_deleted_dir() { + return find_file(kTestPath, [](std::string_view name, bool is_dir) { + return is_dir && is_renamed_deleted_dir(name); + }); +} + class ServiceTest : public testing::Test { protected: InstalldNativeService* service; @@ -193,6 +227,134 @@ TEST_F(ServiceTest, FixupAppData_Moved) { EXPECT_EQ(10000, stat_gid("com.example/bar/file")); } +TEST_F(ServiceTest, DestroyUserData) { + LOG(INFO) << "DestroyUserData"; + + mkdir("com.example", 10000, 10000, 0700); + mkdir("com.example/foo", 10000, 10000, 0700); + touch("com.example/foo/file", 10000, 20000, 0700); + mkdir("com.example/bar", 10000, 20000, 0700); + touch("com.example/bar/file", 10000, 20000, 0700); + + EXPECT_TRUE(exists("com.example/foo")); + EXPECT_TRUE(exists("com.example/foo/file")); + EXPECT_TRUE(exists("com.example/bar")); + EXPECT_TRUE(exists("com.example/bar/file")); + + service->destroyUserData(testUuid, 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE); + + EXPECT_FALSE(exists("com.example/foo")); + EXPECT_FALSE(exists("com.example/foo/file")); + EXPECT_FALSE(exists("com.example/bar")); + EXPECT_FALSE(exists("com.example/bar/file")); + + EXPECT_FALSE(exists_renamed_deleted_dir()); +} + +TEST_F(ServiceTest, DestroyAppData) { + LOG(INFO) << "DestroyAppData"; + + mkdir("com.example", 10000, 10000, 0700); + mkdir("com.example/foo", 10000, 10000, 0700); + touch("com.example/foo/file", 10000, 20000, 0700); + mkdir("com.example/bar", 10000, 20000, 0700); + touch("com.example/bar/file", 10000, 20000, 0700); + + EXPECT_TRUE(exists("com.example/foo")); + EXPECT_TRUE(exists("com.example/foo/file")); + EXPECT_TRUE(exists("com.example/bar")); + EXPECT_TRUE(exists("com.example/bar/file")); + + service->destroyAppData(testUuid, "com.example", 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE, 0); + + EXPECT_FALSE(exists("com.example/foo")); + EXPECT_FALSE(exists("com.example/foo/file")); + EXPECT_FALSE(exists("com.example/bar")); + EXPECT_FALSE(exists("com.example/bar/file")); + + EXPECT_FALSE(exists_renamed_deleted_dir()); +} + +TEST_F(ServiceTest, CleanupDeletedDirs) { + LOG(INFO) << "CleanupDeletedDirs"; + + mkdir("5b14b6458a44==deleted==", 10000, 10000, 0700); + mkdir("5b14b6458a44==deleted==/foo", 10000, 10000, 0700); + touch("5b14b6458a44==deleted==/foo/file", 10000, 20000, 0700); + mkdir("5b14b6458a44==deleted==/bar", 10000, 20000, 0700); + touch("5b14b6458a44==deleted==/bar/file", 10000, 20000, 0700); + + auto fd = create("5b14b6458a44==deleted==/bar/opened_file", 10000, 20000, 0700); + + mkdir("5b14b6458a44==NOTdeleted==", 10000, 10000, 0700); + mkdir("5b14b6458a44==NOTdeleted==/foo", 10000, 10000, 0700); + touch("5b14b6458a44==NOTdeleted==/foo/file", 10000, 20000, 0700); + mkdir("5b14b6458a44==NOTdeleted==/bar", 10000, 20000, 0700); + touch("5b14b6458a44==NOTdeleted==/bar/file", 10000, 20000, 0700); + + mkdir("com.example", 10000, 10000, 0700); + mkdir("com.example/foo", 10000, 10000, 0700); + touch("com.example/foo/file", 10000, 20000, 0700); + mkdir("com.example/bar", 10000, 20000, 0700); + touch("com.example/bar/file", 10000, 20000, 0700); + + mkdir("==deleted==", 10000, 10000, 0700); + mkdir("==deleted==/foo", 10000, 10000, 0700); + touch("==deleted==/foo/file", 10000, 20000, 0700); + mkdir("==deleted==/bar", 10000, 20000, 0700); + touch("==deleted==/bar/file", 10000, 20000, 0700); + + EXPECT_TRUE(exists("5b14b6458a44==deleted==/foo")); + EXPECT_TRUE(exists("5b14b6458a44==deleted==/foo/file")); + EXPECT_TRUE(exists("5b14b6458a44==deleted==/bar")); + EXPECT_TRUE(exists("5b14b6458a44==deleted==/bar/file")); + EXPECT_TRUE(exists("5b14b6458a44==deleted==/bar/opened_file")); + + EXPECT_TRUE(exists("5b14b6458a44==NOTdeleted==/foo")); + EXPECT_TRUE(exists("5b14b6458a44==NOTdeleted==/foo/file")); + EXPECT_TRUE(exists("5b14b6458a44==NOTdeleted==/bar")); + EXPECT_TRUE(exists("5b14b6458a44==NOTdeleted==/bar/file")); + + EXPECT_TRUE(exists("com.example/foo")); + EXPECT_TRUE(exists("com.example/foo/file")); + EXPECT_TRUE(exists("com.example/bar")); + EXPECT_TRUE(exists("com.example/bar/file")); + + EXPECT_TRUE(exists("==deleted==/foo")); + EXPECT_TRUE(exists("==deleted==/foo/file")); + EXPECT_TRUE(exists("==deleted==/bar")); + EXPECT_TRUE(exists("==deleted==/bar/file")); + + EXPECT_TRUE(exists_renamed_deleted_dir()); + + service->cleanupDeletedDirs(testUuid); + + EXPECT_EQ(::close(fd), 0); + + EXPECT_FALSE(exists("5b14b6458a44==deleted==/foo")); + EXPECT_FALSE(exists("5b14b6458a44==deleted==/foo/file")); + EXPECT_FALSE(exists("5b14b6458a44==deleted==/bar")); + EXPECT_FALSE(exists("5b14b6458a44==deleted==/bar/file")); + EXPECT_FALSE(exists("5b14b6458a44==deleted==/bar/opened_file")); + + EXPECT_TRUE(exists("5b14b6458a44==NOTdeleted==/foo")); + EXPECT_TRUE(exists("5b14b6458a44==NOTdeleted==/foo/file")); + EXPECT_TRUE(exists("5b14b6458a44==NOTdeleted==/bar")); + EXPECT_TRUE(exists("5b14b6458a44==NOTdeleted==/bar/file")); + + EXPECT_TRUE(exists("com.example/foo")); + EXPECT_TRUE(exists("com.example/foo/file")); + EXPECT_TRUE(exists("com.example/bar")); + EXPECT_TRUE(exists("com.example/bar/file")); + + EXPECT_FALSE(exists("==deleted==/foo")); + EXPECT_FALSE(exists("==deleted==/foo/file")); + EXPECT_FALSE(exists("==deleted==/bar")); + EXPECT_FALSE(exists("==deleted==/bar/file")); + + EXPECT_FALSE(exists_renamed_deleted_dir()); +} + TEST_F(ServiceTest, HashSecondaryDex) { LOG(INFO) << "HashSecondaryDex"; diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp index 0f8a732345..a4a21b720c 100644 --- a/cmds/installd/utils.cpp +++ b/cmds/installd/utils.cpp @@ -22,9 +22,10 @@ #include <stdlib.h> #include <sys/capability.h> #include <sys/stat.h> +#include <sys/statvfs.h> #include <sys/wait.h> #include <sys/xattr.h> -#include <sys/statvfs.h> +#include <uuid/uuid.h> #include <android-base/file.h> #include <android-base/logging.h> @@ -47,6 +48,7 @@ #define DEBUG_XATTRS 0 +using android::base::Dirname; using android::base::EndsWith; using android::base::Fdopendir; using android::base::StringPrintf; @@ -55,6 +57,10 @@ using android::base::unique_fd; namespace android { namespace installd { +using namespace std::literals; + +static constexpr auto deletedSuffix = "==deleted=="sv; + /** * Check that given string is valid filename, and that it attempts no * parent or child directory traversal. @@ -595,6 +601,86 @@ int delete_dir_contents(const char *pathname, return res; } +static std::string make_unique_name(std::string_view suffix) { + static constexpr auto uuidStringSize = 36; + + uuid_t guid; + uuid_generate(guid); + + std::string name; + const auto suffixSize = suffix.size(); + name.reserve(uuidStringSize + suffixSize); + + name.resize(uuidStringSize); + uuid_unparse(guid, name.data()); + name.append(suffix); + + return name; +} + +static int rename_delete_dir_contents(const std::string& pathname, + int (*exclusion_predicate)(const char*, const int), + bool ignore_if_missing) { + auto temp_dir_name = make_unique_name(deletedSuffix); + auto temp_dir_path = + base::StringPrintf("%s/%s", Dirname(pathname).c_str(), temp_dir_name.c_str()); + + if (::rename(pathname.c_str(), temp_dir_path.c_str())) { + if (ignore_if_missing && (errno == ENOENT)) { + return 0; + } + ALOGE("Couldn't rename %s -> %s: %s \n", pathname.c_str(), temp_dir_path.c_str(), + strerror(errno)); + return -errno; + } + + return delete_dir_contents(temp_dir_path.c_str(), 1, exclusion_predicate, ignore_if_missing); +} + +bool is_renamed_deleted_dir(std::string_view path) { + return path.ends_with(deletedSuffix); +} + +int rename_delete_dir_contents_and_dir(const std::string& pathname, bool ignore_if_missing) { + return rename_delete_dir_contents(pathname, nullptr, ignore_if_missing); +} + +static auto open_dir(const char* dir) { + struct DirCloser { + void operator()(DIR* d) const noexcept { ::closedir(d); } + }; + return std::unique_ptr<DIR, DirCloser>(::opendir(dir)); +} + +void find_and_delete_renamed_deleted_dirs_under_path(const std::string& pathname) { + auto dir = open_dir(pathname.c_str()); + if (!dir) { + return; + } + int dfd = dirfd(dir.get()); + if (dfd < 0) { + ALOGE("Couldn't dirfd %s: %s\n", pathname.c_str(), strerror(errno)); + return; + } + + struct dirent* de; + while ((de = readdir(dir.get()))) { + if (de->d_type != DT_DIR) { + continue; + } + const char* name = de->d_name; + if (is_renamed_deleted_dir({name})) { + LOG(INFO) << "Deleting renamed data directory: " << name; + // Deleting the content. + delete_dir_contents_fd(dfd, name); + // Deleting the directory + if (unlinkat(dfd, name, AT_REMOVEDIR) < 0) { + ALOGE("Couldn't unlinkat %s: %s\n", name, strerror(errno)); + } + } + } +} + int delete_dir_contents_fd(int dfd, const char *name) { int fd, res; diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h index 549fc6cf04..986080d92d 100644 --- a/cmds/installd/utils.h +++ b/cmds/installd/utils.h @@ -120,6 +120,11 @@ int create_dir_if_needed(const std::string& pathname, mode_t mode); int delete_dir_contents(const std::string& pathname, bool ignore_if_missing = false); int delete_dir_contents_and_dir(const std::string& pathname, bool ignore_if_missing = false); +bool is_renamed_deleted_dir(std::string_view path); + +int rename_delete_dir_contents_and_dir(const std::string& pathname, bool ignore_if_missing = false); +void find_and_delete_renamed_deleted_dirs_under_path(const std::string& pathname); + int delete_dir_contents(const char *pathname, int also_delete_dir, int (*exclusion_predicate)(const char *name, const int is_dir), |