e2fsck.c revision 3839e65723771b85975f4263102dd3ceec4523c0
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; 54static int show_version_only = 0; 55 56static int replace_bad_blocks = 0; 57static char *bad_blocks_file = 0; 58 59static int possible_block_sizes[] = { 1024, 2048, 4096, 8192, 0}; 60 61struct resource_track global_rtrack; 62 63static int root_filesystem = 0; 64static int read_only_root = 0; 65 66static void usage(NOARGS) 67{ 68 fprintf(stderr, 69 "Usage: %s [-panyrdfvtFV] [-b superblock] [-B blocksize]\n" 70 "\t\tdevice\n", program_name); 71 exit(FSCK_USAGE); 72} 73 74static void show_stats(ext2_filsys fs) 75{ 76 int inodes, inodes_used, blocks, blocks_used; 77 int dir_links; 78 int num_files, num_links; 79 80 dir_links = 2 * fs_directory_count - 1; 81 num_files = fs_total_count - dir_links; 82 num_links = fs_links_count - dir_links; 83 inodes = fs->super->s_inodes_count; 84 inodes_used = (fs->super->s_inodes_count - 85 fs->super->s_free_inodes_count); 86 blocks = fs->super->s_blocks_count; 87 blocks_used = (fs->super->s_blocks_count - 88 fs->super->s_free_blocks_count); 89 90 if (!verbose) { 91 printf("%s: %d/%d files, %d/%d blocks\n", device_name, 92 inodes_used, inodes, blocks_used, blocks); 93 return; 94 } 95 printf ("\n%6d inode%s used (%d%%)\n", inodes_used, 96 (inodes_used != 1) ? "s" : "", 97 100 * inodes_used / inodes); 98 printf ("%6d block%s used (%d%%)\n" 99 "%6d bad block%s\n", blocks_used, 100 (blocks_used != 1) ? "s" : "", 101 100 * blocks_used / blocks, fs_badblocks_count, 102 fs_badblocks_count != 1 ? "s" : ""); 103 printf ("\n%6d regular file%s\n" 104 "%6d director%s\n" 105 "%6d character device file%s\n" 106 "%6d block device file%s\n" 107 "%6d fifo%s\n" 108 "%6d link%s\n" 109 "%6d symbolic link%s (%d fast symbolic link%s)\n" 110 "%6d socket%s\n" 111 "------\n" 112 "%6d file%s\n", 113 fs_regular_count, (fs_regular_count != 1) ? "s" : "", 114 fs_directory_count, (fs_directory_count != 1) ? "ies" : "y", 115 fs_chardev_count, (fs_chardev_count != 1) ? "s" : "", 116 fs_blockdev_count, (fs_blockdev_count != 1) ? "s" : "", 117 fs_fifo_count, (fs_fifo_count != 1) ? "s" : "", 118 fs_links_count - dir_links, 119 ((fs_links_count - dir_links) != 1) ? "s" : "", 120 fs_symlinks_count, (fs_symlinks_count != 1) ? "s" : "", 121 fs_fast_symlinks_count, (fs_fast_symlinks_count != 1) ? "s" : "", 122 fs_sockets_count, (fs_sockets_count != 1) ? "s" : "", 123 fs_total_count - dir_links, 124 ((fs_total_count - dir_links) != 1) ? "s" : ""); 125} 126 127static void check_mount(NOARGS) 128{ 129 FILE * f; 130 struct mntent * mnt; 131 int cont; 132 int fd; 133 134 if ((f = setmntent (MOUNTED, "r")) == NULL) 135 return; 136 while ((mnt = getmntent (f)) != NULL) 137 if (strcmp (device_name, mnt->mnt_fsname) == 0) 138 break; 139 endmntent (f); 140 if (!mnt) 141 return; 142 143 if (!strcmp(mnt->mnt_dir, "/")) 144 root_filesystem = 1; 145 146 /* 147 * If the root is mounted read-only, then /etc/mtab is 148 * probably not correct; so we won't issue a warning based on 149 * it. 150 */ 151 fd = open(MOUNTED, O_RDWR); 152 if (fd < 0) { 153 if (errno == EROFS) { 154 read_only_root = 1; 155 return; 156 } 157 } else 158 close(fd); 159 160 if (!rwflag) { 161 printf("Warning! %s is mounted.\n", device_name); 162 return; 163 } 164 165 printf ("%s is mounted. ", device_name); 166 if (isatty (0) && isatty (1)) 167 cont = ask_yn("Do you really want to continue", -1); 168 else 169 cont = 0; 170 if (!cont) { 171 printf ("check aborted.\n"); 172 exit (0); 173 } 174 return; 175} 176 177static void sync_disks(NOARGS) 178{ 179 sync(); 180 sync(); 181 sleep(1); 182 sync(); 183} 184 185static void check_super_block(ext2_filsys fs) 186{ 187 blk_t first_block, last_block; 188 int blocks_per_group = fs->super->s_blocks_per_group; 189 int i; 190 191 first_block = fs->super->s_first_data_block; 192 last_block = first_block + blocks_per_group; 193 194 for (i = 0; i < fs->group_desc_count; i++) { 195 if ((fs->group_desc[i].bg_block_bitmap < first_block) || 196 (fs->group_desc[i].bg_block_bitmap >= last_block)) { 197 printf("Block bitmap %ld for group %d not in group.\n", 198 fs->group_desc[i].bg_block_bitmap, i); 199 fatal_error(0); 200 } 201 if ((fs->group_desc[i].bg_inode_bitmap < first_block) || 202 (fs->group_desc[i].bg_inode_bitmap >= last_block)) { 203 printf("Inode bitmap %ld for group %d not in group.\n", 204 fs->group_desc[i].bg_inode_bitmap, i); 205 fatal_error(0); 206 } 207 if ((fs->group_desc[i].bg_inode_table < first_block) || 208 ((fs->group_desc[i].bg_inode_table + 209 fs->inode_blocks_per_group - 1) >= last_block)) { 210 printf("Inode table %ld for group %d not in group.\n", 211 fs->group_desc[i].bg_inode_table, i); 212 fatal_error(0); 213 } 214 first_block += fs->super->s_blocks_per_group; 215 last_block += fs->super->s_blocks_per_group; 216 } 217 return; 218} 219 220/* 221 * This routine checks to see if a filesystem can be skipped; if so, 222 * it will exit with E2FSCK_OK. Under some conditions it will print a 223 * message explaining why a check is being forced. 224 */ 225static void check_if_skip(ext2_filsys fs) 226{ 227 const char *reason = NULL; 228 229 if (force || bad_blocks_file || cflag) 230 return; 231 232 if (fs->super->s_state & EXT2_ERROR_FS) 233 reason = "contains a file system with errors"; 234 else if (fs->super->s_mnt_count >= 235 (unsigned) fs->super->s_max_mnt_count) 236 reason = "has reached maximal mount count"; 237 else if (fs->super->s_checkinterval && 238 time(0) >= (fs->super->s_lastcheck + 239 fs->super->s_checkinterval)) 240 reason = "has gone too long without being checked"; 241 if (reason) { 242 printf("%s %s, check forced.\n", device_name, reason); 243 return; 244 } 245 if (fs->super->s_state & EXT2_VALID_FS) { 246 printf("%s is clean, no check.\n", device_name); 247 exit(FSCK_OK); 248 } 249} 250 251static void PRS(int argc, char *argv[]) 252{ 253 int flush = 0; 254 char c; 255#ifdef MTRACE 256 extern void *mallwatch; 257#endif 258 char *oldpath, newpath[PATH_MAX]; 259 260 /* Update our PATH to include /sbin */ 261 strcpy(newpath, "PATH=/sbin:"); 262 if ((oldpath = getenv("PATH")) != NULL) 263 strcat(newpath, oldpath); 264 putenv(newpath); 265 266 setbuf(stdout, NULL); 267 setbuf(stderr, NULL); 268 initialize_ext2_error_table(); 269 270 if (argc && *argv) 271 program_name = *argv; 272 while ((c = getopt (argc, argv, "panyrcB:dfvtFVM:b:I:P:l:L:")) != EOF) 273 switch (c) { 274 case 'p': 275 case 'a': 276 preen = 1; 277 yflag = nflag = 0; 278 break; 279 case 'n': 280 nflag = 1; 281 preen = yflag = 0; 282 break; 283 case 'y': 284 yflag = 1; 285 preen = nflag = 0; 286 break; 287 case 't': 288 tflag++; 289 break; 290 case 'c': 291 cflag++; 292 break; 293 case 'r': 294 /* What we do by default, anyway! */ 295 break; 296 case 'b': 297 superblock = atoi(optarg); 298 break; 299 case 'B': 300 blocksize = atoi(optarg); 301 break; 302 case 'I': 303 inode_buffer_blocks = atoi(optarg); 304 break; 305 case 'P': 306 process_inode_size = atoi(optarg); 307 break; 308 case 'L': 309 replace_bad_blocks++; 310 case 'l': 311 bad_blocks_file = malloc(strlen(optarg)+1); 312 if (!bad_blocks_file) 313 fatal_error("Couldn't malloc bad_blocks_file"); 314 strcpy(bad_blocks_file, optarg); 315 break; 316 case 'd': 317 debug = 1; 318 break; 319 case 'f': 320 force = 1; 321 break; 322 case 'F': 323 flush = 1; 324 break; 325 case 'v': 326 verbose = 1; 327 break; 328 case 'V': 329 show_version_only = 1; 330 break; 331#ifdef MTRACE 332 case 'M': 333 mallwatch = (void *) strtol(optarg, NULL, 0); 334 break; 335#endif 336 default: 337 usage (); 338 } 339 if (show_version_only) 340 return; 341 if (optind != argc - 1) 342 usage (); 343 if (nflag && !bad_blocks_file && !cflag) 344 rwflag = 0; 345 device_name = argv[optind]; 346 if (flush) { 347 int fd = open(device_name, O_RDONLY, 0); 348 349 if (fd < 0) { 350 com_err("open", errno, "while opening %s for flushing", 351 device_name); 352 exit(FSCK_ERROR); 353 } 354 if (ioctl(fd, BLKFLSBUF, 0) < 0) { 355 com_err("BLKFLSBUF", errno, "while trying to flush %s", 356 device_name); 357 exit(FSCK_ERROR); 358 } 359 close(fd); 360 } 361} 362 363int main (int argc, char *argv[]) 364{ 365 errcode_t retval = 0; 366 int exit_value = FSCK_OK; 367 int i; 368 ext2_filsys fs; 369 370#ifdef MTRACE 371 mtrace(); 372#endif 373#ifdef MCHECK 374 mcheck(0); 375#endif 376 377 init_resource_track(&global_rtrack); 378 379 PRS(argc, argv); 380 381 if (!preen) 382 fprintf (stderr, "e2fsck %s, %s for EXT2 FS %s, %s\n", 383 E2FSPROGS_VERSION, E2FSPROGS_DATE, 384 EXT2FS_VERSION, EXT2FS_DATE); 385 386 if (show_version_only) 387 exit(0); 388 389 check_mount(); 390 391 if (!preen && !nflag && !yflag) { 392 if (!isatty (0) || !isatty (1)) 393 die ("need terminal for interactive repairs"); 394 } 395 sync_disks(); 396 if (superblock && blocksize) { 397 retval = ext2fs_open(device_name, rwflag ? EXT2_FLAG_RW : 0, 398 superblock, blocksize, unix_io_manager, 399 &fs); 400 } else if (superblock) { 401 for (i=0; possible_block_sizes[i]; i++) { 402 retval = ext2fs_open(device_name, 403 rwflag ? EXT2_FLAG_RW : 0, 404 superblock, 405 possible_block_sizes[i], 406 unix_io_manager, &fs); 407 if (!retval) 408 break; 409 } 410 } else 411 retval = ext2fs_open(device_name, rwflag ? EXT2_FLAG_RW : 0, 412 0, 0, unix_io_manager, &fs); 413 if (retval) { 414 com_err(program_name, retval, "while trying to open %s", 415 device_name); 416 printf("Couldn't find valid filesystem superblock.\n"); 417 fatal_error(0); 418 } 419 /* 420 * If the user specified a specific superblock, presumably the 421 * master superblock has been trashed. So we mark the 422 * superblock as dirty, so it can be written out. 423 */ 424 if (superblock && rwflag) 425 ext2fs_mark_super_dirty(fs); 426 427 ehandler_init(fs->io); 428 429 check_super_block(fs); 430 check_if_skip(fs); 431 if (bad_blocks_file) 432 read_bad_blocks_file(fs, bad_blocks_file, replace_bad_blocks); 433 else if (cflag) 434 test_disk(fs); 435 436 /* 437 * Mark the system as valid, 'til proven otherwise 438 */ 439 ext2fs_mark_valid(fs); 440 441 pass1(fs); 442 pass2(fs); 443 pass3(fs); 444 pass4(fs); 445 pass5(fs); 446 447#ifdef MTRACE 448 mtrace_print("Cleanup"); 449#endif 450 if (ext2fs_test_changed(fs)) { 451 exit_value = FSCK_NONDESTRUCT; 452 if (!preen) 453 printf("\n%s: ***** FILE SYSTEM WAS MODIFIED *****\n", 454 device_name); 455 if (root_filesystem && !read_only_root) { 456 printf("%s: ***** REBOOT LINUX *****\n", device_name); 457 exit_value = FSCK_REBOOT; 458 } 459 } 460 if (!ext2fs_test_valid(fs)) 461 exit_value = FSCK_UNCORRECTED; 462 if (rwflag) { 463 if (ext2fs_test_valid(fs)) 464 fs->super->s_state = EXT2_VALID_FS; 465 else 466 fs->super->s_state &= ~EXT2_VALID_FS; 467 fs->super->s_mnt_count = 0; 468 fs->super->s_lastcheck = time(NULL); 469 ext2fs_mark_super_dirty(fs); 470 } 471 show_stats(fs); 472 473 write_bitmaps(fs); 474 ext2fs_close(fs); 475 sync_disks(); 476 477 if (tflag) 478 print_resource_track(&global_rtrack); 479 480 return exit_value; 481} 482