diff options
author | 2022-01-12 21:05:31 +0000 | |
---|---|---|
committer | 2022-03-17 13:03:53 +0000 | |
commit | 7fa4f884f693bd94ad8f13fccc5a989dad0d40c8 (patch) | |
tree | 25e0f12d69755270cf50299b2c9ffb77f0240d40 | |
parent | 0b272119f0c45021db5f8fccb75a467761a537fc (diff) |
Added support to calculate storage using project ids on devices whose
user data is wiped and their add directories are created using project
id
Bug: b/215154615
Test: atest StorageHostTest
Test: atest tests/installd_service_test.cpp
Change-Id: Ib29af7c4c0e80d5b8902899b38de41a807e1cd72
Merged-In: Idfe262d5606a4f577587e75e4a29fdb55c021a37
-rw-r--r-- | cmds/installd/InstalldNativeService.cpp | 161 | ||||
-rw-r--r-- | cmds/installd/InstalldNativeService.h | 1 | ||||
-rw-r--r-- | cmds/installd/binder/android/os/IInstalld.aidl | 2 | ||||
-rw-r--r-- | cmds/installd/tests/installd_service_test.cpp | 77 | ||||
-rw-r--r-- | cmds/installd/utils.cpp | 38 | ||||
-rw-r--r-- | cmds/installd/utils.h | 2 |
6 files changed, 229 insertions, 52 deletions
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp index c5860e83c3..4eb838612e 100644 --- a/cmds/installd/InstalldNativeService.cpp +++ b/cmds/installd/InstalldNativeService.cpp @@ -55,7 +55,8 @@ #include <cutils/fs.h> #include <cutils/properties.h> #include <cutils/sched_policy.h> -#include <log/log.h> // TODO: Move everything to base/logging. +#include <linux/quota.h> +#include <log/log.h> // TODO: Move everything to base/logging. #include <logwrap/logwrap.h> #include <private/android_filesystem_config.h> #include <private/android_projectid_config.h> @@ -458,14 +459,37 @@ done: return res; } -static int prepare_app_dir(const std::string& path, mode_t target_mode, uid_t uid, gid_t gid) { +static bool internal_storage_has_project_id() { + // The following path is populated in setFirstBoot, so if this file is present + // then project ids can be used. + + auto using_project_ids = + StringPrintf("%smisc/installd/using_project_ids", android_data_dir.c_str()); + return access(using_project_ids.c_str(), F_OK) == 0; +} + +static int prepare_app_dir(const std::string& path, mode_t target_mode, uid_t uid, gid_t gid, + long project_id) { if (fs_prepare_dir_strict(path.c_str(), target_mode, uid, gid) != 0) { PLOG(ERROR) << "Failed to prepare " << path; return -1; } + if (internal_storage_has_project_id()) { + return set_quota_project_id(path, project_id, true); + } return 0; } +static int prepare_app_cache_dir(const std::string& parent, const char* name, mode_t target_mode, + uid_t uid, gid_t gid, long project_id) { + auto path = StringPrintf("%s/%s", parent.c_str(), name); + int ret = prepare_app_cache_dir(parent, name, target_mode, uid, gid); + if (ret == 0 && internal_storage_has_project_id()) { + return set_quota_project_id(path, project_id, true); + } + return ret; +} + static bool prepare_app_profile_dir(const std::string& packageName, int32_t appId, int32_t userId) { if (!property_get_bool("dalvik.vm.usejitprofiles", false)) { return true; @@ -625,9 +649,11 @@ static binder::Status createAppDataDirs(const std::string& path, int32_t uid, in } // Prepare only the parent app directory - if (prepare_app_dir(path, targetMode, uid, gid) || - prepare_app_cache_dir(path, "cache", 02771, uid, cacheGid) || - prepare_app_cache_dir(path, "code_cache", 02771, uid, cacheGid)) { + long project_id_app = get_project_id(uid, PROJECT_ID_APP_START); + long project_id_cache_app = get_project_id(uid, PROJECT_ID_APP_CACHE_START); + if (prepare_app_dir(path, targetMode, uid, gid, project_id_app) || + prepare_app_cache_dir(path, "cache", 02771, uid, cacheGid, project_id_cache_app) || + prepare_app_cache_dir(path, "code_cache", 02771, uid, cacheGid, project_id_cache_app)) { return error("Failed to prepare " + path); } @@ -773,7 +799,7 @@ binder::Status InstalldNativeService::createSdkSandboxDataPackageDirectory( LOG(DEBUG) << "Creating app-level sdk data directory: " << packagePath; #endif - if (prepare_app_dir(packagePath, 0751, AID_SYSTEM, AID_SYSTEM)) { + if (prepare_app_dir(packagePath, 0751, AID_SYSTEM, AID_SYSTEM, 0)) { return error("Failed to prepare " + packagePath); } @@ -2088,69 +2114,72 @@ static std::string toString(std::vector<int64_t> values) { return res.str(); } #endif +// On devices without sdcardfs, if internal and external are on +// the same volume, a uid such as u0_a123 is used for both +// internal and external storage; therefore, subtract that +// amount from internal to make sure we don't count it double. +// This needs to happen for data, cache and OBB +static void deductDoubleSpaceIfNeeded(stats* stats, int64_t doubleSpaceToBeDeleted, uid_t uid, + const std::string& uuid) { + if (!supports_sdcardfs()) { + stats->dataSize -= doubleSpaceToBeDeleted; + long obbProjectId = get_project_id(uid, PROJECT_ID_EXT_OBB_START); + int64_t appObbSize = GetOccupiedSpaceForProjectId(uuid, obbProjectId); + stats->dataSize -= appObbSize; + } +} static void collectQuotaStats(const std::string& uuid, int32_t userId, int32_t appId, struct stats* stats, struct stats* extStats) { - int64_t space; + int64_t space, doubleSpaceToBeDeleted = 0; uid_t uid = multiuser_get_uid(userId, appId); - if (stats != nullptr) { - if ((space = GetOccupiedSpaceForUid(uuid, uid)) != -1) { - stats->dataSize += space; - } - - int cacheGid = multiuser_get_cache_gid(userId, appId); - if (cacheGid != -1) { - if ((space = GetOccupiedSpaceForGid(uuid, cacheGid)) != -1) { - stats->cacheSize += space; - } - } - - int sharedGid = multiuser_get_shared_gid(0, appId); - if (sharedGid != -1) { - if ((space = GetOccupiedSpaceForGid(uuid, sharedGid)) != -1) { - stats->codeSize += space; - } - } - } + static const bool supportsProjectId = internal_storage_has_project_id(); if (extStats != nullptr) { - static const bool supportsSdCardFs = supports_sdcardfs(); space = get_occupied_app_space_external(uuid, userId, appId); if (space != -1) { extStats->dataSize += space; - if (!supportsSdCardFs && stats != nullptr) { - // On devices without sdcardfs, if internal and external are on - // the same volume, a uid such as u0_a123 is used for - // application dirs on both internal and external storage; - // therefore, substract that amount from internal to make sure - // we don't count it double. - stats->dataSize -= space; - } + doubleSpaceToBeDeleted += space; } space = get_occupied_app_cache_space_external(uuid, userId, appId); if (space != -1) { extStats->dataSize += space; // cache counts for "data" extStats->cacheSize += space; - if (!supportsSdCardFs && stats != nullptr) { - // On devices without sdcardfs, if internal and external are on - // the same volume, a uid such as u0_a123 is used for both - // internal and external storage; therefore, substract that - // amount from internal to make sure we don't count it double. - stats->dataSize -= space; + doubleSpaceToBeDeleted += space; + } + } + + if (stats != nullptr) { + if (!supportsProjectId) { + if ((space = GetOccupiedSpaceForUid(uuid, uid)) != -1) { + stats->dataSize += space; + } + deductDoubleSpaceIfNeeded(stats, doubleSpaceToBeDeleted, uid, uuid); + int cacheGid = multiuser_get_cache_gid(userId, appId); + if (cacheGid != -1) { + if ((space = GetOccupiedSpaceForGid(uuid, cacheGid)) != -1) { + stats->cacheSize += space; + } + } + } else { + long projectId = get_project_id(uid, PROJECT_ID_APP_START); + if ((space = GetOccupiedSpaceForProjectId(uuid, projectId)) != -1) { + stats->dataSize += space; + } + projectId = get_project_id(uid, PROJECT_ID_APP_CACHE_START); + if ((space = GetOccupiedSpaceForProjectId(uuid, projectId)) != -1) { + stats->cacheSize += space; + stats->dataSize += space; } } - if (!supportsSdCardFs && stats != nullptr) { - // On devices without sdcardfs, the UID of OBBs on external storage - // matches the regular app UID (eg u0_a123); therefore, to avoid - // OBBs being include in stats->dataSize, compute the OBB size for - // this app, and substract it from the size reported on internal - // storage - long obbProjectId = uid - AID_APP_START + PROJECT_ID_EXT_OBB_START; - int64_t appObbSize = GetOccupiedSpaceForProjectId(uuid, obbProjectId); - stats->dataSize -= appObbSize; + int sharedGid = multiuser_get_shared_gid(0, appId); + if (sharedGid != -1) { + if ((space = GetOccupiedSpaceForGid(uuid, sharedGid)) != -1) { + stats->codeSize += space; + } } } } @@ -2273,6 +2302,11 @@ static void collectManualExternalStatsForUser(const std::string& path, struct st fts_close(fts); } static bool ownsExternalStorage(int32_t appId) { + // if project id calculation is supported then, there is no need to + // calculate in a different way and project_id based calculation can work + if (internal_storage_has_project_id()) { + return false; + } // Fetch external storage owner appid and check if it is the same as the // current appId whose size is calculated struct stat s; @@ -3264,6 +3298,33 @@ binder::Status InstalldNativeService::hashSecondaryDexFile( return result ? ok() : error(); } +bool check_if_ioctl_feature_is_supported() { + bool result = false; + auto temp_path = StringPrintf("%smisc/installd/ioctl_check", android_data_dir.c_str()); + if (access(temp_path.c_str(), F_OK) != 0) { + open(temp_path.c_str(), O_CREAT | O_TRUNC | O_RDWR | O_CLOEXEC, 0644); + result = set_quota_project_id(temp_path, 0, true) == 0; + // delete the temp file + // remove the external file + remove(temp_path.c_str()); + } + return result; +} + +binder::Status InstalldNativeService::setFirstBoot() { + ENFORCE_UID(AID_SYSTEM); + std::lock_guard<std::recursive_mutex> lock(mMountsLock); + std::string uuid; + if (GetOccupiedSpaceForProjectId(uuid, 0) != -1 && check_if_ioctl_feature_is_supported()) { + auto first_boot_path = + StringPrintf("%smisc/installd/using_project_ids", android_data_dir.c_str()); + if (access(first_boot_path.c_str(), F_OK) != 0) { + open(first_boot_path.c_str(), O_CREAT | O_TRUNC | O_RDWR | O_CLOEXEC, 0644); + } + } + return ok(); +} + binder::Status InstalldNativeService::invalidateMounts() { ENFORCE_UID(AID_SYSTEM); std::lock_guard<std::recursive_mutex> lock(mMountsLock); diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h index a6b8bfa013..bb28a92fd9 100644 --- a/cmds/installd/InstalldNativeService.h +++ b/cmds/installd/InstalldNativeService.h @@ -169,6 +169,7 @@ public: int32_t storageFlag, std::vector<uint8_t>* _aidl_return); binder::Status invalidateMounts(); + binder::Status setFirstBoot(); binder::Status isQuotaSupported(const std::optional<std::string>& volumeUuid, bool* _aidl_return); binder::Status tryMountDataMirror(const std::optional<std::string>& volumeUuid); diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl index d76ee546d4..c17c6bfce3 100644 --- a/cmds/installd/binder/android/os/IInstalld.aidl +++ b/cmds/installd/binder/android/os/IInstalld.aidl @@ -20,7 +20,7 @@ package android.os; interface IInstalld { void createUserData(@nullable @utf8InCpp String uuid, int userId, int userSerial, int flags); void destroyUserData(@nullable @utf8InCpp String uuid, int userId, int flags); - + void setFirstBoot(); android.os.CreateAppDataResult createAppData(in android.os.CreateAppDataArgs args); android.os.CreateAppDataResult[] createAppDataBatched(in android.os.CreateAppDataArgs[] args); diff --git a/cmds/installd/tests/installd_service_test.cpp b/cmds/installd/tests/installd_service_test.cpp index 4423045d80..e436e7e606 100644 --- a/cmds/installd/tests/installd_service_test.cpp +++ b/cmds/installd/tests/installd_service_test.cpp @@ -463,7 +463,7 @@ TEST_F(ServiceTest, CalculateCache) { EXPECT_TRUE(create_cache_path(buf, "/path/to/file.apk", "isa")); EXPECT_EQ("/data/dalvik-cache/isa/path@to@file.apk@classes.dex", std::string(buf)); } -TEST_F(ServiceTest, GetAppSize) { +TEST_F(ServiceTest, GetAppSizeManualForMedia) { struct stat s; std::string externalPicDir = @@ -507,6 +507,81 @@ TEST_F(ServiceTest, GetAppSize) { system(removeCommand.c_str()); } } +TEST_F(ServiceTest, GetAppSizeProjectID_UID) { + struct stat s; + std::string externalPicDir = + StringPrintf("%s/Pictures", create_data_media_path(nullptr, 0).c_str()); + if (stat(externalPicDir.c_str(), &s) == 0) { + // fetch the appId from the uid of the external storage owning app + int32_t externalStorageAppId = multiuser_get_app_id(s.st_uid); + // Fetch Package Name for the external storage owning app uid + std::string pkg = get_package_name(s.st_uid); + + std::vector<int64_t> externalStorageSize, externalStorageSizeAfterAddingCacheFile; + std::vector<int64_t> ceDataInodes; + + std::vector<std::string> codePaths; + std::vector<std::string> packageNames; + // set up parameters + packageNames.push_back(pkg); + ceDataInodes.push_back(0); + // initialise the mounts + service->invalidateMounts(); + auto using_project_ids = + StringPrintf("%smisc/installd/using_project_ids", android_data_dir.c_str()); + bool usingProjectIds = access(using_project_ids.c_str(), F_OK) == 0; + if (!usingProjectIds) { + service->setFirstBoot(); + } + // call the getAppSize to get the current size of the external storage owning app + service->getAppSize(std::nullopt, packageNames, 0, InstalldNativeService::FLAG_USE_QUOTA, + externalStorageAppId, ceDataInodes, codePaths, &externalStorageSize); + // add a file with 20MB size to the external storage + std::string externalStorageCacheDir = + StringPrintf("%s/%s/cache", create_data_user_ce_path(nullptr, 0).c_str(), + pkg.c_str()); + std::string cacheFileLocation = + StringPrintf("%s/%s", externalStorageCacheDir.c_str(), "External.jpg"); + std::string externalFileContentCommand = + StringPrintf("dd if=/dev/zero of=%s bs=1M count=20", cacheFileLocation.c_str()); + system(externalFileContentCommand.c_str()); + // call the getAppSize again to get the new size of the external storage owning app + service->getAppSize(std::nullopt, packageNames, 0, InstalldNativeService::FLAG_USE_QUOTA, + externalStorageAppId, ceDataInodes, codePaths, + &externalStorageSizeAfterAddingCacheFile); + // check that the size of cache and data increases when cache file is added + int64_t sizeDiffData = externalStorageSizeAfterAddingCacheFile[1] - externalStorageSize[1]; + int64_t sizeDiffCache = externalStorageSizeAfterAddingCacheFile[2] - externalStorageSize[2]; + ASSERT_TRUE(sizeDiffData == sizeDiffCache); + // remove the external file + std::string removeCommand = StringPrintf("rm -f %s", cacheFileLocation.c_str()); + system(removeCommand.c_str()); + // remove the setFirstBoot setting + std::string removeCommand2 = "rm -f /data/misc/installd/using_project_ids"; + system(removeCommand2.c_str()); + // Do now without project id + std::vector<int64_t> sizeWithUID, sizeWithUIDAfterAddingCacheFile; + // call the getAppSize to get the current size of the external storage owning app + service->getAppSize(std::nullopt, packageNames, 0, InstalldNativeService::FLAG_USE_QUOTA, + externalStorageAppId, ceDataInodes, codePaths, &sizeWithUID); + // add a file with 20MB size to the external storage + system(externalFileContentCommand.c_str()); + // call the getAppSize again to get the new size of the external storage owning app + service->getAppSize(std::nullopt, packageNames, 0, InstalldNativeService::FLAG_USE_QUOTA, + externalStorageAppId, ceDataInodes, codePaths, + &sizeWithUIDAfterAddingCacheFile); + // check that the size of cache and data increases when cache file is added + sizeDiffData = sizeWithUIDAfterAddingCacheFile[1] - sizeWithUID[1]; + sizeDiffCache = sizeWithUIDAfterAddingCacheFile[2] - sizeWithUID[2]; + ASSERT_TRUE(sizeDiffData == sizeDiffCache); + // remove the external file + system(removeCommand.c_str()); + // reset the using_project_id if it was initially set + if (usingProjectIds) { + service->setFirstBoot(); + } + } +} TEST_F(ServiceTest, GetAppSizeWrongSizes) { int32_t externalStorageAppId = -1; std::vector<int64_t> externalStorageSize; diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp index 8cfd12313b..fd4d7bd360 100644 --- a/cmds/installd/utils.cpp +++ b/cmds/installd/utils.cpp @@ -37,6 +37,7 @@ #include <android-base/unique_fd.h> #include <cutils/fs.h> #include <cutils/properties.h> +#include <linux/fs.h> #include <log/log.h> #include <private/android_filesystem_config.h> #include <private/android_projectid_config.h> @@ -434,7 +435,44 @@ std::vector<userid_t> get_known_users(const char* volume_uuid) { return users; } +long get_project_id(uid_t uid, long start_project_id_range) { + return uid - AID_APP_START + start_project_id_range; +} + +int set_quota_project_id(const std::string& path, long project_id, bool set_inherit) { + struct fsxattr fsx; + android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC))); + if (fd == -1) { + PLOG(ERROR) << "Failed to open " << path << " to set project id."; + return -1; + } + + if (ioctl(fd, FS_IOC_FSGETXATTR, &fsx) == -1) { + PLOG(ERROR) << "Failed to get extended attributes for " << path << " to get project id."; + return -1; + } + + fsx.fsx_projid = project_id; + if (ioctl(fd, FS_IOC_FSSETXATTR, &fsx) == -1) { + PLOG(ERROR) << "Failed to set project id on " << path; + return -1; + } + if (set_inherit) { + unsigned int flags; + if (ioctl(fd, FS_IOC_GETFLAGS, &flags) == -1) { + PLOG(ERROR) << "Failed to get flags for " << path << " to set project id inheritance."; + return -1; + } + flags |= FS_PROJINHERIT_FL; + + if (ioctl(fd, FS_IOC_SETFLAGS, &flags) == -1) { + PLOG(ERROR) << "Failed to set flags for " << path << " to set project id inheritance."; + return -1; + } + } + return 0; +} int calculate_tree_size(const std::string& path, int64_t* size, int32_t include_gid, int32_t exclude_gid, bool exclude_apps) { FTS *fts; diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h index 54d77f95d6..dfc0bd50d4 100644 --- a/cmds/installd/utils.h +++ b/cmds/installd/utils.h @@ -173,6 +173,8 @@ int prepare_app_cache_dir(const std::string& parent, const char* name, mode_t ta uid_t uid, gid_t gid); bool supports_sdcardfs(); +long get_project_id(uid_t uid, long start_project_id_range); +int set_quota_project_id(const std::string& path, long project_id, bool set_inherit); int64_t get_occupied_app_space_external(const std::string& uuid, int32_t userId, int32_t appId); int64_t get_occupied_app_cache_space_external(const std::string& uuid, int32_t userId, int32_t appId); |