fsck.c revision 519149fb458b0fa69c10fecd83fae42e838cf01d
1/* 2 * pfsck --- A generic, parallelizing front-end for the fsck program. 3 * It will automatically try to run fsck programs in parallel if the 4 * devices are on separate spindles. It is based on the same ideas as 5 * the generic front end for fsck by David Engel and Fred van Kempen, 6 * but it has been completely rewritten from scratch to support 7 * parallel execution. 8 * 9 * Written by Theodore Ts'o, <tytso@mit.edu> 10 * 11 * Usage: fsck [-AVRNTM] [-s] [-t fstype] [fs-options] device 12 * 13 * Miquel van Smoorenburg (miquels@drinkel.ow.org) 20-Oct-1994: 14 * o Changed -t fstype to behave like with mount when -A (all file 15 * systems) or -M (like mount) is specified. 16 * o fsck looks if it can find the fsck.type program to decide 17 * if it should ignore the fs type. This way more fsck programs 18 * can be added without changing this front-end. 19 * o -R flag skip root file system. 20 * 21 * Copyright (C) 1993, 1994, 1995, 1996, 1997 Theodore Ts'o. 22 * 23 * %Begin-Header% 24 * This file may be redistributed under the terms of the GNU Public 25 * License. 26 * %End-Header% 27 */ 28 29#include <sys/types.h> 30#include <sys/wait.h> 31#include <sys/signal.h> 32#include <sys/stat.h> 33#include <limits.h> 34#include <stdio.h> 35#include <string.h> 36#if HAVE_STDLIB_H 37#include <stdlib.h> 38#endif 39#if HAVE_ERRNO_H 40#include <errno.h> 41#endif 42#if HAVE_MNTENT_H 43#include <mntent.h> 44#endif 45#if HAVE_UNISTD_H 46#include <unistd.h> 47#endif 48#if HAVE_ERRNO_H 49#include <errno.h> 50#endif 51#include <malloc.h> 52 53#include "../version.h" 54#include "fsck.h" 55 56static const char *ignored_types[] = { 57 "ignore", 58 "iso9660", 59 "nfs", 60 "proc", 61 "sw", 62 "swap", 63 NULL 64}; 65 66static const char *really_wanted[] = { 67 "minix", 68 "ext2", 69 "xiafs", 70 NULL 71}; 72 73#ifdef DEV_DSK_DEVICES 74static const char *base_devices[] = { 75 "/dev/dsk/hda", 76 "/dev/dsk/hdb", 77 "/dev/dsk/hdc", 78 "/dev/dsk/hdd", 79 "/dev/dsk/hd1a", 80 "/dev/dsk/hd1b", 81 "/dev/dsk/hd1c", 82 "/dev/dsk/hd1d", 83 "/dev/dsk/sda", 84 "/dev/dsk/sdb", 85 "/dev/dsk/sdc", 86 "/dev/dsk/sdd", 87 "/dev/dsk/sde", 88 "/dev/dsk/sdf", 89 "/dev/dsk/sdg", 90 NULL 91}; 92#else 93static const char *base_devices[] = { 94 "/dev/hda", 95 "/dev/hdb", 96 "/dev/hdc", 97 "/dev/hdd", 98 "/dev/hd1a", 99 "/dev/hd1b", 100 "/dev/hd1c", 101 "/dev/hd1d", 102 "/dev/sda", 103 "/dev/sdb", 104 "/dev/sdc", 105 "/dev/sdd", 106 "/dev/sde", 107 "/dev/sdf", 108 "/dev/sdg", 109 NULL 110}; 111#endif 112 113/* 114 * Global variables for options 115 */ 116char *devices[MAX_DEVICES]; 117char *args[MAX_ARGS]; 118int num_devices, num_args; 119 120int verbose = 0; 121int doall = 0; 122int noexecute = 0; 123int serialize = 0; 124int skip_root = 0; 125int like_mount = 0; 126int notitle = 0; 127int parallel_root = 0; 128char *progname; 129char *fstype = NULL; 130struct fs_info *filesys_info; 131struct fsck_instance *instance_list; 132const char *fsck_prefix_path = "/sbin:/sbin/fs.d:/sbin/fs:/etc/fs:/etc"; 133char *fsck_path = 0; 134static int ignore(struct fs_info *); 135 136#ifdef HAVE_STRDUP 137#ifdef _POSIX_SOURCE 138extern char *strdup(const char *s); 139#endif 140#else 141static char *strdup(const char *s) 142{ 143 char *ret; 144 145 ret = malloc(strlen(s)+1); 146 if (ret) 147 strcpy(ret, s); 148 return ret; 149} 150#endif 151 152static void free_instance(struct fsck_instance *i) 153{ 154 if (i->prog) 155 free(i->prog); 156 if (i->device) 157 free(i->device); 158 free(i); 159 return; 160} 161 162/* 163 * Load the filesystem database from /etc/fstab 164 */ 165static void load_fs_info(NOARGS) 166{ 167#if HAVE_MNTENT_H 168 FILE *mntfile; 169 struct mntent *mp; 170 struct fs_info *fs; 171 struct fs_info *fs_last = NULL; 172 int old_fstab = 1; 173 174 filesys_info = NULL; 175 176 /* Open the mount table. */ 177 if ((mntfile = setmntent(MNTTAB, "r")) == NULL) { 178 perror(MNTTAB); 179 exit(EXIT_ERROR); 180 } 181 182 while ((mp = getmntent(mntfile)) != NULL) { 183 fs = malloc(sizeof(struct fs_info)); 184 memset(fs, 0, sizeof(struct fs_info)); 185 fs->device = strdup(mp->mnt_fsname); 186 fs->mountpt = strdup(mp->mnt_dir); 187 fs->type = strdup(mp->mnt_type); 188 fs->opts = strdup(mp->mnt_opts); 189 fs->freq = mp->mnt_freq; 190 fs->passno = mp->mnt_passno; 191 fs->next = NULL; 192 if (!filesys_info) 193 filesys_info = fs; 194 else 195 fs_last->next = fs; 196 fs_last = fs; 197 if (fs->passno) 198 old_fstab = 0; 199 } 200 201 (void) endmntent(mntfile); 202 203 if (old_fstab) { 204 fprintf(stderr, "\007\007\007" 205 "WARNING: Your /etc/fstab does not contain the fsck passno\n"); 206 fprintf(stderr, 207 " field. I will kludge around things for you, but you\n"); 208 fprintf(stderr, 209 " should fix your /etc/fstab file as soon as you can.\n\n"); 210 211 for (fs = filesys_info; fs; fs = fs->next) { 212 fs->passno = 1; 213 } 214 } 215#else 216 filesys_info = NULL; 217#endif /* HAVE_MNTENT_H */ 218} 219 220/* Lookup filesys in /etc/fstab and return the corresponding entry. */ 221static struct fs_info *lookup(char *filesys) 222{ 223 struct fs_info *fs; 224 225 /* No filesys name given. */ 226 if (filesys == NULL) 227 return NULL; 228 229 for (fs = filesys_info; fs; fs = fs->next) { 230 if (!strcmp(filesys, fs->device) || 231 !strcmp(filesys, fs->mountpt)) 232 break; 233 } 234 235 return fs; 236} 237 238/* Find fsck program for a given fs type. */ 239static char *find_fsck(char *type) 240{ 241 char *s; 242 const char *tpl; 243 static char prog[256]; 244 char *p = strdup(fsck_path); 245 struct stat st; 246 247 /* Are we looking for a program or just a type? */ 248 tpl = (strncmp(type, "fsck.", 5) ? "%s/fsck.%s" : "%s/%s"); 249 250 for(s = strtok(p, ":"); s; s = strtok(NULL, ":")) { 251 sprintf(prog, tpl, s, type); 252 if (stat(prog, &st) == 0) break; 253 } 254 free(p); 255 return(s ? prog : NULL); 256} 257 258/* 259 * Execute a particular fsck program, and link it into the list of 260 * child processes we are waiting for. 261 */ 262static int execute(char *prog, char *device) 263{ 264 char *s, *argv[80]; 265 int argc, i; 266 struct fsck_instance *inst; 267 pid_t pid; 268 269 argv[0] = strdup(prog); 270 argc = 1; 271 272 for (i=0; i <num_args; i++) 273 argv[argc++] = strdup(args[i]); 274 275 argv[argc++] = strdup(device); 276 argv[argc] = 0; 277 278 s = find_fsck(prog); 279 if (s == NULL) { 280 fprintf(stderr, "fsck: %s: not found\n", prog); 281 return ENOENT; 282 } 283 284 if (verbose || noexecute) { 285 printf("[%s] ", s); 286 for (i=0; i < argc; i++) 287 printf("%s ", argv[i]); 288 printf("\n"); 289 } 290 if (noexecute) 291 return 0; 292 293 /* Fork and execute the correct program. */ 294 if ((pid = fork()) < 0) { 295 perror("fork"); 296 return errno; 297 } else if (pid == 0) { 298 (void) execv(s, argv); 299 perror(argv[0]); 300 exit(EXIT_ERROR); 301 } 302 inst = malloc(sizeof(struct fsck_instance)); 303 if (!inst) 304 return ENOMEM; 305 memset(inst, 0, sizeof(struct fsck_instance)); 306 inst->pid = pid; 307 inst->prog = strdup(prog); 308 inst->device = strdup(device); 309 inst->next = instance_list; 310 instance_list = inst; 311 312 return 0; 313} 314 315/* 316 * Wait for one child process to exit; when it does, unlink it from 317 * the list of executing child processes, and return it. 318 */ 319static struct fsck_instance *wait_one(NOARGS) 320{ 321 int status; 322 int sig; 323 struct fsck_instance *inst, *prev; 324 pid_t pid; 325 326 if (!instance_list) 327 return NULL; 328 329retry: 330 pid = wait(&status); 331 if (pid < 0) { 332 if ((errno == EINTR) || (errno == EAGAIN)) 333 goto retry; 334 if (errno == ECHILD) { 335 fprintf(stderr, 336 "%s: wait: No more child process?!?\n", 337 progname); 338 return NULL; 339 } 340 perror("wait"); 341 goto retry; 342 } 343 for (prev = 0, inst = instance_list; 344 inst; 345 prev = inst, inst = inst->next) { 346 if (inst->pid == pid) 347 break; 348 } 349 if (!inst) { 350 printf("Unexpected child process %d, status = 0x%x\n", 351 pid, status); 352 goto retry; 353 } 354 if (WIFEXITED(status)) 355 status = WEXITSTATUS(status); 356 else if (WIFSIGNALED(status)) { 357 sig = WTERMSIG(status); 358 if (sig == SIGINT) { 359 status = EXIT_UNCORRECTED; 360 } else { 361 printf("Warning... %s for device %s exited " 362 "with signal %d.\n", 363 inst->prog, inst->device, sig); 364 status = EXIT_ERROR; 365 } 366 } else { 367 printf("%s %s: status is %x, should never happen.\n", 368 inst->prog, inst->device, status); 369 status = EXIT_ERROR; 370 } 371 inst->exit_status = status; 372 if (prev) 373 prev->next = inst->next; 374 else 375 instance_list = inst->next; 376 return inst; 377} 378 379/* 380 * Wait until all executing child processes have exited; return the 381 * logical OR of all of their exit code values. 382 */ 383static int wait_all(NOARGS) 384{ 385 struct fsck_instance *inst; 386 int global_status = 0; 387 388 while (instance_list) { 389 inst = wait_one(); 390 if (!inst) 391 break; 392 global_status |= inst->exit_status; 393 free_instance(inst); 394 } 395 return global_status; 396} 397 398/* 399 * Run the fsck program on a particular device 400 * 401 * If the type is specified using -t, and it isn't prefixed with "no" 402 * (as in "noext2") and only one filesystem type is specified, then 403 * use that type regardless of what is specified in /etc/fstab. 404 * 405 * If the type isn't specified by the user, then use either the type 406 * specified in /etc/fstab, or DEFAULT_FSTYPE. 407 */ 408static void fsck_device(char *device) 409{ 410 const char *type = 0; 411 struct fs_info *fsent; 412 int retval; 413 char prog[80]; 414 415 if (fstype && strncmp(fstype, "no", 2) && !strchr(fstype, ',')) 416 type = fstype; 417 418 if ((fsent = lookup(device))) { 419 device = fsent->device; 420 if (!type) 421 type = fsent->type; 422 } 423 if (!type) 424 type = DEFAULT_FSTYPE; 425 426 sprintf(prog, "fsck.%s", type); 427 retval = execute(prog, device); 428 if (retval) { 429 fprintf(stderr, "%s: Error %d while executing %s for %s\n", 430 progname, retval, prog, device); 431 } 432} 433 434/* See if filesystem type matches the list. */ 435static int fs_match(char *type, char *fs_type) 436{ 437 int ret = 0, negate = 0; 438 char list[128]; 439 char *s; 440 441 if (!fs_type) return(1); 442 443 if (strncmp(fs_type, "no", 2) == 0) { 444 fs_type += 2; 445 negate = 1; 446 } 447 strcpy(list, fs_type); 448 s = strtok(list, ","); 449 while(s) { 450 if (strcmp(s, type) == 0) { 451 ret = 1; 452 break; 453 } 454 s = strtok(NULL, ","); 455 } 456 return(negate ? !ret : ret); 457} 458 459 460/* Check if we should ignore this filesystem. */ 461static int ignore(struct fs_info *fs) 462{ 463 const char *cp; 464 const char **ip; 465 int wanted = 0; 466 467 /* 468 * If the pass number is 0, ignore it. 469 */ 470 if (fs->passno == 0) 471 return 1; 472 473 /* 474 * If a specific fstype is specified, and it doesn't match, 475 * ignore it. 476 */ 477 if (!fs_match(fs->type, fstype)) return 1; 478 479 /* Noauto never matches. */ 480 for (cp = strtok(fs->opts, ","); cp != NULL; cp = strtok(NULL, ",")) { 481 if (!strcmp(cp, "noauto")) 482 return 1; 483 } 484 485 /* Are we ignoring this type? */ 486 for(ip = ignored_types; *ip; ip++) 487 if (strcmp(fs->type, *ip) == 0) return(1); 488 489 /* Do we really really want to check this fs? */ 490 for(ip = really_wanted; *ip; ip++) 491 if (strcmp(fs->type, *ip) == 0) { 492 wanted = 1; 493 break; 494 } 495 496 /* See if the <fsck.fs> program is available. */ 497 if (find_fsck(fs->type) == NULL) { 498 if (wanted) 499 fprintf(stderr, "fsck: cannot check %s: fsck.%s not found\n", 500 fs->device, fs->type); 501 return(1); 502 } 503 504 /* We can and want to check this file system type. */ 505 return 0; 506} 507 508/* 509 * Return the "base device" given a particular device; this is used to 510 * assure that we only fsck one partition on a particular drive at any 511 * one time. Otherwise, the disk heads will be seeking all over the 512 * place. 513 */ 514static const char *base_device(char *device) 515{ 516 const char **base; 517 518 for (base = base_devices; *base; base++) { 519 if (!strncmp(*base, device, strlen(*base))) 520 return *base; 521 } 522 return device; 523} 524 525/* 526 * Returns TRUE if a partition on the same disk is already being 527 * checked. 528 */ 529static int device_already_active(char *device) 530{ 531 struct fsck_instance *inst; 532 const char *base; 533 534 base = base_device(device); 535 536 for (inst = instance_list; inst; inst = inst->next) { 537 if (!strcmp(base, base_device(inst->device))) 538 return 1; 539 } 540 541 return 0; 542} 543 544/* Check all file systems, using the /etc/fstab table. */ 545static int check_all(NOARGS) 546{ 547 struct fs_info *fs = NULL; 548 struct fsck_instance *inst; 549 int status = EXIT_OK; 550 int not_done_yet = 1; 551 int passno = 0; 552 int pass_done; 553 554 if (verbose) 555 printf("Checking all file systems.\n"); 556 557 /* 558 * Find and check the root filesystem first. 559 */ 560 if (!parallel_root) { 561 for (fs = filesys_info; fs; fs = fs->next) { 562 if (!strcmp(fs->mountpt, "/")) 563 break; 564 } 565 if (fs && !skip_root && !ignore(fs)) { 566 fsck_device(fs->device); 567 fs->flags |= FLAG_DONE; 568 status |= wait_all(); 569 if (status > EXIT_NONDESTRUCT) 570 return status; 571 } 572 } 573 if (fs) fs->flags |= FLAG_DONE; 574 575 /* 576 * Mark filesystems that should be ignored as done. 577 */ 578 for (fs = filesys_info; fs; fs = fs->next) { 579 if (ignore(fs)) 580 fs->flags |= FLAG_DONE; 581 } 582 583 while (not_done_yet) { 584 not_done_yet = 0; 585 pass_done = 1; 586 587 for (fs = filesys_info; fs; fs = fs->next) { 588 if (fs->flags & FLAG_DONE) 589 continue; 590 /* 591 * If the filesystem's pass number is higher 592 * than the current pass number, then we don't 593 * do it yet. 594 */ 595 if (fs->passno > passno) { 596 not_done_yet++; 597 continue; 598 } 599 /* 600 * If a filesystem on a particular device has 601 * already been spawned, then we need to defer 602 * this to another pass. 603 */ 604 if (device_already_active(fs->device)) { 605 pass_done = 0; 606 continue; 607 } 608 /* 609 * Spawn off the fsck process 610 */ 611 fsck_device(fs->device); 612 fs->flags |= FLAG_DONE; 613 614 if (serialize) { 615 pass_done = 0; 616 break; /* Only do one filesystem at a time */ 617 618 } 619 } 620 inst = wait_one(); 621 if (inst) { 622 status |= inst->exit_status; 623 free_instance(inst); 624 } 625 if (pass_done) { 626 status |= wait_all(); 627 if (verbose) 628 printf("----------------------------------\n"); 629 passno++; 630 } else 631 not_done_yet++; 632 } 633 status |= wait_all(); 634 return status; 635} 636 637static void usage(NOARGS) 638{ 639 fprintf(stderr, 640 "Usage: fsck [-AV] [-t fstype] [fs-options] filesys\n"); 641 exit(EXIT_USAGE); 642} 643 644static void PRS(int argc, char *argv[]) 645{ 646 int i, j; 647 char *arg; 648 char options[128]; 649 int opt = 0; 650 int opts_for_fsck = 0; 651 652 num_devices = 0; 653 num_args = 0; 654 instance_list = 0; 655 656 progname = argv[0]; 657 658 load_fs_info(); 659 660 for (i=1; i < argc; i++) { 661 arg = argv[i]; 662 if (!arg) 663 continue; 664 if (arg[0] == '/') { 665 if (num_devices >= MAX_DEVICES) { 666 fprintf(stderr, "%s: too many devices\n", 667 progname); 668 exit(1); 669 } 670 devices[num_devices++] = strdup(arg); 671 continue; 672 } 673 if (arg[0] != '-') { 674 if (num_args >= MAX_ARGS) { 675 fprintf(stderr, "%s: too many arguments\n", 676 progname); 677 exit(1); 678 } 679 args[num_args++] = strdup(arg); 680 continue; 681 } 682 for (j=1; arg[j]; j++) { 683 if (opts_for_fsck) { 684 options[++opt] = arg[j]; 685 continue; 686 } 687 switch (arg[j]) { 688 case 'A': 689 doall++; 690 break; 691 case 'V': 692 verbose++; 693 break; 694 case 'N': 695 noexecute++; 696 break; 697 case 'R': 698 skip_root++; 699 break; 700 case 'T': 701 notitle++; 702 break; 703 case 'M': 704 like_mount++; 705 break; 706 case 'P': 707 parallel_root++; 708 break; 709 case 's': 710 serialize++; 711 break; 712 case 't': 713 if (arg[j+1]) { 714 fstype = strdup(arg+j+1); 715 goto next_arg; 716 } 717 if ((i+1) < argc) { 718 i++; 719 fstype = strdup(argv[i]); 720 goto next_arg; 721 } 722 usage(); 723 break; 724 case '-': 725 opts_for_fsck++; 726 break; 727 default: 728 options[++opt] = arg[j]; 729 break; 730 } 731 } 732 next_arg: 733 if (opt) { 734 options[0] = '-'; 735 options[++opt] = '\0'; 736 if (num_args >= MAX_ARGS) { 737 fprintf(stderr, 738 "%s: too many arguments\n", 739 progname); 740 exit(1); 741 } 742 args[num_args++] = strdup(options); 743 opt = 0; 744 } 745 } 746} 747 748int main(int argc, char *argv[]) 749{ 750 int i; 751 int status = 0; 752 char *oldpath = getenv("PATH"); 753 754 PRS(argc, argv); 755 756 if (!notitle) 757 printf("Parallelizing fsck version %s (%s)\n", 758 E2FSPROGS_VERSION, E2FSPROGS_DATE); 759 760 /* Update our search path to include uncommon directories. */ 761 if (oldpath) { 762 fsck_path = malloc (strlen (fsck_prefix_path) + 1 + 763 strlen (oldpath) + 1); 764 strcpy (fsck_path, fsck_prefix_path); 765 strcat (fsck_path, ":"); 766 strcat (fsck_path, oldpath); 767 } else { 768 fsck_path = strdup(fsck_prefix_path); 769 } 770 771 /* If -A was specified ("check all"), do that! */ 772 if (doall) 773 return check_all(); 774 775 for (i = 0 ; i < num_devices; i++) { 776 fsck_device(devices[i]); 777 if (serialize) { 778 struct fsck_instance *inst; 779 780 inst = wait_one(); 781 if (inst) { 782 status |= inst->exit_status; 783 free_instance(inst); 784 } 785 } 786 } 787 status |= wait_all(); 788 free(fsck_path); 789 return status; 790} 791