Whamcloud - gitweb
ChangeLog, journal.c:
[tools/e2fsprogs.git] / e2fsck / unix.c
1 /*
2  * unix.c - The unix-specific code for e2fsck
3  * 
4  * Copyright (C) 1993, 1994, 1995, 1996, 1997 Theodore Ts'o.
5  *
6  * %Begin-Header%
7  * This file may be redistributed under the terms of the GNU Public
8  * License.
9  * %End-Header%
10  */
11
12 #include <stdio.h>
13 #ifdef HAVE_STDLIB_H
14 #include <stdlib.h>
15 #endif
16 #include <string.h>
17 #include <fcntl.h>
18 #include <ctype.h>
19 #include <time.h>
20 #ifdef HAVE_SIGNAL_H
21 #include <signal.h>
22 #endif
23 #ifdef HAVE_GETOPT_H
24 #include <getopt.h>
25 #else
26 extern char *optarg;
27 extern int optind;
28 #endif
29 #include <unistd.h>
30 #ifdef HAVE_ERRNO_H
31 #include <errno.h>
32 #endif
33 #ifdef HAVE_MNTENT_H
34 #include <mntent.h>
35 #endif
36 #include <sys/ioctl.h>
37 #include <malloc.h>
38
39 #include "et/com_err.h"
40 #include "e2fsck.h"
41 #include "problem.h"
42 #include "../version.h"
43
44 /* Command line options */
45 static int blocksize = 0;
46 static int swapfs = 0;
47 static int normalize_swapfs = 0;
48 static int cflag = 0;           /* check disk */
49 static int show_version_only = 0;
50 static int force = 0;
51 static int verbose = 0;
52
53 static int replace_bad_blocks = 0;
54 static char *bad_blocks_file = 0;
55
56 static int possible_block_sizes[] = { 1024, 2048, 4096, 8192, 0};
57
58 static int root_filesystem = 0;
59 static int read_only_root = 0;
60
61 e2fsck_t e2fsck_global_ctx;     /* Try your very best not to use this! */
62
63 static void usage(e2fsck_t ctx)
64 {
65         fprintf(stderr,
66                 _("Usage: %s [-panyrcdfvstFSV] [-b superblock] [-B blocksize]\n"
67                 "\t\t[-I inode_buffer_blocks] [-P process_inode_size]\n"
68                 "\t\t[-l|-L bad_blocks_file] [-C fd] device\n"),
69                 ctx->program_name);
70
71         fprintf(stderr, _("\nEmergency help:\n"
72                 " -p                   Automatic repair (no questions)\n"
73                 " -n                   Make no changes to the filesystem\n"
74                 " -y                   Assume \"yes\" to all questions\n"
75                 " -c                   Check for bad blocks\n"
76                 " -f                   Force checking even if filesystem is marked clean\n"));
77         fprintf(stderr, _(""
78                 " -v                   Be verbose\n"
79                 " -b superblock        Use alternative superblock\n"
80                 " -B blocksize         Force blocksize when looking for superblock\n"
81                 " -l bad_blocks_file   Add to badblocks list\n"
82                 " -L bad_blocks_file   Set badblocks list\n"
83                 ));
84
85         exit(FSCK_USAGE);
86 }
87
88 static void show_stats(e2fsck_t ctx)
89 {
90         ext2_filsys fs = ctx->fs;
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 * ctx->fs_directory_count - 1;
97         num_files = ctx->fs_total_count - dir_links;
98         num_links = ctx->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 * ctx->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                        ctx->device_name, inodes_used, inodes,
112                        frag_percent / 10, frag_percent % 10,
113                        blocks_used, blocks);
114                 return;
115         }
116         /*
117          * This is a bit ugly. But I think there will nearly always be more
118          * than one "thing" to report about, so I won't try writing complex
119          * code to handle one/two/many forms of all words.
120          * Some languages (Italian, at least) never uses the plural form
121          * of foreign words, so in real life this could not be a problem.
122          * md@linux.it - 2000-1-15
123          */
124 #ifdef ENABLE_NLS
125         printf (_("\n%8d inodes used (%d%%)\n"), inodes_used,
126                 (inodes_used != 1), 100 * inodes_used / inodes);
127         printf (_("%8d non-contiguous inodes (%0d.%d%%)\n"),
128                 ctx->fs_fragmented, frag_percent / 10, frag_percent % 10);
129         printf (_("         # of inodes with ind/dind/tind blocks: %d/%d/%d\n"),
130                 ctx->fs_ind_count, ctx->fs_dind_count, ctx->fs_tind_count);
131         printf (_("%8d blocks used (%d%%)\n"
132                 "%8d bad blocks\n"), blocks_used,
133                 100 * blocks_used / blocks, ctx->fs_badblocks_count);
134         printf (_("\n%8d regular files\n"
135                 "%8d directories\n"
136                 "%8d character device files\n"
137                 "%8d block device files\n"
138                 "%8d fifos\n"
139                 "%8d links\n"
140                 "%8d symbolic links (%d fast symbolic links)\n"
141                 "%8d sockets\n"
142                 "--------\n"
143                 "%8d files\n"),
144                 ctx->fs_regular_count,
145                 ctx->fs_directory_count,
146                 ctx->fs_chardev_count,
147                 ctx->fs_blockdev_count,
148                 ctx->fs_fifo_count,
149                 ctx->fs_links_count - dir_links,
150                 ctx->fs_symlinks_count,
151                 ctx->fs_fast_symlinks_count,
152                 ctx->fs_sockets_count,
153                 ctx->fs_total_count - dir_links);
154 #else
155         printf ("\n%8d inode%s used (%d%%)\n", inodes_used,
156                 (inodes_used != 1) ? "s" : "",
157                 100 * inodes_used / inodes);
158         printf ("%8d non-contiguous inodes (%0d.%d%%)\n",
159                 ctx->fs_fragmented, frag_percent / 10, frag_percent % 10);
160         printf ("         # of inodes with ind/dind/tind blocks: %d/%d/%d\n",
161                 ctx->fs_ind_count, ctx->fs_dind_count, ctx->fs_tind_count);
162         printf ("%8d block%s used (%d%%)\n"
163                 "%8d bad block%s\n", blocks_used,
164                 (blocks_used != 1) ? "s" : "",
165                 100 * blocks_used / blocks, ctx->fs_badblocks_count,
166                 ctx->fs_badblocks_count != 1 ? "s" : "");
167         printf ("\n%8d regular file%s\n"
168                 "%8d director%s\n"
169                 "%8d character device file%s\n"
170                 "%8d block device file%s\n"
171                 "%8d fifo%s\n"
172                 "%8d link%s\n"
173                 "%8d symbolic link%s (%d fast symbolic link%s)\n"
174                 "%8d socket%s\n"
175                 "--------\n"
176                 "%8d file%s\n",
177                 ctx->fs_regular_count,
178                 (ctx->fs_regular_count != 1) ? "s" : "",
179                 ctx->fs_directory_count,
180                 (ctx->fs_directory_count != 1) ? "ies" : "y",
181                 ctx->fs_chardev_count,
182                 (ctx->fs_chardev_count != 1) ? "s" : "",
183                 ctx->fs_blockdev_count,
184                 (ctx->fs_blockdev_count != 1) ? "s" : "",
185                 ctx->fs_fifo_count,
186                 (ctx->fs_fifo_count != 1) ? "s" : "",
187                 ctx->fs_links_count - dir_links,
188                 ((ctx->fs_links_count - dir_links) != 1) ? "s" : "",
189                 ctx->fs_symlinks_count,
190                 (ctx->fs_symlinks_count != 1) ? "s" : "",
191                 ctx->fs_fast_symlinks_count,
192                 (ctx->fs_fast_symlinks_count != 1) ? "s" : "",
193                 ctx->fs_sockets_count, (ctx->fs_sockets_count != 1) ? "s" : "",
194                 ctx->fs_total_count - dir_links,
195                 ((ctx->fs_total_count - dir_links) != 1) ? "s" : "");
196 #endif
197 }
198
199 static void check_mount(e2fsck_t ctx)
200 {
201         errcode_t       retval;
202         int             mount_flags, cont, fd;
203
204         retval = ext2fs_check_if_mounted(ctx->filesystem_name, &mount_flags);
205         if (retval) {
206                 com_err("ext2fs_check_if_mount", retval,
207                         _("while determining whether %s is mounted."),
208                         ctx->filesystem_name);
209                 return;
210         }
211         if (!(mount_flags & EXT2_MF_MOUNTED))
212                 return;
213
214 #if (defined(__linux__) && defined(HAVE_MNTENT_H))
215         /*
216          * If the root is mounted read-only, then /etc/mtab is
217          * probably not correct; so we won't issue a warning based on
218          * it.
219          */
220         fd = open(MOUNTED, O_RDWR);
221         if (fd < 0) {
222                 if (errno == EROFS)
223                         return;
224         } else
225                 close(fd);
226 #endif
227         
228         if (ctx->options & E2F_OPT_READONLY) {
229                 printf(_("Warning!  %s is mounted.\n"), ctx->filesystem_name);
230                 return;
231         }
232
233         printf(_("%s is mounted.  "), ctx->filesystem_name);
234         if (!isatty(0) || !isatty(1))
235                 fatal_error(ctx, _("Cannot continue, aborting.\n\n"));
236         printf(_("\n\n\007\007\007\007WARNING!!!  "
237                "Running e2fsck on a mounted filesystem may cause\n"
238                "SEVERE filesystem damage.\007\007\007\n\n"));
239         cont = ask_yn(_("Do you really want to continue"), -1);
240         if (!cont) {
241                 printf (_("check aborted.\n"));
242                 exit (0);
243         }
244         return;
245 }
246
247 /*
248  * This routine checks to see if a filesystem can be skipped; if so,
249  * it will exit with E2FSCK_OK.  Under some conditions it will print a
250  * message explaining why a check is being forced.
251  */
252 static void check_if_skip(e2fsck_t ctx)
253 {
254         ext2_filsys fs = ctx->fs;
255         const char *reason = NULL;
256         
257         if (force || bad_blocks_file || cflag || swapfs)
258                 return;
259         
260         if (fs->super->s_state & EXT2_ERROR_FS)
261                 reason = _("contains a file system with errors");
262         else if ((fs->super->s_state & EXT2_VALID_FS) == 0)
263                 reason = _("was not cleanly unmounted");
264         else if ((fs->super->s_max_mnt_count >= 0) &&
265                  (fs->super->s_mnt_count >=
266                   (unsigned) fs->super->s_max_mnt_count))
267                 reason = _("has reached maximal mount count");
268         else if (fs->super->s_checkinterval &&
269                  time(0) >= (fs->super->s_lastcheck +
270                              fs->super->s_checkinterval))
271                 reason = _("has gone too long without being checked");
272         if (reason) {
273                 printf(_("%s %s, check forced.\n"), ctx->device_name, reason);
274                 return;
275         }
276         printf(_("%s: clean, %d/%d files, %d/%d blocks\n"), ctx->device_name,
277                fs->super->s_inodes_count - fs->super->s_free_inodes_count,
278                fs->super->s_inodes_count,
279                fs->super->s_blocks_count - fs->super->s_free_blocks_count,
280                fs->super->s_blocks_count);
281         ext2fs_close(fs);
282         exit(FSCK_OK);
283 }
284
285 /*
286  * For completion notice
287  */
288 struct percent_tbl {
289         int     max_pass;
290         int     table[32];
291 };
292 struct percent_tbl e2fsck_tbl = {
293         5, { 0, 70, 90, 92,  95, 100 }
294 };
295 static char bar[] =
296         "==============================================================="
297         "===============================================================";
298 static char spaces[] =
299         "                                                               "
300         "                                                               ";
301
302 static float calc_percent(struct percent_tbl *tbl, int pass, int curr,
303                           int max)
304 {
305         float   percent;
306         
307         if (pass <= 0)
308                 return 0.0;
309         if (pass > tbl->max_pass || max == 0)
310                 return 100.0;
311         percent = ((float) curr) / ((float) max);
312         return ((percent * (tbl->table[pass] - tbl->table[pass-1]))
313                 + tbl->table[pass-1]);
314 }
315
316 extern void e2fsck_clear_progbar(e2fsck_t ctx)
317 {
318         if (!(ctx->flags & E2F_FLAG_PROG_BAR))
319                 return;
320         
321         printf("%s\r", spaces + (sizeof(spaces) - 80));
322         ctx->flags &= ~E2F_FLAG_PROG_BAR;
323 }
324
325 static int e2fsck_update_progress(e2fsck_t ctx, int pass,
326                                   unsigned long cur, unsigned long max)
327 {
328         static const char spinner[] = "\\|/-";
329         char buf[80];
330         int     i;
331         float percent;
332         int     tick;
333         struct timeval  tv;
334         static int dpywidth = 0;
335
336         if (pass == 0)
337                 return 0;
338         
339         if (ctx->progress_fd) {
340                 sprintf(buf, "%d %lu %lu\n", pass, cur, max);
341                 write(ctx->progress_fd, buf, strlen(buf));
342         } else {
343                 if (ctx->flags & E2F_FLAG_PROG_SUPPRESS)
344                         return 0;
345                 if (dpywidth == 0) {
346                         dpywidth = 66 - strlen(ctx->device_name);
347                         dpywidth = 8 * (dpywidth / 8);
348                 }
349                 /*
350                  * Calculate the new progress position.  If the
351                  * percentage hasn't changed, then we skip out right
352                  * away. 
353                  */
354                 percent = calc_percent(&e2fsck_tbl, pass, cur, max);
355                 if (ctx->progress_last_percent == (int) 10 * percent)
356                         return 0;
357                 ctx->progress_last_percent = (int) 10 * percent;
358
359                 /*
360                  * If we've already updated the spinner once within
361                  * the last 1/8th of a second, no point doing it
362                  * again.
363                  */
364                 gettimeofday(&tv, NULL);
365                 tick = (tv.tv_sec << 3) + (tv.tv_usec / (1000000 / 8));
366                 if ((tick == ctx->progress_last_time) &&
367                     (cur != max) && (cur != 0))
368                         return 0;
369                 ctx->progress_last_time = tick;
370
371                 /*
372                  * Advance the spinner, and note that the progress bar
373                  * will be on the screen
374                  */
375                 ctx->progress_pos = (ctx->progress_pos+1) & 3;
376                 ctx->flags |= E2F_FLAG_PROG_BAR;
377                 
378                 i = ((percent * dpywidth) + 50) / 100;
379                 printf("%s: |%s%s", ctx->device_name,
380                        bar + (sizeof(bar) - (i+1)),
381                        spaces + (sizeof(spaces) - (dpywidth - i + 1)));
382                 if (percent == 100.0)
383                         fputc('|', stdout);
384                 else
385                         fputc(spinner[ctx->progress_pos & 3], stdout);
386                 printf(" %4.1f%%   \r", percent);
387                 if (percent == 100.0)
388                         e2fsck_clear_progbar(ctx);
389                 fflush(stdout);
390         }
391         return 0;
392 }
393
394 #define PATH_SET "PATH=/sbin"
395
396 static void reserve_stdio_fds(void)
397 {
398         int     fd;
399
400         while (1) {
401                 fd = open("/dev/null", O_RDWR);
402                 if (fd > 2)
403                         break;
404                 if (fd < 0) {
405                         fprintf(stderr, _("ERROR: Couldn't open "
406                                 "/dev/null (%s)\n"),
407                                 strerror(errno));
408                         break;
409                 }
410         }
411         close(fd);
412 }
413
414 #ifdef HAVE_SIGNAL_H
415 static void signal_progress_on(int sig)
416 {
417         e2fsck_t ctx = e2fsck_global_ctx;
418
419         if (!ctx)
420                 return;
421
422         ctx->progress = e2fsck_update_progress;
423         ctx->progress_fd = 0;
424 }
425
426 static void signal_progress_off(int sig)
427 {
428         e2fsck_t ctx = e2fsck_global_ctx;
429
430         if (!ctx)
431                 return;
432
433         e2fsck_clear_progbar(ctx);
434         ctx->progress = 0;
435 }
436 #endif
437
438 static errcode_t PRS(int argc, char *argv[], e2fsck_t *ret_ctx)
439 {
440         int             flush = 0;
441         int             c;
442 #ifdef MTRACE
443         extern void     *mallwatch;
444 #endif
445         char            *oldpath = getenv("PATH");
446         e2fsck_t        ctx;
447         errcode_t       retval;
448 #ifdef HAVE_SIGNAL_H
449         struct sigaction        sa;
450 #endif
451
452         retval = e2fsck_allocate_context(&ctx);
453         if (retval)
454                 return retval;
455
456         *ret_ctx = ctx;
457
458         /* Update our PATH to include /sbin  */
459         if (oldpath) {
460                 char *newpath;
461
462                 newpath = (char *) malloc(sizeof (PATH_SET) + 1 +
463                                           strlen (oldpath));
464                 if (!newpath)
465                         fatal_error(ctx, "Couldn't malloc() newpath");
466                 strcpy (newpath, PATH_SET);
467                 strcat (newpath, ":");
468                 strcat (newpath, oldpath);
469                 putenv (newpath);
470         } else
471                 putenv (PATH_SET);
472
473         setbuf(stdout, NULL);
474         setbuf(stderr, NULL);
475         initialize_ext2_error_table();
476         
477         if (argc && *argv)
478                 ctx->program_name = *argv;
479         else
480                 ctx->program_name = "e2fsck";
481         while ((c = getopt (argc, argv, "panyrcC:B:dfvtFVM:b:I:P:l:L:N:Ss")) != EOF)
482                 switch (c) {
483                 case 'C':
484                         ctx->progress = e2fsck_update_progress;
485                         ctx->progress_fd = atoi(optarg);
486                         break;
487                 case 'p':
488                 case 'a':
489                         ctx->options |= E2F_OPT_PREEN;
490                         ctx->options &= ~(E2F_OPT_YES|E2F_OPT_NO);
491                         break;
492                 case 'n':
493                         ctx->options |= E2F_OPT_NO;
494                         ctx->options &= ~(E2F_OPT_YES|E2F_OPT_PREEN);
495                         break;
496                 case 'y':
497                         ctx->options |= E2F_OPT_YES;
498                         ctx->options &= ~(E2F_OPT_PREEN|E2F_OPT_NO);
499                         break;
500                 case 't':
501 #ifdef RESOURCE_TRACK
502                         if (ctx->options & E2F_OPT_TIME)
503                                 ctx->options |= E2F_OPT_TIME2;
504                         else
505                                 ctx->options |= E2F_OPT_TIME;
506 #else
507                         fprintf(stderr, _("The -t option is not "
508                                 "supported on this version of e2fsck.\n"));
509 #endif
510                         break;
511                 case 'c':
512                         cflag++;
513                         ctx->options |= E2F_OPT_CHECKBLOCKS;
514                         break;
515                 case 'r':
516                         /* What we do by default, anyway! */
517                         break;
518                 case 'b':
519                         ctx->use_superblock = atoi(optarg);
520                         break;
521                 case 'B':
522                         blocksize = atoi(optarg);
523                         break;
524                 case 'I':
525                         ctx->inode_buffer_blocks = atoi(optarg);
526                         break;
527                 case 'P':
528                         ctx->process_inode_size = atoi(optarg);
529                         break;
530                 case 'L':
531                         replace_bad_blocks++;
532                 case 'l':
533                         bad_blocks_file = (char *) malloc(strlen(optarg)+1);
534                         if (!bad_blocks_file)
535                                 fatal_error(ctx,
536                                             "Couldn't malloc bad_blocks_file");
537                         strcpy(bad_blocks_file, optarg);
538                         break;
539                 case 'd':
540                         ctx->options |= E2F_OPT_DEBUG;
541                         break;
542                 case 'f':
543                         force = 1;
544                         break;
545                 case 'F':
546                         flush = 1;
547                         break;
548                 case 'v':
549                         verbose = 1;
550                         break;
551                 case 'V':
552                         show_version_only = 1;
553                         break;
554 #ifdef MTRACE
555                 case 'M':
556                         mallwatch = (void *) strtol(optarg, NULL, 0);
557                         break;
558 #endif
559                 case 'N':
560                         ctx->device_name = optarg;
561                         break;
562                 case 's':
563                         normalize_swapfs = 1;
564                 case 'S':
565                         swapfs = 1;
566                         break;
567                 default:
568                         usage(ctx);
569                 }
570         if (show_version_only)
571                 return 0;
572         if (optind != argc - 1)
573                 usage(ctx);
574         if ((ctx->options & E2F_OPT_NO) && !bad_blocks_file &&
575             !cflag && !swapfs)
576                 ctx->options |= E2F_OPT_READONLY;
577         ctx->filesystem_name = argv[optind];
578         if (flush) {
579                 int     fd = open(ctx->filesystem_name, O_RDONLY, 0);
580
581                 if (fd < 0) {
582                         com_err("open", errno,
583                                 _("while opening %s for flushing"),
584                                 ctx->filesystem_name);
585                         fatal_error(ctx, 0);
586                 }
587                 if ((retval == ext2fs_sync_device(fd, 1))) {
588                         com_err("ext2fs_sync_device", retval,
589                                 _("while trying to flush %s"),
590                                 ctx->filesystem_name);
591                         fatal_error(ctx, 0);
592                 }
593                 close(fd);
594         }
595         if (swapfs) {
596                 if (cflag || bad_blocks_file) {
597                         fprintf(stderr, _("Incompatible options not "
598                                 "allowed when byte-swapping.\n"));
599                         exit(FSCK_USAGE);
600                 }
601         }
602 #ifdef HAVE_SIGNAL_H
603         /*
604          * Set up signal action
605          */
606         memset(&sa, 0, sizeof(struct sigaction));
607 #ifdef SA_RESTART
608         sa.sa_flags = SA_RESTART;
609 #endif
610         e2fsck_global_ctx = ctx;
611         sa.sa_handler = signal_progress_on;
612         sigaction(SIGUSR1, &sa, 0);
613         sa.sa_handler = signal_progress_off;
614         sigaction(SIGUSR2, &sa, 0);
615 #endif
616         return 0;
617 }
618
619 static const char *my_ver_string = E2FSPROGS_VERSION;
620 static const char *my_ver_date = E2FSPROGS_DATE;
621                                         
622 int main (int argc, char *argv[])
623 {
624         errcode_t       retval = 0;
625         int             exit_value = FSCK_OK;
626         int             i;
627         ext2_filsys     fs = 0;
628         io_manager      io_ptr;
629         struct ext2_super_block *sb;
630         const char      *lib_ver_date;
631         int             my_ver, lib_ver;
632         e2fsck_t        ctx;
633         struct problem_context pctx;
634         int flags, run_result;
635         
636         clear_problem_context(&pctx);
637 #ifdef MTRACE
638         mtrace();
639 #endif
640 #ifdef MCHECK
641         mcheck(0);
642 #endif
643 #ifdef ENABLE_NLS
644         setlocale(LC_MESSAGES, "");
645         bindtextdomain(NLS_CAT_NAME, LOCALEDIR);
646         textdomain(NLS_CAT_NAME);
647 #endif
648         my_ver = ext2fs_parse_version_string(my_ver_string);
649         lib_ver = ext2fs_get_library_version(0, &lib_ver_date);
650         if (my_ver > lib_ver) {
651                 fprintf( stderr, _("Error: ext2fs library version "
652                         "out of date!\n"));
653                 show_version_only++;
654         }
655         
656         retval = PRS(argc, argv, &ctx);
657         if (retval) {
658                 com_err("e2fsck", retval,
659                         _("while trying to initialize program"));
660                 exit(FSCK_ERROR);
661         }
662         reserve_stdio_fds();
663         
664 #ifdef RESOURCE_TRACK
665         init_resource_track(&ctx->global_rtrack);
666 #endif
667
668         if (!(ctx->options & E2F_OPT_PREEN) || show_version_only)
669                 fprintf (stderr, _("e2fsck %s, %s for EXT2 FS %s, %s\n"),
670                          my_ver_string, my_ver_date, EXT2FS_VERSION,
671                          EXT2FS_DATE);
672
673         if (show_version_only) {
674                 fprintf(stderr, _("\tUsing %s, %s\n"),
675                         error_message(EXT2_ET_BASE), lib_ver_date);
676                 exit(FSCK_OK);
677         }
678         
679         check_mount(ctx);
680         
681         if (!(ctx->options & E2F_OPT_PREEN) &&
682             !(ctx->options & E2F_OPT_NO) &&
683             !(ctx->options & E2F_OPT_YES)) {
684                 if (!isatty (0) || !isatty (1))
685                         fatal_error(ctx,
686                                     _("need terminal for interactive repairs"));
687         }
688         ctx->superblock = ctx->use_superblock;
689 restart:
690 #if 1
691         io_ptr = unix_io_manager;
692 #else
693         io_ptr = test_io_manager;
694         test_io_backing_manager = unix_io_manager;
695 #endif
696         flags = 0;
697         if ((ctx->options & E2F_OPT_READONLY) == 0)
698                 flags |= EXT2_FLAG_RW;
699
700         if (ctx->superblock && blocksize) {
701                 retval = ext2fs_open(ctx->filesystem_name, flags,
702                                      ctx->superblock, blocksize, io_ptr, &fs);
703         } else if (ctx->superblock) {
704                 for (i=0; possible_block_sizes[i]; i++) {
705                         retval = ext2fs_open(ctx->filesystem_name, flags,
706                                              ctx->superblock,
707                                              possible_block_sizes[i],
708                                              io_ptr, &fs);
709                         if (!retval)
710                                 break;
711                 }
712         } else 
713                 retval = ext2fs_open(ctx->filesystem_name, flags, 
714                                      0, 0, io_ptr, &fs);
715         if (!ctx->superblock && !(ctx->options & E2F_OPT_PREEN) && 
716             ((retval == EXT2_ET_BAD_MAGIC) ||
717              ((retval == 0) && ext2fs_check_desc(fs)))) {
718                 if (!fs || (fs->group_desc_count > 1)) {
719                         printf(_("%s trying backup blocks...\n"),
720                                retval ? _("Couldn't find ext2 superblock,") :
721                                _("Group descriptors look bad..."));
722                         ctx->superblock = get_backup_sb(fs);
723                         if (fs)
724                                 ext2fs_close(fs);
725                         goto restart;
726                 }
727         }
728         if (retval) {
729                 com_err(ctx->program_name, retval, _("while trying to open %s"),
730                         ctx->filesystem_name);
731                 if (retval == EXT2_ET_REV_TOO_HIGH) {
732                         printf(_("The filesystem revision is apparently "
733                                "too high for this version of e2fsck.\n"
734                                "(Or the filesystem superblock "
735                                "is corrupt)\n\n"));
736                         fix_problem(ctx, PR_0_SB_CORRUPT, &pctx);
737                 } else if (retval == EXT2_ET_SHORT_READ)
738                         printf(_("Could this be a zero-length partition?\n"));
739                 else if ((retval == EPERM) || (retval == EACCES))
740                         printf(_("You must have %s access to the "
741                                "filesystem or be root\n"),
742                                (ctx->options & E2F_OPT_READONLY) ?
743                                "r/o" : "r/w");
744                 else if (retval == ENXIO)
745                         printf(_("Possibly non-existent or swap device?\n"));
746 #ifdef EROFS
747                 else if (retval == EROFS)
748                         printf(_("Disk write-protected; use the -n option "
749                                "to do a read-only\n"
750                                "check of the device.\n"));
751 #endif
752                 else
753                         fix_problem(ctx, PR_0_SB_CORRUPT, &pctx);
754                 fatal_error(ctx, 0);
755         }
756         ctx->fs = fs;
757         fs->priv_data = ctx;
758         sb = fs->super;
759         if (sb->s_rev_level > E2FSCK_CURRENT_REV) {
760                 com_err(ctx->program_name, EXT2_ET_REV_TOO_HIGH,
761                         _("while trying to open %s"),
762                         ctx->filesystem_name);
763         get_newer:
764                 fatal_error(ctx, _("Get a newer version of e2fsck!"));
765         }
766
767         /*
768          * Set the device name, which is used whenever we print error
769          * or informational messages to the user.
770          */
771         if (ctx->device_name == 0 &&
772             (sb->s_volume_name[0] != 0)) {
773                 char *cp = malloc(sizeof(sb->s_volume_name)+1);
774                 if (cp) {
775                         strncpy(cp, sb->s_volume_name,
776                                 sizeof(sb->s_volume_name));
777                         cp[sizeof(sb->s_volume_name)] = 0;
778                         ctx->device_name = cp;
779                 }
780         }
781         if (ctx->device_name == 0)
782                 ctx->device_name = ctx->filesystem_name;
783
784         /*
785          * Make sure the ext3 superblock fields are consistent.
786          */
787         retval = e2fsck_check_ext3_journal(ctx);
788         if (retval) {
789                 com_err(ctx->program_name, retval,
790                         _("while checking ext3 journal for %s"),
791                         ctx->device_name);
792                 ext2fs_close(ctx->fs);
793                 fatal_error(ctx, 0);
794         }
795
796         /*
797          * Check to see if we need to do ext3-style recovery.  If so,
798          * do it, and then restart the fsck.
799          */
800         if (sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER) {
801                 if (ctx->options & E2F_OPT_READONLY) {
802                         printf(_("Warning: skipping journal recovery "
803                                  "because doing a read-only filesystem "
804                                  "check.\n"));
805                         io_channel_flush(ctx->fs->io);
806                 } else {
807                         retval = e2fsck_run_ext3_journal(ctx);
808                         if (retval) {
809                                 com_err(ctx->program_name, retval,
810                                 _("while recovering ext3 journal of %s"),
811                                         ctx->device_name);
812                                 fatal_error(ctx, 0);
813                         }
814                         ext2fs_close(ctx->fs);
815                         ctx->fs = 0;
816                         goto restart;
817                 }
818         }
819
820         /*
821          * Check for compatibility with the feature sets.  We need to
822          * be more stringent than ext2fs_open().
823          */
824         if ((sb->s_feature_compat & ~EXT2_LIB_FEATURE_COMPAT_SUPP) ||
825             (sb->s_feature_incompat & ~EXT2_LIB_FEATURE_INCOMPAT_SUPP)) {
826                 com_err(ctx->program_name, EXT2_ET_UNSUPP_FEATURE,
827                         "(%s)", ctx->device_name);
828                 goto get_newer;
829         }
830         if (sb->s_feature_ro_compat & ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP) {
831                 com_err(ctx->program_name, EXT2_ET_RO_UNSUPP_FEATURE,
832                         "(%s)", ctx->device_name);
833                 goto get_newer;
834         }
835 #ifdef ENABLE_COMPRESSION
836         if (sb->s_feature_incompat & EXT2_FEATURE_INCOMPAT_COMPRESSION)
837                 com_err(ctx->program_name, 0,
838                         _("Warning: compression support is experimental.\n"));
839 #endif
840         
841         /*
842          * If the user specified a specific superblock, presumably the
843          * master superblock has been trashed.  So we mark the
844          * superblock as dirty, so it can be written out.
845          */
846         if (ctx->superblock &&
847             !(ctx->options & E2F_OPT_READONLY))
848                 ext2fs_mark_super_dirty(fs);
849
850         /*
851          * Don't overwrite the backup superblock and block
852          * descriptors, until we're sure the filesystem is OK....
853          */
854         fs->flags |= EXT2_FLAG_MASTER_SB_ONLY;
855
856         ehandler_init(fs->io);
857
858         if (ctx->superblock)
859                 set_latch_flags(PR_LATCH_RELOC, PRL_LATCHED, 0);
860         check_super_block(ctx);
861         if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
862                 fatal_error(ctx, 0);
863         check_if_skip(ctx);
864         if (bad_blocks_file)
865                 read_bad_blocks_file(ctx, bad_blocks_file, replace_bad_blocks);
866         else if (cflag)
867                 test_disk(ctx);
868         if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
869                 fatal_error(ctx, 0);
870
871         if (normalize_swapfs) {
872                 if ((fs->flags & EXT2_FLAG_SWAP_BYTES) ==
873                     ext2fs_native_flag()) {
874                         fprintf(stderr, _("%s: Filesystem byte order "
875                                 "already normalized.\n"), ctx->device_name);
876                         fatal_error(ctx, 0);
877                 }
878         }
879         if (swapfs) {
880                 swap_filesys(ctx);
881                 if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
882                         fatal_error(ctx, 0);
883         }
884
885         /*
886          * Mark the system as valid, 'til proven otherwise
887          */
888         ext2fs_mark_valid(fs);
889
890         retval = ext2fs_read_bb_inode(fs, &fs->badblocks);
891         if (retval) {
892                 com_err(ctx->program_name, retval,
893                         _("while reading bad blocks inode"));
894                 preenhalt(ctx);
895                 printf(_("This doesn't bode well,"
896                          " but we'll try to go on...\n"));
897         }
898
899         run_result = e2fsck_run(ctx);
900         e2fsck_clear_progbar(ctx);
901         if (run_result == E2F_FLAG_RESTART) {
902                 printf(_("Restarting e2fsck from the beginning...\n"));
903                 retval = e2fsck_reset_context(ctx);
904                 if (retval) {
905                         com_err(ctx->program_name, retval,
906                                 _("while resetting context"));
907                         fatal_error(ctx, 0);
908                 }
909                 ext2fs_close(fs);
910                 goto restart;
911         }
912         if (run_result & E2F_FLAG_SIGNAL_MASK)
913                 fatal_error(ctx, 0);
914         if (run_result & E2F_FLAG_CANCEL)
915                 ext2fs_unmark_valid(fs);
916
917 #ifdef MTRACE
918         mtrace_print("Cleanup");
919 #endif
920         if (ext2fs_test_changed(fs)) {
921                 exit_value = FSCK_NONDESTRUCT;
922                 if (!(ctx->options & E2F_OPT_PREEN))
923                     printf(_("\n%s: ***** FILE SYSTEM WAS MODIFIED *****\n"),
924                                ctx->device_name);
925                 if (root_filesystem && !read_only_root) {
926                         printf(_("%s: ***** REBOOT LINUX *****\n"),
927                                ctx->device_name);
928                         exit_value = FSCK_REBOOT;
929                 }
930         }
931         if (ext2fs_test_valid(fs))
932                 fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
933         else
934                 exit_value = FSCK_UNCORRECTED;
935         if (!(ctx->options & E2F_OPT_READONLY)) {
936                 if (ext2fs_test_valid(fs)) {
937                         if (!(sb->s_state & EXT2_VALID_FS))
938                                 exit_value = FSCK_NONDESTRUCT;
939                         sb->s_state = EXT2_VALID_FS;
940                 } else
941                         sb->s_state &= ~EXT2_VALID_FS;
942                 sb->s_mnt_count = 0;
943                 sb->s_lastcheck = time(NULL);
944                 ext2fs_mark_super_dirty(fs);
945         }
946         show_stats(ctx);
947
948         e2fsck_write_bitmaps(ctx);
949         
950 #ifdef RESOURCE_TRACK
951         if (ctx->options & E2F_OPT_TIME)
952                 print_resource_track(NULL, &ctx->global_rtrack);
953 #endif
954
955         ext2fs_close(fs);
956         ctx->fs = NULL;
957         e2fsck_free_context(ctx);
958         
959         return exit_value;
960 }