e2fsck.c revision 50e1e10fa0ac12a3e2a9d20a75ee9041873cda96
1/* 2 * e2fsck.c - a consistency checker for the new extended file system. 3 * 4 * Copyright (C) 1993, 1994 Theodore Ts'o. This file may be 5 * redistributed under the terms of the GNU Public License. 6 */ 7 8/* Usage: e2fsck [-dfpnsvy] device 9 * -d -- debugging this program 10 * -f -- check the fs even if it is marked valid 11 * -p -- "preen" the filesystem 12 * -n -- open the filesystem r/o mode; never try to fix problems 13 * -v -- verbose (tells how many files) 14 * -y -- always answer yes to questions 15 * 16 * The device may be a block device or a image of one, but this isn't 17 * enforced (but it's not much fun on a character device :-). 18 */ 19 20#include <string.h> 21#include <fcntl.h> 22#include <ctype.h> 23#include <termios.h> 24#include <time.h> 25#ifdef HAVE_GETOPT_H 26#include <getopt.h> 27#endif 28#include <unistd.h> 29#ifdef HAVE_ERRNO_H 30#include <errno.h> 31#endif 32#ifdef HAVE_MNTENT_H 33#include <mntent.h> 34#endif 35#include <sys/ioctl.h> 36#include <malloc.h> 37 38#include "et/com_err.h" 39#include "e2fsck.h" 40#include "../version.h" 41 42extern int isatty(int); 43 44const char * program_name = "e2fsck"; 45const char * device_name = NULL; 46const char * filesystem_name = NULL; 47 48/* Command line options */ 49int nflag = 0; 50int yflag = 0; 51int tflag = 0; /* Do timing */ 52int cflag = 0; /* check disk */ 53int preen = 0; 54int rwflag = 1; 55int inode_buffer_blocks = 0; 56blk_t superblock; 57int blocksize = 0; 58int verbose = 0; 59int list = 0; 60int debug = 0; 61int force = 0; 62int invalid_bitmaps = 0; 63static int show_version_only = 0; 64 65static int replace_bad_blocks = 0; 66static char *bad_blocks_file = 0; 67 68static int possible_block_sizes[] = { 1024, 2048, 4096, 8192, 0}; 69 70struct resource_track global_rtrack; 71 72static int root_filesystem = 0; 73static int read_only_root = 0; 74 75int *invalid_inode_bitmap; 76int *invalid_block_bitmap; 77int *invalid_inode_table; 78int restart_e2fsck = 0; 79 80static void usage(NOARGS) 81{ 82 fprintf(stderr, 83 "Usage: %s [-panyrcdfvtFV] [-b superblock] [-B blocksize]\n" 84 "\t\t[-I inode_buffer_blocks] [-P process_inode_size]\n" 85 "\t\t[-l|-L bad_blocks_file] device\n", program_name); 86 exit(FSCK_USAGE); 87} 88 89static void show_stats(ext2_filsys fs) 90{ 91 int inodes, inodes_used, blocks, blocks_used; 92 int dir_links; 93 int num_files, num_links; 94 95 dir_links = 2 * fs_directory_count - 1; 96 num_files = fs_total_count - dir_links; 97 num_links = fs_links_count - dir_links; 98 inodes = fs->super->s_inodes_count; 99 inodes_used = (fs->super->s_inodes_count - 100 fs->super->s_free_inodes_count); 101 blocks = fs->super->s_blocks_count; 102 blocks_used = (fs->super->s_blocks_count - 103 fs->super->s_free_blocks_count); 104 105 if (!verbose) { 106 printf("%s: %d/%d files, %d/%d blocks\n", device_name, 107 inodes_used, inodes, blocks_used, blocks); 108 return; 109 } 110 printf ("\n%8d inode%s used (%d%%)\n", inodes_used, 111 (inodes_used != 1) ? "s" : "", 112 100 * inodes_used / inodes); 113 printf (" # of inodes with ind/dind/tind blocks: %d/%d/%d\n", 114 fs_ind_count, fs_dind_count, fs_tind_count); 115 printf ("%8d block%s used (%d%%)\n" 116 "%8d bad block%s\n", blocks_used, 117 (blocks_used != 1) ? "s" : "", 118 100 * blocks_used / blocks, fs_badblocks_count, 119 fs_badblocks_count != 1 ? "s" : ""); 120 printf ("\n%8d regular file%s\n" 121 "%8d director%s\n" 122 "%8d character device file%s\n" 123 "%8d block device file%s\n" 124 "%8d fifo%s\n" 125 "%8d link%s\n" 126 "%8d symbolic link%s (%d fast symbolic link%s)\n" 127 "%8d socket%s\n" 128 "--------\n" 129 "%8d file%s\n", 130 fs_regular_count, (fs_regular_count != 1) ? "s" : "", 131 fs_directory_count, (fs_directory_count != 1) ? "ies" : "y", 132 fs_chardev_count, (fs_chardev_count != 1) ? "s" : "", 133 fs_blockdev_count, (fs_blockdev_count != 1) ? "s" : "", 134 fs_fifo_count, (fs_fifo_count != 1) ? "s" : "", 135 fs_links_count - dir_links, 136 ((fs_links_count - dir_links) != 1) ? "s" : "", 137 fs_symlinks_count, (fs_symlinks_count != 1) ? "s" : "", 138 fs_fast_symlinks_count, (fs_fast_symlinks_count != 1) ? "s" : "", 139 fs_sockets_count, (fs_sockets_count != 1) ? "s" : "", 140 fs_total_count - dir_links, 141 ((fs_total_count - dir_links) != 1) ? "s" : ""); 142} 143 144static void check_mount(NOARGS) 145{ 146 errcode_t retval; 147 int mount_flags, cont; 148 149 retval = ext2fs_check_if_mounted(filesystem_name, &mount_flags); 150 if (retval) { 151 com_err("ext2fs_check_if_mount", retval, 152 "while determining whether %s is mounted.", 153 filesystem_name); 154 return; 155 } 156 if (!(mount_flags & EXT2_MF_MOUNTED)) 157 return; 158 /* 159 * If the root is mounted read-only, then /etc/mtab is 160 * probably not correct; so we won't issue a warning based on 161 * it. 162 */ 163 if ((mount_flags & EXT2_MF_ISROOT) && 164 (mount_flags & EXT2_MF_READONLY)) 165 return; 166 167 if (!rwflag) { 168 printf("Warning! %s is mounted.\n", device_name); 169 return; 170 } 171 172 printf ("%s is mounted. ", device_name); 173 if (isatty (0) && isatty (1)) 174 cont = ask_yn("Do you really want to continue", -1); 175 else 176 cont = 0; 177 if (!cont) { 178 printf ("check aborted.\n"); 179 exit (0); 180 } 181 return; 182} 183 184static void sync_disks(NOARGS) 185{ 186 sync(); 187 sync(); 188 sleep(1); 189 sync(); 190} 191 192#define MIN_CHECK 1 193#define MAX_CHECK 2 194 195static const char *corrupt_msg = "\nThe filesystem superblock is corrupt. " 196 "Try running e2fsck with an alternate\n" 197 "superblock using the -b option. " 198 "(8193 is commonly an alternate superblock;\n" 199 "Hence, 'e2fsck -b 8193 <device>' may recover the filesystem.)\n\n"; 200 201static void check_super_value(const char *descr, unsigned long value, 202 int flags, unsigned long min, unsigned long max) 203{ 204 if (((flags & MIN_CHECK) && (value < min)) || 205 ((flags & MAX_CHECK) && (value > max))) { 206 printf("Corruption found in superblock. (%s = %lu).\n", 207 descr, value); 208 printf(corrupt_msg); 209 fatal_error(0); 210 } 211} 212 213static void check_super_block(ext2_filsys fs) 214{ 215 blk_t first_block, last_block; 216 struct ext2_super_block *s = fs->super; 217 blk_t blocks_per_group = fs->super->s_blocks_per_group; 218 int i; 219 blk_t should_be; 220 errcode_t retval; 221 222 /* 223 * Verify the super block constants... 224 */ 225 check_super_value("inodes_count", s->s_inodes_count, 226 MIN_CHECK, 1, 0); 227 check_super_value("blocks_count", s->s_blocks_count, 228 MIN_CHECK, 1, 0); 229 check_super_value("first_data_block", s->s_first_data_block, 230 MAX_CHECK, 0, s->s_blocks_count); 231 check_super_value("log_frag_size", s->s_log_frag_size, 232 MAX_CHECK, 0, 2); 233 check_super_value("log_block_size", s->s_log_block_size, 234 MIN_CHECK | MAX_CHECK, s->s_log_frag_size, 235 2); 236 check_super_value("frags_per_group", s->s_frags_per_group, 237 MIN_CHECK | MAX_CHECK, 1, 8 * EXT2_BLOCK_SIZE(s)); 238 check_super_value("blocks_per_group", s->s_blocks_per_group, 239 MIN_CHECK | MAX_CHECK, 1, 8 * EXT2_BLOCK_SIZE(s)); 240 check_super_value("inodes_per_group", s->s_inodes_per_group, 241 MIN_CHECK, 1, 0); 242 check_super_value("r_blocks_count", s->s_r_blocks_count, 243 MAX_CHECK, 0, s->s_blocks_count); 244 245 retval = ext2fs_get_device_size(filesystem_name, EXT2_BLOCK_SIZE(s), 246 &should_be); 247 if (retval) { 248 com_err("ext2fs_get_device_size", retval, 249 "while trying to check physical size of filesystem"); 250 fatal_error(0); 251 } 252 if (should_be < s->s_blocks_count) { 253 printf("The filesystem size (according to the superblock) is %d blocks\n", s->s_blocks_count); 254 printf("The physical size of the device is %d blocks\n", 255 should_be); 256 printf("Either the superblock or the partition table is likely to be corrupt!\n"); 257 preenhalt(fs); 258 if (ask("Abort", 1)) 259 fatal_error(0); 260 } 261 262 if (s->s_log_block_size != s->s_log_frag_size) { 263 printf("Superblock block_size = %d, fragsize = %d.\n", 264 EXT2_BLOCK_SIZE(s), EXT2_FRAG_SIZE(s)); 265 printf("This version of e2fsck does not support fragment " 266 "sizes different\n" 267 "from the block size.\n"); 268 fatal_error(0); 269 } 270 271 should_be = s->s_frags_per_group / 272 (s->s_log_block_size - s->s_log_frag_size + 1); 273 if (s->s_blocks_per_group != should_be) { 274 printf("Superblock blocks_per_group = %u, should " 275 "have been %u\n", s->s_blocks_per_group, 276 should_be); 277 printf(corrupt_msg); 278 } 279 280 should_be = (s->s_log_block_size == 0) ? 1 : 0; 281 if (s->s_first_data_block != should_be) { 282 printf("Superblock first_data_block = %u, should " 283 "have been %u\n", s->s_first_data_block, 284 should_be); 285 printf(corrupt_msg); 286 } 287 288 /* 289 * Verify the group descriptors.... 290 */ 291 first_block = fs->super->s_first_data_block; 292 last_block = first_block + blocks_per_group; 293 294 for (i = 0; i < fs->group_desc_count; i++) { 295 if (i == fs->group_desc_count - 1) 296 last_block = fs->super->s_blocks_count; 297 if ((fs->group_desc[i].bg_block_bitmap < first_block) || 298 (fs->group_desc[i].bg_block_bitmap >= last_block)) { 299 printf("Block bitmap for group %d is not in group. " 300 "(block %u)\n", 301 i, fs->group_desc[i].bg_block_bitmap); 302 preenhalt(fs); 303 if (!ask("Relocate", 1)) { 304 fatal_error("Block bitmap not in group"); 305 } 306 fs->group_desc[i].bg_block_bitmap = 0; 307 invalid_block_bitmap[i]++; 308 invalid_bitmaps++; 309 } 310 if ((fs->group_desc[i].bg_inode_bitmap < first_block) || 311 (fs->group_desc[i].bg_inode_bitmap >= last_block)) { 312 printf("Inode bitmap group %d not in group. " 313 "(block %u)\n", 314 i, fs->group_desc[i].bg_inode_bitmap); 315 preenhalt(fs); 316 if (!ask("Relocate", 1)) { 317 fatal_error("Inode bitmap not in group"); 318 } 319 fs->group_desc[i].bg_inode_bitmap = 0; 320 invalid_inode_bitmap[i]++; 321 invalid_bitmaps++; 322 } 323 if ((fs->group_desc[i].bg_inode_table < first_block) || 324 ((fs->group_desc[i].bg_inode_table + 325 fs->inode_blocks_per_group - 1) >= last_block)) { 326 printf("Inode table for group %d not in group. " 327 "(block %u)\n", 328 i, fs->group_desc[i].bg_inode_table); 329 printf("WARNING: SEVERE DATA LOSS POSSIBLE.\n"); 330 preenhalt(fs); 331 if (!ask("Relocate", 1)) { 332 fatal_error("Inode table not in group"); 333 } 334 fs->group_desc[i].bg_inode_table = 0; 335 invalid_inode_table[i]++; 336 invalid_bitmaps++; 337 } 338 first_block += fs->super->s_blocks_per_group; 339 last_block += fs->super->s_blocks_per_group; 340 } 341 return; 342} 343 344/* 345 * This routine checks to see if a filesystem can be skipped; if so, 346 * it will exit with E2FSCK_OK. Under some conditions it will print a 347 * message explaining why a check is being forced. 348 */ 349static void check_if_skip(ext2_filsys fs) 350{ 351 const char *reason = NULL; 352 353 if (force || bad_blocks_file || cflag) 354 return; 355 356 if (fs->super->s_state & EXT2_ERROR_FS) 357 reason = "contains a file system with errors"; 358 else if (fs->super->s_mnt_count >= 359 (unsigned) fs->super->s_max_mnt_count) 360 reason = "has reached maximal mount count"; 361 else if (fs->super->s_checkinterval && 362 time(0) >= (fs->super->s_lastcheck + 363 fs->super->s_checkinterval)) 364 reason = "has gone too long without being checked"; 365 if (reason) { 366 printf("%s %s, check forced.\n", device_name, reason); 367 return; 368 } 369 if (fs->super->s_state & EXT2_VALID_FS) { 370 printf("%s: clean, %d/%d files, %d/%d blocks\n", device_name, 371 fs->super->s_inodes_count - fs->super->s_free_inodes_count, 372 fs->super->s_inodes_count, 373 fs->super->s_blocks_count - fs->super->s_free_blocks_count, 374 fs->super->s_blocks_count); 375 exit(FSCK_OK); 376 } 377} 378 379#define PATH_SET "PATH=/sbin" 380 381static void PRS(int argc, char *argv[]) 382{ 383 int flush = 0; 384 char c; 385#ifdef MTRACE 386 extern void *mallwatch; 387#endif 388 char *oldpath = getenv("PATH"); 389 390 /* Update our PATH to include /sbin */ 391 if (oldpath) { 392 char *newpath; 393 394 newpath = malloc(sizeof (PATH_SET) + 1 + strlen (oldpath)); 395 if (!newpath) 396 fatal_error("Couldn't malloc() newpath"); 397 strcpy (newpath, PATH_SET); 398 strcat (newpath, ":"); 399 strcat (newpath, oldpath); 400 putenv (newpath); 401 } else 402 putenv (PATH_SET); 403 404 setbuf(stdout, NULL); 405 setbuf(stderr, NULL); 406 initialize_ext2_error_table(); 407 408 if (argc && *argv) 409 program_name = *argv; 410 while ((c = getopt (argc, argv, "panyrcB:dfvtFVM:b:I:P:l:L:N:")) != EOF) 411 switch (c) { 412 case 'p': 413 case 'a': 414 preen = 1; 415 yflag = nflag = 0; 416 break; 417 case 'n': 418 nflag = 1; 419 preen = yflag = 0; 420 break; 421 case 'y': 422 yflag = 1; 423 preen = nflag = 0; 424 break; 425 case 't': 426 tflag++; 427 break; 428 case 'c': 429 cflag++; 430 break; 431 case 'r': 432 /* What we do by default, anyway! */ 433 break; 434 case 'b': 435 superblock = atoi(optarg); 436 break; 437 case 'B': 438 blocksize = atoi(optarg); 439 break; 440 case 'I': 441 inode_buffer_blocks = atoi(optarg); 442 break; 443 case 'P': 444 process_inode_size = atoi(optarg); 445 break; 446 case 'L': 447 replace_bad_blocks++; 448 case 'l': 449 bad_blocks_file = malloc(strlen(optarg)+1); 450 if (!bad_blocks_file) 451 fatal_error("Couldn't malloc bad_blocks_file"); 452 strcpy(bad_blocks_file, optarg); 453 break; 454 case 'd': 455 debug = 1; 456 break; 457 case 'f': 458 force = 1; 459 break; 460 case 'F': 461#ifdef BLKFLSBUF 462 flush = 1; 463#else 464 fatal_error ("-F not supported"); 465#endif 466 break; 467 case 'v': 468 verbose = 1; 469 break; 470 case 'V': 471 show_version_only = 1; 472 break; 473#ifdef MTRACE 474 case 'M': 475 mallwatch = (void *) strtol(optarg, NULL, 0); 476 break; 477#endif 478 case 'N': 479 device_name = optarg; 480 break; 481 default: 482 usage (); 483 } 484 if (show_version_only) 485 return; 486 if (optind != argc - 1) 487 usage (); 488 if (nflag && !bad_blocks_file && !cflag) 489 rwflag = 0; 490 filesystem_name = argv[optind]; 491 if (device_name == 0) 492 device_name = filesystem_name; 493 if (flush) { 494#ifdef BLKFLSBUF 495 int fd = open(filesystem_name, O_RDONLY, 0); 496 497 if (fd < 0) { 498 com_err("open", errno, "while opening %s for flushing", 499 filesystem_name); 500 exit(FSCK_ERROR); 501 } 502 if (ioctl(fd, BLKFLSBUF, 0) < 0) { 503 com_err("BLKFLSBUF", errno, "while trying to flush %s", 504 filesystem_name); 505 exit(FSCK_ERROR); 506 } 507 close(fd); 508#else 509 fatal_error ("BLKFLSBUF not supported"); 510#endif /* BLKFLSBUF */ 511 } 512} 513 514int main (int argc, char *argv[]) 515{ 516 errcode_t retval = 0; 517 int exit_value = FSCK_OK; 518 int i; 519 ext2_filsys fs; 520 521#ifdef MTRACE 522 mtrace(); 523#endif 524#ifdef MCHECK 525 mcheck(0); 526#endif 527 528 init_resource_track(&global_rtrack); 529 530 PRS(argc, argv); 531 532 if (!preen) 533 fprintf (stderr, "e2fsck %s, %s for EXT2 FS %s, %s\n", 534 E2FSPROGS_VERSION, E2FSPROGS_DATE, 535 EXT2FS_VERSION, EXT2FS_DATE); 536 537 if (show_version_only) 538 exit(0); 539 540 check_mount(); 541 542 if (!preen && !nflag && !yflag) { 543 if (!isatty (0) || !isatty (1)) 544 die ("need terminal for interactive repairs"); 545 } 546restart: 547 sync_disks(); 548 if (superblock && blocksize) { 549 retval = ext2fs_open(filesystem_name, 550 rwflag ? EXT2_FLAG_RW : 0, 551 superblock, blocksize, unix_io_manager, 552 &fs); 553 } else if (superblock) { 554 for (i=0; possible_block_sizes[i]; i++) { 555 retval = ext2fs_open(filesystem_name, 556 rwflag ? EXT2_FLAG_RW : 0, 557 superblock, 558 possible_block_sizes[i], 559 unix_io_manager, &fs); 560 if (!retval) 561 break; 562 } 563 } else 564 retval = ext2fs_open(filesystem_name, 565 rwflag ? EXT2_FLAG_RW : 0, 566 0, 0, unix_io_manager, &fs); 567 if (retval) { 568 com_err(program_name, retval, "while trying to open %s", 569 filesystem_name); 570 switch (retval) { 571 case EXT2_ET_REV_TOO_HIGH: 572 printf ("Get a newer version of e2fsck!\n"); 573 break; 574 case EXT2_ET_SHORT_READ: 575 printf ("Could this be a zero-length partition?\n"); 576 break; 577 case EPERM: 578 case EACCES: 579 printf("You must have %s access to the " 580 "filesystem or be root\n", 581 rwflag ? "r/w" : "r/o"); 582 break; 583 case ENXIO: 584 printf("Possibly non-existent or swap device?\n"); 585 break; 586 default: 587 printf(corrupt_msg); 588 } 589 fatal_error(0); 590 } 591 592#ifdef EXT2_CURRENT_REV 593 if (fs->super->s_rev_level > E2FSCK_CURRENT_REV) { 594 com_err(program_name, retval, "while trying to open %s", 595 filesystem_name); 596 printf ("Get a newer version of e2fsck!\n"); 597 fatal_error(0); 598 } 599#endif 600 /* 601 * If the user specified a specific superblock, presumably the 602 * master superblock has been trashed. So we mark the 603 * superblock as dirty, so it can be written out. 604 */ 605 if (superblock && rwflag) 606 ext2fs_mark_super_dirty(fs); 607 608 ehandler_init(fs->io); 609 610 invalid_inode_bitmap = allocate_memory(sizeof(int) * 611 fs->group_desc_count, 612 "invalid_inode_bitmap"); 613 invalid_block_bitmap = allocate_memory(sizeof(int) * 614 fs->group_desc_count, 615 "invalid_block_bitmap"); 616 invalid_inode_table = allocate_memory(sizeof(int) * 617 fs->group_desc_count, 618 "invalid_inode_table"); 619 620 check_super_block(fs); 621 check_if_skip(fs); 622 if (bad_blocks_file) 623 read_bad_blocks_file(fs, bad_blocks_file, replace_bad_blocks); 624 else if (cflag) 625 test_disk(fs); 626 627 /* 628 * Mark the system as valid, 'til proven otherwise 629 */ 630 ext2fs_mark_valid(fs); 631 632 pass1(fs); 633 free(invalid_inode_bitmap); 634 free(invalid_block_bitmap); 635 free(invalid_inode_table); 636 if (restart_e2fsck) { 637 ext2fs_close(fs); 638 printf("Restarting e2fsck from the beginning...\n"); 639 restart_e2fsck = 0; 640 goto restart; 641 } 642 pass2(fs); 643 pass3(fs); 644 pass4(fs); 645 pass5(fs); 646 647#ifdef MTRACE 648 mtrace_print("Cleanup"); 649#endif 650 if (ext2fs_test_changed(fs)) { 651 exit_value = FSCK_NONDESTRUCT; 652 if (!preen) 653 printf("\n%s: ***** FILE SYSTEM WAS MODIFIED *****\n", 654 device_name); 655 if (root_filesystem && !read_only_root) { 656 printf("%s: ***** REBOOT LINUX *****\n", device_name); 657 exit_value = FSCK_REBOOT; 658 } 659 } 660 if (!ext2fs_test_valid(fs)) 661 exit_value = FSCK_UNCORRECTED; 662 if (rwflag) { 663 if (ext2fs_test_valid(fs)) 664 fs->super->s_state = EXT2_VALID_FS; 665 else 666 fs->super->s_state &= ~EXT2_VALID_FS; 667 fs->super->s_mnt_count = 0; 668 fs->super->s_lastcheck = time(NULL); 669 ext2fs_mark_super_dirty(fs); 670 } 671 show_stats(fs); 672 673 write_bitmaps(fs); 674 ext2fs_close(fs); 675 sync_disks(); 676 677 if (tflag) 678 print_resource_track(&global_rtrack); 679 680 return exit_value; 681} 682