From e36372423000a906bafae68844ebc6c42d09335a Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Wed, 8 Apr 2015 20:56:42 -0700 Subject: [PATCH] Command to move private app data between volumes. New "mvuserdata" command will move all private app data from one volume UUID to another. It leverages the existing "cp" toybox command to do the heavy lifting for all known users, preserving details like timestamps and permissions. It invokes restorecon() to correctly label the new location when the copy is finished. Changes installd to no longer drop capabilities, so we run as root again. This also allows us to exec "cp" with CAP_DAC_OVERRIDE and CAP_FOWNER still in effect. Bug: 19993667 Change-Id: I1f407a7c4a1af97ca5afc27b04eb16b4936cbdef --- cmds/installd/Android.mk | 6 ++- cmds/installd/commands.cpp | 93 +++++++++++++++++++++++++++++++++++--- cmds/installd/installd.cpp | 61 ++++++------------------- cmds/installd/installd.h | 12 ++++- cmds/installd/utils.cpp | 32 +++++++++++++ 5 files changed, 149 insertions(+), 55 deletions(-) diff --git a/cmds/installd/Android.mk b/cmds/installd/Android.mk index 2171f4db8..6dec7f689 100644 --- a/cmds/installd/Android.mk +++ b/cmds/installd/Android.mk @@ -12,7 +12,10 @@ LOCAL_MODULE := libinstalld LOCAL_MODULE_TAGS := eng tests LOCAL_SRC_FILES := $(common_src_files) LOCAL_CFLAGS := $(common_cflags) -LOCAL_SHARED_LIBRARIES := libbase +LOCAL_SHARED_LIBRARIES := \ + libbase \ + liblogwrap \ + LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk LOCAL_CLANG := true include $(BUILD_STATIC_LIBRARY) @@ -30,6 +33,7 @@ LOCAL_SHARED_LIBRARIES := \ libbase \ libcutils \ liblog \ + liblogwrap \ libselinux \ LOCAL_STATIC_LIBRARIES := libdiskusage diff --git a/cmds/installd/commands.cpp b/cmds/installd/commands.cpp index 32a03f49d..de6fd960f 100644 --- a/cmds/installd/commands.cpp +++ b/cmds/installd/commands.cpp @@ -16,15 +16,18 @@ #include "installd.h" +#include +#include +#include +#include +#include +#include +#include + #include #include #include -#include -#include -#include -#include -#include -#include +#include using android::base::StringPrintf; @@ -38,6 +41,8 @@ dir_rec_t android_media_dir; dir_rec_t android_mnt_expand_dir; dir_rec_array_t android_system_dirs; +static const char* kCpPath = "/system/bin/cp"; + int install(const char *uuid, const char *pkgname, uid_t uid, gid_t gid, const char *seinfo) { if ((uid < AID_SYSTEM) || (gid < AID_SYSTEM)) { @@ -172,6 +177,80 @@ int make_user_data(const char *uuid, const char *pkgname, uid_t uid, userid_t us return 0; } +int move_user_data(const char *from_uuid, const char *to_uuid, + const char *package_name, appid_t appid, const char* seinfo) { + std::vector users = get_known_users(from_uuid); + + // Copy package private data for all known users + for (auto user : users) { + std::string from(create_package_data_path(from_uuid, package_name, user)); + std::string to(create_package_data_path(to_uuid, package_name, user)); + std::string to_user(create_data_user_path(to_uuid, user)); + + // Data source may not exist for all users; that's okay + if (access(from.c_str(), F_OK) != 0) { + LOG(INFO) << "Missing source " << from; + continue; + } + + std::string user_path(create_data_user_path(to_uuid, user)); + if (fs_prepare_dir(user_path.c_str(), 0771, AID_SYSTEM, AID_SYSTEM) != 0) { + LOG(ERROR) << "Failed to prepare user target " << user_path; + goto fail; + } + + uid_t uid = multiuser_get_uid(user, appid); + if (make_user_data(to_uuid, package_name, uid, user, seinfo) != 0) { + LOG(ERROR) << "Failed to create package target " << to; + goto fail; + } + + char *argv[] = { + (char*) kCpPath, + (char*) "-F", /* delete any existing destination file first (--remove-destination) */ + (char*) "-p", /* preserve timestamps, ownership, and permissions */ + (char*) "-R", /* recurse into subdirectories (DEST must be a directory) */ + (char*) "-P", /* Do not follow symlinks [default] */ + (char*) "-d", /* don't dereference symlinks */ + (char*) from.c_str(), + (char*) to_user.c_str() + }; + + LOG(DEBUG) << "Copying " << from << " to " << to; + int rc = android_fork_execvp(ARRAY_SIZE(argv), argv, NULL, false, true); + + if (rc != 0) { + LOG(ERROR) << "Failed copying " << from << " to " << to + << ": status " << rc; + goto fail; + } + + if (restorecon_data(to_uuid, package_name, seinfo, uid) != 0) { + LOG(ERROR) << "Failed to restorecon " << to; + goto fail; + } + } + + // Copy successful, so delete old data + for (auto user : users) { + std::string from(create_package_data_path(from_uuid, package_name, user)); + if (delete_dir_contents(from.c_str(), 1, NULL) != 0) { + LOG(WARNING) << "Failed to delete " << from; + } + } + return 0; + +fail: + // Nuke everything we might have already copied + for (auto user : users) { + std::string to(create_package_data_path(to_uuid, package_name, user)); + if (delete_dir_contents(to.c_str(), 1, NULL) != 0) { + LOG(WARNING) << "Failed to rollback " << to; + } + } + return -1; +} + int make_user_config(userid_t userid) { if (ensure_config_user_dirs(userid) == -1) { @@ -1592,7 +1671,7 @@ int restorecon_data(const char* uuid, const char* pkgName, continue; } - std::string pkgdir(StringPrintf("%s/%s/%s", userdir.c_str(), user, pkgName)); + std::string pkgdir(StringPrintf("%s%s/%s", userdir.c_str(), user, pkgName)); if (stat(pkgdir.c_str(), &s) < 0) { continue; } diff --git a/cmds/installd/installd.cpp b/cmds/installd/installd.cpp index 0f035d00b..3a861812f 100644 --- a/cmds/installd/installd.cpp +++ b/cmds/installd/installd.cpp @@ -14,14 +14,15 @@ ** limitations under the License. */ +#include "installd.h" + +#include + #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 */ @@ -123,6 +124,12 @@ static int do_rm_user_data(char **arg, char reply[REPLY_MAX] __unused) return delete_user_data(parse_null(arg[0]), arg[1], atoi(arg[2])); /* uuid, pkgname, userid */ } +static int do_mv_user_data(char **arg, char reply[REPLY_MAX] __unused) +{ + // from_uuid, to_uuid, pkgname, appid, seinfo + return move_user_data(parse_null(arg[0]), parse_null(arg[1]), arg[2], atoi(arg[3]), arg[4]); +} + static int do_mk_user_data(char **arg, char reply[REPLY_MAX] __unused) { return make_user_data(parse_null(arg[0]), arg[1], atoi(arg[2]), atoi(arg[3]), arg[4]); @@ -193,6 +200,7 @@ struct cmdinfo cmds[] = { { "rmcodecache", 3, do_rm_code_cache }, { "getsize", 8, do_get_size }, { "rmuserdata", 3, do_rm_user_data }, + { "mvuserdata", 5, do_mv_user_data }, { "movefiles", 0, do_movefiles }, { "linklib", 4, do_linklib }, { "mkuserdata", 5, do_mk_user_data }, @@ -621,46 +629,6 @@ fail: 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; @@ -682,13 +650,16 @@ static int log_callback(int type, const char *fmt, ...) { return 0; } -int main(const int argc __unused, const char *argv[] __unused) { +int main(const int argc __unused, char *argv[]) { char buf[BUFFER_MAX]; struct sockaddr addr; socklen_t alen; int lsocket, s; int selinux_enabled = (is_selinux_enabled() > 0); + setenv("ANDROID_LOG_TAGS", "*:v", 1); + android::base::InitLogging(argv); + ALOGI("installd firing up\n"); union selinux_callback cb; @@ -710,8 +681,6 @@ int main(const int argc __unused, const char *argv[] __unused) { 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)); diff --git a/cmds/installd/installd.h b/cmds/installd/installd.h index 395d0ea72..f31bf4f2a 100644 --- a/cmds/installd/installd.h +++ b/cmds/installd/installd.h @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -89,6 +90,8 @@ #define DEXOPT_PATCHOAT_NEEDED 2 #define DEXOPT_SELF_PATCHOAT_NEEDED 3 +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a))) + /* data structures */ typedef struct { @@ -154,6 +157,8 @@ std::string create_data_user_path(const char* volume_uuid, userid_t userid); std::string create_data_media_path(const char* volume_uuid, userid_t userid); +std::vector get_known_users(const char* volume_uuid); + int create_user_config_path(char path[PKG_PATH_MAX], userid_t userid); int create_move_path(char path[PKG_PATH_MAX], @@ -214,7 +219,10 @@ int uninstall(const char *uuid, const char *pkgname, userid_t userid); int renamepkg(const char *oldpkgname, const char *newpkgname); int fix_uid(const char *uuid, const char *pkgname, uid_t uid, gid_t gid); int delete_user_data(const char *uuid, const char *pkgname, userid_t userid); -int make_user_data(const char *uuid, const char *pkgname, uid_t uid, userid_t userid, const char* seinfo); +int make_user_data(const char *uuid, const char *pkgname, uid_t uid, + userid_t userid, const char* seinfo); +int move_user_data(const char* from_uuid, const char *to_uuid, + const char *package_name, appid_t appid, const char* seinfo); int make_user_config(userid_t userid); int delete_user(const char *uuid, userid_t userid); int delete_cache(const char *uuid, const char *pkgname, userid_t userid); @@ -238,3 +246,5 @@ 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, const char *instruction_set); +int move_package_dir(char path[PKG_PATH_MAX], const char *oat_dir, const char *apk_path, + const char *instruction_set); diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp index e10116eb8..ba411cd47 100644 --- a/cmds/installd/utils.cpp +++ b/cmds/installd/utils.cpp @@ -99,6 +99,38 @@ std::string create_data_media_path(const char* volume_uuid, userid_t userid) { return StringPrintf("%s/media/%u", create_data_path(volume_uuid).c_str(), userid); } +std::vector get_known_users(const char* volume_uuid) { + std::vector users; + + // We always have an owner + users.push_back(0); + + std::string path(create_data_path(volume_uuid) + "/" + SECONDARY_USER_PREFIX); + DIR* dir = opendir(path.c_str()); + if (dir == NULL) { + // Unable to discover other users, but at least return owner + PLOG(ERROR) << "Failed to opendir " << path; + return users; + } + + struct dirent* ent; + while ((ent = readdir(dir))) { + if (ent->d_type != DT_DIR) { + continue; + } + + char* end; + userid_t user = strtol(ent->d_name, &end, 10); + if (*end == '\0' && user != 0) { + LOG(DEBUG) << "Found valid user " << user; + users.push_back(user); + } + } + closedir(dir); + + return users; +} + /** * Create the path name for config for a certain userid. * Returns 0 on success, and -1 on failure.