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
This commit is contained in:
Jeff Sharkey 2015-04-08 20:56:42 -07:00
parent 44a38d9337
commit e363724230
5 changed files with 149 additions and 55 deletions

View File

@ -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

View File

@ -16,15 +16,18 @@
#include "installd.h"
#include <base/stringprintf.h>
#include <base/logging.h>
#include <cutils/sched_policy.h>
#include <diskusage/dirsize.h>
#include <logwrap/logwrap.h>
#include <system/thread_defs.h>
#include <selinux/android.h>
#include <inttypes.h>
#include <sys/capability.h>
#include <sys/file.h>
#include <cutils/sched_policy.h>
#include <diskusage/dirsize.h>
#include <selinux/android.h>
#include <system/thread_defs.h>
#include <base/stringprintf.h>
#include <base/logging.h>
#include <unistd.h>
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<userid_t> 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;
}

View File

@ -14,14 +14,15 @@
** limitations under the License.
*/
#include "installd.h"
#include <base/logging.h>
#include <sys/capability.h>
#include <sys/prctl.h>
#include <selinux/android.h>
#include <selinux/avc.h>
#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));

View File

@ -32,6 +32,7 @@
#include <sys/types.h>
#include <sys/wait.h>
#include <string>
#include <vector>
#include <cutils/fs.h>
#include <cutils/sockets.h>
@ -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<userid_t> 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);

View File

@ -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<userid_t> get_known_users(const char* volume_uuid) {
std::vector<userid_t> 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.