• 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 	};
1072 	for (i = 0; i < sizeof(f_type_whitelist)/sizeof(f_type_whitelist[0]); i++) {
1073 		if (f_type_whitelist[i] == fs_buf.f_type)
1074 			return 0;
1075 	}
1076 
1077 	fprintf(stderr, "%s: mounting over filesystem type %#010lx is forbidden\n",
1078 		progname, (unsigned long)fs_buf.f_type);
1079 	return -1;
1080 }
1081 
try_open(const char * dev,char ** devp,int silent)1082 static int try_open(const char *dev, char **devp, int silent)
1083 {
1084 	int fd = open(dev, O_RDWR);
1085 	if (fd != -1) {
1086 		*devp = strdup(dev);
1087 		if (*devp == NULL) {
1088 			fprintf(stderr, "%s: failed to allocate memory\n",
1089 				progname);
1090 			close(fd);
1091 			fd = -1;
1092 		}
1093 	} else if (errno == ENODEV ||
1094 		   errno == ENOENT)/* check for ENOENT too, for the udev case */
1095 		return -2;
1096 	else if (!silent) {
1097 		fprintf(stderr, "%s: failed to open %s: %s\n", progname, dev,
1098 			strerror(errno));
1099 	}
1100 	return fd;
1101 }
1102 
try_open_fuse_device(char ** devp)1103 static int try_open_fuse_device(char **devp)
1104 {
1105 	int fd;
1106 
1107 	drop_privs();
1108 	fd = try_open(FUSE_DEV, devp, 0);
1109 	restore_privs();
1110 	return fd;
1111 }
1112 
open_fuse_device(char ** devp)1113 static int open_fuse_device(char **devp)
1114 {
1115 	int fd = try_open_fuse_device(devp);
1116 	if (fd >= -1)
1117 		return fd;
1118 
1119 	fprintf(stderr,
1120 		"%s: fuse device not found, try 'modprobe fuse' first\n",
1121 		progname);
1122 
1123 	return -1;
1124 }
1125 
1126 
mount_fuse(const char * mnt,const char * opts,const char ** type)1127 static int mount_fuse(const char *mnt, const char *opts, const char **type)
1128 {
1129 	int res;
1130 	int fd;
1131 	char *dev;
1132 	struct stat stbuf;
1133 	char *source = NULL;
1134 	char *mnt_opts = NULL;
1135 	const char *real_mnt = mnt;
1136 	int mountpoint_fd = -1;
1137 
1138 	fd = open_fuse_device(&dev);
1139 	if (fd == -1)
1140 		return -1;
1141 
1142 	drop_privs();
1143 	read_conf();
1144 
1145 	if (getuid() != 0 && mount_max != -1) {
1146 		int mount_count = count_fuse_fs();
1147 		if (mount_count >= mount_max) {
1148 			fprintf(stderr, "%s: too many FUSE filesystems mounted; mount_max=N can be set in %s\n", progname, FUSE_CONF);
1149 			goto fail_close_fd;
1150 		}
1151 	}
1152 
1153 	res = check_perm(&real_mnt, &stbuf, &mountpoint_fd);
1154 	restore_privs();
1155 	if (res != -1)
1156 		res = do_mount(real_mnt, type, stbuf.st_mode & S_IFMT,
1157 			       fd, opts, dev, &source, &mnt_opts);
1158 
1159 	if (mountpoint_fd != -1)
1160 		close(mountpoint_fd);
1161 
1162 	if (res == -1)
1163 		goto fail_close_fd;
1164 
1165 	res = chdir("/");
1166 	if (res == -1) {
1167 		fprintf(stderr, "%s: failed to chdir to '/'\n", progname);
1168 		goto fail_close_fd;
1169 	}
1170 
1171 	if (geteuid() == 0) {
1172 		res = add_mount(source, mnt, *type, mnt_opts);
1173 		if (res == -1) {
1174 			/* Can't clean up mount in a non-racy way */
1175 			goto fail_close_fd;
1176 		}
1177 	}
1178 
1179 out_free:
1180 	free(source);
1181 	free(mnt_opts);
1182 	free(dev);
1183 
1184 	return fd;
1185 
1186 fail_close_fd:
1187 	close(fd);
1188 	fd = -1;
1189 	goto out_free;
1190 }
1191 
send_fd(int sock_fd,int fd)1192 static int send_fd(int sock_fd, int fd)
1193 {
1194 	int retval;
1195 	struct msghdr msg;
1196 	struct cmsghdr *p_cmsg;
1197 	struct iovec vec;
1198 	size_t cmsgbuf[CMSG_SPACE(sizeof(fd)) / sizeof(size_t)];
1199 	int *p_fds;
1200 	char sendchar = 0;
1201 
1202 	msg.msg_control = cmsgbuf;
1203 	msg.msg_controllen = sizeof(cmsgbuf);
1204 	p_cmsg = CMSG_FIRSTHDR(&msg);
1205 	p_cmsg->cmsg_level = SOL_SOCKET;
1206 	p_cmsg->cmsg_type = SCM_RIGHTS;
1207 	p_cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
1208 	p_fds = (int *) CMSG_DATA(p_cmsg);
1209 	*p_fds = fd;
1210 	msg.msg_controllen = p_cmsg->cmsg_len;
1211 	msg.msg_name = NULL;
1212 	msg.msg_namelen = 0;
1213 	msg.msg_iov = &vec;
1214 	msg.msg_iovlen = 1;
1215 	msg.msg_flags = 0;
1216 	/* "To pass file descriptors or credentials you need to send/read at
1217 	 * least one byte" (man 7 unix) */
1218 	vec.iov_base = &sendchar;
1219 	vec.iov_len = sizeof(sendchar);
1220 	while ((retval = sendmsg(sock_fd, &msg, 0)) == -1 && errno == EINTR);
1221 	if (retval != 1) {
1222 		perror("sending file descriptor");
1223 		return -1;
1224 	}
1225 	return 0;
1226 }
1227 
1228 /* Helper for should_auto_unmount
1229  *
1230  * fusermount typically has the s-bit set - initial open of `mnt` was as root
1231  * and got EACCESS as 'allow_other' was not specified.
1232  * Try opening `mnt` again with uid and guid of the calling process.
1233  */
recheck_ENOTCONN_as_owner(const char * mnt)1234 static int recheck_ENOTCONN_as_owner(const char *mnt)
1235 {
1236 	int pid = fork();
1237 	if(pid == -1) {
1238 		perror("fuse: recheck_ENOTCONN_as_owner can't fork");
1239 		_exit(EXIT_FAILURE);
1240 	} else if(pid == 0) {
1241 		uid_t uid = getuid();
1242 		gid_t gid = getgid();
1243 		if(setresgid(gid, gid, gid) == -1) {
1244 			perror("fuse: can't set resgid");
1245 			_exit(EXIT_FAILURE);
1246 		}
1247 		if(setresuid(uid, uid, uid) == -1) {
1248 			perror("fuse: can't set resuid");
1249 			_exit(EXIT_FAILURE);
1250 		}
1251 
1252 		int fd = open(mnt, O_RDONLY);
1253 		if(fd == -1 && errno == ENOTCONN)
1254 			_exit(EXIT_SUCCESS);
1255 		else
1256 			_exit(EXIT_FAILURE);
1257 	} else {
1258 		int status;
1259 		int res = waitpid(pid, &status, 0);
1260 		if (res == -1) {
1261 			perror("fuse: waiting for child failed");
1262 			_exit(EXIT_FAILURE);
1263 		}
1264 		return WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS;
1265 	}
1266 }
1267 
1268 /* The parent fuse process has died: decide whether to auto_unmount.
1269  *
1270  * In the normal case (umount or fusermount -u), the filesystem
1271  * has already been unmounted. If we simply unmount again we can
1272  * cause problems with stacked mounts (e.g. autofs).
1273  *
1274  * So we unmount here only in abnormal case where fuse process has
1275  * died without unmount happening. To detect this, we first look in
1276  * the mount table to make sure the mountpoint is still mounted and
1277  * has proper type. If so, we then see if opening the mount dir is
1278  * returning 'Transport endpoint is not connected'.
1279  *
1280  * The order of these is important, because if autofs is in use,
1281  * opening the dir to check for ENOTCONN will cause a new mount
1282  * in the normal case where filesystem has been unmounted cleanly.
1283  */
should_auto_unmount(const char * mnt,const char * type)1284 static int should_auto_unmount(const char *mnt, const char *type)
1285 {
1286 	char *copy;
1287 	const char *last;
1288 	int result = 0;
1289 	int fd;
1290 
1291 	copy = strdup(mnt);
1292 	if (copy == NULL) {
1293 	fprintf(stderr, "%s: failed to allocate memory\n", progname);
1294 		return 0;
1295 	}
1296 
1297 	if (chdir_to_parent(copy, &last) == -1)
1298 		goto out;
1299 	if (check_is_mount(last, mnt, type) == -1)
1300 		goto out;
1301 
1302 	fd = open(mnt, O_RDONLY);
1303 
1304 	if (fd != -1) {
1305 		close(fd);
1306 	} else {
1307 		switch(errno) {
1308 		case ENOTCONN:
1309 			result = 1;
1310 			break;
1311 		case EACCES:
1312 			result = recheck_ENOTCONN_as_owner(mnt);
1313 			break;
1314 		default:
1315 			result = 0;
1316 			break;
1317 		}
1318 	}
1319 out:
1320 	free(copy);
1321 	return result;
1322 }
1323 
usage(void)1324 static void usage(void)
1325 {
1326 	printf("%s: [options] mountpoint\n"
1327 	       "Options:\n"
1328 	       " -h		    print help\n"
1329 	       " -V		    print version\n"
1330 	       " -o opt[,opt...]    mount options\n"
1331 	       " -u		    unmount\n"
1332 	       " -q		    quiet\n"
1333 	       " -z		    lazy unmount\n",
1334 	       progname);
1335 	exit(1);
1336 }
1337 
show_version(void)1338 static void show_version(void)
1339 {
1340 	printf("fusermount3 version: %s\n", PACKAGE_VERSION);
1341 	exit(0);
1342 }
1343 
main(int argc,char * argv[])1344 int main(int argc, char *argv[])
1345 {
1346 	sigset_t sigset;
1347 	int ch;
1348 	int fd;
1349 	int res;
1350 	char *origmnt;
1351 	char *mnt;
1352 	static int unmount = 0;
1353 	static int lazy = 0;
1354 	static int quiet = 0;
1355 	char *commfd;
1356 	int cfd;
1357 	const char *opts = "";
1358 	const char *type = NULL;
1359 	int setup_auto_unmount_only = 0;
1360 
1361 	static const struct option long_opts[] = {
1362 		{"unmount", no_argument, NULL, 'u'},
1363 		// Note: auto-unmount deliberately does not have a short version.
1364 		// It's meant for internal use by mount.c's setup_auto_unmount.
1365 		{"auto-unmount", no_argument, NULL, 'U'},
1366 		{"lazy",    no_argument, NULL, 'z'},
1367 		{"quiet",   no_argument, NULL, 'q'},
1368 		{"help",    no_argument, NULL, 'h'},
1369 		{"version", no_argument, NULL, 'V'},
1370 		{"options", required_argument, NULL, 'o'},
1371 		{0, 0, 0, 0}};
1372 
1373 	progname = strdup(argc > 0 ? argv[0] : "fusermount");
1374 	if (progname == NULL) {
1375 		fprintf(stderr, "%s: failed to allocate memory\n", argv[0]);
1376 		exit(1);
1377 	}
1378 
1379 	while ((ch = getopt_long(argc, argv, "hVo:uzq", long_opts,
1380 				 NULL)) != -1) {
1381 		switch (ch) {
1382 		case 'h':
1383 			usage();
1384 			break;
1385 
1386 		case 'V':
1387 			show_version();
1388 			break;
1389 
1390 		case 'o':
1391 			opts = optarg;
1392 			break;
1393 
1394 		case 'u':
1395 			unmount = 1;
1396 			break;
1397 		case 'U':
1398 			unmount = 1;
1399 			auto_unmount = 1;
1400 			setup_auto_unmount_only = 1;
1401 			break;
1402 		case 'z':
1403 			lazy = 1;
1404 			break;
1405 
1406 		case 'q':
1407 			quiet = 1;
1408 			break;
1409 
1410 		default:
1411 			exit(1);
1412 		}
1413 	}
1414 
1415 	if (lazy && !unmount) {
1416 		fprintf(stderr, "%s: -z can only be used with -u\n", progname);
1417 		exit(1);
1418 	}
1419 
1420 	if (optind >= argc) {
1421 		fprintf(stderr, "%s: missing mountpoint argument\n", progname);
1422 		exit(1);
1423 	} else if (argc > optind + 1) {
1424 		fprintf(stderr, "%s: extra arguments after the mountpoint\n",
1425 			progname);
1426 		exit(1);
1427 	}
1428 
1429 	origmnt = argv[optind];
1430 
1431 	drop_privs();
1432 	mnt = fuse_mnt_resolve_path(progname, origmnt);
1433 	if (mnt != NULL) {
1434 		res = chdir("/");
1435 		if (res == -1) {
1436 			fprintf(stderr, "%s: failed to chdir to '/'\n", progname);
1437 			goto err_out;
1438 		}
1439 	}
1440 	restore_privs();
1441 	if (mnt == NULL)
1442 		exit(1);
1443 
1444 	umask(033);
1445 	if (!setup_auto_unmount_only && unmount)
1446 		goto do_unmount;
1447 
1448 	commfd = getenv(FUSE_COMMFD_ENV);
1449 	if (commfd == NULL) {
1450 		fprintf(stderr, "%s: old style mounting not supported\n",
1451 			progname);
1452 		goto err_out;
1453 	}
1454 
1455 	cfd = atoi(commfd);
1456 	{
1457 		struct stat statbuf;
1458 		fstat(cfd, &statbuf);
1459 		if(!S_ISSOCK(statbuf.st_mode)) {
1460 			fprintf(stderr,
1461 				"%s: file descriptor %i is not a socket, can't send fuse fd\n",
1462 				progname, cfd);
1463 			goto err_out;
1464 		}
1465 	}
1466 
1467 	if (setup_auto_unmount_only)
1468 		goto wait_for_auto_unmount;
1469 
1470 	fd = mount_fuse(mnt, opts, &type);
1471 	if (fd == -1)
1472 		goto err_out;
1473 
1474 	res = send_fd(cfd, fd);
1475 	if (res != 0) {
1476 		umount2(mnt, MNT_DETACH); /* lazy umount */
1477 		goto err_out;
1478 	}
1479 	close(fd);
1480 
1481 	if (!auto_unmount) {
1482 		free(mnt);
1483 		free((void*) type);
1484 		return 0;
1485 	}
1486 
1487 wait_for_auto_unmount:
1488 	/* Become a daemon and wait for the parent to exit or die.
1489 	   ie For the control socket to get closed.
1490 	   btw We don't want to use daemon() function here because
1491 	   it forks and messes with the file descriptors. */
1492 	setsid();
1493 	res = chdir("/");
1494 	if (res == -1) {
1495 		fprintf(stderr, "%s: failed to chdir to '/'\n", progname);
1496 		goto err_out;
1497 	}
1498 
1499 	sigfillset(&sigset);
1500 	sigprocmask(SIG_BLOCK, &sigset, NULL);
1501 
1502 	lazy  = 1;
1503 	quiet = 1;
1504 
1505 	while (1) {
1506 		unsigned char buf[16];
1507 		int n = recv(cfd, buf, sizeof(buf), 0);
1508 		if (!n)
1509 			break;
1510 
1511 		if (n < 0) {
1512 			if (errno == EINTR)
1513 				continue;
1514 			break;
1515 		}
1516 	}
1517 
1518 	if (!should_auto_unmount(mnt, type)) {
1519 		goto success_out;
1520 	}
1521 
1522 do_unmount:
1523 	if (geteuid() == 0)
1524 		res = unmount_fuse(mnt, quiet, lazy);
1525 	else {
1526 		res = umount2(mnt, lazy ? UMOUNT_DETACH : 0);
1527 		if (res == -1 && !quiet)
1528 			fprintf(stderr,
1529 				"%s: failed to unmount %s: %s\n",
1530 				progname, mnt, strerror(errno));
1531 	}
1532 	if (res == -1)
1533 		goto err_out;
1534 
1535 success_out:
1536 	free((void*) type);
1537 	free(mnt);
1538 	return 0;
1539 
1540 err_out:
1541 	free((void*) type);
1542 	free(mnt);
1543 	exit(1);
1544 }
1545