Whamcloud - gitweb
ChangeLog, dir_iterate.c:
[tools/e2fsprogs.git] / e2fsck / pass1.c
index 2f1d67e..6766f41 100644 (file)
@@ -40,7 +40,6 @@
 #include <errno.h>
 #endif
 
-#include <et/com_err.h>
 #include "e2fsck.h"
 #include "problem.h"
 
 #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_inode_bitmap inode_bb_map = 0;  /* Inodes which are in bad blocks */
-
-ext2fs_block_bitmap block_found_map = 0;
-ext2fs_block_bitmap block_dup_map = 0;
-ext2fs_block_bitmap block_illegal_map = 0;
-
-ext2_icount_t inode_link_info = 0;     
-
 static int process_block(ext2_filsys fs, blk_t *blocknr,
-                        int    blockcnt, blk_t ref_blk, 
-                        int ref_offset, 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, blk_t ref_blk,
-                            int ref_offset, void *private);
-static void check_blocks(ext2_filsys fs, struct problem_context *pctx,
+                            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 alloc_bb_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 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);
+                                 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;
-       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 {
@@ -112,18 +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;
+
+static __u64 ext2_max_sizes[4];
 
 /*
  * Free all memory allocated by pass1 in preparation for restarting
@@ -131,157 +103,182 @@ int process_inode_size = 256;
  */
 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;
-       ext2fs_free_icount(inode_link_info); inode_link_info = 0;
-       free(inodes_to_process);inodes_to_process = 0;
-       ext2fs_free_dblist(fs->dblist); fs->dblist = 0;
-       free_dir_info(fs);
-       if (block_dup_map) {
-               ext2fs_free_block_bitmap(block_dup_map); block_dup_map = 0;
-       }
-       if (inode_bb_map) {
-               ext2fs_free_inode_bitmap(inode_bb_map); inode_bb_map = 0;
-       }
-       if (inode_bad_map) {
-               ext2fs_free_inode_bitmap(inode_bad_map); inode_bad_map = 0;
-       }
-
-       /* 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;
+       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;
 
-       for (i=4; i < EXT2_N_BLOCKS; i++) 
-               if (inode->i_block[i])
-                       return 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;
        }
-       retval = ext2fs_create_icount2(fs, 0, 0, 0, &inode_link_info);
-       if (retval) {
-               com_err("ext2fs_create_icount", retval,
-                       "while creating inode_link_info");
-               fatal_error(0);
+       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 = allocate_memory(process_inode_size *
-                                           sizeof(struct process_inode_block),
-                                           "array of inodes to process");
+       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;
 
-       retval = ext2fs_init_dblist(fs, 0);
-       if (retval) {
-               com_err(program_name, retval,
-                       "while allocating directory block information");
-               fatal_error(0);
+       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;
        }
        ext2fs_inode_scan_flags(scan, EXT2_SF_SKIP_MISSING_ITABLE, 0);
-       retval = ext2fs_get_next_inode(scan, &ino, &inode);
-       if (retval) {
-               com_err(program_name, retval, "while starting inode scan");
-               fatal_error(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);
-       clear_problem_context(&pctx);
+       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;
-               if (inode.i_links_count)
-                       retval = ext2fs_icount_store(inode_link_info, ino, 
-                                                    inode.i_links_count);
-               if (retval) {
-                       com_err("ext2fs_icount_fetch", retval,
-                               "while adding inode %u", ino);
-                       fatal_error(0);
-               }
                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;
                        
@@ -292,12 +289,15 @@ void pass1(ext2_filsys fs)
                        pb.fragmented = 0;
                        pb.inode = &inode;
                        pb.pctx = &pctx;
-                       retval = ext2fs_block_iterate2(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.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;
                }
@@ -308,12 +308,12 @@ void pass1(ext2_filsys fs)
                         * regnerated in pass #3.
                         */
                        if (!LINUX_S_ISDIR(inode.i_mode)) {
-                               if (fix_problem(fs, PR_1_ROOT_NO_DIR, &pctx)) {
+                               if (fix_problem(ctx, PR_1_ROOT_NO_DIR, &pctx)) {
                                        inode.i_dtime = time(0);
                                        inode.i_links_count = 0;
-                                       ext2fs_icount_store(inode_link_info,
+                                       ext2fs_icount_store(ctx->inode_link_info,
                                                            ino, 0);
-                                       e2fsck_write_inode(fs, ino, &inode,
+                                       e2fsck_write_inode(ctx, ino, &inode,
                                                           "pass1");
                                }
                        }
@@ -327,30 +327,28 @@ void pass1(ext2_filsys fs)
                         * as a special case.
                         */
                        if (inode.i_dtime && inode.i_links_count) {
-                               if (fix_problem(fs, PR_1_ROOT_DTIME, &pctx)) {
+                               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");
                                }
                        }
                }
-               if (ino == EXT2_BOOT_LOADER_INO) {
-                       ext2fs_mark_inode_bitmap(inode_used_map, ino);
-                       check_blocks(fs, &pctx, 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) {
-                               if (fix_problem(fs,
+                       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");
                                }
                        }
-                       check_blocks(fs, &pctx, block_buf);
+                       check_blocks(ctx, &pctx, block_buf);
                        goto next;
                }
                /*
@@ -359,10 +357,10 @@ void pass1(ext2_filsys fs)
                 */
                if (!inode.i_links_count) {
                        if (!inode.i_dtime && inode.i_mode) {
-                               if (fix_problem(fs,
+                               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");
                                }
                        }
@@ -379,13 +377,13 @@ void pass1(ext2_filsys fs)
                 * 
                 */
                if (inode.i_dtime) {
-                       if (fix_problem(fs, PR_1_SET_DTIME, &pctx)) {
+                       if (fix_problem(ctx, PR_1_SET_DTIME, &pctx)) {
                                inode.i_dtime = 0;
-                               e2fsck_write_inode(fs, ino, &inode, "pass1");
+                               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;
@@ -404,46 +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);
-                       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++;
+                       ctx->fs_regular_count++;
                else if (LINUX_S_ISCHR (inode.i_mode) &&
                         e2fsck_pass1_check_device_inode(&inode))
-                       fs_chardev_count++;
+                       ctx->fs_chardev_count++;
                else if (LINUX_S_ISBLK (inode.i_mode) &&
                         e2fsck_pass1_check_device_inode(&inode))
-                       fs_blockdev_count++;
+                       ctx->fs_blockdev_count++;
                else if (LINUX_S_ISLNK (inode.i_mode)) {
-                       fs_symlinks_count++;
+                       ctx->fs_symlinks_count++;
                        if (!inode.i_blocks) {
-                               fs_fast_symlinks_count++;
+                               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]) {
@@ -451,59 +452,87 @@ void pass1(ext2_filsys fs)
                        inodes_to_process[process_inode_count].inode = inode;
                        process_inode_count++;
                } else
-                       check_blocks(fs, &pctx, block_buf);
+                       check_blocks(ctx, &pctx, block_buf);
+
+               if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+                       return;
 
-               if (process_inode_count >= process_inode_size)
-                       process_inodes(fs, block_buf);
+               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 == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE) {
-                       if (!inode_bb_map)
-                               alloc_bb_map(fs);
-                       ext2fs_mark_inode_bitmap(inode_bb_map, ino);
-                       ext2fs_mark_inode_bitmap(inode_used_map, ino);
+               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 (retval) {
-                       com_err(program_name, retval,
-                               "while doing inode scan");
-                       fatal_error(0);
+               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);
 
-       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;
+       ext2fs_free_mem((void **) &block_buf);
+       ext2fs_free_block_bitmap(ctx->block_illegal_map);
+       ctx->block_illegal_map = 0;
 
-       if (tflag > 1) {
-               printf("Pass 1: ");
-               print_resource_track(&rtrack);
+       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
 }
 
 /*
@@ -511,16 +540,28 @@ 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;
@@ -533,24 +574,26 @@ static void process_inodes(ext2_filsys fs, char *block_buf)
        printf("begin process_inodes: ");
 #endif
        old_operation = ehandler_operation(0);
-       old_stashed_inode = stashed_inode;
-       old_stashed_ino = stashed_ino;
+       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++) {
-               pctx.inode = stashed_inode = &inodes_to_process[i].inode;
-               pctx.ino = stashed_ino = inodes_to_process[i].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 ", pctx.ino);
 #endif
                sprintf(buf, "reading indirect blocks of inode %lu", pctx.ino);
                ehandler_operation(buf);
-               check_blocks(fs, &pctx, block_buf);
+               check_blocks(ctx, &pctx, block_buf);
+               if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+                       break;
        }
-       stashed_inode = old_stashed_inode;
-       stashed_ino = old_stashed_ino;
+       ctx->stashed_inode = old_stashed_inode;
+       ctx->stashed_ino = old_stashed_ino;
        process_inode_count = 0;
 #if 0
        printf("end process inodes\n");
@@ -558,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;
@@ -572,32 +615,40 @@ static int process_inode_cmp(const void *a, const void *b)
 /*
  * This procedure will allocate the inode bad map table
  */
-static void alloc_bad_map(ext2_filsys fs)
+static void alloc_bad_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, "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 "bb" (badblock) map table
  */
-static void alloc_bb_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, "inode in bad block map",
-                                             &inode_bb_map);
-       if (retval) {
-               com_err("ext2fs_allocate_inode_bitmap", retval,
-                       "while allocating inode in bad block 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;
        }
 }
 
@@ -608,23 +659,29 @@ static void alloc_bb_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;
        
-       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);
+       clear_problem_context(&pctx);
+       
+       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);
        }
 }
 
@@ -632,13 +689,16 @@ 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, struct problem_context *pctx,
+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 (!ext2fs_inode_has_valid_blocks(pctx->inode))
                return;
@@ -652,72 +712,90 @@ static void check_blocks(ext2_filsys fs, struct problem_context *pctx,
        pb.is_dir = LINUX_S_ISDIR(pctx->inode->i_mode);
        pb.inode = inode;
        pb.pctx = pctx;
-       retval = ext2fs_block_iterate2(fs, ino,
+       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);
-       reset_problem_latch(PR_LATCH_BLOCK);
-       if (retval)
-               com_err(program_name, retval,
-                       "while calling ext2fs_block_iterate in check_blocks");
+       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;
-               ext2fs_icount_store(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;
        }
 
        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) {
-               if (fix_problem(fs, PR_1_ZERO_LENGTH_DIR, pctx)) {
+               if (fix_problem(ctx, PR_1_ZERO_LENGTH_DIR, pctx)) {
                        inode->i_links_count = 0;
-                       ext2fs_icount_store(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;
                }
        }
-       if ((pb.is_dir && (inode->i_size !=
-                          (pb.last_block + 1) * fs->blocksize)) ||
-           (inode->i_size < pb.last_block * fs->blocksize)) {
+       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(fs, PR_1_BAD_I_SIZE, pctx)) {
+               if (fix_problem(ctx, PR_1_BAD_I_SIZE, pctx)) {
                        inode->i_size = pctx->num;
-                       e2fsck_write_inode(fs, ino, inode, "check_blocks");
+                       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) {
                pctx->num = pb.num_blocks;
-               if (fix_problem(fs, PR_1_BAD_I_BLOCKS, pctx)) {
+               if (fix_problem(ctx, PR_1_BAD_I_BLOCKS, pctx)) {
                        inode->i_blocks = pb.num_blocks;
-                       e2fsck_write_inode(fs, ino, inode, "check_blocks");
+                       e2fsck_write_inode(ctx, ino, inode, "check_blocks");
                }
                pctx->num = 0;
        }
@@ -780,20 +858,21 @@ static char *describe_illegal_block(ext2_filsys fs, blk_t block)
  */
 int process_block(ext2_filsys fs,
                  blk_t *block_nr,
-                 int blockcnt,
+                 e2_blkcnt_t blockcnt,
                  blk_t ref_block,
                  int ref_offset, 
-                 void *private)
+                 void *priv_data)
 {
        struct process_block_struct *p;
        struct problem_context *pctx;
        blk_t   blk = *block_nr;
        int     ret_code = 0;
        int     problem = 0;
-       errcode_t       retval;
+       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) {
@@ -801,9 +880,11 @@ 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)
@@ -846,18 +927,19 @@ int process_block(ext2_filsys fs,
        if (problem) {
                p->num_illegal_blocks++;
                if (!p->suppress && (p->num_illegal_blocks % 12) == 0) {
-                       if (fix_problem(fs, PR_1_TOO_MANY_BAD_BLOCKS, pctx)) {
+                       if (fix_problem(ctx, PR_1_TOO_MANY_BAD_BLOCKS, pctx)) {
                                p->clear = 1;
                                return BLOCK_ABORT;
                        }
-                       if (ask("Suppress messages", 0)) {
+                       if (fix_problem(ctx, PR_1_SUPPRESS_MESSAGES, pctx)) {
                                p->suppress = 1;
-                               suppress_latch_group(PR_LATCH_BLOCK, 1);
+                               set_latch_flags(PR_LATCH_BLOCK,
+                                               PRL_SUPPRESS, 0);
                        }
                }
                pctx->blk = blk;
                pctx->blkcount = blockcnt;
-               if (fix_problem(fs, problem, pctx)) {
+               if (fix_problem(ctx, problem, pctx)) {
                        blk = *block_nr = 0;
                        ret_code = BLOCK_CHANGED;
                        goto mark_dir;
@@ -867,79 +949,66 @@ int process_block(ext2_filsys fs,
                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)) {
-               retval = ext2fs_add_dir_block(fs->dblist, p->ino,
-                                             blk, blockcnt);
-               if (retval) {
-                       com_err(program_name, retval,
-                               "while adding to directory block list");
-                       fatal_error(0);
+               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;
                }
        }
        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,
+                     e2_blkcnt_t blockcnt,
                      blk_t ref_block,
                      int ref_offset,
-                     void *private)
+                     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 (fix_problem(fs, PR_1_BB_ILLEGAL_BLOCK_NUM, pctx)) {
+               if (fix_problem(ctx, PR_1_BB_ILLEGAL_BLOCK_NUM, pctx)) {
                        *block_nr = 0;
                        return BLOCK_CHANGED;
                } else
@@ -947,24 +1016,26 @@ int process_bad_block(ext2_filsys fs,
        }
 
        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;
        }
        /*
@@ -974,46 +1045,48 @@ int process_bad_block(ext2_filsys fs,
        
        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) {
-                       if (fix_problem(fs, PR_1_BB_BAD_BLOCK, pctx)) {
-                               invalid_block_bitmap[i]++;
-                               invalid_bitmaps++;
+                       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) {
-                       if (fix_problem(fs, PR_1_IB_BAD_BLOCK, pctx)) {
-                               invalid_inode_bitmap[i]++;
-                               invalid_bitmaps++;
+                       if (fix_problem(ctx, PR_1_IB_BAD_BLOCK, pctx)) {
+                               ctx->invalid_inode_bitmap_flag[i]++;
+                               ctx->invalid_bitmaps++;
                        }
                        return 0;
                }
@@ -1027,6 +1100,7 @@ int process_bad_block(ext2_filsys fs,
                         */
                        return 0;
                }
+               first_block += fs->super->s_blocks_per_group;
        }
        /*
         * If we've gotten to this point, then the only
@@ -1035,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);
 }
 
 /*
@@ -1102,37 +1185,39 @@ 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;
@@ -1147,16 +1232,17 @@ static void mark_table_blocks(ext2_filsys fs)
                        /*
                         * Mark this group's copy of the superblock
                         */
-                       ext2fs_mark_block_bitmap(block_found_map, block);
-                       ext2fs_mark_block_bitmap(block_illegal_map, block);
+                       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(block_found_map,
+                               ext2fs_mark_block_bitmap(ctx->block_found_map,
                                                         block + j + 1);
-                               ext2fs_mark_block_bitmap(block_illegal_map,
+                               ext2fs_mark_block_bitmap(ctx->block_illegal_map,
                                                         block + j + 1);
                        }
                }
@@ -1168,18 +1254,18 @@ 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)) {
                                        pctx.blk = b;
-                                       if (fix_problem(fs,
+                                       if (fix_problem(ctx,
                                                PR_1_ITABLE_CONFLICT, &pctx)) {
-                                               invalid_inode_table[i]++;
-                                               invalid_bitmaps++;
+                                               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);
                                }
                        }
@@ -1189,17 +1275,17 @@ static void mark_table_blocks(ext2_filsys fs)
                 * Mark block used for the block bitmap 
                 */
                if (fs->group_desc[i].bg_block_bitmap) {
-                       if (ext2fs_test_block_bitmap(block_found_map,
+                       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(fs, PR_1_BB_CONFLICT, &pctx)) {
-                                       invalid_block_bitmap[i]++;
-                                       invalid_bitmaps++;
+                               if (fix_problem(ctx, PR_1_BB_CONFLICT, &pctx)) {
+                                       ctx->invalid_block_bitmap_flag[i]++;
+                                       ctx->invalid_bitmaps++;
                                }
                        } else {
-                           ext2fs_mark_block_bitmap(block_found_map,
+                           ext2fs_mark_block_bitmap(ctx->block_found_map,
                                     fs->group_desc[i].bg_block_bitmap);
-                           ext2fs_mark_block_bitmap(block_illegal_map,
+                           ext2fs_mark_block_bitmap(ctx->block_illegal_map,
                                     fs->group_desc[i].bg_block_bitmap);
                    }
                        
@@ -1208,17 +1294,17 @@ static void mark_table_blocks(ext2_filsys fs)
                 * Mark block used for the inode bitmap 
                 */
                if (fs->group_desc[i].bg_inode_bitmap) {
-                       if (ext2fs_test_block_bitmap(block_found_map,
+                       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(fs, PR_1_IB_CONFLICT, &pctx)) {
-                                       invalid_inode_bitmap[i]++;
-                                       invalid_bitmaps++;
+                               if (fix_problem(ctx, PR_1_IB_CONFLICT, &pctx)) {
+                                       ctx->invalid_inode_bitmap_flag[i]++;
+                                       ctx->invalid_bitmaps++;
                                } 
                        } else {
-                           ext2fs_mark_block_bitmap(block_found_map,
+                           ext2fs_mark_block_bitmap(ctx->block_found_map,
                                     fs->group_desc[i].bg_inode_bitmap);
-                           ext2fs_mark_block_bitmap(block_illegal_map,
+                           ext2fs_mark_block_bitmap(ctx->block_illegal_map,
                                     fs->group_desc[i].bg_inode_bitmap);
                        }
                }
@@ -1234,41 +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)
+       if (ino != ctx->stashed_ino)
                return EXT2_ET_CALLBACK_NOTHANDLED;
 
        for (i=0; i < EXT2_N_BLOCKS; i++)
-               blocks[i] = stashed_inode->i_block[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;
 }