Create supplemental data directories when app data is created

Supplemental data is closely related to app data, as such we want
their creation to happen at the same time. Owner of the these data
will be the supplemental process instead of the app.

The root directory for supplemental data is
/data/misc_{ce,de}/<user-id>/supplemental. This directory will be
created by vold when user is created.

Installd is responsible for creating app level directories under the
root, e.g, /data/misc_ce/0/supplemental/<app-name>. We also need code
level directory under the app direcotory, but that will be done with a
separate API. CreateAppData is responsible for things at app level, so
we will be maintaining the same level of abstraction.

Instlld will also create the shared directory under the app-level
directory, e.g, /data/misc_ce/0/supplemental/<app-name>/shared and
`cache` and `code_cache` directory under the `shared` directory.

Supplemental data should be removed when app data is removed. This
will be done in follow up Cls too.

Some of the public APIs of installd service was not being used by
anybody else, so made them private.

Bug: 211763739
Bug: 217543371
Test: atest installd_service_test:AppSupplementalDataTest
Ignore-AOSP-First: Feature is being developed in internal branch
Change-Id: I966c76b032821610293c53ba875e2800a5ce4804
Merged-In: I966c76b032821610293c53ba875e2800a5ce4804
(cherry picked from commit c40dff533dd842bf6fae21c05eea472be7106ba2)
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index 4e3aae4..7c21944 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -18,13 +18,9 @@
 
 #define ATRACE_TAG ATRACE_TAG_PACKAGE_MANAGER
 
-#include <algorithm>
 #include <errno.h>
-#include <fstream>
 #include <fts.h>
-#include <functional>
 #include <inttypes.h>
-#include <regex>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -40,6 +36,11 @@
 #include <sys/wait.h>
 #include <sys/xattr.h>
 #include <unistd.h>
+#include <algorithm>
+#include <filesystem>
+#include <fstream>
+#include <functional>
+#include <regex>
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
@@ -720,6 +721,74 @@
             return error("Failed to prepare profiles for " + packageName);
         }
     }
