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
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("fsname=%s", fsname
),
88 FUSE_MOUNT_OPT("subtype=%s", subtype
),
89 FUSE_OPT_KEY("allow_other", KEY_KERN_OPT
),
90 FUSE_OPT_KEY("allow_root", KEY_ALLOW_ROOT
),
91 FUSE_OPT_KEY("nonempty", KEY_FUSERMOUNT_OPT
),
92 FUSE_OPT_KEY("blkdev", KEY_FUSERMOUNT_OPT
),
93 FUSE_OPT_KEY("fsname=", KEY_FUSERMOUNT_OPT
),
94 FUSE_OPT_KEY("subtype=", KEY_SUBTYPE_OPT
),
95 FUSE_OPT_KEY("large_read", KEY_KERN_OPT
),
96 FUSE_OPT_KEY("blksize=", KEY_KERN_OPT
),
97 FUSE_OPT_KEY("default_permissions", KEY_KERN_OPT
),
98 FUSE_OPT_KEY("max_read=", KEY_KERN_OPT
),
99 FUSE_OPT_KEY("max_read=", FUSE_OPT_KEY_KEEP
),
100 FUSE_OPT_KEY("user=", KEY_MTAB_OPT
),
101 FUSE_OPT_KEY("-r", KEY_RO
),
102 FUSE_OPT_KEY("ro", KEY_KERN_FLAG
),
103 FUSE_OPT_KEY("rw", KEY_KERN_FLAG
),
104 FUSE_OPT_KEY("suid", KEY_KERN_FLAG
),
105 FUSE_OPT_KEY("nosuid", KEY_KERN_FLAG
),
106 FUSE_OPT_KEY("dev", KEY_KERN_FLAG
),
107 FUSE_OPT_KEY("nodev", KEY_KERN_FLAG
),
108 FUSE_OPT_KEY("exec", KEY_KERN_FLAG
),
109 FUSE_OPT_KEY("noexec", KEY_KERN_FLAG
),
110 FUSE_OPT_KEY("async", KEY_KERN_FLAG
),
111 FUSE_OPT_KEY("sync", KEY_KERN_FLAG
),
112 FUSE_OPT_KEY("dirsync", KEY_KERN_FLAG
),
113 FUSE_OPT_KEY("atime", KEY_KERN_FLAG
),
114 FUSE_OPT_KEY("noatime", KEY_KERN_FLAG
),
115 FUSE_OPT_KEY("-h", KEY_HELP
),
116 FUSE_OPT_KEY("--help", KEY_HELP
),
117 FUSE_OPT_KEY("-V", KEY_VERSION
),
118 FUSE_OPT_KEY("--version", KEY_VERSION
),
122 static void mount_help(void)
125 " -o allow_other allow access to other users\n"
126 " -o allow_root allow access to root\n"
127 " -o nonempty allow mounts over non-empty file/dir\n"
128 " -o default_permissions enable permission checking by kernel\n"
129 " -o fsname=NAME set filesystem name\n"
130 " -o subtype=NAME set filesystem type\n"
131 " -o large_read issue large read requests (2.4 only)\n"
132 " -o max_read=N set maximum size of read requests\n"
136 static void exec_fusermount(const char *argv
[])
138 execv(FUSERMOUNT_DIR
"/" FUSERMOUNT_PROG
, (char **) argv
);
139 execvp(FUSERMOUNT_PROG
, (char **) argv
);
142 static void mount_version(void)
146 const char *argv
[] = { FUSERMOUNT_PROG
, "--version", NULL
};
147 exec_fusermount(argv
);
149 } else if (pid
!= -1)
150 waitpid(pid
, NULL
, 0);
159 static struct mount_flags mount_flags
[] = {
160 {"rw", MS_RDONLY
, 0},
161 {"ro", MS_RDONLY
, 1},
162 {"suid", MS_NOSUID
, 0},
163 {"nosuid", MS_NOSUID
, 1},
164 {"dev", MS_NODEV
, 0},
165 {"nodev", MS_NODEV
, 1},
166 {"exec", MS_NOEXEC
, 0},
167 {"noexec", MS_NOEXEC
, 1},
168 {"async", MS_SYNCHRONOUS
, 0},
169 {"sync", MS_SYNCHRONOUS
, 1},
170 {"atime", MS_NOATIME
, 0},
171 {"noatime", MS_NOATIME
, 1},
173 {"dirsync", MS_DIRSYNC
, 1},
178 static void set_mount_flag(const char *s
, int *flags
)
182 for (i
= 0; mount_flags
[i
].opt
!= NULL
; i
++) {
183 const char *opt
= mount_flags
[i
].opt
;
184 if (strcmp(opt
, s
) == 0) {
185 if (mount_flags
[i
].on
)
186 *flags
|= mount_flags
[i
].flag
;
188 *flags
&= ~mount_flags
[i
].flag
;
192 fprintf(stderr
, "fuse: internal error, can't find mount flag\n");
196 static int fuse_mount_opt_proc(void *data
, const char *arg
, int key
,
197 struct fuse_args
*outargs
)
199 struct mount_opts
*mo
= data
;
203 if (fuse_opt_add_opt(&mo
->kernel_opts
, "allow_other") == -1 ||
204 fuse_opt_add_arg(outargs
, "-oallow_root") == -1)
212 set_mount_flag(arg
, &mo
->flags
);
216 return fuse_opt_add_opt(&mo
->kernel_opts
, arg
);
218 case KEY_FUSERMOUNT_OPT
:
219 return fuse_opt_add_opt_escaped(&mo
->fusermount_opts
, arg
);
221 case KEY_SUBTYPE_OPT
:
222 return fuse_opt_add_opt(&mo
->subtype_opt
, arg
);
225 return fuse_opt_add_opt(&mo
->mtab_opts
, arg
);
244 static int receive_fd(int fd
)
250 size_t ccmsg
[CMSG_SPACE(sizeof(int)) / sizeof(size_t)];
251 struct cmsghdr
*cmsg
;
260 /* old BSD implementations should use msg_accrights instead of
261 * msg_control; the interface is different. */
262 msg
.msg_control
= ccmsg
;
263 msg
.msg_controllen
= sizeof(ccmsg
);
265 while(((rv
= recvmsg(fd
, &msg
, 0)) == -1) && errno
== EINTR
);
275 cmsg
= CMSG_FIRSTHDR(&msg
);
276 if (!cmsg
->cmsg_type
== SCM_RIGHTS
) {
277 fprintf(stderr
, "got control message of unknown type %d\n",
281 return *(int*)CMSG_DATA(cmsg
);
284 void fuse_kern_unmount(const char *mountpoint
, int fd
)
297 res
= poll(&pfd
, 1, 0);
298 /* If file poll returns POLLERR on the device file descriptor,
299 then the filesystem is already unmounted */
300 if (res
== 1 && (pfd
.revents
& POLLERR
))
303 /* Need to close file descriptor, otherwise synchronous umount
304 would recurse into filesystem, and deadlock */
308 if (geteuid() == 0) {
309 fuse_mnt_umount("fuse", mountpoint
, mountpoint
, 1);
313 res
= umount2(mountpoint
, 2);
322 const char *argv
[] = { FUSERMOUNT_PROG
, "-u", "-q", "-z",
323 "--", mountpoint
, NULL
};
325 exec_fusermount(argv
);
328 waitpid(pid
, NULL
, 0);
331 void fuse_unmount_compat22(const char *mountpoint
)
333 fuse_kern_unmount(mountpoint
, -1);
336 static int fuse_mount_fusermount(const char *mountpoint
, const char *opts
,
344 fprintf(stderr
, "fuse: missing mountpoint parameter\n");
348 res
= socketpair(PF_UNIX
, SOCK_STREAM
, 0, fds
);
350 perror("fuse: socketpair() failed");
356 perror("fuse: fork() failed");
364 const char *argv
[32];
368 int fd
= open("/dev/null", O_RDONLY
);
373 argv
[a
++] = FUSERMOUNT_PROG
;
379 argv
[a
++] = mountpoint
;
383 fcntl(fds
[0], F_SETFD
, 0);
384 snprintf(env
, sizeof(env
), "%i", fds
[0]);
385 setenv(FUSE_COMMFD_ENV
, env
, 1);
386 exec_fusermount(argv
);
387 perror("fuse: failed to exec fusermount");
392 rv
= receive_fd(fds
[1]);
394 waitpid(pid
, NULL
, 0); /* bury zombie */
399 int fuse_mount_compat22(const char *mountpoint
, const char *opts
)
401 return fuse_mount_fusermount(mountpoint
, opts
, 0);
404 static int fuse_mount_sys(const char *mnt
, struct mount_opts
*mo
,
405 const char *mnt_opts
)
408 const char *devname
= "/dev/fuse";
416 fprintf(stderr
, "fuse: missing mountpoint parameter\n");
420 res
= stat(mnt
, &stbuf
);
422 fprintf(stderr
,"fuse: failed to access mountpoint %s: %s\n",
423 mnt
, strerror(errno
));
428 res
= fuse_mnt_check_empty("fuse", mnt
, stbuf
.st_mode
,
434 fd
= open(devname
, O_RDWR
);
436 if (errno
== ENODEV
|| errno
== ENOENT
)
437 fprintf(stderr
, "fuse: device not found, try 'modprobe fuse' first\n");
439 fprintf(stderr
, "fuse: failed to open %s: %s\n",
440 devname
, strerror(errno
));
444 snprintf(tmp
, sizeof(tmp
), "fd=%i,rootmode=%o,user_id=%i,group_id=%i",
445 fd
, stbuf
.st_mode
& S_IFMT
, getuid(), getgid());
447 res
= fuse_opt_add_opt(&mo
->kernel_opts
, tmp
);
451 source
= malloc((mo
->fsname
? strlen(mo
->fsname
) : 0) +
452 (mo
->subtype
? strlen(mo
->subtype
) : 0) +
453 strlen(devname
) + 32);
455 type
= malloc((mo
->subtype
? strlen(mo
->subtype
) : 0) + 32);
456 if (!type
|| !source
) {
457 fprintf(stderr
, "fuse: failed to allocate memory\n");
461 strcpy(type
, mo
->blkdev
? "fuseblk" : "fuse");
464 strcat(type
, mo
->subtype
);
467 mo
->fsname
? mo
->fsname
: (mo
->subtype
? mo
->subtype
: devname
));
469 res
= mount(source
, mnt
, type
, mo
->flags
, mo
->kernel_opts
);
470 if (res
== -1 && errno
== ENODEV
&& mo
->subtype
) {
471 /* Probably missing subtype support */
472 strcpy(type
, mo
->blkdev
? "fuseblk" : "fuse");
475 sprintf(source
, "%s#%s", mo
->subtype
,
478 strcpy(source
, type
);
480 res
= mount(source
, mnt
, type
, mo
->flags
, mo
->kernel_opts
);
484 * Maybe kernel doesn't support unprivileged mounts, in this
485 * case try falling back to fusermount
487 if (errno
== EPERM
) {
490 int errno_save
= errno
;
491 if (mo
->blkdev
&& errno
== ENODEV
&&
492 !fuse_mnt_check_fuseblk())
494 "fuse: 'fuseblk' support missing\n");
496 fprintf(stderr
, "fuse: mount failed: %s\n",
497 strerror(errno_save
));
504 if (geteuid() == 0) {
505 char *newmnt
= fuse_mnt_resolve_path("fuse", mnt
);
510 res
= fuse_mnt_add_mount("fuse", source
, newmnt
, type
,
516 #endif /* __NetBSD__ */
523 umount2(mnt
, 2); /* lazy umount */
531 static int get_mnt_flag_opts(char **mnt_optsp
, int flags
)
535 if (!(flags
& MS_RDONLY
) && fuse_opt_add_opt(mnt_optsp
, "rw") == -1)
538 for (i
= 0; mount_flags
[i
].opt
!= NULL
; i
++) {
539 if (mount_flags
[i
].on
&& (flags
& mount_flags
[i
].flag
) &&
540 fuse_opt_add_opt(mnt_optsp
, mount_flags
[i
].opt
) == -1)
546 int fuse_kern_mount(const char *mountpoint
, struct fuse_args
*args
)
548 struct mount_opts mo
;
550 char *mnt_opts
= NULL
;
552 memset(&mo
, 0, sizeof(mo
));
553 mo
.flags
= MS_NOSUID
| MS_NODEV
;
556 fuse_opt_parse(args
, &mo
, fuse_mount_opts
, fuse_mount_opt_proc
) == -1)
559 if (mo
.allow_other
&& mo
.allow_root
) {
560 fprintf(stderr
, "fuse: 'allow_other' and 'allow_root' options are mutually exclusive\n");
568 if (get_mnt_flag_opts(&mnt_opts
, mo
.flags
) == -1)
570 if (mo
.kernel_opts
&& fuse_opt_add_opt(&mnt_opts
, mo
.kernel_opts
) == -1)
572 if (mo
.mtab_opts
&& fuse_opt_add_opt(&mnt_opts
, mo
.mtab_opts
) == -1)
575 res
= fuse_mount_sys(mountpoint
, &mo
, mnt_opts
);
577 if (mo
.fusermount_opts
&&
578 fuse_opt_add_opt(&mnt_opts
, mo
.fusermount_opts
) == -1)
582 char *tmp_opts
= NULL
;
585 if (fuse_opt_add_opt(&tmp_opts
, mnt_opts
) == -1 ||
586 fuse_opt_add_opt(&tmp_opts
, mo
.subtype_opt
) == -1) {
591 res
= fuse_mount_fusermount(mountpoint
, tmp_opts
, 1);
594 res
= fuse_mount_fusermount(mountpoint
,
597 res
= fuse_mount_fusermount(mountpoint
, mnt_opts
, 0);
604 free(mo
.fusermount_opts
);
605 free(mo
.subtype_opt
);
606 free(mo
.kernel_opts
);
611 FUSE_SYMVER(".symver fuse_mount_compat22,fuse_mount@FUSE_2.2");
612 FUSE_SYMVER(".symver fuse_unmount_compat22,fuse_unmount@FUSE_2.2");