Whamcloud - gitweb
Many files:
[tools/e2fsprogs.git] / e2fsck / e2fsck.c
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
35 extern int isatty(int);
36
37 const char * program_name = "e2fsck";
38 const char * device_name = NULL;
39
40 /* Command line options */
41 int nflag = 0;
42 int yflag = 0;
43 int tflag = 0;                  /* Do timing */
44 int cflag = 0;                  /* check disk */
45 int preen = 0;
46 int rwflag = 1;
47 int inode_buffer_blocks = 0;
48 blk_t superblock;
49 int blocksize = 0;
50 int verbose = 0;
51 int list = 0;
52 int debug = 0;
53 int force = 0;
54 int invalid_bitmaps = 0;
55 static int show_version_only = 0;
56
57 static int replace_bad_blocks = 0;
58 static char *bad_blocks_file = 0;
59
60 static int possible_block_sizes[] = { 1024, 2048, 4096, 8192, 0};
61
62 struct resource_track   global_rtrack;
63
64 static int root_filesystem = 0;
65 static int read_only_root = 0;
66
67 int *invalid_inode_bitmap;
68 int *invalid_block_bitmap;
69 int *invalid_inode_table;
70 int restart_e2fsck = 0;
71
72 static 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
81 static 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
134 static 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
184 static 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
195 static 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
201 static 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
213 static 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  */
331 static 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
357 static 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                                         
470 int 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         }
502 restart:
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 }