xref: /freebsd/sbin/mount_fusefs/mount_fusefs.c (revision 3494f7c0)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2005 Jean-Sebastien Pedron
5  * Copyright (c) 2005 Csaba Henk
6  * All rights reserved.
7  *
8  * Copyright (c) 2019 The FreeBSD Foundation
9  *
10  * Portions of this software were developed by BFF Storage Systems under
11  * sponsorship from the FreeBSD Foundation.
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  * 1. Redistributions of source code must retain the above copyright
17  *    notice, this list of conditions and the following disclaimer.
18  * 2. Redistributions in binary form must reproduce the above copyright
19  *    notice, this list of conditions and the following disclaimer in the
20  *    documentation and/or other materials provided with the distribution.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  *
34  */
35 
36 #include <sys/param.h>
37 #include <sys/mount.h>
38 #include <sys/uio.h>
39 #include <sys/stat.h>
40 #include <sys/sysctl.h>
41 
42 #include <err.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <sysexits.h>
47 #include <unistd.h>
48 #include <fcntl.h>
49 #include <signal.h>
50 #include <getopt.h>
51 #include <limits.h>
52 #include <osreldate.h>
53 #include <paths.h>
54 
55 #include "mntopts.h"
56 
57 #ifndef FUSE4BSD_VERSION
58 #define	FUSE4BSD_VERSION	"0.3.9-pre1"
59 #endif
60 
61 void	__usage_short(void);
62 void	usage(void);
63 void	helpmsg(void);
64 void	showversion(void);
65 
66 static struct mntopt mopts[] = {
67 	#define ALTF_PRIVATE 0x01
68 	{ "private",             0, ALTF_PRIVATE, 1 },
69 	{ "neglect_shares",      0, 0x02, 1 },
70 	{ "push_symlinks_in",    0, 0x04, 1 },
71 	{ "allow_other",         0, 0x08, 1 },
72 	{ "default_permissions", 0, 0x10, 1 },
73 	#define ALTF_MAXREAD 0x20
74 	{ "max_read=",           0, ALTF_MAXREAD, 1 },
75 	#define ALTF_SUBTYPE 0x40
76 	{ "subtype=",            0, ALTF_SUBTYPE, 1 },
77 	#define ALTF_FSNAME 0x80
78 	{ "fsname=",             0, ALTF_FSNAME, 1 },
79 	/*
80 	 * MOPT_AUTOMOUNTED, included by MOPT_STDOPTS, does not fit into
81 	 * the 'flags' argument to nmount(2).  We have to abuse altflags
82 	 * to pass it, as string, via iovec.
83 	 */
84 	#define ALTF_AUTOMOUNTED 0x100
85 	{ "automounted",	0, ALTF_AUTOMOUNTED, 1 },
86 	#define ALTF_INTR 0x200
87 	{ "intr",		0, ALTF_INTR, 1 },
88 	/* Linux specific options, we silently ignore them */
89 	{ "fd=",                 0, 0x00, 1 },
90 	{ "rootmode=",           0, 0x00, 1 },
91 	{ "user_id=",            0, 0x00, 1 },
92 	{ "group_id=",           0, 0x00, 1 },
93 	{ "large_read",          0, 0x00, 1 },
94 	/* "nonempty", just the first two chars are stripped off during parsing */
95 	{ "nempty",              0, 0x00, 1 },
96 	{ "async",               0, MNT_ASYNC, 0},
97 	{ "noasync",             1, MNT_ASYNC, 0},
98 	MOPT_STDOPTS,
99 	MOPT_END
100 };
101 
102 struct mntval {
103 	int mv_flag;
104 	void *mv_value;
105 	int mv_len;
106 };
107 
108 static struct mntval mvals[] = {
109 	{ ALTF_MAXREAD, NULL, 0 },
110 	{ ALTF_SUBTYPE, NULL, 0 },
111 	{ ALTF_FSNAME, NULL, 0 },
112 	{ 0, NULL, 0 }
113 };
114 
115 #define DEFAULT_MOUNT_FLAGS ALTF_PRIVATE
116 
117 int
118 main(int argc, char *argv[])
119 {
120 	struct iovec *iov;
121 	int mntflags, iovlen, verbose = 0;
122 	char *dev = NULL, *dir = NULL, mntpath[MAXPATHLEN];
123 	char *devo = NULL, *diro = NULL;
124 	char ndev[128], fdstr[15];
125 	int i, done = 0, reject_allow_other = 0, safe_level = 0;
126 	int altflags = DEFAULT_MOUNT_FLAGS;
127 	int __altflags = DEFAULT_MOUNT_FLAGS;
128 	int ch = 0;
129 	struct mntopt *mo;
130 	struct mntval *mv;
131 	static struct option longopts[] = {
132 		{"reject-allow_other", no_argument, NULL, 'A'},
133 		{"safe", no_argument, NULL, 'S'},
134 		{"daemon", required_argument, NULL, 'D'},
135 		{"daemon_opts", required_argument, NULL, 'O'},
136 		{"special", required_argument, NULL, 's'},
137 		{"mountpath", required_argument, NULL, 'm'},
138 		{"version", no_argument, NULL, 'V'},
139 		{"help", no_argument, NULL, 'h'},
140 		{0,0,0,0}
141 	};
142 	int pid = 0;
143 	int fd = -1, fdx;
144 	char *ep;
145 	char *daemon_str = NULL, *daemon_opts = NULL;
146 
147 	/*
148 	 * We want a parsing routine which is not sensitive to
149 	 * the position of args/opts; it should extract the
150 	 * first two args and stop at the beginning of the rest.
151 	 * (This makes it easier to call mount_fusefs from external
152 	 * utils than it is with a strict "util flags args" syntax.)
153 	 */
154 
155 	iov = NULL;
156 	iovlen = 0;
157 	mntflags = 0;
158 	/* All in all, I feel it more robust this way... */
159 	unsetenv("POSIXLY_CORRECT");
160 	if (getenv("MOUNT_FUSEFS_IGNORE_UNKNOWN"))
161 		getmnt_silent = 1;
162 	if (getenv("MOUNT_FUSEFS_VERBOSE"))
163 		verbose = 1;
164 
165 	do {
166 		for (i = 0; i < 3; i++) {
167 			if (optind < argc && argv[optind][0] != '-') {
168 				if (dir) {
169 					done = 1;
170 					break;
171 				}
172 				if (dev)
173 					dir = argv[optind];
174 				else
175 					dev = argv[optind];
176 				optind++;
177 			}
178 		}
179 		switch(ch) {
180 		case 'A':
181 			reject_allow_other = 1;
182 			break;
183 		case 'S':
184 			safe_level = 1;
185 			break;
186 		case 'D':
187 			if (daemon_str)
188 				errx(1, "daemon specified inconsistently");
189 			daemon_str = optarg;
190 			break;
191 		case 'O':
192 			if (daemon_opts)
193 				errx(1, "daemon opts specified inconsistently");
194 			daemon_opts = optarg;
195 			break;
196 		case 'o':
197 			getmntopts(optarg, mopts, &mntflags, &altflags);
198 			for (mv = mvals; mv->mv_flag; ++mv) {
199 				if (! (altflags & mv->mv_flag))
200 					continue;
201 				for (mo = mopts; mo->m_flag; ++mo) {
202 					char *p, *q;
203 
204 					if (mo->m_flag != mv->mv_flag)
205 						continue;
206 					p = strstr(optarg, mo->m_option);
207 					if (p) {
208 						p += strlen(mo->m_option);
209 						q = p;
210 						while (*q != '\0' && *q != ',')
211 							q++;
212 						mv->mv_len = q - p + 1;
213 						mv->mv_value = malloc(mv->mv_len);
214 						if (mv->mv_value == NULL)
215 							err(1, "malloc");
216 						memcpy(mv->mv_value, p, mv->mv_len - 1);
217 						((char *)mv->mv_value)[mv->mv_len - 1] = '\0';
218 						break;
219 					}
220 				}
221 			}
222 			break;
223 		case 's':
224 			if (devo)
225 				errx(1, "special specified inconsistently");
226 			devo = optarg;
227 			break;
228 		case 'm':
229 			if (diro)
230 				errx(1, "mount path specified inconsistently");
231 			diro = optarg;
232 			break;
233 		case 'v':
234 			verbose = 1;
235 			break;
236 		case 'h':
237 			helpmsg();
238 			break;
239 		case 'V':
240 			showversion();
241 			break;
242 		case '\0':
243 			break;
244 		case '?':
245 		default:
246 			usage();
247 		}
248 		if (done)
249 			break;
250 	} while ((ch = getopt_long(argc, argv, "AvVho:SD:O:s:m:", longopts, NULL)) != -1);
251 
252 	argc -= optind;
253 	argv += optind;
254 
255 	if (devo) {
256 		if (dev)
257 			errx(1, "special specified inconsistently");
258 		dev = devo;
259 	} else if (diro)
260 		errx(1, "if mountpoint is given via an option, special should also be given via an option");
261 
262 	if (diro) {
263 		if (dir)
264 			errx(1, "mount path specified inconsistently");
265 		dir = diro;
266 	}
267 
268 	if ((! dev) && argc > 0) {
269 		dev = *argv++;
270 		argc--;
271 	}
272 
273 	if ((! dir) && argc > 0) {
274 		dir = *argv++;
275 		argc--;
276 	}
277 
278 	if (! (dev && dir))
279 		errx(1, "missing special and/or mountpoint");
280 
281 	for (mo = mopts; mo->m_flag; ++mo) {
282 		if (altflags & mo->m_flag) {
283 			int iov_done = 0;
284 
285 			if (reject_allow_other &&
286 			    strcmp(mo->m_option, "allow_other") == 0)
287 				/*
288 				 * reject_allow_other is stronger than a
289 				 * negative of allow_other: if this is set,
290 				 * allow_other is blocked, period.
291 				 */
292 				errx(1, "\"allow_other\" usage is banned by respective option");
293 
294 			for (mv = mvals; mv->mv_flag; ++mv) {
295 				if (mo->m_flag != mv->mv_flag)
296 					continue;
297 				if (mv->mv_value) {
298 					build_iovec(&iov, &iovlen, mo->m_option, mv->mv_value, mv->mv_len);
299 					iov_done = 1;
300 					break;
301 				}
302 			}
303 			if (! iov_done)
304 				build_iovec(&iov, &iovlen, mo->m_option,
305 				    __DECONST(void *, ""), -1);
306 		}
307 		if (__altflags & mo->m_flag) {
308 			char *uscore_opt;
309 
310 			if (asprintf(&uscore_opt, "__%s", mo->m_option) == -1)
311 				err(1, "failed to allocate memory");
312 			build_iovec(&iov, &iovlen, uscore_opt,
313 			    __DECONST(void *, ""), -1);
314 			free(uscore_opt);
315 		}
316 	}
317 
318 	if (getenv("MOUNT_FUSEFS_SAFE"))
319 		safe_level = 1;
320 
321 	if (safe_level > 0 && (argc > 0 || daemon_str || daemon_opts))
322 		errx(1, "safe mode, spawning daemon not allowed");
323 
324 	if ((argc > 0 && (daemon_str || daemon_opts)) ||
325 	    (daemon_opts && ! daemon_str))
326 		errx(1, "daemon specified inconsistently");
327 
328 	/*
329 	 * Resolve the mountpoint with realpath(3) and remove unnecessary
330 	 * slashes from the devicename if there are any.
331 	 */
332 	if (checkpath(dir, mntpath) != 0)
333 		err(1, "%s", mntpath);
334 	(void)rmslashes(dev, dev);
335 
336 	if (strcmp(dev, "auto") == 0)
337 		dev = __DECONST(char *, "/dev/fuse");
338 
339 	if (strcmp(dev, "/dev/fuse") == 0) {
340 		if (! (argc > 0 || daemon_str)) {
341 			fprintf(stderr, "Please also specify the fuse daemon to run when mounting via the multiplexer!\n");
342 			usage();
343 		}
344 		if ((fd = open(dev, O_RDWR)) < 0)
345 			err(1, "failed to open fuse device");
346 	} else {
347 		fdx = strtol(dev, &ep, 10);
348 		if (*ep == '\0')
349 			fd = fdx;
350 	}
351 
352 	/* Identifying device */
353 	if (fd >= 0) {
354 		struct stat sbuf;
355 		char *ndevbas, *lep;
356 
357 		if (fstat(fd, &sbuf) == -1)
358 			err(1, "cannot stat device file descriptor");
359 
360 		strcpy(ndev, _PATH_DEV);
361 		ndevbas = ndev + strlen(_PATH_DEV);
362 		devname_r(sbuf.st_rdev, S_IFCHR, ndevbas,
363 		          sizeof(ndev) - strlen(_PATH_DEV));
364 
365 		if (strncmp(ndevbas, "fuse", 4))
366 			errx(1, "mounting inappropriate device");
367 
368 		strtol(ndevbas + 4, &lep, 10);
369 		if (*lep != '\0')
370 			errx(1, "mounting inappropriate device");
371 
372 		dev = ndev;
373 	}
374 
375 	if (argc > 0 || daemon_str) {
376 		char *fds;
377 
378 		if (fd < 0 && (fd = open(dev, O_RDWR)) < 0)
379 			err(1, "failed to open fuse device");
380 
381 		if (asprintf(&fds, "%d", fd) == -1)
382 			err(1, "failed to allocate memory");
383 		setenv("FUSE_DEV_FD", fds, 1);
384 		free(fds);
385 		setenv("FUSE_NO_MOUNT", "1", 1);
386 
387 		if (daemon_str) {
388 			char *bgdaemon;
389 			int len;
390 
391 			if (! daemon_opts)
392 				daemon_opts = __DECONST(char *, "");
393 
394 			len =  strlen(daemon_str) + 1 + strlen(daemon_opts) +
395 			    2 + 1;
396 			bgdaemon = calloc(1, len);
397 
398 			if (! bgdaemon)
399 				err(1, "failed to allocate memory");
400 
401 			strlcpy(bgdaemon, daemon_str, len);
402 			strlcat(bgdaemon, " ", len);
403 			strlcat(bgdaemon, daemon_opts, len);
404 			strlcat(bgdaemon, " &", len);
405 
406 			if (system(bgdaemon))
407 				err(1, "failed to call fuse daemon");
408 		} else {
409 			if ((pid = fork()) < 0)
410 				err(1, "failed to fork for fuse daemon");
411 
412 			if (pid == 0) {
413 				execvp(argv[0], argv);
414 				err(1, "failed to exec fuse daemon");
415 			}
416 		}
417 	}
418 
419 	/* Prepare the options vector for nmount(). build_iovec() is declared
420 	 * in mntopts.h. */
421 	sprintf(fdstr, "%d", fd);
422 	build_iovec(&iov, &iovlen, "fstype", __DECONST(void *, "fusefs"), -1);
423 	build_iovec(&iov, &iovlen, "fspath", mntpath, -1);
424 	build_iovec(&iov, &iovlen, "from", dev, -1);
425 	build_iovec(&iov, &iovlen, "fd", fdstr, -1);
426 
427 	if (verbose)
428 		fprintf(stderr, "mounting fuse daemon on device %s\n", dev);
429 
430 	if (nmount(iov, iovlen, mntflags) < 0)
431 		err(EX_OSERR, "%s on %s", dev, mntpath);
432 
433 	exit(0);
434 }
435 
436 void
437 __usage_short(void) {
438 	fprintf(stderr,
439 	    "usage:\n%s [-A|-S|-v|-V|-h|-D daemon|-O args|-s special|-m node|-o option...] special node [daemon args...]\n\n",
440 	    getprogname());
441 }
442 
443 void
444 usage(void)
445 {
446 	struct mntopt *mo;
447 
448 	__usage_short();
449 
450 	fprintf(stderr, "known options:\n");
451 	for (mo = mopts; mo->m_flag; ++mo)
452 		fprintf(stderr, "\t%s\n", mo->m_option);
453 
454 	fprintf(stderr, "\n(use -h for a detailed description of these options)\n");
455 	exit(EX_USAGE);
456 }
457 
458 void
459 helpmsg(void)
460 {
461 	if (! getenv("MOUNT_FUSEFS_CALL_BY_LIB")) {
462 		__usage_short();
463 		fprintf(stderr, "description of options:\n");
464 	}
465 
466 	/*
467 	 * The main use case of this function is giving info embedded in general
468 	 * FUSE lib help output. Therefore the style and the content of the output
469 	 * tries to fit there as much as possible.
470 	 */
471 	fprintf(stderr,
472 	        "    -o allow_other         allow access to other users\n"
473 	        /* "    -o nonempty            allow mounts over non-empty file/dir\n" */
474 	        "    -o default_permissions enable permission checking by kernel\n"
475 		"    -o intr                interruptible mount\n"
476 	        "    -o fsname=NAME         set filesystem name\n"
477 		/*
478 	        "    -o large_read          issue large read requests (2.4 only)\n"
479 		 */
480 	        "    -o subtype=NAME        set filesystem type\n"
481 	        "    -o max_read=N          set maximum size of read requests\n"
482 	        "    -o noprivate           allow secondary mounting of the filesystem\n"
483 	        "    -o neglect_shares      don't report EBUSY when unmount attempted\n"
484 	        "                           in presence of secondary mounts\n"
485 	        "    -o push_symlinks_in    prefix absolute symlinks with mountpoint\n"
486 	        );
487 	exit(EX_USAGE);
488 }
489 
490 void
491 showversion(void)
492 {
493 	puts("mount_fusefs [fuse4bsd] version: " FUSE4BSD_VERSION);
494 	exit(EX_USAGE);
495 }
496