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