summaryrefslogtreecommitdiffstats
path: root/cmds
diff options
context:
space:
mode:
authorJeff Sharkey <jsharkey@android.com>2015-04-08 20:56:42 -0700
committerJeff Sharkey <jsharkey@android.com>2015-04-10 00:21:35 -0700
commite36372423000a906bafae68844ebc6c42d09335a (patch)
tree1344453051c65a405a0ec2e2c3402ca3266bf9b7 /cmds
parent44a38d9337989742046c1e3faa6e7392ecc47cd4 (diff)
downloadframeworks_native-e36372423000a906bafae68844ebc6c42d09335a.zip
frameworks_native-e36372423000a906bafae68844ebc6c42d09335a.tar.gz
frameworks_native-e36372423000a906bafae68844ebc6c42d09335a.tar.bz2
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
Diffstat (limited to 'cmds')
-rw-r--r--cmds/installd/Android.mk6
-rw-r--r--cmds/installd/commands.cpp93
-rw-r--r--cmds/installd/installd.cpp61
-rw-r--r--cmds/installd/installd.h12
-rw-r--r--cmds/installd/utils.cpp32
5 files changed, 149 insertions, 55 deletions
diff --git a/cmds/installd/Android.mk b/cmds/installd/Android.mk
index 2171f4d..6dec7f6 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 32a03f4..de6fd96 100644
--- a/cmds/installd/commands.cpp
+++ b/cmds/installd/commands.cpp
@@ -16,15 +16,18 @@
#include "installd.h"
-#include <inttypes.h>
-#include <sys/capability.h>
-#include <sys/file.h>
+#include <base/stringprintf.h>
+#include <base/logging.h>
#include <cutils/sched_policy.h>
#include <diskusage/dirsize.h>
-#include <selinux/android.h>
+#include <logwrap/logwrap.h>
#include <system/thread_defs.h>
-#include <base/stringprintf.h>
-#include <base/logging.h>
+#include <selinux/android.h>
+
+#include <inttypes.h>
+#include <sys/capability.h>
+#include <sys/file.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;
}
diff --git a/cmds/installd/installd.cpp b/cmds/installd/installd.cpp
index 0f035d0..3a86181 100644
--- a/cmds/installd/installd.cpp
+++ b/cmds/installd/installd.cpp
@@ -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));
diff --git a/cmds/installd/installd.h b/cmds/installd/installd.h
index 395d0ea..f31bf4f 100644
--- a/cmds/installd/installd.h
+++ b/cmds/installd/installd.h
@@ -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);
diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp
index e10116e..ba411cd 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<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.