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