Move even more vold commands over to Binder.

This moves fstrim, obb and appfuse commands over to the new Binder
interface.  This change also separates creating/destroying and
mounting/unmounting of OBB volumes, which means they finally flow
nicely into the modern VolumeInfo/VolumeBase design.

We now generate unique identifiers for all OBB volumes, instead of
using a shady MD5 hash.

Change all "loop" and "dm" devices to tag the kernel resources with
a vold-specific prefix so that we can clean them up if vold crashes;
there are new destroyAll() methods that handle this cleanup.

Move appfuse mounting/unmounting into VolumeManager so it can be
shared.  Move various model objects into a separate directory to
tidy things up.

Test: cts-tradefed run commandAndExit cts-dev -m CtsOsTestCases -t android.os.storage.cts.StorageManagerTest
Bug: 13758960
Change-Id: I7294e32b3fb6efe07cb3b77bd20166e70b66958f
diff --git a/VolumeManager.cpp b/VolumeManager.cpp
index ddf06eb..022caff 100644
--- a/VolumeManager.cpp
+++ b/VolumeManager.cpp
@@ -48,7 +48,8 @@
 #include <private/android_filesystem_config.h>
 
 #include "Benchmark.h"
-#include "EmulatedVolume.h"
+#include "model/EmulatedVolume.h"
+#include "model/ObbVolume.h"
 #include "VolumeManager.h"
 #include "NetlinkManager.h"
 #include "ResponseCode.h"
@@ -68,6 +69,7 @@
                                          + ((number) & (~((1U << (po2)) - 1))))
 
 using android::base::StringPrintf;
