Setup Android/, Android/data and Android/obb dirs correctly.

Normally sdcardfs takes care of setting up these directories on-demand,
for example when an app requests its private data directory to be
created. On devices without sdcardfs however, we ourselves need to make
sure to setup the UID/GID of these directories correctly.

Introduce a new PrepareAndroidDirs() function which sets the dirs up
correctly. On devices without sdcardfs, that means:

Path              UID         GID         mode
/Android          media_rw    media_rw     771
/Android/data     media_rw    ext_data_rw  771
/Android/obb      media_rw    ext_obb_rw   771

Bug: 146419093
Test: wipe Android/, reboot, with and without sdcardfs, verify
      contents

Change-Id: I3a879089422c7fc449b6a3e6f1c4b386b86687a4
diff --git a/Utils.cpp b/Utils.cpp
index 3915667..be4d293 100644
--- a/Utils.cpp
+++ b/Utils.cpp
@@ -134,7 +134,7 @@
     return ioctl(fd, FS_IOC_FSSETXATTR, &fsx);
 }
 
-int PrepareDirsFromRoot(std::string path, std::string root, mode_t mode, uid_t uid, gid_t gid) {
+int PrepareAppDirsFromRoot(std::string path, std::string root, mode_t mode, uid_t uid, gid_t gid) {
     int ret = 0;
     if (!StartsWith(path, root)) {
         return -1;
@@ -1164,5 +1164,31 @@
     return result;
 }
 
+status_t PrepareAndroidDirs(const std::string& volumeRoot) {
+    std::string androidDir = volumeRoot + kAndroidDir;
+    std::string androidDataDir = volumeRoot + kAppDataDir;
+    std::string androidObbDir = volumeRoot + kAppObbDir;
+
+    bool useSdcardFs = IsFilesystemSupported("sdcardfs");
+
+    if (fs_prepare_dir(androidDir.c_str(), 0771, AID_MEDIA_RW, AID_MEDIA_RW) != 0) {
+        PLOG(ERROR) << "Failed to create " << androidDir;
+        return -errno;
+    }
+
+    gid_t dataGid = useSdcardFs ? AID_MEDIA_RW : AID_EXT_DATA_RW;
+    if (fs_prepare_dir(androidDataDir.c_str(), 0771, AID_MEDIA_RW, dataGid) != 0) {
+        PLOG(ERROR) << "Failed to create " << androidDataDir;
+        return -errno;
+    }
+
+    gid_t obbGid = useSdcardFs ? AID_MEDIA_RW : AID_EXT_OBB_RW;
+    if (fs_prepare_dir(androidObbDir.c_str(), 0771, AID_MEDIA_RW, obbGid) != 0) {
+        PLOG(ERROR) << "Failed to create " << androidObbDir;
+        return -errno;
+    }
+
+    return OK;
+}
 }  // namespace vold
 }  // namespace android
diff --git a/Utils.h b/Utils.h
index 42e8b4e..ec42f39 100644
--- a/Utils.h
+++ b/Utils.h
@@ -36,6 +36,11 @@
 
 static const char* kPropFuse = "persist.sys.fuse";
 
+static const char* kAndroidDir = "/Android/";
+static const char* kAppDataDir = "/Android/data/";
+static const char* kAppMediaDir = "/Android/media/";
+static const char* kAppObbDir = "/Android/obb/";
+
 /* SELinux contexts used depending on the block device type */
 extern security_context_t sBlkidContext;
 extern security_context_t sBlkidUntrustedContext;
@@ -52,8 +57,10 @@
 /*
  * Recursively calls fs_prepare_dir() on all components in 'path', starting at 'root'.
  * 'path' must start with 'root'
+ * ONLY for use with app-specific data directories on external storage!
+ * (eg, /Android/data/com.foo, /Android/obb/com.foo, etc.)
  */
-int PrepareDirsFromRoot(std::string path, std::string root, mode_t mode, uid_t uid, gid_t gid);
+int PrepareAppDirsFromRoot(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);
@@ -164,6 +171,7 @@
 status_t UnmountUserFuse(userid_t userId, const std::string& absolute_lower_path,
                          const std::string& relative_upper_path);
 
