Merge "Command to move private app data between volumes."
This commit is contained in:
commit
dfc30ae393
@ -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