More robust app data and user data removal.
1. rename the folder, so any new files will end up in the renamed
folder.
this also greatly reduces chances that app will be able to create new files.
2. delete the renamed folder
3. provide an api to cleanup renamed/deleted folders on system startup
Bug: 162757029
Test: atest installd_service_test installd_cache_test installd_utils_test installd_dexopt_test installd_otapreopt_test installd_file_test
Change-Id: If1c209d49675f7fa9df60b6136588e3b0a7786e5
diff --git a/cmds/installd/tests/Android.bp b/cmds/installd/tests/Android.bp
index 51f7716..a16587e 100644
--- a/cmds/installd/tests/Android.bp
+++ b/cmds/installd/tests/Android.bp
@@ -8,46 +8,47 @@
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 @@
"libdiskusage",
"libinstalld",
"libziparchive",
- "liblog",
"liblogwrap",
],
test_config: "installd_cache_test.xml",
@@ -78,21 +78,13 @@
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 @@
"libdiskusage",
"libinstalld",
"libziparchive",
- "liblog",
"liblogwrap",
],
test_config: "installd_service_test.xml",
@@ -124,28 +115,19 @@
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 {
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 b831515..69166c3 100644
--- a/cmds/installd/tests/installd_service_test.cpp
+++ b/cmds/installd/tests/installd_service_test.cpp
@@ -75,6 +75,7 @@
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 @@
}
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 @@
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 @@
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 @@
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";