+
+    {
+        auto status = createAppDirectoryForSupplementalData(uuid, packageName, userId, appId,
+                                                            previousAppId, seInfo, flags);
+        if (!status.isOk()) {
+            return status;
+        }
+    }
+
+    return ok();
+}
+
+/**
+ * Responsible for creating /data/user/0/supplemental/<app-name> directory and other
+ * app level sub directories, such as ./shared
+ */
+binder::Status InstalldNativeService::createAppDirectoryForSupplementalData(
+        const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId,
+        int32_t appId, int32_t previousAppId, const std::string& seInfo, int32_t flags) {
+    int32_t supplementalUid = multiuser_get_supplemental_uid(userId, appId);
+    if (supplementalUid == -1) {
+        // There no valid supplemental process for this app. Skip creation of data directory
+        return ok();
+    }
+
+    // TODO(b/211763739): what if uuid is not nullptr or TEST?
+    const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+
+    constexpr int storageFlags[2] = {FLAG_STORAGE_CE, FLAG_STORAGE_DE};
+    for (int i = 0; i < 2; i++) {
+        int currentFlag = storageFlags[i];
+        if ((flags & currentFlag) == 0) {
+            continue;
+        }
+        bool isCeData = (currentFlag == FLAG_STORAGE_CE);
+
+        // /data/misc_{ce,de}/<user-id>/supplemental directory gets created by vold
+        // during user creation
+
+        // Prepare the app directory
+        auto appPath = create_data_misc_supplemental_package_path(uuid_, isCeData, userId,
+                                                                  packageName.c_str());
+        if (prepare_app_dir(appPath, 0751, AID_SYSTEM)) {
+            return error("Failed to prepare " + appPath);
+        }
+
+        // Now prepare the shared directory which will be accessible by all codes
+        auto sharedPath = create_data_misc_supplemental_shared_path(uuid_, isCeData, userId,
+                                                                    packageName.c_str());
+
+        int32_t previousSupplementalUid = multiuser_get_supplemental_uid(userId, previousAppId);
+        int32_t cacheGid = multiuser_get_cache_gid(userId, appId);
+        if (cacheGid == -1) {
+            return exception(binder::Status::EX_ILLEGAL_STATE,
+                             StringPrintf("cacheGid cannot be -1 for supplemental data"));
+        }
+        auto status = createAppDataDirs(sharedPath, supplementalUid, &previousSupplementalUid,
+                                        cacheGid, seInfo, 0700);
+        if (!status.isOk()) {
+            return status;
+        }
+
+        // TODO(b/211763739): We also need to handle art profile creations
+
+        // TODO(b/211763739): And return the CE inode of the supplemental root directory and
+        // app directory under it so we can clear contents while CE storage is locked
+    }
+
     return ok();
 }
 
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index 96783c3..d442979 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -47,14 +47,9 @@
             int32_t flags);
 
     binder::Status createAppData(const std::optional<std::string>& uuid,
-            const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
-            int32_t previousAppId, const std::string& seInfo, int32_t targetSdkVersion,
-            int64_t* _aidl_return);
-    binder::Status createAppDataLocked(const std::optional<std::string>& uuid,
-                                       const std::string& packageName, int32_t userId,
-                                       int32_t flags, int32_t appId, int32_t previousAppId,
-                                       const std::string& seInfo, int32_t targetSdkVersion,
-                                       int64_t* _aidl_return);
+                                 const std::string& packageName, int32_t userId, int32_t flags,
+                                 int32_t appId, int32_t previousAppId, const std::string& seInfo,
+                                 int32_t targetSdkVersion, int64_t* _aidl_return);
 
     binder::Status createAppData(
             const android::os::CreateAppDataArgs& args,
@@ -202,6 +197,18 @@
     std::unordered_map<uid_t, int64_t> mCacheQuotas;
 
     std::string findDataMediaPath(const std::optional<std::string>& uuid, userid_t userid);
+
+    binder::Status createAppDataLocked(const std::optional<std::string>& uuid,
+                                       const std::string& packageName, int32_t userId,
+                                       int32_t flags, int32_t appId, int32_t previousAppId,
+                                       const std::string& seInfo, int32_t targetSdkVersion,
+                                       int64_t* _aidl_return);
+
+    binder::Status createAppDirectoryForSupplementalData(const std::optional<std::string>& uuid,
+                                                         const std::string& packageName,
+                                                         int32_t userId, int32_t appId,
+                                                         int32_t previousAppId,
+                                                         const std::string& seInfo, int32_t flags);
 };
 
 }  // namespace installd
diff --git a/cmds/installd/tests/installd_service_test.cpp b/cmds/installd/tests/installd_service_test.cpp
index 9702d42..499ba70 100644
--- a/cmds/installd/tests/installd_service_test.cpp
+++ b/cmds/installd/tests/installd_service_test.cpp
@@ -74,8 +74,14 @@
 }
 namespace installd {
 
-constexpr const char* kTestUuid = "TEST";
-constexpr const char* kTestPath = "/data/local/tmp/user/0";
+static constexpr const char* kTestUuid = "TEST";
+static constexpr const char* kTestPath = "/data/local/tmp/user/0";
+static constexpr const uid_t kSystemUid = 1000;
+static constexpr const int32_t kTestUserId = 0;
+static constexpr const uid_t kTestAppId = 19999;
+
+const gid_t kTestAppUid = multiuser_get_uid(kTestUserId, kTestAppId);
+const uid_t kTestAppSupplementalUid = multiuser_get_supplemental_uid(kTestUserId, kTestAppId);
 
 #define FLAG_FORCE InstalldNativeService::FLAG_FORCE
 
@@ -943,5 +949,120 @@
           "com.foo", 10000, "", 0, 41, FLAG_STORAGE_DE));
 }
 
