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
gugelfrei
Jeff Sharkey 9 years ago
parent 63123c067a
commit c7b5b570bd

@ -82,7 +82,7 @@ status_t EmulatedVolume::doMount() {
PLOG(ERROR) << "Failed to exec";
}
PLOG(DEBUG) << "FUSE exiting";
LOG(ERROR) << "FUSE exiting";
_exit(1);
}

@ -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);
}

@ -481,6 +481,46 @@ int VolumeManager::setPrimary(const std::shared_ptr<android::vold::VolumeBase>&
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 @@ 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);
}

Loading…
Cancel
Save