1 /* $NetBSD: fsck.c,v 1.21 1999/04/22 04:20:53 abs Exp $ */ 2 3 /* 4 * Copyright (c) 1996 Christos Zoulas. All rights reserved. 5 * Copyright (c) 1980, 1989, 1993, 1994 6 * The Regents of the University of California. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by the University of 19 * California, Berkeley and its contributors. 20 * 4. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 * 36 * From: @(#)mount.c 8.19 (Berkeley) 4/19/94 37 * From: $NetBSD: mount.c,v 1.24 1995/11/18 03:34:29 cgd Exp 38 * $NetBSD: fsck.c,v 1.21 1999/04/22 04:20:53 abs Exp $ 39 */ 40 41 #include <sys/cdefs.h> 42 #ifndef lint 43 __RCSID("$FreeBSD$"); 44 #endif /* not lint */ 45 46 #include <sys/param.h> 47 #include <sys/mount.h> 48 #include <sys/queue.h> 49 #include <sys/wait.h> 50 #define FSTYPENAMES 51 #define DKTYPENAMES 52 #include <sys/disklabel.h> 53 #include <sys/ioctl.h> 54 55 #include <err.h> 56 #include <errno.h> 57 #include <fstab.h> 58 #include <fcntl.h> 59 #include <paths.h> 60 #include <signal.h> 61 #include <stdio.h> 62 #include <stdlib.h> 63 #include <string.h> 64 #include <unistd.h> 65 66 #include "pathnames.h" 67 #include "fsutil.h" 68 69 static enum { IN_LIST, NOT_IN_LIST } which = NOT_IN_LIST; 70 71 TAILQ_HEAD(fstypelist, entry) opthead, selhead; 72 73 struct entry { 74 char *type; 75 char *options; 76 TAILQ_ENTRY(entry) entries; 77 }; 78 79 static char *options = NULL; 80 static int flags = 0; 81 static int forceflag = 0; 82 83 int main __P((int, char *[])); 84 85 static int checkfs __P((const char *, const char *, const char *, char *, 86 pid_t *)); 87 static int selected __P((const char *)); 88 static void addoption __P((char *)); 89 static const char *getoptions __P((const char *)); 90 static void addentry __P((struct fstypelist *, const char *, const char *)); 91 static void maketypelist __P((char *)); 92 static void catopt __P((char **, const char *)); 93 static void mangle __P((char *, int *, const char ***, int *)); 94 static const char *getfslab __P((const char *)); 95 static void usage __P((void)); 96 static int isok __P((struct fstab *)); 97 98 int 99 main(argc, argv) 100 int argc; 101 char *argv[]; 102 { 103 struct fstab *fs; 104 int i, rval = 0; 105 const char *vfstype = NULL; 106 char globopt[3]; 107 108 globopt[0] = '-'; 109 globopt[2] = '\0'; 110 111 TAILQ_INIT(&selhead); 112 TAILQ_INIT(&opthead); 113 114 while ((i = getopt(argc, argv, "BdvpfFnyl:t:T:")) != -1) 115 switch (i) { 116 case 'B': 117 if (flags & CHECK_BACKGRD) 118 errx(1, "Cannot specify -B and -F."); 119 flags |= DO_BACKGRD; 120 break; 121 122 case 'd': 123 flags |= CHECK_DEBUG; 124 break; 125 126 case 'v': 127 flags |= CHECK_VERBOSE; 128 break; 129 130 case 'F': 131 if (flags & DO_BACKGRD) 132 errx(1, "Cannot specify -B and -F."); 133 flags |= CHECK_BACKGRD; 134 break; 135 136 case 'p': 137 flags |= CHECK_PREEN; 138 /*FALLTHROUGH*/ 139 case 'n': 140 case 'y': 141 globopt[1] = i; 142 catopt(&options, globopt); 143 break; 144 145 case 'f': 146 forceflag = 1; 147 globopt[1] = i; 148 catopt(&options, globopt); 149 break; 150 151 case 'l': 152 warnx("Ignoring obsolete -l option\n"); 153 break; 154 155 case 'T': 156 if (*optarg) 157 addoption(optarg); 158 break; 159 160 case 't': 161 if (!TAILQ_EMPTY(&selhead)) 162 errx(1, "only one -t option may be specified."); 163 164 maketypelist(optarg); 165 vfstype = optarg; 166 break; 167 168 case '?': 169 default: 170 usage(); 171 /* NOTREACHED */ 172 } 173 174 argc -= optind; 175 argv += optind; 176 177 if (argc == 0) 178 return checkfstab(flags, isok, checkfs); 179 180 #define BADTYPE(type) \ 181 (strcmp(type, FSTAB_RO) && \ 182 strcmp(type, FSTAB_RW) && strcmp(type, FSTAB_RQ)) 183 184 185 for (; argc--; argv++) { 186 const char *spec, *mntpt, *type, *cp; 187 char device[MAXPATHLEN]; 188 struct statfs *mntp; 189 190 spec = *argv; 191 cp = strrchr(spec, '/'); 192 if (cp == 0) { 193 (void)snprintf(device, sizeof(device), "%s%s", 194 _PATH_DEV, spec); 195 spec = device; 196 } 197 mntp = getmntpt(spec); 198 if (mntp != NULL) { 199 spec = mntp->f_mntfromname; 200 mntpt = mntp->f_mntonname; 201 } 202 if ((fs = getfsfile(spec)) == NULL && 203 (fs = getfsspec(spec)) == NULL) { 204 if (vfstype == NULL) 205 vfstype = getfslab(spec); 206 type = vfstype; 207 devcheck(spec); 208 } else { 209 spec = fs->fs_spec; 210 type = fs->fs_vfstype; 211 mntpt = fs->fs_file; 212 if (BADTYPE(fs->fs_type)) 213 errx(1, "%s has unknown file system type.", 214 spec); 215 } 216 if ((flags & CHECK_BACKGRD) && 217 checkfs(type, spec, mntpt, "-F", NULL) == 0) { 218 printf("%s: DEFER FOR BACKGROUND CHECKING\n", *argv); 219 continue; 220 } 221 if ((flags & DO_BACKGRD) && forceflag == 0 && 222 checkfs(type, spec, mntpt, "-F", NULL) != 0) 223 continue; 224 225 rval |= checkfs(type, spec, mntpt, NULL, NULL); 226 } 227 228 return rval; 229 } 230 231 232 static int 233 isok(fs) 234 struct fstab *fs; 235 { 236 int i; 237 238 if (fs->fs_passno == 0) 239 return (0); 240 if (BADTYPE(fs->fs_type)) 241 return (0); 242 if (!selected(fs->fs_vfstype)) 243 return (0); 244 /* 245 * If the -B flag has been given, then process the needed 246 * background checks. Background checks cannot be run on 247 * filesystems that will be mounted read-only or that were 248 * not mounted at boot time (typically those marked `noauto'). 249 * If these basic tests are passed, check with the filesystem 250 * itself to see if it is willing to do background checking 251 * by invoking its check program with the -F flag. 252 */ 253 if (flags & DO_BACKGRD) { 254 if (!strcmp(fs->fs_type, FSTAB_RO)) 255 return (0); 256 if (getmntpt(fs->fs_spec) == NULL) 257 return (0); 258 if (checkfs(fs->fs_vfstype, fs->fs_spec, fs->fs_file, "-F", 0)) 259 return (0); 260 return (1); 261 } 262 /* 263 * If the -F flag has been given, then consider deferring the 264 * check to background. Background checks cannot be run on 265 * filesystems that will be mounted read-only or that will 266 * not be mounted at boot time (e.g., marked `noauto'). If 267 * these basic tests are passed, check with the filesystem 268 * itself to see if it is willing to defer to background 269 * checking by invoking its check program with the -F flag. 270 */ 271 if ((flags & CHECK_BACKGRD) == 0 || !strcmp(fs->fs_type, FSTAB_RO)) 272 return (1); 273 for (i = strlen(fs->fs_mntops) - 6; i >= 0; i--) 274 if (!strncmp(&fs->fs_mntops[i], "noauto", 6)) 275 break; 276 if (i >= 0) 277 return (1); 278 if (checkfs(fs->fs_vfstype, fs->fs_spec, fs->fs_file, "-F", NULL) != 0) 279 return (1); 280 printf("%s: DEFER FOR BACKGROUND CHECKING\n", fs->fs_spec); 281 return (0); 282 } 283 284 285 static int 286 checkfs(pvfstype, spec, mntpt, auxopt, pidp) 287 const char *pvfstype, *spec, *mntpt; 288 char *auxopt; 289 pid_t *pidp; 290 { 291 /* List of directories containing fsck_xxx subcommands. */ 292 static const char *edirs[] = { 293 _PATH_SBIN, 294 _PATH_USRSBIN, 295 NULL 296 }; 297 const char **argv, **edir; 298 pid_t pid; 299 int argc, i, status, maxargc; 300 char *optbuf, execname[MAXPATHLEN + 1], execbase[MAXPATHLEN]; 301 char *vfstype = NULL; 302 const char *extra = NULL; 303 304 #ifdef __GNUC__ 305 /* Avoid vfork clobbering */ 306 (void) &optbuf; 307 (void) &vfstype; 308 #endif 309 /* 310 * We convert the vfstype to lowercase and any spaces to underscores 311 * to not confuse the issue 312 */ 313 vfstype = strdup(pvfstype); 314 if (vfstype == NULL) 315 perror("strdup(pvfstype)"); 316 for (i = 0; i < strlen(vfstype); i++) { 317 vfstype[i] = tolower(vfstype[i]); 318 if (vfstype[i] == ' ') 319 vfstype[i] = '_'; 320 } 321 322 extra = getoptions(vfstype); 323 optbuf = NULL; 324 if (options) 325 catopt(&optbuf, options); 326 if (extra) 327 catopt(&optbuf, extra); 328 if (auxopt) 329 catopt(&optbuf, auxopt); 330 else if (flags & DO_BACKGRD) 331 catopt(&optbuf, "-B"); 332 333 maxargc = 64; 334 argv = emalloc(sizeof(char *) * maxargc); 335 336 (void) snprintf(execbase, sizeof(execbase), "fsck_%s", vfstype); 337 argc = 0; 338 argv[argc++] = execbase; 339 if (optbuf) 340 mangle(optbuf, &argc, &argv, &maxargc); 341 argv[argc++] = spec; 342 argv[argc] = NULL; 343 344 if (flags & (CHECK_DEBUG|CHECK_VERBOSE)) { 345 (void)printf("start %s %swait", mntpt, 346 pidp ? "no" : ""); 347 for (i = 0; i < argc; i++) 348 (void)printf(" %s", argv[i]); 349 (void)printf("\n"); 350 } 351 352 switch (pid = vfork()) { 353 case -1: /* Error. */ 354 warn("vfork"); 355 if (optbuf) 356 free(optbuf); 357 free(vfstype); 358 return (1); 359 360 case 0: /* Child. */ 361 if ((flags & CHECK_DEBUG) && auxopt == NULL) 362 _exit(0); 363 364 /* Go find an executable. */ 365 edir = edirs; 366 do { 367 (void)snprintf(execname, 368 sizeof(execname), "%s/%s", *edir, execbase); 369 execv(execname, (char * const *)argv); 370 if (errno != ENOENT) { 371 if (spec) 372 warn("exec %s for %s", execname, spec); 373 else 374 warn("exec %s", execname); 375 } 376 } while (*++edir != NULL); 377 378 if (errno == ENOENT) { 379 if (spec) 380 warn("exec %s for %s", execname, spec); 381 else 382 warn("exec %s", execname); 383 } 384 _exit(1); 385 /* NOTREACHED */ 386 387 default: /* Parent. */ 388 if (optbuf) 389 free(optbuf); 390 391 free(vfstype); 392 393 if (pidp) { 394 *pidp = pid; 395 return 0; 396 } 397 398 if (waitpid(pid, &status, 0) < 0) { 399 warn("waitpid"); 400 return (1); 401 } 402 403 if (WIFEXITED(status)) { 404 if (WEXITSTATUS(status) != 0) 405 return (WEXITSTATUS(status)); 406 } 407 else if (WIFSIGNALED(status)) { 408 warnx("%s: %s", spec, strsignal(WTERMSIG(status))); 409 return (1); 410 } 411 break; 412 } 413 414 return (0); 415 } 416 417 418 static int 419 selected(type) 420 const char *type; 421 { 422 struct entry *e; 423 424 /* If no type specified, it's always selected. */ 425 TAILQ_FOREACH(e, &selhead, entries) 426 if (!strncmp(e->type, type, MFSNAMELEN)) 427 return which == IN_LIST ? 1 : 0; 428 429 return which == IN_LIST ? 0 : 1; 430 } 431 432 433 static const char * 434 getoptions(type) 435 const char *type; 436 { 437 struct entry *e; 438 439 TAILQ_FOREACH(e, &opthead, entries) 440 if (!strncmp(e->type, type, MFSNAMELEN)) 441 return e->options; 442 return ""; 443 } 444 445 446 static void 447 addoption(optstr) 448 char *optstr; 449 { 450 char *newoptions; 451 struct entry *e; 452 453 if ((newoptions = strchr(optstr, ':')) == NULL) 454 errx(1, "Invalid option string"); 455 456 *newoptions++ = '\0'; 457 458 TAILQ_FOREACH(e, &opthead, entries) 459 if (!strncmp(e->type, optstr, MFSNAMELEN)) { 460 catopt(&e->options, newoptions); 461 return; 462 } 463 addentry(&opthead, optstr, newoptions); 464 } 465 466 467 static void 468 addentry(list, type, opts) 469 struct fstypelist *list; 470 const char *type; 471 const char *opts; 472 { 473 struct entry *e; 474 475 e = emalloc(sizeof(struct entry)); 476 e->type = estrdup(type); 477 e->options = estrdup(opts); 478 TAILQ_INSERT_TAIL(list, e, entries); 479 } 480 481 482 static void 483 maketypelist(fslist) 484 char *fslist; 485 { 486 char *ptr; 487 488 if ((fslist == NULL) || (fslist[0] == '\0')) 489 errx(1, "empty type list"); 490 491 if (fslist[0] == 'n' && fslist[1] == 'o') { 492 fslist += 2; 493 which = NOT_IN_LIST; 494 } 495 else 496 which = IN_LIST; 497 498 while ((ptr = strsep(&fslist, ",")) != NULL) 499 addentry(&selhead, ptr, ""); 500 501 } 502 503 504 static void 505 catopt(sp, o) 506 char **sp; 507 const char *o; 508 { 509 char *s; 510 size_t i, j; 511 512 s = *sp; 513 if (s) { 514 i = strlen(s); 515 j = i + 1 + strlen(o) + 1; 516 s = erealloc(s, j); 517 (void)snprintf(s + i, j, ",%s", o); 518 } else 519 s = estrdup(o); 520 *sp = s; 521 } 522 523 524 static void 525 mangle(options, argcp, argvp, maxargcp) 526 char *options; 527 int *argcp, *maxargcp; 528 const char ***argvp; 529 { 530 char *p, *s; 531 int argc, maxargc; 532 const char **argv; 533 534 argc = *argcp; 535 argv = *argvp; 536 maxargc = *maxargcp; 537 538 for (s = options; (p = strsep(&s, ",")) != NULL;) { 539 /* Always leave space for one more argument and the NULL. */ 540 if (argc >= maxargc - 3) { 541 maxargc <<= 1; 542 argv = erealloc(argv, maxargc * sizeof(char *)); 543 } 544 if (*p != '\0') { 545 if (*p == '-') { 546 argv[argc++] = p; 547 p = strchr(p, '='); 548 if (p) { 549 *p = '\0'; 550 argv[argc++] = p+1; 551 } 552 } else { 553 argv[argc++] = "-o"; 554 argv[argc++] = p; 555 } 556 } 557 } 558 559 *argcp = argc; 560 *argvp = argv; 561 *maxargcp = maxargc; 562 } 563 564 565 const static char * 566 getfslab(str) 567 const char *str; 568 { 569 struct disklabel dl; 570 int fd; 571 char p; 572 const char *vfstype; 573 u_char t; 574 575 /* deduce the filesystem type from the disk label */ 576 if ((fd = open(str, O_RDONLY)) == -1) 577 err(1, "cannot open `%s'", str); 578 579 if (ioctl(fd, DIOCGDINFO, &dl) == -1) 580 err(1, "cannot get disklabel for `%s'", str); 581 582 (void) close(fd); 583 584 p = str[strlen(str) - 1]; 585 586 if ((p - 'a') >= dl.d_npartitions) 587 errx(1, "partition `%s' is not defined on disk", str); 588 589 if ((t = dl.d_partitions[p - 'a'].p_fstype) >= FSMAXTYPES) 590 errx(1, "partition `%s' is not of a legal vfstype", 591 str); 592 593 if ((vfstype = fstypenames[t]) == NULL) 594 errx(1, "vfstype `%s' on partition `%s' is not supported", 595 fstypenames[t], str); 596 597 return vfstype; 598 } 599 600 601 static void 602 usage() 603 { 604 extern char *__progname; 605 static const char common[] = 606 "[-BFdpvlyn] [-T fstype:fsoptions] [-t fstype]"; 607 608 (void)fprintf(stderr, "Usage: %s %s [special|node]...\n", 609 __progname, common); 610 exit(1); 611 } 612