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