Add installd command to create /data/misc user dir

New command 'mkuser <id>' sets up a user directory in /data/misc/user/,
readable by all apps within that user and writeable by the local system
process.

Change-Id: I5ddde8c4a80f606e723bb41aa64581a0720842d5
diff --git a/cmds/installd/commands.c b/cmds/installd/commands.c
index 886fd3b..34a5353 100644
--- a/cmds/installd/commands.c
+++ b/cmds/installd/commands.c
@@ -265,27 +265,40 @@
     return 0;
 }
 
-int delete_user(userid_t userid)
+int create_user(userid_t userid)
 {
-    char data_path[PKG_PATH_MAX];
-    if (create_user_path(data_path, userid)) {
-        return -1;
-    }
-    if (delete_dir_contents(data_path, 1, NULL)) {
-        return -1;
-    }
-
-    char media_path[PATH_MAX];
-    if (create_user_media_path(media_path, userid) == -1) {
-        return -1;
-    }
-    if (delete_dir_contents(media_path, 1, NULL) == -1) {
+    if (ensure_config_user_dirs(userid) == -1) {
         return -1;
     }
 
     return 0;
 }
 
+int delete_user(userid_t userid)
+{
+    int status = 0;
+
+    char data_path[PKG_PATH_MAX];
+    if ((create_user_path(data_path, userid) != 0)
+            || (delete_dir_contents(data_path, 1, NULL) != 0)) {
+        status = -1;
+    }
+
+    char media_path[PATH_MAX];
+    if ((create_user_media_path(media_path, userid) != 0)
+            || (delete_dir_contents(media_path, 1, NULL) != 0)) {
+        status = -1;
+    }
+
+    char config_path[PATH_MAX];
+    if ((create_user_config_path(config_path, userid) != 0)
+            || (delete_dir_contents(config_path, 1, NULL) != 0)) {
+        status = -1;
+    }
+
+    return status;
+}
+
 int delete_cache(const char *pkgname, userid_t userid)
 {
     char cachedir[PKG_PATH_MAX];
diff --git a/cmds/installd/installd.c b/cmds/installd/installd.c
index 10bf5fa..064ee32 100644
--- a/cmds/installd/installd.c
+++ b/cmds/installd/installd.c
@@ -109,6 +109,11 @@
                              /* pkgname, uid, userid, seinfo */
 }
 
+static int do_mk_user(char **arg, char reply[REPLY_MAX])
+{
+    return create_user(atoi(arg[0])); /* userid */
+}
+
 static int do_rm_user(char **arg, char reply[REPLY_MAX])
 {
     return delete_user(atoi(arg[0])); /* userid */
@@ -157,6 +162,7 @@
     { "movefiles",            0, do_movefiles },
     { "linklib",              3, do_linklib },
     { "mkuserdata",           4, do_mk_user_data },
+    { "mkuser",               1, do_mk_user },
     { "rmuser",               1, do_rm_user },
     { "idmap",                3, do_idmap },
     { "restorecondata",       3, do_restorecon_data },
@@ -483,6 +489,42 @@
         goto fail;
     }
 
+    if (version == 2) {
+        ALOGD("Upgrading to /data/misc/user directories");
+
+        DIR *dir;
+        struct dirent *dirent;
+        char user_data_dir[PATH_MAX];
+
+        dir = opendir(user_data_dir);
+        if (dir != NULL) {
+            while ((dirent = readdir(dir))) {
+                if (dirent->d_type == DT_DIR) {
+                    const char *name = dirent->d_name;
+
+                    // skip "." and ".."
+                    if (name[0] == '.') {
+                        if (name[1] == 0) continue;
+                        if ((name[1] == '.') && (name[2] == 0)) continue;
+                    }
+
+                    // /data/misc/user/<user_id>
+                    if (ensure_config_user_dirs(atoi(name)) == -1) {
+                        goto fail;
+                    }
+                }
+            }
+            closedir(dir);
+        }
+
+        version = 3;
+    }
+
+    if (ensure_config_user_dirs(0) == -1) {
+        ALOGE("Failed to setup misc for user 0");
+        goto fail;
+    }
+
     // Persist layout version if changed
     if (version != oldVersion) {
         if (fs_write_atomic_int(version_path, version) == -1) {
diff --git a/cmds/installd/installd.h b/cmds/installd/installd.h
index 0f7119d..ff26e49 100644
--- a/cmds/installd/installd.h
+++ b/cmds/installd/installd.h
@@ -145,6 +145,8 @@
 
 int create_user_media_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],
                      const char* pkgname,
                      const char* leaf,
@@ -190,6 +192,7 @@
 
 int ensure_dir(const char* path, mode_t mode, uid_t uid, gid_t gid);
 int ensure_media_user_dirs(userid_t userid);
+int ensure_config_user_dirs(userid_t userid);
 int create_profile_file(const char *pkgname, gid_t gid);
 void remove_profile_file(const char *pkgname);
 
@@ -201,6 +204,7 @@
 int fix_uid(const char *pkgname, uid_t uid, gid_t gid);
 int delete_user_data(const char *pkgname, userid_t userid);
 int make_user_data(const char *pkgname, uid_t uid, userid_t userid, const char* seinfo);
+int create_user(userid_t userid);
 int delete_user(userid_t userid);
 int delete_cache(const char *pkgname, userid_t userid);
 int move_dex(const char *src, const char *dst, const char *instruction_set);
diff --git a/cmds/installd/utils.c b/cmds/installd/utils.c
index 671d031..420ad5e 100644
--- a/cmds/installd/utils.c
+++ b/cmds/installd/utils.c
@@ -149,6 +149,17 @@
     return 0;
 }
 
+/**
+ * Create the path name for config for a certain userid.
+ * Returns 0 on success, and -1 on failure.
+ */
+int create_user_config_path(char path[PATH_MAX], userid_t userid) {
+    if (snprintf(path, PATH_MAX, "%s%d", "/data/misc/user/", userid) > PATH_MAX) {
+        return -1;
+    }
+    return 0;
+}
+
 int create_move_path(char path[PKG_PATH_MAX],
     const char* pkgname,
     const char* leaf,
@@ -1006,6 +1017,23 @@
     return 0;
 }
 
+int ensure_config_user_dirs(userid_t userid) {
+    char config_user_path[PATH_MAX];
+    char path[PATH_MAX];
+
+    // writable by system, readable by any app within the same user
+    const int uid = (userid * AID_USER) + AID_SYSTEM;
+    const int gid = (userid * AID_USER) + AID_EVERYBODY;
+
+    // Ensure /data/misc/user/<userid> exists
+    create_user_config_path(config_user_path, userid);
+    if (fs_prepare_dir(config_user_path, 0750, uid, gid) == -1) {
+        return -1;
+    }
+
+   return 0;
+}
+
 int create_profile_file(const char *pkgname, gid_t gid) {
     const char *profile_dir = DALVIK_CACHE_PREFIX "profiles";
     struct stat profileStat;