Null-terminate readlink() result, full remount.

In order to compare results from readlink() calls, we need to null
terminate the read value, otherwise we can end up doing an infinitely
recursive remount in the root namespace.

When remounting inside a namespace, unmount all existing mounts before
mounting the new storage into place.  This also means we need to mount
the user-specific symlinks back into place.

Skip spinning up the FUSE daemon when not visible, otherwise we get
stuck waiting for a daemon that never shows up.

Bug: 22192518, 22204412
Change-Id: Icc7db822354ab7ffc47c39cd0611f65edecc32e5
diff --git a/EmulatedVolume.cpp b/EmulatedVolume.cpp
index e906fa7..d8d9198 100644
--- a/EmulatedVolume.cpp
+++ b/EmulatedVolume.cpp
@@ -82,7 +82,7 @@
             PLOG(ERROR) << "Failed to exec";
         }
 
-        PLOG(DEBUG) << "FUSE exiting";
+        LOG(ERROR) << "FUSE exiting";
         _exit(1);
     }
 
diff --git a/PublicVolume.cpp b/PublicVolume.cpp
index e4fdb86..29a357f 100644
--- a/PublicVolume.cpp
+++ b/PublicVolume.cpp
@@ -137,12 +137,15 @@
         initAsecStage();
     }
 
+    if (!(getMountFlags() & MountFlags::kVisible)) {
+        // Not visible to apps, so no need to spin up FUSE
+        return OK;
+    }
+
     dev_t before = GetDevice(mFuseWrite);
 
     if (!(mFusePid = fork())) {
-        if (!(getMountFlags() & MountFlags::kVisible)) {
-            // TODO: do we need to wrap this device?
-        } else if (getMountFlags() & MountFlags::kPrimary) {
+        if (getMountFlags() & MountFlags::kPrimary) {
             if (execl(kFusePath, kFusePath,
                     "-u", "1023", // AID_MEDIA_RW
                     "-g", "1023", // AID_MEDIA_RW
@@ -163,7 +166,7 @@
             }
         }
 
-        PLOG(DEBUG) << "FUSE exiting";
+        LOG(ERROR) << "FUSE exiting";
         _exit(1);
     }
 
diff --git a/VolumeManager.cpp b/VolumeManager.cpp
index 6caa5c0..d1a0b2f 100755
--- a/VolumeManager.cpp
+++ b/VolumeManager.cpp
@@ -481,6 +481,46 @@
     return 0;
 }
 
+static int sane_readlinkat(int dirfd, const char* path, char* buf, size_t bufsiz) {
+    ssize_t len = readlinkat(dirfd, path, buf, bufsiz);
+    if (len < 0) {
+        return -1;
+    } else if (len == (ssize_t) bufsiz) {
+        return -1;
+    } else {
+        buf[len] = '\0';
+        return 0;
+    }
+}
+
+static int unmount_tree(const char* path) {
+    size_t path_len = strlen(path);
+
+    FILE* fp = setmntent("/proc/mounts", "r");
+    if (fp == NULL) {
+        ALOGE("Error opening /proc/mounts: %s", strerror(errno));
+        return -errno;
+    }
+
+    // Some volumes can be stacked on each other, so force unmount in
+    // reverse order to give us the best chance of success.
+    std::list<std::string> toUnmount;
+    mntent* mentry;
+    while ((mentry = getmntent(fp)) != NULL) {
+        if (strncmp(mentry->mnt_dir, path, path_len) == 0) {
+            toUnmount.push_front(std::string(mentry->mnt_dir));
+        }
+    }
+    endmntent(fp);
+
+    for (auto path : toUnmount) {
+        if (umount2(path.c_str(), MNT_DETACH)) {
+            ALOGW("Failed to unmount %s: %s", path.c_str(), strerror(errno));
+        }
+    }
+    return 0;
+}
+
 int VolumeManager::remountUid(uid_t uid, const std::string& mode) {
     LOG(DEBUG) << "Remounting " << uid << " as mode " << mode;
 
@@ -499,7 +539,7 @@
     }
 
     // Figure out root namespace to compare against below
-    if (readlinkat(dirfd(dir), "1/ns/mnt", rootName, PATH_MAX) == -1) {
+    if (sane_readlinkat(dirfd(dir), "1/ns/mnt", rootName, PATH_MAX) == -1) {
         PLOG(ERROR) << "Failed to readlink";
         closedir(dir);
         return -1;
@@ -524,7 +564,7 @@
 
         // Matches so far, but refuse to touch if in root namespace
         LOG(DEBUG) << "Found matching PID " << de->d_name;
-        if (readlinkat(pidFd, "ns/mnt", pidName, PATH_MAX) == -1) {
+        if (sane_readlinkat(pidFd, "ns/mnt", pidName, PATH_MAX) == -1) {
             PLOG(WARNING) << "Failed to read namespace for " << de->d_name;
             goto next;
         }
@@ -536,18 +576,17 @@
         // We purposefully leave the namespace open across the fork
         nsFd = openat(pidFd, "ns/mnt", O_RDONLY);
         if (nsFd < 0) {
-            PLOG(WARNING) << "Failed to open namespace";
+            PLOG(WARNING) << "Failed to open namespace for " << de->d_name;
             goto next;
         }
 
         if (!(child = fork())) {
             if (setns(nsFd, CLONE_NEWNS) != 0) {
-                PLOG(ERROR) << "Failed to setns";
+                PLOG(ERROR) << "Failed to setns for " << de->d_name;
                 _exit(1);
             }
 
-            // Unmount current view and replace with requested view
-            umount2("/storage", MNT_FORCE);
+            unmount_tree("/storage");
 
             std::string storageSource;
             if (mode == "default") {
@@ -562,9 +601,21 @@
             }
             if (TEMP_FAILURE_RETRY(mount(storageSource.c_str(), "/storage",
                     NULL, MS_BIND | MS_REC | MS_SLAVE, NULL)) == -1) {
-                PLOG(WARNING) << "Failed to mount " << storageSource;
-                return false;
+                PLOG(ERROR) << "Failed to mount " << storageSource << " for "
+                        << de->d_name;
+                _exit(1);
             }
+
+            // Mount user-specific symlink helper into place
+            userid_t user_id = multiuser_get_user_id(uid);
+            std::string userSource(StringPrintf("/mnt/user/%d", user_id));
+            if (TEMP_FAILURE_RETRY(mount(userSource.c_str(), "/storage/self",
+                    NULL, MS_BIND, NULL)) == -1) {
+                PLOG(ERROR) << "Failed to mount " << userSource << " for "
+                        << de->d_name;
+                _exit(1);
+            }
+
             _exit(0);
         }