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