+using android::base::unique_fd;
 
 /*
  * Path to external storage where *only* root can access ASEC image files
@@ -220,6 +222,7 @@
     mSavedDirtyRatio = -1;
     // set dirty ratio to 0 when UMS is active
     mUmsDirtyRatio = 0;
+    mNextObbId = 0;
 }
 
 VolumeManager::~VolumeManager() {
@@ -317,6 +320,9 @@
     // directories that we own, in case we crashed.
     unmountAll();
 
+    Devmapper::destroyAll();
+    Loop::destroyAll();
+
     // Assume that we always have an emulated volume on internal
     // storage; the framework will decide if it should be mounted.
     CHECK(mInternalEmulated == nullptr);
@@ -437,6 +443,11 @@
             return vol;
         }
     }
+    for (const auto& vol : mObbVolumes) {
+        if (vol->getId() == id) {
+            return vol;
+        }
+    }
     return nullptr;
 }
 
@@ -754,7 +765,7 @@
     endmntent(fp);
 
     for (const auto& path : toUnmount) {
-        SLOGW("Tearing down stale mount %s", path.c_str());
+        LOG(DEBUG) << "Tearing down stale mount " << path;
         android::vold::ForceUnmount(path);
     }
 
@@ -1962,3 +1973,223 @@
         return -EINVAL;
     }
 }
+
+static size_t kAppFuseMaxMountPointName = 32;
+
+static android::status_t getMountPath(uid_t uid, const std::string& name, std::string* path) {
+    if (name.size() > kAppFuseMaxMountPointName) {
+        LOG(ERROR) << "AppFuse mount name is too long.";
+        return -EINVAL;
+    }
+    for (size_t i = 0; i < name.size(); i++) {
+        if (!isalnum(name[i])) {
+            LOG(ERROR) << "AppFuse mount name contains invalid character.";
+            return -EINVAL;
+        }
+    }
+    *path = android::base::StringPrintf("/mnt/appfuse/%d_%s", uid, name.c_str());
+    return android::OK;
+}
+
+static android::status_t mountInNamespace(uid_t uid, int device_fd, const std::string& path) {
+    // Remove existing mount.
+    android::vold::ForceUnmount(path);
+
+    const auto opts = android::base::StringPrintf(
+            "fd=%i,"
+            "rootmode=40000,"
+            "default_permissions,"
+            "allow_other,"
+            "user_id=%d,group_id=%d,"
+            "context=\"u:object_r:app_fuse_file:s0\","
+            "fscontext=u:object_r:app_fusefs:s0",
+            device_fd,
+            uid,
+            uid);
+
+    const int result = TEMP_FAILURE_RETRY(mount(
+            "/dev/fuse", path.c_str(), "fuse",
+            MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_NOATIME, opts.c_str()));
+    if (result != 0) {
+        PLOG(ERROR) << "Failed to mount " << path;
+        return -errno;
+    }
+
+    return android::OK;
+}
+
+static android::status_t runCommandInNamespace(const std::string& command,
+                                               uid_t uid,
+                                               pid_t pid,
+                                               const std::string& path,
+                                               int device_fd) {
+    if (DEBUG_APPFUSE) {
+        LOG(DEBUG) << "Run app fuse command " << command << " for the path " << path
+                   << " in namespace " << uid;
+    }
+
+    unique_fd dir(open("/proc", O_RDONLY | O_DIRECTORY | O_CLOEXEC));
+    if (dir.get() == -1) {
+        PLOG(ERROR) << "Failed to open /proc";
+        return -errno;
+    }
+
+    // Obtains process file descriptor.
+    const std::string pid_str = android::base::StringPrintf("%d", pid);
+    const unique_fd pid_fd(
+            openat(dir.get(), pid_str.c_str(), O_RDONLY | O_DIRECTORY | O_CLOEXEC));
+    if (pid_fd.get() == -1) {
+        PLOG(ERROR) << "Failed to open /proc/" << pid;
+        return -errno;
+    }
+
+    // Check UID of process.
+    {
+        struct stat sb;
+        const int result = fstat(pid_fd.get(), &sb);
+        if (result == -1) {
+            PLOG(ERROR) << "Failed to stat /proc/" << pid;
+            return -errno;
+        }
+        if (sb.st_uid != AID_SYSTEM) {
+            LOG(ERROR) << "Only system can mount appfuse. UID expected=" << AID_SYSTEM
+                    << ", actual=" << sb.st_uid;
+            return -EPERM;
+        }
+    }
+
+    // Matches so far, but refuse to touch if in root namespace
+    {
+        char rootName[PATH_MAX];
+        char pidName[PATH_MAX];
+        const int root_result =
+                android::vold::SaneReadLinkAt(dir.get(), "1/ns/mnt", rootName, PATH_MAX);
+        const int pid_result =
+                android::vold::SaneReadLinkAt(pid_fd.get(), "ns/mnt", pidName, PATH_MAX);
+        if (root_result == -1) {
+            LOG(ERROR) << "Failed to readlink for /proc/1/ns/mnt";
+            return -EPERM;
+        }
+        if (pid_result == -1) {
+            LOG(ERROR) << "Failed to readlink for /proc/" << pid << "/ns/mnt";
+            return -EPERM;
+        }
+        if (!strcmp(rootName, pidName)) {
+            LOG(ERROR) << "Don't mount appfuse in root namespace";
+            return -EPERM;
+        }
+    }
+
+    // We purposefully leave the namespace open across the fork
+    unique_fd ns_fd(openat(pid_fd.get(), "ns/mnt", O_RDONLY)); // not O_CLOEXEC
+    if (ns_fd.get() < 0) {
+        PLOG(ERROR) << "Failed to open namespace for /proc/" << pid << "/ns/mnt";
+        return -errno;
+    }
+
+    int child = fork();
+    if (child == 0) {
+        if (setns(ns_fd.get(), CLONE_NEWNS) != 0) {
+            PLOG(ERROR) << "Failed to setns";
+            _exit(-errno);
+        }
+
+        if (command == "mount") {
+            _exit(mountInNamespace(uid, device_fd, path));
+        } else if (command == "unmount") {
+            // If it's just after all FD opened on mount point are closed, umount2 can fail with
+            // EBUSY. To avoid the case, specify MNT_DETACH.
+            if (umount2(path.c_str(), UMOUNT_NOFOLLOW | MNT_DETACH) != 0 &&
+                    errno != EINVAL && errno != ENOENT) {
+                PLOG(ERROR) << "Failed to unmount directory.";
+                _exit(-errno);
+            }
+            if (rmdir(path.c_str()) != 0) {
+                PLOG(ERROR) << "Failed to remove the mount directory.";
+                _exit(-errno);
+            }
+            _exit(android::OK);
+        } else {
+            LOG(ERROR) << "Unknown appfuse command " << command;
+            _exit(-EPERM);
+        }
+    }
+
+    if (child == -1) {
+        PLOG(ERROR) << "Failed to folk child process";
+        return -errno;
+    }
+
+    android::status_t status;
+    TEMP_FAILURE_RETRY(waitpid(child, &status, 0));
+
+    return status;
+}
+
+int VolumeManager::createObb(const std::string& sourcePath, const std::string& sourceKey,
+        int32_t ownerGid, std::string* outVolId) {
+    int id = mNextObbId++;
+
+    auto vol = std::shared_ptr<android::vold::VolumeBase>(
+            new android::vold::ObbVolume(id, sourcePath, sourceKey, ownerGid));
+    vol->create();
+
+    mObbVolumes.push_back(vol);
+    *outVolId = vol->getId();
+    return android::OK;
+}
+
+int VolumeManager::destroyObb(const std::string& volId) {
+    auto i = mObbVolumes.begin();
+    while (i != mObbVolumes.end()) {
+        if ((*i)->getId() == volId) {
+            (*i)->destroy();
+            i = mObbVolumes.erase(i);
+        } else {
+            ++i;
+        }
+    }
+    return android::OK;
+}
+
+int VolumeManager::mountAppFuse(uid_t uid, pid_t pid, int mountId,
+        android::base::unique_fd* device_fd) {
+    std::string name = std::to_string(mountId);
+
+    // Check mount point name.
+    std::string path;
+    if (getMountPath(uid, name, &path) != android::OK) {
+        LOG(ERROR) << "Invalid mount point name";
+        return -1;
+    }
+
+    // Create directories.
+    const android::status_t result = android::vold::PrepareDir(path, 0700, 0, 0);
+    if (result != android::OK) {
+        PLOG(ERROR) << "Failed to prepare directory " << path;
+        return -1;
+    }
+
+    // Open device FD.
+    device_fd->reset(open("/dev/fuse", O_RDWR)); // not O_CLOEXEC
+    if (device_fd->get() == -1) {
+        PLOG(ERROR) << "Failed to open /dev/fuse";
+        return -1;
+    }
+
+    // Mount.
+    return runCommandInNamespace("mount", uid, pid, path, device_fd->get());
+}
+
+int VolumeManager::unmountAppFuse(uid_t uid, pid_t pid, int mountId) {
+    std::string name = std::to_string(mountId);
+
+    // Check mount point name.
+    std::string path;
+    if (getMountPath(uid, name, &path) != android::OK) {
+        LOG(ERROR) << "Invalid mount point name";
+        return -1;
+    }
+
+    return runCommandInNamespace("unmount", uid, pid, path, -1 /* device_fd */);
+}