blob: eb0bb17d34014e0bf4a2ce179729610fcb7713f3 [file] [log] [blame]
bigbiff bigbiff9c754052013-01-09 09:09:08 -05001/*
2 FUSE: Filesystem in Userspace
3 Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
4
5 This program can be distributed under the terms of the GNU LGPLv2.
6 See the file COPYING.LIB.
7*/
8
9#include "config.h"
10#include "fuse_i.h"
11#include "fuse_misc.h"
12#include "fuse_opt.h"
13#include "fuse_common_compat.h"
14#include "mount_util.h"
15
16#include <stdio.h>
17#include <stdlib.h>
18#include <unistd.h>
19#include <stddef.h>
Dees_Troye34c1332013-02-06 19:13:00 +000020#include <string.h>
bigbiff bigbiff9c754052013-01-09 09:09:08 -050021#include <fcntl.h>
22#include <errno.h>
23#include <sys/poll.h>
24#include <sys/socket.h>
25#include <sys/un.h>
26#include <sys/wait.h>
27#include <sys/mount.h>
28
Dees_Troye34c1332013-02-06 19:13:00 +000029#ifdef __NetBSD__
30#include <perfuse.h>
31
32#define MS_RDONLY MNT_RDONLY
33#define MS_NOSUID MNT_NOSUID
34#define MS_NODEV MNT_NODEV
35#define MS_NOEXEC MNT_NOEXEC
36#define MS_SYNCHRONOUS MNT_SYNCHRONOUS
37#define MS_NOATIME MNT_NOATIME
38
Dees_Troye34c1332013-02-06 19:13:00 +000039#define umount2(mnt, flags) unmount(mnt, (flags == 2) ? MNT_FORCE : 0)
40#endif
41
bigbiff bigbiff9c754052013-01-09 09:09:08 -050042#define FUSERMOUNT_PROG "fusermount"
43#define FUSE_COMMFD_ENV "_FUSE_COMMFD"
44
Matt Mower523a0592015-12-13 11:31:00 -060045#if defined(__ANDROID__) && !defined(FUSERMOUNT_DIR)
46# define FUSERMOUNT_DIR "/system/xbin"
47#endif
48
bigbiff bigbiff9c754052013-01-09 09:09:08 -050049#ifndef HAVE_FORK
50#define fork() vfork()
51#endif
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_ROOT,
64 KEY_RO,
65 KEY_HELP,
66 KEY_VERSION,
67};
68
69struct mount_opts {
70 int allow_other;
71 int allow_root;
72 int ishelp;
73 int flags;
74 int nonempty;
Dees_Troye34c1332013-02-06 19:13:00 +000075 int auto_unmount;
bigbiff bigbiff9c754052013-01-09 09:09:08 -050076 int blkdev;
77 char *fsname;
78 char *subtype;
79 char *subtype_opt;
80 char *mtab_opts;
81 char *fusermount_opts;
82 char *kernel_opts;
83};
84
85#define FUSE_MOUNT_OPT(t, p) { t, offsetof(struct mount_opts, p), 1 }
86
87static const struct fuse_opt fuse_mount_opts[] = {
88 FUSE_MOUNT_OPT("allow_other", allow_other),
89 FUSE_MOUNT_OPT("allow_root", allow_root),
90 FUSE_MOUNT_OPT("nonempty", nonempty),
91 FUSE_MOUNT_OPT("blkdev", blkdev),
Dees_Troye34c1332013-02-06 19:13:00 +000092 FUSE_MOUNT_OPT("auto_unmount", auto_unmount),
bigbiff bigbiff9c754052013-01-09 09:09:08 -050093 FUSE_MOUNT_OPT("fsname=%s", fsname),
94 FUSE_MOUNT_OPT("subtype=%s", subtype),
95 FUSE_OPT_KEY("allow_other", KEY_KERN_OPT),
96 FUSE_OPT_KEY("allow_root", KEY_ALLOW_ROOT),
97 FUSE_OPT_KEY("nonempty", KEY_FUSERMOUNT_OPT),
Dees_Troye34c1332013-02-06 19:13:00 +000098 FUSE_OPT_KEY("auto_unmount", KEY_FUSERMOUNT_OPT),
bigbiff bigbiff9c754052013-01-09 09:09:08 -050099 FUSE_OPT_KEY("blkdev", KEY_FUSERMOUNT_OPT),
100 FUSE_OPT_KEY("fsname=", KEY_FUSERMOUNT_OPT),
101 FUSE_OPT_KEY("subtype=", KEY_SUBTYPE_OPT),
102 FUSE_OPT_KEY("large_read", KEY_KERN_OPT),
103 FUSE_OPT_KEY("blksize=", KEY_KERN_OPT),
104 FUSE_OPT_KEY("default_permissions", KEY_KERN_OPT),
105 FUSE_OPT_KEY("max_read=", KEY_KERN_OPT),
106 FUSE_OPT_KEY("max_read=", FUSE_OPT_KEY_KEEP),
107 FUSE_OPT_KEY("user=", KEY_MTAB_OPT),
108 FUSE_OPT_KEY("-r", KEY_RO),
109 FUSE_OPT_KEY("ro", KEY_KERN_FLAG),
110 FUSE_OPT_KEY("rw", KEY_KERN_FLAG),
111 FUSE_OPT_KEY("suid", KEY_KERN_FLAG),
112 FUSE_OPT_KEY("nosuid", KEY_KERN_FLAG),
113 FUSE_OPT_KEY("dev", KEY_KERN_FLAG),
114 FUSE_OPT_KEY("nodev", KEY_KERN_FLAG),
115 FUSE_OPT_KEY("exec", KEY_KERN_FLAG),
116 FUSE_OPT_KEY("noexec", KEY_KERN_FLAG),
117 FUSE_OPT_KEY("async", KEY_KERN_FLAG),
118 FUSE_OPT_KEY("sync", KEY_KERN_FLAG),
119 FUSE_OPT_KEY("dirsync", KEY_KERN_FLAG),
120 FUSE_OPT_KEY("atime", KEY_KERN_FLAG),
121 FUSE_OPT_KEY("noatime", KEY_KERN_FLAG),
122 FUSE_OPT_KEY("-h", KEY_HELP),
123 FUSE_OPT_KEY("--help", KEY_HELP),
124 FUSE_OPT_KEY("-V", KEY_VERSION),
125 FUSE_OPT_KEY("--version", KEY_VERSION),
126 FUSE_OPT_END
127};
128
129static void mount_help(void)
130{
131 fprintf(stderr,
132" -o allow_other allow access to other users\n"
133" -o allow_root allow access to root\n"
Dees_Troye34c1332013-02-06 19:13:00 +0000134" -o auto_unmount auto unmount on process termination\n"
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500135" -o nonempty allow mounts over non-empty file/dir\n"
136" -o default_permissions enable permission checking by kernel\n"
137" -o fsname=NAME set filesystem name\n"
138" -o subtype=NAME set filesystem type\n"
139" -o large_read issue large read requests (2.4 only)\n"
140" -o max_read=N set maximum size of read requests\n"
141"\n");
142}
143
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500144static void exec_fusermount(const char *argv[])
145{
146 execv(FUSERMOUNT_DIR "/" FUSERMOUNT_PROG, (char **) argv);
147 execvp(FUSERMOUNT_PROG, (char **) argv);
148}
149
150static void mount_version(void)
151{
152 int pid = fork();
153 if (!pid) {
154 const char *argv[] = { FUSERMOUNT_PROG, "--version", NULL };
155 exec_fusermount(argv);
156 _exit(1);
157 } else if (pid != -1)
158 waitpid(pid, NULL, 0);
159}
160
161struct mount_flags {
162 const char *opt;
163 unsigned long flag;
164 int on;
165};
166
Dees_Troye34c1332013-02-06 19:13:00 +0000167static const struct mount_flags mount_flags[] = {
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500168 {"rw", MS_RDONLY, 0},
169 {"ro", MS_RDONLY, 1},
170 {"suid", MS_NOSUID, 0},
171 {"nosuid", MS_NOSUID, 1},
172 {"dev", MS_NODEV, 0},
173 {"nodev", MS_NODEV, 1},
174 {"exec", MS_NOEXEC, 0},
175 {"noexec", MS_NOEXEC, 1},
176 {"async", MS_SYNCHRONOUS, 0},
177 {"sync", MS_SYNCHRONOUS, 1},
178 {"atime", MS_NOATIME, 0},
179 {"noatime", MS_NOATIME, 1},
Dees_Troye34c1332013-02-06 19:13:00 +0000180#ifndef __NetBSD__
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500181 {"dirsync", MS_DIRSYNC, 1},
Dees_Troye34c1332013-02-06 19:13:00 +0000182#endif
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500183 {NULL, 0, 0}
184};
185
186static void set_mount_flag(const char *s, int *flags)
187{
188 int i;
189
190 for (i = 0; mount_flags[i].opt != NULL; i++) {
191 const char *opt = mount_flags[i].opt;
192 if (strcmp(opt, s) == 0) {
193 if (mount_flags[i].on)
194 *flags |= mount_flags[i].flag;
195 else
196 *flags &= ~mount_flags[i].flag;
197 return;
198 }
199 }
200 fprintf(stderr, "fuse: internal error, can't find mount flag\n");
201 abort();
202}
203
204static int fuse_mount_opt_proc(void *data, const char *arg, int key,
205 struct fuse_args *outargs)
206{
207 struct mount_opts *mo = data;
208
209 switch (key) {
210 case KEY_ALLOW_ROOT:
211 if (fuse_opt_add_opt(&mo->kernel_opts, "allow_other") == -1 ||
212 fuse_opt_add_arg(outargs, "-oallow_root") == -1)
213 return -1;
214 return 0;
215
216 case KEY_RO:
217 arg = "ro";
218 /* fall through */
219 case KEY_KERN_FLAG:
220 set_mount_flag(arg, &mo->flags);
221 return 0;
222
223 case KEY_KERN_OPT:
224 return fuse_opt_add_opt(&mo->kernel_opts, arg);
225
226 case KEY_FUSERMOUNT_OPT:
227 return fuse_opt_add_opt_escaped(&mo->fusermount_opts, arg);
228
229 case KEY_SUBTYPE_OPT:
230 return fuse_opt_add_opt(&mo->subtype_opt, arg);
231
232 case KEY_MTAB_OPT:
233 return fuse_opt_add_opt(&mo->mtab_opts, arg);
234
235 case KEY_HELP:
236 mount_help();
237 mo->ishelp = 1;
238 break;
239
240 case KEY_VERSION:
241 mount_version();
242 mo->ishelp = 1;
243 break;
244 }
245 return 1;
246}
247
248/* return value:
249 * >= 0 => fd
250 * -1 => error
251 */
252static int receive_fd(int fd)
253{
254 struct msghdr msg;
255 struct iovec iov;
256 char buf[1];
257 int rv;
258 size_t ccmsg[CMSG_SPACE(sizeof(int)) / sizeof(size_t)];
259 struct cmsghdr *cmsg;
260
261 iov.iov_base = buf;
262 iov.iov_len = 1;
263
Dees_Troye34c1332013-02-06 19:13:00 +0000264 memset(&msg, 0, sizeof(msg));
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500265 msg.msg_name = 0;
266 msg.msg_namelen = 0;
267 msg.msg_iov = &iov;
268 msg.msg_iovlen = 1;
269 /* old BSD implementations should use msg_accrights instead of
270 * msg_control; the interface is different. */
271 msg.msg_control = ccmsg;
272 msg.msg_controllen = sizeof(ccmsg);
273
274 while(((rv = recvmsg(fd, &msg, 0)) == -1) && errno == EINTR);
275 if (rv == -1) {
276 perror("recvmsg");
277 return -1;
278 }
279 if(!rv) {
280 /* EOF */
281 return -1;
282 }
283
284 cmsg = CMSG_FIRSTHDR(&msg);
285 if (!cmsg->cmsg_type == SCM_RIGHTS) {
286 fprintf(stderr, "got control message of unknown type %d\n",
287 cmsg->cmsg_type);
288 return -1;
289 }
290 return *(int*)CMSG_DATA(cmsg);
291}
292
293void fuse_kern_unmount(const char *mountpoint, int fd)
294{
295 int res;
296 int pid;
297
298 if (!mountpoint)
299 return;
300
301 if (fd != -1) {
302 struct pollfd pfd;
303
304 pfd.fd = fd;
305 pfd.events = 0;
306 res = poll(&pfd, 1, 0);
Matt Mower523a0592015-12-13 11:31:00 -0600307
308 /* Need to close file descriptor, otherwise synchronous umount
309 would recurse into filesystem, and deadlock.
310
311 Caller expects fuse_kern_unmount to close the fd, so close it
312 anyway. */
313 close(fd);
314
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500315 /* If file poll returns POLLERR on the device file descriptor,
316 then the filesystem is already unmounted */
317 if (res == 1 && (pfd.revents & POLLERR))
318 return;
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500319 }
320
321 if (geteuid() == 0) {
322 fuse_mnt_umount("fuse", mountpoint, mountpoint, 1);
323 return;
324 }
325
326 res = umount2(mountpoint, 2);
327 if (res == 0)
328 return;
329
330 pid = fork();
331 if(pid == -1)
332 return;
333
334 if(pid == 0) {
335 const char *argv[] = { FUSERMOUNT_PROG, "-u", "-q", "-z",
336 "--", mountpoint, NULL };
337
338 exec_fusermount(argv);
339 _exit(1);
340 }
341 waitpid(pid, NULL, 0);
342}
343
344void fuse_unmount_compat22(const char *mountpoint)
345{
346 fuse_kern_unmount(mountpoint, -1);
347}
348
Dees_Troye34c1332013-02-06 19:13:00 +0000349static int fuse_mount_fusermount(const char *mountpoint, struct mount_opts *mo,
350 const char *opts, int quiet)
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500351{
352 int fds[2], pid;
353 int res;
354 int rv;
355
356 if (!mountpoint) {
357 fprintf(stderr, "fuse: missing mountpoint parameter\n");
358 return -1;
359 }
360
361 res = socketpair(PF_UNIX, SOCK_STREAM, 0, fds);
362 if(res == -1) {
363 perror("fuse: socketpair() failed");
364 return -1;
365 }
366
367 pid = fork();
368 if(pid == -1) {
369 perror("fuse: fork() failed");
370 close(fds[0]);
371 close(fds[1]);
372 return -1;
373 }
374
375 if(pid == 0) {
376 char env[10];
377 const char *argv[32];
378 int a = 0;
379
380 if (quiet) {
381 int fd = open("/dev/null", O_RDONLY);
Dees_Troye34c1332013-02-06 19:13:00 +0000382 if (fd != -1) {
383 dup2(fd, 1);
384 dup2(fd, 2);
385 }
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500386 }
387
388 argv[a++] = FUSERMOUNT_PROG;
389 if (opts) {
390 argv[a++] = "-o";
391 argv[a++] = opts;
392 }
393 argv[a++] = "--";
394 argv[a++] = mountpoint;
395 argv[a++] = NULL;
396
397 close(fds[1]);
398 fcntl(fds[0], F_SETFD, 0);
399 snprintf(env, sizeof(env), "%i", fds[0]);
400 setenv(FUSE_COMMFD_ENV, env, 1);
401 exec_fusermount(argv);
402 perror("fuse: failed to exec fusermount");
403 _exit(1);
404 }
405
406 close(fds[0]);
407 rv = receive_fd(fds[1]);
Dees_Troye34c1332013-02-06 19:13:00 +0000408
409 if (!mo->auto_unmount) {
410 /* with auto_unmount option fusermount will not exit until
411 this socket is closed */
412 close(fds[1]);
413 waitpid(pid, NULL, 0); /* bury zombie */
414 }
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500415
416 return rv;
417}
418
419int fuse_mount_compat22(const char *mountpoint, const char *opts)
420{
Dees_Troye34c1332013-02-06 19:13:00 +0000421 struct mount_opts mo;
422 memset(&mo, 0, sizeof(mo));
423 mo.flags = MS_NOSUID | MS_NODEV;
424
425 return fuse_mount_fusermount(mountpoint, &mo, opts, 0);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500426}
427
428static int fuse_mount_sys(const char *mnt, struct mount_opts *mo,
429 const char *mnt_opts)
430{
431 char tmp[128];
432 const char *devname = "/dev/fuse";
433 char *source = NULL;
434 char *type = NULL;
435 struct stat stbuf;
436 int fd;
437 int res;
438
439 if (!mnt) {
440 fprintf(stderr, "fuse: missing mountpoint parameter\n");
441 return -1;
442 }
443
444 res = stat(mnt, &stbuf);
445 if (res == -1) {
446 fprintf(stderr ,"fuse: failed to access mountpoint %s: %s\n",
447 mnt, strerror(errno));
448 return -1;
449 }
450
451 if (!mo->nonempty) {
452 res = fuse_mnt_check_empty("fuse", mnt, stbuf.st_mode,
453 stbuf.st_size);
454 if (res == -1)
455 return -1;
456 }
457
Dees_Troye34c1332013-02-06 19:13:00 +0000458 if (mo->auto_unmount) {
459 /* Tell the caller to fallback to fusermount because
460 auto-unmount does not work otherwise. */
461 return -2;
462 }
463
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500464 fd = open(devname, O_RDWR);
465 if (fd == -1) {
466 if (errno == ENODEV || errno == ENOENT)
467 fprintf(stderr, "fuse: device not found, try 'modprobe fuse' first\n");
468 else
469 fprintf(stderr, "fuse: failed to open %s: %s\n",
470 devname, strerror(errno));
471 return -1;
472 }
473
Matt Mower523a0592015-12-13 11:31:00 -0600474 snprintf(tmp, sizeof(tmp), "fd=%i,rootmode=%o,user_id=%u,group_id=%u",
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500475 fd, stbuf.st_mode & S_IFMT, getuid(), getgid());
476
477 res = fuse_opt_add_opt(&mo->kernel_opts, tmp);
478 if (res == -1)
479 goto out_close;
480
481 source = malloc((mo->fsname ? strlen(mo->fsname) : 0) +
482 (mo->subtype ? strlen(mo->subtype) : 0) +
483 strlen(devname) + 32);
484
485 type = malloc((mo->subtype ? strlen(mo->subtype) : 0) + 32);
486 if (!type || !source) {
487 fprintf(stderr, "fuse: failed to allocate memory\n");
488 goto out_close;
489 }
490
491 strcpy(type, mo->blkdev ? "fuseblk" : "fuse");
492 if (mo->subtype) {
493 strcat(type, ".");
494 strcat(type, mo->subtype);
495 }
496 strcpy(source,
497 mo->fsname ? mo->fsname : (mo->subtype ? mo->subtype : devname));
498
499 res = mount(source, mnt, type, mo->flags, mo->kernel_opts);
500 if (res == -1 && errno == ENODEV && mo->subtype) {
501 /* Probably missing subtype support */
502 strcpy(type, mo->blkdev ? "fuseblk" : "fuse");
503 if (mo->fsname) {
504 if (!mo->blkdev)
505 sprintf(source, "%s#%s", mo->subtype,
506 mo->fsname);
507 } else {
508 strcpy(source, type);
509 }
510 res = mount(source, mnt, type, mo->flags, mo->kernel_opts);
511 }
512 if (res == -1) {
513 /*
514 * Maybe kernel doesn't support unprivileged mounts, in this
515 * case try falling back to fusermount
516 */
517 if (errno == EPERM) {
518 res = -2;
519 } else {
520 int errno_save = errno;
521 if (mo->blkdev && errno == ENODEV &&
522 !fuse_mnt_check_fuseblk())
523 fprintf(stderr,
524 "fuse: 'fuseblk' support missing\n");
525 else
526 fprintf(stderr, "fuse: mount failed: %s\n",
527 strerror(errno_save));
528 }
529
530 goto out_close;
531 }
532
Dees_Troye34c1332013-02-06 19:13:00 +0000533#ifndef __NetBSD__
534#ifndef IGNORE_MTAB
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500535 if (geteuid() == 0) {
536 char *newmnt = fuse_mnt_resolve_path("fuse", mnt);
537 res = -1;
538 if (!newmnt)
539 goto out_umount;
540
541 res = fuse_mnt_add_mount("fuse", source, newmnt, type,
542 mnt_opts);
543 free(newmnt);
544 if (res == -1)
545 goto out_umount;
546 }
Dees_Troye34c1332013-02-06 19:13:00 +0000547#endif /* IGNORE_MTAB */
548#endif /* __NetBSD__ */
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500549 free(type);
550 free(source);
551
552 return fd;
553
554out_umount:
555 umount2(mnt, 2); /* lazy umount */
556out_close:
557 free(type);
558 free(source);
559 close(fd);
560 return res;
561}
562
563static int get_mnt_flag_opts(char **mnt_optsp, int flags)
564{
565 int i;
566
567 if (!(flags & MS_RDONLY) && fuse_opt_add_opt(mnt_optsp, "rw") == -1)
568 return -1;
569
570 for (i = 0; mount_flags[i].opt != NULL; i++) {
571 if (mount_flags[i].on && (flags & mount_flags[i].flag) &&
572 fuse_opt_add_opt(mnt_optsp, mount_flags[i].opt) == -1)
573 return -1;
574 }
575 return 0;
576}
577
578int fuse_kern_mount(const char *mountpoint, struct fuse_args *args)
579{
580 struct mount_opts mo;
581 int res = -1;
582 char *mnt_opts = NULL;
583
584 memset(&mo, 0, sizeof(mo));
585 mo.flags = MS_NOSUID | MS_NODEV;
586
587 if (args &&
588 fuse_opt_parse(args, &mo, fuse_mount_opts, fuse_mount_opt_proc) == -1)
589 return -1;
590
591 if (mo.allow_other && mo.allow_root) {
592 fprintf(stderr, "fuse: 'allow_other' and 'allow_root' options are mutually exclusive\n");
593 goto out;
594 }
595 res = 0;
596 if (mo.ishelp)
597 goto out;
598
599 res = -1;
600 if (get_mnt_flag_opts(&mnt_opts, mo.flags) == -1)
601 goto out;
602 if (mo.kernel_opts && fuse_opt_add_opt(&mnt_opts, mo.kernel_opts) == -1)
603 goto out;
604 if (mo.mtab_opts && fuse_opt_add_opt(&mnt_opts, mo.mtab_opts) == -1)
605 goto out;
606
607 res = fuse_mount_sys(mountpoint, &mo, mnt_opts);
608 if (res == -2) {
609 if (mo.fusermount_opts &&
610 fuse_opt_add_opt(&mnt_opts, mo.fusermount_opts) == -1)
611 goto out;
612
613 if (mo.subtype) {
614 char *tmp_opts = NULL;
615
616 res = -1;
617 if (fuse_opt_add_opt(&tmp_opts, mnt_opts) == -1 ||
618 fuse_opt_add_opt(&tmp_opts, mo.subtype_opt) == -1) {
619 free(tmp_opts);
620 goto out;
621 }
622
Dees_Troye34c1332013-02-06 19:13:00 +0000623 res = fuse_mount_fusermount(mountpoint, &mo, tmp_opts, 1);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500624 free(tmp_opts);
625 if (res == -1)
Dees_Troye34c1332013-02-06 19:13:00 +0000626 res = fuse_mount_fusermount(mountpoint, &mo,
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500627 mnt_opts, 0);
628 } else {
Dees_Troye34c1332013-02-06 19:13:00 +0000629 res = fuse_mount_fusermount(mountpoint, &mo, mnt_opts, 0);
bigbiff bigbiff9c754052013-01-09 09:09:08 -0500630 }
631 }
632out:
633 free(mnt_opts);
634 free(mo.fsname);
635 free(mo.subtype);
636 free(mo.fusermount_opts);
637 free(mo.subtype_opt);
638 free(mo.kernel_opts);
639 free(mo.mtab_opts);
640 return res;
641}
642
643FUSE_SYMVER(".symver fuse_mount_compat22,fuse_mount@FUSE_2.2");
644FUSE_SYMVER(".symver fuse_unmount_compat22,fuse_unmount@FUSE_2.2");