Merge "Replace mkdirs() with setupAppDir()."
diff --git a/Utils.cpp b/Utils.cpp
index d483418..67c48ad 100644
--- a/Utils.cpp
+++ b/Utils.cpp
@@ -55,6 +55,7 @@
 
 using namespace std::chrono_literals;
 using android::base::ReadFileToString;
+using android::base::StartsWith;
 using android::base::StringPrintf;
 
 namespace android {
@@ -114,6 +115,27 @@
     }
 }
 
+int PrepareDirsFromRoot(std::string path, std::string root, mode_t mode, uid_t uid, gid_t gid) {
+    int ret = 0;
+    if (!StartsWith(path, root)) {
+        return -1;
+    }
+    std::string to_create_from_root = path.substr(root.length());
+
+    size_t pos = 0;
+    while ((pos = to_create_from_root.find('/')) != std::string::npos) {
+        auto component = to_create_from_root.substr(0, pos);
+        to_create_from_root.erase(0, pos + 1);
+        root = root + component + "/";
+        ret = fs_prepare_dir(root.c_str(), mode, uid, gid);
+        if (ret) {
+            break;
+        }
+    }
+
+    return ret;
+}
+
 status_t PrepareDir(const std::string& path, mode_t mode, uid_t uid, gid_t gid) {
     std::lock_guard<std::mutex> lock(kSecurityLock);
     const char* cpath = path.c_str();
diff --git a/Utils.h b/Utils.h
index 4c0114a..056a635 100644
--- a/Utils.h
+++ b/Utils.h
@@ -48,6 +48,12 @@
 status_t CreateDeviceNode(const std::string& path, dev_t dev);
 status_t DestroyDeviceNode(const std::string& path);
 
+/*
+ * Recursively calls fs_prepare_dir() on all components in 'path', starting at 'root'.
+ * 'path' must start with 'root'
+ */
+int PrepareDirsFromRoot(std::string path, std::string root, mode_t mode, uid_t uid, gid_t gid);
+
 /* fs_prepare_dir wrapper that creates with SELinux context */
 status_t PrepareDir(const std::string& path, mode_t mode, uid_t uid, gid_t gid);
 
diff --git a/VoldNativeService.cpp b/VoldNativeService.cpp
index b82ea06..78d7ed3 100644
--- a/VoldNativeService.cpp
+++ b/VoldNativeService.cpp
@@ -456,12 +456,14 @@
     return translate(VolumeManager::Instance()->remountUid(uid, remountMode));
 }
 
-binder::Status VoldNativeService::mkdirs(const std::string& path) {
+binder::Status VoldNativeService::setupAppDir(const std::string& path,
+                                              const std::string& appDirRoot, int32_t appUid) {
     ENFORCE_SYSTEM_OR_ROOT;
     CHECK_ARGUMENT_PATH(path);
+    CHECK_ARGUMENT_PATH(appDirRoot);
     ACQUIRE_LOCK;
 
-    return translate(VolumeManager::Instance()->mkdirs(path));
+    return translate(VolumeManager::Instance()->setupAppDir(path, appDirRoot, appUid));
 }
 
 binder::Status VoldNativeService::createObb(const std::string& sourcePath,
diff --git a/VoldNativeService.h b/VoldNativeService.h
index ebd9041..5753b0b 100644
--- a/VoldNativeService.h
+++ b/VoldNativeService.h
@@ -65,7 +65,8 @@
 
     binder::Status remountUid(int32_t uid, int32_t remountMode);
 
-    binder::Status mkdirs(const std::string& path);
+    binder::Status setupAppDir(const std::string& path, const std::string& appDirRoot,
+                               int32_t appUid);
 
     binder::Status createObb(const std::string& sourcePath, const std::string& sourceKey,
                              int32_t ownerGid, std::string* _aidl_return);
diff --git a/VolumeManager.cpp b/VolumeManager.cpp
index 143f53c..bc843b4 100644
--- a/VolumeManager.cpp
+++ b/VolumeManager.cpp
@@ -81,6 +81,7 @@
 using android::vold::CreateDir;
 using android::vold::DeleteDirContents;
 using android::vold::DeleteDirContentsAndDir;
+using android::vold::PrepareDirsFromRoot;
 using android::vold::PrivateVolume;
 using android::vold::Symlink;
 using android::vold::Unlink;
@@ -806,16 +807,21 @@
     return 0;
 }
 
-int VolumeManager::mkdirs(const std::string& path) {
+int VolumeManager::setupAppDir(const std::string& path, const std::string& appDirRoot,
+                               int32_t appUid) {
     // Only offer to create directories for paths managed by vold
-    if (StartsWith(path, "/storage/")) {
-        std::string lower_path = "/mnt/runtime/default/" + path.substr(9);
-        // fs_mkdirs() does symlink checking and relative path enforcement
-        return fs_mkdirs(lower_path.c_str(), 0700);
-    } else {
+    if (!StartsWith(path, "/storage/")) {
         LOG(ERROR) << "Failed to find mounted volume for " << path;
         return -EINVAL;
     }
+
+    // First create the root which holds app dirs, if needed.
+    int ret = PrepareDirsFromRoot(appDirRoot, "/storage/", 0771, AID_MEDIA_RW, AID_MEDIA_RW);
+    if (ret != 0) {
+        return ret;
+    }
+    // Then, create app-specific dirs with the correct UID/GID
+    return PrepareDirsFromRoot(path, appDirRoot, 0770, appUid, AID_MEDIA_RW);
 }
 
 int VolumeManager::createObb(const std::string& sourcePath, const std::string& sourceKey,
diff --git a/VolumeManager.h b/VolumeManager.h
index fad3c00..db32ecd 100644
--- a/VolumeManager.h
+++ b/VolumeManager.h
@@ -113,13 +113,30 @@
     static VolumeManager* Instance();
 
     /*
-     * Ensure that all directories along given path exist, creating parent
-     * directories as needed.  Validates that given path is absolute and that
-     * it contains no relative "." or ".." paths or symlinks.  Last path segment
-     * is treated as filename and ignored, unless the path ends with "/".  Also
-     * ensures that path belongs to a volume managed by vold.
+     * Creates a directory 'path' for an application, automatically creating
+     * directories along the given path if they don't exist yet. 'appDirRoot'
+     * is the "root" directory for app-specific directories of this kind;
+     * 'path' must always start with 'appDirRoot'.
+     *
+     * Example:
+     *   path = /storage/emulated/0/Android/data/com.foo/files/
+     *   appDirRoot = /storage/emulated/0/Android/data/
+     *
+     * This function will set the UID of all app-specific directories below
+     * 'appDirRoot' to the 'appUid' argument. In the given example, the UID
+     * of /storage/emulated/0/Android/data/com.foo and
+     * /storage/emulated/0/Android/data/com.foo/files would be set to 'appUid'.
+     *
+     * The UID of the parent directories will be set according to the
+     * requirements of the underlying filesystem and are of no concern to the
+     * caller.
+     *
+     * Validates that given paths are absolute and that they contain no relative
+     * "." or ".." paths or symlinks.  Last path segment is treated as filename
+     * and ignored, unless the path ends with "/".  Also ensures that path
+     * belongs to a volume managed by vold.
      */
-    int mkdirs(const std::string& path);
+    int setupAppDir(const std::string& path, const std::string& appDirRoot, int32_t appUid);
 
     int createObb(const std::string& path, const std::string& key, int32_t ownerGid,
                   std::string* outVolId);
diff --git a/binder/android/os/IVold.aidl b/binder/android/os/IVold.aidl
index 975d94c..cec38c5 100644
--- a/binder/android/os/IVold.aidl
+++ b/binder/android/os/IVold.aidl
@@ -54,7 +54,7 @@
 
     void remountUid(int uid, int remountMode);
 
-    void mkdirs(@utf8InCpp String path);
+    void setupAppDir(@utf8InCpp String path, @utf8InCpp String appDirRoot, int appUid);
 
     @utf8InCpp String createObb(@utf8InCpp String sourcePath, @utf8InCpp String sourceKey,
                                 int ownerGid);