From 19803807cd7ae01868fcfa50305f4a7dd13765e2 Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Tue, 7 Apr 2015 12:44:51 -0700 Subject: Switch installd to compile as C++. This is the minimal change needed to switch it over to C++, which paves the way for using more robust utilities like std::string. Change-Id: I80ed6280146875eb6ddbbb340c05450388ca13f0 --- cmds/installd/Android.mk | 4 +- cmds/installd/commands.c | 1763 --------------------------- cmds/installd/commands.cpp | 1763 +++++++++++++++++++++++++++ cmds/installd/installd.c | 760 ------------ cmds/installd/installd.cpp | 760 ++++++++++++ cmds/installd/installd.h | 6 +- cmds/installd/tests/installd_utils_test.cpp | 28 +- cmds/installd/utils.c | 1171 ------------------ cmds/installd/utils.cpp | 1171 ++++++++++++++++++ 9 files changed, 3714 insertions(+), 3712 deletions(-) delete mode 100644 cmds/installd/commands.c create mode 100644 cmds/installd/commands.cpp delete mode 100644 cmds/installd/installd.c create mode 100644 cmds/installd/installd.cpp delete mode 100644 cmds/installd/utils.c create mode 100644 cmds/installd/utils.cpp diff --git a/cmds/installd/Android.mk b/cmds/installd/Android.mk index 8224e94..2d90ff3 100644 --- a/cmds/installd/Android.mk +++ b/cmds/installd/Android.mk @@ -1,6 +1,6 @@ LOCAL_PATH := $(call my-dir) -common_src_files := commands.c utils.c +common_src_files := commands.cpp utils.cpp common_cflags := -Wall -Werror # @@ -23,7 +23,7 @@ include $(CLEAR_VARS) LOCAL_MODULE := installd LOCAL_MODULE_TAGS := optional LOCAL_CFLAGS := $(common_cflags) -LOCAL_SRC_FILES := installd.c $(common_src_files) +LOCAL_SRC_FILES := installd.cpp $(common_src_files) LOCAL_SHARED_LIBRARIES := libcutils liblog libselinux LOCAL_STATIC_LIBRARIES := libdiskusage LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk diff --git a/cmds/installd/commands.c b/cmds/installd/commands.c deleted file mode 100644 index 5f72ae4..0000000 --- a/cmds/installd/commands.c +++ /dev/null @@ -1,1763 +0,0 @@ -/* -** Copyright 2008, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - -#include -#include -#include -#include "installd.h" -#include -#include -#include -#include - -/* Directory records that are used in execution of commands. */ -dir_rec_t android_data_dir; -dir_rec_t android_asec_dir; -dir_rec_t android_app_dir; -dir_rec_t android_app_private_dir; -dir_rec_t android_app_lib_dir; -dir_rec_t android_media_dir; -dir_rec_t android_mnt_expand_dir; -dir_rec_array_t android_system_dirs; - -int install(const char *pkgname, uid_t uid, gid_t gid, const char *seinfo) -{ - char pkgdir[PKG_PATH_MAX]; - char libsymlink[PKG_PATH_MAX]; - char applibdir[PKG_PATH_MAX]; - struct stat libStat; - - if ((uid < AID_SYSTEM) || (gid < AID_SYSTEM)) { - ALOGE("invalid uid/gid: %d %d\n", uid, gid); - return -1; - } - - if (create_pkg_path(pkgdir, pkgname, PKG_DIR_POSTFIX, 0)) { - ALOGE("cannot create package path\n"); - return -1; - } - - if (create_pkg_path(libsymlink, pkgname, PKG_LIB_POSTFIX, 0)) { - ALOGE("cannot create package lib symlink origin path\n"); - return -1; - } - - if (create_pkg_path_in_dir(applibdir, &android_app_lib_dir, pkgname, PKG_DIR_POSTFIX)) { - ALOGE("cannot create package lib symlink dest path\n"); - return -1; - } - - if (mkdir(pkgdir, 0751) < 0) { - ALOGE("cannot create dir '%s': %s\n", pkgdir, strerror(errno)); - return -1; - } - if (chmod(pkgdir, 0751) < 0) { - ALOGE("cannot chmod dir '%s': %s\n", pkgdir, strerror(errno)); - unlink(pkgdir); - return -1; - } - - if (lstat(libsymlink, &libStat) < 0) { - if (errno != ENOENT) { - ALOGE("couldn't stat lib dir: %s\n", strerror(errno)); - return -1; - } - } else { - if (S_ISDIR(libStat.st_mode)) { - if (delete_dir_contents(libsymlink, 1, NULL) < 0) { - ALOGE("couldn't delete lib directory during install for: %s", libsymlink); - return -1; - } - } else if (S_ISLNK(libStat.st_mode)) { - if (unlink(libsymlink) < 0) { - ALOGE("couldn't unlink lib directory during install for: %s", libsymlink); - return -1; - } - } - } - - if (selinux_android_setfilecon(pkgdir, pkgname, seinfo, uid) < 0) { - ALOGE("cannot setfilecon dir '%s': %s\n", pkgdir, strerror(errno)); - unlink(libsymlink); - unlink(pkgdir); - return -errno; - } - - if (symlink(applibdir, libsymlink) < 0) { - ALOGE("couldn't symlink directory '%s' -> '%s': %s\n", libsymlink, applibdir, - strerror(errno)); - unlink(pkgdir); - return -1; - } - - if (chown(pkgdir, uid, gid) < 0) { - ALOGE("cannot chown dir '%s': %s\n", pkgdir, strerror(errno)); - unlink(libsymlink); - unlink(pkgdir); - return -1; - } - - return 0; -} - -int uninstall(const char *pkgname, userid_t userid) -{ - char pkgdir[PKG_PATH_MAX]; - - if (create_pkg_path(pkgdir, pkgname, PKG_DIR_POSTFIX, userid)) - return -1; - - remove_profile_file(pkgname); - - /* delete contents AND directory, no exceptions */ - return delete_dir_contents(pkgdir, 1, NULL); -} - -int renamepkg(const char *oldpkgname, const char *newpkgname) -{ - char oldpkgdir[PKG_PATH_MAX]; - char newpkgdir[PKG_PATH_MAX]; - - if (create_pkg_path(oldpkgdir, oldpkgname, PKG_DIR_POSTFIX, 0)) - return -1; - if (create_pkg_path(newpkgdir, newpkgname, PKG_DIR_POSTFIX, 0)) - return -1; - - if (rename(oldpkgdir, newpkgdir) < 0) { - ALOGE("cannot rename dir '%s' to '%s': %s\n", oldpkgdir, newpkgdir, strerror(errno)); - return -errno; - } - return 0; -} - -int fix_uid(const char *pkgname, uid_t uid, gid_t gid) -{ - char pkgdir[PKG_PATH_MAX]; - struct stat s; - - if ((uid < AID_SYSTEM) || (gid < AID_SYSTEM)) { - ALOGE("invalid uid/gid: %d %d\n", uid, gid); - return -1; - } - - if (create_pkg_path(pkgdir, pkgname, PKG_DIR_POSTFIX, 0)) { - ALOGE("cannot create package path\n"); - return -1; - } - - if (stat(pkgdir, &s) < 0) return -1; - - if (s.st_uid != 0 || s.st_gid != 0) { - ALOGE("fixing uid of non-root pkg: %s %" PRIu32 " %" PRIu32 "\n", pkgdir, s.st_uid, s.st_gid); - return -1; - } - - if (chmod(pkgdir, 0751) < 0) { - ALOGE("cannot chmod dir '%s': %s\n", pkgdir, strerror(errno)); - unlink(pkgdir); - return -errno; - } - if (chown(pkgdir, uid, gid) < 0) { - ALOGE("cannot chown dir '%s': %s\n", pkgdir, strerror(errno)); - unlink(pkgdir); - return -errno; - } - - return 0; -} - -int delete_user_data(const char *pkgname, userid_t userid) -{ - char pkgdir[PKG_PATH_MAX]; - - if (create_pkg_path(pkgdir, pkgname, PKG_DIR_POSTFIX, userid)) - return -1; - - return delete_dir_contents(pkgdir, 0, NULL); -} - -int make_user_data(const char *pkgname, uid_t uid, userid_t userid, const char* seinfo) -{ - char pkgdir[PKG_PATH_MAX]; - char applibdir[PKG_PATH_MAX]; - char libsymlink[PKG_PATH_MAX]; - struct stat libStat; - - // Create the data dir for the package - if (create_pkg_path(pkgdir, pkgname, PKG_DIR_POSTFIX, userid)) { - return -1; - } - if (create_pkg_path(libsymlink, pkgname, PKG_LIB_POSTFIX, userid)) { - ALOGE("cannot create package lib symlink origin path\n"); - return -1; - } - if (create_pkg_path_in_dir(applibdir, &android_app_lib_dir, pkgname, PKG_DIR_POSTFIX)) { - ALOGE("cannot create package lib symlink dest path\n"); - return -1; - } - - if (mkdir(pkgdir, 0751) < 0) { - ALOGE("cannot create dir '%s': %s\n", pkgdir, strerror(errno)); - return -errno; - } - if (chmod(pkgdir, 0751) < 0) { - ALOGE("cannot chmod dir '%s': %s\n", pkgdir, strerror(errno)); - unlink(pkgdir); - return -errno; - } - - if (lstat(libsymlink, &libStat) < 0) { - if (errno != ENOENT) { - ALOGE("couldn't stat lib dir for non-primary: %s\n", strerror(errno)); - unlink(pkgdir); - return -1; - } - } else { - if (S_ISDIR(libStat.st_mode)) { - if (delete_dir_contents(libsymlink, 1, NULL) < 0) { - ALOGE("couldn't delete lib directory during install for non-primary: %s", - libsymlink); - unlink(pkgdir); - return -1; - } - } else if (S_ISLNK(libStat.st_mode)) { - if (unlink(libsymlink) < 0) { - ALOGE("couldn't unlink lib directory during install for non-primary: %s", - libsymlink); - unlink(pkgdir); - return -1; - } - } - } - - if (selinux_android_setfilecon(pkgdir, pkgname, seinfo, uid) < 0) { - ALOGE("cannot setfilecon dir '%s': %s\n", pkgdir, strerror(errno)); - unlink(libsymlink); - unlink(pkgdir); - return -errno; - } - - if (symlink(applibdir, libsymlink) < 0) { - ALOGE("couldn't symlink directory for non-primary '%s' -> '%s': %s\n", libsymlink, - applibdir, strerror(errno)); - unlink(pkgdir); - return -1; - } - - if (chown(pkgdir, uid, uid) < 0) { - ALOGE("cannot chown dir '%s': %s\n", pkgdir, strerror(errno)); - unlink(libsymlink); - unlink(pkgdir); - return -errno; - } - - return 0; -} - -int make_user_config(userid_t userid) -{ - if (ensure_config_user_dirs(userid) == -1) { - return -1; - } - - return 0; -} - -int delete_user(userid_t userid) -{ - int status = 0; - - char data_path[PKG_PATH_MAX]; - if ((create_user_path(data_path, userid) != 0) - || (delete_dir_contents(data_path, 1, NULL) != 0)) { - status = -1; - } - - char media_path[PATH_MAX]; - if ((create_user_media_path(media_path, userid) != 0) - || (delete_dir_contents(media_path, 1, NULL) != 0)) { - status = -1; - } - - char config_path[PATH_MAX]; - if ((create_user_config_path(config_path, userid) != 0) - || (delete_dir_contents(config_path, 1, NULL) != 0)) { - status = -1; - } - - return status; -} - -int delete_cache(const char *pkgname, userid_t userid) -{ - char cachedir[PKG_PATH_MAX]; - - if (create_pkg_path(cachedir, pkgname, CACHE_DIR_POSTFIX, userid)) - return -1; - - /* delete contents, not the directory, no exceptions */ - return delete_dir_contents(cachedir, 0, NULL); -} - -int delete_code_cache(const char *pkgname, userid_t userid) -{ - char codecachedir[PKG_PATH_MAX]; - struct stat s; - - if (create_pkg_path(codecachedir, pkgname, CODE_CACHE_DIR_POSTFIX, userid)) - return -1; - - /* it's okay if code cache is missing */ - if (lstat(codecachedir, &s) == -1 && errno == ENOENT) { - return 0; - } - - /* delete contents, not the directory, no exceptions */ - return delete_dir_contents(codecachedir, 0, NULL); -} - -/* Try to ensure free_size bytes of storage are available. - * Returns 0 on success. - * This is rather simple-minded because doing a full LRU would - * be potentially memory-intensive, and without atime it would - * also require that apps constantly modify file metadata even - * when just reading from the cache, which is pretty awful. - */ -int free_cache(int64_t free_size) -{ - cache_t* cache; - int64_t avail; - DIR *d; - struct dirent *de; - char tmpdir[PATH_MAX]; - char *dirpos; - - avail = data_disk_free(); - if (avail < 0) return -1; - - ALOGI("free_cache(%" PRId64 ") avail %" PRId64 "\n", free_size, avail); - if (avail >= free_size) return 0; - - cache = start_cache_collection(); - - // Collect cache files for primary user. - if (create_user_path(tmpdir, 0) == 0) { - //ALOGI("adding cache files from %s\n", tmpdir); - add_cache_files(cache, tmpdir, "cache"); - } - - // Search for other users and add any cache files from them. - snprintf(tmpdir, sizeof(tmpdir), "%s%s", android_data_dir.path, - SECONDARY_USER_PREFIX); - dirpos = tmpdir + strlen(tmpdir); - d = opendir(tmpdir); - if (d != NULL) { - while ((de = readdir(d))) { - if (de->d_type == DT_DIR) { - const char *name = de->d_name; - /* always skip "." and ".." */ - if (name[0] == '.') { - if (name[1] == 0) continue; - if ((name[1] == '.') && (name[2] == 0)) continue; - } - if ((strlen(name)+(dirpos-tmpdir)) < (sizeof(tmpdir)-1)) { - strcpy(dirpos, name); - //ALOGI("adding cache files from %s\n", tmpdir); - add_cache_files(cache, tmpdir, "cache"); - } else { - ALOGW("Path exceeds limit: %s%s", tmpdir, name); - } - } - } - closedir(d); - } - - // Collect cache files on external storage for all users (if it is mounted as part - // of the internal storage). - strcpy(tmpdir, android_media_dir.path); - dirpos = tmpdir + strlen(tmpdir); - d = opendir(tmpdir); - if (d != NULL) { - while ((de = readdir(d))) { - if (de->d_type == DT_DIR) { - const char *name = de->d_name; - /* skip any dir that doesn't start with a number, so not a user */ - if (name[0] < '0' || name[0] > '9') { - continue; - } - if ((strlen(name)+(dirpos-tmpdir)) < (sizeof(tmpdir)-1)) { - strcpy(dirpos, name); - if (lookup_media_dir(tmpdir, "Android") == 0 - && lookup_media_dir(tmpdir, "data") == 0) { - //ALOGI("adding cache files from %s\n", tmpdir); - add_cache_files(cache, tmpdir, "cache"); - } - } else { - ALOGW("Path exceeds limit: %s%s", tmpdir, name); - } - } - } - closedir(d); - } - - clear_cache_files(cache, free_size); - finish_cache_collection(cache); - - return data_disk_free() >= free_size ? 0 : -1; -} - -int move_dex(const char *src, const char *dst, const char *instruction_set) -{ - char src_dex[PKG_PATH_MAX]; - char dst_dex[PKG_PATH_MAX]; - - if (validate_apk_path(src)) { - ALOGE("invalid apk path '%s' (bad prefix)\n", src); - return -1; - } - if (validate_apk_path(dst)) { - ALOGE("invalid apk path '%s' (bad prefix)\n", dst); - return -1; - } - - if (create_cache_path(src_dex, src, instruction_set)) return -1; - if (create_cache_path(dst_dex, dst, instruction_set)) return -1; - - ALOGV("move %s -> %s\n", src_dex, dst_dex); - if (rename(src_dex, dst_dex) < 0) { - ALOGE("Couldn't move %s: %s\n", src_dex, strerror(errno)); - return -1; - } else { - return 0; - } -} - -int rm_dex(const char *path, const char *instruction_set) -{ - char dex_path[PKG_PATH_MAX]; - - if (validate_apk_path(path) && validate_system_app_path(path)) { - ALOGE("invalid apk path '%s' (bad prefix)\n", path); - return -1; - } - - if (create_cache_path(dex_path, path, instruction_set)) return -1; - - ALOGV("unlink %s\n", dex_path); - if (unlink(dex_path) < 0) { - if (errno != ENOENT) { - ALOGE("Couldn't unlink %s: %s\n", dex_path, strerror(errno)); - } - return -1; - } else { - return 0; - } -} - -int get_size(const char *pkgname, userid_t userid, const char *apkpath, - const char *libdirpath, const char *fwdlock_apkpath, const char *asecpath, - const char *instruction_set, int64_t *_codesize, int64_t *_datasize, - int64_t *_cachesize, int64_t* _asecsize) -{ - DIR *d; - int dfd; - struct dirent *de; - struct stat s; - char path[PKG_PATH_MAX]; - - int64_t codesize = 0; - int64_t datasize = 0; - int64_t cachesize = 0; - int64_t asecsize = 0; - - /* count the source apk as code -- but only if it's not - * on the /system partition and its not on the sdcard. - */ - if (validate_system_app_path(apkpath) && - strncmp(apkpath, android_asec_dir.path, android_asec_dir.len) != 0) { - if (stat(apkpath, &s) == 0) { - codesize += stat_size(&s); - } - } - /* count the forward locked apk as code if it is given - */ - if (fwdlock_apkpath != NULL && fwdlock_apkpath[0] != '!') { - if (stat(fwdlock_apkpath, &s) == 0) { - codesize += stat_size(&s); - } - } - /* count the cached dexfile as code */ - if (!create_cache_path(path, apkpath, instruction_set)) { - if (stat(path, &s) == 0) { - codesize += stat_size(&s); - } - } - - /* add in size of any libraries */ - if (libdirpath != NULL && libdirpath[0] != '!') { - d = opendir(libdirpath); - if (d != NULL) { - dfd = dirfd(d); - codesize += calculate_dir_size(dfd); - closedir(d); - } - } - - /* compute asec size if it is given - */ - if (asecpath != NULL && asecpath[0] != '!') { - if (stat(asecpath, &s) == 0) { - asecsize += stat_size(&s); - } - } - - if (create_pkg_path(path, pkgname, PKG_DIR_POSTFIX, userid)) { - goto done; - } - - d = opendir(path); - if (d == NULL) { - goto done; - } - dfd = dirfd(d); - - /* most stuff in the pkgdir is data, except for the "cache" - * directory and below, which is cache, and the "lib" directory - * and below, which is code... - */ - while ((de = readdir(d))) { - const char *name = de->d_name; - - if (de->d_type == DT_DIR) { - int subfd; - int64_t statsize = 0; - int64_t dirsize = 0; - /* always skip "." and ".." */ - if (name[0] == '.') { - if (name[1] == 0) continue; - if ((name[1] == '.') && (name[2] == 0)) continue; - } - if (fstatat(dfd, name, &s, AT_SYMLINK_NOFOLLOW) == 0) { - statsize = stat_size(&s); - } - subfd = openat(dfd, name, O_RDONLY | O_DIRECTORY); - if (subfd >= 0) { - dirsize = calculate_dir_size(subfd); - } - if(!strcmp(name,"lib")) { - codesize += dirsize + statsize; - } else if(!strcmp(name,"cache")) { - cachesize += dirsize + statsize; - } else { - datasize += dirsize + statsize; - } - } else if (de->d_type == DT_LNK && !strcmp(name,"lib")) { - // This is the symbolic link to the application's library - // code. We'll count this as code instead of data, since - // it is not something that the app creates. - if (fstatat(dfd, name, &s, AT_SYMLINK_NOFOLLOW) == 0) { - codesize += stat_size(&s); - } - } else { - if (fstatat(dfd, name, &s, AT_SYMLINK_NOFOLLOW) == 0) { - datasize += stat_size(&s); - } - } - } - closedir(d); -done: - *_codesize = codesize; - *_datasize = datasize; - *_cachesize = cachesize; - *_asecsize = asecsize; - return 0; -} - -int create_cache_path(char path[PKG_PATH_MAX], const char *src, const char *instruction_set) -{ - char *tmp; - int srclen; - int dstlen; - - srclen = strlen(src); - - /* demand that we are an absolute path */ - if ((src == 0) || (src[0] != '/') || strstr(src,"..")) { - return -1; - } - - if (srclen > PKG_PATH_MAX) { // XXX: PKG_NAME_MAX? - return -1; - } - - dstlen = srclen + strlen(DALVIK_CACHE_PREFIX) + - strlen(instruction_set) + - strlen(DALVIK_CACHE_POSTFIX) + 2; - - if (dstlen > PKG_PATH_MAX) { - return -1; - } - - sprintf(path,"%s%s/%s%s", - DALVIK_CACHE_PREFIX, - instruction_set, - src + 1, /* skip the leading / */ - DALVIK_CACHE_POSTFIX); - - for(tmp = path + strlen(DALVIK_CACHE_PREFIX) + strlen(instruction_set) + 1; *tmp; tmp++) { - if (*tmp == '/') { - *tmp = '@'; - } - } - - return 0; -} - -static int split_count(const char *str) -{ - char *ctx; - int count = 0; - char buf[PROPERTY_VALUE_MAX]; - - strncpy(buf, str, sizeof(buf)); - char *pBuf = buf; - - while(strtok_r(pBuf, " ", &ctx) != NULL) { - count++; - pBuf = NULL; - } - - return count; -} - -static int split(char *buf, const char **argv) -{ - char *ctx; - int count = 0; - char *tok; - char *pBuf = buf; - - while((tok = strtok_r(pBuf, " ", &ctx)) != NULL) { - argv[count++] = tok; - pBuf = NULL; - } - - return count; -} - -static void run_patchoat(int input_fd, int oat_fd, const char* input_file_name, - const char* output_file_name, const char *pkgname __unused, const char *instruction_set) -{ - static const int MAX_INT_LEN = 12; // '-'+10dig+'\0' -OR- 0x+8dig - static const unsigned int MAX_INSTRUCTION_SET_LEN = 7; - - static const char* PATCHOAT_BIN = "/system/bin/patchoat"; - if (strlen(instruction_set) >= MAX_INSTRUCTION_SET_LEN) { - ALOGE("Instruction set %s longer than max length of %d", - instruction_set, MAX_INSTRUCTION_SET_LEN); - return; - } - - /* input_file_name/input_fd should be the .odex/.oat file that is precompiled. I think*/ - char instruction_set_arg[strlen("--instruction-set=") + MAX_INSTRUCTION_SET_LEN]; - char output_oat_fd_arg[strlen("--output-oat-fd=") + MAX_INT_LEN]; - char input_oat_fd_arg[strlen("--input-oat-fd=") + MAX_INT_LEN]; - const char* patched_image_location_arg = "--patched-image-location=/system/framework/boot.art"; - // The caller has already gotten all the locks we need. - const char* no_lock_arg = "--no-lock-output"; - sprintf(instruction_set_arg, "--instruction-set=%s", instruction_set); - sprintf(output_oat_fd_arg, "--output-oat-fd=%d", oat_fd); - sprintf(input_oat_fd_arg, "--input-oat-fd=%d", input_fd); - ALOGV("Running %s isa=%s in-fd=%d (%s) out-fd=%d (%s)\n", - PATCHOAT_BIN, instruction_set, input_fd, input_file_name, oat_fd, output_file_name); - - /* patchoat, patched-image-location, no-lock, isa, input-fd, output-fd */ - char* argv[7]; - argv[0] = (char*) PATCHOAT_BIN; - argv[1] = (char*) patched_image_location_arg; - argv[2] = (char*) no_lock_arg; - argv[3] = instruction_set_arg; - argv[4] = output_oat_fd_arg; - argv[5] = input_oat_fd_arg; - argv[6] = NULL; - - execv(PATCHOAT_BIN, (char* const *)argv); - ALOGE("execv(%s) failed: %s\n", PATCHOAT_BIN, strerror(errno)); -} - -static void run_dex2oat(int zip_fd, int oat_fd, const char* input_file_name, - const char* output_file_name, int swap_fd, const char *pkgname, const char *instruction_set, - bool vm_safe_mode, bool debuggable) -{ - static const unsigned int MAX_INSTRUCTION_SET_LEN = 7; - - if (strlen(instruction_set) >= MAX_INSTRUCTION_SET_LEN) { - ALOGE("Instruction set %s longer than max length of %d", - instruction_set, MAX_INSTRUCTION_SET_LEN); - return; - } - - char prop_buf[PROPERTY_VALUE_MAX]; - bool profiler = (property_get("dalvik.vm.profiler", prop_buf, "0") > 0) && (prop_buf[0] == '1'); - - char dex2oat_Xms_flag[PROPERTY_VALUE_MAX]; - bool have_dex2oat_Xms_flag = property_get("dalvik.vm.dex2oat-Xms", dex2oat_Xms_flag, NULL) > 0; - - char dex2oat_Xmx_flag[PROPERTY_VALUE_MAX]; - bool have_dex2oat_Xmx_flag = property_get("dalvik.vm.dex2oat-Xmx", dex2oat_Xmx_flag, NULL) > 0; - - char dex2oat_compiler_filter_flag[PROPERTY_VALUE_MAX]; - bool have_dex2oat_compiler_filter_flag = property_get("dalvik.vm.dex2oat-filter", - dex2oat_compiler_filter_flag, NULL) > 0; - - char dex2oat_threads_buf[PROPERTY_VALUE_MAX]; - bool have_dex2oat_threads_flag = property_get("dalvik.vm.dex2oat-threads", dex2oat_threads_buf, - NULL) > 0; - char dex2oat_threads_arg[PROPERTY_VALUE_MAX + 2]; - if (have_dex2oat_threads_flag) { - sprintf(dex2oat_threads_arg, "-j%s", dex2oat_threads_buf); - } - - char dex2oat_isa_features_key[PROPERTY_KEY_MAX]; - sprintf(dex2oat_isa_features_key, "dalvik.vm.isa.%s.features", instruction_set); - char dex2oat_isa_features[PROPERTY_VALUE_MAX]; - bool have_dex2oat_isa_features = property_get(dex2oat_isa_features_key, - dex2oat_isa_features, NULL) > 0; - - char dex2oat_isa_variant_key[PROPERTY_KEY_MAX]; - sprintf(dex2oat_isa_variant_key, "dalvik.vm.isa.%s.variant", instruction_set); - char dex2oat_isa_variant[PROPERTY_VALUE_MAX]; - bool have_dex2oat_isa_variant = property_get(dex2oat_isa_variant_key, - dex2oat_isa_variant, NULL) > 0; - - const char *dex2oat_norelocation = "-Xnorelocate"; - bool have_dex2oat_relocation_skip_flag = false; - - char dex2oat_flags[PROPERTY_VALUE_MAX]; - int dex2oat_flags_count = property_get("dalvik.vm.dex2oat-flags", - dex2oat_flags, NULL) <= 0 ? 0 : split_count(dex2oat_flags); - ALOGV("dalvik.vm.dex2oat-flags=%s\n", dex2oat_flags); - - // If we booting without the real /data, don't spend time compiling. - char vold_decrypt[PROPERTY_VALUE_MAX]; - bool have_vold_decrypt = property_get("vold.decrypt", vold_decrypt, "") > 0; - bool skip_compilation = (have_vold_decrypt && - (strcmp(vold_decrypt, "trigger_restart_min_framework") == 0 || - (strcmp(vold_decrypt, "1") == 0))); - - char use_jit_property[PROPERTY_VALUE_MAX]; - bool have_jit_property = property_get("debug.usejit", use_jit_property, NULL) > 0; - bool use_jit = have_jit_property && strcmp(use_jit_property, "true") == 0; - - static const char* DEX2OAT_BIN = "/system/bin/dex2oat"; - - static const char* RUNTIME_ARG = "--runtime-arg"; - - static const int MAX_INT_LEN = 12; // '-'+10dig+'\0' -OR- 0x+8dig - - char zip_fd_arg[strlen("--zip-fd=") + MAX_INT_LEN]; - char zip_location_arg[strlen("--zip-location=") + PKG_PATH_MAX]; - char oat_fd_arg[strlen("--oat-fd=") + MAX_INT_LEN]; - char oat_location_arg[strlen("--oat-location=") + PKG_PATH_MAX]; - char instruction_set_arg[strlen("--instruction-set=") + MAX_INSTRUCTION_SET_LEN]; - char instruction_set_variant_arg[strlen("--instruction-set-variant=") + PROPERTY_VALUE_MAX]; - char instruction_set_features_arg[strlen("--instruction-set-features=") + PROPERTY_VALUE_MAX]; - char profile_file_arg[strlen("--profile-file=") + PKG_PATH_MAX]; - char top_k_profile_threshold_arg[strlen("--top-k-profile-threshold=") + PROPERTY_VALUE_MAX]; - char dex2oat_Xms_arg[strlen("-Xms") + PROPERTY_VALUE_MAX]; - char dex2oat_Xmx_arg[strlen("-Xmx") + PROPERTY_VALUE_MAX]; - char dex2oat_compiler_filter_arg[strlen("--compiler-filter=") + PROPERTY_VALUE_MAX]; - bool have_dex2oat_swap_fd = false; - char dex2oat_swap_fd[strlen("--swap-fd=") + MAX_INT_LEN]; - - sprintf(zip_fd_arg, "--zip-fd=%d", zip_fd); - sprintf(zip_location_arg, "--zip-location=%s", input_file_name); - sprintf(oat_fd_arg, "--oat-fd=%d", oat_fd); - sprintf(oat_location_arg, "--oat-location=%s", output_file_name); - sprintf(instruction_set_arg, "--instruction-set=%s", instruction_set); - sprintf(instruction_set_variant_arg, "--instruction-set-variant=%s", dex2oat_isa_variant); - sprintf(instruction_set_features_arg, "--instruction-set-features=%s", dex2oat_isa_features); - if (swap_fd >= 0) { - have_dex2oat_swap_fd = true; - sprintf(dex2oat_swap_fd, "--swap-fd=%d", swap_fd); - } - - bool have_profile_file = false; - bool have_top_k_profile_threshold = false; - if (profiler && (strcmp(pkgname, "*") != 0)) { - char profile_file[PKG_PATH_MAX]; - snprintf(profile_file, sizeof(profile_file), "%s/%s", - DALVIK_CACHE_PREFIX "profiles", pkgname); - struct stat st; - if ((stat(profile_file, &st) == 0) && (st.st_size > 0)) { - sprintf(profile_file_arg, "--profile-file=%s", profile_file); - have_profile_file = true; - if (property_get("dalvik.vm.profile.top-k-thr", prop_buf, NULL) > 0) { - snprintf(top_k_profile_threshold_arg, sizeof(top_k_profile_threshold_arg), - "--top-k-profile-threshold=%s", prop_buf); - have_top_k_profile_threshold = true; - } - } - } - - if (have_dex2oat_Xms_flag) { - sprintf(dex2oat_Xms_arg, "-Xms%s", dex2oat_Xms_flag); - } - if (have_dex2oat_Xmx_flag) { - sprintf(dex2oat_Xmx_arg, "-Xmx%s", dex2oat_Xmx_flag); - } - if (skip_compilation) { - strcpy(dex2oat_compiler_filter_arg, "--compiler-filter=verify-none"); - have_dex2oat_compiler_filter_flag = true; - have_dex2oat_relocation_skip_flag = true; - } else if (vm_safe_mode) { - strcpy(dex2oat_compiler_filter_arg, "--compiler-filter=interpret-only"); - have_dex2oat_compiler_filter_flag = true; - } else if (use_jit) { - strcpy(dex2oat_compiler_filter_arg, "--compiler-filter=verify-at-runtime"); - have_dex2oat_compiler_filter_flag = true; - } else if (have_dex2oat_compiler_filter_flag) { - sprintf(dex2oat_compiler_filter_arg, "--compiler-filter=%s", dex2oat_compiler_filter_flag); - } - - // Check whether all apps should be compiled debuggable. - if (!debuggable) { - debuggable = - (property_get("dalvik.vm.always_debuggable", prop_buf, "0") > 0) && - (prop_buf[0] == '1'); - } - - ALOGV("Running %s in=%s out=%s\n", DEX2OAT_BIN, input_file_name, output_file_name); - - const char* argv[7 // program name, mandatory arguments and the final NULL - + (have_dex2oat_isa_variant ? 1 : 0) - + (have_dex2oat_isa_features ? 1 : 0) - + (have_profile_file ? 1 : 0) - + (have_top_k_profile_threshold ? 1 : 0) - + (have_dex2oat_Xms_flag ? 2 : 0) - + (have_dex2oat_Xmx_flag ? 2 : 0) - + (have_dex2oat_compiler_filter_flag ? 1 : 0) - + (have_dex2oat_threads_flag ? 1 : 0) - + (have_dex2oat_swap_fd ? 1 : 0) - + (have_dex2oat_relocation_skip_flag ? 2 : 0) - + (debuggable ? 1 : 0) - + dex2oat_flags_count]; - int i = 0; - argv[i++] = DEX2OAT_BIN; - argv[i++] = zip_fd_arg; - argv[i++] = zip_location_arg; - argv[i++] = oat_fd_arg; - argv[i++] = oat_location_arg; - argv[i++] = instruction_set_arg; - if (have_dex2oat_isa_variant) { - argv[i++] = instruction_set_variant_arg; - } - if (have_dex2oat_isa_features) { - argv[i++] = instruction_set_features_arg; - } - if (have_profile_file) { - argv[i++] = profile_file_arg; - } - if (have_top_k_profile_threshold) { - argv[i++] = top_k_profile_threshold_arg; - } - if (have_dex2oat_Xms_flag) { - argv[i++] = RUNTIME_ARG; - argv[i++] = dex2oat_Xms_arg; - } - if (have_dex2oat_Xmx_flag) { - argv[i++] = RUNTIME_ARG; - argv[i++] = dex2oat_Xmx_arg; - } - if (have_dex2oat_compiler_filter_flag) { - argv[i++] = dex2oat_compiler_filter_arg; - } - if (have_dex2oat_threads_flag) { - argv[i++] = dex2oat_threads_arg; - } - if (have_dex2oat_swap_fd) { - argv[i++] = dex2oat_swap_fd; - } - if (debuggable) { - argv[i++] = "--debuggable"; - } - if (dex2oat_flags_count) { - i += split(dex2oat_flags, argv + i); - } - if (have_dex2oat_relocation_skip_flag) { - argv[i++] = RUNTIME_ARG; - argv[i++] = dex2oat_norelocation; - } - // Do not add after dex2oat_flags, they should override others for debugging. - argv[i] = NULL; - - execv(DEX2OAT_BIN, (char * const *)argv); - ALOGE("execv(%s) failed: %s\n", DEX2OAT_BIN, strerror(errno)); -} - -static int wait_child(pid_t pid) -{ - int status; - pid_t got_pid; - - while (1) { - got_pid = waitpid(pid, &status, 0); - if (got_pid == -1 && errno == EINTR) { - printf("waitpid interrupted, retrying\n"); - } else { - break; - } - } - if (got_pid != pid) { - ALOGW("waitpid failed: wanted %d, got %d: %s\n", - (int) pid, (int) got_pid, strerror(errno)); - return 1; - } - - if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { - return 0; - } else { - return status; /* always nonzero */ - } -} - -/* - * Whether dexopt should use a swap file when compiling an APK. If kAlwaysProvideSwapFile, do this - * on all devices (dex2oat will make a more informed decision itself, anyways). Otherwise, only do - * this on a low-mem device. - */ -static bool kAlwaysProvideSwapFile = true; - -static bool ShouldUseSwapFileForDexopt() { - if (kAlwaysProvideSwapFile) { - return true; - } - - char low_mem_buf[PROPERTY_VALUE_MAX]; - property_get("ro.config.low_ram", low_mem_buf, ""); - return (strcmp(low_mem_buf, "true") == 0); -} - -/* - * Computes the odex file for the given apk_path and instruction_set. - * /system/framework/whatever.jar -> /system/framework/oat//whatever.odex - * - * Returns false if it failed to determine the odex file path. - */ -static bool calculate_odex_file_path(char path[PKG_PATH_MAX], - const char *apk_path, - const char *instruction_set) -{ - if (strlen(apk_path) + strlen("oat/") + strlen(instruction_set) - + strlen("/") + strlen("odex") + 1 > PKG_PATH_MAX) { - ALOGE("apk_path '%s' may be too long to form odex file path.\n", apk_path); - return false; - } - - strcpy(path, apk_path); - char *end = strrchr(path, '/'); - if (end == NULL) { - ALOGE("apk_path '%s' has no '/'s in it?!\n", apk_path); - return false; - } - const char *apk_end = apk_path + (end - path); // strrchr(apk_path, '/'); - - strcpy(end + 1, "oat/"); // path = /system/framework/oat/\0 - strcat(path, instruction_set); // path = /system/framework/oat/\0 - strcat(path, apk_end); // path = /system/framework/oat//whatever.jar\0 - end = strrchr(path, '.'); - if (end == NULL) { - ALOGE("apk_path '%s' has no extension.\n", apk_path); - return false; - } - strcpy(end + 1, "odex"); - return true; -} - -int dexopt(const char *apk_path, uid_t uid, bool is_public, - const char *pkgname, const char *instruction_set, - bool vm_safe_mode, bool is_patchoat, bool debuggable, const char* oat_dir) -{ - struct utimbuf ut; - struct stat input_stat; - char out_path[PKG_PATH_MAX]; - char swap_file_name[PKG_PATH_MAX]; - const char *input_file; - char in_odex_path[PKG_PATH_MAX]; - int res, input_fd=-1, out_fd=-1, swap_fd=-1; - - // Early best-effort check whether we can fit the the path into our buffers. - // Note: the cache path will require an additional 5 bytes for ".swap", but we'll try to run - // without a swap file, if necessary. - if (strlen(apk_path) >= (PKG_PATH_MAX - 8)) { - ALOGE("apk_path too long '%s'\n", apk_path); - return -1; - } - - if (oat_dir != NULL && oat_dir[0] != '!') { - if (validate_apk_path(oat_dir)) { - ALOGE("invalid oat_dir '%s'\n", oat_dir); - return -1; - } - if (calculate_oat_file_path(out_path, oat_dir, apk_path, instruction_set)) { - return -1; - } - } else { - if (create_cache_path(out_path, apk_path, instruction_set)) { - return -1; - } - } - - if (is_patchoat) { - if (!calculate_odex_file_path(in_odex_path, apk_path, instruction_set)) { - return -1; - } - input_file = in_odex_path; - } else { - input_file = apk_path; - } - - memset(&input_stat, 0, sizeof(input_stat)); - stat(input_file, &input_stat); - - input_fd = open(input_file, O_RDONLY, 0); - if (input_fd < 0) { - ALOGE("installd cannot open '%s' for input during dexopt\n", input_file); - return -1; - } - - unlink(out_path); - out_fd = open(out_path, O_RDWR | O_CREAT | O_EXCL, 0644); - if (out_fd < 0) { - ALOGE("installd cannot open '%s' for output during dexopt\n", out_path); - goto fail; - } - if (fchmod(out_fd, - S_IRUSR|S_IWUSR|S_IRGRP | - (is_public ? S_IROTH : 0)) < 0) { - ALOGE("installd cannot chmod '%s' during dexopt\n", out_path); - goto fail; - } - if (fchown(out_fd, AID_SYSTEM, uid) < 0) { - ALOGE("installd cannot chown '%s' during dexopt\n", out_path); - goto fail; - } - - // Create profile file if there is a package name present. - if (strcmp(pkgname, "*") != 0) { - create_profile_file(pkgname, uid); - } - - // Create a swap file if necessary. - if (!is_patchoat && ShouldUseSwapFileForDexopt()) { - // Make sure there really is enough space. - size_t out_len = strlen(out_path); - if (out_len + strlen(".swap") + 1 <= PKG_PATH_MAX) { - strcpy(swap_file_name, out_path); - strcpy(swap_file_name + strlen(out_path), ".swap"); - unlink(swap_file_name); - swap_fd = open(swap_file_name, O_RDWR | O_CREAT | O_EXCL, 0600); - if (swap_fd < 0) { - // Could not create swap file. Optimistically go on and hope that we can compile - // without it. - ALOGE("installd could not create '%s' for swap during dexopt\n", swap_file_name); - } else { - // Immediately unlink. We don't really want to hit flash. - unlink(swap_file_name); - } - } else { - // Swap file path is too long. Try to run without. - ALOGE("installd could not create swap file for path %s during dexopt\n", out_path); - } - } - - ALOGV("DexInv: --- BEGIN '%s' ---\n", input_file); - - pid_t pid; - pid = fork(); - if (pid == 0) { - /* child -- drop privileges before continuing */ - if (setgid(uid) != 0) { - ALOGE("setgid(%d) failed in installd during dexopt\n", uid); - exit(64); - } - if (setuid(uid) != 0) { - ALOGE("setuid(%d) failed in installd during dexopt\n", uid); - exit(65); - } - // drop capabilities - struct __user_cap_header_struct capheader; - struct __user_cap_data_struct capdata[2]; - memset(&capheader, 0, sizeof(capheader)); - memset(&capdata, 0, sizeof(capdata)); - capheader.version = _LINUX_CAPABILITY_VERSION_3; - if (capset(&capheader, &capdata[0]) < 0) { - ALOGE("capset failed: %s\n", strerror(errno)); - exit(66); - } - if (set_sched_policy(0, SP_BACKGROUND) < 0) { - ALOGE("set_sched_policy failed: %s\n", strerror(errno)); - exit(70); - } - if (setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND) < 0) { - ALOGE("setpriority failed: %s\n", strerror(errno)); - exit(71); - } - if (flock(out_fd, LOCK_EX | LOCK_NB) != 0) { - ALOGE("flock(%s) failed: %s\n", out_path, strerror(errno)); - exit(67); - } - - if (is_patchoat) { - run_patchoat(input_fd, out_fd, input_file, out_path, pkgname, instruction_set); - } else { - const char *input_file_name = strrchr(input_file, '/'); - if (input_file_name == NULL) { - input_file_name = input_file; - } else { - input_file_name++; - } - run_dex2oat(input_fd, out_fd, input_file_name, out_path, swap_fd, pkgname, - instruction_set, vm_safe_mode, debuggable); - } - exit(68); /* only get here on exec failure */ - } else { - res = wait_child(pid); - if (res == 0) { - ALOGV("DexInv: --- END '%s' (success) ---\n", input_file); - } else { - ALOGE("DexInv: --- END '%s' --- status=0x%04x, process failed\n", input_file, res); - goto fail; - } - } - - ut.actime = input_stat.st_atime; - ut.modtime = input_stat.st_mtime; - utime(out_path, &ut); - - close(out_fd); - close(input_fd); - if (swap_fd != -1) { - close(swap_fd); - } - return 0; - -fail: - if (out_fd >= 0) { - close(out_fd); - unlink(out_path); - } - if (input_fd >= 0) { - close(input_fd); - } - return -1; -} - -int mark_boot_complete(const char* instruction_set) -{ - char boot_marker_path[PKG_PATH_MAX]; - sprintf(boot_marker_path,"%s%s/.booting", DALVIK_CACHE_PREFIX, instruction_set); - - ALOGV("mark_boot_complete : %s", boot_marker_path); - if (unlink(boot_marker_path) != 0) { - ALOGE("Unable to unlink boot marker at %s, error=%s", boot_marker_path, - strerror(errno)); - return -1; - } - - return 0; -} - -void mkinnerdirs(char* path, int basepos, mode_t mode, int uid, int gid, - struct stat* statbuf) -{ - while (path[basepos] != 0) { - if (path[basepos] == '/') { - path[basepos] = 0; - if (lstat(path, statbuf) < 0) { - ALOGV("Making directory: %s\n", path); - if (mkdir(path, mode) == 0) { - chown(path, uid, gid); - } else { - ALOGW("Unable to make directory %s: %s\n", path, strerror(errno)); - } - } - path[basepos] = '/'; - basepos++; - } - basepos++; - } -} - -int movefileordir(char* srcpath, char* dstpath, int dstbasepos, - int dstuid, int dstgid, struct stat* statbuf) -{ - DIR *d; - struct dirent *de; - int res; - - int srcend = strlen(srcpath); - int dstend = strlen(dstpath); - - if (lstat(srcpath, statbuf) < 0) { - ALOGW("Unable to stat %s: %s\n", srcpath, strerror(errno)); - return 1; - } - - if ((statbuf->st_mode&S_IFDIR) == 0) { - mkinnerdirs(dstpath, dstbasepos, S_IRWXU|S_IRWXG|S_IXOTH, - dstuid, dstgid, statbuf); - ALOGV("Renaming %s to %s (uid %d)\n", srcpath, dstpath, dstuid); - if (rename(srcpath, dstpath) >= 0) { - if (chown(dstpath, dstuid, dstgid) < 0) { - ALOGE("cannot chown %s: %s\n", dstpath, strerror(errno)); - unlink(dstpath); - return 1; - } - } else { - ALOGW("Unable to rename %s to %s: %s\n", - srcpath, dstpath, strerror(errno)); - return 1; - } - return 0; - } - - d = opendir(srcpath); - if (d == NULL) { - ALOGW("Unable to opendir %s: %s\n", srcpath, strerror(errno)); - return 1; - } - - res = 0; - - while ((de = readdir(d))) { - const char *name = de->d_name; - /* always skip "." and ".." */ - if (name[0] == '.') { - if (name[1] == 0) continue; - if ((name[1] == '.') && (name[2] == 0)) continue; - } - - if ((srcend+strlen(name)) >= (PKG_PATH_MAX-2)) { - ALOGW("Source path too long; skipping: %s/%s\n", srcpath, name); - continue; - } - - if ((dstend+strlen(name)) >= (PKG_PATH_MAX-2)) { - ALOGW("Destination path too long; skipping: %s/%s\n", dstpath, name); - continue; - } - - srcpath[srcend] = dstpath[dstend] = '/'; - strcpy(srcpath+srcend+1, name); - strcpy(dstpath+dstend+1, name); - - if (movefileordir(srcpath, dstpath, dstbasepos, dstuid, dstgid, statbuf) != 0) { - res = 1; - } - - // Note: we will be leaving empty directories behind in srcpath, - // but that is okay, the package manager will be erasing all of the - // data associated with .apks that disappear. - - srcpath[srcend] = dstpath[dstend] = 0; - } - - closedir(d); - return res; -} - -int movefiles() -{ - DIR *d; - int dfd, subfd; - struct dirent *de; - struct stat s; - char buf[PKG_PATH_MAX+1]; - int bufp, bufe, bufi, readlen; - - char srcpkg[PKG_NAME_MAX]; - char dstpkg[PKG_NAME_MAX]; - char srcpath[PKG_PATH_MAX]; - char dstpath[PKG_PATH_MAX]; - int dstuid=-1, dstgid=-1; - int hasspace; - - d = opendir(UPDATE_COMMANDS_DIR_PREFIX); - if (d == NULL) { - goto done; - } - dfd = dirfd(d); - - /* Iterate through all files in the directory, executing the - * file movements requested there-in. - */ - while ((de = readdir(d))) { - const char *name = de->d_name; - - if (de->d_type == DT_DIR) { - continue; - } else { - subfd = openat(dfd, name, O_RDONLY); - if (subfd < 0) { - ALOGW("Unable to open update commands at %s%s\n", - UPDATE_COMMANDS_DIR_PREFIX, name); - continue; - } - - bufp = 0; - bufe = 0; - buf[PKG_PATH_MAX] = 0; - srcpkg[0] = dstpkg[0] = 0; - while (1) { - bufi = bufp; - while (bufi < bufe && buf[bufi] != '\n') { - bufi++; - } - if (bufi < bufe) { - buf[bufi] = 0; - ALOGV("Processing line: %s\n", buf+bufp); - hasspace = 0; - while (bufp < bufi && isspace(buf[bufp])) { - hasspace = 1; - bufp++; - } - if (buf[bufp] == '#' || bufp == bufi) { - // skip comments and empty lines. - } else if (hasspace) { - if (dstpkg[0] == 0) { - ALOGW("Path before package line in %s%s: %s\n", - UPDATE_COMMANDS_DIR_PREFIX, name, buf+bufp); - } else if (srcpkg[0] == 0) { - // Skip -- source package no longer exists. - } else { - ALOGV("Move file: %s (from %s to %s)\n", buf+bufp, srcpkg, dstpkg); - if (!create_move_path(srcpath, srcpkg, buf+bufp, 0) && - !create_move_path(dstpath, dstpkg, buf+bufp, 0)) { - movefileordir(srcpath, dstpath, - strlen(dstpath)-strlen(buf+bufp), - dstuid, dstgid, &s); - } - } - } else { - char* div = strchr(buf+bufp, ':'); - if (div == NULL) { - ALOGW("Bad package spec in %s%s; no ':' sep: %s\n", - UPDATE_COMMANDS_DIR_PREFIX, name, buf+bufp); - } else { - *div = 0; - div++; - if (strlen(buf+bufp) < PKG_NAME_MAX) { - strcpy(dstpkg, buf+bufp); - } else { - srcpkg[0] = dstpkg[0] = 0; - ALOGW("Package name too long in %s%s: %s\n", - UPDATE_COMMANDS_DIR_PREFIX, name, buf+bufp); - } - if (strlen(div) < PKG_NAME_MAX) { - strcpy(srcpkg, div); - } else { - srcpkg[0] = dstpkg[0] = 0; - ALOGW("Package name too long in %s%s: %s\n", - UPDATE_COMMANDS_DIR_PREFIX, name, div); - } - if (srcpkg[0] != 0) { - if (!create_pkg_path(srcpath, srcpkg, PKG_DIR_POSTFIX, 0)) { - if (lstat(srcpath, &s) < 0) { - // Package no longer exists -- skip. - srcpkg[0] = 0; - } - } else { - srcpkg[0] = 0; - ALOGW("Can't create path %s in %s%s\n", - div, UPDATE_COMMANDS_DIR_PREFIX, name); - } - if (srcpkg[0] != 0) { - if (!create_pkg_path(dstpath, dstpkg, PKG_DIR_POSTFIX, 0)) { - if (lstat(dstpath, &s) == 0) { - dstuid = s.st_uid; - dstgid = s.st_gid; - } else { - // Destination package doesn't - // exist... due to original-package, - // this is normal, so don't be - // noisy about it. - srcpkg[0] = 0; - } - } else { - srcpkg[0] = 0; - ALOGW("Can't create path %s in %s%s\n", - div, UPDATE_COMMANDS_DIR_PREFIX, name); - } - } - ALOGV("Transfering from %s to %s: uid=%d\n", - srcpkg, dstpkg, dstuid); - } - } - } - bufp = bufi+1; - } else { - if (bufp == 0) { - if (bufp < bufe) { - ALOGW("Line too long in %s%s, skipping: %s\n", - UPDATE_COMMANDS_DIR_PREFIX, name, buf); - } - } else if (bufp < bufe) { - memcpy(buf, buf+bufp, bufe-bufp); - bufe -= bufp; - bufp = 0; - } - readlen = read(subfd, buf+bufe, PKG_PATH_MAX-bufe); - if (readlen < 0) { - ALOGW("Failure reading update commands in %s%s: %s\n", - UPDATE_COMMANDS_DIR_PREFIX, name, strerror(errno)); - break; - } else if (readlen == 0) { - break; - } - bufe += readlen; - buf[bufe] = 0; - ALOGV("Read buf: %s\n", buf); - } - } - close(subfd); - } - } - closedir(d); -done: - return 0; -} - -int linklib(const char* pkgname, const char* asecLibDir, int userId) -{ - char pkgdir[PKG_PATH_MAX]; - char libsymlink[PKG_PATH_MAX]; - struct stat s, libStat; - int rc = 0; - - if (create_pkg_path(pkgdir, pkgname, PKG_DIR_POSTFIX, userId)) { - ALOGE("cannot create package path\n"); - return -1; - } - if (create_pkg_path(libsymlink, pkgname, PKG_LIB_POSTFIX, userId)) { - ALOGE("cannot create package lib symlink origin path\n"); - return -1; - } - - if (stat(pkgdir, &s) < 0) return -1; - - if (chown(pkgdir, AID_INSTALL, AID_INSTALL) < 0) { - ALOGE("failed to chown '%s': %s\n", pkgdir, strerror(errno)); - return -1; - } - - if (chmod(pkgdir, 0700) < 0) { - ALOGE("linklib() 1: failed to chmod '%s': %s\n", pkgdir, strerror(errno)); - rc = -1; - goto out; - } - - if (lstat(libsymlink, &libStat) < 0) { - if (errno != ENOENT) { - ALOGE("couldn't stat lib dir: %s\n", strerror(errno)); - rc = -1; - goto out; - } - } else { - if (S_ISDIR(libStat.st_mode)) { - if (delete_dir_contents(libsymlink, 1, NULL) < 0) { - rc = -1; - goto out; - } - } else if (S_ISLNK(libStat.st_mode)) { - if (unlink(libsymlink) < 0) { - ALOGE("couldn't unlink lib dir: %s\n", strerror(errno)); - rc = -1; - goto out; - } - } - } - - if (symlink(asecLibDir, libsymlink) < 0) { - ALOGE("couldn't symlink directory '%s' -> '%s': %s\n", libsymlink, asecLibDir, - strerror(errno)); - rc = -errno; - goto out; - } - -out: - if (chmod(pkgdir, s.st_mode) < 0) { - ALOGE("linklib() 2: failed to chmod '%s': %s\n", pkgdir, strerror(errno)); - rc = -errno; - } - - if (chown(pkgdir, s.st_uid, s.st_gid) < 0) { - ALOGE("failed to chown '%s' : %s\n", pkgdir, strerror(errno)); - return -errno; - } - - return rc; -} - -static void run_idmap(const char *target_apk, const char *overlay_apk, int idmap_fd) -{ - static const char *IDMAP_BIN = "/system/bin/idmap"; - static const size_t MAX_INT_LEN = 32; - char idmap_str[MAX_INT_LEN]; - - snprintf(idmap_str, sizeof(idmap_str), "%d", idmap_fd); - - execl(IDMAP_BIN, IDMAP_BIN, "--fd", target_apk, overlay_apk, idmap_str, (char*)NULL); - ALOGE("execl(%s) failed: %s\n", IDMAP_BIN, strerror(errno)); -} - -// Transform string /a/b/c.apk to (prefix)/a@b@c.apk@(suffix) -// eg /a/b/c.apk to /data/resource-cache/a@b@c.apk@idmap -static int flatten_path(const char *prefix, const char *suffix, - const char *overlay_path, char *idmap_path, size_t N) -{ - if (overlay_path == NULL || idmap_path == NULL) { - return -1; - } - const size_t len_overlay_path = strlen(overlay_path); - // will access overlay_path + 1 further below; requires absolute path - if (len_overlay_path < 2 || *overlay_path != '/') { - return -1; - } - const size_t len_idmap_root = strlen(prefix); - const size_t len_suffix = strlen(suffix); - if (SIZE_MAX - len_idmap_root < len_overlay_path || - SIZE_MAX - (len_idmap_root + len_overlay_path) < len_suffix) { - // additions below would cause overflow - return -1; - } - if (N < len_idmap_root + len_overlay_path + len_suffix) { - return -1; - } - memset(idmap_path, 0, N); - snprintf(idmap_path, N, "%s%s%s", prefix, overlay_path + 1, suffix); - char *ch = idmap_path + len_idmap_root; - while (*ch != '\0') { - if (*ch == '/') { - *ch = '@'; - } - ++ch; - } - return 0; -} - -int idmap(const char *target_apk, const char *overlay_apk, uid_t uid) -{ - ALOGV("idmap target_apk=%s overlay_apk=%s uid=%d\n", target_apk, overlay_apk, uid); - - int idmap_fd = -1; - char idmap_path[PATH_MAX]; - - if (flatten_path(IDMAP_PREFIX, IDMAP_SUFFIX, overlay_apk, - idmap_path, sizeof(idmap_path)) == -1) { - ALOGE("idmap cannot generate idmap path for overlay %s\n", overlay_apk); - goto fail; - } - - unlink(idmap_path); - idmap_fd = open(idmap_path, O_RDWR | O_CREAT | O_EXCL, 0644); - if (idmap_fd < 0) { - ALOGE("idmap cannot open '%s' for output: %s\n", idmap_path, strerror(errno)); - goto fail; - } - if (fchown(idmap_fd, AID_SYSTEM, uid) < 0) { - ALOGE("idmap cannot chown '%s'\n", idmap_path); - goto fail; - } - if (fchmod(idmap_fd, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) < 0) { - ALOGE("idmap cannot chmod '%s'\n", idmap_path); - goto fail; - } - - pid_t pid; - pid = fork(); - if (pid == 0) { - /* child -- drop privileges before continuing */ - if (setgid(uid) != 0) { - ALOGE("setgid(%d) failed during idmap\n", uid); - exit(1); - } - if (setuid(uid) != 0) { - ALOGE("setuid(%d) failed during idmap\n", uid); - exit(1); - } - if (flock(idmap_fd, LOCK_EX | LOCK_NB) != 0) { - ALOGE("flock(%s) failed during idmap: %s\n", idmap_path, strerror(errno)); - exit(1); - } - - run_idmap(target_apk, overlay_apk, idmap_fd); - exit(1); /* only if exec call to idmap failed */ - } else { - int status = wait_child(pid); - if (status != 0) { - ALOGE("idmap failed, status=0x%04x\n", status); - goto fail; - } - } - - close(idmap_fd); - return 0; -fail: - if (idmap_fd >= 0) { - close(idmap_fd); - unlink(idmap_path); - } - return -1; -} - -int restorecon_data(const char* pkgName, const char* seinfo, uid_t uid) -{ - struct dirent *entry; - DIR *d; - struct stat s; - char *userdir; - char *primarydir; - char *pkgdir; - int ret = 0; - - // SELINUX_ANDROID_RESTORECON_DATADATA flag is set by libselinux. Not needed here. - unsigned int flags = SELINUX_ANDROID_RESTORECON_RECURSE; - - if (!pkgName || !seinfo) { - ALOGE("Package name or seinfo tag is null when trying to restorecon."); - return -1; - } - - if (asprintf(&primarydir, "%s%s%s", android_data_dir.path, PRIMARY_USER_PREFIX, pkgName) < 0) { - return -1; - } - - // Relabel for primary user. - if (selinux_android_restorecon_pkgdir(primarydir, seinfo, uid, flags) < 0) { - ALOGE("restorecon failed for %s: %s\n", primarydir, strerror(errno)); - ret |= -1; - } - - if (asprintf(&userdir, "%s%s", android_data_dir.path, SECONDARY_USER_PREFIX) < 0) { - free(primarydir); - return -1; - } - - // Relabel package directory for all secondary users. - d = opendir(userdir); - if (d == NULL) { - free(primarydir); - free(userdir); - return -1; - } - - while ((entry = readdir(d))) { - if (entry->d_type != DT_DIR) { - continue; - } - - const char *user = entry->d_name; - // Ignore "." and ".." - if (!strcmp(user, ".") || !strcmp(user, "..")) { - continue; - } - - // user directories start with a number - if (user[0] < '0' || user[0] > '9') { - ALOGE("Expecting numbered directory during restorecon. Instead got '%s'.", user); - continue; - } - - if (asprintf(&pkgdir, "%s%s/%s", userdir, user, pkgName) < 0) { - continue; - } - - if (stat(pkgdir, &s) < 0) { - free(pkgdir); - continue; - } - - if (selinux_android_restorecon_pkgdir(pkgdir, seinfo, s.st_uid, flags) < 0) { - ALOGE("restorecon failed for %s: %s\n", pkgdir, strerror(errno)); - ret |= -1; - } - free(pkgdir); - } - - closedir(d); - free(primarydir); - free(userdir); - return ret; -} - -int create_oat_dir(const char* oat_dir, const char* instruction_set) -{ - char oat_instr_dir[PKG_PATH_MAX]; - - if (validate_apk_path(oat_dir)) { - ALOGE("invalid apk path '%s' (bad prefix)\n", oat_dir); - return -1; - } - if (fs_prepare_dir(oat_dir, S_IRWXU | S_IRWXG | S_IXOTH, AID_SYSTEM, AID_INSTALL)) { - return -1; - } - if (selinux_android_restorecon(oat_dir, 0)) { - ALOGE("cannot restorecon dir '%s': %s\n", oat_dir, strerror(errno)); - return -1; - } - snprintf(oat_instr_dir, PKG_PATH_MAX, "%s/%s", oat_dir, instruction_set); - if (fs_prepare_dir(oat_instr_dir, S_IRWXU | S_IRWXG | S_IXOTH, AID_SYSTEM, AID_INSTALL)) { - return -1; - } - return 0; -} - -int rm_package_dir(const char* apk_path) -{ - if (validate_apk_path(apk_path)) { - ALOGE("invalid apk path '%s' (bad prefix)\n", apk_path); - return -1; - } - return delete_dir_contents(apk_path, 1 /* also_delete_dir */ , NULL /* exclusion_predicate */); -} - -int calculate_oat_file_path(char path[PKG_PATH_MAX], const char *oat_dir, const char *apk_path, - const char *instruction_set) { - char *file_name_start; - char *file_name_end; - - file_name_start = strrchr(apk_path, '/'); - if (file_name_start == NULL) { - ALOGE("apk_path '%s' has no '/'s in it\n", apk_path); - return -1; - } - file_name_end = strrchr(apk_path, '.'); - if (file_name_end < file_name_start) { - ALOGE("apk_path '%s' has no extension\n", apk_path); - return -1; - } - - // Calculate file_name - int file_name_len = file_name_end - file_name_start - 1; - char file_name[file_name_len + 1]; - memcpy(file_name, file_name_start + 1, file_name_len); - file_name[file_name_len] = '\0'; - - // /oat//.odex - snprintf(path, PKG_PATH_MAX, "%s/%s/%s.odex", oat_dir, instruction_set, file_name); - return 0; -} diff --git a/cmds/installd/commands.cpp b/cmds/installd/commands.cpp new file mode 100644 index 0000000..5f72ae4 --- /dev/null +++ b/cmds/installd/commands.cpp @@ -0,0 +1,1763 @@ +/* +** Copyright 2008, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include +#include +#include +#include "installd.h" +#include +#include +#include +#include + +/* Directory records that are used in execution of commands. */ +dir_rec_t android_data_dir; +dir_rec_t android_asec_dir; +dir_rec_t android_app_dir; +dir_rec_t android_app_private_dir; +dir_rec_t android_app_lib_dir; +dir_rec_t android_media_dir; +dir_rec_t android_mnt_expand_dir; +dir_rec_array_t android_system_dirs; + +int install(const char *pkgname, uid_t uid, gid_t gid, const char *seinfo) +{ + char pkgdir[PKG_PATH_MAX]; + char libsymlink[PKG_PATH_MAX]; + char applibdir[PKG_PATH_MAX]; + struct stat libStat; + + if ((uid < AID_SYSTEM) || (gid < AID_SYSTEM)) { + ALOGE("invalid uid/gid: %d %d\n", uid, gid); + return -1; + } + + if (create_pkg_path(pkgdir, pkgname, PKG_DIR_POSTFIX, 0)) { + ALOGE("cannot create package path\n"); + return -1; + } + + if (create_pkg_path(libsymlink, pkgname, PKG_LIB_POSTFIX, 0)) { + ALOGE("cannot create package lib symlink origin path\n"); + return -1; + } + + if (create_pkg_path_in_dir(applibdir, &android_app_lib_dir, pkgname, PKG_DIR_POSTFIX)) { + ALOGE("cannot create package lib symlink dest path\n"); + return -1; + } + + if (mkdir(pkgdir, 0751) < 0) { + ALOGE("cannot create dir '%s': %s\n", pkgdir, strerror(errno)); + return -1; + } + if (chmod(pkgdir, 0751) < 0) { + ALOGE("cannot chmod dir '%s': %s\n", pkgdir, strerror(errno)); + unlink(pkgdir); + return -1; + } + + if (lstat(libsymlink, &libStat) < 0) { + if (errno != ENOENT) { + ALOGE("couldn't stat lib dir: %s\n", strerror(errno)); + return -1; + } + } else { + if (S_ISDIR(libStat.st_mode)) { + if (delete_dir_contents(libsymlink, 1, NULL) < 0) { + ALOGE("couldn't delete lib directory during install for: %s", libsymlink); + return -1; + } + } else if (S_ISLNK(libStat.st_mode)) { + if (unlink(libsymlink) < 0) { + ALOGE("couldn't unlink lib directory during install for: %s", libsymlink); + return -1; + } + } + } + + if (selinux_android_setfilecon(pkgdir, pkgname, seinfo, uid) < 0) { + ALOGE("cannot setfilecon dir '%s': %s\n", pkgdir, strerror(errno)); + unlink(libsymlink); + unlink(pkgdir); + return -errno; + } + + if (symlink(applibdir, libsymlink) < 0) { + ALOGE("couldn't symlink directory '%s' -> '%s': %s\n", libsymlink, applibdir, + strerror(errno)); + unlink(pkgdir); + return -1; + } + + if (chown(pkgdir, uid, gid) < 0) { + ALOGE("cannot chown dir '%s': %s\n", pkgdir, strerror(errno)); + unlink(libsymlink); + unlink(pkgdir); + return -1; + } + + return 0; +} + +int uninstall(const char *pkgname, userid_t userid) +{ + char pkgdir[PKG_PATH_MAX]; + + if (create_pkg_path(pkgdir, pkgname, PKG_DIR_POSTFIX, userid)) + return -1; + + remove_profile_file(pkgname); + + /* delete contents AND directory, no exceptions */ + return delete_dir_contents(pkgdir, 1, NULL); +} + +int renamepkg(const char *oldpkgname, const char *newpkgname) +{ + char oldpkgdir[PKG_PATH_MAX]; + char newpkgdir[PKG_PATH_MAX]; + + if (create_pkg_path(oldpkgdir, oldpkgname, PKG_DIR_POSTFIX, 0)) + return -1; + if (create_pkg_path(newpkgdir, newpkgname, PKG_DIR_POSTFIX, 0)) + return -1; + + if (rename(oldpkgdir, newpkgdir) < 0) { + ALOGE("cannot rename dir '%s' to '%s': %s\n", oldpkgdir, newpkgdir, strerror(errno)); + return -errno; + } + return 0; +} + +int fix_uid(const char *pkgname, uid_t uid, gid_t gid) +{ + char pkgdir[PKG_PATH_MAX]; + struct stat s; + + if ((uid < AID_SYSTEM) || (gid < AID_SYSTEM)) { + ALOGE("invalid uid/gid: %d %d\n", uid, gid); + return -1; + } + + if (create_pkg_path(pkgdir, pkgname, PKG_DIR_POSTFIX, 0)) { + ALOGE("cannot create package path\n"); + return -1; + } + + if (stat(pkgdir, &s) < 0) return -1; + + if (s.st_uid != 0 || s.st_gid != 0) { + ALOGE("fixing uid of non-root pkg: %s %" PRIu32 " %" PRIu32 "\n", pkgdir, s.st_uid, s.st_gid); + return -1; + } + + if (chmod(pkgdir, 0751) < 0) { + ALOGE("cannot chmod dir '%s': %s\n", pkgdir, strerror(errno)); + unlink(pkgdir); + return -errno; + } + if (chown(pkgdir, uid, gid) < 0) { + ALOGE("cannot chown dir '%s': %s\n", pkgdir, strerror(errno)); + unlink(pkgdir); + return -errno; + } + + return 0; +} + +int delete_user_data(const char *pkgname, userid_t userid) +{ + char pkgdir[PKG_PATH_MAX]; + + if (create_pkg_path(pkgdir, pkgname, PKG_DIR_POSTFIX, userid)) + return -1; + + return delete_dir_contents(pkgdir, 0, NULL); +} + +int make_user_data(const char *pkgname, uid_t uid, userid_t userid, const char* seinfo) +{ + char pkgdir[PKG_PATH_MAX]; + char applibdir[PKG_PATH_MAX]; + char libsymlink[PKG_PATH_MAX]; + struct stat libStat; + + // Create the data dir for the package + if (create_pkg_path(pkgdir, pkgname, PKG_DIR_POSTFIX, userid)) { + return -1; + } + if (create_pkg_path(libsymlink, pkgname, PKG_LIB_POSTFIX, userid)) { + ALOGE("cannot create package lib symlink origin path\n"); + return -1; + } + if (create_pkg_path_in_dir(applibdir, &android_app_lib_dir, pkgname, PKG_DIR_POSTFIX)) { + ALOGE("cannot create package lib symlink dest path\n"); + return -1; + } + + if (mkdir(pkgdir, 0751) < 0) { + ALOGE("cannot create dir '%s': %s\n", pkgdir, strerror(errno)); + return -errno; + } + if (chmod(pkgdir, 0751) < 0) { + ALOGE("cannot chmod dir '%s': %s\n", pkgdir, strerror(errno)); + unlink(pkgdir); + return -errno; + } + + if (lstat(libsymlink, &libStat) < 0) { + if (errno != ENOENT) { + ALOGE("couldn't stat lib dir for non-primary: %s\n", strerror(errno)); + unlink(pkgdir); + return -1; + } + } else { + if (S_ISDIR(libStat.st_mode)) { + if (delete_dir_contents(libsymlink, 1, NULL) < 0) { + ALOGE("couldn't delete lib directory during install for non-primary: %s", + libsymlink); + unlink(pkgdir); + return -1; + } + } else if (S_ISLNK(libStat.st_mode)) { + if (unlink(libsymlink) < 0) { + ALOGE("couldn't unlink lib directory during install for non-primary: %s", + libsymlink); + unlink(pkgdir); + return -1; + } + } + } + + if (selinux_android_setfilecon(pkgdir, pkgname, seinfo, uid) < 0) { + ALOGE("cannot setfilecon dir '%s': %s\n", pkgdir, strerror(errno)); + unlink(libsymlink); + unlink(pkgdir); + return -errno; + } + + if (symlink(applibdir, libsymlink) < 0) { + ALOGE("couldn't symlink directory for non-primary '%s' -> '%s': %s\n", libsymlink, + applibdir, strerror(errno)); + unlink(pkgdir); + return -1; + } + + if (chown(pkgdir, uid, uid) < 0) { + ALOGE("cannot chown dir '%s': %s\n", pkgdir, strerror(errno)); + unlink(libsymlink); + unlink(pkgdir); + return -errno; + } + + return 0; +} + +int make_user_config(userid_t userid) +{ + if (ensure_config_user_dirs(userid) == -1) { + return -1; + } + + return 0; +} + +int delete_user(userid_t userid) +{ + int status = 0; + + char data_path[PKG_PATH_MAX]; + if ((create_user_path(data_path, userid) != 0) + || (delete_dir_contents(data_path, 1, NULL) != 0)) { + status = -1; + } + + char media_path[PATH_MAX]; + if ((create_user_media_path(media_path, userid) != 0) + || (delete_dir_contents(media_path, 1, NULL) != 0)) { + status = -1; + } + + char config_path[PATH_MAX]; + if ((create_user_config_path(config_path, userid) != 0) + || (delete_dir_contents(config_path, 1, NULL) != 0)) { + status = -1; + } + + return status; +} + +int delete_cache(const char *pkgname, userid_t userid) +{ + char cachedir[PKG_PATH_MAX]; + + if (create_pkg_path(cachedir, pkgname, CACHE_DIR_POSTFIX, userid)) + return -1; + + /* delete contents, not the directory, no exceptions */ + return delete_dir_contents(cachedir, 0, NULL); +} + +int delete_code_cache(const char *pkgname, userid_t userid) +{ + char codecachedir[PKG_PATH_MAX]; + struct stat s; + + if (create_pkg_path(codecachedir, pkgname, CODE_CACHE_DIR_POSTFIX, userid)) + return -1; + + /* it's okay if code cache is missing */ + if (lstat(codecachedir, &s) == -1 && errno == ENOENT) { + return 0; + } + + /* delete contents, not the directory, no exceptions */ + return delete_dir_contents(codecachedir, 0, NULL); +} + +/* Try to ensure free_size bytes of storage are available. + * Returns 0 on success. + * This is rather simple-minded because doing a full LRU would + * be potentially memory-intensive, and without atime it would + * also require that apps constantly modify file metadata even + * when just reading from the cache, which is pretty awful. + */ +int free_cache(int64_t free_size) +{ + cache_t* cache; + int64_t avail; + DIR *d; + struct dirent *de; + char tmpdir[PATH_MAX]; + char *dirpos; + + avail = data_disk_free(); + if (avail < 0) return -1; + + ALOGI("free_cache(%" PRId64 ") avail %" PRId64 "\n", free_size, avail); + if (avail >= free_size) return 0; + + cache = start_cache_collection(); + + // Collect cache files for primary user. + if (create_user_path(tmpdir, 0) == 0) { + //ALOGI("adding cache files from %s\n", tmpdir); + add_cache_files(cache, tmpdir, "cache"); + } + + // Search for other users and add any cache files from them. + snprintf(tmpdir, sizeof(tmpdir), "%s%s", android_data_dir.path, + SECONDARY_USER_PREFIX); + dirpos = tmpdir + strlen(tmpdir); + d = opendir(tmpdir); + if (d != NULL) { + while ((de = readdir(d))) { + if (de->d_type == DT_DIR) { + const char *name = de->d_name; + /* always skip "." and ".." */ + if (name[0] == '.') { + if (name[1] == 0) continue; + if ((name[1] == '.') && (name[2] == 0)) continue; + } + if ((strlen(name)+(dirpos-tmpdir)) < (sizeof(tmpdir)-1)) { + strcpy(dirpos, name); + //ALOGI("adding cache files from %s\n", tmpdir); + add_cache_files(cache, tmpdir, "cache"); + } else { + ALOGW("Path exceeds limit: %s%s", tmpdir, name); + } + } + } + closedir(d); + } + + // Collect cache files on external storage for all users (if it is mounted as part + // of the internal storage). + strcpy(tmpdir, android_media_dir.path); + dirpos = tmpdir + strlen(tmpdir); + d = opendir(tmpdir); + if (d != NULL) { + while ((de = readdir(d))) { + if (de->d_type == DT_DIR) { + const char *name = de->d_name; + /* skip any dir that doesn't start with a number, so not a user */ + if (name[0] < '0' || name[0] > '9') { + continue; + } + if ((strlen(name)+(dirpos-tmpdir)) < (sizeof(tmpdir)-1)) { + strcpy(dirpos, name); + if (lookup_media_dir(tmpdir, "Android") == 0 + && lookup_media_dir(tmpdir, "data") == 0) { + //ALOGI("adding cache files from %s\n", tmpdir); + add_cache_files(cache, tmpdir, "cache"); + } + } else { + ALOGW("Path exceeds limit: %s%s", tmpdir, name); + } + } + } + closedir(d); + } + + clear_cache_files(cache, free_size); + finish_cache_collection(cache); + + return data_disk_free() >= free_size ? 0 : -1; +} + +int move_dex(const char *src, const char *dst, const char *instruction_set) +{ + char src_dex[PKG_PATH_MAX]; + char dst_dex[PKG_PATH_MAX]; + + if (validate_apk_path(src)) { + ALOGE("invalid apk path '%s' (bad prefix)\n", src); + return -1; + } + if (validate_apk_path(dst)) { + ALOGE("invalid apk path '%s' (bad prefix)\n", dst); + return -1; + } + + if (create_cache_path(src_dex, src, instruction_set)) return -1; + if (create_cache_path(dst_dex, dst, instruction_set)) return -1; + + ALOGV("move %s -> %s\n", src_dex, dst_dex); + if (rename(src_dex, dst_dex) < 0) { + ALOGE("Couldn't move %s: %s\n", src_dex, strerror(errno)); + return -1; + } else { + return 0; + } +} + +int rm_dex(const char *path, const char *instruction_set) +{ + char dex_path[PKG_PATH_MAX]; + + if (validate_apk_path(path) && validate_system_app_path(path)) { + ALOGE("invalid apk path '%s' (bad prefix)\n", path); + return -1; + } + + if (create_cache_path(dex_path, path, instruction_set)) return -1; + + ALOGV("unlink %s\n", dex_path); + if (unlink(dex_path) < 0) { + if (errno != ENOENT) { + ALOGE("Couldn't unlink %s: %s\n", dex_path, strerror(errno)); + } + return -1; + } else { + return 0; + } +} + +int get_size(const char *pkgname, userid_t userid, const char *apkpath, + const char *libdirpath, const char *fwdlock_apkpath, const char *asecpath, + const char *instruction_set, int64_t *_codesize, int64_t *_datasize, + int64_t *_cachesize, int64_t* _asecsize) +{ + DIR *d; + int dfd; + struct dirent *de; + struct stat s; + char path[PKG_PATH_MAX]; + + int64_t codesize = 0; + int64_t datasize = 0; + int64_t cachesize = 0; + int64_t asecsize = 0; + + /* count the source apk as code -- but only if it's not + * on the /system partition and its not on the sdcard. + */ + if (validate_system_app_path(apkpath) && + strncmp(apkpath, android_asec_dir.path, android_asec_dir.len) != 0) { + if (stat(apkpath, &s) == 0) { + codesize += stat_size(&s); + } + } + /* count the forward locked apk as code if it is given + */ + if (fwdlock_apkpath != NULL && fwdlock_apkpath[0] != '!') { + if (stat(fwdlock_apkpath, &s) == 0) { + codesize += stat_size(&s); + } + } + /* count the cached dexfile as code */ + if (!create_cache_path(path, apkpath, instruction_set)) { + if (stat(path, &s) == 0) { + codesize += stat_size(&s); + } + } + + /* add in size of any libraries */ + if (libdirpath != NULL && libdirpath[0] != '!') { + d = opendir(libdirpath); + if (d != NULL) { + dfd = dirfd(d); + codesize += calculate_dir_size(dfd); + closedir(d); + } + } + + /* compute asec size if it is given + */ + if (asecpath != NULL && asecpath[0] != '!') { + if (stat(asecpath, &s) == 0) { + asecsize += stat_size(&s); + } + } + + if (create_pkg_path(path, pkgname, PKG_DIR_POSTFIX, userid)) { + goto done; + } + + d = opendir(path); + if (d == NULL) { + goto done; + } + dfd = dirfd(d); + + /* most stuff in the pkgdir is data, except for the "cache" + * directory and below, which is cache, and the "lib" directory + * and below, which is code... + */ + while ((de = readdir(d))) { + const char *name = de->d_name; + + if (de->d_type == DT_DIR) { + int subfd; + int64_t statsize = 0; + int64_t dirsize = 0; + /* always skip "." and ".." */ + if (name[0] == '.') { + if (name[1] == 0) continue; + if ((name[1] == '.') && (name[2] == 0)) continue; + } + if (fstatat(dfd, name, &s, AT_SYMLINK_NOFOLLOW) == 0) { + statsize = stat_size(&s); + } + subfd = openat(dfd, name, O_RDONLY | O_DIRECTORY); + if (subfd >= 0) { + dirsize = calculate_dir_size(subfd); + } + if(!strcmp(name,"lib")) { + codesize += dirsize + statsize; + } else if(!strcmp(name,"cache")) { + cachesize += dirsize + statsize; + } else { + datasize += dirsize + statsize; + } + } else if (de->d_type == DT_LNK && !strcmp(name,"lib")) { + // This is the symbolic link to the application's library + // code. We'll count this as code instead of data, since + // it is not something that the app creates. + if (fstatat(dfd, name, &s, AT_SYMLINK_NOFOLLOW) == 0) { + codesize += stat_size(&s); + } + } else { + if (fstatat(dfd, name, &s, AT_SYMLINK_NOFOLLOW) == 0) { + datasize += stat_size(&s); + } + } + } + closedir(d); +done: + *_codesize = codesize; + *_datasize = datasize; + *_cachesize = cachesize; + *_asecsize = asecsize; + return 0; +} + +int create_cache_path(char path[PKG_PATH_MAX], const char *src, const char *instruction_set) +{ + char *tmp; + int srclen; + int dstlen; + + srclen = strlen(src); + + /* demand that we are an absolute path */ + if ((src == 0) || (src[0] != '/') || strstr(src,"..")) { + return -1; + } + + if (srclen > PKG_PATH_MAX) { // XXX: PKG_NAME_MAX? + return -1; + } + + dstlen = srclen + strlen(DALVIK_CACHE_PREFIX) + + strlen(instruction_set) + + strlen(DALVIK_CACHE_POSTFIX) + 2; + + if (dstlen > PKG_PATH_MAX) { + return -1; + } + + sprintf(path,"%s%s/%s%s", + DALVIK_CACHE_PREFIX, + instruction_set, + src + 1, /* skip the leading / */ + DALVIK_CACHE_POSTFIX); + + for(tmp = path + strlen(DALVIK_CACHE_PREFIX) + strlen(instruction_set) + 1; *tmp; tmp++) { + if (*tmp == '/') { + *tmp = '@'; + } + } + + return 0; +} + +static int split_count(const char *str) +{ + char *ctx; + int count = 0; + char buf[PROPERTY_VALUE_MAX]; + + strncpy(buf, str, sizeof(buf)); + char *pBuf = buf; + + while(strtok_r(pBuf, " ", &ctx) != NULL) { + count++; + pBuf = NULL; + } + + return count; +} + +static int split(char *buf, const char **argv) +{ + char *ctx; + int count = 0; + char *tok; + char *pBuf = buf; + + while((tok = strtok_r(pBuf, " ", &ctx)) != NULL) { + argv[count++] = tok; + pBuf = NULL; + } + + return count; +} + +static void run_patchoat(int input_fd, int oat_fd, const char* input_file_name, + const char* output_file_name, const char *pkgname __unused, const char *instruction_set) +{ + static const int MAX_INT_LEN = 12; // '-'+10dig+'\0' -OR- 0x+8dig + static const unsigned int MAX_INSTRUCTION_SET_LEN = 7; + + static const char* PATCHOAT_BIN = "/system/bin/patchoat"; + if (strlen(instruction_set) >= MAX_INSTRUCTION_SET_LEN) { + ALOGE("Instruction set %s longer than max length of %d", + instruction_set, MAX_INSTRUCTION_SET_LEN); + return; + } + + /* input_file_name/input_fd should be the .odex/.oat file that is precompiled. I think*/ + char instruction_set_arg[strlen("--instruction-set=") + MAX_INSTRUCTION_SET_LEN]; + char output_oat_fd_arg[strlen("--output-oat-fd=") + MAX_INT_LEN]; + char input_oat_fd_arg[strlen("--input-oat-fd=") + MAX_INT_LEN]; + const char* patched_image_location_arg = "--patched-image-location=/system/framework/boot.art"; + // The caller has already gotten all the locks we need. + const char* no_lock_arg = "--no-lock-output"; + sprintf(instruction_set_arg, "--instruction-set=%s", instruction_set); + sprintf(output_oat_fd_arg, "--output-oat-fd=%d", oat_fd); + sprintf(input_oat_fd_arg, "--input-oat-fd=%d", input_fd); + ALOGV("Running %s isa=%s in-fd=%d (%s) out-fd=%d (%s)\n", + PATCHOAT_BIN, instruction_set, input_fd, input_file_name, oat_fd, output_file_name); + + /* patchoat, patched-image-location, no-lock, isa, input-fd, output-fd */ + char* argv[7]; + argv[0] = (char*) PATCHOAT_BIN; + argv[1] = (char*) patched_image_location_arg; + argv[2] = (char*) no_lock_arg; + argv[3] = instruction_set_arg; + argv[4] = output_oat_fd_arg; + argv[5] = input_oat_fd_arg; + argv[6] = NULL; + + execv(PATCHOAT_BIN, (char* const *)argv); + ALOGE("execv(%s) failed: %s\n", PATCHOAT_BIN, strerror(errno)); +} + +static void run_dex2oat(int zip_fd, int oat_fd, const char* input_file_name, + const char* output_file_name, int swap_fd, const char *pkgname, const char *instruction_set, + bool vm_safe_mode, bool debuggable) +{ + static const unsigned int MAX_INSTRUCTION_SET_LEN = 7; + + if (strlen(instruction_set) >= MAX_INSTRUCTION_SET_LEN) { + ALOGE("Instruction set %s longer than max length of %d", + instruction_set, MAX_INSTRUCTION_SET_LEN); + return; + } + + char prop_buf[PROPERTY_VALUE_MAX]; + bool profiler = (property_get("dalvik.vm.profiler", prop_buf, "0") > 0) && (prop_buf[0] == '1'); + + char dex2oat_Xms_flag[PROPERTY_VALUE_MAX]; + bool have_dex2oat_Xms_flag = property_get("dalvik.vm.dex2oat-Xms", dex2oat_Xms_flag, NULL) > 0; + + char dex2oat_Xmx_flag[PROPERTY_VALUE_MAX]; + bool have_dex2oat_Xmx_flag = property_get("dalvik.vm.dex2oat-Xmx", dex2oat_Xmx_flag, NULL) > 0; + + char dex2oat_compiler_filter_flag[PROPERTY_VALUE_MAX]; + bool have_dex2oat_compiler_filter_flag = property_get("dalvik.vm.dex2oat-filter", + dex2oat_compiler_filter_flag, NULL) > 0; + + char dex2oat_threads_buf[PROPERTY_VALUE_MAX]; + bool have_dex2oat_threads_flag = property_get("dalvik.vm.dex2oat-threads", dex2oat_threads_buf, + NULL) > 0; + char dex2oat_threads_arg[PROPERTY_VALUE_MAX + 2]; + if (have_dex2oat_threads_flag) { + sprintf(dex2oat_threads_arg, "-j%s", dex2oat_threads_buf); + } + + char dex2oat_isa_features_key[PROPERTY_KEY_MAX]; + sprintf(dex2oat_isa_features_key, "dalvik.vm.isa.%s.features", instruction_set); + char dex2oat_isa_features[PROPERTY_VALUE_MAX]; + bool have_dex2oat_isa_features = property_get(dex2oat_isa_features_key, + dex2oat_isa_features, NULL) > 0; + + char dex2oat_isa_variant_key[PROPERTY_KEY_MAX]; + sprintf(dex2oat_isa_variant_key, "dalvik.vm.isa.%s.variant", instruction_set); + char dex2oat_isa_variant[PROPERTY_VALUE_MAX]; + bool have_dex2oat_isa_variant = property_get(dex2oat_isa_variant_key, + dex2oat_isa_variant, NULL) > 0; + + const char *dex2oat_norelocation = "-Xnorelocate"; + bool have_dex2oat_relocation_skip_flag = false; + + char dex2oat_flags[PROPERTY_VALUE_MAX]; + int dex2oat_flags_count = property_get("dalvik.vm.dex2oat-flags", + dex2oat_flags, NULL) <= 0 ? 0 : split_count(dex2oat_flags); + ALOGV("dalvik.vm.dex2oat-flags=%s\n", dex2oat_flags); + + // If we booting without the real /data, don't spend time compiling. + char vold_decrypt[PROPERTY_VALUE_MAX]; + bool have_vold_decrypt = property_get("vold.decrypt", vold_decrypt, "") > 0; + bool skip_compilation = (have_vold_decrypt && + (strcmp(vold_decrypt, "trigger_restart_min_framework") == 0 || + (strcmp(vold_decrypt, "1") == 0))); + + char use_jit_property[PROPERTY_VALUE_MAX]; + bool have_jit_property = property_get("debug.usejit", use_jit_property, NULL) > 0; + bool use_jit = have_jit_property && strcmp(use_jit_property, "true") == 0; + + static const char* DEX2OAT_BIN = "/system/bin/dex2oat"; + + static const char* RUNTIME_ARG = "--runtime-arg"; + + static const int MAX_INT_LEN = 12; // '-'+10dig+'\0' -OR- 0x+8dig + + char zip_fd_arg[strlen("--zip-fd=") + MAX_INT_LEN]; + char zip_location_arg[strlen("--zip-location=") + PKG_PATH_MAX]; + char oat_fd_arg[strlen("--oat-fd=") + MAX_INT_LEN]; + char oat_location_arg[strlen("--oat-location=") + PKG_PATH_MAX]; + char instruction_set_arg[strlen("--instruction-set=") + MAX_INSTRUCTION_SET_LEN]; + char instruction_set_variant_arg[strlen("--instruction-set-variant=") + PROPERTY_VALUE_MAX]; + char instruction_set_features_arg[strlen("--instruction-set-features=") + PROPERTY_VALUE_MAX]; + char profile_file_arg[strlen("--profile-file=") + PKG_PATH_MAX]; + char top_k_profile_threshold_arg[strlen("--top-k-profile-threshold=") + PROPERTY_VALUE_MAX]; + char dex2oat_Xms_arg[strlen("-Xms") + PROPERTY_VALUE_MAX]; + char dex2oat_Xmx_arg[strlen("-Xmx") + PROPERTY_VALUE_MAX]; + char dex2oat_compiler_filter_arg[strlen("--compiler-filter=") + PROPERTY_VALUE_MAX]; + bool have_dex2oat_swap_fd = false; + char dex2oat_swap_fd[strlen("--swap-fd=") + MAX_INT_LEN]; + + sprintf(zip_fd_arg, "--zip-fd=%d", zip_fd); + sprintf(zip_location_arg, "--zip-location=%s", input_file_name); + sprintf(oat_fd_arg, "--oat-fd=%d", oat_fd); + sprintf(oat_location_arg, "--oat-location=%s", output_file_name); + sprintf(instruction_set_arg, "--instruction-set=%s", instruction_set); + sprintf(instruction_set_variant_arg, "--instruction-set-variant=%s", dex2oat_isa_variant); + sprintf(instruction_set_features_arg, "--instruction-set-features=%s", dex2oat_isa_features); + if (swap_fd >= 0) { + have_dex2oat_swap_fd = true; + sprintf(dex2oat_swap_fd, "--swap-fd=%d", swap_fd); + } + + bool have_profile_file = false; + bool have_top_k_profile_threshold = false; + if (profiler && (strcmp(pkgname, "*") != 0)) { + char profile_file[PKG_PATH_MAX]; + snprintf(profile_file, sizeof(profile_file), "%s/%s", + DALVIK_CACHE_PREFIX "profiles", pkgname); + struct stat st; + if ((stat(profile_file, &st) == 0) && (st.st_size > 0)) { + sprintf(profile_file_arg, "--profile-file=%s", profile_file); + have_profile_file = true; + if (property_get("dalvik.vm.profile.top-k-thr", prop_buf, NULL) > 0) { + snprintf(top_k_profile_threshold_arg, sizeof(top_k_profile_threshold_arg), + "--top-k-profile-threshold=%s", prop_buf); + have_top_k_profile_threshold = true; + } + } + } + + if (have_dex2oat_Xms_flag) { + sprintf(dex2oat_Xms_arg, "-Xms%s", dex2oat_Xms_flag); + } + if (have_dex2oat_Xmx_flag) { + sprintf(dex2oat_Xmx_arg, "-Xmx%s", dex2oat_Xmx_flag); + } + if (skip_compilation) { + strcpy(dex2oat_compiler_filter_arg, "--compiler-filter=verify-none"); + have_dex2oat_compiler_filter_flag = true; + have_dex2oat_relocation_skip_flag = true; + } else if (vm_safe_mode) { + strcpy(dex2oat_compiler_filter_arg, "--compiler-filter=interpret-only"); + have_dex2oat_compiler_filter_flag = true; + } else if (use_jit) { + strcpy(dex2oat_compiler_filter_arg, "--compiler-filter=verify-at-runtime"); + have_dex2oat_compiler_filter_flag = true; + } else if (have_dex2oat_compiler_filter_flag) { + sprintf(dex2oat_compiler_filter_arg, "--compiler-filter=%s", dex2oat_compiler_filter_flag); + } + + // Check whether all apps should be compiled debuggable. + if (!debuggable) { + debuggable = + (property_get("dalvik.vm.always_debuggable", prop_buf, "0") > 0) && + (prop_buf[0] == '1'); + } + + ALOGV("Running %s in=%s out=%s\n", DEX2OAT_BIN, input_file_name, output_file_name); + + const char* argv[7 // program name, mandatory arguments and the final NULL + + (have_dex2oat_isa_variant ? 1 : 0) + + (have_dex2oat_isa_features ? 1 : 0) + + (have_profile_file ? 1 : 0) + + (have_top_k_profile_threshold ? 1 : 0) + + (have_dex2oat_Xms_flag ? 2 : 0) + + (have_dex2oat_Xmx_flag ? 2 : 0) + + (have_dex2oat_compiler_filter_flag ? 1 : 0) + + (have_dex2oat_threads_flag ? 1 : 0) + + (have_dex2oat_swap_fd ? 1 : 0) + + (have_dex2oat_relocation_skip_flag ? 2 : 0) + + (debuggable ? 1 : 0) + + dex2oat_flags_count]; + int i = 0; + argv[i++] = DEX2OAT_BIN; + argv[i++] = zip_fd_arg; + argv[i++] = zip_location_arg; + argv[i++] = oat_fd_arg; + argv[i++] = oat_location_arg; + argv[i++] = instruction_set_arg; + if (have_dex2oat_isa_variant) { + argv[i++] = instruction_set_variant_arg; + } + if (have_dex2oat_isa_features) { + argv[i++] = instruction_set_features_arg; + } + if (have_profile_file) { + argv[i++] = profile_file_arg; + } + if (have_top_k_profile_threshold) { + argv[i++] = top_k_profile_threshold_arg; + } + if (have_dex2oat_Xms_flag) { + argv[i++] = RUNTIME_ARG; + argv[i++] = dex2oat_Xms_arg; + } + if (have_dex2oat_Xmx_flag) { + argv[i++] = RUNTIME_ARG; + argv[i++] = dex2oat_Xmx_arg; + } + if (have_dex2oat_compiler_filter_flag) { + argv[i++] = dex2oat_compiler_filter_arg; + } + if (have_dex2oat_threads_flag) { + argv[i++] = dex2oat_threads_arg; + } + if (have_dex2oat_swap_fd) { + argv[i++] = dex2oat_swap_fd; + } + if (debuggable) { + argv[i++] = "--debuggable"; + } + if (dex2oat_flags_count) { + i += split(dex2oat_flags, argv + i); + } + if (have_dex2oat_relocation_skip_flag) { + argv[i++] = RUNTIME_ARG; + argv[i++] = dex2oat_norelocation; + } + // Do not add after dex2oat_flags, they should override others for debugging. + argv[i] = NULL; + + execv(DEX2OAT_BIN, (char * const *)argv); + ALOGE("execv(%s) failed: %s\n", DEX2OAT_BIN, strerror(errno)); +} + +static int wait_child(pid_t pid) +{ + int status; + pid_t got_pid; + + while (1) { + got_pid = waitpid(pid, &status, 0); + if (got_pid == -1 && errno == EINTR) { + printf("waitpid interrupted, retrying\n"); + } else { + break; + } + } + if (got_pid != pid) { + ALOGW("waitpid failed: wanted %d, got %d: %s\n", + (int) pid, (int) got_pid, strerror(errno)); + return 1; + } + + if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { + return 0; + } else { + return status; /* always nonzero */ + } +} + +/* + * Whether dexopt should use a swap file when compiling an APK. If kAlwaysProvideSwapFile, do this + * on all devices (dex2oat will make a more informed decision itself, anyways). Otherwise, only do + * this on a low-mem device. + */ +static bool kAlwaysProvideSwapFile = true; + +static bool ShouldUseSwapFileForDexopt() { + if (kAlwaysProvideSwapFile) { + return true; + } + + char low_mem_buf[PROPERTY_VALUE_MAX]; + property_get("ro.config.low_ram", low_mem_buf, ""); + return (strcmp(low_mem_buf, "true") == 0); +} + +/* + * Computes the odex file for the given apk_path and instruction_set. + * /system/framework/whatever.jar -> /system/framework/oat//whatever.odex + * + * Returns false if it failed to determine the odex file path. + */ +static bool calculate_odex_file_path(char path[PKG_PATH_MAX], + const char *apk_path, + const char *instruction_set) +{ + if (strlen(apk_path) + strlen("oat/") + strlen(instruction_set) + + strlen("/") + strlen("odex") + 1 > PKG_PATH_MAX) { + ALOGE("apk_path '%s' may be too long to form odex file path.\n", apk_path); + return false; + } + + strcpy(path, apk_path); + char *end = strrchr(path, '/'); + if (end == NULL) { + ALOGE("apk_path '%s' has no '/'s in it?!\n", apk_path); + return false; + } + const char *apk_end = apk_path + (end - path); // strrchr(apk_path, '/'); + + strcpy(end + 1, "oat/"); // path = /system/framework/oat/\0 + strcat(path, instruction_set); // path = /system/framework/oat/\0 + strcat(path, apk_end); // path = /system/framework/oat//whatever.jar\0 + end = strrchr(path, '.'); + if (end == NULL) { + ALOGE("apk_path '%s' has no extension.\n", apk_path); + return false; + } + strcpy(end + 1, "odex"); + return true; +} + +int dexopt(const char *apk_path, uid_t uid, bool is_public, + const char *pkgname, const char *instruction_set, + bool vm_safe_mode, bool is_patchoat, bool debuggable, const char* oat_dir) +{ + struct utimbuf ut; + struct stat input_stat; + char out_path[PKG_PATH_MAX]; + char swap_file_name[PKG_PATH_MAX]; + const char *input_file; + char in_odex_path[PKG_PATH_MAX]; + int res, input_fd=-1, out_fd=-1, swap_fd=-1; + + // Early best-effort check whether we can fit the the path into our buffers. + // Note: the cache path will require an additional 5 bytes for ".swap", but we'll try to run + // without a swap file, if necessary. + if (strlen(apk_path) >= (PKG_PATH_MAX - 8)) { + ALOGE("apk_path too long '%s'\n", apk_path); + return -1; + } + + if (oat_dir != NULL && oat_dir[0] != '!') { + if (validate_apk_path(oat_dir)) { + ALOGE("invalid oat_dir '%s'\n", oat_dir); + return -1; + } + if (calculate_oat_file_path(out_path, oat_dir, apk_path, instruction_set)) { + return -1; + } + } else { + if (create_cache_path(out_path, apk_path, instruction_set)) { + return -1; + } + } + + if (is_patchoat) { + if (!calculate_odex_file_path(in_odex_path, apk_path, instruction_set)) { + return -1; + } + input_file = in_odex_path; + } else { + input_file = apk_path; + } + + memset(&input_stat, 0, sizeof(input_stat)); + stat(input_file, &input_stat); + + input_fd = open(input_file, O_RDONLY, 0); + if (input_fd < 0) { + ALOGE("installd cannot open '%s' for input during dexopt\n", input_file); + return -1; + } + + unlink(out_path); + out_fd = open(out_path, O_RDWR | O_CREAT | O_EXCL, 0644); + if (out_fd < 0) { + ALOGE("installd cannot open '%s' for output during dexopt\n", out_path); + goto fail; + } + if (fchmod(out_fd, + S_IRUSR|S_IWUSR|S_IRGRP | + (is_public ? S_IROTH : 0)) < 0) { + ALOGE("installd cannot chmod '%s' during dexopt\n", out_path); + goto fail; + } + if (fchown(out_fd, AID_SYSTEM, uid) < 0) { + ALOGE("installd cannot chown '%s' during dexopt\n", out_path); + goto fail; + } + + // Create profile file if there is a package name present. + if (strcmp(pkgname, "*") != 0) { + create_profile_file(pkgname, uid); + } + + // Create a swap file if necessary. + if (!is_patchoat && ShouldUseSwapFileForDexopt()) { + // Make sure there really is enough space. + size_t out_len = strlen(out_path); + if (out_len + strlen(".swap") + 1 <= PKG_PATH_MAX) { + strcpy(swap_file_name, out_path); + strcpy(swap_file_name + strlen(out_path), ".swap"); + unlink(swap_file_name); + swap_fd = open(swap_file_name, O_RDWR | O_CREAT | O_EXCL, 0600); + if (swap_fd < 0) { + // Could not create swap file. Optimistically go on and hope that we can compile + // without it. + ALOGE("installd could not create '%s' for swap during dexopt\n", swap_file_name); + } else { + // Immediately unlink. We don't really want to hit flash. + unlink(swap_file_name); + } + } else { + // Swap file path is too long. Try to run without. + ALOGE("installd could not create swap file for path %s during dexopt\n", out_path); + } + } + + ALOGV("DexInv: --- BEGIN '%s' ---\n", input_file); + + pid_t pid; + pid = fork(); + if (pid == 0) { + /* child -- drop privileges before continuing */ + if (setgid(uid) != 0) { + ALOGE("setgid(%d) failed in installd during dexopt\n", uid); + exit(64); + } + if (setuid(uid) != 0) { + ALOGE("setuid(%d) failed in installd during dexopt\n", uid); + exit(65); + } + // drop capabilities + struct __user_cap_header_struct capheader; + struct __user_cap_data_struct capdata[2]; + memset(&capheader, 0, sizeof(capheader)); + memset(&capdata, 0, sizeof(capdata)); + capheader.version = _LINUX_CAPABILITY_VERSION_3; + if (capset(&capheader, &capdata[0]) < 0) { + ALOGE("capset failed: %s\n", strerror(errno)); + exit(66); + } + if (set_sched_policy(0, SP_BACKGROUND) < 0) { + ALOGE("set_sched_policy failed: %s\n", strerror(errno)); + exit(70); + } + if (setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND) < 0) { + ALOGE("setpriority failed: %s\n", strerror(errno)); + exit(71); + } + if (flock(out_fd, LOCK_EX | LOCK_NB) != 0) { + ALOGE("flock(%s) failed: %s\n", out_path, strerror(errno)); + exit(67); + } + + if (is_patchoat) { + run_patchoat(input_fd, out_fd, input_file, out_path, pkgname, instruction_set); + } else { + const char *input_file_name = strrchr(input_file, '/'); + if (input_file_name == NULL) { + input_file_name = input_file; + } else { + input_file_name++; + } + run_dex2oat(input_fd, out_fd, input_file_name, out_path, swap_fd, pkgname, + instruction_set, vm_safe_mode, debuggable); + } + exit(68); /* only get here on exec failure */ + } else { + res = wait_child(pid); + if (res == 0) { + ALOGV("DexInv: --- END '%s' (success) ---\n", input_file); + } else { + ALOGE("DexInv: --- END '%s' --- status=0x%04x, process failed\n", input_file, res); + goto fail; + } + } + + ut.actime = input_stat.st_atime; + ut.modtime = input_stat.st_mtime; + utime(out_path, &ut); + + close(out_fd); + close(input_fd); + if (swap_fd != -1) { + close(swap_fd); + } + return 0; + +fail: + if (out_fd >= 0) { + close(out_fd); + unlink(out_path); + } + if (input_fd >= 0) { + close(input_fd); + } + return -1; +} + +int mark_boot_complete(const char* instruction_set) +{ + char boot_marker_path[PKG_PATH_MAX]; + sprintf(boot_marker_path,"%s%s/.booting", DALVIK_CACHE_PREFIX, instruction_set); + + ALOGV("mark_boot_complete : %s", boot_marker_path); + if (unlink(boot_marker_path) != 0) { + ALOGE("Unable to unlink boot marker at %s, error=%s", boot_marker_path, + strerror(errno)); + return -1; + } + + return 0; +} + +void mkinnerdirs(char* path, int basepos, mode_t mode, int uid, int gid, + struct stat* statbuf) +{ + while (path[basepos] != 0) { + if (path[basepos] == '/') { + path[basepos] = 0; + if (lstat(path, statbuf) < 0) { + ALOGV("Making directory: %s\n", path); + if (mkdir(path, mode) == 0) { + chown(path, uid, gid); + } else { + ALOGW("Unable to make directory %s: %s\n", path, strerror(errno)); + } + } + path[basepos] = '/'; + basepos++; + } + basepos++; + } +} + +int movefileordir(char* srcpath, char* dstpath, int dstbasepos, + int dstuid, int dstgid, struct stat* statbuf) +{ + DIR *d; + struct dirent *de; + int res; + + int srcend = strlen(srcpath); + int dstend = strlen(dstpath); + + if (lstat(srcpath, statbuf) < 0) { + ALOGW("Unable to stat %s: %s\n", srcpath, strerror(errno)); + return 1; + } + + if ((statbuf->st_mode&S_IFDIR) == 0) { + mkinnerdirs(dstpath, dstbasepos, S_IRWXU|S_IRWXG|S_IXOTH, + dstuid, dstgid, statbuf); + ALOGV("Renaming %s to %s (uid %d)\n", srcpath, dstpath, dstuid); + if (rename(srcpath, dstpath) >= 0) { + if (chown(dstpath, dstuid, dstgid) < 0) { + ALOGE("cannot chown %s: %s\n", dstpath, strerror(errno)); + unlink(dstpath); + return 1; + } + } else { + ALOGW("Unable to rename %s to %s: %s\n", + srcpath, dstpath, strerror(errno)); + return 1; + } + return 0; + } + + d = opendir(srcpath); + if (d == NULL) { + ALOGW("Unable to opendir %s: %s\n", srcpath, strerror(errno)); + return 1; + } + + res = 0; + + while ((de = readdir(d))) { + const char *name = de->d_name; + /* always skip "." and ".." */ + if (name[0] == '.') { + if (name[1] == 0) continue; + if ((name[1] == '.') && (name[2] == 0)) continue; + } + + if ((srcend+strlen(name)) >= (PKG_PATH_MAX-2)) { + ALOGW("Source path too long; skipping: %s/%s\n", srcpath, name); + continue; + } + + if ((dstend+strlen(name)) >= (PKG_PATH_MAX-2)) { + ALOGW("Destination path too long; skipping: %s/%s\n", dstpath, name); + continue; + } + + srcpath[srcend] = dstpath[dstend] = '/'; + strcpy(srcpath+srcend+1, name); + strcpy(dstpath+dstend+1, name); + + if (movefileordir(srcpath, dstpath, dstbasepos, dstuid, dstgid, statbuf) != 0) { + res = 1; + } + + // Note: we will be leaving empty directories behind in srcpath, + // but that is okay, the package manager will be erasing all of the + // data associated with .apks that disappear. + + srcpath[srcend] = dstpath[dstend] = 0; + } + + closedir(d); + return res; +} + +int movefiles() +{ + DIR *d; + int dfd, subfd; + struct dirent *de; + struct stat s; + char buf[PKG_PATH_MAX+1]; + int bufp, bufe, bufi, readlen; + + char srcpkg[PKG_NAME_MAX]; + char dstpkg[PKG_NAME_MAX]; + char srcpath[PKG_PATH_MAX]; + char dstpath[PKG_PATH_MAX]; + int dstuid=-1, dstgid=-1; + int hasspace; + + d = opendir(UPDATE_COMMANDS_DIR_PREFIX); + if (d == NULL) { + goto done; + } + dfd = dirfd(d); + + /* Iterate through all files in the directory, executing the + * file movements requested there-in. + */ + while ((de = readdir(d))) { + const char *name = de->d_name; + + if (de->d_type == DT_DIR) { + continue; + } else { + subfd = openat(dfd, name, O_RDONLY); + if (subfd < 0) { + ALOGW("Unable to open update commands at %s%s\n", + UPDATE_COMMANDS_DIR_PREFIX, name); + continue; + } + + bufp = 0; + bufe = 0; + buf[PKG_PATH_MAX] = 0; + srcpkg[0] = dstpkg[0] = 0; + while (1) { + bufi = bufp; + while (bufi < bufe && buf[bufi] != '\n') { + bufi++; + } + if (bufi < bufe) { + buf[bufi] = 0; + ALOGV("Processing line: %s\n", buf+bufp); + hasspace = 0; + while (bufp < bufi && isspace(buf[bufp])) { + hasspace = 1; + bufp++; + } + if (buf[bufp] == '#' || bufp == bufi) { + // skip comments and empty lines. + } else if (hasspace) { + if (dstpkg[0] == 0) { + ALOGW("Path before package line in %s%s: %s\n", + UPDATE_COMMANDS_DIR_PREFIX, name, buf+bufp); + } else if (srcpkg[0] == 0) { + // Skip -- source package no longer exists. + } else { + ALOGV("Move file: %s (from %s to %s)\n", buf+bufp, srcpkg, dstpkg); + if (!create_move_path(srcpath, srcpkg, buf+bufp, 0) && + !create_move_path(dstpath, dstpkg, buf+bufp, 0)) { + movefileordir(srcpath, dstpath, + strlen(dstpath)-strlen(buf+bufp), + dstuid, dstgid, &s); + } + } + } else { + char* div = strchr(buf+bufp, ':'); + if (div == NULL) { + ALOGW("Bad package spec in %s%s; no ':' sep: %s\n", + UPDATE_COMMANDS_DIR_PREFIX, name, buf+bufp); + } else { + *div = 0; + div++; + if (strlen(buf+bufp) < PKG_NAME_MAX) { + strcpy(dstpkg, buf+bufp); + } else { + srcpkg[0] = dstpkg[0] = 0; + ALOGW("Package name too long in %s%s: %s\n", + UPDATE_COMMANDS_DIR_PREFIX, name, buf+bufp); + } + if (strlen(div) < PKG_NAME_MAX) { + strcpy(srcpkg, div); + } else { + srcpkg[0] = dstpkg[0] = 0; + ALOGW("Package name too long in %s%s: %s\n", + UPDATE_COMMANDS_DIR_PREFIX, name, div); + } + if (srcpkg[0] != 0) { + if (!create_pkg_path(srcpath, srcpkg, PKG_DIR_POSTFIX, 0)) { + if (lstat(srcpath, &s) < 0) { + // Package no longer exists -- skip. + srcpkg[0] = 0; + } + } else { + srcpkg[0] = 0; + ALOGW("Can't create path %s in %s%s\n", + div, UPDATE_COMMANDS_DIR_PREFIX, name); + } + if (srcpkg[0] != 0) { + if (!create_pkg_path(dstpath, dstpkg, PKG_DIR_POSTFIX, 0)) { + if (lstat(dstpath, &s) == 0) { + dstuid = s.st_uid; + dstgid = s.st_gid; + } else { + // Destination package doesn't + // exist... due to original-package, + // this is normal, so don't be + // noisy about it. + srcpkg[0] = 0; + } + } else { + srcpkg[0] = 0; + ALOGW("Can't create path %s in %s%s\n", + div, UPDATE_COMMANDS_DIR_PREFIX, name); + } + } + ALOGV("Transfering from %s to %s: uid=%d\n", + srcpkg, dstpkg, dstuid); + } + } + } + bufp = bufi+1; + } else { + if (bufp == 0) { + if (bufp < bufe) { + ALOGW("Line too long in %s%s, skipping: %s\n", + UPDATE_COMMANDS_DIR_PREFIX, name, buf); + } + } else if (bufp < bufe) { + memcpy(buf, buf+bufp, bufe-bufp); + bufe -= bufp; + bufp = 0; + } + readlen = read(subfd, buf+bufe, PKG_PATH_MAX-bufe); + if (readlen < 0) { + ALOGW("Failure reading update commands in %s%s: %s\n", + UPDATE_COMMANDS_DIR_PREFIX, name, strerror(errno)); + break; + } else if (readlen == 0) { + break; + } + bufe += readlen; + buf[bufe] = 0; + ALOGV("Read buf: %s\n", buf); + } + } + close(subfd); + } + } + closedir(d); +done: + return 0; +} + +int linklib(const char* pkgname, const char* asecLibDir, int userId) +{ + char pkgdir[PKG_PATH_MAX]; + char libsymlink[PKG_PATH_MAX]; + struct stat s, libStat; + int rc = 0; + + if (create_pkg_path(pkgdir, pkgname, PKG_DIR_POSTFIX, userId)) { + ALOGE("cannot create package path\n"); + return -1; + } + if (create_pkg_path(libsymlink, pkgname, PKG_LIB_POSTFIX, userId)) { + ALOGE("cannot create package lib symlink origin path\n"); + return -1; + } + + if (stat(pkgdir, &s) < 0) return -1; + + if (chown(pkgdir, AID_INSTALL, AID_INSTALL) < 0) { + ALOGE("failed to chown '%s': %s\n", pkgdir, strerror(errno)); + return -1; + } + + if (chmod(pkgdir, 0700) < 0) { + ALOGE("linklib() 1: failed to chmod '%s': %s\n", pkgdir, strerror(errno)); + rc = -1; + goto out; + } + + if (lstat(libsymlink, &libStat) < 0) { + if (errno != ENOENT) { + ALOGE("couldn't stat lib dir: %s\n", strerror(errno)); + rc = -1; + goto out; + } + } else { + if (S_ISDIR(libStat.st_mode)) { + if (delete_dir_contents(libsymlink, 1, NULL) < 0) { + rc = -1; + goto out; + } + } else if (S_ISLNK(libStat.st_mode)) { + if (unlink(libsymlink) < 0) { + ALOGE("couldn't unlink lib dir: %s\n", strerror(errno)); + rc = -1; + goto out; + } + } + } + + if (symlink(asecLibDir, libsymlink) < 0) { + ALOGE("couldn't symlink directory '%s' -> '%s': %s\n", libsymlink, asecLibDir, + strerror(errno)); + rc = -errno; + goto out; + } + +out: + if (chmod(pkgdir, s.st_mode) < 0) { + ALOGE("linklib() 2: failed to chmod '%s': %s\n", pkgdir, strerror(errno)); + rc = -errno; + } + + if (chown(pkgdir, s.st_uid, s.st_gid) < 0) { + ALOGE("failed to chown '%s' : %s\n", pkgdir, strerror(errno)); + return -errno; + } + + return rc; +} + +static void run_idmap(const char *target_apk, const char *overlay_apk, int idmap_fd) +{ + static const char *IDMAP_BIN = "/system/bin/idmap"; + static const size_t MAX_INT_LEN = 32; + char idmap_str[MAX_INT_LEN]; + + snprintf(idmap_str, sizeof(idmap_str), "%d", idmap_fd); + + execl(IDMAP_BIN, IDMAP_BIN, "--fd", target_apk, overlay_apk, idmap_str, (char*)NULL); + ALOGE("execl(%s) failed: %s\n", IDMAP_BIN, strerror(errno)); +} + +// Transform string /a/b/c.apk to (prefix)/a@b@c.apk@(suffix) +// eg /a/b/c.apk to /data/resource-cache/a@b@c.apk@idmap +static int flatten_path(const char *prefix, const char *suffix, + const char *overlay_path, char *idmap_path, size_t N) +{ + if (overlay_path == NULL || idmap_path == NULL) { + return -1; + } + const size_t len_overlay_path = strlen(overlay_path); + // will access overlay_path + 1 further below; requires absolute path + if (len_overlay_path < 2 || *overlay_path != '/') { + return -1; + } + const size_t len_idmap_root = strlen(prefix); + const size_t len_suffix = strlen(suffix); + if (SIZE_MAX - len_idmap_root < len_overlay_path || + SIZE_MAX - (len_idmap_root + len_overlay_path) < len_suffix) { + // additions below would cause overflow + return -1; + } + if (N < len_idmap_root + len_overlay_path + len_suffix) { + return -1; + } + memset(idmap_path, 0, N); + snprintf(idmap_path, N, "%s%s%s", prefix, overlay_path + 1, suffix); + char *ch = idmap_path + len_idmap_root; + while (*ch != '\0') { + if (*ch == '/') { + *ch = '@'; + } + ++ch; + } + return 0; +} + +int idmap(const char *target_apk, const char *overlay_apk, uid_t uid) +{ + ALOGV("idmap target_apk=%s overlay_apk=%s uid=%d\n", target_apk, overlay_apk, uid); + + int idmap_fd = -1; + char idmap_path[PATH_MAX]; + + if (flatten_path(IDMAP_PREFIX, IDMAP_SUFFIX, overlay_apk, + idmap_path, sizeof(idmap_path)) == -1) { + ALOGE("idmap cannot generate idmap path for overlay %s\n", overlay_apk); + goto fail; + } + + unlink(idmap_path); + idmap_fd = open(idmap_path, O_RDWR | O_CREAT | O_EXCL, 0644); + if (idmap_fd < 0) { + ALOGE("idmap cannot open '%s' for output: %s\n", idmap_path, strerror(errno)); + goto fail; + } + if (fchown(idmap_fd, AID_SYSTEM, uid) < 0) { + ALOGE("idmap cannot chown '%s'\n", idmap_path); + goto fail; + } + if (fchmod(idmap_fd, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) < 0) { + ALOGE("idmap cannot chmod '%s'\n", idmap_path); + goto fail; + } + + pid_t pid; + pid = fork(); + if (pid == 0) { + /* child -- drop privileges before continuing */ + if (setgid(uid) != 0) { + ALOGE("setgid(%d) failed during idmap\n", uid); + exit(1); + } + if (setuid(uid) != 0) { + ALOGE("setuid(%d) failed during idmap\n", uid); + exit(1); + } + if (flock(idmap_fd, LOCK_EX | LOCK_NB) != 0) { + ALOGE("flock(%s) failed during idmap: %s\n", idmap_path, strerror(errno)); + exit(1); + } + + run_idmap(target_apk, overlay_apk, idmap_fd); + exit(1); /* only if exec call to idmap failed */ + } else { + int status = wait_child(pid); + if (status != 0) { + ALOGE("idmap failed, status=0x%04x\n", status); + goto fail; + } + } + + close(idmap_fd); + return 0; +fail: + if (idmap_fd >= 0) { + close(idmap_fd); + unlink(idmap_path); + } + return -1; +} + +int restorecon_data(const char* pkgName, const char* seinfo, uid_t uid) +{ + struct dirent *entry; + DIR *d; + struct stat s; + char *userdir; + char *primarydir; + char *pkgdir; + int ret = 0; + + // SELINUX_ANDROID_RESTORECON_DATADATA flag is set by libselinux. Not needed here. + unsigned int flags = SELINUX_ANDROID_RESTORECON_RECURSE; + + if (!pkgName || !seinfo) { + ALOGE("Package name or seinfo tag is null when trying to restorecon."); + return -1; + } + + if (asprintf(&primarydir, "%s%s%s", android_data_dir.path, PRIMARY_USER_PREFIX, pkgName) < 0) { + return -1; + } + + // Relabel for primary user. + if (selinux_android_restorecon_pkgdir(primarydir, seinfo, uid, flags) < 0) { + ALOGE("restorecon failed for %s: %s\n", primarydir, strerror(errno)); + ret |= -1; + } + + if (asprintf(&userdir, "%s%s", android_data_dir.path, SECONDARY_USER_PREFIX) < 0) { + free(primarydir); + return -1; + } + + // Relabel package directory for all secondary users. + d = opendir(userdir); + if (d == NULL) { + free(primarydir); + free(userdir); + return -1; + } + + while ((entry = readdir(d))) { + if (entry->d_type != DT_DIR) { + continue; + } + + const char *user = entry->d_name; + // Ignore "." and ".." + if (!strcmp(user, ".") || !strcmp(user, "..")) { + continue; + } + + // user directories start with a number + if (user[0] < '0' || user[0] > '9') { + ALOGE("Expecting numbered directory during restorecon. Instead got '%s'.", user); + continue; + } + + if (asprintf(&pkgdir, "%s%s/%s", userdir, user, pkgName) < 0) { + continue; + } + + if (stat(pkgdir, &s) < 0) { + free(pkgdir); + continue; + } + + if (selinux_android_restorecon_pkgdir(pkgdir, seinfo, s.st_uid, flags) < 0) { + ALOGE("restorecon failed for %s: %s\n", pkgdir, strerror(errno)); + ret |= -1; + } + free(pkgdir); + } + + closedir(d); + free(primarydir); + free(userdir); + return ret; +} + +int create_oat_dir(const char* oat_dir, const char* instruction_set) +{ + char oat_instr_dir[PKG_PATH_MAX]; + + if (validate_apk_path(oat_dir)) { + ALOGE("invalid apk path '%s' (bad prefix)\n", oat_dir); + return -1; + } + if (fs_prepare_dir(oat_dir, S_IRWXU | S_IRWXG | S_IXOTH, AID_SYSTEM, AID_INSTALL)) { + return -1; + } + if (selinux_android_restorecon(oat_dir, 0)) { + ALOGE("cannot restorecon dir '%s': %s\n", oat_dir, strerror(errno)); + return -1; + } + snprintf(oat_instr_dir, PKG_PATH_MAX, "%s/%s", oat_dir, instruction_set); + if (fs_prepare_dir(oat_instr_dir, S_IRWXU | S_IRWXG | S_IXOTH, AID_SYSTEM, AID_INSTALL)) { + return -1; + } + return 0; +} + +int rm_package_dir(const char* apk_path) +{ + if (validate_apk_path(apk_path)) { + ALOGE("invalid apk path '%s' (bad prefix)\n", apk_path); + return -1; + } + return delete_dir_contents(apk_path, 1 /* also_delete_dir */ , NULL /* exclusion_predicate */); +} + +int calculate_oat_file_path(char path[PKG_PATH_MAX], const char *oat_dir, const char *apk_path, + const char *instruction_set) { + char *file_name_start; + char *file_name_end; + + file_name_start = strrchr(apk_path, '/'); + if (file_name_start == NULL) { + ALOGE("apk_path '%s' has no '/'s in it\n", apk_path); + return -1; + } + file_name_end = strrchr(apk_path, '.'); + if (file_name_end < file_name_start) { + ALOGE("apk_path '%s' has no extension\n", apk_path); + return -1; + } + + // Calculate file_name + int file_name_len = file_name_end - file_name_start - 1; + char file_name[file_name_len + 1]; + memcpy(file_name, file_name_start + 1, file_name_len); + file_name[file_name_len] = '\0'; + + // /oat//.odex + snprintf(path, PKG_PATH_MAX, "%s/%s/%s.odex", oat_dir, instruction_set, file_name); + return 0; +} diff --git a/cmds/installd/installd.c b/cmds/installd/installd.c deleted file mode 100644 index 930ba2f..0000000 --- a/cmds/installd/installd.c +++ /dev/null @@ -1,760 +0,0 @@ -/* -** Copyright 2008, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - -#include -#include -#include -#include - -#include "installd.h" - - -#define BUFFER_MAX 1024 /* input buffer for commands */ -#define TOKEN_MAX 16 /* max number of arguments in buffer */ -#define REPLY_MAX 256 /* largest reply allowed */ - -static int do_ping(char **arg __unused, char reply[REPLY_MAX] __unused) -{ - return 0; -} - -static int do_install(char **arg, char reply[REPLY_MAX] __unused) -{ - return install(arg[0], atoi(arg[1]), atoi(arg[2]), arg[3]); /* pkgname, uid, gid, seinfo */ -} - -static int do_dexopt(char **arg, char reply[REPLY_MAX] __unused) -{ - /* apk_path, uid, is_public, pkgname, instruction_set, vm_safe_mode, should_relocate - debuggable, outputPath */ - return dexopt(arg[0], atoi(arg[1]), atoi(arg[2]), arg[3], arg[4], atoi(arg[5]), 0, - atoi(arg[6]), arg[7]); -} - -static int do_mark_boot_complete(char **arg, char reply[REPLY_MAX] __unused) -{ - return mark_boot_complete(arg[0] /* instruction set */); -} - -static int do_move_dex(char **arg, char reply[REPLY_MAX] __unused) -{ - return move_dex(arg[0], arg[1], arg[2]); /* src, dst, instruction_set */ -} - -static int do_rm_dex(char **arg, char reply[REPLY_MAX] __unused) -{ - return rm_dex(arg[0], arg[1]); /* pkgname, instruction_set */ -} - -static int do_remove(char **arg, char reply[REPLY_MAX] __unused) -{ - return uninstall(arg[0], atoi(arg[1])); /* pkgname, userid */ -} - -static int do_rename(char **arg, char reply[REPLY_MAX] __unused) -{ - return renamepkg(arg[0], arg[1]); /* oldpkgname, newpkgname */ -} - -static int do_fixuid(char **arg, char reply[REPLY_MAX] __unused) -{ - return fix_uid(arg[0], atoi(arg[1]), atoi(arg[2])); /* pkgname, uid, gid */ -} - -static int do_free_cache(char **arg, char reply[REPLY_MAX] __unused) /* TODO int:free_size */ -{ - return free_cache((int64_t)atoll(arg[0])); /* free_size */ -} - -static int do_rm_cache(char **arg, char reply[REPLY_MAX] __unused) -{ - return delete_cache(arg[0], atoi(arg[1])); /* pkgname, userid */ -} - -static int do_rm_code_cache(char **arg, char reply[REPLY_MAX] __unused) -{ - return delete_code_cache(arg[0], atoi(arg[1])); /* pkgname, userid */ -} - -static int do_get_size(char **arg, char reply[REPLY_MAX]) -{ - int64_t codesize = 0; - int64_t datasize = 0; - int64_t cachesize = 0; - int64_t asecsize = 0; - int res = 0; - - /* pkgdir, userid, apkpath */ - res = get_size(arg[0], atoi(arg[1]), arg[2], arg[3], arg[4], arg[5], - arg[6], &codesize, &datasize, &cachesize, &asecsize); - - /* - * Each int64_t can take up 22 characters printed out. Make sure it - * doesn't go over REPLY_MAX in the future. - */ - snprintf(reply, REPLY_MAX, "%" PRId64 " %" PRId64 " %" PRId64 " %" PRId64, - codesize, datasize, cachesize, asecsize); - return res; -} - -static int do_rm_user_data(char **arg, char reply[REPLY_MAX] __unused) -{ - return delete_user_data(arg[0], atoi(arg[1])); /* pkgname, userid */ -} - -static int do_mk_user_data(char **arg, char reply[REPLY_MAX] __unused) -{ - return make_user_data(arg[0], atoi(arg[1]), atoi(arg[2]), arg[3]); - /* pkgname, uid, userid, seinfo */ -} - -static int do_mk_user_config(char **arg, char reply[REPLY_MAX] __unused) -{ - return make_user_config(atoi(arg[0])); /* userid */ -} - -static int do_rm_user(char **arg, char reply[REPLY_MAX] __unused) -{ - return delete_user(atoi(arg[0])); /* userid */ -} - -static int do_movefiles(char **arg __unused, char reply[REPLY_MAX] __unused) -{ - return movefiles(); -} - -static int do_linklib(char **arg, char reply[REPLY_MAX] __unused) -{ - return linklib(arg[0], arg[1], atoi(arg[2])); -} - -static int do_idmap(char **arg, char reply[REPLY_MAX] __unused) -{ - return idmap(arg[0], arg[1], atoi(arg[2])); -} - -static int do_restorecon_data(char **arg, char reply[REPLY_MAX] __attribute__((unused))) -{ - return restorecon_data(arg[0], arg[1], atoi(arg[2])); - /* pkgName, seinfo, uid*/ -} - -static int do_patchoat(char **arg, char reply[REPLY_MAX] __unused) { - /* apk_path, uid, is_public, pkgname, instruction_set, vm_safe_mode, should_relocate, - debuggable, outputPath */ - return dexopt(arg[0], atoi(arg[1]), atoi(arg[2]), arg[3], arg[4], 0, 1, 0, "!"); -} - -static int do_create_oat_dir(char **arg, char reply[REPLY_MAX] __unused) -{ - /* oat_dir, instruction_set */ - return create_oat_dir(arg[0], arg[1]); -} - -static int do_rm_package_dir(char **arg, char reply[REPLY_MAX] __unused) -{ - /* oat_dir */ - return rm_package_dir(arg[0]); -} - -struct cmdinfo { - const char *name; - unsigned numargs; - int (*func)(char **arg, char reply[REPLY_MAX]); -}; - -struct cmdinfo cmds[] = { - { "ping", 0, do_ping }, - { "install", 4, do_install }, - { "dexopt", 8, do_dexopt }, - { "markbootcomplete", 1, do_mark_boot_complete }, - { "movedex", 3, do_move_dex }, - { "rmdex", 2, do_rm_dex }, - { "remove", 2, do_remove }, - { "rename", 2, do_rename }, - { "fixuid", 3, do_fixuid }, - { "freecache", 1, do_free_cache }, - { "rmcache", 2, do_rm_cache }, - { "rmcodecache", 2, do_rm_code_cache }, - { "getsize", 7, do_get_size }, - { "rmuserdata", 2, do_rm_user_data }, - { "movefiles", 0, do_movefiles }, - { "linklib", 3, do_linklib }, - { "mkuserdata", 4, do_mk_user_data }, - { "mkuserconfig", 1, do_mk_user_config }, - { "rmuser", 1, do_rm_user }, - { "idmap", 3, do_idmap }, - { "restorecondata", 3, do_restorecon_data }, - { "patchoat", 5, do_patchoat }, - { "createoatdir", 2, do_create_oat_dir }, - { "rmpackagedir", 1, do_rm_package_dir}, -}; - -static int readx(int s, void *_buf, int count) -{ - char *buf = _buf; - int n = 0, r; - if (count < 0) return -1; - while (n < count) { - r = read(s, buf + n, count - n); - if (r < 0) { - if (errno == EINTR) continue; - ALOGE("read error: %s\n", strerror(errno)); - return -1; - } - if (r == 0) { - ALOGE("eof\n"); - return -1; /* EOF */ - } - n += r; - } - return 0; -} - -static int writex(int s, const void *_buf, int count) -{ - const char *buf = _buf; - int n = 0, r; - if (count < 0) return -1; - while (n < count) { - r = write(s, buf + n, count - n); - if (r < 0) { - if (errno == EINTR) continue; - ALOGE("write error: %s\n", strerror(errno)); - return -1; - } - n += r; - } - return 0; -} - - -/* Tokenize the command buffer, locate a matching command, - * ensure that the required number of arguments are provided, - * call the function(), return the result. - */ -static int execute(int s, char cmd[BUFFER_MAX]) -{ - char reply[REPLY_MAX]; - char *arg[TOKEN_MAX+1]; - unsigned i; - unsigned n = 0; - unsigned short count; - int ret = -1; - - // ALOGI("execute('%s')\n", cmd); - - /* default reply is "" */ - reply[0] = 0; - - /* n is number of args (not counting arg[0]) */ - arg[0] = cmd; - while (*cmd) { - if (isspace(*cmd)) { - *cmd++ = 0; - n++; - arg[n] = cmd; - if (n == TOKEN_MAX) { - ALOGE("too many arguments\n"); - goto done; - } - } - if (*cmd) { - cmd++; - } - } - - for (i = 0; i < sizeof(cmds) / sizeof(cmds[0]); i++) { - if (!strcmp(cmds[i].name,arg[0])) { - if (n != cmds[i].numargs) { - ALOGE("%s requires %d arguments (%d given)\n", - cmds[i].name, cmds[i].numargs, n); - } else { - ret = cmds[i].func(arg + 1, reply); - } - goto done; - } - } - ALOGE("unsupported command '%s'\n", arg[0]); - -done: - if (reply[0]) { - n = snprintf(cmd, BUFFER_MAX, "%d %s", ret, reply); - } else { - n = snprintf(cmd, BUFFER_MAX, "%d", ret); - } - if (n > BUFFER_MAX) n = BUFFER_MAX; - count = n; - - // ALOGI("reply: '%s'\n", cmd); - if (writex(s, &count, sizeof(count))) return -1; - if (writex(s, cmd, count)) return -1; - return 0; -} - -/** - * Initialize all the global variables that are used elsewhere. Returns 0 upon - * success and -1 on error. - */ -void free_globals() { - size_t i; - - for (i = 0; i < android_system_dirs.count; i++) { - if (android_system_dirs.dirs[i].path != NULL) { - free(android_system_dirs.dirs[i].path); - } - } - - free(android_system_dirs.dirs); -} - -int initialize_globals() { - // Get the android data directory. - if (get_path_from_env(&android_data_dir, "ANDROID_DATA") < 0) { - return -1; - } - - // Get the android app directory. - if (copy_and_append(&android_app_dir, &android_data_dir, APP_SUBDIR) < 0) { - return -1; - } - - // Get the android protected app directory. - if (copy_and_append(&android_app_private_dir, &android_data_dir, PRIVATE_APP_SUBDIR) < 0) { - return -1; - } - - // Get the android app native library directory. - if (copy_and_append(&android_app_lib_dir, &android_data_dir, APP_LIB_SUBDIR) < 0) { - return -1; - } - - // Get the sd-card ASEC mount point. - if (get_path_from_env(&android_asec_dir, "ASEC_MOUNTPOINT") < 0) { - return -1; - } - - // Get the android media directory. - if (copy_and_append(&android_media_dir, &android_data_dir, MEDIA_SUBDIR) < 0) { - return -1; - } - - // Get the android external app directory. - if (get_path_from_string(&android_mnt_expand_dir, "/mnt/expand") < 0) { - return -1; - } - - // Take note of the system and vendor directories. - android_system_dirs.count = 4; - - android_system_dirs.dirs = calloc(android_system_dirs.count, sizeof(dir_rec_t)); - if (android_system_dirs.dirs == NULL) { - ALOGE("Couldn't allocate array for dirs; aborting\n"); - return -1; - } - - dir_rec_t android_root_dir; - if (get_path_from_env(&android_root_dir, "ANDROID_ROOT") < 0) { - ALOGE("Missing ANDROID_ROOT; aborting\n"); - return -1; - } - - android_system_dirs.dirs[0].path = build_string2(android_root_dir.path, APP_SUBDIR); - android_system_dirs.dirs[0].len = strlen(android_system_dirs.dirs[0].path); - - android_system_dirs.dirs[1].path = build_string2(android_root_dir.path, PRIV_APP_SUBDIR); - android_system_dirs.dirs[1].len = strlen(android_system_dirs.dirs[1].path); - - android_system_dirs.dirs[2].path = "/vendor/app/"; - android_system_dirs.dirs[2].len = strlen(android_system_dirs.dirs[2].path); - - android_system_dirs.dirs[3].path = "/oem/app/"; - android_system_dirs.dirs[3].len = strlen(android_system_dirs.dirs[3].path); - - return 0; -} - -int initialize_directories() { - int res = -1; - - // Read current filesystem layout version to handle upgrade paths - char version_path[PATH_MAX]; - snprintf(version_path, PATH_MAX, "%s.layout_version", android_data_dir.path); - - int oldVersion; - if (fs_read_atomic_int(version_path, &oldVersion) == -1) { - oldVersion = 0; - } - int version = oldVersion; - - // /data/user - char *user_data_dir = build_string2(android_data_dir.path, SECONDARY_USER_PREFIX); - // /data/data - char *legacy_data_dir = build_string2(android_data_dir.path, PRIMARY_USER_PREFIX); - // /data/user/0 - char *primary_data_dir = build_string3(android_data_dir.path, SECONDARY_USER_PREFIX, "0"); - if (!user_data_dir || !legacy_data_dir || !primary_data_dir) { - goto fail; - } - - // Make the /data/user directory if necessary - if (access(user_data_dir, R_OK) < 0) { - if (mkdir(user_data_dir, 0711) < 0) { - goto fail; - } - if (chown(user_data_dir, AID_SYSTEM, AID_SYSTEM) < 0) { - goto fail; - } - if (chmod(user_data_dir, 0711) < 0) { - goto fail; - } - } - // Make the /data/user/0 symlink to /data/data if necessary - if (access(primary_data_dir, R_OK) < 0) { - if (symlink(legacy_data_dir, primary_data_dir)) { - goto fail; - } - } - - if (version == 0) { - // Introducing multi-user, so migrate /data/media contents into /data/media/0 - ALOGD("Upgrading /data/media for multi-user"); - - // Ensure /data/media - if (fs_prepare_dir(android_media_dir.path, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) { - goto fail; - } - - // /data/media.tmp - char media_tmp_dir[PATH_MAX]; - snprintf(media_tmp_dir, PATH_MAX, "%smedia.tmp", android_data_dir.path); - - // Only copy when upgrade not already in progress - if (access(media_tmp_dir, F_OK) == -1) { - if (rename(android_media_dir.path, media_tmp_dir) == -1) { - ALOGE("Failed to move legacy media path: %s", strerror(errno)); - goto fail; - } - } - - // Create /data/media again - if (fs_prepare_dir(android_media_dir.path, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) { - goto fail; - } - - if (selinux_android_restorecon(android_media_dir.path, 0)) { - goto fail; - } - - // /data/media/0 - char owner_media_dir[PATH_MAX]; - snprintf(owner_media_dir, PATH_MAX, "%s0", android_media_dir.path); - - // Move any owner data into place - if (access(media_tmp_dir, F_OK) == 0) { - if (rename(media_tmp_dir, owner_media_dir) == -1) { - ALOGE("Failed to move owner media path: %s", strerror(errno)); - goto fail; - } - } - - // Ensure media directories for any existing users - DIR *dir; - struct dirent *dirent; - char user_media_dir[PATH_MAX]; - - dir = opendir(user_data_dir); - if (dir != NULL) { - while ((dirent = readdir(dir))) { - if (dirent->d_type == DT_DIR) { - const char *name = dirent->d_name; - - // skip "." and ".." - if (name[0] == '.') { - if (name[1] == 0) continue; - if ((name[1] == '.') && (name[2] == 0)) continue; - } - - // /data/media/ - snprintf(user_media_dir, PATH_MAX, "%s%s", android_media_dir.path, name); - if (fs_prepare_dir(user_media_dir, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) { - goto fail; - } - } - } - closedir(dir); - } - - version = 1; - } - - // /data/media/obb - char media_obb_dir[PATH_MAX]; - snprintf(media_obb_dir, PATH_MAX, "%sobb", android_media_dir.path); - - if (version == 1) { - // Introducing /data/media/obb for sharing OBB across users; migrate - // any existing OBB files from owner. - ALOGD("Upgrading to shared /data/media/obb"); - - // /data/media/0/Android/obb - char owner_obb_path[PATH_MAX]; - snprintf(owner_obb_path, PATH_MAX, "%s0/Android/obb", android_media_dir.path); - - // Only move if target doesn't already exist - if (access(media_obb_dir, F_OK) != 0 && access(owner_obb_path, F_OK) == 0) { - if (rename(owner_obb_path, media_obb_dir) == -1) { - ALOGE("Failed to move OBB from owner: %s", strerror(errno)); - goto fail; - } - } - - version = 2; - } - - if (ensure_media_user_dirs(0) == -1) { - ALOGE("Failed to setup media for user 0"); - goto fail; - } - if (fs_prepare_dir(media_obb_dir, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) { - goto fail; - } - - if (ensure_config_user_dirs(0) == -1) { - ALOGE("Failed to setup misc for user 0"); - goto fail; - } - - if (version == 2) { - ALOGD("Upgrading to /data/misc/user directories"); - - char misc_dir[PATH_MAX]; - snprintf(misc_dir, PATH_MAX, "%smisc", android_data_dir.path); - - char keychain_added_dir[PATH_MAX]; - snprintf(keychain_added_dir, PATH_MAX, "%s/keychain/cacerts-added", misc_dir); - - char keychain_removed_dir[PATH_MAX]; - snprintf(keychain_removed_dir, PATH_MAX, "%s/keychain/cacerts-removed", misc_dir); - - DIR *dir; - struct dirent *dirent; - dir = opendir(user_data_dir); - if (dir != NULL) { - while ((dirent = readdir(dir))) { - const char *name = dirent->d_name; - - // skip "." and ".." - if (name[0] == '.') { - if (name[1] == 0) continue; - if ((name[1] == '.') && (name[2] == 0)) continue; - } - - uint32_t user_id = atoi(name); - - // /data/misc/user/ - if (ensure_config_user_dirs(user_id) == -1) { - goto fail; - } - - char misc_added_dir[PATH_MAX]; - snprintf(misc_added_dir, PATH_MAX, "%s/user/%s/cacerts-added", misc_dir, name); - - char misc_removed_dir[PATH_MAX]; - snprintf(misc_removed_dir, PATH_MAX, "%s/user/%s/cacerts-removed", misc_dir, name); - - uid_t uid = multiuser_get_uid(user_id, AID_SYSTEM); - gid_t gid = uid; - if (access(keychain_added_dir, F_OK) == 0) { - if (copy_dir_files(keychain_added_dir, misc_added_dir, uid, gid) != 0) { - ALOGE("Some files failed to copy"); - } - } - if (access(keychain_removed_dir, F_OK) == 0) { - if (copy_dir_files(keychain_removed_dir, misc_removed_dir, uid, gid) != 0) { - ALOGE("Some files failed to copy"); - } - } - } - closedir(dir); - - if (access(keychain_added_dir, F_OK) == 0) { - delete_dir_contents(keychain_added_dir, 1, 0); - } - if (access(keychain_removed_dir, F_OK) == 0) { - delete_dir_contents(keychain_removed_dir, 1, 0); - } - } - - version = 3; - } - - // Persist layout version if changed - if (version != oldVersion) { - if (fs_write_atomic_int(version_path, version) == -1) { - ALOGE("Failed to save version to %s: %s", version_path, strerror(errno)); - goto fail; - } - } - - // Success! - res = 0; - -fail: - free(user_data_dir); - free(legacy_data_dir); - free(primary_data_dir); - return res; -} - -static void drop_privileges() { - if (prctl(PR_SET_KEEPCAPS, 1) < 0) { - ALOGE("prctl(PR_SET_KEEPCAPS) failed: %s\n", strerror(errno)); - exit(1); - } - - if (setgid(AID_INSTALL) < 0) { - ALOGE("setgid() can't drop privileges; exiting.\n"); - exit(1); - } - - if (setuid(AID_INSTALL) < 0) { - ALOGE("setuid() can't drop privileges; exiting.\n"); - exit(1); - } - - struct __user_cap_header_struct capheader; - struct __user_cap_data_struct capdata[2]; - memset(&capheader, 0, sizeof(capheader)); - memset(&capdata, 0, sizeof(capdata)); - capheader.version = _LINUX_CAPABILITY_VERSION_3; - capheader.pid = 0; - - capdata[CAP_TO_INDEX(CAP_DAC_OVERRIDE)].permitted |= CAP_TO_MASK(CAP_DAC_OVERRIDE); - capdata[CAP_TO_INDEX(CAP_CHOWN)].permitted |= CAP_TO_MASK(CAP_CHOWN); - capdata[CAP_TO_INDEX(CAP_SETUID)].permitted |= CAP_TO_MASK(CAP_SETUID); - capdata[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID); - capdata[CAP_TO_INDEX(CAP_FOWNER)].permitted |= CAP_TO_MASK(CAP_FOWNER); - - capdata[0].effective = capdata[0].permitted; - capdata[1].effective = capdata[1].permitted; - capdata[0].inheritable = 0; - capdata[1].inheritable = 0; - - if (capset(&capheader, &capdata[0]) < 0) { - ALOGE("capset failed: %s\n", strerror(errno)); - exit(1); - } -} - -static int log_callback(int type, const char *fmt, ...) { - va_list ap; - int priority; - - switch (type) { - case SELINUX_WARNING: - priority = ANDROID_LOG_WARN; - break; - case SELINUX_INFO: - priority = ANDROID_LOG_INFO; - break; - default: - priority = ANDROID_LOG_ERROR; - break; - } - va_start(ap, fmt); - LOG_PRI_VA(priority, "SELinux", fmt, ap); - va_end(ap); - return 0; -} - -int main(const int argc __unused, const char *argv[] __unused) { - char buf[BUFFER_MAX]; - struct sockaddr addr; - socklen_t alen; - int lsocket, s; - int selinux_enabled = (is_selinux_enabled() > 0); - - ALOGI("installd firing up\n"); - - union selinux_callback cb; - cb.func_log = log_callback; - selinux_set_callback(SELINUX_CB_LOG, cb); - - if (initialize_globals() < 0) { - ALOGE("Could not initialize globals; exiting.\n"); - exit(1); - } - - if (initialize_directories() < 0) { - ALOGE("Could not create directories; exiting.\n"); - exit(1); - } - - if (selinux_enabled && selinux_status_open(true) < 0) { - ALOGE("Could not open selinux status; exiting.\n"); - exit(1); - } - - drop_privileges(); - - lsocket = android_get_control_socket(SOCKET_PATH); - if (lsocket < 0) { - ALOGE("Failed to get socket from environment: %s\n", strerror(errno)); - exit(1); - } - if (listen(lsocket, 5)) { - ALOGE("Listen on socket failed: %s\n", strerror(errno)); - exit(1); - } - fcntl(lsocket, F_SETFD, FD_CLOEXEC); - - for (;;) { - alen = sizeof(addr); - s = accept(lsocket, &addr, &alen); - if (s < 0) { - ALOGE("Accept failed: %s\n", strerror(errno)); - continue; - } - fcntl(s, F_SETFD, FD_CLOEXEC); - - ALOGI("new connection\n"); - for (;;) { - unsigned short count; - if (readx(s, &count, sizeof(count))) { - ALOGE("failed to read size\n"); - break; - } - if ((count < 1) || (count >= BUFFER_MAX)) { - ALOGE("invalid size %d\n", count); - break; - } - if (readx(s, buf, count)) { - ALOGE("failed to read command\n"); - break; - } - buf[count] = 0; - if (selinux_enabled && selinux_status_updated() > 0) { - selinux_android_seapp_context_reload(); - } - if (execute(s, buf)) break; - } - ALOGI("closing connection\n"); - close(s); - } - - return 0; -} diff --git a/cmds/installd/installd.cpp b/cmds/installd/installd.cpp new file mode 100644 index 0000000..ad2478b --- /dev/null +++ b/cmds/installd/installd.cpp @@ -0,0 +1,760 @@ +/* +** Copyright 2008, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include +#include +#include +#include + +#include "installd.h" + + +#define BUFFER_MAX 1024 /* input buffer for commands */ +#define TOKEN_MAX 16 /* max number of arguments in buffer */ +#define REPLY_MAX 256 /* largest reply allowed */ + +static int do_ping(char **arg __unused, char reply[REPLY_MAX] __unused) +{ + return 0; +} + +static int do_install(char **arg, char reply[REPLY_MAX] __unused) +{ + return install(arg[0], atoi(arg[1]), atoi(arg[2]), arg[3]); /* pkgname, uid, gid, seinfo */ +} + +static int do_dexopt(char **arg, char reply[REPLY_MAX] __unused) +{ + /* apk_path, uid, is_public, pkgname, instruction_set, vm_safe_mode, should_relocate + debuggable, outputPath */ + return dexopt(arg[0], atoi(arg[1]), atoi(arg[2]), arg[3], arg[4], atoi(arg[5]), 0, + atoi(arg[6]), arg[7]); +} + +static int do_mark_boot_complete(char **arg, char reply[REPLY_MAX] __unused) +{ + return mark_boot_complete(arg[0] /* instruction set */); +} + +static int do_move_dex(char **arg, char reply[REPLY_MAX] __unused) +{ + return move_dex(arg[0], arg[1], arg[2]); /* src, dst, instruction_set */ +} + +static int do_rm_dex(char **arg, char reply[REPLY_MAX] __unused) +{ + return rm_dex(arg[0], arg[1]); /* pkgname, instruction_set */ +} + +static int do_remove(char **arg, char reply[REPLY_MAX] __unused) +{ + return uninstall(arg[0], atoi(arg[1])); /* pkgname, userid */ +} + +static int do_rename(char **arg, char reply[REPLY_MAX] __unused) +{ + return renamepkg(arg[0], arg[1]); /* oldpkgname, newpkgname */ +} + +static int do_fixuid(char **arg, char reply[REPLY_MAX] __unused) +{ + return fix_uid(arg[0], atoi(arg[1]), atoi(arg[2])); /* pkgname, uid, gid */ +} + +static int do_free_cache(char **arg, char reply[REPLY_MAX] __unused) /* TODO int:free_size */ +{ + return free_cache((int64_t)atoll(arg[0])); /* free_size */ +} + +static int do_rm_cache(char **arg, char reply[REPLY_MAX] __unused) +{ + return delete_cache(arg[0], atoi(arg[1])); /* pkgname, userid */ +} + +static int do_rm_code_cache(char **arg, char reply[REPLY_MAX] __unused) +{ + return delete_code_cache(arg[0], atoi(arg[1])); /* pkgname, userid */ +} + +static int do_get_size(char **arg, char reply[REPLY_MAX]) +{ + int64_t codesize = 0; + int64_t datasize = 0; + int64_t cachesize = 0; + int64_t asecsize = 0; + int res = 0; + + /* pkgdir, userid, apkpath */ + res = get_size(arg[0], atoi(arg[1]), arg[2], arg[3], arg[4], arg[5], + arg[6], &codesize, &datasize, &cachesize, &asecsize); + + /* + * Each int64_t can take up 22 characters printed out. Make sure it + * doesn't go over REPLY_MAX in the future. + */ + snprintf(reply, REPLY_MAX, "%" PRId64 " %" PRId64 " %" PRId64 " %" PRId64, + codesize, datasize, cachesize, asecsize); + return res; +} + +static int do_rm_user_data(char **arg, char reply[REPLY_MAX] __unused) +{ + return delete_user_data(arg[0], atoi(arg[1])); /* pkgname, userid */ +} + +static int do_mk_user_data(char **arg, char reply[REPLY_MAX] __unused) +{ + return make_user_data(arg[0], atoi(arg[1]), atoi(arg[2]), arg[3]); + /* pkgname, uid, userid, seinfo */ +} + +static int do_mk_user_config(char **arg, char reply[REPLY_MAX] __unused) +{ + return make_user_config(atoi(arg[0])); /* userid */ +} + +static int do_rm_user(char **arg, char reply[REPLY_MAX] __unused) +{ + return delete_user(atoi(arg[0])); /* userid */ +} + +static int do_movefiles(char **arg __unused, char reply[REPLY_MAX] __unused) +{ + return movefiles(); +} + +static int do_linklib(char **arg, char reply[REPLY_MAX] __unused) +{ + return linklib(arg[0], arg[1], atoi(arg[2])); +} + +static int do_idmap(char **arg, char reply[REPLY_MAX] __unused) +{ + return idmap(arg[0], arg[1], atoi(arg[2])); +} + +static int do_restorecon_data(char **arg, char reply[REPLY_MAX] __attribute__((unused))) +{ + return restorecon_data(arg[0], arg[1], atoi(arg[2])); + /* pkgName, seinfo, uid*/ +} + +static int do_patchoat(char **arg, char reply[REPLY_MAX] __unused) { + /* apk_path, uid, is_public, pkgname, instruction_set, vm_safe_mode, should_relocate, + debuggable, outputPath */ + return dexopt(arg[0], atoi(arg[1]), atoi(arg[2]), arg[3], arg[4], 0, 1, 0, "!"); +} + +static int do_create_oat_dir(char **arg, char reply[REPLY_MAX] __unused) +{ + /* oat_dir, instruction_set */ + return create_oat_dir(arg[0], arg[1]); +} + +static int do_rm_package_dir(char **arg, char reply[REPLY_MAX] __unused) +{ + /* oat_dir */ + return rm_package_dir(arg[0]); +} + +struct cmdinfo { + const char *name; + unsigned numargs; + int (*func)(char **arg, char reply[REPLY_MAX]); +}; + +struct cmdinfo cmds[] = { + { "ping", 0, do_ping }, + { "install", 4, do_install }, + { "dexopt", 8, do_dexopt }, + { "markbootcomplete", 1, do_mark_boot_complete }, + { "movedex", 3, do_move_dex }, + { "rmdex", 2, do_rm_dex }, + { "remove", 2, do_remove }, + { "rename", 2, do_rename }, + { "fixuid", 3, do_fixuid }, + { "freecache", 1, do_free_cache }, + { "rmcache", 2, do_rm_cache }, + { "rmcodecache", 2, do_rm_code_cache }, + { "getsize", 7, do_get_size }, + { "rmuserdata", 2, do_rm_user_data }, + { "movefiles", 0, do_movefiles }, + { "linklib", 3, do_linklib }, + { "mkuserdata", 4, do_mk_user_data }, + { "mkuserconfig", 1, do_mk_user_config }, + { "rmuser", 1, do_rm_user }, + { "idmap", 3, do_idmap }, + { "restorecondata", 3, do_restorecon_data }, + { "patchoat", 5, do_patchoat }, + { "createoatdir", 2, do_create_oat_dir }, + { "rmpackagedir", 1, do_rm_package_dir}, +}; + +static int readx(int s, void *_buf, int count) +{ + char *buf = (char *) _buf; + int n = 0, r; + if (count < 0) return -1; + while (n < count) { + r = read(s, buf + n, count - n); + if (r < 0) { + if (errno == EINTR) continue; + ALOGE("read error: %s\n", strerror(errno)); + return -1; + } + if (r == 0) { + ALOGE("eof\n"); + return -1; /* EOF */ + } + n += r; + } + return 0; +} + +static int writex(int s, const void *_buf, int count) +{ + const char *buf = (const char *) _buf; + int n = 0, r; + if (count < 0) return -1; + while (n < count) { + r = write(s, buf + n, count - n); + if (r < 0) { + if (errno == EINTR) continue; + ALOGE("write error: %s\n", strerror(errno)); + return -1; + } + n += r; + } + return 0; +} + + +/* Tokenize the command buffer, locate a matching command, + * ensure that the required number of arguments are provided, + * call the function(), return the result. + */ +static int execute(int s, char cmd[BUFFER_MAX]) +{ + char reply[REPLY_MAX]; + char *arg[TOKEN_MAX+1]; + unsigned i; + unsigned n = 0; + unsigned short count; + int ret = -1; + + // ALOGI("execute('%s')\n", cmd); + + /* default reply is "" */ + reply[0] = 0; + + /* n is number of args (not counting arg[0]) */ + arg[0] = cmd; + while (*cmd) { + if (isspace(*cmd)) { + *cmd++ = 0; + n++; + arg[n] = cmd; + if (n == TOKEN_MAX) { + ALOGE("too many arguments\n"); + goto done; + } + } + if (*cmd) { + cmd++; + } + } + + for (i = 0; i < sizeof(cmds) / sizeof(cmds[0]); i++) { + if (!strcmp(cmds[i].name,arg[0])) { + if (n != cmds[i].numargs) { + ALOGE("%s requires %d arguments (%d given)\n", + cmds[i].name, cmds[i].numargs, n); + } else { + ret = cmds[i].func(arg + 1, reply); + } + goto done; + } + } + ALOGE("unsupported command '%s'\n", arg[0]); + +done: + if (reply[0]) { + n = snprintf(cmd, BUFFER_MAX, "%d %s", ret, reply); + } else { + n = snprintf(cmd, BUFFER_MAX, "%d", ret); + } + if (n > BUFFER_MAX) n = BUFFER_MAX; + count = n; + + // ALOGI("reply: '%s'\n", cmd); + if (writex(s, &count, sizeof(count))) return -1; + if (writex(s, cmd, count)) return -1; + return 0; +} + +/** + * Initialize all the global variables that are used elsewhere. Returns 0 upon + * success and -1 on error. + */ +void free_globals() { + size_t i; + + for (i = 0; i < android_system_dirs.count; i++) { + if (android_system_dirs.dirs[i].path != NULL) { + free(android_system_dirs.dirs[i].path); + } + } + + free(android_system_dirs.dirs); +} + +int initialize_globals() { + // Get the android data directory. + if (get_path_from_env(&android_data_dir, "ANDROID_DATA") < 0) { + return -1; + } + + // Get the android app directory. + if (copy_and_append(&android_app_dir, &android_data_dir, APP_SUBDIR) < 0) { + return -1; + } + + // Get the android protected app directory. + if (copy_and_append(&android_app_private_dir, &android_data_dir, PRIVATE_APP_SUBDIR) < 0) { + return -1; + } + + // Get the android app native library directory. + if (copy_and_append(&android_app_lib_dir, &android_data_dir, APP_LIB_SUBDIR) < 0) { + return -1; + } + + // Get the sd-card ASEC mount point. + if (get_path_from_env(&android_asec_dir, "ASEC_MOUNTPOINT") < 0) { + return -1; + } + + // Get the android media directory. + if (copy_and_append(&android_media_dir, &android_data_dir, MEDIA_SUBDIR) < 0) { + return -1; + } + + // Get the android external app directory. + if (get_path_from_string(&android_mnt_expand_dir, "/mnt/expand/") < 0) { + return -1; + } + + // Take note of the system and vendor directories. + android_system_dirs.count = 4; + + android_system_dirs.dirs = (dir_rec_t*) calloc(android_system_dirs.count, sizeof(dir_rec_t)); + if (android_system_dirs.dirs == NULL) { + ALOGE("Couldn't allocate array for dirs; aborting\n"); + return -1; + } + + dir_rec_t android_root_dir; + if (get_path_from_env(&android_root_dir, "ANDROID_ROOT") < 0) { + ALOGE("Missing ANDROID_ROOT; aborting\n"); + return -1; + } + + android_system_dirs.dirs[0].path = build_string2(android_root_dir.path, APP_SUBDIR); + android_system_dirs.dirs[0].len = strlen(android_system_dirs.dirs[0].path); + + android_system_dirs.dirs[1].path = build_string2(android_root_dir.path, PRIV_APP_SUBDIR); + android_system_dirs.dirs[1].len = strlen(android_system_dirs.dirs[1].path); + + android_system_dirs.dirs[2].path = strdup("/vendor/app/"); + android_system_dirs.dirs[2].len = strlen(android_system_dirs.dirs[2].path); + + android_system_dirs.dirs[3].path = strdup("/oem/app/"); + android_system_dirs.dirs[3].len = strlen(android_system_dirs.dirs[3].path); + + return 0; +} + +int initialize_directories() { + int res = -1; + + // Read current filesystem layout version to handle upgrade paths + char version_path[PATH_MAX]; + snprintf(version_path, PATH_MAX, "%s.layout_version", android_data_dir.path); + + int oldVersion; + if (fs_read_atomic_int(version_path, &oldVersion) == -1) { + oldVersion = 0; + } + int version = oldVersion; + + // /data/user + char *user_data_dir = build_string2(android_data_dir.path, SECONDARY_USER_PREFIX); + // /data/data + char *legacy_data_dir = build_string2(android_data_dir.path, PRIMARY_USER_PREFIX); + // /data/user/0 + char *primary_data_dir = build_string3(android_data_dir.path, SECONDARY_USER_PREFIX, "0"); + if (!user_data_dir || !legacy_data_dir || !primary_data_dir) { + goto fail; + } + + // Make the /data/user directory if necessary + if (access(user_data_dir, R_OK) < 0) { + if (mkdir(user_data_dir, 0711) < 0) { + goto fail; + } + if (chown(user_data_dir, AID_SYSTEM, AID_SYSTEM) < 0) { + goto fail; + } + if (chmod(user_data_dir, 0711) < 0) { + goto fail; + } + } + // Make the /data/user/0 symlink to /data/data if necessary + if (access(primary_data_dir, R_OK) < 0) { + if (symlink(legacy_data_dir, primary_data_dir)) { + goto fail; + } + } + + if (version == 0) { + // Introducing multi-user, so migrate /data/media contents into /data/media/0 + ALOGD("Upgrading /data/media for multi-user"); + + // Ensure /data/media + if (fs_prepare_dir(android_media_dir.path, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) { + goto fail; + } + + // /data/media.tmp + char media_tmp_dir[PATH_MAX]; + snprintf(media_tmp_dir, PATH_MAX, "%smedia.tmp", android_data_dir.path); + + // Only copy when upgrade not already in progress + if (access(media_tmp_dir, F_OK) == -1) { + if (rename(android_media_dir.path, media_tmp_dir) == -1) { + ALOGE("Failed to move legacy media path: %s", strerror(errno)); + goto fail; + } + } + + // Create /data/media again + if (fs_prepare_dir(android_media_dir.path, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) { + goto fail; + } + + if (selinux_android_restorecon(android_media_dir.path, 0)) { + goto fail; + } + + // /data/media/0 + char owner_media_dir[PATH_MAX]; + snprintf(owner_media_dir, PATH_MAX, "%s0", android_media_dir.path); + + // Move any owner data into place + if (access(media_tmp_dir, F_OK) == 0) { + if (rename(media_tmp_dir, owner_media_dir) == -1) { + ALOGE("Failed to move owner media path: %s", strerror(errno)); + goto fail; + } + } + + // Ensure media directories for any existing users + DIR *dir; + struct dirent *dirent; + char user_media_dir[PATH_MAX]; + + dir = opendir(user_data_dir); + if (dir != NULL) { + while ((dirent = readdir(dir))) { + if (dirent->d_type == DT_DIR) { + const char *name = dirent->d_name; + + // skip "." and ".." + if (name[0] == '.') { + if (name[1] == 0) continue; + if ((name[1] == '.') && (name[2] == 0)) continue; + } + + // /data/media/ + snprintf(user_media_dir, PATH_MAX, "%s%s", android_media_dir.path, name); + if (fs_prepare_dir(user_media_dir, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) { + goto fail; + } + } + } + closedir(dir); + } + + version = 1; + } + + // /data/media/obb + char media_obb_dir[PATH_MAX]; + snprintf(media_obb_dir, PATH_MAX, "%sobb", android_media_dir.path); + + if (version == 1) { + // Introducing /data/media/obb for sharing OBB across users; migrate + // any existing OBB files from owner. + ALOGD("Upgrading to shared /data/media/obb"); + + // /data/media/0/Android/obb + char owner_obb_path[PATH_MAX]; + snprintf(owner_obb_path, PATH_MAX, "%s0/Android/obb", android_media_dir.path); + + // Only move if target doesn't already exist + if (access(media_obb_dir, F_OK) != 0 && access(owner_obb_path, F_OK) == 0) { + if (rename(owner_obb_path, media_obb_dir) == -1) { + ALOGE("Failed to move OBB from owner: %s", strerror(errno)); + goto fail; + } + } + + version = 2; + } + + if (ensure_media_user_dirs(0) == -1) { + ALOGE("Failed to setup media for user 0"); + goto fail; + } + if (fs_prepare_dir(media_obb_dir, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) { + goto fail; + } + + if (ensure_config_user_dirs(0) == -1) { + ALOGE("Failed to setup misc for user 0"); + goto fail; + } + + if (version == 2) { + ALOGD("Upgrading to /data/misc/user directories"); + + char misc_dir[PATH_MAX]; + snprintf(misc_dir, PATH_MAX, "%smisc", android_data_dir.path); + + char keychain_added_dir[PATH_MAX]; + snprintf(keychain_added_dir, PATH_MAX, "%s/keychain/cacerts-added", misc_dir); + + char keychain_removed_dir[PATH_MAX]; + snprintf(keychain_removed_dir, PATH_MAX, "%s/keychain/cacerts-removed", misc_dir); + + DIR *dir; + struct dirent *dirent; + dir = opendir(user_data_dir); + if (dir != NULL) { + while ((dirent = readdir(dir))) { + const char *name = dirent->d_name; + + // skip "." and ".." + if (name[0] == '.') { + if (name[1] == 0) continue; + if ((name[1] == '.') && (name[2] == 0)) continue; + } + + uint32_t user_id = atoi(name); + + // /data/misc/user/ + if (ensure_config_user_dirs(user_id) == -1) { + goto fail; + } + + char misc_added_dir[PATH_MAX]; + snprintf(misc_added_dir, PATH_MAX, "%s/user/%s/cacerts-added", misc_dir, name); + + char misc_removed_dir[PATH_MAX]; + snprintf(misc_removed_dir, PATH_MAX, "%s/user/%s/cacerts-removed", misc_dir, name); + + uid_t uid = multiuser_get_uid(user_id, AID_SYSTEM); + gid_t gid = uid; + if (access(keychain_added_dir, F_OK) == 0) { + if (copy_dir_files(keychain_added_dir, misc_added_dir, uid, gid) != 0) { + ALOGE("Some files failed to copy"); + } + } + if (access(keychain_removed_dir, F_OK) == 0) { + if (copy_dir_files(keychain_removed_dir, misc_removed_dir, uid, gid) != 0) { + ALOGE("Some files failed to copy"); + } + } + } + closedir(dir); + + if (access(keychain_added_dir, F_OK) == 0) { + delete_dir_contents(keychain_added_dir, 1, 0); + } + if (access(keychain_removed_dir, F_OK) == 0) { + delete_dir_contents(keychain_removed_dir, 1, 0); + } + } + + version = 3; + } + + // Persist layout version if changed + if (version != oldVersion) { + if (fs_write_atomic_int(version_path, version) == -1) { + ALOGE("Failed to save version to %s: %s", version_path, strerror(errno)); + goto fail; + } + } + + // Success! + res = 0; + +fail: + free(user_data_dir); + free(legacy_data_dir); + free(primary_data_dir); + return res; +} + +static void drop_privileges() { + if (prctl(PR_SET_KEEPCAPS, 1) < 0) { + ALOGE("prctl(PR_SET_KEEPCAPS) failed: %s\n", strerror(errno)); + exit(1); + } + + if (setgid(AID_INSTALL) < 0) { + ALOGE("setgid() can't drop privileges; exiting.\n"); + exit(1); + } + + if (setuid(AID_INSTALL) < 0) { + ALOGE("setuid() can't drop privileges; exiting.\n"); + exit(1); + } + + struct __user_cap_header_struct capheader; + struct __user_cap_data_struct capdata[2]; + memset(&capheader, 0, sizeof(capheader)); + memset(&capdata, 0, sizeof(capdata)); + capheader.version = _LINUX_CAPABILITY_VERSION_3; + capheader.pid = 0; + + capdata[CAP_TO_INDEX(CAP_DAC_OVERRIDE)].permitted |= CAP_TO_MASK(CAP_DAC_OVERRIDE); + capdata[CAP_TO_INDEX(CAP_CHOWN)].permitted |= CAP_TO_MASK(CAP_CHOWN); + capdata[CAP_TO_INDEX(CAP_SETUID)].permitted |= CAP_TO_MASK(CAP_SETUID); + capdata[CAP_TO_INDEX(CAP_SETGID)].permitted |= CAP_TO_MASK(CAP_SETGID); + capdata[CAP_TO_INDEX(CAP_FOWNER)].permitted |= CAP_TO_MASK(CAP_FOWNER); + + capdata[0].effective = capdata[0].permitted; + capdata[1].effective = capdata[1].permitted; + capdata[0].inheritable = 0; + capdata[1].inheritable = 0; + + if (capset(&capheader, &capdata[0]) < 0) { + ALOGE("capset failed: %s\n", strerror(errno)); + exit(1); + } +} + +static int log_callback(int type, const char *fmt, ...) { + va_list ap; + int priority; + + switch (type) { + case SELINUX_WARNING: + priority = ANDROID_LOG_WARN; + break; + case SELINUX_INFO: + priority = ANDROID_LOG_INFO; + break; + default: + priority = ANDROID_LOG_ERROR; + break; + } + va_start(ap, fmt); + LOG_PRI_VA(priority, "SELinux", fmt, ap); + va_end(ap); + return 0; +} + +int main(const int argc __unused, const char *argv[] __unused) { + char buf[BUFFER_MAX]; + struct sockaddr addr; + socklen_t alen; + int lsocket, s; + int selinux_enabled = (is_selinux_enabled() > 0); + + ALOGI("installd firing up\n"); + + union selinux_callback cb; + cb.func_log = log_callback; + selinux_set_callback(SELINUX_CB_LOG, cb); + + if (initialize_globals() < 0) { + ALOGE("Could not initialize globals; exiting.\n"); + exit(1); + } + + if (initialize_directories() < 0) { + ALOGE("Could not create directories; exiting.\n"); + exit(1); + } + + if (selinux_enabled && selinux_status_open(true) < 0) { + ALOGE("Could not open selinux status; exiting.\n"); + exit(1); + } + + drop_privileges(); + + lsocket = android_get_control_socket(SOCKET_PATH); + if (lsocket < 0) { + ALOGE("Failed to get socket from environment: %s\n", strerror(errno)); + exit(1); + } + if (listen(lsocket, 5)) { + ALOGE("Listen on socket failed: %s\n", strerror(errno)); + exit(1); + } + fcntl(lsocket, F_SETFD, FD_CLOEXEC); + + for (;;) { + alen = sizeof(addr); + s = accept(lsocket, &addr, &alen); + if (s < 0) { + ALOGE("Accept failed: %s\n", strerror(errno)); + continue; + } + fcntl(s, F_SETFD, FD_CLOEXEC); + + ALOGI("new connection\n"); + for (;;) { + unsigned short count; + if (readx(s, &count, sizeof(count))) { + ALOGE("failed to read size\n"); + break; + } + if ((count < 1) || (count >= BUFFER_MAX)) { + ALOGE("invalid size %d\n", count); + break; + } + if (readx(s, buf, count)) { + ALOGE("failed to read command\n"); + break; + } + buf[count] = 0; + if (selinux_enabled && selinux_status_updated() > 0) { + selinux_android_seapp_context_reload(); + } + if (execute(s, buf)) break; + } + ALOGI("closing connection\n"); + close(s); + } + + return 0; +} diff --git a/cmds/installd/installd.h b/cmds/installd/installd.h index 7c52b78..0ebc0ba 100644 --- a/cmds/installd/installd.h +++ b/cmds/installd/installd.h @@ -192,8 +192,8 @@ int validate_apk_path(const char *path); int append_and_increment(char** dst, const char* src, size_t* dst_size); -char *build_string2(char *s1, char *s2); -char *build_string3(char *s1, char *s2, char *s3); +char *build_string2(const char *s1, const char *s2); +char *build_string3(const char *s1, const char *s2, const char *s3); int ensure_dir(const char* path, mode_t mode, uid_t uid, gid_t gid); int ensure_media_user_dirs(userid_t userid); @@ -227,7 +227,7 @@ int mark_boot_complete(const char *instruction_set); int movefiles(); int linklib(const char* target, const char* source, int userId); int idmap(const char *target_path, const char *overlay_path, uid_t uid); -int restorecon_data(); +int restorecon_data(const char* pkgName, const char* seinfo, uid_t uid); int create_oat_dir(const char* oat_dir, const char *instruction_set); int rm_package_dir(const char* apk_path); int calculate_oat_file_path(char path[PKG_PATH_MAX], const char *oat_dir, const char *apk_path, diff --git a/cmds/installd/tests/installd_utils_test.cpp b/cmds/installd/tests/installd_utils_test.cpp index 94e4792..68d150b 100644 --- a/cmds/installd/tests/installd_utils_test.cpp +++ b/cmds/installd/tests/installd_utils_test.cpp @@ -17,19 +17,18 @@ #include #include -#define LOG_TAG "utils_test" -#include - #include -extern "C" { #include "installd.h" -} + +#undef LOG_TAG +#define LOG_TAG "utils_test" #define TEST_DATA_DIR "/data/" #define TEST_APP_DIR "/data/app/" #define TEST_APP_PRIVATE_DIR "/data/app-private/" #define TEST_ASEC_DIR "/mnt/asec/" +#define TEST_EXPAND_DIR "/mnt/expand/" #define TEST_SYSTEM_DIR1 "/system/app/" #define TEST_SYSTEM_DIR2 "/vendor/app/" @@ -49,25 +48,28 @@ namespace android { class UtilsTest : public testing::Test { protected: virtual void SetUp() { - android_app_dir.path = TEST_APP_DIR; + android_app_dir.path = (char*) TEST_APP_DIR; android_app_dir.len = strlen(TEST_APP_DIR); - android_app_private_dir.path = TEST_APP_PRIVATE_DIR; + android_app_private_dir.path = (char*) TEST_APP_PRIVATE_DIR; android_app_private_dir.len = strlen(TEST_APP_PRIVATE_DIR); - android_data_dir.path = TEST_DATA_DIR; + android_data_dir.path = (char*) TEST_DATA_DIR; android_data_dir.len = strlen(TEST_DATA_DIR); - android_asec_dir.path = TEST_ASEC_DIR; + android_asec_dir.path = (char*) TEST_ASEC_DIR; android_asec_dir.len = strlen(TEST_ASEC_DIR); + android_mnt_expand_dir.path = (char*) TEST_EXPAND_DIR; + android_mnt_expand_dir.len = strlen(TEST_EXPAND_DIR); + android_system_dirs.count = 2; android_system_dirs.dirs = (dir_rec_t*) calloc(android_system_dirs.count, sizeof(dir_rec_t)); - android_system_dirs.dirs[0].path = TEST_SYSTEM_DIR1; + android_system_dirs.dirs[0].path = (char*) TEST_SYSTEM_DIR1; android_system_dirs.dirs[0].len = strlen(TEST_SYSTEM_DIR1); - android_system_dirs.dirs[1].path = TEST_SYSTEM_DIR2; + android_system_dirs.dirs[1].path = (char*) TEST_SYSTEM_DIR2; android_system_dirs.dirs[1].len = strlen(TEST_SYSTEM_DIR2); } @@ -373,7 +375,7 @@ TEST_F(UtilsTest, CreatePkgPathInDir_ProtectedDir) { char path[PKG_PATH_MAX]; dir_rec_t dir; - dir.path = "/data/app-private/"; + dir.path = (char*) "/data/app-private/"; dir.len = strlen(dir.path); EXPECT_EQ(0, create_pkg_path_in_dir(path, &dir, "com.example.package", ".apk")) @@ -432,7 +434,7 @@ TEST_F(UtilsTest, CopyAndAppend_Normal) { dir_rec_t dst; dir_rec_t src; - src.path = "/data/"; + src.path = (char*) "/data/"; src.len = strlen(src.path); EXPECT_EQ(0, copy_and_append(&dst, &src, "app/")) diff --git a/cmds/installd/utils.c b/cmds/installd/utils.c deleted file mode 100644 index 54f90ab..0000000 --- a/cmds/installd/utils.c +++ /dev/null @@ -1,1171 +0,0 @@ -/* -** Copyright 2008, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - -#include "installd.h" - -#define CACHE_NOISY(x) //x - -int create_pkg_path_in_dir(char path[PKG_PATH_MAX], - const dir_rec_t* dir, - const char* pkgname, - const char* postfix) -{ - const size_t postfix_len = strlen(postfix); - - const size_t pkgname_len = strlen(pkgname); - if (pkgname_len > PKG_NAME_MAX) { - return -1; - } - - if (is_valid_package_name(pkgname) < 0) { - return -1; - } - - if ((pkgname_len + dir->len + postfix_len) >= PKG_PATH_MAX) { - return -1; - } - - char *dst = path; - size_t dst_size = PKG_PATH_MAX; - - if (append_and_increment(&dst, dir->path, &dst_size) < 0 - || append_and_increment(&dst, pkgname, &dst_size) < 0 - || append_and_increment(&dst, postfix, &dst_size) < 0) { - ALOGE("Error building APK path"); - return -1; - } - - return 0; -} - -/** - * Create the package path name for a given package name with a postfix for - * a certain userid. Returns 0 on success, and -1 on failure. - */ -int create_pkg_path(char path[PKG_PATH_MAX], - const char *pkgname, - const char *postfix, - userid_t userid) -{ - size_t userid_len; - char* userid_prefix; - if (userid == 0) { - userid_prefix = PRIMARY_USER_PREFIX; - userid_len = 0; - } else { - userid_prefix = SECONDARY_USER_PREFIX; - userid_len = snprintf(NULL, 0, "%d", userid); - } - - const size_t prefix_len = android_data_dir.len + strlen(userid_prefix) - + userid_len + 1 /*slash*/; - char prefix[prefix_len + 1]; - - char *dst = prefix; - size_t dst_size = sizeof(prefix); - - if (append_and_increment(&dst, android_data_dir.path, &dst_size) < 0 - || append_and_increment(&dst, userid_prefix, &dst_size) < 0) { - ALOGE("Error building prefix for APK path"); - return -1; - } - - if (userid != 0) { - int ret = snprintf(dst, dst_size, "%d/", userid); - if (ret < 0 || (size_t) ret != userid_len + 1) { - ALOGW("Error appending UID to APK path"); - return -1; - } - } - - dir_rec_t dir; - dir.path = prefix; - dir.len = prefix_len; - - return create_pkg_path_in_dir(path, &dir, pkgname, postfix); -} - -/** - * Create the path name for user data for a certain userid. - * Returns 0 on success, and -1 on failure. - */ -int create_user_path(char path[PKG_PATH_MAX], - userid_t userid) -{ - size_t userid_len; - char* userid_prefix; - if (userid == 0) { - userid_prefix = PRIMARY_USER_PREFIX; - userid_len = 0; - } else { - userid_prefix = SECONDARY_USER_PREFIX; - userid_len = snprintf(NULL, 0, "%d/", userid); - } - - char *dst = path; - size_t dst_size = PKG_PATH_MAX; - - if (append_and_increment(&dst, android_data_dir.path, &dst_size) < 0 - || append_and_increment(&dst, userid_prefix, &dst_size) < 0) { - ALOGE("Error building prefix for user path"); - return -1; - } - - if (userid != 0) { - if (dst_size < userid_len + 1) { - ALOGE("Error building user path"); - return -1; - } - int ret = snprintf(dst, dst_size, "%d/", userid); - if (ret < 0 || (size_t) ret != userid_len) { - ALOGE("Error appending userid to path"); - return -1; - } - } - return 0; -} - -/** - * Create the path name for media for a certain userid. - * Returns 0 on success, and -1 on failure. - */ -int create_user_media_path(char path[PATH_MAX], userid_t userid) { - if (snprintf(path, PATH_MAX, "%s%d", android_media_dir.path, userid) > PATH_MAX) { - return -1; - } - return 0; -} - -/** - * Create the path name for config for a certain userid. - * Returns 0 on success, and -1 on failure. - */ -int create_user_config_path(char path[PATH_MAX], userid_t userid) { - if (snprintf(path, PATH_MAX, "%s%d", "/data/misc/user/", userid) > PATH_MAX) { - return -1; - } - return 0; -} - -int create_move_path(char path[PKG_PATH_MAX], - const char* pkgname, - const char* leaf, - userid_t userid __unused) -{ - if ((android_data_dir.len + strlen(PRIMARY_USER_PREFIX) + strlen(pkgname) + strlen(leaf) + 1) - >= PKG_PATH_MAX) { - return -1; - } - - sprintf(path, "%s%s%s/%s", android_data_dir.path, PRIMARY_USER_PREFIX, pkgname, leaf); - return 0; -} - -/** - * Checks whether the package name is valid. Returns -1 on error and - * 0 on success. - */ -int is_valid_package_name(const char* pkgname) { - const char *x = pkgname; - int alpha = -1; - - while (*x) { - if (isalnum(*x) || (*x == '_')) { - /* alphanumeric or underscore are fine */ - } else if (*x == '.') { - if ((x == pkgname) || (x[1] == '.') || (x[1] == 0)) { - /* periods must not be first, last, or doubled */ - ALOGE("invalid package name '%s'\n", pkgname); - return -1; - } - } else if (*x == '-') { - /* Suffix -X is fine to let versioning of packages. - But whatever follows should be alphanumeric.*/ - alpha = 1; - } else { - /* anything not A-Z, a-z, 0-9, _, or . is invalid */ - ALOGE("invalid package name '%s'\n", pkgname); - return -1; - } - - x++; - } - - if (alpha == 1) { - // Skip current character - x++; - while (*x) { - if (!isalnum(*x)) { - ALOGE("invalid package name '%s' should include only numbers after -\n", pkgname); - return -1; - } - x++; - } - } - - return 0; -} - -static int _delete_dir_contents(DIR *d, - int (*exclusion_predicate)(const char *name, const int is_dir)) -{ - int result = 0; - struct dirent *de; - int dfd; - - dfd = dirfd(d); - - if (dfd < 0) return -1; - - while ((de = readdir(d))) { - const char *name = de->d_name; - - /* check using the exclusion predicate, if provided */ - if (exclusion_predicate && exclusion_predicate(name, (de->d_type == DT_DIR))) { - continue; - } - - if (de->d_type == DT_DIR) { - int subfd; - DIR *subdir; - - /* 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) { - ALOGE("Couldn't openat %s: %s\n", name, strerror(errno)); - result = -1; - continue; - } - subdir = fdopendir(subfd); - if (subdir == NULL) { - ALOGE("Couldn't fdopendir %s: %s\n", name, strerror(errno)); - close(subfd); - result = -1; - continue; - } - if (_delete_dir_contents(subdir, exclusion_predicate)) { - result = -1; - } - closedir(subdir); - if (unlinkat(dfd, name, AT_REMOVEDIR) < 0) { - ALOGE("Couldn't unlinkat %s: %s\n", name, strerror(errno)); - result = -1; - } - } else { - if (unlinkat(dfd, name, 0) < 0) { - ALOGE("Couldn't unlinkat %s: %s\n", name, strerror(errno)); - result = -1; - } - } - } - - return result; -} - -int delete_dir_contents(const char *pathname, - int also_delete_dir, - int (*exclusion_predicate)(const char*, const int)) -{ - int res = 0; - DIR *d; - - d = opendir(pathname); - if (d == NULL) { - ALOGE("Couldn't opendir %s: %s\n", pathname, strerror(errno)); - return -errno; - } - res = _delete_dir_contents(d, exclusion_predicate); - closedir(d); - if (also_delete_dir) { - if (rmdir(pathname)) { - ALOGE("Couldn't rmdir %s: %s\n", pathname, strerror(errno)); - res = -1; - } - } - return res; -} - -int delete_dir_contents_fd(int dfd, const char *name) -{ - int fd, res; - DIR *d; - - fd = openat(dfd, name, O_RDONLY | O_DIRECTORY); - if (fd < 0) { - ALOGE("Couldn't openat %s: %s\n", name, strerror(errno)); - return -1; - } - d = fdopendir(fd); - if (d == NULL) { - ALOGE("Couldn't fdopendir %s: %s\n", name, strerror(errno)); - close(fd); - return -1; - } - res = _delete_dir_contents(d, 0); - closedir(d); - return res; -} - -static int _copy_owner_permissions(int srcfd, int dstfd) -{ - struct stat st; - if (fstat(srcfd, &st) != 0) { - return -1; - } - if (fchmod(dstfd, st.st_mode) != 0) { - return -1; - } - return 0; -} - -static int _copy_dir_files(int sdfd, int ddfd, uid_t owner, gid_t group) -{ - int result = 0; - if (_copy_owner_permissions(sdfd, ddfd) != 0) { - ALOGE("_copy_dir_files failed to copy dir permissions\n"); - } - if (fchown(ddfd, owner, group) != 0) { - ALOGE("_copy_dir_files failed to change dir owner\n"); - } - - DIR *ds = fdopendir(sdfd); - if (ds == NULL) { - ALOGE("Couldn't fdopendir: %s\n", strerror(errno)); - return -1; - } - struct dirent *de; - while ((de = readdir(ds))) { - if (de->d_type != DT_REG) { - continue; - } - - const char *name = de->d_name; - int fsfd = openat(sdfd, name, O_RDONLY | O_NOFOLLOW | O_CLOEXEC); - int fdfd = openat(ddfd, name, O_WRONLY | O_NOFOLLOW | O_CLOEXEC | O_CREAT, 0600); - if (fsfd == -1 || fdfd == -1) { - ALOGW("Couldn't copy %s: %s\n", name, strerror(errno)); - } else { - if (_copy_owner_permissions(fsfd, fdfd) != 0) { - ALOGE("Failed to change file permissions\n"); - } - if (fchown(fdfd, owner, group) != 0) { - ALOGE("Failed to change file owner\n"); - } - - char buf[8192]; - ssize_t size; - while ((size = read(fsfd, buf, sizeof(buf))) > 0) { - write(fdfd, buf, size); - } - if (size < 0) { - ALOGW("Couldn't copy %s: %s\n", name, strerror(errno)); - result = -1; - } - } - close(fdfd); - close(fsfd); - } - - return result; -} - -int copy_dir_files(const char *srcname, - const char *dstname, - uid_t owner, - uid_t group) -{ - int res = 0; - DIR *ds = NULL; - DIR *dd = NULL; - - ds = opendir(srcname); - if (ds == NULL) { - ALOGE("Couldn't opendir %s: %s\n", srcname, strerror(errno)); - return -errno; - } - - mkdir(dstname, 0600); - dd = opendir(dstname); - if (dd == NULL) { - ALOGE("Couldn't opendir %s: %s\n", dstname, strerror(errno)); - closedir(ds); - return -errno; - } - - int sdfd = dirfd(ds); - int ddfd = dirfd(dd); - if (sdfd != -1 && ddfd != -1) { - res = _copy_dir_files(sdfd, ddfd, owner, group); - } else { - res = -errno; - } - closedir(dd); - closedir(ds); - return res; -} - -int lookup_media_dir(char basepath[PATH_MAX], const char *dir) -{ - DIR *d; - struct dirent *de; - struct stat s; - char* dirpos = basepath + strlen(basepath); - - if ((*(dirpos-1)) != '/') { - *dirpos = '/'; - dirpos++; - } - - CACHE_NOISY(ALOGI("Looking up %s in %s\n", dir, basepath)); - // Verify the path won't extend beyond our buffer, to avoid - // repeated checking later. - if ((dirpos-basepath+strlen(dir)) >= (PATH_MAX-1)) { - ALOGW("Path exceeds limit: %s%s", basepath, dir); - return -1; - } - - // First, can we find this directory with the case that is given? - strcpy(dirpos, dir); - if (stat(basepath, &s) >= 0) { - CACHE_NOISY(ALOGI("Found direct: %s\n", basepath)); - return 0; - } - - // Not found with that case... search through all entries to find - // one that matches regardless of case. - *dirpos = 0; - - d = opendir(basepath); - if (d == NULL) { - return -1; - } - - while ((de = readdir(d))) { - if (strcasecmp(de->d_name, dir) == 0) { - strcpy(dirpos, de->d_name); - closedir(d); - CACHE_NOISY(ALOGI("Found search: %s\n", basepath)); - return 0; - } - } - - ALOGW("Couldn't find %s in %s", dir, basepath); - closedir(d); - return -1; -} - -int64_t data_disk_free() -{ - struct statfs sfs; - if (statfs(android_data_dir.path, &sfs) == 0) { - return sfs.f_bavail * sfs.f_bsize; - } else { - ALOGE("Couldn't statfs %s: %s\n", android_data_dir.path, strerror(errno)); - return -1; - } -} - -cache_t* start_cache_collection() -{ - cache_t* cache = (cache_t*)calloc(1, sizeof(cache_t)); - return cache; -} - -#define CACHE_BLOCK_SIZE (512*1024) - -static void* _cache_malloc(cache_t* cache, size_t len) -{ - len = (len+3)&~3; - if (len > (CACHE_BLOCK_SIZE/2)) { - // It doesn't make sense to try to put this allocation into one - // of our blocks, because it is so big. Instead, make a new dedicated - // block for it. - int8_t* res = (int8_t*)malloc(len+sizeof(void*)); - if (res == NULL) { - return NULL; - } - CACHE_NOISY(ALOGI("Allocated large cache mem block: %p size %d", res, len)); - // Link it into our list of blocks, not disrupting the current one. - if (cache->memBlocks == NULL) { - *(void**)res = NULL; - cache->memBlocks = res; - } else { - *(void**)res = *(void**)cache->memBlocks; - *(void**)cache->memBlocks = res; - } - return res + sizeof(void*); - } - int8_t* res = cache->curMemBlockAvail; - int8_t* nextPos = res + len; - if (cache->memBlocks == NULL || nextPos > cache->curMemBlockEnd) { - int8_t* newBlock = malloc(CACHE_BLOCK_SIZE); - if (newBlock == NULL) { - return NULL; - } - CACHE_NOISY(ALOGI("Allocated new cache mem block: %p", newBlock)); - *(void**)newBlock = cache->memBlocks; - cache->memBlocks = newBlock; - res = cache->curMemBlockAvail = newBlock + sizeof(void*); - cache->curMemBlockEnd = newBlock + CACHE_BLOCK_SIZE; - nextPos = res + len; - } - CACHE_NOISY(ALOGI("cache_malloc: ret %p size %d, block=%p, nextPos=%p", - res, len, cache->memBlocks, nextPos)); - cache->curMemBlockAvail = nextPos; - return res; -} - -static void* _cache_realloc(cache_t* cache, void* cur, size_t origLen, size_t len) -{ - // This isn't really a realloc, but it is good enough for our purposes here. - void* alloc = _cache_malloc(cache, len); - if (alloc != NULL && cur != NULL) { - memcpy(alloc, cur, origLen < len ? origLen : len); - } - return alloc; -} - -static void _inc_num_cache_collected(cache_t* cache) -{ - cache->numCollected++; - if ((cache->numCollected%20000) == 0) { - ALOGI("Collected cache so far: %zd directories, %zd files", - cache->numDirs, cache->numFiles); - } -} - -static cache_dir_t* _add_cache_dir_t(cache_t* cache, cache_dir_t* parent, const char *name) -{ - size_t nameLen = strlen(name); - cache_dir_t* dir = (cache_dir_t*)_cache_malloc(cache, sizeof(cache_dir_t)+nameLen+1); - if (dir != NULL) { - dir->parent = parent; - dir->childCount = 0; - dir->hiddenCount = 0; - dir->deleted = 0; - strcpy(dir->name, name); - if (cache->numDirs >= cache->availDirs) { - size_t newAvail = cache->availDirs < 1000 ? 1000 : cache->availDirs*2; - cache_dir_t** newDirs = (cache_dir_t**)_cache_realloc(cache, cache->dirs, - cache->availDirs*sizeof(cache_dir_t*), newAvail*sizeof(cache_dir_t*)); - if (newDirs == NULL) { - ALOGE("Failure growing cache dirs array for %s\n", name); - return NULL; - } - cache->availDirs = newAvail; - cache->dirs = newDirs; - } - cache->dirs[cache->numDirs] = dir; - cache->numDirs++; - if (parent != NULL) { - parent->childCount++; - } - _inc_num_cache_collected(cache); - } else { - ALOGE("Failure allocating cache_dir_t for %s\n", name); - } - return dir; -} - -static cache_file_t* _add_cache_file_t(cache_t* cache, cache_dir_t* dir, time_t modTime, - const char *name) -{ - size_t nameLen = strlen(name); - cache_file_t* file = (cache_file_t*)_cache_malloc(cache, sizeof(cache_file_t)+nameLen+1); - if (file != NULL) { - file->dir = dir; - file->modTime = modTime; - strcpy(file->name, name); - if (cache->numFiles >= cache->availFiles) { - size_t newAvail = cache->availFiles < 1000 ? 1000 : cache->availFiles*2; - cache_file_t** newFiles = (cache_file_t**)_cache_realloc(cache, cache->files, - cache->availFiles*sizeof(cache_file_t*), newAvail*sizeof(cache_file_t*)); - if (newFiles == NULL) { - ALOGE("Failure growing cache file array for %s\n", name); - return NULL; - } - cache->availFiles = newAvail; - cache->files = newFiles; - } - CACHE_NOISY(ALOGI("Setting file %p at position %d in array %p", file, - cache->numFiles, cache->files)); - cache->files[cache->numFiles] = file; - cache->numFiles++; - dir->childCount++; - _inc_num_cache_collected(cache); - } else { - ALOGE("Failure allocating cache_file_t for %s\n", name); - } - return file; -} - -static int _add_cache_files(cache_t *cache, cache_dir_t *parentDir, const char *dirName, - DIR* dir, char *pathBase, char *pathPos, size_t pathAvailLen) -{ - struct dirent *de; - cache_dir_t* cacheDir = NULL; - int dfd; - - CACHE_NOISY(ALOGI("_add_cache_files: parent=%p dirName=%s dir=%p pathBase=%s", - parentDir, dirName, dir, pathBase)); - - dfd = dirfd(dir); - - if (dfd < 0) return 0; - - // Sub-directories always get added to the data structure, so if they - // are empty we will know about them to delete them later. - cacheDir = _add_cache_dir_t(cache, parentDir, dirName); - - while ((de = readdir(dir))) { - const char *name = de->d_name; - - if (de->d_type == DT_DIR) { - int subfd; - DIR *subdir; - - /* 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) { - ALOGE("Couldn't openat %s: %s\n", name, strerror(errno)); - continue; - } - subdir = fdopendir(subfd); - if (subdir == NULL) { - ALOGE("Couldn't fdopendir %s: %s\n", name, strerror(errno)); - close(subfd); - continue; - } - if (cacheDir == NULL) { - cacheDir = _add_cache_dir_t(cache, parentDir, dirName); - } - if (cacheDir != NULL) { - // Update pathBase for the new path... this may change dirName - // if that is also pointing to the path, but we are done with it - // now. - size_t finallen = snprintf(pathPos, pathAvailLen, "/%s", name); - CACHE_NOISY(ALOGI("Collecting dir %s\n", pathBase)); - if (finallen < pathAvailLen) { - _add_cache_files(cache, cacheDir, name, subdir, pathBase, - pathPos+finallen, pathAvailLen-finallen); - } else { - // Whoops, the final path is too long! We'll just delete - // this directory. - ALOGW("Cache dir %s truncated in path %s; deleting dir\n", - name, pathBase); - _delete_dir_contents(subdir, NULL); - if (unlinkat(dfd, name, AT_REMOVEDIR) < 0) { - ALOGE("Couldn't unlinkat %s: %s\n", name, strerror(errno)); - } - } - } - closedir(subdir); - } else if (de->d_type == DT_REG) { - // Skip files that start with '.'; they will be deleted if - // their entire directory is deleted. This allows for metadata - // like ".nomedia" to remain in the directory until the entire - // directory is deleted. - if (cacheDir == NULL) { - cacheDir = _add_cache_dir_t(cache, parentDir, dirName); - } - if (name[0] == '.') { - cacheDir->hiddenCount++; - continue; - } - if (cacheDir != NULL) { - // Build final full path for file... this may change dirName - // if that is also pointing to the path, but we are done with it - // now. - size_t finallen = snprintf(pathPos, pathAvailLen, "/%s", name); - CACHE_NOISY(ALOGI("Collecting file %s\n", pathBase)); - if (finallen < pathAvailLen) { - struct stat s; - if (stat(pathBase, &s) >= 0) { - _add_cache_file_t(cache, cacheDir, s.st_mtime, name); - } else { - ALOGW("Unable to stat cache file %s; deleting\n", pathBase); - if (unlink(pathBase) < 0) { - ALOGE("Couldn't unlink %s: %s\n", pathBase, strerror(errno)); - } - } - } else { - // Whoops, the final path is too long! We'll just delete - // this file. - ALOGW("Cache file %s truncated in path %s; deleting\n", - name, pathBase); - if (unlinkat(dfd, name, 0) < 0) { - *pathPos = 0; - ALOGE("Couldn't unlinkat %s in %s: %s\n", name, pathBase, - strerror(errno)); - } - } - } - } else { - cacheDir->hiddenCount++; - } - } - return 0; -} - -void add_cache_files(cache_t* cache, const char *basepath, const char *cachedir) -{ - DIR *d; - struct dirent *de; - char dirname[PATH_MAX]; - - CACHE_NOISY(ALOGI("add_cache_files: base=%s cachedir=%s\n", basepath, cachedir)); - - d = opendir(basepath); - if (d == NULL) { - return; - } - - while ((de = readdir(d))) { - if (de->d_type == DT_DIR) { - DIR* subdir; - const char *name = de->d_name; - char* pathpos; - - /* always skip "." and ".." */ - if (name[0] == '.') { - if (name[1] == 0) continue; - if ((name[1] == '.') && (name[2] == 0)) continue; - } - - strcpy(dirname, basepath); - pathpos = dirname + strlen(dirname); - if ((*(pathpos-1)) != '/') { - *pathpos = '/'; - pathpos++; - *pathpos = 0; - } - if (cachedir != NULL) { - snprintf(pathpos, sizeof(dirname)-(pathpos-dirname), "%s/%s", name, cachedir); - } else { - snprintf(pathpos, sizeof(dirname)-(pathpos-dirname), "%s", name); - } - CACHE_NOISY(ALOGI("Adding cache files from dir: %s\n", dirname)); - subdir = opendir(dirname); - if (subdir != NULL) { - size_t dirnameLen = strlen(dirname); - _add_cache_files(cache, NULL, dirname, subdir, dirname, dirname+dirnameLen, - PATH_MAX - dirnameLen); - closedir(subdir); - } - } - } - - closedir(d); -} - -static char *create_dir_path(char path[PATH_MAX], cache_dir_t* dir) -{ - char *pos = path; - if (dir->parent != NULL) { - pos = create_dir_path(path, dir->parent); - } - // Note that we don't need to worry about going beyond the buffer, - // since when we were constructing the cache entries our maximum - // buffer size for full paths was PATH_MAX. - strcpy(pos, dir->name); - pos += strlen(pos); - *pos = '/'; - pos++; - *pos = 0; - return pos; -} - -static void delete_cache_dir(char path[PATH_MAX], cache_dir_t* dir) -{ - if (dir->parent != NULL) { - create_dir_path(path, dir); - ALOGI("DEL DIR %s\n", path); - if (dir->hiddenCount <= 0) { - if (rmdir(path)) { - ALOGE("Couldn't rmdir %s: %s\n", path, strerror(errno)); - return; - } - } else { - // The directory contains hidden files so we need to delete - // them along with the directory itself. - if (delete_dir_contents(path, 1, NULL)) { - return; - } - } - dir->parent->childCount--; - dir->deleted = 1; - if (dir->parent->childCount <= 0) { - delete_cache_dir(path, dir->parent); - } - } else if (dir->hiddenCount > 0) { - // This is a root directory, but it has hidden files. Get rid of - // all of those files, but not the directory itself. - create_dir_path(path, dir); - ALOGI("DEL CONTENTS %s\n", path); - delete_dir_contents(path, 0, NULL); - } -} - -static int cache_modtime_sort(const void *lhsP, const void *rhsP) -{ - const cache_file_t *lhs = *(const cache_file_t**)lhsP; - const cache_file_t *rhs = *(const cache_file_t**)rhsP; - return lhs->modTime < rhs->modTime ? -1 : (lhs->modTime > rhs->modTime ? 1 : 0); -} - -void clear_cache_files(cache_t* cache, int64_t free_size) -{ - size_t i; - int skip = 0; - char path[PATH_MAX]; - - ALOGI("Collected cache files: %zd directories, %zd files", - cache->numDirs, cache->numFiles); - - CACHE_NOISY(ALOGI("Sorting files...")); - qsort(cache->files, cache->numFiles, sizeof(cache_file_t*), - cache_modtime_sort); - - CACHE_NOISY(ALOGI("Cleaning empty directories...")); - for (i=cache->numDirs; i>0; i--) { - cache_dir_t* dir = cache->dirs[i-1]; - if (dir->childCount <= 0 && !dir->deleted) { - delete_cache_dir(path, dir); - } - } - - CACHE_NOISY(ALOGI("Trimming files...")); - for (i=0; inumFiles; i++) { - skip++; - if (skip > 10) { - if (data_disk_free() > free_size) { - return; - } - skip = 0; - } - cache_file_t* file = cache->files[i]; - strcpy(create_dir_path(path, file->dir), file->name); - ALOGI("DEL (mod %d) %s\n", (int)file->modTime, path); - if (unlink(path) < 0) { - ALOGE("Couldn't unlink %s: %s\n", path, strerror(errno)); - } - file->dir->childCount--; - if (file->dir->childCount <= 0) { - delete_cache_dir(path, file->dir); - } - } -} - -void finish_cache_collection(cache_t* cache) -{ - CACHE_NOISY(size_t i;) - - CACHE_NOISY(ALOGI("clear_cache_files: %d dirs, %d files\n", cache->numDirs, cache->numFiles)); - CACHE_NOISY( - for (i=0; inumDirs; i++) { - cache_dir_t* dir = cache->dirs[i]; - ALOGI("dir #%d: %p %s parent=%p\n", i, dir, dir->name, dir->parent); - }) - CACHE_NOISY( - for (i=0; inumFiles; i++) { - cache_file_t* file = cache->files[i]; - ALOGI("file #%d: %p %s time=%d dir=%p\n", i, file, file->name, - (int)file->modTime, file->dir); - }) - void* block = cache->memBlocks; - while (block != NULL) { - void* nextBlock = *(void**)block; - CACHE_NOISY(ALOGI("Freeing cache mem block: %p", block)); - free(block); - block = nextBlock; - } - free(cache); -} - -/** - * Validate that the path is valid in the context of the provided directory. - * The path is allowed to have at most one subdirectory and no indirections - * to top level directories (i.e. have ".."). - */ -static int validate_path(const dir_rec_t* dir, const char* path, int maxSubdirs) { - size_t dir_len = dir->len; - const char* subdir = strchr(path + dir_len, '/'); - - // Only allow the path to have at most one subdirectory. - if (subdir != NULL) { - ++subdir; - if ((--maxSubdirs == 0) && strchr(subdir, '/') != NULL) { - ALOGE("invalid apk path '%s' (subdir?)\n", path); - return -1; - } - } - - // Directories can't have a period directly after the directory markers to prevent "..". - if ((path[dir_len] == '.') || ((subdir != NULL) && (*subdir == '.'))) { - ALOGE("invalid apk path '%s' (trickery)\n", path); - return -1; - } - - return 0; -} - -/** - * Checks whether a path points to a system app (.apk file). Returns 0 - * if it is a system app or -1 if it is not. - */ -int validate_system_app_path(const char* path) { - size_t i; - - for (i = 0; i < android_system_dirs.count; i++) { - const size_t dir_len = android_system_dirs.dirs[i].len; - if (!strncmp(path, android_system_dirs.dirs[i].path, dir_len)) { - return validate_path(android_system_dirs.dirs + i, path, 1); - } - } - - return -1; -} - -/** - * Get the contents of a environment variable that contains a path. Caller - * owns the string that is inserted into the directory record. Returns - * 0 on success and -1 on error. - */ -int get_path_from_env(dir_rec_t* rec, const char* var) { - const char* path = getenv(var); - int ret = get_path_from_string(rec, path); - if (ret < 0) { - ALOGW("Problem finding value for environment variable %s\n", var); - } - return ret; -} - -/** - * Puts the string into the record as a directory. Appends '/' to the end - * of all paths. Caller owns the string that is inserted into the directory - * record. A null value will result in an error. - * - * Returns 0 on success and -1 on error. - */ -int get_path_from_string(dir_rec_t* rec, const char* path) { - if (path == NULL) { - return -1; - } else { - const size_t path_len = strlen(path); - if (path_len <= 0) { - return -1; - } - - // Make sure path is absolute. - if (path[0] != '/') { - return -1; - } - - if (path[path_len - 1] == '/') { - // Path ends with a forward slash. Make our own copy. - - rec->path = strdup(path); - if (rec->path == NULL) { - return -1; - } - - rec->len = path_len; - } else { - // Path does not end with a slash. Generate a new string. - char *dst; - - // Add space for slash and terminating null. - size_t dst_size = path_len + 2; - - rec->path = malloc(dst_size); - if (rec->path == NULL) { - return -1; - } - - dst = rec->path; - - if (append_and_increment(&dst, path, &dst_size) < 0 - || append_and_increment(&dst, "/", &dst_size)) { - ALOGE("Error canonicalizing path"); - return -1; - } - - rec->len = dst - rec->path; - } - } - return 0; -} - -int copy_and_append(dir_rec_t* dst, const dir_rec_t* src, const char* suffix) { - dst->len = src->len + strlen(suffix); - const size_t dstSize = dst->len + 1; - dst->path = (char*) malloc(dstSize); - - if (dst->path == NULL - || snprintf(dst->path, dstSize, "%s%s", src->path, suffix) - != (ssize_t) dst->len) { - ALOGE("Could not allocate memory to hold appended path; aborting\n"); - return -1; - } - - return 0; -} - -/** - * Check whether path points to a valid path for an APK file. Only one level of - * subdirectory names is allowed. Returns -1 when an invalid path is encountered - * and 0 when a valid path is encountered. - */ -int validate_apk_path(const char *path) -{ - const dir_rec_t* dir = NULL; - int maxSubdirs = 1; - - if (!strncmp(path, android_app_dir.path, android_app_dir.len)) { - dir = &android_app_dir; - } else if (!strncmp(path, android_app_private_dir.path, android_app_private_dir.len)) { - dir = &android_app_private_dir; - } else if (!strncmp(path, android_asec_dir.path, android_asec_dir.len)) { - dir = &android_asec_dir; - } else if (!strncmp(path, android_mnt_expand_dir.path, android_mnt_expand_dir.len)) { - dir = &android_mnt_expand_dir; - maxSubdirs = 2; - } else { - return -1; - } - - return validate_path(dir, path, maxSubdirs); -} - -int append_and_increment(char** dst, const char* src, size_t* dst_size) { - ssize_t ret = strlcpy(*dst, src, *dst_size); - if (ret < 0 || (size_t) ret >= *dst_size) { - return -1; - } - *dst += ret; - *dst_size -= ret; - return 0; -} - -char *build_string2(char *s1, char *s2) { - if (s1 == NULL || s2 == NULL) return NULL; - - int len_s1 = strlen(s1); - int len_s2 = strlen(s2); - int len = len_s1 + len_s2 + 1; - char *result = malloc(len); - if (result == NULL) return NULL; - - strcpy(result, s1); - strcpy(result + len_s1, s2); - - return result; -} - -char *build_string3(char *s1, char *s2, char *s3) { - if (s1 == NULL || s2 == NULL || s3 == NULL) return NULL; - - int len_s1 = strlen(s1); - int len_s2 = strlen(s2); - int len_s3 = strlen(s3); - int len = len_s1 + len_s2 + len_s3 + 1; - char *result = malloc(len); - if (result == NULL) return NULL; - - strcpy(result, s1); - strcpy(result + len_s1, s2); - strcpy(result + len_s1 + len_s2, s3); - - return result; -} - -/* Ensure that /data/media directories are prepared for given user. */ -int ensure_media_user_dirs(userid_t userid) { - char media_user_path[PATH_MAX]; - - // Ensure /data/media/ exists - create_user_media_path(media_user_path, userid); - if (fs_prepare_dir(media_user_path, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) { - return -1; - } - - return 0; -} - -int ensure_config_user_dirs(userid_t userid) { - char config_user_path[PATH_MAX]; - - // writable by system, readable by any app within the same user - const int uid = multiuser_get_uid(userid, AID_SYSTEM); - const int gid = multiuser_get_uid(userid, AID_EVERYBODY); - - // Ensure /data/misc/user/ exists - create_user_config_path(config_user_path, userid); - if (fs_prepare_dir(config_user_path, 0750, uid, gid) == -1) { - return -1; - } - - return 0; -} - -int create_profile_file(const char *pkgname, gid_t gid) { - const char *profile_dir = DALVIK_CACHE_PREFIX "profiles"; - char profile_file[PKG_PATH_MAX]; - - snprintf(profile_file, sizeof(profile_file), "%s/%s", profile_dir, pkgname); - - // The 'system' user needs to be able to read the profile to determine if dex2oat - // needs to be run. This is done in dalvik.system.DexFile.isDexOptNeededInternal(). So - // we assign ownership to AID_SYSTEM and ensure it's not world-readable. - - int fd = open(profile_file, O_WRONLY | O_CREAT | O_NOFOLLOW | O_CLOEXEC, 0660); - - // Always set the uid/gid/permissions. The file could have been previously created - // with different permissions. - if (fd >= 0) { - if (fchown(fd, AID_SYSTEM, gid) < 0) { - ALOGE("cannot chown profile file '%s': %s\n", profile_file, strerror(errno)); - close(fd); - unlink(profile_file); - return -1; - } - - if (fchmod(fd, 0660) < 0) { - ALOGE("cannot chmod profile file '%s': %s\n", profile_file, strerror(errno)); - close(fd); - unlink(profile_file); - return -1; - } - close(fd); - } - return 0; -} - -void remove_profile_file(const char *pkgname) { - char profile_file[PKG_PATH_MAX]; - snprintf(profile_file, sizeof(profile_file), "%s/%s", DALVIK_CACHE_PREFIX "profiles", pkgname); - unlink(profile_file); -} diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp new file mode 100644 index 0000000..df2bbce --- /dev/null +++ b/cmds/installd/utils.cpp @@ -0,0 +1,1171 @@ +/* +** Copyright 2008, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "installd.h" + +#define CACHE_NOISY(x) //x + +int create_pkg_path_in_dir(char path[PKG_PATH_MAX], + const dir_rec_t* dir, + const char* pkgname, + const char* postfix) +{ + const size_t postfix_len = strlen(postfix); + + const size_t pkgname_len = strlen(pkgname); + if (pkgname_len > PKG_NAME_MAX) { + return -1; + } + + if (is_valid_package_name(pkgname) < 0) { + return -1; + } + + if ((pkgname_len + dir->len + postfix_len) >= PKG_PATH_MAX) { + return -1; + } + + char *dst = path; + size_t dst_size = PKG_PATH_MAX; + + if (append_and_increment(&dst, dir->path, &dst_size) < 0 + || append_and_increment(&dst, pkgname, &dst_size) < 0 + || append_and_increment(&dst, postfix, &dst_size) < 0) { + ALOGE("Error building APK path"); + return -1; + } + + return 0; +} + +/** + * Create the package path name for a given package name with a postfix for + * a certain userid. Returns 0 on success, and -1 on failure. + */ +int create_pkg_path(char path[PKG_PATH_MAX], + const char *pkgname, + const char *postfix, + userid_t userid) +{ + size_t userid_len; + const char* userid_prefix; + if (userid == 0) { + userid_prefix = PRIMARY_USER_PREFIX; + userid_len = 0; + } else { + userid_prefix = SECONDARY_USER_PREFIX; + userid_len = snprintf(NULL, 0, "%d", userid); + } + + const size_t prefix_len = android_data_dir.len + strlen(userid_prefix) + + userid_len + 1 /*slash*/; + char prefix[prefix_len + 1]; + + char *dst = prefix; + size_t dst_size = sizeof(prefix); + + if (append_and_increment(&dst, android_data_dir.path, &dst_size) < 0 + || append_and_increment(&dst, userid_prefix, &dst_size) < 0) { + ALOGE("Error building prefix for APK path"); + return -1; + } + + if (userid != 0) { + int ret = snprintf(dst, dst_size, "%d/", userid); + if (ret < 0 || (size_t) ret != userid_len + 1) { + ALOGW("Error appending UID to APK path"); + return -1; + } + } + + dir_rec_t dir; + dir.path = prefix; + dir.len = prefix_len; + + return create_pkg_path_in_dir(path, &dir, pkgname, postfix); +} + +/** + * Create the path name for user data for a certain userid. + * Returns 0 on success, and -1 on failure. + */ +int create_user_path(char path[PKG_PATH_MAX], + userid_t userid) +{ + size_t userid_len; + const char* userid_prefix; + if (userid == 0) { + userid_prefix = PRIMARY_USER_PREFIX; + userid_len = 0; + } else { + userid_prefix = SECONDARY_USER_PREFIX; + userid_len = snprintf(NULL, 0, "%d/", userid); + } + + char *dst = path; + size_t dst_size = PKG_PATH_MAX; + + if (append_and_increment(&dst, android_data_dir.path, &dst_size) < 0 + || append_and_increment(&dst, userid_prefix, &dst_size) < 0) { + ALOGE("Error building prefix for user path"); + return -1; + } + + if (userid != 0) { + if (dst_size < userid_len + 1) { + ALOGE("Error building user path"); + return -1; + } + int ret = snprintf(dst, dst_size, "%d/", userid); + if (ret < 0 || (size_t) ret != userid_len) { + ALOGE("Error appending userid to path"); + return -1; + } + } + return 0; +} + +/** + * Create the path name for media for a certain userid. + * Returns 0 on success, and -1 on failure. + */ +int create_user_media_path(char path[PATH_MAX], userid_t userid) { + if (snprintf(path, PATH_MAX, "%s%d", android_media_dir.path, userid) > PATH_MAX) { + return -1; + } + return 0; +} + +/** + * Create the path name for config for a certain userid. + * Returns 0 on success, and -1 on failure. + */ +int create_user_config_path(char path[PATH_MAX], userid_t userid) { + if (snprintf(path, PATH_MAX, "%s%d", "/data/misc/user/", userid) > PATH_MAX) { + return -1; + } + return 0; +} + +int create_move_path(char path[PKG_PATH_MAX], + const char* pkgname, + const char* leaf, + userid_t userid __unused) +{ + if ((android_data_dir.len + strlen(PRIMARY_USER_PREFIX) + strlen(pkgname) + strlen(leaf) + 1) + >= PKG_PATH_MAX) { + return -1; + } + + sprintf(path, "%s%s%s/%s", android_data_dir.path, PRIMARY_USER_PREFIX, pkgname, leaf); + return 0; +} + +/** + * Checks whether the package name is valid. Returns -1 on error and + * 0 on success. + */ +int is_valid_package_name(const char* pkgname) { + const char *x = pkgname; + int alpha = -1; + + while (*x) { + if (isalnum(*x) || (*x == '_')) { + /* alphanumeric or underscore are fine */ + } else if (*x == '.') { + if ((x == pkgname) || (x[1] == '.') || (x[1] == 0)) { + /* periods must not be first, last, or doubled */ + ALOGE("invalid package name '%s'\n", pkgname); + return -1; + } + } else if (*x == '-') { + /* Suffix -X is fine to let versioning of packages. + But whatever follows should be alphanumeric.*/ + alpha = 1; + } else { + /* anything not A-Z, a-z, 0-9, _, or . is invalid */ + ALOGE("invalid package name '%s'\n", pkgname); + return -1; + } + + x++; + } + + if (alpha == 1) { + // Skip current character + x++; + while (*x) { + if (!isalnum(*x)) { + ALOGE("invalid package name '%s' should include only numbers after -\n", pkgname); + return -1; + } + x++; + } + } + + return 0; +} + +static int _delete_dir_contents(DIR *d, + int (*exclusion_predicate)(const char *name, const int is_dir)) +{ + int result = 0; + struct dirent *de; + int dfd; + + dfd = dirfd(d); + + if (dfd < 0) return -1; + + while ((de = readdir(d))) { + const char *name = de->d_name; + + /* check using the exclusion predicate, if provided */ + if (exclusion_predicate && exclusion_predicate(name, (de->d_type == DT_DIR))) { + continue; + } + + if (de->d_type == DT_DIR) { + int subfd; + DIR *subdir; + + /* 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) { + ALOGE("Couldn't openat %s: %s\n", name, strerror(errno)); + result = -1; + continue; + } + subdir = fdopendir(subfd); + if (subdir == NULL) { + ALOGE("Couldn't fdopendir %s: %s\n", name, strerror(errno)); + close(subfd); + result = -1; + continue; + } + if (_delete_dir_contents(subdir, exclusion_predicate)) { + result = -1; + } + closedir(subdir); + if (unlinkat(dfd, name, AT_REMOVEDIR) < 0) { + ALOGE("Couldn't unlinkat %s: %s\n", name, strerror(errno)); + result = -1; + } + } else { + if (unlinkat(dfd, name, 0) < 0) { + ALOGE("Couldn't unlinkat %s: %s\n", name, strerror(errno)); + result = -1; + } + } + } + + return result; +} + +int delete_dir_contents(const char *pathname, + int also_delete_dir, + int (*exclusion_predicate)(const char*, const int)) +{ + int res = 0; + DIR *d; + + d = opendir(pathname); + if (d == NULL) { + ALOGE("Couldn't opendir %s: %s\n", pathname, strerror(errno)); + return -errno; + } + res = _delete_dir_contents(d, exclusion_predicate); + closedir(d); + if (also_delete_dir) { + if (rmdir(pathname)) { + ALOGE("Couldn't rmdir %s: %s\n", pathname, strerror(errno)); + res = -1; + } + } + return res; +} + +int delete_dir_contents_fd(int dfd, const char *name) +{ + int fd, res; + DIR *d; + + fd = openat(dfd, name, O_RDONLY | O_DIRECTORY); + if (fd < 0) { + ALOGE("Couldn't openat %s: %s\n", name, strerror(errno)); + return -1; + } + d = fdopendir(fd); + if (d == NULL) { + ALOGE("Couldn't fdopendir %s: %s\n", name, strerror(errno)); + close(fd); + return -1; + } + res = _delete_dir_contents(d, 0); + closedir(d); + return res; +} + +static int _copy_owner_permissions(int srcfd, int dstfd) +{ + struct stat st; + if (fstat(srcfd, &st) != 0) { + return -1; + } + if (fchmod(dstfd, st.st_mode) != 0) { + return -1; + } + return 0; +} + +static int _copy_dir_files(int sdfd, int ddfd, uid_t owner, gid_t group) +{ + int result = 0; + if (_copy_owner_permissions(sdfd, ddfd) != 0) { + ALOGE("_copy_dir_files failed to copy dir permissions\n"); + } + if (fchown(ddfd, owner, group) != 0) { + ALOGE("_copy_dir_files failed to change dir owner\n"); + } + + DIR *ds = fdopendir(sdfd); + if (ds == NULL) { + ALOGE("Couldn't fdopendir: %s\n", strerror(errno)); + return -1; + } + struct dirent *de; + while ((de = readdir(ds))) { + if (de->d_type != DT_REG) { + continue; + } + + const char *name = de->d_name; + int fsfd = openat(sdfd, name, O_RDONLY | O_NOFOLLOW | O_CLOEXEC); + int fdfd = openat(ddfd, name, O_WRONLY | O_NOFOLLOW | O_CLOEXEC | O_CREAT, 0600); + if (fsfd == -1 || fdfd == -1) { + ALOGW("Couldn't copy %s: %s\n", name, strerror(errno)); + } else { + if (_copy_owner_permissions(fsfd, fdfd) != 0) { + ALOGE("Failed to change file permissions\n"); + } + if (fchown(fdfd, owner, group) != 0) { + ALOGE("Failed to change file owner\n"); + } + + char buf[8192]; + ssize_t size; + while ((size = read(fsfd, buf, sizeof(buf))) > 0) { + write(fdfd, buf, size); + } + if (size < 0) { + ALOGW("Couldn't copy %s: %s\n", name, strerror(errno)); + result = -1; + } + } + close(fdfd); + close(fsfd); + } + + return result; +} + +int copy_dir_files(const char *srcname, + const char *dstname, + uid_t owner, + uid_t group) +{ + int res = 0; + DIR *ds = NULL; + DIR *dd = NULL; + + ds = opendir(srcname); + if (ds == NULL) { + ALOGE("Couldn't opendir %s: %s\n", srcname, strerror(errno)); + return -errno; + } + + mkdir(dstname, 0600); + dd = opendir(dstname); + if (dd == NULL) { + ALOGE("Couldn't opendir %s: %s\n", dstname, strerror(errno)); + closedir(ds); + return -errno; + } + + int sdfd = dirfd(ds); + int ddfd = dirfd(dd); + if (sdfd != -1 && ddfd != -1) { + res = _copy_dir_files(sdfd, ddfd, owner, group); + } else { + res = -errno; + } + closedir(dd); + closedir(ds); + return res; +} + +int lookup_media_dir(char basepath[PATH_MAX], const char *dir) +{ + DIR *d; + struct dirent *de; + struct stat s; + char* dirpos = basepath + strlen(basepath); + + if ((*(dirpos-1)) != '/') { + *dirpos = '/'; + dirpos++; + } + + CACHE_NOISY(ALOGI("Looking up %s in %s\n", dir, basepath)); + // Verify the path won't extend beyond our buffer, to avoid + // repeated checking later. + if ((dirpos-basepath+strlen(dir)) >= (PATH_MAX-1)) { + ALOGW("Path exceeds limit: %s%s", basepath, dir); + return -1; + } + + // First, can we find this directory with the case that is given? + strcpy(dirpos, dir); + if (stat(basepath, &s) >= 0) { + CACHE_NOISY(ALOGI("Found direct: %s\n", basepath)); + return 0; + } + + // Not found with that case... search through all entries to find + // one that matches regardless of case. + *dirpos = 0; + + d = opendir(basepath); + if (d == NULL) { + return -1; + } + + while ((de = readdir(d))) { + if (strcasecmp(de->d_name, dir) == 0) { + strcpy(dirpos, de->d_name); + closedir(d); + CACHE_NOISY(ALOGI("Found search: %s\n", basepath)); + return 0; + } + } + + ALOGW("Couldn't find %s in %s", dir, basepath); + closedir(d); + return -1; +} + +int64_t data_disk_free() +{ + struct statfs sfs; + if (statfs(android_data_dir.path, &sfs) == 0) { + return sfs.f_bavail * sfs.f_bsize; + } else { + ALOGE("Couldn't statfs %s: %s\n", android_data_dir.path, strerror(errno)); + return -1; + } +} + +cache_t* start_cache_collection() +{ + cache_t* cache = (cache_t*)calloc(1, sizeof(cache_t)); + return cache; +} + +#define CACHE_BLOCK_SIZE (512*1024) + +static void* _cache_malloc(cache_t* cache, size_t len) +{ + len = (len+3)&~3; + if (len > (CACHE_BLOCK_SIZE/2)) { + // It doesn't make sense to try to put this allocation into one + // of our blocks, because it is so big. Instead, make a new dedicated + // block for it. + int8_t* res = (int8_t*)malloc(len+sizeof(void*)); + if (res == NULL) { + return NULL; + } + CACHE_NOISY(ALOGI("Allocated large cache mem block: %p size %d", res, len)); + // Link it into our list of blocks, not disrupting the current one. + if (cache->memBlocks == NULL) { + *(void**)res = NULL; + cache->memBlocks = res; + } else { + *(void**)res = *(void**)cache->memBlocks; + *(void**)cache->memBlocks = res; + } + return res + sizeof(void*); + } + int8_t* res = cache->curMemBlockAvail; + int8_t* nextPos = res + len; + if (cache->memBlocks == NULL || nextPos > cache->curMemBlockEnd) { + int8_t* newBlock = (int8_t*) malloc(CACHE_BLOCK_SIZE); + if (newBlock == NULL) { + return NULL; + } + CACHE_NOISY(ALOGI("Allocated new cache mem block: %p", newBlock)); + *(void**)newBlock = cache->memBlocks; + cache->memBlocks = newBlock; + res = cache->curMemBlockAvail = newBlock + sizeof(void*); + cache->curMemBlockEnd = newBlock + CACHE_BLOCK_SIZE; + nextPos = res + len; + } + CACHE_NOISY(ALOGI("cache_malloc: ret %p size %d, block=%p, nextPos=%p", + res, len, cache->memBlocks, nextPos)); + cache->curMemBlockAvail = nextPos; + return res; +} + +static void* _cache_realloc(cache_t* cache, void* cur, size_t origLen, size_t len) +{ + // This isn't really a realloc, but it is good enough for our purposes here. + void* alloc = _cache_malloc(cache, len); + if (alloc != NULL && cur != NULL) { + memcpy(alloc, cur, origLen < len ? origLen : len); + } + return alloc; +} + +static void _inc_num_cache_collected(cache_t* cache) +{ + cache->numCollected++; + if ((cache->numCollected%20000) == 0) { + ALOGI("Collected cache so far: %zd directories, %zd files", + cache->numDirs, cache->numFiles); + } +} + +static cache_dir_t* _add_cache_dir_t(cache_t* cache, cache_dir_t* parent, const char *name) +{ + size_t nameLen = strlen(name); + cache_dir_t* dir = (cache_dir_t*)_cache_malloc(cache, sizeof(cache_dir_t)+nameLen+1); + if (dir != NULL) { + dir->parent = parent; + dir->childCount = 0; + dir->hiddenCount = 0; + dir->deleted = 0; + strcpy(dir->name, name); + if (cache->numDirs >= cache->availDirs) { + size_t newAvail = cache->availDirs < 1000 ? 1000 : cache->availDirs*2; + cache_dir_t** newDirs = (cache_dir_t**)_cache_realloc(cache, cache->dirs, + cache->availDirs*sizeof(cache_dir_t*), newAvail*sizeof(cache_dir_t*)); + if (newDirs == NULL) { + ALOGE("Failure growing cache dirs array for %s\n", name); + return NULL; + } + cache->availDirs = newAvail; + cache->dirs = newDirs; + } + cache->dirs[cache->numDirs] = dir; + cache->numDirs++; + if (parent != NULL) { + parent->childCount++; + } + _inc_num_cache_collected(cache); + } else { + ALOGE("Failure allocating cache_dir_t for %s\n", name); + } + return dir; +} + +static cache_file_t* _add_cache_file_t(cache_t* cache, cache_dir_t* dir, time_t modTime, + const char *name) +{ + size_t nameLen = strlen(name); + cache_file_t* file = (cache_file_t*)_cache_malloc(cache, sizeof(cache_file_t)+nameLen+1); + if (file != NULL) { + file->dir = dir; + file->modTime = modTime; + strcpy(file->name, name); + if (cache->numFiles >= cache->availFiles) { + size_t newAvail = cache->availFiles < 1000 ? 1000 : cache->availFiles*2; + cache_file_t** newFiles = (cache_file_t**)_cache_realloc(cache, cache->files, + cache->availFiles*sizeof(cache_file_t*), newAvail*sizeof(cache_file_t*)); + if (newFiles == NULL) { + ALOGE("Failure growing cache file array for %s\n", name); + return NULL; + } + cache->availFiles = newAvail; + cache->files = newFiles; + } + CACHE_NOISY(ALOGI("Setting file %p at position %d in array %p", file, + cache->numFiles, cache->files)); + cache->files[cache->numFiles] = file; + cache->numFiles++; + dir->childCount++; + _inc_num_cache_collected(cache); + } else { + ALOGE("Failure allocating cache_file_t for %s\n", name); + } + return file; +} + +static int _add_cache_files(cache_t *cache, cache_dir_t *parentDir, const char *dirName, + DIR* dir, char *pathBase, char *pathPos, size_t pathAvailLen) +{ + struct dirent *de; + cache_dir_t* cacheDir = NULL; + int dfd; + + CACHE_NOISY(ALOGI("_add_cache_files: parent=%p dirName=%s dir=%p pathBase=%s", + parentDir, dirName, dir, pathBase)); + + dfd = dirfd(dir); + + if (dfd < 0) return 0; + + // Sub-directories always get added to the data structure, so if they + // are empty we will know about them to delete them later. + cacheDir = _add_cache_dir_t(cache, parentDir, dirName); + + while ((de = readdir(dir))) { + const char *name = de->d_name; + + if (de->d_type == DT_DIR) { + int subfd; + DIR *subdir; + + /* 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) { + ALOGE("Couldn't openat %s: %s\n", name, strerror(errno)); + continue; + } + subdir = fdopendir(subfd); + if (subdir == NULL) { + ALOGE("Couldn't fdopendir %s: %s\n", name, strerror(errno)); + close(subfd); + continue; + } + if (cacheDir == NULL) { + cacheDir = _add_cache_dir_t(cache, parentDir, dirName); + } + if (cacheDir != NULL) { + // Update pathBase for the new path... this may change dirName + // if that is also pointing to the path, but we are done with it + // now. + size_t finallen = snprintf(pathPos, pathAvailLen, "/%s", name); + CACHE_NOISY(ALOGI("Collecting dir %s\n", pathBase)); + if (finallen < pathAvailLen) { + _add_cache_files(cache, cacheDir, name, subdir, pathBase, + pathPos+finallen, pathAvailLen-finallen); + } else { + // Whoops, the final path is too long! We'll just delete + // this directory. + ALOGW("Cache dir %s truncated in path %s; deleting dir\n", + name, pathBase); + _delete_dir_contents(subdir, NULL); + if (unlinkat(dfd, name, AT_REMOVEDIR) < 0) { + ALOGE("Couldn't unlinkat %s: %s\n", name, strerror(errno)); + } + } + } + closedir(subdir); + } else if (de->d_type == DT_REG) { + // Skip files that start with '.'; they will be deleted if + // their entire directory is deleted. This allows for metadata + // like ".nomedia" to remain in the directory until the entire + // directory is deleted. + if (cacheDir == NULL) { + cacheDir = _add_cache_dir_t(cache, parentDir, dirName); + } + if (name[0] == '.') { + cacheDir->hiddenCount++; + continue; + } + if (cacheDir != NULL) { + // Build final full path for file... this may change dirName + // if that is also pointing to the path, but we are done with it + // now. + size_t finallen = snprintf(pathPos, pathAvailLen, "/%s", name); + CACHE_NOISY(ALOGI("Collecting file %s\n", pathBase)); + if (finallen < pathAvailLen) { + struct stat s; + if (stat(pathBase, &s) >= 0) { + _add_cache_file_t(cache, cacheDir, s.st_mtime, name); + } else { + ALOGW("Unable to stat cache file %s; deleting\n", pathBase); + if (unlink(pathBase) < 0) { + ALOGE("Couldn't unlink %s: %s\n", pathBase, strerror(errno)); + } + } + } else { + // Whoops, the final path is too long! We'll just delete + // this file. + ALOGW("Cache file %s truncated in path %s; deleting\n", + name, pathBase); + if (unlinkat(dfd, name, 0) < 0) { + *pathPos = 0; + ALOGE("Couldn't unlinkat %s in %s: %s\n", name, pathBase, + strerror(errno)); + } + } + } + } else { + cacheDir->hiddenCount++; + } + } + return 0; +} + +void add_cache_files(cache_t* cache, const char *basepath, const char *cachedir) +{ + DIR *d; + struct dirent *de; + char dirname[PATH_MAX]; + + CACHE_NOISY(ALOGI("add_cache_files: base=%s cachedir=%s\n", basepath, cachedir)); + + d = opendir(basepath); + if (d == NULL) { + return; + } + + while ((de = readdir(d))) { + if (de->d_type == DT_DIR) { + DIR* subdir; + const char *name = de->d_name; + char* pathpos; + + /* always skip "." and ".." */ + if (name[0] == '.') { + if (name[1] == 0) continue; + if ((name[1] == '.') && (name[2] == 0)) continue; + } + + strcpy(dirname, basepath); + pathpos = dirname + strlen(dirname); + if ((*(pathpos-1)) != '/') { + *pathpos = '/'; + pathpos++; + *pathpos = 0; + } + if (cachedir != NULL) { + snprintf(pathpos, sizeof(dirname)-(pathpos-dirname), "%s/%s", name, cachedir); + } else { + snprintf(pathpos, sizeof(dirname)-(pathpos-dirname), "%s", name); + } + CACHE_NOISY(ALOGI("Adding cache files from dir: %s\n", dirname)); + subdir = opendir(dirname); + if (subdir != NULL) { + size_t dirnameLen = strlen(dirname); + _add_cache_files(cache, NULL, dirname, subdir, dirname, dirname+dirnameLen, + PATH_MAX - dirnameLen); + closedir(subdir); + } + } + } + + closedir(d); +} + +static char *create_dir_path(char path[PATH_MAX], cache_dir_t* dir) +{ + char *pos = path; + if (dir->parent != NULL) { + pos = create_dir_path(path, dir->parent); + } + // Note that we don't need to worry about going beyond the buffer, + // since when we were constructing the cache entries our maximum + // buffer size for full paths was PATH_MAX. + strcpy(pos, dir->name); + pos += strlen(pos); + *pos = '/'; + pos++; + *pos = 0; + return pos; +} + +static void delete_cache_dir(char path[PATH_MAX], cache_dir_t* dir) +{ + if (dir->parent != NULL) { + create_dir_path(path, dir); + ALOGI("DEL DIR %s\n", path); + if (dir->hiddenCount <= 0) { + if (rmdir(path)) { + ALOGE("Couldn't rmdir %s: %s\n", path, strerror(errno)); + return; + } + } else { + // The directory contains hidden files so we need to delete + // them along with the directory itself. + if (delete_dir_contents(path, 1, NULL)) { + return; + } + } + dir->parent->childCount--; + dir->deleted = 1; + if (dir->parent->childCount <= 0) { + delete_cache_dir(path, dir->parent); + } + } else if (dir->hiddenCount > 0) { + // This is a root directory, but it has hidden files. Get rid of + // all of those files, but not the directory itself. + create_dir_path(path, dir); + ALOGI("DEL CONTENTS %s\n", path); + delete_dir_contents(path, 0, NULL); + } +} + +static int cache_modtime_sort(const void *lhsP, const void *rhsP) +{ + const cache_file_t *lhs = *(const cache_file_t**)lhsP; + const cache_file_t *rhs = *(const cache_file_t**)rhsP; + return lhs->modTime < rhs->modTime ? -1 : (lhs->modTime > rhs->modTime ? 1 : 0); +} + +void clear_cache_files(cache_t* cache, int64_t free_size) +{ + size_t i; + int skip = 0; + char path[PATH_MAX]; + + ALOGI("Collected cache files: %zd directories, %zd files", + cache->numDirs, cache->numFiles); + + CACHE_NOISY(ALOGI("Sorting files...")); + qsort(cache->files, cache->numFiles, sizeof(cache_file_t*), + cache_modtime_sort); + + CACHE_NOISY(ALOGI("Cleaning empty directories...")); + for (i=cache->numDirs; i>0; i--) { + cache_dir_t* dir = cache->dirs[i-1]; + if (dir->childCount <= 0 && !dir->deleted) { + delete_cache_dir(path, dir); + } + } + + CACHE_NOISY(ALOGI("Trimming files...")); + for (i=0; inumFiles; i++) { + skip++; + if (skip > 10) { + if (data_disk_free() > free_size) { + return; + } + skip = 0; + } + cache_file_t* file = cache->files[i]; + strcpy(create_dir_path(path, file->dir), file->name); + ALOGI("DEL (mod %d) %s\n", (int)file->modTime, path); + if (unlink(path) < 0) { + ALOGE("Couldn't unlink %s: %s\n", path, strerror(errno)); + } + file->dir->childCount--; + if (file->dir->childCount <= 0) { + delete_cache_dir(path, file->dir); + } + } +} + +void finish_cache_collection(cache_t* cache) +{ + CACHE_NOISY(size_t i;) + + CACHE_NOISY(ALOGI("clear_cache_files: %d dirs, %d files\n", cache->numDirs, cache->numFiles)); + CACHE_NOISY( + for (i=0; inumDirs; i++) { + cache_dir_t* dir = cache->dirs[i]; + ALOGI("dir #%d: %p %s parent=%p\n", i, dir, dir->name, dir->parent); + }) + CACHE_NOISY( + for (i=0; inumFiles; i++) { + cache_file_t* file = cache->files[i]; + ALOGI("file #%d: %p %s time=%d dir=%p\n", i, file, file->name, + (int)file->modTime, file->dir); + }) + void* block = cache->memBlocks; + while (block != NULL) { + void* nextBlock = *(void**)block; + CACHE_NOISY(ALOGI("Freeing cache mem block: %p", block)); + free(block); + block = nextBlock; + } + free(cache); +} + +/** + * Validate that the path is valid in the context of the provided directory. + * The path is allowed to have at most one subdirectory and no indirections + * to top level directories (i.e. have ".."). + */ +static int validate_path(const dir_rec_t* dir, const char* path, int maxSubdirs) { + size_t dir_len = dir->len; + const char* subdir = strchr(path + dir_len, '/'); + + // Only allow the path to have at most one subdirectory. + if (subdir != NULL) { + ++subdir; + if ((--maxSubdirs == 0) && strchr(subdir, '/') != NULL) { + ALOGE("invalid apk path '%s' (subdir?)\n", path); + return -1; + } + } + + // Directories can't have a period directly after the directory markers to prevent "..". + if ((path[dir_len] == '.') || ((subdir != NULL) && (*subdir == '.'))) { + ALOGE("invalid apk path '%s' (trickery)\n", path); + return -1; + } + + return 0; +} + +/** + * Checks whether a path points to a system app (.apk file). Returns 0 + * if it is a system app or -1 if it is not. + */ +int validate_system_app_path(const char* path) { + size_t i; + + for (i = 0; i < android_system_dirs.count; i++) { + const size_t dir_len = android_system_dirs.dirs[i].len; + if (!strncmp(path, android_system_dirs.dirs[i].path, dir_len)) { + return validate_path(android_system_dirs.dirs + i, path, 1); + } + } + + return -1; +} + +/** + * Get the contents of a environment variable that contains a path. Caller + * owns the string that is inserted into the directory record. Returns + * 0 on success and -1 on error. + */ +int get_path_from_env(dir_rec_t* rec, const char* var) { + const char* path = getenv(var); + int ret = get_path_from_string(rec, path); + if (ret < 0) { + ALOGW("Problem finding value for environment variable %s\n", var); + } + return ret; +} + +/** + * Puts the string into the record as a directory. Appends '/' to the end + * of all paths. Caller owns the string that is inserted into the directory + * record. A null value will result in an error. + * + * Returns 0 on success and -1 on error. + */ +int get_path_from_string(dir_rec_t* rec, const char* path) { + if (path == NULL) { + return -1; + } else { + const size_t path_len = strlen(path); + if (path_len <= 0) { + return -1; + } + + // Make sure path is absolute. + if (path[0] != '/') { + return -1; + } + + if (path[path_len - 1] == '/') { + // Path ends with a forward slash. Make our own copy. + + rec->path = strdup(path); + if (rec->path == NULL) { + return -1; + } + + rec->len = path_len; + } else { + // Path does not end with a slash. Generate a new string. + char *dst; + + // Add space for slash and terminating null. + size_t dst_size = path_len + 2; + + rec->path = (char*) malloc(dst_size); + if (rec->path == NULL) { + return -1; + } + + dst = rec->path; + + if (append_and_increment(&dst, path, &dst_size) < 0 + || append_and_increment(&dst, "/", &dst_size)) { + ALOGE("Error canonicalizing path"); + return -1; + } + + rec->len = dst - rec->path; + } + } + return 0; +} + +int copy_and_append(dir_rec_t* dst, const dir_rec_t* src, const char* suffix) { + dst->len = src->len + strlen(suffix); + const size_t dstSize = dst->len + 1; + dst->path = (char*) malloc(dstSize); + + if (dst->path == NULL + || snprintf(dst->path, dstSize, "%s%s", src->path, suffix) + != (ssize_t) dst->len) { + ALOGE("Could not allocate memory to hold appended path; aborting\n"); + return -1; + } + + return 0; +} + +/** + * Check whether path points to a valid path for an APK file. Only one level of + * subdirectory names is allowed. Returns -1 when an invalid path is encountered + * and 0 when a valid path is encountered. + */ +int validate_apk_path(const char *path) +{ + const dir_rec_t* dir = NULL; + int maxSubdirs = 1; + + if (!strncmp(path, android_app_dir.path, android_app_dir.len)) { + dir = &android_app_dir; + } else if (!strncmp(path, android_app_private_dir.path, android_app_private_dir.len)) { + dir = &android_app_private_dir; + } else if (!strncmp(path, android_asec_dir.path, android_asec_dir.len)) { + dir = &android_asec_dir; + } else if (!strncmp(path, android_mnt_expand_dir.path, android_mnt_expand_dir.len)) { + dir = &android_mnt_expand_dir; + maxSubdirs = 2; + } else { + return -1; + } + + return validate_path(dir, path, maxSubdirs); +} + +int append_and_increment(char** dst, const char* src, size_t* dst_size) { + ssize_t ret = strlcpy(*dst, src, *dst_size); + if (ret < 0 || (size_t) ret >= *dst_size) { + return -1; + } + *dst += ret; + *dst_size -= ret; + return 0; +} + +char *build_string2(const char *s1, const char *s2) { + if (s1 == NULL || s2 == NULL) return NULL; + + int len_s1 = strlen(s1); + int len_s2 = strlen(s2); + int len = len_s1 + len_s2 + 1; + char *result = (char *) malloc(len); + if (result == NULL) return NULL; + + strcpy(result, s1); + strcpy(result + len_s1, s2); + + return result; +} + +char *build_string3(const char *s1, const char *s2, const char *s3) { + if (s1 == NULL || s2 == NULL || s3 == NULL) return NULL; + + int len_s1 = strlen(s1); + int len_s2 = strlen(s2); + int len_s3 = strlen(s3); + int len = len_s1 + len_s2 + len_s3 + 1; + char *result = (char *) malloc(len); + if (result == NULL) return NULL; + + strcpy(result, s1); + strcpy(result + len_s1, s2); + strcpy(result + len_s1 + len_s2, s3); + + return result; +} + +/* Ensure that /data/media directories are prepared for given user. */ +int ensure_media_user_dirs(userid_t userid) { + char media_user_path[PATH_MAX]; + + // Ensure /data/media/ exists + create_user_media_path(media_user_path, userid); + if (fs_prepare_dir(media_user_path, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) { + return -1; + } + + return 0; +} + +int ensure_config_user_dirs(userid_t userid) { + char config_user_path[PATH_MAX]; + + // writable by system, readable by any app within the same user + const int uid = multiuser_get_uid(userid, AID_SYSTEM); + const int gid = multiuser_get_uid(userid, AID_EVERYBODY); + + // Ensure /data/misc/user/ exists + create_user_config_path(config_user_path, userid); + if (fs_prepare_dir(config_user_path, 0750, uid, gid) == -1) { + return -1; + } + + return 0; +} + +int create_profile_file(const char *pkgname, gid_t gid) { + const char *profile_dir = DALVIK_CACHE_PREFIX "profiles"; + char profile_file[PKG_PATH_MAX]; + + snprintf(profile_file, sizeof(profile_file), "%s/%s", profile_dir, pkgname); + + // The 'system' user needs to be able to read the profile to determine if dex2oat + // needs to be run. This is done in dalvik.system.DexFile.isDexOptNeededInternal(). So + // we assign ownership to AID_SYSTEM and ensure it's not world-readable. + + int fd = open(profile_file, O_WRONLY | O_CREAT | O_NOFOLLOW | O_CLOEXEC, 0660); + + // Always set the uid/gid/permissions. The file could have been previously created + // with different permissions. + if (fd >= 0) { + if (fchown(fd, AID_SYSTEM, gid) < 0) { + ALOGE("cannot chown profile file '%s': %s\n", profile_file, strerror(errno)); + close(fd); + unlink(profile_file); + return -1; + } + + if (fchmod(fd, 0660) < 0) { + ALOGE("cannot chmod profile file '%s': %s\n", profile_file, strerror(errno)); + close(fd); + unlink(profile_file); + return -1; + } + close(fd); + } + return 0; +} + +void remove_profile_file(const char *pkgname) { + char profile_file[PKG_PATH_MAX]; + snprintf(profile_file, sizeof(profile_file), "%s/%s", DALVIK_CACHE_PREFIX "profiles", pkgname); + unlink(profile_file); +} -- cgit v1.1