Whamcloud - gitweb
ChangeLog, dir_iterate.c:
[tools/e2fsprogs.git] / e2fsck / pass1.c
index 0c86096..6766f41 100644 (file)
@@ -1,8 +1,12 @@
 /*
  * pass1.c -- pass #1 of e2fsck: sequential scan of the inode table
  * 
- * Copyright (C) 1993, 1994 Theodore Ts'o.  This file may be
- * redistributed under the terms of the GNU Public License.
+ * Copyright (C) 1993, 1994, 1995, 1996, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
  * 
  * Pass 1 of e2fsck iterates over all the inodes in the filesystems,
  * and applies the following tests to each inode:
@@ -16,6 +20,7 @@
  *     - A bitmap of which inodes are in use.          (inode_used_map)
  *     - A bitmap of which inodes are directories.     (inode_dir_map)
  *     - A bitmap of which inodes have bad fields.     (inode_bad_map)
+ *     - A bitmap of which inodes are in bad blocks.   (inode_bb_map)
  *     - A bitmap of which blocks are in use.          (block_found_map)
  *     - A bitmap of which blocks are in use by two inodes     (block_dup_map)
  *     - The data blocks of the directory inodes.      (dir_map)
@@ -35,8 +40,8 @@
 #include <errno.h>
 #endif
 
-#include <et/com_err.h>
 #include "e2fsck.h"
+#include "problem.h"
 
 #ifdef NO_INLINE_FUNCS
 #define _INLINE_
 #define _INLINE_ inline
 #endif
 
-/* Files counts */
-int fs_directory_count = 0;
-int fs_regular_count = 0;
-int fs_blockdev_count = 0;
-int fs_chardev_count = 0;
-int fs_links_count = 0;
-int fs_symlinks_count = 0;
-int fs_fast_symlinks_count = 0;
-int fs_fifo_count = 0;
-int fs_total_count = 0;
-int fs_badblocks_count = 0;
-int fs_sockets_count = 0;
-int fs_ind_count = 0;
-int fs_dind_count = 0;
-int fs_tind_count = 0;
-int fs_fragmented = 0;
-
-ext2fs_inode_bitmap inode_used_map = 0;        /* Inodes which are in use */
-ext2fs_inode_bitmap inode_bad_map = 0; /* Inodes which are bad in some way */
-ext2fs_inode_bitmap inode_dir_map = 0; /* Inodes which are directories */
-
-ext2fs_block_bitmap block_found_map = 0;
-ext2fs_block_bitmap block_dup_map = 0;
-ext2fs_block_bitmap block_illegal_map = 0;
-
-unsigned short * inode_link_info = NULL;
-
 static int process_block(ext2_filsys fs, blk_t *blocknr,
-                        int    blockcnt, void  *private);
+                        e2_blkcnt_t blockcnt, blk_t ref_blk, 
+                        int ref_offset, void *priv_data);
 static int process_bad_block(ext2_filsys fs, blk_t *block_nr,
-                            int blockcnt, void *private);
-static void check_blocks(ext2_filsys fs, ino_t ino, struct ext2_inode *inode,
+                            e2_blkcnt_t blockcnt, blk_t ref_blk,
+                            int ref_offset, void *priv_data);
+static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
                         char *block_buf);