+class AppSupplementalDataTest : public testing::Test {
+public:
+    void CheckFileAccess(const std::string& path, uid_t uid, mode_t mode) {
+        const auto fullPath = "/data/local/tmp/" + path;
+        ASSERT_TRUE(exists(fullPath.c_str())) << "For path: " << fullPath;
+        struct stat st;
+        ASSERT_EQ(0, stat(fullPath.c_str(), &st));
+        ASSERT_EQ(uid, st.st_uid) << "For path: " << fullPath;
+        ASSERT_EQ(uid, st.st_gid) << "For path: " << fullPath;
+        ASSERT_EQ(mode, st.st_mode) << "For path: " << fullPath;
+    }
+
+    bool exists(const char* path) { return ::access(path, F_OK) == 0; }
+
+    // Creates a default CreateAppDataArgs object
+    android::os::CreateAppDataArgs createAppDataArgs() {
+        android::os::CreateAppDataArgs args;
+        args.uuid = kTestUuid;
+        args.packageName = "com.foo";
+        args.userId = kTestUserId;
+        args.appId = kTestAppId;
+        args.seInfo = "default";
+        args.flags = FLAG_STORAGE_CE | FLAG_STORAGE_DE;
+        return args;
+    }
+
+protected:
+    InstalldNativeService* service;
+
+    virtual void SetUp() {
+        setenv("ANDROID_LOG_TAGS", "*:v", 1);
+        android::base::InitLogging(nullptr);
+
+        service = new InstalldNativeService();
+        clearAppData();
+        ASSERT_TRUE(mkdirs("/data/local/tmp/user/0", 0700));
+        ASSERT_TRUE(mkdirs("/data/local/tmp/user_de/0", 0700));
+        ASSERT_TRUE(mkdirs("/data/local/tmp/misc_ce/0/supplemental", 0700));
+        ASSERT_TRUE(mkdirs("/data/local/tmp/misc_de/0/supplemental", 0700));
+
+        init_globals_from_data_and_root();
+    }
+
+    virtual void TearDown() {
+        delete service;
+        clearAppData();
+    }
+
+private:
+    void clearAppData() {
+        ASSERT_EQ(0, delete_dir_contents_and_dir("/data/local/tmp/user_de", true));
+        ASSERT_EQ(0, delete_dir_contents_and_dir("/data/local/tmp/misc_ce", true));
+        ASSERT_EQ(0, delete_dir_contents_and_dir("/data/local/tmp/misc_de", true));
+        ASSERT_EQ(0, delete_dir_contents_and_dir("/data/local/tmp/user_de", true));
+    }
+};
+
+TEST_F(AppSupplementalDataTest, CreateAppData_CreatesSupplementalAppData) {
+    android::os::CreateAppDataResult result;
+    android::os::CreateAppDataArgs args = createAppDataArgs();
+    args.packageName = "com.foo";
+    args.flags = FLAG_STORAGE_CE | FLAG_STORAGE_DE;
+
+    // Create the app user data.
+    ASSERT_BINDER_SUCCESS(service->createAppData(args, &result));
+
+    CheckFileAccess("misc_ce/0/supplemental/com.foo", kSystemUid, S_IFDIR | 0751);
+    CheckFileAccess("misc_ce/0/supplemental/com.foo/shared", kTestAppSupplementalUid,
+                    S_IFDIR | 0700);
+    CheckFileAccess("misc_ce/0/supplemental/com.foo/shared/cache", kTestAppSupplementalUid,
+                    S_IFDIR | S_ISGID | 0771);
+    CheckFileAccess("misc_ce/0/supplemental/com.foo/shared/code_cache", kTestAppSupplementalUid,
+                    S_IFDIR | S_ISGID | 0771);
+
+    CheckFileAccess("misc_de/0/supplemental/com.foo", kSystemUid, S_IFDIR | 0751);
+    CheckFileAccess("misc_de/0/supplemental/com.foo/shared", kTestAppSupplementalUid,
+                    S_IFDIR | 0700);
+    CheckFileAccess("misc_de/0/supplemental/com.foo/shared/cache", kTestAppSupplementalUid,
+                    S_IFDIR | S_ISGID | 0771);
+    CheckFileAccess("misc_de/0/supplemental/com.foo/shared/code_cache", kTestAppSupplementalUid,
+                    S_IFDIR | S_ISGID | 0771);
+}
+
+TEST_F(AppSupplementalDataTest, CreateAppData_CreatesSupplementalAppData_WithoutDeFlag) {
+    android::os::CreateAppDataResult result;
+    android::os::CreateAppDataArgs args = createAppDataArgs();
+    args.packageName = "com.foo";
+    args.flags = FLAG_STORAGE_CE;
+
+    // Create the app user data.
+    ASSERT_BINDER_SUCCESS(service->createAppData(args, &result));
+
+    // Only CE paths should exist
+    CheckFileAccess("misc_ce/0/supplemental/com.foo", kSystemUid, S_IFDIR | 0751);
+
+    // DE paths should not exist
+    ASSERT_FALSE(exists("/data/local/tmp/misc_de/0/supplemental/com.foo"));
+}
+
+TEST_F(AppSupplementalDataTest, CreateAppData_CreatesSupplementalAppData_WithoutCeFlag) {
+    android::os::CreateAppDataResult result;
+    android::os::CreateAppDataArgs args = createAppDataArgs();
+    args.packageName = "com.foo";
+    args.flags = FLAG_STORAGE_DE;
+
+    // Create the app user data.
+    ASSERT_BINDER_SUCCESS(service->createAppData(args, &result));
+
+    // CE paths should not exist
+    ASSERT_FALSE(exists("/data/local/tmp/misc_ce/0/supplemental/com.foo"));
+
+    // Only DE paths should exist
+    CheckFileAccess("misc_de/0/supplemental/com.foo", kSystemUid, S_IFDIR | 0751);
+}
+
 }  // namespace installd
 }  // namespace android
