summaryrefslogtreecommitdiffstats
path: root/contrib/fuse-util/fusermount.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/fuse-util/fusermount.c')
-rw-r--r--contrib/fuse-util/fusermount.c965
1 files changed, 965 insertions, 0 deletions
diff --git a/contrib/fuse-util/fusermount.c b/contrib/fuse-util/fusermount.c
new file mode 100644
index 00000000000..c3ecc86cc44
--- /dev/null
+++ b/contrib/fuse-util/fusermount.c
@@ -0,0 +1,965 @@
+/*
+ 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 */
+
+#include <config.h>
+
+#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 <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>
+
+#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
+
+static const char *progname;
+
+static int user_allow_other = 0;
+static int mount_max = 1000;
+
+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
+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)
+{
+ 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;
+
+ 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 fuse_mnt_umount(progname, mnt, lazy);
+}
+
+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, 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)
+{
+ unsigned opt_len = strlen(opt);
+
+ if (*val)
+ free(*val);
+ *val = (char *) malloc(len - opt_len + 1);
+ if (!*val) {
+ fprintf(stderr, "%s: failed to allocate memory\n", progname);
+ return 0;
+ }
+
+ memcpy(*val, s + opt_len, len - opt_len);
+ (*val)[len - opt_len] = '\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 check_empty = 1;
+ 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] && s[len] != ','; len++);
+ 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, "nonempty")) {
+ check_empty = 0;
+ } 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") ||
+ opt_eq(s, len, "allow_root"))) {
+ 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=%i,group_id=%i",
+ fd, rootmode, getuid(), getgid());
+
+ if (check_empty &&
+ fuse_mnt_check_empty(progname, mnt, rootmode, rootsize) == -1)
+ goto err;
+
+ 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=%i",
+ 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;
+ } else {
+ *sourcep = source;
+ *typep = type;
+ *mnt_optsp = mnt_opts;
+ }
+ free(fsname);
+ free(optbuf);
+
+ return res;
+
+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 *currdir_fd,
+ 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)) {
+ *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(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 currdir_fd = -1;
+ 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);
+ close(fd);
+ return -1;
+ }
+ }
+
+ res = check_version(dev);
+ if (res != -1) {
+ res = check_perm(&real_mnt, &stbuf, &currdir_fd,
+ &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 (currdir_fd != -1) {
+ fchdir(currdir_fd);
+ close(currdir_fd);
+ }
+ if (mountpoint_fd != -1)
+ close(mountpoint_fd);
+
+ if (res == -1) {
+ close(fd);
+ return -1;
+ }
+
+ if (geteuid() == 0) {
+ res = add_mount(source, mnt, type, mnt_opts);
+ if (res == -1) {
+ umount2(mnt, 2); /* lazy umount */
+ close(fd);
+ return -1;
+ }
+ }
+
+ free(source);
+ free(type);
+ free(mnt_opts);
+ free(dev);
+
+ return fd;
+}
+
+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)
+{
+ printf("fusermount version: %s\n", PACKAGE_VERSION);
+ exit(0);
+}
+
+int main(int argc, char *argv[])
+{
+ 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);
+ restore_privs();
+ if (mnt == NULL)
+ exit(1);
+
+ umask(033);
+ if (unmount) {
+ if (geteuid() == 0)
+ res = unmount_fuse(mnt, quiet, lazy);
+ else {
+ res = umount2(mnt, lazy ? 2 : 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;
+ }
+
+ 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);
+
+ return 0;
+}