xref: /aosp_15_r20/external/libfuse/lib/mount.c (revision 9e5649576b786774a32d7b0252c9cd8c6538fa49)
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