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