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