1 /* $NetBSD: mount.c,v 1.84 2007/07/17 23:56:01 christos Exp $ */ 2 3 /* 4 * Copyright (c) 1980, 1989, 1993, 1994 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 #ifndef lint 34 __COPYRIGHT("@(#) Copyright (c) 1980, 1989, 1993, 1994\n\ 35 The Regents of the University of California. All rights reserved.\n"); 36 #endif /* not lint */ 37 38 #ifndef lint 39 #if 0 40 static char sccsid[] = "@(#)mount.c 8.25 (Berkeley) 5/8/95"; 41 #else 42 __RCSID("$NetBSD: mount.c,v 1.84 2007/07/17 23:56:01 christos Exp $"); 43 #endif 44 #endif /* not lint */ 45 46 #include <sys/param.h> 47 #include <sys/mount.h> 48 #include <sys/wait.h> 49 50 #include <err.h> 51 #include <errno.h> 52 #include <fstab.h> 53 #include <pwd.h> 54 #include <signal.h> 55 #include <stdio.h> 56 #include <stdlib.h> 57 #include <string.h> 58 #include <unistd.h> 59 60 #define MOUNTNAMES 61 #include <fcntl.h> 62 #include <sys/disk.h> 63 #include <sys/disklabel.h> 64 #include <sys/ioctl.h> 65 66 #include "pathnames.h" 67 #include "vfslist.h" 68 69 static int debug, verbose; 70 71 static void catopt(char **, const char *); 72 static const char * 73 getfslab(const char *str); 74 static struct statvfs * 75 getmntpt(const char *); 76 static int getmntargs(struct statvfs *, char *, size_t); 77 static int hasopt(const char *, const char *); 78 static void mangle(char *, int *, const char ** volatile *, int *); 79 static int mountfs(const char *, const char *, const char *, 80 int, const char *, const char *, int, char *, size_t); 81 static void prmount(struct statvfs *); 82 static void usage(void); 83 84 85 /* Map from mount otions to printable formats. */ 86 static const struct opt { 87 int o_opt; 88 int o_silent; 89 const char *o_name; 90 } optnames[] = { 91 __MNT_FLAGS 92 }; 93 94 static char ffs_fstype[] = "ffs"; 95 96 int 97 main(int argc, char *argv[]) 98 { 99 const char *mntfromname, *mntonname, **vfslist, *vfstype; 100 struct fstab *fs; 101 struct statvfs *mntbuf; 102 FILE *mountdfp; 103 int all, ch, forceall, i, init_flags, mntsize, rval; 104 char *options; 105 const char *mountopts, *fstypename; 106 char canonical_path_buf[MAXPATHLEN]; 107 char *canonical_path; 108 109 /* started as "mount" */ 110 all = forceall = init_flags = 0; 111 options = NULL; 112 vfslist = NULL; 113 vfstype = ffs_fstype; 114 while ((ch = getopt(argc, argv, "Aadfo:rwt:uv")) != -1) 115 switch (ch) { 116 case 'A': 117 all = forceall = 1; 118 break; 119 case 'a': 120 all = 1; 121 break; 122 case 'd': 123 debug = 1; 124 break; 125 case 'f': 126 init_flags |= MNT_FORCE; 127 break; 128 case 'o': 129 if (*optarg) 130 catopt(&options, optarg); 131 break; 132 case 'r': 133 init_flags |= MNT_RDONLY; 134 break; 135 case 't': 136 if (vfslist != NULL) 137 errx(1, 138 "only one -t option may be specified."); 139 vfslist = makevfslist(optarg); 140 vfstype = optarg; 141 break; 142 case 'u': 143 init_flags |= MNT_UPDATE; 144 break; 145 case 'v': 146 verbose++; 147 break; 148 case 'w': 149 init_flags &= ~MNT_RDONLY; 150 break; 151 case '?': 152 default: 153 usage(); 154 /* NOTREACHED */ 155 } 156 argc -= optind; 157 argv += optind; 158 159 #define BADTYPE(type) \ 160 (strcmp(type, FSTAB_RO) && \ 161 strcmp(type, FSTAB_RW) && strcmp(type, FSTAB_RQ)) 162 163 rval = 0; 164 switch (argc) { 165 case 0: 166 if (all) 167 while ((fs = getfsent()) != NULL) { 168 if (BADTYPE(fs->fs_type)) 169 continue; 170 if (checkvfsname(fs->fs_vfstype, vfslist)) 171 continue; 172 if (hasopt(fs->fs_mntops, "noauto")) 173 continue; 174 if (strcmp(fs->fs_spec, "from_mount") == 0) { 175 if ((mntbuf = getmntpt(fs->fs_file)) == NULL) 176 errx(1, 177 "unknown file system %s.", 178 fs->fs_file); 179 mntfromname = mntbuf->f_mntfromname; 180 } else 181 mntfromname = fs->fs_spec; 182 if (mountfs(fs->fs_vfstype, mntfromname, 183 fs->fs_file, init_flags, options, 184 fs->fs_mntops, !forceall, NULL, 0)) 185 rval = 1; 186 } 187 else { 188 if ((mntsize = getmntinfo(&mntbuf, MNT_NOWAIT)) == 0) 189 err(1, "getmntinfo"); 190 for (i = 0; i < mntsize; i++) { 191 if (checkvfsname(mntbuf[i].f_fstypename, 192 vfslist)) 193 continue; 194 prmount(&mntbuf[i]); 195 } 196 } 197 exit(rval); 198 /* NOTREACHED */ 199 case 1: 200 if (vfslist != NULL) { 201 usage(); 202 /* NOTREACHED */ 203 } 204 205 /* 206 * Create a canonical version of the device or mount path 207 * passed to us. It's ok for this to fail. It's also ok 208 * for the result to be exactly the same as the original. 209 */ 210 canonical_path = realpath(*argv, canonical_path_buf); 211 212 if (init_flags & MNT_UPDATE) { 213 /* 214 * Try looking up the canonical path first, 215 * then try exactly what the user entered. 216 */ 217 if ((canonical_path == NULL || 218 (mntbuf = getmntpt(canonical_path)) == NULL) && 219 (mntbuf = getmntpt(*argv)) == NULL 220 ) 221 { 222 errx(1, 223 "unknown special file or file system %s.", 224 *argv); 225 } 226 mntfromname = mntbuf->f_mntfromname; 227 if ((fs = getfsfile(mntbuf->f_mntonname)) != NULL) { 228 if (strcmp(fs->fs_spec, "from_mount") != 0) 229 mntfromname = fs->fs_spec; 230 /* ignore the fstab file options. */ 231 fs->fs_mntops = NULL; 232 } 233 mntonname = mntbuf->f_mntonname; 234 fstypename = mntbuf->f_fstypename; 235 mountopts = NULL; 236 } else { 237 /* 238 * Try looking up the canonical path first, 239 * then try exactly what the user entered. 240 */ 241 if (canonical_path == NULL || 242 ((fs = getfsfile(canonical_path)) == NULL && 243 (fs = getfsspec(canonical_path)) == NULL)) 244 { 245 if ((fs = getfsfile(*argv)) == NULL && 246 (fs = getfsspec(*argv)) == NULL) 247 { 248 errx(1, 249 "%s: unknown special file or file system.", 250 *argv); 251 } 252 } 253 if (BADTYPE(fs->fs_type)) 254 errx(1, "%s has unknown file system type.", 255 *argv); 256 if (strcmp(fs->fs_spec, "from_mount") == 0) { 257 if ((canonical_path == NULL || 258 (mntbuf = getmntpt(canonical_path)) == NULL) && 259 (mntbuf = getmntpt(*argv)) == NULL 260 ) 261 { 262 errx(1, 263 "unknown special file or file system %s.", 264 *argv); 265 } 266 mntfromname = mntbuf->f_mntfromname; 267 } else 268 mntfromname = fs->fs_spec; 269 mntonname = fs->fs_file; 270 fstypename = fs->fs_vfstype; 271 mountopts = fs->fs_mntops; 272 } 273 rval = mountfs(fstypename, mntfromname, 274 mntonname, init_flags, options, mountopts, 0, NULL, 0); 275 break; 276 case 2: 277 /* 278 * If -t flag has not been specified, and spec contains either 279 * a ':' or a '@' then assume that an NFS filesystem is being 280 * specified ala Sun. 281 */ 282 if (vfslist == NULL) { 283 if (strpbrk(argv[0], ":@") != NULL) 284 vfstype = "nfs"; 285 else { 286 vfstype = getfslab(argv[0]); 287 if (vfstype == NULL) 288 vfstype = ffs_fstype; 289 } 290 } 291 rval = mountfs(vfstype, 292 argv[0], argv[1], init_flags, options, NULL, 0, NULL, 0); 293 break; 294 default: 295 usage(); 296 /* NOTREACHED */ 297 } 298 299 /* 300 * If the mount was successfully, and done by root, tell mountd the 301 * good news. Pid checks are probably unnecessary, but don't hurt. 302 */ 303 if (rval == 0 && getuid() == 0 && 304 (mountdfp = fopen(_PATH_MOUNTDPID, "r")) != NULL) { 305 int pid; 306 307 if (fscanf(mountdfp, "%d", &pid) == 1 && 308 pid > 0 && kill(pid, SIGHUP) == -1 && errno != ESRCH) 309 err(1, "signal mountd"); 310 (void)fclose(mountdfp); 311 } 312 313 exit(rval); 314 /* NOTREACHED */ 315 } 316 317 int 318 hasopt(const char *mntopts, const char *option) 319 { 320 int negative, found; 321 char *opt, *optbuf; 322 323 if (option[0] == 'n' && option[1] == 'o') { 324 negative = 1; 325 option += 2; 326 } else 327 negative = 0; 328 optbuf = strdup(mntopts); 329 found = 0; 330 for (opt = optbuf; (opt = strtok(opt, ",")) != NULL; opt = NULL) { 331 if (opt[0] == 'n' && opt[1] == 'o') { 332 if (!strcasecmp(opt + 2, option)) 333 found = negative; 334 } else if (!strcasecmp(opt, option)) 335 found = !negative; 336 } 337 free(optbuf); 338 return (found); 339 } 340 341 static int 342 mountfs(const char *vfstype, const char *spec, const char *name, 343 int flags, const char *options, const char *mntopts, 344 int skipmounted, char *buf, size_t buflen) 345 { 346 /* List of directories containing mount_xxx subcommands. */ 347 static const char *edirs[] = { 348 #ifdef RESCUEDIR 349 RESCUEDIR, 350 #endif 351 _PATH_SBIN, 352 _PATH_USRSBIN, 353 NULL 354 }; 355 const char ** volatile argv, **edir; 356 struct statvfs *sfp, sf; 357 pid_t pid; 358 int pfd[2]; 359 int argc, numfs, i, status, maxargc; 360 char *optbuf, execname[MAXPATHLEN + 1], execbase[MAXPATHLEN], 361 mntpath[MAXPATHLEN]; 362 volatile int getargs; 363 364 if (realpath(name, mntpath) == NULL) { 365 warn("realpath %s", name); 366 return (1); 367 } 368 369 name = mntpath; 370 371 optbuf = NULL; 372 if (mntopts) 373 catopt(&optbuf, mntopts); 374 375 if (options) { 376 catopt(&optbuf, options); 377 getargs = strstr(options, "getargs") != NULL; 378 } else 379 getargs = 0; 380 381 if (!mntopts && !options) 382 catopt(&optbuf, "rw"); 383 384 if (getargs == 0 && strcmp(name, "/") == 0) 385 flags |= MNT_UPDATE; 386 else if (skipmounted) { 387 if ((numfs = getmntinfo(&sfp, MNT_WAIT)) == 0) { 388 warn("getmntinfo"); 389 return (1); 390 } 391 for(i = 0; i < numfs; i++) { 392 /* 393 * XXX can't check f_mntfromname, 394 * thanks to mfs, union, etc. 395 */ 396 if (strncmp(name, sfp[i].f_mntonname, MNAMELEN) == 0 && 397 strncmp(vfstype, sfp[i].f_fstypename, 398 sizeof(sfp[i].f_fstypename)) == 0) { 399 if (verbose) 400 (void)printf("%s on %s type %.*s: " 401 "%s\n", 402 sfp[i].f_mntfromname, 403 sfp[i].f_mntonname, 404 (int)sizeof(sfp[i].f_fstypename), 405 sfp[i].f_fstypename, 406 "already mounted"); 407 return (0); 408 } 409 } 410 } 411 if (flags & MNT_FORCE) 412 catopt(&optbuf, "force"); 413 if (flags & MNT_RDONLY) 414 catopt(&optbuf, "ro"); 415 416 if (flags & MNT_UPDATE) { 417 catopt(&optbuf, "update"); 418 /* Figure out the fstype only if we defaulted to ffs */ 419 if (vfstype == ffs_fstype && statvfs(name, &sf) != -1) 420 vfstype = sf.f_fstypename; 421 } 422 423 maxargc = 64; 424 argv = malloc(sizeof(char *) * maxargc); 425 if (argv == NULL) 426 err(1, "malloc"); 427 428 (void) snprintf(execbase, sizeof(execbase), "mount_%s", vfstype); 429 argc = 0; 430 argv[argc++] = execbase; 431 if (optbuf) 432 mangle(optbuf, &argc, &argv, &maxargc); 433 argv[argc++] = spec; 434 argv[argc++] = name; 435 argv[argc] = NULL; 436 437 if ((verbose && buf == NULL) || debug) { 438 (void)printf("exec:"); 439 for (i = 0; i < argc; i++) 440 (void)printf(" %s", argv[i]); 441 (void)printf("\n"); 442 } 443 444 if (buf) { 445 if (pipe(pfd) == -1) 446 warn("Cannot create pipe"); 447 } 448 449 switch (pid = vfork()) { 450 case -1: /* Error. */ 451 warn("vfork"); 452 if (optbuf) 453 free(optbuf); 454 free(argv); 455 return (1); 456 457 case 0: /* Child. */ 458 if (debug) 459 _exit(0); 460 461 if (buf) { 462 (void)close(pfd[0]); 463 (void)close(STDOUT_FILENO); 464 if (dup2(pfd[1], STDOUT_FILENO) == -1) 465 warn("Cannot open fd to mount program"); 466 } 467 468 /* Go find an executable. */ 469 edir = edirs; 470 do { 471 (void)snprintf(execname, 472 sizeof(execname), "%s/%s", *edir, execbase); 473 (void)execv(execname, __UNCONST(argv)); 474 if (errno != ENOENT) 475 warn("exec %s for %s", execname, name); 476 } while (*++edir != NULL); 477 478 if (errno == ENOENT) 479 warnx("%s not found for %s", execbase, name); 480 _exit(1); 481 /* NOTREACHED */ 482 483 default: /* Parent. */ 484 if (optbuf) 485 free(optbuf); 486 free(argv); 487 488 if (buf || getargs) { 489 char tbuf[1024], *ptr; 490 int nread; 491 492 if (buf == NULL) { 493 ptr = tbuf; 494 buflen = sizeof(tbuf) - 1; 495 } else { 496 ptr = buf; 497 buflen--; 498 } 499 (void)close(pfd[1]); 500 (void)signal(SIGPIPE, SIG_IGN); 501 while ((nread = read(pfd[0], ptr, buflen)) > 0) { 502 buflen -= nread; 503 ptr += nread; 504 } 505 *ptr = '\0'; 506 if (buflen == 0) { 507 while (read(pfd[0], &nread, sizeof(nread)) > 0) 508 continue; 509 } 510 if (buf == NULL) 511 (void)fprintf(stdout, "%s", tbuf); 512 } 513 514 if (waitpid(pid, &status, 0) < 0) { 515 warn("waitpid"); 516 return (1); 517 } 518 519 if (WIFEXITED(status)) { 520 if (WEXITSTATUS(status) != 0) 521 return (WEXITSTATUS(status)); 522 } else if (WIFSIGNALED(status)) { 523 warnx("%s: %s", name, strsignal(WTERMSIG(status))); 524 return (1); 525 } 526 527 if (buf == NULL) { 528 if (verbose) { 529 if (statvfs(name, &sf) < 0) { 530 warn("statvfs %s", name); 531 return (1); 532 } 533 prmount(&sf); 534 } 535 } 536 break; 537 } 538 539 return (0); 540 } 541 542 static void 543 prmount(struct statvfs *sfp) 544 { 545 int flags; 546 const struct opt *o; 547 struct passwd *pw; 548 int f; 549 550 (void)printf("%s on %s type %.*s", sfp->f_mntfromname, 551 sfp->f_mntonname, (int)sizeof(sfp->f_fstypename), 552 sfp->f_fstypename); 553 554 flags = sfp->f_flag & MNT_VISFLAGMASK; 555 for (f = 0, o = optnames; flags && o < 556 &optnames[sizeof(optnames)/sizeof(optnames[0])]; o++) 557 if (flags & o->o_opt) { 558 if (!o->o_silent || verbose) 559 (void)printf("%s%s", !f++ ? " (" : ", ", 560 o->o_name); 561 flags &= ~o->o_opt; 562 } 563 if (flags) 564 (void)printf("%sunknown flag%s %#x", !f++ ? " (" : ", ", 565 flags & (flags - 1) ? "s" : "", flags); 566 if (sfp->f_owner) { 567 (void)printf("%smounted by ", !f++ ? " (" : ", "); 568 if ((pw = getpwuid(sfp->f_owner)) != NULL) 569 (void)printf("%s", pw->pw_name); 570 else 571 (void)printf("%d", sfp->f_owner); 572 } 573 if (verbose) 574 (void)printf("%sfsid: 0x%x/0x%x", 575 !f++ ? " (" /* ) */: ", ", 576 sfp->f_fsidx.__fsid_val[0], sfp->f_fsidx.__fsid_val[1]); 577 578 if (verbose) { 579 (void)printf("%s", !f++ ? " (" : ", "); 580 (void)printf("reads: sync %" PRIu64 " async %" PRIu64 "", 581 sfp->f_syncreads, sfp->f_asyncreads); 582 (void)printf(", writes: sync %" PRIu64 " async %" PRIu64 "", 583 sfp->f_syncwrites, sfp->f_asyncwrites); 584 if (verbose > 1) { 585 char buf[2048]; 586 587 if (getmntargs(sfp, buf, sizeof(buf))) 588 printf(", [%s: %s]", sfp->f_fstypename, buf); 589 } 590 printf(")\n"); 591 } else 592 (void)printf("%s", f ? ")\n" : "\n"); 593 } 594 595 static int 596 getmntargs(struct statvfs *sfs, char *buf, size_t buflen) 597 { 598 599 if (mountfs(sfs->f_fstypename, sfs->f_mntfromname, sfs->f_mntonname, 0, 600 "getargs", NULL, 0, buf, buflen)) 601 return (0); 602 else { 603 if (*buf == '\0') 604 return (0); 605 if ((buf = strchr(buf, '\n')) != NULL) 606 *buf = '\0'; 607 return (1); 608 } 609 } 610 611 static struct statvfs * 612 getmntpt(const char *name) 613 { 614 struct statvfs *mntbuf; 615 int i, mntsize; 616 617 mntsize = getmntinfo(&mntbuf, MNT_NOWAIT); 618 for (i = 0; i < mntsize; i++) 619 if (strcmp(mntbuf[i].f_mntfromname, name) == 0 || 620 strcmp(mntbuf[i].f_mntonname, name) == 0) 621 return (&mntbuf[i]); 622 return (NULL); 623 } 624 625 static void 626 catopt(char **sp, const char *o) 627 { 628 char *s, *n; 629 630 s = *sp; 631 if (s) { 632 if (asprintf(&n, "%s,%s", s, o) < 0) 633 err(1, "asprintf"); 634 free(s); 635 s = n; 636 } else 637 s = strdup(o); 638 *sp = s; 639 } 640 641 static void 642 mangle(char *options, int *argcp, const char ** volatile *argvp, int *maxargcp) 643 { 644 char *p, *s; 645 int argc, maxargc; 646 const char **argv, **nargv; 647 648 argc = *argcp; 649 argv = *argvp; 650 maxargc = *maxargcp; 651 652 for (s = options; (p = strsep(&s, ",")) != NULL;) { 653 /* Always leave space for one more argument and the NULL. */ 654 if (argc >= maxargc - 4) { 655 nargv = realloc(argv, (maxargc << 1) * sizeof(char *)); 656 if (!nargv) 657 err(1, "realloc"); 658 argv = nargv; 659 maxargc <<= 1; 660 } 661 if (*p != '\0') { 662 if (*p == '-') { 663 argv[argc++] = p; 664 p = strchr(p, '='); 665 if (p) { 666 *p = '\0'; 667 argv[argc++] = p+1; 668 } 669 } else if (strcmp(p, "rw") != 0) { 670 argv[argc++] = "-o"; 671 argv[argc++] = p; 672 } 673 } 674 } 675 676 *argcp = argc; 677 *argvp = argv; 678 *maxargcp = maxargc; 679 } 680 681 /* Deduce the filesystem type from the disk label. */ 682 static const char * 683 getfslab(const char *str) 684 { 685 static struct dkwedge_info dkw; 686 struct disklabel dl; 687 int fd; 688 int part; 689 const char *vfstype; 690 u_char fstype; 691 char buf[MAXPATHLEN + 1]; 692 char *sp, *ep; 693 694 if ((fd = open(str, O_RDONLY)) == -1) { 695 /* 696 * Iff we get EBUSY try the raw device. Since mount always uses 697 * the block device we know we are never passed a raw device. 698 */ 699 if (errno != EBUSY) 700 err(1, "cannot open `%s'", str); 701 strlcpy(buf, str, MAXPATHLEN); 702 if ((sp = strrchr(buf, '/')) != NULL) 703 ++sp; 704 else 705 sp = buf; 706 for (ep = sp + strlen(sp) + 1; ep > sp; ep--) 707 *ep = *(ep - 1); 708 *sp = 'r'; 709 710 /* Silently fail here - mount call can display error */ 711 if ((fd = open(buf, O_RDONLY)) == -1) 712 return (NULL); 713 } 714 715 /* Check to see if this is a wedge. */ 716 if (ioctl(fd, DIOCGWEDGEINFO, &dkw) == 0) { 717 /* Yup, this is easy. */ 718 (void) close(fd); 719 return (dkw.dkw_ptype); 720 } 721 722 if (ioctl(fd, DIOCGDINFO, &dl) == -1) { 723 (void) close(fd); 724 return (NULL); 725 } 726 727 (void) close(fd); 728 729 part = str[strlen(str) - 1] - 'a'; 730 731 if (part < 0 || part >= dl.d_npartitions) 732 return (NULL); 733 734 /* Return NULL for unknown types - caller can fall back to ffs */ 735 if ((fstype = dl.d_partitions[part].p_fstype) >= FSMAXMOUNTNAMES) 736 vfstype = NULL; 737 else 738 vfstype = mountnames[fstype]; 739 740 return (vfstype); 741 } 742 743 static void 744 usage(void) 745 { 746 747 (void)fprintf(stderr, 748 "usage: mount %s\n mount %s\n mount %s\n", 749 "[-Aadfruvw] [-t type]", 750 "[-dfruvw] special | node", 751 "[-dfruvw] [-o options] [-t type] special node"); 752 exit(1); 753 /* NOTREACHED */ 754 } 755