Many files:
  pass*.c, super.c: Massive changes to avoid using printf and com_err
  	routines.  All diagnostic messages are now routed through the
  	fix_problem interface.
  pass2.c (check_dir_block): Check for duplicate '.' and '..' entries.
  problem.c, problem.h: Add new problem codes PR_2_DUP_DOT and
  	PR_2_DUP_DOT_DOT.
  problem.c: Added new problem codes for some of the superblock
  	corruption checks, and for the pass header messages.  ("Pass
  	1: xxxxx")
  util.c (print_resource_track): Now takes a description argument.
  super.c, unix.c, e2fsck.c: New files to separate out the
  	operating-specific operations out from e2fsck.c.  e2fsck.c now
  	contains the global e2fsck context management routines, and
  	super.c contains the "pass 0" initial validation of the
  	superblock and global block group descriptors.
  pass1.c, pass2.c, pass3.c, pass4.c, pass5.c, util.c: Eliminate
  	(nearly) all global variables and moved them to the e2fsck
  	context structure.
  problem.c, problem.h: Added new problem codes PR_0_SB_CORRUPT,
  	PR_0_FS_SIZE_WRONG, PR_0_NO_FRAGMENTS, PR_0_BLOCKS_PER_GROUP,
  	PR_0_FIRST_DATA_BLOCK
expect.1, expect.2:
  Updated tests to align with e2fsck problem.c changes.

diff --git a/e2fsck/ChangeLog b/e2fsck/ChangeLog
index 9fceacd..efa67c3 100644
--- a/e2fsck/ChangeLog
+++ b/e2fsck/ChangeLog
@@ -1,3 +1,43 @@
+Fri Oct  3 13:40:03 1997  Theodore Ts'o  <tytso@rsts-11.mit.edu>
+
+	* pass*.c, super.c: Massive changes to avoid using printf and
+		com_err routines.   All diagnostic messages are now routed
+		through the fix_problem interface.
+
+Sat Sep  6 17:13:28 1997  Theodore Ts'o  <tytso@rsts-11.mit.edu>
+
+	* pass2.c (check_dir_block): Check for duplicate '.' and '..'
+		entries. 
+
+	* problem.c, problem.h: Add new problem codes PR_2_DUP_DOT and
+	 	PR_2_DUP_DOT_DOT.
+
+Tue Sep  2 09:04:51 1997  Theodore Ts'o  <tytso@rsts-11.mit.edu>
+
+	* problem.c: Added new problem codes for some of the
+		superblock corruption checks, and for the pass header
+		messages.  ("Pass 1: xxxxx")
+
+	* util.c (print_resource_track): Now takes a description
+		argument. 
+
+Mon Aug 25 10:23:13 1997  Theodore Ts'o  <tytso@rsts-11.mit.edu>
+
+	* super.c, unix.c, e2fsck.c: New files to separate out the
+		operating-specific operations out from e2fsck.c.  
+		e2fsck.c now contains the global e2fsck context management
+		routines, and super.c contains the "pass 0" initial
+		validation of the superblock and global block group
+		descriptors. 
+
+	* pass1.c, pass2.c, pass3.c, pass4.c, pass5.c, util.c: Eliminate
+		(nearly) all global variables and moved them to the e2fsck
+		context structure.
+
+	* problem.c, problem.h: Added new problem codes PR_0_SB_CORRUPT,
+ 		PR_0_FS_SIZE_WRONG, PR_0_NO_FRAGMENTS,
+ 		PR_0_BLOCKS_PER_GROUP, PR_0_FIRST_DATA_BLOCK
+
 Thu Aug 14 10:55:21 1997  Theodore Ts'o  <tytso@rsts-11.mit.edu>
 
 	* message.c: Add compression for the word "Illegal"
diff --git a/e2fsck/Makefile.in b/e2fsck/Makefile.in
index 24a9efb..e16c12f 100644
--- a/e2fsck/Makefile.in
+++ b/e2fsck/Makefile.in
@@ -52,17 +52,19 @@
 #
 #MCHECK= -DMCHECK
 
-OBJS= e2fsck.o pass1.o pass1b.o pass2.o pass3.o pass4.o pass5.o \
-	swapfs.o badblocks.o util.o dirinfo.o ehandler.o problem.o message.o \
-	$(MTRACE_OBJ)
+OBJS= unix.o e2fsck.o super.o pass1.o pass1b.o pass2.o pass3.o pass4.o \
+	pass5.o swapfs.o badblocks.o util.o dirinfo.o ehandler.o \
+	problem.o message.o $(MTRACE_OBJ)
 
-PROFILED_OBJS= profiled/e2fsck.o profiled/pass1.o profiled/pass1b.o \
+PROFILED_OBJS= profiled/unix.o profiled/e2fsck.o profiled/super.o \
+	profiled/pass1.o profiled/pass1b.o \
 	profiled/pass2.o profiled/pass3.o profiled/pass4.o profiled/pass5.o \
 	profiled/badblocks.o profiled/util.o profiled/dirinfo.o \
 	profiled/ehandler.o profiled/message.o profiled/problem.o \
 	profiled/swapfs.o
 
 SRCS= $(srcdir)/e2fsck.c \
+	$(srcdir)/super.c \
 	$(srcdir)/pass1.c \
 	$(srcdir)/pass1b.c \
 	$(srcdir)/pass2.c \
@@ -71,6 +73,7 @@
 	$(srcdir)/pass5.c \
 	$(srcdir)/badblocks.c \
 	$(srcdir)/util.c \
+	$(srcdir)/unix.c \
 	$(srcdir)/dirinfo.c \
 	$(srcdir)/ehandler.c \
 	$(srcdir)/problem.c \
diff --git a/e2fsck/badblocks.c b/e2fsck/badblocks.c
index e6ab4bc..a9addf7 100644
--- a/e2fsck/badblocks.c
+++ b/e2fsck/badblocks.c
@@ -23,15 +23,16 @@
 	return;
 }
 
