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