Mount /dev/fuse on /mnt/user/<userid>/<volumeid>

Since system_server cannot mount devices by itself,
add a binder interface to vold that system_server
can call to initiate this mount when required.

BUG: 135341433
Test: manual
Test: atest --test-mapping packages/providers/MediaProvider
Test: ExternalStorageHostTest DownloadProviderTests

Change-Id: If4fd02a1f1a8d921a3f96783d8c73e085c5b7ca1
diff --git a/model/EmulatedVolume.cpp b/model/EmulatedVolume.cpp
index 552fe2f..ca314ef 100644
--- a/model/EmulatedVolume.cpp
+++ b/model/EmulatedVolume.cpp
@@ -15,10 +15,13 @@
  */
 
 #include "EmulatedVolume.h"
+
+#include "AppFuseUtil.h"
 #include "Utils.h"
 #include "VolumeManager.h"
 
 #include <android-base/logging.h>
+#include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <cutils/fs.h>
 #include <private/android_filesystem_config.h>
@@ -81,7 +84,22 @@
 
     dev_t before = GetDevice(mFuseFull);
 
+    bool isFuse = base::GetBoolProperty(kPropFuse, false);
+
+    if (isFuse) {
+        LOG(INFO) << "Mounting emulated fuse volume";
+        int fd = -1;
+        int result = MountUserFuse(getMountUserId(), label, &fd);
+        if (result != 0) {
+            PLOG(ERROR) << "Failed to mount emulated fuse volume";
+            return -result;
+        }
+        setDeviceFd(fd);
+        return OK;
+    }
+
     if (!(mFusePid = fork())) {
+        LOG(INFO) << "Executing sdcardfs";
         // clang-format off
         if (execl(kFusePath, kFusePath,
                 "-u", "1023", // AID_MEDIA_RW
@@ -131,6 +149,31 @@
     // ENOTCONN until the unmount completes. This is an exotic and unusual
     // error code and might cause broken behaviour in applications.
     KillProcessesUsingPath(getPath());
+
+    bool isFuse = base::GetBoolProperty(kPropFuse, false);
+    if (isFuse) {
+        // We could have migrated storage to an adopted private volume, so always
+        // call primary storage "emulated" to avoid media rescans.
+        std::string label = mLabel;
+        if (getMountFlags() & MountFlags::kPrimary) {
+            label = "emulated";
+        }
+        std::string path(StringPrintf("/mnt/user/%d/%s", getMountUserId(), label.c_str()));
+        status_t result = ForceUnmount(path);
+        if (result != OK) {
+            // TODO(135341433): MNT_DETACH is needed for fuse because umount2 can fail with EBUSY.
+            // Figure out why we get EBUSY and remove this special casing if possible.
+            if (!umount2(path.c_str(), UMOUNT_NOFOLLOW | MNT_DETACH) || errno == EINVAL ||
+                errno == ENOENT) {
+                PLOG(INFO) << "ForceUnmount failed on emulated fuse volume";
+            }
+        }
+
+        rmdir(path.c_str());
+        setDeviceFd(-1);
+        return OK;
+    }
+
     ForceUnmount(mFuseDefault);
     ForceUnmount(mFuseRead);
     ForceUnmount(mFuseWrite);
diff --git a/model/PublicVolume.cpp b/model/PublicVolume.cpp
index 0a6b351..2c0b4cc 100644
--- a/model/PublicVolume.cpp
+++ b/model/PublicVolume.cpp
@@ -15,6 +15,8 @@
  */
 
 #include "PublicVolume.h"
+
+#include "AppFuseUtil.h"
 #include "Utils.h"
 #include "VolumeManager.h"
 #include "fs/Exfat.h"
@@ -167,6 +169,20 @@
 
     dev_t before = GetDevice(mFuseFull);
 
+    bool isFuse = base::GetBoolProperty(kPropFuse, false);
+
+    if (isFuse) {
+        LOG(INFO) << "Mounting public fuse volume";
+        int fd = -1;
+        int result = MountUserFuse(getMountUserId(), stableName, &fd);
+        if (result != 0) {
+            LOG(ERROR) << "Failed to mount public fuse volume";
+            return -result;
+        }
+        setDeviceFd(fd);
+        return OK;
+    }
+
     if (!(mFusePid = fork())) {
         if (getMountFlags() & MountFlags::kPrimary) {
             // clang-format off
@@ -229,6 +245,30 @@
     // error code and might cause broken behaviour in applications.
     KillProcessesUsingPath(getPath());
 
+    bool isFuse = base::GetBoolProperty(kPropFuse, false);
+    if (isFuse) {
+        // Use UUID as stable name, if available
+        std::string stableName = getId();
+        if (!mFsUuid.empty()) {
+            stableName = mFsUuid;
+        }
+
+        std::string path(StringPrintf("/mnt/user/%d/%s", getMountUserId(), stableName.c_str()));
+        status_t result = ForceUnmount(path);
+        if (result != OK) {
+            // TODO(135341433): MNT_DETACH is needed for fuse because umount2 can fail with EBUSY.
+            // Figure out why we get EBUSY and remove this special casing if possible.
+            if (!umount2(path.c_str(), UMOUNT_NOFOLLOW | MNT_DETACH) || errno == EINVAL ||
+                errno == ENOENT) {
+                PLOG(INFO) << "ForceUnmount failed on public fuse volume";
+            }
+        }
+
+        rmdir(path.c_str());
+        setDeviceFd(-1);
+        return OK;
+    }
+
     ForceUnmount(kAsecPath);
 
     ForceUnmount(mFuseDefault);
diff --git a/model/VolumeBase.cpp b/model/VolumeBase.cpp
index ffc7900..fa007ad 100644
--- a/model/VolumeBase.cpp
+++ b/model/VolumeBase.cpp
@@ -143,6 +143,16 @@
     return OK;
 }
 
+status_t VolumeBase::setDeviceFd(int deviceFd) {
+    if ((mState != State::kChecking)) {
+        LOG(WARNING) << getId() << " device fd change requires state checking";
+        return -EBUSY;
+    }
+
+    mDeviceFd.reset(deviceFd);
+    return OK;
+}
+
 android::sp<android::os::IVoldListener> VolumeBase::getListener() const {
     if (mSilent) {
         return nullptr;
diff --git a/model/VolumeBase.h b/model/VolumeBase.h
index 53eeb6f..fa9eee4 100644
--- a/model/VolumeBase.h
+++ b/model/VolumeBase.h
@@ -87,6 +87,7 @@
     State getState() const { return mState; }
     const std::string& getPath() const { return mPath; }
     const std::string& getInternalPath() const { return mInternalPath; }
+    int getDeviceFd() const { return dup(mDeviceFd.get()); }
 
     status_t setDiskId(const std::string& diskId);
     status_t setPartGuid(const std::string& partGuid);
@@ -121,6 +122,7 @@
     status_t setId(const std::string& id);
     status_t setPath(const std::string& path);
     status_t setInternalPath(const std::string& internalPath);
+    status_t setDeviceFd(int deviceFd);
 
     android::sp<android::os::IVoldListener> getListener() const;
 
@@ -147,6 +149,8 @@
     std::string mInternalPath;
     /* Flag indicating that volume should emit no events */
     bool mSilent;
+    /* The filedescriptor for the fuse device, if the volume uses fuse, or -1 otherwise */
+    android::base::unique_fd mDeviceFd;
 
     /* Volumes stacked on top of this volume */
     std::list<std::shared_ptr<VolumeBase>> mVolumes;