+status_t PrepareAndroidDirs(const std::string& volumeRoot);
 }  // namespace vold
 }  // namespace android
 
diff --git a/VolumeManager.cpp b/VolumeManager.cpp
index 3de89ab..e6d593a 100644
--- a/VolumeManager.cpp
+++ b/VolumeManager.cpp
@@ -81,7 +81,9 @@
 using android::vold::CreateDir;
 using android::vold::DeleteDirContents;
 using android::vold::DeleteDirContentsAndDir;
-using android::vold::PrepareDirsFromRoot;
+using android::vold::IsFilesystemSupported;
+using android::vold::PrepareAndroidDirs;
+using android::vold::PrepareAppDirsFromRoot;
 using android::vold::PrivateVolume;
 using android::vold::Symlink;
 using android::vold::Unlink;
@@ -822,6 +824,29 @@
     return 0;
 }
 
+static gid_t getAppDirGid(const std::string& appDir) {
+    // Create app-specific dirs with the correct UID/GID
+    gid_t gid = AID_MEDIA_RW;
+    if (!IsFilesystemSupported("sdcardfs")) {
+        if (appDir == android::vold::kAppDataDir) {
+            gid = AID_EXT_DATA_RW;
+        } else if (appDir == android::vold::kAppObbDir) {
+            gid = AID_EXT_OBB_RW;
+        } else if (appDir == android::vold::kAppMediaDir) {
+            gid = AID_MEDIA_RW;
+        } else {
+            gid = AID_MEDIA_RW;
+        }
+    }
+
+    return gid;
+}
+
+static bool isValidAppDirRoot(const std::string& appDirRoot) {
+    return appDirRoot == android::vold::kAppDataDir || appDirRoot == android::vold::kAppMediaDir ||
+           appDirRoot == android::vold::kAppObbDir;
+}
+
 int VolumeManager::setupAppDir(const std::string& path, const std::string& appDirRoot,
                                int32_t appUid) {
     // Only offer to create directories for paths managed by vold
@@ -868,14 +893,25 @@
     const std::string lowerAppDirRoot =
             volume->getInternalPath() + appDirRoot.substr(volume->getPath().length());
 
-    // First create the root which holds app dirs, if needed.
-    int ret = PrepareDirsFromRoot(lowerAppDirRoot, volume->getInternalPath(), 0771, AID_MEDIA_RW,
-                                  AID_MEDIA_RW);
+    // Do some sanity checking on the app dir (relative from root)
+    const std::string volumeRoot = volume->getRootPath();  // eg /data/media/0
+
+    // eg, if lowerAppDirRoot = /data/media/0/Android/data, this is /Android/data
+    const std::string relativeAppRoot = lowerAppDirRoot.substr(volumeRoot.length());
+    if (!isValidAppDirRoot(relativeAppRoot)) {
+        LOG(ERROR) << path << " is not a valid application directory.";
+        return -EINVAL;
+    }
+
+    // Make sure the Android/ directories exist and are setup correctly
+    int ret = PrepareAndroidDirs(volumeRoot);
     if (ret != 0) {
+        LOG(ERROR) << "Failed to prepare Android/ directories.";
         return ret;
     }
-    // Then, create app-specific dirs with the correct UID/GID
-    return PrepareDirsFromRoot(lowerPath, lowerAppDirRoot, 0770, appUid, AID_MEDIA_RW);
+
+    gid_t gid = getAppDirGid(relativeAppRoot);
+    return PrepareAppDirsFromRoot(lowerPath, lowerAppDirRoot, 0770, appUid, gid);
 }
 
 int VolumeManager::createObb(const std::string& sourcePath, const std::string& sourceKey,
diff --git a/model/EmulatedVolume.cpp b/model/EmulatedVolume.cpp
index 082dea5..8f8c87a 100644
--- a/model/EmulatedVolume.cpp
+++ b/model/EmulatedVolume.cpp
@@ -73,17 +73,8 @@
     }
 }
 
