blob: 78fe3c9244b99944233532c3eb908eae89ecba15 [file] [log] [blame]
Theodore Ts'o3839e651997-04-26 13:21:57 +00001/*
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;
Theodore Ts'of3db3561997-04-26 13:34:30 +000054int invalid_bitmaps = 0;
Theodore Ts'o3839e651997-04-26 13:21:57 +000055static 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
Theodore Ts'of3db3561997-04-26 13:34:30 +000067int *invalid_inode_bitmap;
68int *invalid_block_bitmap;
69int *invalid_inode_table;
70int restart_e2fsck = 0;
71
Theodore Ts'o3839e651997-04-26 13:21:57 +000072static void usage(NOARGS)
73{
74 fprintf(stderr,
Theodore Ts'of3db3561997-04-26 13:34:30 +000075 "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);
Theodore Ts'o3839e651997-04-26 13:21:57 +000078 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
Theodore Ts'of3db3561997-04-26 13:34:30 +0000192#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
Theodore Ts'o3839e651997-04-26 13:21:57 +0000213static void check_super_block(ext2_filsys fs)
214{
215 blk_t first_block, last_block;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000216 struct ext2_super_block *s = fs->super;
217 blk_t blocks_per_group = fs->super->s_blocks_per_group;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000218 int i;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000219 blk_t should_be;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000220
Theodore Ts'of3db3561997-04-26 13:34:30 +0000221 /*
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 */
Theodore Ts'o3839e651997-04-26 13:21:57 +0000273 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++) {
Theodore Ts'of3db3561997-04-26 13:34:30 +0000277 if (i == fs->group_desc_count - 1)
278 last_block = fs->super->s_blocks_count;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000279 if ((fs->group_desc[i].bg_block_bitmap < first_block) ||
280 (fs->group_desc[i].bg_block_bitmap >= last_block)) {
Theodore Ts'of3db3561997-04-26 13:34:30 +0000281 printf("Block bitmap %lu for group %d is "
282 "not in group.\n",
Theodore Ts'o3839e651997-04-26 13:21:57 +0000283 fs->group_desc[i].bg_block_bitmap, i);
Theodore Ts'of3db3561997-04-26 13:34:30 +0000284 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++;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000291 }
292 if ((fs->group_desc[i].bg_inode_bitmap < first_block) ||
293 (fs->group_desc[i].bg_inode_bitmap >= last_block)) {
Theodore Ts'of3db3561997-04-26 13:34:30 +0000294 printf("Warning: Inode bitmap %lu for group %d "
295 "not in group.\n",
Theodore Ts'o3839e651997-04-26 13:21:57 +0000296 fs->group_desc[i].bg_inode_bitmap, i);
Theodore Ts'of3db3561997-04-26 13:34:30 +0000297 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++;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000304 }
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)) {
Theodore Ts'of3db3561997-04-26 13:34:30 +0000308 printf("Warning: Inode table %lu for group %d "
309 "not in group.\n",
Theodore Ts'o3839e651997-04-26 13:21:57 +0000310 fs->group_desc[i].bg_inode_table, i);
Theodore Ts'of3db3561997-04-26 13:34:30 +0000311 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++;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000319 }
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{
Theodore Ts'of3db3561997-04-26 13:34:30 +0000359 int flush = 0;
360 char c;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000361#ifdef MTRACE
Theodore Ts'of3db3561997-04-26 13:34:30 +0000362 extern void *mallwatch;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000363#endif
Theodore Ts'of3db3561997-04-26 13:34:30 +0000364 char *oldpath;
365 static char newpath[PATH_MAX];
Theodore Ts'o3839e651997-04-26 13:21:57 +0000366
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 }
Theodore Ts'of3db3561997-04-26 13:34:30 +0000502restart:
Theodore Ts'o3839e651997-04-26 13:21:57 +0000503 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);
Theodore Ts'of3db3561997-04-26 13:34:30 +0000524 if (retval == EXT2_ET_REV_TOO_HIGH)
525 printf ("Get a newer version of e2fsck!\n");
526 else
527 printf(corrupt_msg);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000528 fatal_error(0);
529 }
Theodore Ts'of3db3561997-04-26 13:34:30 +0000530
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
Theodore Ts'o3839e651997-04-26 13:21:57 +0000539 /*
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
Theodore Ts'of3db3561997-04-26 13:34:30 +0000549 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
Theodore Ts'o3839e651997-04-26 13:21:57 +0000559 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);
Theodore Ts'of3db3561997-04-26 13:34:30 +0000572 if (restart_e2fsck) {
573 ext2fs_close(fs);
574 printf("Restarting e2fsck from the beginning...\n");
575 restart_e2fsck = 0;
576 goto restart;
577 }
Theodore Ts'o3839e651997-04-26 13:21:57 +0000578 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}