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