"Fossies" - the Fresh Open Source Software Archive

Member "fuse-3.2.3/util/fusermount.c" (11 May 2018, 27993 Bytes) of package /linux/misc/fuse-3.2.3.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "fusermount.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 3.2.2_vs_3.2.3.

    1 /*
    2   FUSE: Filesystem in Userspace
    3   Copyright (C) 2001-2007  Miklos Szeredi <miklos@szeredi.hu>
    4 
    5   This program can be distributed under the terms of the GNU GPL.
    6   See the file COPYING.
    7 */
    8 /* This program does the mounting and unmounting of FUSE filesystems */
    9 
   10 #define _GNU_SOURCE /* for clone */
   11 #include <config.h>
   12 
   13 #include "mount_util.h"
   14 #include <stdio.h>
   15 #include <stdlib.h>
   16 #include <string.h>
   17 #include <ctype.h>
   18 #include <unistd.h>
   19 #include <getopt.h>
   20 #include <errno.h>
   21 #include <fcntl.h>
   22 #include <pwd.h>
   23 #include <paths.h>
   24 #include <mntent.h>
   25 #include <sys/wait.h>
   26 #include <sys/stat.h>
   27 #include <sys/mount.h>
   28 #include <sys/fsuid.h>
   29 #include <sys/socket.h>
   30 #include <sys/utsname.h>
   31 #include <sched.h>
   32 
   33 #define FUSE_COMMFD_ENV     "_FUSE_COMMFD"
   34 
   35 #define FUSE_DEV "/dev/fuse"
   36 #define FUSE_CONF "/etc/fuse.conf"
   37 
   38 #ifndef MS_DIRSYNC
   39 #define MS_DIRSYNC 128
   40 #endif
   41 #ifndef MS_REC
   42 #define MS_REC 16384
   43 #endif
   44 #ifndef MS_PRIVATE
   45 #define MS_PRIVATE (1<<18)
   46 #endif
   47 
   48 #ifndef UMOUNT_DETACH
   49 #define UMOUNT_DETACH   0x00000002  /* Just detach from the tree */
   50 #endif
   51 #ifndef UMOUNT_NOFOLLOW
   52 #define UMOUNT_NOFOLLOW 0x00000008  /* Don't follow symlink on umount */
   53 #endif
   54 #ifndef UMOUNT_UNUSED
   55 #define UMOUNT_UNUSED   0x80000000  /* Flag guaranteed to be unused */
   56 #endif
   57 
   58 static const char *progname;
   59 
   60 static int user_allow_other = 0;
   61 static int mount_max = 1000;
   62 
   63 static int auto_unmount = 0;
   64 
   65 static const char *get_user_name(void)
   66 {
   67     struct passwd *pw = getpwuid(getuid());
   68     if (pw != NULL && pw->pw_name != NULL)
   69         return pw->pw_name;
   70     else {
   71         fprintf(stderr, "%s: could not determine username\n", progname);
   72         return NULL;
   73     }
   74 }
   75 
   76 static uid_t oldfsuid;
   77 static gid_t oldfsgid;
   78 
   79 static void drop_privs(void)
   80 {
   81     if (getuid() != 0) {
   82         oldfsuid = setfsuid(getuid());
   83         oldfsgid = setfsgid(getgid());
   84     }
   85 }
   86 
   87 static void restore_privs(void)
   88 {
   89     if (getuid() != 0) {
   90         setfsuid(oldfsuid);
   91         setfsgid(oldfsgid);
   92     }
   93 }
   94 
   95 #ifndef IGNORE_MTAB
   96 /*
   97  * Make sure that /etc/mtab is checked and updated atomically
   98  */
   99 static int lock_umount(void)
  100 {
  101     const char *mtab_lock = _PATH_MOUNTED ".fuselock";
  102     int mtablock;
  103     int res;
  104     struct stat mtab_stat;
  105 
  106     /* /etc/mtab could be a symlink to /proc/mounts */
  107     if (lstat(_PATH_MOUNTED, &mtab_stat) == 0 && S_ISLNK(mtab_stat.st_mode))
  108         return -1;
  109 
  110     mtablock = open(mtab_lock, O_RDWR | O_CREAT, 0600);
  111     if (mtablock == -1) {
  112         fprintf(stderr, "%s: unable to open fuse lock file: %s\n",
  113             progname, strerror(errno));
  114         return -1;
  115     }
  116     res = lockf(mtablock, F_LOCK, 0);
  117     if (res < 0) {
  118         fprintf(stderr, "%s: error getting lock: %s\n", progname,
  119             strerror(errno));
  120         close(mtablock);
  121         return -1;
  122     }
  123 
  124     return mtablock;
  125 }
  126 
  127 static void unlock_umount(int mtablock)
  128 {
  129     if (mtablock >= 0) {
  130         int res;
  131 
  132         res = lockf(mtablock, F_ULOCK, 0);
  133         if (res < 0) {
  134             fprintf(stderr, "%s: error releasing lock: %s\n",
  135                 progname, strerror(errno));
  136         }
  137         close(mtablock);
  138     }
  139 }
  140 
  141 static int add_mount(const char *source, const char *mnt, const char *type,
  142              const char *opts)
  143 {
  144     return fuse_mnt_add_mount(progname, source, mnt, type, opts);
  145 }
  146 
  147 static int may_unmount(const char *mnt, int quiet)
  148 {
  149     struct mntent *entp;
  150     FILE *fp;
  151     const char *user = NULL;
  152     char uidstr[32];
  153     unsigned uidlen = 0;
  154     int found;
  155     const char *mtab = _PATH_MOUNTED;
  156 
  157     user = get_user_name();
  158     if (user == NULL)
  159         return -1;
  160 
  161     fp = setmntent(mtab, "r");
  162     if (fp == NULL) {
  163         fprintf(stderr, "%s: failed to open %s: %s\n", progname, mtab,
  164             strerror(errno));
  165         return -1;
  166     }
  167 
  168     uidlen = sprintf(uidstr, "%u", getuid());
  169 
  170     found = 0;
  171     while ((entp = getmntent(fp)) != NULL) {
  172         if (!found && strcmp(entp->mnt_dir, mnt) == 0 &&
  173             (strcmp(entp->mnt_type, "fuse") == 0 ||
  174              strcmp(entp->mnt_type, "fuseblk") == 0 ||
  175              strncmp(entp->mnt_type, "fuse.", 5) == 0 ||
  176              strncmp(entp->mnt_type, "fuseblk.", 8) == 0)) {
  177             char *p = strstr(entp->mnt_opts, "user=");
  178             if (p &&
  179                 (p == entp->mnt_opts || *(p-1) == ',') &&
  180                 strcmp(p + 5, user) == 0) {
  181                 found = 1;
  182                 break;
  183             }
  184             /* /etc/mtab is a link pointing to
  185                /proc/mounts: */
  186             else if ((p =
  187                   strstr(entp->mnt_opts, "user_id=")) &&
  188                  (p == entp->mnt_opts ||
  189                   *(p-1) == ',') &&
  190                  strncmp(p + 8, uidstr, uidlen) == 0 &&
  191                  (*(p+8+uidlen) == ',' ||
  192                   *(p+8+uidlen) == '\0')) {
  193                 found = 1;
  194                 break;
  195             }
  196         }
  197     }
  198     endmntent(fp);
  199 
  200     if (!found) {
  201         if (!quiet)
  202             fprintf(stderr,
  203                 "%s: entry for %s not found in %s\n",
  204                 progname, mnt, mtab);
  205         return -1;
  206     }
  207 
  208     return 0;
  209 }
  210 
  211 /*
  212  * Check whether the file specified in "fusermount3 -u" is really a
  213  * mountpoint and not a symlink.  This is necessary otherwise the user
  214  * could move the mountpoint away and replace it with a symlink
  215  * pointing to an arbitrary mount, thereby tricking fusermount3 into
  216  * unmounting that (umount(2) will follow symlinks).
  217  *
  218  * This is the child process running in a separate mount namespace, so
  219  * we don't mess with the global namespace and if the process is
  220  * killed for any reason, mounts are automatically cleaned up.
  221  *
  222  * First make sure nothing is propagated back into the parent
  223  * namespace by marking all mounts "private".
  224  *
  225  * Then bind mount parent onto a stable base where the user can't move
  226  * it around.
  227  *
  228  * Finally check /proc/mounts for an entry matching the requested
  229  * mountpoint.  If it's found then we are OK, and the user can't move
  230  * it around within the parent directory as rename() will return
  231  * EBUSY.  Be careful to ignore any mounts that existed before the
  232  * bind.
  233  */
  234 static int check_is_mount_child(void *p)
  235 {
  236     const char **a = p;
  237     const char *last = a[0];
  238     const char *mnt = a[1];
  239     int res;
  240     const char *procmounts = "/proc/mounts";
  241     int found;
  242     FILE *fp;
  243     struct mntent *entp;
  244     int count;
  245 
  246     res = mount("", "/", "", MS_PRIVATE | MS_REC, NULL);
  247     if (res == -1) {
  248         fprintf(stderr, "%s: failed to mark mounts private: %s\n",
  249             progname, strerror(errno));
  250         return 1;
  251     }
  252 
  253     fp = setmntent(procmounts, "r");
  254     if (fp == NULL) {
  255         fprintf(stderr, "%s: failed to open %s: %s\n", progname,
  256             procmounts, strerror(errno));
  257         return 1;
  258     }
  259 
  260     count = 0;
  261     while (getmntent(fp) != NULL)
  262         count++;
  263     endmntent(fp);
  264 
  265     fp = setmntent(procmounts, "r");
  266     if (fp == NULL) {
  267         fprintf(stderr, "%s: failed to open %s: %s\n", progname,
  268             procmounts, strerror(errno));
  269         return 1;
  270     }
  271 
  272     res = mount(".", "/", "", MS_BIND | MS_REC, NULL);
  273     if (res == -1) {
  274         fprintf(stderr, "%s: failed to bind parent to /: %s\n",
  275             progname, strerror(errno));
  276         return 1;
  277     }
  278 
  279     found = 0;
  280     while ((entp = getmntent(fp)) != NULL) {
  281         if (count > 0) {
  282             count--;
  283             continue;
  284         }
  285         if (entp->mnt_dir[0] == '/' &&
  286             strcmp(entp->mnt_dir + 1, last) == 0) {
  287             found = 1;
  288             break;
  289         }
  290     }
  291     endmntent(fp);
  292 
  293     if (!found) {
  294         fprintf(stderr, "%s: %s not mounted\n", progname, mnt);
  295         return 1;
  296     }
  297 
  298     return 0;
  299 }
  300 
  301 static pid_t clone_newns(void *a)
  302 {
  303     char buf[131072];
  304     char *stack = buf + (sizeof(buf) / 2 - ((size_t) buf & 15));
  305 
  306 #ifdef __ia64__
  307     extern int __clone2(int (*fn)(void *),
  308                 void *child_stack_base, size_t stack_size,
  309                 int flags, void *arg, pid_t *ptid,
  310                 void *tls, pid_t *ctid);
  311 
  312     return __clone2(check_is_mount_child, stack, sizeof(buf) / 2,
  313             CLONE_NEWNS, a, NULL, NULL, NULL);
  314 #else
  315     return clone(check_is_mount_child, stack, CLONE_NEWNS, a);
  316 #endif
  317 }
  318 
  319 static int check_is_mount(const char *last, const char *mnt)
  320 {
  321     pid_t pid, p;
  322     int status;
  323     const char *a[2] = { last, mnt };
  324 
  325     pid = clone_newns((void *) a);
  326     if (pid == (pid_t) -1) {
  327         fprintf(stderr, "%s: failed to clone namespace: %s\n",
  328             progname, strerror(errno));
  329         return -1;
  330     }
  331     p = waitpid(pid, &status, __WCLONE);
  332     if (p == (pid_t) -1) {
  333         fprintf(stderr, "%s: waitpid failed: %s\n",
  334             progname, strerror(errno));
  335         return -1;
  336     }
  337     if (!WIFEXITED(status)) {
  338         fprintf(stderr, "%s: child terminated abnormally (status %i)\n",
  339             progname, status);
  340         return -1;
  341     }
  342     if (WEXITSTATUS(status) != 0)
  343         return -1;
  344 
  345     return 0;
  346 }
  347 
  348 static int chdir_to_parent(char *copy, const char **lastp)
  349 {
  350     char *tmp;
  351     const char *parent;
  352     char buf[65536];
  353     int res;
  354 
  355     tmp = strrchr(copy, '/');
  356     if (tmp == NULL || tmp[1] == '\0') {
  357         fprintf(stderr, "%s: internal error: invalid abs path: <%s>\n",
  358             progname, copy);
  359         return -1;
  360     }
  361     if (tmp != copy) {
  362         *tmp = '\0';
  363         parent = copy;
  364         *lastp = tmp + 1;
  365     } else if (tmp[1] != '\0') {
  366         *lastp = tmp + 1;
  367         parent = "/";
  368     } else {
  369         *lastp = ".";
  370         parent = "/";
  371     }
  372 
  373     res = chdir(parent);
  374     if (res == -1) {
  375         fprintf(stderr, "%s: failed to chdir to %s: %s\n",
  376             progname, parent, strerror(errno));
  377         return -1;
  378     }
  379 
  380     if (getcwd(buf, sizeof(buf)) == NULL) {
  381         fprintf(stderr, "%s: failed to obtain current directory: %s\n",
  382             progname, strerror(errno));
  383         return -1;
  384     }
  385     if (strcmp(buf, parent) != 0) {
  386         fprintf(stderr, "%s: mountpoint moved (%s -> %s)\n", progname,
  387             parent, buf);
  388         return -1;
  389 
  390     }
  391 
  392     return 0;
  393 }
  394 
  395 /* Check whether the kernel supports UMOUNT_NOFOLLOW flag */
  396 static int umount_nofollow_support(void)
  397 {
  398     int res = umount2("", UMOUNT_UNUSED);
  399     if (res != -1 || errno != EINVAL)
  400         return 0;
  401 
  402     res = umount2("", UMOUNT_NOFOLLOW);
  403     if (res != -1 || errno != ENOENT)
  404         return 0;
  405 
  406     return 1;
  407 }
  408 
  409 static int unmount_fuse_locked(const char *mnt, int quiet, int lazy)
  410 {
  411     int res;
  412     char *copy;
  413     const char *last;
  414     int umount_flags = lazy ? UMOUNT_DETACH : 0;
  415 
  416     if (getuid() != 0) {
  417         res = may_unmount(mnt, quiet);
  418         if (res == -1)
  419             return -1;
  420     }
  421 
  422     copy = strdup(mnt);
  423     if (copy == NULL) {
  424         fprintf(stderr, "%s: failed to allocate memory\n", progname);
  425         return -1;
  426     }
  427 
  428     res = chdir_to_parent(copy, &last);
  429     if (res == -1)
  430         goto out;
  431 
  432     if (umount_nofollow_support()) {
  433         umount_flags |= UMOUNT_NOFOLLOW;
  434     } else {
  435         res = check_is_mount(last, mnt);
  436         if (res == -1)
  437             goto out;
  438     }
  439 
  440     res = umount2(last, umount_flags);
  441     if (res == -1 && !quiet) {
  442         fprintf(stderr, "%s: failed to unmount %s: %s\n",
  443             progname, mnt, strerror(errno));
  444     }
  445 
  446 out:
  447     if (res == -1)
  448         return -1;
  449 
  450     res = chdir("/");
  451     if (res == -1) {
  452         fprintf(stderr, "%s: failed to chdir to '/'\n", progname);
  453         return -1;
  454     }
  455 
  456     return fuse_mnt_remove_mount(progname, mnt);
  457 }
  458 
  459 static int unmount_fuse(const char *mnt, int quiet, int lazy)
  460 {
  461     int res;
  462     int mtablock = lock_umount();
  463 
  464     res = unmount_fuse_locked(mnt, quiet, lazy);
  465     unlock_umount(mtablock);
  466 
  467     return res;
  468 }
  469 
  470 static int count_fuse_fs(void)
  471 {
  472     struct mntent *entp;
  473     int count = 0;
  474     const char *mtab = _PATH_MOUNTED;
  475     FILE *fp = setmntent(mtab, "r");
  476     if (fp == NULL) {
  477         fprintf(stderr, "%s: failed to open %s: %s\n", progname, mtab,
  478             strerror(errno));
  479         return -1;
  480     }
  481     while ((entp = getmntent(fp)) != NULL) {
  482         if (strcmp(entp->mnt_type, "fuse") == 0 ||
  483             strncmp(entp->mnt_type, "fuse.", 5) == 0)
  484             count ++;
  485     }
  486     endmntent(fp);
  487     return count;
  488 }
  489 
  490 
  491 #else /* IGNORE_MTAB */
  492 static int count_fuse_fs(void)
  493 {
  494     return 0;
  495 }
  496 
  497 static int add_mount(const char *source, const char *mnt, const char *type,
  498              const char *opts)
  499 {
  500     (void) source;
  501     (void) mnt;
  502     (void) type;
  503     (void) opts;
  504     return 0;
  505 }
  506 
  507 static int unmount_fuse(const char *mnt, int quiet, int lazy)
  508 {
  509     (void) quiet;
  510     return fuse_mnt_umount(progname, mnt, mnt, lazy);
  511 }
  512 #endif /* IGNORE_MTAB */
  513 
  514 static void strip_line(char *line)
  515 {
  516     char *s = strchr(line, '#');
  517     if (s != NULL)
  518         s[0] = '\0';
  519     for (s = line + strlen(line) - 1;
  520          s >= line && isspace((unsigned char) *s); s--);
  521     s[1] = '\0';
  522     for (s = line; isspace((unsigned char) *s); s++);
  523     if (s != line)
  524         memmove(line, s, strlen(s)+1);
  525 }
  526 
  527 static void parse_line(char *line, int linenum)
  528 {
  529     int tmp;
  530     if (strcmp(line, "user_allow_other") == 0)
  531         user_allow_other = 1;
  532     else if (sscanf(line, "mount_max = %i", &tmp) == 1)
  533         mount_max = tmp;
  534     else if(line[0])
  535         fprintf(stderr,
  536             "%s: unknown parameter in %s at line %i: '%s'\n",
  537             progname, FUSE_CONF, linenum, line);
  538 }
  539 
  540 static void read_conf(void)
  541 {
  542     FILE *fp = fopen(FUSE_CONF, "r");
  543     if (fp != NULL) {
  544         int linenum = 1;
  545         char line[256];
  546         int isnewline = 1;
  547         while (fgets(line, sizeof(line), fp) != NULL) {
  548             if (isnewline) {
  549                 if (line[strlen(line)-1] == '\n') {
  550                     strip_line(line);
  551                     parse_line(line, linenum);
  552                 } else {
  553                     isnewline = 0;
  554                 }
  555             } else if(line[strlen(line)-1] == '\n') {
  556                 fprintf(stderr, "%s: reading %s: line %i too long\n", progname, FUSE_CONF, linenum);
  557 
  558                 isnewline = 1;
  559             }
  560             if (isnewline)
  561                 linenum ++;
  562         }
  563         if (!isnewline) {
  564             fprintf(stderr, "%s: reading %s: missing newline at end of file\n", progname, FUSE_CONF);
  565 
  566         }
  567         fclose(fp);
  568     } else if (errno != ENOENT) {
  569         fprintf(stderr, "%s: failed to open %s: %s\n",
  570             progname, FUSE_CONF, strerror(errno));
  571     }
  572 }
  573 
  574 static int begins_with(const char *s, const char *beg)
  575 {
  576     if (strncmp(s, beg, strlen(beg)) == 0)
  577         return 1;
  578     else
  579         return 0;
  580 }
  581 
  582 struct mount_flags {
  583     const char *opt;
  584     unsigned long flag;
  585     int on;
  586     int safe;
  587 };
  588 
  589 static struct mount_flags mount_flags[] = {
  590     {"rw",      MS_RDONLY,      0, 1},
  591     {"ro",      MS_RDONLY,      1, 1},
  592     {"suid",    MS_NOSUID,      0, 0},
  593     {"nosuid",  MS_NOSUID,      1, 1},
  594     {"dev",     MS_NODEV,       0, 0},
  595     {"nodev",   MS_NODEV,       1, 1},
  596     {"exec",    MS_NOEXEC,      0, 1},
  597     {"noexec",  MS_NOEXEC,      1, 1},
  598     {"async",   MS_SYNCHRONOUS, 0, 1},
  599     {"sync",    MS_SYNCHRONOUS, 1, 1},
  600     {"atime",   MS_NOATIME,     0, 1},
  601     {"noatime", MS_NOATIME,     1, 1},
  602     {"dirsync", MS_DIRSYNC,     1, 1},
  603     {NULL,      0,          0, 0}
  604 };
  605 
  606 static int find_mount_flag(const char *s, unsigned len, int *on, int *flag)
  607 {
  608     int i;
  609 
  610     for (i = 0; mount_flags[i].opt != NULL; i++) {
  611         const char *opt = mount_flags[i].opt;
  612         if (strlen(opt) == len && strncmp(opt, s, len) == 0) {
  613             *on = mount_flags[i].on;
  614             *flag = mount_flags[i].flag;
  615             if (!mount_flags[i].safe && getuid() != 0) {
  616                 *flag = 0;
  617                 fprintf(stderr,
  618                     "%s: unsafe option %s ignored\n",
  619                     progname, opt);
  620             }
  621             return 1;
  622         }
  623     }
  624     return 0;
  625 }
  626 
  627 static int add_option(char **optsp, const char *opt, unsigned expand)
  628 {
  629     char *newopts;
  630     if (*optsp == NULL)
  631         newopts = strdup(opt);
  632     else {
  633         unsigned oldsize = strlen(*optsp);
  634         unsigned newsize = oldsize + 1 + strlen(opt) + expand + 1;
  635         newopts = (char *) realloc(*optsp, newsize);
  636         if (newopts)
  637             sprintf(newopts + oldsize, ",%s", opt);
  638     }
  639     if (newopts == NULL) {
  640         fprintf(stderr, "%s: failed to allocate memory\n", progname);
  641         return -1;
  642     }
  643     *optsp = newopts;
  644     return 0;
  645 }
  646 
  647 static int get_mnt_opts(int flags, char *opts, char **mnt_optsp)
  648 {
  649     int i;
  650     int l;
  651 
  652     if (!(flags & MS_RDONLY) && add_option(mnt_optsp, "rw", 0) == -1)
  653         return -1;
  654 
  655     for (i = 0; mount_flags[i].opt != NULL; i++) {
  656         if (mount_flags[i].on && (flags & mount_flags[i].flag) &&
  657             add_option(mnt_optsp, mount_flags[i].opt, 0) == -1)
  658             return -1;
  659     }
  660 
  661     if (add_option(mnt_optsp, opts, 0) == -1)
  662         return -1;
  663     /* remove comma from end of opts*/
  664     l = strlen(*mnt_optsp);
  665     if ((*mnt_optsp)[l-1] == ',')
  666         (*mnt_optsp)[l-1] = '\0';
  667     if (getuid() != 0) {
  668         const char *user = get_user_name();
  669         if (user == NULL)
  670             return -1;
  671 
  672         if (add_option(mnt_optsp, "user=", strlen(user)) == -1)
  673             return -1;
  674         strcat(*mnt_optsp, user);
  675     }
  676     return 0;
  677 }
  678 
  679 static int opt_eq(const char *s, unsigned len, const char *opt)
  680 {
  681     if(strlen(opt) == len && strncmp(s, opt, len) == 0)
  682         return 1;
  683     else
  684         return 0;
  685 }
  686 
  687 static int get_string_opt(const char *s, unsigned len, const char *opt,
  688               char **val)
  689 {
  690     int i;
  691     unsigned opt_len = strlen(opt);
  692     char *d;
  693 
  694     if (*val)
  695         free(*val);
  696     *val = (char *) malloc(len - opt_len + 1);
  697     if (!*val) {
  698         fprintf(stderr, "%s: failed to allocate memory\n", progname);
  699         return 0;
  700     }
  701 
  702     d = *val;
  703     s += opt_len;
  704     len -= opt_len;
  705     for (i = 0; i < len; i++) {
  706         if (s[i] == '\\' && i + 1 < len)
  707             i++;
  708         *d++ = s[i];
  709     }
  710     *d = '\0';
  711     return 1;
  712 }
  713 
  714 static int do_mount(const char *mnt, char **typep, mode_t rootmode,
  715             int fd, const char *opts, const char *dev, char **sourcep,
  716             char **mnt_optsp)
  717 {
  718     int res;
  719     int flags = MS_NOSUID | MS_NODEV;
  720     char *optbuf;
  721     char *mnt_opts = NULL;
  722     const char *s;
  723     char *d;
  724     char *fsname = NULL;
  725     char *subtype = NULL;
  726     char *source = NULL;
  727     char *type = NULL;
  728     int blkdev = 0;
  729 
  730     optbuf = (char *) malloc(strlen(opts) + 128);
  731     if (!optbuf) {
  732         fprintf(stderr, "%s: failed to allocate memory\n", progname);
  733         return -1;
  734     }
  735 
  736     for (s = opts, d = optbuf; *s;) {
  737         unsigned len;
  738         const char *fsname_str = "fsname=";
  739         const char *subtype_str = "subtype=";
  740         for (len = 0; s[len]; len++) {
  741             if (s[len] == '\\' && s[len + 1])
  742                 len++;
  743             else if (s[len] == ',')
  744                 break;
  745         }
  746         if (begins_with(s, fsname_str)) {
  747             if (!get_string_opt(s, len, fsname_str, &fsname))
  748                 goto err;
  749         } else if (begins_with(s, subtype_str)) {
  750             if (!get_string_opt(s, len, subtype_str, &subtype))
  751                 goto err;
  752         } else if (opt_eq(s, len, "blkdev")) {
  753             if (getuid() != 0) {
  754                 fprintf(stderr,
  755                     "%s: option blkdev is privileged\n",
  756                     progname);
  757                 goto err;
  758             }
  759             blkdev = 1;
  760         } else if (opt_eq(s, len, "auto_unmount")) {
  761             auto_unmount = 1;
  762         } else if (!begins_with(s, "fd=") &&
  763                !begins_with(s, "rootmode=") &&
  764                !begins_with(s, "user_id=") &&
  765                !begins_with(s, "group_id=")) {
  766             int on;
  767             int flag;
  768             int skip_option = 0;
  769             if (opt_eq(s, len, "large_read")) {
  770                 struct utsname utsname;
  771                 unsigned kmaj, kmin;
  772                 res = uname(&utsname);
  773                 if (res == 0 &&
  774                     sscanf(utsname.release, "%u.%u",
  775                        &kmaj, &kmin) == 2 &&
  776                     (kmaj > 2 || (kmaj == 2 && kmin > 4))) {
  777                     fprintf(stderr, "%s: note: 'large_read' mount option is deprecated for %i.%i kernels\n", progname, kmaj, kmin);
  778                     skip_option = 1;
  779                 }
  780             }
  781             if (getuid() != 0 && !user_allow_other &&
  782                 (opt_eq(s, len, "allow_other") ||
  783                  opt_eq(s, len, "allow_root"))) {
  784                 fprintf(stderr, "%s: option %.*s only allowed if 'user_allow_other' is set in %s\n", progname, len, s, FUSE_CONF);
  785                 goto err;
  786             }
  787             if (!skip_option) {
  788                 if (find_mount_flag(s, len, &on, &flag)) {
  789                     if (on)
  790                         flags |= flag;
  791                     else
  792                         flags  &= ~flag;
  793                 } else {
  794                     memcpy(d, s, len);
  795                     d += len;
  796                     *d++ = ',';
  797                 }
  798             }
  799         }
  800         s += len;
  801         if (*s)
  802             s++;
  803     }
  804     *d = '\0';
  805     res = get_mnt_opts(flags, optbuf, &mnt_opts);
  806     if (res == -1)
  807         goto err;
  808 
  809     sprintf(d, "fd=%i,rootmode=%o,user_id=%u,group_id=%u",
  810         fd, rootmode, getuid(), getgid());
  811 
  812     source = malloc((fsname ? strlen(fsname) : 0) +
  813             (subtype ? strlen(subtype) : 0) + strlen(dev) + 32);
  814 
  815     type = malloc((subtype ? strlen(subtype) : 0) + 32);
  816     if (!type || !source) {
  817         fprintf(stderr, "%s: failed to allocate memory\n", progname);
  818         goto err;
  819     }
  820 
  821     if (subtype)
  822         sprintf(type, "%s.%s", blkdev ? "fuseblk" : "fuse", subtype);
  823     else
  824         strcpy(type, blkdev ? "fuseblk" : "fuse");
  825 
  826     if (fsname)
  827         strcpy(source, fsname);
  828     else
  829         strcpy(source, subtype ? subtype : dev);
  830 
  831     res = mount(source, mnt, type, flags, optbuf);
  832     if (res == -1 && errno == ENODEV && subtype) {
  833         /* Probably missing subtype support */
  834         strcpy(type, blkdev ? "fuseblk" : "fuse");
  835         if (fsname) {
  836             if (!blkdev)
  837                 sprintf(source, "%s#%s", subtype, fsname);
  838         } else {
  839             strcpy(source, type);
  840         }
  841 
  842         res = mount(source, mnt, type, flags, optbuf);
  843     }
  844     if (res == -1 && errno == EINVAL) {
  845         /* It could be an old version not supporting group_id */
  846         sprintf(d, "fd=%i,rootmode=%o,user_id=%u",
  847             fd, rootmode, getuid());
  848         res = mount(source, mnt, type, flags, optbuf);
  849     }
  850     if (res == -1) {
  851         int errno_save = errno;
  852         if (blkdev && errno == ENODEV && !fuse_mnt_check_fuseblk())
  853             fprintf(stderr, "%s: 'fuseblk' support missing\n",
  854                 progname);
  855         else
  856             fprintf(stderr, "%s: mount failed: %s\n", progname,
  857                 strerror(errno_save));
  858         goto err;
  859     }
  860     *sourcep = source;
  861     *typep = type;
  862     *mnt_optsp = mnt_opts;
  863     free(fsname);
  864     free(optbuf);
  865 
  866     return 0;
  867 
  868 err:
  869     free(fsname);
  870     free(subtype);
  871     free(source);
  872     free(type);
  873     free(mnt_opts);
  874     free(optbuf);
  875     return -1;
  876 }
  877 
  878 static int check_perm(const char **mntp, struct stat *stbuf, int *mountpoint_fd)
  879 {
  880     int res;
  881     const char *mnt = *mntp;
  882     const char *origmnt = mnt;
  883 
  884     res = lstat(mnt, stbuf);
  885     if (res == -1) {
  886         fprintf(stderr, "%s: failed to access mountpoint %s: %s\n",
  887             progname, mnt, strerror(errno));
  888         return -1;
  889     }
  890 
  891     /* No permission checking is done for root */
  892     if (getuid() == 0)
  893         return 0;
  894 
  895     if (S_ISDIR(stbuf->st_mode)) {
  896         res = chdir(mnt);
  897         if (res == -1) {
  898             fprintf(stderr,
  899                 "%s: failed to chdir to mountpoint: %s\n",
  900                 progname, strerror(errno));
  901             return -1;
  902         }
  903         mnt = *mntp = ".";
  904         res = lstat(mnt, stbuf);
  905         if (res == -1) {
  906             fprintf(stderr,
  907                 "%s: failed to access mountpoint %s: %s\n",
  908                 progname, origmnt, strerror(errno));
  909             return -1;
  910         }
  911 
  912         if ((stbuf->st_mode & S_ISVTX) && stbuf->st_uid != getuid()) {
  913             fprintf(stderr, "%s: mountpoint %s not owned by user\n",
  914                 progname, origmnt);
  915             return -1;
  916         }
  917 
  918         res = access(mnt, W_OK);
  919         if (res == -1) {
  920             fprintf(stderr, "%s: user has no write access to mountpoint %s\n",
  921                 progname, origmnt);
  922             return -1;
  923         }
  924     } else if (S_ISREG(stbuf->st_mode)) {
  925         static char procfile[256];
  926         *mountpoint_fd = open(mnt, O_WRONLY);
  927         if (*mountpoint_fd == -1) {
  928             fprintf(stderr, "%s: failed to open %s: %s\n",
  929                 progname, mnt, strerror(errno));
  930             return -1;
  931         }
  932         res = fstat(*mountpoint_fd, stbuf);
  933         if (res == -1) {
  934             fprintf(stderr,
  935                 "%s: failed to access mountpoint %s: %s\n",
  936                 progname, mnt, strerror(errno));
  937             return -1;
  938         }
  939         if (!S_ISREG(stbuf->st_mode)) {
  940             fprintf(stderr,
  941                 "%s: mountpoint %s is no longer a regular file\n",
  942                 progname, mnt);
  943             return -1;
  944         }
  945 
  946         sprintf(procfile, "/proc/self/fd/%i", *mountpoint_fd);
  947         *mntp = procfile;
  948     } else {
  949         fprintf(stderr,
  950             "%s: mountpoint %s is not a directory or a regular file\n",
  951             progname, mnt);
  952         return -1;
  953     }
  954 
  955 
  956     return 0;
  957 }
  958 
  959 static int try_open(const char *dev, char **devp, int silent)
  960 {
  961     int fd = open(dev, O_RDWR);
  962     if (fd != -1) {
  963         *devp = strdup(dev);
  964         if (*devp == NULL) {
  965             fprintf(stderr, "%s: failed to allocate memory\n",
  966                 progname);
  967             close(fd);
  968             fd = -1;
  969         }
  970     } else if (errno == ENODEV ||
  971            errno == ENOENT)/* check for ENOENT too, for the udev case */
  972         return -2;
  973     else if (!silent) {
  974         fprintf(stderr, "%s: failed to open %s: %s\n", progname, dev,
  975             strerror(errno));
  976     }
  977     return fd;
  978 }
  979 
  980 static int try_open_fuse_device(char **devp)
  981 {
  982     int fd;
  983 
  984     drop_privs();
  985     fd = try_open(FUSE_DEV, devp, 0);
  986     restore_privs();
  987     return fd;
  988 }
  989 
  990 static int open_fuse_device(char **devp)
  991 {
  992     int fd = try_open_fuse_device(devp);
  993     if (fd >= -1)
  994         return fd;
  995 
  996     fprintf(stderr,
  997         "%s: fuse device not found, try 'modprobe fuse' first\n",
  998         progname);
  999 
 1000     return -1;
 1001 }
 1002 
 1003 
 1004 static int mount_fuse(const char *mnt, const char *opts)
 1005 {
 1006     int res;
 1007     int fd;
 1008     char *dev;
 1009     struct stat stbuf;
 1010     char *type = NULL;
 1011     char *source = NULL;
 1012     char *mnt_opts = NULL;
 1013     const char *real_mnt = mnt;
 1014     int mountpoint_fd = -1;
 1015 
 1016     fd = open_fuse_device(&dev);
 1017     if (fd == -1)
 1018         return -1;
 1019 
 1020     drop_privs();
 1021     read_conf();
 1022 
 1023     if (getuid() != 0 && mount_max != -1) {
 1024         int mount_count = count_fuse_fs();
 1025         if (mount_count >= mount_max) {
 1026             fprintf(stderr, "%s: too many FUSE filesystems mounted; mount_max=N can be set in %s\n", progname, FUSE_CONF);
 1027             goto fail_close_fd;
 1028         }
 1029     }
 1030 
 1031     res = check_perm(&real_mnt, &stbuf, &mountpoint_fd);
 1032     restore_privs();
 1033     if (res != -1)
 1034         res = do_mount(real_mnt, &type, stbuf.st_mode & S_IFMT,
 1035                    fd, opts, dev, &source, &mnt_opts);
 1036 
 1037     if (mountpoint_fd != -1)
 1038         close(mountpoint_fd);
 1039 
 1040     if (res == -1)
 1041         goto fail_close_fd;
 1042 
 1043     res = chdir("/");
 1044     if (res == -1) {
 1045         fprintf(stderr, "%s: failed to chdir to '/'\n", progname);
 1046         goto fail_close_fd;
 1047     }
 1048 
 1049     if (geteuid() == 0) {
 1050         res = add_mount(source, mnt, type, mnt_opts);
 1051         if (res == -1) {
 1052             /* Can't clean up mount in a non-racy way */
 1053             goto fail_close_fd;
 1054         }
 1055     }
 1056 
 1057 out_free:
 1058     free(source);
 1059     free(type);
 1060     free(mnt_opts);
 1061     free(dev);
 1062 
 1063     return fd;
 1064 
 1065 fail_close_fd:
 1066     close(fd);
 1067     fd = -1;
 1068     goto out_free;
 1069 }
 1070 
 1071 static int send_fd(int sock_fd, int fd)
 1072 {
 1073     int retval;
 1074     struct msghdr msg;
 1075     struct cmsghdr *p_cmsg;
 1076     struct iovec vec;
 1077     size_t cmsgbuf[CMSG_SPACE(sizeof(fd)) / sizeof(size_t)];
 1078     int *p_fds;
 1079     char sendchar = 0;
 1080 
 1081     msg.msg_control = cmsgbuf;
 1082     msg.msg_controllen = sizeof(cmsgbuf);
 1083     p_cmsg = CMSG_FIRSTHDR(&msg);
 1084     p_cmsg->cmsg_level = SOL_SOCKET;
 1085     p_cmsg->cmsg_type = SCM_RIGHTS;
 1086     p_cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
 1087     p_fds = (int *) CMSG_DATA(p_cmsg);
 1088     *p_fds = fd;
 1089     msg.msg_controllen = p_cmsg->cmsg_len;
 1090     msg.msg_name = NULL;
 1091     msg.msg_namelen = 0;
 1092     msg.msg_iov = &vec;
 1093     msg.msg_iovlen = 1;
 1094     msg.msg_flags = 0;
 1095     /* "To pass file descriptors or credentials you need to send/read at
 1096      * least one byte" (man 7 unix) */
 1097     vec.iov_base = &sendchar;
 1098     vec.iov_len = sizeof(sendchar);
 1099     while ((retval = sendmsg(sock_fd, &msg, 0)) == -1 && errno == EINTR);
 1100     if (retval != 1) {
 1101         perror("sending file descriptor");
 1102         return -1;
 1103     }
 1104     return 0;
 1105 }
 1106 
 1107 static void usage(void)
 1108 {
 1109     printf("%s: [options] mountpoint\n"
 1110            "Options:\n"
 1111            " -h         print help\n"
 1112            " -V         print version\n"
 1113            " -o opt[,opt...]   mount options\n"
 1114            " -u         unmount\n"
 1115            " -q         quiet\n"
 1116            " -z         lazy unmount\n",
 1117            progname);
 1118     exit(1);
 1119 }
 1120 
 1121 static void show_version(void)
 1122 {
 1123     printf("fusermount3 version: %s\n", PACKAGE_VERSION);
 1124     exit(0);
 1125 }
 1126 
 1127 int main(int argc, char *argv[])
 1128 {
 1129     sigset_t sigset;
 1130     int ch;
 1131     int fd;
 1132     int res;
 1133     char *origmnt;
 1134     char *mnt;
 1135     static int unmount = 0;
 1136     static int lazy = 0;
 1137     static int quiet = 0;
 1138     char *commfd;
 1139     int cfd;
 1140     const char *opts = "";
 1141 
 1142     static const struct option long_opts[] = {
 1143         {"unmount", no_argument, NULL, 'u'},
 1144         {"lazy",    no_argument, NULL, 'z'},
 1145         {"quiet",   no_argument, NULL, 'q'},
 1146         {"help",    no_argument, NULL, 'h'},
 1147         {"version", no_argument, NULL, 'V'},
 1148         {0, 0, 0, 0}};
 1149 
 1150     progname = strdup(argv[0]);
 1151     if (progname == NULL) {
 1152         fprintf(stderr, "%s: failed to allocate memory\n", argv[0]);
 1153         exit(1);
 1154     }
 1155 
 1156     while ((ch = getopt_long(argc, argv, "hVo:uzq", long_opts,
 1157                  NULL)) != -1) {
 1158         switch (ch) {
 1159         case 'h':
 1160             usage();
 1161             break;
 1162 
 1163         case 'V':
 1164             show_version();
 1165             break;
 1166 
 1167         case 'o':
 1168             opts = optarg;
 1169             break;
 1170 
 1171         case 'u':
 1172             unmount = 1;
 1173             break;
 1174 
 1175         case 'z':
 1176             lazy = 1;
 1177             break;
 1178 
 1179         case 'q':
 1180             quiet = 1;
 1181             break;
 1182 
 1183         default:
 1184             exit(1);
 1185         }
 1186     }
 1187 
 1188     if (lazy && !unmount) {
 1189         fprintf(stderr, "%s: -z can only be used with -u\n", progname);
 1190         exit(1);
 1191     }
 1192 
 1193     if (optind >= argc) {
 1194         fprintf(stderr, "%s: missing mountpoint argument\n", progname);
 1195         exit(1);
 1196     } else if (argc > optind + 1) {
 1197         fprintf(stderr, "%s: extra arguments after the mountpoint\n",
 1198             progname);
 1199         exit(1);
 1200     }
 1201 
 1202     origmnt = argv[optind];
 1203 
 1204     drop_privs();
 1205     mnt = fuse_mnt_resolve_path(progname, origmnt);
 1206     if (mnt != NULL) {
 1207         res = chdir("/");
 1208         if (res == -1) {
 1209             fprintf(stderr, "%s: failed to chdir to '/'\n", progname);
 1210             goto err_out;
 1211         }
 1212     }
 1213     restore_privs();
 1214     if (mnt == NULL)
 1215         exit(1);
 1216 
 1217     umask(033);
 1218     if (unmount)
 1219         goto do_unmount;
 1220 
 1221     commfd = getenv(FUSE_COMMFD_ENV);
 1222     if (commfd == NULL) {
 1223         fprintf(stderr, "%s: old style mounting not supported\n",
 1224             progname);
 1225         goto err_out;
 1226     }
 1227 
 1228     fd = mount_fuse(mnt, opts);
 1229     if (fd == -1)
 1230         goto err_out;
 1231 
 1232     cfd = atoi(commfd);
 1233     res = send_fd(cfd, fd);
 1234     if (res == -1)
 1235         goto err_out;
 1236     close(fd);
 1237 
 1238     if (!auto_unmount) {
 1239         free(mnt);
 1240         return 0;
 1241     }
 1242 
 1243     /* Become a daemon and wait for the parent to exit or die.
 1244        ie For the control socket to get closed.
 1245        btw We don't want to use daemon() function here because
 1246        it forks and messes with the file descriptors. */
 1247     setsid();
 1248     res = chdir("/");
 1249     if (res == -1) {
 1250         fprintf(stderr, "%s: failed to chdir to '/'\n", progname);
 1251         goto err_out;
 1252     }
 1253 
 1254     sigfillset(&sigset);
 1255     sigprocmask(SIG_BLOCK, &sigset, NULL);
 1256 
 1257     lazy  = 1;
 1258     quiet = 1;
 1259 
 1260     while (1) {
 1261         unsigned char buf[16];
 1262         int n = recv(cfd, buf, sizeof(buf), 0);
 1263         if (!n)
 1264             break;
 1265 
 1266         if (n < 0) {
 1267             if (errno == EINTR)
 1268                 continue;
 1269             break;
 1270         }
 1271     }
 1272 
 1273 do_unmount:
 1274     if (geteuid() == 0)
 1275         res = unmount_fuse(mnt, quiet, lazy);
 1276     else {
 1277         res = umount2(mnt, lazy ? UMOUNT_DETACH : 0);
 1278         if (res == -1 && !quiet)
 1279             fprintf(stderr,
 1280                 "%s: failed to unmount %s: %s\n",
 1281                 progname, mnt, strerror(errno));
 1282     }
 1283     if (res == -1)
 1284         goto err_out;
 1285     return 0;
 1286 
 1287 err_out:
 1288     free(mnt);
 1289     exit(1);
 1290 }