From c7b5b570bd05ed3bc921b0c2dc346416a52b4e3e Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Tue, 30 Jun 2015 15:54:17 -0700 Subject: [PATCH] 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 --- EmulatedVolume.cpp | 2 +- PublicVolume.cpp | 11 +++++--- VolumeManager.cpp | 67 ++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 67 insertions(+), 13 deletions(-) diff --git a/EmulatedVolume.cpp b/EmulatedVolume.cpp index e906fa7..d8d9198 100644 --- a/EmulatedVolume.cpp +++ b/EmulatedVolume.cpp @@ -82,7 +82,7 @@ status_t EmulatedVolume::doMount() { 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 @@ status_t PublicVolume::doMount() { 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 @@ status_t PublicVolume::doMount() { } } - 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 @@ int VolumeManager::setPrimary(const std::shared_ptr& 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 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 @@ int VolumeManager::remountUid(uid_t uid, const std::string& mode) { } // 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 @@ int VolumeManager::remountUid(uid_t uid, const std::string& mode) { // 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 @@ int VolumeManager::remountUid(uid_t uid, const std::string& mode) { // 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 @@ int VolumeManager::remountUid(uid_t uid, const std::string& mode) { } 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); }