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
;
256 memset(&msg
, 0, sizeof(msg
));
261 /* old BSD implementations should use msg_accrights instead of
262 * msg_control; the interface is different. */
263 msg
.msg_control
= ccmsg
;
264 msg
.msg_controllen
= sizeof(ccmsg
);
266 while(((rv
= recvmsg(fd
, &msg
, 0)) == -1) && errno
== EINTR
);
276 cmsg
= CMSG_FIRSTHDR(&msg
);
277 if (!cmsg
->cmsg_type
== SCM_RIGHTS
) {
278 fprintf(stderr
, "got control message of unknown type %d\n",
282 return *(int*)CMSG_DATA(cmsg
);
285 void fuse_kern_unmount(const char *mountpoint
, int fd
)
298 res
= poll(&pfd
, 1, 0);
299 /* If file poll returns POLLERR on the device file descriptor,
300 then the filesystem is already unmounted */
301 if (res
== 1 && (pfd
.revents
& POLLERR
))
304 /* Need to close file descriptor, otherwise synchronous umount
305 would recurse into filesystem, and deadlock */
309 if (geteuid() == 0) {
310 fuse_mnt_umount("fuse", mountpoint
, mountpoint
, 1);
314 res
= umount2(mountpoint
, 2);
323 const char *argv
[] = { FUSERMOUNT_PROG
, "-u", "-q", "-z",
324 "--", mountpoint
, NULL
};
326 exec_fusermount(argv
);
329 waitpid(pid
, NULL
, 0);
332 void fuse_unmount_compat22(const char *mountpoint
)
334 fuse_kern_unmount(mountpoint
, -1);
337 static int fuse_mount_fusermount(const char *mountpoint
, const char *opts
,
345 fprintf(stderr
, "fuse: missing mountpoint parameter\n");
349 res
= socketpair(PF_UNIX
, SOCK_STREAM
, 0, fds
);
351 perror("fuse: socketpair() failed");
357 perror("fuse: fork() failed");
365 const char *argv
[32];
369 int fd
= open("/dev/null", O_RDONLY
);
376 argv
[a
++] = FUSERMOUNT_PROG
;
382 argv
[a
++] = mountpoint
;
386 fcntl(fds
[0], F_SETFD
, 0);
387 snprintf(env
, sizeof(env
), "%i", fds
[0]);
388 setenv(FUSE_COMMFD_ENV
, env
, 1);
389 exec_fusermount(argv
);
390 perror("fuse: failed to exec fusermount");
395 rv
= receive_fd(fds
[1]);
397 waitpid(pid
, NULL
, 0); /* bury zombie */
402 int fuse_mount_compat22(const char *mountpoint
, const char *opts
)
404 return fuse_mount_fusermount(mountpoint
, opts
, 0);
407 static int fuse_mount_sys(const char *mnt
, struct mount_opts
*mo
,
408 const char *mnt_opts
)
411 const char *devname
= "/dev/fuse";
419 fprintf(stderr
, "fuse: missing mountpoint parameter\n");
423 res
= stat(mnt
, &stbuf
);
425 fprintf(stderr
,"fuse: failed to access mountpoint %s: %s\n",
426 mnt
, strerror(errno
));
431 res
= fuse_mnt_check_empty("fuse", mnt
, stbuf
.st_mode
,
437 fd
= open(devname
, O_RDWR
);
439 if (errno
== ENODEV
|| errno
== ENOENT
)
440 fprintf(stderr
, "fuse: device not found, try 'modprobe fuse' first\n");
442 fprintf(stderr
, "fuse: failed to open %s: %s\n",
443 devname
, strerror(errno
));
447 snprintf(tmp
, sizeof(tmp
), "fd=%i,rootmode=%o,user_id=%i,group_id=%i",
448 fd
, stbuf
.st_mode
& S_IFMT
, getuid(), getgid());
450 res
= fuse_opt_add_opt(&mo
->kernel_opts
, tmp
);
454 source
= malloc((mo
->fsname
? strlen(mo
->fsname
) : 0) +
455 (mo
->subtype
? strlen(mo
->subtype
) : 0) +
456 strlen(devname
) + 32);
458 type
= malloc((mo
->subtype
? strlen(mo
->subtype
) : 0) + 32);
459 if (!type
|| !source
) {
460 fprintf(stderr
, "fuse: failed to allocate memory\n");
464 strcpy(type
, mo
->blkdev
? "fuseblk" : "fuse");
467 strcat(type
, mo
->subtype
);
470 mo
->fsname
? mo
->fsname
: (mo
->subtype
? mo
->subtype
: devname
));
472 res
= mount(source
, mnt
, type
, mo
->flags
, mo
->kernel_opts
);
473 if (res
== -1 && errno
== ENODEV
&& mo
->subtype
) {
474 /* Probably missing subtype support */
475 strcpy(type
, mo
->blkdev
? "fuseblk" : "fuse");
478 sprintf(source
, "%s#%s", mo
->subtype
,
481 strcpy(source
, type
);
483 res
= mount(source
, mnt
, type
, mo
->flags
, mo
->kernel_opts
);
487 * Maybe kernel doesn't support unprivileged mounts, in this
488 * case try falling back to fusermount
490 if (errno
== EPERM
) {
493 int errno_save
= errno
;
494 if (mo
->blkdev
&& errno
== ENODEV
&&
495 !fuse_mnt_check_fuseblk())
497 "fuse: 'fuseblk' support missing\n");
499 fprintf(stderr
, "fuse: mount failed: %s\n",
500 strerror(errno_save
));
507 if (geteuid() == 0) {
508 char *newmnt
= fuse_mnt_resolve_path("fuse", mnt
);
513 res
= fuse_mnt_add_mount("fuse", source
, newmnt
, type
,
519 #endif /* __NetBSD__ */
526 umount2(mnt
, 2); /* lazy umount */
534 static int get_mnt_flag_opts(char **mnt_optsp
, int flags
)
538 if (!(flags
& MS_RDONLY
) && fuse_opt_add_opt(mnt_optsp
, "rw") == -1)
541 for (i
= 0; mount_flags
[i
].opt
!= NULL
; i
++) {
542 if (mount_flags
[i
].on
&& (flags
& mount_flags
[i
].flag
) &&
543 fuse_opt_add_opt(mnt_optsp
, mount_flags
[i
].opt
) == -1)
549 int fuse_kern_mount(const char *mountpoint
, struct fuse_args
*args
)
551 struct mount_opts mo
;
553 char *mnt_opts
= NULL
;
555 memset(&mo
, 0, sizeof(mo
));
556 mo
.flags
= MS_NOSUID
| MS_NODEV
;
559 fuse_opt_parse(args
, &mo
, fuse_mount_opts
, fuse_mount_opt_proc
) == -1)
562 if (mo
.allow_other
&& mo
.allow_root
) {
563 fprintf(stderr
, "fuse: 'allow_other' and 'allow_root' options are mutually exclusive\n");
571 if (get_mnt_flag_opts(&mnt_opts
, mo
.flags
) == -1)
573 if (mo
.kernel_opts
&& fuse_opt_add_opt(&mnt_opts
, mo
.kernel_opts
) == -1)
575 if (mo
.mtab_opts
&& fuse_opt_add_opt(&mnt_opts
, mo
.mtab_opts
) == -1)
578 res
= fuse_mount_sys(mountpoint
, &mo
, mnt_opts
);
580 if (mo
.fusermount_opts
&&
581 fuse_opt_add_opt(&mnt_opts
, mo
.fusermount_opts
) == -1)
585 char *tmp_opts
= NULL
;
588 if (fuse_opt_add_opt(&tmp_opts
, mnt_opts
) == -1 ||
589 fuse_opt_add_opt(&tmp_opts
, mo
.subtype_opt
) == -1) {
594 res
= fuse_mount_fusermount(mountpoint
, tmp_opts
, 1);
597 res
= fuse_mount_fusermount(mountpoint
,
600 res
= fuse_mount_fusermount(mountpoint
, mnt_opts
, 0);
607 free(mo
.fusermount_opts
);
608 free(mo
.subtype_opt
);
609 free(mo
.kernel_opts
);
614 FUSE_SYMVER(".symver fuse_mount_compat22,fuse_mount@FUSE_2.2");
615 FUSE_SYMVER(".symver fuse_unmount_compat22,fuse_unmount@FUSE_2.2");