1 /*
2   FUSE: Filesystem in Userspace
3   Copyright (C) 2005-2008 Csaba Henk <csaba.henk@creo.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 "fuse_i.h"
10 #include "fuse_misc.h"
11 #include "fuse_opt.h"
12 
13 #include <sys/param.h>
14 #include <sys/mount.h>
15 #include <sys/stat.h>
16 #include <sys/wait.h>
17 #include <sys/sysctl.h>
18 #ifndef __DragonFly__
19 #include <sys/user.h>
20 #endif
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <unistd.h>
24 #include <stddef.h>
25 #include <fcntl.h>
26 #include <errno.h>
27 #include <string.h>
28 #include <paths.h>
29 #include <limits.h>
30 
31 #define FUSERMOUNT_PROG		"mount_fusefs"
32 #define FUSE_DEV_TRUNK		"/dev/fuse"
33 
34 enum {
35 	KEY_ALLOW_ROOT,
36 	KEY_RO,
37 	KEY_HELP,
38 	KEY_VERSION,
39 	KEY_KERN
40 };
41 
42 struct mount_opts {
43 	int allow_other;
44 	int allow_root;
45 	int ishelp;
46 	char *kernel_opts;
47 };
48 
49 #define FUSE_DUAL_OPT_KEY(templ, key) 				\
50 	FUSE_OPT_KEY(templ, key), FUSE_OPT_KEY("no" templ, key)
51 
52 static const struct fuse_opt fuse_mount_opts[] = {
53 	{ "allow_other", offsetof(struct mount_opts, allow_other), 1 },
54 	{ "allow_root", offsetof(struct mount_opts, allow_root), 1 },
55 	FUSE_OPT_KEY("allow_root",		KEY_ALLOW_ROOT),
56 	FUSE_OPT_KEY("-r",			KEY_RO),
57 	FUSE_OPT_KEY("-h",			KEY_HELP),
58 	FUSE_OPT_KEY("--help",			KEY_HELP),
59 	FUSE_OPT_KEY("-V",			KEY_VERSION),
60 	FUSE_OPT_KEY("--version",		KEY_VERSION),
61 	/* standard FreeBSD mount options */
62 	FUSE_DUAL_OPT_KEY("dev",		KEY_KERN),
63 	FUSE_DUAL_OPT_KEY("async",		KEY_KERN),
64 	FUSE_DUAL_OPT_KEY("atime",		KEY_KERN),
65 	FUSE_DUAL_OPT_KEY("dev",		KEY_KERN),
66 	FUSE_DUAL_OPT_KEY("exec",		KEY_KERN),
67 	FUSE_DUAL_OPT_KEY("suid",		KEY_KERN),
68 	FUSE_DUAL_OPT_KEY("symfollow",		KEY_KERN),
69 	FUSE_DUAL_OPT_KEY("rdonly",		KEY_KERN),
70 	FUSE_DUAL_OPT_KEY("sync",		KEY_KERN),
71 	FUSE_DUAL_OPT_KEY("union",		KEY_KERN),
72 	FUSE_DUAL_OPT_KEY("userquota",		KEY_KERN),
73 	FUSE_DUAL_OPT_KEY("groupquota",		KEY_KERN),
74 	FUSE_DUAL_OPT_KEY("clusterr",		KEY_KERN),
75 	FUSE_DUAL_OPT_KEY("clusterw",		KEY_KERN),
76 	FUSE_DUAL_OPT_KEY("suiddir",		KEY_KERN),
77 	FUSE_DUAL_OPT_KEY("snapshot",		KEY_KERN),
78 	FUSE_DUAL_OPT_KEY("multilabel",		KEY_KERN),
79 	FUSE_DUAL_OPT_KEY("acls",		KEY_KERN),
80 	FUSE_DUAL_OPT_KEY("force",		KEY_KERN),
81 	FUSE_DUAL_OPT_KEY("update",		KEY_KERN),
82 	FUSE_DUAL_OPT_KEY("ro",			KEY_KERN),
83 	FUSE_DUAL_OPT_KEY("rw",			KEY_KERN),
84 	FUSE_DUAL_OPT_KEY("auto",		KEY_KERN),
85 	FUSE_DUAL_OPT_KEY("automounted",	KEY_KERN),
86 	/* options supported under both Linux and FBSD */
87 	FUSE_DUAL_OPT_KEY("allow_other",	KEY_KERN),
88 	FUSE_DUAL_OPT_KEY("default_permissions",KEY_KERN),
89 	FUSE_OPT_KEY("max_read=",		KEY_KERN),
90 	FUSE_OPT_KEY("subtype=",		KEY_KERN),
91 	/* FBSD FUSE specific mount options */
92 	FUSE_DUAL_OPT_KEY("private",		KEY_KERN),
93 	FUSE_DUAL_OPT_KEY("neglect_shares",	KEY_KERN),
94 	FUSE_DUAL_OPT_KEY("push_symlinks_in",	KEY_KERN),
95 	FUSE_OPT_KEY("nosync_unmount",		KEY_KERN),
96 	/* stock FBSD mountopt parsing routine lets anything be negated... */
97 	/*
98 	 * Linux specific mount options, but let just the mount util
99 	 * handle them
100 	 */
101 	FUSE_OPT_KEY("fsname=",			KEY_KERN),
102 	FUSE_OPT_KEY("nonempty",		KEY_KERN),
103 	FUSE_OPT_KEY("large_read",		KEY_KERN),
104 	FUSE_OPT_END
105 };
106 
mount_help(void)107 static void mount_help(void)
108 {
109 	fprintf(stderr,
110 		"    -o allow_root          allow access to root\n"
111 		);
112 	system(FUSERMOUNT_PROG " --help");
113 	fputc('\n', stderr);
114 }
115 
mount_version(void)116 static void mount_version(void)
117 {
118 	system(FUSERMOUNT_PROG " --version");
119 }
120 
fuse_mount_opt_proc(void * data,const char * arg,int key,struct fuse_args * outargs)121 static int fuse_mount_opt_proc(void *data, const char *arg, int key,
122 			       struct fuse_args *outargs)
123 {
124 	struct mount_opts *mo = data;
125 
126 	switch (key) {
127 	case KEY_ALLOW_ROOT:
128 		if (fuse_opt_add_opt(&mo->kernel_opts, "allow_other") == -1 ||
129 		    fuse_opt_add_arg(outargs, "-oallow_root") == -1)
130 			return -1;
131 		return 0;
132 
133 	case KEY_RO:
134 		arg = "ro";
135 		/* fall through */
136 
137 	case KEY_KERN:
138 		return fuse_opt_add_opt(&mo->kernel_opts, arg);
139 
140 	case KEY_HELP:
141 		mount_help();
142 		mo->ishelp = 1;
143 		break;
144 
145 	case KEY_VERSION:
146 		mount_version();
147 		mo->ishelp = 1;
148 		break;
149 	}
150 	return 1;
151 }
152 
fuse_unmount_compat22(const char * mountpoint)153 void fuse_unmount_compat22(const char *mountpoint)
154 {
155 	char dev[128];
156 	char *ssc, *umount_cmd;
157 	FILE *sf;
158 	int rv;
159 	char seekscript[] =
160 		/* error message is annoying in help output */
161 		"exec 2>/dev/null; "
162 		"/usr/bin/fstat " FUSE_DEV_TRUNK "* | "
163 		"/usr/bin/awk 'BEGIN{ getline; if (! ($3 == \"PID\" && $10 == \"NAME\")) exit 1; }; "
164 		"              { if ($3 == %d) print $10; }' | "
165 		"/usr/bin/sort | "
166 		"/usr/bin/uniq | "
167 		"/usr/bin/awk '{ i += 1; if (i > 1){ exit 1; }; printf; }; END{ if (i == 0) exit 1; }'";
168 
169 	(void) mountpoint;
170 
171 	/*
172 	 * If we don't know the fd, we have to resort to the scripted
173 	 * solution -- iterating over the fd-s is unpractical, as we
174 	 * don't know how many of open files we have. (This could be
175 	 * looked up in procfs -- however, that's optional on FBSD; or
176 	 * read out from the kmem -- however, that's bound to
177 	 * privileges (in fact, that's what happens when we call the
178 	 * setgid kmem fstat(1) utility).
179 	 */
180 	if (asprintf(&ssc, seekscript, getpid()) == -1)
181 		return;
182 
183 	errno = 0;
184 	sf = popen(ssc, "r");
185 	free(ssc);
186 	if (! sf)
187 		return;
188 
189 	fgets(dev, sizeof(dev), sf);
190 	rv = pclose(sf);
191 	if (rv)
192 		return;
193 
194 	if (asprintf(&umount_cmd, "/sbin/umount %s", dev) == -1)
195 		return;
196 	system(umount_cmd);
197 	free(umount_cmd);
198 }
199 
fuse_kern_unmount(const char * mountpoint,int fd)200 void fuse_kern_unmount(const char *mountpoint, int fd)
201 {
202 	char *ep, dev[128];
203 	struct stat sbuf;
204 
205 	unmount(mountpoint, MNT_FORCE);
206 	close(fd);
207 }
208 
209 /* Check if kernel is doing init in background */
init_backgrounded(void)210 static int init_backgrounded(void)
211 {
212 #ifdef __DragonFly__
213 	/* XXX sysctl missing */
214 	unsigned ibg;
215 	size_t len;
216 #else
217 	unsigned ibg, len;
218 #endif
219 
220 	len = sizeof(ibg);
221 
222 	if (sysctlbyname("vfs.fuse.init_backgrounded", &ibg, &len, NULL, 0))
223 		return 0;
224 
225 	return ibg;
226 }
227 
228 
fuse_mount_core(const char * mountpoint,const char * opts)229 static int fuse_mount_core(const char *mountpoint, const char *opts)
230 {
231 	const char *mountprog = FUSERMOUNT_PROG;
232 	int fd;
233 	char *fdnam, *dev;
234 	pid_t pid, cpid;
235 	int status;
236 
237 	fdnam = getenv("FUSE_DEV_FD");
238 
239 	if (fdnam) {
240 		char *ep;
241 
242 		fd = strtol(fdnam, &ep, 10);
243 
244 		if (*ep != '\0') {
245 			fprintf(stderr, "invalid value given in FUSE_DEV_FD\n");
246 			return -1;
247 		}
248 
249 		if (fd < 0)
250 			return -1;
251 
252 		goto mount;
253 	}
254 
255 	dev = getenv("FUSE_DEV_NAME");
256 
257 	if (! dev)
258 		dev = (char *)FUSE_DEV_TRUNK;
259 
260 	if ((fd = open(dev, O_RDWR)) < 0) {
261 		perror("fuse: failed to open fuse device");
262 		return -1;
263 	}
264 
265 mount:
266 	if (getenv("FUSE_NO_MOUNT") || ! mountpoint)
267 		goto out;
268 
269 	pid = fork();
270 	cpid = pid;
271 
272 	if (pid == -1) {
273 		perror("fuse: fork() failed");
274 		close(fd);
275 		return -1;
276 	}
277 
278 	if (pid == 0) {
279 		if (! init_backgrounded()) {
280 			/*
281 			 * If init is not backgrounded, we have to
282 			 * call the mount util backgrounded, to avoid
283 			 * deadlock.
284 			 */
285 
286 			pid = fork();
287 
288 			if (pid == -1) {
289 				perror("fuse: fork() failed");
290 				close(fd);
291 				exit(1);
292 			}
293 		}
294 
295 		if (pid == 0) {
296 			const char *argv[32];
297 			int a = 0;
298 
299 			if (! fdnam && asprintf(&fdnam, "%d", fd) == -1) {
300 				perror("fuse: failed to assemble mount arguments");
301 				exit(1);
302 			}
303 
304 			argv[a++] = mountprog;
305 			if (opts) {
306 				argv[a++] = "-o";
307 				argv[a++] = opts;
308 			}
309 			argv[a++] = fdnam;
310 			argv[a++] = mountpoint;
311 			argv[a++] = NULL;
312 			execvp(mountprog, (char **) argv);
313 			perror("fuse: failed to exec mount program");
314 			exit(1);
315 		}
316 
317 		exit(0);
318 	}
319 
320 	if (waitpid(cpid, &status, 0) == -1 || WEXITSTATUS(status) != 0) {
321 		perror("fuse: failed to mount file system");
322 		close(fd);
323 		return -1;
324 	}
325 
326 out:
327 	return fd;
328 }
329 
fuse_kern_mount(const char * mountpoint,struct fuse_args * args)330 int fuse_kern_mount(const char *mountpoint, struct fuse_args *args)
331 {
332 	struct mount_opts mo;
333 	int res = -1;
334 
335 	memset(&mo, 0, sizeof(mo));
336 	/* mount util should not try to spawn the daemon */
337 	setenv("MOUNT_FUSEFS_SAFE", "1", 1);
338 	/* to notify the mount util it's called from lib */
339 	setenv("MOUNT_FUSEFS_CALL_BY_LIB", "1", 1);
340 
341 	if (args &&
342 	    fuse_opt_parse(args, &mo, fuse_mount_opts, fuse_mount_opt_proc) == -1)
343 		return -1;
344 
345 	if (mo.allow_other && mo.allow_root) {
346 		fprintf(stderr, "fuse: 'allow_other' and 'allow_root' options are mutually exclusive\n");
347 		goto out;
348 	}
349 	if (mo.ishelp)
350 		return 0;
351 
352 	res = fuse_mount_core(mountpoint, mo.kernel_opts);
353 out:
354 	free(mo.kernel_opts);
355 	return res;
356 }
357 
358 FUSE_SYMVER(".symver fuse_unmount_compat22,fuse_unmount@FUSE_2.2");
359