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