diff --git a/cmds/installd/tests/installd_utils_test.cpp b/cmds/installd/tests/installd_utils_test.cpp
index 514b881..8d1ccdc 100644
--- a/cmds/installd/tests/installd_utils_test.cpp
+++ b/cmds/installd/tests/installd_utils_test.cpp
@@ -658,6 +658,40 @@
     ASSERT_NE(0, create_dir_if_needed("/data/local/tmp/user/0/bar/baz", 0700));
 }
 
+TEST_F(UtilsTest, TestSupplementalDataPaths) {
+    // Ce data paths
+    EXPECT_EQ("/data/misc_ce/0/supplemental",
+              create_data_misc_supplemental_path(nullptr, /*isCeData=*/true, 0));
+    EXPECT_EQ("/data/misc_ce/10/supplemental",
+              create_data_misc_supplemental_path(nullptr, true, 10));
+
+    EXPECT_EQ("/data/misc_ce/0/supplemental/com.foo",
+              create_data_misc_supplemental_package_path(nullptr, true, 0, "com.foo"));
+    EXPECT_EQ("/data/misc_ce/10/supplemental/com.foo",
+              create_data_misc_supplemental_package_path(nullptr, true, 10, "com.foo"));
+
+    EXPECT_EQ("/data/misc_ce/0/supplemental/com.foo/shared",
+              create_data_misc_supplemental_shared_path(nullptr, true, 0, "com.foo"));
+    EXPECT_EQ("/data/misc_ce/10/supplemental/com.foo/shared",
+              create_data_misc_supplemental_shared_path(nullptr, true, 10, "com.foo"));
+
+    // De data paths
+    EXPECT_EQ("/data/misc_de/0/supplemental",
+              create_data_misc_supplemental_path(nullptr, /*isCeData=*/false, 0));
+    EXPECT_EQ("/data/misc_de/10/supplemental",
+              create_data_misc_supplemental_path(nullptr, false, 10));
+
+    EXPECT_EQ("/data/misc_de/0/supplemental/com.foo",
+              create_data_misc_supplemental_package_path(nullptr, false, 0, "com.foo"));
+    EXPECT_EQ("/data/misc_de/10/supplemental/com.foo",
+              create_data_misc_supplemental_package_path(nullptr, false, 10, "com.foo"));
+
+    EXPECT_EQ("/data/misc_de/0/supplemental/com.foo/shared",
+              create_data_misc_supplemental_shared_path(nullptr, false, 0, "com.foo"));
+    EXPECT_EQ("/data/misc_de/10/supplemental/com.foo/shared",
+              create_data_misc_supplemental_shared_path(nullptr, false, 10, "com.foo"));
+}
+
 TEST_F(UtilsTest, WaitChild) {
     pid_t pid = fork();
     if (pid == 0) {
diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp
index f04ee33..6650b76 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -197,6 +197,43 @@
     return StringPrintf("%s/user_de/%u", data.c_str(), userid);
 }
 
+/**
+ * Create the path name where supplemental data for all apps will be stored.
+ * E.g. /data/misc_ce/0/supplemental
+ */
+std::string create_data_misc_supplemental_path(const char* uuid, bool isCeData, userid_t user) {
+    std::string data(create_data_path(uuid));
+    if (isCeData) {
+        return StringPrintf("%s/misc_ce/%d/supplemental", data.c_str(), user);
+    } else {
+        return StringPrintf("%s/misc_de/%d/supplemental", data.c_str(), user);
+    }
+}
+
+/**
+ * Create the path name where code data for all codes in a particular app will be stored.
+ * E.g. /data/misc_ce/0/supplemental/<app-name>
+ */
+std::string create_data_misc_supplemental_package_path(const char* volume_uuid, bool isCeData,
+                                                       userid_t user, const char* package_name) {
+    check_package_name(package_name);
+    return StringPrintf("%s/%s",
+                        create_data_misc_supplemental_path(volume_uuid, isCeData, user).c_str(),
+                        package_name);
+}
+
+/**
+ * Create the path name where shared code data for a particular app will be stored.
+ * E.g. /data/misc_ce/0/supplemental/<app-name>/shared
+ */
+std::string create_data_misc_supplemental_shared_path(const char* volume_uuid, bool isCeData,
+                                                      userid_t user, const char* package_name) {
+    return StringPrintf("%s/shared",
+                        create_data_misc_supplemental_package_path(volume_uuid, isCeData, user,
+                                                                   package_name)
+                                .c_str());
+}
+
 std::string create_data_misc_ce_rollback_base_path(const char* volume_uuid, userid_t user) {
     return StringPrintf("%s/misc_ce/%u/rollback", create_data_path(volume_uuid).c_str(), user);
 }
diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h
index 3973894..2d00845 100644
--- a/cmds/installd/utils.h
+++ b/cmds/installd/utils.h
@@ -60,6 +60,13 @@
 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_supplemental_path(const char* volume_uuid, bool isCeData,
+                                               userid_t userid);
+std::string create_data_misc_supplemental_package_path(const char* volume_uuid, bool isCeData,
+                                                       userid_t userid, const char* package_name);
+std::string create_data_misc_supplemental_shared_path(const char* volume_uuid, bool isCeData,
+                                                      userid_t userid, const char* package_name);
+
 std::string create_data_misc_ce_rollback_base_path(const char* volume_uuid, userid_t user);
 std::string create_data_misc_de_rollback_base_path(const char* volume_uuid, userid_t user);
 std::string create_data_misc_ce_rollback_path(const char* volume_uuid, userid_t user,