Merge "Allow external_storage or media_rw gid access /mnt/media_rw"
diff --git a/Utils.cpp b/Utils.cpp
index 202b98d..67e92c9 100644
--- a/Utils.cpp
+++ b/Utils.cpp
@@ -115,6 +115,25 @@
     }
 }
 
+int SetQuotaProjectId(std::string path, long projectId) {
+    struct fsxattr fsx;
+
+    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
+    if (fd == -1) {
+        PLOG(ERROR) << "Failed to open " << path << " to set project id.";
+        return -1;
+    }
+
+    int ret = ioctl(fd, FS_IOC_FSGETXATTR, &fsx);
+    if (ret == -1) {
+        PLOG(ERROR) << "Failed to get extended attributes for " << path << " to get project id.";
+        return ret;
+    }
+
+    fsx.fsx_projid = projectId;
+    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 ret = 0;
     if (!StartsWith(path, root)) {
diff --git a/Utils.h b/Utils.h
index 056a635..42e8b4e 100644
--- a/Utils.h
+++ b/Utils.h
@@ -48,6 +48,7 @@
 status_t CreateDeviceNode(const std::string& path, dev_t dev);
 status_t DestroyDeviceNode(const std::string& path);
 
+int SetQuotaProjectId(std::string path, long projectId);
 /*
  * Recursively calls fs_prepare_dir() on all components in 'path', starting at 'root'.
  * 'path' must start with 'root'
diff --git a/VolumeManager.cpp b/VolumeManager.cpp
index d8b1e32..3de89ab 100644
--- a/VolumeManager.cpp
+++ b/VolumeManager.cpp
@@ -830,16 +830,47 @@
         return -EINVAL;
     }
 
+    // Find the volume it belongs to
+    auto filter_fn = [&](const VolumeBase& vol) {
+        if (vol.getState() != VolumeBase::State::kMounted) {
+            // The volume must be mounted
+            return false;
+        }
+        if ((vol.getMountFlags() & VolumeBase::MountFlags::kVisible) == 0) {
+            // and visible
+            return false;
+        }
+        if (vol.getInternalPath().empty()) {
+            return false;
+        }
+        if (vol.getMountUserId() != USER_UNKNOWN &&
+            vol.getMountUserId() != multiuser_get_user_id(appUid)) {
+            // The app dir must be created on a volume with the same user-id
+            return false;
+        }
+        if (!path.empty() && StartsWith(path, vol.getPath())) {
+            return true;
+        }
+
+        return false;
+    };
+    auto volume = findVolumeWithFilter(filter_fn);
+    if (volume == nullptr) {
+        LOG(ERROR) << "Failed to find mounted volume for " << path;
+        return -EINVAL;
+    }
     // Convert paths to lower filesystem paths to avoid making FUSE requests for these reasons:
     // 1. A FUSE request from vold puts vold at risk of hanging if the FUSE daemon is down
     // 2. The FUSE daemon prevents requests on /mnt/user/0/emulated/<userid != 0> and a request
     // on /storage/emulated/10 means /mnt/user/0/emulated/10
-    // TODO(b/146419093): Use lower filesystem paths that don't depend on sdcardfs
-    const std::string lowerPath = "/mnt/runtime/default/" + path.substr(9);
-    const std::string lowerAppDirRoot = "/mnt/runtime/default/" + appDirRoot.substr(9);
+    const std::string lowerPath =
+            volume->getInternalPath() + path.substr(volume->getPath().length());
+    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, "/mnt/runtime/default/", 0771, AID_MEDIA_RW, AID_MEDIA_RW);
+    int ret = PrepareDirsFromRoot(lowerAppDirRoot, volume->getInternalPath(), 0771, AID_MEDIA_RW,
+                                  AID_MEDIA_RW);
     if (ret != 0) {
         return ret;
     }
diff --git a/VolumeManager.h b/VolumeManager.h
index cacab85..eb48736 100644
--- a/VolumeManager.h
+++ b/VolumeManager.h
@@ -83,6 +83,24 @@
     std::shared_ptr<android::vold::Disk> findDisk(const std::string& id);
     std::shared_ptr<android::vold::VolumeBase> findVolume(const std::string& id);
 
+    template <typename Fn>
+    std::shared_ptr<android::vold::VolumeBase> findVolumeWithFilter(Fn fn) {
+        for (const auto& vol : mInternalEmulatedVolumes) {
+            if (fn(*vol)) {
+                return vol;
+            }
+        }
+        for (const auto& disk : mDisks) {
+            for (const auto& vol : disk->getVolumes()) {
+                if (fn(*vol)) {
+                    return vol;
+                }
+            }
+        }
+
+        return nullptr;
+    }
+
     void listVolumes(android::vold::VolumeBase::Type type, std::list<std::string>& list) const;
 
     const std::set<userid_t>& getStartedUsers() const { return mStartedUsers; }
diff --git a/model/Disk.cpp b/model/Disk.cpp
index b66c336..f8357a9 100644
--- a/model/Disk.cpp
+++ b/model/Disk.cpp
@@ -162,6 +162,17 @@
     }
 }
 
+std::vector<std::shared_ptr<VolumeBase>> Disk::getVolumes() const {
+    std::vector<std::shared_ptr<VolumeBase>> vols;
+    for (const auto& vol : mVolumes) {
+        vols.push_back(vol);
+        auto stackedVolumes = vol->getVolumes();
+        vols.insert(vols.end(), stackedVolumes.begin(), stackedVolumes.end());
+    }
+
+    return vols;
+}
+
 status_t Disk::create() {
     CHECK(!mCreated);
     mCreated = true;
diff --git a/model/Disk.h b/model/Disk.h
index 889e906..d82d141 100644
--- a/model/Disk.h
+++ b/model/Disk.h
@@ -67,6 +67,8 @@
 
     void listVolumes(VolumeBase::Type type, std::list<std::string>& list) const;
 
+    std::vector<std::shared_ptr<VolumeBase>> getVolumes() const;
+
     status_t create();
     status_t destroy();