"Fossies" - the Fresh Open Source Software Archive

Member "fuse-3.2.1/util/fusermount.c" (14 Nov 2017, 27974 Bytes) of package /linux/misc/fuse-3.2.1.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.

    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()
  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     return fuse_mnt_umount(progname, mnt, mnt, lazy);
  510 }
  511 #endif /* IGNORE_MTAB */
  512 
  513 static void strip_line(char *line)
  514 {
  515     char *s = strchr(line, '#');
  516     if (s != NULL)
  517         s[0] = '\0';
  518     for (s = line + strlen(line) - 1;
  519          s >= line && isspace((unsigned char) *s); s--);
  520     s[1] = '\0';
  521     for (s = line; isspace((unsigned char) *s); s++);
  522     if (s != line)
  523         memmove(line, s, strlen(s)+1);
  524 }
  525 
  526 static void parse_line(char *line, int linenum)
  527 {
  528     int tmp;
  529     if (strcmp(line, "user_allow_other") == 0)
  530         user_allow_other = 1;
  531     else if (sscanf(line, "mount_max = %i", &tmp) == 1)
  532         mount_max = tmp;
  533     else if(line[0])
  534         fprintf(stderr,
  535             "%s: unknown parameter in %s at line %i: '%s'\n",
  536             progname, FUSE_CONF, linenum, line);
  537 }
  538 
  539 static void read_conf(void)
  540 {
  541     FILE *fp = fopen(FUSE_CONF, "r");
  542     if (fp != NULL) {
  543         int linenum = 1;
  544         char line[256];
  545         int isnewline = 1;
  546         while (fgets(line, sizeof(line), fp) != NULL) {
  547             if (isnewline) {
  548                 if (line[strlen(line)-1] == '\n') {
  549                     strip_line(line);
  550                     parse_line(line, linenum);
  551                 } else {
  552                     isnewline = 0;
  553                 }
  554             } else if(line[strlen(line)-1] == '\n') {
  555                 fprintf(stderr, "%s: reading %s: line %i too long\n", progname, FUSE_CONF, linenum);
  556 
  557                 isnewline = 1;
  558             }
  559             if (isnewline)
  560                 linenum ++;
  561         }
  562         if (!isnewline) {
  563             fprintf(stderr, "%s: reading %s: missing newline at end of file\n", progname, FUSE_CONF);
  564 
  565         }
  566         fclose(fp);
  567     } else if (errno != ENOENT) {
  568         fprintf(stderr, "%s: failed to open %s: %s\n",
  569             progname, FUSE_CONF, strerror(errno));
  570     }
  571 }
  572 
  573 static int begins_with(const char *s, const char *beg)
  574 {
  575     if (strncmp(s, beg, strlen(beg)) == 0)
  576         return 1;
  577     else
  578         return 0;
  579 }
  580 
  581 struct mount_flags {
  582     const char *opt;
  583     unsigned long flag;
  584     int on;
  585     int safe;
  586 };
  587 
  588 static struct mount_flags mount_flags[] = {
  589     {"rw",      MS_RDONLY,      0, 1},
  590     {"ro",      MS_RDONLY,      1, 1},
  591     {"suid",    MS_NOSUID,      0, 0},
  592     {"nosuid",  MS_NOSUID,      1, 1},
  593     {"dev",     MS_NODEV,       0, 0},
  594     {"nodev",   MS_NODEV,       1, 1},
  595     {"exec",    MS_NOEXEC,      0, 1},
  596     {"noexec",  MS_NOEXEC,      1, 1},
  597     {"async",   MS_SYNCHRONOUS, 0, 1},
  598     {"sync",    MS_SYNCHRONOUS, 1, 1},
  599     {"atime",   MS_NOATIME,     0, 1},
  600     {"noatime", MS_NOATIME,     1, 1},
  601     {"dirsync", MS_DIRSYNC,     1, 1},
  602     {NULL,      0,          0, 0}
  603 };
  604 
  605 static int find_mount_flag(const char *s, unsigned len, int *on, int *flag)
  606 {
  607     int i;
  608 
  609     for (i = 0; mount_flags[i].opt != NULL; i++) {
  610         const char *opt = mount_flags[i].opt;
  611         if (strlen(opt) == len && strncmp(opt, s, len) == 0) {
  612             *on = mount_flags[i].on;
  613             *flag = mount_flags[i].flag;
  614             if (!mount_flags[i].safe && getuid() != 0) {
  615                 *flag = 0;
  616                 fprintf(stderr,
  617                     "%s: unsafe option %s ignored\n",
  618                     progname, opt);
  619             }
  620             return 1;
  621         }
  622     }
  623     return 0;
  624 }
  625 
  626 static int add_option(char **optsp, const char *opt, unsigned expand)
  627 {
  628     char *newopts;
  629     if (*optsp == NULL)
  630         newopts = strdup(opt);
  631     else {
  632         unsigned oldsize = strlen(*optsp);
  633         unsigned newsize = oldsize + 1 + strlen(opt) + expand + 1;
  634         newopts = (char *) realloc(*optsp, newsize);
  635         if (newopts)
  636             sprintf(newopts + oldsize, ",%s", opt);
  637     }
  638     if (newopts == NULL) {
  639         fprintf(stderr, "%s: failed to allocate memory\n", progname);
  640         return -1;
  641     }
  642     *optsp = newopts;
  643     return 0;
  644 }
  645 
  646 static int get_mnt_opts(int flags, char *opts, char **mnt_optsp)
  647 {
  648     int i;
  649     int l;
  650 
  651     if (!(flags & MS_RDONLY) && add_option(mnt_optsp, "rw", 0) == -1)
  652         return -1;
  653 
  654     for (i = 0; mount_flags[i].opt != NULL; i++) {
  655         if (mount_flags[i].on && (flags & mount_flags[i].flag) &&
  656             add_option(mnt_optsp, mount_flags[i].opt, 0) == -1)
  657             return -1;
  658     }
  659 
  660     if (add_option(mnt_optsp, opts, 0) == -1)
  661         return -1;
  662     /* remove comma from end of opts*/
  663     l = strlen(*mnt_optsp);
  664     if ((*mnt_optsp)[l-1] == ',')
  665         (*mnt_optsp)[l-1] = '\0';
  666     if (getuid() != 0) {
  667         const char *user = get_user_name();
  668         if (user == NULL)
  669             return -1;
  670 
  671         if (add_option(mnt_optsp, "user=", strlen(user)) == -1)
  672             return -1;
  673         strcat(*mnt_optsp, user);
  674     }
  675     return 0;
  676 }
  677 
  678 static int opt_eq(const char *s, unsigned len, const char *opt)
  679 {
  680     if(strlen(opt) == len && strncmp(s, opt, len) == 0)
  681         return 1;
  682     else
  683         return 0;
  684 }
  685 
  686 static int get_string_opt(const char *s, unsigned len, const char *opt,
  687               char **val)
  688 {
  689     int i;
  690     unsigned opt_len = strlen(opt);
  691     char *d;
  692 
  693     if (*val)
  694         free(*val);
  695     *val = (char *) malloc(len - opt_len + 1);
  696     if (!*val) {
  697         fprintf(stderr, "%s: failed to allocate memory\n", progname);
  698         return 0;
  699     }
  700 
  701     d = *val;
  702     s += opt_len;
  703     len -= opt_len;
  704     for (i = 0; i < len; i++) {
  705         if (s[i] == '\\' && i + 1 < len)
  706             i++;
  707         *d++ = s[i];
  708     }
  709     *d = '\0';
  710     return 1;
  711 }
  712 
  713 static int do_mount(const char *mnt, char **typep, mode_t rootmode,
  714             int fd, const char *opts, const char *dev, char **sourcep,
  715             char **mnt_optsp)
  716 {
  717     int res;
  718     int flags = MS_NOSUID | MS_NODEV;
  719     char *optbuf;
  720     char *mnt_opts = NULL;
  721     const char *s;
  722     char *d;
  723     char *fsname = NULL;
  724     char *subtype = NULL;
  725     char *source = NULL;
  726     char *type = NULL;
  727     int blkdev = 0;
  728 
  729     optbuf = (char *) malloc(strlen(opts) + 128);
  730     if (!optbuf) {
  731         fprintf(stderr, "%s: failed to allocate memory\n", progname);
  732         return -1;
  733     }
  734 
  735     for (s = opts, d = optbuf; *s;) {
  736         unsigned len;
  737         const char *fsname_str = "fsname=";
  738         const char *subtype_str = "subtype=";
  739         for (len = 0; s[len]; len++) {
  740             if (s[len] == '\\' && s[len + 1])
  741                 len++;
  742             else if (s[len] == ',')
  743                 break;
  744         }
  745         if (begins_with(s, fsname_str)) {
  746             if (!get_string_opt(s, len, fsname_str, &fsname))
  747                 goto err;
  748         } else if (begins_with(s, subtype_str)) {
  749             if (!get_string_opt(s, len, subtype_str, &subtype))
  750                 goto err;
  751         } else if (opt_eq(s, len, "blkdev")) {
  752             if (getuid() != 0) {
  753                 fprintf(stderr,
  754                     "%s: option blkdev is privileged\n",
  755                     progname);
  756                 goto err;
  757             }
  758             blkdev = 1;
  759         } else if (opt_eq(s, len, "auto_unmount")) {
  760             auto_unmount = 1;
  761         } else if (!begins_with(s, "fd=") &&
  762                !begins_with(s, "rootmode=") &&
  763                !begins_with(s, "user_id=") &&
  764                !begins_with(s, "group_id=")) {
  765             int on;
  766             int flag;
  767             int skip_option = 0;
  768             if (opt_eq(s, len, "large_read")) {
  769                 struct utsname utsname;
  770                 unsigned kmaj, kmin;
  771                 res = uname(&utsname);
  772                 if (res == 0 &&
  773                     sscanf(utsname.release, "%u.%u",
  774                        &kmaj, &kmin) == 2 &&
  775                     (kmaj > 2 || (kmaj == 2 && kmin > 4))) {
  776                     fprintf(stderr, "%s: note: 'large_read' mount option is deprecated for %i.%i kernels\n", progname, kmaj, kmin);
  777                     skip_option = 1;
  778                 }
  779             }
  780             if (getuid() != 0 && !user_allow_other &&
  781                 (opt_eq(s, len, "allow_other") ||
  782                  opt_eq(s, len, "allow_root"))) {
  783                 fprintf(stderr, "%s: option %.*s only allowed if 'user_allow_other' is set in %s\n", progname, len, s, FUSE_CONF);
  784                 goto err;
  785             }
  786             if (!skip_option) {
  787                 if (find_mount_flag(s, len, &on, &flag)) {
  788                     if (on)
  789                         flags |= flag;
  790                     else
  791                         flags  &= ~flag;
  792                 } else {
  793                     memcpy(d, s, len);
  794                     d += len;
  795                     *d++ = ',';
  796                 }
  797             }
  798         }
  799         s += len;
  800         if (*s)
  801             s++;
  802     }
  803     *d = '\0';
  804     res = get_mnt_opts(flags, optbuf, &mnt_opts);
  805     if (res == -1)
  806         goto err;
  807 
  808     sprintf(d, "fd=%i,rootmode=%o,user_id=%u,group_id=%u",
  809         fd, rootmode, getuid(), getgid());
  810 
  811     source = malloc((fsname ? strlen(fsname) : 0) +
  812             (subtype ? strlen(subtype) : 0) + strlen(dev) + 32);
  813 
  814     type = malloc((subtype ? strlen(subtype) : 0) + 32);
  815     if (!type || !source) {
  816         fprintf(stderr, "%s: failed to allocate memory\n", progname);
  817         goto err;
  818     }
  819 
  820     if (subtype)
  821         sprintf(type, "%s.%s", blkdev ? "fuseblk" : "fuse", subtype);
  822     else
  823         strcpy(type, blkdev ? "fuseblk" : "fuse");
  824 
  825     if (fsname)
  826         strcpy(source, fsname);
  827     else
  828         strcpy(source, subtype ? subtype : dev);
  829 
  830     res = mount(source, mnt, type, flags, optbuf);
  831     if (res == -1 && errno == ENODEV && subtype) {
  832         /* Probably missing subtype support */
  833         strcpy(type, blkdev ? "fuseblk" : "fuse");
  834         if (fsname) {
  835             if (!blkdev)
  836                 sprintf(source, "%s#%s", subtype, fsname);
  837         } else {
  838             strcpy(source, type);
  839         }
  840 
  841         res = mount(source, mnt, type, flags, optbuf);
  842     }
  843     if (res == -1 && errno == EINVAL) {
  844         /* It could be an old version not supporting group_id */
  845         sprintf(d, "fd=%i,rootmode=%o,user_id=%u",
  846             fd, rootmode, getuid());
  847         res = mount(source, mnt, type, flags, optbuf);
  848     }
  849     if (res == -1) {
  850         int errno_save = errno;
  851         if (blkdev && errno == ENODEV && !fuse_mnt_check_fuseblk())
  852             fprintf(stderr, "%s: 'fuseblk' support missing\n",
  853                 progname);
  854         else
  855             fprintf(stderr, "%s: mount failed: %s\n", progname,
  856                 strerror(errno_save));
  857         goto err;
  858     }
  859     *sourcep = source;
  860     *typep = type;
  861     *mnt_optsp = mnt_opts;
  862     free(fsname);
  863     free(optbuf);
  864 
  865     return 0;
  866 
  867 err:
  868     free(fsname);
  869     free(subtype);
  870     free(source);
  871     free(type);
  872     free(mnt_opts);
  873     free(optbuf);
  874     return -1;
  875 }
  876 
  877 static int check_perm(const char **mntp, struct stat *stbuf, int *mountpoint_fd)
  878 {
  879     int res;
  880     const char *mnt = *mntp;
  881     const char *origmnt = mnt;
  882 
  883     res = lstat(mnt, stbuf);
  884     if (res == -1) {
  885         fprintf(stderr, "%s: failed to access mountpoint %s: %s\n",
  886             progname, mnt, strerror(errno));
  887         return -1;
  888     }
  889 
  890     /* No permission checking is done for root */
  891     if (getuid() == 0)
  892         return 0;
  893 
  894     if (S_ISDIR(stbuf->st_mode)) {
  895         res = chdir(mnt);
  896         if (res == -1) {
  897             fprintf(stderr,
  898                 "%s: failed to chdir to mountpoint: %s\n",
  899                 progname, strerror(errno));
  900             return -1;
  901         }
  902         mnt = *mntp = ".";
  903         res = lstat(mnt, stbuf);
  904         if (res == -1) {
  905             fprintf(stderr,
  906                 "%s: failed to access mountpoint %s: %s\n",
  907                 progname, origmnt, strerror(errno));
  908             return -1;
  909         }
  910 
  911         if ((stbuf->st_mode & S_ISVTX) && stbuf->st_uid != getuid()) {
  912             fprintf(stderr, "%s: mountpoint %s not owned by user\n",
  913                 progname, origmnt);
  914             return -1;
  915         }
  916 
  917         res = access(mnt, W_OK);
  918         if (res == -1) {
  919             fprintf(stderr, "%s: user has no write access to mountpoint %s\n",
  920                 progname, origmnt);
  921             return -1;
  922         }
  923     } else if (S_ISREG(stbuf->st_mode)) {
  924         static char procfile[256];
  925         *mountpoint_fd = open(mnt, O_WRONLY);
  926         if (*mountpoint_fd == -1) {
  927             fprintf(stderr, "%s: failed to open %s: %s\n",
  928                 progname, mnt, strerror(errno));
  929             return -1;
  930         }
  931         res = fstat(*mountpoint_fd, stbuf);
  932         if (res == -1) {
  933             fprintf(stderr,
  934                 "%s: failed to access mountpoint %s: %s\n",
  935                 progname, mnt, strerror(errno));
  936             return -1;
  937         }
  938         if (!S_ISREG(stbuf->st_mode)) {
  939             fprintf(stderr,
  940                 "%s: mountpoint %s is no longer a regular file\n",
  941                 progname, mnt);
  942             return -1;
  943         }
  944 
  945         sprintf(procfile, "/proc/self/fd/%i", *mountpoint_fd);
  946         *mntp = procfile;
  947     } else {
  948         fprintf(stderr,
  949             "%s: mountpoint %s is not a directory or a regular file\n",
  950             progname, mnt);
  951         return -1;
  952     }
  953 
  954 
  955     return 0;
  956 }
  957 
  958 static int try_open(const char *dev, char **devp, int silent)
  959 {
  960     int fd = open(dev, O_RDWR);
  961     if (fd != -1) {
  962         *devp = strdup(dev);
  963         if (*devp == NULL) {
  964             fprintf(stderr, "%s: failed to allocate memory\n",
  965                 progname);
  966             close(fd);
  967             fd = -1;
  968         }
  969     } else if (errno == ENODEV ||
  970            errno == ENOENT)/* check for ENOENT too, for the udev case */
  971         return -2;
  972     else if (!silent) {
  973         fprintf(stderr, "%s: failed to open %s: %s\n", progname, dev,
  974             strerror(errno));
  975     }
  976     return fd;
  977 }
  978 
  979 static int try_open_fuse_device(char **devp)
  980 {
  981     int fd;
  982 
  983     drop_privs();
  984     fd = try_open(FUSE_DEV, devp, 0);
  985     restore_privs();
  986     return fd;
  987 }
  988 
  989 static int open_fuse_device(char **devp)
  990 {
  991     int fd = try_open_fuse_device(devp);
  992     if (fd >= -1)
  993         return fd;
  994 
  995     fprintf(stderr,
  996         "%s: fuse device not found, try 'modprobe fuse' first\n",
  997         progname);
  998 
  999     return -1;
 1000 }
 1001 
 1002 
 1003 static int mount_fuse(const char *mnt, const char *opts)
 1004 {
 1005     int res;
 1006     int fd;
 1007     char *dev;
 1008     struct stat stbuf;
 1009     char *type = NULL;
 1010     char *source = NULL;
 1011     char *mnt_opts = NULL;
 1012     const char *real_mnt = mnt;
 1013     int mountpoint_fd = -1;
 1014 
 1015     fd = open_fuse_device(&dev);
 1016     if (fd == -1)
 1017         return -1;
 1018 
 1019     drop_privs();
 1020     read_conf();
 1021 
 1022     if (getuid() != 0 && mount_max != -1) {
 1023         int mount_count = count_fuse_fs();
 1024         if (mount_count >= mount_max) {
 1025             fprintf(stderr, "%s: too many FUSE filesystems mounted; mount_max=N can be set in %s\n", progname, FUSE_CONF);
 1026             goto fail_close_fd;
 1027         }
 1028     }
 1029 
 1030     res = check_perm(&real_mnt, &stbuf, &mountpoint_fd);
 1031     restore_privs();
 1032     if (res != -1)
 1033         res = do_mount(real_mnt, &type, stbuf.st_mode & S_IFMT,
 1034                    fd, opts, dev, &source, &mnt_opts);
 1035 
 1036     if (mountpoint_fd != -1)
 1037         close(mountpoint_fd);
 1038 
 1039     if (res == -1)
 1040         goto fail_close_fd;
 1041 
 1042     res = chdir("/");
 1043     if (res == -1) {
 1044         fprintf(stderr, "%s: failed to chdir to '/'\n", progname);
 1045         goto fail_close_fd;
 1046     }
 1047 
 1048     if (geteuid() == 0) {
 1049         res = add_mount(source, mnt, type, mnt_opts);
 1050         if (res == -1) {
 1051             /* Can't clean up mount in a non-racy way */
 1052             goto fail_close_fd;
 1053         }
 1054     }
 1055 
 1056 out_free:
 1057     free(source);
 1058     free(type);
 1059     free(mnt_opts);
 1060     free(dev);
 1061 
 1062     return fd;
 1063 
 1064 fail_close_fd:
 1065     close(fd);
 1066     fd = -1;
 1067     goto out_free;
 1068 }
 1069 
 1070 static int send_fd(int sock_fd, int fd)
 1071 {
 1072     int retval;
 1073     struct msghdr msg;
 1074     struct cmsghdr *p_cmsg;
 1075     struct iovec vec;
 1076     size_t cmsgbuf[CMSG_SPACE(sizeof(fd)) / sizeof(size_t)];
 1077     int *p_fds;
 1078     char sendchar = 0;
 1079 
 1080     msg.msg_control = cmsgbuf;
 1081     msg.msg_controllen = sizeof(cmsgbuf);
 1082     p_cmsg = CMSG_FIRSTHDR(&msg);
 1083     p_cmsg->cmsg_level = SOL_SOCKET;
 1084     p_cmsg->cmsg_type = SCM_RIGHTS;
 1085     p_cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
 1086     p_fds = (int *) CMSG_DATA(p_cmsg);
 1087     *p_fds = fd;
 1088     msg.msg_controllen = p_cmsg->cmsg_len;
 1089     msg.msg_name = NULL;
 1090     msg.msg_namelen = 0;
 1091     msg.msg_iov = &vec;
 1092     msg.msg_iovlen = 1;
 1093     msg.msg_flags = 0;
 1094     /* "To pass file descriptors or credentials you need to send/read at
 1095      * least one byte" (man 7 unix) */
 1096     vec.iov_base = &sendchar;
 1097     vec.iov_len = sizeof(sendchar);
 1098     while ((retval = sendmsg(sock_fd, &msg, 0)) == -1 && errno == EINTR);
 1099     if (retval != 1) {
 1100         perror("sending file descriptor");
 1101         return -1;
 1102     }
 1103     return 0;
 1104 }
 1105 
 1106 static void usage(void)
 1107 {
 1108     printf("%s: [options] mountpoint\n"
 1109            "Options:\n"
 1110            " -h         print help\n"
 1111            " -V         print version\n"
 1112            " -o opt[,opt...]   mount options\n"
 1113            " -u         unmount\n"
 1114            " -q         quiet\n"
 1115            " -z         lazy unmount\n",
 1116            progname);
 1117     exit(1);
 1118 }
 1119 
 1120 static void show_version(void)
 1121 {
 1122     printf("fusermount3 version: %s\n", PACKAGE_VERSION);
 1123     exit(0);
 1124 }
 1125 
 1126 int main(int argc, char *argv[])
 1127 {
 1128     sigset_t sigset;
 1129     int ch;
 1130     int fd;
 1131     int res;
 1132     char *origmnt;
 1133     char *mnt;
 1134     static int unmount = 0;
 1135     static int lazy = 0;
 1136     static int quiet = 0;
 1137     char *commfd;
 1138     int cfd;
 1139     const char *opts = "";
 1140 
 1141     static const struct option long_opts[] = {
 1142         {"unmount", no_argument, NULL, 'u'},
 1143         {"lazy",    no_argument, NULL, 'z'},
 1144         {"quiet",   no_argument, NULL, 'q'},
 1145         {"help",    no_argument, NULL, 'h'},
 1146         {"version", no_argument, NULL, 'V'},
 1147         {0, 0, 0, 0}};
 1148 
 1149     progname = strdup(argv[0]);
 1150     if (progname == NULL) {
 1151         fprintf(stderr, "%s: failed to allocate memory\n", argv[0]);
 1152         exit(1);
 1153     }
 1154 
 1155     while ((ch = getopt_long(argc, argv, "hVo:uzq", long_opts,
 1156                  NULL)) != -1) {
 1157         switch (ch) {
 1158         case 'h':
 1159             usage();
 1160             break;
 1161 
 1162         case 'V':
 1163             show_version();
 1164             break;
 1165 
 1166         case 'o':
 1167             opts = optarg;
 1168             break;
 1169 
 1170         case 'u':
 1171             unmount = 1;
 1172             break;
 1173 
 1174         case 'z':
 1175             lazy = 1;
 1176             break;
 1177 
 1178         case 'q':
 1179             quiet = 1;
 1180             break;
 1181 
 1182         default:
 1183             exit(1);
 1184         }
 1185     }
 1186 
 1187     if (lazy && !unmount) {
 1188         fprintf(stderr, "%s: -z can only be used with -u\n", progname);
 1189         exit(1);
 1190     }
 1191 
 1192     if (optind >= argc) {
 1193         fprintf(stderr, "%s: missing mountpoint argument\n", progname);
 1194         exit(1);
 1195     } else if (argc > optind + 1) {
 1196         fprintf(stderr, "%s: extra arguments after the mountpoint\n",
 1197             progname);
 1198         exit(1);
 1199     }
 1200 
 1201     origmnt = argv[optind];
 1202 
 1203     drop_privs();
 1204     mnt = fuse_mnt_resolve_path(progname, origmnt);
 1205     if (mnt != NULL) {
 1206         res = chdir("/");
 1207         if (res == -1) {
 1208             fprintf(stderr, "%s: failed to chdir to '/'\n", progname);
 1209             goto err_out;
 1210         }
 1211     }
 1212     restore_privs();
 1213     if (mnt == NULL)
 1214         exit(1);
 1215 
 1216     umask(033);
 1217     if (unmount)
 1218         goto do_unmount;
 1219 
 1220     commfd = getenv(FUSE_COMMFD_ENV);
 1221     if (commfd == NULL) {
 1222         fprintf(stderr, "%s: old style mounting not supported\n",
 1223             progname);
 1224         goto err_out;
 1225     }
 1226 
 1227     fd = mount_fuse(mnt, opts);
 1228     if (fd == -1)
 1229         goto err_out;
 1230 
 1231     cfd = atoi(commfd);
 1232     res = send_fd(cfd, fd);
 1233     if (res == -1)
 1234         goto err_out;
 1235     close(fd);
 1236 
 1237     if (!auto_unmount) {
 1238         free(mnt);
 1239         return 0;
 1240     }
 1241 
 1242     /* Become a daemon and wait for the parent to exit or die.
 1243        ie For the control socket to get closed.
 1244        btw We don't want to use daemon() function here because
 1245        it forks and messes with the file descriptors. */
 1246     setsid();
 1247     res = chdir("/");
 1248     if (res == -1) {
 1249         fprintf(stderr, "%s: failed to chdir to '/'\n", progname);
 1250         goto err_out;
 1251     }
 1252 
 1253     sigfillset(&sigset);
 1254     sigprocmask(SIG_BLOCK, &sigset, NULL);
 1255 
 1256     lazy  = 1;
 1257     quiet = 1;
 1258 
 1259     while (1) {
 1260         unsigned char buf[16];
 1261         int n = recv(cfd, buf, sizeof(buf), 0);
 1262         if (!n)
 1263             break;
 1264 
 1265         if (n < 0) {
 1266             if (errno == EINTR)
 1267                 continue;
 1268             break;
 1269         }
 1270     }
 1271 
 1272 do_unmount:
 1273     if (geteuid() == 0)
 1274         res = unmount_fuse(mnt, quiet, lazy);
 1275     else {
 1276         res = umount2(mnt, lazy ? UMOUNT_DETACH : 0);
 1277         if (res == -1 && !quiet)
 1278             fprintf(stderr,
 1279                 "%s: failed to unmount %s: %s\n",
 1280                 progname, mnt, strerror(errno));
 1281     }
 1282     if (res == -1)
 1283         goto err_out;
 1284     return 0;
 1285 
 1286 err_out:
 1287     free(mnt);
 1288     exit(1);
 1289 }