diff options
Diffstat (limited to 'contrib')
| -rw-r--r-- | contrib/fuse-lib/mount.c | 13 | ||||
| -rw-r--r-- | contrib/fuse-util/fusermount.c | 379 | ||||
| -rw-r--r-- | contrib/fuse-util/mount_util.h | 3 | 
3 files changed, 331 insertions, 64 deletions
diff --git a/contrib/fuse-lib/mount.c b/contrib/fuse-lib/mount.c index cf8dc5b4afb..e88293b38c7 100644 --- a/contrib/fuse-lib/mount.c +++ b/contrib/fuse-lib/mount.c @@ -351,18 +351,19 @@ fuse_mount_fusermount (const char *mountpoint, const char *opts)  static  #endif  int -fuse_mnt_umount (const char *progname, const char *mnt, int lazy) +fuse_mnt_umount (const char *progname, const char *abs_mnt, +                 const char *rel_mnt, int lazy)  {          int res;          int status;          sigset_t blockmask;          sigset_t oldmask; -        if (!mtab_needs_update (mnt)) { -                res = umount2 (mnt, lazy ? 2 : 0); +        if (!mtab_needs_update (abs_mnt)) { +                res = umount2 (rel_mnt, lazy ? 2 : 0);                  if (res == -1)                          GFFUSE_LOGERR ("%s: failed to unmount %s: %s", -                                       progname, mnt, strerror (errno)); +                                       progname, abs_mnt, strerror (errno));                  return res;          } @@ -383,7 +384,7 @@ fuse_mnt_umount (const char *progname, const char *mnt, int lazy)          if (res == 0) {                  sigprocmask (SIG_SETMASK, &oldmask, NULL);                  setuid (geteuid ()); -                execl ("/bin/umount", "/bin/umount", "-i", mnt, +                execl ("/bin/umount", "/bin/umount", "-i", rel_mnt,                        lazy ? "-l" : NULL, NULL);                  GFFUSE_LOGERR ("%s: failed to execute /bin/umount: %s",                                 progname, strerror (errno)); @@ -483,7 +484,7 @@ gf_fuse_unmount (const char *mountpoint, int fd)          }          if (geteuid () == 0) { -                fuse_mnt_umount ("fuse", mountpoint, 1); +                fuse_mnt_umount ("fuse", mountpoint, mountpoint, 1);                  return;          } 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 */ diff --git a/contrib/fuse-util/mount_util.h b/contrib/fuse-util/mount_util.h index cf54d9d0d02..f392f99f17a 100644 --- a/contrib/fuse-util/mount_util.h +++ b/contrib/fuse-util/mount_util.h @@ -10,7 +10,8 @@  int fuse_mnt_add_mount(const char *progname, const char *fsname,  		       const char *mnt, const char *type, const char *opts); -int fuse_mnt_umount(const char *progname, const char *mnt, int lazy); +int fuse_mnt_umount(const char *progname, const char *abs_mnt, +		    const char *rel_mnt, int lazy);  char *fuse_mnt_resolve_path(const char *progname, const char *orig);  int fuse_mnt_check_empty(const char *progname, const char *mnt,  			 mode_t rootmode, off_t rootsize);  | 
