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