libfuse: fuse-3.17.1-rc0/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 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 0x736675005346544e /* UFSD */,
1145 0x58465342 /* XFS_SB_MAGIC */,
1146 0x2FC12FC1 /* ZFS_SUPER_MAGIC */,
1147 0x858458f6 /* RAMFS_MAGIC */,
1148 };
1149 for (i = 0; i < sizeof(f_type_whitelist)/sizeof(f_type_whitelist[0]); i++) {
1150 if (f_type_whitelist[i] == fs_buf.f_type)
1151 return 0;
1152 }
1153
1154 fprintf(stderr, "%s: mounting over filesystem type %#010lx is forbidden\n",
1155 progname, (unsigned long)fs_buf.f_type);
1156 return -1;
1157}
1158
1159static int try_open(const char *dev, char **devp, int silent)
1160{
1161 int fd = open(dev, O_RDWR);
1162 if (fd != -1) {
1163 *devp = strdup(dev);
1164 if (*devp == NULL) {
1165 fprintf(stderr, "%s: failed to allocate memory\n",
1166 progname);
1167 close(fd);
1168 fd = -1;
1169 }
1170 } else if (errno == ENODEV ||
1171 errno == ENOENT)/* check for ENOENT too, for the udev case */
1172 return -2;
1173 else if (!silent) {
1174 fprintf(stderr, "%s: failed to open %s: %s\n", progname, dev,
1175 strerror(errno));
1176 }
1177 return fd;
1178}
1179
1180static int try_open_fuse_device(char **devp)
1181{
1182 int fd;
1183
1184 drop_privs();
1185 fd = try_open(FUSE_DEV, devp, 0);
1186 restore_privs();
1187 return fd;
1188}
1189
1190static int open_fuse_device(char **devp)
1191{
1192 int fd = try_open_fuse_device(devp);
1193 if (fd >= -1)
1194 return fd;
1195
1196 fprintf(stderr,
1197 "%s: fuse device not found, try 'modprobe fuse' first\n",
1198 progname);
1199
1200 return -1;
1201}
1202
1203
1204static int mount_fuse(const char *mnt, const char *opts, const char **type)
1205{
1206 int res;
1207 int fd;
1208 char *dev;
1209 struct stat stbuf;
1210 char *source = NULL;
1211 char *mnt_opts = NULL;
1212 const char *real_mnt = mnt;
1213 int mountpoint_fd = -1;
1214 char *do_mount_opts = NULL;
1215 char *x_opts = NULL;
1216
1217 fd = open_fuse_device(&dev);
1218 if (fd == -1)
1219 return -1;
1220
1221 drop_privs();
1222 read_conf();
1223
1224 if (getuid() != 0 && mount_max != -1) {
1225 int mount_count = count_fuse_fs();
1226 if (mount_count >= mount_max) {
1227 fprintf(stderr, "%s: too many FUSE filesystems mounted; mount_max=N can be set in %s\n", progname, FUSE_CONF);
1228 goto fail_close_fd;
1229 }
1230 }
1231
1232 // Extract any options starting with "x-"
1233 res= extract_x_options(opts, &do_mount_opts, &x_opts);
1234 if (res)
1235 goto fail_close_fd;
1236
1237 res = check_perm(&real_mnt, &stbuf, &mountpoint_fd);
1238 restore_privs();
1239 if (res != -1)
1240 res = do_mount(real_mnt, type, stbuf.st_mode & S_IFMT,
1241 fd, do_mount_opts, dev, &source, &mnt_opts);
1242
1243 if (mountpoint_fd != -1)
1244 close(mountpoint_fd);
1245
1246 if (res == -1)
1247 goto fail_close_fd;
1248
1249 res = chdir("/");
1250 if (res == -1) {
1251 fprintf(stderr, "%s: failed to chdir to '/'\n", progname);
1252 goto fail_close_fd;
1253 }
1254
1255 if (geteuid() == 0) {
1256 if (x_opts && strlen(x_opts) > 0) {
1257 /*
1258 * Add back the options starting with "x-" to opts from
1259 * do_mount. +2 for ',' and '\0'
1260 */
1261 size_t mnt_opts_len = strlen(mnt_opts);
1262 size_t x_mnt_opts_len = mnt_opts_len+
1263 strlen(x_opts) + 2;
1264 char *x_mnt_opts = calloc(1, x_mnt_opts_len);
1265
1266 if (mnt_opts_len) {
1267 strcpy(x_mnt_opts, mnt_opts);
1268 strncat(x_mnt_opts, ",", 2);
1269 }
1270
1271 strncat(x_mnt_opts, x_opts,
1272 x_mnt_opts_len - mnt_opts_len - 2);
1273
1274 free(mnt_opts);
1275 mnt_opts = x_mnt_opts;
1276 }
1277
1278 res = add_mount(source, mnt, *type, mnt_opts);
1279 if (res == -1) {
1280 /* Can't clean up mount in a non-racy way */
1281 goto fail_close_fd;
1282 }
1283 }
1284
1285out_free:
1286 free(source);
1287 free(mnt_opts);
1288 free(dev);
1289 free(x_opts);
1290 free(do_mount_opts);
1291
1292 return fd;
1293
1294fail_close_fd:
1295 close(fd);
1296 fd = -1;
1297 goto out_free;
1298}
1299
1300static int send_fd(int sock_fd, int fd)
1301{
1302 int retval;
1303 struct msghdr msg;
1304 struct cmsghdr *p_cmsg;
1305 struct iovec vec;
1306 size_t cmsgbuf[CMSG_SPACE(sizeof(fd)) / sizeof(size_t)];
1307 int *p_fds;
1308 char sendchar = 0;
1309
1310 msg.msg_control = cmsgbuf;
1311 msg.msg_controllen = sizeof(cmsgbuf);
1312 p_cmsg = CMSG_FIRSTHDR(&msg);
1313 p_cmsg->cmsg_level = SOL_SOCKET;
1314 p_cmsg->cmsg_type = SCM_RIGHTS;
1315 p_cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
1316 p_fds = (int *) CMSG_DATA(p_cmsg);
1317 *p_fds = fd;
1318 msg.msg_controllen = p_cmsg->cmsg_len;
1319 msg.msg_name = NULL;
1320 msg.msg_namelen = 0;
1321 msg.msg_iov = &vec;
1322 msg.msg_iovlen = 1;
1323 msg.msg_flags = 0;
1324 /* "To pass file descriptors or credentials you need to send/read at
1325 * least one byte" (man 7 unix) */
1326 vec.iov_base = &sendchar;
1327 vec.iov_len = sizeof(sendchar);
1328 while ((retval = sendmsg(sock_fd, &msg, 0)) == -1 && errno == EINTR);
1329 if (retval != 1) {
1330 perror("sending file descriptor");
1331 return -1;
1332 }
1333 return 0;
1334}
1335
1336/* Helper for should_auto_unmount
1337 *
1338 * fusermount typically has the s-bit set - initial open of `mnt` was as root
1339 * and got EACCESS as 'allow_other' was not specified.
1340 * Try opening `mnt` again with uid and guid of the calling process.
1341 */
1342static int recheck_ENOTCONN_as_owner(const char *mnt)
1343{
1344 int pid = fork();
1345 if(pid == -1) {
1346 perror("fuse: recheck_ENOTCONN_as_owner can't fork");
1347 _exit(EXIT_FAILURE);
1348 } else if(pid == 0) {
1349 uid_t uid = getuid();
1350 gid_t gid = getgid();
1351 if(setresgid(gid, gid, gid) == -1) {
1352 perror("fuse: can't set resgid");
1353 _exit(EXIT_FAILURE);
1354 }
1355 if(setresuid(uid, uid, uid) == -1) {
1356 perror("fuse: can't set resuid");
1357 _exit(EXIT_FAILURE);
1358 }
1359
1360 int fd = open(mnt, O_RDONLY);
1361 if(fd == -1 && errno == ENOTCONN)
1362 _exit(EXIT_SUCCESS);
1363 else
1364 _exit(EXIT_FAILURE);
1365 } else {
1366 int status;
1367 int res = waitpid(pid, &status, 0);
1368 if (res == -1) {
1369 perror("fuse: waiting for child failed");
1370 _exit(EXIT_FAILURE);
1371 }
1372 return WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS;
1373 }
1374}
1375
1376/* The parent fuse process has died: decide whether to auto_unmount.
1377 *
1378 * In the normal case (umount or fusermount -u), the filesystem
1379 * has already been unmounted. If we simply unmount again we can
1380 * cause problems with stacked mounts (e.g. autofs).
1381 *
1382 * So we unmount here only in abnormal case where fuse process has
1383 * died without unmount happening. To detect this, we first look in
1384 * the mount table to make sure the mountpoint is still mounted and
1385 * has proper type. If so, we then see if opening the mount dir is
1386 * returning 'Transport endpoint is not connected'.
1387 *
1388 * The order of these is important, because if autofs is in use,
1389 * opening the dir to check for ENOTCONN will cause a new mount
1390 * in the normal case where filesystem has been unmounted cleanly.
1391 */
1392static int should_auto_unmount(const char *mnt, const char *type)
1393{
1394 char *copy;
1395 const char *last;
1396 int result = 0;
1397 int fd;
1398
1399 copy = strdup(mnt);
1400 if (copy == NULL) {
1401 fprintf(stderr, "%s: failed to allocate memory\n", progname);
1402 return 0;
1403 }
1404
1405 if (chdir_to_parent(copy, &last) == -1)
1406 goto out;
1407 if (check_is_mount(last, mnt, type) == -1)
1408 goto out;
1409
1410 fd = open(mnt, O_RDONLY);
1411
1412 if (fd != -1) {
1413 close(fd);
1414 } else {
1415 switch(errno) {
1416 case ENOTCONN:
1417 result = 1;
1418 break;
1419 case EACCES:
1420 result = recheck_ENOTCONN_as_owner(mnt);
1421 break;
1422 default:
1423 result = 0;
1424 break;
1425 }
1426 }
1427out:
1428 free(copy);
1429 return result;
1430}
1431
1432static void usage(void)
1433{
1434 printf("%s: [options] mountpoint\n"
1435 "Options:\n"
1436 " -h print help\n"
1437 " -V print version\n"
1438 " -o opt[,opt...] mount options\n"
1439 " -u unmount\n"
1440 " -q quiet\n"
1441 " -z lazy unmount\n",
1442 progname);
1443 exit(1);
1444}
1445
1446static void show_version(void)
1447{
1448 printf("fusermount3 version: %s\n", PACKAGE_VERSION);
1449 exit(0);
1450}
1451
1452/*
1453 * Close all inherited fds that are not needed
1454 * Ideally these wouldn't come up at all, applications should better
1455 * use FD_CLOEXEC / O_CLOEXEC
1456 */
1457static void close_inherited_fds(int cfd)
1458{
1459 int max_fd = sysconf(_SC_OPEN_MAX);
1460 int rc;
1461
1462#ifdef CLOSE_RANGE_CLOEXEC
1463 /* high range first to be able to log errors through stdout/err*/
1464 rc = close_range(cfd + 1, ~0U, 0);
1465 if (rc < 0) {
1466 fprintf(stderr, "Failed to close high range of FDs: %s",
1467 strerror(errno));
1468 goto fallback;
1469 }
1470
1471 rc = close_range(0, cfd - 1, 0);
1472 if (rc < 0) {
1473 fprintf(stderr, "Failed to close low range of FDs: %s",
1474 strerror(errno));
1475 goto fallback;
1476 }
1477#endif
1478
1479fallback:
1480 /*
1481 * This also needs to close stdout/stderr, as the application
1482 * using libfuse might have closed these FDs and might be using
1483 * it. Although issue is now that logging errors won't be possible
1484 * after that.
1485 */
1486 for (int fd = 0; fd <= max_fd; fd++) {
1487 if (fd != cfd)
1488 close(fd);
1489 }
1490}
1491
1492int main(int argc, char *argv[])
1493{
1494 sigset_t sigset;
1495 int ch;
1496 int fd;
1497 int res;
1498 char *origmnt;
1499 char *mnt;
1500 static int unmount = 0;
1501 static int lazy = 0;
1502 static int quiet = 0;
1503 char *commfd = NULL;
1504 long cfd;
1505 const char *opts = "";
1506 const char *type = NULL;
1507 int setup_auto_unmount_only = 0;
1508
1509 static const struct option long_opts[] = {
1510 {"unmount", no_argument, NULL, 'u'},
1511 {"lazy", no_argument, NULL, 'z'},
1512 {"quiet", no_argument, NULL, 'q'},
1513 {"help", no_argument, NULL, 'h'},
1514 {"version", no_argument, NULL, 'V'},
1515 {"options", required_argument, NULL, 'o'},
1516 // Note: auto-unmount and comm-fd don't have short versions.
1517 // They'ne meant for internal use by mount.c
1518 {"auto-unmount", no_argument, NULL, 'U'},
1519 {"comm-fd", required_argument, NULL, 'c'},
1520 {0, 0, 0, 0}};
1521
1522 progname = strdup(argc > 0 ? argv[0] : "fusermount");
1523 if (progname == NULL) {
1524 fprintf(stderr, "%s: failed to allocate memory\n", argv[0]);
1525 exit(1);
1526 }
1527
1528 while ((ch = getopt_long(argc, argv, "hVo:uzq", long_opts,
1529 NULL)) != -1) {
1530 switch (ch) {
1531 case 'h':
1532 usage();
1533 break;
1534
1535 case 'V':
1536 show_version();
1537 break;
1538
1539 case 'o':
1540 opts = optarg;
1541 break;
1542
1543 case 'u':
1544 unmount = 1;
1545 break;
1546 case 'U':
1547 unmount = 1;
1548 auto_unmount = 1;
1549 setup_auto_unmount_only = 1;
1550 break;
1551 case 'c':
1552 commfd = optarg;
1553 break;
1554 case 'z':
1555 lazy = 1;
1556 break;
1557
1558 case 'q':
1559 quiet = 1;
1560 break;
1561
1562 default:
1563 exit(1);
1564 }
1565 }
1566
1567 if (lazy && !unmount) {
1568 fprintf(stderr, "%s: -z can only be used with -u\n", progname);
1569 exit(1);
1570 }
1571
1572 if (optind >= argc) {
1573 fprintf(stderr, "%s: missing mountpoint argument\n", progname);
1574 exit(1);
1575 } else if (argc > optind + 1) {
1576 fprintf(stderr, "%s: extra arguments after the mountpoint\n",
1577 progname);
1578 exit(1);
1579 }
1580
1581 origmnt = argv[optind];
1582
1583 drop_privs();
1584 mnt = fuse_mnt_resolve_path(progname, origmnt);
1585 if (mnt != NULL) {
1586 res = chdir("/");
1587 if (res == -1) {
1588 fprintf(stderr, "%s: failed to chdir to '/'\n", progname);
1589 goto err_out;
1590 }
1591 }
1592 restore_privs();
1593 if (mnt == NULL)
1594 exit(1);
1595
1596 umask(033);
1597 if (!setup_auto_unmount_only && unmount)
1598 goto do_unmount;
1599
1600 if(commfd == NULL)
1601 commfd = getenv(FUSE_COMMFD_ENV);
1602 if (commfd == NULL) {
1603 fprintf(stderr, "%s: old style mounting not supported\n",
1604 progname);
1605 goto err_out;
1606 }
1607
1608 res = libfuse_strtol(commfd, &cfd);
1609 if (res) {
1610 fprintf(stderr,
1611 "%s: invalid _FUSE_COMMFD: %s\n",
1612 progname, commfd);
1613 goto err_out;
1614
1615 }
1616 {
1617 struct stat statbuf;
1618 fstat(cfd, &statbuf);
1619 if(!S_ISSOCK(statbuf.st_mode)) {
1620 fprintf(stderr,
1621 "%s: file descriptor %li is not a socket, can't send fuse fd\n",
1622 progname, cfd);
1623 goto err_out;
1624 }
1625 }
1626
1627 if (setup_auto_unmount_only)
1628 goto wait_for_auto_unmount;
1629
1630 fd = mount_fuse(mnt, opts, &type);
1631 if (fd == -1)
1632 goto err_out;
1633
1634 res = send_fd(cfd, fd);
1635 if (res != 0) {
1636 umount2(mnt, MNT_DETACH); /* lazy umount */
1637 goto err_out;
1638 }
1639 close(fd);
1640
1641 if (!auto_unmount) {
1642 free(mnt);
1643 free((void*) type);
1644 return 0;
1645 }
1646
1647wait_for_auto_unmount:
1648 /* Become a daemon and wait for the parent to exit or die.
1649 ie For the control socket to get closed.
1650 Btw, we don't want to use daemon() function here because
1651 it forks and messes with the file descriptors. */
1652
1653 close_inherited_fds(cfd);
1654
1655 setsid();
1656 res = chdir("/");
1657 if (res == -1) {
1658 fprintf(stderr, "%s: failed to chdir to '/'\n", progname);
1659 goto err_out;
1660 }
1661
1662 sigfillset(&sigset);
1663 sigprocmask(SIG_BLOCK, &sigset, NULL);
1664
1665 lazy = 1;
1666 quiet = 1;
1667
1668 while (1) {
1669 unsigned char buf[16];
1670 int n = recv(cfd, buf, sizeof(buf), 0);
1671 if (!n)
1672 break;
1673
1674 if (n < 0) {
1675 if (errno == EINTR)
1676 continue;
1677 break;
1678 }
1679 }
1680
1681 if (!should_auto_unmount(mnt, type)) {
1682 goto success_out;
1683 }
1684
1685do_unmount:
1686 if (geteuid() == 0)
1687 res = unmount_fuse(mnt, quiet, lazy);
1688 else {
1689 res = umount2(mnt, lazy ? UMOUNT_DETACH : 0);
1690 if (res == -1 && !quiet)
1691 fprintf(stderr,
1692 "%s: failed to unmount %s: %s\n",
1693 progname, mnt, strerror(errno));
1694 }
1695 if (res == -1)
1696 goto err_out;
1697
1698success_out:
1699 free((void*) type);
1700 free(mnt);
1701 return 0;
1702
1703err_out:
1704 free((void*) type);
1705 free(mnt);
1706 exit(1);
1707}