-void read_bad_blocks_file(ext2_filsys fs, const char *bad_blocks_file,
+void read_bad_blocks_file(e2fsck_t ctx, const char *bad_blocks_file,
 			  int replace_bad_blocks)
 {
+	ext2_filsys fs = ctx->fs;
 	errcode_t	retval;
 	badblocks_list	bb_list = 0;
 	FILE		*f;
 	char		buf[1024];
 
-	read_bitmaps(fs);
+	read_bitmaps(ctx);
 
 	/*
 	 * Make sure the bad block inode is sane.  If there are any
@@ -72,8 +73,8 @@
 		}
 	} else {
 		sprintf(buf, "badblocks -b %d %s%s %d", fs->blocksize,
-			preen ? "" : "-s ", fs->device_name,
-			fs->super->s_blocks_count);
+			(ctx->options & E2F_OPT_PREEN) ? "" : "-s ",
+			fs->device_name, fs->super->s_blocks_count);
 		f = popen(buf, "r");
 		if (!f) {
 			com_err("read_bad_blocks_file", errno,
@@ -106,9 +107,9 @@
 	return;
 }
 
-void test_disk(ext2_filsys fs)
+void test_disk(e2fsck_t ctx)
 {
-	read_bad_blocks_file(fs, 0, 1);
+	read_bad_blocks_file(ctx, 0, 1);
 }
 
 static int check_bb_inode_blocks(ext2_filsys fs, blk_t *block_nr, int blockcnt,
diff --git a/e2fsck/e2fsck.c b/e2fsck/e2fsck.c
index 57390ee..592541b 100644
--- a/e2fsck/e2fsck.c
+++ b/e2fsck/e2fsck.c
@@ -9,841 +9,117 @@
  * %End-Header%
  */
 
-/* Usage: e2fsck [-dfpnsvy] device
- *	-d -- debugging this program
- *	-f -- check the fs even if it is marked valid
- *	-p -- "preen" the filesystem
- * 	-n -- open the filesystem r/o mode; never try to fix problems
- *	-v -- verbose (tells how many files)
- * 	-y -- always answer yes to questions
- *
- * The device may be a block device or a image of one, but this isn't
- * enforced (but it's not much fun on a character device :-). 
- */
-
-#include <stdio.h>
-#ifdef HAVE_STDLIB_H
-#include <stdlib.h>
-#endif
-#include <string.h>
-#include <fcntl.h>
-#include <ctype.h>
-#include <termios.h>
-#include <time.h>
-#ifdef HAVE_GETOPT_H
-#include <getopt.h>
-#endif
-#include <unistd.h>
-#ifdef HAVE_ERRNO_H
 #include <errno.h>
-#endif
-#ifdef HAVE_MNTENT_H
-#include <mntent.h>
-#endif
-#include <sys/ioctl.h>
-#include <malloc.h>
 
 #include "et/com_err.h"
-#include "uuid/uuid.h"
+
 #include "e2fsck.h"
 #include "problem.h"
-#include "../version.h"
 
-extern int isatty(int);
-
-const char * program_name = "e2fsck";
-const char * device_name = NULL;
-const char * filesystem_name = NULL;
-
-/* Command line options */
-int nflag = 0;
-int yflag = 0;
-int tflag = 0;			/* Do timing */
-int cflag = 0;			/* check disk */
-int preen = 0;
-int rwflag = 1;
-int swapfs = 0;
-int normalize_swapfs = 0;
-int inode_buffer_blocks = 0;
-blk_t use_superblock;
-blk_t superblock;
-int blocksize = 0;
-int verbose = 0;
-int list = 0;
-int debug = 0;
-int force = 0;
-int invalid_bitmaps = 0;
-static int show_version_only = 0;
-
-static int replace_bad_blocks = 0;
-static char *bad_blocks_file = 0;
-
-static int possible_block_sizes[] = { 1024, 2048, 4096, 8192, 0};
-
-struct resource_track	global_rtrack;
-
-static int root_filesystem = 0;
-static int read_only_root = 0;
-
-int *invalid_inode_bitmap;
-int *invalid_block_bitmap;
-int *invalid_inode_table;
-int restart_e2fsck = 0;
-
-static void usage(NOARGS)
+/*
+ * This function allocates an e2fsck context 
+ */
+errcode_t e2fsck_allocate_context(e2fsck_t *ret)
 {
-	fprintf(stderr,
-		"Usage: %s [-panyrcdfvstFSV] [-b superblock] [-B blocksize]\n"
-		"\t\t[-I inode_buffer_blocks] [-P process_inode_size]\n"
-		"\t\t[-l|-L bad_blocks_file] device\n", program_name);
-	exit(FSCK_USAGE);
-}
+	e2fsck_t	context;
 
-static void show_stats(ext2_filsys fs)
-{
-	int inodes, inodes_used, blocks, blocks_used;
-	int dir_links;
-	int num_files, num_links;
-	int frag_percent;
+	context = malloc(sizeof(struct e2fsck_struct));
+	if (!context)
+		return ENOMEM;
 
-	dir_links = 2 * fs_directory_count - 1;
-	num_files = fs_total_count - dir_links;
-	num_links = fs_links_count - dir_links;
-	inodes = fs->super->s_inodes_count;
-	inodes_used = (fs->super->s_inodes_count -
-		       fs->super->s_free_inodes_count);
-	blocks = fs->super->s_blocks_count;
-	blocks_used = (fs->super->s_blocks_count -
-		       fs->super->s_free_blocks_count);
+	memset(context, 0, sizeof(struct e2fsck_struct));
 
-	frag_percent = (10000 * fs_fragmented) / inodes_used;
-	frag_percent = (frag_percent + 5) / 10;
-	
-	if (!verbose) {
-		printf("%s: %d/%d files (%0d.%d%% non-contiguous), %d/%d blocks\n",
-		       device_name, inodes_used, inodes,
-		       frag_percent / 10, frag_percent % 10,
-		       blocks_used, blocks);
-		return;
-	}
-	printf ("\n%8d inode%s used (%d%%)\n", inodes_used,
-		(inodes_used != 1) ? "s" : "",
-		100 * inodes_used / inodes);
-	printf ("%8d non-contiguous inodes (%0d.%d%%)\n",
-		fs_fragmented, frag_percent / 10, frag_percent % 10);
-	printf ("         # of inodes with ind/dind/tind blocks: %d/%d/%d\n",
-		fs_ind_count, fs_dind_count, fs_tind_count);
-	printf ("%8d block%s used (%d%%)\n"
-		"%8d bad block%s\n", blocks_used,
-		(blocks_used != 1) ? "s" : "",
-		100 * blocks_used / blocks, fs_badblocks_count,
-		fs_badblocks_count != 1 ? "s" : "");
-	printf ("\n%8d regular file%s\n"
-		"%8d director%s\n"
-		"%8d character device file%s\n"
-		"%8d block device file%s\n"
-		"%8d fifo%s\n"
-		"%8d link%s\n"
-		"%8d symbolic link%s (%d fast symbolic link%s)\n"
-		"%8d socket%s\n"
-		"--------\n"
-		"%8d file%s\n",
-		fs_regular_count, (fs_regular_count != 1) ? "s" : "",
-		fs_directory_count, (fs_directory_count != 1) ? "ies" : "y",
-		fs_chardev_count, (fs_chardev_count != 1) ? "s" : "",
-		fs_blockdev_count, (fs_blockdev_count != 1) ? "s" : "",
-		fs_fifo_count, (fs_fifo_count != 1) ? "s" : "",
-		fs_links_count - dir_links,
-		((fs_links_count - dir_links) != 1) ? "s" : "",
-		fs_symlinks_count, (fs_symlinks_count != 1) ? "s" : "",
-		fs_fast_symlinks_count, (fs_fast_symlinks_count != 1) ? "s" : "",
-		fs_sockets_count, (fs_sockets_count != 1) ? "s" : "",
-		fs_total_count - dir_links,
-		((fs_total_count - dir_links) != 1) ? "s" : "");
-}
+	context->process_inode_size = 256;
 
-static void check_mount(NOARGS)
-{
-	errcode_t	retval;
-	int		mount_flags, cont, fd;
-
-	retval = ext2fs_check_if_mounted(filesystem_name, &mount_flags);
-	if (retval) {
-		com_err("ext2fs_check_if_mount", retval,
-			"while determining whether %s is mounted.",
-			filesystem_name);
-		return;
-	}
-	if (!(mount_flags & EXT2_MF_MOUNTED))
-		return;
-
-#if (defined(__linux__) && defined(HAVE_MNTENT_H))
-	/*
-	 * If the root is mounted read-only, then /etc/mtab is
-	 * probably not correct; so we won't issue a warning based on
-	 * it.
-	 */
-	fd = open(MOUNTED, O_RDWR);
-	if (fd < 0) {
-		if (errno == EROFS)
-			return;
-	} else
-		close(fd);
-#endif
-	
-	if (!rwflag) {
-		printf("Warning!  %s is mounted.\n", device_name);
-		return;
-	}
-
-	printf("%s is mounted.\n\n", device_name);
-	printf("\a\a\a\aWARNING!!!  Running e2fsck on a mounted filesystem "
-	       "may cause\nSEVERE filesystem damage.\a\a\a\n\n");
-	if (isatty (0) && isatty (1))
-		cont = ask_yn("Do you really want to continue", -1);
-	else
-		cont = 0;
-	if (!cont) {
-		printf ("check aborted.\n");
-		exit (0);
-	}
-	return;
-}
-
-static void sync_disks(NOARGS)
-{
-	sync();
-	sync();
-	sleep(1);
-	sync();
-}
-
-static blk_t get_backup_sb(ext2_filsys fs)
-{
-	if (!fs || !fs->super)
-		return 8193;
-	return fs->super->s_blocks_per_group + 1;
-}
-
-#define MIN_CHECK 1
-#define MAX_CHECK 2
-
-static const char *corrupt_msg =
-"\nThe superblock could not be read or does not describe a correct ext2\n"
-"filesystem.  If the device is valid and it really contains an ext2\n"
-"filesystem (and not swap or ufs or something else), then the superblock\n"
-"is corrupt, and you might try running e2fsck with an alternate superblock:\n"
-"    e2fsck -b %d <device>\n\n";
-
-static void check_super_value(ext2_filsys fs, const char *descr,
-			      unsigned long value, int flags,
-			      unsigned long min, unsigned long max)
-{
-	if (((flags & MIN_CHECK) && (value < min)) ||
-	    ((flags & MAX_CHECK) && (value > max))) {
-		printf("Corruption found in superblock.  (%s = %lu).\n",
-		       descr, value);
-		printf(corrupt_msg, get_backup_sb(fs));
-		fatal_error(0);
-	}
-}
-
-static void relocate_hint(ext2_filsys fs)
-{
-	static hint_issued = 0;
-
-	/*
-	 * Only issue the hint once, and only if we're using the
-	 * primary superblocks.
-	 */
-	if (hint_issued || superblock)
-		return;
-
-	printf("Note: if there is several inode or block bitmap blocks\n"
-	       "which require relocation, or one part of the inode table\n"
-	       "which must be moved, you may wish to try running e2fsck\n"
-	       "with the '-b %d' option first.  The problem may lie only\n"
-	       "with the primary block group descriptor, and the backup\n"
-	       "block group descriptor may be OK.\n\n", get_backup_sb(fs));
-	hint_issued = 1;
-}
-
-	
-static void check_super_block(ext2_filsys fs)
-{
-	blk_t	first_block, last_block;
-	struct ext2fs_sb *s = (struct ext2fs_sb *) fs->super;
-	blk_t	blocks_per_group = fs->super->s_blocks_per_group;
-	int	i;
-	blk_t	should_be;
-	errcode_t retval;
-	struct problem_context	pctx;
-
-	clear_problem_context(&pctx);
-
-	/*
-	 * Verify the super block constants...
-	 */
-	check_super_value(fs, "inodes_count", s->s_inodes_count,
-			  MIN_CHECK, 1, 0);
-	check_super_value(fs, "blocks_count", s->s_blocks_count,
-			  MIN_CHECK, 1, 0);
-	check_super_value(fs, "first_data_block", s->s_first_data_block,
-			  MAX_CHECK, 0, s->s_blocks_count);
-	check_super_value(fs, "log_frag_size", s->s_log_frag_size,
-			  MAX_CHECK, 0, 2);
-	check_super_value(fs, "log_block_size", s->s_log_block_size,
-			  MIN_CHECK | MAX_CHECK, s->s_log_frag_size,
-			  2);
-	check_super_value(fs, "frags_per_group", s->s_frags_per_group,
-			  MIN_CHECK | MAX_CHECK, 1, 8 * EXT2_BLOCK_SIZE(s));
-	check_super_value(fs, "blocks_per_group", s->s_blocks_per_group,
-			  MIN_CHECK | MAX_CHECK, 1, 8 * EXT2_BLOCK_SIZE(s));
-	check_super_value(fs, "inodes_per_group", s->s_inodes_per_group,
-			  MIN_CHECK, 1, 0);
-	check_super_value(fs, "r_blocks_count", s->s_r_blocks_count,
-			  MAX_CHECK, 0, s->s_blocks_count);
-
-	retval = ext2fs_get_device_size(filesystem_name, EXT2_BLOCK_SIZE(s),
-					&should_be);
-	if (retval) {
-		com_err("ext2fs_get_device_size", retval,
-			"while trying to check physical size of filesystem");
-		fatal_error(0);
-	}
-	if (should_be < s->s_blocks_count) {
-		printf("The filesystem size (according to the superblock) is %d blocks\n", s->s_blocks_count);
-		printf("The physical size of the device is %d blocks\n",
-		       should_be);
-		printf("Either the superblock or the partition table is likely to be corrupt!\n");
-		preenhalt(fs);
-		if (ask("Abort", 1))
-			fatal_error(0);
-	}
-
-	if (s->s_log_block_size != s->s_log_frag_size) {
-		printf("Superblock block_size = %d, fragsize = %d.\n",
-		       EXT2_BLOCK_SIZE(s), EXT2_FRAG_SIZE(s));
-		printf("This version of e2fsck does not support fragment "
-		       "sizes different\n"
-		       "from the block size.\n");
-		fatal_error(0);
-	}
-
-	should_be = s->s_frags_per_group /
-		(s->s_log_block_size - s->s_log_frag_size + 1);
-	if (s->s_blocks_per_group != should_be) {
-		printf("Superblock blocks_per_group = %u, should "
-		       "have been %u\n", s->s_blocks_per_group,
-		       should_be);
-		printf(corrupt_msg, get_backup_sb(fs));
-		fatal_error(0);
-	}
-
-	should_be = (s->s_log_block_size == 0) ? 1 : 0;
-	if (s->s_first_data_block != should_be) {
-		printf("Superblock first_data_block = %u, should "
-		       "have been %u\n", s->s_first_data_block,
-		       should_be);
-		printf(corrupt_msg, get_backup_sb(fs));
-		fatal_error(0);
-	}
-
-	/*
-	 * Verify the group descriptors....
-	 */
-	first_block =  fs->super->s_first_data_block;
-	last_block = first_block + blocks_per_group;
-
-	for (i = 0; i < fs->group_desc_count; i++) {
-		pctx.group = i;
-		
-		if (i == fs->group_desc_count - 1)
-			last_block = fs->super->s_blocks_count;
-		if ((fs->group_desc[i].bg_block_bitmap < first_block) ||
-		    (fs->group_desc[i].bg_block_bitmap >= last_block)) {
-			relocate_hint(fs);
-			pctx.blk = fs->group_desc[i].bg_block_bitmap;
-			if (fix_problem(fs, PR_0_BB_NOT_GROUP, &pctx)) {
-				fs->group_desc[i].bg_block_bitmap = 0;
-				invalid_block_bitmap[i]++;
-				invalid_bitmaps++;
-			}
-		}
-		if ((fs->group_desc[i].bg_inode_bitmap < first_block) ||
-		    (fs->group_desc[i].bg_inode_bitmap >= last_block)) {
-			relocate_hint(fs);
-			pctx.blk = fs->group_desc[i].bg_inode_bitmap;
-			if (fix_problem(fs, PR_0_IB_NOT_GROUP, &pctx)) {
-				fs->group_desc[i].bg_inode_bitmap = 0;
-				invalid_inode_bitmap[i]++;
-				invalid_bitmaps++;
-			}
-		}
-		if ((fs->group_desc[i].bg_inode_table < first_block) ||
-		    ((fs->group_desc[i].bg_inode_table +
-		      fs->inode_blocks_per_group - 1) >= last_block)) {
-			relocate_hint(fs);
-			pctx.blk = fs->group_desc[i].bg_inode_table;
-			if (fix_problem(fs, PR_0_ITABLE_NOT_GROUP, &pctx)) {
-				fs->group_desc[i].bg_inode_table = 0;
-				invalid_inode_table[i]++;
-				invalid_bitmaps++;
-			}
-		}
-		first_block += fs->super->s_blocks_per_group;
-		last_block += fs->super->s_blocks_per_group;
-	}
-	/*
-	 * If we have invalid bitmaps, set the error state of the
-	 * filesystem.
-	 */
-	if (invalid_bitmaps && rwflag) {
-		fs->super->s_state &= ~EXT2_VALID_FS;
-		ext2fs_mark_super_dirty(fs);
-	}
-
-	/*
-	 * If the UUID field isn't assigned, assign it.
-	 */
-	if (rwflag && uuid_is_null(s->s_uuid)) {
-		if (preen)
-			printf("%s: Adding UUID to filesystem.\n",
-			       device_name);
-		else
-			printf("Filesystem did not have a UUID; "
-			       "generating one.\n\n");
-		uuid_generate(s->s_uuid);
-		ext2fs_mark_super_dirty(fs);
-	}
-	return;
+	*ret = context;
+	return 0;
 }
 
 /*
- * This routine checks to see if a filesystem can be skipped; if so,
- * it will exit with E2FSCK_OK.  Under some conditions it will print a
- * message explaining why a check is being forced.
+ * This function resets an e2fsck context; it is called when e2fsck
+ * needs to be restarted.
  */
-static void check_if_skip(ext2_filsys fs)
+errcode_t e2fsck_reset_context(e2fsck_t ctx)
 {
-	const char *reason = NULL;
-	
-	if (force || bad_blocks_file || cflag || swapfs)
-		return;
-	
-	if (fs->super->s_state & EXT2_ERROR_FS)
-		reason = "contains a file system with errors";
-	else if (fs->super->s_mnt_count >=
-		 (unsigned) fs->super->s_max_mnt_count)
-		reason = "has reached maximal mount count";
-	else if (fs->super->s_checkinterval &&
-		 time(0) >= (fs->super->s_lastcheck +
-			     fs->super->s_checkinterval))
-		reason = "has gone too long without being checked";
-	else if ((fs->super->s_state & EXT2_VALID_FS) == 0)
-		reason = "was not cleanly unmounted";
-	if (reason) {
-		printf("%s %s, check forced.\n", device_name, reason);
-		return;
+	if (ctx->inode_used_map) {
+		ext2fs_free_inode_bitmap(ctx->inode_used_map);
+		ctx->inode_used_map = 0;
 	}
-	printf("%s: clean, %d/%d files, %d/%d blocks\n", device_name,
-	       fs->super->s_inodes_count - fs->super->s_free_inodes_count,
-	       fs->super->s_inodes_count,
-	       fs->super->s_blocks_count - fs->super->s_free_blocks_count,
-	       fs->super->s_blocks_count);
-	ext2fs_close(fs);
-	exit(FSCK_OK);
-}	
+	if (ctx->inode_dir_map) {
+		ext2fs_free_inode_bitmap(ctx->inode_dir_map);
+		ctx->inode_dir_map = 0;
+	}
+	if (ctx->block_found_map) {
+		ext2fs_free_block_bitmap(ctx->block_found_map);
+		ctx->block_found_map = 0;
+	}
+	if (ctx->inode_link_info) {
+		ext2fs_free_icount(ctx->inode_link_info);
+		ctx->inode_link_info = 0;
+	}
+	if (ctx->fs->dblist) {
+		ext2fs_free_dblist(ctx->fs->dblist);
+		ctx->fs->dblist = 0;
+	}
+	free_dir_info(ctx->fs);
+	if (ctx->block_dup_map) {
+		ext2fs_free_block_bitmap(ctx->block_dup_map);
+		ctx->block_dup_map = 0;
+	}
+	if (ctx->inode_bb_map) {
+		ext2fs_free_inode_bitmap(ctx->inode_bb_map);
+		ctx->inode_bb_map = 0;
+	}
+	if (ctx->inode_bad_map) {
+		ext2fs_free_inode_bitmap(ctx->inode_bad_map);
+		ctx->inode_bad_map = 0;
+	}
 
-#define PATH_SET "PATH=/sbin"
+	/*
+	 * Clear the array of invalid meta-data flags
+	 */
+	if (ctx->invalid_inode_bitmap_flag) {
+		free(ctx->invalid_inode_bitmap_flag);
+		ctx->invalid_inode_bitmap_flag = 0;
+	}
+	if (ctx->invalid_block_bitmap_flag) {
+		free(ctx->invalid_block_bitmap_flag);
+		ctx->invalid_block_bitmap_flag = 0;
+	}
+	if (ctx->invalid_inode_table_flag) {
+		free(ctx->invalid_inode_table_flag);
+		ctx->invalid_inode_table_flag = 0;
+	}
 
-static void PRS(int argc, char *argv[])
-{
-	int		flush = 0;
-	char		c;
-#ifdef MTRACE
-	extern void	*mallwatch;
-#endif
-	char		*oldpath = getenv("PATH");
+	/* Clear statistic counters */
+	ctx->fs_directory_count = 0;
+	ctx->fs_regular_count = 0;
+	ctx->fs_blockdev_count = 0;
+	ctx->fs_chardev_count = 0;
+	ctx->fs_links_count = 0;
+	ctx->fs_symlinks_count = 0;
+	ctx->fs_fast_symlinks_count = 0;
+	ctx->fs_fifo_count = 0;
+	ctx->fs_total_count = 0;
+	ctx->fs_badblocks_count = 0;
+	ctx->fs_sockets_count = 0;
+	ctx->fs_ind_count = 0;
+	ctx->fs_dind_count = 0;
+	ctx->fs_tind_count = 0;
+	ctx->fs_fragmented = 0;
 
-	/* Update our PATH to include /sbin  */
-	if (oldpath) {
-		char *newpath;
-
-		newpath = malloc(sizeof (PATH_SET) + 1 + strlen (oldpath));
-		if (!newpath)
-			fatal_error("Couldn't malloc() newpath");
-		strcpy (newpath, PATH_SET);
-		strcat (newpath, ":");
-		strcat (newpath, oldpath);
-		putenv (newpath);
-	} else
-		putenv (PATH_SET);
-
-	setbuf(stdout, NULL);
-	setbuf(stderr, NULL);
-	initialize_ext2_error_table();
+	/* Reset the superblock to the user's requested value */
+	ctx->superblock = ctx->use_superblock;
 	
-	if (argc && *argv)
-		program_name = *argv;
-	while ((c = getopt (argc, argv, "panyrcB:dfvtFVM:b:I:P:l:L:N:Ss")) != EOF)
-		switch (c) {
-		case 'p':
-		case 'a':
-			preen = 1;
-			yflag = nflag = 0;
-			break;
-		case 'n':
-			nflag = 1;
-			preen = yflag = 0;
-			break;
-		case 'y':
-			yflag = 1;
-			preen = nflag = 0;
-			break;
-		case 't':
-			tflag++;
-			break;
-		case 'c':
-			cflag++;
-			break;
-		case 'r':
-			/* What we do by default, anyway! */
-			break;
-		case 'b':
-			use_superblock = atoi(optarg);
-			break;
-		case 'B':
-			blocksize = atoi(optarg);
-			break;
-		case 'I':
-			inode_buffer_blocks = atoi(optarg);
-			break;
-		case 'P':
-			process_inode_size = atoi(optarg);
-			break;
-		case 'L':
-			replace_bad_blocks++;
-		case 'l':
-			bad_blocks_file = malloc(strlen(optarg)+1);
-			if (!bad_blocks_file)
-				fatal_error("Couldn't malloc bad_blocks_file");
-			strcpy(bad_blocks_file, optarg);
-			break;
-		case 'd':
-			debug = 1;
-			break;
-		case 'f':
-			force = 1;
-			break;
-		case 'F':
-#ifdef BLKFLSBUF
-			flush = 1;
-#else
-			fatal_error ("-F not supported");
-#endif
-			break;
-		case 'v':
-			verbose = 1;
-			break;
-		case 'V':
-			show_version_only = 1;
-			break;
-#ifdef MTRACE
-		case 'M':
-			mallwatch = (void *) strtol(optarg, NULL, 0);
-			break;
-#endif
-		case 'N':
-			device_name = optarg;
-			break;
-		case 's':
-			normalize_swapfs = 1;
-		case 'S':
-			swapfs = 1;
-			break;
-		default:
-			usage ();
-		}
-	if (show_version_only)
-		return;
-	if (optind != argc - 1)
-		usage ();
-	if (nflag && !bad_blocks_file && !cflag && !swapfs)
-		rwflag = 0;
-	filesystem_name = argv[optind];
-	if (device_name == 0)
-		device_name = filesystem_name;
-	if (flush) {
-#ifdef BLKFLSBUF
-		int	fd = open(filesystem_name, O_RDONLY, 0);
-
-		if (fd < 0) {
-			com_err("open", errno, "while opening %s for flushing",
-				filesystem_name);
-			exit(FSCK_ERROR);
-		}
-		if (ioctl(fd, BLKFLSBUF, 0) < 0) {
-			com_err("BLKFLSBUF", errno, "while trying to flush %s",
-				filesystem_name);
-			exit(FSCK_ERROR);
-		}
-		close(fd);
-#else
-		fatal_error ("BLKFLSBUF not supported");
-#endif /* BLKFLSBUF */
-	}
-	if (swapfs) {
-		if (cflag || bad_blocks_file) {
-			fprintf(stderr, "Incompatible options not "
-				"allowed when byte-swapping.\n");
-			fatal_error(0);
-		}
-	}
+	return 0;
 }
 
-static const char *my_ver_string = E2FSPROGS_VERSION;
-static const char *my_ver_date = E2FSPROGS_DATE;
-					
-int main (int argc, char *argv[])
+void e2fsck_free_context(e2fsck_t ctx)
 {
-	errcode_t	retval = 0;
-	int		exit_value = FSCK_OK;
-	int		i;
-	ext2_filsys	fs = 0;
-	io_manager	io_ptr;
-	struct ext2fs_sb *s;
-	const char	*lib_ver_date;
-	int		my_ver, lib_ver;
+	if (!ctx)
+		return;
 	
-#ifdef MTRACE
-	mtrace();
-#endif
-#ifdef MCHECK
-	mcheck(0);
-#endif
-	my_ver = ext2fs_parse_version_string(my_ver_string);
-	lib_ver = ext2fs_get_library_version(0, &lib_ver_date);
-	if (my_ver > lib_ver) {
-		fprintf( stderr, "Error: ext2fs library version "
-			"out of date!\n");
-		show_version_only++;
-	}
+	e2fsck_reset_context(ctx);
 	
-	init_resource_track(&global_rtrack);
-
-	PRS(argc, argv);
-
-	if (!preen || show_version_only)
-		fprintf (stderr, "e2fsck %s, %s for EXT2 FS %s, %s\n",
-			 my_ver_string, my_ver_date, EXT2FS_VERSION,
-			 EXT2FS_DATE);
-
-	if (show_version_only) {
-		fprintf(stderr, "\tUsing %s, %s\n",
-			error_message(EXT2_ET_BASE), lib_ver_date);
-		exit(0);
-	}
-	
-	check_mount();
-	
-	if (!preen && !nflag && !yflag) {
-		if (!isatty (0) || !isatty (1))
-			die ("need terminal for interactive repairs");
-	}
-	superblock = use_superblock;
-restart:
-#if 1
-	io_ptr = unix_io_manager;
-#else
-	io_ptr = test_io_manager;
-	test_io_backing_manager = unix_io_manager;
-#endif
-	sync_disks();
-	if (superblock && blocksize) {
-		retval = ext2fs_open(filesystem_name,
-				     rwflag ? EXT2_FLAG_RW : 0,
-				     superblock, blocksize, io_ptr, &fs);
-	} else if (superblock) {
-		for (i=0; possible_block_sizes[i]; i++) {
-			retval = ext2fs_open(filesystem_name,
-					     rwflag ? EXT2_FLAG_RW : 0,
-					     superblock,
-					     possible_block_sizes[i],
-					     io_ptr, &fs);
-			if (!retval)
-				break;
-		}
-	} else 
-		retval = ext2fs_open(filesystem_name,
-				     rwflag ? EXT2_FLAG_RW : 0,
-				     0, 0, io_ptr, &fs);
-	if (!superblock && !preen && 
-	    ((retval == EXT2_ET_BAD_MAGIC) ||
-	     ((retval == 0) && ext2fs_check_desc(fs)))) {
-		if (!fs || (fs->group_desc_count > 1)) {
-			printf("%s trying backup blocks...\n",
-			       retval ? "Couldn't find ext2 superblock," :
-			       "Group descriptors look bad...");
-			superblock = get_backup_sb(fs);
-			if (fs)
-				ext2fs_close(fs);
-			goto restart;
-		}
-	}
-	if (retval) {
-		com_err(program_name, retval, "while trying to open %s",
-			filesystem_name);
-		if (retval == EXT2_ET_REV_TOO_HIGH)
-			printf ("Get a newer version of e2fsck!\n");
-		else if (retval == EXT2_ET_SHORT_READ)
-			printf ("Could this be a zero-length partition?\n");
-		else if ((retval == EPERM) || (retval == EACCES))
-			printf("You must have %s access to the "
-			       "filesystem or be root\n",
-			       rwflag ? "r/w" : "r/o");
-		else if (retval == ENXIO)
-			printf("Possibly non-existent or swap device?\n");
-		else
-			printf(corrupt_msg, get_backup_sb(fs));
-		fatal_error(0);
-	}
-#ifdef	EXT2_CURRENT_REV
-	if (fs->super->s_rev_level > E2FSCK_CURRENT_REV) {
-		com_err(program_name, EXT2_ET_REV_TOO_HIGH,
-			"while trying to open %s",
-			filesystem_name);
-		goto get_newer;
-	}
-#endif
-	/*
-	 * Check for compatibility with the feature sets.  We need to
-	 * be more stringent than ext2fs_open().
-	 */
-	s = (struct ext2fs_sb *) fs->super;
-	if ((s->s_feature_compat & ~EXT2_LIB_FEATURE_COMPAT_SUPP) ||
-	    (s->s_feature_incompat & ~EXT2_LIB_FEATURE_INCOMPAT_SUPP)) {
-		com_err(program_name, EXT2_ET_UNSUPP_FEATURE,
-			"(%s)", filesystem_name);
-	get_newer:
-		printf ("Get a newer version of e2fsck!\n");
-		fatal_error(0);
-	}
-	if (s->s_feature_ro_compat & ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP) {
-		com_err(program_name, EXT2_ET_RO_UNSUPP_FEATURE,
-			"(%s)", filesystem_name);
-		goto get_newer;
-	}
-	
-	/*
-	 * If the user specified a specific superblock, presumably the
-	 * master superblock has been trashed.  So we mark the
-	 * superblock as dirty, so it can be written out.
-	 */
-	if (superblock && rwflag)
-		ext2fs_mark_super_dirty(fs);
-
-	/*
-	 * Don't overwrite the backup superblock and block
-	 * descriptors, until we're sure the filesystem is OK....
-	 */
-	fs->flags |= EXT2_FLAG_MASTER_SB_ONLY;
-
-	ehandler_init(fs->io);
-
-	invalid_inode_bitmap = allocate_memory(sizeof(int) *
-					       fs->group_desc_count,
-					       "invalid_inode_bitmap");
-	invalid_block_bitmap = allocate_memory(sizeof(int) *
-					       fs->group_desc_count,
-					       "invalid_block_bitmap");
-	invalid_inode_table = allocate_memory(sizeof(int) *
-					      fs->group_desc_count,
-					      "invalid_inode_table");
-		
-	check_super_block(fs);
-	check_if_skip(fs);
-	if (bad_blocks_file)
-		read_bad_blocks_file(fs, bad_blocks_file, replace_bad_blocks);
-	else if (cflag)
-		test_disk(fs);
-
-	if (normalize_swapfs) {
-		if ((fs->flags & EXT2_FLAG_SWAP_BYTES) ==
-		    ext2fs_native_flag()) {
-			fprintf(stderr, "%s: Filesystem byte order "
-				"already normalized.\n", device_name);
-			fatal_error(0);
-		}
-	}
-	if (swapfs)
-		swap_filesys(fs);
-
-	/*
-	 * Mark the system as valid, 'til proven otherwise
-	 */
-	ext2fs_mark_valid(fs);
-
-	retval = ext2fs_read_bb_inode(fs, &fs->badblocks);
-	if (retval) {
-		com_err(program_name, retval,
-			"while reading bad blocks inode");
-		preenhalt(fs);
-		printf("This doesn't bode well, but we'll try to go on...\n");
-	}
-	
-	pass1(fs);
-	free(invalid_inode_bitmap);
-	free(invalid_block_bitmap);
-	free(invalid_inode_table);
-	if (restart_e2fsck) {
-		ext2fs_close(fs);
-		printf("Restarting e2fsck from the beginning...\n");
-		restart_e2fsck = 0;
-		superblock = use_superblock;
-		goto restart;
-	}
-	pass2(fs);
-	pass3(fs);
-	pass4(fs);
-	pass5(fs);
-
-#ifdef MTRACE
-	mtrace_print("Cleanup");
-#endif
-	if (ext2fs_test_changed(fs)) {
-		exit_value = FSCK_NONDESTRUCT;
-		if (!preen)
-			printf("\n%s: ***** FILE SYSTEM WAS MODIFIED *****\n",
-			       device_name);
-		if (root_filesystem && !read_only_root) {
-			printf("%s: ***** REBOOT LINUX *****\n", device_name);
-			exit_value = FSCK_REBOOT;
-		}
-	}
-	if (ext2fs_test_valid(fs))
-		fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
-	else
-		exit_value = FSCK_UNCORRECTED;
-	if (rwflag) {
-		if (ext2fs_test_valid(fs)) {
-			if (!(fs->super->s_state & EXT2_VALID_FS))
-				exit_value = FSCK_NONDESTRUCT;
-			fs->super->s_state = EXT2_VALID_FS;
-		} else
-			fs->super->s_state &= ~EXT2_VALID_FS;
-		fs->super->s_mnt_count = 0;
-		fs->super->s_lastcheck = time(NULL);
-		ext2fs_mark_super_dirty(fs);
-	}
-	show_stats(fs);
-
-	write_bitmaps(fs);
-	ext2fs_close(fs);
-	sync_disks();
-	
-	if (tflag)
-		print_resource_track(&global_rtrack);
-	
-	return exit_value;
+	free(ctx);
 }
diff --git a/e2fsck/e2fsck.h b/e2fsck/e2fsck.h
index c6dbd57..27d703a 100644
--- a/e2fsck/e2fsck.h
+++ b/e2fsck/e2fsck.h
@@ -45,12 +45,6 @@
 #define E2FSCK_CURRENT_REV	1
 
 /*
- * Inode count arrays
- */
-extern ext2_icount_t	inode_count;
-extern ext2_icount_t	inode_link_info;
-
-/*
  * The directory information structure; stores directory information
  * collected in earlier passes, to avoid disk i/o in fetching the
  * directory information.
@@ -73,85 +67,110 @@
 };
 
 /*
- * Variables
+ * E2fsck options
  */
-extern const char * program_name;
-extern const char * device_name;
-
-extern ext2fs_inode_bitmap inode_used_map; /* Inodes which are in use */
-extern ext2fs_inode_bitmap inode_bad_map; /* Inodes which are bad somehow */
-extern ext2fs_inode_bitmap inode_dir_map; /* Inodes which are directories */
-extern ext2fs_inode_bitmap inode_bb_map; /* Inodes which are in bad blocks */
-
-extern ext2fs_block_bitmap block_found_map; /* Blocks which are in use */
-extern ext2fs_block_bitmap block_dup_map; /* Blocks which are used by more than once */
-extern ext2fs_block_bitmap block_illegal_map; /* Meta-data blocks */
-
-extern const char *fix_msg[2];	/* Fixed or ignored! */
-extern const char *clear_msg[2]; /* Cleared or ignored! */
-
-extern int *invalid_inode_bitmap;
-extern int *invalid_block_bitmap;
-extern int *invalid_inode_table;
-extern int restart_e2fsck;
-
-/* Command line options */
-extern int nflag;
-extern int yflag;
-extern int tflag;
-extern int preen;
-extern int verbose;
-extern int list;
-extern int debug;
-extern int force;
-
-extern int rwflag;
-
-extern int inode_buffer_blocks;
-extern int process_inode_size;
-extern int directory_blocks;
-
-extern int no_bad_inode;
-extern int no_lpf;
-extern int lpf_corrupted;
-
-/* Files counts */
-extern int fs_directory_count;
-extern int fs_regular_count;
-extern int fs_blockdev_count;
-extern int fs_chardev_count;
-extern int fs_links_count;
-extern int fs_symlinks_count;
-extern int fs_fast_symlinks_count;
-extern int fs_fifo_count;
-extern int fs_total_count;
-extern int fs_badblocks_count;
-extern int fs_sockets_count;
-extern int fs_ind_count;
-extern int fs_dind_count;
-extern int fs_tind_count;
-extern int fs_fragmented;
-
-extern struct resource_track	global_rtrack;
-
-extern int invalid_bitmaps;
+#define E2F_OPT_READONLY	0x0001
+#define E2F_OPT_PREEN		0x0002
+#define E2F_OPT_YES		0x0004
+#define E2F_OPT_NO		0x0008
+#define E2F_OPT_TIME		0x0010
+#define E2F_OPT_TIME2		0x0020
+#define E2F_OPT_CHECKBLOCKS	0x0040
+#define E2F_OPT_DEBUG		0x0080
 
 /*
- * For pass1_check_directory and pass1_get_blocks
+ * This is the global e2fsck structure.
  */
-extern ino_t stashed_ino;
-extern struct ext2_inode *stashed_inode;
+struct e2fsck_struct {
+	ext2_filsys fs;
+	const char *program_name;
+	const char *filesystem_name;
+	const char *device_name;
+	int	options;
+	blk_t	use_superblock;	/* sb requested by user */
+	blk_t	superblock;	/* sb used to open fs */
+
+	ext2fs_inode_bitmap inode_used_map; /* Inodes which are in use */
+	ext2fs_inode_bitmap inode_bad_map; /* Inodes which are bad somehow */
+	ext2fs_inode_bitmap inode_dir_map; /* Inodes which are directories */
+	ext2fs_inode_bitmap inode_bb_map; /* Inodes which are in bad blocks */
+
+	ext2fs_block_bitmap block_found_map; /* Blocks which are in use */
+	ext2fs_block_bitmap block_dup_map; /* Blks referenced more than once */
+	ext2fs_block_bitmap block_illegal_map; /* Meta-data blocks */
+
+	/*
+	 * Inode count arrays
+	 */
+	ext2_icount_t	inode_count;
+	ext2_icount_t inode_link_info;
+
+	/*
+	 * Array of flags indicating whether an inode bitmap, block
+	 * bitmap, or inode table is invalid
+	 */
+	int *invalid_inode_bitmap_flag;
+	int *invalid_block_bitmap_flag;
+	int *invalid_inode_table_flag;
+	int invalid_bitmaps;	/* There are invalid bitmaps/itable */
+
+	/*
+	 * For pass1_check_directory and pass1_get_blocks
+	 */
+	ino_t stashed_ino;
+	struct ext2_inode *stashed_inode;
+
+	/*
+	 * Tuning parameters
+	 */
+	int process_inode_size;
+	int inode_buffer_blocks;
+
+	/*
+	 * For timing purposes
+	 */
+	struct resource_track	global_rtrack;
+
+	/* File counts */
+	int fs_directory_count;
+	int fs_regular_count;
+	int fs_blockdev_count;
+	int fs_chardev_count;
+	int fs_links_count;
+	int fs_symlinks_count;
+	int fs_fast_symlinks_count;
+	int fs_fifo_count;
+	int fs_total_count;
+	int fs_badblocks_count;
+	int fs_sockets_count;
+	int fs_ind_count;
+	int fs_dind_count;
+	int fs_tind_count;
+	int fs_fragmented;
+};
+
+typedef struct e2fsck_struct *e2fsck_t;
+
+/*
+ * Variables
+ */
+extern int restart_e2fsck;
 
 /*
  * Procedure declarations
  */
 
-extern void pass1(ext2_filsys fs);
-extern void pass1_dupblocks(ext2_filsys fs, char *block_buf);
-extern void pass2(ext2_filsys fs);
-extern void pass3(ext2_filsys fs);
-extern void pass4(ext2_filsys fs);
-extern void pass5(ext2_filsys fs);
+extern void pass1(e2fsck_t ctx);
+extern void pass1_dupblocks(e2fsck_t ctx, char *block_buf);
+extern void pass2(e2fsck_t ctx);
+extern void pass3(e2fsck_t ctx);
+extern void pass4(e2fsck_t ctx);
+extern void pass5(e2fsck_t ctx);
+
+/* e2fsck.c */
+errcode_t e2fsck_allocate_context(e2fsck_t *ret);
+errcode_t e2fsck_reset_context(e2fsck_t ctx);
+void e2fsck_free_context(e2fsck_t ctx);
 
 /* pass1.c */
 extern errcode_t pass1_check_directory(ext2_filsys fs, ino_t ino);
@@ -163,9 +182,9 @@
 extern int e2fsck_pass1_check_device_inode(struct ext2_inode *inode);
 
 /* badblock.c */
-extern void read_bad_blocks_file(ext2_filsys fs, const char *bad_blocks_file,
+extern void read_bad_blocks_file(e2fsck_t ctx, const char *bad_blocks_file,
 				 int replace_bad_blocks);
-extern void test_disk(ext2_filsys fs);
+extern void test_disk(e2fsck_t ctx);
 
 /* dirinfo.c */
 extern void add_dir_info(ext2_filsys fs, ino_t ino, ino_t parent);
@@ -178,18 +197,22 @@
 extern const char *ehandler_operation(const char *op);
 extern void ehandler_init(io_channel channel);
 
+/* super.c */
+void check_super_block(e2fsck_t ctx);
+
 /* swapfs.c */
-void swap_filesys(ext2_filsys fs);
+void swap_filesys(e2fsck_t ctx);
 
 /* util.c */
 extern void *allocate_memory(int size, const char *description);
-extern int ask(const char * string, int def);
+extern int ask(e2fsck_t ctx, const char * string, int def);
 extern int ask_yn(const char * string, int def);
 extern void fatal_error (const char * fmt_string);
-extern void read_bitmaps(ext2_filsys fs);
-extern void write_bitmaps(ext2_filsys fs);
-extern void preenhalt(ext2_filsys fs);
-extern void print_resource_track(struct resource_track *track);
+extern void read_bitmaps(e2fsck_t ctx);
+extern void write_bitmaps(e2fsck_t ctx);
+extern void preenhalt(e2fsck_t ctx);
+extern void print_resource_track(const char *desc,
+				 struct resource_track *track);
 extern void init_resource_track(struct resource_track *track);
 extern int inode_has_valid_blocks(struct ext2_inode *inode);
 extern void e2fsck_read_inode(ext2_filsys fs, unsigned long ino,
@@ -199,10 +222,11 @@
 #ifdef MTRACE
 extern void mtrace_print(char *mesg);
 #endif
+extern blk_t get_backup_sb(ext2_filsys fs);
 
 #define die(str)	fatal_error(str)
 
 /*
  * pass3.c
  */
-extern int reconnect_file(ext2_filsys fs, ino_t inode);
+extern int reconnect_file(e2fsck_t ctx, ino_t inode);
diff --git a/e2fsck/ehandler.c b/e2fsck/ehandler.c
index 131a0ab..b370756 100644
--- a/e2fsck/ehandler.c
+++ b/e2fsck/ehandler.c
@@ -29,6 +29,10 @@
 {
 	int	i;
 	char	*p;
+	ext2_filsys fs = channel->app_data;
+	e2fsck_t ctx;
+
+	ctx = fs->private;
 
 	/*
 	 * If more than one block was read, try reading each block
@@ -51,8 +55,8 @@
 	else
 		printf("Error reading block %lu (%s).  ", block,
 		       error_message(error));
-	preenhalt(NULL);
-	if (ask("Ignore error", 1))
+	preenhalt(ctx);
+	if (ask(ctx, "Ignore error", 1))
 		return 0;
 
 	return error;
@@ -68,7 +72,11 @@
 {
 	int		i;
 	const char	*p;
+	ext2_filsys fs = channel->app_data;
+	e2fsck_t ctx;
 	
+	ctx = fs->private;
+
 	/*
 	 * If more than one block was written, try writing each block
 	 * separately.  We could use the actual bytes read to figure
@@ -91,8 +99,8 @@
 	else
 		printf("Error writing block %lu (%s).  ", block,
 		       error_message(error));
-	preenhalt(NULL);
-	if (ask("Ignore error", 1))
+	preenhalt(ctx);
+	if (ask(ctx, "Ignore error", 1))
 		return 0;
 
 	return error;
diff --git a/e2fsck/iscan.c b/e2fsck/iscan.c
index d964b34..5a67b94 100644
--- a/e2fsck/iscan.c
+++ b/e2fsck/iscan.c
@@ -139,7 +139,7 @@
 		num_inodes++;
 	}
 	
-	print_resource_track(&global_rtrack);
+	print_resource_track(NULL, &global_rtrack);
 	printf("%d inodes scanned.\n", num_inodes);
 	
 	exit(0);
diff --git a/e2fsck/message.c b/e2fsck/message.c
index b9f9dea..673c463 100644
--- a/e2fsck/message.c
+++ b/e2fsck/message.c
@@ -15,8 +15,11 @@
  *
  * 	%b	<blk>			block number
  * 	%B	<blkcount>		integer
+ * 	%c	<blk2>			block number
  * 	%di	<dirent>->ino		inode number
  * 	%dn	<dirent>->name		string
+ * 	%dr	<dirent>->rec_len
+ * 	%dl	<dirent>->name_len
  * 	%D	<dir> 			inode number
  * 	%g	<group>			integer
  * 	%i	<ino>			inode number
@@ -29,6 +32,7 @@
  * 	%If	<inode> -> i_file_acl
  * 	%Id	<inode> -> i_dir_acl
  * 	%j	<ino2>			inode number
+ * 	%m	<com_err error message>
  * 	%N	<num>
  *	%p	ext2fs_get_pathname of directory <ino>
  * 	%P	ext2fs_get_pathname of <dirent>->ino with <ino2> as
@@ -37,6 +41,8 @@
  * 	%q	ext2fs_get_pathname of directory <dir>
  * 	%Q	ext2fs_get_pathname of directory <ino> with <dir> as
  * 			the containing directory.
+ * 	%s	<str>			miscellaneous string
+ * 	%S	backup superblock
  *
  * The following '@' expansions are supported:
  *
@@ -49,12 +55,15 @@
  * 	@d	directory
  * 	@e	entry
  * 	@E	Entry '%Dn' in %p (%i)
+ * 	@f	filesystem
  * 	@F	for @i %i (%Q) is
  * 	@g	group
  * 	@l	lost+found
  * 	@L	is a link
  * 	@u	unattached
  * 	@r	root inode
+ * 	@s	should be 
+ * 	@S	superblock
  * 	@z	zero-length
  */
 
@@ -90,6 +99,7 @@
 	"ddirectory",
 	"eentry",
 	"E@e '%Dn' in %p (%i)",
+	"ffilesystem",
 	"Ffor @i %i (%Q) is",
 	"ggroup",
 	"llost+found",
@@ -97,6 +107,7 @@
 	"uunattached",
 	"rroot @i",
 	"sshould be",
+	"Ssuper@b",
 	"zzero-length",
 	"@@",
 	0
@@ -145,8 +156,8 @@
  * expansion; an @ expression can contain further '@' and '%'
  * expressions. 
  */
-static _INLINE_ void expand_at_expression(ext2_filsys fs, char ch,
-					  struct problem_context *ctx,
+static _INLINE_ void expand_at_expression(e2fsck_t ctx, char ch,
+					  struct problem_context *pctx,
 					  int *first)
 {
 	const char **cpp, *str;
@@ -162,7 +173,7 @@
 			*first = 0;
 			fputc(toupper(*str++), stdout);
 		}
-		print_e2fsck_message(fs, str, ctx, *first);
+		print_e2fsck_message(ctx, str, pctx, *first);
 	} else
 		printf("@%c", ch);
 }
@@ -242,6 +253,12 @@
 			len = dirent->rec_len;
 		printf("%.*s", dirent->name_len, dirent->name);
 		break;
+	case 'r':
+		printf("%u", dirent->rec_len);
+		break;
+	case 'l':
+		printf("%u", dirent->name_len);
+		break;
 	default:
 	no_dirent:
 		printf("%%D%c", ch);
@@ -265,6 +282,9 @@
 	case 'B':
 		printf("%d", ctx->blkcount);
 		break;
+	case 'c':
+		printf("%u", ctx->blk2);
+		break;
 	case 'd':
 		printf("%lu", ctx->dir);
 		break;
@@ -277,6 +297,9 @@
 	case 'j':
 		printf("%lu", ctx->ino2);
 		break;
+	case 'm':
+		printf("%s", error_message(ctx->errcode));
+		break;
 	case 'N':
 		printf("%u", ctx->num);
 		break;
@@ -293,6 +316,12 @@
 	case 'Q':
 		print_pathname(fs, ctx->dir, ctx->ino);
 		break;
+	case 'S':
+		printf("%d", get_backup_sb(fs));
+		break;
+	case 's':
+		printf("%s", ctx->str);
+		break;
 	default:
 	no_context:
 		printf("%%%c", ch);
@@ -300,25 +329,26 @@
 	}
 }	
 
-void print_e2fsck_message(ext2_filsys fs, const char *msg,
-			  struct problem_context *ctx, int first)
+void print_e2fsck_message(e2fsck_t ctx, const char *msg,
+			  struct problem_context *pctx, int first)
 {
+	ext2_filsys fs = ctx->fs;
 	const char *	cp;
 	int		i;
 	
 	for (cp = msg; *cp; cp++) {
 		if (cp[0] == '@') {
 			cp++;
-			expand_at_expression(fs, *cp, ctx, &first);
+			expand_at_expression(ctx, *cp, pctx, &first);
 		} else if (cp[0] == '%' && cp[1] == 'I') {
 			cp += 2;
-			expand_inode_expression(*cp, ctx);
+			expand_inode_expression(*cp, pctx);
 		} else if (cp[0] == '%' && cp[1] == 'D') {
 			cp += 2;
-			expand_dirent_expression(*cp, ctx);
+			expand_dirent_expression(*cp, pctx);
 		} else if ((cp[0] == '%')) {
 			cp++;
-			expand_percent_expression(fs, *cp, ctx);
+			expand_percent_expression(fs, *cp, pctx);
 		} else {
 			for (i=0; cp[i]; i++)
 				if ((cp[i] == '@') || cp[i] == '%')
diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index 2f1d67e..bd046d2 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -40,7 +40,6 @@
 #include <errno.h>
 #endif
 
-#include <et/com_err.h>
 #include "e2fsck.h"
 #include "problem.h"
 
@@ -50,47 +49,19 @@
 #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);
 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,
+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 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 int 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);
@@ -105,6 +76,7 @@
 	blk_t	previous_block;
 	struct ext2_inode *inode;
 	struct problem_context *pctx;
+	e2fsck_t	ctx;
 };
 
 struct process_inode_block {
@@ -113,17 +85,10 @@
 };
 
 /*
- * For pass1_check_directory and pass1_get_blocks
- */
-ino_t stashed_ino;
-struct ext2_inode *stashed_inode;
-
-/*
  * For the inodes to process list.
  */
 static struct process_inode_block *inodes_to_process;
 static int process_inode_count;
-int process_inode_size = 256;
 
 /*
  * Free all memory allocated by pass1 in preparation for restarting
@@ -131,39 +96,7 @@
  */
 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;
 }
 
 /*
@@ -180,21 +113,22 @@
 	return 1;
 }
 
-void pass1(ext2_filsys fs)
+void pass1(e2fsck_t ctx)
 {
+	ext2_filsys fs = ctx->fs;
 	ino_t	ino;
 	struct ext2_inode inode;
 	ext2_inode_scan	scan;
 	char		*block_buf;
-	errcode_t	retval;
 	struct resource_track	rtrack;
 	unsigned char	frag, fsize;
 	struct		problem_context pctx;
 	
 	init_resource_track(&rtrack);
-	
-	if (!preen)
-		printf("Pass 1: Checking inodes, blocks, and sizes\n");
+	clear_problem_context(&pctx);
+
+	if (!(ctx->options & E2F_OPT_PREEN))
+		fix_problem(ctx, PR_1_PASS_HEADER, &pctx);
 
 #ifdef MTRACE
 	mtrace_print("Pass 1");
@@ -203,85 +137,85 @@
 	/*
 	 * 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");
+	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);
 		fatal_error(0);
 	}
-	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");
+	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);
 		fatal_error(0);
 	}
-	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");
+	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);
 		fatal_error(0);
 	}
-	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");
+	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);
 		fatal_error(0);
 	}
-	retval = ext2fs_create_icount2(fs, 0, 0, 0, &inode_link_info);
-	if (retval) {
-		com_err("ext2fs_create_icount", retval,
-			"while creating inode_link_info");
+	pctx.errcode = ext2fs_create_icount2(fs, 0, 0, 0,
+					     &ctx->inode_link_info);
+	if (pctx.errcode) {
+		fix_problem(ctx, PR_1_ALLOCATE_ICOUNT, &pctx);
 		fatal_error(0);
 	}
-	inodes_to_process = allocate_memory(process_inode_size *
+	inodes_to_process = allocate_memory(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");
+	pctx.errcode = ext2fs_init_dblist(fs, 0);
+	if (pctx.errcode) {
+		fix_problem(ctx, PR_1_ALLOCATE_DBCOUNT, &pctx);
 		fatal_error(0);
 	}
 
-	mark_table_blocks(fs);
+	mark_table_blocks(ctx);
 	block_buf = allocate_memory(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");
+	pctx.errcode = ext2fs_open_inode_scan(fs, ctx->inode_buffer_blocks, 
+					      &scan);
+	if (pctx.errcode) {
+		fix_problem(ctx, PR_1_ISCAN_ERROR, &pctx);
 		fatal_error(0);
 	}
 	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");
+	pctx.errcode = ext2fs_get_next_inode(scan, &ino, &inode);
+	if (pctx.errcode) {
+		fix_problem(ctx, PR_1_ISCAN_ERROR, &pctx);
 		fatal_error(0);
 	}
-	stashed_inode = &inode;
+	ctx->stashed_inode = &inode;
 	ext2fs_set_inode_callback(scan, scan_callback, block_buf);
-	clear_problem_context(&pctx);
 	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);
+				fatal_error(0);
+			}
+		}
 		if (ino == EXT2_BAD_INO) {
 			struct process_block_struct pb;
 			
@@ -292,12 +226,14 @@
 			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);
+				fatal_error(0);
+			}
+			ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino);
 			clear_problem_context(&pctx);
 			goto next;
 		}
@@ -308,10 +244,10 @@
 			 * 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,
 							   "pass1");
@@ -327,7 +263,7 @@
 			 * 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,
 							   "pass1");
@@ -335,22 +271,22 @@
 			}
 		}
 		if (ino == EXT2_BOOT_LOADER_INO) {
-			ext2fs_mark_inode_bitmap(inode_used_map, ino);
-			check_blocks(fs, &pctx, block_buf);
+			ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino);
+			check_blocks(ctx, &pctx, block_buf);
 			goto next;
 		}
 		if ((ino != EXT2_ROOT_INO) &&
 		    (ino < EXT2_FIRST_INODE(fs->super))) {
-			ext2fs_mark_inode_bitmap(inode_used_map, ino);
+			ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino);
 			if (inode.i_mode != 0) {
-				if (fix_problem(fs,
+				if (fix_problem(ctx,
 					    PR_1_RESERVED_BAD_MODE, &pctx)) {
 					inode.i_mode = 0;
 					e2fsck_write_inode(fs, ino, &inode,
 							   "pass1");
 				}
 			}
-			check_blocks(fs, &pctx, block_buf);
+			check_blocks(ctx, &pctx, block_buf);
 			goto next;
 		}
 		/*
@@ -359,7 +295,7 @@
 		 */
 		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,
@@ -379,13 +315,13 @@
 		 * 
 		 */
 		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");
 			}
 		}
 		
-		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;
@@ -405,45 +341,45 @@
 		
 		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);
+			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);
+			ext2fs_mark_inode_bitmap(ctx->inode_dir_map, ino);
 			add_dir_info(fs, ino, 0);
-			fs_directory_count++;
+			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++;
+			ctx->fs_fifo_count++;
 		else if (LINUX_S_ISSOCK (inode.i_mode))
-		        fs_sockets_count++;
+		        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,43 +387,42 @@
 			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 (process_inode_count >= process_inode_size)
-			process_inodes(fs, block_buf);
+		if (process_inode_count >= ctx->process_inode_size)
+			process_inodes(ctx, block_buf);
 	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 (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");
+		if (pctx.errcode) {
+			fix_problem(ctx, PR_1_ISCAN_ERROR, &pctx);
 			fatal_error(0);
 		}
 	}
-	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) {
 		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);
+		pass1_dupblocks(ctx, block_buf);
 	}
 	free(inodes_to_process);
 endit:
@@ -497,13 +432,11 @@
 	fs->write_inode = 0;
 	
 	free(block_buf);
-	ext2fs_free_block_bitmap(block_illegal_map);
-	block_illegal_map = 0;
+	ext2fs_free_block_bitmap(ctx->block_illegal_map);
+	ctx->block_illegal_map = 0;
 
-	if (tflag > 1) {
-		printf("Pass 1: ");
-		print_resource_track(&rtrack);
-	}
+	if (ctx->options & E2F_OPT_TIME2)
+		print_resource_track("Pass 1", &rtrack);
 }
 
 /*
@@ -513,14 +446,14 @@
 static errcode_t scan_callback(ext2_filsys fs, ext2_inode_scan scan,
 			       dgrp_t group, void * private)
 {
-	process_inodes(fs, (char *) private);
+	process_inodes((e2fsck_t) fs->private, (char *) private);
 	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 +466,24 @@
 	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);
 	}
-	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");
@@ -572,15 +505,17 @@
 /*
  * 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");
+	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);
 		fatal_error(0);
 	}
 }
@@ -588,15 +523,17 @@
 /*
  * 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");
+	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);
 		fatal_error(0);
 	}
 }
@@ -608,23 +545,27 @@
  * 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");
+	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);
 				fatal_error(0);
 			}
 		}
-		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,11 +573,11 @@
  * 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;
 	
@@ -652,30 +593,26 @@
 	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");
+	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);
-		}
 		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);
+		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
@@ -692,14 +629,14 @@
 	       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--;
+			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;
 		}
 	}
@@ -707,7 +644,7 @@
 			   (pb.last_block + 1) * fs->blocksize)) ||
 	    (inode->i_size < pb.last_block * fs->blocksize)) {
 		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");
 		}
@@ -715,7 +652,7 @@
 	}
 	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");
 		}
@@ -790,10 +727,11 @@
 	blk_t	blk = *block_nr;
 	int	ret_code = 0;
 	int	problem = 0;
-	errcode_t	retval;
+	e2fsck_t	ctx;
 
 	p = (struct process_block_struct *) private;
 	pctx = p->pctx;
+	ctx = p->ctx;
 
 	if (blk == 0) {
 		if (p->is_dir == 0) {
@@ -801,9 +739,11 @@
 			 * 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 +786,19 @@
 	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 (ask(ctx, "Suppress messages", 0)) {
 				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,51 +808,34 @@
 		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");
+		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);
 			fatal_error(0);
 		}
 	}
 	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))
+		fatal_error(0);
 }
 
 int process_bad_block(ext2_filsys fs,
@@ -926,20 +850,21 @@
 	int		first_block;
 	int		i;
 	struct problem_context *pctx;
+	e2fsck_t	ctx;
 
 	if (!blk)
 		return 0;
 	
 	p = (struct process_block_struct *) private;
+	ctx = p->ctx;
 	pctx = p->pctx;
 	
 	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 +872,24 @@
 	}
 
 	if (blockcnt < 0) {
-		if (ext2fs_test_block_bitmap(block_found_map, blk))
-			bad_block_indirect(fs, blk);
+		if (ext2fs_test_block_bitmap(ctx->block_found_map, blk))
+			bad_block_indirect(ctx, blk);
 		else
-			mark_block_used(fs, blk);
+			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 +899,45 @@
 	
 	for (i = 0; i < fs->group_desc_count; i++ ) {
 		pctx->group = i;
+		pctx->blk = blk;
 		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;
 		}
 		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;
 		}
@@ -1035,62 +959,69 @@
 	 */
 	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);
 		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);
