e2fsck.c revision f3db3566b5e1342e49dffc5ec3f418a838584194
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#include <getopt.h> 26#include <unistd.h> 27#include <mntent.h> 28#include <sys/ioctl.h> 29#include <malloc.h> 30 31#include "et/com_err.h" 32#include "e2fsck.h" 33#include "../version.h" 34 35extern int isatty(int); 36 37const char * program_name = "e2fsck"; 38const char * device_name = NULL; 39 40/* Command line options */ 41int nflag = 0; 42int yflag = 0; 43int tflag = 0; /* Do timing */ 44int cflag = 0; /* check disk */ 45int preen = 0; 46int rwflag = 1; 47int inode_buffer_blocks = 0; 48blk_t superblock; 49int blocksize = 0; 50int verbose = 0; 51int list = 0; 52int debug = 0; 53int force = 0; 54int invalid_bitmaps = 0; 55static int show_version_only = 0; 56 57static int replace_bad_blocks = 0; 58static char *bad_blocks_file = 0; 59 60static int possible_block_sizes[] = { 1024, 2048, 4096, 8192, 0}; 61 62struct resource_track global_rtrack; 63 64static int root_filesystem = 0; 65static int read_only_root = 0; 66 67int *invalid_inode_bitmap; 68int *invalid_block_bitmap; 69int *invalid_inode_table; 70int restart_e2fsck = 0; 71 72static void usage(NOARGS) 73{ 74 fprintf(stderr, 75 "Usage: %s [-panyrcdfvtFV] [-b superblock] [-B blocksize]\n" 76 "\t\t[-I inode_buffer_blocks] [-P process_inode_size]\n" 77 "\t\t[-l|-L bad_blocks_file] device\n", program_name); 78 exit(FSCK_USAGE); 79} 80 81static void show_stats(ext2_filsys fs) 82{ 83 int inodes, inodes_used, blocks, blocks_used; 84 int dir_links; 85 int num_files, num_links; 86 87 dir_links = 2 * fs_directory_count - 1; 88 num_files = fs_total_count - dir_links; 89 num_links = fs_links_count - dir_links; 90 inodes = fs->super->s_inodes_count; 91 inodes_used = (fs->super->s_inodes_count - 92 fs->super->s_free_inodes_count); 93 blocks = fs->super->s_blocks_count; 94 blocks_used = (fs->super->s_blocks_count - 95 fs->super->s_free_blocks_count); 96 97 if (!verbose) { 98 printf("%s: %d/%d files, %d/%d blocks\n", device_name, 99 inodes_used, inodes, blocks_used, blocks); 100 return; 101 } 102 printf ("\n%6d inode%s used (%d%%)\n", inodes_used, 103 (inodes_used != 1) ? "s" : "", 104 100 * inodes_used / inodes); 105 printf ("%6d block%s used (%d%%)\n" 106 "%6d bad block%s\n", blocks_used, 107 (blocks_used != 1) ? "s" : "", 108 100 * blocks_used / blocks, fs_badblocks_count, 109 fs_badblocks_count != 1 ? "s" : ""); 110 printf ("\n%6d regular file%s\n" 111 "%6d director%s\n" 112 "%6d character device file%s\n" 113 "%6d block device file%s\n" 114 "%6d fifo%s\n" 115 "%6d link%s\n" 116 "%6d symbolic link%s (%d fast symbolic link%s)\n" 117 "%6d socket%s\n" 118 "------\n" 119 "%6d file%s\n", 120 fs_regular_count, (fs_regular_count != 1) ? "s" : "", 121 fs_directory_count, (fs_directory_count != 1) ? "ies" : "y", 122 fs_chardev_count, (fs_chardev_count != 1) ? "s" : "", 123 fs_blockdev_count, (fs_blockdev_count != 1) ? "s" : "", 124 fs_fifo_count, (fs_fifo_count != 1) ? "s" : "", 125 fs_links_count - dir_links, 126 ((fs_links_count - dir_links) != 1) ? "s" : "", 127 fs_symlinks_count, (fs_symlinks_count != 1) ? "s" : "", 128 fs_fast_symlinks_count, (fs_fast_symlinks_count != 1) ? "s" : "", 129 fs_sockets_count, (fs_sockets_count != 1) ? "s" : "", 130 fs_total_count - dir_links, 131 ((fs_total_count - dir_links) != 1) ? "s" : ""); 132} 133 134static void check_mount(NOARGS) 135{ 136 FILE * f; 137 struct mntent * mnt; 138 int cont; 139 int fd; 140 141 if ((f = setmntent (MOUNTED, "r")) == NULL) 142 return; 143 while ((mnt = getmntent (f)) != NULL) 144 if (strcmp (device_name, mnt->mnt_fsname) == 0) 145 break; 146 endmntent (f); 147 if (!mnt) 148 return; 149 150 if (!strcmp(mnt->mnt_dir, "/")) 151 root_filesystem = 1; 152 153 /* 154 * If the root is mounted read-only, then /etc/mtab is 155 * probably not correct; so we won't issue a warning based on 156 * it. 157 */ 158 fd = open(MOUNTED, O_RDWR); 159 if (fd < 0) { 160 if (errno == EROFS) { 161 read_only_root = 1; 162 return; 163 } 164 } else 165 close(fd); 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 221 /* 222 * Verify the super block constants... 223 */ 224 check_super_value("inodes_count", s->s_inodes_count, 225 MIN_CHECK, 1, 0); 226 check_super_value("blocks_count", s->s_blocks_count, 227 MIN_CHECK, 1, 0); 228 check_super_value("first_data_block", s->s_first_data_block, 229 MAX_CHECK, 0, s->s_blocks_count); 230 check_super_value("log_frag_size", s->s_log_frag_size, 231 MAX_CHECK, 0, 2); 232 check_super_value("log_block_size", s->s_log_block_size, 233 MIN_CHECK | MAX_CHECK, s->s_log_frag_size, 234 2); 235 check_super_value("frags_per_group", s->s_frags_per_group, 236 MIN_CHECK | MAX_CHECK, 1, 8 * EXT2_BLOCK_SIZE(s)); 237 check_super_value("blocks_per_group", s->s_blocks_per_group, 238 MIN_CHECK | MAX_CHECK, 1, 8 * EXT2_BLOCK_SIZE(s)); 239 check_super_value("inodes_per_group", s->s_inodes_per_group, 240 MIN_CHECK, 1, 0); 241 check_super_value("r_blocks_count", s->s_r_blocks_count, 242 MAX_CHECK, 0, s->s_blocks_count); 243 244 if (s->s_log_block_size != s->s_log_frag_size) { 245 printf("Superblock block_size = %d, fragsize = %d.\n", 246 EXT2_BLOCK_SIZE(s), EXT2_FRAG_SIZE(s)); 247 printf("This version of e2fsck does not support fragment " 248 "sizes different\n" 249 "from the block size.\n"); 250 fatal_error(0); 251 } 252 253 should_be = s->s_frags_per_group / 254 (s->s_log_block_size - s->s_log_frag_size + 1); 255 if (s->s_blocks_per_group != should_be) { 256 printf("Superblock blocks_per_group = %lu, should " 257 "have been %lu\n", s->s_blocks_per_group, 258 should_be); 259 printf(corrupt_msg); 260 } 261 262 should_be = (s->s_log_block_size == 0) ? 1 : 0; 263 if (s->s_first_data_block != should_be) { 264 printf("Superblock first_data_block = %lu, should " 265 "have been %lu\n", s->s_first_data_block, 266 should_be); 267 printf(corrupt_msg); 268 } 269 270 /* 271 * Verify the group descriptors.... 272 */ 273 first_block = fs->super->s_first_data_block; 274 last_block = first_block + blocks_per_group; 275 276 for (i = 0; i < fs->group_desc_count; i++) { 277 if (i == fs->group_desc_count - 1) 278 last_block = fs->super->s_blocks_count; 279 if ((fs->group_desc[i].bg_block_bitmap < first_block) || 280 (fs->group_desc[i].bg_block_bitmap >= last_block)) { 281 printf("Block bitmap %lu for group %d is " 282 "not in group.\n", 283 fs->group_desc[i].bg_block_bitmap, i); 284 preenhalt(); 285 if (!ask("Continue (and relocate)", 1)) { 286 fatal_error(0); 287 } 288 fs->group_desc[i].bg_block_bitmap = 0; 289 invalid_block_bitmap[i]++; 290 invalid_bitmaps++; 291 } 292 if ((fs->group_desc[i].bg_inode_bitmap < first_block) || 293 (fs->group_desc[i].bg_inode_bitmap >= last_block)) { 294 printf("Warning: Inode bitmap %lu for group %d " 295 "not in group.\n", 296 fs->group_desc[i].bg_inode_bitmap, i); 297 preenhalt(); 298 if (!ask("Continue", 1)) { 299 fatal_error(0); 300 } 301 fs->group_desc[i].bg_inode_bitmap = 0; 302 invalid_inode_bitmap[i]++; 303 invalid_bitmaps++; 304 } 305 if ((fs->group_desc[i].bg_inode_table < first_block) || 306 ((fs->group_desc[i].bg_inode_table + 307 fs->inode_blocks_per_group - 1) >= last_block)) { 308 printf("Warning: Inode table %lu for group %d " 309 "not in group.\n", 310 fs->group_desc[i].bg_inode_table, i); 311 printf("WARNING: SEVERE DATA LOSS POSSIBLE.\n"); 312 preenhalt(); 313 if (!ask("Continue", 1)) { 314 fatal_error(0); 315 } 316 fs->group_desc[i].bg_inode_table = 0; 317 invalid_inode_table[i]++; 318 invalid_bitmaps++; 319 } 320 first_block += fs->super->s_blocks_per_group; 321 last_block += fs->super->s_blocks_per_group; 322 } 323 return; 324} 325 326/* 327 * This routine checks to see if a filesystem can be skipped; if so, 328 * it will exit with E2FSCK_OK. Under some conditions it will print a 329 * message explaining why a check is being forced. 330 */ 331static void check_if_skip(ext2_filsys fs) 332{ 333 const char *reason = NULL; 334 335 if (force || bad_blocks_file || cflag) 336 return; 337 338 if (fs->super->s_state & EXT2_ERROR_FS) 339 reason = "contains a file system with errors"; 340 else if (fs->super->s_mnt_count >= 341 (unsigned) fs->super->s_max_mnt_count) 342 reason = "has reached maximal mount count"; 343 else if (fs->super->s_checkinterval && 344 time(0) >= (fs->super->s_lastcheck + 345 fs->super->s_checkinterval)) 346 reason = "has gone too long without being checked"; 347 if (reason) { 348 printf("%s %s, check forced.\n", device_name, reason); 349 return; 350 } 351 if (fs->super->s_state & EXT2_VALID_FS) { 352 printf("%s is clean, no check.\n", device_name); 353 exit(FSCK_OK); 354 } 355} 356 357static void PRS(int argc, char *argv[]) 358{ 359 int flush = 0; 360 char c; 361#ifdef MTRACE 362 extern void *mallwatch; 363#endif 364 char *oldpath; 365 static char newpath[PATH_MAX]; 366 367 /* Update our PATH to include /sbin */ 368 strcpy(newpath, "PATH=/sbin:"); 369 if ((oldpath = getenv("PATH")) != NULL) 370 strcat(newpath, oldpath); 371 putenv(newpath); 372 373 setbuf(stdout, NULL); 374 setbuf(stderr, NULL); 375 initialize_ext2_error_table(); 376 377 if (argc && *argv) 378 program_name = *argv; 379 while ((c = getopt (argc, argv, "panyrcB:dfvtFVM:b:I:P:l:L:")) != EOF) 380 switch (c) { 381 case 'p': 382 case 'a': 383 preen = 1; 384 yflag = nflag = 0; 385 break; 386 case 'n': 387 nflag = 1; 388 preen = yflag = 0; 389 break; 390 case 'y': 391 yflag = 1; 392 preen = nflag = 0; 393 break; 394 case 't': 395 tflag++; 396 break; 397 case 'c': 398 cflag++; 399 break; 400 case 'r': 401 /* What we do by default, anyway! */ 402 break; 403 case 'b': 404 superblock = atoi(optarg); 405 break; 406 case 'B': 407 blocksize = atoi(optarg); 408 break; 409 case 'I': 410 inode_buffer_blocks = atoi(optarg); 411 break; 412 case 'P': 413 process_inode_size = atoi(optarg); 414 break; 415 case 'L': 416 replace_bad_blocks++; 417 case 'l': 418 bad_blocks_file = malloc(strlen(optarg)+1); 419 if (!bad_blocks_file) 420 fatal_error("Couldn't malloc bad_blocks_file"); 421 strcpy(bad_blocks_file, optarg); 422 break; 423 case 'd': 424 debug = 1; 425 break; 426 case 'f': 427 force = 1; 428 break; 429 case 'F': 430 flush = 1; 431 break; 432 case 'v': 433 verbose = 1; 434 break; 435 case 'V': 436 show_version_only = 1; 437 break; 438#ifdef MTRACE 439 case 'M': 440 mallwatch = (void *) strtol(optarg, NULL, 0); 441 break; 442#endif 443 default: 444 usage (); 445 } 446 if (show_version_only) 447 return; 448 if (optind != argc - 1) 449 usage (); 450 if (nflag && !bad_blocks_file && !cflag) 451 rwflag = 0; 452 device_name = argv[optind]; 453 if (flush) { 454 int fd = open(device_name, O_RDONLY, 0); 455 456 if (fd < 0) { 457 com_err("open", errno, "while opening %s for flushing", 458 device_name); 459 exit(FSCK_ERROR); 460 } 461 if (ioctl(fd, BLKFLSBUF, 0) < 0) { 462 com_err("BLKFLSBUF", errno, "while trying to flush %s", 463 device_name); 464 exit(FSCK_ERROR); 465 } 466 close(fd); 467 } 468} 469 470int main (int argc, char *argv[]) 471{ 472 errcode_t retval = 0; 473 int exit_value = FSCK_OK; 474 int i; 475 ext2_filsys fs; 476 477#ifdef MTRACE 478 mtrace(); 479#endif 480#ifdef MCHECK 481 mcheck(0); 482#endif 483 484 init_resource_track(&global_rtrack); 485 486 PRS(argc, argv); 487 488 if (!preen) 489 fprintf (stderr, "e2fsck %s, %s for EXT2 FS %s, %s\n", 490 E2FSPROGS_VERSION, E2FSPROGS_DATE, 491 EXT2FS_VERSION, EXT2FS_DATE); 492 493 if (show_version_only) 494 exit(0); 495 496 check_mount(); 497 498 if (!preen && !nflag && !yflag) { 499 if (!isatty (0) || !isatty (1)) 500 die ("need terminal for interactive repairs"); 501 } 502restart: 503 sync_disks(); 504 if (superblock && blocksize) { 505 retval = ext2fs_open(device_name, rwflag ? EXT2_FLAG_RW : 0, 506 superblock, blocksize, unix_io_manager, 507 &fs); 508 } else if (superblock) { 509 for (i=0; possible_block_sizes[i]; i++) { 510 retval = ext2fs_open(device_name, 511 rwflag ? EXT2_FLAG_RW : 0, 512 superblock, 513 possible_block_sizes[i], 514 unix_io_manager, &fs); 515 if (!retval) 516 break; 517 } 518 } else 519 retval = ext2fs_open(device_name, rwflag ? EXT2_FLAG_RW : 0, 520 0, 0, unix_io_manager, &fs); 521 if (retval) { 522 com_err(program_name, retval, "while trying to open %s", 523 device_name); 524 if (retval == EXT2_ET_REV_TOO_HIGH) 525 printf ("Get a newer version of e2fsck!\n"); 526 else 527 printf(corrupt_msg); 528 fatal_error(0); 529 } 530 531#ifdef EXT2_CURRENT_REV 532 if (fs->super->s_rev_level > E2FSCK_CURRENT_REV) { 533 com_err(program_name, retval, "while trying to open %s", 534 device_name); 535 printf ("Get a newer version of e2fsck!\n"); 536 fatal_error(0); 537 } 538#endif 539 /* 540 * If the user specified a specific superblock, presumably the 541 * master superblock has been trashed. So we mark the 542 * superblock as dirty, so it can be written out. 543 */ 544 if (superblock && rwflag) 545 ext2fs_mark_super_dirty(fs); 546 547 ehandler_init(fs->io); 548 549 invalid_inode_bitmap = allocate_memory(sizeof(int) * 550 fs->group_desc_count, 551 "invalid_inode_bitmap"); 552 invalid_block_bitmap = allocate_memory(sizeof(int) * 553 fs->group_desc_count, 554 "invalid_block_bitmap"); 555 invalid_inode_table = allocate_memory(sizeof(int) * 556 fs->group_desc_count, 557 "invalid_inode_table"); 558 559 check_super_block(fs); 560 check_if_skip(fs); 561 if (bad_blocks_file) 562 read_bad_blocks_file(fs, bad_blocks_file, replace_bad_blocks); 563 else if (cflag) 564 test_disk(fs); 565 566 /* 567 * Mark the system as valid, 'til proven otherwise 568 */ 569 ext2fs_mark_valid(fs); 570 571 pass1(fs); 572 if (restart_e2fsck) { 573 ext2fs_close(fs); 574 printf("Restarting e2fsck from the beginning...\n"); 575 restart_e2fsck = 0; 576 goto restart; 577 } 578 pass2(fs); 579 pass3(fs); 580 pass4(fs); 581 pass5(fs); 582 583#ifdef MTRACE 584 mtrace_print("Cleanup"); 585#endif 586 if (ext2fs_test_changed(fs)) { 587 exit_value = FSCK_NONDESTRUCT; 588 if (!preen) 589 printf("\n%s: ***** FILE SYSTEM WAS MODIFIED *****\n", 590 device_name); 591 if (root_filesystem && !read_only_root) { 592 printf("%s: ***** REBOOT LINUX *****\n", device_name); 593 exit_value = FSCK_REBOOT; 594 } 595 } 596 if (!ext2fs_test_valid(fs)) 597 exit_value = FSCK_UNCORRECTED; 598 if (rwflag) { 599 if (ext2fs_test_valid(fs)) 600 fs->super->s_state = EXT2_VALID_FS; 601 else 602 fs->super->s_state &= ~EXT2_VALID_FS; 603 fs->super->s_mnt_count = 0; 604 fs->super->s_lastcheck = time(NULL); 605 ext2fs_mark_super_dirty(fs); 606 } 607 show_stats(fs); 608 609 write_bitmaps(fs); 610 ext2fs_close(fs); 611 sync_disks(); 612 613 if (tflag) 614 print_resource_track(&global_rtrack); 615 616 return exit_value; 617} 618