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/Utils.cpp b/Utils.cpp
index 1616d80..bd3d452 100644
--- a/Utils.cpp
+++ b/Utils.cpp
@@ -165,7 +165,7 @@
     if (!umount2(cpath, UMOUNT_NOFOLLOW) || errno == EINVAL || errno == ENOENT) {
         return OK;
     }
-
+    PLOG(INFO) << "ForceUnmount failed";
     return -errno;
 }
 
@@ -985,5 +985,50 @@
     return true;
 }
 
+int MountUserFuse(userid_t user_id, const std::string& relative_path, int* device_fd) {
+    std::string path(StringPrintf("/mnt/user/%d/%s", user_id, relative_path.c_str()));
+
+    // Force remove the existing mount before we attempt to prepare the
+    // directory. If we have a dangling mount, then PrepareDir may fail if the
+    // indirection to FUSE doesn't work.
+    android::status_t result = android::vold::ForceUnmount(path);
+    if (result != android::OK) {
+        PLOG(ERROR) << "Failed to unmount " << path;
+        return -1;
+    }
+
+    // Create directories.
+    result = android::vold::PrepareDir(path, 0700, AID_ROOT, AID_ROOT);
+    if (result != android::OK) {
+        PLOG(ERROR) << "Failed to prepare directory " << path;
+        return -1;
+    }
+
+    // Open device fd.
+    *device_fd = open("/dev/fuse", O_RDWR | O_CLOEXEC);
+    if (*device_fd == -1) {
+        PLOG(ERROR) << "Failed to open /dev/fuse";
+        return -1;
+    }
+
+    // Note: leaving out default_permissions since we don't want kernel to do lower filesystem
+    // permission checks before routing to FUSE daemon.
+    const auto opts = StringPrintf(
+        "fd=%i,"
+        "rootmode=40000,"
+        "allow_other,"
+        "user_id=0,group_id=0,",
+        *device_fd);
+
+    const int result_int =
+        TEMP_FAILURE_RETRY(mount("/dev/fuse", path.c_str(), "fuse",
+                                 MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_NOATIME, opts.c_str()));
+    if (result_int != 0) {
+        PLOG(ERROR) << "Failed to mount " << path;
+        return -errno;
+    }
+    return 0;
+}
+
 }  // namespace vold
 }  // namespace android