diff options
author | Csaba Henk <csaba@gluster.com> | 2010-02-20 18:53:38 +0000 |
---|---|---|
committer | Anand V. Avati <avati@dev.gluster.com> | 2010-02-21 00:17:42 -0800 |
commit | 94d31fdaf967f0d224a2bfa7b275b0344e0f5700 (patch) | |
tree | c1b5134d26aa145d36da5c1b732f3a10ea56422c /contrib/fuse-util/fusermount.c | |
parent | f9269f4776141ae78fb10d1bf5ae63da7595a369 (diff) |
fuse: bring over recent mounting code changes from libfuse upstream
Following commits were ported (commit ids as of
http://git.gluster.com/?p=users/csaba/fuse.git
repo):
commit 06fe3eb9c864b69bea98600c0a7eab7b63834735
Author: mszeredi <mszeredi>
Date: Thu Feb 18 11:05:12 2010 +0000
* Fix stack alignment for clone()
ChangeLog | 4 ++++
include/fuse_lowlevel.h | 1 +
util/fusermount.c | 9 ++++-----
3 files changed, 9 insertions(+), 5 deletions(-)
commit dfe1aab6520d70d72d36edf0508fef9a865daa5f
Author: mszeredi <mszeredi>
Date: Tue Jan 26 18:20:12 2010 +0000
* Fix race if two "fusermount -u" instances are run in parallel.
Reported by Dan Rosenberg
* Make sure that the path to be unmounted doesn't refer to a
symlink
ChangeLog | 8 +
lib/mount.c | 2 +-
lib/mount_util.c | 31 +++--
lib/mount_util.h | 3 +-
util/fusermount.c | 380 +++++++++++++++++++++++++++++++++++++++++++++--------
5 files changed, 351 insertions(+), 73 deletions(-)
Signed-off-by: Csaba Henk <csaba@gluster.com>
Signed-off-by: Anand V. Avati <avati@dev.gluster.com>
BUG: 657 (Metabug for tracking fuse upstream)
URL: http://bugs.gluster.com/cgi-bin/bugzilla3/show_bug.cgi?id=657
Diffstat (limited to 'contrib/fuse-util/fusermount.c')
-rw-r--r-- | contrib/fuse-util/fusermount.c | 379 |
1 files changed, 322 insertions, 57 deletions
diff --git a/contrib/fuse-util/fusermount.c b/contrib/fuse-util/fusermount.c index c3ecc86cc44..6123c66e30b 100644 --- a/contrib/fuse-util/fusermount.c +++ b/contrib/fuse-util/fusermount.c @@ -26,6 +26,7 @@ #include <sys/fsuid.h> #include <sys/socket.h> #include <sys/utsname.h> +#include <sched.h> #define FUSE_COMMFD_ENV "_FUSE_COMMFD" @@ -37,6 +38,12 @@ #ifndef MS_DIRSYNC #define MS_DIRSYNC 128 #endif +#ifndef MS_REC +#define MS_REC 16384 +#endif +#ifndef MS_SLAVE +#define MS_SLAVE (1<<19) +#endif static const char *progname; @@ -74,77 +81,335 @@ static void restore_privs(void) } #ifndef IGNORE_MTAB +/* + * Make sure that /etc/mtab is checked and updated atomically + */ +static int lock_umount(void) +{ + const char *mtab_lock = _PATH_MOUNTED ".fuselock"; + int mtablock; + int res; + struct stat mtab_stat; + + /* /etc/mtab could be a symlink to /proc/mounts */ + if (lstat(_PATH_MOUNTED, &mtab_stat) == 0 && S_ISLNK(mtab_stat.st_mode)) + return -1; + + mtablock = open(mtab_lock, O_RDWR | O_CREAT, 0600); + if (mtablock == -1) { + fprintf(stderr, "%s: unable to open fuse lock file: %s\n", + progname, strerror(errno)); + return -1; + } + res = lockf(mtablock, F_LOCK, 0); + if (res < 0) { + fprintf(stderr, "%s: error getting lock: %s\n", progname, + strerror(errno)); + close(mtablock); + return -1; + } + + return mtablock; +} + +static void unlock_umount(int mtablock) +{ + lockf(mtablock, F_ULOCK, 0); + close(mtablock); +} + static int add_mount(const char *source, const char *mnt, const char *type, const char *opts) { return fuse_mnt_add_mount(progname, source, mnt, type, opts); } -static int unmount_fuse(const char *mnt, int quiet, int lazy) +static int may_unmount(const char *mnt, int quiet) { - if (getuid() != 0) { - struct mntent *entp; - FILE *fp; - const char *user = NULL; - char uidstr[32]; - unsigned uidlen = 0; - int found; - const char *mtab = _PATH_MOUNTED; - - user = get_user_name(); - if (user == NULL) - return -1; + struct mntent *entp; + FILE *fp; + const char *user = NULL; + char uidstr[32]; + unsigned uidlen = 0; + int found; + const char *mtab = _PATH_MOUNTED; - fp = setmntent(mtab, "r"); - if (fp == NULL) { - fprintf(stderr, - "%s: failed to open %s: %s\n", progname, mtab, - strerror(errno)); - return -1; - } + user = get_user_name(); + if (user == NULL) + return -1; - uidlen = sprintf(uidstr, "%u", getuid()); - - found = 0; - while ((entp = getmntent(fp)) != NULL) { - if (!found && strcmp(entp->mnt_dir, mnt) == 0 && - (strcmp(entp->mnt_type, "fuse") == 0 || - strcmp(entp->mnt_type, "fuseblk") == 0 || - strncmp(entp->mnt_type, "fuse.", 5) == 0 || - strncmp(entp->mnt_type, "fuseblk.", 8) == 0)) { - char *p = strstr(entp->mnt_opts, "user="); - if (p && - (p == entp->mnt_opts || *(p-1) == ',') && - strcmp(p + 5, user) == 0) { - found = 1; - break; - } - /* /etc/mtab is a link pointing to - /proc/mounts: */ - else if ((p = - strstr(entp->mnt_opts, "user_id=")) && - (p == entp->mnt_opts || - *(p-1) == ',') && - strncmp(p + 8, uidstr, uidlen) == 0 && - (*(p+8+uidlen) == ',' || - *(p+8+uidlen) == '\0')) { - found = 1; - break; - } + fp = setmntent(mtab, "r"); + if (fp == NULL) { + fprintf(stderr, "%s: failed to open %s: %s\n", progname, mtab, + strerror(errno)); + return -1; + } + + uidlen = sprintf(uidstr, "%u", getuid()); + + found = 0; + while ((entp = getmntent(fp)) != NULL) { + if (!found && strcmp(entp->mnt_dir, mnt) == 0 && + (strcmp(entp->mnt_type, "fuse") == 0 || + strcmp(entp->mnt_type, "fuseblk") == 0 || + strncmp(entp->mnt_type, "fuse.", 5) == 0 || + strncmp(entp->mnt_type, "fuseblk.", 8) == 0)) { + char *p = strstr(entp->mnt_opts, "user="); + if (p && + (p == entp->mnt_opts || *(p-1) == ',') && + strcmp(p + 5, user) == 0) { + found = 1; + break; + } + /* /etc/mtab is a link pointing to + /proc/mounts: */ + else if ((p = + strstr(entp->mnt_opts, "user_id=")) && + (p == entp->mnt_opts || + *(p-1) == ',') && + strncmp(p + 8, uidstr, uidlen) == 0 && + (*(p+8+uidlen) == ',' || + *(p+8+uidlen) == '\0')) { + found = 1; + break; } } - endmntent(fp); + } + endmntent(fp); - if (!found) { - if (!quiet) - fprintf(stderr, - "%s: entry for %s not found in %s\n", - progname, mnt, mtab); - return -1; + if (!found) { + if (!quiet) + fprintf(stderr, + "%s: entry for %s not found in %s\n", + progname, mnt, mtab); + return -1; + } + + return 0; +} + +/* + * Check whether the file specified in "fusermount -u" is really a + * mountpoint and not a symlink. This is necessary otherwise the user + * could move the mountpoint away and replace it with a symlink + * pointing to an arbitrary mount, thereby tricking fusermount into + * unmounting that (umount(2) will follow symlinks). + * + * This is the child process running in a separate mount namespace, so + * we don't mess with the global namespace and if the process is + * killed for any reason, mounts are automatically cleaned up. + * + * First make sure nothing is propagated back into the parent + * namespace by marking all mounts "slave". + * + * Then bind mount parent onto a stable base where the user can't move + * it around. Use "/tmp", since it will almost certainly exist, but + * anything similar would do as well. + * + * Finally check /proc/mounts for an entry matching the requested + * mountpoint. If it's found then we are OK, and the user can't move + * it around within the parent directory as rename() will return EBUSY. + */ +static int check_is_mount_child(void *p) +{ + const char **a = p; + const char *last = a[0]; + const char *mnt = a[1]; + int res; + const char *procmounts = "/proc/mounts"; + int found; + FILE *fp; + struct mntent *entp; + + res = mount("", "/", "", MS_SLAVE | MS_REC, NULL); + if (res == -1) { + fprintf(stderr, "%s: failed to mark mounts slave: %s\n", + progname, strerror(errno)); + return 1; + } + + res = mount(".", "/tmp", "", MS_BIND | MS_REC, NULL); + if (res == -1) { + fprintf(stderr, "%s: failed to bind parent to /tmp: %s\n", + progname, strerror(errno)); + return 1; + } + + fp = setmntent(procmounts, "r"); + if (fp == NULL) { + fprintf(stderr, "%s: failed to open %s: %s\n", progname, + procmounts, strerror(errno)); + return 1; + } + + found = 0; + while ((entp = getmntent(fp)) != NULL) { + if (strncmp(entp->mnt_dir, "/tmp/", 5) == 0 && + strcmp(entp->mnt_dir + 5, last) == 0) { + found = 1; + break; } } + endmntent(fp); + + if (!found) { + fprintf(stderr, "%s: %s not mounted\n", progname, mnt); + return 1; + } + + return 0; +} + +static pid_t clone_newns(void *a) +{ + char buf[131072]; + char *stack = buf + (sizeof(buf) / 2 - ((size_t) buf & 15)); + +#ifdef __ia64__ + extern int __clone2(int (*fn)(void *), + void *child_stack_base, size_t stack_size, + int flags, void *arg, pid_t *ptid, + void *tls, pid_t *ctid); + + return __clone2(check_is_mount_child, stack, sizeof(buf) / 2, + CLONE_NEWNS, a, NULL, NULL, NULL); +#else + return clone(check_is_mount_child, stack, CLONE_NEWNS, a); +#endif +} + +static int check_is_mount(const char *last, const char *mnt) +{ + pid_t pid, p; + int status; + const char *a[2] = { last, mnt }; + + pid = clone_newns((void *) a); + if (pid == (pid_t) -1) { + fprintf(stderr, "%s: failed to clone namespace: %s\n", + progname, strerror(errno)); + return -1; + } + p = waitpid(pid, &status, __WCLONE); + if (p == (pid_t) -1) { + fprintf(stderr, "%s: waitpid failed: %s\n", + progname, strerror(errno)); + return -1; + } + if (!WIFEXITED(status)) { + fprintf(stderr, "%s: child terminated abnormally (status %i)\n", + progname, status); + return -1; + } + if (WEXITSTATUS(status) != 0) + return -1; + + return 0; +} + +static int chdir_to_parent(char *copy, const char **lastp, int *currdir_fd) +{ + char *tmp; + const char *parent; + char buf[65536]; + int res; + + tmp = strrchr(copy, '/'); + if (tmp == NULL || tmp[1] == '\0') { + fprintf(stderr, "%s: internal error: invalid abs path: <%s>\n", + progname, copy); + return -1; + } + if (tmp != copy) { + *tmp = '\0'; + parent = copy; + *lastp = tmp + 1; + } else if (tmp[1] != '\0') { + *lastp = tmp + 1; + parent = "/"; + } else { + *lastp = "."; + parent = "/"; + } + + *currdir_fd = open(".", O_RDONLY); + if (*currdir_fd == -1) { + fprintf(stderr, + "%s: failed to open current directory: %s\n", + progname, strerror(errno)); + return -1; + } + + res = chdir(parent); + if (res == -1) { + fprintf(stderr, "%s: failed to chdir to %s: %s\n", + progname, parent, strerror(errno)); + return -1; + } + + if (getcwd(buf, sizeof(buf)) == NULL) { + fprintf(stderr, "%s: failed to obtain current directory: %s\n", + progname, strerror(errno)); + return -1; + } + if (strcmp(buf, parent) != 0) { + fprintf(stderr, "%s: mountpoint moved (%s -> %s)\n", progname, + parent, buf); + return -1; - return fuse_mnt_umount(progname, mnt, lazy); + } + + return 0; +} + +static int unmount_fuse_locked(const char *mnt, int quiet, int lazy) +{ + int currdir_fd = -1; + char *copy; + const char *last; + int res; + + if (getuid() != 0) { + res = may_unmount(mnt, quiet); + if (res == -1) + return -1; + } + + copy = strdup(mnt); + if (copy == NULL) { + fprintf(stderr, "%s: failed to allocate memory\n", progname); + return -1; + } + + res = chdir_to_parent(copy, &last, &currdir_fd); + if (res == -1) + goto out; + + res = check_is_mount(last, mnt); + if (res == -1) + goto out; + + res = fuse_mnt_umount(progname, mnt, last, lazy); + +out: + free(copy); + if (currdir_fd != -1) { + fchdir(currdir_fd); + close(currdir_fd); + } + + return res; +} + +static int unmount_fuse(const char *mnt, int quiet, int lazy) +{ + int res; + int mtablock = lock_umount(); + + res = unmount_fuse_locked(mnt, quiet, lazy); + unlock_umount(mtablock); + + return res; } static int count_fuse_fs(void) @@ -186,7 +451,7 @@ static int add_mount(const char *source, const char *mnt, const char *type, static int unmount_fuse(const char *mnt, int quiet, int lazy) { - return fuse_mnt_umount(progname, mnt, lazy); + return fuse_mnt_umount(progname, mnt, mnt, lazy); } #endif /* IGNORE_MTAB */ |