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