Add fixupAppDir() API.

This can be used to fixup application directories in case they have been
created by some other entity besides vold; the main use case for this
API right now is OBB directories, which can be created by installers
outside of vold; on devices without sdcardfs, such directories and the
files contained therein are not setup correctly. This API will make sure
everything is setup the way it needs to be setup.

Bug: 146419093
Test: inspect OBB dir after install
Change-Id: I2e35b7ac2992dbb21cc950e53651ffc07cfca907
diff --git a/Android.bp b/Android.bp
index a420078..676c958 100644
--- a/Android.bp
+++ b/Android.bp
@@ -167,6 +167,7 @@
     ],
     whole_static_libs: [
         "com.android.sysprop.apex",
+        "libc++fs"
     ],
 }
 
diff --git a/Utils.cpp b/Utils.cpp
index 35839ac..9f9b357 100644
--- a/Utils.cpp
+++ b/Utils.cpp
@@ -47,6 +47,7 @@
 #include <sys/xattr.h>
 #include <unistd.h>
 
+#include <filesystem>
 #include <list>
 #include <mutex>
 #include <regex>
@@ -228,7 +229,40 @@
     return ret;
 }
 
-int PrepareAppDirFromRoot(const std::string& path, const std::string& root, int appUid) {
+static int FixupAppDir(const std::string& path, mode_t mode, uid_t uid, gid_t gid, long projectId) {
+    namespace fs = std::filesystem;
+
+    // Setup the directory itself correctly
+    int ret = PrepareDirWithProjectId(path, mode, uid, gid, projectId);
+    if (ret != OK) {
+        return ret;
+    }
+
+    // Fixup all of its file entries
+    for (const auto& itEntry : fs::directory_iterator(path)) {
+        ret = lchown(itEntry.path().c_str(), uid, gid);
+        if (ret != 0) {
+            return ret;
+        }
+
+        ret = chmod(itEntry.path().c_str(), mode);
+        if (ret != 0) {
+            return ret;
+        }
+
+        if (!IsFilesystemSupported("sdcardfs")) {
+            ret = SetQuotaProjectId(itEntry.path(), projectId);
+            if (ret != 0) {
+                return ret;
+            }
+        }
+    }
+
+    return OK;
+}
+
+int PrepareAppDirFromRoot(const std::string& path, const std::string& root, int appUid,
+                          bool fixupExisting) {
     long projectId;
     size_t pos;
     int ret = 0;
@@ -302,7 +336,15 @@
         } else {
             projectId = uid - AID_APP_START + AID_EXT_GID_START;
         }
-        ret = PrepareDirWithProjectId(pathToCreate, mode, uid, gid, projectId);
+
+        if (fixupExisting && access(pathToCreate.c_str(), F_OK) == 0) {
+            // Fixup all files in this existing directory with the correct UID/GID
+            // and project ID.
+            ret = FixupAppDir(pathToCreate, mode, uid, gid, projectId);
+        } else {
+            ret = PrepareDirWithProjectId(pathToCreate, mode, uid, gid, projectId);
+        }
+
         if (ret != 0) {
             return ret;
         }
diff --git a/Utils.h b/Utils.h
index 54b8dd8..21abc4d 100644
--- a/Utils.h
+++ b/Utils.h
@@ -57,7 +57,8 @@
  * ONLY for use with app-specific data directories on external storage!
  * (eg, /Android/data/com.foo, /Android/obb/com.foo, etc.)
  */
-int PrepareAppDirFromRoot(const std::string& path, const std::string& root, int appUid);
+int PrepareAppDirFromRoot(const std::string& path, const std::string& root, int appUid,
+                          bool fixupExisting);
 
 /* 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 6aa9670..08b4661 100644
--- a/VoldNativeService.cpp
+++ b/VoldNativeService.cpp
@@ -466,6 +466,14 @@
     return translate(VolumeManager::Instance()->setupAppDir(path, appUid));
 }
 
+binder::Status VoldNativeService::fixupAppDir(const std::string& path, int32_t appUid) {
+    ENFORCE_SYSTEM_OR_ROOT;
+    CHECK_ARGUMENT_PATH(path);
+    ACQUIRE_LOCK;
+
+    return translate(VolumeManager::Instance()->fixupAppDir(path, appUid));
+}
+
 binder::Status VoldNativeService::createObb(const std::string& sourcePath,
                                             const std::string& sourceKey, int32_t ownerGid,
                                             std::string* _aidl_return) {
diff --git a/VoldNativeService.h b/VoldNativeService.h
index 6d00d2d..e04c259 100644
--- a/VoldNativeService.h
+++ b/VoldNativeService.h
@@ -66,6 +66,7 @@
     binder::Status remountUid(int32_t uid, int32_t remountMode);
 
     binder::Status setupAppDir(const std::string& path, int32_t appUid);
+    binder::Status fixupAppDir(const std::string& path, 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 6f15846..f7b36bf 100644
--- a/VolumeManager.cpp
+++ b/VolumeManager.cpp
@@ -814,7 +814,7 @@
     return 0;
 }
 
-int VolumeManager::setupAppDir(const std::string& path, int32_t appUid) {
+int VolumeManager::setupAppDir(const std::string& path, int32_t appUid, bool fixupExistingOnly) {
     // Only offer to create directories for paths managed by vold
     if (!StartsWith(path, "/storage/")) {
         LOG(ERROR) << "Failed to find mounted volume for " << path;
@@ -859,8 +859,21 @@
 
     const std::string volumeRoot = volume->getRootPath();  // eg /data/media/0
 
+    if (fixupExistingOnly && (access(lowerPath.c_str(), F_OK) != 0)) {
+        // Nothing to fixup
+        return OK;
+    }
+
     // Create the app paths we need from the root
-    return PrepareAppDirFromRoot(lowerPath, volumeRoot, appUid);
+    return PrepareAppDirFromRoot(lowerPath, volumeRoot, appUid, fixupExistingOnly);
+}
+
+int VolumeManager::fixupAppDir(const std::string& path, int32_t appUid) {
+    if (IsFilesystemSupported("sdcardfs")) {
+        //sdcardfs magically does this for us
+        return OK;
+    }
+    return setupAppDir(path, appUid, true /* fixupExistingOnly */);
 }
 
 int VolumeManager::createObb(const std::string& sourcePath, const std::string& sourceKey,
diff --git a/VolumeManager.h b/VolumeManager.h
index 765349d..afea54e 100644
--- a/VolumeManager.h
+++ b/VolumeManager.h
@@ -157,12 +157,23 @@
      * requirements of the underlying filesystem and are of no concern to the
      * caller.
      *
+     * If fixupExistingOnly is set, we make sure to fixup any existing dirs and
+     * files in the passed in path, but only if that path exists; if it doesn't
+     * exist, this function doesn't create them.
+     *
      * 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 setupAppDir(const std::string& path, int32_t appUid);
+    int setupAppDir(const std::string& path, int32_t appUid, bool fixupExistingOnly = false);
+
+    /**
+     * Fixes up an existing application directory, as if it was created with
+     * setupAppDir() above. This includes fixing up the UID/GID, permissions and
+     * project IDs of the contained files and directories.
+     */
+    int fixupAppDir(const std::string& path, 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 78598b3..f1ada6c 100644
--- a/binder/android/os/IVold.aidl
+++ b/binder/android/os/IVold.aidl
@@ -55,6 +55,7 @@
     void remountUid(int uid, int remountMode);
 
     void setupAppDir(@utf8InCpp String path, int appUid);
+    void fixupAppDir(@utf8InCpp String path, int appUid);
 
     @utf8InCpp String createObb(@utf8InCpp String sourcePath, @utf8InCpp String sourceKey,
                                 int ownerGid);