-// Creates a bind mount from source to target, creating the source (!) directory
-// if not yet present.
+// Creates a bind mount from source to target
 static status_t doFuseBindMount(const std::string& source, const std::string& target) {
-    if (access(source.c_str(), F_OK) != 0) {
-        // Android path may not exist yet if users has just been created; create it on
-        // the lower fs.
-        if (fs_prepare_dir(source.c_str(), 0771, AID_MEDIA_RW, AID_MEDIA_RW) != 0) {
-            PLOG(ERROR) << "Failed to create " << source;
-            return -errno;
-        }
-    }
     LOG(INFO) << "Bind mounting " << source << " on " << target;
     auto status = BindMount(source, target);
     if (status != OK) {
@@ -232,11 +223,19 @@
         LOG(INFO) << "Mounting emulated fuse volume";
         android::base::unique_fd fd;
         int user_id = getMountUserId();
-        int result = MountUserFuse(user_id, getInternalPath(), label, &fd);
+        auto volumeRoot = getRootPath();
 
-        if (result != 0) {
+        // Make sure Android/ dirs exist for bind mounting
+        status_t res = PrepareAndroidDirs(volumeRoot);
+        if (res != OK) {
+            LOG(ERROR) << "Failed to prepare Android/ directories";
+            return res;
+        }
+
+        res = MountUserFuse(user_id, getInternalPath(), label, &fd);
+        if (res != 0) {
             PLOG(ERROR) << "Failed to mount emulated fuse volume";
-            return -result;
+            return res;
         }
 
         mFuseMounted = true;
@@ -252,7 +251,7 @@
         }
 
         // Only do the bind-mounts when we know for sure the FUSE daemon can resolve the path.
-        status_t res = mountFuseBindMounts();
+        res = mountFuseBindMounts();
         if (res != OK) {
             fd.reset();
             doUnmount();
@@ -317,5 +316,12 @@
     return OK;
 }
 
+std::string EmulatedVolume::getRootPath() const {
+    int user_id = getMountUserId();
+    std::string volumeRoot = StringPrintf("%s/%d", getInternalPath().c_str(), user_id);
+
+    return volumeRoot;
+}
+
 }  // namespace vold
 }  // namespace android
diff --git a/model/EmulatedVolume.h b/model/EmulatedVolume.h
index 4f76a60..3f1b2e3 100644
--- a/model/EmulatedVolume.h
+++ b/model/EmulatedVolume.h
@@ -40,6 +40,7 @@
     explicit EmulatedVolume(const std::string& rawPath, int userId);
     EmulatedVolume(const std::string& rawPath, dev_t device, const std::string& fsUuid, int userId);
     virtual ~EmulatedVolume();
+    std::string getRootPath() const override;
 
   protected:
     status_t doMount() override;
diff --git a/model/VolumeBase.cpp b/model/VolumeBase.cpp
index 636c065..687d4f7 100644
--- a/model/VolumeBase.cpp
+++ b/model/VolumeBase.cpp
@@ -274,6 +274,11 @@
     return -ENOTSUP;
 }
 
+std::string VolumeBase::getRootPath() const {
+    // Usually the same as the internal path, except for emulated volumes.
+    return getInternalPath();
+}
+
 std::ostream& VolumeBase::operator<<(std::ostream& stream) const {
     return stream << " VolumeBase{id=" << mId << ",mountFlags=" << mMountFlags
                   << ",mountUserId=" << mMountUserId << "}";
diff --git a/model/VolumeBase.h b/model/VolumeBase.h
index 1d88d1b..078bb0c 100644
--- a/model/VolumeBase.h
+++ b/model/VolumeBase.h
@@ -110,6 +110,8 @@
     status_t unmount();
     status_t format(const std::string& fsType);
 
+    virtual std::string getRootPath() const;
+
     std::ostream& operator<<(std::ostream& stream) const;
 
   protected: