Shared OBB storage across users.

To avoid downloading large OBB files separately for each user,
provide a shared view of /sdcard/Android/obb to all apps.  Added
upgrade step to migrate the owners existing OBB files to become
the default view.

Bug: 7008879
Change-Id: I199321552fa7d4b97d5ed7fc3b3bc41f23618601
diff --git a/cmds/installd/commands.c b/cmds/installd/commands.c
index a52f74a..ab64747 100644
--- a/cmds/installd/commands.c
+++ b/cmds/installd/commands.c
@@ -241,7 +241,6 @@
 {
     char src_data_dir[PKG_PATH_MAX];
     char pkg_path[PKG_PATH_MAX];
-    char media_path[PATH_MAX];
     DIR *d;
     struct dirent *de;
     struct stat s;
@@ -250,9 +249,6 @@
     if (create_persona_path(src_data_dir, src_persona)) {
         return -1;
     }
-    if (create_persona_media_path(media_path, (userid_t) target_persona) == -1) {
-        return -1;
-    }
 
     d = opendir(src_data_dir);
     if (d != NULL) {
@@ -281,10 +277,10 @@
         closedir(d);
     }
 
-    // ensure /data/media/<user_id> exists
-    if (ensure_dir(media_path, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) {
+    if (ensure_media_user_dirs((userid_t) target_persona) == -1) {
         return -1;
     }
+
     return 0;
 }
 
diff --git a/cmds/installd/installd.c b/cmds/installd/installd.c
index d51004a..d559639 100644
--- a/cmds/installd/installd.c
+++ b/cmds/installd/installd.c
@@ -333,19 +333,16 @@
 
 int initialize_directories() {
     int res = -1;
-    int version = 0;
-    FILE* file;
 
     // Read current filesystem layout version to handle upgrade paths
     char version_path[PATH_MAX];
-    if (snprintf(version_path, PATH_MAX, "%s.layout_version", android_data_dir.path) > PATH_MAX) {
-        return -1;
+    snprintf(version_path, PATH_MAX, "%s.layout_version", android_data_dir.path);
+
+    int oldVersion;
+    if (fs_read_atomic_int(version_path, &oldVersion) == -1) {
+        oldVersion = 0;
     }
-    file = fopen(version_path, "r");
-    if (file != NULL) {
-        fscanf(file, "%d", &version);
-        fclose(file);
-    }
+    int version = oldVersion;
 
     // /data/user
     char *user_data_dir = build_string2(android_data_dir.path, SECONDARY_USER_PREFIX);
@@ -376,16 +373,12 @@
         }
     }
 
-    // /data/media/0
-    char owner_media_dir[PATH_MAX];
-    create_persona_media_path(owner_media_dir, 0);
-
     if (version == 0) {
         // Introducing multi-user, so migrate /data/media contents into /data/media/0
-        ALOGD("Migrating /data/media for multi-user");
+        ALOGD("Upgrading /data/media for multi-user");
 
         // Ensure /data/media
-        if (ensure_dir(android_media_dir.path, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) {
+        if (fs_prepare_dir(android_media_dir.path, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) {
             goto fail;
         }
 
@@ -402,10 +395,14 @@
         }
 
         // Create /data/media again
-        if (ensure_dir(android_media_dir.path, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) {
+        if (fs_prepare_dir(android_media_dir.path, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) {
             goto fail;
         }
 
+        // /data/media/0
+        char owner_media_dir[PATH_MAX];
+        snprintf(owner_media_dir, PATH_MAX, "%s0", android_media_dir.path);
+
         // Move any owner data into place
         if (access(media_tmp_dir, F_OK) == 0) {
             if (rename(media_tmp_dir, owner_media_dir) == -1) {
@@ -433,8 +430,7 @@
 
                     // /data/media/<user_id>
                     snprintf(user_media_dir, PATH_MAX, "%s%s", android_media_dir.path, name);
-                    if (ensure_dir(user_media_dir, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) {
-                        ALOGE("Failed to ensure %s: %s", user_media_dir, strerror(errno));
+                    if (fs_prepare_dir(user_media_dir, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) {
                         goto fail;
                     }
                 }
@@ -445,20 +441,44 @@
         version = 1;
     }
 
-    // Ensure /data/media/0 is always ready
-    if (ensure_dir(owner_media_dir, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) {
+    // /data/media/obb
+    char media_obb_dir[PATH_MAX];
+    snprintf(media_obb_dir, PATH_MAX, "%sobb", android_media_dir.path);
+
+    if (version == 1) {
+        // Introducing /data/media/obb for sharing OBB across users; migrate
+        // any existing OBB files from owner.
+        ALOGD("Upgrading to shared /data/media/obb");
+
+        // /data/media/0/Android/obb
+        char owner_obb_path[PATH_MAX];
+        snprintf(owner_obb_path, PATH_MAX, "%s0/Android/obb", android_media_dir.path);
+
+        // Only move if target doesn't already exist
+        if (access(media_obb_dir, F_OK) != 0 && access(owner_obb_path, F_OK) == 0) {
+            if (rename(owner_obb_path, media_obb_dir) == -1) {
+                ALOGE("Failed to move OBB from owner: %s", strerror(errno));
+                goto fail;
+            }
+        }
+
+        version = 2;
+    }
+
+    if (ensure_media_user_dirs(0) == -1) {
+        ALOGE("Failed to setup media for user 0");
+        goto fail;
+    }
+    if (fs_prepare_dir(media_obb_dir, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) {
         goto fail;
     }
 
-    // Persist our current version
-    file = fopen(version_path, "w");
-    if (file != NULL) {
-        fprintf(file, "%d", version);
-        fsync(fileno(file));
-        fclose(file);
-    } else {
-        ALOGE("Failed to save version to %s: %s", version_path, strerror(errno));
-        goto fail;
+    // Persist layout version if changed
+    if (version != oldVersion) {
+        if (fs_write_atomic_int(version_path, version) == -1) {
+            ALOGE("Failed to save version to %s: %s", version_path, strerror(errno));
+            goto fail;
+        }
     }
 
     // Success!
diff --git a/cmds/installd/installd.h b/cmds/installd/installd.h
index 3201427..5b81d2c 100644
--- a/cmds/installd/installd.h
+++ b/cmds/installd/installd.h
@@ -32,6 +32,7 @@
 #include <sys/types.h>
 #include <sys/wait.h>
 
+#include <cutils/fs.h>
 #include <cutils/sockets.h>
 #include <cutils/log.h>
 #include <cutils/properties.h>
diff --git a/cmds/installd/utils.c b/cmds/installd/utils.c
index 80247f1..625a35e 100644
--- a/cmds/installd/utils.c
+++ b/cmds/installd/utils.c
@@ -991,39 +991,14 @@
     return result;
 }
 
-/* Ensure that directory exists with given mode and owners. */
-int ensure_dir(const char* path, mode_t mode, uid_t uid, gid_t gid) {
-    // Check if path needs to be created
-    struct stat sb;
-    if (stat(path, &sb) == -1) {
-        if (errno == ENOENT) {
-            goto create;
-        } else {
-            ALOGE("Failed to stat(%s): %s", path, strerror(errno));
-            return -1;
-        }
-    }
+/* Ensure that /data/media directories are prepared for given user. */
+int ensure_media_user_dirs(userid_t userid) {
+    char media_user_path[PATH_MAX];
+    char path[PATH_MAX];
 
-    // Exists, verify status
-    if (sb.st_mode == mode || sb.st_uid == uid || sb.st_gid == gid) {
-        return 0;
-    } else {
-        goto fixup;
-    }
-
-create:
-    if (mkdir(path, mode) == -1) {
-        ALOGE("Failed to mkdir(%s): %s", path, strerror(errno));
-        return -1;
-    }
-
-fixup:
-    if (chown(path, uid, gid) == -1) {
-        ALOGE("Failed to chown(%s, %d, %d): %s", path, uid, gid, strerror(errno));
-        return -1;
-    }
-    if (chmod(path, mode) == -1) {
-        ALOGE("Failed to chown(%s, %d): %s", path, mode, strerror(errno));
+    // Ensure /data/media/<userid> exists
+    create_persona_media_path(media_user_path, userid);
+    if (fs_prepare_dir(media_user_path, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) {
         return -1;
     }