1 /*
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>
20 #include <string.h>
21 #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 
29 #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 
39 #define umount2(mnt, flags) unmount(mnt, (flags == 2) ? MNT_FORCE : 0)
40 #endif
41 
42 #define FUSERMOUNT_PROG		"fusermount"
43 #define FUSE_COMMFD_ENV		"_FUSE_COMMFD"
44 
45 #ifndef HAVE_FORK
46 #define fork() vfork()
47 #endif
48 
49 #ifndef MS_DIRSYNC
50 #define MS_DIRSYNC 128
51 #endif
52 
53 enum {
54 	KEY_KERN_FLAG,
55 	KEY_KERN_OPT,
56 	KEY_FUSERMOUNT_OPT,
57 	KEY_SUBTYPE_OPT,
58 	KEY_MTAB_OPT,
59 	KEY_ALLOW_ROOT,
60 	KEY_RO,
61 	KEY_HELP,
62 	KEY_VERSION,
63 };
64 
65 struct mount_opts {
66 	int allow_other;
67 	int allow_root;
68 	int ishelp;
69 	int flags;
70 	int nonempty;
71 	int auto_unmount;
72 	int blkdev;
73 	char *fsname;
74 	char *subtype;
75 	char *subtype_opt;
76 	char *mtab_opts;
77 	char *fusermount_opts;
78 	char *kernel_opts;
79 };
80 
81 #define FUSE_MOUNT_OPT(t, p) { t, offsetof(struct mount_opts, p), 1 }
82 
83 static const struct fuse_opt fuse_mount_opts[] = {
84 	FUSE_MOUNT_OPT("allow_other",		allow_other),
85 	FUSE_MOUNT_OPT("allow_root",		allow_root),
86 	FUSE_MOUNT_OPT("nonempty",		nonempty),
87 	FUSE_MOUNT_OPT("blkdev",		blkdev),
88 	FUSE_MOUNT_OPT("auto_unmount",		auto_unmount),
89 	FUSE_MOUNT_OPT("fsname=%s",		fsname),
90 	FUSE_MOUNT_OPT("subtype=%s",		subtype),
91 	FUSE_OPT_KEY("allow_other",		KEY_KERN_OPT),
92 	FUSE_OPT_KEY("allow_root",		KEY_ALLOW_ROOT),
93 	FUSE_OPT_KEY("nonempty",		KEY_FUSERMOUNT_OPT),
94 	FUSE_OPT_KEY("auto_unmount",		KEY_FUSERMOUNT_OPT),
95 	FUSE_OPT_KEY("blkdev",			KEY_FUSERMOUNT_OPT),
96 	FUSE_OPT_KEY("fsname=",			KEY_FUSERMOUNT_OPT),
97 	FUSE_OPT_KEY("subtype=",		KEY_SUBTYPE_OPT),
98 	FUSE_OPT_KEY("large_read",		KEY_KERN_OPT),
99 	FUSE_OPT_KEY("blksize=",		KEY_KERN_OPT),
100 	FUSE_OPT_KEY("default_permissions",	KEY_KERN_OPT),
101 	FUSE_OPT_KEY("context=",		KEY_KERN_OPT),
102 	FUSE_OPT_KEY("fscontext=",		KEY_KERN_OPT),
103 	FUSE_OPT_KEY("defcontext=",		KEY_KERN_OPT),
104 	FUSE_OPT_KEY("rootcontext=",		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 
mount_help(void)129 static 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"
134 "    -o auto_unmount        auto unmount on process termination\n"
135 "    -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 
exec_fusermount(const char * argv[])144 static void exec_fusermount(const char *argv[])
145 {
146 	execv(FUSERMOUNT_DIR "/" FUSERMOUNT_PROG, (char **) argv);
147 	execvp(FUSERMOUNT_PROG, (char **) argv);
148 }
149 
mount_version(void)150 static 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 
161 struct mount_flags {
162 	const char *opt;
163 	unsigned long flag;
164 	int on;
165 };
166 
167 static const struct mount_flags mount_flags[] = {
168 	{"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},
180 #ifndef __NetBSD__
181 	{"dirsync", MS_DIRSYNC,	    1},
182 #endif
183 	{NULL,	    0,		    0}
184 };
185 
set_mount_flag(const char * s,int * flags)186 static 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 
fuse_mount_opt_proc(void * data,const char * arg,int key,struct fuse_args * outargs)204 static 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  */
receive_fd(int fd)252 static 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 
264 	memset(&msg, 0, sizeof(msg));
265 	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 
fuse_kern_unmount(const char * mountpoint,int fd)293 void 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);
307 
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 
315 		/* 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;
319 	}
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 
fuse_unmount_compat22(const char * mountpoint)344 void fuse_unmount_compat22(const char *mountpoint)
345 {
346 	fuse_kern_unmount(mountpoint, -1);
347 }
348 
fuse_mount_fusermount(const char * mountpoint,struct mount_opts * mo,const char * opts,int quiet)349 static int fuse_mount_fusermount(const char *mountpoint, struct mount_opts *mo,
350 		const char *opts, int quiet)
351 {
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);
382 			if (fd != -1) {
383 				dup2(fd, 1);
384 				dup2(fd, 2);
385 			}
386 		}
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]);
408 
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 	}
415 
416 	return rv;
417 }
418 
fuse_mount_compat22(const char * mountpoint,const char * opts)419 int fuse_mount_compat22(const char *mountpoint, const char *opts)
420 {
421 	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);
426 }
427 
fuse_mount_sys(const char * mnt,struct mount_opts * mo,const char * mnt_opts)428 static 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 
458 	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 
464 	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 
474 	snprintf(tmp, sizeof(tmp),  "fd=%i,rootmode=%o,user_id=%u,group_id=%u",
475 		 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 
533 #ifndef __NetBSD__
534 #ifndef IGNORE_MTAB
535 	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 	}
547 #endif /* IGNORE_MTAB */
548 #endif /* __NetBSD__ */
549 	free(type);
550 	free(source);
551 
552 	return fd;
553 
554 out_umount:
555 	umount2(mnt, 2); /* lazy umount */
556 out_close:
557 	free(type);
558 	free(source);
559 	close(fd);
560 	return res;
561 }
562 
get_mnt_flag_opts(char ** mnt_optsp,int flags)563 static 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 
fuse_kern_mount(const char * mountpoint,struct fuse_args * args)578 int 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 
623 			res = fuse_mount_fusermount(mountpoint, &mo, tmp_opts, 1);
624 			free(tmp_opts);
625 			if (res == -1)
626 				res = fuse_mount_fusermount(mountpoint, &mo,
627 							    mnt_opts, 0);
628 		} else {
629 			res = fuse_mount_fusermount(mountpoint, &mo, mnt_opts, 0);
630 		}
631 	}
632 out:
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 
643 FUSE_SYMVER(".symver fuse_mount_compat22,fuse_mount@FUSE_2.2");
644 FUSE_SYMVER(".symver fuse_unmount_compat22,fuse_unmount@FUSE_2.2");
645