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