• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
41 static const char *progname;
42 
43 static int user_allow_other = 0;
44 static int mount_max = 1000;
45 
46 static 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.
unescape(char * buf)53 static 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 
GETMNTENT(FILE * stream)85 static 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 
get_user_name(void)101 static 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 
112 static uid_t oldfsuid;
113 static gid_t oldfsgid;
114 
drop_privs(void)115 static void drop_privs(void)
116 {
117 	if (getuid() != 0) {
118 		oldfsuid = setfsuid(getuid());
119 		oldfsgid = setfsgid(getgid());
120 	}
121 }
122 
restore_privs(void)123 static 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  */
lock_umount(void)135 static 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 
unlock_umount(int mtablock)163 static 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 
add_mount(const char * source,const char * mnt,const char * type,const char * opts)177 static 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 
may_unmount(const char * mnt,int quiet)183 static 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  */
check_is_mount_child(void * p)271 static 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 
clone_newns(void * a)340 static 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 
check_is_mount(const char * last,const char * mnt,const char * type)358 static 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 
chdir_to_parent(char * copy,const char ** lastp)387 static 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
unmount_fuse_locked(const char * mnt,int quiet,int lazy)435 static 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 
466 out:
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 
unmount_fuse(const char * mnt,int quiet,int lazy)480 static 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 
count_fuse_fs(void)491 static 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 */
count_fuse_fs(void)513 static int count_fuse_fs(void)
514 {
515 	return 0;
516 }
517 
add_mount(const char * source,const char * mnt,const char * type,const char * opts)518 static 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 
unmount_fuse(const char * mnt,int quiet,int lazy)528 static 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 
strip_line(char * line)535 static 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 
parse_line(char * line,int linenum)548 static 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 
read_conf(void)561 static 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 
begins_with(const char * s,const char * beg)604 static 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 
612 struct mount_flags {
613 	const char *opt;
614 	unsigned long flag;
615 	int on;
616 	int safe;
617 };
618 
619 static 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 
find_mount_flag(const char * s,unsigned len,int * on,int * flag)644 static 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 
add_option(char ** optsp,const char * opt,unsigned expand)665 static 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 
get_mnt_opts(int flags,char * opts,char ** mnt_optsp)685 static 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 
opt_eq(const char * s,unsigned len,const char * opt)717 static 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 
get_string_opt(const char * s,unsigned len,const char * opt,char ** val)725 static 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  */
mount_notrunc(const char * source,const char * target,const char * filesystemtype,unsigned long mountflags,const char * data)757 static 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 
do_mount(const char * mnt,const char ** typep,mode_t rootmode,int fd,const char * opts,const char * dev,char ** sourcep,char ** mnt_optsp)769 static 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 
932 err:
933 	free(fsname);
934 	free(subtype);
935 	free(source);
936 	free(type);
937 	free(mnt_opts);
938 	free(optbuf);
939 	return -1;
940 }
941 
check_perm(const char ** mntp,struct stat * stbuf,int * mountpoint_fd)942 static 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 		0x5346414f /* OPENAFS_SUPER_MAGIC */,
1062 		0x794C7630 /* OVERLAYFS_SUPER_MAGIC */,
1063 		0x52654973 /* REISERFS_SUPER_MAGIC */,
1064 		0xFE534D42 /* SMB2_SUPER_MAGIC */,
1065 		0x73717368 /* SQUASHFS_MAGIC */,
1066 		0x01021994 /* TMPFS_MAGIC */,
1067 		0x24051905 /* UBIFS_SUPER_MAGIC */,
1068 		0x736675005346544e /* UFSD */,
1069 		0x58465342 /* XFS_SB_MAGIC */,
1070 		0x2FC12FC1 /* ZFS_SUPER_MAGIC */,
1071 		0xFEF52024 /* HMFS_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 
try_open(const char * dev,char ** devp,int silent)1083 static 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 
try_open_fuse_device(char ** devp)1104 static 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 
open_fuse_device(char ** devp)1114 static 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 
mount_fuse(const char * mnt,const char * opts,const char ** type)1128 static 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 
1180 out_free:
1181 	free(source);
1182 	free(mnt_opts);
1183 	free(dev);
1184 
1185 	return fd;
1186 
1187 fail_close_fd:
1188 	close(fd);
1189 	fd = -1;
1190 	goto out_free;
1191 }
1192 
send_fd(int sock_fd,int fd)1193 static 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  */
recheck_ENOTCONN_as_owner(const char * mnt)1235 static 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  */
should_auto_unmount(const char * mnt,const char * type)1285 static 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 	}
1320 out:
1321 	free(copy);
1322 	return result;
1323 }
1324 
usage(void)1325 static 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 
show_version(void)1339 static void show_version(void)
1340 {
1341 	printf("fusermount3 version: %s\n", PACKAGE_VERSION);
1342 	exit(0);
1343 }
1344 
main(int argc,char * argv[])1345 int 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 
1488 wait_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 
1523 do_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 
1536 success_out:
1537 	free((void*) type);
1538 	free(mnt);
1539 	return 0;
1540 
1541 err_out:
1542 	free((void*) type);
1543 	free(mnt);
1544 	exit(1);
1545 }
1546