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 "mount_util.h"
23 #include <sys/socket.h>
26 #include <sys/mount.h>
31 #define MS_RDONLY MNT_RDONLY
32 #define MS_NOSUID MNT_NOSUID
33 #define MS_NODEV MNT_NODEV
34 #define MS_NOEXEC MNT_NOEXEC
35 #define MS_SYNCHRONOUS MNT_SYNCHRONOUS
36 #define MS_NOATIME MNT_NOATIME
38 #define umount2(mnt, flags) unmount(mnt, (flags == 2) ? MNT_FORCE : 0)
41 #define FUSERMOUNT_PROG "fusermount"
42 #define FUSE_COMMFD_ENV "_FUSE_COMMFD"
45 #define fork() vfork()
49 #define MS_DIRSYNC 128
76 char *fusermount_opts
;
80 #define FUSE_MOUNT_OPT(t, p) { t, offsetof(struct mount_opts, p), 1 }
82 static const struct fuse_opt fuse_mount_opts
[] = {
83 FUSE_MOUNT_OPT("allow_other", allow_other
),
84 FUSE_MOUNT_OPT("allow_root", allow_root
),
85 FUSE_MOUNT_OPT("nonempty", nonempty
),
86 FUSE_MOUNT_OPT("blkdev", blkdev
),
87 FUSE_MOUNT_OPT("auto_unmount", auto_unmount
),
88 FUSE_MOUNT_OPT("fsname=%s", fsname
),
89 FUSE_MOUNT_OPT("subtype=%s", subtype
),
90 FUSE_OPT_KEY("allow_other", KEY_KERN_OPT
),
91 FUSE_OPT_KEY("allow_root", KEY_ALLOW_ROOT
),
92 FUSE_OPT_KEY("nonempty", KEY_FUSERMOUNT_OPT
),
93 FUSE_OPT_KEY("auto_unmount", KEY_FUSERMOUNT_OPT
),
94 FUSE_OPT_KEY("blkdev", KEY_FUSERMOUNT_OPT
),
95 FUSE_OPT_KEY("fsname=", KEY_FUSERMOUNT_OPT
),
96 FUSE_OPT_KEY("subtype=", KEY_SUBTYPE_OPT
),
97 FUSE_OPT_KEY("large_read", KEY_KERN_OPT
),
98 FUSE_OPT_KEY("blksize=", KEY_KERN_OPT
),
99 FUSE_OPT_KEY("default_permissions", KEY_KERN_OPT
),
100 FUSE_OPT_KEY("context=", KEY_KERN_OPT
),
101 FUSE_OPT_KEY("fscontext=", KEY_KERN_OPT
),
102 FUSE_OPT_KEY("defcontext=", KEY_KERN_OPT
),
103 FUSE_OPT_KEY("rootcontext=", KEY_KERN_OPT
),
104 FUSE_OPT_KEY("max_read=", KEY_KERN_OPT
),
105 FUSE_OPT_KEY("max_read=", FUSE_OPT_KEY_KEEP
),
106 FUSE_OPT_KEY("user=", KEY_MTAB_OPT
),
107 FUSE_OPT_KEY("-r", KEY_RO
),
108 FUSE_OPT_KEY("ro", KEY_KERN_FLAG
),
109 FUSE_OPT_KEY("rw", KEY_KERN_FLAG
),
110 FUSE_OPT_KEY("suid", KEY_KERN_FLAG
),
111 FUSE_OPT_KEY("nosuid", KEY_KERN_FLAG
),
112 FUSE_OPT_KEY("dev", KEY_KERN_FLAG
),
113 FUSE_OPT_KEY("nodev", KEY_KERN_FLAG
),
114 FUSE_OPT_KEY("exec", KEY_KERN_FLAG
),
115 FUSE_OPT_KEY("noexec", KEY_KERN_FLAG
),
116 FUSE_OPT_KEY("async", KEY_KERN_FLAG
),
117 FUSE_OPT_KEY("sync", KEY_KERN_FLAG
),
118 FUSE_OPT_KEY("dirsync", KEY_KERN_FLAG
),
119 FUSE_OPT_KEY("atime", KEY_KERN_FLAG
),
120 FUSE_OPT_KEY("noatime", KEY_KERN_FLAG
),
121 FUSE_OPT_KEY("-h", KEY_HELP
),
122 FUSE_OPT_KEY("--help", KEY_HELP
),
123 FUSE_OPT_KEY("-V", KEY_VERSION
),
124 FUSE_OPT_KEY("--version", KEY_VERSION
),
128 static void mount_help(void)
131 " -o allow_other allow access to other users\n"
132 " -o allow_root allow access to root\n"
133 " -o auto_unmount auto unmount on process termination\n"
134 " -o nonempty allow mounts over non-empty file/dir\n"
135 " -o default_permissions enable permission checking by kernel\n"
136 " -o fsname=NAME set filesystem name\n"
137 " -o subtype=NAME set filesystem type\n"
138 " -o large_read issue large read requests (2.4 only)\n"
139 " -o max_read=N set maximum size of read requests\n"
143 static void exec_fusermount(const char *argv
[])
145 execv(FUSERMOUNT_DIR
"/" FUSERMOUNT_PROG
, (char **) argv
);
146 execvp(FUSERMOUNT_PROG
, (char **) argv
);
149 static void mount_version(void)
153 const char *argv
[] = { FUSERMOUNT_PROG
, "--version", NULL
};
154 exec_fusermount(argv
);
156 } else if (pid
!= -1)
157 waitpid(pid
, NULL
, 0);
166 static const struct mount_flags mount_flags
[] = {
167 {"rw", MS_RDONLY
, 0},
168 {"ro", MS_RDONLY
, 1},
169 {"suid", MS_NOSUID
, 0},
170 {"nosuid", MS_NOSUID
, 1},
171 {"dev", MS_NODEV
, 0},
172 {"nodev", MS_NODEV
, 1},
173 {"exec", MS_NOEXEC
, 0},
174 {"noexec", MS_NOEXEC
, 1},
175 {"async", MS_SYNCHRONOUS
, 0},
176 {"sync", MS_SYNCHRONOUS
, 1},
177 {"atime", MS_NOATIME
, 0},
178 {"noatime", MS_NOATIME
, 1},
180 {"dirsync", MS_DIRSYNC
, 1},
185 static void set_mount_flag(const char *s
, int *flags
)
189 for (i
= 0; mount_flags
[i
].opt
!= NULL
; i
++) {
190 const char *opt
= mount_flags
[i
].opt
;
191 if (strcmp(opt
, s
) == 0) {
192 if (mount_flags
[i
].on
)
193 *flags
|= mount_flags
[i
].flag
;
195 *flags
&= ~mount_flags
[i
].flag
;
199 fprintf(stderr
, "fuse: internal error, can't find mount flag\n");
203 static int fuse_mount_opt_proc(void *data
, const char *arg
, int key
,
204 struct fuse_args
*outargs
)
206 struct mount_opts
*mo
= data
;
210 if (fuse_opt_add_opt(&mo
->kernel_opts
, "allow_other") == -1 ||
211 fuse_opt_add_arg(outargs
, "-oallow_root") == -1)
219 set_mount_flag(arg
, &mo
->flags
);
223 return fuse_opt_add_opt(&mo
->kernel_opts
, arg
);
225 case KEY_FUSERMOUNT_OPT
:
226 return fuse_opt_add_opt_escaped(&mo
->fusermount_opts
, arg
);
228 case KEY_SUBTYPE_OPT
:
229 return fuse_opt_add_opt(&mo
->subtype_opt
, arg
);
232 return fuse_opt_add_opt(&mo
->mtab_opts
, arg
);
251 static int receive_fd(int fd
)
257 size_t ccmsg
[CMSG_SPACE(sizeof(int)) / sizeof(size_t)];
258 struct cmsghdr
*cmsg
;
263 memset(&msg
, 0, sizeof(msg
));
268 /* old BSD implementations should use msg_accrights instead of
269 * msg_control; the interface is different. */
270 msg
.msg_control
= ccmsg
;
271 msg
.msg_controllen
= sizeof(ccmsg
);
273 while(((rv
= recvmsg(fd
, &msg
, 0)) == -1) && errno
== EINTR
);
283 cmsg
= CMSG_FIRSTHDR(&msg
);
284 if (!cmsg
->cmsg_type
== SCM_RIGHTS
) {
285 fprintf(stderr
, "got control message of unknown type %d\n",
289 return *(int*)CMSG_DATA(cmsg
);
292 void fuse_kern_unmount(const char *mountpoint
, int fd
)
302 res
= poll(&pfd
, 1, 0);
304 /* Need to close file descriptor, otherwise synchronous umount
305 would recurse into filesystem, and deadlock.
307 Caller expects fuse_kern_unmount to close the fd, so close it
311 /* If file poll returns POLLERR on the device file descriptor,
312 then the filesystem is already unmounted */
313 if (res
== 1 && (pfd
.revents
& POLLERR
))
317 if (geteuid() == 0) {
318 fuse_mnt_umount("fuse", mountpoint
, mountpoint
, 1);
322 res
= umount2(mountpoint
, 2);
331 const char *argv
[] = { FUSERMOUNT_PROG
, "-u", "-q", "-z",
332 "--", mountpoint
, NULL
};
334 exec_fusermount(argv
);
337 waitpid(pid
, NULL
, 0);
340 static int fuse_mount_fusermount(const char *mountpoint
, struct mount_opts
*mo
,
341 const char *opts
, int quiet
)
348 fprintf(stderr
, "fuse: missing mountpoint parameter\n");
352 res
= socketpair(PF_UNIX
, SOCK_STREAM
, 0, fds
);
354 perror("fuse: socketpair() failed");
360 perror("fuse: fork() failed");
368 const char *argv
[32];
372 int fd
= open("/dev/null", O_RDONLY
);
379 argv
[a
++] = FUSERMOUNT_PROG
;
385 argv
[a
++] = mountpoint
;
389 fcntl(fds
[0], F_SETFD
, 0);
390 snprintf(env
, sizeof(env
), "%i", fds
[0]);
391 setenv(FUSE_COMMFD_ENV
, env
, 1);
392 exec_fusermount(argv
);
393 perror("fuse: failed to exec fusermount");
398 rv
= receive_fd(fds
[1]);
400 if (!mo
->auto_unmount
) {
401 /* with auto_unmount option fusermount will not exit until
402 this socket is closed */
404 waitpid(pid
, NULL
, 0); /* bury zombie */
408 fcntl(rv
, F_SETFD
, FD_CLOEXEC
);
417 static int fuse_mount_sys(const char *mnt
, struct mount_opts
*mo
,
418 const char *mnt_opts
)
421 const char *devname
= "/dev/fuse";
429 fprintf(stderr
, "fuse: missing mountpoint parameter\n");
433 res
= stat(mnt
, &stbuf
);
435 fprintf(stderr
,"fuse: failed to access mountpoint %s: %s\n",
436 mnt
, strerror(errno
));
441 res
= fuse_mnt_check_empty("fuse", mnt
, stbuf
.st_mode
,
447 if (mo
->auto_unmount
) {
448 /* Tell the caller to fallback to fusermount because
449 auto-unmount does not work otherwise. */
453 fd
= open(devname
, O_RDWR
| O_CLOEXEC
);
455 if (errno
== ENODEV
|| errno
== ENOENT
)
456 fprintf(stderr
, "fuse: device not found, try 'modprobe fuse' first\n");
458 fprintf(stderr
, "fuse: failed to open %s: %s\n",
459 devname
, strerror(errno
));
463 fcntl(fd
, F_SETFD
, FD_CLOEXEC
);
465 snprintf(tmp
, sizeof(tmp
), "fd=%i,rootmode=%o,user_id=%u,group_id=%u",
466 fd
, stbuf
.st_mode
& S_IFMT
, getuid(), getgid());
468 res
= fuse_opt_add_opt(&mo
->kernel_opts
, tmp
);
472 source
= malloc((mo
->fsname
? strlen(mo
->fsname
) : 0) +
473 (mo
->subtype
? strlen(mo
->subtype
) : 0) +
474 strlen(devname
) + 32);
476 type
= malloc((mo
->subtype
? strlen(mo
->subtype
) : 0) + 32);
477 if (!type
|| !source
) {
478 fprintf(stderr
, "fuse: failed to allocate memory\n");
482 strcpy(type
, mo
->blkdev
? "fuseblk" : "fuse");
485 strcat(type
, mo
->subtype
);
488 mo
->fsname
? mo
->fsname
: (mo
->subtype
? mo
->subtype
: devname
));
490 res
= mount(source
, mnt
, type
, mo
->flags
, mo
->kernel_opts
);
491 if (res
== -1 && errno
== ENODEV
&& mo
->subtype
) {
492 /* Probably missing subtype support */
493 strcpy(type
, mo
->blkdev
? "fuseblk" : "fuse");
496 sprintf(source
, "%s#%s", mo
->subtype
,
499 strcpy(source
, type
);
501 res
= mount(source
, mnt
, type
, mo
->flags
, mo
->kernel_opts
);
505 * Maybe kernel doesn't support unprivileged mounts, in this
506 * case try falling back to fusermount
508 if (errno
== EPERM
) {
511 int errno_save
= errno
;
512 if (mo
->blkdev
&& errno
== ENODEV
&&
513 !fuse_mnt_check_fuseblk())
515 "fuse: 'fuseblk' support missing\n");
517 fprintf(stderr
, "fuse: mount failed: %s\n",
518 strerror(errno_save
));
526 if (geteuid() == 0) {
527 char *newmnt
= fuse_mnt_resolve_path("fuse", mnt
);
532 res
= fuse_mnt_add_mount("fuse", source
, newmnt
, type
,
538 #endif /* IGNORE_MTAB */
539 #endif /* __NetBSD__ */
546 umount2(mnt
, 2); /* lazy umount */
554 static int get_mnt_flag_opts(char **mnt_optsp
, int flags
)
558 if (!(flags
& MS_RDONLY
) && fuse_opt_add_opt(mnt_optsp
, "rw") == -1)
561 for (i
= 0; mount_flags
[i
].opt
!= NULL
; i
++) {
562 if (mount_flags
[i
].on
&& (flags
& mount_flags
[i
].flag
) &&
563 fuse_opt_add_opt(mnt_optsp
, mount_flags
[i
].opt
) == -1)
569 int fuse_kern_mount(const char *mountpoint
, struct fuse_args
*args
)
571 struct mount_opts mo
;
573 char *mnt_opts
= NULL
;
575 memset(&mo
, 0, sizeof(mo
));
576 mo
.flags
= MS_NOSUID
| MS_NODEV
;
579 fuse_opt_parse(args
, &mo
, fuse_mount_opts
, fuse_mount_opt_proc
) == -1)
582 if (mo
.allow_other
&& mo
.allow_root
) {
583 fprintf(stderr
, "fuse: 'allow_other' and 'allow_root' options are mutually exclusive\n");
591 if (get_mnt_flag_opts(&mnt_opts
, mo
.flags
) == -1)
593 if (mo
.kernel_opts
&& fuse_opt_add_opt(&mnt_opts
, mo
.kernel_opts
) == -1)
595 if (mo
.mtab_opts
&& fuse_opt_add_opt(&mnt_opts
, mo
.mtab_opts
) == -1)
598 res
= fuse_mount_sys(mountpoint
, &mo
, mnt_opts
);
600 if (mo
.fusermount_opts
&&
601 fuse_opt_add_opt(&mnt_opts
, mo
.fusermount_opts
) == -1)
605 char *tmp_opts
= NULL
;
608 if (fuse_opt_add_opt(&tmp_opts
, mnt_opts
) == -1 ||
609 fuse_opt_add_opt(&tmp_opts
, mo
.subtype_opt
) == -1) {
614 res
= fuse_mount_fusermount(mountpoint
, &mo
, tmp_opts
, 1);
617 res
= fuse_mount_fusermount(mountpoint
, &mo
,
620 res
= fuse_mount_fusermount(mountpoint
, &mo
, mnt_opts
, 0);
627 free(mo
.fusermount_opts
);
628 free(mo
.subtype_opt
);
629 free(mo
.kernel_opts
);