libfuse: lib/mount.c Source File

libfuse

mount.c
1/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 Architecture specific file system mounting (Linux).
6
7 This program can be distributed under the terms of the GNU LGPLv2.
8 See the file COPYING.LIB.
9*/
10
11#include "fuse_config.h"
12#include "fuse_i.h"
13#include "fuse_misc.h"
14#include "fuse_opt.h"
15#include "mount_util.h"
16
17#include <stdio.h>
18#include <stdlib.h>
19#include <unistd.h>
20#include <stddef.h>
21#include <string.h>
22#include <fcntl.h>
23#include <errno.h>
24#include <poll.h>
25#include <sys/socket.h>
26#include <sys/un.h>
27#include <sys/wait.h>
28
29#include "fuse_mount_compat.h"
30
31#ifdef __NetBSD__
32#include <perfuse.h>
33
34#define MS_RDONLY MNT_RDONLY
35#define MS_NOSUID MNT_NOSUID
36#define MS_NODEV MNT_NODEV
37#define MS_NOEXEC MNT_NOEXEC
38#define MS_SYNCHRONOUS MNT_SYNCHRONOUS
39#define MS_NOATIME MNT_NOATIME
40
41#define umount2(mnt, flags) unmount(mnt, (flags == 2) ? MNT_FORCE : 0)
42#endif
43
44#define FUSERMOUNT_PROG "fusermount3"
45#define FUSE_COMMFD_ENV "_FUSE_COMMFD"
46
47#ifndef HAVE_FORK
48#define fork() vfork()
49#endif
50
51#ifndef MS_DIRSYNC
52#define MS_DIRSYNC 128
53#endif
54
55enum {
56 KEY_KERN_FLAG,
57 KEY_KERN_OPT,
58 KEY_FUSERMOUNT_OPT,
59 KEY_SUBTYPE_OPT,
60 KEY_MTAB_OPT,
61 KEY_ALLOW_OTHER,
62 KEY_RO,
63};
64
65struct mount_opts {
66 int allow_other;
67 int flags;
68 int auto_unmount;
69 int blkdev;
70 char *fsname;
71 char *subtype;
72 char *subtype_opt;
73 char *mtab_opts;
74 char *fusermount_opts;
75 char *kernel_opts;
76 unsigned max_read;
77};
78
79#define FUSE_MOUNT_OPT(t, p) { t, offsetof(struct mount_opts, p), 1 }
80
81static const struct fuse_opt fuse_mount_opts[] = {
82 FUSE_MOUNT_OPT("allow_other", allow_other),
83 FUSE_MOUNT_OPT("blkdev", blkdev),
84 FUSE_MOUNT_OPT("auto_unmount", auto_unmount),
85 FUSE_MOUNT_OPT("fsname=%s", fsname),
86 FUSE_MOUNT_OPT("max_read=%u", max_read),
87 FUSE_MOUNT_OPT("subtype=%s", subtype),
88 FUSE_OPT_KEY("allow_other", KEY_KERN_OPT),
89 FUSE_OPT_KEY("auto_unmount", KEY_FUSERMOUNT_OPT),
90 FUSE_OPT_KEY("blkdev", KEY_FUSERMOUNT_OPT),
91 FUSE_OPT_KEY("fsname=", KEY_FUSERMOUNT_OPT),
92 FUSE_OPT_KEY("subtype=", KEY_SUBTYPE_OPT),
93 FUSE_OPT_KEY("blksize=", KEY_KERN_OPT),
94 FUSE_OPT_KEY("default_permissions", KEY_KERN_OPT),
95 FUSE_OPT_KEY("context=", KEY_KERN_OPT),
96 FUSE_OPT_KEY("fscontext=", KEY_KERN_OPT),
97 FUSE_OPT_KEY("defcontext=", KEY_KERN_OPT),
98 FUSE_OPT_KEY("rootcontext=", KEY_KERN_OPT),
99 FUSE_OPT_KEY("max_read=", KEY_KERN_OPT),
100 FUSE_OPT_KEY("user=", KEY_MTAB_OPT),
101 FUSE_OPT_KEY("-n", KEY_MTAB_OPT),
102 FUSE_OPT_KEY("-r", KEY_RO),
103 FUSE_OPT_KEY("ro", KEY_KERN_FLAG),
104 FUSE_OPT_KEY("rw", KEY_KERN_FLAG),
105 FUSE_OPT_KEY("suid", KEY_KERN_FLAG),
106 FUSE_OPT_KEY("nosuid", KEY_KERN_FLAG),
107 FUSE_OPT_KEY("dev", KEY_KERN_FLAG),
108 FUSE_OPT_KEY("nodev", KEY_KERN_FLAG),
109 FUSE_OPT_KEY("exec", KEY_KERN_FLAG),
110 FUSE_OPT_KEY("noexec", KEY_KERN_FLAG),
111 FUSE_OPT_KEY("async", KEY_KERN_FLAG),
112 FUSE_OPT_KEY("sync", KEY_KERN_FLAG),
113 FUSE_OPT_KEY("dirsync", KEY_KERN_FLAG),
114 FUSE_OPT_KEY("noatime", KEY_KERN_FLAG),
115 FUSE_OPT_KEY("nodiratime", KEY_KERN_FLAG),
116 FUSE_OPT_KEY("nostrictatime", KEY_KERN_FLAG),
117 FUSE_OPT_END
118};
119
120static void exec_fusermount(const char *argv[])
121{
122 execv(FUSERMOUNT_DIR "/" FUSERMOUNT_PROG, (char **) argv);
123 execvp(FUSERMOUNT_PROG, (char **) argv);
124}
125
126void fuse_mount_version(void)
127{
128 int pid = fork();
129 if (!pid) {
130 const char *argv[] = { FUSERMOUNT_PROG, "--version", NULL };
131 exec_fusermount(argv);
132 _exit(1);
133 } else if (pid != -1)
134 waitpid(pid, NULL, 0);
135}
136
137struct mount_flags {
138 const char *opt;
139 unsigned long flag;
140 int on;
141};
142
143static const struct mount_flags mount_flags[] = {
144 {"rw", MS_RDONLY, 0},
145 {"ro", MS_RDONLY, 1},
146 {"suid", MS_NOSUID, 0},
147 {"nosuid", MS_NOSUID, 1},
148 {"dev", MS_NODEV, 0},
149 {"nodev", MS_NODEV, 1},
150 {"exec", MS_NOEXEC, 0},
151 {"noexec", MS_NOEXEC, 1},
152 {"async", MS_SYNCHRONOUS, 0},
153 {"sync", MS_SYNCHRONOUS, 1},
154 {"noatime", MS_NOATIME, 1},
155 {"nodiratime", MS_NODIRATIME, 1},
156 {"norelatime", MS_RELATIME, 0},
157 {"nostrictatime", MS_STRICTATIME, 0},
158#ifndef __NetBSD__
159 {"dirsync", MS_DIRSYNC, 1},
160#endif
161 {NULL, 0, 0}
162};
163
164unsigned get_max_read(struct mount_opts *o)
165{
166 return o->max_read;
167}
168
169static void set_mount_flag(const char *s, int *flags)
170{
171 int i;
172
173 for (i = 0; mount_flags[i].opt != NULL; i++) {
174 const char *opt = mount_flags[i].opt;
175 if (strcmp(opt, s) == 0) {
176 if (mount_flags[i].on)
177 *flags |= mount_flags[i].flag;
178 else
179 *flags &= ~mount_flags[i].flag;
180 return;
181 }
182 }
183 fuse_log(FUSE_LOG_ERR, "fuse: internal error, can't find mount flag\n");
184 abort();
185}
186
187static int fuse_mount_opt_proc(void *data, const char *arg, int key,
188 struct fuse_args *outargs)
189{
190 (void) outargs;
191 struct mount_opts *mo = data;
192
193 switch (key) {
194 case KEY_RO:
195 arg = "ro";
196 /* fall through */
197 case KEY_KERN_FLAG:
198 set_mount_flag(arg, &mo->flags);
199 return 0;
200
201 case KEY_KERN_OPT:
202 return fuse_opt_add_opt(&mo->kernel_opts, arg);
203
204 case KEY_FUSERMOUNT_OPT:
205 return fuse_opt_add_opt_escaped(&mo->fusermount_opts, arg);
206
207 case KEY_SUBTYPE_OPT:
208 return fuse_opt_add_opt(&mo->subtype_opt, arg);
209
210 case KEY_MTAB_OPT:
211 return fuse_opt_add_opt(&mo->mtab_opts, arg);
212 }
213
214 /* Pass through unknown options */
215 return 1;
216}
217
218/* return value:
219 * >= 0 => fd
220 * -1 => error
221 */
222static int receive_fd(int fd)
223{
224 struct msghdr msg;
225 struct iovec iov;
226 char buf[1];
227 int rv;
228 size_t ccmsg[CMSG_SPACE(sizeof(int)) / sizeof(size_t)];
229 struct cmsghdr *cmsg;
230
231 iov.iov_base = buf;
232 iov.iov_len = 1;
233
234 memset(&msg, 0, sizeof(msg));
235 msg.msg_name = 0;
236 msg.msg_namelen = 0;
237 msg.msg_iov = &iov;
238 msg.msg_iovlen = 1;
239 /* old BSD implementations should use msg_accrights instead of
240 * msg_control; the interface is different. */
241 msg.msg_control = ccmsg;
242 msg.msg_controllen = sizeof(ccmsg);
243
244 while(((rv = recvmsg(fd, &msg, 0)) == -1) && errno == EINTR);
245 if (rv == -1) {
246 perror("recvmsg");
247 return -1;
248 }
249 if(!rv) {
250 /* EOF */
251 return -1;
252 }
253
254 cmsg = CMSG_FIRSTHDR(&msg);
255 if (cmsg->cmsg_type != SCM_RIGHTS) {
256 fuse_log(FUSE_LOG_ERR, "got control message of unknown type %d\n",
257 cmsg->cmsg_type);
258 return -1;
259 }
260 return *(int*)CMSG_DATA(cmsg);
261}
262
263void fuse_kern_unmount(const char *mountpoint, int fd)
264{
265 int res;
266 int pid;
267
268 if (fd != -1) {
269 struct pollfd pfd;
270
271 pfd.fd = fd;
272 pfd.events = 0;
273 res = poll(&pfd, 1, 0);
274
275 /* Need to close file descriptor, otherwise synchronous umount
276 would recurse into filesystem, and deadlock.
277
278 Caller expects fuse_kern_unmount to close the fd, so close it
279 anyway. */
280 close(fd);
281
282 /* If file poll returns POLLERR on the device file descriptor,
283 then the filesystem is already unmounted or the connection
284 was severed via /sys/fs/fuse/connections/NNN/abort */
285 if (res == 1 && (pfd.revents & POLLERR))
286 return;
287 }
288
289 if (geteuid() == 0) {
290 fuse_mnt_umount("fuse", mountpoint, mountpoint, 1);
291 return;
292 }
293
294 res = umount2(mountpoint, 2);
295 if (res == 0)
296 return;
297
298 pid = fork();
299 if(pid == -1)
300 return;
301
302 if(pid == 0) {
303 const char *argv[] = { FUSERMOUNT_PROG, "-u", "-q", "-z",
304 "--", mountpoint, NULL };
305
306 exec_fusermount(argv);
307 _exit(1);
308 }
309 waitpid(pid, NULL, 0);
310}
311
312static int setup_auto_unmount(const char *mountpoint, int quiet)
313{
314 int fds[2], pid;
315 int res;
316
317 if (!mountpoint) {
318 fuse_log(FUSE_LOG_ERR, "fuse: missing mountpoint parameter\n");
319 return -1;
320 }
321
322 res = socketpair(PF_UNIX, SOCK_STREAM, 0, fds);
323 if(res == -1) {
324 perror("fuse: socketpair() failed");
325 return -1;
326 }
327
328 pid = fork();
329 if(pid == -1) {
330 perror("fuse: fork() failed");
331 close(fds[0]);
332 close(fds[1]);
333 return -1;
334 }
335
336 if(pid == 0) {
337 char env[10];
338 const char *argv[32];
339 int a = 0;
340
341 if (quiet) {
342 int fd = open("/dev/null", O_RDONLY);
343 if (fd != -1) {
344 dup2(fd, 1);
345 dup2(fd, 2);
346 }
347 }
348
349 argv[a++] = FUSERMOUNT_PROG;
350 argv[a++] = "--auto-unmount";
351 argv[a++] = "--";
352 argv[a++] = mountpoint;
353 argv[a++] = NULL;
354
355 close(fds[1]);
356 fcntl(fds[0], F_SETFD, 0);
357 snprintf(env, sizeof(env), "%i", fds[0]);
358 setenv(FUSE_COMMFD_ENV, env, 1);
359 exec_fusermount(argv);
360 perror("fuse: failed to exec fusermount3");
361 _exit(1);
362 }
363
364 close(fds[0]);
365
366 // Now fusermount3 will only exit when fds[1] closes automatically when our
367 // process exits.
368 return 0;
369}
370
371static int fuse_mount_fusermount(const char *mountpoint, struct mount_opts *mo,
372 const char *opts, int quiet)
373{
374 int fds[2], pid;
375 int res;
376 int rv;
377
378 if (!mountpoint) {
379 fuse_log(FUSE_LOG_ERR, "fuse: missing mountpoint parameter\n");
380 return -1;
381 }
382
383 res = socketpair(PF_UNIX, SOCK_STREAM, 0, fds);
384 if(res == -1) {
385 perror("fuse: socketpair() failed");
386 return -1;
387 }
388
389 pid = fork();
390 if(pid == -1) {
391 perror("fuse: fork() failed");
392 close(fds[0]);
393 close(fds[1]);
394 return -1;
395 }
396
397 if(pid == 0) {
398 char env[10];
399 const char *argv[32];
400 int a = 0;
401
402 if (quiet) {
403 int fd = open("/dev/null", O_RDONLY);
404 if (fd != -1) {
405 dup2(fd, 1);
406 dup2(fd, 2);
407 }
408 }
409
410 argv[a++] = FUSERMOUNT_PROG;
411 if (opts) {
412 argv[a++] = "-o";
413 argv[a++] = opts;
414 }
415 argv[a++] = "--";
416 argv[a++] = mountpoint;
417 argv[a++] = NULL;
418
419 close(fds[1]);
420 fcntl(fds[0], F_SETFD, 0);
421 snprintf(env, sizeof(env), "%i", fds[0]);
422 setenv(FUSE_COMMFD_ENV, env, 1);
423 exec_fusermount(argv);
424 perror("fuse: failed to exec fusermount3");
425 _exit(1);
426 }
427
428 close(fds[0]);
429 rv = receive_fd(fds[1]);
430
431 if (!mo->auto_unmount) {
432 /* with auto_unmount option fusermount3 will not exit until
433 this socket is closed */
434 close(fds[1]);
435 waitpid(pid, NULL, 0); /* bury zombie */
436 }
437
438 if (rv >= 0)
439 fcntl(rv, F_SETFD, FD_CLOEXEC);
440
441 return rv;
442}
443
444#ifndef O_CLOEXEC
445#define O_CLOEXEC 0
446#endif
447
448static int fuse_mount_sys(const char *mnt, struct mount_opts *mo,
449 const char *mnt_opts)
450{
451 char tmp[128];
452 const char *devname = "/dev/fuse";
453 char *source = NULL;
454 char *type = NULL;
455 struct stat stbuf;
456 int fd;
457 int res;
458
459 if (!mnt) {
460 fuse_log(FUSE_LOG_ERR, "fuse: missing mountpoint parameter\n");
461 return -1;
462 }
463
464 res = stat(mnt, &stbuf);
465 if (res == -1) {
466 fuse_log(FUSE_LOG_ERR, "fuse: failed to access mountpoint %s: %s\n",
467 mnt, strerror(errno));
468 return -1;
469 }
470
471 fd = open(devname, O_RDWR | O_CLOEXEC);
472 if (fd == -1) {
473 if (errno == ENODEV || errno == ENOENT)
474 fuse_log(FUSE_LOG_ERR, "fuse: device not found, try 'modprobe fuse' first\n");
475 else
476 fuse_log(FUSE_LOG_ERR, "fuse: failed to open %s: %s\n",
477 devname, strerror(errno));
478 return -1;
479 }
480 if (!O_CLOEXEC)
481 fcntl(fd, F_SETFD, FD_CLOEXEC);
482
483 snprintf(tmp, sizeof(tmp), "fd=%i,rootmode=%o,user_id=%u,group_id=%u",
484 fd, stbuf.st_mode & S_IFMT, getuid(), getgid());
485
486 res = fuse_opt_add_opt(&mo->kernel_opts, tmp);
487 if (res == -1)
488 goto out_close;
489
490 source = malloc((mo->fsname ? strlen(mo->fsname) : 0) +
491 (mo->subtype ? strlen(mo->subtype) : 0) +
492 strlen(devname) + 32);
493
494 type = malloc((mo->subtype ? strlen(mo->subtype) : 0) + 32);
495 if (!type || !source) {
496 fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate memory\n");
497 goto out_close;
498 }
499
500 strcpy(type, mo->blkdev ? "fuseblk" : "fuse");
501 if (mo->subtype) {
502 strcat(type, ".");
503 strcat(type, mo->subtype);
504 }
505 strcpy(source,
506 mo->fsname ? mo->fsname : (mo->subtype ? mo->subtype : devname));
507
508 res = mount(source, mnt, type, mo->flags, mo->kernel_opts);
509 if (res == -1 && errno == ENODEV && mo->subtype) {
510 /* Probably missing subtype support */
511 strcpy(type, mo->blkdev ? "fuseblk" : "fuse");
512 if (mo->fsname) {
513 if (!mo->blkdev)
514 sprintf(source, "%s#%s", mo->subtype,
515 mo->fsname);
516 } else {
517 strcpy(source, type);
518 }
519 res = mount(source, mnt, type, mo->flags, mo->kernel_opts);
520 }
521 if (res == -1) {
522 /*
523 * Maybe kernel doesn't support unprivileged mounts, in this
524 * case try falling back to fusermount3
525 */
526 if (errno == EPERM) {
527 res = -2;
528 } else {
529 int errno_save = errno;
530 if (mo->blkdev && errno == ENODEV &&
531 !fuse_mnt_check_fuseblk())
532 fuse_log(FUSE_LOG_ERR,
533 "fuse: 'fuseblk' support missing\n");
534 else
535 fuse_log(FUSE_LOG_ERR, "fuse: mount failed: %s\n",
536 strerror(errno_save));
537 }
538
539 goto out_close;
540 }
541
542#ifndef IGNORE_MTAB
543 if (geteuid() == 0) {
544 char *newmnt = fuse_mnt_resolve_path("fuse", mnt);
545 res = -1;
546 if (!newmnt)
547 goto out_umount;
548
549 res = fuse_mnt_add_mount("fuse", source, newmnt, type,
550 mnt_opts);
551 free(newmnt);
552 if (res == -1)
553 goto out_umount;
554 }
555#endif /* IGNORE_MTAB */
556 free(type);
557 free(source);
558
559 return fd;
560
561out_umount:
562 umount2(mnt, 2); /* lazy umount */
563out_close:
564 free(type);
565 free(source);
566 close(fd);
567 return res;
568}
569
570static int get_mnt_flag_opts(char **mnt_optsp, int flags)
571{
572 int i;
573
574 if (!(flags & MS_RDONLY) && fuse_opt_add_opt(mnt_optsp, "rw") == -1)
575 return -1;
576
577 for (i = 0; mount_flags[i].opt != NULL; i++) {
578 if (mount_flags[i].on && (flags & mount_flags[i].flag) &&
579 fuse_opt_add_opt(mnt_optsp, mount_flags[i].opt) == -1)
580 return -1;
581 }
582 return 0;
583}
584
585struct mount_opts *parse_mount_opts(struct fuse_args *args)
586{
587 struct mount_opts *mo;
588
589 mo = (struct mount_opts*) malloc(sizeof(struct mount_opts));
590 if (mo == NULL)
591 return NULL;
592
593 memset(mo, 0, sizeof(struct mount_opts));
594 mo->flags = MS_NOSUID | MS_NODEV;
595
596 if (args &&
597 fuse_opt_parse(args, mo, fuse_mount_opts, fuse_mount_opt_proc) == -1)
598 goto err_out;
599
600 return mo;
601
602err_out:
603 destroy_mount_opts(mo);
604 return NULL;
605}
606
607void destroy_mount_opts(struct mount_opts *mo)
608{
609 free(mo->fsname);
610 free(mo->subtype);
611 free(mo->fusermount_opts);
612 free(mo->subtype_opt);
613 free(mo->kernel_opts);
614 free(mo->mtab_opts);
615 free(mo);
616}
617
618
619int fuse_kern_mount(const char *mountpoint, struct mount_opts *mo)
620{
621 int res = -1;
622 char *mnt_opts = NULL;
623
624 res = -1;
625 if (get_mnt_flag_opts(&mnt_opts, mo->flags) == -1)
626 goto out;
627 if (mo->kernel_opts && fuse_opt_add_opt(&mnt_opts, mo->kernel_opts) == -1)
628 goto out;
629 if (mo->mtab_opts && fuse_opt_add_opt(&mnt_opts, mo->mtab_opts) == -1)
630 goto out;
631
632 res = fuse_mount_sys(mountpoint, mo, mnt_opts);
633 if (res >= 0 && mo->auto_unmount) {
634 if(0 > setup_auto_unmount(mountpoint, 0)) {
635 // Something went wrong, let's umount like in fuse_mount_sys.
636 umount2(mountpoint, MNT_DETACH); /* lazy umount */
637 res = -1;
638 }
639 } else if (res == -2) {
640 if (mo->fusermount_opts &&
641 fuse_opt_add_opt(&mnt_opts, mo->fusermount_opts) == -1)
642 goto out;
643
644 if (mo->subtype) {
645 char *tmp_opts = NULL;
646
647 res = -1;
648 if (fuse_opt_add_opt(&tmp_opts, mnt_opts) == -1 ||
649 fuse_opt_add_opt(&tmp_opts, mo->subtype_opt) == -1) {
650 free(tmp_opts);
651 goto out;
652 }
653
654 res = fuse_mount_fusermount(mountpoint, mo, tmp_opts, 1);
655 free(tmp_opts);
656 if (res == -1)
657 res = fuse_mount_fusermount(mountpoint, mo,
658 mnt_opts, 0);
659 } else {
660 res = fuse_mount_fusermount(mountpoint, mo, mnt_opts, 0);
661 }
662 }
663out:
664 free(mnt_opts);
665 return res;
666}
fuse_log
void fuse_log(enum fuse_log_level level, const char *fmt,...)
Definition: fuse_log.c:33
fuse_opt.h
FUSE_OPT_KEY
#define FUSE_OPT_KEY(templ, key)
Definition: fuse_opt.h:98
fuse_opt_add_opt_escaped
int fuse_opt_add_opt_escaped(char **opts, const char *opt)
Definition: fuse_opt.c:144
fuse_opt_parse
int fuse_opt_parse(struct fuse_args *args, void *data, const struct fuse_opt opts[], fuse_opt_proc_t proc)
Definition: fuse_opt.c:398
fuse_opt_add_opt
int fuse_opt_add_opt(char **opts, const char *opt)
Definition: fuse_opt.c:139
FUSE_OPT_END
#define FUSE_OPT_END
Definition: fuse_opt.h:104
fuse_args
Definition: fuse_opt.h:109
fuse_opt
Definition: fuse_opt.h:77