-static void mark_table_blocks(ext2_filsys fs);
-static void alloc_bad_map(ext2_filsys fs);
-static void handle_fs_bad_blocks(ext2_filsys fs);
-static void process_inodes(ext2_filsys fs, char *block_buf);
-static int process_inode_cmp(const void *a, const void *b);
-static int dir_block_cmp(const void *a, const void *b);
+static void mark_table_blocks(e2fsck_t ctx);
+static void alloc_bad_map(e2fsck_t ctx);
+static void alloc_bb_map(e2fsck_t ctx);
+static void handle_fs_bad_blocks(e2fsck_t ctx);
+static void process_inodes(e2fsck_t ctx, char *block_buf);
+static EXT2_QSORT_TYPE process_inode_cmp(const void *a, const void *b);
 static errcode_t scan_callback(ext2_filsys fs, ext2_inode_scan scan,
-                                 dgrp_t group, void * private);
-static char *describe_illegal_block(ext2_filsys fs, blk_t block);
+                                 dgrp_t group, void * priv_data);
+/* static char *describe_illegal_block(ext2_filsys fs, blk_t block); */
 
 struct process_block_struct {
-       ino_t   ino;
-       int     is_dir:1, clear:1, suppress:1, fragmented:1;
-       int     num_blocks;
-       int     last_block;
-       int     num_illegal_blocks;
-       int     fix;
-       blk_t   previous_block;
+       ino_t           ino;
+       int             is_dir:1, clear:1, suppress:1, fragmented:1;
+       blk_t           num_blocks;
+       e2_blkcnt_t     last_block;
+       int             num_illegal_blocks;
+       blk_t           previous_block;
        struct ext2_inode *inode;
+       struct problem_context *pctx;
+       e2fsck_t        ctx;
 };
 
 struct process_inode_block {
@@ -103,25 +84,18 @@ struct process_inode_block {
        struct ext2_inode inode;
 };
 
-/*
- * For pass1_check_directory and pass1_get_blocks
- */
-ino_t stashed_ino;
-struct ext2_inode *stashed_inode;
+struct scan_callback_struct {
+       e2fsck_t        ctx;
+       char            *block_buf;
+};
 
 /*
  * For the inodes to process list.
  */
 static struct process_inode_block *inodes_to_process;
 static int process_inode_count;
-int process_inode_size = 256;
 
-/*
- * For the directory blocks list.
- */
-struct dir_block_struct *dir_blocks = 0;
-int    dir_block_count = 0;
-int    dir_block_size = 0;
+static __u64 ext2_max_sizes[4];
 
 /*
  * Free all memory allocated by pass1 in preparation for restarting
@@ -129,137 +103,202 @@ int     dir_block_size = 0;
  */
 static void unwind_pass1(ext2_filsys fs)
 {
-       ext2fs_free_inode_bitmap(inode_used_map);       inode_used_map = 0;
-       ext2fs_free_inode_bitmap(inode_dir_map);        inode_dir_map = 0;
-       ext2fs_free_block_bitmap(block_found_map);      block_found_map = 0;
-       free(inode_link_info);  inode_link_info = 0;
-       free(inodes_to_process);inodes_to_process = 0;
-       free(dir_blocks);       dir_blocks = 0;
-       dir_block_size = 0;
-       if (block_dup_map) {
-               ext2fs_free_block_bitmap(block_dup_map); block_dup_map = 0;
-       }
+       ext2fs_free_mem((void **) &inodes_to_process);
+       inodes_to_process = 0;
+}
+
+/*
+ * Check to make sure a device inode is real.  Returns 1 if the device
+ * checks out, 0 if not.
+ *
+ * Note: this routine is now also used to check FIFO's and Sockets,
+ * since they have the same requirement; the i_block fields should be
+ * zero. 
+ */
+int e2fsck_pass1_check_device_inode(struct ext2_inode *inode)
+{
+       int     i;
 
-       /* Clear statistic counters */
-       fs_directory_count = 0;
-       fs_regular_count = 0;
-       fs_blockdev_count = 0;
-       fs_chardev_count = 0;
-       fs_links_count = 0;
-       fs_symlinks_count = 0;
-       fs_fast_symlinks_count = 0;
-       fs_fifo_count = 0;
-       fs_total_count = 0;
-       fs_badblocks_count = 0;
-       fs_sockets_count = 0;
-       fs_ind_count = 0;
-       fs_dind_count = 0;
-       fs_tind_count = 0;
-       fs_fragmented = 0;
+       /*
+        * We should be able to do the test below all the time, but
+        * because the kernel doesn't forcibly clear the device
+        * inode's additional i_block fields, there are some rare
+        * occasions when a legitimate device inode will have non-zero
+        * additional i_block fields.  So for now, we only complain
+        * when the immutable flag is set, which should never happen
+        * for devices.  (And that's when the problem is caused, since
+        * you can't set or clear immutable flags for devices.)  Once
+        * the kernel has been fixed we can change this...
+        */
+       if (inode->i_flags & EXT2_IMMUTABLE_FL) {
+               for (i=4; i < EXT2_N_BLOCKS; i++) 
+                       if (inode->i_block[i])
+                               return 0;
+       }
+       return 1;
 }
 
-void pass1(ext2_filsys fs)
+void e2fsck_pass1(e2fsck_t ctx)
 {
+       int     i;
+       __u64   max_sizes;
+       ext2_filsys fs = ctx->fs;
        ino_t   ino;
        struct ext2_inode inode;
        ext2_inode_scan scan;
        char            *block_buf;
-       errcode_t       retval;
+#ifdef RESOURCE_TRACK
        struct resource_track   rtrack;
+#endif
        unsigned char   frag, fsize;
+       struct          problem_context pctx;
+       struct          scan_callback_struct scan_struct;
+       struct ext2fs_sb *sb;
        
+#ifdef RESOURCE_TRACK
        init_resource_track(&rtrack);
-       
-       if (!preen)
-               printf("Pass 1: Checking inodes, blocks, and sizes\n");
+#endif
+       clear_problem_context(&pctx);
+
+       if (!(ctx->options & E2F_OPT_PREEN))
+               fix_problem(ctx, PR_1_PASS_HEADER, &pctx);
 
 #ifdef MTRACE
        mtrace_print("Pass 1");
 #endif
 
+#define EXT2_BPP(bits) (1UL << ((bits) - 2))
+
+       for (i=0; i < 4; i++) {
+               max_sizes = EXT2_NDIR_BLOCKS + EXT2_BPP(10+i);
+               max_sizes = max_sizes + EXT2_BPP(10+i) * EXT2_BPP(10+i);
+               max_sizes = (max_sizes +
+                            (__u64) EXT2_BPP(10+i) * EXT2_BPP(10+i) *
+                            EXT2_BPP(10+i));
+               max_sizes = (max_sizes * (1UL << (10+i))) - 1;
+               ext2_max_sizes[i] = max_sizes;
+       }
+#undef EXT2_BPP
+       
        /*
         * Allocate bitmaps structures
         */
-       retval = ext2fs_allocate_inode_bitmap(fs, "in-use inode map",
-                                             &inode_used_map);
-       if (retval) {
-               com_err("ext2fs_allocate_inode_bitmap", retval,
-                       "while allocating inode_used_map");
-               fatal_error(0);
+       pctx.errcode = ext2fs_allocate_inode_bitmap(fs, "in-use inode map",
+                                             &ctx->inode_used_map);
+       if (pctx.errcode) {
+               pctx.num = 1;
+               fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
+               ctx->flags |= E2F_FLAG_ABORT;
+               return;
        }
-       retval = ext2fs_allocate_inode_bitmap(fs, "directory inode map",
-                                             &inode_dir_map);
-       if (retval) {
-               com_err("ext2fs_allocate_inode_bitmap", retval,
-                       "while allocating inode_dir_map");
-               fatal_error(0);
+       pctx.errcode = ext2fs_allocate_inode_bitmap(fs, "directory inode map",
+                                             &ctx->inode_dir_map);
+       if (pctx.errcode) {
+               pctx.num = 2;
+               fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
+               ctx->flags |= E2F_FLAG_ABORT;
+               return;
        }
-       retval = ext2fs_allocate_block_bitmap(fs, "in-use block map",
-                                             &block_found_map);
-       if (retval) {
-               com_err("ext2fs_allocate_block_bitmap", retval,
-                       "while allocating block_found_map");
-               fatal_error(0);
+       pctx.errcode = ext2fs_allocate_block_bitmap(fs, "in-use block map",
+                                             &ctx->block_found_map);
+       if (pctx.errcode) {
+               pctx.num = 1;
+               fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR, &pctx);
+               ctx->flags |= E2F_FLAG_ABORT;
+               return;
        }
-       retval = ext2fs_allocate_block_bitmap(fs, "illegal block map",
-                                             &block_illegal_map);
-       if (retval) {
-               com_err("ext2fs_allocate_block_bitmap", retval,
-                       "while allocating block_illegal_map");
-               fatal_error(0);
+       pctx.errcode = ext2fs_allocate_block_bitmap(fs, "illegal block map",
+                                             &ctx->block_illegal_map);
+       if (pctx.errcode) {
+               pctx.num = 2;
+               fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR, &pctx);
+               ctx->flags |= E2F_FLAG_ABORT;
+               return;
        }
-       inode_link_info = allocate_memory((fs->super->s_inodes_count + 1) *
-                                         sizeof(unsigned short),
-                                         "inode link count array");
-       inodes_to_process = allocate_memory(process_inode_size *
-                                           sizeof(struct process_inode_block),
-                                           "array of inodes to process");
+       pctx.errcode = ext2fs_create_icount2(fs, 0, 0, 0,
+                                            &ctx->inode_link_info);
+       if (pctx.errcode) {
+               fix_problem(ctx, PR_1_ALLOCATE_ICOUNT, &pctx);
+               ctx->flags |= E2F_FLAG_ABORT;
+               return;
+       }
+       inodes_to_process = (struct process_inode_block *)
+               e2fsck_allocate_memory(ctx,
+                                      (ctx->process_inode_size *
+                                       sizeof(struct process_inode_block)),
+                                      "array of inodes to process");
        process_inode_count = 0;
 
-       dir_block_size = get_num_dirs(fs) * 4;
-       dir_block_count = 0;
-       dir_blocks = allocate_memory(sizeof(struct dir_block_struct) *
-                                    dir_block_size,
-                                    "directory block information");
+       pctx.errcode = ext2fs_init_dblist(fs, 0);
+       if (pctx.errcode) {
+               fix_problem(ctx, PR_1_ALLOCATE_DBCOUNT, &pctx);
+               ctx->flags |= E2F_FLAG_ABORT;
+               return;
+       }
 
-       mark_table_blocks(fs);
-       block_buf = allocate_memory(fs->blocksize * 3, "block interate buffer");
+       mark_table_blocks(ctx);
+       block_buf = (char *) e2fsck_allocate_memory(ctx, fs->blocksize * 3,
+                                                   "block interate buffer");
        fs->get_blocks = pass1_get_blocks;
        fs->check_directory = pass1_check_directory;
        fs->read_inode = pass1_read_inode;
        fs->write_inode = pass1_write_inode;
        ehandler_operation("doing inode scan");
-       retval = ext2fs_open_inode_scan(fs, inode_buffer_blocks, &scan);
-       if (retval) {
-               com_err(program_name, retval, "while opening inode scan");
-               fatal_error(0);
+       pctx.errcode = ext2fs_open_inode_scan(fs, ctx->inode_buffer_blocks, 
+                                             &scan);
+       if (pctx.errcode) {
+               fix_problem(ctx, PR_1_ISCAN_ERROR, &pctx);
+               ctx->flags |= E2F_FLAG_ABORT;
+               return;
        }
-       retval = ext2fs_get_next_inode(scan, &ino, &inode);
-       if (retval) {
-               com_err(program_name, retval, "while starting inode scan");
-               fatal_error(0);
+       ext2fs_inode_scan_flags(scan, EXT2_SF_SKIP_MISSING_ITABLE, 0);
+       pctx.errcode = ext2fs_get_next_inode(scan, &ino, &inode);
+       if (pctx.errcode) {
+               fix_problem(ctx, PR_1_ISCAN_ERROR, &pctx);
+               ctx->flags |= E2F_FLAG_ABORT;
+               return;
        }
-       stashed_inode = &inode;
-       ext2fs_set_inode_callback(scan, scan_callback, block_buf);
+       ctx->stashed_inode = &inode;
+       scan_struct.ctx = ctx;
+       scan_struct.block_buf = block_buf;
+       ext2fs_set_inode_callback(scan, scan_callback, &scan_struct);
+       if (ctx->progress)
+               if ((ctx->progress)(ctx, 1, 0, ctx->fs->group_desc_count))
+                       return;
        while (ino) {
-               stashed_ino = ino;
-               inode_link_info[ino] = inode.i_links_count;
+               pctx.ino = ino;
+               pctx.inode = &inode;
+               ctx->stashed_ino = ino;
+               if (inode.i_links_count) {
+                       pctx.errcode = ext2fs_icount_store(ctx->inode_link_info, 
+                                          ino, inode.i_links_count);
+                       if (pctx.errcode) {
+                               pctx.num = inode.i_links_count;
+                               fix_problem(ctx, PR_1_ICOUNT_STORE, &pctx);
+                               ctx->flags |= E2F_FLAG_ABORT;
+                               return;
+                       }
+               }
                if (ino == EXT2_BAD_INO) {
                        struct process_block_struct pb;
                        
                        pb.ino = EXT2_BAD_INO;
                        pb.num_blocks = pb.last_block = 0;
                        pb.num_illegal_blocks = 0;
-                       pb.suppress = pb.clear = pb.is_dir = 0;
+                       pb.suppress = 0; pb.clear = 0; pb.is_dir = 0;
                        pb.fragmented = 0;
-                       pb.fix = -1;
                        pb.inode = &inode;
-                       retval = ext2fs_block_iterate(fs, ino, 0, block_buf,
-                                                     process_bad_block, &pb);
-                       if (retval)
-                               com_err(program_name, retval, "while calling e2fsc_block_interate in pass 1");
-
-                       ext2fs_mark_inode_bitmap(inode_used_map, ino);
+                       pb.pctx = &pctx;
+                       pb.ctx = ctx;
+                       pctx.errcode = ext2fs_block_iterate2(fs, ino, 0, 
+                                    block_buf, process_bad_block, &pb);
+                       if (pctx.errcode) {
+                               fix_problem(ctx, PR_1_BLOCK_ITERATE, &pctx);
+                               ctx->flags |= E2F_FLAG_ABORT;
+                               return;
+                       }
+                       ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino);
+                       clear_problem_context(&pctx);
                        goto next;
                }
                if (ino == EXT2_ROOT_INO) {
@@ -269,16 +308,14 @@ void pass1(ext2_filsys fs)
                         * regnerated in pass #3.
                         */
                        if (!LINUX_S_ISDIR(inode.i_mode)) {
-                               printf("Root inode is not a directory.  ");
-                               preenhalt(fs);
-                               if (ask("Clear", 1)) {
+                               if (fix_problem(ctx, PR_1_ROOT_NO_DIR, &pctx)) {
                                        inode.i_dtime = time(0);
                                        inode.i_links_count = 0;
-                                       inode_link_info[ino] = 0;
-                                       e2fsck_write_inode(fs, ino, &inode,
+                                       ext2fs_icount_store(ctx->inode_link_info,
+                                                           ino, 0);
+                                       e2fsck_write_inode(ctx, ino, &inode,
                                                           "pass1");
-                               } else 
-                                       ext2fs_unmark_valid(fs);
+                               }
                        }
                        /*
                         * If dtime is set, offer to clear it.  mke2fs
@@ -290,34 +327,28 @@ void pass1(ext2_filsys fs)
                         * as a special case.
                         */
                        if (inode.i_dtime && inode.i_links_count) {
-                               if (ask("Root inode has dtime set "
-                                       "(probably due to old mke2fs).  Fix",
-                                       1)) {
+                               if (fix_problem(ctx, PR_1_ROOT_DTIME, &pctx)) {
                                        inode.i_dtime = 0;
-                                       e2fsck_write_inode(fs, ino, &inode,
+                                       e2fsck_write_inode(ctx, ino, &inode,
                                                           "pass1");
-                               } else
-                                       ext2fs_unmark_valid(fs);
+                               }
                        }
                }
-               if (ino == EXT2_BOOT_LOADER_INO) {
-                       ext2fs_mark_inode_bitmap(inode_used_map, ino);
-                       check_blocks(fs, ino, &inode, block_buf);
-                       goto next;
-               }
                if ((ino != EXT2_ROOT_INO) &&
                    (ino < EXT2_FIRST_INODE(fs->super))) {
-                       ext2fs_mark_inode_bitmap(inode_used_map, ino);
-                       if (inode.i_mode != 0) {
-                               printf("Reserved inode %lu has bad mode.  ", ino);
-                               if (ask("Clear", 1)) {
+                       ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino);
+                       if (((ino == EXT2_BOOT_LOADER_INO) &&
+                            LINUX_S_ISDIR(inode.i_mode)) ||
+                           ((ino != EXT2_BOOT_LOADER_INO) &&
+                            (inode.i_mode != 0))) {
+                               if (fix_problem(ctx,
+                                           PR_1_RESERVED_BAD_MODE, &pctx)) {
                                        inode.i_mode = 0;
-                                       e2fsck_write_inode(fs, ino, &inode,
+                                       e2fsck_write_inode(ctx, ino, &inode,
                                                           "pass1");
-                               } else 
-                                       ext2fs_unmark_valid(fs);
+                               }
                        }
-                       check_blocks(fs, ino, &inode, block_buf);
+                       check_blocks(ctx, &pctx, block_buf);
                        goto next;
                }
                /*
@@ -326,14 +357,12 @@ void pass1(ext2_filsys fs)
                 */
                if (!inode.i_links_count) {
                        if (!inode.i_dtime && inode.i_mode) {
-                               printf("Deleted inode %lu has zero dtime.\n",
-                                      ino);
-                               if (ask("Set dtime", 1)) {
+                               if (fix_problem(ctx,
+                                           PR_1_ZERO_DTIME, &pctx)) {
                                        inode.i_dtime = time(0);
-                                       e2fsck_write_inode(fs, ino, &inode,
+                                       e2fsck_write_inode(ctx, ino, &inode,
                                                           "pass1");
-                               } else
-                                       ext2fs_unmark_valid(fs);
+                               }
                        }
                        goto next;
                }
@@ -348,16 +377,13 @@ void pass1(ext2_filsys fs)
                 * 
                 */
                if (inode.i_dtime) {
-                       printf("Inode %lu is in use, but has dtime set\n",
-                              ino);
-                       if (ask("Clear dtime", 1)) {
+                       if (fix_problem(ctx, PR_1_SET_DTIME, &pctx)) {
                                inode.i_dtime = 0;
-                               e2fsck_write_inode(fs, ino, &inode, "pass1");
-                       } else
-                               ext2fs_unmark_valid(fs);
+                               e2fsck_write_inode(ctx, ino, &inode, "pass1");
+                       }
                }
                
-               ext2fs_mark_inode_bitmap(inode_used_map, ino);
+               ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino);
                switch (fs->super->s_creator_os) {
                    case EXT2_OS_LINUX:
                        frag = inode.osd2.linux2.l_i_frag;
@@ -376,42 +402,49 @@ void pass1(ext2_filsys fs)
                }
                
                if (inode.i_faddr || frag || fsize
-                   || inode.i_file_acl || inode.i_dir_acl) {
-                       if (!inode_bad_map)
-                               alloc_bad_map(fs);
-                       ext2fs_mark_inode_bitmap(inode_bad_map, ino);
+                   || inode.i_file_acl ||
+                   (LINUX_S_ISDIR(inode.i_mode) && inode.i_dir_acl)) {
+                       if (!ctx->inode_bad_map)
+                               alloc_bad_map(ctx);
+                       ext2fs_mark_inode_bitmap(ctx->inode_bad_map, ino);
                }
                
                if (LINUX_S_ISDIR(inode.i_mode)) {
-                       ext2fs_mark_inode_bitmap(inode_dir_map, ino);
-                       add_dir_info(fs, ino, 0, &inode);
-                       fs_directory_count++;
+                       ext2fs_mark_inode_bitmap(ctx->inode_dir_map, ino);
+                       e2fsck_add_dir_info(ctx, ino, 0);
+                       ctx->fs_directory_count++;
                } else if (LINUX_S_ISREG (inode.i_mode))
-                       fs_regular_count++;
-               else if (LINUX_S_ISCHR (inode.i_mode))
-                       fs_chardev_count++;
-               else if (LINUX_S_ISBLK (inode.i_mode))
-                       fs_blockdev_count++;
+                       ctx->fs_regular_count++;
+               else if (LINUX_S_ISCHR (inode.i_mode) &&
+                        e2fsck_pass1_check_device_inode(&inode))
+                       ctx->fs_chardev_count++;
+               else if (LINUX_S_ISBLK (inode.i_mode) &&
+                        e2fsck_pass1_check_device_inode(&inode))
+                       ctx->fs_blockdev_count++;
                else if (LINUX_S_ISLNK (inode.i_mode)) {
-                       fs_symlinks_count++;
-                       if (!inode.i_blocks)
-                               fs_fast_symlinks_count++;
+                       ctx->fs_symlinks_count++;
+                       if (!inode.i_blocks) {
+                               ctx->fs_fast_symlinks_count++;
+                               goto next;
+                       }
                }
-               else if (LINUX_S_ISFIFO (inode.i_mode))
-                       fs_fifo_count++;
-               else if (LINUX_S_ISSOCK (inode.i_mode))
-                       fs_sockets_count++;
+               else if (LINUX_S_ISFIFO (inode.i_mode) &&
+                         e2fsck_pass1_check_device_inode(&inode))
+                       ctx->fs_fifo_count++;
+               else if ((LINUX_S_ISSOCK (inode.i_mode)) &&
+                        e2fsck_pass1_check_device_inode(&inode))
+                       ctx->fs_sockets_count++;
                else {
-                       if (!inode_bad_map)
-                               alloc_bad_map(fs);
-                       ext2fs_mark_inode_bitmap(inode_bad_map, ino);
+                       if (!ctx->inode_bad_map)
+                               alloc_bad_map(ctx);
+                       ext2fs_mark_inode_bitmap(ctx->inode_bad_map, ino);
                }
                if (inode.i_block[EXT2_IND_BLOCK])
-                       fs_ind_count++;
+                       ctx->fs_ind_count++;
                if (inode.i_block[EXT2_DIND_BLOCK])
-                       fs_dind_count++;
+                       ctx->fs_dind_count++;
                if (inode.i_block[EXT2_TIND_BLOCK])
-                       fs_tind_count++;
+                       ctx->fs_tind_count++;
                if (inode.i_block[EXT2_IND_BLOCK] ||
                    inode.i_block[EXT2_DIND_BLOCK] ||
                    inode.i_block[EXT2_TIND_BLOCK]) {
@@ -419,55 +452,87 @@ void pass1(ext2_filsys fs)
                        inodes_to_process[process_inode_count].inode = inode;
                        process_inode_count++;
                } else
-                       check_blocks(fs, ino, &inode, block_buf);
+                       check_blocks(ctx, &pctx, block_buf);
 
-               if (process_inode_count >= process_inode_size)
-                       process_inodes(fs, block_buf);
+               if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+                       return;
+
+               if (process_inode_count >= ctx->process_inode_size) {
+                       process_inodes(ctx, block_buf);
+
+                       if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+                               return;
+               }
        next:
-               retval = ext2fs_get_next_inode(scan, &ino, &inode);
-               if (retval) {
-                       com_err(program_name, retval,
-                               "while doing inode scan");
-                       fatal_error(0);
+               pctx.errcode = ext2fs_get_next_inode(scan, &ino, &inode);
+               if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+                       return;
+               if (pctx.errcode == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE) {
+                       if (!ctx->inode_bb_map)
+                               alloc_bb_map(ctx);
+                       ext2fs_mark_inode_bitmap(ctx->inode_bb_map, ino);
+                       ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino);
+                       goto next;
+               }
+               if (pctx.errcode) {
+                       fix_problem(ctx, PR_1_ISCAN_ERROR, &pctx);
+                       ctx->flags |= E2F_FLAG_ABORT;
+                       return;
                }
        }
-       process_inodes(fs, block_buf);
+       process_inodes(ctx, block_buf);
        ext2fs_close_inode_scan(scan);
        ehandler_operation(0);
 
-       qsort(dir_blocks, dir_block_count, sizeof(struct dir_block_struct),
-             dir_block_cmp);
-
-       if (invalid_bitmaps)
-               handle_fs_bad_blocks(fs);
+       if (ctx->invalid_bitmaps)
+               handle_fs_bad_blocks(ctx);
 
-       if (restart_e2fsck) {
+       if (ctx->flags & E2F_FLAG_RESTART) {
                unwind_pass1(fs);
                goto endit;
        }
 
-       if (block_dup_map) {
-               if (preen) {
-                       printf("Duplicate or bad blocks in use!\n");
-                       preenhalt(fs);
+       if (ctx->block_dup_map) {
+               if (ctx->options & E2F_OPT_PREEN) {
+                       clear_problem_context(&pctx);
+                       fix_problem(ctx, PR_1_DUP_BLOCKS_PREENSTOP, &pctx);
                }
-               pass1_dupblocks(fs, block_buf);
+               e2fsck_pass1_dupblocks(ctx, block_buf);
        }
-       free(inodes_to_process);
+       ext2fs_free_mem((void **) &inodes_to_process);
 endit:
        fs->get_blocks = 0;
        fs->check_directory = 0;
        fs->read_inode = 0;
        fs->write_inode = 0;
        
-       free(block_buf);
-       ext2fs_free_block_bitmap(block_illegal_map);
-       block_illegal_map = 0;
-       
-       if (tflag > 1) {
-               printf("Pass 1: ");
-               print_resource_track(&rtrack);
+       ext2fs_free_mem((void **) &block_buf);
+       ext2fs_free_block_bitmap(ctx->block_illegal_map);
+       ctx->block_illegal_map = 0;
+
+       sb = (struct ext2fs_sb *) fs->super;
+       if (ctx->large_files && 
+           !(sb->s_feature_ro_compat & 
+             EXT2_FEATURE_RO_COMPAT_LARGE_FILE)) {
+               if (fix_problem(ctx, PR_1_FEATURE_LARGE_FILES, &pctx)) {
+                       sb->s_feature_ro_compat |= 
+                               EXT2_FEATURE_RO_COMPAT_LARGE_FILE;
+                       ext2fs_mark_super_dirty(fs);
+               }
+       } else if (!ctx->large_files &&
+           (sb->s_feature_ro_compat &
+             EXT2_FEATURE_RO_COMPAT_LARGE_FILE)) {
+               if (fs->flags & EXT2_FLAG_RW) {
+                       sb->s_feature_ro_compat &= 
+                               ~EXT2_FEATURE_RO_COMPAT_LARGE_FILE;
+                       ext2fs_mark_super_dirty(fs);
+               }
        }
+       
+#ifdef RESOURCE_TRACK
+       if (ctx->options & E2F_OPT_TIME2)
+               print_resource_track("Pass 1", &rtrack);
+#endif
 }
 
 /*
@@ -475,43 +540,60 @@ endit:
  * glock group, call process_inodes.
  */
 static errcode_t scan_callback(ext2_filsys fs, ext2_inode_scan scan,
-                              dgrp_t group, void * private)
+                              dgrp_t group, void * priv_data)
 {
-       process_inodes(fs, (char *) private);
+       struct scan_callback_struct *scan_struct;
+       e2fsck_t ctx;
+
+       scan_struct = (struct scan_callback_struct *) priv_data;
+       ctx = scan_struct->ctx;
+       
+       process_inodes((e2fsck_t) fs->priv_data, scan_struct->block_buf);
+
+       if (ctx->progress)
+               if ((ctx->progress)(ctx, 1, group+1,
+                                   ctx->fs->group_desc_count))
+                       return EXT2_ET_CANCEL_REQUESTED;
+
        return 0;
 }
 
 /*
  * Process the inodes in the "inodes to process" list.
  */
-static void process_inodes(ext2_filsys fs, char *block_buf)
+static void process_inodes(e2fsck_t ctx, char *block_buf)
 {
        int                     i;
        struct ext2_inode       *old_stashed_inode;
-       ino_t                   ino;
+       ino_t                   old_stashed_ino;
        const char              *old_operation;
        char                    buf[80];
-
+       struct problem_context  pctx;
+       
 #if 0
        printf("begin process_inodes: ");
 #endif
        old_operation = ehandler_operation(0);
-       old_stashed_inode = stashed_inode;
+       old_stashed_inode = ctx->stashed_inode;
+       old_stashed_ino = ctx->stashed_ino;
        qsort(inodes_to_process, process_inode_count,
                      sizeof(struct process_inode_block), process_inode_cmp);
+       clear_problem_context(&pctx);
        for (i=0; i < process_inode_count; i++) {
-               stashed_inode = &inodes_to_process[i].inode;
-               ino = inodes_to_process[i].ino;
-               stashed_ino = ino;
+               pctx.inode = ctx->stashed_inode = &inodes_to_process[i].inode;
+               pctx.ino = ctx->stashed_ino = inodes_to_process[i].ino;
+               
 #if 0
-               printf("%u ", ino);
+               printf("%u ", pctx.ino);
 #endif
-               sprintf(buf, "reading indirect blocks of inode %lu", ino);
+               sprintf(buf, "reading indirect blocks of inode %lu", pctx.ino);
                ehandler_operation(buf);
-               check_blocks(fs, ino, stashed_inode, block_buf);
-               
+               check_blocks(ctx, &pctx, block_buf);
+               if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+                       break;
        }
-       stashed_inode = old_stashed_inode;
+       ctx->stashed_inode = old_stashed_inode;
+       ctx->stashed_ino = old_stashed_ino;
        process_inode_count = 0;
 #if 0
        printf("end process inodes\n");
@@ -519,7 +601,7 @@ static void process_inodes(ext2_filsys fs, char *block_buf)
        ehandler_operation(old_operation);
 }
 
-static int process_inode_cmp(const void *a, const void *b)
+static EXT2_QSORT_TYPE process_inode_cmp(const void *a, const void *b)
 {
        const struct process_inode_block *ib_a =
                (const struct process_inode_block *) a;
@@ -530,35 +612,43 @@ static int process_inode_cmp(const void *a, const void *b)
                ib_b->inode.i_block[EXT2_IND_BLOCK]);
 }
 
-static int dir_block_cmp(const void *a, const void *b)
+/*
+ * This procedure will allocate the inode bad map table
+ */
+static void alloc_bad_map(e2fsck_t ctx)
 {
-       const struct dir_block_struct *db_a =
-               (const struct dir_block_struct *) a;
-       const struct dir_block_struct *db_b =
-               (const struct dir_block_struct *) b;
-
-       if (db_a->blk != db_b->blk)
-               return (db_a->blk - db_b->blk);
+       struct          problem_context pctx;
        
-       if (db_a->ino != db_b->ino)
-               return (db_a->ino - db_b->ino);
-
-       return (db_a->blockcnt - db_b->blockcnt);
+       clear_problem_context(&pctx);
+       
+       pctx.errcode = ext2fs_allocate_inode_bitmap(ctx->fs, "bad inode map",
+                                             &ctx->inode_bad_map);
+       if (pctx.errcode) {
+               pctx.num = 3;
+               fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
+               /* Should never get here */
+               ctx->flags |= E2F_FLAG_ABORT;
+               return;
+       }
 }
 
 /*
- * This procedure will allocate the inode bad map table
+ * This procedure will allocate the inode "bb" (badblock) map table
  */
-static void alloc_bad_map(ext2_filsys fs)
+static void alloc_bb_map(e2fsck_t ctx)
 {
-       errcode_t       retval;
+       struct          problem_context pctx;
        
-       retval = ext2fs_allocate_inode_bitmap(fs, "bad inode map",
-                                             &inode_bad_map);
-       if (retval) {
-               com_err("ext2fs_allocate_inode_bitmap", retval,
-                       "while allocating inode_bad_map");
-               fatal_error(0);
+       clear_problem_context(&pctx);
+       pctx.errcode = ext2fs_allocate_inode_bitmap(ctx->fs,
+                                             "inode in bad block map",
+                                             &ctx->inode_bb_map);
+       if (pctx.errcode) {
+               pctx.num = 4;
+               fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
+               /* Should never get here */
+               ctx->flags |= E2F_FLAG_ABORT;
+               return;
        }
 }
 
@@ -569,23 +659,29 @@ static void alloc_bad_map(ext2_filsys fs)
  * WARNING: Assumes checks have already been done to make sure block
  * is valid.  This is true in both process_block and process_bad_block.
  */
-static _INLINE_ void mark_block_used(ext2_filsys fs, blk_t block)
+static _INLINE_ void mark_block_used(e2fsck_t ctx, blk_t block)
 {
-       errcode_t       retval;
+       struct          problem_context pctx;
+       
+       clear_problem_context(&pctx);
        
-       if (ext2fs_fast_test_block_bitmap(block_found_map, block)) {
-               if (!block_dup_map) {
-                       retval = ext2fs_allocate_block_bitmap(fs,
-                             "multiply claimed block map", &block_dup_map);
-                       if (retval) {
-                               com_err("ext2fs_allocate_block_bitmap", retval,
-                                       "while allocating block_dup_map");
-                               fatal_error(0);
+       if (ext2fs_fast_test_block_bitmap(ctx->block_found_map, block)) {
+               if (!ctx->block_dup_map) {
+                       pctx.errcode = ext2fs_allocate_block_bitmap(ctx->fs,
+                             "multiply claimed block map",
+                             &ctx->block_dup_map);
+                       if (pctx.errcode) {
+                               pctx.num = 3;
+                               fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR, 
+                                           &pctx);
+                               /* Should never get here */
+                               ctx->flags |= E2F_FLAG_ABORT;
+                               return;
                        }
                }
-               ext2fs_fast_mark_block_bitmap(block_dup_map, block);
+               ext2fs_fast_mark_block_bitmap(ctx->block_dup_map, block);
        } else {
-               ext2fs_fast_mark_block_bitmap(block_found_map, block);
+               ext2fs_fast_mark_block_bitmap(ctx->block_found_map, block);
        }
 }
 
@@ -593,101 +689,119 @@ static _INLINE_ void mark_block_used(ext2_filsys fs, blk_t block)
  * This subroutine is called on each inode to account for all of the
  * blocks used by that inode.
  */
-static void check_blocks(ext2_filsys fs, ino_t ino, struct ext2_inode *inode,
+static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
                         char *block_buf)
 {
+       ext2_filsys fs = ctx->fs;
        struct process_block_struct pb;
-       errcode_t       retval;
+       ino_t           ino = pctx->ino;
+       struct ext2_inode *inode = pctx->inode;
+       int             bad_size = 0;
+       __u64           size;
+       struct ext2fs_sb        *sb;
        
-       if (!inode_has_valid_blocks(inode))
+       if (!ext2fs_inode_has_valid_blocks(pctx->inode))
                return;
        
        pb.ino = ino;
        pb.num_blocks = pb.last_block = 0;
        pb.num_illegal_blocks = 0;
-       pb.suppress = pb.clear = 0;
+       pb.suppress = 0; pb.clear = 0;
        pb.fragmented = 0;
        pb.previous_block = 0;
-       pb.is_dir = LINUX_S_ISDIR(inode->i_mode);
-       pb.fix = -1;
+       pb.is_dir = LINUX_S_ISDIR(pctx->inode->i_mode);
        pb.inode = inode;
-       retval = ext2fs_block_iterate(fs, ino,
-                                     pb.is_dir ? BLOCK_FLAG_HOLE : 0,
-                                     block_buf, process_block, &pb);
-       if (retval)
-               com_err(program_name, retval,
-                       "while calling ext2fs_block_iterate in check_blocks");
+       pb.pctx = pctx;
+       pb.ctx = ctx;
+       pctx->ino = ino;
+       pctx->errcode = ext2fs_block_iterate2(fs, ino,
+                                      pb.is_dir ? BLOCK_FLAG_HOLE : 0,
+                                      block_buf, process_block, &pb);
+       if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+               return;
+       end_problem_latch(ctx, PR_LATCH_BLOCK);
+       if (pctx->errcode)
+               fix_problem(ctx, PR_1_BLOCK_ITERATE, pctx);
 
        if (pb.fragmented && pb.num_blocks < fs->super->s_blocks_per_group)
-               fs_fragmented++;
+               ctx->fs_fragmented++;
 
        if (pb.clear) {
-               e2fsck_read_inode(fs, ino, inode, "check_blocks");
-               if (retval) {
-                       com_err("check_blocks", retval,
-                               "while reading to be cleared inode %d", ino);
-                       fatal_error(0);
-               }
+               e2fsck_read_inode(ctx, ino, inode, "check_blocks");
                inode->i_links_count = 0;
-               inode_link_info[ino] = 0;
+               ext2fs_icount_store(ctx->inode_link_info, ino, 0);
                inode->i_dtime = time(0);
-               e2fsck_write_inode(fs, ino, inode, "check_blocks");
-               ext2fs_unmark_inode_bitmap(inode_dir_map, ino);
-               ext2fs_unmark_inode_bitmap(inode_used_map, ino);
+               e2fsck_write_inode(ctx, ino, inode, "check_blocks");
+               ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, ino);
+               ext2fs_unmark_inode_bitmap(ctx->inode_used_map, ino);
                /*
                 * The inode was probably partially accounted for
                 * before processing was aborted, so we need to
                 * restart the pass 1 scan.
                 */
-               restart_e2fsck++;
+               ctx->flags |= E2F_FLAG_RESTART;
                return;
        }
 
-       if (pb.fix > 0)
-               e2fsck_read_inode(fs, ino, inode, "check_blocks");
-
        pb.num_blocks *= (fs->blocksize / 512);
 #if 0
-       printf("inode %u, i_size = %lu, last_block = %lu, i_blocks=%lu, num_blocks = %lu\n",
+       printf("inode %u, i_size = %lu, last_block = %lld, i_blocks=%lu, num_blocks = %lu\n",
               ino, inode->i_size, pb.last_block, inode->i_blocks,
               pb.num_blocks);
 #endif
        if (!pb.num_blocks && pb.is_dir) {
-               printf("Inode %lu is a zero length directory.  ", ino);
-               if (ask("Clear", 1)) {
+               if (fix_problem(ctx, PR_1_ZERO_LENGTH_DIR, pctx)) {
                        inode->i_links_count = 0;
-                       inode_link_info[ino] = 0;
+                       ext2fs_icount_store(ctx->inode_link_info, ino, 0);
                        inode->i_dtime = time(0);
-                       e2fsck_write_inode(fs, ino, inode, "check_blocks");
-                       ext2fs_unmark_inode_bitmap(inode_dir_map, ino);
-                       ext2fs_unmark_inode_bitmap(inode_used_map, ino);
-                       fs_directory_count--;
+                       e2fsck_write_inode(ctx, ino, inode, "check_blocks");
+                       ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, ino);
+                       ext2fs_unmark_inode_bitmap(ctx->inode_used_map, ino);
+                       ctx->fs_directory_count--;
                        pb.is_dir = 0;
-               } else
-                       ext2fs_unmark_valid(fs);
+               }
        }
-       if ((pb.is_dir && (inode->i_size != (pb.last_block + 1) * fs->blocksize)) ||
-           (inode->i_size < pb.last_block * fs->blocksize)) {
-               printf ("%s %lu, incorrect size, %u (counted = %u). ",
-                       pb.is_dir ? "Directory" : "Inode", ino, inode->i_size,
-                       (pb.last_block+1) * fs->blocksize);
-               if (ask ("Set size to counted", 1)) {
-                       inode->i_size = (pb.last_block+1) * fs->blocksize;
-                       e2fsck_write_inode(fs, ino, inode, "check_blocks");
-               } else
-                       ext2fs_unmark_valid(fs);
+       if (pb.is_dir) {
+               int nblock = inode->i_size >> EXT2_BLOCK_SIZE_BITS(fs->super);
+               if ((nblock > (pb.last_block + 1)) ||
+                   ((inode->i_size & (fs->blocksize-1)) != 0))
+                       bad_size = 1;
+               else if (nblock < (pb.last_block + 1)) {
+                       sb = (struct ext2fs_sb *) fs->super;
+                       if (((pb.last_block + 1) - nblock) >
+                           sb->s_prealloc_dir_blocks)
+                               bad_size = 2;
+               }
+       } else {
+               size = inode->i_size + ((__u64) inode->i_size_high << 32);
+               if ((size < pb.last_block * fs->blocksize))
+                       bad_size = 3;
+               else if (size > ext2_max_sizes[fs->super->s_log_block_size])
+                       bad_size = 4;
+       }
+       if (bad_size) {
+               pctx->num = (pb.last_block+1) * fs->blocksize;
+               if (fix_problem(ctx, PR_1_BAD_I_SIZE, pctx)) {
+                       inode->i_size = pctx->num;
+                       if (!pb.is_dir)
+                               inode->i_size_high = pctx->num >> 32;
+                       e2fsck_write_inode(ctx, ino, inode, "check_blocks");
+               }
+               pctx->num = 0;
        }
+       if (!pb.is_dir && inode->i_size_high)
+               ctx->large_files++;
        if (pb.num_blocks != inode->i_blocks) {
-               printf ("Inode %lu, i_blocks wrong %u (counted=%u).  ",
-                       ino, inode->i_blocks, pb.num_blocks);
-               if (ask ("Set i_blocks to counted", 1)) {
+               pctx->num = pb.num_blocks;
+               if (fix_problem(ctx, PR_1_BAD_I_BLOCKS, pctx)) {
                        inode->i_blocks = pb.num_blocks;
-                       e2fsck_write_inode(fs, ino, inode, "check_blocks");
-               } else
-                               ext2fs_unmark_valid(fs);
+                       e2fsck_write_inode(ctx, ino, inode, "check_blocks");
+               }
+               pctx->num = 0;
        }
 }
 
+#if 0
 /*
  * Helper function called by process block when an illegal block is
  * found.  It returns a description about why the block is illegal
@@ -737,21 +851,28 @@ static char *describe_illegal_block(ext2_filsys fs, blk_t block)
        }
        return(problem);
 }
+#endif
 
 /*
  * This is a helper function for check_blocks().
  */
 int process_block(ext2_filsys fs,
                  blk_t *block_nr,
-                 int blockcnt,
-                 void *private)
+                 e2_blkcnt_t blockcnt,
+                 blk_t ref_block,
+                 int ref_offset, 
+                 void *priv_data)
 {
        struct process_block_struct *p;
-       char    *problem;
+       struct problem_context *pctx;
        blk_t   blk = *block_nr;
        int     ret_code = 0;
+       int     problem = 0;
+       e2fsck_t        ctx;
 
-       p = (struct process_block_struct *) private;
+       p = (struct process_block_struct *) priv_data;
+       pctx = p->pctx;
+       ctx = p->ctx;
 
        if (blk == 0) {
                if (p->is_dir == 0) {
@@ -759,16 +880,20 @@ int process_block(ext2_filsys fs,
                         * Should never happen, since only directories
                         * get called with BLOCK_FLAG_HOLE
                         */
+#if DEBUG_E2FSCK
                        printf("process_block() called with blk == 0, "
                               "blockcnt=%d, inode %lu???\n",
                               blockcnt, p->ino);
+#endif
                        return 0;
                }
                if (blockcnt < 0)
                        return 0;
                if (blockcnt * fs->blocksize < p->inode->i_size) {
-                       printf("Hole found in directory inode %lu!  "
-                              "(blkcnt=%d)\n", p->ino, blockcnt);
+#if 0
+                       printf("Missing block (#%d) in directory inode %lu!\n",
+                              blockcnt, p->ino);
+#endif
                        goto mark_dir;
                }
                return 0;
@@ -790,151 +915,127 @@ int process_block(ext2_filsys fs,
        }
        p->previous_block = blk;
        
-       
        if (blk < fs->super->s_first_data_block ||
-           blk >= fs->super->s_blocks_count ||
-           ext2fs_test_block_bitmap(block_illegal_map, blk)) {
-               problem = describe_illegal_block(fs, blk);
-               if (preen) {
-                       printf("Block %u of inode %lu %s\n", blk, p->ino,
-                              problem);
-                       preenhalt(fs);
-               }
-               if (p->fix == -1) {
-                       printf("Remove illegal block(s) in inode %lu", p->ino);
-                       p->fix = ask("", 1);
-               }
+           blk >= fs->super->s_blocks_count)
+               problem = PR_1_ILLEGAL_BLOCK_NUM;
+#if 0
+       else
+               if (ext2fs_test_block_bitmap(block_illegal_map, blk))
+                       problem = PR_1_BLOCK_OVERLAPS_METADATA;
+#endif
+
+       if (problem) {
                p->num_illegal_blocks++;
-               if (!p->suppress && (p->num_illegal_blocks % 20) == 0) {
-                       printf("Too many illegal blocks in inode %lu.\n",
-                              p->ino);
-                       if (ask("Clear inode", 1)) {
+               if (!p->suppress && (p->num_illegal_blocks % 12) == 0) {
+                       if (fix_problem(ctx, PR_1_TOO_MANY_BAD_BLOCKS, pctx)) {
                                p->clear = 1;
                                return BLOCK_ABORT;
                        }
-                       if (ask("Supress messages", 0)) {
+                       if (fix_problem(ctx, PR_1_SUPPRESS_MESSAGES, pctx)) {
                                p->suppress = 1;
+                               set_latch_flags(PR_LATCH_BLOCK,
+                                               PRL_SUPPRESS, 0);
                        }
                }
-               if (!p->suppress)
-                       printf("Block #%d (%u) %s.  %s\n", blockcnt, blk,
-                              problem, clear_msg[p->fix]);
-               if (p->fix) {
+               pctx->blk = blk;
+               pctx->blkcount = blockcnt;
+               if (fix_problem(ctx, problem, pctx)) {
                        blk = *block_nr = 0;
                        ret_code = BLOCK_CHANGED;
                        goto mark_dir;
-               } else {
-                       ext2fs_unmark_valid(fs);
+               } else
                        return 0;
-               }
+               pctx->blk = 0;
+               pctx->blkcount = -1;
        }
 
-       mark_block_used(fs, blk);
+       mark_block_used(ctx, blk);
        p->num_blocks++;
        if (blockcnt >= 0)
                p->last_block = blockcnt;
 mark_dir:
        if (p->is_dir && (blockcnt >= 0)) {
-               if (dir_block_count >= dir_block_size) {
-                       dir_block_size += 100;
-                       dir_blocks = realloc(dir_blocks,
-                                            dir_block_size *
-                                            sizeof(struct dir_block_struct));
-                       if (dir_blocks == 0)
-                               fatal_error("Not enough memory to "
-                                           "realloc dir_blocks");
+               pctx->errcode = ext2fs_add_dir_block(fs->dblist, p->ino,
+                                                   blk, blockcnt);
+               if (pctx->errcode) {
+                       pctx->blk = blk;
+                       pctx->num = blockcnt;
+                       fix_problem(ctx, PR_1_ADD_DBLOCK, pctx);
+                       /* Should never get here */
+                       ctx->flags |= E2F_FLAG_ABORT;
+                       return BLOCK_ABORT;
                }
-
-               dir_blocks[dir_block_count].blk = blk;
-               dir_blocks[dir_block_count].ino = p->ino;
-               dir_blocks[dir_block_count].blockcnt = blockcnt;
-               dir_block_count++;
        }
        return ret_code;
 }
 
-static void bad_block_indirect(ext2_filsys fs, blk_t blk)
+static void bad_block_indirect(e2fsck_t ctx, blk_t blk)
 {
-       printf("Bad block %u used as bad block indirect block?!?\n", blk);
-       preenhalt(fs);
-       printf("\nThis inconsistency can not be fixed with "
-              "e2fsck; to fix it, use\n"
-              """dumpe2fs -b"" to dump out the bad block "
-              "list and ""e2fsck -L filename""\n"
-              "to read it back in again.\n");
-       if (ask("Continue", 0))
-               return;
-       fatal_error(0);
-}
+       struct problem_context pctx;
 
-static int bad_primary_block(ext2_filsys fs, blk_t *block_nr)
-{
-       printf("\nIf the block is really bad, the filesystem can not be "
-              "fixed.\n");
-       preenhalt(fs);
-       printf("You can clear the this block from the bad block list\n");
-       printf("and hope that block is really OK, but there are no "
-              "guarantees.\n\n");
-       if (ask("Clear (and hope for the best)", 1)) {
-               *block_nr = 0;
-               return 1;
-       }
-       ext2fs_unmark_valid(fs);
-       return 0;
+       clear_problem_context(&pctx);
+       /*
+        * Prompt to see if we should continue or not.
+        */
+       if (!fix_problem(ctx, PR_1_BBINODE_BAD_METABLOCK, &pctx))
+               ctx->flags |= E2F_FLAG_ABORT;
 }
 
 int process_bad_block(ext2_filsys fs,
                      blk_t *block_nr,
-                     int blockcnt,
-                     void *private)
+                     e2_blkcnt_t blockcnt,
+                     blk_t ref_block,
+                     int ref_offset,
+                     void *priv_data)
 {
        struct process_block_struct *p;
        blk_t           blk = *block_nr;
        int             first_block;
        int             i;
-       
+       struct problem_context *pctx;
+       e2fsck_t        ctx;
+
        if (!blk)
                return 0;
-       p = (struct process_block_struct *) private;
+       
+       p = (struct process_block_struct *) priv_data;
+       ctx = p->ctx;
+       pctx = p->pctx;
+       
+       pctx->ino = EXT2_BAD_INO;
+       pctx->blk = blk;
+       pctx->blkcount = blockcnt;
 
        if ((blk < fs->super->s_first_data_block) ||
            (blk >= fs->super->s_blocks_count)) {
-               if (preen) {
-                       printf("Illegal block %u in bad block inode\n", blk);
-                       preenhalt(fs);
-               }
-               if (p->fix == -1)
-                       p->fix = ask("Remove illegal block(s) in bad block inode", 1);
-               printf("Illegal block %u in bad block inode.  %s\n", blk,
-                      clear_msg[p->fix]);
-               if (p->fix) {
+               if (fix_problem(ctx, PR_1_BB_ILLEGAL_BLOCK_NUM, pctx)) {
                        *block_nr = 0;
                        return BLOCK_CHANGED;
-               } else {
-                       ext2fs_unmark_valid(fs);
+               } else
                        return 0;
-               }
        }
 
        if (blockcnt < 0) {
-               if (ext2fs_test_block_bitmap(block_found_map, blk))
-                       bad_block_indirect(fs, blk);
-               else
-                       mark_block_used(fs, blk);
+               if (ext2fs_test_block_bitmap(ctx->block_found_map, blk)) {
+                       bad_block_indirect(ctx, blk);
+                       if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+                               return BLOCK_ABORT;
+               } else
+                       mark_block_used(ctx, blk);
                return 0;
        }
 #if 0 
        printf ("DEBUG: Marking %u as bad.\n", blk);
 #endif
-       fs_badblocks_count++;
+       ctx->fs_badblocks_count++;
        /*
         * If the block is not used, then mark it as used and return.
         * If it is already marked as found, this must mean that
         * there's an overlap between the filesystem table blocks
         * (bitmaps and inode table) and the bad block list.
         */
-       if (!ext2fs_test_block_bitmap(block_found_map, blk)) {
-               ext2fs_mark_block_bitmap(block_found_map, blk);
+       if (!ext2fs_test_block_bitmap(ctx->block_found_map, blk)) {
+               ext2fs_mark_block_bitmap(ctx->block_found_map, blk);
                return 0;
        }
        /*
@@ -943,66 +1044,60 @@ int process_bad_block(ext2_filsys fs,
        first_block = fs->super->s_first_data_block;
        
        for (i = 0; i < fs->group_desc_count; i++ ) {
+               pctx->group = i;
+               pctx->blk = blk;
+               if (!ext2fs_bg_has_super(fs, i))
+                       goto skip_super;
                if (blk == first_block) {
                        if (i == 0) {
-                               printf("The primary superblock (%u) is "
-                                      "on the bad block list.\n", blk);
-                               if (bad_primary_block(fs, block_nr))
+                               if (fix_problem(ctx,
+                                               PR_1_BAD_PRIMARY_SUPERBLOCK,
+                                               pctx)) {
+                                       *block_nr = 0;
                                        return BLOCK_CHANGED;
+                               }
                                return 0;
                        }
-                       if (!preen)
-                               printf("Warning: Group %d's superblock "
-                                      "(%u) is bad.\n", i, blk);
+                       fix_problem(ctx, PR_1_BAD_SUPERBLOCK, pctx);
                        return 0;
                }
                if ((blk > first_block) &&
                    (blk <= first_block + fs->desc_blocks)) {
                        if (i == 0) {
-                               printf("Block %u in the primary group "
-                                      "descriptors is on the bad block "
-                                      "list\n", blk);
-                               if (bad_primary_block(fs, block_nr))
+                               pctx->blk = *block_nr;
+                               if (fix_problem(ctx,
+                       PR_1_BAD_PRIMARY_GROUP_DESCRIPTOR, pctx)) {
+                                       *block_nr = 0;
                                        return BLOCK_CHANGED;
+                               }
                                return 0;
                        }
-                       if (!preen)
-                               printf("Warning: Group %d's copy of the "
-                                      "group descriptors has a bad "
-                                      "block (%u).\n", i, blk);
+                       fix_problem(ctx, PR_1_BAD_GROUP_DESCRIPTORS, pctx);
                        return 0;
                }
+       skip_super:
                if (blk == fs->group_desc[i].bg_block_bitmap) {
-                       printf("Group %d's block bitmap (%u) is bad.  ",
-                              i, blk);
-                       if (ask("Relocate", 1)) {
-                               invalid_block_bitmap[i]++;
-                               invalid_bitmaps++;
-                       } else
-                               ext2fs_unmark_valid(fs);
+                       if (fix_problem(ctx, PR_1_BB_BAD_BLOCK, pctx)) {
+                               ctx->invalid_block_bitmap_flag[i]++;
+                               ctx->invalid_bitmaps++;
+                       }
                        return 0;
                }
                if (blk == fs->group_desc[i].bg_inode_bitmap) {
-                       printf("Group %d's inode bitmap (%u) is bad.  ",
-                              i, blk);
-                       if (ask("Relocate", 1)) {
-                               invalid_inode_bitmap[i]++;
-                               invalid_bitmaps++;
-                       } else
-                               ext2fs_unmark_valid(fs);
+                       if (fix_problem(ctx, PR_1_IB_BAD_BLOCK, pctx)) {
+                               ctx->invalid_inode_bitmap_flag[i]++;
+                               ctx->invalid_bitmaps++;
+                       }
                        return 0;
                }
                if ((blk >= fs->group_desc[i].bg_inode_table) &&
                    (blk < (fs->group_desc[i].bg_inode_table +
                            fs->inode_blocks_per_group))) {
-                       printf("WARNING: Severe data loss possible!!!!\n");
-                       printf("Bad block %u in group %d's inode table.  ",
-                              blk, i);
-                       if (ask("Relocate", 1)) {
-                               invalid_inode_table[i]++;
-                               invalid_bitmaps++;
-                       } else
-                               ext2fs_unmark_valid(fs);
+                       /*
+                        * If there are bad blocks in the inode table,
+                        * the inode scan code will try to do
+                        * something reasonable automatically.
+                        */
                        return 0;
                }
                first_block += fs->super->s_blocks_per_group;
@@ -1014,64 +1109,73 @@ int process_bad_block(ext2_filsys fs,
         */
        if ((blk == p->inode->i_block[EXT2_IND_BLOCK]) ||
            p->inode->i_block[EXT2_DIND_BLOCK]) {
-               bad_block_indirect(fs, blk);
+               bad_block_indirect(ctx, blk);
+               if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+                       return BLOCK_ABORT;
                return 0;
        }
-       
-       printf("Programming error?  block #%u claimed for no reason "
-              "in process_bad_block.\n", blk);
+
+       pctx->group = -1;
+
+       /* Warn user that the block wasn't claimed */
+       fix_problem(ctx, PR_1_PROGERR_CLAIMED_BLOCK, pctx);
+
        return 0;
 }
 
-static void new_table_block(ext2_filsys fs, blk_t first_block, int group, 
+static void new_table_block(e2fsck_t ctx, blk_t first_block, int group, 
                            const char *name, int num, blk_t *new_block)
 {
-       errcode_t       retval;
+       ext2_filsys fs = ctx->fs;
        blk_t           old_block = *new_block;
        int             i;
        char            *buf;
-       
-       retval = ext2fs_get_free_blocks(fs, first_block,
+       struct problem_context  pctx;
+
+       clear_problem_context(&pctx);
+
+       pctx.group = group;
+       pctx.blk = old_block;
+       pctx.str = name;
+
+       pctx.errcode = ext2fs_get_free_blocks(fs, first_block,
                        first_block + fs->super->s_blocks_per_group,
-                                       num, block_found_map, new_block);
-       if (retval) {
-               printf("Could not allocate %d block(s) for %s: %s\n",
-                      num, name, error_message(retval));
+                                       num, ctx->block_found_map, new_block);
+       if (pctx.errcode) {
+               pctx.num = num;
+               fix_problem(ctx, PR_1_RELOC_BLOCK_ALLOCATE, &pctx);
                ext2fs_unmark_valid(fs);
                return;
        }
-       buf = malloc(fs->blocksize);
-       if (!buf) {
-               printf("Could not allocate block buffer for relocating %s\n",
-                      name);
+       pctx.errcode = ext2fs_get_mem(fs->blocksize, (void **) &buf);
+       if (pctx.errcode) {
+               fix_problem(ctx, PR_1_RELOC_MEMORY_ALLOCATE, &pctx);
                ext2fs_unmark_valid(fs);
                return;
        }
        ext2fs_mark_super_dirty(fs);
-       printf("Relocating group %d's %s ", group, name);
-       if (old_block)
-               printf("from %u ", old_block);
-       printf("to %u...\n", *new_block);
+       pctx.blk2 = *new_block;
+       fix_problem(ctx, (old_block ? PR_1_RELOC_FROM_TO :
+                         PR_1_RELOC_TO), &pctx);
+       pctx.blk2 = 0;
        for (i = 0; i < num; i++) {
-               ext2fs_mark_block_bitmap(block_found_map, (*new_block)+i);
+               pctx.blk = i;
+               ext2fs_mark_block_bitmap(ctx->block_found_map, (*new_block)+i);
                if (old_block) {
-                       retval = io_channel_read_blk(fs->io, old_block + i,
-                                                    1, buf);
-                       if (retval)
-                               printf("Warning: could not read block %u "
-                                      "of %s: %s\n",
-                                      old_block + i, name,
-                                      error_message(retval));
+                       pctx.errcode = io_channel_read_blk(fs->io,
+                                  old_block + i, 1, buf);
+                       if (pctx.errcode)
+                               fix_problem(ctx, PR_1_RELOC_READ_ERR, &pctx);
                } else
                        memset(buf, 0, fs->blocksize);
 
-               retval = io_channel_write_blk(fs->io, (*new_block) + i,
+               pctx.blk = (*new_block) + i;
+               pctx.errcode = io_channel_write_blk(fs->io, pctx.blk,
                                              1, buf);
-               if (retval)
-                       printf("Warning: could not write block %u for %s: %s\n",
-                              (*new_block) + i, name, error_message(retval));
+               if (pctx.errcode)
+                       fix_problem(ctx, PR_1_RELOC_WRITE_ERR, &pctx);
        }
-       free(buf);
+       ext2fs_free_mem((void **) &buf);
 }
 
 /*
@@ -1081,90 +1185,68 @@ static void new_table_block(ext2_filsys fs, blk_t first_block, int group,
  * out, so we can try to allocate new block(s) to replace the bad
  * blocks.
  */
-static void handle_fs_bad_blocks(ext2_filsys fs)
+static void handle_fs_bad_blocks(e2fsck_t ctx)
 {
+       ext2_filsys fs = ctx->fs;
        int             i;
        int             first_block = fs->super->s_first_data_block;
 
        for (i = 0; i < fs->group_desc_count; i++) {
-               if (invalid_block_bitmap[i]) {
-                       new_table_block(fs, first_block, i, "block bitmap", 1
-                                       &fs->group_desc[i].bg_block_bitmap);
+               if (ctx->invalid_block_bitmap_flag[i]) {
+                       new_table_block(ctx, first_block, i, "block bitmap"
+                                       1, &fs->group_desc[i].bg_block_bitmap);
                }
-               if (invalid_inode_bitmap[i]) {
-                       new_table_block(fs, first_block, i, "inode bitmap", 1
-                                       &fs->group_desc[i].bg_inode_bitmap);
+               if (ctx->invalid_inode_bitmap_flag[i]) {
+                       new_table_block(ctx, first_block, i, "inode bitmap"
+                                       1, &fs->group_desc[i].bg_inode_bitmap);
                }
-               if (invalid_inode_table[i]) {
-                       new_table_block(fs, first_block, i, "inode table",
+               if (ctx->invalid_inode_table_flag[i]) {
+                       new_table_block(ctx, first_block, i, "inode table",
                                        fs->inode_blocks_per_group, 
                                        &fs->group_desc[i].bg_inode_table);
-                       restart_e2fsck++;
+                       ctx->flags |= E2F_FLAG_RESTART;
                }
                first_block += fs->super->s_blocks_per_group;
        }
-       invalid_bitmaps = 0;
+       ctx->invalid_bitmaps = 0;
 }
 
 /*
  * This routine marks all blocks which are used by the superblock,
  * group descriptors, inode bitmaps, and block bitmaps.
  */
-static void mark_table_blocks(ext2_filsys fs)
+static void mark_table_blocks(e2fsck_t ctx)
 {
+       ext2_filsys fs = ctx->fs;
        blk_t   block, b;
        int     i,j;
+       struct problem_context pctx;
+       
+       clear_problem_context(&pctx);
        
        block = fs->super->s_first_data_block;
        for (i = 0; i < fs->group_desc_count; i++) {
-               /*
-                * Mark block used for the block bitmap 
-                */
-               if (fs->group_desc[i].bg_block_bitmap) {
-                       if (ext2fs_test_block_bitmap(block_found_map,
-                                    fs->group_desc[i].bg_block_bitmap)) {
-                               printf("Group %i's block bitmap at %u "
-                                      "conflicts with some other fs block.\n",
-                                      i, fs->group_desc[i].bg_block_bitmap);
-                               preenhalt(fs);
-                               if (ask("Relocate", 1)) {
-                                       invalid_block_bitmap[i]++;
-                                       invalid_bitmaps++;
-                               } else {
-                                       ext2fs_unmark_valid(fs);
-                               }
-                       } else {
-                           ext2fs_mark_block_bitmap(block_found_map,
-                                    fs->group_desc[i].bg_block_bitmap);
-                           ext2fs_mark_block_bitmap(block_illegal_map,
-                                    fs->group_desc[i].bg_block_bitmap);
-                   }
-                       
-               }
-               /*
-                * Mark block used for the inode bitmap 
-                */
-               if (fs->group_desc[i].bg_inode_bitmap) {
-                       if (ext2fs_test_block_bitmap(block_found_map,
-                                    fs->group_desc[i].bg_inode_bitmap)) {
-                               printf("Group %i's inode bitmap at %u "
-                                      "conflicts with some other fs block.\n",
-                                      i, fs->group_desc[i].bg_inode_bitmap);
-                               preenhalt(fs);
-                               if (ask("Relocate", 1)) {
-                                       invalid_inode_bitmap[i]++;
-                                       invalid_bitmaps++;
-                               } else {
-                                       ext2fs_unmark_valid(fs);
-                               }
-                       } else {
-                           ext2fs_mark_block_bitmap(block_found_map,
-                                    fs->group_desc[i].bg_inode_bitmap);
-                           ext2fs_mark_block_bitmap(block_illegal_map,
-                                    fs->group_desc[i].bg_inode_bitmap);
+               pctx.group = i;
+
+               if (ext2fs_bg_has_super(fs, i)) {
+                       /*
+                        * Mark this group's copy of the superblock
+                        */
+                       ext2fs_mark_block_bitmap(ctx->block_found_map, block);
+                       ext2fs_mark_block_bitmap(ctx->block_illegal_map,
+                                                block);
+               
+                       /*
+                        * Mark this group's copy of the descriptors
+                        */
+                       for (j = 0; j < fs->desc_blocks; j++) {
+                               ext2fs_mark_block_bitmap(ctx->block_found_map,
+                                                        block + j + 1);
+                               ext2fs_mark_block_bitmap(ctx->block_illegal_map,
+                                                        block + j + 1);
                        }
                }
-                   
+               
                /*
                 * Mark the blocks used for the inode table
                 */
@@ -1172,42 +1254,59 @@ static void mark_table_blocks(ext2_filsys fs)
                        for (j = 0, b = fs->group_desc[i].bg_inode_table;
                             j < fs->inode_blocks_per_group;
                             j++, b++) {
-                               if (ext2fs_test_block_bitmap(block_found_map,
+                               if (ext2fs_test_block_bitmap(ctx->block_found_map,
                                                             b)) {
-                                       printf("Group %i's inode table at %u "
-                                              "conflicts with some other "
-                                              "fs block.\n",
-                                              i, b);
-                                       preenhalt(fs);
-                                       if (ask("Relocate", 1)) {
-                                               invalid_inode_table[i]++;
-                                               invalid_bitmaps++;
-                                       } else {
-                                               ext2fs_unmark_valid(fs);
+                                       pctx.blk = b;
+                                       if (fix_problem(ctx,
+                                               PR_1_ITABLE_CONFLICT, &pctx)) {
+                                               ctx->invalid_inode_table_flag[i]++;
+                                               ctx->invalid_bitmaps++;
                                        }
                                } else {
-                                   ext2fs_mark_block_bitmap(block_found_map,
+                                   ext2fs_mark_block_bitmap(ctx->block_found_map,
                                                             b);
-                                   ext2fs_mark_block_bitmap(block_illegal_map,
+                                   ext2fs_mark_block_bitmap(ctx->block_illegal_map,
                                                             b);
-                                   }
+                               }
                        }
                }
                            
                /*
-                * Mark this group's copy of the superblock
+                * Mark block used for the block bitmap 
                 */
-               ext2fs_mark_block_bitmap(block_found_map, block);
-               ext2fs_mark_block_bitmap(block_illegal_map, block);
-               
+               if (fs->group_desc[i].bg_block_bitmap) {
+                       if (ext2fs_test_block_bitmap(ctx->block_found_map,
+                                    fs->group_desc[i].bg_block_bitmap)) {
+                               pctx.blk = fs->group_desc[i].bg_block_bitmap;
+                               if (fix_problem(ctx, PR_1_BB_CONFLICT, &pctx)) {
+                                       ctx->invalid_block_bitmap_flag[i]++;
+                                       ctx->invalid_bitmaps++;
+                               }
+                       } else {
+                           ext2fs_mark_block_bitmap(ctx->block_found_map,
+                                    fs->group_desc[i].bg_block_bitmap);
+                           ext2fs_mark_block_bitmap(ctx->block_illegal_map,
+                                    fs->group_desc[i].bg_block_bitmap);
+                   }
+                       
+               }
                /*
-                * Mark this group's copy of the descriptors
+                * Mark block used for the inode bitmap 
                 */
-               for (j = 0; j < fs->desc_blocks; j++) {
-                       ext2fs_mark_block_bitmap(block_found_map,
-                                                block + j + 1);
-                       ext2fs_mark_block_bitmap(block_illegal_map,
-                                                block + j + 1);
+               if (fs->group_desc[i].bg_inode_bitmap) {
+                       if (ext2fs_test_block_bitmap(ctx->block_found_map,
+                                    fs->group_desc[i].bg_inode_bitmap)) {
+                               pctx.blk = fs->group_desc[i].bg_inode_bitmap;
+                               if (fix_problem(ctx, PR_1_IB_CONFLICT, &pctx)) {
+                                       ctx->invalid_inode_bitmap_flag[i]++;
+                                       ctx->invalid_bitmaps++;
+                               } 
+                       } else {
+                           ext2fs_mark_block_bitmap(ctx->block_found_map,
+                                    fs->group_desc[i].bg_inode_bitmap);
+                           ext2fs_mark_block_bitmap(ctx->block_illegal_map,
+                                    fs->group_desc[i].bg_inode_bitmap);
+                       }
                }
                block += fs->super->s_blocks_per_group;
        }
@@ -1221,44 +1320,45 @@ static void mark_table_blocks(ext2_filsys fs)
  */
 errcode_t pass1_get_blocks(ext2_filsys fs, ino_t ino, blk_t *blocks)
 {
+       e2fsck_t ctx = (e2fsck_t) fs->priv_data;
        int     i;
        
-       if (ino == stashed_ino) {
-               for (i=0; i < EXT2_N_BLOCKS; i++)
-                       blocks[i] = stashed_inode->i_block[i];
-               return 0;
-       }
-       printf("INTERNAL ERROR: pass1_get_blocks: unexpected inode #%lu\n",
-              ino);
-       printf("\t(was expecting %lu)\n", stashed_ino);
-       exit(FSCK_ERROR);
+       if (ino != ctx->stashed_ino)
+               return EXT2_ET_CALLBACK_NOTHANDLED;
+
+       for (i=0; i < EXT2_N_BLOCKS; i++)
+               blocks[i] = ctx->stashed_inode->i_block[i];
+       return 0;
 }
 
 errcode_t pass1_read_inode(ext2_filsys fs, ino_t ino, struct ext2_inode *inode)
 {
-       if (ino != stashed_ino)
+       e2fsck_t ctx = (e2fsck_t) fs->priv_data;
+
+       if (ino != ctx->stashed_ino)
                return EXT2_ET_CALLBACK_NOTHANDLED;
-       *inode = *stashed_inode;
+       *inode = *ctx->stashed_inode;
        return 0;
 }
 
 errcode_t pass1_write_inode(ext2_filsys fs, ino_t ino,
                            struct ext2_inode *inode)
 {
-       if (ino == stashed_ino)
-               *stashed_inode = *inode;
+       e2fsck_t ctx = (e2fsck_t) fs->priv_data;
+
+       if (ino == ctx->stashed_ino)
+               *ctx->stashed_inode = *inode;
        return EXT2_ET_CALLBACK_NOTHANDLED;
 }
 
 errcode_t pass1_check_directory(ext2_filsys fs, ino_t ino)
 {
-       if (ino == stashed_ino) {
-               if (!LINUX_S_ISDIR(stashed_inode->i_mode))
-                       return ENOTDIR;
-               return 0;
-       }
-       printf("INTERNAL ERROR: pass1_check_directory: unexpected inode #%lu\n",
-              ino);
-       printf("\t(was expecting %lu)\n", stashed_ino);
-       exit(FSCK_ERROR);
+       e2fsck_t ctx = (e2fsck_t) fs->priv_data;
+
+       if (ino != ctx->stashed_ino)
+               return EXT2_ET_CALLBACK_NOTHANDLED;
+
+       if (!LINUX_S_ISDIR(ctx->stashed_inode->i_mode))
+               return EXT2_ET_NO_DIRECTORY;
+       return 0;
 }