diff options
| -rw-r--r-- | cmds/installd/InstalldNativeService.cpp | 316 | ||||
| -rw-r--r-- | cmds/installd/InstalldNativeService.h | 5 | ||||
| -rw-r--r-- | cmds/installd/binder/android/os/IInstalld.aidl | 3 | ||||
| -rw-r--r-- | cmds/installd/utils.cpp | 47 | ||||
| -rw-r--r-- | cmds/installd/utils.h | 8 |
5 files changed, 313 insertions, 66 deletions
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp index 52be255297..0339ae50cb 100644 --- a/cmds/installd/InstalldNativeService.cpp +++ b/cmds/installd/InstalldNativeService.cpp @@ -18,12 +18,17 @@ #include <errno.h> #include <inttypes.h> +#include <fstream> +#include <fts.h> #include <regex> #include <stdlib.h> +#include <string.h> #include <sys/capability.h> #include <sys/file.h> #include <sys/resource.h> +#include <sys/quota.h> #include <sys/stat.h> +#include <sys/statvfs.h> #include <sys/types.h> #include <sys/wait.h> #include <sys/xattr.h> @@ -37,7 +42,6 @@ #include <cutils/log.h> // TODO: Move everything to base/logging. #include <cutils/properties.h> #include <cutils/sched_policy.h> -#include <diskusage/dirsize.h> #include <logwrap/logwrap.h> #include <private/android_filesystem_config.h> #include <selinux/android.h> @@ -53,6 +57,8 @@ #define LOG_TAG "installd" #endif +#define MEASURE_EXTERNAL 0 + using android::base::StringPrintf; namespace android { @@ -77,7 +83,7 @@ static constexpr int FLAG_STORAGE_CE = 1 << 1; // NOTE: keep in sync with Installer static constexpr int FLAG_CLEAR_CACHE_ONLY = 1 << 8; static constexpr int FLAG_CLEAR_CODE_CACHE_ONLY = 1 << 9; - +static constexpr int FLAG_USE_QUOTA = 1 << 12; namespace { @@ -259,9 +265,70 @@ static int prepare_app_dir(const std::string& path, mode_t target_mode, uid_t ui return 0; } -static int prepare_app_dir(const std::string& parent, const char* name, mode_t target_mode, - uid_t uid) { - return prepare_app_dir(StringPrintf("%s/%s", parent.c_str(), name), target_mode, uid); +/** + * Prepare an app cache directory, which offers to fix-up the GID and + * directory mode flags during a platform upgrade. + */ +static int prepare_app_cache_dir(const std::string& parent, const char* name, mode_t target_mode, + uid_t uid, gid_t gid) { + auto path = StringPrintf("%s/%s", parent.c_str(), name); + struct stat st; + if (stat(path.c_str(), &st) != 0) { + if (errno == ENOENT) { + // This is fine, just create it + if (fs_prepare_dir_strict(path.c_str(), target_mode, uid, gid) != 0) { + PLOG(ERROR) << "Failed to prepare " << path; + return -1; + } else { + return 0; + } + } else { + PLOG(ERROR) << "Failed to stat " << path; + return -1; + } + } + + if (st.st_uid != uid) { + // Mismatched UID is real trouble; we can't recover + LOG(ERROR) << "Mismatched UID at " << path << ": found " << st.st_uid + << " but expected " << uid; + return -1; + } else if (st.st_gid == gid && st.st_mode == target_mode) { + // Everything looks good! + return 0; + } + + // Directory is owned correctly, but GID or mode mismatch means it's + // probably a platform upgrade so we need to fix them + FTS *fts; + FTSENT *p; + char *argv[] = { (char*) path.c_str(), nullptr }; + if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_XDEV, NULL))) { + PLOG(ERROR) << "Failed to fts_open " << path; + return -1; + } + while ((p = fts_read(fts)) != NULL) { + switch (p->fts_info) { + case FTS_DP: + if (chmod(p->fts_accpath, target_mode) != 0) { + PLOG(WARNING) << "Failed to chmod " << p->fts_path; + } + // Intentional fall through to also set GID + case FTS_F: + if (chown(p->fts_accpath, -1, gid) != 0) { + PLOG(WARNING) << "Failed to chown " << p->fts_path; + } + break; + case FTS_SL: + case FTS_SLNONE: + if (lchown(p->fts_accpath, -1, gid) != 0) { + PLOG(WARNING) << "Failed to chown " << p->fts_path; + } + break; + } + } + fts_close(fts); + return 0; } binder::Status InstalldNativeService::createAppData(const std::unique_ptr<std::string>& uuid, @@ -278,14 +345,16 @@ binder::Status InstalldNativeService::createAppData(const std::unique_ptr<std::s if (_aidl_return != nullptr) *_aidl_return = -1; uid_t uid = multiuser_get_uid(userId, appId); - mode_t target_mode = targetSdkVersion >= MIN_RESTRICTED_HOME_SDK_VERSION ? 0700 : 0751; + gid_t cacheGid = multiuser_get_cache_gid(userId, appId); + mode_t targetMode = targetSdkVersion >= MIN_RESTRICTED_HOME_SDK_VERSION ? 0700 : 0751; + if (flags & FLAG_STORAGE_CE) { auto path = create_data_user_ce_package_path(uuid_, userId, pkgname); bool existing = (access(path.c_str(), F_OK) == 0); - if (prepare_app_dir(path, target_mode, uid) || - prepare_app_dir(path, "cache", 0771, uid) || - prepare_app_dir(path, "code_cache", 0771, uid)) { + if (prepare_app_dir(path, targetMode, uid) || + prepare_app_cache_dir(path, "cache", 02771, uid, cacheGid) || + prepare_app_cache_dir(path, "code_cache", 02771, uid, cacheGid)) { return error("Failed to prepare " + path); } @@ -314,7 +383,9 @@ binder::Status InstalldNativeService::createAppData(const std::unique_ptr<std::s auto path = create_data_user_de_package_path(uuid_, userId, pkgname); bool existing = (access(path.c_str(), F_OK) == 0); - if (prepare_app_dir(path, target_mode, uid)) { + if (prepare_app_dir(path, targetMode, uid) || + prepare_app_cache_dir(path, "cache", 02771, uid, cacheGid) || + prepare_app_cache_dir(path, "code_cache", 02771, uid, cacheGid)) { return error("Failed to prepare " + path); } @@ -779,8 +850,91 @@ binder::Status InstalldNativeService::rmdex(const std::string& codePath, } } -static void add_app_data_size(std::string& path, int64_t *codesize, int64_t *datasize, - int64_t *cachesize) { +static bool uuidEquals(const std::unique_ptr<std::string>& a, + const std::unique_ptr<std::string>& b) { + if (!a && !b) { + return true; + } else if (!a && b) { + return false; + } else if (a && !b) { + return false; + } else { + return *a == *b; + } +} + +struct stats { + int64_t codeSize; + int64_t dataSize; + int64_t cacheSize; +}; + +static void collectQuotaStats(const std::unique_ptr<std::string>& uuid, int32_t userId, + int32_t appId, struct stats* stats) { + struct dqblk dq; + + auto path = create_data_path(uuid ? uuid->c_str() : nullptr); + std::string device; + { + std::ifstream in("/proc/mounts"); + if (!in.is_open()) { + PLOG(ERROR) << "Failed to read mounts"; + return; + } + std::string source; + std::string target; + while (!in.eof()) { + std::getline(in, source, ' '); + std::getline(in, target, ' '); + if (target == path) { + device = source; + break; + } + // Skip to next line + std::getline(in, source); + } + } + if (device.empty()) { + PLOG(ERROR) << "Failed to resolve block device for " << path; + return; + } + + uid_t uid = multiuser_get_uid(userId, appId); + if (quotactl(QCMD(Q_GETQUOTA, USRQUOTA), device.c_str(), uid, + reinterpret_cast<char*>(&dq)) != 0) { + if (errno != ESRCH) { + PLOG(ERROR) << "Failed to quotactl " << device << " for UID " << uid; + } + } else { + stats->dataSize += dq.dqb_curspace; + } + + int cacheGid = multiuser_get_cache_gid(userId, appId); + if (cacheGid != -1) { + if (quotactl(QCMD(Q_GETQUOTA, GRPQUOTA), device.c_str(), cacheGid, + reinterpret_cast<char*>(&dq)) != 0) { + if (errno != ESRCH) { + PLOG(ERROR) << "Failed to quotactl " << device << " for GID " << cacheGid; + } + } else { + stats->cacheSize += dq.dqb_curspace; + } + } + + int sharedGid = multiuser_get_shared_app_gid(uid); + if (sharedGid != -1) { + if (quotactl(QCMD(Q_GETQUOTA, GRPQUOTA), device.c_str(), sharedGid, + reinterpret_cast<char*>(&dq)) != 0) { + if (errno != ESRCH) { + PLOG(ERROR) << "Failed to quotactl " << device << " for GID " << sharedGid; + } + } else { + stats->codeSize += dq.dqb_curspace; + } + } +} + +static void collectManualStats(std::string& path, struct stats* stats) { DIR *d; int dfd; struct dirent *de; @@ -788,87 +942,123 @@ static void add_app_data_size(std::string& path, int64_t *codesize, int64_t *dat d = opendir(path.c_str()); if (d == nullptr) { - PLOG(WARNING) << "Failed to open " << path; + if (errno != ENOENT) { + PLOG(WARNING) << "Failed to open " << path; + } return; } dfd = dirfd(d); while ((de = readdir(d))) { const char *name = de->d_name; - int64_t statsize = 0; + int64_t size = 0; if (fstatat(dfd, name, &s, AT_SYMLINK_NOFOLLOW) == 0) { - statsize = stat_size(&s); + size = s.st_blocks * 512; } if (de->d_type == DT_DIR) { - int subfd; - int64_t dirsize = 0; - /* always skip "." and ".." */ - if (name[0] == '.') { - if (name[1] == 0) continue; - if ((name[1] == '.') && (name[2] == 0)) continue; - } - subfd = openat(dfd, name, O_RDONLY | O_DIRECTORY); - if (subfd >= 0) { - dirsize = calculate_dir_size(subfd); - close(subfd); + if (!strcmp(name, ".")) { + // Don't recurse, but still count node size + } else if (!strcmp(name, "..")) { + // Don't recurse or count node size + continue; + } else { + // Measure all children nodes + size = 0; + calculate_tree_size(StringPrintf("%s/%s", path.c_str(), name), &size); } - // TODO: check xattrs! + if (!strcmp(name, "cache") || !strcmp(name, "code_cache")) { - *datasize += statsize; - *cachesize += dirsize; - } else { - *datasize += dirsize + statsize; + stats->cacheSize += size; } - } else if (de->d_type == DT_LNK && !strcmp(name, "lib")) { - *codesize += statsize; - } else { - *datasize += statsize; } + + // Legacy symlink isn't owned by app + if (de->d_type == DT_LNK && !strcmp(name, "lib")) { + continue; + } + + // Everything found inside is considered data + stats->dataSize += size; } closedir(d); } binder::Status InstalldNativeService::getAppSize(const std::unique_ptr<std::string>& uuid, - const std::string& packageName, int32_t userId, int32_t flags, int64_t ceDataInode, - const std::string& codePath, std::vector<int64_t>* _aidl_return) { + const std::string& packageName, int32_t userId, int32_t flags, int32_t appId, + int64_t ceDataInode, const std::string& codePath, + const std::unique_ptr<std::string>& externalUuid, std::vector<int64_t>* _aidl_return) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); const char* uuid_ = uuid ? uuid->c_str() : nullptr; + const char* extuuid_ = externalUuid ? externalUuid->c_str() : nullptr; const char* pkgname = packageName.c_str(); - const char* code_path = codePath.c_str(); - DIR *d; - int dfd; - int64_t codesize = 0; - int64_t datasize = 0; - int64_t cachesize = 0; - int64_t asecsize = 0; + // Here's a summary of the common storage locations across the platform, + // and how they're each tagged: + // + // /data/app/com.example UID system + // /data/app/com.example/oat UID system + // /data/user/0/com.example UID u0_a10 GID u0_a10 + // /data/user/0/com.example/cache UID u0_a10 GID u0_a10_cache + // /data/media/0/Android/data/com.example UID u0_a10 GID u0_a10 + // /data/media/0/Android/data/com.example/cache UID u0_a10 GID u0_a10_cache + // /data/media/0/Android/obb/com.example UID system + + struct stats stats; + memset(&stats, 0, sizeof(stats)); + + auto obbCodePath = create_data_media_package_path(extuuid_, userId, pkgname, "obb"); + calculate_tree_size(obbCodePath, &stats.codeSize); + + if (flags & FLAG_USE_QUOTA) { + calculate_tree_size(codePath, &stats.codeSize, + 0, multiuser_get_shared_gid(userId, appId)); + + collectQuotaStats(uuid, userId, appId, &stats); + + // If external storage lives on a different storage device, also + // collect quota stats from that block device + if (!uuidEquals(uuid, externalUuid)) { + collectQuotaStats(externalUuid, userId, appId, &stats); + } + } else { + calculate_tree_size(codePath, &stats.codeSize); - d = opendir(code_path); - if (d != nullptr) { - dfd = dirfd(d); - codesize += calculate_dir_size(dfd); - closedir(d); - } + auto cePath = create_data_user_ce_package_path(uuid_, userId, pkgname, ceDataInode); + collectManualStats(cePath, &stats); - if (flags & FLAG_STORAGE_CE) { - auto path = create_data_user_ce_package_path(uuid_, userId, pkgname, ceDataInode); - add_app_data_size(path, &codesize, &datasize, &cachesize); - } - if (flags & FLAG_STORAGE_DE) { - auto path = create_data_user_de_package_path(uuid_, userId, pkgname); - add_app_data_size(path, &codesize, &datasize, &cachesize); + auto dePath = create_data_user_de_package_path(uuid_, userId, pkgname); + collectManualStats(dePath, &stats); + + auto userProfilePath = create_data_user_profile_package_path(userId, pkgname); + calculate_tree_size(userProfilePath, &stats.dataSize); + + auto refProfilePath = create_data_ref_profile_package_path(pkgname); + calculate_tree_size(refProfilePath, &stats.codeSize); + + calculate_tree_size(create_data_dalvik_cache_path(), &stats.codeSize, + multiuser_get_shared_gid(userId, appId), 0); + + calculate_tree_size(create_data_misc_foreign_dex_path(userId), &stats.dataSize, + multiuser_get_uid(userId, appId), 0); + +#if MEASURE_EXTERNAL + auto extPath = create_data_media_package_path(extuuid_, userId, pkgname, "data"); + collectManualStats(extPath, &stats); + + auto mediaPath = create_data_media_package_path(extuuid_, userId, pkgname, "media"); + calculate_tree_size(mediaPath, &stats.dataSize); +#endif } - std::vector<int64_t> res; - res.push_back(codesize); - res.push_back(datasize); - res.push_back(cachesize); - res.push_back(asecsize); - *_aidl_return = res; + std::vector<int64_t> ret; + ret.push_back(stats.codeSize); + ret.push_back(stats.dataSize); + ret.push_back(stats.cacheSize); + *_aidl_return = ret; return ok(); } diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h index d2564e59e4..cad9e436e2 100644 --- a/cmds/installd/InstalldNativeService.h +++ b/cmds/installd/InstalldNativeService.h @@ -56,8 +56,9 @@ public: binder::Status destroyAppData(const std::unique_ptr<std::string>& uuid, const std::string& packageName, int32_t userId, int32_t flags, int64_t ceDataInode); binder::Status getAppSize(const std::unique_ptr<std::string>& uuid, - const std::string& packageName, int32_t userId, int32_t flags, int64_t ceDataInode, - const std::string& codePath, std::vector<int64_t>* _aidl_return); + const std::string& packageName, int32_t userId, int32_t flags, int32_t appId, + int64_t ceDataInode, const std::string& codePath, + const std::unique_ptr<std::string>& externalUuid, std::vector<int64_t>* _aidl_return); binder::Status moveCompleteApp(const std::unique_ptr<std::string>& fromUuid, const std::unique_ptr<std::string>& toUuid, const std::string& packageName, diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl index 46564c4445..8c5d2f4a4b 100644 --- a/cmds/installd/binder/android/os/IInstalld.aidl +++ b/cmds/installd/binder/android/os/IInstalld.aidl @@ -32,7 +32,8 @@ interface IInstalld { void destroyAppData(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName, int userId, int flags, long ceDataInode); long[] getAppSize(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName, - int userId, int flags, long ceDataInode, @utf8InCpp String codePath); + int userId, int flags, int appId, long ceDataInode, @utf8InCpp String codePath, + @nullable @utf8InCpp String externalUuid); void moveCompleteApp(@nullable @utf8InCpp String fromUuid, @nullable @utf8InCpp String toUuid, @utf8InCpp String packageName, @utf8InCpp String dataAppName, int appId, diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp index 37e303da97..e1a59d4d9a 100644 --- a/cmds/installd/utils.cpp +++ b/cmds/installd/utils.cpp @@ -18,6 +18,7 @@ #include <errno.h> #include <fcntl.h> +#include <fts.h> #include <stdlib.h> #include <sys/stat.h> #include <sys/wait.h> @@ -198,6 +199,12 @@ std::string create_data_media_path(const char* volume_uuid, userid_t userid) { return StringPrintf("%s/media/%u", create_data_path(volume_uuid).c_str(), userid); } +std::string create_data_media_package_path(const char* volume_uuid, userid_t userid, + const char* data_type, const char* package_name) { + return StringPrintf("%s/Android/%s/%s", create_data_media_path(volume_uuid, userid).c_str(), + data_type, package_name); +} + std::string create_data_misc_legacy_path(userid_t userid) { return StringPrintf("%s/misc/user/%u", create_data_path(nullptr).c_str(), userid); } @@ -216,6 +223,14 @@ std::string create_data_ref_profile_package_path(const char* package_name) { return StringPrintf("%s/ref/%s", android_profiles_dir.path, package_name); } +std::string create_data_dalvik_cache_path() { + return "/data/dalvik-cache"; +} + +std::string create_data_misc_foreign_dex_path(userid_t userid) { + return StringPrintf("/data/misc/profiles/cur/%d/foreign-dex", userid); +} + // Keep profile paths in sync with ActivityThread. constexpr const char* PRIMARY_PROFILE_NAME = "primary.prof"; @@ -255,6 +270,38 @@ std::vector<userid_t> get_known_users(const char* volume_uuid) { return users; } +int calculate_tree_size(const std::string& path, int64_t* size, + gid_t include_gid, gid_t exclude_gid) { + FTS *fts; + FTSENT *p; + char *argv[] = { (char*) path.c_str(), nullptr }; + if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_XDEV, NULL))) { + if (errno != ENOENT) { + PLOG(ERROR) << "Failed to fts_open " << path; + } + return -1; + } + while ((p = fts_read(fts)) != NULL) { + switch (p->fts_info) { + case FTS_D: + case FTS_DEFAULT: + case FTS_F: + case FTS_SL: + case FTS_SLNONE: + if (include_gid != 0 && p->fts_statp->st_gid != include_gid) { + break; + } + if (exclude_gid != 0 && p->fts_statp->st_gid == exclude_gid) { + break; + } + *size += (p->fts_statp->st_blocks * 512); + break; + } + } + fts_close(fts); + return 0; +} + int create_move_path(char path[PKG_PATH_MAX], const char* pkgname, const char* leaf, diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h index 4c299fd55e..ff041183cb 100644 --- a/cmds/installd/utils.h +++ b/cmds/installd/utils.h @@ -86,6 +86,8 @@ std::string create_data_user_de_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_package_path(const char* volume_uuid, userid_t userid, + const char* data_type, const char* package_name); std::string create_data_misc_legacy_path(userid_t userid); @@ -93,10 +95,16 @@ std::string create_data_user_profiles_path(userid_t userid); std::string create_data_user_profile_package_path(userid_t user, const char* package_name); std::string create_data_ref_profile_package_path(const char* package_name); +std::string create_data_dalvik_cache_path(); +std::string create_data_misc_foreign_dex_path(userid_t userid); + std::string create_primary_profile(const std::string& profile_dir); std::vector<userid_t> get_known_users(const char* volume_uuid); +int calculate_tree_size(const std::string& path, int64_t* size, + gid_t include_gid = 0, gid_t exclude_gid = 0); + int create_user_config_path(char path[PKG_PATH_MAX], userid_t userid); int create_move_path(char path[PKG_PATH_MAX], |