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