mirror of https://github.com/trapexit/mergerfs.git
				
				
			
			You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							1327 lines
						
					
					
						
							28 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							1327 lines
						
					
					
						
							28 KiB
						
					
					
				
								/*
							 | 
						|
								  FUSE: Filesystem in Userspace
							 | 
						|
								  Copyright (C) 2001-2007  Miklos Szeredi <miklos@szeredi.hu>
							 | 
						|
								
							 | 
						|
								  This program can be distributed under the terms of the GNU GPL.
							 | 
						|
								  See the file COPYING.
							 | 
						|
								*/
							 | 
						|
								/* This program does the mounting and unmounting of FUSE filesystems */
							 | 
						|
								
							 | 
						|
								#define _GNU_SOURCE /* for clone */
							 | 
						|
								
							 | 
						|
								#include "mount_util.h"
							 | 
						|
								#include <stdio.h>
							 | 
						|
								#include <stdlib.h>
							 | 
						|
								#include <string.h>
							 | 
						|
								#include <ctype.h>
							 | 
						|
								#include <unistd.h>
							 | 
						|
								#include <getopt.h>
							 | 
						|
								#include <errno.h>
							 | 
						|
								#include <fcntl.h>
							 | 
						|
								#include <pwd.h>
							 | 
						|
								#include <paths.h>
							 | 
						|
								#include <mntent.h>
							 | 
						|
								#include <sys/wait.h>
							 | 
						|
								#include <sys/stat.h>
							 | 
						|
								#include <sys/mount.h>
							 | 
						|
								#include <sys/fsuid.h>
							 | 
						|
								#include <sys/socket.h>
							 | 
						|
								#include <sys/utsname.h>
							 | 
						|
								#include <sched.h>
							 | 
						|
								
							 | 
						|
								#define FUSE_COMMFD_ENV	"_FUSE_COMMFD"
							 | 
						|
								
							 | 
						|
								#define FUSE_DEV_OLD "/proc/fs/fuse/dev"
							 | 
						|
								#define FUSE_DEV_NEW "/dev/fuse"
							 | 
						|
								#define FUSE_VERSION_FILE_OLD "/proc/fs/fuse/version"
							 | 
						|
								#define FUSE_CONF "/etc/fuse.conf"
							 | 
						|
								
							 | 
						|
								#ifndef MS_DIRSYNC
							 | 
						|
								#define MS_DIRSYNC 128
							 | 
						|
								#endif
							 | 
						|
								#ifndef MS_REC
							 | 
						|
								#define MS_REC 16384
							 | 
						|
								#endif
							 | 
						|
								#ifndef MS_PRIVATE
							 | 
						|
								#define MS_PRIVATE (1<<18)
							 | 
						|
								#endif
							 | 
						|
								
							 | 
						|
								#ifndef UMOUNT_DETACH
							 | 
						|
								#define UMOUNT_DETACH	0x00000002	/* Just detach from the tree */
							 | 
						|
								#endif
							 | 
						|
								#ifndef UMOUNT_NOFOLLOW
							 | 
						|
								#define UMOUNT_NOFOLLOW	0x00000008	/* Don't follow symlink on umount */
							 | 
						|
								#endif
							 | 
						|
								#ifndef UMOUNT_UNUSED
							 | 
						|
								#define UMOUNT_UNUSED	0x80000000	/* Flag guaranteed to be unused */
							 | 
						|
								#endif
							 | 
						|
								
							 | 
						|
								static const char *progname;
							 | 
						|
								
							 | 
						|
								static int user_allow_other = 0;
							 | 
						|
								static int mount_max = 1000;
							 | 
						|
								
							 | 
						|
								static int auto_unmount = 0;
							 | 
						|
								
							 | 
						|
								static const char *get_user_name(void)
							 | 
						|
								{
							 | 
						|
									struct passwd *pw = getpwuid(getuid());
							 | 
						|
									if (pw != NULL && pw->pw_name != NULL)
							 | 
						|
										return pw->pw_name;
							 | 
						|
									else {
							 | 
						|
										fprintf(stderr, "%s: could not determine username\n", progname);
							 | 
						|
										return NULL;
							 | 
						|
									}
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								static uid_t oldfsuid;
							 | 
						|
								static gid_t oldfsgid;
							 | 
						|
								
							 | 
						|
								static void drop_privs(void)
							 | 
						|
								{
							 | 
						|
									if (getuid() != 0) {
							 | 
						|
										oldfsuid = setfsuid(getuid());
							 | 
						|
										oldfsgid = setfsgid(getgid());
							 | 
						|
									}
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								static void restore_privs(void)
							 | 
						|
								{
							 | 
						|
									if (getuid() != 0) {
							 | 
						|
										setfsuid(oldfsuid);
							 | 
						|
										setfsgid(oldfsgid);
							 | 
						|
									}
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								#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)
							 | 
						|
								{
							 | 
						|
									if (mtablock >= 0) {
							 | 
						|
										int res;
							 | 
						|
								
							 | 
						|
										res = lockf(mtablock, F_ULOCK, 0);
							 | 
						|
										if (res < 0) {
							 | 
						|
											fprintf(stderr, "%s: error releasing lock: %s\n",
							 | 
						|
												progname, strerror(errno));
							 | 
						|
										}
							 | 
						|
										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 may_unmount(const char *mnt, int quiet)
							 | 
						|
								{
							 | 
						|
									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;
							 | 
						|
								
							 | 
						|
									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);
							 | 
						|
								
							 | 
						|
									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 "private".
							 | 
						|
								 *
							 | 
						|
								 * Then bind mount parent onto a stable base where the user can't move
							 | 
						|
								 * it around.
							 | 
						|
								 *
							 | 
						|
								 * 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.  Be careful to ignore any mounts that existed before the
							 | 
						|
								 * bind.
							 | 
						|
								 */
							 | 
						|
								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;
							 | 
						|
									int count;
							 | 
						|
								
							 | 
						|
									res = mount("", "/", "", MS_PRIVATE | MS_REC, NULL);
							 | 
						|
									if (res == -1) {
							 | 
						|
										fprintf(stderr, "%s: failed to mark mounts private: %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;
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									count = 0;
							 | 
						|
									while (getmntent(fp) != NULL)
							 | 
						|
										count++;
							 | 
						|
									endmntent(fp);
							 | 
						|
								
							 | 
						|
									fp = setmntent(procmounts, "r");
							 | 
						|
									if (fp == NULL) {
							 | 
						|
										fprintf(stderr, "%s: failed to open %s: %s\n", progname,
							 | 
						|
											procmounts, strerror(errno));
							 | 
						|
										return 1;
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									res = mount(".", "/", "", MS_BIND | MS_REC, NULL);
							 | 
						|
									if (res == -1) {
							 | 
						|
										fprintf(stderr, "%s: failed to bind parent to /: %s\n",
							 | 
						|
											progname, strerror(errno));
							 | 
						|
										return 1;
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									found = 0;
							 | 
						|
									while ((entp = getmntent(fp)) != NULL) {
							 | 
						|
										if (count > 0) {
							 | 
						|
											count--;
							 | 
						|
											continue;
							 | 
						|
										}
							 | 
						|
										if (entp->mnt_dir[0] == '/' &&
							 | 
						|
										    strcmp(entp->mnt_dir + 1, 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)
							 | 
						|
								{
							 | 
						|
									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 = "/";
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									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 0;
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								/* Check whether the kernel supports UMOUNT_NOFOLLOW flag */
							 | 
						|
								static int umount_nofollow_support(void)
							 | 
						|
								{
							 | 
						|
									int res = umount2("", UMOUNT_UNUSED);
							 | 
						|
									if (res != -1 || errno != EINVAL)
							 | 
						|
										return 0;
							 | 
						|
								
							 | 
						|
									res = umount2("", UMOUNT_NOFOLLOW);
							 | 
						|
									if (res != -1 || errno != ENOENT)
							 | 
						|
										return 0;
							 | 
						|
								
							 | 
						|
									return 1;
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								static int unmount_fuse_locked(const char *mnt, int quiet, int lazy)
							 | 
						|
								{
							 | 
						|
									int res;
							 | 
						|
									char *copy;
							 | 
						|
									const char *last;
							 | 
						|
									int umount_flags = lazy ? UMOUNT_DETACH : 0;
							 | 
						|
								
							 | 
						|
									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);
							 | 
						|
									if (res == -1)
							 | 
						|
										goto out;
							 | 
						|
								
							 | 
						|
									if (umount_nofollow_support()) {
							 | 
						|
										umount_flags |= UMOUNT_NOFOLLOW;
							 | 
						|
									} else {
							 | 
						|
										res = check_is_mount(last, mnt);
							 | 
						|
										if (res == -1)
							 | 
						|
											goto out;
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									res = umount2(last, umount_flags);
							 | 
						|
									if (res == -1 && !quiet) {
							 | 
						|
										fprintf(stderr, "%s: failed to unmount %s: %s\n",
							 | 
						|
											progname, mnt, strerror(errno));
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
								out:
							 | 
						|
									if (res == -1)
							 | 
						|
										return -1;
							 | 
						|
								
							 | 
						|
									res = chdir("/");
							 | 
						|
									if (res == -1) {
							 | 
						|
										fprintf(stderr, "%s: failed to chdir to '/'\n", progname);
							 | 
						|
										return -1;
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									return fuse_mnt_remove_mount(progname, mnt);
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								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)
							 | 
						|
								{
							 | 
						|
									struct mntent *entp;
							 | 
						|
									int count = 0;
							 | 
						|
									const char *mtab = _PATH_MOUNTED;
							 | 
						|
									FILE *fp = setmntent(mtab, "r");
							 | 
						|
									if (fp == NULL) {
							 | 
						|
										fprintf(stderr, "%s: failed to open %s: %s\n", progname, mtab,
							 | 
						|
											strerror(errno));
							 | 
						|
										return -1;
							 | 
						|
									}
							 | 
						|
									while ((entp = getmntent(fp)) != NULL) {
							 | 
						|
										if (strcmp(entp->mnt_type, "fuse") == 0 ||
							 | 
						|
										    strncmp(entp->mnt_type, "fuse.", 5) == 0)
							 | 
						|
											count ++;
							 | 
						|
									}
							 | 
						|
									endmntent(fp);
							 | 
						|
									return count;
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								#else /* IGNORE_MTAB */
							 | 
						|
								static int count_fuse_fs()
							 | 
						|
								{
							 | 
						|
									return 0;
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								static int add_mount(const char *source, const char *mnt, const char *type,
							 | 
						|
										     const char *opts)
							 | 
						|
								{
							 | 
						|
									(void) source;
							 | 
						|
									(void) mnt;
							 | 
						|
									(void) type;
							 | 
						|
									(void) opts;
							 | 
						|
									return 0;
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								static int unmount_fuse(const char *mnt, int quiet, int lazy)
							 | 
						|
								{
							 | 
						|
									return fuse_mnt_umount(progname, mnt, mnt, lazy);
							 | 
						|
								}
							 | 
						|
								#endif /* IGNORE_MTAB */
							 | 
						|
								
							 | 
						|
								static void strip_line(char *line)
							 | 
						|
								{
							 | 
						|
									char *s = strchr(line, '#');
							 | 
						|
									if (s != NULL)
							 | 
						|
										s[0] = '\0';
							 | 
						|
									for (s = line + strlen(line) - 1;
							 | 
						|
									     s >= line && isspace((unsigned char) *s); s--);
							 | 
						|
									s[1] = '\0';
							 | 
						|
									for (s = line; isspace((unsigned char) *s); s++);
							 | 
						|
									if (s != line)
							 | 
						|
										memmove(line, s, strlen(s)+1);
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								static void parse_line(char *line, int linenum)
							 | 
						|
								{
							 | 
						|
									int tmp;
							 | 
						|
									if (strcmp(line, "user_allow_other") == 0)
							 | 
						|
										user_allow_other = 1;
							 | 
						|
									else if (sscanf(line, "mount_max = %i", &tmp) == 1)
							 | 
						|
										mount_max = tmp;
							 | 
						|
									else if(line[0])
							 | 
						|
										fprintf(stderr,
							 | 
						|
											"%s: unknown parameter in %s at line %i: '%s'\n",
							 | 
						|
											progname, FUSE_CONF, linenum, line);
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								static void read_conf(void)
							 | 
						|
								{
							 | 
						|
									FILE *fp = fopen(FUSE_CONF, "r");
							 | 
						|
									if (fp != NULL) {
							 | 
						|
										int linenum = 1;
							 | 
						|
										char line[256];
							 | 
						|
										int isnewline = 1;
							 | 
						|
										while (fgets(line, sizeof(line), fp) != NULL) {
							 | 
						|
											if (isnewline) {
							 | 
						|
												if (line[strlen(line)-1] == '\n') {
							 | 
						|
													strip_line(line);
							 | 
						|
													parse_line(line, linenum);
							 | 
						|
												} else {
							 | 
						|
													isnewline = 0;
							 | 
						|
												}
							 | 
						|
											} else if(line[strlen(line)-1] == '\n') {
							 | 
						|
												fprintf(stderr, "%s: reading %s: line %i too long\n", progname, FUSE_CONF, linenum);
							 | 
						|
								
							 | 
						|
												isnewline = 1;
							 | 
						|
											}
							 | 
						|
											if (isnewline)
							 | 
						|
												linenum ++;
							 | 
						|
										}
							 | 
						|
										if (!isnewline) {
							 | 
						|
											fprintf(stderr, "%s: reading %s: missing newline at end of file\n", progname, FUSE_CONF);
							 | 
						|
								
							 | 
						|
										}
							 | 
						|
										fclose(fp);
							 | 
						|
									} else if (errno != ENOENT) {
							 | 
						|
										fprintf(stderr, "%s: failed to open %s: %s\n",
							 | 
						|
											progname, FUSE_CONF, strerror(errno));
							 | 
						|
									}
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								static int begins_with(const char *s, const char *beg)
							 | 
						|
								{
							 | 
						|
									if (strncmp(s, beg, strlen(beg)) == 0)
							 | 
						|
										return 1;
							 | 
						|
									else
							 | 
						|
										return 0;
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								struct mount_flags {
							 | 
						|
									const char *opt;
							 | 
						|
									unsigned long flag;
							 | 
						|
									int on;
							 | 
						|
									int safe;
							 | 
						|
								};
							 | 
						|
								
							 | 
						|
								static struct mount_flags mount_flags[] = {
							 | 
						|
									{"rw",	    MS_RDONLY,	    0, 1},
							 | 
						|
									{"ro",	    MS_RDONLY,	    1, 1},
							 | 
						|
									{"suid",    MS_NOSUID,	    0, 0},
							 | 
						|
									{"nosuid",  MS_NOSUID,	    1, 1},
							 | 
						|
									{"dev",	    MS_NODEV,	    0, 0},
							 | 
						|
									{"nodev",   MS_NODEV,	    1, 1},
							 | 
						|
									{"exec",    MS_NOEXEC,	    0, 1},
							 | 
						|
									{"noexec",  MS_NOEXEC,	    1, 1},
							 | 
						|
									{"async",   MS_SYNCHRONOUS, 0, 1},
							 | 
						|
									{"sync",    MS_SYNCHRONOUS, 1, 1},
							 | 
						|
									{"atime",   MS_NOATIME,	    0, 1},
							 | 
						|
									{"noatime", MS_NOATIME,	    1, 1},
							 | 
						|
									{"dirsync", MS_DIRSYNC,	    1, 1},
							 | 
						|
									{NULL,	    0,		    0, 0}
							 | 
						|
								};
							 | 
						|
								
							 | 
						|
								static int find_mount_flag(const char *s, unsigned len, int *on, int *flag)
							 | 
						|
								{
							 | 
						|
									int i;
							 | 
						|
								
							 | 
						|
									for (i = 0; mount_flags[i].opt != NULL; i++) {
							 | 
						|
										const char *opt = mount_flags[i].opt;
							 | 
						|
										if (strlen(opt) == len && strncmp(opt, s, len) == 0) {
							 | 
						|
											*on = mount_flags[i].on;
							 | 
						|
											*flag = mount_flags[i].flag;
							 | 
						|
											if (!mount_flags[i].safe && getuid() != 0) {
							 | 
						|
												*flag = 0;
							 | 
						|
												fprintf(stderr,
							 | 
						|
													"%s: unsafe option %s ignored\n",
							 | 
						|
													progname, opt);
							 | 
						|
											}
							 | 
						|
											return 1;
							 | 
						|
										}
							 | 
						|
									}
							 | 
						|
									return 0;
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								static int add_option(char **optsp, const char *opt, unsigned expand)
							 | 
						|
								{
							 | 
						|
									char *newopts;
							 | 
						|
									if (*optsp == NULL)
							 | 
						|
										newopts = strdup(opt);
							 | 
						|
									else {
							 | 
						|
										unsigned oldsize = strlen(*optsp);
							 | 
						|
										unsigned newsize = oldsize + 1 + strlen(opt) + expand + 1;
							 | 
						|
										newopts = (char *) realloc(*optsp, newsize);
							 | 
						|
										if (newopts)
							 | 
						|
											sprintf(newopts + oldsize, ",%s", opt);
							 | 
						|
									}
							 | 
						|
									if (newopts == NULL) {
							 | 
						|
										fprintf(stderr, "%s: failed to allocate memory\n", progname);
							 | 
						|
										return -1;
							 | 
						|
									}
							 | 
						|
									*optsp = newopts;
							 | 
						|
									return 0;
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								static int get_mnt_opts(int flags, char *opts, char **mnt_optsp)
							 | 
						|
								{
							 | 
						|
									int i;
							 | 
						|
									int l;
							 | 
						|
								
							 | 
						|
									if (!(flags & MS_RDONLY) && add_option(mnt_optsp, "rw", 0) == -1)
							 | 
						|
										return -1;
							 | 
						|
								
							 | 
						|
									for (i = 0; mount_flags[i].opt != NULL; i++) {
							 | 
						|
										if (mount_flags[i].on && (flags & mount_flags[i].flag) &&
							 | 
						|
										    add_option(mnt_optsp, mount_flags[i].opt, 0) == -1)
							 | 
						|
											return -1;
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									if (add_option(mnt_optsp, opts, 0) == -1)
							 | 
						|
										return -1;
							 | 
						|
									/* remove comma from end of opts*/
							 | 
						|
									l = strlen(*mnt_optsp);
							 | 
						|
									if ((*mnt_optsp)[l-1] == ',')
							 | 
						|
										(*mnt_optsp)[l-1] = '\0';
							 | 
						|
									if (getuid() != 0) {
							 | 
						|
										const char *user = get_user_name();
							 | 
						|
										if (user == NULL)
							 | 
						|
											return -1;
							 | 
						|
								
							 | 
						|
										if (add_option(mnt_optsp, "user=", strlen(user)) == -1)
							 | 
						|
											return -1;
							 | 
						|
										strcat(*mnt_optsp, user);
							 | 
						|
									}
							 | 
						|
									return 0;
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								static int opt_eq(const char *s, unsigned len, const char *opt)
							 | 
						|
								{
							 | 
						|
									if(strlen(opt) == len && strncmp(s, opt, len) == 0)
							 | 
						|
										return 1;
							 | 
						|
									else
							 | 
						|
										return 0;
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								static int get_string_opt(const char *s, unsigned len, const char *opt,
							 | 
						|
											  char **val)
							 | 
						|
								{
							 | 
						|
									int i;
							 | 
						|
									unsigned opt_len = strlen(opt);
							 | 
						|
									char *d;
							 | 
						|
								
							 | 
						|
									if (*val)
							 | 
						|
										free(*val);
							 | 
						|
									*val = (char *) malloc(len - opt_len + 1);
							 | 
						|
									if (!*val) {
							 | 
						|
										fprintf(stderr, "%s: failed to allocate memory\n", progname);
							 | 
						|
										return 0;
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									d = *val;
							 | 
						|
									s += opt_len;
							 | 
						|
									len -= opt_len;
							 | 
						|
									for (i = 0; i < len; i++) {
							 | 
						|
										if (s[i] == '\\' && i + 1 < len)
							 | 
						|
											i++;
							 | 
						|
										*d++ = s[i];
							 | 
						|
									}
							 | 
						|
									*d = '\0';
							 | 
						|
									return 1;
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								static int do_mount(const char *mnt, char **typep, mode_t rootmode,
							 | 
						|
										    int fd, const char *opts, const char *dev, char **sourcep,
							 | 
						|
										    char **mnt_optsp, off_t rootsize)
							 | 
						|
								{
							 | 
						|
									int res;
							 | 
						|
									int flags = MS_NOSUID | MS_NODEV;
							 | 
						|
									char *optbuf;
							 | 
						|
									char *mnt_opts = NULL;
							 | 
						|
									const char *s;
							 | 
						|
									char *d;
							 | 
						|
									char *fsname = NULL;
							 | 
						|
									char *subtype = NULL;
							 | 
						|
									char *source = NULL;
							 | 
						|
									char *type = NULL;
							 | 
						|
									int blkdev = 0;
							 | 
						|
								
							 | 
						|
									optbuf = (char *) malloc(strlen(opts) + 128);
							 | 
						|
									if (!optbuf) {
							 | 
						|
										fprintf(stderr, "%s: failed to allocate memory\n", progname);
							 | 
						|
										return -1;
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									for (s = opts, d = optbuf; *s;) {
							 | 
						|
										unsigned len;
							 | 
						|
										const char *fsname_str = "fsname=";
							 | 
						|
										const char *subtype_str = "subtype=";
							 | 
						|
										for (len = 0; s[len]; len++) {
							 | 
						|
											if (s[len] == '\\' && s[len + 1])
							 | 
						|
												len++;
							 | 
						|
											else if (s[len] == ',')
							 | 
						|
												break;
							 | 
						|
										}
							 | 
						|
										if (begins_with(s, fsname_str)) {
							 | 
						|
											if (!get_string_opt(s, len, fsname_str, &fsname))
							 | 
						|
												goto err;
							 | 
						|
										} else if (begins_with(s, subtype_str)) {
							 | 
						|
											if (!get_string_opt(s, len, subtype_str, &subtype))
							 | 
						|
												goto err;
							 | 
						|
										} else if (opt_eq(s, len, "blkdev")) {
							 | 
						|
											if (getuid() != 0) {
							 | 
						|
												fprintf(stderr,
							 | 
						|
													"%s: option blkdev is privileged\n",
							 | 
						|
													progname);
							 | 
						|
												goto err;
							 | 
						|
											}
							 | 
						|
											blkdev = 1;
							 | 
						|
										} else if (opt_eq(s, len, "auto_unmount")) {
							 | 
						|
											auto_unmount = 1;
							 | 
						|
										} else if (!begins_with(s, "fd=") &&
							 | 
						|
											   !begins_with(s, "rootmode=") &&
							 | 
						|
											   !begins_with(s, "user_id=") &&
							 | 
						|
											   !begins_with(s, "group_id=")) {
							 | 
						|
											int on;
							 | 
						|
											int flag;
							 | 
						|
											int skip_option = 0;
							 | 
						|
											if (opt_eq(s, len, "large_read")) {
							 | 
						|
												struct utsname utsname;
							 | 
						|
												unsigned kmaj, kmin;
							 | 
						|
												res = uname(&utsname);
							 | 
						|
												if (res == 0 &&
							 | 
						|
												    sscanf(utsname.release, "%u.%u",
							 | 
						|
													   &kmaj, &kmin) == 2 &&
							 | 
						|
												    (kmaj > 2 || (kmaj == 2 && kmin > 4))) {
							 | 
						|
													fprintf(stderr, "%s: note: 'large_read' mount option is deprecated for %i.%i kernels\n", progname, kmaj, kmin);
							 | 
						|
													skip_option = 1;
							 | 
						|
												}
							 | 
						|
											}
							 | 
						|
											if (getuid() != 0 && !user_allow_other && opt_eq(s, len, "allow_other")) {
							 | 
						|
												fprintf(stderr, "%s: option %.*s only allowed if 'user_allow_other' is set in /etc/fuse.conf\n", progname, len, s);
							 | 
						|
												goto err;
							 | 
						|
											}
							 | 
						|
											if (!skip_option) {
							 | 
						|
												if (find_mount_flag(s, len, &on, &flag)) {
							 | 
						|
													if (on)
							 | 
						|
														flags |= flag;
							 | 
						|
													else
							 | 
						|
														flags  &= ~flag;
							 | 
						|
												} else {
							 | 
						|
													memcpy(d, s, len);
							 | 
						|
													d += len;
							 | 
						|
													*d++ = ',';
							 | 
						|
												}
							 | 
						|
											}
							 | 
						|
										}
							 | 
						|
										s += len;
							 | 
						|
										if (*s)
							 | 
						|
											s++;
							 | 
						|
									}
							 | 
						|
									*d = '\0';
							 | 
						|
									res = get_mnt_opts(flags, optbuf, &mnt_opts);
							 | 
						|
									if (res == -1)
							 | 
						|
										goto err;
							 | 
						|
								
							 | 
						|
									sprintf(d, "fd=%i,rootmode=%o,user_id=%u,group_id=%u",
							 | 
						|
										fd, rootmode, getuid(), getgid());
							 | 
						|
								
							 | 
						|
									source = malloc((fsname ? strlen(fsname) : 0) +
							 | 
						|
											(subtype ? strlen(subtype) : 0) + strlen(dev) + 32);
							 | 
						|
								
							 | 
						|
									type = malloc((subtype ? strlen(subtype) : 0) + 32);
							 | 
						|
									if (!type || !source) {
							 | 
						|
										fprintf(stderr, "%s: failed to allocate memory\n", progname);
							 | 
						|
										goto err;
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									if (subtype)
							 | 
						|
										sprintf(type, "%s.%s", blkdev ? "fuseblk" : "fuse", subtype);
							 | 
						|
									else
							 | 
						|
										strcpy(type, blkdev ? "fuseblk" : "fuse");
							 | 
						|
								
							 | 
						|
									if (fsname)
							 | 
						|
										strcpy(source, fsname);
							 | 
						|
									else
							 | 
						|
										strcpy(source, subtype ? subtype : dev);
							 | 
						|
								
							 | 
						|
									res = mount(source, mnt, type, flags, optbuf);
							 | 
						|
									if (res == -1 && errno == ENODEV && subtype) {
							 | 
						|
										/* Probably missing subtype support */
							 | 
						|
										strcpy(type, blkdev ? "fuseblk" : "fuse");
							 | 
						|
										if (fsname) {
							 | 
						|
											if (!blkdev)
							 | 
						|
												sprintf(source, "%s#%s", subtype, fsname);
							 | 
						|
										} else {
							 | 
						|
											strcpy(source, type);
							 | 
						|
										}
							 | 
						|
								
							 | 
						|
										res = mount(source, mnt, type, flags, optbuf);
							 | 
						|
									}
							 | 
						|
									if (res == -1 && errno == EINVAL) {
							 | 
						|
										/* It could be an old version not supporting group_id */
							 | 
						|
										sprintf(d, "fd=%i,rootmode=%o,user_id=%u",
							 | 
						|
											fd, rootmode, getuid());
							 | 
						|
										res = mount(source, mnt, type, flags, optbuf);
							 | 
						|
									}
							 | 
						|
									if (res == -1) {
							 | 
						|
										int errno_save = errno;
							 | 
						|
										if (blkdev && errno == ENODEV && !fuse_mnt_check_fuseblk())
							 | 
						|
											fprintf(stderr, "%s: 'fuseblk' support missing\n",
							 | 
						|
												progname);
							 | 
						|
										else
							 | 
						|
											fprintf(stderr, "%s: mount failed: %s\n", progname,
							 | 
						|
												strerror(errno_save));
							 | 
						|
										goto err;
							 | 
						|
									}
							 | 
						|
									*sourcep = source;
							 | 
						|
									*typep = type;
							 | 
						|
									*mnt_optsp = mnt_opts;
							 | 
						|
									free(fsname);
							 | 
						|
									free(optbuf);
							 | 
						|
								
							 | 
						|
									return 0;
							 | 
						|
								
							 | 
						|
								err:
							 | 
						|
									free(fsname);
							 | 
						|
									free(subtype);
							 | 
						|
									free(source);
							 | 
						|
									free(type);
							 | 
						|
									free(mnt_opts);
							 | 
						|
									free(optbuf);
							 | 
						|
									return -1;
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								static int check_version(const char *dev)
							 | 
						|
								{
							 | 
						|
									int res;
							 | 
						|
									int majorver;
							 | 
						|
									int minorver;
							 | 
						|
									const char *version_file;
							 | 
						|
									FILE *vf;
							 | 
						|
								
							 | 
						|
									if (strcmp(dev, FUSE_DEV_OLD) != 0)
							 | 
						|
										return 0;
							 | 
						|
								
							 | 
						|
									version_file = FUSE_VERSION_FILE_OLD;
							 | 
						|
									vf = fopen(version_file, "r");
							 | 
						|
									if (vf == NULL) {
							 | 
						|
										fprintf(stderr, "%s: kernel interface too old\n", progname);
							 | 
						|
										return -1;
							 | 
						|
									}
							 | 
						|
									res = fscanf(vf, "%i.%i", &majorver, &minorver);
							 | 
						|
									fclose(vf);
							 | 
						|
									if (res != 2) {
							 | 
						|
										fprintf(stderr, "%s: error reading %s\n", progname,
							 | 
						|
											version_file);
							 | 
						|
										return -1;
							 | 
						|
									}
							 | 
						|
									if (majorver < 3) {
							 | 
						|
										fprintf(stderr, "%s: kernel interface too old\n", progname);
							 | 
						|
										return -1;
							 | 
						|
									}
							 | 
						|
									return 0;
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								static int check_perm(const char **mntp, struct stat *stbuf, int *mountpoint_fd)
							 | 
						|
								{
							 | 
						|
									int res;
							 | 
						|
									const char *mnt = *mntp;
							 | 
						|
									const char *origmnt = mnt;
							 | 
						|
								
							 | 
						|
									res = lstat(mnt, stbuf);
							 | 
						|
									if (res == -1) {
							 | 
						|
										fprintf(stderr, "%s: failed to access mountpoint %s: %s\n",
							 | 
						|
											progname, mnt, strerror(errno));
							 | 
						|
										return -1;
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									/* No permission checking is done for root */
							 | 
						|
									if (getuid() == 0)
							 | 
						|
										return 0;
							 | 
						|
								
							 | 
						|
									if (S_ISDIR(stbuf->st_mode)) {
							 | 
						|
										res = chdir(mnt);
							 | 
						|
										if (res == -1) {
							 | 
						|
											fprintf(stderr,
							 | 
						|
												"%s: failed to chdir to mountpoint: %s\n",
							 | 
						|
												progname, strerror(errno));
							 | 
						|
											return -1;
							 | 
						|
										}
							 | 
						|
										mnt = *mntp = ".";
							 | 
						|
										res = lstat(mnt, stbuf);
							 | 
						|
										if (res == -1) {
							 | 
						|
											fprintf(stderr,
							 | 
						|
												"%s: failed to access mountpoint %s: %s\n",
							 | 
						|
												progname, origmnt, strerror(errno));
							 | 
						|
											return -1;
							 | 
						|
										}
							 | 
						|
								
							 | 
						|
										if ((stbuf->st_mode & S_ISVTX) && stbuf->st_uid != getuid()) {
							 | 
						|
											fprintf(stderr, "%s: mountpoint %s not owned by user\n",
							 | 
						|
												progname, origmnt);
							 | 
						|
											return -1;
							 | 
						|
										}
							 | 
						|
								
							 | 
						|
										res = access(mnt, W_OK);
							 | 
						|
										if (res == -1) {
							 | 
						|
											fprintf(stderr, "%s: user has no write access to mountpoint %s\n",
							 | 
						|
												progname, origmnt);
							 | 
						|
											return -1;
							 | 
						|
										}
							 | 
						|
									} else if (S_ISREG(stbuf->st_mode)) {
							 | 
						|
										static char procfile[256];
							 | 
						|
										*mountpoint_fd = open(mnt, O_WRONLY);
							 | 
						|
										if (*mountpoint_fd == -1) {
							 | 
						|
											fprintf(stderr, "%s: failed to open %s: %s\n",
							 | 
						|
												progname, mnt, strerror(errno));
							 | 
						|
											return -1;
							 | 
						|
										}
							 | 
						|
										res = fstat(*mountpoint_fd, stbuf);
							 | 
						|
										if (res == -1) {
							 | 
						|
											fprintf(stderr,
							 | 
						|
												"%s: failed to access mountpoint %s: %s\n",
							 | 
						|
												progname, mnt, strerror(errno));
							 | 
						|
											return -1;
							 | 
						|
										}
							 | 
						|
										if (!S_ISREG(stbuf->st_mode)) {
							 | 
						|
											fprintf(stderr,
							 | 
						|
												"%s: mountpoint %s is no longer a regular file\n",
							 | 
						|
												progname, mnt);
							 | 
						|
											return -1;
							 | 
						|
										}
							 | 
						|
								
							 | 
						|
										sprintf(procfile, "/proc/self/fd/%i", *mountpoint_fd);
							 | 
						|
										*mntp = procfile;
							 | 
						|
									} else {
							 | 
						|
										fprintf(stderr,
							 | 
						|
											"%s: mountpoint %s is not a directory or a regular file\n",
							 | 
						|
											progname, mnt);
							 | 
						|
										return -1;
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
								
							 | 
						|
									return 0;
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								static int try_open(const char *dev, char **devp, int silent)
							 | 
						|
								{
							 | 
						|
									int fd = open(dev, O_RDWR);
							 | 
						|
									if (fd != -1) {
							 | 
						|
										*devp = strdup(dev);
							 | 
						|
										if (*devp == NULL) {
							 | 
						|
											fprintf(stderr, "%s: failed to allocate memory\n",
							 | 
						|
												progname);
							 | 
						|
											close(fd);
							 | 
						|
											fd = -1;
							 | 
						|
										}
							 | 
						|
									} else if (errno == ENODEV ||
							 | 
						|
										   errno == ENOENT)/* check for ENOENT too, for the udev case */
							 | 
						|
										return -2;
							 | 
						|
									else if (!silent) {
							 | 
						|
										fprintf(stderr, "%s: failed to open %s: %s\n", progname, dev,
							 | 
						|
											strerror(errno));
							 | 
						|
									}
							 | 
						|
									return fd;
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								static int try_open_fuse_device(char **devp)
							 | 
						|
								{
							 | 
						|
									int fd;
							 | 
						|
									int err;
							 | 
						|
								
							 | 
						|
									drop_privs();
							 | 
						|
									fd = try_open(FUSE_DEV_NEW, devp, 0);
							 | 
						|
									restore_privs();
							 | 
						|
									if (fd >= 0)
							 | 
						|
										return fd;
							 | 
						|
								
							 | 
						|
									err = fd;
							 | 
						|
									fd = try_open(FUSE_DEV_OLD, devp, 1);
							 | 
						|
									if (fd >= 0)
							 | 
						|
										return fd;
							 | 
						|
								
							 | 
						|
									return err;
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								static int open_fuse_device(char **devp)
							 | 
						|
								{
							 | 
						|
									int fd = try_open_fuse_device(devp);
							 | 
						|
									if (fd >= -1)
							 | 
						|
										return fd;
							 | 
						|
								
							 | 
						|
									fprintf(stderr,
							 | 
						|
										"%s: fuse device not found, try 'modprobe fuse' first\n",
							 | 
						|
										progname);
							 | 
						|
								
							 | 
						|
									return -1;
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								static int mount_fuse(const char *mnt, const char *opts)
							 | 
						|
								{
							 | 
						|
									int res;
							 | 
						|
									int fd;
							 | 
						|
									char *dev;
							 | 
						|
									struct stat stbuf;
							 | 
						|
									char *type = NULL;
							 | 
						|
									char *source = NULL;
							 | 
						|
									char *mnt_opts = NULL;
							 | 
						|
									const char *real_mnt = mnt;
							 | 
						|
									int mountpoint_fd = -1;
							 | 
						|
								
							 | 
						|
									fd = open_fuse_device(&dev);
							 | 
						|
									if (fd == -1)
							 | 
						|
										return -1;
							 | 
						|
								
							 | 
						|
									drop_privs();
							 | 
						|
									read_conf();
							 | 
						|
								
							 | 
						|
									if (getuid() != 0 && mount_max != -1) {
							 | 
						|
										int mount_count = count_fuse_fs();
							 | 
						|
										if (mount_count >= mount_max) {
							 | 
						|
											fprintf(stderr, "%s: too many FUSE filesystems mounted; mount_max=N can be set in /etc/fuse.conf\n", progname);
							 | 
						|
											goto fail_close_fd;
							 | 
						|
										}
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									res = check_version(dev);
							 | 
						|
									if (res != -1) {
							 | 
						|
										res = check_perm(&real_mnt, &stbuf, &mountpoint_fd);
							 | 
						|
										restore_privs();
							 | 
						|
										if (res != -1)
							 | 
						|
											res = do_mount(real_mnt, &type, stbuf.st_mode & S_IFMT,
							 | 
						|
												       fd, opts, dev, &source, &mnt_opts,
							 | 
						|
												       stbuf.st_size);
							 | 
						|
									} else
							 | 
						|
										restore_privs();
							 | 
						|
								
							 | 
						|
									if (mountpoint_fd != -1)
							 | 
						|
										close(mountpoint_fd);
							 | 
						|
								
							 | 
						|
									if (res == -1)
							 | 
						|
										goto fail_close_fd;
							 | 
						|
								
							 | 
						|
									res = chdir("/");
							 | 
						|
									if (res == -1) {
							 | 
						|
										fprintf(stderr, "%s: failed to chdir to '/'\n", progname);
							 | 
						|
										goto fail_close_fd;
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									if (geteuid() == 0) {
							 | 
						|
										res = add_mount(source, mnt, type, mnt_opts);
							 | 
						|
										if (res == -1) {
							 | 
						|
											/* Can't clean up mount in a non-racy way */
							 | 
						|
											goto fail_close_fd;
							 | 
						|
										}
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
								out_free:
							 | 
						|
									free(source);
							 | 
						|
									free(type);
							 | 
						|
									free(mnt_opts);
							 | 
						|
									free(dev);
							 | 
						|
								
							 | 
						|
									return fd;
							 | 
						|
								
							 | 
						|
								fail_close_fd:
							 | 
						|
									close(fd);
							 | 
						|
									fd = -1;
							 | 
						|
									goto out_free;
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								static int send_fd(int sock_fd, int fd)
							 | 
						|
								{
							 | 
						|
									int retval;
							 | 
						|
									struct msghdr msg;
							 | 
						|
									struct cmsghdr *p_cmsg;
							 | 
						|
									struct iovec vec;
							 | 
						|
									size_t cmsgbuf[CMSG_SPACE(sizeof(fd)) / sizeof(size_t)];
							 | 
						|
									int *p_fds;
							 | 
						|
									char sendchar = 0;
							 | 
						|
								
							 | 
						|
									msg.msg_control = cmsgbuf;
							 | 
						|
									msg.msg_controllen = sizeof(cmsgbuf);
							 | 
						|
									p_cmsg = CMSG_FIRSTHDR(&msg);
							 | 
						|
									p_cmsg->cmsg_level = SOL_SOCKET;
							 | 
						|
									p_cmsg->cmsg_type = SCM_RIGHTS;
							 | 
						|
									p_cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
							 | 
						|
									p_fds = (int *) CMSG_DATA(p_cmsg);
							 | 
						|
									*p_fds = fd;
							 | 
						|
									msg.msg_controllen = p_cmsg->cmsg_len;
							 | 
						|
									msg.msg_name = NULL;
							 | 
						|
									msg.msg_namelen = 0;
							 | 
						|
									msg.msg_iov = &vec;
							 | 
						|
									msg.msg_iovlen = 1;
							 | 
						|
									msg.msg_flags = 0;
							 | 
						|
									/* "To pass file descriptors or credentials you need to send/read at
							 | 
						|
									 * least one byte" (man 7 unix) */
							 | 
						|
									vec.iov_base = &sendchar;
							 | 
						|
									vec.iov_len = sizeof(sendchar);
							 | 
						|
									while ((retval = sendmsg(sock_fd, &msg, 0)) == -1 && errno == EINTR);
							 | 
						|
									if (retval != 1) {
							 | 
						|
										perror("sending file descriptor");
							 | 
						|
										return -1;
							 | 
						|
									}
							 | 
						|
									return 0;
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								static void usage(void)
							 | 
						|
								{
							 | 
						|
									fprintf(stderr,
							 | 
						|
										"%s: [options] mountpoint\n"
							 | 
						|
										"Options:\n"
							 | 
						|
										" -h		    print help\n"
							 | 
						|
										" -V		    print version\n"
							 | 
						|
										" -o opt[,opt...]   mount options\n"
							 | 
						|
										" -u		    unmount\n"
							 | 
						|
										" -q		    quiet\n"
							 | 
						|
										" -z		    lazy unmount\n",
							 | 
						|
										progname);
							 | 
						|
									exit(1);
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								static void show_version(void)
							 | 
						|
								{
							 | 
						|
								  exit(0);
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								int main(int argc, char *argv[])
							 | 
						|
								{
							 | 
						|
									sigset_t sigset;
							 | 
						|
									int ch;
							 | 
						|
									int fd;
							 | 
						|
									int res;
							 | 
						|
									char *origmnt;
							 | 
						|
									char *mnt;
							 | 
						|
									static int unmount = 0;
							 | 
						|
									static int lazy = 0;
							 | 
						|
									static int quiet = 0;
							 | 
						|
									char *commfd;
							 | 
						|
									int cfd;
							 | 
						|
									const char *opts = "";
							 | 
						|
								
							 | 
						|
									static const struct option long_opts[] = {
							 | 
						|
										{"unmount", no_argument, NULL, 'u'},
							 | 
						|
										{"lazy",    no_argument, NULL, 'z'},
							 | 
						|
										{"quiet",   no_argument, NULL, 'q'},
							 | 
						|
										{"help",    no_argument, NULL, 'h'},
							 | 
						|
										{"version", no_argument, NULL, 'V'},
							 | 
						|
										{0, 0, 0, 0}};
							 | 
						|
								
							 | 
						|
									progname = strdup(argv[0]);
							 | 
						|
									if (progname == NULL) {
							 | 
						|
										fprintf(stderr, "%s: failed to allocate memory\n", argv[0]);
							 | 
						|
										exit(1);
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									while ((ch = getopt_long(argc, argv, "hVo:uzq", long_opts,
							 | 
						|
												 NULL)) != -1) {
							 | 
						|
										switch (ch) {
							 | 
						|
										case 'h':
							 | 
						|
											usage();
							 | 
						|
											break;
							 | 
						|
								
							 | 
						|
										case 'V':
							 | 
						|
											show_version();
							 | 
						|
											break;
							 | 
						|
								
							 | 
						|
										case 'o':
							 | 
						|
											opts = optarg;
							 | 
						|
											break;
							 | 
						|
								
							 | 
						|
										case 'u':
							 | 
						|
											unmount = 1;
							 | 
						|
											break;
							 | 
						|
								
							 | 
						|
										case 'z':
							 | 
						|
											lazy = 1;
							 | 
						|
											break;
							 | 
						|
								
							 | 
						|
										case 'q':
							 | 
						|
											quiet = 1;
							 | 
						|
											break;
							 | 
						|
								
							 | 
						|
										default:
							 | 
						|
											exit(1);
							 | 
						|
										}
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									if (lazy && !unmount) {
							 | 
						|
										fprintf(stderr, "%s: -z can only be used with -u\n", progname);
							 | 
						|
										exit(1);
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									if (optind >= argc) {
							 | 
						|
										fprintf(stderr, "%s: missing mountpoint argument\n", progname);
							 | 
						|
										exit(1);
							 | 
						|
									} else if (argc > optind + 1) {
							 | 
						|
										fprintf(stderr, "%s: extra arguments after the mountpoint\n",
							 | 
						|
											progname);
							 | 
						|
										exit(1);
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									origmnt = argv[optind];
							 | 
						|
								
							 | 
						|
									drop_privs();
							 | 
						|
									mnt = fuse_mnt_resolve_path(progname, origmnt);
							 | 
						|
									if (mnt != NULL) {
							 | 
						|
										res = chdir("/");
							 | 
						|
										if (res == -1) {
							 | 
						|
											fprintf(stderr, "%s: failed to chdir to '/'\n", progname);
							 | 
						|
											exit(1);
							 | 
						|
										}
							 | 
						|
									}
							 | 
						|
									restore_privs();
							 | 
						|
									if (mnt == NULL)
							 | 
						|
										exit(1);
							 | 
						|
								
							 | 
						|
									umask(033);
							 | 
						|
									if (unmount)
							 | 
						|
										goto do_unmount;
							 | 
						|
								
							 | 
						|
									commfd = getenv(FUSE_COMMFD_ENV);
							 | 
						|
									if (commfd == NULL) {
							 | 
						|
										fprintf(stderr, "%s: old style mounting not supported\n",
							 | 
						|
											progname);
							 | 
						|
										exit(1);
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									fd = mount_fuse(mnt, opts);
							 | 
						|
									if (fd == -1)
							 | 
						|
										exit(1);
							 | 
						|
								
							 | 
						|
									cfd = atoi(commfd);
							 | 
						|
									res = send_fd(cfd, fd);
							 | 
						|
									if (res == -1)
							 | 
						|
										exit(1);
							 | 
						|
									close(fd);
							 | 
						|
								
							 | 
						|
									if (!auto_unmount)
							 | 
						|
										return 0;
							 | 
						|
								
							 | 
						|
									/* Become a daemon and wait for the parent to exit or die.
							 | 
						|
									   ie For the control socket to get closed.
							 | 
						|
									   btw We don't want to use daemon() function here because
							 | 
						|
									   it forks and messes with the file descriptors. */
							 | 
						|
									setsid();
							 | 
						|
									res = chdir("/");
							 | 
						|
									if (res == -1) {
							 | 
						|
										fprintf(stderr, "%s: failed to chdir to '/'\n", progname);
							 | 
						|
										exit(1);
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									sigfillset(&sigset);
							 | 
						|
									sigprocmask(SIG_BLOCK, &sigset, NULL);
							 | 
						|
								
							 | 
						|
									lazy  = 1;
							 | 
						|
									quiet = 1;
							 | 
						|
								
							 | 
						|
									while (1) {
							 | 
						|
										unsigned char buf[16];
							 | 
						|
										int n = recv(cfd, buf, sizeof(buf), 0);
							 | 
						|
										if (!n)
							 | 
						|
											break;
							 | 
						|
								
							 | 
						|
										if (n < 0) {
							 | 
						|
											if (errno == EINTR)
							 | 
						|
												continue;
							 | 
						|
											break;
							 | 
						|
										}
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
								do_unmount:
							 | 
						|
									if (geteuid() == 0)
							 | 
						|
										res = unmount_fuse(mnt, quiet, lazy);
							 | 
						|
									else {
							 | 
						|
										res = umount2(mnt, lazy ? UMOUNT_DETACH : 0);
							 | 
						|
										if (res == -1 && !quiet)
							 | 
						|
											fprintf(stderr,
							 | 
						|
												"%s: failed to unmount %s: %s\n",
							 | 
						|
												progname, mnt, strerror(errno));
							 | 
						|
									}
							 | 
						|
									if (res == -1)
							 | 
						|
										exit(1);
							 | 
						|
									return 0;
							 | 
						|
								}
							 |