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