libfuse: fuse-3.17.4/util/fusermount.c Source File

libfuse

fusermount.c
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 GPLv2.
6 See the file COPYING.
7*/
8/* This program does the mounting and unmounting of FUSE filesystems */
9
10#define _GNU_SOURCE /* for clone,strchrnul and close_range */
11#include "fuse_config.h"
12#include "mount_util.h"
13#include "util.h"
14
15#include <stdio.h>
16#include <stdlib.h>
17#include <string.h>
18#include <ctype.h>
19#include <unistd.h>
20#include <getopt.h>
21#include <errno.h>
22#include <fcntl.h>
23#include <pwd.h>
24#include <paths.h>
25#include <mntent.h>
26#include <sys/wait.h>
27#include <sys/stat.h>
28#include <sys/param.h>
29
30#include "fuse_mount_compat.h"
31
32#include <sys/fsuid.h>
33#include <sys/socket.h>
34#include <sys/utsname.h>
35#include <sched.h>
36#include <stdbool.h>
37#include <sys/vfs.h>
38
39#ifdef HAVE_CLOSE_RANGE
40#include <linux/close_range.h>
41#endif
42
43#define FUSE_COMMFD_ENV "_FUSE_COMMFD"
44
45#define FUSE_DEV "/dev/fuse"
46
47static const char *progname;
48
49static int user_allow_other = 0;
50static int mount_max = 1000;
51
52static int auto_unmount = 0;
53
54#ifdef GETMNTENT_NEEDS_UNESCAPING
55// Older versions of musl libc don't unescape entries in /etc/mtab
56
57// unescapes octal sequences like \040 in-place
58// That's ok, because unescaping can not extend the length of the string.
59static void unescape(char *buf) {
60 char *src = buf;
61 char *dest = buf;
62 while (1) {
63 char *next_src = strchrnul(src, '\\');
64 int offset = next_src - src;
65 memmove(dest, src, offset);
66 src = next_src;
67 dest += offset;
68
69 if(*src == '\0') {
70 *dest = *src;
71 return;
72 }
73 src++;
74
75 if('0' <= src[0] && src[0] < '2' &&
76 '0' <= src[1] && src[1] < '8' &&
77 '0' <= src[2] && src[2] < '8') {
78 *dest++ = (src[0] - '0') << 6
79 | (src[1] - '0') << 3
80 | (src[2] - '0') << 0;
81 src += 3;
82 } else if (src[0] == '\\') {
83 *dest++ = '\\';
84 src += 1;
85 } else {
86 *dest++ = '\\';
87 }
88 }
89}
90
91static struct mntent *GETMNTENT(FILE *stream)
92{
93 struct mntent *entp = getmntent(stream);
94 if(entp != NULL) {
95 unescape(entp->mnt_fsname);
96 unescape(entp->mnt_dir);
97 unescape(entp->mnt_type);
98 unescape(entp->mnt_opts);
99 }
100 return entp;
101}
102#else
103#define GETMNTENT getmntent
104#endif // GETMNTENT_NEEDS_UNESCAPING
105
106/*
107 * Take a ',' separated option string and extract "x-" options
108 */
109static int extract_x_options(const char *original, char **non_x_opts,
110 char **x_opts)
111{
112 size_t orig_len;
113 const char *opt, *opt_end;
114
115 orig_len = strlen(original) + 1;
116
117 *non_x_opts = calloc(1, orig_len);
118 *x_opts = calloc(1, orig_len);
119
120 size_t non_x_opts_len = orig_len;
121 size_t x_opts_len = orig_len;
122
123 if (*non_x_opts == NULL || *x_opts == NULL) {
124 fprintf(stderr, "%s: Failed to allocate %zuB.\n",
125 __func__, orig_len);
126 return -ENOMEM;
127 }
128
129 for (opt = original; opt < original + orig_len; opt = opt_end + 1) {
130 char *opt_buf;
131
132 opt_end = strchr(opt, ',');
133 if (opt_end == NULL)
134 opt_end = original + orig_len;
135
136 size_t opt_len = opt_end - opt;
137 size_t opt_len_left = orig_len - (opt - original);
138 size_t buf_len;
139 bool is_x_opts;
140
141 if (strncmp(opt, "x-", MIN(2, opt_len_left)) == 0) {
142 buf_len = x_opts_len;
143 is_x_opts = true;
144 opt_buf = *x_opts;
145 } else {
146 buf_len = non_x_opts_len;
147 is_x_opts = false;
148 opt_buf = *non_x_opts;
149 }
150
151 if (buf_len < orig_len) {
152 strncat(opt_buf, ",", 2);
153 buf_len -= 1;
154 }
155
156 /* omits ',' */
157 if ((ssize_t)(buf_len - opt_len) < 0) {
158 /* This would be a bug */
159 fprintf(stderr, "%s: no buf space left in copy, orig='%s'\n",
160 __func__, original);
161 return -EIO;
162 }
163
164 strncat(opt_buf, opt, opt_end - opt);
165 buf_len -= opt_len;
166
167 if (is_x_opts)
168 x_opts_len = buf_len;
169 else
170 non_x_opts_len = buf_len;
171 }
172
173 return 0;
174}
175
176static const char *get_user_name(void)
177{
178 struct passwd *pw = getpwuid(getuid());
179 if (pw != NULL && pw->pw_name != NULL)
180 return pw->pw_name;
181 else {
182 fprintf(stderr, "%s: could not determine username\n", progname);
183 return NULL;
184 }
185}
186
187static uid_t oldfsuid;
188static gid_t oldfsgid;
189
190static void drop_privs(void)
191{
192 if (getuid() != 0) {
193 oldfsuid = setfsuid(getuid());
194 oldfsgid = setfsgid(getgid());
195 }
196}
197
198static void restore_privs(void)
199{
200 if (getuid() != 0) {
201 setfsuid(oldfsuid);
202 setfsgid(oldfsgid);
203 }
204}
205
206#ifndef IGNORE_MTAB
207/*
208 * Make sure that /etc/mtab is checked and updated atomically
209 */
210static int lock_umount(void)
211{
212 const char *mtab_lock = _PATH_MOUNTED ".fuselock";
213 int mtablock;
214 int res;
215 struct stat mtab_stat;
216
217 /* /etc/mtab could be a symlink to /proc/mounts */
218 if (lstat(_PATH_MOUNTED, &mtab_stat) == 0 && S_ISLNK(mtab_stat.st_mode))
219 return -1;
220
221 mtablock = open(mtab_lock, O_RDWR | O_CREAT, 0600);
222 if (mtablock == -1) {
223 fprintf(stderr, "%s: unable to open fuse lock file: %s\n",
224 progname, strerror(errno));
225 return -1;
226 }
227 res = lockf(mtablock, F_LOCK, 0);
228 if (res < 0) {
229 fprintf(stderr, "%s: error getting lock: %s\n", progname,
230 strerror(errno));
231 close(mtablock);
232 return -1;
233 }
234
235 return mtablock;
236}
237
238static void unlock_umount(int mtablock)
239{
240 if (mtablock >= 0) {
241 int res;
242
243 res = lockf(mtablock, F_ULOCK, 0);
244 if (res < 0) {
245 fprintf(stderr, "%s: error releasing lock: %s\n",
246 progname, strerror(errno));
247 }
248 close(mtablock);
249 }
250}
251
252static int add_mount(const char *source, const char *mnt, const char *type,
253 const char *opts)
254{
255 return fuse_mnt_add_mount(progname, source, mnt, type, opts);
256}
257
258static int may_unmount(const char *mnt, int quiet)
259{
260 struct mntent *entp;
261 FILE *fp;
262 const char *user = NULL;
263 char uidstr[32];
264 unsigned uidlen = 0;
265 int found;
266 const char *mtab = _PATH_MOUNTED;
267
268 user = get_user_name();
269 if (user == NULL)
270 return -1;
271
272 fp = setmntent(mtab, "r");
273 if (fp == NULL) {
274 fprintf(stderr, "%s: failed to open %s: %s\n", progname, mtab,
275 strerror(errno));
276 return -1;
277 }
278
279 uidlen = sprintf(uidstr, "%u", getuid());
280
281 found = 0;
282 while ((entp = GETMNTENT(fp)) != NULL) {
283 if (!found && strcmp(entp->mnt_dir, mnt) == 0 &&
284 (strcmp(entp->mnt_type, "fuse") == 0 ||
285 strcmp(entp->mnt_type, "fuseblk") == 0 ||
286 strncmp(entp->mnt_type, "fuse.", 5) == 0 ||
287 strncmp(entp->mnt_type, "fuseblk.", 8) == 0)) {
288 char *p = strstr(entp->mnt_opts, "user=");
289 if (p &&
290 (p == entp->mnt_opts || *(p-1) == ',') &&
291 strcmp(p + 5, user) == 0) {
292 found = 1;
293 break;
294 }
295 /* /etc/mtab is a link pointing to
296 /proc/mounts: */
297 else if ((p =
298 strstr(entp->mnt_opts, "user_id=")) &&
299 (p == entp->mnt_opts ||
300 *(p-1) == ',') &&
301 strncmp(p + 8, uidstr, uidlen) == 0 &&
302 (*(p+8+uidlen) == ',' ||
303 *(p+8+uidlen) == '\0')) {
304 found = 1;
305 break;
306 }
307 }
308 }
309 endmntent(fp);
310
311 if (!found) {
312 if (!quiet)
313 fprintf(stderr,
314 "%s: entry for %s not found in %s\n",
315 progname, mnt, mtab);
316 return -1;
317 }
318
319 return 0;
320}
321#endif
322
323/*
324 * Check whether the file specified in "fusermount3 -u" is really a
325 * mountpoint and not a symlink. This is necessary otherwise the user
326 * could move the mountpoint away and replace it with a symlink
327 * pointing to an arbitrary mount, thereby tricking fusermount3 into
328 * unmounting that (umount(2) will follow symlinks).
329 *
330 * This is the child process running in a separate mount namespace, so
331 * we don't mess with the global namespace and if the process is
332 * killed for any reason, mounts are automatically cleaned up.
333 *
334 * First make sure nothing is propagated back into the parent
335 * namespace by marking all mounts "private".
336 *
337 * Then bind mount parent onto a stable base where the user can't move
338 * it around.
339 *
340 * Finally check /proc/mounts for an entry matching the requested
341 * mountpoint. If it's found then we are OK, and the user can't move
342 * it around within the parent directory as rename() will return
343 * EBUSY. Be careful to ignore any mounts that existed before the
344 * bind.
345 */
346static int check_is_mount_child(void *p)
347{
348 const char **a = p;
349 const char *last = a[0];
350 const char *mnt = a[1];
351 const char *type = a[2];
352 int res;
353 const char *procmounts = "/proc/mounts";
354 int found;
355 FILE *fp;
356 struct mntent *entp;
357 int count;
358
359 res = mount("", "/", "", MS_PRIVATE | MS_REC, NULL);
360 if (res == -1) {
361 fprintf(stderr, "%s: failed to mark mounts private: %s\n",
362 progname, strerror(errno));
363 return 1;
364 }
365
366 fp = setmntent(procmounts, "r");
367 if (fp == NULL) {
368 fprintf(stderr, "%s: failed to open %s: %s\n", progname,
369 procmounts, strerror(errno));
370 return 1;
371 }
372
373 count = 0;
374 while (GETMNTENT(fp) != NULL)
375 count++;
376 endmntent(fp);
377
378 fp = setmntent(procmounts, "r");
379 if (fp == NULL) {
380 fprintf(stderr, "%s: failed to open %s: %s\n", progname,
381 procmounts, strerror(errno));
382 return 1;
383 }
384
385 res = mount(".", "/", "", MS_BIND | MS_REC, NULL);
386 if (res == -1) {
387 fprintf(stderr, "%s: failed to bind parent to /: %s\n",
388 progname, strerror(errno));
389 return 1;
390 }
391
392 found = 0;
393 while ((entp = GETMNTENT(fp)) != NULL) {
394 if (count > 0) {
395 count--;
396 continue;
397 }
398 if (entp->mnt_dir[0] == '/' &&
399 strcmp(entp->mnt_dir + 1, last) == 0 &&
400 (!type || strcmp(entp->mnt_type, type) == 0)) {
401 found = 1;
402 break;
403 }
404 }
405 endmntent(fp);
406
407 if (!found) {
408 fprintf(stderr, "%s: %s not mounted\n", progname, mnt);
409 return 1;
410 }
411
412 return 0;
413}
414
415static pid_t clone_newns(void *a)
416{
417 char buf[131072];
418 char *stack = buf + (sizeof(buf) / 2 - ((size_t) buf & 15));
419
420#ifdef __ia64__
421 extern int __clone2(int (*fn)(void *),
422 void *child_stack_base, size_t stack_size,
423 int flags, void *arg, pid_t *ptid,
424 void *tls, pid_t *ctid);
425
426 return __clone2(check_is_mount_child, stack, sizeof(buf) / 2,
427 CLONE_NEWNS, a, NULL, NULL, NULL);
428#else
429 return clone(check_is_mount_child, stack, CLONE_NEWNS, a);
430#endif
431}
432
433static int check_is_mount(const char *last, const char *mnt, const char *type)
434{
435 pid_t pid, p;
436 int status;
437 const char *a[3] = { last, mnt, type };
438
439 pid = clone_newns((void *) a);
440 if (pid == (pid_t) -1) {
441 fprintf(stderr, "%s: failed to clone namespace: %s\n",
442 progname, strerror(errno));
443 return -1;
444 }
445 p = waitpid(pid, &status, __WCLONE);
446 if (p == (pid_t) -1) {
447 fprintf(stderr, "%s: waitpid failed: %s\n",
448 progname, strerror(errno));
449 return -1;
450 }
451 if (!WIFEXITED(status)) {
452 fprintf(stderr, "%s: child terminated abnormally (status %i)\n",
453 progname, status);
454 return -1;
455 }
456 if (WEXITSTATUS(status) != 0)
457 return -1;
458
459 return 0;
460}
461
462static int chdir_to_parent(char *copy, const char **lastp)
463{
464 char *tmp;
465 const char *parent;
466 char buf[65536];
467 int res;
468
469 tmp = strrchr(copy, '/');
470 if (tmp == NULL || tmp[1] == '\0') {
471 fprintf(stderr, "%s: internal error: invalid abs path: <%s>\n",
472 progname, copy);
473 return -1;
474 }
475 if (tmp != copy) {
476 *tmp = '\0';
477 parent = copy;
478 *lastp = tmp + 1;
479 } else if (tmp[1] != '\0') {
480 *lastp = tmp + 1;
481 parent = "/";
482 } else {
483 *lastp = ".";
484 parent = "/";
485 }
486
487 res = chdir(parent);
488 if (res == -1) {
489 fprintf(stderr, "%s: failed to chdir to %s: %s\n",
490 progname, parent, strerror(errno));
491 return -1;
492 }
493
494 if (getcwd(buf, sizeof(buf)) == NULL) {
495 fprintf(stderr, "%s: failed to obtain current directory: %s\n",
496 progname, strerror(errno));
497 return -1;
498 }
499 if (strcmp(buf, parent) != 0) {
500 fprintf(stderr, "%s: mountpoint moved (%s -> %s)\n", progname,
501 parent, buf);
502 return -1;
503
504 }
505
506 return 0;
507}
508
509#ifndef IGNORE_MTAB
510static int unmount_fuse_locked(const char *mnt, int quiet, int lazy)
511{
512 int res;
513 char *copy;
514 const char *last;
515 int umount_flags = (lazy ? UMOUNT_DETACH : 0) | UMOUNT_NOFOLLOW;
516
517 if (getuid() != 0) {
518 res = may_unmount(mnt, quiet);
519 if (res == -1)
520 return -1;
521 }
522
523 copy = strdup(mnt);
524 if (copy == NULL) {
525 fprintf(stderr, "%s: failed to allocate memory\n", progname);
526 return -1;
527 }
528
529 drop_privs();
530 res = chdir_to_parent(copy, &last);
531 if (res == -1) {
532 restore_privs();
533 goto out;
534 }
535
536 res = umount2(last, umount_flags);
537 restore_privs();
538 if (res == -1 && !quiet) {
539 fprintf(stderr, "%s: failed to unmount %s: %s\n",
540 progname, mnt, strerror(errno));
541 }
542
543out:
544 free(copy);
545 if (res == -1)
546 return -1;
547
548 res = chdir("/");
549 if (res == -1) {
550 fprintf(stderr, "%s: failed to chdir to '/'\n", progname);
551 return -1;
552 }
553
554 return fuse_mnt_remove_mount(progname, mnt);
555}
556
557static int unmount_fuse(const char *mnt, int quiet, int lazy)
558{
559 int res;
560 int mtablock = lock_umount();
561
562 res = unmount_fuse_locked(mnt, quiet, lazy);
563 unlock_umount(mtablock);
564
565 return res;
566}
567
568static int count_fuse_fs(void)
569{
570 struct mntent *entp;
571 int count = 0;
572 const char *mtab = _PATH_MOUNTED;
573 FILE *fp = setmntent(mtab, "r");
574 if (fp == NULL) {
575 fprintf(stderr, "%s: failed to open %s: %s\n", progname, mtab,
576 strerror(errno));
577 return -1;
578 }
579 while ((entp = GETMNTENT(fp)) != NULL) {
580 if (strcmp(entp->mnt_type, "fuse") == 0 ||
581 strncmp(entp->mnt_type, "fuse.", 5) == 0)
582 count ++;
583 }
584 endmntent(fp);
585 return count;
586}
587
588
589#else /* IGNORE_MTAB */
590static int count_fuse_fs(void)
591{
592 return 0;
593}
594
595static int add_mount(const char *source, const char *mnt, const char *type,
596 const char *opts)
597{
598 (void) source;
599 (void) mnt;
600 (void) type;
601 (void) opts;
602 return 0;
603}
604
605static int unmount_fuse(const char *mnt, int quiet, int lazy)
606{
607 (void) quiet;
608 return fuse_mnt_umount(progname, mnt, mnt, lazy);
609}
610#endif /* IGNORE_MTAB */
611
612static void strip_line(char *line)
613{
614 char *s = strchr(line, '#');
615 if (s != NULL)
616 s[0] = '\0';
617 for (s = line + strlen(line) - 1;
618 s >= line && isspace((unsigned char) *s); s--);
619 s[1] = '\0';
620 for (s = line; isspace((unsigned char) *s); s++);
621 if (s != line)
622 memmove(line, s, strlen(s)+1);
623}
624
625static void parse_line(char *line, int linenum)
626{
627 int tmp;
628 if (strcmp(line, "user_allow_other") == 0)
629 user_allow_other = 1;
630 else if (sscanf(line, "mount_max = %i", &tmp) == 1)
631 mount_max = tmp;
632 else if(line[0])
633 fprintf(stderr,
634 "%s: unknown parameter in %s at line %i: '%s'\n",
635 progname, FUSE_CONF, linenum, line);
636}
637
638static void read_conf(void)
639{
640 FILE *fp = fopen(FUSE_CONF, "r");
641 if (fp != NULL) {
642 int linenum = 1;
643 char line[256];
644 int isnewline = 1;
645 while (fgets(line, sizeof(line), fp) != NULL) {
646 if (isnewline) {
647 if (line[strlen(line)-1] == '\n') {
648 strip_line(line);
649 parse_line(line, linenum);
650 } else {
651 isnewline = 0;
652 }
653 } else if(line[strlen(line)-1] == '\n') {
654 fprintf(stderr, "%s: reading %s: line %i too long\n", progname, FUSE_CONF, linenum);
655
656 isnewline = 1;
657 }
658 if (isnewline)
659 linenum ++;
660 }
661 if (!isnewline) {
662 fprintf(stderr, "%s: reading %s: missing newline at end of file\n", progname, FUSE_CONF);
663
664 }
665 if (ferror(fp)) {
666 fprintf(stderr, "%s: reading %s: read failed\n", progname, FUSE_CONF);
667 exit(1);
668 }
669 fclose(fp);
670 } else if (errno != ENOENT) {
671 bool fatal = (errno != EACCES && errno != ELOOP &&
672 errno != ENAMETOOLONG && errno != ENOTDIR &&
673 errno != EOVERFLOW);
674 fprintf(stderr, "%s: failed to open %s: %s\n",
675 progname, FUSE_CONF, strerror(errno));
676 if (fatal)
677 exit(1);
678 }
679}
680
681static int begins_with(const char *s, const char *beg)
682{
683 if (strncmp(s, beg, strlen(beg)) == 0)
684 return 1;
685 else
686 return 0;
687}
688
689struct mount_flags {
690 const char *opt;
691 unsigned long flag;
692 int on;
693 int safe;
694};
695
696static struct mount_flags mount_flags[] = {
697 {"rw", MS_RDONLY, 0, 1},
698 {"ro", MS_RDONLY, 1, 1},
699 {"suid", MS_NOSUID, 0, 0},
700 {"nosuid", MS_NOSUID, 1, 1},
701 {"dev", MS_NODEV, 0, 0},
702 {"nodev", MS_NODEV, 1, 1},
703 {"exec", MS_NOEXEC, 0, 1},
704 {"noexec", MS_NOEXEC, 1, 1},
705 {"async", MS_SYNCHRONOUS, 0, 1},
706 {"sync", MS_SYNCHRONOUS, 1, 1},
707 {"atime", MS_NOATIME, 0, 1},
708 {"noatime", MS_NOATIME, 1, 1},
709 {"diratime", MS_NODIRATIME, 0, 1},
710 {"nodiratime", MS_NODIRATIME, 1, 1},
711 {"lazytime", MS_LAZYTIME, 1, 1},
712 {"nolazytime", MS_LAZYTIME, 0, 1},
713 {"relatime", MS_RELATIME, 1, 1},
714 {"norelatime", MS_RELATIME, 0, 1},
715 {"strictatime", MS_STRICTATIME, 1, 1},
716 {"nostrictatime", MS_STRICTATIME, 0, 1},
717 {"dirsync", MS_DIRSYNC, 1, 1},
718 {"symfollow", MS_NOSYMFOLLOW, 0, 1},
719 {"nosymfollow", MS_NOSYMFOLLOW, 1, 1},
720 {NULL, 0, 0, 0}
721};
722
723static int find_mount_flag(const char *s, unsigned len, int *on, int *flag)
724{
725 int i;
726
727 for (i = 0; mount_flags[i].opt != NULL; i++) {
728 const char *opt = mount_flags[i].opt;
729 if (strlen(opt) == len && strncmp(opt, s, len) == 0) {
730 *on = mount_flags[i].on;
731 *flag = mount_flags[i].flag;
732 if (!mount_flags[i].safe && getuid() != 0) {
733 *flag = 0;
734 fprintf(stderr,
735 "%s: unsafe option %s ignored\n",
736 progname, opt);
737 }
738 return 1;
739 }
740 }
741 return 0;
742}
743
744static int add_option(char **optsp, const char *opt, unsigned expand)
745{
746 char *newopts;
747 if (*optsp == NULL)
748 newopts = strdup(opt);
749 else {
750 unsigned oldsize = strlen(*optsp);
751 unsigned newsize = oldsize + 1 + strlen(opt) + expand + 1;
752 newopts = (char *) realloc(*optsp, newsize);
753 if (newopts)
754 sprintf(newopts + oldsize, ",%s", opt);
755 }
756 if (newopts == NULL) {
757 fprintf(stderr, "%s: failed to allocate memory\n", progname);
758 return -1;
759 }
760 *optsp = newopts;
761 return 0;
762}
763
764static int get_mnt_opts(int flags, char *opts, char **mnt_optsp)
765{
766 int i;
767 int l;
768
769 if (!(flags & MS_RDONLY) && add_option(mnt_optsp, "rw", 0) == -1)
770 return -1;
771
772 for (i = 0; mount_flags[i].opt != NULL; i++) {
773 if (mount_flags[i].on && (flags & mount_flags[i].flag) &&
774 add_option(mnt_optsp, mount_flags[i].opt, 0) == -1)
775 return -1;
776 }
777
778 if (add_option(mnt_optsp, opts, 0) == -1)
779 return -1;
780 /* remove comma from end of opts*/
781 l = strlen(*mnt_optsp);
782 if ((*mnt_optsp)[l-1] == ',')
783 (*mnt_optsp)[l-1] = '\0';
784 if (getuid() != 0) {
785 const char *user = get_user_name();
786 if (user == NULL)
787 return -1;
788
789 if (add_option(mnt_optsp, "user=", strlen(user)) == -1)
790 return -1;
791 strcat(*mnt_optsp, user);
792 }
793 return 0;
794}
795
796static int opt_eq(const char *s, unsigned len, const char *opt)
797{
798 if(strlen(opt) == len && strncmp(s, opt, len) == 0)
799 return 1;
800 else
801 return 0;
802}
803
804static int get_string_opt(const char *s, unsigned len, const char *opt,
805 char **val)
806{
807 int i;
808 unsigned opt_len = strlen(opt);
809 char *d;
810
811 if (*val)
812 free(*val);
813 *val = (char *) malloc(len - opt_len + 1);
814 if (!*val) {
815 fprintf(stderr, "%s: failed to allocate memory\n", progname);
816 return 0;
817 }
818
819 d = *val;
820 s += opt_len;
821 len -= opt_len;
822 for (i = 0; i < len; i++) {
823 if (s[i] == '\\' && i + 1 < len)
824 i++;
825 *d++ = s[i];
826 }
827 *d = '\0';
828 return 1;
829}
830
831/* The kernel silently truncates the "data" argument to PAGE_SIZE-1 characters.
832 * This can be dangerous if it e.g. truncates the option "group_id=1000" to
833 * "group_id=1".
834 * This wrapper detects this case and bails out with an error.
835 */
836static int mount_notrunc(const char *source, const char *target,
837 const char *filesystemtype, unsigned long mountflags,
838 const char *data) {
839 if (strlen(data) > sysconf(_SC_PAGESIZE) - 1) {
840 fprintf(stderr, "%s: mount options too long\n", progname);
841 errno = EINVAL;
842 return -1;
843 }
844 return mount(source, target, filesystemtype, mountflags, data);
845}
846
847
848static int do_mount(const char *mnt, const char **typep, mode_t rootmode,
849 int fd, const char *opts, const char *dev, char **sourcep,
850 char **mnt_optsp)
851{
852 int res;
853 int flags = MS_NOSUID | MS_NODEV;
854 char *optbuf;
855 char *mnt_opts = NULL;
856 const char *s;
857 char *d;
858 char *fsname = NULL;
859 char *subtype = NULL;
860 char *source = NULL;
861 char *type = NULL;
862 int blkdev = 0;
863
864 optbuf = (char *) malloc(strlen(opts) + 128);
865 if (!optbuf) {
866 fprintf(stderr, "%s: failed to allocate memory\n", progname);
867 return -1;
868 }
869
870 for (s = opts, d = optbuf; *s;) {
871 unsigned len;
872 const char *fsname_str = "fsname=";
873 const char *subtype_str = "subtype=";
874 bool escape_ok = begins_with(s, fsname_str) ||
875 begins_with(s, subtype_str);
876 for (len = 0; s[len]; len++) {
877 if (escape_ok && s[len] == '\\' && s[len + 1])
878 len++;
879 else if (s[len] == ',')
880 break;
881 }
882 if (begins_with(s, fsname_str)) {
883 if (!get_string_opt(s, len, fsname_str, &fsname))
884 goto err;
885 } else if (begins_with(s, subtype_str)) {
886 if (!get_string_opt(s, len, subtype_str, &subtype))
887 goto err;
888 } else if (opt_eq(s, len, "blkdev")) {
889 if (getuid() != 0) {
890 fprintf(stderr,
891 "%s: option blkdev is privileged\n",
892 progname);
893 goto err;
894 }
895 blkdev = 1;
896 } else if (opt_eq(s, len, "auto_unmount")) {
897 auto_unmount = 1;
898 } else if (!opt_eq(s, len, "nonempty") &&
899 !begins_with(s, "fd=") &&
900 !begins_with(s, "rootmode=") &&
901 !begins_with(s, "user_id=") &&
902 !begins_with(s, "group_id=")) {
903 int on;
904 int flag;
905 int skip_option = 0;
906 if (opt_eq(s, len, "large_read")) {
907 struct utsname utsname;
908 unsigned kmaj, kmin;
909 res = uname(&utsname);
910 if (res == 0 &&
911 sscanf(utsname.release, "%u.%u",
912 &kmaj, &kmin) == 2 &&
913 (kmaj > 2 || (kmaj == 2 && kmin > 4))) {
914 fprintf(stderr, "%s: note: 'large_read' mount option is deprecated for %i.%i kernels\n", progname, kmaj, kmin);
915 skip_option = 1;
916 }
917 }
918 if (getuid() != 0 && !user_allow_other &&
919 (opt_eq(s, len, "allow_other") ||
920 opt_eq(s, len, "allow_root"))) {
921 fprintf(stderr, "%s: option %.*s only allowed if 'user_allow_other' is set in %s\n", progname, len, s, FUSE_CONF);
922 goto err;
923 }
924 if (!skip_option) {
925 if (find_mount_flag(s, len, &on, &flag)) {
926 if (on)
927 flags |= flag;
928 else
929 flags &= ~flag;
930 } else if (opt_eq(s, len, "default_permissions") ||
931 opt_eq(s, len, "allow_other") ||
932 begins_with(s, "max_read=") ||
933 begins_with(s, "blksize=")) {
934 memcpy(d, s, len);
935 d += len;
936 *d++ = ',';
937 } else {
938 fprintf(stderr, "%s: unknown option '%.*s'\n", progname, len, s);
939 exit(1);
940 }
941 }
942 }
943 s += len;
944 if (*s)
945 s++;
946 }
947 *d = '\0';
948 res = get_mnt_opts(flags, optbuf, &mnt_opts);
949 if (res == -1)
950 goto err;
951
952 sprintf(d, "fd=%i,rootmode=%o,user_id=%u,group_id=%u",
953 fd, rootmode, getuid(), getgid());
954
955 source = malloc((fsname ? strlen(fsname) : 0) +
956 (subtype ? strlen(subtype) : 0) + strlen(dev) + 32);
957
958 type = malloc((subtype ? strlen(subtype) : 0) + 32);
959 if (!type || !source) {
960 fprintf(stderr, "%s: failed to allocate memory\n", progname);
961 goto err;
962 }
963
964 if (subtype)
965 sprintf(type, "%s.%s", blkdev ? "fuseblk" : "fuse", subtype);
966 else
967 strcpy(type, blkdev ? "fuseblk" : "fuse");
968
969 if (fsname)
970 strcpy(source, fsname);
971 else
972 strcpy(source, subtype ? subtype : dev);
973
974 res = mount_notrunc(source, mnt, type, flags, optbuf);
975 if (res == -1 && errno == ENODEV && subtype) {
976 /* Probably missing subtype support */
977 strcpy(type, blkdev ? "fuseblk" : "fuse");
978 if (fsname) {
979 if (!blkdev)
980 sprintf(source, "%s#%s", subtype, fsname);
981 } else {
982 strcpy(source, type);
983 }
984
985 res = mount_notrunc(source, mnt, type, flags, optbuf);
986 }
987 if (res == -1 && errno == EINVAL) {
988 /* It could be an old version not supporting group_id */
989 sprintf(d, "fd=%i,rootmode=%o,user_id=%u",
990 fd, rootmode, getuid());
991 res = mount_notrunc(source, mnt, type, flags, optbuf);
992 }
993 if (res == -1) {
994 int errno_save = errno;
995 if (blkdev && errno == ENODEV && !fuse_mnt_check_fuseblk())
996 fprintf(stderr, "%s: 'fuseblk' support missing\n",
997 progname);
998 else
999 fprintf(stderr, "%s: mount failed: %s\n", progname,
1000 strerror(errno_save));
1001 goto err;
1002 }
1003 *sourcep = source;
1004 *typep = type;
1005 *mnt_optsp = mnt_opts;
1006 free(fsname);
1007 free(optbuf);
1008
1009 return 0;
1010
1011err:
1012 free(fsname);
1013 free(subtype);
1014 free(source);
1015 free(type);
1016 free(mnt_opts);
1017 free(optbuf);
1018 return -1;
1019}
1020
1021static int check_perm(const char **mntp, struct stat *stbuf, int *mountpoint_fd)
1022{
1023 int res;
1024 const char *mnt = *mntp;
1025 const char *origmnt = mnt;
1026 struct statfs fs_buf;
1027 size_t i;
1028
1029 res = lstat(mnt, stbuf);
1030 if (res == -1) {
1031 fprintf(stderr, "%s: failed to access mountpoint %s: %s\n",
1032 progname, mnt, strerror(errno));
1033 return -1;
1034 }
1035
1036 /* No permission checking is done for root */
1037 if (getuid() == 0)
1038 return 0;
1039
1040 if (S_ISDIR(stbuf->st_mode)) {
1041 res = chdir(mnt);
1042 if (res == -1) {
1043 fprintf(stderr,
1044 "%s: failed to chdir to mountpoint: %s\n",
1045 progname, strerror(errno));
1046 return -1;
1047 }
1048 mnt = *mntp = ".";
1049 res = lstat(mnt, stbuf);
1050 if (res == -1) {
1051 fprintf(stderr,
1052 "%s: failed to access mountpoint %s: %s\n",
1053 progname, origmnt, strerror(errno));
1054 return -1;
1055 }
1056
1057 if ((stbuf->st_mode & S_ISVTX) && stbuf->st_uid != getuid()) {
1058 fprintf(stderr, "%s: mountpoint %s not owned by user\n",
1059 progname, origmnt);
1060 return -1;
1061 }
1062
1063 res = access(mnt, W_OK);
1064 if (res == -1) {
1065 fprintf(stderr, "%s: user has no write access to mountpoint %s\n",
1066 progname, origmnt);
1067 return -1;
1068 }
1069 } else if (S_ISREG(stbuf->st_mode)) {
1070 static char procfile[256];
1071 *mountpoint_fd = open(mnt, O_WRONLY);
1072 if (*mountpoint_fd == -1) {
1073 fprintf(stderr, "%s: failed to open %s: %s\n",
1074 progname, mnt, strerror(errno));
1075 return -1;
1076 }
1077 res = fstat(*mountpoint_fd, stbuf);
1078 if (res == -1) {
1079 fprintf(stderr,
1080 "%s: failed to access mountpoint %s: %s\n",
1081 progname, mnt, strerror(errno));
1082 return -1;
1083 }
1084 if (!S_ISREG(stbuf->st_mode)) {
1085 fprintf(stderr,
1086 "%s: mountpoint %s is no longer a regular file\n",
1087 progname, mnt);
1088 return -1;
1089 }
1090
1091 sprintf(procfile, "/proc/self/fd/%i", *mountpoint_fd);
1092 *mntp = procfile;
1093 } else {
1094 fprintf(stderr,
1095 "%s: mountpoint %s is not a directory or a regular file\n",
1096 progname, mnt);
1097 return -1;
1098 }
1099
1100 /* Do not permit mounting over anything in procfs - it has a couple
1101 * places to which we have "write access" without being supposed to be
1102 * able to just put anything we want there.
1103 * Luckily, without allow_other, we can't get other users to actually
1104 * use any fake information we try to put there anyway.
1105 * Use a whitelist to be safe. */
1106 if (statfs(*mntp, &fs_buf)) {
1107 fprintf(stderr, "%s: failed to access mountpoint %s: %s\n",
1108 progname, mnt, strerror(errno));
1109 return -1;
1110 }
1111
1112 /* Define permitted filesystems for the mount target. This was
1113 * originally the same list as used by the ecryptfs mount helper
1114 * (https://bazaar.launchpad.net/~ecryptfs/ecryptfs/trunk/view/head:/src/utils/mount.ecryptfs_private.c#L225)
1115 * but got expanded as we found more filesystems that needed to be
1116 * overlaid. */
1117 typeof(fs_buf.f_type) f_type_whitelist[] = {
1118 0x61756673 /* AUFS_SUPER_MAGIC */,
1119 0x00000187 /* AUTOFS_SUPER_MAGIC */,
1120 0xCA451A4E /* BCACHEFS_STATFS_MAGIC */,
1121 0x9123683E /* BTRFS_SUPER_MAGIC */,
1122 0x00C36400 /* CEPH_SUPER_MAGIC */,
1123 0xFF534D42 /* CIFS_MAGIC_NUMBER */,
1124 0x0000F15F /* ECRYPTFS_SUPER_MAGIC */,
1125 0X2011BAB0 /* EXFAT_SUPER_MAGIC */,
1126 0x0000EF53 /* EXT[234]_SUPER_MAGIC */,
1127 0xF2F52010 /* F2FS_SUPER_MAGIC */,
1128 0x65735546 /* FUSE_SUPER_MAGIC */,
1129 0x01161970 /* GFS2_MAGIC */,
1130 0x47504653 /* GPFS_SUPER_MAGIC */,
1131 0x0000482b /* HFSPLUS_SUPER_MAGIC */,
1132 0x000072B6 /* JFFS2_SUPER_MAGIC */,
1133 0x3153464A /* JFS_SUPER_MAGIC */,
1134 0x0BD00BD0 /* LL_SUPER_MAGIC */,
1135 0X00004D44 /* MSDOS_SUPER_MAGIC */,
1136 0x0000564C /* NCP_SUPER_MAGIC */,
1137 0x00006969 /* NFS_SUPER_MAGIC */,
1138 0x00003434 /* NILFS_SUPER_MAGIC */,
1139 0x5346544E /* NTFS_SB_MAGIC */,
1140 0x7366746E /* NTFS3_SUPER_MAGIC */,
1141 0x5346414f /* OPENAFS_SUPER_MAGIC */,
1142 0x794C7630 /* OVERLAYFS_SUPER_MAGIC */,
1143 0xAAD7AAEA /* PANFS_SUPER_MAGIC */,
1144 0x52654973 /* REISERFS_SUPER_MAGIC */,
1145 0xFE534D42 /* SMB2_SUPER_MAGIC */,
1146 0x73717368 /* SQUASHFS_MAGIC */,
1147 0x01021994 /* TMPFS_MAGIC */,
1148 0x24051905 /* UBIFS_SUPER_MAGIC */,
1149#if __SIZEOF_LONG__ > 4
1150 0x736675005346544e /* UFSD */,
1151#endif
1152 0x58465342 /* XFS_SB_MAGIC */,
1153 0x2FC12FC1 /* ZFS_SUPER_MAGIC */,
1154 0x858458f6 /* RAMFS_MAGIC */,
1155 };
1156 for (i = 0; i < sizeof(f_type_whitelist)/sizeof(f_type_whitelist[0]); i++) {
1157 if (f_type_whitelist[i] == fs_buf.f_type)
1158 return 0;
1159 }
1160
1161 fprintf(stderr, "%s: mounting over filesystem type %#010lx is forbidden\n",
1162 progname, (unsigned long)fs_buf.f_type);
1163 return -1;
1164}
1165
1166static int try_open(const char *dev, char **devp, int silent)
1167{
1168 int fd = open(dev, O_RDWR);
1169 if (fd != -1) {
1170 *devp = strdup(dev);
1171 if (*devp == NULL) {
1172 fprintf(stderr, "%s: failed to allocate memory\n",
1173 progname);
1174 close(fd);
1175 fd = -1;
1176 }
1177 } else if (errno == ENODEV ||
1178 errno == ENOENT)/* check for ENOENT too, for the udev case */
1179 return -2;
1180 else if (!silent) {
1181 fprintf(stderr, "%s: failed to open %s: %s\n", progname, dev,
1182 strerror(errno));
1183 }
1184 return fd;
1185}
1186
1187static int try_open_fuse_device(char **devp)
1188{
1189 int fd;
1190
1191 drop_privs();
1192 fd = try_open(FUSE_DEV, devp, 0);
1193 restore_privs();
1194 return fd;
1195}
1196
1197static int open_fuse_device(char **devp)
1198{
1199 int fd = try_open_fuse_device(devp);
1200 if (fd >= -1)
1201 return fd;
1202
1203 fprintf(stderr,
1204 "%s: fuse device not found, try 'modprobe fuse' first\n",
1205 progname);
1206
1207 return -1;
1208}
1209
1210
1211static int mount_fuse(const char *mnt, const char *opts, const char **type)
1212{
1213 int res;
1214 int fd;
1215 char *dev;
1216 struct stat stbuf;
1217 char *source = NULL;
1218 char *mnt_opts = NULL;
1219 const char *real_mnt = mnt;
1220 int mountpoint_fd = -1;
1221 char *do_mount_opts = NULL;
1222 char *x_opts = NULL;
1223
1224 fd = open_fuse_device(&dev);
1225 if (fd == -1)
1226 return -1;
1227
1228 drop_privs();
1229 read_conf();
1230
1231 if (getuid() != 0 && mount_max != -1) {
1232 int mount_count = count_fuse_fs();
1233 if (mount_count >= mount_max) {
1234 fprintf(stderr, "%s: too many FUSE filesystems mounted; mount_max=N can be set in %s\n", progname, FUSE_CONF);
1235 goto fail_close_fd;
1236 }
1237 }
1238
1239 // Extract any options starting with "x-"
1240 res= extract_x_options(opts, &do_mount_opts, &x_opts);
1241 if (res)
1242 goto fail_close_fd;
1243
1244 res = check_perm(&real_mnt, &stbuf, &mountpoint_fd);
1245 restore_privs();
1246 if (res != -1)
1247 res = do_mount(real_mnt, type, stbuf.st_mode & S_IFMT,
1248 fd, do_mount_opts, dev, &source, &mnt_opts);
1249
1250 if (mountpoint_fd != -1)
1251 close(mountpoint_fd);
1252
1253 if (res == -1)
1254 goto fail_close_fd;
1255
1256 res = chdir("/");
1257 if (res == -1) {
1258 fprintf(stderr, "%s: failed to chdir to '/'\n", progname);
1259 goto fail_close_fd;
1260 }
1261
1262 if (geteuid() == 0) {
1263 if (x_opts && strlen(x_opts) > 0) {
1264 /*
1265 * Add back the options starting with "x-" to opts from
1266 * do_mount. +2 for ',' and '\0'
1267 */
1268 size_t mnt_opts_len = strlen(mnt_opts);
1269 size_t x_mnt_opts_len = mnt_opts_len+
1270 strlen(x_opts) + 2;
1271 char *x_mnt_opts = calloc(1, x_mnt_opts_len);
1272
1273 if (mnt_opts_len) {
1274 strcpy(x_mnt_opts, mnt_opts);
1275 strncat(x_mnt_opts, ",", 2);
1276 }
1277
1278 strncat(x_mnt_opts, x_opts,
1279 x_mnt_opts_len - mnt_opts_len - 2);
1280
1281 free(mnt_opts);
1282 mnt_opts = x_mnt_opts;
1283 }
1284
1285 res = add_mount(source, mnt, *type, mnt_opts);
1286 if (res == -1) {
1287 /* Can't clean up mount in a non-racy way */
1288 goto fail_close_fd;
1289 }
1290 }
1291
1292out_free:
1293 free(source);
1294 free(mnt_opts);
1295 free(dev);
1296 free(x_opts);
1297 free(do_mount_opts);
1298
1299 return fd;
1300
1301fail_close_fd:
1302 close(fd);
1303 fd = -1;
1304 goto out_free;
1305}
1306
1307static int send_fd(int sock_fd, int fd)
1308{
1309 int retval;
1310 struct msghdr msg;
1311 struct cmsghdr *p_cmsg;
1312 struct iovec vec;
1313 size_t cmsgbuf[CMSG_SPACE(sizeof(fd)) / sizeof(size_t)];
1314 int *p_fds;
1315 char sendchar = 0;
1316
1317 msg.msg_control = cmsgbuf;
1318 msg.msg_controllen = sizeof(cmsgbuf);
1319 p_cmsg = CMSG_FIRSTHDR(&msg);
1320 p_cmsg->cmsg_level = SOL_SOCKET;
1321 p_cmsg->cmsg_type = SCM_RIGHTS;
1322 p_cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
1323 p_fds = (int *) CMSG_DATA(p_cmsg);
1324 *p_fds = fd;
1325 msg.msg_controllen = p_cmsg->cmsg_len;
1326 msg.msg_name = NULL;
1327 msg.msg_namelen = 0;
1328 msg.msg_iov = &vec;
1329 msg.msg_iovlen = 1;
1330 msg.msg_flags = 0;
1331 /* "To pass file descriptors or credentials you need to send/read at
1332 * least one byte" (man 7 unix) */
1333 vec.iov_base = &sendchar;
1334 vec.iov_len = sizeof(sendchar);
1335 while ((retval = sendmsg(sock_fd, &msg, 0)) == -1 && errno == EINTR);
1336 if (retval != 1) {
1337 perror("sending file descriptor");
1338 return -1;
1339 }
1340 return 0;
1341}
1342
1343/* Helper for should_auto_unmount
1344 *
1345 * fusermount typically has the s-bit set - initial open of `mnt` was as root
1346 * and got EACCESS as 'allow_other' was not specified.
1347 * Try opening `mnt` again with uid and guid of the calling process.
1348 */
1349static int recheck_ENOTCONN_as_owner(const char *mnt)
1350{
1351 int pid = fork();
1352 if(pid == -1) {
1353 perror("fuse: recheck_ENOTCONN_as_owner can't fork");
1354 _exit(EXIT_FAILURE);
1355 } else if(pid == 0) {
1356 uid_t uid = getuid();
1357 gid_t gid = getgid();
1358 if(setresgid(gid, gid, gid) == -1) {
1359 perror("fuse: can't set resgid");
1360 _exit(EXIT_FAILURE);
1361 }
1362 if(setresuid(uid, uid, uid) == -1) {
1363 perror("fuse: can't set resuid");
1364 _exit(EXIT_FAILURE);
1365 }
1366
1367 int fd = open(mnt, O_RDONLY);
1368 if(fd == -1 && errno == ENOTCONN)
1369 _exit(EXIT_SUCCESS);
1370 else
1371 _exit(EXIT_FAILURE);
1372 } else {
1373 int status;
1374 int res = waitpid(pid, &status, 0);
1375 if (res == -1) {
1376 perror("fuse: waiting for child failed");
1377 _exit(EXIT_FAILURE);
1378 }
1379 return WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS;
1380 }
1381}
1382
1383/* The parent fuse process has died: decide whether to auto_unmount.
1384 *
1385 * In the normal case (umount or fusermount -u), the filesystem
1386 * has already been unmounted. If we simply unmount again we can
1387 * cause problems with stacked mounts (e.g. autofs).
1388 *
1389 * So we unmount here only in abnormal case where fuse process has
1390 * died without unmount happening. To detect this, we first look in
1391 * the mount table to make sure the mountpoint is still mounted and
1392 * has proper type. If so, we then see if opening the mount dir is
1393 * returning 'Transport endpoint is not connected'.
1394 *
1395 * The order of these is important, because if autofs is in use,
1396 * opening the dir to check for ENOTCONN will cause a new mount
1397 * in the normal case where filesystem has been unmounted cleanly.
1398 */
1399static int should_auto_unmount(const char *mnt, const char *type)
1400{
1401 char *copy;
1402 const char *last;
1403 int result = 0;
1404 int fd;
1405
1406 copy = strdup(mnt);
1407 if (copy == NULL) {
1408 fprintf(stderr, "%s: failed to allocate memory\n", progname);
1409 return 0;
1410 }
1411
1412 if (chdir_to_parent(copy, &last) == -1)
1413 goto out;
1414 if (check_is_mount(last, mnt, type) == -1)
1415 goto out;
1416
1417 fd = open(mnt, O_RDONLY);
1418
1419 if (fd != -1) {
1420 close(fd);
1421 } else {
1422 switch(errno) {
1423 case ENOTCONN:
1424 result = 1;
1425 break;
1426 case EACCES:
1427 result = recheck_ENOTCONN_as_owner(mnt);
1428 break;
1429 default:
1430 result = 0;
1431 break;
1432 }
1433 }
1434out:
1435 free(copy);
1436 return result;
1437}
1438
1439static void usage(void)
1440{
1441 printf("%s: [options] mountpoint\n"
1442 "Options:\n"
1443 " -h print help\n"
1444 " -V print version\n"
1445 " -o opt[,opt...] mount options\n"
1446 " -u unmount\n"
1447 " -q quiet\n"
1448 " -z lazy unmount\n",
1449 progname);
1450 exit(1);
1451}
1452
1453static void show_version(void)
1454{
1455 printf("fusermount3 version: %s\n", PACKAGE_VERSION);
1456 exit(0);
1457}
1458
1459static void close_range_loop(int min_fd, int max_fd, int cfd)
1460{
1461 for (int fd = min_fd; fd <= max_fd; fd++)
1462 if (fd != cfd)
1463 close(fd);
1464}
1465
1466/*
1467 * Close all inherited fds that are not needed
1468 * Ideally these wouldn't come up at all, applications should better
1469 * use FD_CLOEXEC / O_CLOEXEC
1470 */
1471static int close_inherited_fds(int cfd)
1472{
1473 int rc = -1;
1474 int nullfd;
1475
1476 /* We can't even report an error */
1477 if (cfd <= STDERR_FILENO)
1478 return -EINVAL;
1479
1480#ifdef HAVE_CLOSE_RANGE
1481 if (cfd < STDERR_FILENO + 2) {
1482 close_range_loop(STDERR_FILENO + 1, cfd - 1, cfd);
1483 } else {
1484 rc = close_range(STDERR_FILENO + 1, cfd - 1, 0);
1485 if (rc < 0)
1486 goto fallback;
1487 }
1488
1489 /* Close high range */
1490 rc = close_range(cfd + 1, ~0U, 0);
1491#else
1492 goto fallback; /* make use of fallback to avoid compiler warnings */
1493#endif
1494
1495fallback:
1496 if (rc < 0) {
1497 int max_fd = sysconf(_SC_OPEN_MAX) - 1;
1498
1499 close_range_loop(STDERR_FILENO + 1, max_fd, cfd);
1500 }
1501
1502 nullfd = open("/dev/null", O_RDWR);
1503 if (nullfd < 0) {
1504 perror("fusermount: cannot open /dev/null");
1505 return -errno;
1506 }
1507
1508 /* Redirect stdin, stdout, stderr to /dev/null */
1509 dup2(nullfd, STDIN_FILENO);
1510 dup2(nullfd, STDOUT_FILENO);
1511 dup2(nullfd, STDERR_FILENO);
1512 if (nullfd > STDERR_FILENO)
1513 close(nullfd);
1514
1515 return 0;
1516}
1517
1518int main(int argc, char *argv[])
1519{
1520 sigset_t sigset;
1521 int ch;
1522 int fd;
1523 int res;
1524 char *origmnt;
1525 char *mnt;
1526 static int unmount = 0;
1527 static int lazy = 0;
1528 static int quiet = 0;
1529 char *commfd = NULL;
1530 long cfd;
1531 const char *opts = "";
1532 const char *type = NULL;
1533 int setup_auto_unmount_only = 0;
1534
1535 static const struct option long_opts[] = {
1536 {"unmount", no_argument, NULL, 'u'},
1537 {"lazy", no_argument, NULL, 'z'},
1538 {"quiet", no_argument, NULL, 'q'},
1539 {"help", no_argument, NULL, 'h'},
1540 {"version", no_argument, NULL, 'V'},
1541 {"options", required_argument, NULL, 'o'},
1542 // Note: auto-unmount and comm-fd don't have short versions.
1543 // They'ne meant for internal use by mount.c
1544 {"auto-unmount", no_argument, NULL, 'U'},
1545 {"comm-fd", required_argument, NULL, 'c'},
1546 {0, 0, 0, 0}};
1547
1548 progname = strdup(argc > 0 ? argv[0] : "fusermount");
1549 if (progname == NULL) {
1550 fprintf(stderr, "%s: failed to allocate memory\n", argv[0]);
1551 exit(1);
1552 }
1553
1554 while ((ch = getopt_long(argc, argv, "hVo:uzq", long_opts,
1555 NULL)) != -1) {
1556 switch (ch) {
1557 case 'h':
1558 usage();
1559 break;
1560
1561 case 'V':
1562 show_version();
1563 break;
1564
1565 case 'o':
1566 opts = optarg;
1567 break;
1568
1569 case 'u':
1570 unmount = 1;
1571 break;
1572 case 'U':
1573 unmount = 1;
1574 auto_unmount = 1;
1575 setup_auto_unmount_only = 1;
1576 break;
1577 case 'c':
1578 commfd = optarg;
1579 break;
1580 case 'z':
1581 lazy = 1;
1582 break;
1583
1584 case 'q':
1585 quiet = 1;
1586 break;
1587
1588 default:
1589 exit(1);
1590 }
1591 }
1592
1593 if (lazy && !unmount) {
1594 fprintf(stderr, "%s: -z can only be used with -u\n", progname);
1595 exit(1);
1596 }
1597
1598 if (optind >= argc) {
1599 fprintf(stderr, "%s: missing mountpoint argument\n", progname);
1600 exit(1);
1601 } else if (argc > optind + 1) {
1602 fprintf(stderr, "%s: extra arguments after the mountpoint\n",
1603 progname);
1604 exit(1);
1605 }
1606
1607 origmnt = argv[optind];
1608
1609 drop_privs();
1610 mnt = fuse_mnt_resolve_path(progname, origmnt);
1611 if (mnt != NULL) {
1612 res = chdir("/");
1613 if (res == -1) {
1614 fprintf(stderr, "%s: failed to chdir to '/'\n", progname);
1615 goto err_out;
1616 }
1617 }
1618 restore_privs();
1619 if (mnt == NULL)
1620 exit(1);
1621
1622 umask(033);
1623 if (!setup_auto_unmount_only && unmount)
1624 goto do_unmount;
1625
1626 if(commfd == NULL)
1627 commfd = getenv(FUSE_COMMFD_ENV);
1628 if (commfd == NULL) {
1629 fprintf(stderr, "%s: old style mounting not supported\n",
1630 progname);
1631 goto err_out;
1632 }
1633
1634 res = libfuse_strtol(commfd, &cfd);
1635 if (res) {
1636 fprintf(stderr,
1637 "%s: invalid _FUSE_COMMFD: %s\n",
1638 progname, commfd);
1639 goto err_out;
1640
1641 }
1642
1643 {
1644 struct stat statbuf;
1645 fstat(cfd, &statbuf);
1646 if(!S_ISSOCK(statbuf.st_mode)) {
1647 fprintf(stderr,
1648 "%s: file descriptor %li is not a socket, can't send fuse fd\n",
1649 progname, cfd);
1650 goto err_out;
1651 }
1652 }
1653
1654 if (setup_auto_unmount_only)
1655 goto wait_for_auto_unmount;
1656
1657 fd = mount_fuse(mnt, opts, &type);
1658 if (fd == -1)
1659 goto err_out;
1660
1661 res = send_fd(cfd, fd);
1662 if (res != 0) {
1663 umount2(mnt, MNT_DETACH); /* lazy umount */
1664 goto err_out;
1665 }
1666 close(fd);
1667
1668 if (!auto_unmount) {
1669 free(mnt);
1670 free((void*) type);
1671 return 0;
1672 }
1673
1674wait_for_auto_unmount:
1675 /* Become a daemon and wait for the parent to exit or die.
1676 ie For the control socket to get closed.
1677 Btw, we don't want to use daemon() function here because
1678 it forks and messes with the file descriptors. */
1679
1680 res = close_inherited_fds(cfd);
1681 if (res < 0)
1682 exit(EXIT_FAILURE);
1683
1684 setsid();
1685 res = chdir("/");
1686 if (res == -1) {
1687 fprintf(stderr, "%s: failed to chdir to '/'\n", progname);
1688 goto err_out;
1689 }
1690
1691 sigfillset(&sigset);
1692 sigprocmask(SIG_BLOCK, &sigset, NULL);
1693
1694 lazy = 1;
1695 quiet = 1;
1696
1697 while (1) {
1698 unsigned char buf[16];
1699 int n = recv(cfd, buf, sizeof(buf), 0);
1700 if (!n)
1701 break;
1702
1703 if (n < 0) {
1704 if (errno == EINTR)
1705 continue;
1706 break;
1707 }
1708 }
1709
1710 if (!should_auto_unmount(mnt, type)) {
1711 goto success_out;
1712 }
1713
1714do_unmount:
1715 if (geteuid() == 0)
1716 res = unmount_fuse(mnt, quiet, lazy);
1717 else {
1718 res = umount2(mnt, lazy ? UMOUNT_DETACH : 0);
1719 if (res == -1 && !quiet)
1720 fprintf(stderr,
1721 "%s: failed to unmount %s: %s\n",
1722 progname, mnt, strerror(errno));
1723 }
1724 if (res == -1)
1725 goto err_out;
1726
1727success_out:
1728 free((void*) type);
1729 free(mnt);
1730 return 0;
1731
1732err_out:
1733 free((void*) type);
1734 free(mnt);
1735 exit(1);
1736}