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