xref: /third_party/libfuse/lib/mount.c (revision 6881f68f)
1/*
2  FUSE: Filesystem in Userspace
3  Copyright (C) 2001-2007  Miklos Szeredi <miklos@szeredi.hu>
4
5  Architecture specific file system mounting (Linux).
6
7  This program can be distributed under the terms of the GNU LGPLv2.
8  See the file COPYING.LIB.
9*/
10
11#include "fuse_config.h"
12#include "fuse_i.h"
13#include "fuse_misc.h"
14#include "fuse_opt.h"
15#include "mount_util.h"
16
17#include <stdio.h>
18#include <stdlib.h>
19#include <unistd.h>
20#include <stddef.h>
21#include <string.h>
22#include <fcntl.h>
23#include <errno.h>
24#include <poll.h>
25#include <sys/socket.h>
26#include <sys/un.h>
27#include <sys/wait.h>
28
29#include "fuse_mount_compat.h"
30
31#ifdef __NetBSD__
32#include <perfuse.h>
33
34#define MS_RDONLY	MNT_RDONLY
35#define MS_NOSUID	MNT_NOSUID
36#define MS_NODEV	MNT_NODEV
37#define MS_NOEXEC	MNT_NOEXEC
38#define MS_SYNCHRONOUS	MNT_SYNCHRONOUS
39#define MS_NOATIME	MNT_NOATIME
40
41#define umount2(mnt, flags) unmount(mnt, (flags == 2) ? MNT_FORCE : 0)
42#endif
43
44#define FUSERMOUNT_PROG		"fusermount3"
45#define FUSE_COMMFD_ENV		"_FUSE_COMMFD"
46
47#ifndef HAVE_FORK
48#define fork() vfork()
49#endif
50
51#ifndef MS_DIRSYNC
52#define MS_DIRSYNC 128
53#endif
54
55enum {
56	KEY_KERN_FLAG,
57	KEY_KERN_OPT,
58	KEY_FUSERMOUNT_OPT,
59	KEY_SUBTYPE_OPT,
60	KEY_MTAB_OPT,
61	KEY_ALLOW_OTHER,
62	KEY_RO,
63};
64
65struct mount_opts {
66	int allow_other;
67	int flags;
68	int auto_unmount;
69	int blkdev;
70	char *fsname;
71	char *subtype;
72	char *subtype_opt;
73	char *mtab_opts;
74	char *fusermount_opts;
75	char *kernel_opts;
76	unsigned max_read;
77};
78
79#define FUSE_MOUNT_OPT(t, p) { t, offsetof(struct mount_opts, p), 1 }
80
81static const struct fuse_opt fuse_mount_opts[] = {
82	FUSE_MOUNT_OPT("allow_other",		allow_other),
83	FUSE_MOUNT_OPT("blkdev",		blkdev),
84	FUSE_MOUNT_OPT("auto_unmount",		auto_unmount),
85	FUSE_MOUNT_OPT("fsname=%s",		fsname),
86	FUSE_MOUNT_OPT("max_read=%u",		max_read),
87	FUSE_MOUNT_OPT("subtype=%s",		subtype),
88	FUSE_OPT_KEY("allow_other",		KEY_KERN_OPT),
89	FUSE_OPT_KEY("auto_unmount",		KEY_FUSERMOUNT_OPT),
90	FUSE_OPT_KEY("blkdev",			KEY_FUSERMOUNT_OPT),
91	FUSE_OPT_KEY("fsname=",			KEY_FUSERMOUNT_OPT),
92	FUSE_OPT_KEY("subtype=",		KEY_SUBTYPE_OPT),
93	FUSE_OPT_KEY("blksize=",		KEY_KERN_OPT),
94	FUSE_OPT_KEY("default_permissions",	KEY_KERN_OPT),
95	FUSE_OPT_KEY("context=",		KEY_KERN_OPT),
96	FUSE_OPT_KEY("fscontext=",		KEY_KERN_OPT),
97	FUSE_OPT_KEY("defcontext=",		KEY_KERN_OPT),
98	FUSE_OPT_KEY("rootcontext=",		KEY_KERN_OPT),
99	FUSE_OPT_KEY("max_read=",		KEY_KERN_OPT),
100	FUSE_OPT_KEY("user=",			KEY_MTAB_OPT),
101	FUSE_OPT_KEY("-n",			KEY_MTAB_OPT),
102	FUSE_OPT_KEY("-r",			KEY_RO),
103	FUSE_OPT_KEY("ro",			KEY_KERN_FLAG),
104	FUSE_OPT_KEY("rw",			KEY_KERN_FLAG),
105	FUSE_OPT_KEY("suid",			KEY_KERN_FLAG),
106	FUSE_OPT_KEY("nosuid",			KEY_KERN_FLAG),
107	FUSE_OPT_KEY("dev",			KEY_KERN_FLAG),
108	FUSE_OPT_KEY("nodev",			KEY_KERN_FLAG),
109	FUSE_OPT_KEY("exec",			KEY_KERN_FLAG),
110	FUSE_OPT_KEY("noexec",			KEY_KERN_FLAG),
111	FUSE_OPT_KEY("async",			KEY_KERN_FLAG),
112	FUSE_OPT_KEY("sync",			KEY_KERN_FLAG),
113	FUSE_OPT_KEY("dirsync",			KEY_KERN_FLAG),
114	FUSE_OPT_KEY("noatime",			KEY_KERN_FLAG),
115	FUSE_OPT_KEY("nodiratime",		KEY_KERN_FLAG),
116	FUSE_OPT_KEY("nostrictatime",		KEY_KERN_FLAG),
117	FUSE_OPT_END
118};
119
120static void exec_fusermount(const char *argv[])
121{
122	execv(FUSERMOUNT_DIR "/" FUSERMOUNT_PROG, (char **) argv);
123	execvp(FUSERMOUNT_PROG, (char **) argv);
124}
125
126void fuse_mount_version(void)
127{
128	int pid = fork();
129	if (!pid) {
130		const char *argv[] = { FUSERMOUNT_PROG, "--version", NULL };
131		exec_fusermount(argv);
132		_exit(1);
133	} else if (pid != -1)
134		waitpid(pid, NULL, 0);
135}
136
137struct mount_flags {
138	const char *opt;
139	unsigned long flag;
140	int on;
141};
142
143static const struct mount_flags mount_flags[] = {
144	{"rw",	    MS_RDONLY,	    0},
145	{"ro",	    MS_RDONLY,	    1},
146	{"suid",    MS_NOSUID,	    0},
147	{"nosuid",  MS_NOSUID,	    1},
148	{"dev",	    MS_NODEV,	    0},
149	{"nodev",   MS_NODEV,	    1},
150	{"exec",    MS_NOEXEC,	    0},
151	{"noexec",  MS_NOEXEC,	    1},
152	{"async",   MS_SYNCHRONOUS, 0},
153	{"sync",    MS_SYNCHRONOUS, 1},
154	{"noatime", MS_NOATIME,	    1},
155	{"nodiratime",	    MS_NODIRATIME,	1},
156	{"norelatime",	    MS_RELATIME,	0},
157	{"nostrictatime",   MS_STRICTATIME,	0},
158#ifndef __NetBSD__
159	{"dirsync", MS_DIRSYNC,	    1},
160#endif
161	{NULL,	    0,		    0}
162};
163
164unsigned get_max_read(struct mount_opts *o)
165{
166	return o->max_read;
167}
168
169static void set_mount_flag(const char *s, int *flags)
170{
171	int i;
172
173	for (i = 0; mount_flags[i].opt != NULL; i++) {
174		const char *opt = mount_flags[i].opt;
175		if (strcmp(opt, s) == 0) {
176			if (mount_flags[i].on)
177				*flags |= mount_flags[i].flag;
178			else
179				*flags &= ~mount_flags[i].flag;
180			return;
181		}
182	}
183	fuse_log(FUSE_LOG_ERR, "fuse: internal error, can't find mount flag\n");
184	abort();
185}
186
187static int fuse_mount_opt_proc(void *data, const char *arg, int key,
188			       struct fuse_args *outargs)
189{
190	(void) outargs;
191	struct mount_opts *mo = data;
192
193	switch (key) {
194	case KEY_RO:
195		arg = "ro";
196		/* fall through */
197	case KEY_KERN_FLAG:
198		set_mount_flag(arg, &mo->flags);
199		return 0;
200
201	case KEY_KERN_OPT:
202		return fuse_opt_add_opt(&mo->kernel_opts, arg);
203
204	case KEY_FUSERMOUNT_OPT:
205		return fuse_opt_add_opt_escaped(&mo->fusermount_opts, arg);
206
207	case KEY_SUBTYPE_OPT:
208		return fuse_opt_add_opt(&mo->subtype_opt, arg);
209
210	case KEY_MTAB_OPT:
211		return fuse_opt_add_opt(&mo->mtab_opts, arg);
212	}
213
214	/* Pass through unknown options */
215	return 1;
216}
217
218/* return value:
219 * >= 0	 => fd
220 * -1	 => error
221 */
222static int receive_fd(int fd)
223{
224	struct msghdr msg;
225	struct iovec iov;
226	char buf[1];
227	int rv;
228	size_t ccmsg[CMSG_SPACE(sizeof(int)) / sizeof(size_t)];
229	struct cmsghdr *cmsg;
230
231	iov.iov_base = buf;
232	iov.iov_len = 1;
233
234	memset(&msg, 0, sizeof(msg));
235	msg.msg_name = 0;
236	msg.msg_namelen = 0;
237	msg.msg_iov = &iov;
238	msg.msg_iovlen = 1;
239	/* old BSD implementations should use msg_accrights instead of
240	 * msg_control; the interface is different. */
241	msg.msg_control = ccmsg;
242	msg.msg_controllen = sizeof(ccmsg);
243
244	while(((rv = recvmsg(fd, &msg, 0)) == -1) && errno == EINTR);
245	if (rv == -1) {
246		perror("recvmsg");
247		return -1;
248	}
249	if(!rv) {
250		/* EOF */
251		return -1;
252	}
253
254	cmsg = CMSG_FIRSTHDR(&msg);
255	if (cmsg->cmsg_type != SCM_RIGHTS) {
256		fuse_log(FUSE_LOG_ERR, "got control message of unknown type %d\n",
257			cmsg->cmsg_type);
258		return -1;
259	}
260	return *(int*)CMSG_DATA(cmsg);
261}
262
263void fuse_kern_unmount(const char *mountpoint, int fd)
264{
265	int res;
266	int pid;
267
268	if (fd != -1) {
269		struct pollfd pfd;
270
271		pfd.fd = fd;
272		pfd.events = 0;
273		res = poll(&pfd, 1, 0);
274
275		/* Need to close file descriptor, otherwise synchronous umount
276		   would recurse into filesystem, and deadlock.
277
278		   Caller expects fuse_kern_unmount to close the fd, so close it
279		   anyway. */
280		close(fd);
281
282		/* If file poll returns POLLERR on the device file descriptor,
283		   then the filesystem is already unmounted or the connection
284		   was severed via /sys/fs/fuse/connections/NNN/abort */
285		if (res == 1 && (pfd.revents & POLLERR))
286			return;
287	}
288
289	if (geteuid() == 0) {
290		fuse_mnt_umount("fuse", mountpoint, mountpoint,  1);
291		return;
292	}
293
294	res = umount2(mountpoint, 2);
295	if (res == 0)
296		return;
297
298	pid = fork();
299	if(pid == -1)
300		return;
301
302	if(pid == 0) {
303		const char *argv[] = { FUSERMOUNT_PROG, "-u", "-q", "-z",
304				       "--", mountpoint, NULL };
305
306		exec_fusermount(argv);
307		_exit(1);
308	}
309	waitpid(pid, NULL, 0);
310}
311
312static int setup_auto_unmount(const char *mountpoint, int quiet)
313{
314	int fds[2], pid;
315	int res;
316
317	if (!mountpoint) {
318		fuse_log(FUSE_LOG_ERR, "fuse: missing mountpoint parameter\n");
319		return -1;
320	}
321
322	res = socketpair(PF_UNIX, SOCK_STREAM, 0, fds);
323	if(res == -1) {
324		perror("fuse: socketpair() failed");
325		return -1;
326	}
327
328	pid = fork();
329	if(pid == -1) {
330		perror("fuse: fork() failed");
331		close(fds[0]);
332		close(fds[1]);
333		return -1;
334	}
335
336	if(pid == 0) {
337		char env[10];
338		const char *argv[32];
339		int a = 0;
340
341		if (quiet) {
342			int fd = open("/dev/null", O_RDONLY);
343			if (fd != -1) {
344				dup2(fd, 1);
345				dup2(fd, 2);
346			}
347		}
348
349		argv[a++] = FUSERMOUNT_PROG;
350		argv[a++] = "--auto-unmount";
351		argv[a++] = "--";
352		argv[a++] = mountpoint;
353		argv[a++] = NULL;
354
355		close(fds[1]);
356		fcntl(fds[0], F_SETFD, 0);
357		snprintf(env, sizeof(env), "%i", fds[0]);
358		setenv(FUSE_COMMFD_ENV, env, 1);
359		exec_fusermount(argv);
360		perror("fuse: failed to exec fusermount3");
361		_exit(1);
362	}
363
364	close(fds[0]);
365
366	// Now fusermount3 will only exit when fds[1] closes automatically when our
367	// process exits.
368	return 0;
369}
370
371static int fuse_mount_fusermount(const char *mountpoint, struct mount_opts *mo,
372		const char *opts, int quiet)
373{
374	int fds[2], pid;
375	int res;
376	int rv;
377
378	if (!mountpoint) {
379		fuse_log(FUSE_LOG_ERR, "fuse: missing mountpoint parameter\n");
380		return -1;
381	}
382
383	res = socketpair(PF_UNIX, SOCK_STREAM, 0, fds);
384	if(res == -1) {
385		perror("fuse: socketpair() failed");
386		return -1;
387	}
388
389	pid = fork();
390	if(pid == -1) {
391		perror("fuse: fork() failed");
392		close(fds[0]);
393		close(fds[1]);
394		return -1;
395	}
396
397	if(pid == 0) {
398		char env[10];
399		const char *argv[32];
400		int a = 0;
401
402		if (quiet) {
403			int fd = open("/dev/null", O_RDONLY);
404			if (fd != -1) {
405				dup2(fd, 1);
406				dup2(fd, 2);
407			}
408		}
409
410		argv[a++] = FUSERMOUNT_PROG;
411		if (opts) {
412			argv[a++] = "-o";
413			argv[a++] = opts;
414		}
415		argv[a++] = "--";
416		argv[a++] = mountpoint;
417		argv[a++] = NULL;
418
419		close(fds[1]);
420		fcntl(fds[0], F_SETFD, 0);
421		snprintf(env, sizeof(env), "%i", fds[0]);
422		setenv(FUSE_COMMFD_ENV, env, 1);
423		exec_fusermount(argv);
424		perror("fuse: failed to exec fusermount3");
425		_exit(1);
426	}
427
428	close(fds[0]);
429	rv = receive_fd(fds[1]);
430
431	if (!mo->auto_unmount) {
432		/* with auto_unmount option fusermount3 will not exit until
433		   this socket is closed */
434		close(fds[1]);
435		waitpid(pid, NULL, 0); /* bury zombie */
436	}
437
438	if (rv >= 0)
439		fcntl(rv, F_SETFD, FD_CLOEXEC);
440
441	return rv;
442}
443
444#ifndef O_CLOEXEC
445#define O_CLOEXEC 0
446#endif
447
448static int fuse_mount_sys(const char *mnt, struct mount_opts *mo,
449			  const char *mnt_opts)
450{
451	char tmp[128];
452	const char *devname = "/dev/fuse";
453	char *source = NULL;
454	char *type = NULL;
455	struct stat stbuf;
456	int fd;
457	int res;
458
459	if (!mnt) {
460		fuse_log(FUSE_LOG_ERR, "fuse: missing mountpoint parameter\n");
461		return -1;
462	}
463
464	res = stat(mnt, &stbuf);
465	if (res == -1) {
466		fuse_log(FUSE_LOG_ERR, "fuse: failed to access mountpoint %s: %s\n",
467			mnt, strerror(errno));
468		return -1;
469	}
470
471	fd = open(devname, O_RDWR | O_CLOEXEC);
472	if (fd == -1) {
473		if (errno == ENODEV || errno == ENOENT)
474			fuse_log(FUSE_LOG_ERR, "fuse: device not found, try 'modprobe fuse' first\n");
475		else
476			fuse_log(FUSE_LOG_ERR, "fuse: failed to open %s: %s\n",
477				devname, strerror(errno));
478		return -1;
479	}
480	if (!O_CLOEXEC)
481		fcntl(fd, F_SETFD, FD_CLOEXEC);
482
483	snprintf(tmp, sizeof(tmp),  "fd=%i,rootmode=%o,user_id=%u,group_id=%u",
484		 fd, stbuf.st_mode & S_IFMT, getuid(), getgid());
485
486	res = fuse_opt_add_opt(&mo->kernel_opts, tmp);
487	if (res == -1)
488		goto out_close;
489
490	source = malloc((mo->fsname ? strlen(mo->fsname) : 0) +
491			(mo->subtype ? strlen(mo->subtype) : 0) +
492			strlen(devname) + 32);
493
494	type = malloc((mo->subtype ? strlen(mo->subtype) : 0) + 32);
495	if (!type || !source) {
496		fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate memory\n");
497		goto out_close;
498	}
499
500	strcpy(type, mo->blkdev ? "fuseblk" : "fuse");
501	if (mo->subtype) {
502		strcat(type, ".");
503		strcat(type, mo->subtype);
504	}
505	strcpy(source,
506	       mo->fsname ? mo->fsname : (mo->subtype ? mo->subtype : devname));
507
508	res = mount(source, mnt, type, mo->flags, mo->kernel_opts);
509	if (res == -1 && errno == ENODEV && mo->subtype) {
510		/* Probably missing subtype support */
511		strcpy(type, mo->blkdev ? "fuseblk" : "fuse");
512		if (mo->fsname) {
513			if (!mo->blkdev)
514				sprintf(source, "%s#%s", mo->subtype,
515					mo->fsname);
516		} else {
517			strcpy(source, type);
518		}
519		res = mount(source, mnt, type, mo->flags, mo->kernel_opts);
520	}
521	if (res == -1) {
522		/*
523		 * Maybe kernel doesn't support unprivileged mounts, in this
524		 * case try falling back to fusermount3
525		 */
526		if (errno == EPERM) {
527			res = -2;
528		} else {
529			int errno_save = errno;
530			if (mo->blkdev && errno == ENODEV &&
531			    !fuse_mnt_check_fuseblk())
532				fuse_log(FUSE_LOG_ERR,
533					"fuse: 'fuseblk' support missing\n");
534			else
535				fuse_log(FUSE_LOG_ERR, "fuse: mount failed: %s\n",
536					strerror(errno_save));
537		}
538
539		goto out_close;
540	}
541
542#ifndef IGNORE_MTAB
543	if (geteuid() == 0) {
544		char *newmnt = fuse_mnt_resolve_path("fuse", mnt);
545		res = -1;
546		if (!newmnt)
547			goto out_umount;
548
549		res = fuse_mnt_add_mount("fuse", source, newmnt, type,
550					 mnt_opts);
551		free(newmnt);
552		if (res == -1)
553			goto out_umount;
554	}
555#endif /* IGNORE_MTAB */
556	free(type);
557	free(source);
558
559	return fd;
560
561out_umount:
562	umount2(mnt, 2); /* lazy umount */
563out_close:
564	free(type);
565	free(source);
566	close(fd);
567	return res;
568}
569
570static int get_mnt_flag_opts(char **mnt_optsp, int flags)
571{
572	int i;
573
574	if (!(flags & MS_RDONLY) && fuse_opt_add_opt(mnt_optsp, "rw") == -1)
575		return -1;
576
577	for (i = 0; mount_flags[i].opt != NULL; i++) {
578		if (mount_flags[i].on && (flags & mount_flags[i].flag) &&
579		    fuse_opt_add_opt(mnt_optsp, mount_flags[i].opt) == -1)
580			return -1;
581	}
582	return 0;
583}
584
585struct mount_opts *parse_mount_opts(struct fuse_args *args)
586{
587	struct mount_opts *mo;
588
589	mo = (struct mount_opts*) malloc(sizeof(struct mount_opts));
590	if (mo == NULL)
591		return NULL;
592
593	memset(mo, 0, sizeof(struct mount_opts));
594	mo->flags = MS_NOSUID | MS_NODEV;
595
596	if (args &&
597	    fuse_opt_parse(args, mo, fuse_mount_opts, fuse_mount_opt_proc) == -1)
598		goto err_out;
599
600	return mo;
601
602err_out:
603	destroy_mount_opts(mo);
604	return NULL;
605}
606
607void destroy_mount_opts(struct mount_opts *mo)
608{
609	free(mo->fsname);
610	free(mo->subtype);
611	free(mo->fusermount_opts);
612	free(mo->subtype_opt);
613	free(mo->kernel_opts);
614	free(mo->mtab_opts);
615	free(mo);
616}
617
618
619int fuse_kern_mount(const char *mountpoint, struct mount_opts *mo)
620{
621	int res = -1;
622	char *mnt_opts = NULL;
623
624	res = -1;
625	if (get_mnt_flag_opts(&mnt_opts, mo->flags) == -1)
626		goto out;
627	if (mo->kernel_opts && fuse_opt_add_opt(&mnt_opts, mo->kernel_opts) == -1)
628		goto out;
629	if (mo->mtab_opts &&  fuse_opt_add_opt(&mnt_opts, mo->mtab_opts) == -1)
630		goto out;
631
632	res = fuse_mount_sys(mountpoint, mo, mnt_opts);
633	if (res >= 0 && mo->auto_unmount) {
634		if(0 > setup_auto_unmount(mountpoint, 0)) {
635			// Something went wrong, let's umount like in fuse_mount_sys.
636			umount2(mountpoint, MNT_DETACH); /* lazy umount */
637			res = -1;
638		}
639	} else if (res == -2) {
640		if (mo->fusermount_opts &&
641		    fuse_opt_add_opt(&mnt_opts, mo->fusermount_opts) == -1)
642			goto out;
643
644		if (mo->subtype) {
645			char *tmp_opts = NULL;
646
647			res = -1;
648			if (fuse_opt_add_opt(&tmp_opts, mnt_opts) == -1 ||
649			    fuse_opt_add_opt(&tmp_opts, mo->subtype_opt) == -1) {
650				free(tmp_opts);
651				goto out;
652			}
653
654			res = fuse_mount_fusermount(mountpoint, mo, tmp_opts, 1);
655			free(tmp_opts);
656			if (res == -1)
657				res = fuse_mount_fusermount(mountpoint, mo,
658							    mnt_opts, 0);
659		} else {
660			res = fuse_mount_fusermount(mountpoint, mo, mnt_opts, 0);
661		}
662	}
663out:
664	free(mnt_opts);
665	return res;
666}
667