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