Whamcloud - gitweb
Make e2fsck uninit block group aware
[tools/e2fsprogs.git] / e2fsck / unix.c
index 43a235f..7b662e4 100644 (file)
@@ -33,8 +33,18 @@ extern int optind;
 #ifdef HAVE_MNTENT_H
 #include <mntent.h>
 #endif
+#ifdef HAVE_SYS_IOCTL_H
 #include <sys/ioctl.h>
+#endif
+#ifdef HAVE_MALLOC_H
 #include <malloc.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_DIRENT_H
+#include <dirent.h>
+#endif
 
 #include "et/com_err.h"
 #include "e2fsck.h"
@@ -42,41 +52,40 @@ extern int optind;
 #include "../version.h"
 
 /* Command line options */
-static int swapfs = 0;
-static int normalize_swapfs = 0;
-static int cflag = 0;          /* check disk */
-static int show_version_only = 0;
-static int verbose = 0;
+static int cflag;              /* check disk */
+static int show_version_only;
+static int verbose;
 
-static int replace_bad_blocks = 0;
-static char *bad_blocks_file = 0;
-
-static int possible_block_sizes[] = { 1024, 2048, 4096, 8192, 0};
-
-static int root_filesystem = 0;
-static int read_only_root = 0;
+static int replace_bad_blocks;
+static int keep_bad_blocks;
+static char *bad_blocks_file;
 
 e2fsck_t e2fsck_global_ctx;    /* Try your very best not to use this! */
 
