2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
5 This program can be distributed under the terms of the GNU LGPLv2.
6 See the file COPYING.LIB.
11 #include "fuse_misc.h"
13 #include "fuse_common_compat.h"
14 #include "mount_util.h"
24 #include <sys/socket.h>
27 #include <sys/mount.h>
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
39 #define umount2(mnt, flags) unmount(mnt, (flags == 2) ? MNT_FORCE : 0)
42 #define FUSERMOUNT_PROG "fusermount"
43 #define FUSE_COMMFD_ENV "_FUSE_COMMFD"
46 #define fork() vfork()
50 #define MS_DIRSYNC 128
77 char *fusermount_opts;
81 #define FUSE_MOUNT_OPT(t, p) { t, offsetof(struct mount_opts, p), 1 }
83 static const struct fuse_opt fuse_mount_opts[] = {
84 FUSE_MOUNT_OPT("allow_other", allow_other),
85 FUSE_MOUNT_OPT("allow_root", allow_root),
86 FUSE_MOUNT_OPT("nonempty", nonempty),
87 FUSE_MOUNT_OPT("blkdev", blkdev),
88 FUSE_MOUNT_OPT("auto_unmount", auto_unmount),
89 FUSE_MOUNT_OPT("fsname=%s", fsname),
90 FUSE_MOUNT_OPT("subtype=%s", subtype),
91 FUSE_OPT_KEY("allow_other", KEY_KERN_OPT),
92 FUSE_OPT_KEY("allow_root", KEY_ALLOW_ROOT),
93 FUSE_OPT_KEY("nonempty", KEY_FUSERMOUNT_OPT),
94 FUSE_OPT_KEY("auto_unmount", KEY_FUSERMOUNT_OPT),
95 FUSE_OPT_KEY("blkdev", KEY_FUSERMOUNT_OPT),
96 FUSE_OPT_KEY("fsname=", KEY_FUSERMOUNT_OPT),
97 FUSE_OPT_KEY("subtype=", KEY_SUBTYPE_OPT),
98 FUSE_OPT_KEY("large_read", KEY_KERN_OPT),
99 FUSE_OPT_KEY("blksize=", KEY_KERN_OPT),
100 FUSE_OPT_KEY("default_permissions", KEY_KERN_OPT),
101 FUSE_OPT_KEY("max_read=", KEY_KERN_OPT),
102 FUSE_OPT_KEY("max_read=", FUSE_OPT_KEY_KEEP),
103 FUSE_OPT_KEY("user=", KEY_MTAB_OPT),
104 FUSE_OPT_KEY("-r", KEY_RO),
105 FUSE_OPT_KEY("ro", KEY_KERN_FLAG),
106 FUSE_OPT_KEY("rw", KEY_KERN_FLAG),
107 FUSE_OPT_KEY("suid", KEY_KERN_FLAG),
108 FUSE_OPT_KEY("nosuid", KEY_KERN_FLAG),
109 FUSE_OPT_KEY("dev", KEY_KERN_FLAG),
110 FUSE_OPT_KEY("nodev", KEY_KERN_FLAG),
111 FUSE_OPT_KEY("exec", KEY_KERN_FLAG),
112 FUSE_OPT_KEY("noexec", KEY_KERN_FLAG),
113 FUSE_OPT_KEY("async", KEY_KERN_FLAG),
114 FUSE_OPT_KEY("sync", KEY_KERN_FLAG),
115 FUSE_OPT_KEY("dirsync", KEY_KERN_FLAG),
116 FUSE_OPT_KEY("atime", KEY_KERN_FLAG),
117 FUSE_OPT_KEY("noatime", KEY_KERN_FLAG),
118 FUSE_OPT_KEY("-h", KEY_HELP),
119 FUSE_OPT_KEY("--help", KEY_HELP),
120 FUSE_OPT_KEY("-V", KEY_VERSION),
121 FUSE_OPT_KEY("--version", KEY_VERSION),
125 static void mount_help(void)
128 " -o allow_other allow access to other users\n"
129 " -o allow_root allow access to root\n"
130 " -o auto_unmount auto unmount on process termination\n"
131 " -o nonempty allow mounts over non-empty file/dir\n"
132 " -o default_permissions enable permission checking by kernel\n"
133 " -o fsname=NAME set filesystem name\n"
134 " -o subtype=NAME set filesystem type\n"
135 " -o large_read issue large read requests (2.4 only)\n"
136 " -o max_read=N set maximum size of read requests\n"
140 static void exec_fusermount(const char *argv[])
142 execv(FUSERMOUNT_DIR "/" FUSERMOUNT_PROG, (char **) argv);
143 execvp(FUSERMOUNT_PROG, (char **) argv);
146 static void mount_version(void)
150 const char *argv[] = { FUSERMOUNT_PROG, "--version", NULL };
151 exec_fusermount(argv);
153 } else if (pid != -1)
154 waitpid(pid, NULL, 0);
163 static const struct mount_flags mount_flags[] = {
164 {"rw", MS_RDONLY, 0},
165 {"ro", MS_RDONLY, 1},
166 {"suid", MS_NOSUID, 0},
167 {"nosuid", MS_NOSUID, 1},
168 {"dev", MS_NODEV, 0},
169 {"nodev", MS_NODEV, 1},
170 {"exec", MS_NOEXEC, 0},
171 {"noexec", MS_NOEXEC, 1},
172 {"async", MS_SYNCHRONOUS, 0},
173 {"sync", MS_SYNCHRONOUS, 1},
174 {"atime", MS_NOATIME, 0},
175 {"noatime", MS_NOATIME, 1},
177 {"dirsync", MS_DIRSYNC, 1},
182 static void set_mount_flag(const char *s, int *flags)
186 for (i = 0; mount_flags[i].opt != NULL; i++) {
187 const char *opt = mount_flags[i].opt;
188 if (strcmp(opt, s) == 0) {
189 if (mount_flags[i].on)
190 *flags |= mount_flags[i].flag;
192 *flags &= ~mount_flags[i].flag;
196 fprintf(stderr, "fuse: internal error, can't find mount flag\n");
200 static int fuse_mount_opt_proc(void *data, const char *arg, int key,
201 struct fuse_args *outargs)
203 struct mount_opts *mo = data;
207 if (fuse_opt_add_opt(&mo->kernel_opts, "allow_other") == -1 ||
208 fuse_opt_add_arg(outargs, "-oallow_root") == -1)
216 set_mount_flag(arg, &mo->flags);
220 return fuse_opt_add_opt(&mo->kernel_opts, arg);
222 case KEY_FUSERMOUNT_OPT:
223 return fuse_opt_add_opt_escaped(&mo->fusermount_opts, arg);
225 case KEY_SUBTYPE_OPT:
226 return fuse_opt_add_opt(&mo->subtype_opt, arg);
229 return fuse_opt_add_opt(&mo->mtab_opts, arg);
248 static int receive_fd(int fd)
254 size_t ccmsg[CMSG_SPACE(sizeof(int)) / sizeof(size_t)];
255 struct cmsghdr *cmsg;
260 memset(&msg, 0, sizeof(msg));
265 /* old BSD implementations should use msg_accrights instead of
266 * msg_control; the interface is different. */
267 msg.msg_control = ccmsg;
268 msg.msg_controllen = sizeof(ccmsg);
270 while(((rv = recvmsg(fd, &msg, 0)) == -1) && errno == EINTR);
280 cmsg = CMSG_FIRSTHDR(&msg);
281 if (!cmsg->cmsg_type == SCM_RIGHTS) {
282 fprintf(stderr, "got control message of unknown type %d\n",
286 return *(int*)CMSG_DATA(cmsg);
289 void fuse_kern_unmount(const char *mountpoint, int fd)
302 res = poll(&pfd, 1, 0);
303 /* If file poll returns POLLERR on the device file descriptor,
304 then the filesystem is already unmounted */
305 if (res == 1 && (pfd.revents & POLLERR))
308 /* Need to close file descriptor, otherwise synchronous umount
309 would recurse into filesystem, and deadlock */
313 if (geteuid() == 0) {
314 fuse_mnt_umount("fuse", mountpoint, mountpoint, 1);
318 res = umount2(mountpoint, 2);
327 const char *argv[] = { FUSERMOUNT_PROG, "-u", "-q", "-z",
328 "--", mountpoint, NULL };
330 exec_fusermount(argv);
333 waitpid(pid, NULL, 0);
336 void fuse_unmount_compat22(const char *mountpoint)
338 fuse_kern_unmount(mountpoint, -1);
341 static int fuse_mount_fusermount(const char *mountpoint, struct mount_opts *mo,
342 const char *opts, int quiet)
349 fprintf(stderr, "fuse: missing mountpoint parameter\n");
353 res = socketpair(PF_UNIX, SOCK_STREAM, 0, fds);
355 perror("fuse: socketpair() failed");
361 perror("fuse: fork() failed");
369 const char *argv[32];
373 int fd = open("/dev/null", O_RDONLY);
380 argv[a++] = FUSERMOUNT_PROG;
386 argv[a++] = mountpoint;
390 fcntl(fds[0], F_SETFD, 0);
391 snprintf(env, sizeof(env), "%i", fds[0]);
392 setenv(FUSE_COMMFD_ENV, env, 1);
393 exec_fusermount(argv);
394 perror("fuse: failed to exec fusermount");
399 rv = receive_fd(fds[1]);
401 if (!mo->auto_unmount) {
402 /* with auto_unmount option fusermount will not exit until
403 this socket is closed */
405 waitpid(pid, NULL, 0); /* bury zombie */
411 int fuse_mount_compat22(const char *mountpoint, const char *opts)
413 struct mount_opts mo;
414 memset(&mo, 0, sizeof(mo));
415 mo.flags = MS_NOSUID | MS_NODEV;
417 return fuse_mount_fusermount(mountpoint, &mo, opts, 0);
420 static int fuse_mount_sys(const char *mnt, struct mount_opts *mo,
421 const char *mnt_opts)
424 const char *devname = "/dev/fuse";
432 fprintf(stderr, "fuse: missing mountpoint parameter\n");
436 res = stat(mnt, &stbuf);
438 fprintf(stderr ,"fuse: failed to access mountpoint %s: %s\n",
439 mnt, strerror(errno));
444 res = fuse_mnt_check_empty("fuse", mnt, stbuf.st_mode,
450 if (mo->auto_unmount) {
451 /* Tell the caller to fallback to fusermount because
452 auto-unmount does not work otherwise. */
456 fd = open(devname, O_RDWR);
458 if (errno == ENODEV || errno == ENOENT)
459 fprintf(stderr, "fuse: device not found, try 'modprobe fuse' first\n");
461 fprintf(stderr, "fuse: failed to open %s: %s\n",
462 devname, strerror(errno));
466 snprintf(tmp, sizeof(tmp), "fd=%i,rootmode=%o,user_id=%i,group_id=%i",
467 fd, stbuf.st_mode & S_IFMT, getuid(), getgid());
469 res = fuse_opt_add_opt(&mo->kernel_opts, tmp);
473 source = malloc((mo->fsname ? strlen(mo->fsname) : 0) +
474 (mo->subtype ? strlen(mo->subtype) : 0) +
475 strlen(devname) + 32);
477 type = malloc((mo->subtype ? strlen(mo->subtype) : 0) + 32);
478 if (!type || !source) {
479 fprintf(stderr, "fuse: failed to allocate memory\n");
483 strcpy(type, mo->blkdev ? "fuseblk" : "fuse");
486 strcat(type, mo->subtype);
489 mo->fsname ? mo->fsname : (mo->subtype ? mo->subtype : devname));
491 res = mount(source, mnt, type, mo->flags, mo->kernel_opts);
492 if (res == -1 && errno == ENODEV && mo->subtype) {
493 /* Probably missing subtype support */
494 strcpy(type, mo->blkdev ? "fuseblk" : "fuse");
497 sprintf(source, "%s#%s", mo->subtype,
500 strcpy(source, type);
502 res = mount(source, mnt, type, mo->flags, mo->kernel_opts);
506 * Maybe kernel doesn't support unprivileged mounts, in this
507 * case try falling back to fusermount
509 if (errno == EPERM) {
512 int errno_save = errno;
513 if (mo->blkdev && errno == ENODEV &&
514 !fuse_mnt_check_fuseblk())
516 "fuse: 'fuseblk' support missing\n");
518 fprintf(stderr, "fuse: mount failed: %s\n",
519 strerror(errno_save));
527 if (geteuid() == 0) {
528 char *newmnt = fuse_mnt_resolve_path("fuse", mnt);
533 res = fuse_mnt_add_mount("fuse", source, newmnt, type,
539 #endif /* IGNORE_MTAB */
540 #endif /* __NetBSD__ */
547 umount2(mnt, 2); /* lazy umount */
555 static int get_mnt_flag_opts(char **mnt_optsp, int flags)
559 if (!(flags & MS_RDONLY) && fuse_opt_add_opt(mnt_optsp, "rw") == -1)
562 for (i = 0; mount_flags[i].opt != NULL; i++) {
563 if (mount_flags[i].on && (flags & mount_flags[i].flag) &&
564 fuse_opt_add_opt(mnt_optsp, mount_flags[i].opt) == -1)
570 int fuse_kern_mount(const char *mountpoint, struct fuse_args *args)
572 struct mount_opts mo;
574 char *mnt_opts = NULL;
576 memset(&mo, 0, sizeof(mo));
577 mo.flags = MS_NOSUID | MS_NODEV;
580 fuse_opt_parse(args, &mo, fuse_mount_opts, fuse_mount_opt_proc) == -1)
583 if (mo.allow_other && mo.allow_root) {
584 fprintf(stderr, "fuse: 'allow_other' and 'allow_root' options are mutually exclusive\n");
592 if (get_mnt_flag_opts(&mnt_opts, mo.flags) == -1)
594 if (mo.kernel_opts && fuse_opt_add_opt(&mnt_opts, mo.kernel_opts) == -1)
596 if (mo.mtab_opts && fuse_opt_add_opt(&mnt_opts, mo.mtab_opts) == -1)
599 res = fuse_mount_sys(mountpoint, &mo, mnt_opts);
601 if (mo.fusermount_opts &&
602 fuse_opt_add_opt(&mnt_opts, mo.fusermount_opts) == -1)
606 char *tmp_opts = NULL;
609 if (fuse_opt_add_opt(&tmp_opts, mnt_opts) == -1 ||
610 fuse_opt_add_opt(&tmp_opts, mo.subtype_opt) == -1) {
615 res = fuse_mount_fusermount(mountpoint, &mo, tmp_opts, 1);
618 res = fuse_mount_fusermount(mountpoint, &mo,
621 res = fuse_mount_fusermount(mountpoint, &mo, mnt_opts, 0);
628 free(mo.fusermount_opts);
629 free(mo.subtype_opt);
630 free(mo.kernel_opts);
635 FUSE_SYMVER(".symver fuse_mount_compat22,fuse_mount@FUSE_2.2");
636 FUSE_SYMVER(".symver fuse_unmount_compat22,fuse_unmount@FUSE_2.2");