+		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);
 }
@@ -1102,37 +1033,39 @@
  * 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++;
 		}
 		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 +1080,17 @@
 			/*
 			 * 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 +1102,18 @@
 			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 +1123,17 @@
 		 * 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 +1142,17 @@
 		 * 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 +1168,45 @@
  */
 errcode_t pass1_get_blocks(ext2_filsys fs, ino_t ino, blk_t *blocks)
 {
+	e2fsck_t ctx = fs->private;
 	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 = fs->private;
+
+	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 = fs->private;
+
+	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 = fs->private;
+
+	if (ino != ctx->stashed_ino)
+		return EXT2_ET_CALLBACK_NOTHANDLED;
+
+	if (!LINUX_S_ISDIR(ctx->stashed_inode->i_mode))
+		return ENOTDIR;
+	return 0;
 }
diff --git a/e2fsck/pass1b.c b/e2fsck/pass1b.c
index 0edefde..30c5924 100644
--- a/e2fsck/pass1b.c
+++ b/e2fsck/pass1b.c
@@ -89,12 +89,12 @@
 
 static int process_pass1b_block(ext2_filsys fs, blk_t	*blocknr,
 				int	blockcnt, void	*private);
-static void delete_file(ext2_filsys fs, struct dup_inode *dp,
+static void delete_file(e2fsck_t ctx, struct dup_inode *dp,
 			char *block_buf);
-static int clone_file(ext2_filsys fs, struct dup_inode *dp, char* block_buf);
-static void pass1b(ext2_filsys fs, char *block_buf);
-static void pass1c(ext2_filsys fs, char *block_buf);
-static void pass1d(ext2_filsys fs, char *block_buf);
+static int clone_file(e2fsck_t ctx, struct dup_inode *dp, char* block_buf);
+static void pass1b(e2fsck_t ctx, char *block_buf);
+static void pass1c(e2fsck_t ctx, char *block_buf);
+static void pass1d(e2fsck_t ctx, char *block_buf);
 
 static struct dup_block *dup_blk = 0;
 static struct dup_inode *dup_ino = 0;
@@ -105,30 +105,32 @@
 /*
  * Main procedure for handling duplicate blocks
  */
-void pass1_dupblocks(ext2_filsys fs, char *block_buf)
+void pass1_dupblocks(e2fsck_t ctx, char *block_buf)
 {
-	errcode_t		retval;
+	ext2_filsys 		fs = ctx->fs;
 	struct dup_block	*p, *q, *next_p, *next_q;
 	struct dup_inode	*r, *next_r;
+	struct problem_context	pctx;
+
+	clear_problem_context(&pctx);
 	
-	retval = ext2fs_allocate_inode_bitmap(fs,
+	pctx.errcode = ext2fs_allocate_inode_bitmap(fs,
 		      "multiply claimed inode map", &inode_dup_map);
-	if (retval) {
-		com_err("ext2fs_allocate_inode_bitmap", retval,
-			"while allocating inode_dup_map");
+	if (pctx.errcode) {
+		fix_problem(ctx, PR_1B_ALLOCATE_IBITMAP_ERROR, &pctx);
 		fatal_error(0);
 	}
 	
-	pass1b(fs, block_buf);
-	pass1c(fs, block_buf);
-	pass1d(fs, block_buf);
+	pass1b(ctx, block_buf);
+	pass1c(ctx, block_buf);
+	pass1d(ctx, block_buf);
 
 	/*
 	 * Time to free all of the accumulated data structures that we
 	 * don't need anymore.
 	 */
-	ext2fs_free_inode_bitmap(inode_dup_map);    inode_dup_map = 0;
-	ext2fs_free_block_bitmap(block_dup_map);    block_dup_map = 0;
+	ext2fs_free_inode_bitmap(inode_dup_map); inode_dup_map = 0;
+	ext2fs_free_block_bitmap(ctx->block_dup_map); ctx->block_dup_map = 0;
 	for (p = dup_blk; p; p = next_p) {
 		next_p = p->next_block;
 		for (q = p; q; q = next_q) {
@@ -148,34 +150,42 @@
 struct process_block_struct {
 	ino_t	ino;
 	int	dup_blocks;
+	e2fsck_t ctx;
+	struct problem_context *pctx;
 };
 
-void pass1b(ext2_filsys fs, char *block_buf)
+void pass1b(e2fsck_t ctx, char *block_buf)
 {
+	ext2_filsys fs = ctx->fs;
 	ino_t	ino;
 	struct ext2_inode inode;
 	ext2_inode_scan	scan;
 	errcode_t	retval;
 	struct process_block_struct pb;
 	struct dup_inode *dp;
+	struct problem_context pctx;
+
+	clear_problem_context(&pctx);
 	
-	printf("Duplicate blocks found... invoking duplicate block passes.\n");
-	printf("Pass 1B: Rescan for duplicate/bad blocks\n");
-	retval = ext2fs_open_inode_scan(fs, inode_buffer_blocks, &scan);
-	if (retval) {
-		com_err(program_name, retval, "while opening inode scan");
+	fix_problem(ctx, PR_1B_PASS_HEADER, &pctx);
+	pctx.errcode = ext2fs_open_inode_scan(fs, ctx->inode_buffer_blocks,
+					      &scan);
+	if (pctx.errcode) {
+		fix_problem(ctx, PR_1B_ISCAN_ERROR, &pctx);
 		fatal_error(0);
 	}
-	retval = ext2fs_get_next_inode(scan, &ino, &inode);
-	if (retval) {
-		com_err(program_name, retval, "while starting inode scan");
+	pctx.errcode = ext2fs_get_next_inode(scan, &ino, &inode);
+	if (pctx.errcode) {
+		fix_problem(ctx, PR_1B_ISCAN_ERROR, &pctx);
 		fatal_error(0);
 	}
-	stashed_inode = &inode;
+	ctx->stashed_inode = &inode;
+	pb.ctx = ctx;
+	pb.pctx = &pctx;
 	while (ino) {
-		stashed_ino = ino;
+		pctx.ino = ctx->stashed_ino = ino;
 		if ((ino != EXT2_BAD_INO) &&
-		    (!ext2fs_test_inode_bitmap(inode_used_map, ino) ||
+		    (!ext2fs_test_inode_bitmap(ctx->inode_used_map, ino) ||
 		     !ext2fs_inode_has_valid_blocks(&inode)))
 			goto next;
 
@@ -184,8 +194,7 @@
 		retval = ext2fs_block_iterate(fs, ino, 0, block_buf,
 					      process_pass1b_block, &pb);
 		if (pb.dup_blocks) {
-			if (ino != EXT2_BAD_INO)
-				printf("\n");
+			end_problem_latch(ctx, PR_LATCH_DBLOCK);
 			dp = allocate_memory(sizeof(struct dup_inode),
 					     "duplicate inode record");
 			dp->ino = ino;
@@ -198,16 +207,15 @@
 				dup_inode_count++;
 		}
 		if (retval)
-			com_err(program_name, retval,
+			com_err(ctx->program_name, retval,
 				"while calling ext2fs_block_iterate in pass1b");
 		
 	next:
-		retval = ext2fs_get_next_inode(scan, &ino, &inode);
-		if (retval == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE)
+		pctx.errcode = ext2fs_get_next_inode(scan, &ino, &inode);
+		if (pctx.errcode == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE)
 			goto next;
-		if (retval) {
-			com_err(program_name, retval,
-				"while doing inode scan");
+		if (pctx.errcode) {
+			fix_problem(ctx, PR_1B_ISCAN_ERROR, &pctx);
 			fatal_error(0);
 		}
 	}
@@ -224,21 +232,21 @@
 	struct process_block_struct *p;
 	struct dup_block *dp, *q, *r;
 	int i;
+	e2fsck_t ctx;
 
 	if (!*block_nr)
 		return 0;
 	p = (struct process_block_struct *) private;
+	ctx = p->ctx;
 	
-	if (ext2fs_test_block_bitmap(block_dup_map, *block_nr)) {
+	if (ext2fs_test_block_bitmap(ctx->block_dup_map, *block_nr)) {
 		/* OK, this is a duplicate block */
 		if (p->ino != EXT2_BAD_INO) {
-			if (!p->dup_blocks)
-				printf("Duplicate/bad block(s) in inode %lu:",
-				       p->ino);
-			printf(" %u", *block_nr);
+			p->pctx->blk = *block_nr;
+			fix_problem(ctx, PR_1B_DUP_BLOCK, p->pctx);
 		}
 		p->dup_blocks++;
-		ext2fs_mark_block_bitmap(block_dup_map, *block_nr);
+		ext2fs_mark_block_bitmap(ctx->block_dup_map, *block_nr);
 		ext2fs_mark_inode_bitmap(inode_dup_map, p->ino);
 		dp = allocate_memory(sizeof(struct dup_block),
 				      "duplicate block record");
@@ -314,13 +322,17 @@
 }
 
 
-void pass1c(ext2_filsys fs, char *block_buf)
+void pass1c(e2fsck_t ctx, char *block_buf)
 {
+	ext2_filsys fs = ctx->fs;
 	struct dup_inode	*p;
 	int	inodes_left = dup_inode_count;
 	struct search_dir_struct sd;
+	struct problem_context pctx;
 
-	printf("Pass 1C: Scan directories for inodes with dup blocks.\n");
+	clear_problem_context(&pctx);
+
+	fix_problem(ctx, PR_1C_PASS_HEADER, &pctx);
 
 	/*
 	 * First check to see if any of the inodes with dup blocks is
@@ -344,23 +356,25 @@
 				  search_dirent_proc, &sd);
 }	
 
-static void pass1d(ext2_filsys fs, char *block_buf)
+static void pass1d(e2fsck_t ctx, char *block_buf)
 {
+	ext2_filsys fs = ctx->fs;
 	struct dup_inode	*p, *s;
 	struct dup_block	*q, *r;
 	ino_t	*shared;
 	int	shared_len;
 	int	i;
-	errcode_t	retval;
 	int	file_ok;
 	int	meta_data = 0;
 	struct problem_context pctx;
-	
-	printf("Pass 1D: Reconciling duplicate blocks\n");
-	read_bitmaps(fs);
 
-	printf("(There are %d inodes containing duplicate/bad blocks.)\n\n",
-	       dup_inode_count);
+	clear_problem_context(&pctx);
+	
+	fix_problem(ctx, PR_1D_PASS_HEADER, &pctx);
+	read_bitmaps(ctx);
+
+	pctx.num = dup_inode_count;
+	fix_problem(ctx, PR_1D_NUM_DUP_INODES, &pctx);
 	shared = allocate_memory(sizeof(ino_t) * dup_inode_count,
 				 "Shared inode list");
 	for (p = dup_ino; p; p = p->next) {
@@ -385,7 +399,7 @@
 				continue;
 			if (q->num_bad > 1)
 				file_ok = 0;
-			if (ext2fs_test_block_bitmap(block_illegal_map,
+			if (ext2fs_test_block_bitmap(ctx->block_illegal_map,
 						     q->block)) {
 				file_ok = 0;
 				meta_data = 1;
@@ -412,18 +426,17 @@
 		/*
 		 * Report the inode that we are working on
 		 */
-		clear_problem_context(&pctx);
 		pctx.inode = &p->inode;
 		pctx.ino = p->ino;
 		pctx.dir = p->dir;
 		pctx.blkcount = p->num_dupblocks;
 		pctx.num = meta_data ? shared_len+1 : shared_len;
-		fix_problem(fs, PR_1B_DUP_FILE, &pctx);
+		fix_problem(ctx, PR_1D_DUP_FILE, &pctx);
 		pctx.blkcount = 0;
 		pctx.num = 0;
 		
 		if (meta_data)
-			fix_problem(fs, PR_1B_SHARE_METADATA, &pctx);
+			fix_problem(ctx, PR_1D_SHARE_METADATA, &pctx);
 		
 		for (i = 0; i < shared_len; i++) {
 			for (s = dup_ino; s; s = s->next)
@@ -437,28 +450,23 @@
 			pctx.inode = &s->inode;
 			pctx.ino = s->ino;
 			pctx.dir = s->dir;
-			fix_problem(fs, PR_1B_DUP_FILE_LIST, &pctx);
+			fix_problem(ctx, PR_1D_DUP_FILE_LIST, &pctx);
 		}
 		if (file_ok) {
-			printf("Duplicated blocks already reassigned or cloned.\n\n");
+			fix_problem(ctx, PR_1D_DUP_BLOCKS_DEALT, &pctx);
 			continue;
 		}
-			
-		if (ask("Clone duplicate/bad blocks", 1)) {
-			retval = clone_file(fs, p, block_buf);
-			if (retval)
-				printf("Couldn't clone file: %s\n",
-				       error_message(retval));
-			else {
-				printf("\n");
+		if (fix_problem(ctx, PR_1D_CLONE_QUESTION, &pctx)) {
+			pctx.errcode = clone_file(ctx, p, block_buf);
+			if (pctx.errcode)
+				fix_problem(ctx, PR_1D_CLONE_ERROR, &pctx);
+			else
 				continue;
-			}
 		}
-		if (ask("Delete file", 1))
-			delete_file(fs, p, block_buf);
+		if (fix_problem(ctx, PR_1D_DELETE_QUESTION, &pctx))
+			delete_file(ctx, p, block_buf);
 		else
 			ext2fs_unmark_valid(fs);
-		printf("\n");
 	}
 	free(shared);
 }
@@ -468,40 +476,46 @@
 			     int blockcnt,
 			     void *private)
 {
+	struct process_block_struct *pb = private;
 	struct dup_block *p;
+	e2fsck_t ctx;
+
+	ctx = pb->ctx;
 
 	if (!*block_nr)
 		return 0;
 
-	if (ext2fs_test_block_bitmap(block_dup_map, *block_nr)) {
+	if (ext2fs_test_block_bitmap(ctx->block_dup_map, *block_nr)) {
 		for (p = dup_blk; p; p = p->next_block)
 			if (p->block == *block_nr)
 				break;
 		if (p) {
 			p->num_bad--;
 			if (p->num_bad == 1)
-				ext2fs_unmark_block_bitmap(block_dup_map,
+				ext2fs_unmark_block_bitmap(ctx->block_dup_map,
 							   *block_nr);
 		} else
 			com_err("delete_file_block", 0,
 				"internal error; can't find dup_blk for %d\n",
 				*block_nr);
 	} else {
-		ext2fs_unmark_block_bitmap(block_found_map, *block_nr);
+		ext2fs_unmark_block_bitmap(ctx->block_found_map, *block_nr);
 		ext2fs_unmark_block_bitmap(fs->block_map, *block_nr);
 	}
 		
 	return 0;
 }
 		
-static void delete_file(ext2_filsys fs, struct dup_inode *dp, char* block_buf)
+static void delete_file(e2fsck_t ctx, struct dup_inode *dp, char* block_buf)
 {
+	ext2_filsys fs = ctx->fs;
 	errcode_t	retval;
 	struct process_block_struct pb;
 	struct ext2_inode	inode;
 
 	pb.ino = dp->ino;
 	pb.dup_blocks = dp->num_dupblocks;
+	pb.ctx = ctx;
 	
 	retval = ext2fs_block_iterate(fs, dp->ino, 0, block_buf,
 				      delete_file_block, &pb);
@@ -509,10 +523,10 @@
 		com_err("delete_file", retval,
 			"while calling ext2fs_block_iterate for inode %d",
 			dp->ino);
-	ext2fs_unmark_inode_bitmap(inode_used_map, dp->ino);
-	ext2fs_unmark_inode_bitmap(inode_dir_map, dp->ino);
-	if (inode_bad_map)
-		ext2fs_unmark_inode_bitmap(inode_bad_map, dp->ino);
+	ext2fs_unmark_inode_bitmap(ctx->inode_used_map, dp->ino);
+	ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, dp->ino);
+	if (ctx->inode_bad_map)
+		ext2fs_unmark_inode_bitmap(ctx->inode_bad_map, dp->ino);
 	ext2fs_unmark_inode_bitmap(fs->inode_map, dp->ino);
 	ext2fs_mark_ib_dirty(fs);
 	ext2fs_mark_bb_dirty(fs);
@@ -526,6 +540,7 @@
 	errcode_t	errcode;
 	ino_t		dir;
 	char	*buf;
+	e2fsck_t ctx;
 };
 
 static int clone_file_block(ext2_filsys fs,
@@ -537,16 +552,19 @@
 	blk_t	new_block;
 	errcode_t	retval;
 	struct clone_struct *cs = (struct clone_struct *) private;
+	e2fsck_t ctx;
 
+	ctx = cs->ctx;
+	
 	if (!*block_nr)
 		return 0;
 
-	if (ext2fs_test_block_bitmap(block_dup_map, *block_nr)) {
+	if (ext2fs_test_block_bitmap(ctx->block_dup_map, *block_nr)) {
 		for (p = dup_blk; p; p = p->next_block)
 			if (p->block == *block_nr)
 				break;
 		if (p) {
-			retval = ext2fs_new_block(fs, 0, block_found_map,
+			retval = ext2fs_new_block(fs, 0, ctx->block_found_map,
 						  &new_block);
 			if (retval) {
 				cs->errcode = retval;
@@ -574,10 +592,10 @@
 			}
 			p->num_bad--;
 			if (p->num_bad == 1)
-				ext2fs_unmark_block_bitmap(block_dup_map,
+				ext2fs_unmark_block_bitmap(ctx->block_dup_map,
 							   *block_nr);
 			*block_nr = new_block;
-			ext2fs_mark_block_bitmap(block_found_map,
+			ext2fs_mark_block_bitmap(ctx->block_found_map,
 						 new_block);
 			ext2fs_mark_block_bitmap(fs->block_map, new_block);
 			return BLOCK_CHANGED;
@@ -589,18 +607,20 @@
 	return 0;
 }
 		
-static int clone_file(ext2_filsys fs, struct dup_inode *dp, char* block_buf)
+static int clone_file(e2fsck_t ctx, struct dup_inode *dp, char* block_buf)
 {
+	ext2_filsys fs = ctx->fs;
 	errcode_t	retval;
 	struct clone_struct cs;
 
 	cs.errcode = 0;
-	cs.buf = malloc(fs->blocksize);
 	cs.dir = 0;
+	cs.ctx = ctx;
+	cs.buf = malloc(fs->blocksize);
 	if (!cs.buf)
 		return ENOMEM;
 
-	if (ext2fs_test_inode_bitmap(inode_dir_map, dp->ino))
+	if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, dp->ino))
 		cs.dir = dp->ino;
 	
 	retval = ext2fs_block_iterate(fs, dp->ino, 0, block_buf,
@@ -620,8 +640,3 @@
 	}
 	return 0;
 }
-
-
-	
-
-	
diff --git a/e2fsck/pass2.c b/e2fsck/pass2.c
index 2c52944..bcd8aa2 100644
--- a/e2fsck/pass2.c
+++ b/e2fsck/pass2.c
@@ -40,23 +40,19 @@
  * 	- The inode_bad_map bitmap
  */
 
-#include "et/com_err.h"
-
 #include "e2fsck.h"
 #include "problem.h"
 
 /*
  * Keeps track of how many times an inode is referenced.
  */
-ext2_icount_t inode_count = 0;	
-
-static void deallocate_inode(ext2_filsys fs, ino_t ino,
+static void deallocate_inode(e2fsck_t ctx, ino_t ino,
 			     char* block_buf);
-static int process_bad_inode(ext2_filsys fs, ino_t dir, ino_t ino);
+static int process_bad_inode(e2fsck_t ctx, ino_t dir, ino_t ino);
 static int check_dir_block(ext2_filsys fs,
 			   struct ext2_db_entry *dir_blocks_info,
 			   void *private);
-static int allocate_dir_block(ext2_filsys fs,
+static int allocate_dir_block(e2fsck_t ctx,
 			      struct ext2_db_entry *dir_blocks_info,
 			      char *buf, struct problem_context *pctx);
 static int update_dir_block(ext2_filsys fs,
@@ -67,30 +63,33 @@
 struct check_dir_struct {
 	char *buf;
 	struct problem_context	pctx;
+	e2fsck_t ctx;
 };	
 
-void pass2(ext2_filsys fs)
+void pass2(e2fsck_t ctx)
 {
+	ext2_filsys 	fs = ctx->fs;
 	char	*buf;
 	struct resource_track	rtrack;
 	struct dir_info *dir;
-	errcode_t	retval;
-	ino_t		size;
 	struct check_dir_struct cd;
 		
 	init_resource_track(&rtrack);
 
+	clear_problem_context(&cd.pctx);
+
 #ifdef MTRACE
 	mtrace_print("Pass 2");
 #endif
 
-	if (!preen)
-		printf("Pass 2: Checking directory structure\n");
-	retval = ext2fs_create_icount2(fs, EXT2_ICOUNT_OPT_INCREMENT,
-				       0, inode_link_info, &inode_count);
-	if (retval) {
-		com_err("ext2fs_create_icount", retval,
-			"while creating inode_count");
+	if (!(ctx->options & E2F_OPT_PREEN))
+		fix_problem(ctx, PR_2_PASS_HEADER, &cd.pctx);
+
+	cd.pctx.errcode = ext2fs_create_icount2(fs, EXT2_ICOUNT_OPT_INCREMENT,
+						0, ctx->inode_link_info,
+						&ctx->inode_count);
+	if (cd.pctx.errcode) {
+		fix_problem(ctx, PR_2_ALLOCATE_ICOUNT, &cd.pctx);
 		fatal_error(0);
 	}
 	buf = allocate_memory(fs->blocksize, "directory scan buffer");
@@ -105,33 +104,31 @@
 		dir->parent = EXT2_ROOT_INO;
 
 	cd.buf = buf;
-	clear_problem_context(&cd.pctx);
+	cd.ctx = ctx;
 	
-	retval = ext2fs_dblist_iterate(fs->dblist, check_dir_block, &cd);
-	if (retval) {
-		com_err("ext2fs_dblist_iterate", retval,
-			"while iterating through dblist");
+	cd.pctx.errcode = ext2fs_dblist_iterate(fs->dblist, check_dir_block,
+						&cd);
+	if (cd.pctx.errcode) {
+		fix_problem(ctx, PR_2_DBLIST_ITERATE, &cd.pctx);
 		fatal_error(0);
 	}
 	
 	free(buf);
 	ext2fs_free_dblist(fs->dblist);
 
-	if (inode_bad_map) {
-		ext2fs_free_inode_bitmap(inode_bad_map);
-		inode_bad_map = 0;
+	if (ctx->inode_bad_map) {
+		ext2fs_free_inode_bitmap(ctx->inode_bad_map);
+		ctx->inode_bad_map = 0;
 	}
-	if (tflag > 1) {
-		printf("Pass 2: ");
-		print_resource_track(&rtrack);
-	}
+	if (ctx->options & E2F_OPT_TIME2)
+		print_resource_track("Pass 2", &rtrack);
 }
 
 /*
  * Make sure the first entry in the directory is '.', and that the
  * directory entry is sane.
  */
-static int check_dot(ext2_filsys fs,
+static int check_dot(e2fsck_t ctx,
 		     struct ext2_dir_entry *dirent,
 		     ino_t ino, struct problem_context *pctx)
 {
@@ -150,7 +147,7 @@
 		problem = PR_2_DOT_NULL_TERM;
 	
 	if (problem) {
-		if (fix_problem(fs, problem, pctx)) {
+		if (fix_problem(ctx, problem, pctx)) {
 			if (dirent->rec_len < 12)
 				dirent->rec_len = 12;
 			dirent->inode = ino;
@@ -162,7 +159,7 @@
 		}
 	}
 	if (dirent->inode != ino) {
-		if (fix_problem(fs, PR_2_BAD_INODE_DOT, pctx)) {
+		if (fix_problem(ctx, PR_2_BAD_INODE_DOT, pctx)) {
 			dirent->inode = ino;
 			status = 1;
 		}
@@ -170,9 +167,9 @@
 	if (dirent->rec_len > 12) {
 		new_len = dirent->rec_len - 12;
 		if (new_len > 12) {
-			preenhalt(fs);
+			preenhalt(ctx);
 			if (created ||
-			    ask("Directory entry for '.' is big.  Split", 1)) {
+			    ask(ctx, "Directory entry for '.' is big.  Split", 1)) {
 				nextdir = (struct ext2_dir_entry *)
 					((char *) dirent + 12);
 				dirent->rec_len = 12;
@@ -191,7 +188,7 @@
  * directory entry is sane.  We do not check the inode number of '..'
  * here; this gets done in pass 3.
  */
-static int check_dotdot(ext2_filsys fs,
+static int check_dotdot(e2fsck_t ctx,
 			struct ext2_dir_entry *dirent,
 			struct dir_info *dir, struct problem_context *pctx)
 {
@@ -207,7 +204,7 @@
 		problem = PR_2_DOT_DOT_NULL_TERM;
 
 	if (problem) {
-		if (fix_problem(fs, problem, pctx)) {
+		if (fix_problem(ctx, problem, pctx)) {
 			if (dirent->rec_len < 12)
 				dirent->rec_len = 12;
 			/*
@@ -232,7 +229,7 @@
  * Check to make sure a directory entry doesn't contain any illegal
  * characters.
  */
-static int check_name(ext2_filsys fs,
+static int check_name(e2fsck_t ctx,
 		      struct ext2_dir_entry *dirent,
 		      ino_t dir_ino, struct problem_context *pctx)
 {
@@ -243,7 +240,7 @@
 	for ( i = 0; i < dirent->name_len; i++) {
 		if (dirent->name[i] == '/' || dirent->name[i] == '\0') {
 			if (fixup < 0) {
-				fixup = fix_problem(fs, PR_2_BAD_NAME, pctx);
+				fixup = fix_problem(ctx, PR_2_BAD_NAME, pctx);
 			}
 			if (fixup) {
 				dirent->name[i] = '.';
@@ -262,19 +259,23 @@
 	struct ext2_dir_entry 	*dirent;
 	int			offset = 0;
 	int			dir_modified = 0;
-	errcode_t		retval;
 	int			dot_state;
 	blk_t			block_nr = db->blk;
 	ino_t 			ino = db->ino;
 	__u16			links;
 	struct check_dir_struct	*cd = private;
-	char 			*buf = cd->buf;
+	char 			*buf;
+	e2fsck_t		ctx;
+	int			problem;
+
+	buf = cd->buf;
+	ctx = cd->ctx;
 	
 	/*
 	 * Make sure the inode is still in use (could have been 
 	 * deleted in the duplicate/bad blocks pass.
 	 */
-	if (!(ext2fs_test_inode_bitmap(inode_used_map, ino))) 
+	if (!(ext2fs_test_inode_bitmap(ctx->inode_used_map, ino))) 
 		return 0;
 
 	cd->pctx.ino = ino;
@@ -285,7 +286,7 @@
 	cd->pctx.num = 0;
 
 	if (db->blk == 0) {
-		if (allocate_dir_block(fs, db, buf, &cd->pctx))
+		if (allocate_dir_block(ctx, db, buf, &cd->pctx))
 			return 0;
 		block_nr = db->blk;
 	}
@@ -300,14 +301,16 @@
 	       db->blockcnt, ino);
 #endif
 	
-	retval = ext2fs_read_dir_block(fs, block_nr, buf);
-	if (retval) {
-		com_err(program_name, retval,
-			"while reading directory block %d", block_nr);
+	cd->pctx.errcode = ext2fs_read_dir_block(fs, block_nr, buf);
+	if (cd->pctx.errcode) {
+		if (!fix_problem(ctx, PR_2_READ_DIRBLOCK, &cd->pctx))
+			fatal_error(0);
+		memset(buf, 0, fs->blocksize);
 	}
 
 	do {
 		dot_state++;
+		problem = 0;
 		dirent = (struct ext2_dir_entry *) (buf + offset);
 		cd->pctx.dirent = dirent;
 		cd->pctx.num = offset;
@@ -315,7 +318,7 @@
 		    (dirent->rec_len < 8) ||
 		    ((dirent->rec_len % 4) != 0) ||
 		    ((dirent->name_len+8) > dirent->rec_len)) {
-			if (fix_problem(fs, PR_2_DIR_CORRUPTED, &cd->pctx)) {
+			if (fix_problem(ctx, PR_2_DIR_CORRUPTED, &cd->pctx)) {
 				dirent->rec_len = fs->blocksize - offset;
 				dirent->name_len = 0;
 				dirent->inode = 0;
@@ -323,28 +326,27 @@
 			} else
 				return DIRENT_ABORT;
 		}
-
 		if (dirent->name_len > EXT2_NAME_LEN) {
-			if (fix_problem(fs, PR_2_FILENAME_LONG, &cd->pctx)) {
+			if (fix_problem(ctx, PR_2_FILENAME_LONG, &cd->pctx)) {
 				dirent->name_len = EXT2_NAME_LEN;
 				dir_modified++;
 			}
 		}
 
 		if (dot_state == 1) {
-			if (check_dot(fs, dirent, ino, &cd->pctx))
+			if (check_dot(ctx, dirent, ino, &cd->pctx))
 				dir_modified++;
 		} else if (dot_state == 2) {
 			dir = get_dir_info(ino);
 			if (!dir) {
-				printf("Internal error: couldn't find dir_info for %lu\n",
-				       ino);
+				fix_problem(ctx, PR_2_NO_DIRINFO, &cd->pctx);
 				fatal_error(0);
 			}
-			if (check_dotdot(fs, dirent, dir, &cd->pctx))
+			if (check_dotdot(ctx, dirent, dir, &cd->pctx))
 				dir_modified++;
 		} else if (dirent->inode == ino) {
-			if (fix_problem(fs, PR_2_LINK_DOT, &cd->pctx)) {
+			problem = PR_2_LINK_DOT;
+			if (fix_problem(ctx, PR_2_LINK_DOT, &cd->pctx)) {
 				dirent->inode = 0;
 				dir_modified++;
 				goto next;
@@ -353,44 +355,66 @@
 		if (!dirent->inode) 
 			goto next;
 		
-		if (check_name(fs, dirent, ino, &cd->pctx))
-			dir_modified++;
-
 		/*
 		 * Make sure the inode listed is a legal one.
 		 */ 
 		if (((dirent->inode != EXT2_ROOT_INO) &&
 		     (dirent->inode < EXT2_FIRST_INODE(fs->super))) ||
 		    (dirent->inode > fs->super->s_inodes_count)) {
-			if (fix_problem(fs, PR_2_BAD_INO, &cd->pctx)) {
-				dirent->inode = 0;
-				dir_modified++;
-				goto next;
-			}
-		}
-
-		/*
-		 * If the inode is unused, offer to clear it.
-		 */
-		if (!(ext2fs_test_inode_bitmap(inode_used_map,
+			problem = PR_2_BAD_INO;
+		} else if (!(ext2fs_test_inode_bitmap(ctx->inode_used_map,
 					       dirent->inode))) {
-			if (fix_problem(fs, PR_2_UNUSED_INODE, &cd->pctx)) {
-				dirent->inode = 0;
-				dir_modified++;
-				goto next;
-			}
+			/*
+			 * If the inode is unused, offer to clear it.
+			 */
+			problem = PR_2_UNUSED_INODE;
+		} else if (ctx->inode_bb_map &&
+			   (ext2fs_test_inode_bitmap(ctx->inode_bb_map,
+						     dirent->inode))) {
+			/*
+			 * If the inode is in a bad block, offer to
+			 * clear it.
+			 */
+			problem = PR_2_BB_INODE;
+		} else if ((dot_state > 2) &&
+			   (dirent->name_len == 1) &&
+			   (dirent->name[0] == '.')) {
+			/*
+			 * If there's a '.' entry in anything other
+			 * than the first directory entry, it's a
+			 * duplicate entry that should be removed.
+			 */
+			problem = PR_2_DUP_DOT;
+		} else if ((dot_state > 2) &&
+			   (dirent->name_len == 2) &&
+			   (dirent->name[0] == '.') && 
+			   (dirent->name[1] == '.')) {
+			/*
+			 * If there's a '..' entry in anything other
+			 * than the second directory entry, it's a
+			 * duplicate entry that should be removed.
+			 */
+			problem = PR_2_DUP_DOT_DOT;
+		} else if ((dot_state > 2) &&
+			   (dirent->inode == EXT2_ROOT_INO)) {
+			/*
+			 * Don't allow links to the root directory.
+			 * We check this specially to make sure we
+			 * catch this error case even if the root
+			 * directory hasn't been created yet.
+			 */
+			problem = PR_2_LINK_ROOT;
 		}
 
-		/*
-		 * If the inode is in a bad block, offer to clear it.
-		 */
-		if (inode_bb_map &&
-		    (ext2fs_test_inode_bitmap(inode_bb_map,
-					      dirent->inode))) {
-			if (fix_problem(fs, PR_2_BB_INODE, &cd->pctx)) {
+		if (problem) {
+			if (fix_problem(ctx, problem, &cd->pctx)) {
 				dirent->inode = 0;
 				dir_modified++;
 				goto next;
+			} else {
+				ext2fs_unmark_valid(fs);
+				if (problem == PR_2_BAD_INO)
+					goto next;
 			}
 		}
 
@@ -400,30 +424,19 @@
 		 * (We wait until now so that we can display the
 		 * pathname to the user.)
 		 */
-		if (inode_bad_map &&
-		    ext2fs_test_inode_bitmap(inode_bad_map,
+		if (ctx->inode_bad_map &&
+		    ext2fs_test_inode_bitmap(ctx->inode_bad_map,
 					     dirent->inode)) {
-			if (process_bad_inode(fs, ino, dirent->inode)) {
+			if (process_bad_inode(ctx, ino, dirent->inode)) {
 				dirent->inode = 0;
 				dir_modified++;
 				goto next;
 			}
 		}
 
-		/*
-		 * Don't allow links to the root directory.  We check
-		 * this specially to make sure we catch this error
-		 * case even if the root directory hasn't been created
-		 * yet.
-		 */
-		if ((dot_state > 2) && (dirent->inode == EXT2_ROOT_INO)) {
-			if (fix_problem(fs, PR_2_LINK_ROOT, &cd->pctx)) {
-				dirent->inode = 0;
-				dir_modified++;
-				goto next;
-			}
-		}
-		
+		if (check_name(ctx, dirent, ino, &cd->pctx))
+			dir_modified++;
+
 		/*
 		 * If this is a directory, then mark its parent in its
 		 * dir_info structure.  If the parent field is already
@@ -432,17 +445,17 @@
 		 * and ask the user if he/she wants to clear this one.
 		 */
 		if ((dot_state > 2) &&
-		    (ext2fs_test_inode_bitmap(inode_dir_map,
+		    (ext2fs_test_inode_bitmap(ctx->inode_dir_map,
 					      dirent->inode))) {
 			subdir = get_dir_info(dirent->inode);
 			if (!subdir) {
-				printf("INTERNAL ERROR: missing dir %u\n",
-				       dirent->inode);
+				cd->pctx.ino = dirent->inode;
+				fix_problem(ctx, PR_2_NO_DIRINFO, &cd->pctx);
 				fatal_error(0);
 			}
 			if (subdir->parent) {
 				cd->pctx.ino2 = subdir->parent;
-				if (fix_problem(fs, PR_2_LINK_DIR,
+				if (fix_problem(ctx, PR_2_LINK_DIR,
 						&cd->pctx)) {
 					dirent->inode = 0;
 					dir_modified++;
@@ -453,10 +466,11 @@
 				subdir->parent = ino;
 		}
 		
-		ext2fs_icount_increment(inode_count, dirent->inode, &links);
+		ext2fs_icount_increment(ctx->inode_count, dirent->inode,
+					&links);
 		if (links > 1)
-			fs_links_count++;
-		fs_total_count++;
+			ctx->fs_links_count++;
+		ctx->fs_total_count++;
 	next:
 		offset += dirent->rec_len;
 	} while (offset < fs->blocksize);
@@ -464,15 +478,17 @@
 	printf("\n");
 #endif
 	if (offset != fs->blocksize) {
-		printf("Final rec_len is %d, should be %d\n",
-		       dirent->rec_len,
-		       dirent->rec_len - fs->blocksize + offset);
+		cd->pctx.num = dirent->rec_len - fs->blocksize + offset;
+		if (fix_problem(ctx, PR_2_FINAL_RECLEN, &cd->pctx)) {
+			dirent->rec_len = cd->pctx.num;
+			dir_modified++;
+		}
 	}
 	if (dir_modified) {
-		retval = ext2fs_write_dir_block(fs, block_nr, buf);
-		if (retval) {
-			com_err(program_name, retval,
-				"while writing directory block %d", block_nr);
+		cd->pctx.errcode = ext2fs_write_dir_block(fs, block_nr, buf);
+		if (cd->pctx.errcode) {
+			if (!fix_problem(ctx, PR_2_WRITE_DIRBLOCK, &cd->pctx))
+				fatal_error(0);
 		}
 		ext2fs_mark_changed(fs);
 	}
@@ -488,9 +504,11 @@
 			     int blockcnt,
 			     void *private)
 {
+	e2fsck_t	ctx = (e2fsck_t) private;
+	
 	if (!*block_nr)
 		return 0;
-	ext2fs_unmark_block_bitmap(block_found_map, *block_nr);
+	ext2fs_unmark_block_bitmap(ctx->block_found_map, *block_nr);
 	ext2fs_unmark_block_bitmap(fs->block_map, *block_nr);
 	return 0;
 }
@@ -498,26 +516,29 @@
 /*
  * This fuction deallocates an inode
  */
-static void deallocate_inode(ext2_filsys fs, ino_t ino,
+static void deallocate_inode(e2fsck_t ctx, ino_t ino,
 			     char* block_buf)
 {
-	errcode_t		retval;
+	ext2_filsys fs = ctx->fs;
 	struct ext2_inode	inode;
-
-	ext2fs_icount_store(inode_link_info, ino, 0);
+	struct problem_context	pctx;
+	
+	ext2fs_icount_store(ctx->inode_link_info, ino, 0);
 	e2fsck_read_inode(fs, ino, &inode, "deallocate_inode");
 	inode.i_links_count = 0;
 	inode.i_dtime = time(0);
 	e2fsck_write_inode(fs, ino, &inode, "deallocate_inode");
+	clear_problem_context(&pctx);
+	pctx.ino = ino;
 
 	/*
 	 * Fix up the bitmaps...
 	 */
-	read_bitmaps(fs);
-	ext2fs_unmark_inode_bitmap(inode_used_map, ino);
-	ext2fs_unmark_inode_bitmap(inode_dir_map, ino);
-	if (inode_bad_map)
-		ext2fs_unmark_inode_bitmap(inode_bad_map, ino);
+	read_bitmaps(ctx);
+	ext2fs_unmark_inode_bitmap(ctx->inode_used_map, ino);
+	ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, ino);
+	if (ctx->inode_bad_map)
+		ext2fs_unmark_inode_bitmap(ctx->inode_bad_map, ino);
 	ext2fs_unmark_inode_bitmap(fs->inode_map, ino);
 	ext2fs_mark_ib_dirty(fs);
 
@@ -525,16 +546,17 @@
 		return;
 	
 	ext2fs_mark_bb_dirty(fs);
-	retval = ext2fs_block_iterate(fs, ino, 0, block_buf,
-				      deallocate_inode_block, 0);
-	if (retval)
-		com_err("deallocate_inode", retval,
-			"while calling ext2fs_block_iterate for inode %d",
-			ino);
+	pctx.errcode = ext2fs_block_iterate(fs, ino, 0, block_buf,
+					    deallocate_inode_block, ctx);
+	if (pctx.errcode) {
+		fix_problem(ctx, PR_2_DEALLOC_INODE, &pctx);
+		fatal_error(0);
+	}
 }
 
-static int process_bad_inode(ext2_filsys fs, ino_t dir, ino_t ino)
+static int process_bad_inode(e2fsck_t ctx, ino_t dir, ino_t ino)
 {
+	ext2_filsys fs = ctx->fs;
 	struct ext2_inode	inode;
 	int			inode_modified = 0;
 	unsigned char		*frag, *fsize;
@@ -551,31 +573,31 @@
 	    !LINUX_S_ISCHR(inode.i_mode) && !LINUX_S_ISBLK(inode.i_mode) &&
 	    !LINUX_S_ISLNK(inode.i_mode) && !LINUX_S_ISFIFO(inode.i_mode) &&
 	    !(LINUX_S_ISSOCK(inode.i_mode))) {
-		if (fix_problem(fs, PR_2_BAD_MODE, &pctx)) {
-			deallocate_inode(fs, ino, 0);
+		if (fix_problem(ctx, PR_2_BAD_MODE, &pctx)) {
+			deallocate_inode(ctx, ino, 0);
 			return 1;
 		}
 	}
 
 	if (LINUX_S_ISCHR(inode.i_mode)
 	    && !e2fsck_pass1_check_device_inode(&inode)) {
-		if (fix_problem(fs, PR_2_BAD_CHAR_DEV, &pctx)) {
-			deallocate_inode(fs, ino, 0);
+		if (fix_problem(ctx, PR_2_BAD_CHAR_DEV, &pctx)) {
+			deallocate_inode(ctx, ino, 0);
 			return 1;
 		}
 	}
 		
 	if (LINUX_S_ISBLK(inode.i_mode)
 	    && !e2fsck_pass1_check_device_inode(&inode)) {
-		if (fix_problem(fs, PR_2_BAD_BLOCK_DEV, &pctx)) {
-			deallocate_inode(fs, ino, 0);
+		if (fix_problem(ctx, PR_2_BAD_BLOCK_DEV, &pctx)) {
+			deallocate_inode(ctx, ino, 0);
 			return 1;
 		}
 	}
 
 		
 	if (inode.i_faddr &&
-	    fix_problem(fs, PR_2_FADDR_ZERO, &pctx)) {
+	    fix_problem(ctx, PR_2_FADDR_ZERO, &pctx)) {
 		inode.i_faddr = 0;
 		inode_modified++;
 	}
@@ -598,7 +620,7 @@
 	}
 	if (frag && *frag) {
 		pctx.num = *frag;
-		if (fix_problem(fs, PR_2_FRAG_ZERO, &pctx)) {
+		if (fix_problem(ctx, PR_2_FRAG_ZERO, &pctx)) {
 			*frag = 0;
 			inode_modified++;
 		}
@@ -606,7 +628,7 @@
 	}
 	if (fsize && *fsize) {
 		pctx.num = *fsize;
-		if (fix_problem(fs, PR_2_FSIZE_ZERO, &pctx)) {
+		if (fix_problem(ctx, PR_2_FSIZE_ZERO, &pctx)) {
 			*fsize = 0;
 			inode_modified++;
 		}
@@ -614,12 +636,12 @@
 	}
 
 	if (inode.i_file_acl &&
-	    fix_problem(fs, PR_2_FILE_ACL_ZERO, &pctx)) {
+	    fix_problem(ctx, PR_2_FILE_ACL_ZERO, &pctx)) {
 		inode.i_file_acl = 0;
 		inode_modified++;
 	}
 	if (inode.i_dir_acl &&
-	    fix_problem(fs, PR_2_DIR_ACL_ZERO, &pctx)) {
+	    fix_problem(ctx, PR_2_DIR_ACL_ZERO, &pctx)) {
 		inode.i_dir_acl = 0;
 		inode_modified++;
 	}
@@ -635,34 +657,34 @@
  * 	a "hole" in it, or if a directory has a illegal block number
  * 	that was zeroed out and now needs to be replaced.
  */
-static int allocate_dir_block(ext2_filsys fs,
+static int allocate_dir_block(e2fsck_t ctx,
 			      struct ext2_db_entry *db,
 			      char *buf, struct problem_context *pctx)
 {
+	ext2_filsys fs = ctx->fs;
 	blk_t			blk;
 	char			*block;
 	struct ext2_inode	inode;
-	errcode_t		retval;
 
-	if (fix_problem(fs, PR_2_DIRECTORY_HOLE, pctx) == 0)
+	if (fix_problem(ctx, PR_2_DIRECTORY_HOLE, pctx) == 0)
 		return 1;
 
 	/*
 	 * Read the inode and block bitmaps in; we'll be messing with
 	 * them.
 	 */
-	read_bitmaps(fs);
+	read_bitmaps(ctx);
 	
 	/*
 	 * First, find a free block
 	 */
-	retval = ext2fs_new_block(fs, 0, block_found_map, &blk);
-	if (retval) {
-		com_err("allocate_dir_block", retval,
-			"while trying to fill a hole in a directory inode");
+	pctx->errcode = ext2fs_new_block(fs, 0, ctx->block_found_map, &blk);
+	if (pctx->errcode) {
+		pctx->str = "ext2fs_new_block";
+		fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx);
 		return 1;
 	}
-	ext2fs_mark_block_bitmap(block_found_map, blk);
+	ext2fs_mark_block_bitmap(ctx->block_found_map, blk);
 	ext2fs_mark_block_bitmap(fs->block_map, blk);
 	ext2fs_mark_bb_dirty(fs);
 
@@ -670,22 +692,22 @@
 	 * Now let's create the actual data block for the inode
 	 */
 	if (db->blockcnt)
-		retval = ext2fs_new_dir_block(fs, 0, 0, &block);
+		pctx->errcode = ext2fs_new_dir_block(fs, 0, 0, &block);
 	else
-		retval = ext2fs_new_dir_block(fs, db->ino, EXT2_ROOT_INO,
-					      &block);
+		pctx->errcode = ext2fs_new_dir_block(fs, db->ino,
+						     EXT2_ROOT_INO, &block);
 
-	if (retval) {
-		com_err("allocate_dir_block", retval,
-			"while creating new directory block");
+	if (pctx->errcode) {
+		pctx->str = "ext2fs_new_dir_block";
+		fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx);
 		return 1;
 	}
 
-	retval = ext2fs_write_dir_block(fs, blk, block);
+	pctx->errcode = ext2fs_write_dir_block(fs, blk, block);
 	free(block);
-	if (retval) {
-		com_err("allocate_dir_block", retval,
-			"while writing an empty directory block");
+	if (pctx->errcode) {
+		pctx->str = "ext2fs_write_dir_block";
+		fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx);
 		return 1;
 	}
 
@@ -702,11 +724,11 @@
 	 * Finally, update the block pointers for the inode
 	 */
 	db->blk = blk;
-	retval = ext2fs_block_iterate(fs, db->ino, BLOCK_FLAG_HOLE,
+	pctx->errcode = ext2fs_block_iterate(fs, db->ino, BLOCK_FLAG_HOLE,
 				      0, update_dir_block, db);
-	if (retval) {
-		com_err("allocate_dir_block", retval,
-			"while calling ext2fs_block_iterate");
+	if (pctx->errcode) {
+		pctx->str = "ext2fs_block_iterate";
+		fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx);
 		return 1;
 	}
 
diff --git a/e2fsck/pass3.c b/e2fsck/pass3.c
index 2f97aa4..7d7c649 100644
--- a/e2fsck/pass3.c
+++ b/e2fsck/pass3.c
@@ -37,18 +37,17 @@
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
 #endif
-#include "et/com_err.h"
 
 #include "e2fsck.h"
 #include "problem.h"
 
-static void check_root(ext2_filsys fs);
-static void check_directory(ext2_filsys fs, struct dir_info *dir,
+static void check_root(e2fsck_t ctx);
+static void check_directory(e2fsck_t ctx, struct dir_info *dir,
 			    struct problem_context *pctx);
-static ino_t get_lost_and_found(ext2_filsys fs);
-static void fix_dotdot(ext2_filsys fs, struct dir_info *dir, ino_t parent);
-static errcode_t adjust_inode_count(ext2_filsys fs, ino_t ino, int adj);
-static errcode_t expand_directory(ext2_filsys fs, ino_t dir);
+static ino_t get_lost_and_found(e2fsck_t ctx);
+static void fix_dotdot(e2fsck_t ctx, struct dir_info *dir, ino_t parent);
+static errcode_t adjust_inode_count(e2fsck_t ctx, ino_t ino, int adj);
+static errcode_t expand_directory(e2fsck_t ctx, ino_t dir);
 
 static ino_t lost_and_found = 0;
 static int bad_lost_and_found = 0;
@@ -56,120 +55,120 @@
 static ext2fs_inode_bitmap inode_loop_detect;
 static ext2fs_inode_bitmap inode_done_map;
 	
-void pass3(ext2_filsys fs)
+void pass3(e2fsck_t ctx)
 {
+	ext2_filsys fs = ctx->fs;
 	int		i;
-	errcode_t	retval;
 	struct resource_track	rtrack;
 	struct problem_context	pctx;
 	struct dir_info	*dir;
 	
 	init_resource_track(&rtrack);
 
+	clear_problem_context(&pctx);
+
 #ifdef MTRACE
 	mtrace_print("Pass 3");
 #endif
 
-	if (!preen)
-		printf("Pass 3: Checking directory connectivity\n");
+	if (!(ctx->options & E2F_OPT_PREEN))
+		fix_problem(ctx, PR_3_PASS_HEADER, &pctx);
 
 	/*
 	 * Allocate some bitmaps to do loop detection.
 	 */
-	retval = ext2fs_allocate_inode_bitmap(fs,
-					      "inode loop detection bitmap",
-					      &inode_loop_detect);
-	if (retval) {
-		com_err("ext2fs_allocate_inode_bitmap", retval,
-			"while allocating inode_loop_detect");
+	pctx.errcode = ext2fs_allocate_inode_bitmap(fs,
+		    "inode loop detection bitmap", &inode_loop_detect);
+	if (pctx.errcode) {
+		pctx.num = 1;
+		fix_problem(ctx, PR_3_ALLOCATE_IBITMAP_ERROR, &pctx);
 		fatal_error(0);
 	}
-	retval = ext2fs_allocate_inode_bitmap(fs, "inode done bitmap",
-					      &inode_done_map);
-	if (retval) {
-		com_err("ext2fs_allocate_inode_bitmap", retval,
-			"while allocating inode_done_map");
+	pctx.errcode = ext2fs_allocate_inode_bitmap(fs, "inode done bitmap",
+						    &inode_done_map);
+	if (pctx.errcode) {
+		pctx.num = 2;
+		fix_problem(ctx, PR_3_ALLOCATE_IBITMAP_ERROR, &pctx);
 		fatal_error(0);
 	}
-	if (tflag) {
-		printf("Peak memory: ");
-		print_resource_track(&global_rtrack);
-	}
+	if (ctx->options & E2F_OPT_TIME)
+		print_resource_track("Peak memory", &ctx->global_rtrack);
 
-	check_root(fs);
+	check_root(ctx);
 	ext2fs_mark_inode_bitmap(inode_done_map, EXT2_ROOT_INO);
 
-	clear_problem_context(&pctx);
 	for (i=0; (dir = dir_info_iter(&i)) != 0;) {
-		if (ext2fs_test_inode_bitmap(inode_dir_map, dir->ino))
-			check_directory(fs, dir, &pctx);
+		if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, dir->ino))
+			check_directory(ctx, dir, &pctx);
 	}
 	
 	
 	free_dir_info(fs);
 	ext2fs_free_inode_bitmap(inode_loop_detect);
 	ext2fs_free_inode_bitmap(inode_done_map);
-	if (tflag > 1) {
-		printf("Pass 3: ");
-		print_resource_track(&rtrack);
-	}
+	if (ctx->options & E2F_OPT_TIME2)
+		print_resource_track("Pass 3", &rtrack);
 }
 
 /*
  * This makes sure the root inode is present; if not, we ask if the
  * user wants us to create it.  Not creating it is a fatal error.
  */
-static void check_root(ext2_filsys fs)
+static void check_root(e2fsck_t ctx)
 {
+	ext2_filsys fs = ctx->fs;
 	blk_t			blk;
-	errcode_t		retval;
 	struct ext2_inode	inode;
 	char *			block;
+	struct problem_context	pctx;
 	
-	if (ext2fs_test_inode_bitmap(inode_used_map, EXT2_ROOT_INO)) {
+	clear_problem_context(&pctx);
+	
+	if (ext2fs_test_inode_bitmap(ctx->inode_used_map, EXT2_ROOT_INO)) {
 		/*
 		 * If the root inode is a directory, die here.  The
 		 * user must have answered 'no' in pass1 when we
 		 * offered to clear it.
 		 */
-		if (!(ext2fs_test_inode_bitmap(inode_dir_map, EXT2_ROOT_INO)))
+		if (!(ext2fs_test_inode_bitmap(ctx->inode_dir_map,
+					       EXT2_ROOT_INO)))
 			fatal_error("Root inode not directory");
 		return;
 	}
 
-	if (!fix_problem(fs, PR_3_NO_ROOT_INODE, 0))
+	if (!fix_problem(ctx, PR_3_NO_ROOT_INODE, &pctx))
 		fatal_error("Cannot proceed without a root inode.");
 
-	read_bitmaps(fs);
+	read_bitmaps(ctx);
 	
 	/*
 	 * First, find a free block
 	 */
-	retval = ext2fs_new_block(fs, 0, block_found_map, &blk);
-	if (retval) {
-		com_err("ext2fs_new_block", retval,
-			"while trying to create root directory");
+	pctx.errcode = ext2fs_new_block(fs, 0, ctx->block_found_map, &blk);
+	if (pctx.errcode) {
+		pctx.str = "ext2fs_new_block";
+		fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx);
 		fatal_error(0);
 	}
-	ext2fs_mark_block_bitmap(block_found_map, blk);
+	ext2fs_mark_block_bitmap(ctx->block_found_map, blk);
 	ext2fs_mark_block_bitmap(fs->block_map, blk);
 	ext2fs_mark_bb_dirty(fs);
 
 	/*
 	 * Now let's create the actual data block for the inode
 	 */
-	retval = ext2fs_new_dir_block(fs, EXT2_ROOT_INO, EXT2_ROOT_INO,
-				      &block);
-	if (retval) {
-		com_err("ext2fs_new_dir_block", retval,
-			"while creating new root directory");
+	pctx.errcode = ext2fs_new_dir_block(fs, EXT2_ROOT_INO, EXT2_ROOT_INO,
+					    &block);
+	if (pctx.errcode) {
+		pctx.str = "ext2fs_new_dir_block";
+		fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx);
 		fatal_error(0);
 	}
 
-	retval = ext2fs_write_dir_block(fs, blk, block);
-	if (retval) {
-		com_err("ext2fs_write_dir_block", retval,
-			"while writing the root directory block");
+	pctx.errcode = ext2fs_write_dir_block(fs, blk, block);
+	if (pctx.errcode) {
+		pctx.str = "ext2fs_write_dir_block";
+		fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx);
 		fatal_error(0);
 	}
 	free(block);
@@ -188,10 +187,10 @@
 	/*
 	 * Write out the inode.
 	 */
-	retval = ext2fs_write_inode(fs, EXT2_ROOT_INO, &inode);
-	if (retval) {
-		com_err("ext2fs_write_inode", retval,
-			"While trying to create /lost+found");
+	pctx.errcode = ext2fs_write_inode(fs, EXT2_ROOT_INO, &inode);
+	if (pctx.errcode) {
+		pctx.str = "ext2fs_write_inode";
+		fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx);
 		fatal_error(0);
 	}
 	
@@ -199,11 +198,11 @@
 	 * Miscellaneous bookkeeping...
 	 */
 	add_dir_info(fs, EXT2_ROOT_INO, EXT2_ROOT_INO);
-	ext2fs_icount_store(inode_count, EXT2_ROOT_INO, 2);
-	ext2fs_icount_store(inode_link_info, EXT2_ROOT_INO, 2);
+	ext2fs_icount_store(ctx->inode_count, EXT2_ROOT_INO, 2);
+	ext2fs_icount_store(ctx->inode_link_info, EXT2_ROOT_INO, 2);
 
-	ext2fs_mark_inode_bitmap(inode_used_map, EXT2_ROOT_INO);
-	ext2fs_mark_inode_bitmap(inode_dir_map, EXT2_ROOT_INO);
+	ext2fs_mark_inode_bitmap(ctx->inode_used_map, EXT2_ROOT_INO);
+	ext2fs_mark_inode_bitmap(ctx->inode_dir_map, EXT2_ROOT_INO);
 	ext2fs_mark_inode_bitmap(fs->inode_map, EXT2_ROOT_INO);
 	ext2fs_mark_ib_dirty(fs);
 }
@@ -216,9 +215,10 @@
  * a loop, we treat that as a disconnected directory and offer to
  * reparent it to lost+found.
  */
-static void check_directory(ext2_filsys fs, struct dir_info *dir,
+static void check_directory(e2fsck_t ctx, struct dir_info *dir,
 			    struct problem_context *pctx)
 {
+	ext2_filsys fs = ctx->fs;
 	struct dir_info *p = dir;
 
 	ext2fs_clear_inode_bitmap(inode_loop_detect);
@@ -258,12 +258,12 @@
 	 * inode; offer to reconnect it to lost+found.
 	 */
 	pctx->ino = p->ino;
-	if (fix_problem(fs, PR_3_UNCONNECTED_DIR, pctx)) {
-		if (reconnect_file(fs, p->ino))
+	if (fix_problem(ctx, PR_3_UNCONNECTED_DIR, pctx)) {
+		if (reconnect_file(ctx, p->ino))
 			ext2fs_unmark_valid(fs);
 		else {
 			p->parent = lost_and_found;
-			fix_dotdot(fs, p, lost_and_found);
+			fix_dotdot(ctx, p, lost_and_found);
 		}
 	}
 
@@ -276,8 +276,8 @@
 		pctx->ino = dir->ino;
 		pctx->ino2 = dir->dotdot;
 		pctx->dir = dir->parent;
-		if (fix_problem(fs, PR_3_BAD_DOT_DOT, pctx))
-			fix_dotdot(fs, dir, dir->parent);
+		if (fix_problem(ctx, PR_3_BAD_DOT_DOT, pctx))
+			fix_dotdot(ctx, dir, dir->parent);
 	}
 }	
 
@@ -285,56 +285,61 @@
  * This routine gets the lost_and_found inode, making it a directory
  * if necessary
  */
-ino_t get_lost_and_found(ext2_filsys fs)
+ino_t get_lost_and_found(e2fsck_t ctx)
 {
+	ext2_filsys fs = ctx->fs;
 	ino_t			ino;
 	blk_t			blk;
 	errcode_t		retval;
 	struct ext2_inode	inode;
 	char *			block;
 	const char 		name[] = "lost+found";
+	struct 	problem_context	pctx;
 
+	clear_problem_context(&pctx);
+	
 	retval = ext2fs_lookup(fs, EXT2_ROOT_INO, name,
 			       sizeof(name)-1, 0, &ino);
 	if (!retval)
 		return ino;
-	if (retval != ENOENT)
-		printf("Error while trying to find /lost+found: %s",
-		       error_message(retval));
-	if (!fix_problem(fs, PR_3_NO_LF_DIR, 0))
+	if (retval != ENOENT) {
+		pctx.errcode = retval;
+		fix_problem(ctx, PR_3_ERR_FIND_LPF, &pctx);
+	}
+	if (!fix_problem(ctx, PR_3_NO_LF_DIR, 0))
 		return 0;
 
 	/*
 	 * Read the inode and block bitmaps in; we'll be messing with
 	 * them.
 	 */
-	read_bitmaps(fs);
+	read_bitmaps(ctx);
 	
 	/*
 	 * First, find a free block
 	 */
-	retval = ext2fs_new_block(fs, 0, block_found_map, &blk);
+	retval = ext2fs_new_block(fs, 0, ctx->block_found_map, &blk);
 	if (retval) {
-		com_err("ext2fs_new_block", retval,
-			"while trying to create /lost+found directory");
+		pctx.errcode = retval;
+		fix_problem(ctx, PR_3_ERR_LPF_NEW_BLOCK, &pctx);
 		return 0;
 	}
-	ext2fs_mark_block_bitmap(block_found_map, blk);
+	ext2fs_mark_block_bitmap(ctx->block_found_map, blk);
 	ext2fs_mark_block_bitmap(fs->block_map, blk);
 	ext2fs_mark_bb_dirty(fs);
 
 	/*
 	 * Next find a free inode.
 	 */
-	retval = ext2fs_new_inode(fs, EXT2_ROOT_INO, 040755, inode_used_map,
-				  &ino);
+	retval = ext2fs_new_inode(fs, EXT2_ROOT_INO, 040755,
+				  ctx->inode_used_map, &ino);
 	if (retval) {
-		com_err("ext2fs_new_inode", retval,
-			"while trying to create /lost+found directory");
+		pctx.errcode = retval;
+		fix_problem(ctx, PR_3_ERR_LPF_NEW_INODE, &pctx);
 		return 0;
 	}
-	ext2fs_mark_inode_bitmap(inode_used_map, ino);
-	ext2fs_mark_inode_bitmap(inode_dir_map, ino);
+	ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino);
+	ext2fs_mark_inode_bitmap(ctx->inode_dir_map, ino);
 	ext2fs_mark_inode_bitmap(fs->inode_map, ino);
 	ext2fs_mark_ib_dirty(fs);
 
@@ -343,16 +348,16 @@
 	 */
 	retval = ext2fs_new_dir_block(fs, ino, EXT2_ROOT_INO, &block);
 	if (retval) {
-		com_err("ext2fs_new_dir_block", retval,
-			"while creating new directory block");
+		pctx.errcode = retval;
+		fix_problem(ctx, PR_3_ERR_LPF_NEW_DIR_BLOCK, &pctx);
 		return 0;
 	}
 
 	retval = ext2fs_write_dir_block(fs, blk, block);
 	free(block);
 	if (retval) {
-		com_err("ext2fs_write_dir_block", retval,
-			"while writing the directory block for /lost+found");
+		pctx.errcode = retval;
+		fix_problem(ctx, PR_3_ERR_LPF_WRITE_BLOCK, &pctx);
 		return 0;
 	}
 
@@ -370,18 +375,19 @@
 	/*
 	 * Next, write out the inode.
 	 */
-	retval = ext2fs_write_inode(fs, ino, &inode);
-	if (retval) {
-		com_err("ext2fs_write_inode", retval,
-			"While trying to create /lost+found");
+	pctx.errcode = ext2fs_write_inode(fs, ino, &inode);
+	if (pctx.errcode) {
+		pctx.str = "ext2fs_write_inode";
+		fix_problem(ctx, PR_3_CREATE_LPF_ERROR, &pctx);
 		return 0;
 	}
 	/*
 	 * Finally, create the directory link
 	 */
-	retval = ext2fs_link(fs, EXT2_ROOT_INO, name, ino, 0);
-	if (retval) {
-		com_err("ext2fs_link", retval, "While creating /lost+found");
+	pctx.errcode = ext2fs_link(fs, EXT2_ROOT_INO, name, ino, 0);
+	if (pctx.errcode) {
+		pctx.str = "ext2fs_link";
+		fix_problem(ctx, PR_3_CREATE_LPF_ERROR, &pctx);
 		return 0;
 	}
 
@@ -389,9 +395,9 @@
 	 * Miscellaneous bookkeeping that needs to be kept straight.
 	 */
 	add_dir_info(fs, ino, EXT2_ROOT_INO);
-	adjust_inode_count(fs, EXT2_ROOT_INO, +1);
-	ext2fs_icount_store(inode_count, ino, 2);
-	ext2fs_icount_store(inode_link_info, ino, 2);
+	adjust_inode_count(ctx, EXT2_ROOT_INO, +1);
+	ext2fs_icount_store(ctx->inode_count, ino, 2);
+	ext2fs_icount_store(ctx->inode_link_info, ino, 2);
 #if 0
 	printf("/lost+found created; inode #%lu\n", ino);
 #endif
@@ -401,44 +407,45 @@
 /*
  * This routine will connect a file to lost+found
  */
-int reconnect_file(ext2_filsys fs, ino_t inode)
+int reconnect_file(e2fsck_t ctx, ino_t inode)
 {
+	ext2_filsys fs = ctx->fs;
 	errcode_t	retval;
 	char		name[80];
-	
+	struct problem_context	pctx;
+
+	clear_problem_context(&pctx);
+	pctx.ino = inode;
+
+	if (!bad_lost_and_found && !lost_and_found) {
+		lost_and_found = get_lost_and_found(ctx);
+		if (!lost_and_found)
+			bad_lost_and_found++;
+	}
 	if (bad_lost_and_found) {
-		printf("Bad or nonexistent /lost+found.  Cannot reconnect.\n");
+		fix_problem(ctx, PR_3_NO_LPF, &pctx);
 		return 1;
 	}
-	if (!lost_and_found) {
-		lost_and_found = get_lost_and_found(fs);
-		if (!lost_and_found) {
-			printf("Bad or nonexistent /lost+found.  Cannot reconnect.\n");
-			bad_lost_and_found++;
-			return 1;
-		}
-	}
-
+	
 	sprintf(name, "#%lu", inode);
 	retval = ext2fs_link(fs, lost_and_found, name, inode, 0);
 	if (retval == EXT2_ET_DIR_NO_SPACE) {
-		if (!fix_problem(fs, PR_3_EXPAND_LF_DIR, 0))
+		if (!fix_problem(ctx, PR_3_EXPAND_LF_DIR, &pctx))
 			return 1;
-		retval = expand_directory(fs, lost_and_found);
+		retval = expand_directory(ctx, lost_and_found);
 		if (retval) {
-			printf("Could not expand /lost+found: %s\n",
-			       error_message(retval));
+			pctx.errcode = retval;
+			fix_problem(ctx, PR_3_CANT_EXPAND_LPF, &pctx);
 			return 1;
 		}
 		retval = ext2fs_link(fs, lost_and_found, name, inode, 0);
 	}
 	if (retval) {
-		printf("Could not reconnect %lu: %s\n", inode,
-		       error_message(retval));
+		pctx.errcode = retval;
+		fix_problem(ctx, PR_3_CANT_RECONNECT, &pctx);
 		return 1;
 	}
-
-	adjust_inode_count(fs, inode, +1);
+	adjust_inode_count(ctx, inode, +1);
 
 	return 0;
 }
@@ -446,8 +453,9 @@
 /*
  * Utility routine to adjust the inode counts on an inode.
  */
-static errcode_t adjust_inode_count(ext2_filsys fs, ino_t ino, int adj)
+static errcode_t adjust_inode_count(e2fsck_t ctx, ino_t ino, int adj)
 {
+	ext2_filsys fs = ctx->fs;
 	errcode_t		retval;
 	struct ext2_inode 	inode;
 	
@@ -465,11 +473,11 @@
 
 	inode.i_links_count += adj;
 	if (adj == 1) {
-		ext2fs_icount_increment(inode_count, ino, 0);
-		ext2fs_icount_increment(inode_link_info, ino, 0);
+		ext2fs_icount_increment(ctx->inode_count, ino, 0);
+		ext2fs_icount_increment(ctx->inode_link_info, ino, 0);
 	} else {
-		ext2fs_icount_decrement(inode_count, ino, 0);
-		ext2fs_icount_decrement(inode_link_info, ino, 0);
+		ext2fs_icount_decrement(ctx->inode_count, ino, 0);
+		ext2fs_icount_decrement(ctx->inode_link_info, ino, 0);
 	}
 	
 
@@ -487,6 +495,7 @@
 	ext2_filsys	fs;
 	ino_t		parent;
 	int		done;
+	e2fsck_t	ctx;
 };
 
 static int fix_dotdot_proc(struct ext2_dir_entry *dirent,
@@ -497,35 +506,42 @@
 {
 	struct fix_dotdot_struct *fp = (struct fix_dotdot_struct *) private;
 	errcode_t	retval;
+	struct problem_context pctx;
 
 	if (dirent->name_len != 2)
 		return 0;
 	if (strncmp(dirent->name, "..", 2))
 		return 0;
-	
-	retval = adjust_inode_count(fp->fs, dirent->inode, -1);
-	if (retval)
-		printf("Error while adjusting inode count on inode %u\n",
-		       dirent->inode);
-	retval = adjust_inode_count(fp->fs, fp->parent, 1);
-	if (retval)
-		printf("Error while adjusting inode count on inode %lu\n",
-		       fp->parent);
 
+	clear_problem_context(&pctx);
+	
+	retval = adjust_inode_count(fp->ctx, dirent->inode, -1);
+	if (retval) {
+		pctx.errcode = retval;
+		fix_problem(fp->ctx, PR_3_ADJUST_INODE, &pctx);
+	}
+	retval = adjust_inode_count(fp->ctx, fp->parent, 1);
+	if (retval) {
+		pctx.errcode = retval;
+		fix_problem(fp->ctx, PR_3_ADJUST_INODE, &pctx);
+	}
 	dirent->inode = fp->parent;
 
 	fp->done++;
 	return DIRENT_ABORT | DIRENT_CHANGED;
 }
 
-static void fix_dotdot(ext2_filsys fs, struct dir_info *dir, ino_t parent)
+static void fix_dotdot(e2fsck_t ctx, struct dir_info *dir, ino_t parent)
 {
+	ext2_filsys fs = ctx->fs;
 	errcode_t	retval;
 	struct fix_dotdot_struct fp;
+	struct problem_context pctx;
 
 	fp.fs = fs;
 	fp.parent = parent;
 	fp.done = 0;
+	fp.ctx = ctx;
 
 #if 0
 	printf("Fixing '..' of inode %lu to be %lu...\n", dir->ino, parent);
@@ -534,9 +550,11 @@
 	retval = ext2fs_dir_iterate(fs, dir->ino, DIRENT_FLAG_INCLUDE_EMPTY,
 				    0, fix_dotdot_proc, &fp);
 	if (retval || !fp.done) {
-		printf("Couldn't fix parent of inode %lu: %s\n\n",
-		       dir->ino, retval ? error_message(retval) :
-		       "Couldn't find parent direntory entry");
+		clear_problem_context(&pctx);
+		pctx.ino = dir->ino;
+		pctx.errcode = retval;
+		fix_problem(ctx, retval ? PR_3_FIX_PARENT_ERR :
+			    PR_3_FIX_PARENT_NOFIND, &pctx);
 		ext2fs_unmark_valid(fs);
 	}
 	dir->dotdot = parent;
@@ -552,6 +570,7 @@
 struct expand_dir_struct {
 	int	done;
 	errcode_t	err;
+	e2fsck_t	ctx;
 };
 
 static int expand_dir_proc(ext2_filsys fs,
@@ -564,12 +583,16 @@
 	static blk_t	last_blk = 0;
 	char		*block;
 	errcode_t	retval;
+	e2fsck_t	ctx;
+
+	ctx = es->ctx;
 	
 	if (*blocknr) {
 		last_blk = *blocknr;
 		return 0;
 	}
-	retval = ext2fs_new_block(fs, last_blk, block_found_map, &new_blk);
+	retval = ext2fs_new_block(fs, last_blk, ctx->block_found_map,
+				  &new_blk);
 	if (retval) {
 		es->err = retval;
 		return BLOCK_ABORT;
@@ -596,7 +619,7 @@
 	}
 	free(block);
 	*blocknr = new_blk;
-	ext2fs_mark_block_bitmap(block_found_map, new_blk);
+	ext2fs_mark_block_bitmap(ctx->block_found_map, new_blk);
 	ext2fs_mark_block_bitmap(fs->block_map, new_blk);
 	ext2fs_mark_bb_dirty(fs);
 	if (es->done)
@@ -605,8 +628,9 @@
 		return BLOCK_CHANGED;
 }
 
-static errcode_t expand_directory(ext2_filsys fs, ino_t dir)
+static errcode_t expand_directory(e2fsck_t ctx, ino_t dir)
 {
+	ext2_filsys fs = ctx->fs;
 	errcode_t	retval;
 	struct expand_dir_struct es;
 	struct ext2_inode	inode;
@@ -620,6 +644,7 @@
 	
 	es.done = 0;
 	es.err = 0;
+	es.ctx = ctx;
 	
 	retval = ext2fs_block_iterate(fs, dir, BLOCK_FLAG_APPEND,
 				      0, expand_dir_proc, &es);
@@ -643,6 +668,3 @@
 
 	return 0;
 }
-
-
-		
diff --git a/e2fsck/pass4.c b/e2fsck/pass4.c
index 627b554..f5784cf 100644
--- a/e2fsck/pass4.c
+++ b/e2fsck/pass4.c
@@ -22,8 +22,9 @@
  * This subroutine returns 1 then the caller shouldn't bother with the
  * rest of the pass 4 tests.
  */
-static int disconnect_inode(ext2_filsys fs, ino_t i)
+static int disconnect_inode(e2fsck_t ctx, ino_t i)
 {
+	ext2_filsys fs = ctx->fs;
 	struct ext2_inode	inode;
 	struct problem_context	pctx;
 
@@ -37,8 +38,8 @@
 		/*
 		 * This is a zero-length file; prompt to delete it...
 		 */
-		if (fix_problem(fs, PR_4_ZERO_LEN_INODE, &pctx)) {
-			ext2fs_icount_store(inode_link_info, i, 0);
+		if (fix_problem(ctx, PR_4_ZERO_LEN_INODE, &pctx)) {
+			ext2fs_icount_store(ctx->inode_link_info, i, 0);
 			inode.i_links_count = 0;
 			inode.i_dtime = time(0);
 			e2fsck_write_inode(fs, i, &inode,
@@ -46,9 +47,9 @@
 			/*
 			 * Fix up the bitmaps...
 			 */
-			read_bitmaps(fs);
-			ext2fs_unmark_inode_bitmap(inode_used_map, i);
-			ext2fs_unmark_inode_bitmap(inode_dir_map, i);
+			read_bitmaps(ctx);
+			ext2fs_unmark_inode_bitmap(ctx->inode_used_map, i);
+			ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, i);
 			ext2fs_unmark_inode_bitmap(fs->inode_map, i);
 			ext2fs_mark_ib_dirty(fs);
 			return 0;
@@ -58,8 +59,8 @@
 	/*
 	 * Prompt to reconnect.
 	 */
-	if (fix_problem(fs, PR_4_UNATTACHED_INODE, &pctx)) {
-		if (reconnect_file(fs, i))
+	if (fix_problem(ctx, PR_4_UNATTACHED_INODE, &pctx)) {
+		if (reconnect_file(ctx, i))
 			ext2fs_unmark_valid(fs);
 	} else {
 		/*
@@ -74,8 +75,9 @@
 }
 
 
-void pass4(ext2_filsys fs)
+void pass4(e2fsck_t ctx)
 {
+	ext2_filsys fs = ctx->fs;
 	ino_t	i;
 	struct ext2_inode	inode;
 	struct resource_track	rtrack;
@@ -88,52 +90,50 @@
 	mtrace_print("Pass 4");
 #endif
 
-	if (!preen)
-		printf("Pass 4: Checking reference counts\n");
 	clear_problem_context(&pctx);
+
+	if (!(ctx->options & E2F_OPT_PREEN))
+		fix_problem(ctx, PR_4_PASS_HEADER, &pctx);
+
 	for (i=1; i <= fs->super->s_inodes_count; i++) {
 		if (i == EXT2_BAD_INO ||
 		    (i > EXT2_ROOT_INO && i < EXT2_FIRST_INODE(fs->super)))
 			continue;
-		if (!(ext2fs_test_inode_bitmap(inode_used_map, i)) ||
-		    (inode_bb_map &&
-		     ext2fs_test_inode_bitmap(inode_bb_map, i)))
+		if (!(ext2fs_test_inode_bitmap(ctx->inode_used_map, i)) ||
+		    (ctx->inode_bb_map &&
+		     ext2fs_test_inode_bitmap(ctx->inode_bb_map, i)))
 			continue;
-		ext2fs_icount_fetch(inode_link_info, i, &link_count);
-		ext2fs_icount_fetch(inode_count, i, &link_counted);
+		ext2fs_icount_fetch(ctx->inode_link_info, i, &link_count);
+		ext2fs_icount_fetch(ctx->inode_count, i, &link_counted);
 		if (link_counted == 0) {
-			if (disconnect_inode(fs, i))
+			if (disconnect_inode(ctx, i))
 				continue;
-			ext2fs_icount_fetch(inode_link_info, i, &link_count);
-			ext2fs_icount_fetch(inode_count, i, &link_counted);
+			ext2fs_icount_fetch(ctx->inode_link_info, i,
+					    &link_count);
+			ext2fs_icount_fetch(ctx->inode_count, i,
+					    &link_counted);
 		}
 		if (link_counted != link_count) {
 			e2fsck_read_inode(fs, i, &inode, "pass4");
 			pctx.ino = i;
 			pctx.inode = &inode;
 			if (link_count != inode.i_links_count) {
-				printf("WARNING: PROGRAMMING BUG IN E2FSCK!\n");
-				printf("\tOR SOME BONEHEAD (YOU) IS CHECKING "
-				       "A MOUNTED (LIVE) FILESYSTEM.\n"); 
-				printf("inode_link_info[%ld] is %u, "
-				       "inode.i_links_count is %d.  "
-				       "They should be the same!\n",
-				       i, link_count,
-				       inode.i_links_count);
+				pctx.num = link_count;
+				fix_problem(ctx,
+					    PR_4_INCONSISTENT_COUNT, &pctx);
 			}
 			pctx.num = link_counted;
-			if (fix_problem(fs, PR_4_BAD_REF_COUNT, &pctx)) {
+			if (fix_problem(ctx, PR_4_BAD_REF_COUNT, &pctx)) {
 				inode.i_links_count = link_counted;
 				e2fsck_write_inode(fs, i, &inode, "pass4");
 			}
 		}
 	}
-	ext2fs_free_icount(inode_link_info); inode_link_info = 0;
-	ext2fs_free_icount(inode_count); inode_count = 0;
-	ext2fs_free_inode_bitmap(inode_bb_map);
-	if (tflag > 1) {
-		printf("Pass 4: ");
-		print_resource_track(&rtrack);
-	}
+	ext2fs_free_icount(ctx->inode_link_info); ctx->inode_link_info = 0;
+	ext2fs_free_icount(ctx->inode_count); ctx->inode_count = 0;
+	ext2fs_free_inode_bitmap(ctx->inode_bb_map);
+	ctx->inode_bb_map = 0;
+	if (ctx->options & E2F_OPT_TIME2)
+		print_resource_track("Pass 4", &rtrack);
 }
 
diff --git a/e2fsck/pass5.c b/e2fsck/pass5.c
index def492a..499c92c 100644
--- a/e2fsck/pass5.c
+++ b/e2fsck/pass5.c
@@ -10,21 +10,18 @@
  * 
  */
 
-#include "et/com_err.h"
-
 #include "e2fsck.h"
+#include "problem.h"
 
-static void check_block_bitmaps(ext2_filsys fs);
-static void check_inode_bitmaps(ext2_filsys fs);
-static void check_inode_end(ext2_filsys fs);
-static void check_block_end(ext2_filsys fs);
+static void check_block_bitmaps(e2fsck_t ctx);
+static void check_inode_bitmaps(e2fsck_t ctx);
+static void check_inode_end(e2fsck_t ctx);
+static void check_block_end(e2fsck_t ctx);
 
-static int do_fix = -1;
-static const char *fix_question = "Fix summary information";
-
-void pass5(ext2_filsys fs)
+void pass5(e2fsck_t ctx)
 {
 	struct resource_track	rtrack;
+	struct problem_context	pctx;
 	
 #ifdef MTRACE
 	mtrace_print("Pass 5");
@@ -32,28 +29,32 @@
 
 	init_resource_track(&rtrack);
 	
-	if (!preen)
-		printf("Pass 5: Checking group summary information\n");
+	clear_problem_context(&pctx);
 
-	read_bitmaps(fs);
+	if (!(ctx->options & E2F_OPT_PREEN))
+		fix_problem(ctx, PR_5_PASS_HEADER, &pctx);
 
-	check_block_bitmaps(fs);
-	check_inode_bitmaps(fs);
-	check_inode_end(fs);
-	check_block_end(fs);
+	read_bitmaps(ctx);
 
-	ext2fs_free_inode_bitmap(inode_used_map);
-	ext2fs_free_inode_bitmap(inode_dir_map);
-	ext2fs_free_block_bitmap(block_found_map);
+	check_block_bitmaps(ctx);
+	check_inode_bitmaps(ctx);
+	check_inode_end(ctx);
+	check_block_end(ctx);
 
-	if (tflag > 1) {
-		printf("Pass 5: ");
-		print_resource_track(&rtrack);
-	}
+	ext2fs_free_inode_bitmap(ctx->inode_used_map);
+	ctx->inode_used_map = 0;
+	ext2fs_free_inode_bitmap(ctx->inode_dir_map);
+	ctx->inode_dir_map = 0;
+	ext2fs_free_block_bitmap(ctx->block_found_map);
+	ctx->block_found_map = 0;
+
+	if (ctx->options & E2F_OPT_TIME2)
+		print_resource_track("Pass 5", &rtrack);
 }
 
-static void check_block_bitmaps(ext2_filsys fs)
+static void check_block_bitmaps(e2fsck_t ctx)
 {
+	ext2_filsys fs = ctx->fs;
 	blk_t	i;
 	int	*free_array;
 	int	group = 0;
@@ -61,21 +62,25 @@
 	int	free_blocks = 0;
 	int	group_free = 0;
 	int	actual, bitmap;
-	const char 	*print_header = "Block bitmap differences:";
+	struct problem_context	pctx;
+	int	problem, fixit;
+	errcode_t	retval;
 	
+	clear_problem_context(&pctx);
 	free_array = allocate_memory(fs->group_desc_count * sizeof(int),
 				     "free block count array");
 
 	if ((fs->super->s_first_data_block <
-	     ext2fs_get_block_bitmap_start(block_found_map)) ||
+	     ext2fs_get_block_bitmap_start(ctx->block_found_map)) ||
 	    (fs->super->s_blocks_count-1 >
-	     ext2fs_get_block_bitmap_end(block_found_map))) {
-		printf("PROGRAMMING ERROR: filesystem endpoints (%d, %d)\n\t"
-		       "don't match block_found_map endpoints (%d, %d).\n",
-		       fs->super->s_first_data_block,
-		       fs->super->s_blocks_count -1,
-		       ext2fs_get_block_bitmap_start(block_found_map),
-		       ext2fs_get_block_bitmap_end(block_found_map));
+	     ext2fs_get_block_bitmap_end(ctx->block_found_map))) {
+		pctx.num = 1;
+		pctx.blk = fs->super->s_first_data_block;
+		pctx.blk2 = fs->super->s_blocks_count -1;
+		pctx.ino = ext2fs_get_block_bitmap_start(ctx->block_found_map);
+		pctx.ino2 = ext2fs_get_block_bitmap_end(ctx->block_found_map);
+		fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx);
+		/* fatal */
 		fatal_error(0);
 	}
 		       
@@ -83,56 +88,40 @@
 	     ext2fs_get_block_bitmap_start(fs->block_map)) ||
 	    (fs->super->s_blocks_count-1 >
 	     ext2fs_get_block_bitmap_end(fs->block_map))) {
-		printf("PROGRAMMING ERROR: filesystem endpoints (%d, %d)\n\t"
-		       "don't match fs->block_map endpoints (%d, %d).\n",
-		       fs->super->s_first_data_block,
-		       fs->super->s_blocks_count -1,
-		       ext2fs_get_block_bitmap_start(fs->block_map),
-		       ext2fs_get_block_bitmap_end(fs->block_map));
+		pctx.num = 2;
+		pctx.blk = fs->super->s_first_data_block;
+		pctx.blk2 = fs->super->s_blocks_count -1;
+		pctx.ino = ext2fs_get_block_bitmap_start(fs->block_map);
+		pctx.ino2 = ext2fs_get_block_bitmap_end(fs->block_map);
+		fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx);
+		/* fatal */
 		fatal_error(0);
 	}
 		       
-		       
+redo_counts:		       
 	for (i = fs->super->s_first_data_block;
 	     i < fs->super->s_blocks_count;
 	     i++) {
-		actual = ext2fs_fast_test_block_bitmap(block_found_map, i);
+		actual = ext2fs_fast_test_block_bitmap(ctx->block_found_map, i);
 		bitmap = ext2fs_fast_test_block_bitmap(fs->block_map, i);
 		
 		if (actual == bitmap)
 			goto do_counts;
-
-		if (do_fix < 0)
-			do_fix = ask(fix_question, 1);
-		if (!preen && print_header) {
-			printf(print_header);
-			print_header = 0;
-		}
+		
 		if (!actual && bitmap) {
 			/*
 			 * Block not used, but marked in use in the bitmap.
 			 */
-			if (!preen)
-				printf(" -%u", i);
-			if (do_fix)
-				ext2fs_unmark_block_bitmap(fs->block_map,
-							   i);
+			problem = PR_5_UNUSED_BLOCK;
 		} else {
 			/*
 			 * Block used, but not marked in use in the bitmap.
 			 */
-			if (!preen)
-				printf(" +%u", i);
-			if (do_fix)
-				ext2fs_mark_block_bitmap(fs->block_map,
-							 i);
+			problem = PR_5_BLOCK_USED;
 		}
-		if (do_fix) {
-			ext2fs_mark_bb_dirty(fs);
-			bitmap = actual;
-		} else
-			ext2fs_unmark_valid(fs);
-			
+		pctx.blk = i;
+		fix_problem(ctx, problem, &pctx);
+		
 	do_counts:
 		if (!bitmap) {
 			group_free++;
@@ -147,18 +136,30 @@
 			group_free = 0;
 		}
 	}
-	if (!print_header)
-		printf(".  %s\n", fix_msg[do_fix]);
+	fixit = end_problem_latch(ctx, 	PR_LATCH_BBITMAP);
+	if (fixit == 1) {
+		ext2fs_free_block_bitmap(fs->block_map);
+		retval = ext2fs_copy_bitmap(ctx->block_found_map,
+						  &fs->block_map);
+		/* XXX check retval --- should never fail! */
+		ext2fs_set_bitmap_padding(fs->block_map);
+		ext2fs_mark_bb_dirty(fs);
+		
+		/* Redo the counts */
+		blocks = 0; free_blocks = 0; group_free = 0; group = 0;
+		memset(free_array, 0, fs->group_desc_count * sizeof(int));
+		goto redo_counts;
+	} else if (fixit == 0)
+		ext2fs_unmark_valid(fs);
+
 	for (i = 0; i < fs->group_desc_count; i++) {
 		if (free_array[i] != fs->group_desc[i].bg_free_blocks_count) {
-			if (do_fix < 0)
-				do_fix = ask(fix_question, 1);
-			if (!preen)
-				printf("Free blocks count wrong for "
-				       "group %u (%u, counted=%d).  %s\n", i,
-				       fs->group_desc[i].bg_free_blocks_count,
-				       free_array[i], fix_msg[do_fix]);
-			if (do_fix) {
+			pctx.group = i;
+			pctx.blk = fs->group_desc[i].bg_free_blocks_count;
+			pctx.blk2 = free_array[i];
+
+			if (fix_problem(ctx, PR_5_FREE_BLOCK_COUNT_GROUP,
+					&pctx)) {
 				fs->group_desc[i].bg_free_blocks_count =
 					free_array[i];
 				ext2fs_mark_super_dirty(fs);
@@ -167,14 +168,11 @@
 		}
 	}
 	if (free_blocks != fs->super->s_free_blocks_count) {
-		if (do_fix < 0)
-			do_fix = ask(fix_question, 1);
-		if (!preen)
-			printf("Free blocks count wrong "
-			       "(%u, counted=%d).  %s\n",
-			       fs->super->s_free_blocks_count, free_blocks,
-			       fix_msg[do_fix]);
-		if (do_fix) {
+		pctx.group = 0;
+		pctx.blk = fs->super->s_free_blocks_count;
+		pctx.blk2 = free_blocks;
+
+		if (fix_problem(ctx, PR_5_FREE_BLOCK_COUNT, &pctx)) {
 			fs->super->s_free_blocks_count = free_blocks;
 			ext2fs_mark_super_dirty(fs);
 		} else
@@ -183,8 +181,9 @@
 	free(free_array);
 }
 			
-static void check_inode_bitmaps(ext2_filsys fs)
+static void check_inode_bitmaps(e2fsck_t ctx)
 {
+	ext2_filsys fs = ctx->fs;
 	ino_t	i;
 	int	free_inodes = 0;
 	int	group_free = 0;
@@ -194,77 +193,70 @@
 	int	*free_array;
 	int	*dir_array;
 	int	actual, bitmap;
-	const char *print_header = "Inode bitmap differences:";
+	errcode_t	retval;
+	struct problem_context	pctx;
+	int	problem, fixit;
 	
+	clear_problem_context(&pctx);
 	free_array = allocate_memory(fs->group_desc_count * sizeof(int),
 				     "free inode count array");
 				     
 	dir_array = allocate_memory(fs->group_desc_count * sizeof(int),
 				    "directory count array");
 				     
-	if ((1 < ext2fs_get_inode_bitmap_start(inode_used_map)) ||
+	if ((1 < ext2fs_get_inode_bitmap_start(ctx->inode_used_map)) ||
 	    (fs->super->s_inodes_count > 
-	     ext2fs_get_inode_bitmap_end(inode_used_map))) {
-		printf("PROGRAMMING ERROR: filesystem inode endpoints (%d, %d)\n\t"
-		       "don't match inode_used_map endpoints (%d, %d).\n",
-		       1, fs->super->s_inodes_count,
-		       ext2fs_get_inode_bitmap_start(inode_used_map),
-		       ext2fs_get_inode_bitmap_end(inode_used_map));
+	     ext2fs_get_inode_bitmap_end(ctx->inode_used_map))) {
+		pctx.num = 3;
+		pctx.blk = 1;
+		pctx.blk2 = fs->super->s_inodes_count;
+		pctx.ino = ext2fs_get_inode_bitmap_start(ctx->inode_used_map);
+		pctx.ino2 = ext2fs_get_inode_bitmap_end(ctx->inode_used_map);
+		fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx);
+		/* fatal */
 		fatal_error(0);
 	}
 	if ((1 < ext2fs_get_inode_bitmap_start(fs->inode_map)) ||
 	    (fs->super->s_inodes_count > 
 	     ext2fs_get_inode_bitmap_end(fs->inode_map))) {
-		printf("PROGRAMMING ERROR: filesystem inode endpoints (%d, %d)\n\t"
-		       "don't match fs->inode_map endpoints (%d, %d).\n",
-		       1, fs->super->s_inodes_count,
-		       ext2fs_get_inode_bitmap_start(fs->inode_map),
-		       ext2fs_get_inode_bitmap_end(fs->inode_map));
+		pctx.num = 4;
+		pctx.blk = 1;
+		pctx.blk2 = fs->super->s_inodes_count;
+		pctx.ino = ext2fs_get_inode_bitmap_start(fs->inode_map);
+		pctx.ino2 = ext2fs_get_inode_bitmap_end(fs->inode_map);
+		fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx);
+		/* fatal */
 		fatal_error(0);
 	}
 
+redo_counts:
 	for (i = 1; i <= fs->super->s_inodes_count; i++) {
-		actual = ext2fs_fast_test_inode_bitmap(inode_used_map, i);
+		actual = ext2fs_fast_test_inode_bitmap(ctx->inode_used_map, i);
 		bitmap = ext2fs_fast_test_inode_bitmap(fs->inode_map, i);
 		
 		if (actual == bitmap)
 			goto do_counts;
 		
-		if (do_fix < 0)
-			do_fix = ask(fix_question, 1);
-		if (!preen && print_header) {
-			printf(print_header);
-			print_header = 0;
-		}
 		if (!actual && bitmap) {
 			/*
 			 * Inode wasn't used, but marked in bitmap
 			 */
-			if (!preen)
-				printf(" -%lu", i);
-			if (do_fix)
-				ext2fs_unmark_inode_bitmap(fs->inode_map, i);
-		} else if (actual && !bitmap) {
+			problem = PR_5_UNUSED_INODE;
+		} else /* if (actual && !bitmap) */ {
 			/*
 			 * Inode used, but not in bitmap
 			 */
-			if (!preen)
-				printf (" +%lu", i);
-			if (do_fix)
-				ext2fs_mark_inode_bitmap(fs->inode_map, i);
+			problem = PR_5_INODE_USED;
 		}
-		if (do_fix) {
-			ext2fs_mark_ib_dirty(fs);
-			bitmap = actual;
-		} else
-			ext2fs_unmark_valid(fs);
-			
+		pctx.ino = i;
+		fix_problem(ctx, problem, &pctx);
+		
 do_counts:
 		if (!bitmap) {
 			group_free++;
 			free_inodes++;
 		} else {
-			if (ext2fs_test_inode_bitmap(inode_dir_map, i))
+			if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, i))
 				dirs_count++;
 		}
 		inodes++;
@@ -278,19 +270,31 @@
 			dirs_count = 0;
 		}
 	}
-	if (!print_header)
-		printf(".  %s\n", fix_msg[do_fix]);
+	fixit = end_problem_latch(ctx, PR_LATCH_IBITMAP);
+	if (fixit == 1) {
+		ext2fs_free_inode_bitmap(fs->inode_map);
+		retval = ext2fs_copy_bitmap(ctx->inode_used_map,
+						  &fs->inode_map);
+		/* XXX check retval --- should never fail! */
+		ext2fs_set_bitmap_padding(fs->inode_map);
+		ext2fs_mark_ib_dirty(fs);
+
+		/* redo counts */
+		inodes = 0; free_inodes = 0; group_free = 0;
+		dirs_count = 0; group = 0;
+		memset(free_array, 0, fs->group_desc_count * sizeof(int));
+		memset(dir_array, 0, fs->group_desc_count * sizeof(int));
+		goto redo_counts;
+	} else if (fixit == 0)
+		ext2fs_unmark_valid(fs);
 	
 	for (i = 0; i < fs->group_desc_count; i++) {
 		if (free_array[i] != fs->group_desc[i].bg_free_inodes_count) {
-			if (do_fix < 0)
-				do_fix = ask(fix_question, 1);
-			if (!preen)
-				printf ("Free inodes count wrong for "
-					"group #%lu (%u, counted=%d).  %s\n", i,
-					fs->group_desc[i].bg_free_inodes_count,
-					free_array[i], fix_msg[do_fix]);
-			if (do_fix) {
+			pctx.group = i;
+			pctx.ino = fs->group_desc[i].bg_free_inodes_count;
+			pctx.ino2 = free_array[i];
+			if (fix_problem(ctx, PR_5_FREE_INODE_COUNT_GROUP,
+					&pctx)) {
 				fs->group_desc[i].bg_free_inodes_count =
 					free_array[i];
 				ext2fs_mark_super_dirty(fs);
@@ -298,14 +302,12 @@
 				ext2fs_unmark_valid(fs);
 		}
 		if (dir_array[i] != fs->group_desc[i].bg_used_dirs_count) {
-			if (do_fix < 0)
-				do_fix = ask(fix_question, 1);
-			if (!preen)
-				printf ("Directories count wrong for "
-					"group #%lu (%u, counted=%d).  %s\n", i,
-					fs->group_desc[i].bg_used_dirs_count,
-					dir_array[i], fix_msg[do_fix]);
-			if (do_fix) {
+			pctx.group = i;
+			pctx.ino = fs->group_desc[i].bg_used_dirs_count;
+			pctx.ino2 = dir_array[i];
+
+			if (fix_problem(ctx, PR_5_FREE_DIR_COUNT_GROUP,
+					&pctx)) {
 				fs->group_desc[i].bg_used_dirs_count =
 					dir_array[i];
 				ext2fs_mark_super_dirty(fs);
@@ -314,14 +316,11 @@
 		}
 	}
 	if (free_inodes != fs->super->s_free_inodes_count) {
-		if (do_fix < 0)
-			do_fix = ask(fix_question, 1);
-		if (!preen)
-			printf("Free inodes count wrong "
-			       "(%u, counted=%d).  %s\n",
-			       fs->super->s_free_inodes_count, free_inodes,
-			       fix_msg[do_fix]);
-		if (do_fix) {
+		pctx.group = -1;
+		pctx.ino = fs->super->s_free_inodes_count;
+		pctx.ino2 = free_inodes;
+
+		if (fix_problem(ctx, PR_5_FREE_INODE_COUNT, &pctx)) {
 			fs->super->s_free_inodes_count = free_inodes;
 			ext2fs_mark_super_dirty(fs);
 		} else
@@ -331,17 +330,20 @@
 	free(dir_array);
 }
 
-static void check_inode_end(ext2_filsys fs)
+static void check_inode_end(e2fsck_t ctx)
 {
+	ext2_filsys fs = ctx->fs;
 	ino_t	end, save_inodes_count, i;
-	errcode_t	retval;
+	struct problem_context	pctx;
+
+	clear_problem_context(&pctx);
 
 	end = EXT2_INODES_PER_GROUP(fs->super) * fs->group_desc_count;
-	retval = ext2fs_fudge_inode_bitmap_end(fs->inode_map, end,
-					       &save_inodes_count);
-	if (retval) {
-		com_err("check_inode_end", retval,
-			"while trying to fudge end of inode bitmap");
+	pctx.errcode = ext2fs_fudge_inode_bitmap_end(fs->inode_map, end,
+						     &save_inodes_count);
+	if (pctx.errcode) {
+		pctx.num = 1;
+		fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx);
 		fatal_error(0);
 	}
 	if (save_inodes_count == end)
@@ -349,8 +351,7 @@
 	
 	for (i = save_inodes_count + 1; i <= end; i++) {
 		if (!ext2fs_test_inode_bitmap(fs->inode_map, i)) {
-			printf("Padding at end of inode bitmap is not set. ");
-			if (ask("Fix", 1)) {
+			if (fix_problem(ctx, PR_5_INODE_BMAP_PADDING, &pctx)) {
 				for (i = save_inodes_count + 1; i <= end; i++)
 					ext2fs_mark_inode_bitmap(fs->inode_map,
 								 i);
@@ -361,27 +362,30 @@
 		}
 	}
 
-	retval = ext2fs_fudge_inode_bitmap_end(fs->inode_map,
-					       save_inodes_count, 0);
-	if (retval) {
-		com_err("check_inode_end", retval,
-			"while trying to fudge end of inode bitmap back");
+	pctx.errcode = ext2fs_fudge_inode_bitmap_end(fs->inode_map,
+						     save_inodes_count, 0);
+	if (pctx.errcode) {
+		pctx.num = 2;
+		fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx);
 		fatal_error(0);
 	}
 }
 
-static void check_block_end(ext2_filsys fs)
+static void check_block_end(e2fsck_t ctx)
 {
+	ext2_filsys fs = ctx->fs;
 	blk_t	end, save_blocks_count, i;
-	errcode_t	retval;
+	struct problem_context	pctx;
+
+	clear_problem_context(&pctx);
 
 	end = fs->block_map->start +
 		(EXT2_BLOCKS_PER_GROUP(fs->super) * fs->group_desc_count) - 1;
-	retval = ext2fs_fudge_block_bitmap_end(fs->block_map, end,
-					       &save_blocks_count);
-	if (retval) {
-		com_err("check_block_end", retval,
-			"while trying to fudge end of block bitmap");
+	pctx.errcode = ext2fs_fudge_block_bitmap_end(fs->block_map, end,
+						     &save_blocks_count);
+	if (pctx.errcode) {
+		pctx.num = 3;
+		fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx);
 		fatal_error(0);
 	}
 	if (save_blocks_count == end)
@@ -389,9 +393,7 @@
 	
 	for (i = save_blocks_count + 1; i <= end; i++) {
 		if (!ext2fs_test_block_bitmap(fs->block_map, i)) {
-			printf("Padding at end of block bitmap is not set. ");
-
-			if (ask("Fix", 1)) {
+			if (fix_problem(ctx, PR_5_BLOCK_BMAP_PADDING, &pctx)) {
 				for (i = save_blocks_count + 1; i < end; i++)
 					ext2fs_mark_block_bitmap(fs->block_map,
 								 i);
@@ -402,12 +404,14 @@
 		}
 	}
 
-	retval = ext2fs_fudge_block_bitmap_end(fs->block_map,
-					       save_blocks_count, 0);
-	if (retval) {
-		com_err("check_block_end", retval,
-			"while trying to fudge end of block bitmap back");
+	pctx.errcode = ext2fs_fudge_block_bitmap_end(fs->block_map,
+						     save_blocks_count, 0);
+	if (pctx.errcode) {
+		pctx.num = 4;
+		fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx);
 		fatal_error(0);
 	}
 }
 
+
+
diff --git a/e2fsck/problem.c b/e2fsck/problem.c
index 512e2c6..eae98b1 100644
--- a/e2fsck/problem.c
+++ b/e2fsck/problem.c
@@ -19,71 +19,152 @@
 
 #include "problem.h"
 
-#define PROMPT_FIX	0
-#define PROMPT_CLEAR	1
-#define PROMPT_RELOCATE	2
-#define PROMPT_ALLOCATE 3
-#define PROMPT_EXPAND	4
-#define PROMPT_CONNECT 	5
-#define PROMPT_CREATE	6
-#define PROMPT_SALVAGE	7
-#define PROMPT_TRUNCATE	8
-#define PROMPT_CLEAR_INODE 9
+#define PROMPT_NONE	0
+#define PROMPT_FIX	1
+#define PROMPT_CLEAR	2
+#define PROMPT_RELOCATE	3
+#define PROMPT_ALLOCATE 4
+#define PROMPT_EXPAND	5
+#define PROMPT_CONNECT 	6
+#define PROMPT_CREATE	7
+#define PROMPT_SALVAGE	8
+#define PROMPT_TRUNCATE	9
+#define PROMPT_CLEAR_INODE 10
+#define PROMPT_ABORT 	11
+#define PROMPT_SPLIT 	12
+#define PROMPT_CONTINUE	13
+#define PROMPT_CLONE	14
+#define PROMPT_DELETE 	15
 
 /*
  * These are the prompts which are used to ask the user if they want
  * to fix a problem.
  */
 static const char *prompt[] = {
-	"Fix",			/* 0 */
-	"Clear",		/* 1 */
-	"Relocate",		/* 2 */
-	"Allocate",		/* 3 */
-	"Expand",		/* 4 */
-	"Connect to /lost+found", /* 5 */
-	"Create",		/* 6 */	
-	"Salvage",		/* 7 */
-	"Truncate",		/* 8 */
-	"Clear inode"		/* 9 */
-	};
+	"(no prompt)",		/* 0 */
+	"Fix",			/* 1 */
+	"Clear",		/* 2 */
+	"Relocate",		/* 3 */
+	"Allocate",		/* 4 */
+	"Expand",		/* 5 */
+	"Connect to /lost+found", /* 6 */
+	"Create",		/* 7 */	
+	"Salvage",		/* 8 */
+	"Truncate",		/* 9 */
+	"Clear inode",		/* 10 */
+	"Abort",		/* 11 */
+	"Split",		/* 12 */
+	"Continue",		/* 13 */
+	"Clone duplicate/bad blocks", /* 14 */
+	"Delete file",		/* 15 */
+};
 
 /*
  * These messages are printed when we are preen mode and we will be
  * automatically fixing the problem.
  */
 static const char *preen_msg[] = {
-	"FIXED",		/* 0 */
-	"CLEARED",		/* 1 */
-	"RELOCATED",		/* 2 */
-	"ALLOCATED",		/* 3 */
-	"EXPANDED",		/* 4 */
-	"RECONNECTED",		/* 5 */
-	"CREATED",		/* 6 */
-	"SALVAGED",		/* 7 */
-	"TRUNCATED",		/* 8 */
-	"INODE CLEARED"		/* 9 */
+	"(NONE)",		/* 0 */
+	"FIXED",		/* 1 */
+	"CLEARED",		/* 2 */
+	"RELOCATED",		/* 3 */
+	"ALLOCATED",		/* 4 */
+	"EXPANDED",		/* 5 */
+	"RECONNECTED",		/* 6 */
+	"CREATED",		/* 7 */
+	"SALVAGED",		/* 8 */
+	"TRUNCATED",		/* 9 */
+	"INODE CLEARED",	/* 10 */
+	"ABORTED",		/* 11 */
+	"SPLIT",		/* 12 */
+	"CONTINUING",		/* 13 */
+	"DUPLICATE/BAD BLOCKS CLONED", /* 14 */
+	"FILE DELETED",		/* 15 */
 };
 
-static struct e2fsck_problem problem_table[] = {
+static const struct e2fsck_problem problem_table[] = {
 
 	/* Pre-Pass 1 errors */
 
 	/* Block bitmap not in group */
 	{ PR_0_BB_NOT_GROUP, "@b @B for @g %g is not in @g.  (@b %b)\n",
-	  PROMPT_RELOCATE, 0 }, 
+	  PROMPT_RELOCATE, PR_LATCH_RELOC }, 
 
 	/* Inode bitmap not in group */
 	{ PR_0_IB_NOT_GROUP, "@i @B for @g %g is not in @g.  (@b %b)\n",
-	  PROMPT_RELOCATE, 0 }, 
+	  PROMPT_RELOCATE, PR_LATCH_RELOC }, 
 
 	/* Inode table not in group */
 	{ PR_0_ITABLE_NOT_GROUP,
 	  "@i table for @g %g is not in @g.  (@b %b)\n"
 	  "WARNING: SEVERE DATA LOSS POSSIBLE.\n",
-	  PROMPT_RELOCATE, 0 }, 
+	  PROMPT_RELOCATE, PR_LATCH_RELOC },
+
+	/* Superblock corrupt */
+	{ PR_0_SB_CORRUPT,
+	  "\nThe @S could not be read or does not describe a correct ext2\n"
+	  "@f.  If the device is valid and it really contains an ext2\n"
+	  "@f (and not swap or ufs or something else), then the @S\n"
+  "is corrupt, and you might try running e2fsck with an alternate @S:\n"
+	  "    e2fsck -b %S <device>\n\n",
+	  PROMPT_NONE, PR_FATAL },
+
+	/* Filesystem size is wrong */
+	{ PR_0_FS_SIZE_WRONG,
+	  "The @f size (according to the @S) is %b @bs\n"
+	  "The physical size of the device is %c @bs\n"
+	  "Either the @S or the partition table is likely to be corrupt!\n",
+	  PROMPT_ABORT, 0 },
+
+	/* Fragments not supported */		  
+	{ PR_0_NO_FRAGMENTS,
+	  "@S @b_size = %b, fragsize = %c.\n"
+	  "This version of e2fsck does not support fragment sizes different\n"
+	  "from the @b size.\n",
+	  PROMPT_NONE, PR_FATAL },
+
+	  /* Bad blocks_per_group */
+	{ PR_0_BLOCKS_PER_GROUP,
+	  "@S @bs_per_group = %b, should have been %c\n",
+	  PROMPT_NONE, PR_AFTER_CODE, PR_0_SB_CORRUPT },
+
+	/* Bad first_data_block */
+	{ PR_0_FIRST_DATA_BLOCK,
+	  "@S first_data_@b = %b, should have been %c\n",
+	  PROMPT_NONE, PR_AFTER_CODE, PR_0_SB_CORRUPT },
 	
+	/* Adding UUID to filesystem */
+	{ PR_0_ADD_UUID,
+	  "@f did not have a UUID; generating one.\n\n",
+	  PROMPT_NONE, 0 },
+
+	/* Relocate hint */
+	{ PR_0_RELOCATE_HINT,
+	  "Note: if there is several inode or block bitmap blocks\n"
+	  "which require relocation, or one part of the inode table\n"
+	  "which must be moved, you may wish to try running e2fsck\n"
+	  "with the '-b %S' option first.  The problem may lie only\n"
+	  "with the primary block group descriptor, and the backup\n"
+	  "block group descriptor may be OK.\n\n",
+	  PROMPT_NONE, PR_PREEN_OK | PR_NOCOLLATE },
+
+	/* Miscellaneous superblock corruption */
+	{ PR_0_MISC_CORRUPT_SUPER,
+	  "Corruption found in @S.  (%s = %N).\n",	  
+	  PROMPT_NONE, PR_AFTER_CODE, PR_0_SB_CORRUPT },
+
+	/* Error determing physical device size of filesystem */
+	{ PR_0_GETSIZE_ERROR,	  
+	  "Error determining size of the physical device: %m\n",
+	  PROMPT_NONE, PR_FATAL },
+		  
 	/* Pass 1 errors */
 	
+	/* Pass 1: Checking inodes, blocks, and sizes */
+	{ PR_1_PASS_HEADER,
+	  "Pass 1: Checking @is, @bs, and sizes\n",
+	  PROMPT_NONE, 0 },
+		  
 	/* Root directory is not an inode */
 	{ PR_1_ROOT_NO_DIR, "@r is not a @d.  ",
 	  PROMPT_CLEAR, 0 }, 
@@ -141,12 +222,12 @@
 	/* Inode has incorrect i_size */
 	{ PR_1_BAD_I_SIZE,
 	  "@i %i, i_size is %Is, @s %N.  ",
-		  PROMPT_FIX, PR_PREEN_OK },
+	  PROMPT_FIX, PR_PREEN_OK },
 		  
 	/* Inode has incorrect i_blocks */
 	{ PR_1_BAD_I_BLOCKS,
-	  "@i %i, i_blocks is %Ib, @s %N.  ",
-		  PROMPT_FIX, PR_PREEN_OK },
+	  "@i %i, i_@bs is %Ib, @s %N.  ",
+	  PROMPT_FIX, PR_PREEN_OK },
 
 	/* Illegal block number in inode */
 	{ PR_1_ILLEGAL_BLOCK_NUM,
@@ -155,7 +236,7 @@
 
 	/* Block number overlaps fs metadata */
 	{ PR_1_BLOCK_OVERLAPS_METADATA,
-	  "@b #%B (%b) overlaps filesystem metadata in @i %i.  ",
+	  "@b #%B (%b) overlaps @f metadata in @i %i.  ",
 	  PROMPT_CLEAR, PR_LATCH_BLOCK },
 
 	/* Inode has illegal blocks (latch question) */
@@ -178,26 +259,230 @@
 	  "Bad @b @i has illegal @b(s).  ",
 	  PROMPT_CLEAR, 0 },
 
+	/* Duplicate or bad blocks in use! */
+	{ PR_1_DUP_BLOCKS_PREENSTOP,
+	  "Duplicate or bad @b in use!\n",
+	  PROMPT_NONE, 0 },
+
+	/* Bad block used as bad block indirect block */	  
+	{ PR_1_BBINODE_BAD_METABLOCK,
+	  "Bad @b %b used as bad @b indirect @b?!?\n",
+	  PROMPT_NONE, PR_AFTER_CODE, PR_1_BBINODE_BAD_METABLOCK_PROMPT },
+
+	/* Inconsistency can't be fixed prompt */	  
+	{ PR_1_BBINODE_BAD_METABLOCK_PROMPT,
+	  "\nThis inconsistency can not be fixed with e2fsck; to fix it, use\n"
+	  """dumpe2fs -b"" to dump out the bad @b "
+	  "list and ""e2fsck -L filename""\n"
+	  "to read it back in again.\n",
+	  PROMPT_CONTINUE, PR_PREEN_NOMSG },
+
+	/* Bad primary block */
+	{ PR_1_BAD_PRIMARY_BLOCK,  
+	  "\nIf the @b is really bad, the @f can not be fixed.\n",
+	  PROMPT_NONE, PR_AFTER_CODE, PR_1_BAD_PRIMARY_BLOCK_PROMPT },
+		  
+	/* Bad primary block prompt */
+	{ PR_1_BAD_PRIMARY_BLOCK_PROMPT,	  
+	  "You can clear the this @b (and hope for the best) from the\n"
+	  "bad @b list and hope that @b is really OK, but there are no\n"
+	  "guarantees.\n\n",
+	  PROMPT_CLEAR, PR_PREEN_NOMSG },
+
+	/* Bad primary superblock */
+	{ PR_1_BAD_PRIMARY_SUPERBLOCK,
+	  "The primary @S (%b) is on the bad @b list.\n",
+	  PROMPT_NONE, PR_AFTER_CODE, PR_1_BAD_PRIMARY_BLOCK },
+		  
+	/* Bad primary block group descriptors */
+	{ PR_1_BAD_PRIMARY_GROUP_DESCRIPTOR,
+	  "Block %b in the primary @g descriptors "
+	  "is on the bad block list\n",
+	  PROMPT_NONE, PR_AFTER_CODE, PR_1_BAD_PRIMARY_BLOCK },
+		  
+	/* Bad superblock in group */
+	{ PR_1_BAD_SUPERBLOCK,
+	  "Warning: Group %g's @S (%b) is bad.\n",
+	  PROMPT_NONE, PR_PREEN_OK | PR_PREEN_NOMSG },
+		  
+	/* Bad block group descriptors in group */
+	{ PR_1_BAD_GROUP_DESCRIPTORS,
+	  "Warning: Group %d's copy of the @g descriptors has a bad "
+	  "@b (%b).\n",
+	  PROMPT_NONE, PR_PREEN_OK | PR_PREEN_NOMSG },
+
+	/* Block claimed for no reason */	  
+	{ PR_1_PROGERR_CLAIMED_BLOCK,
+	  "Programming error?  @b #%b claimed for no reason in "
+	  "process_bad_@b.\n",
+	  PROMPT_NONE, PR_PREEN_OK },
+
+	/* Could not allocate blocks for relocating metadata */
+	{ PR_1_RELOC_BLOCK_ALLOCATE,
+	  "Could not allocate %N @b(s) for %s: %m\n",
+	  PROMPT_NONE, PR_PREEN_OK },
+		
+	/* Could not allocate memory during relocation process */
+	{ PR_1_RELOC_MEMORY_ALLOCATE,
+	  "Could not allocate @b buffer for relocating %s\n",
+	  PROMPT_NONE, PR_PREEN_OK },
+		
+	/* Relocating metadata group information from X to Y */	
+	{ PR_1_RELOC_FROM_TO,
+	  "Relocating @g %g's %s from %b to %c...\n",
+	  PROMPT_NONE, PR_PREEN_OK },
+		
+	/* Relocating metatdata group information to X */
+	{ PR_1_RELOC_TO,
+	  "Relocating @g %g's %s to %c...\n",
+	  PROMPT_NONE, PR_PREEN_OK },
+		
+	/* Block read error during relocation process */
+	{ PR_1_RELOC_READ_ERR,
+	  "Warning: could not read @b %b of %s: %m\n",
+	  PROMPT_NONE, PR_PREEN_OK },
+		
+	/* Block write error during relocation process */
+	{ PR_1_RELOC_WRITE_ERR,
+	  "Warning: could not write @b %b for %s: %m\n",
+	  PROMPT_NONE, PR_PREEN_OK },
+
+	/* Error allocating inode bitmap */
+	{ PR_1_ALLOCATE_IBITMAP_ERROR,
+	  "Error allocating @i @B (%N): %m\n",
+	  PROMPT_NONE, PR_FATAL },
+
+	/* Error allocating block bitmap */
+	{ PR_1_ALLOCATE_BBITMAP_ERROR,
+	  "Error allocating @b @B (%N): %m\n",
+	  PROMPT_NONE, PR_FATAL },
+
+	/* Error allocating icount structure */
+	{ PR_1_ALLOCATE_ICOUNT,
+	  "Error allocating icount link information: %m\n",
+	  PROMPT_NONE, PR_FATAL },
+
+	/* Error allocating dbcount */
+	{ PR_1_ALLOCATE_DBCOUNT,
+	  "Error allocating directory @b array: %m\n",
+	  PROMPT_NONE, PR_FATAL },
+
+	/* Error while scanning inodes */
+	{ PR_1_ISCAN_ERROR,
+	  "Error while scanning inodes (%i): %m\n",
+	  PROMPT_NONE, PR_FATAL },
+
+	/* Error while iterating over blocks */
+	{ PR_1_BLOCK_ITERATE,
+	  "Error while iterating over blocks in inode %i: %m\n",
+	  PROMPT_NONE, PR_FATAL },
+
+	/* Error while storing inode count information */	  
+	{ PR_1_ICOUNT_STORE,
+	  "Error storing inode count information (inode=%i, count=%N): %m\n",
+	  PROMPT_NONE, PR_FATAL },
+
+	/* Error while storing directory block information */	  
+	{ PR_1_ADD_DBLOCK,
+	  "Error storing dir block information "
+	  "(inode=%i, block=%b, num=%N): %m\n",
+	  PROMPT_NONE, PR_FATAL },
+
+	/* Error while reading inode (for clearing) */	  
+	{ PR_1_READ_INODE,
+	  "Error reading inode %i: %m\n",
+	  PROMPT_NONE, PR_FATAL },
+		  
 	/* Pass 1b errors */
 
+	/* Pass 1B: Rescan for duplicate/bad blocks */
+	{ PR_1B_PASS_HEADER,
+	  "Duplicate @bs found... invoking duplicate @b passes.\n"
+	  "Pass 1B: Rescan for duplicate/bad @bs\n",
+	  PROMPT_NONE, 0 },
+
+	/* Duplicate/bad block(s) header */
+	{ PR_1B_DUP_BLOCK_HEADER,	  
+	  "Duplicate/bad @b(s) in @i %i:",
+	  PROMPT_NONE, 0 },
+
+	/* Duplicate/bad block(s) in inode */
+	{ PR_1B_DUP_BLOCK,	  
+	  " %b",
+	  PROMPT_NONE, PR_LATCH_DBLOCK },
+
+	/* Duplicate/bad block(s) end */
+	{ PR_1B_DUP_BLOCK_END,
+	  "\n",
+	  PROMPT_NONE, 0 },
+		  
+	/* Error while scanning inodes */
+	{ PR_1B_ISCAN_ERROR,
+	  "Error while scanning inodes (%i): %m\n",
+	  PROMPT_NONE, PR_FATAL },
+
+	/* Error allocating inode bitmap */
+	{ PR_1B_ALLOCATE_IBITMAP_ERROR,
+	  "Error allocating @i @B (inode_dup_map): %m\n",
+	  PROMPT_NONE, PR_FATAL },
+
+
+	/* Pass 1C: Scan directories for inodes with dup blocks. */
+	{ PR_1C_PASS_HEADER,
+	  "Pass 1C: Scan directories for @is with dup @bs.\n",
+	  PROMPT_NONE, 0 },
+
+		  
+	/* Pass 1D: Reconciling duplicate blocks */
+	{ PR_1D_PASS_HEADER,
+	  "Pass 1D: Reconciling duplicate @bs\n",
+	  PROMPT_NONE, 0 },
+		  
 	/* File has duplicate blocks */
-	{ PR_1B_DUP_FILE,
+	{ PR_1D_DUP_FILE,
 	  "File %Q (@i #%i, mod time %IM) \n"
 	  "  has %B duplicate @b(s), shared with %N file(s):\n",
-	  PROMPT_FIX, PR_MSG_ONLY },
+	  PROMPT_NONE, 0 },
 		  
 	/* List of files sharing duplicate blocks */	
-	{ PR_1B_DUP_FILE_LIST,
+	{ PR_1D_DUP_FILE_LIST,
 	  "\t%Q (@i #%i, mod time %IM)\n",
-	  PROMPT_FIX, PR_MSG_ONLY },
+	  PROMPT_NONE, 0 },
 	  
 	/* File sharing blocks with filesystem metadata  */	
-	{ PR_1B_SHARE_METADATA,
-	  "\t<filesystem metadata>\n",
-	  PROMPT_FIX, PR_MSG_ONLY },
+	{ PR_1D_SHARE_METADATA,
+	  "\t<@f metadata>\n",
+	  PROMPT_NONE, 0 },
+
+	/* Report of how many duplicate/bad inodes */	
+	{ PR_1D_NUM_DUP_INODES,
+	  "(There are %N @is containing duplicate/bad @bs.)\n\n",
+	  PROMPT_NONE, 0 },
+
+	/* Duplicated blocks already reassigned or cloned. */
+	{ PR_1D_DUP_BLOCKS_DEALT,
+	  "Duplicated @bs already reassigned or cloned.\n\n",
+	  PROMPT_NONE, 0 },
+
+	/* Clone duplicate/bad blocks? */
+	{ PR_1D_CLONE_QUESTION,
+	  "", PROMPT_CLONE, PR_NO_OK },
+		  
+	/* Delete file? */
+	{ PR_1D_DELETE_QUESTION,
+	  "", PROMPT_DELETE, 0 },
+
+	/* Couldn't clone file (error) */
+	{ PR_1D_CLONE_ERROR,
+	  "Couldn't clone file: %m\n", PROMPT_NONE, 0 },
 
 	/* Pass 2 errors */
 
+	/* Pass 2: Checking directory structure */
+	{ PR_2_PASS_HEADER,
+	  "Pass 2: Checking @d structure\n",
+	  PROMPT_NONE, 0 },
+		  
 	/* Bad inode number for '.' */
 	{ PR_2_BAD_INODE_DOT,
 	  "Bad @i number for '.' in @d @i %i.\n",
@@ -323,8 +608,64 @@
 	  "@i %i (%Q) is an @I @b device.\n",
 	  PROMPT_CLEAR, 0 },
 
-	  /* Pass 3 errors */
+	/* Duplicate '.' entry */
+	{ PR_2_DUP_DOT,
+	  "@E is duplicate '.' @e.\n",
+	  PROMPT_FIX, 0 },	  
 
+	/* Duplicate '..' entry */
+	{ PR_2_DUP_DOT_DOT,
+	  "@E is duplicate '..' @e.\n",
+	  PROMPT_FIX, 0 },
+
+	/* Internal error: couldn't find dir_info */
+	{ PR_2_NO_DIRINFO,
+	  "Internal error: couldn't find dir_info for %i.\n",
+	  PROMPT_NONE, PR_FATAL },
+
+	/* Final rec_len is wrong */
+	{ PR_2_FINAL_RECLEN,
+	  "@E has rec_len of %dr, should be %N.\n",
+	  PROMPT_FIX, 0 },
+		  
+	/* Error allocating icount structure */
+	{ PR_2_ALLOCATE_ICOUNT,
+	  "Error allocating icount structure: %m\n",
+	  PROMPT_NONE, PR_FATAL },
+
+	/* Error iterating over directory blocks */
+	{ PR_2_DBLIST_ITERATE,
+	  "Error interating over directory blocks: %m\n",
+	  PROMPT_NONE, PR_FATAL },
+
+	/* Error reading directory block */
+	{ PR_2_READ_DIRBLOCK,
+	  "Error reading directory block %b (inode %i): %m\n",
+	  PROMPT_CONTINUE, 0 },
+
+	/* Error writing directory block */
+	{ PR_2_WRITE_DIRBLOCK,
+	  "Error writing directory block %b (inode %i): %m\n",
+	  PROMPT_CONTINUE, 0 },
+
+	/* Error allocating new directory block */
+	{ PR_2_ALLOC_DIRBOCK,
+	  "Error allocating new directory block for inode %i (%s): %m\n",
+	  PROMPT_NONE, 0 },
+
+	/* Error deallocating inode */
+	{ PR_2_DEALLOC_INODE,
+	  "Error deallocating inode %i: %m\n",
+	  PROMPT_NONE, PR_FATAL },
+
+
+	/* Pass 3 errors */
+
+	/* Pass 3: Checking directory connectivity */
+	{ PR_3_PASS_HEADER,
+	  "Pass 3: Checking @d connectivity\n",
+	  PROMPT_NONE, 0 },
+		  
 	/* Root inode not allocated */
 	{ PR_3_NO_ROOT_INODE,
 	  "@r not allocated.  ",
@@ -350,8 +691,83 @@
 	  "'..' in %Q (%i) is %P (%j), @s %q (%d).\n",
 	  PROMPT_FIX, 0 },
 
+	/* Bad or non-existent /lost+found.  Cannot reconnect */
+	{ PR_3_NO_LPF,
+	  "Bad or non-existent /@l.  Cannot reconnect\n",
+	  PROMPT_NONE, 0 },
+
+	/* Could not expand /lost+found */
+	{ PR_3_CANT_EXPAND_LPF,
+	  "Could not expand /@l: %m\n",
+	  PROMPT_NONE, 0 },
+
+	/* Could not reconnect inode */
+	{ PR_3_CANT_RECONNECT,
+	  "Could not reconnect %i: %m\n",
+	  PROMPT_NONE, 0 },
+
+	/* Error while trying to find /lost+found */
+	{ PR_3_ERR_FIND_LPF,
+	  "Error while trying to find /@l: %m\n",
+	  PROMPT_NONE, 0 },
+
+	/* Error in ext2fs_new_block while creating /lost+found */
+	{ PR_3_ERR_LPF_NEW_BLOCK, 
+	  "ext2fs_new_@b: %m while trying to create /@l @d\n",
+	  PROMPT_NONE, 0 },
+		  
+	/* Error in ext2fs_new_inode while creating /lost+found */
+	{ PR_3_ERR_LPF_NEW_INODE,
+	  "ext2fs_new_@i: %m while trying to create /@l @d\n",
+	  PROMPT_NONE, 0 },
+
+	/* Error in ext2fs_new_dir_block while creating /lost+found */	  
+	{ PR_3_ERR_LPF_NEW_DIR_BLOCK,
+	  "ext2fs_new_dir_@b: %m while creating new @d @b\n",
+	  PROMPT_NONE, 0 },
+		  
+	/* Error while writing directory block for /lost+found */
+	{ PR_3_ERR_LPF_WRITE_BLOCK,
+	  "ext2fs_write_dir_@b: %m while writing the @d @b for /@l\n",
+	  PROMPT_NONE, 0 },
+
+	/* Error while adjusting inode count */
+	{ PR_3_ADJUST_INODE,
+	  "Error while adjusting @i count on @i %i\n",
+	  PROMPT_NONE, 0 },
+
+	/* Couldn't fix parent directory -- error */
+	{ PR_3_FIX_PARENT_ERR,
+	  "Couldn't fix parent of @i %i: %m\n\n",
+	  PROMPT_NONE, 0 },
+
+	/* Couldn't fix parent directory -- couldn't find it */	  
+	{ PR_3_FIX_PARENT_NOFIND,
+	  "Couldn't fix parent of @i %i: Couldn't find parent @d entry\n\n",
+	  PROMPT_NONE, 0 },
+
+	/* Error allocating inode bitmap */
+	{ PR_3_ALLOCATE_IBITMAP_ERROR,
+	  "Error allocating @i @B (%N): %m\n",
+	  PROMPT_NONE, PR_FATAL },
+
+	/* Error creating root directory */
+	{ PR_3_CREATE_ROOT_ERROR,
+	  "Error creating root @d (%s): %m\n",
+	  PROMPT_NONE, PR_FATAL },	  
+
+	/* Error creating lost and found directory */
+	{ PR_3_CREATE_LPF_ERROR,
+	  "Error creating /@l @d (%s): %m\n",
+	  PROMPT_NONE, PR_FATAL },  
+
 	/* Pass 4 errors */
 	
+	/* Pass 4: Checking reference counts */
+	{ PR_4_PASS_HEADER,
+	  "Pass 4: Checking reference counts\n",
+	  PROMPT_NONE, 0 },
+		  
 	/* Unattached zero-length inode */
 	{ PR_4_ZERO_LEN_INODE,
 	  "@u @z @i %i.  ",
@@ -366,7 +782,107 @@
 	{ PR_4_BAD_REF_COUNT,
 	  "@i %i ref count is %Il, @s %N.  ",
 	  PROMPT_FIX, PR_PREEN_OK },
-	
+
+	{ PR_4_INCONSISTENT_COUNT,
+	  "WARNING: PROGRAMMING BUG IN E2FSCK!\n"
+	  "\tOR SOME BONEHEAD (YOU) IS CHECKING A MOUNTED (LIVE) FILESYSTEM.\n"
+	  "@i_link_info[%i] is %N, @i.i_links_count is %Il.  "
+	  "They should be the same!\n",
+	  PROMPT_NONE, 0 },
+
+	/* Pass 5 errors */
+		  
+	/* Pass 5: Checking group summary information */
+	{ PR_5_PASS_HEADER,
+	  "Pass 5: Checking @g summary information\n",
+	  PROMPT_NONE, 0 },
+		  
+	/* Padding at end of inode bitmap is not set. */
+	{ PR_5_INODE_BMAP_PADDING,
+	  "Padding at end of @i @B is not set. ",
+	  PROMPT_FIX, PR_PREEN_OK },
+		  
+	/* Padding at end of block bitmap is not set. */
+	{ PR_5_BLOCK_BMAP_PADDING,
+	  "Padding at end of @b @B is not set. ",
+	  PROMPT_FIX, PR_PREEN_OK },
+		
+	/* Block bitmap differences header */
+	{ PR_5_BLOCK_BITMAP_HEADER,
+	  "@b @B differences: ",
+	  PROMPT_NONE, PR_PREEN_OK | PR_PREEN_NOMSG},
+
+	/* Block not used, but marked in bitmap */
+	{ PR_5_UNUSED_BLOCK,
+	  " -%b",
+	  PROMPT_NONE, PR_LATCH_BBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
+		  
+	/* Block used, but not marked used in bitmap */
+	{ PR_5_BLOCK_USED,
+	  " +%b",
+	  PROMPT_NONE, PR_LATCH_BBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
+
+	/* Block bitmap differences end */	  
+	{ PR_5_BLOCK_BITMAP_END,
+	  "\n",
+	  PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
+
+	/* Inode bitmap differences header */
+	{ PR_5_INODE_BITMAP_HEADER,
+	  "@i @B differences: ",
+	  PROMPT_NONE, PR_PREEN_OK | PR_PREEN_NOMSG },
+
+	/* Inode not used, but marked in bitmap */
+	{ PR_5_UNUSED_INODE,
+	  " -%i",
+	  PROMPT_NONE, PR_LATCH_IBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
+		  
+	/* Inode used, but not marked used in bitmap */
+	{ PR_5_INODE_USED,
+	  " +%i",
+	  PROMPT_NONE, PR_LATCH_IBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
+
+	/* Inode bitmap differences end */	  
+	{ PR_5_INODE_BITMAP_END,
+	  "\n",
+	  PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
+
+	/* Free inodes count for group wrong */
+	{ PR_5_FREE_INODE_COUNT_GROUP,
+	  "Free @is count wrong for @g #%g (%i, counted=%j).\n",
+	  PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
+
+	/* Directories count for group wrong */
+	{ PR_5_FREE_DIR_COUNT_GROUP,
+	  "Directories count wrong for @g #%g (%i, counted=%j).\n",
+	  PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
+
+	/* Free inodes count wrong */
+	{ PR_5_FREE_INODE_COUNT,
+	  "Free @is count wrong (%i, counted=%j).\n",
+	  PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
+
+	/* Free blocks count for group wrong */
+	{ PR_5_FREE_BLOCK_COUNT_GROUP,
+	  "Free @bs count wrong for @g #%g (%b, counted=%c).\n",
+	  PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
+
+	/* Free blocks count wrong */
+	{ PR_5_FREE_BLOCK_COUNT,
+	  "Free @bs count wrong (%b, counted=%c).\n",
+	  PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
+
+	/* Programming error: bitmap endpoints don't match */
+	{ PR_5_BMAP_ENDPOINTS,
+	  "PROGRAMMING ERROR: @f (#%N) @B endpoints (%b, %c) don't "
+	  "match calculated @B endpoints (%i, %j)\n",
+	  PROMPT_NONE, PR_FATAL },
+
+	/* Internal error: fudging end of bitmap */
+	{ PR_5_FUDGE_BITMAP_ERROR,
+	  "Internal error: fudging end of bitmap (%N)\n",
+	  PROMPT_NONE, PR_FATAL },	  
+	  
 	{ 0 }
 };
 
@@ -376,14 +892,17 @@
  * question for the set of problems, and all of the associated
  * problems will be either fixed or not fixed.
  */
-char pr_latch[7];		/* Latch flags register */
-char pr_suppress[7];		/* Latch groups which are suppressed */
-int latch_question[7] = {
-	PR_1_INODE_BLOCK_LATCH,
-	PR_1_INODE_BBLOCK_LATCH	
+static struct latch_descr pr_latch_info[] = {
+	{ PR_LATCH_BLOCK, PR_1_INODE_BLOCK_LATCH, 0 },
+	{ PR_LATCH_BBLOCK, PR_1_INODE_BBLOCK_LATCH, 0 },
+	{ PR_LATCH_IBITMAP, PR_5_INODE_BITMAP_HEADER, PR_5_INODE_BITMAP_END },
+	{ PR_LATCH_BBITMAP, PR_5_BLOCK_BITMAP_HEADER, PR_5_BLOCK_BITMAP_END },
+	{ PR_LATCH_RELOC, PR_0_RELOCATE_HINT, 0 },
+	{ PR_LATCH_DBLOCK, PR_1B_DUP_BLOCK_HEADER, PR_1B_DUP_BLOCK_END },
+	{ -1, 0, 0 },
 };
 
-static struct e2fsck_problem *find_problem(int code)
+static const struct e2fsck_problem *find_problem(int code)
 {
 	int 	i;
 
@@ -394,15 +913,53 @@
 	return 0;
 }
 
-void reset_problem_latch(int mask)
+static struct latch_descr *find_latch(int code)
 {
-	pr_latch[PR_LATCH(mask)] = 0;
-	pr_suppress[PR_LATCH(mask)] = 0;
+	int	i;
+
+	for (i=0; pr_latch_info[i].latch_code >= 0; i++) {
+		if (pr_latch_info[i].latch_code == code)
+			return &pr_latch_info[i];
+	}
+	return 0;
 }
 
-void suppress_latch_group(int mask, int value)
+int end_problem_latch(e2fsck_t ctx, int mask)
 {
-	pr_suppress[PR_LATCH(mask)] = value;
+	struct latch_descr *ldesc;
+	struct problem_context pctx;
+	int answer = -1;
+	
+	ldesc = find_latch(mask);
+	if (ldesc->end_message && (ldesc->flags & PRL_LATCHED)) {
+		clear_problem_context(&pctx);
+		answer = fix_problem(ctx, ldesc->end_message, &pctx);
+	}
+	ldesc->flags &= ~(PRL_VARIABLE);
+	return answer;
+}
+
+int set_latch_flags(int mask, int setflags, int clearflags)
+{
+	struct latch_descr *ldesc;
+
+	ldesc = find_latch(mask);
+	if (!ldesc)
+		return -1;
+	ldesc->flags |= setflags;
+	ldesc->flags &= ~clearflags;
+	return 0;
+}
+
+int get_latch_flags(int mask, int *value)
+{
+	struct latch_descr *ldesc;
+
+	ldesc = find_latch(mask);
+	if (!ldesc)
+		return -1;
+	*value = ldesc->flags;
+	return 0;
 }
 
 void clear_problem_context(struct problem_context *ctx)
@@ -412,11 +969,13 @@
 	ctx->group = -1;
 }
 
-int fix_problem(ext2_filsys fs, int code, struct problem_context *ctx)
+int fix_problem(e2fsck_t ctx, problem_t code, struct problem_context *pctx)
 {
-	struct e2fsck_problem *ptr;
-	int 		def_yn, answer;
-	int		latch;
+	ext2_filsys fs = ctx->fs;
+	const struct e2fsck_problem *ptr;
+	struct latch_descr *ldesc = 0;
+	const char *message;
+	int 		def_yn, answer, ans;
 	int		print_answer = 0;
 	int		suppress = 0;
 
@@ -432,45 +991,72 @@
 	 * latch question, if it exists
 	 */
 	if (ptr->flags & PR_LATCH_MASK) {
-		latch = PR_LATCH(ptr->flags);
-		if (latch_question[latch] && !pr_latch[latch])
-			pr_latch[latch] = fix_problem(fs,
-						      latch_question[latch],
-						      ctx) + 1;
-		if (pr_suppress[latch])
+		ldesc = find_latch(ptr->flags & PR_LATCH_MASK);
+		if (ldesc->question && !(ldesc->flags & PRL_LATCHED)) {
+			ans = fix_problem(ctx, ldesc->question, pctx);
+			if (ans == 1)
+				ldesc->flags |= PRL_YES;
+			if (ans == 0)
+				ldesc->flags |= PRL_NO;
+			ldesc->flags |= PRL_LATCHED;
+		}
+		if (ldesc->flags & PRL_SUPPRESS)
 			suppress++;
 	}
-
+	if ((ptr->flags & PR_PREEN_NOMSG) &&
+	    (ctx->options & E2F_OPT_PREEN))
+		suppress++;
 	if (!suppress) {
-		if (preen)
-			printf("%s: ", device_name);
-		print_e2fsck_message(fs, ptr->e2p_description, ctx, 1);
+		message = ptr->e2p_description;
+		if (ctx->options & E2F_OPT_PREEN) {
+			printf("%s: ", ctx->device_name);
+#if 0
+			if (ptr->e2p_preen_msg)
+				message = ptr->e2p_preen_msg;
+#endif
+		}
+		print_e2fsck_message(ctx, message, pctx, 1);
 	}
-	if (!(ptr->flags & PR_PREEN_OK))
-		preenhalt(fs);
+	if (!(ptr->flags & PR_PREEN_OK) && (ptr->prompt != PROMPT_NONE))
+		preenhalt(ctx);
 
-	if (ptr->flags & PR_MSG_ONLY)
-		return 1;
-	
-	if (preen) {
-		answer = def_yn;
-		print_answer = 1;
-	} else if (ptr->flags & PR_LATCH_MASK) {
-		latch = PR_LATCH(ptr->flags);
-		if (!pr_latch[latch])
-			pr_latch[latch] =
-				ask(prompt[(int) ptr->prompt], def_yn) + 1;
+	if (ptr->flags & PR_FATAL)
+		fatal_error(0);
+
+	if (ptr->prompt == PROMPT_NONE) {
+		if (ptr->flags & PR_NOCOLLATE)
+			answer = -1;
 		else
-			print_answer = 1;
-		answer = pr_latch[latch] - 1;
-	} else
-		answer = ask(prompt[(int) ptr->prompt], def_yn);
-	if (!answer && !(ptr->flags & PR_NO_OK))
-		ext2fs_unmark_valid(fs);
+			answer = def_yn;
+	} else {
+		if (ctx->options & E2F_OPT_PREEN) {
+			answer = def_yn;
+			if (!(ptr->flags & PR_PREEN_NOMSG))
+				print_answer = 1;
+		} else if ((ptr->flags & PR_LATCH_MASK) &&
+			   (ldesc->flags & (PRL_YES | PRL_NO))) {
+			if (!suppress)
+				print_answer = 1;
+			if (ldesc->flags & PRL_YES)
+				answer = 1;
+			else
+				answer = 0;
+		} else
+			answer = ask(ctx, prompt[(int) ptr->prompt], def_yn);
+		if (!answer && !(ptr->flags & PR_NO_OK))
+			ext2fs_unmark_valid(fs);
 	
-	if (print_answer)
-		printf("%s.\n",
-		       answer ? preen_msg[(int) ptr->prompt] : "IGNORED");
+		if (print_answer)
+			printf("%s.\n", answer ?
+			       preen_msg[(int) ptr->prompt] : "IGNORED");
 	
+	}
+
+	if (ptr->flags & PR_AFTER_CODE)
+		(void) fix_problem(ctx, ptr->second_code, pctx);
+
+	if (ptr->prompt == PROMPT_ABORT)
+		fatal_error(0);
+
 	return answer;
 }
diff --git a/e2fsck/problem.h b/e2fsck/problem.h
index 5e1fd9b..3fbbb2a 100644
--- a/e2fsck/problem.h
+++ b/e2fsck/problem.h
@@ -9,26 +9,43 @@
  * %End-Header%
  */
 
+typedef __u32 problem_t;
+
 struct problem_context {
+	errcode_t	errcode;
 	ino_t ino, ino2, dir;
 	struct ext2_inode *inode;
 	struct ext2_dir_entry *dirent;
-	blk_t	blk;
+	blk_t	blk, blk2;
 	int	blkcount, group;
 	__u32	num;
+	const char *str;
 };
 
 struct e2fsck_problem {
-	int		e2p_code;
+	problem_t	e2p_code;
 	const char *	e2p_description;
 	char		prompt;
 	short		flags;
+	problem_t	second_code;
+};
+
+struct latch_descr {
+	int		latch_code;
+	problem_t	question;
+	problem_t	end_message;
+	int		flags;
 };
 
 #define PR_PREEN_OK	0x0001	/* Don't need to do preenhalt */
 #define PR_NO_OK	0x0002	/* If user answers no, don't make fs invalid */
 #define PR_NO_DEFAULT	0x0004	/* Default to no */
 #define PR_MSG_ONLY	0x0008	/* Print message only */
+#define PR_FATAL	0x0080	/* Fatal error */
+#define PR_AFTER_CODE	0x0100	/* After asking the first question, */
+				/* ask another */
+#define PR_PREEN_NOMSG	0x0200	/* Don't print a message if we're preening */
+#define PR_NOCOLLATE	0x0400	/* Don't collate answers for this latch */
 
 /*
  * We define a set of "latch groups"; these are problems which are
@@ -38,10 +55,24 @@
 #define PR_LATCH_MASK	0x0070  /* Latch mask */
 #define PR_LATCH_BLOCK	0x0010	/* Latch for illegal blocks (pass 1) */
 #define PR_LATCH_BBLOCK	0x0020	/* Latch for bad block inode blocks (pass 1) */
+#define PR_LATCH_IBITMAP 0x0030 /* Latch for pass 5 inode bitmap proc. */
+#define PR_LATCH_BBITMAP 0x0040 /* Latch for pass 5 inode bitmap proc. */
+#define PR_LATCH_RELOC	0x0050  /* Latch for superblock relocate hint */
+#define PR_LATCH_DBLOCK	0x0060	/* Latch for pass 1b dup block headers */
 
 #define PR_LATCH(x)	((((x) & PR_LATCH_MASK) >> 4) - 1)
 
 /*
+ * Latch group descriptor flags
+ */
+#define PRL_YES		0x0001	/* Answer yes */
+#define PRL_NO		0x0002	/* Answer no */
+#define PRL_LATCHED	0x0004	/* The latch group is latched */
+#define PRL_SUPPRESS	0x0008	/* Suppress all latch group questions */
+
+#define PRL_VARIABLE	0x000f	/* All the flags that need to be reset */
+
+/*
  * Pre-Pass 1 errors
  */
 
@@ -54,84 +85,234 @@
 /* Inode table not in group */
 #define PR_0_ITABLE_NOT_GROUP	0x000003
 
+/* Superblock corrupt */
+#define PR_0_SB_CORRUPT		0x000004
+
+/* Filesystem size is wrong */
+#define PR_0_FS_SIZE_WRONG	0x000005
+
+/* Fragments not supported */
+#define PR_0_NO_FRAGMENTS	0x000006
+
+/* Bad blocks_per_group */
+#define PR_0_BLOCKS_PER_GROUP	0x000007
+
+/* Bad first_data_block */
+#define PR_0_FIRST_DATA_BLOCK	0x000008
+
+/* Adding UUID to filesystem */
+#define PR_0_ADD_UUID		0x000009
+
+/* Relocate hint */	
+#define PR_0_RELOCATE_HINT	0x00000A
+
+/* Miscellaneous superblock corruption */
+#define PR_0_MISC_CORRUPT_SUPER	0x00000B
+	
+/* Error determing physical device size of filesystem */
+#define PR_0_GETSIZE_ERROR	0x00000C
+
 /*
  * Pass 1 errors
  */
 
+/* Pass 1: Checking inodes, blocks, and sizes */
+#define PR_1_PASS_HEADER		0x010000
+
 /* Root directory is not an inode */
-#define PR_1_ROOT_NO_DIR	0x010001
+#define PR_1_ROOT_NO_DIR		0x010001
 
 /* Root directory has dtime set */
-#define PR_1_ROOT_DTIME		0x010002
+#define PR_1_ROOT_DTIME			0x010002
 
 /* Reserved inode has bad mode */
-#define PR_1_RESERVED_BAD_MODE	0x010003
+#define PR_1_RESERVED_BAD_MODE		0x010003
 
 /* Deleted inode has zero dtime */
-#define PR_1_ZERO_DTIME		0x010004
+#define PR_1_ZERO_DTIME			0x010004
 
 /* Inode in use, but dtime set */
-#define PR_1_SET_DTIME		0x010005
+#define PR_1_SET_DTIME			0x010005
 
 /* Zero-length directory */
-#define PR_1_ZERO_LENGTH_DIR	0x010006
+#define PR_1_ZERO_LENGTH_DIR		0x010006
 
 /* Block bitmap conflicts with some other fs block */
-#define PR_1_BB_CONFLICT	0x010007
+#define PR_1_BB_CONFLICT		0x010007
 
 /* Inode bitmap conflicts with some other fs block */
-#define PR_1_IB_CONFLICT	0x010008
+#define PR_1_IB_CONFLICT		0x010008
 
 /* Inode table conflicts with some other fs block */
-#define PR_1_ITABLE_CONFLICT	0x010009
+#define PR_1_ITABLE_CONFLICT		0x010009
 
 /* Block bitmap is on a bad block */
-#define PR_1_BB_BAD_BLOCK	0x01000A
+#define PR_1_BB_BAD_BLOCK		0x01000A
 
 /* Inode bitmap is on a bad block */
-#define PR_1_IB_BAD_BLOCK	0x01000B
+#define PR_1_IB_BAD_BLOCK		0x01000B
 
 /* Inode has incorrect i_size */
-#define PR_1_BAD_I_SIZE		0x01000C
+#define PR_1_BAD_I_SIZE			0x01000C
 
 /* Inode has incorrect i_blocks */
-#define PR_1_BAD_I_BLOCKS	0x01000D
+#define PR_1_BAD_I_BLOCKS		0x01000D
 
 /* Illegal block number in inode */
-#define PR_1_ILLEGAL_BLOCK_NUM	0x01000E
+#define PR_1_ILLEGAL_BLOCK_NUM		0x01000E
 
 /* Block number overlaps fs metadata */
 #define PR_1_BLOCK_OVERLAPS_METADATA	0x01000F
 
 /* Inode has illegal blocks (latch question) */
-#define PR_1_INODE_BLOCK_LATCH	0x010010
+#define PR_1_INODE_BLOCK_LATCH		0x010010
 
 /* Too many bad blocks in inode */
-#define	PR_1_TOO_MANY_BAD_BLOCKS 0x010011
+#define	PR_1_TOO_MANY_BAD_BLOCKS 	0x010011
 	
 /* Illegal block number in bad block inode */
-#define PR_1_BB_ILLEGAL_BLOCK_NUM 0x010012
+#define PR_1_BB_ILLEGAL_BLOCK_NUM 	0x010012
 
 /* Bad block inode has illegal blocks (latch question) */
-#define PR_1_INODE_BBLOCK_LATCH	0x010013
+#define PR_1_INODE_BBLOCK_LATCH		0x010013
+
+/* Duplicate or bad blocks in use! */
+#define PR_1_DUP_BLOCKS_PREENSTOP	0x010014
+	
+/* Bad block used as bad block indirect block */	  
+#define PR_1_BBINODE_BAD_METABLOCK	0x010015
+
+/* Inconsistency can't be fixed prompt */
+#define PR_1_BBINODE_BAD_METABLOCK_PROMPT 0x010016
+	
+/* Bad primary block */
+#define PR_1_BAD_PRIMARY_BLOCK		0x0100017
+		  
+/* Bad primary block prompt */
+#define PR_1_BAD_PRIMARY_BLOCK_PROMPT	0x0100018
+
+/* Bad primary superblock */
+#define PR_1_BAD_PRIMARY_SUPERBLOCK	0x0100019
+
+/* Bad primary block group descriptors */
+#define PR_1_BAD_PRIMARY_GROUP_DESCRIPTOR 0x010001A
+
+/* Bad superblock in group */
+#define PR_1_BAD_SUPERBLOCK		0x010001B
+
+/* Bad block group descriptors in group */
+#define PR_1_BAD_GROUP_DESCRIPTORS	0x010001C
+
+/* Block claimed for no reason */	  
+#define PR_1_PROGERR_CLAIMED_BLOCK	0x010001D
+
+/* Could not allocate blocks for relocating metadata */
+#define PR_1_RELOC_BLOCK_ALLOCATE	0x010001E
+		
+/* Could not allocate memory during relocation process */
+#define PR_1_RELOC_MEMORY_ALLOCATE	0x010001F
+		
+/* Relocating metadata group information from X to Y */	
+#define PR_1_RELOC_FROM_TO		0x0100020
+		
+/* Relocating metatdata group information to X */
+#define PR_1_RELOC_TO			0x0100021
+		
+/* Block read error during relocation process */
+#define PR_1_RELOC_READ_ERR		0x0100022
+		
+/* Block write error during relocation process */
+#define PR_1_RELOC_WRITE_ERR		0x0100023
+
+/* Error allocating inode bitmap */
+#define PR_1_ALLOCATE_IBITMAP_ERROR	0x0100024
+
+/* Error allocating block bitmap */
+#define PR_1_ALLOCATE_BBITMAP_ERROR	0x0100025
+
+/* Error allocating icount structure */
+#define PR_1_ALLOCATE_ICOUNT		0x0100026
+	
+/* Error allocating dbcount */
+#define PR_1_ALLOCATE_DBCOUNT		0x0100027
+
+/* Error while scanning inodes */
+#define PR_1_ISCAN_ERROR		0x0100028
+
+/* Error while iterating over blocks */
+#define PR_1_BLOCK_ITERATE		0x0100029
+
+/* Error while storing inode count information */	  
+#define PR_1_ICOUNT_STORE		0x010002A
+
+/* Error while storing directory block information */	  
+#define PR_1_ADD_DBLOCK			0x010002B
+
+/* Error while reading inode (for clearing) */
+#define PR_1_READ_INODE			0x010002C
+
 
 /*
  * Pass 1b errors
  */
 
+/* Pass 1B: Rescan for duplicate/bad blocks */
+#define PR_1B_PASS_HEADER	0x011000
+
+/* Duplicate/bad block(s) header */
+#define PR_1B_DUP_BLOCK_HEADER	0x011001
+
+/* Duplicate/bad block(s) in inode */
+#define PR_1B_DUP_BLOCK		0x011002
+
+/* Duplicate/bad block(s) end */
+#define PR_1B_DUP_BLOCK_END	0x011003
+	
+/* Error while scanning inodes */
+#define PR_1B_ISCAN_ERROR	0x011004
+
+/* Error allocating inode bitmap */
+#define PR_1B_ALLOCATE_IBITMAP_ERROR 0x011005
+
+
+/* Pass 1C: Scan directories for inodes with dup blocks. */
+#define PR_1C_PASS_HEADER	0x012000
+
+
+/* Pass 1D: Reconciling duplicate blocks */
+#define PR_1D_PASS_HEADER	0x013000
+
 /* File has duplicate blocks */
-#define PR_1B_DUP_FILE		0x011001
+#define PR_1D_DUP_FILE		0x013001
 
 /* List of files sharing duplicate blocks */	
-#define PR_1B_DUP_FILE_LIST	0x011002
+#define PR_1D_DUP_FILE_LIST	0x013002
 
 /* File sharing blocks with filesystem metadata  */	
-#define PR_1B_SHARE_METADATA	0x011003
+#define PR_1D_SHARE_METADATA	0x013003
 
+/* Report of how many duplicate/bad inodes */	
+#define PR_1D_NUM_DUP_INODES	0x013004
+
+/* Duplicated blocks already reassigned or cloned. */
+#define PR_1D_DUP_BLOCKS_DEALT	0x013005
+
+/* Clone duplicate/bad blocks? */
+#define PR_1D_CLONE_QUESTION	0x013006
+
+/* Delete file? */
+#define PR_1D_DELETE_QUESTION	0x013007
+
+/* Couldn't clone file (error) */
+#define PR_1D_CLONE_ERROR	0x013008
+		
 /*
  * Pass 2 errors
  */
 
+/* Pass 2: Checking directory structure */
+#define PR_2_PASS_HEADER	0x020000
+
 /* Bad inode number for '.' */
 #define PR_2_BAD_INODE_DOT	0x020001
 
@@ -207,30 +388,107 @@
 /* Illegal block device in inode */
 #define PR_2_BAD_BLOCK_DEV	0x020019
 
+/* Duplicate '.' entry */
+#define PR_2_DUP_DOT		0x02001A
+
+/* Duplicate '..' entry */
+#define PR_2_DUP_DOT_DOT	0x02001B
+	
+/* Internal error: couldn't find dir_info */
+#define PR_2_NO_DIRINFO		0x02001C
+
+/* Final rec_len is wrong */
+#define PR_2_FINAL_RECLEN	0x02001D
+
+/* Error allocating icount structure */
+#define PR_2_ALLOCATE_ICOUNT	0x02001E
+
+/* Error iterating over directory blocks */
+#define PR_2_DBLIST_ITERATE	0x02001F
+
+/* Error reading directory block */
+#define PR_2_READ_DIRBLOCK	0x020020
+
+/* Error writing directory block */
+#define PR_2_WRITE_DIRBLOCK	0x020021
+
+/* Error allocating new directory block */
+#define PR_2_ALLOC_DIRBOCK	0x020022
+
+/* Error deallocating inode */
+#define PR_2_DEALLOC_INODE	0x020023
+
 /*
  * Pass 3 errors
  */
 
+/* Pass 3: Checking directory connectivity */
+#define PR_3_PASS_HEADER		0x030000
+
 /* Root inode not allocated */
-#define PR_3_NO_ROOT_INODE	0x030001
+#define PR_3_NO_ROOT_INODE		0x030001
 
 /* No room in lost+found */
-#define PR_3_EXPAND_LF_DIR	0x030002
+#define PR_3_EXPAND_LF_DIR		0x030002
 
 /* Unconnected directory inode */
-#define PR_3_UNCONNECTED_DIR	0x030003
+#define PR_3_UNCONNECTED_DIR		0x030003
 
 /* /lost+found not found */
-#define PR_3_NO_LF_DIR		0x030004
+#define PR_3_NO_LF_DIR			0x030004
 
 /* .. entry is incorrect */
-#define PR_3_BAD_DOT_DOT	0x030005
+#define PR_3_BAD_DOT_DOT		0x030005
 
+/* Bad or non-existent /lost+found.  Cannot reconnect */
+#define PR_3_NO_LPF			0x030006
+
+/* Could not expand /lost+found */
+#define PR_3_CANT_EXPAND_LPF		0x030007
+
+/* Could not reconnect inode */
+#define PR_3_CANT_RECONNECT		0x030008
+
+/* Error while trying to find /lost+found */
+#define PR_3_ERR_FIND_LPF		0x030009
+
+/* Error in ext2fs_new_block while creating /lost+found */
+#define PR_3_ERR_LPF_NEW_BLOCK		0x03000A
+
+/* Error in ext2fs_new_inode while creating /lost+found */
+#define PR_3_ERR_LPF_NEW_INODE		0x03000B
+
+/* Error in ext2fs_new_dir_block while creating /lost+found */	  
+#define PR_3_ERR_LPF_NEW_DIR_BLOCK	0x03000C
+		  
+/* Error while writing directory block for /lost+found */
+#define PR_3_ERR_LPF_WRITE_BLOCK	0x03000D
+
+/* Error while adjusting inode count */
+#define PR_3_ADJUST_INODE		0x03000E
+
+/* Couldn't fix parent directory -- error */
+#define PR_3_FIX_PARENT_ERR		0x03000F
+
+/* Couldn't fix parent directory -- couldn't find it */	  
+#define PR_3_FIX_PARENT_NOFIND		0x030010
+	
+/* Error allocating inode bitmap */
+#define PR_3_ALLOCATE_IBITMAP_ERROR	0x030011
+		  
+/* Error creating root directory */
+#define PR_3_CREATE_ROOT_ERROR		0x030012
+
+/* Error creating lost and found directory */
+#define PR_3_CREATE_LPF_ERROR		0x030013
 
 /*
  * Pass 4 errors
  */
 
+/* Pass 4: Checking reference counts */
+#define PR_4_PASS_HEADER	0x040000
+
 /* Unattached zero-length inode */
 #define PR_4_ZERO_LEN_INODE	0x040001
 
@@ -240,19 +498,77 @@
 /* Inode ref count wrong */
 #define PR_4_BAD_REF_COUNT	0x040003
 
+/* Inconsistent inode count information cached */
+#define PR_4_INCONSISTENT_COUNT	0x040004
+
 /*
  * Pass 5 errors
  */
 
+/* Pass 5: Checking group summary information */
+#define PR_5_PASS_HEADER		0x050000
+	
+/* Padding at end of inode bitmap is not set. */
+#define PR_5_INODE_BMAP_PADDING		0x050001
+
+/* Padding at end of block bitmap is not set. */
+#define PR_5_BLOCK_BMAP_PADDING		0x050002
+
+/* Block bitmap differences header */
+#define PR_5_BLOCK_BITMAP_HEADER 	0x050003
+
+/* Block not used, but marked in bitmap */
+#define PR_5_UNUSED_BLOCK		0x050004
+	  
+/* Block used, but not marked used in bitmap */
+#define PR_5_BLOCK_USED			0x050005
+
+/* Block bitmap differences end */	  
+#define PR_5_BLOCK_BITMAP_END		0x050006
+
+/* Inode bitmap differences header */
+#define PR_5_INODE_BITMAP_HEADER	0x050007
+
+/* Inode not used, but marked in bitmap */
+#define PR_5_UNUSED_INODE		0x050008
+	  
+/* Inode used, but not marked used in bitmap */
+#define PR_5_INODE_USED			0x050009
+
+/* Inode bitmap differences end */	  
+#define PR_5_INODE_BITMAP_END		0x05000A
+
+/* Free inodes count for group wrong */
+#define PR_5_FREE_INODE_COUNT_GROUP	0x05000B
+
+/* Directories count for group wrong */
+#define PR_5_FREE_DIR_COUNT_GROUP	0x05000C
+
+/* Free inodes count wrong */
+#define PR_5_FREE_INODE_COUNT	0x05000D
+
+/* Free blocks count for group wrong */
+#define PR_5_FREE_BLOCK_COUNT_GROUP	0x05000E
+
+/* Free blocks count wrong */
+#define PR_5_FREE_BLOCK_COUNT		0x05000F
+
+/* Programming error: bitmap endpoints don't match */
+#define PR_5_BMAP_ENDPOINTS		0x050010
+	
+/* Internal error: fudging end of bitmap */
+#define PR_5_FUDGE_BITMAP_ERROR		0x050011
+	
 /*
  * Function declarations
  */
-int fix_problem(ext2_filsys fs, int code, struct problem_context *ctx);
-void reset_problem_latch(int mask);
-void suppress_latch_group(int mask, int value);
+int fix_problem(e2fsck_t ctx, problem_t code, struct problem_context *pctx);
+int end_problem_latch(e2fsck_t ctx, int mask);
+int set_latch_flags(int mask, int setflags, int clearflags);
+int get_latch_flags(int mask, int *value);
 void clear_problem_context(struct problem_context *ctx);
 
 /* message.c */
-void print_e2fsck_message(ext2_filsys fs, const char *msg,
-			  struct problem_context *ctx, int first);
+void print_e2fsck_message(e2fsck_t ctx, const char *msg,
+			  struct problem_context *pctx, int first);
 
diff --git a/e2fsck/scantest.c b/e2fsck/scantest.c
index 29d1e88..f82e67f 100644
--- a/e2fsck/scantest.c
+++ b/e2fsck/scantest.c
@@ -72,7 +72,7 @@
 		((float) (tv1->tv_usec - tv2->tv_usec)) / 1000000);
 }
 
-void print_resource_track(struct resource_track *track)
+static void print_resource_track(struct resource_track *track)
 {
 	struct rusage r;
 	struct timeval time_end;
diff --git a/e2fsck/super.c b/e2fsck/super.c
new file mode 100644
index 0000000..d6a0c98
--- /dev/null
+++ b/e2fsck/super.c
@@ -0,0 +1,204 @@
+/*
+ * e2fsck.c - superblock checks
+ * 
+ * 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%
+ */
+
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#include <string.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <termios.h>
+#include <time.h>
+#ifdef HAVE_GETOPT_H
+#include <getopt.h>
+#endif
+#include <unistd.h>
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_MNTENT_H
+#include <mntent.h>
+#endif
+#include <sys/ioctl.h>
+#include <malloc.h>
+
+#include "uuid/uuid.h"
+#include "e2fsck.h"
+#include "problem.h"
+#include "../version.h"
+
+#define MIN_CHECK 1
+#define MAX_CHECK 2
+
+static void check_super_value(e2fsck_t ctx, const char *descr,
+			      unsigned long value, int flags,
+			      unsigned long min, unsigned long max)
+{
+	struct		problem_context pctx;
+
+	if (((flags & MIN_CHECK) && (value < min)) ||
+	    ((flags & MAX_CHECK) && (value > max))) {
+		clear_problem_context(&pctx);
+		pctx.num = value;
+		pctx.str = descr;
+		fix_problem(ctx, PR_0_MISC_CORRUPT_SUPER, &pctx);
+		fatal_error(0);	/* never get here! */
+	}
+}
+
+void check_super_block(e2fsck_t ctx)
+{
+	ext2_filsys fs = ctx->fs;
+	blk_t	first_block, last_block;
+	struct ext2fs_sb *s = (struct ext2fs_sb *) fs->super;
+	blk_t	blocks_per_group = fs->super->s_blocks_per_group;
+	int	i;
+	blk_t	should_be;
+	struct problem_context	pctx;
+
+	ctx->invalid_inode_bitmap_flag = allocate_memory(sizeof(int) *
+					       fs->group_desc_count,
+					       "invalid_inode_bitmap");
+	ctx->invalid_block_bitmap_flag = allocate_memory(sizeof(int) *
+					       fs->group_desc_count,
+					       "invalid_block_bitmap");
+	ctx->invalid_inode_table_flag = allocate_memory(sizeof(int) *
+					      fs->group_desc_count,
+					      "invalid_inode_table");
+		
+	clear_problem_context(&pctx);
+
+	/*
+	 * Verify the super block constants...
+	 */
+	check_super_value(ctx, "inodes_count", s->s_inodes_count,
+			  MIN_CHECK, 1, 0);
+	check_super_value(ctx, "blocks_count", s->s_blocks_count,
+			  MIN_CHECK, 1, 0);
+	check_super_value(ctx, "first_data_block", s->s_first_data_block,
+			  MAX_CHECK, 0, s->s_blocks_count);
+	check_super_value(ctx, "log_frag_size", s->s_log_frag_size,
+			  MAX_CHECK, 0, 2);
+	check_super_value(ctx, "log_block_size", s->s_log_block_size,
+			  MIN_CHECK | MAX_CHECK, s->s_log_frag_size,
+			  2);
+	check_super_value(ctx, "frags_per_group", s->s_frags_per_group,
+			  MIN_CHECK | MAX_CHECK, 1, 8 * EXT2_BLOCK_SIZE(s));
+	check_super_value(ctx, "blocks_per_group", s->s_blocks_per_group,
+			  MIN_CHECK | MAX_CHECK, 1, 8 * EXT2_BLOCK_SIZE(s));
+	check_super_value(ctx, "inodes_per_group", s->s_inodes_per_group,
+			  MIN_CHECK, 1, 0);
+	check_super_value(ctx, "r_blocks_count", s->s_r_blocks_count,
+			  MAX_CHECK, 0, s->s_blocks_count);
+
+	pctx.errcode = ext2fs_get_device_size(ctx->filesystem_name,
+					      EXT2_BLOCK_SIZE(s),
+					      &should_be);
+	if (pctx.errcode) {
+		fix_problem(ctx, PR_0_GETSIZE_ERROR, &pctx);
+		fatal_error(0);
+	}
+	if (should_be < s->s_blocks_count) {
+		pctx.blk = s->s_blocks_count;
+		pctx.blk2 = should_be;
+		if (fix_problem(ctx, PR_0_FS_SIZE_WRONG, &pctx))
+			fatal_error(0);
+	}
+
+	if (s->s_log_block_size != s->s_log_frag_size) {
+		pctx.blk = EXT2_BLOCK_SIZE(s);
+		pctx.blk2 = EXT2_FRAG_SIZE(s);
+		fix_problem(ctx, PR_0_NO_FRAGMENTS, &pctx);
+		fatal_error(0);
+	}
+
+	should_be = s->s_frags_per_group /
+		(s->s_log_block_size - s->s_log_frag_size + 1);
+	if (s->s_blocks_per_group != should_be) {
+		pctx.blk = s->s_blocks_per_group;
+		pctx.blk2 = should_be;
+		fix_problem(ctx, PR_0_BLOCKS_PER_GROUP, &pctx);
+		fatal_error(0);
+	}
+
+	should_be = (s->s_log_block_size == 0) ? 1 : 0;
+	if (s->s_first_data_block != should_be) {
+		pctx.blk = s->s_first_data_block;
+		pctx.blk2 = should_be;
+		fix_problem(ctx, PR_0_FIRST_DATA_BLOCK, &pctx);
+		fatal_error(0);
+	}
+
+	/*
+	 * Verify the group descriptors....
+	 */
+	first_block =  fs->super->s_first_data_block;
+	last_block = first_block + blocks_per_group;
+
+	for (i = 0; i < fs->group_desc_count; i++) {
+		pctx.group = i;
+		
+		if (i == fs->group_desc_count - 1)
+			last_block = fs->super->s_blocks_count;
+		if ((fs->group_desc[i].bg_block_bitmap < first_block) ||
+		    (fs->group_desc[i].bg_block_bitmap >= last_block)) {
+			pctx.blk = fs->group_desc[i].bg_block_bitmap;
+			if (fix_problem(ctx, PR_0_BB_NOT_GROUP, &pctx)) {
+				fs->group_desc[i].bg_block_bitmap = 0;
+				ctx->invalid_block_bitmap_flag[i]++;
+				ctx->invalid_bitmaps++;
+			}
+		}
+		if ((fs->group_desc[i].bg_inode_bitmap < first_block) ||
+		    (fs->group_desc[i].bg_inode_bitmap >= last_block)) {
+			pctx.blk = fs->group_desc[i].bg_inode_bitmap;
+			if (fix_problem(ctx, PR_0_IB_NOT_GROUP, &pctx)) {
+				fs->group_desc[i].bg_inode_bitmap = 0;
+				ctx->invalid_inode_bitmap_flag[i]++;
+				ctx->invalid_bitmaps++;
+			}
+		}
+		if ((fs->group_desc[i].bg_inode_table < first_block) ||
+		    ((fs->group_desc[i].bg_inode_table +
+		      fs->inode_blocks_per_group - 1) >= last_block)) {
+			pctx.blk = fs->group_desc[i].bg_inode_table;
+			if (fix_problem(ctx, PR_0_ITABLE_NOT_GROUP, &pctx)) {
+				fs->group_desc[i].bg_inode_table = 0;
+				ctx->invalid_inode_table_flag[i]++;
+				ctx->invalid_bitmaps++;
+			}
+		}
+		first_block += fs->super->s_blocks_per_group;
+		last_block += fs->super->s_blocks_per_group;
+	}
+	/*
+	 * If we have invalid bitmaps, set the error state of the
+	 * filesystem.
+	 */
+	if (ctx->invalid_bitmaps && !(ctx->options & E2F_OPT_READONLY)) {
+		fs->super->s_state &= ~EXT2_VALID_FS;
+		ext2fs_mark_super_dirty(fs);
+	}
+
+	/*
+	 * If the UUID field isn't assigned, assign it.
+	 */
+	if (!(ctx->options & E2F_OPT_READONLY) && uuid_is_null(s->s_uuid)) {
+		clear_problem_context(&pctx);
+		if (fix_problem(ctx, PR_0_ADD_UUID, &pctx)) {
+			uuid_generate(s->s_uuid);
+			ext2fs_mark_super_dirty(fs);
+		}
+	}
+	return;
+}
+
diff --git a/e2fsck/swapfs.c b/e2fsck/swapfs.c
index a317cc7..64d4775 100644
--- a/e2fsck/swapfs.c
+++ b/e2fsck/swapfs.c
@@ -102,8 +102,9 @@
 	}
 }
 
-static void swap_inodes(ext2_filsys fs)
+static void swap_inodes(e2fsck_t ctx)
 {
+	ext2_filsys fs = ctx->fs;
 	int			i, group;
 	ino_t			ino = 1;
 	char 			*buf, *block_buf;
@@ -135,8 +136,8 @@
 		inode = (struct ext2_inode *) buf;
 		for (i=0; i < fs->super->s_inodes_per_group;
 		     i++, ino++, inode++) {
-			stashed_ino = ino;
-			stashed_inode = inode;
+			ctx->stashed_ino = ino;
+			ctx->stashed_inode = inode;
 			
 			if (fs->flags & EXT2_FLAG_SWAP_BYTES_READ)
 				ext2fs_swap_inode(fs, inode, inode, 0);
@@ -173,13 +174,14 @@
 	fs->get_blocks = 0;
 }
 
-void swap_filesys(ext2_filsys fs)
+void swap_filesys(e2fsck_t ctx)
 {
+	ext2_filsys fs = ctx->fs;
 	struct resource_track	rtrack;
 
 	init_resource_track(&rtrack);
 
-	if (!preen)
+	if (!(ctx->options & E2F_OPT_PREEN))
 		printf("Pass 0: Doing byte-swap of filesystem\n");
 	
 #ifdef MTRACE
@@ -190,7 +192,7 @@
 		fprintf(stderr, "%s: the filesystem must be freshly "
 			"checked using fsck\n"
 			"and not mounted before trying to "
-			"byte-swap it.\n", device_name);
+			"byte-swap it.\n", ctx->device_name);
 		fatal_error(0);
 	}
 	if (fs->flags & EXT2_FLAG_SWAP_BYTES) {
@@ -201,17 +203,15 @@
 		fs->flags &= ~EXT2_FLAG_SWAP_BYTES_READ;
 		fs->flags |= EXT2_FLAG_SWAP_BYTES_WRITE;
 	}
-	swap_inodes(fs);
+	swap_inodes(ctx);
 	if (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE)
 		fs->flags |= EXT2_FLAG_SWAP_BYTES;
 	fs->flags &= ~(EXT2_FLAG_SWAP_BYTES_READ|
 		       EXT2_FLAG_SWAP_BYTES_WRITE);
 	ext2fs_flush(fs);
 	
-	if (tflag > 1) {
-		printf("Byte swap: ");
-		print_resource_track(&rtrack);
-	}
+	if (ctx->options & E2F_OPT_TIME2)
+		print_resource_track("Byte swap", &rtrack);
 }
 
 
diff --git a/e2fsck/unix.c b/e2fsck/unix.c
new file mode 100644
index 0000000..7bc0963
--- /dev/null
+++ b/e2fsck/unix.c
@@ -0,0 +1,651 @@
+/*
+ * unix.c - The unix-specific code for e2fsck
+ * 
+ * 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%
+ */
+
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#include <string.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <termios.h>
+#include <time.h>
+#ifdef HAVE_GETOPT_H
+#include <getopt.h>
+#endif
+#include <unistd.h>
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_MNTENT_H
+#include <mntent.h>
+#endif
+#include <sys/ioctl.h>
+#include <malloc.h>
+
+#include "et/com_err.h"
+#include "e2fsck.h"
+#include "problem.h"
+#include "../version.h"
+
+extern int isatty(int);
+
+/* Command line options */
+static int blocksize = 0;
+static int swapfs = 0;
+static int normalize_swapfs = 0;
+static int cflag = 0;		/* check disk */
+static int show_version_only = 0;
+static int force = 0;
+static int verbose = 0;
+
+static int replace_bad_blocks = 0;
+static char *bad_blocks_file = 0;
+
+static int possible_block_sizes[] = { 1024, 2048, 4096, 8192, 0};
+
+static int root_filesystem = 0;
+static int read_only_root = 0;
+
+int restart_e2fsck = 0;
+
+static void usage(e2fsck_t ctx)
+{
+	fprintf(stderr,
+		"Usage: %s [-panyrcdfvstFSV] [-b superblock] [-B blocksize]\n"
+		"\t\t[-I inode_buffer_blocks] [-P process_inode_size]\n"
+		"\t\t[-l|-L bad_blocks_file] device\n", ctx->program_name);
+	exit(FSCK_USAGE);
+}
+
+static void show_stats(e2fsck_t	ctx)
+{
+	ext2_filsys fs = ctx->fs;
+	int inodes, inodes_used, blocks, blocks_used;
+	int dir_links;
+	int num_files, num_links;
+	int frag_percent;
+
+	dir_links = 2 * ctx->fs_directory_count - 1;
+	num_files = ctx->fs_total_count - dir_links;
+	num_links = ctx->fs_links_count - dir_links;
+	inodes = fs->super->s_inodes_count;
+	inodes_used = (fs->super->s_inodes_count -
+		       fs->super->s_free_inodes_count);
+	blocks = fs->super->s_blocks_count;
+	blocks_used = (fs->super->s_blocks_count -
+		       fs->super->s_free_blocks_count);
+
+	frag_percent = (10000 * ctx->fs_fragmented) / inodes_used;
+	frag_percent = (frag_percent + 5) / 10;
+	
+	if (!verbose) {
+		printf("%s: %d/%d files (%0d.%d%% non-contiguous), %d/%d blocks\n",
+		       ctx->device_name, inodes_used, inodes,
+		       frag_percent / 10, frag_percent % 10,
+		       blocks_used, blocks);
+		return;
+	}
+	printf ("\n%8d inode%s used (%d%%)\n", inodes_used,
+		(inodes_used != 1) ? "s" : "",
+		100 * inodes_used / inodes);
+	printf ("%8d non-contiguous inodes (%0d.%d%%)\n",
+		ctx->fs_fragmented, frag_percent / 10, frag_percent % 10);
+	printf ("         # of inodes with ind/dind/tind blocks: %d/%d/%d\n",
+		ctx->fs_ind_count, ctx->fs_dind_count, ctx->fs_tind_count);
+	printf ("%8d block%s used (%d%%)\n"
+		"%8d bad block%s\n", blocks_used,
+		(blocks_used != 1) ? "s" : "",
+		100 * blocks_used / blocks, ctx->fs_badblocks_count,
+		ctx->fs_badblocks_count != 1 ? "s" : "");
+	printf ("\n%8d regular file%s\n"
+		"%8d director%s\n"
+		"%8d character device file%s\n"
+		"%8d block device file%s\n"
+		"%8d fifo%s\n"
+		"%8d link%s\n"
+		"%8d symbolic link%s (%d fast symbolic link%s)\n"
+		"%8d socket%s\n"
+		"--------\n"
+		"%8d file%s\n",
+		ctx->fs_regular_count,
+		(ctx->fs_regular_count != 1) ? "s" : "",
+		ctx->fs_directory_count,
+		(ctx->fs_directory_count != 1) ? "ies" : "y",
+		ctx->fs_chardev_count,
+		(ctx->fs_chardev_count != 1) ? "s" : "",
+		ctx->fs_blockdev_count,
+		(ctx->fs_blockdev_count != 1) ? "s" : "",
+		ctx->fs_fifo_count,
+		(ctx->fs_fifo_count != 1) ? "s" : "",
+		ctx->fs_links_count - dir_links,
+		((ctx->fs_links_count - dir_links) != 1) ? "s" : "",
+		ctx->fs_symlinks_count,
+		(ctx->fs_symlinks_count != 1) ? "s" : "",
+		ctx->fs_fast_symlinks_count,
+		(ctx->fs_fast_symlinks_count != 1) ? "s" : "",
+		ctx->fs_sockets_count, (ctx->fs_sockets_count != 1) ? "s" : "",
+		ctx->fs_total_count - dir_links,
+		((ctx->fs_total_count - dir_links) != 1) ? "s" : "");
+}
+
+static void check_mount(e2fsck_t ctx)
+{
+	errcode_t	retval;
+	int		mount_flags, cont, fd;
+
+	retval = ext2fs_check_if_mounted(ctx->filesystem_name, &mount_flags);
+	if (retval) {
+		com_err("ext2fs_check_if_mount", retval,
+			"while determining whether %s is mounted.",
+			ctx->filesystem_name);
+		return;
+	}
+	if (!(mount_flags & EXT2_MF_MOUNTED))
+		return;
+
+#if (defined(__linux__) && defined(HAVE_MNTENT_H))
+	/*
+	 * If the root is mounted read-only, then /etc/mtab is
+	 * probably not correct; so we won't issue a warning based on
+	 * it.
+	 */
+	fd = open(MOUNTED, O_RDWR);
+	if (fd < 0) {
+		if (errno == EROFS)
+			return;
+	} else
+		close(fd);
+#endif
+	
+	if (ctx->options & E2F_OPT_READONLY) {
+		printf("Warning!  %s is mounted.\n", ctx->device_name);
+		return;
+	}
+
+	printf("%s is mounted.\n\n", ctx->device_name);
+	printf("\a\a\a\aWARNING!!!  Running e2fsck on a mounted filesystem "
+	       "may cause\nSEVERE filesystem damage.\a\a\a\n\n");
+	if (isatty (0) && isatty (1))
+		cont = ask_yn("Do you really want to continue", -1);
+	else
+		cont = 0;
+	if (!cont) {
+		printf ("check aborted.\n");
+		exit (0);
+	}
+	return;
+}
+
+static void sync_disks(NOARGS)
+{
+	sync();
+	sync();
+	sleep(1);
+	sync();
+}
+
+/*
+ * This routine checks to see if a filesystem can be skipped; if so,
+ * it will exit with E2FSCK_OK.  Under some conditions it will print a
+ * message explaining why a check is being forced.
+ */
+static void check_if_skip(e2fsck_t ctx)
+{
+	ext2_filsys fs = ctx->fs;
+	const char *reason = NULL;
+	
+	if (force || bad_blocks_file || cflag || swapfs)
+		return;
+	
+	if (fs->super->s_state & EXT2_ERROR_FS)
+		reason = "contains a file system with errors";
+	else if (fs->super->s_mnt_count >=
+		 (unsigned) fs->super->s_max_mnt_count)
+		reason = "has reached maximal mount count";
+	else if (fs->super->s_checkinterval &&
+		 time(0) >= (fs->super->s_lastcheck +
+			     fs->super->s_checkinterval))
+		reason = "has gone too long without being checked";
+	else if ((fs->super->s_state & EXT2_VALID_FS) == 0)
+		reason = "was not cleanly unmounted";
+	if (reason) {
+		printf("%s %s, check forced.\n", ctx->device_name, reason);
+		return;
+	}
+	printf("%s: clean, %d/%d files, %d/%d blocks\n", ctx->device_name,
+	       fs->super->s_inodes_count - fs->super->s_free_inodes_count,
+	       fs->super->s_inodes_count,
+	       fs->super->s_blocks_count - fs->super->s_free_blocks_count,
+	       fs->super->s_blocks_count);
+	ext2fs_close(fs);
+	exit(FSCK_OK);
+}
+
+
+#define PATH_SET "PATH=/sbin"
+
+static errcode_t PRS(int argc, char *argv[], e2fsck_t *ret_ctx)
+{
+	int		flush = 0;
+	char		c;
+#ifdef MTRACE
+	extern void	*mallwatch;
+#endif
+	char		*oldpath = getenv("PATH");
+	e2fsck_t	ctx;
+	errcode_t	retval;
+
+	retval = e2fsck_allocate_context(&ctx);
+	if (retval)
+		return retval;
+
+	*ret_ctx = ctx;
+
+	/* Update our PATH to include /sbin  */
+	if (oldpath) {
+		char *newpath;
+
+		newpath = malloc(sizeof (PATH_SET) + 1 + strlen (oldpath));
+		if (!newpath)
+			fatal_error("Couldn't malloc() newpath");
+		strcpy (newpath, PATH_SET);
+		strcat (newpath, ":");
+		strcat (newpath, oldpath);
+		putenv (newpath);
+	} else
+		putenv (PATH_SET);
+
+	setbuf(stdout, NULL);
+	setbuf(stderr, NULL);
+	initialize_ext2_error_table();
+	
+	if (argc && *argv)
+		ctx->program_name = *argv;
+	else
+		ctx->program_name = "e2fsck";
+	while ((c = getopt (argc, argv, "panyrcB:dfvtFVM:b:I:P:l:L:N:Ss")) != EOF)
+		switch (c) {
+		case 'p':
+		case 'a':
+			ctx->options |= E2F_OPT_PREEN;
+			ctx->options &= ~(E2F_OPT_YES|E2F_OPT_NO);
+			break;
+		case 'n':
+			ctx->options |= E2F_OPT_NO;
+			ctx->options &= ~(E2F_OPT_YES|E2F_OPT_PREEN);
+			break;
+		case 'y':
+			ctx->options |= E2F_OPT_YES;
+			ctx->options &= ~(E2F_OPT_PREEN|E2F_OPT_NO);
+			break;
+		case 't':
+			if (ctx->options & E2F_OPT_TIME)
+				ctx->options |= E2F_OPT_TIME2;
+			else
+				ctx->options |= E2F_OPT_TIME;
+			break;
+		case 'c':
+			cflag++;
+			ctx->options |= E2F_OPT_CHECKBLOCKS;
+			break;
+		case 'r':
+			/* What we do by default, anyway! */
+			break;
+		case 'b':
+			ctx->use_superblock = atoi(optarg);
+			break;
+		case 'B':
+			blocksize = atoi(optarg);
+			break;
+		case 'I':
+			ctx->inode_buffer_blocks = atoi(optarg);
+			break;
+		case 'P':
+			ctx->process_inode_size = atoi(optarg);
+			break;
+		case 'L':
+			replace_bad_blocks++;
+		case 'l':
+			bad_blocks_file = malloc(strlen(optarg)+1);
+			if (!bad_blocks_file)
+				fatal_error("Couldn't malloc bad_blocks_file");
+			strcpy(bad_blocks_file, optarg);
+			break;
+		case 'd':
+			ctx->options |= E2F_OPT_DEBUG;
+			break;
+		case 'f':
+			force = 1;
+			break;
+		case 'F':
+#ifdef BLKFLSBUF
+			flush = 1;
+#else
+			fatal_error ("-F not supported");
+#endif
+			break;
+		case 'v':
+			verbose = 1;
+			break;
+		case 'V':
+			show_version_only = 1;
+			break;
+#ifdef MTRACE
+		case 'M':
+			mallwatch = (void *) strtol(optarg, NULL, 0);
+			break;
+#endif
+		case 'N':
+			ctx->device_name = optarg;
+			break;
+		case 's':
+			normalize_swapfs = 1;
+		case 'S':
+			swapfs = 1;
+			break;
+		default:
+			usage(ctx);
+		}
+	if (show_version_only)
+		return 0;
+	if (optind != argc - 1)
+		usage(ctx);
+	if ((ctx->options & E2F_OPT_NO) && !bad_blocks_file &&
+	    !cflag && !swapfs)
+		ctx->options |= E2F_OPT_READONLY;
+	ctx->filesystem_name = argv[optind];
+	if (ctx->device_name == 0)
+		ctx->device_name = ctx->filesystem_name;
+	if (flush) {
+#ifdef BLKFLSBUF
+		int	fd = open(ctx->filesystem_name, O_RDONLY, 0);
+
+		if (fd < 0) {
+			com_err("open", errno, "while opening %s for flushing",
+				ctx->filesystem_name);
+			exit(FSCK_ERROR);
+		}
+		if (ioctl(fd, BLKFLSBUF, 0) < 0) {
+			com_err("BLKFLSBUF", errno, "while trying to flush %s",
+				ctx->filesystem_name);
+			exit(FSCK_ERROR);
+		}
+		close(fd);
+#else
+		fatal_error ("BLKFLSBUF not supported");
+#endif /* BLKFLSBUF */
+	}
+	if (swapfs) {
+		if (cflag || bad_blocks_file) {
+			fprintf(stderr, "Incompatible options not "
+				"allowed when byte-swapping.\n");
+			fatal_error(0);
+		}
+	}
+	return 0;
+}
+
+static const char *my_ver_string = E2FSPROGS_VERSION;
+static const char *my_ver_date = E2FSPROGS_DATE;
+					
+int main (int argc, char *argv[])
+{
+	errcode_t	retval = 0;
+	int		exit_value = FSCK_OK;
+	int		i;
+	ext2_filsys	fs = 0;
+	io_manager	io_ptr;
+	struct ext2fs_sb *s;
+	const char	*lib_ver_date;
+	int		my_ver, lib_ver;
+	e2fsck_t	ctx;
+	struct problem_context pctx;
+	int flags;
+	
+	clear_problem_context(&pctx);
+#ifdef MTRACE
+	mtrace();
+#endif
+#ifdef MCHECK
+	mcheck(0);
+#endif
+	my_ver = ext2fs_parse_version_string(my_ver_string);
+	lib_ver = ext2fs_get_library_version(0, &lib_ver_date);
+	if (my_ver > lib_ver) {
+		fprintf( stderr, "Error: ext2fs library version "
+			"out of date!\n");
+		show_version_only++;
+	}
+	
+	retval = PRS(argc, argv, &ctx);
+	if (retval) {
+		com_err("e2fsck", retval,
+			"while trying to initialize program");
+		exit(1);
+	}
+
+	init_resource_track(&ctx->global_rtrack);
+
+	if (!(ctx->options & E2F_OPT_PREEN) || show_version_only)
+		fprintf (stderr, "e2fsck %s, %s for EXT2 FS %s, %s\n",
+			 my_ver_string, my_ver_date, EXT2FS_VERSION,
+			 EXT2FS_DATE);
+
+	if (show_version_only) {
+		fprintf(stderr, "\tUsing %s, %s\n",
+			error_message(EXT2_ET_BASE), lib_ver_date);
+		exit(0);
+	}
+	
+	check_mount(ctx);
+	
+	if (!(ctx->options & E2F_OPT_PREEN) &&
+	    !(ctx->options & E2F_OPT_NO) &&
+	    !(ctx->options & E2F_OPT_YES)) {
+		if (!isatty (0) || !isatty (1))
+			die ("need terminal for interactive repairs");
+	}
+	ctx->superblock = ctx->use_superblock;
+restart:
+#if 1
+	io_ptr = unix_io_manager;
+#else
+	io_ptr = test_io_manager;
+	test_io_backing_manager = unix_io_manager;
+#endif
+	sync_disks();
+	flags = (ctx->options & E2F_OPT_READONLY) ? 0 : EXT2_FLAG_RW;
+	if (ctx->superblock && blocksize) {
+		retval = ext2fs_open(ctx->filesystem_name, flags,
+				     ctx->superblock, blocksize, io_ptr, &fs);
+	} else if (ctx->superblock) {
+		for (i=0; possible_block_sizes[i]; i++) {
+			retval = ext2fs_open(ctx->filesystem_name, flags,
+					     ctx->superblock,
+					     possible_block_sizes[i],
+					     io_ptr, &fs);
+			if (!retval)
+				break;
+		}
+	} else 
+		retval = ext2fs_open(ctx->filesystem_name, flags, 
+				     0, 0, io_ptr, &fs);
+	if (!ctx->superblock && !(ctx->options & E2F_OPT_PREEN) && 
+	    ((retval == EXT2_ET_BAD_MAGIC) ||
+	     ((retval == 0) && ext2fs_check_desc(fs)))) {
+		if (!fs || (fs->group_desc_count > 1)) {
+			printf("%s trying backup blocks...\n",
+			       retval ? "Couldn't find ext2 superblock," :
+			       "Group descriptors look bad...");
+			ctx->superblock = get_backup_sb(fs);
+			if (fs)
+				ext2fs_close(fs);
+			goto restart;
+		}
+	}
+	if (retval) {
+		com_err(ctx->program_name, retval, "while trying to open %s",
+			ctx->filesystem_name);
+		if (retval == EXT2_ET_REV_TOO_HIGH)
+			printf ("Get a newer version of e2fsck!\n");
+		else if (retval == EXT2_ET_SHORT_READ)
+			printf ("Could this be a zero-length partition?\n");
+		else if ((retval == EPERM) || (retval == EACCES))
+			printf("You must have %s access to the "
+			       "filesystem or be root\n",
+			       (ctx->options & E2F_OPT_READONLY) ?
+			       "r/o" : "r/w");
+		else if (retval == ENXIO)
+			printf("Possibly non-existent or swap device?\n");
+		else
+			fix_problem(ctx, PR_0_SB_CORRUPT, &pctx);
+		fatal_error(0);
+	}
+	ctx->fs = fs;
+	fs->private = ctx;
+#ifdef	EXT2_CURRENT_REV
+	if (fs->super->s_rev_level > E2FSCK_CURRENT_REV) {
+		com_err(ctx->program_name, EXT2_ET_REV_TOO_HIGH,
+			"while trying to open %s",
+			ctx->filesystem_name);
+		goto get_newer;
+	}
+#endif
+	/*
+	 * Check for compatibility with the feature sets.  We need to
+	 * be more stringent than ext2fs_open().
+	 */
+	s = (struct ext2fs_sb *) fs->super;
+	if ((s->s_feature_compat & ~EXT2_LIB_FEATURE_COMPAT_SUPP) ||
+	    (s->s_feature_incompat & ~EXT2_LIB_FEATURE_INCOMPAT_SUPP)) {
+		com_err(ctx->program_name, EXT2_ET_UNSUPP_FEATURE,
+			"(%s)", ctx->filesystem_name);
+	get_newer:
+		printf ("Get a newer version of e2fsck!\n");
+		fatal_error(0);
+	}
+	if (s->s_feature_ro_compat & ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP) {
+		com_err(ctx->program_name, EXT2_ET_RO_UNSUPP_FEATURE,
+			"(%s)", ctx->filesystem_name);
+		goto get_newer;
+	}
+	
+	/*
+	 * If the user specified a specific superblock, presumably the
+	 * master superblock has been trashed.  So we mark the
+	 * superblock as dirty, so it can be written out.
+	 */
+	if (ctx->superblock &&
+	    !(ctx->options & E2F_OPT_READONLY))
+		ext2fs_mark_super_dirty(fs);
+
+	/*
+	 * Don't overwrite the backup superblock and block
+	 * descriptors, until we're sure the filesystem is OK....
+	 */
+	fs->flags |= EXT2_FLAG_MASTER_SB_ONLY;
+
+	ehandler_init(fs->io);
+
+	if (ctx->superblock)
+		set_latch_flags(PR_LATCH_RELOC, PRL_LATCHED, 0);
+	check_super_block(ctx);
+	check_if_skip(ctx);
+	if (bad_blocks_file)
+		read_bad_blocks_file(ctx, bad_blocks_file, replace_bad_blocks);
+	else if (cflag)
+		test_disk(ctx);
+
+	if (normalize_swapfs) {
+		if ((fs->flags & EXT2_FLAG_SWAP_BYTES) ==
+		    ext2fs_native_flag()) {
+			fprintf(stderr, "%s: Filesystem byte order "
+				"already normalized.\n", ctx->device_name);
+			fatal_error(0);
+		}
+	}
+	if (swapfs)
+		swap_filesys(ctx);
+
+	/*
+	 * Mark the system as valid, 'til proven otherwise
+	 */
+	ext2fs_mark_valid(fs);
+
+	retval = ext2fs_read_bb_inode(fs, &fs->badblocks);
+	if (retval) {
+		com_err(ctx->program_name, retval,
+			"while reading bad blocks inode");
+		preenhalt(ctx);
+		printf("This doesn't bode well, but we'll try to go on...\n");
+	}
+	
+	pass1(ctx);
+	if (restart_e2fsck) {
+		ext2fs_close(fs);
+		printf("Restarting e2fsck from the beginning...\n");
+		restart_e2fsck = 0;
+		retval = e2fsck_reset_context(ctx);
+		if (retval) {
+			com_err(ctx->program_name, retval,
+				"while resetting context");
+			exit(1);
+		}
+		goto restart;
+	}
+	pass2(ctx);
+	pass3(ctx);
+	pass4(ctx);
+	pass5(ctx);
+
+#ifdef MTRACE
+	mtrace_print("Cleanup");
+#endif
+	if (ext2fs_test_changed(fs)) {
+		exit_value = FSCK_NONDESTRUCT;
+		if (!(ctx->options & E2F_OPT_PREEN))
+			printf("\n%s: ***** FILE SYSTEM WAS MODIFIED *****\n",
+			       ctx->device_name);
+		if (root_filesystem && !read_only_root) {
+			printf("%s: ***** REBOOT LINUX *****\n",
+			       ctx->device_name);
+			exit_value = FSCK_REBOOT;
+		}
+	}
+	if (ext2fs_test_valid(fs))
+		fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
+	else
+		exit_value = FSCK_UNCORRECTED;
+	if (!(ctx->options & E2F_OPT_READONLY)) {
+		if (ext2fs_test_valid(fs)) {
+			if (!(fs->super->s_state & EXT2_VALID_FS))
+				exit_value = FSCK_NONDESTRUCT;
+			fs->super->s_state = EXT2_VALID_FS;
+		} else
+			fs->super->s_state &= ~EXT2_VALID_FS;
+		fs->super->s_mnt_count = 0;
+		fs->super->s_lastcheck = time(NULL);
+		ext2fs_mark_super_dirty(fs);
+	}
+	show_stats(ctx);
+
+	write_bitmaps(ctx);
+	ext2fs_close(fs);
+	sync_disks();
+	
+	if (ctx->options & E2F_OPT_TIME)
+		print_resource_track(NULL, &ctx->global_rtrack);
+
+	e2fsck_free_context(ctx);
+	
+	return exit_value;
+}
diff --git a/e2fsck/util.c b/e2fsck/util.c
index 24f77d2..879e7ba 100644
--- a/e2fsck/util.c
+++ b/e2fsck/util.c
@@ -20,12 +20,10 @@
 #include <sys/time.h>
 #include <sys/resource.h>
 
-const char * fix_msg[2] = { "IGNORED", "FIXED" };
-
 void fatal_error (const char *msg)
 {
 	if (msg) 
-		fprintf (stderr, "%s: %s\n", program_name, msg);
+		fprintf (stderr, "e2fsck: %s\n", msg);
 	exit(FSCK_ERROR);
 }
 
@@ -46,7 +44,6 @@
 	return ret;
 }
 
-
 int ask_yn(const char * string, int def)
 {
 	int		c;
@@ -91,31 +88,32 @@
 	return def;
 }
 
-int ask (const char * string, int def)
+int ask (e2fsck_t ctx, const char * string, int def)
 {
-	if (nflag) {
+	if (ctx->options & E2F_OPT_NO) {
 		printf ("%s? no\n\n", string);
 		return 0;
 	}
-	if (yflag) {
+	if (ctx->options & E2F_OPT_YES) {
 		printf ("%s? yes\n\n", string);
 		return 1;
 	}
-	if (preen) {
+	if (ctx->options & E2F_OPT_PREEN) {
 		printf ("%s? %s\n\n", string, def ? "yes" : "no");
 		return def;
 	}
 	return ask_yn(string, def);
 }
 
-void read_bitmaps(ext2_filsys fs)
+void read_bitmaps(e2fsck_t ctx)
 {
+	ext2_filsys fs = ctx->fs;
 	errcode_t	retval;
 
-	if (invalid_bitmaps) {
-		com_err(program_name, 0,
+	if (ctx->invalid_bitmaps) {
+		com_err(ctx->program_name, 0,
 			"read_bitmaps: illegal bitmap block(s) for %s",
-			device_name);
+			ctx->device_name);
 		fatal_error(0);
 	}
 
@@ -123,15 +121,16 @@
 	retval = ext2fs_read_bitmaps(fs);
 	ehandler_operation(0);
 	if (retval) {
-		com_err(program_name, retval,
+		com_err(ctx->program_name, retval,
 			"while retrying to read bitmaps for %s",
-			device_name);
+			ctx->device_name);
 		fatal_error(0);
 	}
 }
 
-void write_bitmaps(ext2_filsys fs)
+void write_bitmaps(e2fsck_t ctx)
 {
+	ext2_filsys fs = ctx->fs;
 	errcode_t	retval;
 
 	if (ext2fs_test_bb_dirty(fs)) {
@@ -139,9 +138,9 @@
 		retval = ext2fs_write_block_bitmap(fs);
 		ehandler_operation(0);
 		if (retval) {
-			com_err(program_name, retval,
+			com_err(ctx->program_name, retval,
 				"while retrying to write block bitmaps for %s",
-				device_name);
+				ctx->device_name);
 			fatal_error(0);
 		}
 	}
@@ -151,21 +150,23 @@
 		retval = ext2fs_write_inode_bitmap(fs);
 		ehandler_operation(0);
 		if (retval) {
-			com_err(program_name, retval,
+			com_err(ctx->program_name, retval,
 				"while retrying to write inode bitmaps for %s",
-				device_name);
+				ctx->device_name);
 			fatal_error(0);
 		}
 	}
 }
 
-void preenhalt(ext2_filsys fs)
+void preenhalt(e2fsck_t ctx)
 {
-	if (!preen)
+	ext2_filsys fs = ctx->fs;
+
+	if (!(ctx->options & E2F_OPT_PREEN))
 		return;
 	fprintf(stderr, "\n\n%s: UNEXPECTED INCONSISTENCY; "
 		"RUN fsck MANUALLY.\n\t(i.e., without -a or -p options)\n",
-	       device_name);
+	       ctx->device_name);
 	if (fs != NULL) {
 		fs->super->s_state |= EXT2_ERROR_FS;
 		ext2fs_mark_super_dirty(fs);
@@ -183,6 +184,9 @@
 	track->brk_start = sbrk(0);
 	gettimeofday(&track->time_start, 0);
 #ifdef HAVE_GETRUSAGE
+#ifdef solaris
+	memcpy(&r, 0, sizeof(struct rusage));
+#endif
 	getrusage(RUSAGE_SELF, &r);
 	track->user_start = r.ru_utime;
 	track->system_start = r.ru_stime;
@@ -205,7 +209,7 @@
 		((float) (tv1->tv_usec - tv2->tv_usec)) / 1000000);
 }
 
-void print_resource_track(struct resource_track *track)
+void print_resource_track(const char *desc, struct resource_track *track)
 {
 #ifdef HAVE_GETRUSAGE
 	struct rusage r;
@@ -213,6 +217,10 @@
 	struct timeval time_end;
 
 	gettimeofday(&time_end, 0);
+
+	if (desc)
+		printf("%s :", desc);
+	
 #ifdef HAVE_GETRUSAGE
 	getrusage(RUSAGE_SELF, &r);
 
@@ -265,4 +273,10 @@
 }
 #endif
 
+blk_t get_backup_sb(ext2_filsys fs)
+{
+	if (!fs || !fs->super)
+		return 8193;
+	return fs->super->s_blocks_per_group + 1;
+}