+#ifdef CONFIG_JBD_DEBUG                /* Enabled by configure --enable-jfs-debug */
+int journal_enable_debug = -1;
+#endif
+
 static void usage(e2fsck_t ctx)
 {
        fprintf(stderr,
-               _("Usage: %s [-panyrcdfvstFSV] [-b superblock] [-B blocksize]\n"
+               _("Usage: %s [-panyrcdfvtDFV] [-b superblock] [-B blocksize]\n"
                "\t\t[-I inode_buffer_blocks] [-P process_inode_size]\n"
-               "\t\t[-l|-L bad_blocks_file] [-C fd] [-j ext-journal] device\n"),
+               "\t\t[-l|-L bad_blocks_file] [-C fd] [-j external_journal]\n"
+               "\t\t[-E extended-options] device\n"),
                ctx->program_name);
 
        fprintf(stderr, _("\nEmergency help:\n"
                " -p                   Automatic repair (no questions)\n"
                " -n                   Make no changes to the filesystem\n"
                " -y                   Assume \"yes\" to all questions\n"
-               " -c                   Check for bad blocks\n"
+               " -c                   Check for bad blocks and add them to the badblock list\n"
                " -f                   Force checking even if filesystem is marked clean\n"));
        fprintf(stderr, _(""
                " -v                   Be verbose\n"
                " -b superblock        Use alternative superblock\n"
                " -B blocksize         Force blocksize when looking for superblock\n"
-               " -j external-journal  Set location of the external journal\n"
+               " -j external_journal  Set location of the external journal\n"
                " -l bad_blocks_file   Add to badblocks list\n"
                " -L bad_blocks_file   Set badblocks list\n"
                ));
@@ -87,7 +96,8 @@ static void usage(e2fsck_t ctx)
 static void show_stats(e2fsck_t        ctx)
 {
        ext2_filsys fs = ctx->fs;
-       int inodes, inodes_used, blocks, blocks_used;
+       ext2_ino_t inodes, inodes_used;
+       blk_t blocks, blocks_used;
        int dir_links;
        int num_files, num_links;
        int frag_percent;
@@ -106,105 +116,59 @@ static void show_stats(e2fsck_t  ctx)
        frag_percent = (frag_percent + 5) / 10;
        
        if (!verbose) {
-               printf(_("%s: %d/%d files (%0d.%d%% non-contiguous), %d/%d blocks\n"),
+               printf(_("%s: %u/%u files (%0d.%d%% non-contiguous), %u/%u blocks\n"),
                       ctx->device_name, inodes_used, inodes,
                       frag_percent / 10, frag_percent % 10,
                       blocks_used, blocks);
                return;
        }
-       /*
-        * This is a bit ugly. But I think there will nearly always be more
-        * than one "thing" to report about, so I won't try writing complex
-        * code to handle one/two/many forms of all words.
-        * Some languages (Italian, at least) never uses the plural form
-        * of foreign words, so in real life this could not be a problem.
-        * md@linux.it - 2000-1-15
-        */
-#ifdef ENABLE_NLS
-       printf (_("\n%8d inodes used (%d%%)\n"), inodes_used,
-               (inodes_used != 1), 100 * inodes_used / inodes);
-       printf (_("%8d non-contiguous inodes (%0d.%d%%)\n"),
+       printf (P_("\n%8u inode used (%2.2f%%)\n", "\n%8u inodes used (%2.2f%%)\n",
+                  inodes_used), inodes_used, 100.0 * inodes_used / inodes);
+       printf (P_("%8u non-contiguous inode (%0d.%d%%)\n",
+                  "%8u non-contiguous inodes (%0d.%d%%)\n",
+                  ctx->fs_fragmented),
                ctx->fs_fragmented, frag_percent / 10, frag_percent % 10);
-       printf (_("         # of inodes with ind/dind/tind blocks: %d/%d/%d\n"),
+       printf (_("         # of inodes with ind/dind/tind blocks: %u/%u/%u\n"),
                ctx->fs_ind_count, ctx->fs_dind_count, ctx->fs_tind_count);
-       printf (_("%8d blocks used (%d%%)\n"
-               "%8d bad blocks\n"), blocks_used,
-               (int) ((long long) 100 * blocks_used / blocks),
-               ctx->fs_badblocks_count);
-       printf(_("%8d large files\n"), ctx->large_files);
-       printf (_("\n%8d regular files\n"
-               "%8d directories\n"
-               "%8d character device files\n"
-               "%8d block device files\n"
-               "%8d fifos\n"
-               "%8d links\n"
-               "%8d symbolic links (%d fast symbolic links)\n"
-               "%8d sockets\n"
-               "--------\n"
-               "%8d files\n"),
-               ctx->fs_regular_count,
-               ctx->fs_directory_count,
-               ctx->fs_chardev_count,
-               ctx->fs_blockdev_count,
-               ctx->fs_fifo_count,
-               ctx->fs_links_count - dir_links,
-               ctx->fs_symlinks_count,
-               ctx->fs_fast_symlinks_count,
-               ctx->fs_sockets_count,
+       printf (P_("%8u block used (%2.2f%%)\n", "%8u blocks used (%2.2f%%)\n",
+                  blocks_used), blocks_used, 100.0 * blocks_used / blocks);
+       printf (P_("%8u bad block\n", "%8u bad blocks\n",
+                  ctx->fs_badblocks_count), ctx->fs_badblocks_count);
+       printf (P_("%8u large file\n", "%8u large files\n",
+                  ctx->large_files), ctx->large_files);
+       printf (P_("\n%8u regular file\n", "\n%8u regular files\n",
+                  ctx->fs_regular_count), ctx->fs_regular_count);
+       printf (P_("%8u directory\n", "%8u directories\n",
+                  ctx->fs_directory_count), ctx->fs_directory_count);
+       printf (P_("%8u character device file\n",
+                  "%8u character device files\n", ctx->fs_chardev_count),
+               ctx->fs_chardev_count);
+       printf (P_("%8u block device file\n", "%8u block device files\n",
+                  ctx->fs_blockdev_count), ctx->fs_blockdev_count);
+       printf (P_("%8u fifo\n", "%8u fifos\n", ctx->fs_fifo_count),
+               ctx->fs_fifo_count);
+       printf (P_("%8u link\n", "%8u links\n",
+                  ctx->fs_links_count - dir_links),
+               ctx->fs_links_count - dir_links);
+       printf (P_("%8u symbolic link", "%8u symbolic links",
+                  ctx->fs_symlinks_count), ctx->fs_symlinks_count);
+       printf (P_(" (%u fast symbolic link)\n", " (%u fast symbolic links)\n",
+                  ctx->fs_fast_symlinks_count), ctx->fs_fast_symlinks_count);
+       printf (P_("%8u socket\n", "%8u sockets\n", ctx->fs_sockets_count),
+               ctx->fs_sockets_count);
+       printf ("--------\n");
+       printf (P_("%8u file\n", "%8u files\n",
+                  ctx->fs_total_count - dir_links),
                ctx->fs_total_count - dir_links);
-#else
-       printf ("\n%8d inode%s used (%d%%)\n", inodes_used,
-               (inodes_used != 1) ? "s" : "",
-               100 * inodes_used / inodes);
-       printf ("%8d non-contiguous inodes (%0d.%d%%)\n",
-               ctx->fs_fragmented, frag_percent / 10, frag_percent % 10);
-       printf ("         # of inodes with ind/dind/tind blocks: %d/%d/%d\n",
-               ctx->fs_ind_count, ctx->fs_dind_count, ctx->fs_tind_count);
-       printf ("%8d block%s used (%d%%)\n"
-               "%8d bad block%s\n", blocks_used,
-               (blocks_used != 1) ? "s" : "",
-               100 * blocks_used / blocks, ctx->fs_badblocks_count,
-               ctx->fs_badblocks_count != 1 ? "s" : "");
-       printf(_("%8d large file%s\n"), ctx->large_files,
-              (ctx->large_files != 1) ? "s" : "");
-       printf ("\n%8d regular file%s\n"
-               "%8d director%s\n"
-               "%8d character device file%s\n"
-               "%8d block device file%s\n"
-               "%8d fifo%s\n"
-               "%8d link%s\n"
-               "%8d symbolic link%s (%d fast symbolic link%s)\n"
-               "%8d socket%s\n"
-               "--------\n"
-               "%8d file%s\n",
-               ctx->fs_regular_count,
-               (ctx->fs_regular_count != 1) ? "s" : "",
-               ctx->fs_directory_count,
-               (ctx->fs_directory_count != 1) ? "ies" : "y",
-               ctx->fs_chardev_count,
-               (ctx->fs_chardev_count != 1) ? "s" : "",
-               ctx->fs_blockdev_count,
-               (ctx->fs_blockdev_count != 1) ? "s" : "",
-               ctx->fs_fifo_count,
-               (ctx->fs_fifo_count != 1) ? "s" : "",
-               ctx->fs_links_count - dir_links,
-               ((ctx->fs_links_count - dir_links) != 1) ? "s" : "",
-               ctx->fs_symlinks_count,
-               (ctx->fs_symlinks_count != 1) ? "s" : "",
-               ctx->fs_fast_symlinks_count,
-               (ctx->fs_fast_symlinks_count != 1) ? "s" : "",
-               ctx->fs_sockets_count, (ctx->fs_sockets_count != 1) ? "s" : "",
-               ctx->fs_total_count - dir_links,
-               ((ctx->fs_total_count - dir_links) != 1) ? "s" : "");
-#endif
 }
 
 static void check_mount(e2fsck_t ctx)
 {
        errcode_t       retval;
-       int             mount_flags, cont;
+       int             cont;
 
-       retval = ext2fs_check_if_mounted(ctx->filesystem_name, &mount_flags);
+       retval = ext2fs_check_if_mounted(ctx->filesystem_name,
+                                        &ctx->mount_flags);
        if (retval) {
                com_err("ext2fs_check_if_mount", retval,
                        _("while determining whether %s is mounted."),
@@ -213,21 +177,24 @@ static void check_mount(e2fsck_t ctx)
        }
 
        /*
-        * If the filesystem isn't mounted, or it's the root filesystem
-        * and it's mounted read-only, then everything's fine.
+        * If the filesystem isn't mounted, or it's the root
+        * filesystem and it's mounted read-only, and we're not doing
+        * a read/write check, then everything's fine.
         */
-       if ((!(mount_flags & EXT2_MF_MOUNTED)) ||
-           ((mount_flags & EXT2_MF_ISROOT) &&
-            (mount_flags & EXT2_MF_READONLY)))
+       if ((!(ctx->mount_flags & EXT2_MF_MOUNTED)) ||
+           ((ctx->mount_flags & EXT2_MF_ISROOT) &&
+            (ctx->mount_flags & EXT2_MF_READONLY) &&
+            !(ctx->options & E2F_OPT_WRITECHECK)))
                return;
 
-       if (ctx->options & E2F_OPT_READONLY) {
+       if ((ctx->options & E2F_OPT_READONLY) &&
+           !(ctx->options & E2F_OPT_WRITECHECK)) {
                printf(_("Warning!  %s is mounted.\n"), ctx->filesystem_name);
                return;
        }
 
        printf(_("%s is mounted.  "), ctx->filesystem_name);
-       if (!isatty(0) || !isatty(1))
+       if (!ctx->interactive)
                fatal_error(ctx, _("Cannot continue, aborting.\n\n"));
        printf(_("\n\n\007\007\007\007WARNING!!!  "
               "Running e2fsck on a mounted filesystem may cause\n"
@@ -240,6 +207,44 @@ static void check_mount(e2fsck_t ctx)
        return;
 }
 
+static int is_on_batt(void)
+{
+       FILE    *f;
+       DIR     *d;
+       char    tmp[80], tmp2[80], fname[80];
+       unsigned int    acflag;
+       struct dirent*  de;
+
+       f = fopen("/proc/apm", "r");
+       if (f) {
+               if (fscanf(f, "%s %s %s %x", tmp, tmp, tmp, &acflag) != 4) 
+                       acflag = 1;
+               fclose(f);
+               return (acflag != 1);
+       }
+       d = opendir("/proc/acpi/ac_adapter");
+       if (d) {
+               while ((de=readdir(d)) != NULL) {
+                       if (!strncmp(".", de->d_name, 1))
+                               continue;
+                       snprintf(fname, 80, "/proc/acpi/ac_adapter/%s/state", 
+                                de->d_name);
+                       f = fopen(fname, "r");
+                       if (!f)
+                               continue;
+                       if (fscanf(f, "%s %s", tmp2, tmp) != 2)
+                               tmp[0] = 0;
+                       fclose(f);
+                       if (strncmp(tmp, "off-line", 8) == 0) {
+                               closedir(d);
+                               return 1;
+                       }
+               }
+               closedir(d);
+       }
+       return 0;
+}
+
 /*
  * This routine checks to see if a filesystem can be skipped; if so,
  * it will exit with E2FSCK_OK.  Under some conditions it will print a
@@ -250,25 +255,45 @@ static void check_if_skip(e2fsck_t ctx)
        ext2_filsys fs = ctx->fs;
        const char *reason = NULL;
        unsigned int reason_arg = 0;
-       
-       if ((ctx->options & E2F_OPT_FORCE) || bad_blocks_file ||
-           cflag || swapfs)
+       long next_check;
+       int batt = is_on_batt();
+       int defer_check_on_battery;
+       time_t lastcheck;
+
+       profile_get_boolean(ctx->profile, "options",
+                           "defer_check_on_battery", 0, 1, 
+                           &defer_check_on_battery);
+       if (!defer_check_on_battery)
+               batt = 0;
+
+       if ((ctx->options & E2F_OPT_FORCE) || bad_blocks_file || cflag)
                return;
        
-       if (fs->super->s_state & EXT2_ERROR_FS)
+       lastcheck = fs->super->s_lastcheck;
+       if (lastcheck > ctx->now)
+               lastcheck -= ctx->time_fudge;
+       if ((fs->super->s_state & EXT2_ERROR_FS) ||
+           !ext2fs_test_valid(fs))
                reason = _(" contains a file system with errors");
        else if ((fs->super->s_state & EXT2_VALID_FS) == 0)
                reason = _(" was not cleanly unmounted");
+       else if (check_backup_super_block(ctx))
+               reason = _(" primary superblock features different from backup");
        else if ((fs->super->s_max_mnt_count > 0) &&
                 (fs->super->s_mnt_count >=
                  (unsigned) fs->super->s_max_mnt_count)) {
                reason = _(" has been mounted %u times without being checked");
                reason_arg = fs->super->s_mnt_count;
+               if (batt && (fs->super->s_mnt_count < 
+                            (unsigned) fs->super->s_max_mnt_count*2))
+                       reason = 0;
        } else if (fs->super->s_checkinterval &&
-                time(0) >= (fs->super->s_lastcheck +
-                            fs->super->s_checkinterval)) {
+                  ((ctx->now - lastcheck) >= fs->super->s_checkinterval)) {
                reason = _(" has gone %u days without being checked");
-               reason_arg = (time(0) - fs->super->s_lastcheck)/(3600*24);
+               reason_arg = (ctx->now - fs->super->s_lastcheck)/(3600*24);
+               if (batt && ((ctx->now - fs->super->s_lastcheck) < 
+                            fs->super->s_checkinterval*2))
+                       reason = 0;
        }
        if (reason) {
                fputs(ctx->device_name, stdout);
@@ -276,11 +301,31 @@ static void check_if_skip(e2fsck_t ctx)
                fputs(_(", check forced.\n"), stdout);
                return;
        }
-       printf(_("%s: clean, %d/%d files, %d/%d blocks\n"), ctx->device_name,
+       printf(_("%s: clean, %u/%u files, %u/%u blocks"), ctx->device_name,
               fs->super->s_inodes_count - fs->super->s_free_inodes_count,
               fs->super->s_inodes_count,
               fs->super->s_blocks_count - fs->super->s_free_blocks_count,
               fs->super->s_blocks_count);
+       next_check = 100000;
+       if (fs->super->s_max_mnt_count > 0) {
+               next_check = fs->super->s_max_mnt_count - fs->super->s_mnt_count;
+               if (next_check <= 0) 
+                       next_check = 1;
+       }
+       if (fs->super->s_checkinterval &&
+           ((ctx->now - fs->super->s_lastcheck) >= fs->super->s_checkinterval))
+               next_check = 1;
+       if (next_check <= 5) {
+               if (next_check == 1) {
+                       if (batt) 
+                               fputs(_(" (check deferred; on battery)"),
+                                     stdout);
+                       else
+                               fputs(_(" (check after next mount)"), stdout);
+               } else
+                       printf(_(" (check in %ld mounts)"), next_check);
+       }
+       fputc('\n', stdout);
        ext2fs_close(fs);
        ctx->fs = NULL;
        e2fsck_free_context(ctx);
@@ -297,12 +342,7 @@ struct percent_tbl {
 struct percent_tbl e2fsck_tbl = {
        5, { 0, 70, 90, 92,  95, 100 }
 };
-static char bar[] =
-       "==============================================================="
-       "===============================================================";
-static char spaces[] =
-       "                                                               "
-       "                                                               ";
+static char bar[128], spaces[128];
 
 static float calc_percent(struct percent_tbl *tbl, int pass, int curr,
                          int max)
@@ -323,20 +363,86 @@ extern void e2fsck_clear_progbar(e2fsck_t ctx)
        if (!(ctx->flags & E2F_FLAG_PROG_BAR))
                return;
        
-       printf("%s\r", spaces + (sizeof(spaces) - 80));
+       printf("%s%s\r%s", ctx->start_meta, spaces + (sizeof(spaces) - 80),
+              ctx->stop_meta);
+       fflush(stdout);
        ctx->flags &= ~E2F_FLAG_PROG_BAR;
 }
 
+int e2fsck_simple_progress(e2fsck_t ctx, const char *label, float percent,
+                          unsigned int dpynum)
+{
+       static const char spinner[] = "\\|/-";
+       int     i;
+       unsigned int    tick;
+       struct timeval  tv;
+       int dpywidth;
+       int fixed_percent;
+
+       if (ctx->flags & E2F_FLAG_PROG_SUPPRESS)
+               return 0;
+
+       /*
+        * Calculate the new progress position.  If the
+        * percentage hasn't changed, then we skip out right
+        * away. 
+        */
+       fixed_percent = (int) ((10 * percent) + 0.5);
+       if (ctx->progress_last_percent == fixed_percent)
+               return 0;
+       ctx->progress_last_percent = fixed_percent;
+
+       /*
+        * If we've already updated the spinner once within
+        * the last 1/8th of a second, no point doing it
+        * again.
+        */
+       gettimeofday(&tv, NULL);
+       tick = (tv.tv_sec << 3) + (tv.tv_usec / (1000000 / 8));
+       if ((tick == ctx->progress_last_time) &&
+           (fixed_percent != 0) && (fixed_percent != 1000))
+               return 0;
+       ctx->progress_last_time = tick;
+
+       /*
+        * Advance the spinner, and note that the progress bar
+        * will be on the screen
+        */
+       ctx->progress_pos = (ctx->progress_pos+1) & 3;
+       ctx->flags |= E2F_FLAG_PROG_BAR;
+
+       dpywidth = 66 - strlen(label);
+       dpywidth = 8 * (dpywidth / 8);
+       if (dpynum)
+               dpywidth -= 8;
+
+       i = ((percent * dpywidth) + 50) / 100;
+       printf("%s%s: |%s%s", ctx->start_meta, label,
+              bar + (sizeof(bar) - (i+1)),
+              spaces + (sizeof(spaces) - (dpywidth - i + 1)));
+       if (fixed_percent == 1000)
+               fputc('|', stdout);
+       else
+               fputc(spinner[ctx->progress_pos & 3], stdout);
+       printf(" %4.1f%%  ", percent);
+       if (dpynum)
+               printf("%u\r", dpynum);
+       else
+               fputs(" \r", stdout);
+       fputs(ctx->stop_meta, stdout);
+       
+       if (fixed_percent == 1000)
+               e2fsck_clear_progbar(ctx);
+       fflush(stdout);
+
+       return 0;
+}
+
 static int e2fsck_update_progress(e2fsck_t ctx, int pass,
                                  unsigned long cur, unsigned long max)
 {
-       static const char spinner[] = "\\|/-";
        char buf[80];
-       int     i;
        float percent;
-       int     tick;
-       struct timeval  tv;
-       static int dpywidth = 0;
 
        if (pass == 0)
                return 0;
@@ -345,53 +451,9 @@ static int e2fsck_update_progress(e2fsck_t ctx, int pass,
                sprintf(buf, "%d %lu %lu\n", pass, cur, max);
                write(ctx->progress_fd, buf, strlen(buf));
        } else {
-               if (ctx->flags & E2F_FLAG_PROG_SUPPRESS)
-                       return 0;
-               if (dpywidth == 0) {
-                       dpywidth = 66 - strlen(ctx->device_name);
-                       dpywidth = 8 * (dpywidth / 8);
-               }
-               /*
-                * Calculate the new progress position.  If the
-                * percentage hasn't changed, then we skip out right
-                * away. 
-                */
                percent = calc_percent(&e2fsck_tbl, pass, cur, max);
-               if (ctx->progress_last_percent == (int) 10 * percent)
-                       return 0;
-               ctx->progress_last_percent = (int) 10 * percent;
-
-               /*
-                * If we've already updated the spinner once within
-                * the last 1/8th of a second, no point doing it
-                * again.
-                */
-               gettimeofday(&tv, NULL);
-               tick = (tv.tv_sec << 3) + (tv.tv_usec / (1000000 / 8));
-               if ((tick == ctx->progress_last_time) &&
-                   (cur != max) && (cur != 0))
-                       return 0;
-               ctx->progress_last_time = tick;
-
-               /*
-                * Advance the spinner, and note that the progress bar
-                * will be on the screen
-                */
-               ctx->progress_pos = (ctx->progress_pos+1) & 3;
-               ctx->flags |= E2F_FLAG_PROG_BAR;
-               
-               i = ((percent * dpywidth) + 50) / 100;
-               printf("%s: |%s%s", ctx->device_name,
-                      bar + (sizeof(bar) - (i+1)),
-                      spaces + (sizeof(spaces) - (dpywidth - i + 1)));
-               if (percent == 100.0)
-                       fputc('|', stdout);
-               else
-                       fputc(spinner[ctx->progress_pos & 3], stdout);
-               printf(" %4.1f%%   \r", percent);
-               if (percent == 100.0)
-                       e2fsck_clear_progbar(ctx);
-               fflush(stdout);
+               e2fsck_simple_progress(ctx, ctx->device_name,
+                                      percent, 0);
        }
        return 0;
 }
@@ -417,7 +479,7 @@ static void reserve_stdio_fds(void)
 }
 
 #ifdef HAVE_SIGNAL_H
-static void signal_progress_on(int sig)
+static void signal_progress_on(int sig EXT2FS_ATTR((unused)))
 {
        e2fsck_t ctx = e2fsck_global_ctx;
 
@@ -428,7 +490,7 @@ static void signal_progress_on(int sig)
        ctx->progress_fd = 0;
 }
 
-static void signal_progress_off(int sig)
+static void signal_progress_off(int sig EXT2FS_ATTR((unused)))
 {
        e2fsck_t ctx = e2fsck_global_ctx;
 
@@ -438,8 +500,81 @@ static void signal_progress_off(int sig)
        e2fsck_clear_progbar(ctx);
        ctx->progress = 0;
 }
+
+static void signal_cancel(int sig EXT2FS_ATTR((unused)))
+{
+       e2fsck_t ctx = e2fsck_global_ctx;
+
+       if (!ctx)
+               exit(FSCK_CANCELED);
+
+       ctx->flags |= E2F_FLAG_CANCEL;
+}
 #endif
 
+static void parse_extended_opts(e2fsck_t ctx, const char *opts)
+{
+       char    *buf, *token, *next, *p, *arg;
+       int     ea_ver;
+       int     extended_usage = 0;
+
+       buf = string_copy(ctx, opts, 0);
+       for (token = buf; token && *token; token = next) {
+               p = strchr(token, ',');
+               next = 0;
+               if (p) {
+                       *p = 0;
+                       next = p+1;
+               }
+               arg = strchr(token, '=');
+               if (arg) {
+                       *arg = 0;
+                       arg++;
+               }
+               if (strcmp(token, "ea_ver") == 0) {
+                       if (!arg) {
+                               extended_usage++;
+                               continue;
+                       }
+                       ea_ver = strtoul(arg, &p, 0);
+                       if (*p ||
+                           ((ea_ver != 1) && (ea_ver != 2))) {
+                               fprintf(stderr,
+                                       _("Invalid EA version.\n"));
+                               extended_usage++;
+                               continue;
+                       }
+                       ctx->ext_attr_ver = ea_ver;
+               } else {
+                       fprintf(stderr, _("Unknown extended option: %s\n"),
+                               token);
+                       extended_usage++;
+               }
+       }
+       free(buf);
+
+       if (extended_usage) {
+               fputs(("\nExtended options are separated by commas, "
+                      "and may take an argument which\n"
+                      "is set off by an equals ('=') sign.  "
+                       "Valid extended options are:\n"
+                       "\tea_ver=<ea_version (1 or 2)>\n"
+                       "\tuninit_groups\n"
+                       "\tinit_groups\n\n"), stderr);
+               exit(1);
+       }
+}
+
+static void syntax_err_report(const char *filename, long err, int line_num)
+{
+       fprintf(stderr, 
+               _("Syntax error in e2fsck config file (%s, line #%d)\n\t%s\n"),
+               filename, line_num, error_message(err));
+       exit(FSCK_ERROR);
+}
+
+static const char *config_fn[] = { ROOT_SYSCONFDIR "/e2fsck.conf", 0 };
+
 static errcode_t PRS(int argc, char *argv[], e2fsck_t *ret_ctx)
 {
        int             flush = 0;
@@ -452,6 +587,12 @@ static errcode_t PRS(int argc, char *argv[], e2fsck_t *ret_ctx)
 #ifdef HAVE_SIGNAL_H
        struct sigaction        sa;
 #endif
+       char            *extended_opts = 0;
+       char            *cp;
+       int             res;            /* result of sscanf */
+#ifdef CONFIG_JBD_DEBUG
+       char            *jbd_debug;
+#endif
 
        retval = e2fsck_allocate_context(&ctx);
        if (retval)
@@ -459,19 +600,32 @@ static errcode_t PRS(int argc, char *argv[], e2fsck_t *ret_ctx)
 
        *ret_ctx = ctx;
 
-       setbuf(stdout, NULL);
-       setbuf(stderr, NULL);
-       initialize_ext2_error_table();
+       setvbuf(stdout, NULL, _IONBF, BUFSIZ);
+       setvbuf(stderr, NULL, _IONBF, BUFSIZ);
+       if (isatty(0) && isatty(1)) {
+               ctx->interactive = 1;
+       } else {
+               ctx->start_meta[0] = '\001';
+               ctx->stop_meta[0] = '\002';
+       }
+       memset(bar, '=', sizeof(bar)-1);
+       memset(spaces, ' ', sizeof(spaces)-1);
+       add_error_table(&et_ext2_error_table);
+       add_error_table(&et_prof_error_table);
+       blkid_get_cache(&ctx->blkid, NULL);
        
        if (argc && *argv)
                ctx->program_name = *argv;
        else
                ctx->program_name = "e2fsck";
-       while ((c = getopt (argc, argv, "panyrcC:B:dfvtFVM:b:I:j:P:l:L:N:Ss")) != EOF)
+       while ((c = getopt (argc, argv, "panyrcC:B:dE:fvtFVM:b:I:j:P:l:L:N:SsDk")) != EOF)
                switch (c) {
                case 'C':
                        ctx->progress = e2fsck_update_progress;
-                       ctx->progress_fd = atoi(optarg);
+                       res = sscanf(optarg, "%d", &ctx->progress_fd);
+                       if (res != 1)
+                               goto sscanf_err;
+
                        if (!ctx->progress_fd)
                                break;
                        /* Validate the file descriptor to avoid disasters */
@@ -486,18 +640,30 @@ static errcode_t PRS(int argc, char *argv[], e2fsck_t *ret_ctx)
                        } else
                                close(fd);
                        break;
+               case 'D':
+                       ctx->options |= E2F_OPT_COMPRESS_DIRS;
+                       break;
+               case 'E':
+                       extended_opts = optarg;
+                       break;
                case 'p':
                case 'a':
+                       if (ctx->options & (E2F_OPT_YES|E2F_OPT_NO)) {
+                       conflict_opt:
+                               fatal_error(ctx, 
+       _("Only one of the options -p/-a, -n or -y may be specified."));
+                       }
                        ctx->options |= E2F_OPT_PREEN;
-                       ctx->options &= ~(E2F_OPT_YES|E2F_OPT_NO);
                        break;
                case 'n':
+                       if (ctx->options & (E2F_OPT_YES|E2F_OPT_PREEN))
+                               goto conflict_opt;
                        ctx->options |= E2F_OPT_NO;
-                       ctx->options &= ~(E2F_OPT_YES|E2F_OPT_PREEN);
                        break;
                case 'y':
+                       if (ctx->options & (E2F_OPT_PREEN|E2F_OPT_NO))
+                               goto conflict_opt;
                        ctx->options |= E2F_OPT_YES;
-                       ctx->options &= ~(E2F_OPT_PREEN|E2F_OPT_NO);
                        break;
                case 't':
 #ifdef RESOURCE_TRACK
@@ -511,36 +677,39 @@ static errcode_t PRS(int argc, char *argv[], e2fsck_t *ret_ctx)
 #endif
                        break;
                case 'c':
-                       cflag++;
+                       if (cflag++)
+                               ctx->options |= E2F_OPT_WRITECHECK;
                        ctx->options |= E2F_OPT_CHECKBLOCKS;
                        break;
                case 'r':
                        /* What we do by default, anyway! */
                        break;
                case 'b':
-                       ctx->use_superblock = atoi(optarg);
+                       res = sscanf(optarg, "%d", &ctx->use_superblock);
+                       if (res != 1)
+                               goto sscanf_err;
                        ctx->flags |= E2F_FLAG_SB_SPECIFIED;
                        break;
                case 'B':
                        ctx->blocksize = atoi(optarg);
                        break;
                case 'I':
-                       ctx->inode_buffer_blocks = atoi(optarg);
+                       res = sscanf(optarg, "%d", &ctx->inode_buffer_blocks);
+                       if (res != 1)
+                               goto sscanf_err;
                        break;
                case 'j':
-                       ctx->journal_name = optarg;
+                       ctx->journal_name = string_copy(ctx, optarg, 0);
                        break;
                case 'P':
-                       ctx->process_inode_size = atoi(optarg);
+                       res = sscanf(optarg, "%d", &ctx->process_inode_size);
+                       if (res != 1)
+                               goto sscanf_err;
                        break;
                case 'L':
                        replace_bad_blocks++;
                case 'l':
-                       bad_blocks_file = (char *) malloc(strlen(optarg)+1);
-                       if (!bad_blocks_file)
-                               fatal_error(ctx,
-                                           "Couldn't malloc bad_blocks_file");
-                       strcpy(bad_blocks_file, optarg);
+                       bad_blocks_file = string_copy(ctx, optarg, 0);
                        break;
                case 'd':
                        ctx->options |= E2F_OPT_DEBUG;
@@ -565,20 +734,9 @@ static errcode_t PRS(int argc, char *argv[], e2fsck_t *ret_ctx)
                case 'N':
                        ctx->device_name = optarg;
                        break;
-#ifdef ENABLE_SWAPFS
-               case 's':
-                       normalize_swapfs = 1;
-               case 'S':
-                       swapfs = 1;
+               case 'k':
+                       keep_bad_blocks++;
                        break;
-#else
-               case 's':
-               case 'S':
-                       fprintf(stderr, _("Byte-swapping filesystems "
-                                         "not compiled in this version "
-                                         "of e2fsck\n"));
-                       exit(1);
-#endif
                default:
                        usage(ctx);
                }
@@ -587,9 +745,26 @@ static errcode_t PRS(int argc, char *argv[], e2fsck_t *ret_ctx)
        if (optind != argc - 1)
                usage(ctx);
        if ((ctx->options & E2F_OPT_NO) && !bad_blocks_file &&
-           !cflag && !swapfs)
+           !cflag && !(ctx->options & E2F_OPT_COMPRESS_DIRS))
                ctx->options |= E2F_OPT_READONLY;
-       ctx->filesystem_name = argv[optind];
+
+       ctx->io_options = strchr(argv[optind], '?');
+       if (ctx->io_options) 
+               *ctx->io_options++ = 0;
+       ctx->filesystem_name = blkid_get_devname(ctx->blkid, argv[optind], 0);
+       if (!ctx->filesystem_name) {
+               com_err(ctx->program_name, 0, _("Unable to resolve '%s'"), 
+                       argv[optind]);
+               fatal_error(ctx, 0);
+       }
+       if (extended_opts)
+               parse_extended_opts(ctx, extended_opts);
+
+       if ((cp = getenv("E2FSCK_CONFIG")) != NULL)
+               config_fn[0] = cp;
+       profile_set_syntax_err_cb(syntax_err_report);
+       profile_init(config_fn, &ctx->profile);
+
        if (flush) {
                fd = open(ctx->filesystem_name, O_RDONLY, 0);
                if (fd < 0) {
@@ -606,20 +781,19 @@ static errcode_t PRS(int argc, char *argv[], e2fsck_t *ret_ctx)
                }
                close(fd);
        }
-#ifdef ENABLE_SWAPFS
-       if (swapfs) {
-               if (cflag || bad_blocks_file) {
-                       fprintf(stderr, _("Incompatible options not "
-                               "allowed when byte-swapping.\n"));
-                       exit(FSCK_USAGE);
-               }
+       if (cflag && bad_blocks_file) {
+               fprintf(stderr, _("The -c and the -l/-L options may "
+                                 "not be both used at the same time.\n"));
+               exit(FSCK_USAGE);
        }
-#endif
 #ifdef HAVE_SIGNAL_H
        /*
         * Set up signal action
         */
        memset(&sa, 0, sizeof(struct sigaction));
+       sa.sa_handler = signal_cancel;
+       sigaction(SIGINT, &sa, 0);
+       sigaction(SIGTERM, &sa, 0);
 #ifdef SA_RESTART
        sa.sa_flags = SA_RESTART;
 #endif
@@ -633,31 +807,50 @@ static errcode_t PRS(int argc, char *argv[], e2fsck_t *ret_ctx)
        /* Update our PATH to include /sbin if we need to run badblocks  */
        if (cflag) {
                char *oldpath = getenv("PATH");
+               char *newpath;
+               int len = sizeof(PATH_SET) + 1;
+
+               if (oldpath)
+                       len += strlen(oldpath);
+
+               newpath = malloc(len);
+               if (!newpath)
+                       fatal_error(ctx, "Couldn't malloc() newpath");
+               strcpy(newpath, PATH_SET);
+
                if (oldpath) {
-                       char *newpath;
-
-                       newpath = (char *) malloc(sizeof (PATH_SET) + 1 +
-                                                 strlen (oldpath));
-                       if (!newpath)
-                               fatal_error(ctx, "Couldn't malloc() newpath");
-                       strcpy (newpath, PATH_SET);
-                       strcat (newpath, ":");
-                       strcat (newpath, oldpath);
-                       putenv (newpath);
-               } else
-                       putenv (PATH_SET);
+                       strcat(newpath, ":");
+                       strcat(newpath, oldpath);
+               }
+               putenv(newpath);
+       }
+#ifdef CONFIG_JBD_DEBUG
+       jbd_debug = getenv("E2FSCK_JBD_DEBUG");
+       if (jbd_debug) {
+               res = sscanf(jbd_debug, "%d", &journal_enable_debug);
+               if (res != 1) {
+                       fprintf(stderr,
+                               _("E2FSCK_JBD_DEBUG \"%s\" not an integer\n\n"),
+                               jbd_debug);
+                       exit (1);
+               }
        }
+#endif
        return 0;
+
+sscanf_err:
+       fprintf(stderr, _("\nInvalid non-numeric argument to -%c (\"%s\")\n\n"),
+               c, optarg);
+       exit (1);
 }
 
 static const char *my_ver_string = E2FSPROGS_VERSION;
 static const char *my_ver_date = E2FSPROGS_DATE;
-                                       
+
 int main (int argc, char *argv[])
 {
-       errcode_t       retval = 0;
+       errcode_t       retval = 0, orig_retval = 0;
        int             exit_value = FSCK_OK;
-       int             i;
        ext2_filsys     fs = 0;
        io_manager      io_ptr;
        struct ext2_super_block *sb;
@@ -666,6 +859,9 @@ int main (int argc, char *argv[])
        e2fsck_t        ctx;
        struct problem_context pctx;
        int flags, run_result;
+       int journal_size;
+       int sysval, sys_page_size = 4096;
+       __u32 features[3];
        
        clear_problem_context(&pctx);
 #ifdef MTRACE
@@ -676,6 +872,7 @@ int main (int argc, char *argv[])
 #endif
 #ifdef ENABLE_NLS
        setlocale(LC_MESSAGES, "");
+       setlocale(LC_CTYPE, "");
        bindtextdomain(NLS_CAT_NAME, LOCALEDIR);
        textdomain(NLS_CAT_NAME);
 #endif
@@ -696,11 +893,11 @@ int main (int argc, char *argv[])
        reserve_stdio_fds();
        
 #ifdef RESOURCE_TRACK
-       init_resource_track(&ctx->global_rtrack);
+       init_resource_track(&ctx->global_rtrack, NULL);
 #endif
 
        if (!(ctx->options & E2F_OPT_PREEN) || show_version_only)
-               fprintf (stderr, "e2fsck %s (%s)\n", my_ver_string,
+               fprintf(stderr, "e2fsck %s (%s)\n", my_ver_string,
                         my_ver_date);
 
        if (show_version_only) {
@@ -714,53 +911,79 @@ int main (int argc, char *argv[])
        if (!(ctx->options & E2F_OPT_PREEN) &&
            !(ctx->options & E2F_OPT_NO) &&
            !(ctx->options & E2F_OPT_YES)) {
-               if (!isatty (0) || !isatty (1))
+               if (!ctx->interactive)
                        fatal_error(ctx,
                                    _("need terminal for interactive repairs"));
        }
        ctx->superblock = ctx->use_superblock;
 restart:
-#if 1
-       io_ptr = unix_io_manager;
-#else
+#ifdef CONFIG_TESTIO_DEBUG
        io_ptr = test_io_manager;
        test_io_backing_manager = unix_io_manager;
+#else
+       io_ptr = unix_io_manager;
 #endif
-       flags = 0;
+       flags = EXT2_FLAG_NOFREE_ON_ERROR;
        if ((ctx->options & E2F_OPT_READONLY) == 0)
                flags |= EXT2_FLAG_RW;
+       if ((ctx->mount_flags & EXT2_MF_MOUNTED) == 0)
+               flags |= EXT2_FLAG_EXCLUSIVE;
 
        if (ctx->superblock && ctx->blocksize) {
-               retval = ext2fs_open(ctx->filesystem_name, flags,
-                                    ctx->superblock, ctx->blocksize,
-                                    io_ptr, &fs);
+               retval = ext2fs_open2(ctx->filesystem_name, ctx->io_options, 
+                                     flags, ctx->superblock, ctx->blocksize,
+                                     io_ptr, &fs);
        } else if (ctx->superblock) {
-               for (i=0; possible_block_sizes[i]; i++) {
-                       retval = ext2fs_open(ctx->filesystem_name, flags,
-                                            ctx->superblock,
-                                            possible_block_sizes[i],
-                                            io_ptr, &fs);
+               int blocksize;
+               for (blocksize = EXT2_MIN_BLOCK_SIZE;
+                    blocksize <= EXT2_MAX_BLOCK_SIZE; blocksize *= 2) {
+                       retval = ext2fs_open2(ctx->filesystem_name, 
+                                             ctx->io_options, flags,
+                                             ctx->superblock, blocksize,
+                                             io_ptr, &fs);
                        if (!retval)
                                break;
                }
        } else 
-               retval = ext2fs_open(ctx->filesystem_name, flags, 
-                                    0, 0, io_ptr, &fs);
+               retval = ext2fs_open2(ctx->filesystem_name, ctx->io_options, 
+                                     flags, 0, 0, io_ptr, &fs);
        if (!ctx->superblock && !(ctx->options & E2F_OPT_PREEN) &&
            !(ctx->flags & E2F_FLAG_SB_SPECIFIED) &&
            ((retval == EXT2_ET_BAD_MAGIC) ||
+            (retval == EXT2_ET_CORRUPT_SUPERBLOCK) ||
             ((retval == 0) && ext2fs_check_desc(fs)))) {
+               if (fs->flags & EXT2_FLAG_NOFREE_ON_ERROR) {
+                       ext2fs_free(fs);
+                       fs = NULL;
+               }
                if (!fs || (fs->group_desc_count > 1)) {
-                       printf(_("%s trying backup blocks...\n"),
-                              retval ? _("Couldn't find ext2 superblock,") :
+                       printf(_("%s: %s trying backup blocks...\n"),
+                              ctx->program_name, 
+                              retval ? _("Superblock invalid,") :
                               _("Group descriptors look bad..."));
                        get_backup_sb(ctx, fs, ctx->filesystem_name, io_ptr);
                        if (fs)
                                ext2fs_close(fs);
+                       orig_retval = retval;
                        goto restart;
                }
        }
+       if (((retval == EXT2_ET_UNSUPP_FEATURE) ||
+            (retval == EXT2_ET_RO_UNSUPP_FEATURE)) &&
+           fs && fs->super) {
+               sb = fs->super;
+               features[0] = (sb->s_feature_compat & 
+                              ~EXT2_LIB_FEATURE_COMPAT_SUPP);
+               features[1] = (sb->s_feature_incompat & 
+                              ~EXT2_LIB_FEATURE_INCOMPAT_SUPP);
+               features[2] = (sb->s_feature_ro_compat & 
+                              ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP);
+               if (features[0] || features[1] || features[2])
+                       goto print_unsupp_features;
+       }
        if (retval) {
+               if (orig_retval)
+                       retval = orig_retval;
                com_err(ctx->program_name, retval, _("while trying to open %s"),
                        ctx->filesystem_name);
                if (retval == EXT2_ET_REV_TOO_HIGH) {
@@ -778,6 +1001,9 @@ restart:
                               "r/o" : "r/w");
                else if (retval == ENXIO)
                        printf(_("Possibly non-existent or swap device?\n"));
+               else if (retval == EBUSY)
+                       printf(_("Filesystem mounted or opened exclusively "
+                                "by another program?\n"));
 #ifdef EROFS
                else if (retval == EROFS)
                        printf(_("Disk write-protected; use the -n option "
@@ -788,8 +1014,56 @@ restart:
                        fix_problem(ctx, PR_0_SB_CORRUPT, &pctx);
                fatal_error(ctx, 0);
        }
+       /*
+        * We only update the master superblock because (a) paranoia;
+        * we don't want to corrupt the backup superblocks, and (b) we
+        * don't need to update the mount count and last checked
+        * fields in the backup superblock (the kernel doesn't update
+        * the backup superblocks anyway).  With newer versions of the
+        * library this flag is set by ext2fs_open2(), but we set this
+        * here just to be sure.  (No, we don't support e2fsck running
+        * with some other libext2fs than the one that it was shipped
+        * with, but just in case....)
+        */
+       fs->flags |= EXT2_FLAG_MASTER_SB_ONLY;
+
+       if (!(ctx->flags & E2F_FLAG_GOT_DEVSIZE)) {
+               __u32 blocksize = EXT2_BLOCK_SIZE(fs->super);
+               int need_restart = 0;
+
+               pctx.errcode = ext2fs_get_device_size(ctx->filesystem_name,
+                                                     blocksize,
+                                                     &ctx->num_blocks);
+               /*
+                * The floppy driver refuses to allow anyone else to
+                * open the device if has been opened with O_EXCL;
+                * this is unlike other block device drivers in Linux.
+                * To handle this, we close the filesystem and then
+                * reopen the filesystem after we get the device size.
+                */
+               if (pctx.errcode == EBUSY) {
+                       ext2fs_close(fs);
+                       need_restart++;
+                       pctx.errcode = 
+                               ext2fs_get_device_size(ctx->filesystem_name, 
+                                                      blocksize,
+                                                      &ctx->num_blocks);
+               }
+               if (pctx.errcode == EXT2_ET_UNIMPLEMENTED)
+                       ctx->num_blocks = 0;
+               else if (pctx.errcode) {
+                       fix_problem(ctx, PR_0_GETSIZE_ERROR, &pctx);
+                       ctx->flags |= E2F_FLAG_ABORT;
+                       fatal_error(ctx, 0);
+               }
+               ctx->flags |= E2F_FLAG_GOT_DEVSIZE;
+               if (need_restart)
+                       goto restart;
+       }
+
        ctx->fs = fs;
        fs->priv_data = ctx;
+       fs->now = ctx->now;
        sb = fs->super;
        if (sb->s_rev_level > E2FSCK_CURRENT_REV) {
                com_err(ctx->program_name, EXT2_ET_REV_TOO_HIGH,
@@ -805,13 +1079,8 @@ restart:
         */
        if (ctx->device_name == 0 &&
            (sb->s_volume_name[0] != 0)) {
-               char *cp = malloc(sizeof(sb->s_volume_name)+1);
-               if (cp) {
-                       strncpy(cp, sb->s_volume_name,
-                               sizeof(sb->s_volume_name));
-                       cp[sizeof(sb->s_volume_name)] = 0;
-                       ctx->device_name = cp;
-               }
+               ctx->device_name = string_copy(ctx, sb->s_volume_name,
+                                              sizeof(sb->s_volume_name));
        }
        if (ctx->device_name == 0)
                ctx->device_name = ctx->filesystem_name;
@@ -824,7 +1093,6 @@ restart:
                com_err(ctx->program_name, retval,
                        _("while checking ext3 journal for %s"),
                        ctx->device_name);
-               ext2fs_close(ctx->fs);
                fatal_error(ctx, 0);
        }
 
@@ -839,6 +1107,17 @@ restart:
                                 "check.\n"));
                        io_channel_flush(ctx->fs->io);
                } else {
+                       if (ctx->flags & E2F_FLAG_RESTARTED) {
+                               /*
+                                * Whoops, we attempted to run the
+                                * journal twice.  This should never
+                                * happen, unless the hardware or
+                                * device driver is being bogus.
+                                */
+                               com_err(ctx->program_name, 0,
+                                       _("unable to set superblock flags on %s\n"), ctx->device_name);
+                               fatal_error(ctx, 0);
+                       }
                        retval = e2fsck_run_ext3_journal(ctx);
                        if (retval) {
                                com_err(ctx->program_name, retval,
@@ -848,6 +1127,7 @@ restart:
                        }
                        ext2fs_close(ctx->fs);
                        ctx->fs = 0;
+                       ctx->flags |= E2F_FLAG_RESTARTED;
                        goto restart;
                }
        }
@@ -856,15 +1136,26 @@ restart:
         * Check for compatibility with the feature sets.  We need to
         * be more stringent than ext2fs_open().
         */
-       if ((sb->s_feature_compat & ~EXT2_LIB_FEATURE_COMPAT_SUPP) ||
-           (sb->s_feature_incompat & ~EXT2_LIB_FEATURE_INCOMPAT_SUPP)) {
-               com_err(ctx->program_name, EXT2_ET_UNSUPP_FEATURE,
-                       "(%s)", ctx->device_name);
-               goto get_newer;
-       }
-       if (sb->s_feature_ro_compat & ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP) {
-               com_err(ctx->program_name, EXT2_ET_RO_UNSUPP_FEATURE,
-                       "(%s)", ctx->device_name);
+       features[0] = sb->s_feature_compat & ~EXT2_LIB_FEATURE_COMPAT_SUPP;
+       features[1] = sb->s_feature_incompat & ~EXT2_LIB_FEATURE_INCOMPAT_SUPP;
+       features[2] = (sb->s_feature_ro_compat & 
+                      ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP);
+print_unsupp_features:
+       if (features[0] || features[1] || features[2]) {
+               int     i, j;
+               __u32   *mask = features, m;
+
+               fprintf(stderr, _("%s has unsupported feature(s):"), 
+                       ctx->filesystem_name);
+
+               for (i=0; i <3; i++,mask++) {
+                       for (j=0,m=1; j < 32; j++, m<<=1) {
+                               if (*mask & m)
+                                       fprintf(stderr, " %s", 
+                                               e2p_feature2string(i, m));
+                       }
+               }
+               putc('\n', stderr);
                goto get_newer;
        }
 #ifdef ENABLE_COMPRESSION
@@ -872,7 +1163,16 @@ restart:
                com_err(ctx->program_name, 0,
                        _("Warning: compression support is experimental.\n"));
 #endif
-       
+#ifndef ENABLE_HTREE
+       if (sb->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) {
+               com_err(ctx->program_name, 0,
+                       _("E2fsck not compiled with HTREE support,\n\t"
+                         "but filesystem %s has HTREE directories.\n"),
+                       ctx->device_name);
+               goto get_newer;
+       }
+#endif
+
        /*
         * If the user specified a specific superblock, presumably the
         * master superblock has been trashed.  So we mark the
@@ -883,15 +1183,24 @@ restart:
                ext2fs_mark_super_dirty(fs);
 
        /*
-        * Don't overwrite the backup superblock and block
-        * descriptors, until we're sure the filesystem is OK....
+        * Calculate the number of filesystem blocks per pagesize.  If
+        * fs->blocksize > page_size, set the number of blocks per
+        * pagesize to 1 to avoid division by zero errors.
         */
-       fs->flags |= EXT2_FLAG_MASTER_SB_ONLY;
+#ifdef _SC_PAGESIZE
+       sysval = sysconf(_SC_PAGESIZE);
+       if (sysval > 0)
+               sys_page_size = sysval;
+#endif /* _SC_PAGESIZE */
+       ctx->blocks_per_page = sys_page_size / fs->blocksize;
+       if (ctx->blocks_per_page == 0)
+               ctx->blocks_per_page = 1;
 
        ehandler_init(fs->io);
 
        if (ctx->superblock)
                set_latch_flags(PR_LATCH_RELOC, PRL_LATCHED, 0);
+       ext2fs_mark_valid(fs);
        check_super_block(ctx);
        if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
                fatal_error(ctx, 0);
@@ -899,24 +1208,9 @@ restart:
        if (bad_blocks_file)
                read_bad_blocks_file(ctx, bad_blocks_file, replace_bad_blocks);
        else if (cflag)
-               test_disk(ctx);
+               read_bad_blocks_file(ctx, 0, !keep_bad_blocks); /* Test disk */
        if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
                fatal_error(ctx, 0);
-#ifdef ENABLE_SWAPFS
-       if (normalize_swapfs) {
-               if ((fs->flags & EXT2_FLAG_SWAP_BYTES) ==
-                   ext2fs_native_flag()) {
-                       fprintf(stderr, _("%s: Filesystem byte order "
-                               "already normalized.\n"), ctx->device_name);
-                       fatal_error(ctx, 0);
-               }
-       }
-       if (swapfs) {
-               swap_filesys(ctx);
-               if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
-                       fatal_error(ctx, 0);
-       }
-#endif
 
        /*
         * Mark the system as valid, 'til proven otherwise
@@ -932,8 +1226,48 @@ restart:
                         " but we'll try to go on...\n"));
        }
 
+       /*
+        * Save the journal size in megabytes.
+        * Try and use the journal size from the backup else let e2fsck
+        * find the default journal size.
+        */
+       if (sb->s_jnl_backup_type == EXT3_JNL_BACKUP_BLOCKS)
+               journal_size = sb->s_jnl_blocks[16] >> 20;
+       else
+               journal_size = -1;
+
        run_result = e2fsck_run(ctx);
        e2fsck_clear_progbar(ctx);
+
+       if (ctx->flags & E2F_FLAG_JOURNAL_INODE) {
+               if (fix_problem(ctx, PR_6_RECREATE_JOURNAL, &pctx)) {
+                       if (journal_size < 1024)
+                               journal_size = ext2fs_default_journal_size(fs->super->s_blocks_count);
+                       if (journal_size < 0) {
+                               fs->super->s_feature_compat &=
+                                       ~EXT3_FEATURE_COMPAT_HAS_JOURNAL;
+                               fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
+                               com_err(ctx->program_name, 0, 
+                                       _("Couldn't determine journal size"));
+                               goto no_journal;
+                       }
+                       printf(_("Creating journal (%d blocks): "),
+                              journal_size);
+                       fflush(stdout);
+                       retval = ext2fs_add_journal_inode(fs,
+                                                         journal_size, 0);
+                       if (retval) {
+                               com_err("Error ", retval,
+                                       _("\n\twhile trying to create journal"));
+                               goto no_journal;
+                       }
+                       printf(_(" Done.\n"));
+                       printf(_("\n*** journal has been re-created - "
+                                      "filesystem is now ext3 again ***\n"));
+               }
+       }
+no_journal:
+
        if (run_result == E2F_FLAG_RESTART) {
                printf(_("Restarting e2fsck from the beginning...\n"));
                retval = e2fsck_reset_context(ctx);
@@ -945,55 +1279,83 @@ restart:
                ext2fs_close(fs);
                goto restart;
        }
-       if (run_result & E2F_FLAG_SIGNAL_MASK)
-               fatal_error(ctx, 0);
-       if (run_result & E2F_FLAG_CANCEL)
-               ext2fs_unmark_valid(fs);
+       if (run_result & E2F_FLAG_CANCEL) {
+               printf(_("%s: e2fsck canceled.\n"), ctx->device_name ?
+                      ctx->device_name : ctx->filesystem_name);
+               exit_value |= FSCK_CANCELED;
+       }
+       if (run_result & E2F_FLAG_ABORT)
+               fatal_error(ctx, _("aborted"));
+       if (check_backup_super_block(ctx)) {
+               fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
+               ext2fs_mark_super_dirty(fs);
+       }
 
 #ifdef MTRACE
        mtrace_print("Cleanup");
 #endif
        if (ext2fs_test_changed(fs)) {
-               exit_value = FSCK_NONDESTRUCT;
+               exit_value |= FSCK_NONDESTRUCT;
                if (!(ctx->options & E2F_OPT_PREEN))
                    printf(_("\n%s: ***** FILE SYSTEM WAS MODIFIED *****\n"),
                               ctx->device_name);
-               if (root_filesystem && !read_only_root) {
+               if (ctx->mount_flags & EXT2_MF_ISROOT) {
                        printf(_("%s: ***** REBOOT LINUX *****\n"),
                               ctx->device_name);
-                       exit_value = FSCK_REBOOT;
+                       exit_value |= FSCK_REBOOT;
                }
        }
-       if (ext2fs_test_valid(fs))
-               fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
-       else {
+       if (!ext2fs_test_valid(fs) ||
+           ((exit_value & FSCK_CANCELED) && 
+            (sb->s_state & EXT2_ERROR_FS))) {
                printf(_("\n%s: ********** WARNING: Filesystem still has "
                         "errors **********\n\n"), ctx->device_name);
-               exit_value = FSCK_UNCORRECTED;
+               exit_value |= FSCK_UNCORRECTED;
+               exit_value &= ~FSCK_NONDESTRUCT;
        }
-       if (!(ctx->options & E2F_OPT_READONLY)) {
-               if (ext2fs_test_valid(fs)) {
-                       if (!(sb->s_state & EXT2_VALID_FS))
-                               exit_value = FSCK_NONDESTRUCT;
-                       sb->s_state = EXT2_VALID_FS;
-               } else
-                       sb->s_state &= ~EXT2_VALID_FS;
-               sb->s_mnt_count = 0;
-               sb->s_lastcheck = time(NULL);
-               ext2fs_mark_super_dirty(fs);
+       if (exit_value & FSCK_CANCELED) {
+               int     allow_cancellation;
+
+               profile_get_boolean(ctx->profile, "options",
+                                   "allow_cancellation", 0, 0, 
+                                   &allow_cancellation);
+               exit_value &= ~FSCK_NONDESTRUCT;
+               if (allow_cancellation && ext2fs_test_valid(fs) &&
+                   (sb->s_state & EXT2_VALID_FS) && 
+                   !(sb->s_state & EXT2_ERROR_FS))
+                       exit_value = 0;
+       } else {
+               show_stats(ctx);
+               if (!(ctx->options & E2F_OPT_READONLY)) {
+                       if (ext2fs_test_valid(fs)) {
+                               if (!(sb->s_state & EXT2_VALID_FS))
+                                       exit_value |= FSCK_NONDESTRUCT;
+                               sb->s_state = EXT2_VALID_FS;
+                       } else
+                               sb->s_state &= ~EXT2_VALID_FS;
+                       sb->s_mnt_count = 0;
+                       sb->s_lastcheck = ctx->now;
+                       ext2fs_mark_super_dirty(fs);
+               }
        }
-       show_stats(ctx);
+
+       if (sb->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_GDT_CSUM &&
+           !(ctx->options & E2F_OPT_READONLY))
+               ext2fs_set_gdt_csum(ctx->fs);
 
        e2fsck_write_bitmaps(ctx);
-       
-       ext2fs_close(fs);
-       ctx->fs = NULL;
-       e2fsck_free_context(ctx);
-       
 #ifdef RESOURCE_TRACK
+       io_channel_flush(ctx->fs->io);
        if (ctx->options & E2F_OPT_TIME)
-               print_resource_track(NULL, &ctx->global_rtrack);
+               print_resource_track(NULL, &ctx->global_rtrack, ctx->fs->io);
 #endif
+       ext2fs_close(fs);
+       ctx->fs = NULL;
+       free(ctx->filesystem_name);
+       free(ctx->journal_name);
 
+       e2fsck_free_context(ctx);
+       remove_error_table(&et_ext2_error_table);
+       remove_error_table(&et_prof_error_table);
        return exit_value;
 }