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