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:
parent
44a38d9337
commit
e363724230
@ -12,7 +12,10 @@ LOCAL_MODULE := libinstalld
|
|||||||
LOCAL_MODULE_TAGS := eng tests
|
LOCAL_MODULE_TAGS := eng tests
|
||||||
LOCAL_SRC_FILES := $(common_src_files)
|
LOCAL_SRC_FILES := $(common_src_files)
|
||||||
LOCAL_CFLAGS := $(common_cflags)
|
LOCAL_CFLAGS := $(common_cflags)
|
||||||
LOCAL_SHARED_LIBRARIES := libbase
|
LOCAL_SHARED_LIBRARIES := \
|
||||||
|
libbase \
|
||||||
|
liblogwrap \
|
||||||
|
|
||||||
LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
|
LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
|
||||||
LOCAL_CLANG := true
|
LOCAL_CLANG := true
|
||||||
include $(BUILD_STATIC_LIBRARY)
|
include $(BUILD_STATIC_LIBRARY)
|
||||||
@ -30,6 +33,7 @@ LOCAL_SHARED_LIBRARIES := \
|
|||||||
libbase \
|
libbase \
|
||||||
libcutils \
|
libcutils \
|
||||||
liblog \
|
liblog \
|
||||||
|
liblogwrap \
|
||||||
libselinux \
|
libselinux \
|
||||||
|
|
||||||
LOCAL_STATIC_LIBRARIES := libdiskusage
|
LOCAL_STATIC_LIBRARIES := libdiskusage
|
||||||
|
@ -16,15 +16,18 @@
|
|||||||
|
|
||||||
#include "installd.h"
|
#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 <inttypes.h>
|
||||||
#include <sys/capability.h>
|
#include <sys/capability.h>
|
||||||
#include <sys/file.h>
|
#include <sys/file.h>
|
||||||
#include <cutils/sched_policy.h>
|
#include <unistd.h>
|
||||||
#include <diskusage/dirsize.h>
|
|
||||||
#include <selinux/android.h>
|
|
||||||
#include <system/thread_defs.h>
|
|
||||||
#include <base/stringprintf.h>
|
|
||||||
#include <base/logging.h>
|
|
||||||
|
|
||||||
using android::base::StringPrintf;
|
using android::base::StringPrintf;
|
||||||
|
|
||||||
@ -38,6 +41,8 @@ dir_rec_t android_media_dir;
|
|||||||
dir_rec_t android_mnt_expand_dir;
|
dir_rec_t android_mnt_expand_dir;
|
||||||
dir_rec_array_t android_system_dirs;
|
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)
|
int install(const char *uuid, const char *pkgname, uid_t uid, gid_t gid, const char *seinfo)
|
||||||
{
|
{
|
||||||
if ((uid < AID_SYSTEM) || (gid < AID_SYSTEM)) {
|
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;
|
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)
|
int make_user_config(userid_t userid)
|
||||||
{
|
{
|
||||||
if (ensure_config_user_dirs(userid) == -1) {
|
if (ensure_config_user_dirs(userid) == -1) {
|
||||||
@ -1592,7 +1671,7 @@ int restorecon_data(const char* uuid, const char* pkgName,
|
|||||||
continue;
|
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) {
|
if (stat(pkgdir.c_str(), &s) < 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -14,14 +14,15 @@
|
|||||||
** limitations under the License.
|
** limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "installd.h"
|
||||||
|
|
||||||
|
#include <base/logging.h>
|
||||||
|
|
||||||
#include <sys/capability.h>
|
#include <sys/capability.h>
|
||||||
#include <sys/prctl.h>
|
#include <sys/prctl.h>
|
||||||
#include <selinux/android.h>
|
#include <selinux/android.h>
|
||||||
#include <selinux/avc.h>
|
#include <selinux/avc.h>
|
||||||
|
|
||||||
#include "installd.h"
|
|
||||||
|
|
||||||
|
|
||||||
#define BUFFER_MAX 1024 /* input buffer for commands */
|
#define BUFFER_MAX 1024 /* input buffer for commands */
|
||||||
#define TOKEN_MAX 16 /* max number of arguments in buffer */
|
#define TOKEN_MAX 16 /* max number of arguments in buffer */
|
||||||
#define REPLY_MAX 256 /* largest reply allowed */
|
#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 */
|
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)
|
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]);
|
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 },
|
{ "rmcodecache", 3, do_rm_code_cache },
|
||||||
{ "getsize", 8, do_get_size },
|
{ "getsize", 8, do_get_size },
|
||||||
{ "rmuserdata", 3, do_rm_user_data },
|
{ "rmuserdata", 3, do_rm_user_data },
|
||||||
|
{ "mvuserdata", 5, do_mv_user_data },
|
||||||
{ "movefiles", 0, do_movefiles },
|
{ "movefiles", 0, do_movefiles },
|
||||||
{ "linklib", 4, do_linklib },
|
{ "linklib", 4, do_linklib },
|
||||||
{ "mkuserdata", 5, do_mk_user_data },
|
{ "mkuserdata", 5, do_mk_user_data },
|
||||||
@ -621,46 +629,6 @@ fail:
|
|||||||
return res;
|
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, ...) {
|
static int log_callback(int type, const char *fmt, ...) {
|
||||||
va_list ap;
|
va_list ap;
|
||||||
int priority;
|
int priority;
|
||||||
@ -682,13 +650,16 @@ static int log_callback(int type, const char *fmt, ...) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(const int argc __unused, const char *argv[] __unused) {
|
int main(const int argc __unused, char *argv[]) {
|
||||||
char buf[BUFFER_MAX];
|
char buf[BUFFER_MAX];
|
||||||
struct sockaddr addr;
|
struct sockaddr addr;
|
||||||
socklen_t alen;
|
socklen_t alen;
|
||||||
int lsocket, s;
|
int lsocket, s;
|
||||||
int selinux_enabled = (is_selinux_enabled() > 0);
|
int selinux_enabled = (is_selinux_enabled() > 0);
|
||||||
|
|
||||||
|
setenv("ANDROID_LOG_TAGS", "*:v", 1);
|
||||||
|
android::base::InitLogging(argv);
|
||||||
|
|
||||||
ALOGI("installd firing up\n");
|
ALOGI("installd firing up\n");
|
||||||
|
|
||||||
union selinux_callback cb;
|
union selinux_callback cb;
|
||||||
@ -710,8 +681,6 @@ int main(const int argc __unused, const char *argv[] __unused) {
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
drop_privileges();
|
|
||||||
|
|
||||||
lsocket = android_get_control_socket(SOCKET_PATH);
|
lsocket = android_get_control_socket(SOCKET_PATH);
|
||||||
if (lsocket < 0) {
|
if (lsocket < 0) {
|
||||||
ALOGE("Failed to get socket from environment: %s\n", strerror(errno));
|
ALOGE("Failed to get socket from environment: %s\n", strerror(errno));
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include <cutils/fs.h>
|
#include <cutils/fs.h>
|
||||||
#include <cutils/sockets.h>
|
#include <cutils/sockets.h>
|
||||||
@ -89,6 +90,8 @@
|
|||||||
#define DEXOPT_PATCHOAT_NEEDED 2
|
#define DEXOPT_PATCHOAT_NEEDED 2
|
||||||
#define DEXOPT_SELF_PATCHOAT_NEEDED 3
|
#define DEXOPT_SELF_PATCHOAT_NEEDED 3
|
||||||
|
|
||||||
|
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
|
||||||
|
|
||||||
/* data structures */
|
/* data structures */
|
||||||
|
|
||||||
typedef struct {
|
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::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_user_config_path(char path[PKG_PATH_MAX], userid_t userid);
|
||||||
|
|
||||||
int create_move_path(char path[PKG_PATH_MAX],
|
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 renamepkg(const char *oldpkgname, const char *newpkgname);
|
||||||
int fix_uid(const char *uuid, const char *pkgname, uid_t uid, gid_t gid);
|
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 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 make_user_config(userid_t userid);
|
||||||
int delete_user(const char *uuid, userid_t userid);
|
int delete_user(const char *uuid, userid_t userid);
|
||||||
int delete_cache(const char *uuid, const char *pkgname, 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 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,
|
int calculate_oat_file_path(char path[PKG_PATH_MAX], const char *oat_dir, const char *apk_path,
|
||||||
const char *instruction_set);
|
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);
|
||||||
|
@ -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);
|
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.
|
* Create the path name for config for a certain userid.
|
||||||
* Returns 0 on success, and -1 on failure.
|
* Returns 0 on success, and -1 on failure.
|
||||||
|
Loading…
Reference in New Issue
Block a user