libfuse: add "time_gran" option
[fuse.git] / lib / mount.c
blob111b32b509807185ce2a2adbb9795c647207c306
1 /*
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.
7 */
9 #include "config.h"
10 #include "fuse_i.h"
11 #include "fuse_misc.h"
12 #include "fuse_opt.h"
13 #include "mount_util.h"
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <unistd.h>
18 #include <stddef.h>
19 #include <string.h>
20 #include <fcntl.h>
21 #include <errno.h>
22 #include <sys/poll.h>
23 #include <sys/socket.h>
24 #include <sys/un.h>
25 #include <sys/wait.h>
26 #include <sys/mount.h>
28 #ifdef __NetBSD__
29 #include <perfuse.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)
39 #endif
41 #define FUSERMOUNT_PROG "fusermount"
42 #define FUSE_COMMFD_ENV "_FUSE_COMMFD"
44 #ifndef HAVE_FORK
45 #define fork() vfork()
46 #endif
48 #ifndef MS_DIRSYNC
49 #define MS_DIRSYNC 128
50 #endif
52 enum {
53 KEY_KERN_FLAG,
54 KEY_KERN_OPT,
55 KEY_FUSERMOUNT_OPT,
56 KEY_SUBTYPE_OPT,
57 KEY_MTAB_OPT,
58 KEY_ALLOW_ROOT,
59 KEY_RO,
60 KEY_HELP,
61 KEY_VERSION,
64 struct mount_opts {
65 int allow_other;
66 int allow_root;
67 int ishelp;
68 int flags;
69 int nonempty;
70 int auto_unmount;
71 int blkdev;
72 char *fsname;
73 char *subtype;
74 char *subtype_opt;
75 char *mtab_opts;
76 char *fusermount_opts;
77 char *kernel_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),
125 FUSE_OPT_END
128 static void mount_help(void)
130 printf(
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"
140 "\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)
151 int pid = fork();
152 if (!pid) {
153 const char *argv[] = { FUSERMOUNT_PROG, "--version", NULL };
154 exec_fusermount(argv);
155 _exit(1);
156 } else if (pid != -1)
157 waitpid(pid, NULL, 0);
160 struct mount_flags {
161 const char *opt;
162 unsigned long flag;
163 int on;
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},
179 #ifndef __NetBSD__
180 {"dirsync", MS_DIRSYNC, 1},
181 #endif
182 {NULL, 0, 0}
185 static void set_mount_flag(const char *s, int *flags)
187 int i;
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;
194 else
195 *flags &= ~mount_flags[i].flag;
196 return;
199 fprintf(stderr, "fuse: internal error, can't find mount flag\n");
200 abort();
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;
208 switch (key) {
209 case KEY_ALLOW_ROOT:
210 if (fuse_opt_add_opt(&mo->kernel_opts, "allow_other") == -1 ||
211 fuse_opt_add_arg(outargs, "-oallow_root") == -1)
212 return -1;
213 return 0;
215 case KEY_RO:
216 arg = "ro";
217 /* fall through */
218 case KEY_KERN_FLAG:
219 set_mount_flag(arg, &mo->flags);
220 return 0;
222 case KEY_KERN_OPT:
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);
231 case KEY_MTAB_OPT:
232 return fuse_opt_add_opt(&mo->mtab_opts, arg);
234 case KEY_HELP:
235 mount_help();
236 mo->ishelp = 1;
237 break;
239 case KEY_VERSION:
240 mount_version();
241 mo->ishelp = 1;
242 break;
244 return 1;
247 /* return value:
248 * >= 0 => fd
249 * -1 => error
251 static int receive_fd(int fd)
253 struct msghdr msg;
254 struct iovec iov;
255 char buf[1];
256 int rv;
257 size_t ccmsg[CMSG_SPACE(sizeof(int)) / sizeof(size_t)];
258 struct cmsghdr *cmsg;
260 iov.iov_base = buf;
261 iov.iov_len = 1;
263 memset(&msg, 0, sizeof(msg));
264 msg.msg_name = 0;
265 msg.msg_namelen = 0;
266 msg.msg_iov = &iov;
267 msg.msg_iovlen = 1;
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);
274 if (rv == -1) {
275 perror("recvmsg");
276 return -1;
278 if(!rv) {
279 /* EOF */
280 return -1;
283 cmsg = CMSG_FIRSTHDR(&msg);
284 if (!cmsg->cmsg_type == SCM_RIGHTS) {
285 fprintf(stderr, "got control message of unknown type %d\n",
286 cmsg->cmsg_type);
287 return -1;
289 return *(int*)CMSG_DATA(cmsg);
292 void fuse_kern_unmount(const char *mountpoint, int fd)
294 int res;
295 int pid;
297 if (fd != -1) {
298 struct pollfd pfd;
300 pfd.fd = fd;
301 pfd.events = 0;
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
308 anyway. */
309 close(fd);
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))
314 return;
317 if (geteuid() == 0) {
318 fuse_mnt_umount("fuse", mountpoint, mountpoint, 1);
319 return;
322 res = umount2(mountpoint, 2);
323 if (res == 0)
324 return;
326 pid = fork();
327 if(pid == -1)
328 return;
330 if(pid == 0) {
331 const char *argv[] = { FUSERMOUNT_PROG, "-u", "-q", "-z",
332 "--", mountpoint, NULL };
334 exec_fusermount(argv);
335 _exit(1);
337 waitpid(pid, NULL, 0);
340 static int fuse_mount_fusermount(const char *mountpoint, struct mount_opts *mo,
341 const char *opts, int quiet)
343 int fds[2], pid;
344 int res;
345 int rv;
347 if (!mountpoint) {
348 fprintf(stderr, "fuse: missing mountpoint parameter\n");
349 return -1;
352 res = socketpair(PF_UNIX, SOCK_STREAM, 0, fds);
353 if(res == -1) {
354 perror("fuse: socketpair() failed");
355 return -1;
358 pid = fork();
359 if(pid == -1) {
360 perror("fuse: fork() failed");
361 close(fds[0]);
362 close(fds[1]);
363 return -1;
366 if(pid == 0) {
367 char env[10];
368 const char *argv[32];
369 int a = 0;
371 if (quiet) {
372 int fd = open("/dev/null", O_RDONLY);
373 if (fd != -1) {
374 dup2(fd, 1);
375 dup2(fd, 2);
379 argv[a++] = FUSERMOUNT_PROG;
380 if (opts) {
381 argv[a++] = "-o";
382 argv[a++] = opts;
384 argv[a++] = "--";
385 argv[a++] = mountpoint;
386 argv[a++] = NULL;
388 close(fds[1]);
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");
394 _exit(1);
397 close(fds[0]);
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 */
403 close(fds[1]);
404 waitpid(pid, NULL, 0); /* bury zombie */
407 if (rv >= 0)
408 fcntl(rv, F_SETFD, FD_CLOEXEC);
410 return rv;
413 #ifndef O_CLOEXEC
414 #define O_CLOEXEC 0
415 #endif
417 static int fuse_mount_sys(const char *mnt, struct mount_opts *mo,
418 const char *mnt_opts)
420 char tmp[128];
421 const char *devname = "/dev/fuse";
422 char *source = NULL;
423 char *type = NULL;
424 struct stat stbuf;
425 int fd;
426 int res;
428 if (!mnt) {
429 fprintf(stderr, "fuse: missing mountpoint parameter\n");
430 return -1;
433 res = stat(mnt, &stbuf);
434 if (res == -1) {
435 fprintf(stderr ,"fuse: failed to access mountpoint %s: %s\n",
436 mnt, strerror(errno));
437 return -1;
440 if (!mo->nonempty) {
441 res = fuse_mnt_check_empty("fuse", mnt, stbuf.st_mode,
442 stbuf.st_size);
443 if (res == -1)
444 return -1;
447 if (mo->auto_unmount) {
448 /* Tell the caller to fallback to fusermount because
449 auto-unmount does not work otherwise. */
450 return -2;
453 fd = open(devname, O_RDWR | O_CLOEXEC);
454 if (fd == -1) {
455 if (errno == ENODEV || errno == ENOENT)
456 fprintf(stderr, "fuse: device not found, try 'modprobe fuse' first\n");
457 else
458 fprintf(stderr, "fuse: failed to open %s: %s\n",
459 devname, strerror(errno));
460 return -1;
462 if (!O_CLOEXEC)
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);
469 if (res == -1)
470 goto out_close;
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");
479 goto out_close;
482 strcpy(type, mo->blkdev ? "fuseblk" : "fuse");
483 if (mo->subtype) {
484 strcat(type, ".");
485 strcat(type, mo->subtype);
487 strcpy(source,
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");
494 if (mo->fsname) {
495 if (!mo->blkdev)
496 sprintf(source, "%s#%s", mo->subtype,
497 mo->fsname);
498 } else {
499 strcpy(source, type);
501 res = mount(source, mnt, type, mo->flags, mo->kernel_opts);
503 if (res == -1) {
505 * Maybe kernel doesn't support unprivileged mounts, in this
506 * case try falling back to fusermount
508 if (errno == EPERM) {
509 res = -2;
510 } else {
511 int errno_save = errno;
512 if (mo->blkdev && errno == ENODEV &&
513 !fuse_mnt_check_fuseblk())
514 fprintf(stderr,
515 "fuse: 'fuseblk' support missing\n");
516 else
517 fprintf(stderr, "fuse: mount failed: %s\n",
518 strerror(errno_save));
521 goto out_close;
524 #ifndef __NetBSD__
525 #ifndef IGNORE_MTAB
526 if (geteuid() == 0) {
527 char *newmnt = fuse_mnt_resolve_path("fuse", mnt);
528 res = -1;
529 if (!newmnt)
530 goto out_umount;
532 res = fuse_mnt_add_mount("fuse", source, newmnt, type,
533 mnt_opts);
534 free(newmnt);
535 if (res == -1)
536 goto out_umount;
538 #endif /* IGNORE_MTAB */
539 #endif /* __NetBSD__ */
540 free(type);
541 free(source);
543 return fd;
545 out_umount:
546 umount2(mnt, 2); /* lazy umount */
547 out_close:
548 free(type);
549 free(source);
550 close(fd);
551 return res;
554 static int get_mnt_flag_opts(char **mnt_optsp, int flags)
556 int i;
558 if (!(flags & MS_RDONLY) && fuse_opt_add_opt(mnt_optsp, "rw") == -1)
559 return -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)
564 return -1;
566 return 0;
569 int fuse_kern_mount(const char *mountpoint, struct fuse_args *args)
571 struct mount_opts mo;
572 int res = -1;
573 char *mnt_opts = NULL;
575 memset(&mo, 0, sizeof(mo));
576 mo.flags = MS_NOSUID | MS_NODEV;
578 if (args &&
579 fuse_opt_parse(args, &mo, fuse_mount_opts, fuse_mount_opt_proc) == -1)
580 return -1;
582 if (mo.allow_other && mo.allow_root) {
583 fprintf(stderr, "fuse: 'allow_other' and 'allow_root' options are mutually exclusive\n");
584 goto out;
586 res = 0;
587 if (mo.ishelp)
588 goto out;
590 res = -1;
591 if (get_mnt_flag_opts(&mnt_opts, mo.flags) == -1)
592 goto out;
593 if (mo.kernel_opts && fuse_opt_add_opt(&mnt_opts, mo.kernel_opts) == -1)
594 goto out;
595 if (mo.mtab_opts && fuse_opt_add_opt(&mnt_opts, mo.mtab_opts) == -1)
596 goto out;
598 res = fuse_mount_sys(mountpoint, &mo, mnt_opts);
599 if (res == -2) {
600 if (mo.fusermount_opts &&
601 fuse_opt_add_opt(&mnt_opts, mo.fusermount_opts) == -1)
602 goto out;
604 if (mo.subtype) {
605 char *tmp_opts = NULL;
607 res = -1;
608 if (fuse_opt_add_opt(&tmp_opts, mnt_opts) == -1 ||
609 fuse_opt_add_opt(&tmp_opts, mo.subtype_opt) == -1) {
610 free(tmp_opts);
611 goto out;
614 res = fuse_mount_fusermount(mountpoint, &mo, tmp_opts, 1);
615 free(tmp_opts);
616 if (res == -1)
617 res = fuse_mount_fusermount(mountpoint, &mo,
618 mnt_opts, 0);
619 } else {
620 res = fuse_mount_fusermount(mountpoint, &mo, mnt_opts, 0);
623 out:
624 free(mnt_opts);
625 free(mo.fsname);
626 free(mo.subtype);
627 free(mo.fusermount_opts);
628 free(mo.subtype_opt);
629 free(mo.kernel_opts);
630 free(mo.mtab_opts);
631 return res;