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 __FBSDID("$FreeBSD$"); 43 44 #include <sys/param.h> 45 #include <sys/mount.h> 46 #include <sys/queue.h> 47 #include <sys/wait.h> 48 #define FSTYPENAMES 49 #include <sys/disklabel.h> 50 #include <sys/ioctl.h> 51 52 #include <ctype.h> 53 #include <err.h> 54 #include <errno.h> 55 #include <fstab.h> 56 #include <fcntl.h> 57 #include <paths.h> 58 #include <signal.h> 59 #include <stdio.h> 60 #include <stdlib.h> 61 #include <string.h> 62 #include <unistd.h> 63 64 #include "fsutil.h" 65 66 static enum { IN_LIST, NOT_IN_LIST } which = NOT_IN_LIST; 67 68 static TAILQ_HEAD(fstypelist, entry) opthead, selhead; 69 70 struct entry { 71 char *type; 72 char *options; 73 TAILQ_ENTRY(entry) entries; 74 }; 75 76 static char *options = NULL; 77 static int flags = 0; 78 static int forceflag = 0; 79 80 static int checkfs(const char *, const char *, const char *, char *, pid_t *); 81 static int selected(const char *); 82 static void addoption(char *); 83 static const char *getoptions(const char *); 84 static void addentry(struct fstypelist *, const char *, const char *); 85 static void maketypelist(char *); 86 static void catopt(char **, const char *); 87 static void mangle(char *, int *, const char ***, int *); 88 static const char *getfslab(const char *); 89 static void usage(void) __dead2; 90 static int isok(struct fstab *); 91 92 int 93 main(int argc, char *argv[]) 94 { 95 struct fstab *fs; 96 int i, rval = 0; 97 const char *vfstype = NULL; 98 char globopt[3]; 99 const char *etc_fstab; 100 101 globopt[0] = '-'; 102 globopt[2] = '\0'; 103 104 TAILQ_INIT(&selhead); 105 TAILQ_INIT(&opthead); 106 107 etc_fstab = NULL; 108 while ((i = getopt(argc, argv, "BCdvpfFnyl:t:T:c:")) != -1) 109 switch (i) { 110 case 'B': 111 if (flags & CHECK_BACKGRD) 112 errx(1, "Cannot specify -B and -F."); 113 flags |= DO_BACKGRD; 114 break; 115 116 case 'd': 117 flags |= CHECK_DEBUG; 118 break; 119 120 case 'v': 121 flags |= CHECK_VERBOSE; 122 break; 123 124 case 'F': 125 if (flags & DO_BACKGRD) 126 errx(1, "Cannot specify -B and -F."); 127 flags |= CHECK_BACKGRD; 128 break; 129 130 case 'p': 131 flags |= CHECK_PREEN; 132 /*FALLTHROUGH*/ 133 case 'C': 134 flags |= CHECK_CLEAN; 135 /*FALLTHROUGH*/ 136 case 'n': 137 case 'y': 138 globopt[1] = i; 139 catopt(&options, globopt); 140 break; 141 142 case 'f': 143 forceflag = 1; 144 globopt[1] = i; 145 catopt(&options, globopt); 146 break; 147 148 case 'l': 149 warnx("Ignoring obsolete -l option\n"); 150 break; 151 152 case 'T': 153 if (*optarg) 154 addoption(optarg); 155 break; 156 157 case 't': 158 if (!TAILQ_EMPTY(&selhead)) 159 errx(1, "only one -t option may be specified."); 160 161 maketypelist(optarg); 162 vfstype = optarg; 163 break; 164 165 case 'c': 166 etc_fstab = optarg; 167 break; 168 169 case '?': 170 default: 171 usage(); 172 /* NOTREACHED */ 173 } 174 175 argc -= optind; 176 argv += optind; 177 178 if (etc_fstab != NULL) 179 setfstab(etc_fstab); 180 181 if (argc == 0) 182 return checkfstab(flags, isok, checkfs); 183 184 #define BADTYPE(type) \ 185 (strcmp(type, FSTAB_RO) && \ 186 strcmp(type, FSTAB_RW) && strcmp(type, FSTAB_RQ)) 187 188 189 for (; argc--; argv++) { 190 const char *spec, *mntpt, *type, *cp; 191 char device[MAXPATHLEN]; 192 struct statfs *mntp; 193 194 spec = *argv; 195 cp = strrchr(spec, '/'); 196 if (cp == 0) { 197 (void)snprintf(device, sizeof(device), "%s%s", 198 _PATH_DEV, spec); 199 spec = device; 200 } 201 mntp = getmntpt(spec); 202 if (mntp != NULL) { 203 spec = mntp->f_mntfromname; 204 mntpt = mntp->f_mntonname; 205 } 206 if ((fs = getfsfile(spec)) == NULL && 207 (fs = getfsspec(spec)) == NULL) { 208 if (vfstype == NULL) 209 vfstype = getfslab(spec); 210 if (vfstype == NULL) 211 errx(1, "Could not determine filesystem type"); 212 type = vfstype; 213 devcheck(spec); 214 } else { 215 spec = fs->fs_spec; 216 type = fs->fs_vfstype; 217 mntpt = fs->fs_file; 218 if (BADTYPE(fs->fs_type)) 219 errx(1, "%s has unknown file system type.", 220 spec); 221 } 222 if ((flags & CHECK_BACKGRD) && 223 checkfs(type, spec, mntpt, "-F", NULL) == 0) { 224 printf("%s: DEFER FOR BACKGROUND CHECKING\n", *argv); 225 continue; 226 } 227 if ((flags & DO_BACKGRD) && forceflag == 0 && 228 checkfs(type, spec, mntpt, "-F", NULL) != 0) 229 continue; 230 231 rval |= checkfs(type, spec, mntpt, NULL, NULL); 232 } 233 234 return rval; 235 } 236 237 238 static int 239 isok(struct fstab *fs) 240 { 241 int i; 242 243 if (fs->fs_passno == 0) 244 return (0); 245 if (BADTYPE(fs->fs_type)) 246 return (0); 247 if (!selected(fs->fs_vfstype)) 248 return (0); 249 /* 250 * If the -B flag has been given, then process the needed 251 * background checks. Background checks cannot be run on 252 * file systems that will be mounted read-only or that were 253 * not mounted at boot time (typically those marked `noauto'). 254 * If these basic tests are passed, check with the file system 255 * itself to see if it is willing to do background checking 256 * by invoking its check program with the -F flag. 257 */ 258 if (flags & DO_BACKGRD) { 259 if (!strcmp(fs->fs_type, FSTAB_RO)) 260 return (0); 261 if (getmntpt(fs->fs_spec) == NULL) 262 return (0); 263 if (checkfs(fs->fs_vfstype, fs->fs_spec, fs->fs_file, "-F", 0)) 264 return (0); 265 return (1); 266 } 267 /* 268 * If the -F flag has been given, then consider deferring the 269 * check to background. Background checks cannot be run on 270 * file systems that will be mounted read-only or that will 271 * not be mounted at boot time (e.g., marked `noauto'). If 272 * these basic tests are passed, check with the file system 273 * itself to see if it is willing to defer to background 274 * checking by invoking its check program with the -F flag. 275 */ 276 if ((flags & CHECK_BACKGRD) == 0 || !strcmp(fs->fs_type, FSTAB_RO)) 277 return (1); 278 for (i = strlen(fs->fs_mntops) - 6; i >= 0; i--) 279 if (!strncmp(&fs->fs_mntops[i], "noauto", 6)) 280 break; 281 if (i >= 0) 282 return (1); 283 if (checkfs(fs->fs_vfstype, fs->fs_spec, fs->fs_file, "-F", NULL) != 0) 284 return (1); 285 printf("%s: DEFER FOR BACKGROUND CHECKING\n", fs->fs_spec); 286 return (0); 287 } 288 289 290 static int 291 checkfs(const char *pvfstype, const char *spec, const char *mntpt, 292 char *auxopt, pid_t *pidp) 293 { 294 const char **argv; 295 pid_t pid; 296 int argc, i, status, maxargc; 297 char *optbuf, execbase[MAXPATHLEN]; 298 char *vfstype = NULL; 299 const char *extra = NULL; 300 301 #ifdef __GNUC__ 302 /* Avoid vfork clobbering */ 303 (void) &optbuf; 304 (void) &vfstype; 305 #endif 306 /* 307 * We convert the vfstype to lowercase and any spaces to underscores 308 * to not confuse the issue 309 * 310 * XXX This is a kludge to make automatic filesystem type guessing 311 * from the disklabel work for "4.2BSD" filesystems. It does a 312 * very limited subset of transliteration to a normalised form of 313 * filesystem name, and we do not seem to enforce a filesystem 314 * name character set. 315 */ 316 vfstype = strdup(pvfstype); 317 if (vfstype == NULL) 318 perror("strdup(pvfstype)"); 319 for (i = 0; i < strlen(vfstype); i++) { 320 vfstype[i] = tolower(vfstype[i]); 321 if (vfstype[i] == ' ') 322 vfstype[i] = '_'; 323 } 324 325 extra = getoptions(vfstype); 326 optbuf = NULL; 327 if (options) 328 catopt(&optbuf, options); 329 if (extra) 330 catopt(&optbuf, extra); 331 if (auxopt) 332 catopt(&optbuf, auxopt); 333 else if (flags & DO_BACKGRD) 334 catopt(&optbuf, "-B"); 335 336 maxargc = 64; 337 argv = emalloc(sizeof(char *) * maxargc); 338 339 (void) snprintf(execbase, sizeof(execbase), "fsck_%s", vfstype); 340 argc = 0; 341 argv[argc++] = execbase; 342 if (optbuf) 343 mangle(optbuf, &argc, &argv, &maxargc); 344 argv[argc++] = spec; 345 argv[argc] = NULL; 346 347 if (flags & (CHECK_DEBUG|CHECK_VERBOSE)) { 348 (void)printf("start %s %swait", mntpt, 349 pidp ? "no" : ""); 350 for (i = 0; i < argc; i++) 351 (void)printf(" %s", argv[i]); 352 (void)printf("\n"); 353 } 354 355 switch (pid = vfork()) { 356 case -1: /* Error. */ 357 warn("vfork"); 358 if (optbuf) 359 free(optbuf); 360 free(vfstype); 361 return (1); 362 363 case 0: /* Child. */ 364 if ((flags & CHECK_DEBUG) && auxopt == NULL) 365 _exit(0); 366 367 /* Go find an executable. */ 368 execvP(execbase, _PATH_SYSPATH, (char * const *)argv); 369 if (spec) 370 warn("exec %s for %s in %s", execbase, spec, _PATH_SYSPATH); 371 else 372 warn("exec %s in %s", execbase, _PATH_SYSPATH); 373 _exit(1); 374 /* NOTREACHED */ 375 376 default: /* Parent. */ 377 if (optbuf) 378 free(optbuf); 379 380 free(vfstype); 381 382 if (pidp) { 383 *pidp = pid; 384 return 0; 385 } 386 387 if (waitpid(pid, &status, 0) < 0) { 388 warn("waitpid"); 389 return (1); 390 } 391 392 if (WIFEXITED(status)) { 393 if (WEXITSTATUS(status) != 0) 394 return (WEXITSTATUS(status)); 395 } 396 else if (WIFSIGNALED(status)) { 397 warnx("%s: %s", spec, strsignal(WTERMSIG(status))); 398 return (1); 399 } 400 break; 401 } 402 403 return (0); 404 } 405 406 407 static int 408 selected(const char *type) 409 { 410 struct entry *e; 411 412 /* If no type specified, it's always selected. */ 413 TAILQ_FOREACH(e, &selhead, entries) 414 if (!strncmp(e->type, type, MFSNAMELEN)) 415 return which == IN_LIST ? 1 : 0; 416 417 return which == IN_LIST ? 0 : 1; 418 } 419 420 421 static const char * 422 getoptions(const char *type) 423 { 424 struct entry *e; 425 426 TAILQ_FOREACH(e, &opthead, entries) 427 if (!strncmp(e->type, type, MFSNAMELEN)) 428 return e->options; 429 return ""; 430 } 431 432 433 static void 434 addoption(char *optstr) 435 { 436 char *newoptions; 437 struct entry *e; 438 439 if ((newoptions = strchr(optstr, ':')) == NULL) 440 errx(1, "Invalid option string"); 441 442 *newoptions++ = '\0'; 443 444 TAILQ_FOREACH(e, &opthead, entries) 445 if (!strncmp(e->type, optstr, MFSNAMELEN)) { 446 catopt(&e->options, newoptions); 447 return; 448 } 449 addentry(&opthead, optstr, newoptions); 450 } 451 452 453 static void 454 addentry(struct fstypelist *list, const char *type, const char *opts) 455 { 456 struct entry *e; 457 458 e = emalloc(sizeof(struct entry)); 459 e->type = estrdup(type); 460 e->options = estrdup(opts); 461 TAILQ_INSERT_TAIL(list, e, entries); 462 } 463 464 465 static void 466 maketypelist(char *fslist) 467 { 468 char *ptr; 469 470 if ((fslist == NULL) || (fslist[0] == '\0')) 471 errx(1, "empty type list"); 472 473 if (fslist[0] == 'n' && fslist[1] == 'o') { 474 fslist += 2; 475 which = NOT_IN_LIST; 476 } 477 else 478 which = IN_LIST; 479 480 while ((ptr = strsep(&fslist, ",")) != NULL) 481 addentry(&selhead, ptr, ""); 482 483 } 484 485 486 static void 487 catopt(char **sp, const char *o) 488 { 489 char *s; 490 size_t i, j; 491 492 s = *sp; 493 if (s) { 494 i = strlen(s); 495 j = i + 1 + strlen(o) + 1; 496 s = erealloc(s, j); 497 (void)snprintf(s + i, j, ",%s", o); 498 } else 499 s = estrdup(o); 500 *sp = s; 501 } 502 503 504 static void 505 mangle(char *options, int *argcp, const char ***argvp, int *maxargcp) 506 { 507 char *p, *s; 508 int argc, maxargc; 509 const char **argv; 510 511 argc = *argcp; 512 argv = *argvp; 513 maxargc = *maxargcp; 514 515 for (s = options; (p = strsep(&s, ",")) != NULL;) { 516 /* Always leave space for one more argument and the NULL. */ 517 if (argc >= maxargc - 3) { 518 maxargc <<= 1; 519 argv = erealloc(argv, maxargc * sizeof(char *)); 520 } 521 if (*p != '\0') { 522 if (*p == '-') { 523 argv[argc++] = p; 524 p = strchr(p, '='); 525 if (p) { 526 *p = '\0'; 527 argv[argc++] = p+1; 528 } 529 } else { 530 argv[argc++] = "-o"; 531 argv[argc++] = p; 532 } 533 } 534 } 535 536 *argcp = argc; 537 *argvp = argv; 538 *maxargcp = maxargc; 539 } 540 541 542 const static char * 543 getfslab(const char *str) 544 { 545 struct disklabel dl; 546 int fd; 547 char p; 548 const char *vfstype; 549 u_char t; 550 551 /* deduce the file system type from the disk label */ 552 if ((fd = open(str, O_RDONLY)) == -1) 553 err(1, "cannot open `%s'", str); 554 555 if (ioctl(fd, DIOCGDINFO, &dl) == -1) { 556 (void) close(fd); 557 return(NULL); 558 } 559 560 (void) close(fd); 561 562 p = str[strlen(str) - 1]; 563 564 if ((p - 'a') >= dl.d_npartitions) 565 errx(1, "partition `%s' is not defined on disk", str); 566 567 if ((t = dl.d_partitions[p - 'a'].p_fstype) >= FSMAXTYPES) 568 errx(1, "partition `%s' is not of a legal vfstype", 569 str); 570 571 if ((vfstype = fstypenames[t]) == NULL) 572 errx(1, "vfstype `%s' on partition `%s' is not supported", 573 fstypenames[t], str); 574 575 return vfstype; 576 } 577 578 579 static void 580 usage(void) 581 { 582 static const char common[] = 583 "[-Cdfnpvy] [-B | -F] [-T fstype:fsoptions] [-t fstype] [-c fstab]"; 584 585 (void)fprintf(stderr, "usage: %s %s [special | node] ...\n", 586 getprogname(), common); 587 exit(1); 588 } 589