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