blob: af7218fa4b2b32ff0f375e86e566710bf5a8d4cd [file] [log] [blame]
bigbiff bigbiff9c754052013-01-09 09:09:08 -05001/*
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 LGPLv2.
6 See the file COPYING.LIB.
7*/
8
9#include "config.h"
10#include "fuse_i.h"
11#include "fuse_misc.h"
12#include "fuse_opt.h"
13#include "fuse_common_compat.h"
14#include "mount_util.h"
15
16#include <stdio.h>
17#include <stdlib.h>
18#include <unistd.h>
19#include <stddef.h>
Dees_Troye34c1332013-02-06 19:13:00 +000020#include <string.h>
bigbiff bigbiff9c754052013-01-09 09:09:08 -050021#include <fcntl.h>
22#include <errno.h>
23#include <sys/poll.h>
24#include <sys/socket.h>
25#include <sys/un.h>
26#include <sys/wait.h>
27#include <sys/mount.h>
28
Dees_Troye34c1332013-02-06 19:13:00 +000029#ifdef __NetBSD__
30#include <perfuse.h>
31
32#define MS_RDONLY MNT_RDONLY
33#define MS_NOSUID MNT_NOSUID
34#define MS_NODEV MNT_NODEV
35#define MS_NOEXEC MNT_NOEXEC
36#define MS_SYNCHRONOUS MNT_SYNCHRONOUS
37#define MS_NOATIME MNT_NOATIME
38
39
40#define umount2(mnt, flags) unmount(mnt, (flags == 2) ? MNT_FORCE : 0)
41#endif
42
bigbiff bigbiff9c754052013-01-09 09:09:08 -050043#define FUSERMOUNT_PROG "fusermount"
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
54enum {
55 KEY_KERN_FLAG,
56 KEY_KERN_OPT,
57 KEY_FUSERMOUNT_OPT,
58 KEY_SUBTYPE_OPT,
59 KEY_MTAB_OPT,
60 KEY_ALLOW_ROOT,
61 KEY_RO,
62 KEY_HELP,
63 KEY_VERSION,
64};
65
66struct mount_opts {
67 int allow_other;
68 int allow_root;
69 int ishelp;
70 int flags;
71 int nonempty;
Dees_Troye34c1332013-02-06 19:13:00 +000072 int auto_unmount;
bigbiff bigbiff9c754052013-01-09 09:09:08 -050073 int blkdev;
74 char *fsname;
75 char *subtype;
76 char *subtype_opt;
77 char *mtab_opts;
78 char *fusermount_opts;
79 char *kernel_opts;
80};
81
82#define FUSE_MOUNT_OPT(t, p) { t, offsetof(struct mount_opts, p), 1 }
83
84static const struct fuse_opt fuse_mount_opts[] = {
85 FUSE_MOUNT_OPT("allow_other", allow_other),
86 FUSE_MOUNT_OPT("allow_root", allow_root),
87 FUSE_MOUNT_OPT("nonempty", nonempty),
88 FUSE_MOUNT_OPT("blkdev", blkdev),
Dees_Troye34c1332013-02-06 19:13:00 +000089 FUSE_MOUNT_OPT("auto_unmount", auto_unmount),
bigbiff bigbiff9c754052013-01-09 09:09:08 -050090 FUSE_MOUNT_OPT("fsname=%s", fsname),
91 FUSE_MOUNT_OPT("subtype=%s", subtype),
92 FUSE_OPT_KEY("allow_other", KEY_KERN_OPT),
93 FUSE_OPT_KEY("allow_root", KEY_ALLOW_ROOT),
94 FUSE_OPT_KEY("nonempty", KEY_FUSERMOUNT_OPT),
Dees_Troye34c1332013-02-06 19:13:00 +000095 FUSE_OPT_KEY("auto_unmount", KEY_FUSERMOUNT_OPT),
bigbiff bigbiff9c754052013-01-09 09:09:08 -050096 FUSE_OPT_KEY("blkdev", KEY_FUSERMOUNT_OPT),
97 FUSE_OPT_KEY("fsname=", KEY_FUSERMOUNT_OPT),
98 FUSE_OPT_KEY("subtype=", KEY_SUBTYPE_OPT),
99 FUSE_OPT_KEY("large_read", KEY_KERN_OPT),
100 FUSE_OPT_KEY("blksize=", KEY_KERN_OPT),
101 FUSE_OPT_KEY("default_permissions", KEY_KERN_OPT),
102 FUSE_OPT_KEY("max_read=", KEY_KERN_OPT),
103 FUSE_OPT_KEY("max_read=", FUSE_OPT_KEY_KEEP),
104 FUSE_OPT_KEY("user=", KEY_MTAB_OPT),
105 FUSE_OPT_KEY("-r", KEY_RO),
106 FUSE_OPT_KEY("ro", KEY_KERN_FLAG),
107 FUSE_OPT_KEY("rw", KEY_KERN_FLAG),
108 FUSE_OPT_KEY("suid", KEY_KERN_FLAG),
109 FUSE_OPT_KEY("nosuid", KEY_KERN_FLAG),
110 FUSE_OPT_KEY("dev", KEY_KERN_FLAG),
111 FUSE_OPT_KEY("nodev", KEY_KERN_FLAG),
112 FUSE_OPT_KEY("exec", KEY_KERN_FLAG),
113 FUSE_OPT_KEY("noexec", KEY_KERN_FLAG),
114 FUSE_OPT_KEY("async", KEY_KERN_FLAG),
115 FUSE_OPT_KEY("sync", KEY_KERN_FLAG),
116 FUSE_OPT_KEY("dirsync", KEY_KERN_FLAG),
117 FUSE_OPT_KEY("atime", KEY_KERN_FLAG),
118 FUSE_OPT_KEY("noatime", KEY_KERN_FLAG),
119 FUSE_OPT_KEY("-h", KEY_HELP),
120 FUSE_OPT_KEY("--help", KEY_HELP),
121 FUSE_OPT_KEY("-V", KEY_VERSION),
122 FUSE_OPT_KEY("--version", KEY_VERSION),
123 FUSE_OPT_END
124};
125
126static void mount_help(void)
127{
128 fprintf(stderr,
129" -o allow_other allow access to other users\n"
130" -o allow_root allow access to root\n"
Dees_Troye34c1332013-02-06 19:13:00 +0000131" -o auto_unmount auto unmount on process termination\n"
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500132" -o nonempty allow mounts over non-empty file/dir\n"
133" -o default_permissions enable permission checking by kernel\n"
134" -o fsname=NAME set filesystem name\n"
135" -o subtype=NAME set filesystem type\n"
136" -o large_read issue large read requests (2.4 only)\n"
137" -o max_read=N set maximum size of read requests\n"
138"\n");
139}
140
141#define FUSERMOUNT_DIR "/usr/bin"
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500142static void exec_fusermount(const char *argv[])
143{
144 execv(FUSERMOUNT_DIR "/" FUSERMOUNT_PROG, (char **) argv);
145 execvp(FUSERMOUNT_PROG, (char **) argv);
146}
147
148static void mount_version(void)
149{
150 int pid = fork();
151 if (!pid) {
152 const char *argv[] = { FUSERMOUNT_PROG, "--version", NULL };
153 exec_fusermount(argv);
154 _exit(1);
155 } else if (pid != -1)
156 waitpid(pid, NULL, 0);
157}
158
159struct mount_flags {
160 const char *opt;
161 unsigned long flag;
162 int on;
163};
164
Dees_Troye34c1332013-02-06 19:13:00 +0000165static const struct mount_flags mount_flags[] = {
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500166 {"rw", MS_RDONLY, 0},
167 {"ro", MS_RDONLY, 1},
168 {"suid", MS_NOSUID, 0},
169 {"nosuid", MS_NOSUID, 1},
170 {"dev", MS_NODEV, 0},
171 {"nodev", MS_NODEV, 1},
172 {"exec", MS_NOEXEC, 0},
173 {"noexec", MS_NOEXEC, 1},
174 {"async", MS_SYNCHRONOUS, 0},
175 {"sync", MS_SYNCHRONOUS, 1},
176 {"atime", MS_NOATIME, 0},
177 {"noatime", MS_NOATIME, 1},
Dees_Troye34c1332013-02-06 19:13:00 +0000178#ifndef __NetBSD__
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500179 {"dirsync", MS_DIRSYNC, 1},
Dees_Troye34c1332013-02-06 19:13:00 +0000180#endif
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500181 {NULL, 0, 0}
182};
183
184static void set_mount_flag(const char *s, int *flags)
185{
186 int i;
187
188 for (i = 0; mount_flags[i].opt != NULL; i++) {
189 const char *opt = mount_flags[i].opt;
190 if (strcmp(opt, s) == 0) {
191 if (mount_flags[i].on)
192 *flags |= mount_flags[i].flag;
193 else
194 *flags &= ~mount_flags[i].flag;
195 return;
196 }
197 }
198 fprintf(stderr, "fuse: internal error, can't find mount flag\n");
199 abort();
200}
201
202static int fuse_mount_opt_proc(void *data, const char *arg, int key,
203 struct fuse_args *outargs)
204{
205 struct mount_opts *mo = data;
206
207 switch (key) {
208 case KEY_ALLOW_ROOT:
209 if (fuse_opt_add_opt(&mo->kernel_opts, "allow_other") == -1 ||
210 fuse_opt_add_arg(outargs, "-oallow_root") == -1)
211 return -1;
212 return 0;
213
214 case KEY_RO:
215 arg = "ro";
216 /* fall through */
217 case KEY_KERN_FLAG:
218 set_mount_flag(arg, &mo->flags);
219 return 0;
220
221 case KEY_KERN_OPT:
222 return fuse_opt_add_opt(&mo->kernel_opts, arg);
223
224 case KEY_FUSERMOUNT_OPT:
225 return fuse_opt_add_opt_escaped(&mo->fusermount_opts, arg);
226
227 case KEY_SUBTYPE_OPT:
228 return fuse_opt_add_opt(&mo->subtype_opt, arg);
229
230 case KEY_MTAB_OPT:
231 return fuse_opt_add_opt(&mo->mtab_opts, arg);
232
233 case KEY_HELP:
234 mount_help();
235 mo->ishelp = 1;
236 break;
237
238 case KEY_VERSION:
239 mount_version();
240 mo->ishelp = 1;
241 break;
242 }
243 return 1;
244}
245
246/* return value:
247 * >= 0 => fd
248 * -1 => error
249 */
250static int receive_fd(int fd)
251{
252 struct msghdr msg;
253 struct iovec iov;
254 char buf[1];
255 int rv;
256 size_t ccmsg[CMSG_SPACE(sizeof(int)) / sizeof(size_t)];
257 struct cmsghdr *cmsg;
258
259 iov.iov_base = buf;
260 iov.iov_len = 1;
261
Dees_Troye34c1332013-02-06 19:13:00 +0000262 memset(&msg, 0, sizeof(msg));
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500263 msg.msg_name = 0;
264 msg.msg_namelen = 0;
265 msg.msg_iov = &iov;
266 msg.msg_iovlen = 1;
267 /* old BSD implementations should use msg_accrights instead of
268 * msg_control; the interface is different. */
269 msg.msg_control = ccmsg;
270 msg.msg_controllen = sizeof(ccmsg);
271
272 while(((rv = recvmsg(fd, &msg, 0)) == -1) && errno == EINTR);
273 if (rv == -1) {
274 perror("recvmsg");
275 return -1;
276 }
277 if(!rv) {
278 /* EOF */
279 return -1;
280 }
281
282 cmsg = CMSG_FIRSTHDR(&msg);
283 if (!cmsg->cmsg_type == SCM_RIGHTS) {
284 fprintf(stderr, "got control message of unknown type %d\n",
285 cmsg->cmsg_type);
286 return -1;
287 }
288 return *(int*)CMSG_DATA(cmsg);
289}
290
291void fuse_kern_unmount(const char *mountpoint, int fd)
292{
293 int res;
294 int pid;
295
296 if (!mountpoint)
297 return;
298
299 if (fd != -1) {
300 struct pollfd pfd;
301
302 pfd.fd = fd;
303 pfd.events = 0;
304 res = poll(&pfd, 1, 0);
305 /* If file poll returns POLLERR on the device file descriptor,
306 then the filesystem is already unmounted */
307 if (res == 1 && (pfd.revents & POLLERR))
308 return;
309
310 /* Need to close file descriptor, otherwise synchronous umount
311 would recurse into filesystem, and deadlock */
312 close(fd);
313 }
314
315 if (geteuid() == 0) {
316 fuse_mnt_umount("fuse", mountpoint, mountpoint, 1);
317 return;
318 }
319
320 res = umount2(mountpoint, 2);
321 if (res == 0)
322 return;
323
324 pid = fork();
325 if(pid == -1)
326 return;
327
328 if(pid == 0) {
329 const char *argv[] = { FUSERMOUNT_PROG, "-u", "-q", "-z",
330 "--", mountpoint, NULL };
331
332 exec_fusermount(argv);
333 _exit(1);
334 }
335 waitpid(pid, NULL, 0);
336}
337
338void fuse_unmount_compat22(const char *mountpoint)
339{
340 fuse_kern_unmount(mountpoint, -1);
341}
342
Dees_Troye34c1332013-02-06 19:13:00 +0000343static int fuse_mount_fusermount(const char *mountpoint, struct mount_opts *mo,
344 const char *opts, int quiet)
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500345{
346 int fds[2], pid;
347 int res;
348 int rv;
349
350 if (!mountpoint) {
351 fprintf(stderr, "fuse: missing mountpoint parameter\n");
352 return -1;
353 }
354
355 res = socketpair(PF_UNIX, SOCK_STREAM, 0, fds);
356 if(res == -1) {
357 perror("fuse: socketpair() failed");
358 return -1;
359 }
360
361 pid = fork();
362 if(pid == -1) {
363 perror("fuse: fork() failed");
364 close(fds[0]);
365 close(fds[1]);
366 return -1;
367 }
368
369 if(pid == 0) {
370 char env[10];
371 const char *argv[32];
372 int a = 0;
373
374 if (quiet) {
375 int fd = open("/dev/null", O_RDONLY);
Dees_Troye34c1332013-02-06 19:13:00 +0000376 if (fd != -1) {
377 dup2(fd, 1);
378 dup2(fd, 2);
379 }
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500380 }
381
382 argv[a++] = FUSERMOUNT_PROG;
383 if (opts) {
384 argv[a++] = "-o";
385 argv[a++] = opts;
386 }
387 argv[a++] = "--";
388 argv[a++] = mountpoint;
389 argv[a++] = NULL;
390
391 close(fds[1]);
392 fcntl(fds[0], F_SETFD, 0);
393 snprintf(env, sizeof(env), "%i", fds[0]);
394 setenv(FUSE_COMMFD_ENV, env, 1);
395 exec_fusermount(argv);
396 perror("fuse: failed to exec fusermount");
397 _exit(1);
398 }
399
400 close(fds[0]);
401 rv = receive_fd(fds[1]);
Dees_Troye34c1332013-02-06 19:13:00 +0000402
403 if (!mo->auto_unmount) {
404 /* with auto_unmount option fusermount will not exit until
405 this socket is closed */
406 close(fds[1]);
407 waitpid(pid, NULL, 0); /* bury zombie */
408 }
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500409
410 return rv;
411}
412
413int fuse_mount_compat22(const char *mountpoint, const char *opts)
414{
Dees_Troye34c1332013-02-06 19:13:00 +0000415 struct mount_opts mo;
416 memset(&mo, 0, sizeof(mo));
417 mo.flags = MS_NOSUID | MS_NODEV;
418
419 return fuse_mount_fusermount(mountpoint, &mo, opts, 0);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500420}
421
422static int fuse_mount_sys(const char *mnt, struct mount_opts *mo,
423 const char *mnt_opts)
424{
425 char tmp[128];
426 const char *devname = "/dev/fuse";
427 char *source = NULL;
428 char *type = NULL;
429 struct stat stbuf;
430 int fd;
431 int res;
432
433 if (!mnt) {
434 fprintf(stderr, "fuse: missing mountpoint parameter\n");
435 return -1;
436 }
437
438 res = stat(mnt, &stbuf);
439 if (res == -1) {
440 fprintf(stderr ,"fuse: failed to access mountpoint %s: %s\n",
441 mnt, strerror(errno));
442 return -1;
443 }
444
445 if (!mo->nonempty) {
446 res = fuse_mnt_check_empty("fuse", mnt, stbuf.st_mode,
447 stbuf.st_size);
448 if (res == -1)
449 return -1;
450 }
451
Dees_Troye34c1332013-02-06 19:13:00 +0000452 if (mo->auto_unmount) {
453 /* Tell the caller to fallback to fusermount because
454 auto-unmount does not work otherwise. */
455 return -2;
456 }
457
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500458 fd = open(devname, O_RDWR);
459 if (fd == -1) {
460 if (errno == ENODEV || errno == ENOENT)
461 fprintf(stderr, "fuse: device not found, try 'modprobe fuse' first\n");
462 else
463 fprintf(stderr, "fuse: failed to open %s: %s\n",
464 devname, strerror(errno));
465 return -1;
466 }
467
468 snprintf(tmp, sizeof(tmp), "fd=%i,rootmode=%o,user_id=%i,group_id=%i",
469 fd, stbuf.st_mode & S_IFMT, getuid(), getgid());
470
471 res = fuse_opt_add_opt(&mo->kernel_opts, tmp);
472 if (res == -1)
473 goto out_close;
474
475 source = malloc((mo->fsname ? strlen(mo->fsname) : 0) +
476 (mo->subtype ? strlen(mo->subtype) : 0) +
477 strlen(devname) + 32);
478
479 type = malloc((mo->subtype ? strlen(mo->subtype) : 0) + 32);
480 if (!type || !source) {
481 fprintf(stderr, "fuse: failed to allocate memory\n");
482 goto out_close;
483 }
484
485 strcpy(type, mo->blkdev ? "fuseblk" : "fuse");
486 if (mo->subtype) {
487 strcat(type, ".");
488 strcat(type, mo->subtype);
489 }
490 strcpy(source,
491 mo->fsname ? mo->fsname : (mo->subtype ? mo->subtype : devname));
492
493 res = mount(source, mnt, type, mo->flags, mo->kernel_opts);
494 if (res == -1 && errno == ENODEV && mo->subtype) {
495 /* Probably missing subtype support */
496 strcpy(type, mo->blkdev ? "fuseblk" : "fuse");
497 if (mo->fsname) {
498 if (!mo->blkdev)
499 sprintf(source, "%s#%s", mo->subtype,
500 mo->fsname);
501 } else {
502 strcpy(source, type);
503 }
504 res = mount(source, mnt, type, mo->flags, mo->kernel_opts);
505 }
506 if (res == -1) {
507 /*
508 * Maybe kernel doesn't support unprivileged mounts, in this
509 * case try falling back to fusermount
510 */
511 if (errno == EPERM) {
512 res = -2;
513 } else {
514 int errno_save = errno;
515 if (mo->blkdev && errno == ENODEV &&
516 !fuse_mnt_check_fuseblk())
517 fprintf(stderr,
518 "fuse: 'fuseblk' support missing\n");
519 else
520 fprintf(stderr, "fuse: mount failed: %s\n",
521 strerror(errno_save));
522 }
523
524 goto out_close;
525 }
526
Dees_Troye34c1332013-02-06 19:13:00 +0000527#ifndef __NetBSD__
528#ifndef IGNORE_MTAB
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500529 if (geteuid() == 0) {
530 char *newmnt = fuse_mnt_resolve_path("fuse", mnt);
531 res = -1;
532 if (!newmnt)
533 goto out_umount;
534
535 res = fuse_mnt_add_mount("fuse", source, newmnt, type,
536 mnt_opts);
537 free(newmnt);
538 if (res == -1)
539 goto out_umount;
540 }
Dees_Troye34c1332013-02-06 19:13:00 +0000541#endif /* IGNORE_MTAB */
542#endif /* __NetBSD__ */
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500543 free(type);
544 free(source);
545
546 return fd;
547
548out_umount:
549 umount2(mnt, 2); /* lazy umount */
550out_close:
551 free(type);
552 free(source);
553 close(fd);
554 return res;
555}
556
557static int get_mnt_flag_opts(char **mnt_optsp, int flags)
558{
559 int i;
560
561 if (!(flags & MS_RDONLY) && fuse_opt_add_opt(mnt_optsp, "rw") == -1)
562 return -1;
563
564 for (i = 0; mount_flags[i].opt != NULL; i++) {
565 if (mount_flags[i].on && (flags & mount_flags[i].flag) &&
566 fuse_opt_add_opt(mnt_optsp, mount_flags[i].opt) == -1)
567 return -1;
568 }
569 return 0;
570}
571
572int fuse_kern_mount(const char *mountpoint, struct fuse_args *args)
573{
574 struct mount_opts mo;
575 int res = -1;
576 char *mnt_opts = NULL;
577
578 memset(&mo, 0, sizeof(mo));
579 mo.flags = MS_NOSUID | MS_NODEV;
580
581 if (args &&
582 fuse_opt_parse(args, &mo, fuse_mount_opts, fuse_mount_opt_proc) == -1)
583 return -1;
584
585 if (mo.allow_other && mo.allow_root) {
586 fprintf(stderr, "fuse: 'allow_other' and 'allow_root' options are mutually exclusive\n");
587 goto out;
588 }
589 res = 0;
590 if (mo.ishelp)
591 goto out;
592
593 res = -1;
594 if (get_mnt_flag_opts(&mnt_opts, mo.flags) == -1)
595 goto out;
596 if (mo.kernel_opts && fuse_opt_add_opt(&mnt_opts, mo.kernel_opts) == -1)
597 goto out;
598 if (mo.mtab_opts && fuse_opt_add_opt(&mnt_opts, mo.mtab_opts) == -1)
599 goto out;
600
601 res = fuse_mount_sys(mountpoint, &mo, mnt_opts);
602 if (res == -2) {
603 if (mo.fusermount_opts &&
604 fuse_opt_add_opt(&mnt_opts, mo.fusermount_opts) == -1)
605 goto out;
606
607 if (mo.subtype) {
608 char *tmp_opts = NULL;
609
610 res = -1;
611 if (fuse_opt_add_opt(&tmp_opts, mnt_opts) == -1 ||
612 fuse_opt_add_opt(&tmp_opts, mo.subtype_opt) == -1) {
613 free(tmp_opts);
614 goto out;
615 }
616
Dees_Troye34c1332013-02-06 19:13:00 +0000617 res = fuse_mount_fusermount(mountpoint, &mo, tmp_opts, 1);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500618 free(tmp_opts);
619 if (res == -1)
Dees_Troye34c1332013-02-06 19:13:00 +0000620 res = fuse_mount_fusermount(mountpoint, &mo,
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500621 mnt_opts, 0);
622 } else {
Dees_Troye34c1332013-02-06 19:13:00 +0000623 res = fuse_mount_fusermount(mountpoint, &mo, mnt_opts, 0);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500624 }
625 }
626out:
627 free(mnt_opts);
628 free(mo.fsname);
629 free(mo.subtype);
630 free(mo.fusermount_opts);
631 free(mo.subtype_opt);
632 free(mo.kernel_opts);
633 free(mo.mtab_opts);
634 return res;
635}
636
637FUSE_SYMVER(".symver fuse_mount_compat22,fuse_mount@FUSE_2.2");
638FUSE_SYMVER(".symver fuse_unmount_compat22,fuse_unmount@FUSE_2.2");