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