Many files:
  Checkin of work to date.  (Pretty much completely working now.)

diff --git a/resize/Makefile.in b/resize/Makefile.in
index d5991f8..bda475d 100644
--- a/resize/Makefile.in
+++ b/resize/Makefile.in
@@ -12,21 +12,28 @@
 @MCONFIG@
 
 PROGS=		resize2fs
+TEST_PROGS=	test_extent
 MANPAGES=	resize2fs.8
 
-RESIZE_OBJS= inodemap.o resize2fs.o main.o
+RESIZE_OBJS= extent.o ext2_block_move.o ext2_inode_move.o resize2fs.o \
+	main.o sim_progress.o 
 
-SRCS= $(srcdir)/inodemap.c \
+TEST_EXTENT_OBJS= extent.o test_extent.o
+
+SRCS= $(srcdir)/extent.c \
+	$(srcdir)/ext2_block_move.c \
+	$(srcdir)/ext2_inode_move.c \
 	$(srcdir)/resize2fs.c \
-	$(srcdir)/main.c 
+	$(srcdir)/main.c \
+	$(srcdir)/sim_progress.c
 
-LIBS= $(LIBEXT2FS) $(LIBE2P) $(LIBCOM_ERR)  $(LIBUUID)
-DEPLIBS= $(LIBEXT2FS) $(LIBE2P) $(LIBCOM_ERR)  $(LIBUUID)
+LIBS= $(LIBEXT2FS) $(LIBCOM_ERR)  $(LIBUUID)
+DEPLIBS= $(LIBEXT2FS) $(LIBCOM_ERR)  $(LIBUUID)
 
 .c.o:
 	$(CC) -c $(ALL_CFLAGS) $< -o $@
 
-all:: $(PROGS) $(MANPAGES)
+all:: $(PROGS) $(TEST_PROGS) $(MANPAGES)
 
 resize2fs: $(RESIZE_OBJS) $(DEPLIBS)
 	$(CC) $(ALL_LDFLAGS) -o resize2fs $(RESIZE_OBJS) $(LIBS)
@@ -35,6 +42,9 @@
 	-$(CHMOD) +x $(SUBSTITUTE)
 	$(SUBSTITUTE) $(srcdir)/resize2fs.8.in resize2fs.8
 
+test_extent: $(TEST_EXTENT_OBJS)
+	$(CC) $(ALL_LDFLAGS) -o test_extent $(TEST_EXTENT_OBJS) $(LIBS)
+	
 installdirs:
 	$(top_srcdir)/mkinstalldirs $(DESTDIR)$(usbindir) \
 		$(DESTDIR)$(man8dir) $(DESTDIR)$(cat8dir)
@@ -56,13 +66,52 @@
 		$(RM) -f $(man8dir)/$$i; \
 	done
 
+test_extent.out: test_extent $(srcdir)/test_extent.in
+	./test_extent < $(srcdir)/test_extent.in > test_extent.out
+
+check: test_extent.out
+	@if cmp -s test_extent.out $(srcdir)/test_extent.in ; then \
+		echo "Test succeeded." ; \
+	else \
+		echo "Test failed!" ; \
+		diff test_extent.out $(srcdir)/test_extent.in ; \
+		exit 1 ; \
+	fi
+	
 clean:
-	$(RM) -f $(PROGS) $(MANPAGES) \#* *.s *.o *.a *~ core
+	$(RM) -f $(PROGS) $(TEST_PROGS) $(MANPAGES) \#* *.s *.o *.a *~ core \
+		test_extent.out
 
 mostlyclean: clean
 distclean: clean
 	$(RM) -f .depend Makefile
 
+#
+# Kludge to create a "special" e2fsprogs distribution file.
+#
+
+SRCROOT = `echo e2fsprogs-@E2FSPROGS_VERSION@ | sed -e 's/-WIP//' \
+			-e 's/pre-//' -e 's/-PLUS//'`
+TAR=tar
+
+$(top_srcdir)/.exclude-file:
+	(cd $(top_srcdir)/.. ; find e2fsprogs \( -name \*~ -o -name \*.orig \
+		-o -name CVS -o -name \*.rej \) -print \
+		> .exclude-file)
+	echo "$(SRCROOT)/build" >> $(top_srcdir)/.exclude-file
+	echo "$(SRCROOT)/rpm.log" >> $(top_srcdir)/.exclude-file
+	echo "$(SRCROOT)/.exclude-file" >> $(top_srcdir)/.exclude-file
+	echo $(SRCROOT)/e2fsprogs-@E2FSPROGS_VERSION@.tar.gz \
+		>> $(top_srcdir)/.exclude-file
+	
+source_tar_file: $(top_srcdir)/.exclude-file
+	(cd $(top_srcdir)/..; a=$(SRCROOT); rm -f $$a ; ln -sf e2fsprogs $$a ; \
+		$(TAR) -c -h -v -f - \
+			-X $$a/.exclude-file $$a | \
+		gzip -9 > e2fsprogs-@E2FSPROGS_VERSION@.tar.gz)
+	rm -f $(top_srcdir)/.exclude-file
+
+
 # +++ Dependency line eater +++
 # 
 # Makefile dependencies follow.  This must be the last section in
diff --git a/resize/NOTES b/resize/NOTES
index e04d180..ccbf810 100644
--- a/resize/NOTES
+++ b/resize/NOTES
@@ -1,8 +1,2 @@
 TODO
 
-*) Inode table relocation
-
-*) Inode relocation
-
-*) Summary information collection
-
diff --git a/resize/ext2_block_move.c b/resize/ext2_block_move.c
new file mode 100644
index 0000000..ec60edf
--- /dev/null
+++ b/resize/ext2_block_move.c
@@ -0,0 +1,230 @@
+/*
+ * ext2_block_move.c --- ext2resizer block mover
+ *
+ * Copyright (C) 1997 Theodore Ts'o
+ * 
+ * %Begin-Header%
+ * All rights reserved.
+ * %End-Header%
+ */
+
+#include "resize2fs.h"
+
+struct process_block_struct {
+	ino_t			ino;
+	struct ext2_inode *	inode;
+	ext2_extent		bmap;
+	errcode_t		error;
+	int			is_dir;
+	int			flags;
+};
+
+static int process_block(ext2_filsys fs, blk_t	*block_nr,
+			 int blockcnt, blk_t ref_block,
+			 int ref_offset, void *private)
+{
+	struct process_block_struct *pb = private;
+	errcode_t	retval;
+	blk_t		block, new;
+	int		ret = 0;
+
+	block = *block_nr;
+
+	new = ext2fs_extent_translate(pb->bmap, block);
+	if (new) {
+		*block_nr = new;
+		ret |= BLOCK_CHANGED;
+		if (pb->flags & RESIZE_DEBUG_BMOVE)
+			printf("ino=%ld, blockcnt=%d, %u->%u\n", pb->ino,
+			       blockcnt, block, new);
+	}
+
+	if (pb->is_dir) {
+		retval = ext2fs_add_dir_block(fs->dblist, pb->ino,
+					      *block_nr, blockcnt);
+		if (retval) {
+			pb->error = retval;
+			ret |= BLOCK_ABORT;
+		}
+	}
+
+	return ret;
+}
+
+errcode_t ext2fs_block_move(ext2_resize_t rfs)
+{
+	ext2_extent		bmap;
+	blk_t			blk, old, new;
+	ext2_filsys		fs = rfs->new_fs;
+	ext2_filsys		old_fs = rfs->old_fs;
+	ino_t			ino;
+	struct ext2_inode 	inode;
+	errcode_t		retval;
+	struct process_block_struct pb;
+	ext2_inode_scan		scan = 0;
+	char			*block_buf;
+	int			size, c;
+	int			to_move, moved;
+	ext2_sim_progmeter progress = 0;
+
+	new = fs->super->s_first_data_block;
+	if (!rfs->itable_buf) {
+		rfs->itable_buf = malloc(fs->blocksize *
+					 fs->inode_blocks_per_group);
+		if (!rfs->itable_buf)
+			return ENOMEM;
+	}
+	retval = ext2fs_create_extent_table(&bmap, 0);
+	if (retval)
+		return retval;
+
+	/*
+	 * The first step is to figure out where all of the blocks
+	 * will go.
+	 */
+	to_move = moved = 0;
+	for (blk = old_fs->super->s_first_data_block;
+	     blk < old_fs->super->s_blocks_count; blk++) {
+		if (!ext2fs_test_block_bitmap(old_fs->block_map, blk))
+			continue;
+		if (!ext2fs_test_block_bitmap(rfs->move_blocks, blk))
+			continue;
+
+		while (1) {
+			if (new >= fs->super->s_blocks_count) {
+				retval = ENOSPC;
+				goto errout;
+			}
+			if (!ext2fs_test_block_bitmap(fs->block_map, new) &&
+			    !ext2fs_test_block_bitmap(rfs->reserve_blocks,
+						      new))
+				break;
+			new++;
+		}
+		ext2fs_mark_block_bitmap(fs->block_map, new);
+		ext2fs_add_extent_entry(bmap, blk, new);
+		to_move++;
+	}
+	if (to_move == 0)
+		return 0;
+	/*
+	 * Step two is to actually move the blocks
+	 */
+	retval =  ext2fs_iterate_extent(bmap, 0, 0, 0);
+	if (retval) goto errout;
+
+	if (rfs->flags & RESIZE_PERCENT_COMPLETE) {
+		retval = ext2fs_progress_init(&progress,
+		      "Relocating blocks", 30, 40, to_move, 0);
+		if (retval)
+			return retval;
+	}
+	
+	while (1) {
+		retval = ext2fs_iterate_extent(bmap, &old, &new, &size);
+		if (retval) goto errout;
+		if (!size)
+			break;
+		if (rfs->flags & RESIZE_DEBUG_BMOVE)
+			printf("Moving %d blocks %u->%u\n", size,
+			       old, new);
+		do {
+			c = size;
+			if (c > fs->inode_blocks_per_group)
+				c = fs->inode_blocks_per_group;
+			retval = io_channel_read_blk(fs->io, old, c,
+						     rfs->itable_buf);
+			if (retval) goto errout;
+			retval = io_channel_write_blk(fs->io, new, c,
+						      rfs->itable_buf);
+			if (retval) goto errout;
+			size -= c;
+			new += c;
+			old += c;
+			moved += c;
+			io_channel_flush(fs->io);
+			if (progress)
+				ext2fs_progress_update(progress, moved);
+		} while (size > 0);
+		io_channel_flush(fs->io);
+	}
+	if (progress) {
+		ext2fs_progress_close(progress);
+		progress = 0;
+	}
+	
+	/*
+	 * Step 3 is where we update the block pointers
+	 */
+	retval = ext2fs_open_inode_scan(old_fs, 0, &scan);
+	if (retval) goto errout;
+
+	pb.error = 0;
+	pb.bmap = bmap;	
+	pb.flags = rfs->flags;
+
+	block_buf = malloc(old_fs->blocksize * 3);
+	if (!block_buf) {
+		retval = ENOMEM;
+		goto errout;
+	}
+
+	/*
+	 * We're going to initialize the dblist while we're at it.
+	 */
+	if (old_fs->dblist) {
+		ext2fs_free_dblist(old_fs->dblist);
+		old_fs->dblist = NULL;
+	}
+	retval = ext2fs_init_dblist(old_fs, 0);
+	if (retval)
+		return retval;
+
+	retval = ext2fs_get_next_inode(scan, &ino, &inode);
+	if (retval) goto errout;
+	
+	if (rfs->flags & RESIZE_PERCENT_COMPLETE) {
+		retval = ext2fs_progress_init(&progress,
+		      "Updating block references", 30, 40,
+		      old_fs->super->s_inodes_count, 0);
+		if (retval)
+			return retval;
+	}
+	
+	while (ino) {
+		if ((inode.i_links_count == 0) ||
+		    !ext2fs_inode_has_valid_blocks(&inode))
+			goto next;
+		
+		pb.ino = ino;
+		pb.inode = &inode;
+
+		pb.is_dir = LINUX_S_ISDIR(inode.i_mode);
+		
+		retval = ext2fs_block_iterate2(old_fs, ino, 0, block_buf,
+					      process_block, &pb);
+		if (retval)
+			goto errout;
+		if (pb.error) {
+			retval = pb.error;
+			goto errout;
+		}
+
+	next:
+		if (progress)
+			ext2fs_progress_update(progress, ino);
+		retval = ext2fs_get_next_inode(scan, &ino, &inode);
+		if (retval == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE)
+			goto next;
+	}
+	retval = 0;
+errout:
+	if (progress)
+		ext2fs_progress_close(progress);
+	
+	ext2fs_free_extent_table(bmap);
+	if (scan)
+		ext2fs_close_inode_scan(scan);
+	return retval;
+}
+
diff --git a/resize/ext2_inode_move.c b/resize/ext2_inode_move.c
new file mode 100644
index 0000000..5d26b15
--- /dev/null
+++ b/resize/ext2_inode_move.c
@@ -0,0 +1,300 @@
+/*
+ * ext2_inode_move.c --- ext2resizer inode mover
+ *
+ * Copyright (C) 1997 Theodore Ts'o
+ * 
+ * %Begin-Header%
+ * All rights reserved.
+ * %End-Header%
+ */
+
+#include "resize2fs.h"
+
+/*
+ * Progress callback
+ */
+struct callback_info {
+	ext2_sim_progmeter progress;
+	int	offset;
+};
+		
+static errcode_t progress_callback(ext2_filsys fs, ext2_inode_scan scan,
+				   dgrp_t group, void * private)
+{
+	struct callback_info *cb = private;
+
+	if (!cb->progress)
+		return 0;
+
+	ext2fs_progress_update(cb->progress, group - cb->offset + 1);
+	return 0;
+}
+
+
+struct istruct {
+	ext2_sim_progmeter progress;
+	ext2_extent	imap;
+	int		flags;
+	int		num;
+};
+
+static int check_and_change_inodes(ino_t dir, int entry,
+				   struct ext2_dir_entry *dirent, int offset,
+				   int	blocksize, char *buf, void *private)
+{
+	struct istruct *is = private;
+	ino_t	new;
+
+	if (is->progress && offset == 0) {
+		ext2fs_progress_update(is->progress, ++is->num);
+	}
+
+	if (!dirent->inode)
+		return 0;
+
+	new = ext2fs_extent_translate(is->imap, dirent->inode);
+
+	if (!new)
+		return 0;
+	if (is->flags & RESIZE_DEBUG_INODEMAP)
+		printf("Inode translate (dir=%ld, name=%.*s, %u->%ld)\n",
+		       dir, dirent->name_len, dirent->name,
+		       dirent->inode, new);
+
+	dirent->inode = new;
+
+	return DIRENT_CHANGED;
+}
+
+/*
+ * Function to obtain the dblist information (if we didn't get it
+ * earlier)
+ */
+struct process_block_struct {
+	ino_t			ino;
+	struct ext2_inode *	inode;
+	errcode_t		error;
+};
+
+static int process_block(ext2_filsys fs, blk_t	*block_nr,
+			 int blockcnt, blk_t ref_block,
+			 int ref_offset, void *private)
+{
+	struct process_block_struct *pb = private;
+	errcode_t	retval;
+	int		ret = 0;
+
+	retval = ext2fs_add_dir_block(fs->dblist, pb->ino,
+				      *block_nr, blockcnt);
+	if (retval) {
+		pb->error = retval;
+		ret |= BLOCK_ABORT;
+	}
+	return ret;
+}
+
+static errcode_t get_dblist(ext2_filsys fs, int flags)
+{
+	ext2_inode_scan		scan = 0;
+	errcode_t		retval;
+	char			*block_buf;
+	struct process_block_struct	pb;
+	ext2_sim_progmeter 	progress = 0; 
+	ino_t			ino;
+	struct ext2_inode 	inode;
+
+	retval = ext2fs_open_inode_scan(fs, 0, &scan);
+	if (retval) goto errout;
+
+	pb.error = 0;
+
+	block_buf = malloc(fs->blocksize * 3);
+	if (!block_buf) {
+		retval = ENOMEM;
+		goto errout;
+	}
+
+	/*
+	 * We're going to initialize the dblist while we're at it.
+	 */
+	if (fs->dblist) {
+		ext2fs_free_dblist(fs->dblist);
+		fs->dblist = NULL;
+	}
+	retval = ext2fs_init_dblist(fs, 0);
+	if (retval)
+		return retval;
+
+	retval = ext2fs_get_next_inode(scan, &ino, &inode);
+	if (retval) goto errout;
+	
+	if (flags & RESIZE_PERCENT_COMPLETE) {
+		retval = ext2fs_progress_init(&progress,
+		      "Finding directories", 30, 40,
+		      fs->super->s_inodes_count, 0);
+		if (retval)
+			return retval;
+	}
+	
+	while (ino) {
+		if ((inode.i_links_count == 0) ||
+		    !ext2fs_inode_has_valid_blocks(&inode) ||
+		    !LINUX_S_ISDIR(inode.i_mode))
+			goto next;
+		
+		pb.ino = ino;
+		pb.inode = &inode;
+
+		retval = ext2fs_block_iterate2(fs, ino, 0, block_buf,
+					      process_block, &pb);
+		if (retval)
+			goto errout;
+		if (pb.error) {
+			retval = pb.error;
+			goto errout;
+		}
+
+	next:
+		if (progress)
+			ext2fs_progress_update(progress, ino);
+		retval = ext2fs_get_next_inode(scan, &ino, &inode);
+		if (retval == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE)
+			goto next;
+	}
+	retval = 0;
+
+errout:
+	if (progress)
+		ext2fs_progress_close(progress);
+	if (scan)
+		ext2fs_close_inode_scan(scan);
+	return retval;
+}
+
+
+errcode_t ext2fs_inode_move(ext2_resize_t rfs)
+{
+	ino_t			ino, new;
+	struct ext2_inode 	inode;
+	ext2_inode_scan 	scan = NULL;
+	ext2_extent		imap;
+	errcode_t		retval;
+	int			group;
+	struct istruct 		is;
+	struct callback_info	callback_info;
+	ext2_sim_progmeter 	progress = 0; 
+
+	if (rfs->old_fs->group_desc_count <=
+	    rfs->new_fs->group_desc_count)
+		return 0;
+
+	retval = ext2fs_create_extent_table(&imap, 0);
+	if (retval)
+		return retval;
+
+	retval = ext2fs_open_inode_scan(rfs->old_fs, 0, &scan);
+	if (retval) goto errout;
+
+	retval = ext2fs_inode_scan_goto_blockgroup(scan,
+				   rfs->new_fs->group_desc_count);
+	if (retval) goto errout;
+
+	
+	if (rfs->flags & RESIZE_PERCENT_COMPLETE) {
+		callback_info.offset = rfs->new_fs->group_desc_count;
+	
+		group = (rfs->old_fs->group_desc_count -
+			 rfs->new_fs->group_desc_count);
+	
+		retval = ext2fs_progress_init(&progress,
+		      "Moving inodes", 30, 40, group, 0);
+		if (retval)
+			return retval;
+		ext2fs_set_inode_callback(scan, progress_callback,
+					  &callback_info);
+	}
+	callback_info.progress = progress;
+
+	new = EXT2_FIRST_INODE(rfs->new_fs->super);
+	/*
+	 * First, copy all of the inodes that need to be moved
+	 * elsewhere in the inode table
+	 */
+	while (1) {
+		retval = ext2fs_get_next_inode(scan, &ino, &inode);
+		if (retval) goto errout;
+
+		if (!ino)
+			break;
+		
+		if (!ext2fs_test_inode_bitmap(rfs->old_fs->inode_map, ino)) 
+			continue;
+
+		/*
+		 * Find a new inode
+		 */
+		while (1) { 
+			if (!ext2fs_test_inode_bitmap(rfs->new_fs->inode_map, 
+						      new))
+				break;
+			new++;
+			if (new > rfs->new_fs->super->s_inodes_count) {
+				retval = ENOSPC;
+				goto errout;
+			}
+		}
+		ext2fs_mark_inode_bitmap(rfs->new_fs->inode_map, new);
+		retval = ext2fs_write_inode(rfs->old_fs, new, &inode);
+		if (retval) goto errout;
+
+		group = (new-1) / EXT2_INODES_PER_GROUP(rfs->new_fs->super);
+		if (LINUX_S_ISDIR(inode.i_mode))
+			rfs->new_fs->group_desc[group].bg_used_dirs_count++;
+		
+		if (rfs->flags & RESIZE_DEBUG_INODEMAP)
+			printf("Inode moved %ld->%ld\n", ino, new);
+
+		ext2fs_add_extent_entry(imap, ino, new);
+	}
+	io_channel_flush(rfs->new_fs->io);
+	if (progress) {
+		ext2fs_progress_close(progress);
+		progress = 0;
+	}
+	/*
+	 * Get the list of directory blocks, if necessary
+	 */
+	if (!rfs->old_fs->dblist) {
+		retval = get_dblist(rfs->old_fs, rfs->flags);
+		if (retval) goto errout;
+	}
+	/*
+	 * Now, we iterate over all of the directories to update the
+	 * inode references
+	 */
+	if (rfs->flags & RESIZE_PERCENT_COMPLETE) {
+		retval = ext2fs_progress_init(&progress,
+		      "Updating inode references", 30, 40,
+		      ext2fs_dblist_count(rfs->old_fs->dblist), 0);
+		if (retval)
+			return retval;
+	}
+	is.imap = imap;
+	is.flags = rfs->flags;
+	is.num = 0;
+	is.progress = progress;
+
+	retval = ext2fs_dblist_dir_iterate(rfs->old_fs->dblist,
+					   DIRENT_FLAG_INCLUDE_EMPTY, 0,
+					   check_and_change_inodes, &is);
+	/* if (retval) goto errout; */
+
+errout:
+	if (progress)
+		ext2fs_progress_close(progress);
+	ext2fs_free_extent_table(imap);
+	if (scan)
+		ext2fs_close_inode_scan(scan);
+	return retval;
+}
+
diff --git a/resize/extent.c b/resize/extent.c
new file mode 100644
index 0000000..0f0ef4c
--- /dev/null
+++ b/resize/extent.c
@@ -0,0 +1,224 @@
+/*
+ * extent.c --- ext2 extent abstraction
+ *
+ * This abstraction is used to provide a compact way of representing a
+ * translation table, for moving multiple contiguous ranges (extents)
+ * of blocks or inodes.
+ *
+ * Copyright (C) 1997 Theodore Ts'o
+ * 
+ * %Begin-Header%
+ * All rights reserved.
+ * %End-Header%
+ */
+
+#include "resize2fs.h"
+
+struct ext2_extent_entry {
+	__u32	old, new;
+	int	size;
+};
+
+struct _ext2_extent {
+	struct ext2_extent_entry *list;
+	int	cursor;
+	int	size;
+	int	num;
+	int	sorted;
+};
+
+/*
+ * Create an extent table
+ */
+errcode_t ext2fs_create_extent_table(ext2_extent *ret_extent, int size) 
+{
+	ext2_extent	new;
+
+	new = malloc(sizeof(struct _ext2_extent));
+	if (!new)
+		return ENOMEM;
+	memset(new, 0, sizeof(struct _ext2_extent));
+
+	new->size = size ? size : 50;
+	new->cursor = 0;
+	new->num = 0;
+	new->sorted = 1;
+
+	new->list = malloc(sizeof(struct ext2_extent_entry) * new->size);
+	if (!new->list) {
+		free(new);
+		return ENOMEM;
+	}
+	memset(new->list, 0, sizeof(struct ext2_extent_entry) * new->size);
+	*ret_extent = new;
+	return 0;
+}
+
+/*
+ * Free an extent table
+ */
+void ext2fs_free_extent_table(ext2_extent extent)
+{
+	if (extent->list)
+		free(extent->list);
+	extent->list = 0;
+	extent->size = 0;
+	extent->num = 0;
+	free(extent);
+}
+
+/*
+ * Add an entry to the extent table
+ */
+errcode_t ext2fs_add_extent_entry(ext2_extent extent, __u32 old, __u32 new)
+{
+	struct ext2_extent_entry *p;
+	int	newsize;
+	int	curr;
+	struct	ext2_extent_entry *ent;
+
+	if (extent->num >= extent->size) {
+		newsize = extent->size + 100;
+		p = realloc(extent->list,
+			    sizeof(struct ext2_extent_entry) * newsize);
+		if (!p)
+			return ENOMEM;
+		extent->list = p;
+		extent->size = newsize;
+	}
+	curr = extent->num;
+	ent = extent->list + curr;
+	if (curr) {
+		/*
+		 * Check to see if this can be coalesced with the last
+		 * extent
+		 */
+		ent--;
+		if ((ent->old + ent->size == old) &&
+		    (ent->new + ent->size == new)) {
+			ent->size++;
+			return 0;
+		}
+		/*
+		 * Now see if we're going to ruin the sorting
+		 */
+		if (ent->old + ent->size > old)
+			extent->sorted = 0;
+		ent++;
+	}
+	ent->old = old;
+	ent->new = new;
+	ent->size = 1;
+	extent->num++;
+	return 0;
+}
+
+/*
+ * Helper function for qsort
+ */
+static int extent_cmp(const void *a, const void *b)
+{
+	const struct ext2_extent_entry *db_a = a;
+	const struct ext2_extent_entry *db_b = b;
+	
+	return (db_a->old - db_b->old);
+}	
+
+/*
+ * Given an inode map and inode number, look up the old inode number
+ * and return the new inode number
+ */
+__u32 ext2fs_extent_translate(ext2_extent extent, __u32 old)
+{
+	int	low, high, mid;
+	ino_t	lowval, highval;
+	float	range;
+
+	if (!extent->sorted) {
+		qsort(extent->list, extent->num,
+		      sizeof(struct ext2_extent_entry), extent_cmp);
+		extent->sorted = 1;
+	}
+	low = 0;
+	high = extent->num-1;
+	while (low <= high) {
+#if 0
+		mid = (low+high)/2;
+#else
+		if (low == high)
+			mid = low;
+		else {
+			/* Interpolate for efficiency */
+			lowval = extent->list[low].old;
+			highval = extent->list[high].old;
+
+			if (old < lowval)
+				range = 0;
+			else if (old > highval)
+				range = 1;
+			else 
+				range = ((float) (old - lowval)) /
+					(highval - lowval);
+			mid = low + ((int) (range * (high-low)));
+		}
+#endif
+		if ((old >= extent->list[mid].old) &&
+		    (old < extent->list[mid].old + extent->list[mid].size))
+			return (extent->list[mid].new +
+				(old - extent->list[mid].old));
+		if (old < extent->list[mid].old)
+			high = mid-1;
+		else
+			low = mid+1;
+	}
+	return 0;
+}
+
+/*
+ * For debugging only
+ */
+void ext2fs_extent_dump(ext2_extent extent, FILE *out)
+{
+	int	i;
+	struct ext2_extent_entry *ent;
+	
+	fputs("# Extent dump:\n", out);
+	fprintf(out, "#\tNum=%d, Size=%d, Cursor=%d, Sorted=%d\n",
+	       extent->num, extent->size, extent->cursor, extent->sorted);
+	for (i=0, ent=extent->list; i < extent->num; i++, ent++) {
+		fprintf(out, "#\t\t %u -> %u (%d)\n", ent->old,
+			ent->new, ent->size);
+	}
+}
+
+/*
+ * Iterate over the contents of the extent table
+ */
+errcode_t ext2fs_iterate_extent(ext2_extent extent, __u32 *old,
+				__u32 *new, int *size)
+{
+	struct ext2_extent_entry *ent;
+	
+	if (!old) {
+		extent->cursor = 0;
+		return 0;
+	}
+
+	if (extent->cursor >= extent->num) {
+		*old = 0;
+		*new = 0;
+		*size = 0;
+		return 0;
+	}
+
+	ent = extent->list + extent->cursor++;
+
+	*old = ent->old;
+	*new = ent->new;
+	*size = ent->size;
+	return 0;
+}
+	
+	
+		
+	       
diff --git a/resize/inodemap.c b/resize/inodemap.c
deleted file mode 100644
index 8f419e6..0000000
--- a/resize/inodemap.c
+++ /dev/null
@@ -1,250 +0,0 @@
-/*
- * inodemap.c --- ext2resizer indoe mapper
- *
- * Copyright (C) 1997 Theodore Ts'o
- * 
- * %Begin-Header%
- * All rights reserved.
- * %End-Header%
- */
-
-#include "resize2fs.h"
-
-struct inode_map_entry {
-	ino_t	old, new;
-};
-
-struct inode_map_struct {
-	struct inode_map_entry *list;
-	int	size;
-	int	num;
-	int	sorted;
-};
-
-typedef struct inode_map_struct *inode_map;
-
-/*
- * Create inode map table
- */
-static errcode_t create_inode_map(inode_map *imap, int size) 
-{
-	inode_map	new;
-
-	new = malloc(sizeof(struct inode_map_struct));
-	if (!new)
-		return ENOMEM;
-	memset(new, 0, sizeof(struct inode_map_struct));
-
-	new->size = size ? size : 50;
-	new->num = 0;
-	new->sorted = 1;
-
-	new->list = malloc(sizeof(struct inode_map_struct) * new->size);
-	if (!new->list) {
-		free(new);
-		return ENOMEM;
-	}
-	*imap = new;
-	return 0;
-}
-
-/*
- * Add an entry to the inode table map
- */
-static errcode_t add_inode_map_entry(inode_map imap, ino_t old, ino_t new)
-{
-	struct inode_map_entry *p;
-	int	newsize;
-
-	if (imap->num >= imap->size) {
-		newsize = imap->size + 100;
-		p = realloc(imap->list,
-			    sizeof(struct inode_map_struct) * newsize);
-		if (!p)
-			return ENOMEM;
-		imap->list = p;
-		imap->size = newsize;
-	}
-	if (imap->num) {
-		if (imap->list[imap->num-1].old > old)
-			imap->sorted = 0;
-	}
-	imap->list[imap->num].old = old;
-	imap->list[imap->num].new = new;
-	imap->num++;
-	return 0;
-}
-
-/*
- * Helper function for qsort
- */
-static int inode_map_cmp(const void *a, const void *b)
-{
-	const struct inode_map_entry *db_a =
-		(const struct inode_map_entry *) a;
-	const struct inode_map_entry *db_b =
-		(const struct inode_map_entry *) b;
-	
-	return (db_a->old - db_b->old);
-}	
-
-/*
- * Given an inode map and inode number, look up the old inode number
- * and return the new inode number
- */
-static ino_t inode_map_translate(inode_map imap, ino_t old)
-{
-	int	low, high, mid;
-	ino_t	lowval, highval;
-	float	range;
-
-	if (!imap->sorted) {
-		qsort(imap->list, imap->num,
-		      sizeof(struct inode_map_entry), inode_map_cmp);
-		imap->sorted = 1;
-	}
-	low = 0;
-	high = imap->num-1;
-	while (low <= high) {
-#if 0
-		mid = (low+high)/2;
-#else
-		if (low == high)
-			mid = low;
-		else {
-			/* Interpolate for efficiency */
-			lowval = imap->list[low].old;
-			highval = imap->list[high].old;
-
-			if (old < lowval)
-				range = 0;
-			else if (old > highval)
-				range = 1;
-			else 
-				range = ((float) (old - lowval)) /
-					(highval - lowval);
-			mid = low + ((int) (range * (high-low)));
-		}
-#endif
-		if (old == imap->list[mid].old)
-			return imap->list[mid].new;
-		if (old < imap->list[mid].old)
-			high = mid-1;
-		else
-			low = mid+1;
-	}
-	return 0;
-}
-
-struct istruct {
-	inode_map	imap;
-	int		flags;
-};
-
-int check_and_change_inodes(ino_t dir, int entry,
-			    struct ext2_dir_entry *dirent, int offset,
-			    int	blocksize, char *buf, void *private)
-{
-	struct istruct *is = private;
-	ino_t	new;
-
-	if (!dirent->inode)
-		return 0;
-
-	new = inode_map_translate(is->imap, dirent->inode);
-
-	if (!new)
-		return 0;
-	if (is->flags & RESIZE_DEBUG_INODEMAP)
-		printf("Inode translate (dir=%ld, name=%.*s, %ld->%ld)\n",
-		       dir, dirent->name_len, dirent->name,
-		       dirent->inode, new);
-
-	dirent->inode = new;
-
-	return DIRENT_CHANGED;
-}
-
-errcode_t ext2fs_inode_move(ext2_resize_t rfs)
-{
-	ino_t			ino, start, end, new;
-	struct ext2_inode 	inode;
-	ext2_inode_scan 	scan;
-	inode_map		imap;
-	errcode_t		retval;
-	int			group;
-	struct istruct 		is;
-	
-	if (rfs->old_fs->group_desc_count <=
-	    rfs->new_fs->group_desc_count)
-		return 0;
-
-	retval = create_inode_map(&imap, 0);
-	if (retval)
-		return retval;
-
-	retval = ext2fs_open_inode_scan(rfs->old_fs, 0, &scan);
-	if (retval)
-		return retval;
-
-	retval = ext2fs_inode_scan_goto_blockgroup(scan,
-				   rfs->new_fs->group_desc_count);
-	if (retval) {
-		ext2fs_close_inode_scan(scan);
-		return retval;
-	}
-
-	new = EXT2_FIRST_INODE(rfs->new_fs->super);
-
-	/*
-	 * First, copy all of the inodes that need to be moved
-	 * elsewhere in the inode table
-	 */
-	while (1) {
-		retval = ext2fs_get_next_inode(scan, &ino, &inode);
-		if (retval)
-			return retval;
-		if (!ino)
-			break;
-		
-		if (!ext2fs_test_inode_bitmap(rfs->old_fs->inode_map, ino)) 
-			continue;
-
-		/*
-		 * Find a new inode
-		 */
-		while (1) { 
-			if (!ext2fs_test_inode_bitmap(rfs->new_fs->inode_map, 
-						      new))
-				break;
-			new++;
-			if (new > rfs->new_fs->super->s_inodes_count)
-				return ENOSPC;
-		}
-		ext2fs_mark_inode_bitmap(rfs->new_fs->inode_map, new);
-		retval = ext2fs_write_inode(rfs->old_fs, new, &inode);
-		if (retval)
-			return retval;
-		group = (new-1) / EXT2_INODES_PER_GROUP(rfs->new_fs->super);
-		if (LINUX_S_ISDIR(inode.i_mode))
-			rfs->new_fs->group_desc[group].bg_used_dirs_count++;
-		
-		if (rfs->flags & RESIZE_DEBUG_INODEMAP)
-			printf("Inode moved %ld->%ld\n", ino, new);
-
-		add_inode_map_entry(imap, ino, new);
-	}
-	/*
-	 * Now, we iterate over all of the directories to update the
-	 * inode references
-	 */
-	is.imap = imap;
-	is.flags = rfs->flags;
-	retval = ext2fs_dblist_dir_iterate(rfs->old_fs->dblist, 0, 0,
-					   check_and_change_inodes, &is);
-	if (retval)
-		return retval;
-
-	return 0;
-}
-
diff --git a/resize/main.c b/resize/main.c
index 3d3c589..b5e5a7f 100644
--- a/resize/main.c
+++ b/resize/main.c
@@ -8,6 +8,13 @@
  * %End-Header%
  */
 
+#ifdef HAVE_GETOPT_H
+#include <getopt.h>
+#endif
+#include <fcntl.h>
+#include <sys/ioctl.h>
+
+
 #include "resize2fs.h"
 
 #define E2FSPROGS_VERSION "1.10"
@@ -15,9 +22,9 @@
 
 char *program_name, *device_name;
 
-static volatile void usage (char *program_name)
+static volatile void usage (char *prog)
 {
-	fprintf (stderr, "usage: %s device new-size\n", program_name);
+	fprintf (stderr, "usage: %s [-d debug_flags] [-p] [-F] device new-size\n", prog);
 	exit (1);
 }
 
@@ -27,6 +34,8 @@
 	ext2_filsys	fs;
 	int		c;
 	int		flags = 0;
+	int		flush = 0;
+	int		fd;
 	blk_t		new_size;
 	io_manager	io_ptr;
 
@@ -36,11 +45,14 @@
 	if (argc && *argv)
 		program_name = *argv;
 	
-	while ((c = getopt (argc, argv, "d:hp")) != EOF) {
+	while ((c = getopt (argc, argv, "d:Fhp")) != EOF) {
 		switch (c) {
 		case 'h':
 			usage(program_name);
 			break;
+		case 'F':
+			flush = 1;
+			break;
 		case 'd':
 			flags |= atoi(optarg);
 			break;
@@ -57,6 +69,27 @@
 	new_size = atoi(argv[optind++]);
 	initialize_ext2_error_table();
 
+	if (flush) {
+#ifdef BLKFLSBUF
+		fd = open(device_name, O_RDONLY, 0);
+
+		if (fd < 0) {
+			com_err("open", errno, "while opening %s for flushing",
+				device_name);
+			exit(1);
+		}
+		if (ioctl(fd, BLKFLSBUF, 0) < 0) {
+			com_err("BLKFLSBUF", errno, "while trying to flush %s",
+				device_name);
+			exit(1);
+		}
+		close(fd);
+#else
+		fprintf(stderr, "BLKFLSBUF not supported");
+		exit(1);
+#endif /* BLKFLSBUF */
+	}
+
 	if (flags & RESIZE_DEBUG_IO) {
 		io_ptr = test_io_manager;
 		test_io_backing_manager = unix_io_manager;
diff --git a/resize/resize2fs.8.in b/resize/resize2fs.8.in
index ee2c933..ac2c7fd 100644
--- a/resize/resize2fs.8.in
+++ b/resize/resize2fs.8.in
@@ -7,23 +7,56 @@
 .SH SYNOPSIS
 .B resize2fs
 [
-device
+.B \-d 
+.I debug-flags
 ]
+[
+.B \-p
+]
+[
+.B \-F
+]
+.I device
+.I size
 .SH DESCRIPTION
 The 
-.B resize2fs
+.B resize2fs 
 program will resize ext2 file systems.  It can be used to enlarge or
-shrink an ext2 file system.
-.br
+shrink an ext2 file system so that it will have 
+.I size 
+blocks.
+The device containing the ext2 filesystem is specified by the 
 .I device
-is the special file corresponding to the device containing the ext2
-file system (e.g /dev/hdXX).
+parameter.
 .SH OPTIONS
 .TP
-.I -h
-Displays a help message.
+.I \-d debug-flags
+Turns on various resize2fs debugging features.  
+.I debug-flags
+should be computed by adding the numbers of the desired features 
+from the following list:
+.br
+\	1\	\-\ Print out all disk I/O 
+.br
+\	2\	\-\ Debug block relocations
+.br
+\	8\	\-\ Debug inode relocations
+.br
+\	16\	\-\ Debug moving the inode table
+.TP
+.I \-p
+Prints out a percentage completion bars for each 
+.B resize2fs
+operation, so that the user can keep track of what
+the program is doing.
+.TP 
+.I \-F
+Flush the filesystem device's buffer caches before beginning.  Only
+really useful for doing 
+.B resize2fs
+time trials.
 .SH AUTHOR
-.B debugfs
+.B resize2fs
 was written by Theodore Ts'o <tytso@mit.edu>.
 .SH SEE ALSO
 .BR dumpe2fs (8),
diff --git a/resize/resize2fs.c b/resize/resize2fs.c
index b90c075..66e0b20 100644
--- a/resize/resize2fs.c
+++ b/resize/resize2fs.c
@@ -29,13 +29,14 @@
 {
 	ext2_filsys fs;
 	int		overhead = 0;
-	int		rem;
+	int		rem, adj = 0;
 	errcode_t	retval;
 	ino_t		real_end;
 	blk_t		blk, group_block;
 	unsigned long	i, j;
 	struct ext2_group_desc *new;
 	int		old_numblocks, numblocks, adjblocks;
+	ext2_sim_progmeter progress = 0;
 	
 	fs = rfs->new_fs;
 	fs->super->s_blocks_count = new_size;
@@ -96,13 +97,20 @@
 			(fs->super->s_blocks_count - blk);
 
 	/*
+	 * Adjust the number of reserved blocks
+	 */
+	blk = rfs->old_fs->super->s_r_blocks_count * 100 /
+		rfs->old_fs->super->s_blocks_count;
+	fs->super->s_r_blocks_count = ((fs->super->s_blocks_count * blk)
+				       / 100);
+
+	/*
 	 * Adjust the bitmaps for size
 	 */
 	retval = ext2fs_resize_inode_bitmap(fs->super->s_inodes_count,
 					    fs->super->s_inodes_count,
 					    fs->inode_map);
-	if (retval)
-		return retval;
+	if (retval) goto errout;
 	
 	real_end = ((EXT2_BLOCKS_PER_GROUP(fs->super)
 		     * fs->group_desc_count)) - 1 +
@@ -110,8 +118,7 @@
 	retval = ext2fs_resize_block_bitmap(fs->super->s_blocks_count-1,
 					    real_end, fs->block_map);
 
-	if (retval)
-		return retval;
+	if (retval) goto errout;
 
 	/*
 	 * Reallocate the group descriptors as necessary.
@@ -127,8 +134,10 @@
 	/*
 	 * Fix the count of the last (old) block group
 	 */
-	if (rfs->old_fs->group_desc_count > fs->group_desc_count)
-		return 0;
+	if (rfs->old_fs->group_desc_count > fs->group_desc_count) {
+		retval = 0;
+		goto errout;
+	}
 	old_numblocks = (rfs->old_fs->super->s_blocks_count -
 			 rfs->old_fs->super->s_first_data_block) %
 				 rfs->old_fs->super->s_blocks_per_group;
@@ -148,14 +157,26 @@
 	/*
 	 * Initialize the new block group descriptors
 	 */
-	if (rfs->old_fs->group_desc_count >= fs->group_desc_count)
-		return 0;
+	if (rfs->old_fs->group_desc_count >= fs->group_desc_count) {
+		retval = 0;
+		goto errout;
+	}
 	rfs->itable_buf = malloc(fs->blocksize * fs->inode_blocks_per_group);
-	if (!rfs->itable_buf)
-		return ENOMEM;
+	if (!rfs->itable_buf) {
+		retval = ENOMEM;
+		goto errout;
+	}
 	memset(rfs->itable_buf, 0, fs->blocksize * fs->inode_blocks_per_group);
 	group_block = fs->super->s_first_data_block +
 		rfs->old_fs->group_desc_count * fs->super->s_blocks_per_group;
+
+	if (rfs->flags & RESIZE_PERCENT_COMPLETE) {
+		adj = rfs->old_fs->group_desc_count;
+		retval = ext2fs_progress_init(&progress,
+		      "Initializing inode table", 30, 40,
+		      fs->group_desc_count - adj, 0);
+		if (retval) goto errout;
+	}
 	for (i = rfs->old_fs->group_desc_count;
 	     i < fs->group_desc_count; i++) {
 		memset(&fs->group_desc[i], 0,
@@ -189,8 +210,7 @@
 		fs->group_desc[i].bg_used_dirs_count = 0;
 
 		retval = ext2fs_allocate_group_table(fs, i, 0);
-		if (retval)
-			return retval;
+		if (retval) goto errout;
 
 		/*
 		 * Write out the new inode table
@@ -199,54 +219,155 @@
 					      fs->group_desc[i].bg_inode_table,
 					      fs->inode_blocks_per_group,
 					      rfs->itable_buf);
-		if (retval)
-			return retval;
+		if (retval) goto errout;
+
+		/* io_channel_flush(fs->io); */
+		if (progress)
+			ext2fs_progress_update(progress, i - adj + 1);
 		
 		group_block += fs->super->s_blocks_per_group;
 	}
+	io_channel_flush(fs->io);
+	retval = 0;
+
+errout:
+	if (progress)
+		ext2fs_progress_close(progress);
+	return retval;
+}
+
+/*
+ * This helper function creates a block bitmap with all of the
+ * filesystem meta-data blocks.
+ */
+static errcode_t mark_table_blocks(ext2_filsys fs,
+				   ext2fs_block_bitmap *ret_bmap)
+{
+	blk_t			block, b;
+	int			i,j;
+	ext2fs_block_bitmap	bmap;
+	errcode_t		retval;
+
+	retval = ext2fs_allocate_block_bitmap(fs, "meta-data blocks", &bmap);
+	if (retval)
+		return retval;
+	
+	block = fs->super->s_first_data_block;
+	for (i = 0; i < fs->group_desc_count; i++) {
+		if (ext2fs_bg_has_super(fs, i)) {
+			/*
+			 * Mark this group's copy of the superblock
+			 */
+			ext2fs_mark_block_bitmap(bmap, block);
+		
+			/*
+			 * Mark this group's copy of the descriptors
+			 */
+			for (j = 0; j < fs->desc_blocks; j++)
+				ext2fs_mark_block_bitmap(bmap, block + j + 1);
+		}
+		
+		/*
+		 * Mark the blocks used for the inode table
+		 */
+		for (j = 0, b = fs->group_desc[i].bg_inode_table;
+		     j < fs->inode_blocks_per_group;
+		     j++, b++)
+			ext2fs_mark_block_bitmap(bmap, b);
+			    
+		/*
+		 * Mark block used for the block bitmap 
+		 */
+		ext2fs_mark_block_bitmap(bmap,
+					 fs->group_desc[i].bg_block_bitmap);
+		/*
+		 * Mark block used for the inode bitmap 
+		 */
+		ext2fs_mark_block_bitmap(bmap,
+					 fs->group_desc[i].bg_inode_bitmap);
+		block += fs->super->s_blocks_per_group;
+	}
+	*ret_bmap = bmap;
 	return 0;
 }
 
+
+
+/*
+ * Some helper CPP macros
+ */
+#define FS_BLOCK_BM(fs, i) ((fs)->group_desc[(i)].bg_block_bitmap)
+#define FS_INODE_BM(fs, i) ((fs)->group_desc[(i)].bg_inode_bitmap)
+#define FS_INODE_TB(fs, i) ((fs)->group_desc[(i)].bg_inode_table)
+
+#define IS_BLOCK_BM(fs, i, blk) ((blk) == FS_BLOCK_BM((fs),(i)))
+#define IS_INODE_BM(fs, i, blk) ((blk) == FS_INODE_BM((fs),(i)))
+
+#define IS_INODE_TB(fs, i, blk) (((blk) >= FS_INODE_TB((fs), (i))) && \
+				 ((blk) < (FS_INODE_TB((fs), (i)) + \
+					   (fs)->inode_blocks_per_group)))
+
 /*
  * This routine marks and unmarks reserved blocks in the new block
  * bitmap.  It also determines which blocks need to be moved and
  * places this information into the move_blocks bitmap.
  */
-static errcode_t determine_relocations(ext2_resize_t rfs)
+static errcode_t blocks_to_move(ext2_resize_t rfs)
 {
-	int	i, j, max, adj;
+	int	i, j, max;
 	blk_t	blk, group_blk;
 	unsigned long old_blocks, new_blocks;
 	errcode_t	retval;
-	ext2_filsys 	fs = rfs->new_fs;
+	ext2_filsys 	fs, old_fs;
+	ext2fs_block_bitmap	meta_bmap;
 
-	retval = ext2fs_allocate_block_bitmap(rfs->old_fs,
-					      "blocks to be moved",
+	fs = rfs->new_fs;
+	old_fs = rfs->old_fs;
+	if (old_fs->super->s_blocks_count > fs->super->s_blocks_count)
+		fs = rfs->old_fs;
+	
+	retval = ext2fs_allocate_block_bitmap(fs, "reserved blocks",
 					      &rfs->reserve_blocks);
 	if (retval)
 		return retval;
 
+	retval = ext2fs_allocate_block_bitmap(fs, "blocks to be moved",
+					      &rfs->move_blocks);
+	if (retval)
+		return retval;
+
+	retval = mark_table_blocks(fs, &meta_bmap);
+	if (retval)
+		return retval;
+
+	fs = rfs->new_fs;
+	
 	/*
 	 * If we're shrinking the filesystem, we need to move all of
 	 * the blocks that don't fit any more
 	 */
 	for (blk = fs->super->s_blocks_count;
-	     blk < rfs->old_fs->super->s_blocks_count; blk++) {
-		if (ext2fs_test_block_bitmap(rfs->old_fs->block_map, blk))
+	     blk < old_fs->super->s_blocks_count; blk++) {
+		if (ext2fs_test_block_bitmap(old_fs->block_map, blk) &&
+		    !ext2fs_test_block_bitmap(meta_bmap, blk)) {
+			ext2fs_mark_block_bitmap(rfs->move_blocks, blk);
 			rfs->needed_blocks++;
+		}
 		ext2fs_mark_block_bitmap(rfs->reserve_blocks, blk);
 	}
 	
-	old_blocks = rfs->old_fs->desc_blocks;
+	old_blocks = old_fs->desc_blocks;
 	new_blocks = fs->desc_blocks;
 
-	if (old_blocks == new_blocks)
-		return 0;
+	if (old_blocks == new_blocks) {
+		retval = 0;
+		goto errout;
+	}
 
 	max = fs->group_desc_count;
-	if (max > rfs->old_fs->group_desc_count)
-		max = rfs->old_fs->group_desc_count;
-	group_blk = rfs->old_fs->super->s_first_data_block;
+	if (max > old_fs->group_desc_count)
+		max = old_fs->group_desc_count;
+	group_blk = old_fs->super->s_first_data_block;
 	/*
 	 * If we're reducing the number of descriptor blocks, this
 	 * makes life easy.  :-)   We just have to mark some extra
@@ -258,15 +379,16 @@
 				group_blk += fs->super->s_blocks_per_group;
 				continue;
 			}
-			for (blk = group_blk+1+old_blocks;
-			     blk < group_blk+1+new_blocks; blk++) {
+			for (blk = group_blk+1+new_blocks;
+			     blk < group_blk+1+old_blocks; blk++) {
 				ext2fs_unmark_block_bitmap(fs->block_map,
 							   blk);
 				rfs->needed_blocks--;
 			}
 			group_blk += fs->super->s_blocks_per_group;
 		}
-		return 0;
+		retval = 0;
+		goto errout;
 	}
 	/*
 	 * If we're increasing the number of descriptor blocks, life
@@ -283,28 +405,26 @@
 
 			/*
 			 * Check to see if we overlap with the inode
-			 * or block bitmap
+			 * or block bitmap, or the inode tables.  If
+			 * not, and the block is in use, then mark it
+			 * as a block to be moved.
 			 */
-			if (blk == fs->group_desc[i].bg_block_bitmap) {
-				fs->group_desc[i].bg_block_bitmap = 0;
+			if (IS_BLOCK_BM(fs, i, blk)) {
+				FS_BLOCK_BM(fs, i) = 0;
+				rfs->needed_blocks++;
+			} else if (IS_INODE_BM(fs, i, blk)) {
+				FS_INODE_BM(fs, i) = 0;
+				rfs->needed_blocks++;
+			} else if (IS_INODE_TB(fs, i, blk)) {
+				FS_INODE_TB(fs, i) = 0;
+				rfs->needed_blocks++;
+			} else if (ext2fs_test_block_bitmap(old_fs->block_map,
+							    blk) &&
+				   !ext2fs_test_block_bitmap(meta_bmap, blk)) {
+				ext2fs_mark_block_bitmap(rfs->move_blocks,
+							 blk);
 				rfs->needed_blocks++;
 			}
-			if (blk == fs->group_desc[i].bg_inode_bitmap) {
-				fs->group_desc[i].bg_inode_bitmap = 0;
-				rfs->needed_blocks++;
-			}
-			/*
-			 * Check to see if we overlap with the inode
-			 * table
-			 */
-			if (blk < fs->group_desc[i].bg_inode_table)
-				continue;
-			if (blk >= (fs->group_desc[i].bg_inode_table +
-				    fs->inode_blocks_per_group))
-				continue;
-			blk = fs->group_desc[i].bg_inode_table +
-				fs->inode_blocks_per_group - 1;
-			fs->group_desc[i].bg_inode_table = 0;
 		}
 		if (fs->group_desc[i].bg_inode_table &&
 		    fs->group_desc[i].bg_inode_bitmap &&
@@ -312,9 +432,8 @@
 			goto next_group;
 
 		/*
-		 * Allocate the missing bitmap and inode table
-		 * structures, passing in rfs->reserve_blocks to
-		 * prevent a conflict.  
+		 * Reserve the existing meta blocks that we know
+		 * aren't to be moved.
 		 */
 		if (fs->group_desc[i].bg_block_bitmap)
 			ext2fs_mark_block_bitmap(rfs->reserve_blocks,
@@ -328,19 +447,34 @@
 				ext2fs_mark_block_bitmap(rfs->reserve_blocks,
 							 blk);
 
+		/*
+		 * Allocate the missing data structures
+		 */
 		retval = ext2fs_allocate_group_table(fs, i,
 						     rfs->reserve_blocks);
 		if (retval)
-			return retval;
+			goto errout;
 
 		/*
-		 * Now make sure these blocks are reserved in the new
-		 * block bitmap
+		 * For those structures that have changed, we need to
+		 * do bookkeepping.
 		 */
-		ext2fs_mark_block_bitmap(fs->block_map,
-					 fs->group_desc[i].bg_block_bitmap);
-		ext2fs_mark_block_bitmap(fs->block_map,
-					 fs->group_desc[i].bg_inode_bitmap);
+		if (FS_BLOCK_BM(old_fs, i) !=
+		    (blk = FS_BLOCK_BM(fs, i))) {
+			ext2fs_mark_block_bitmap(fs->block_map, blk);
+			if (ext2fs_test_block_bitmap(old_fs->block_map, blk) &&
+			    !ext2fs_test_block_bitmap(meta_bmap, blk))
+				ext2fs_mark_block_bitmap(rfs->move_blocks,
+							 blk);
+		}
+		if (FS_INODE_BM(old_fs, i) !=
+		    (blk = FS_INODE_BM(fs, i))) {
+			ext2fs_mark_block_bitmap(fs->block_map, blk);
+			if (ext2fs_test_block_bitmap(old_fs->block_map, blk) &&
+			    !ext2fs_test_block_bitmap(meta_bmap, blk))
+				ext2fs_mark_block_bitmap(rfs->move_blocks,
+							 blk);
+		}
 
 		/*
 		 * The inode table, if we need to relocate it, is
@@ -349,29 +483,25 @@
 		 * can't have the inode table be destroyed during the
 		 * block relocation phase.
 		 */
-		adj = fs->group_desc[i].bg_inode_table -
-			rfs->old_fs->group_desc[i].bg_inode_table;
-		if (!adj)
+		if (FS_INODE_TB(fs, i) == FS_INODE_TB(old_fs, i))
 			goto next_group; /* inode table not moved */
 
-		/*
-		 * Figure out how many blocks we need to have free.
-		 * This takes into account that we need to reserve
-		 * both inode tables, which may be overallping.
-		 */
-		if (adj < 0)
-			adj = -adj;
-		if (adj > fs->inode_blocks_per_group)
-			adj = fs->inode_blocks_per_group;
-		rfs->needed_blocks += fs->inode_blocks_per_group + adj;
+		rfs->needed_blocks += fs->inode_blocks_per_group;
 
 		/*
 		 * Mark the new inode table as in use in the new block
-		 * allocation bitmap.
+		 * allocation bitmap, and move any blocks that might 
+		 * be necessary.
 		 */
 		for (blk = fs->group_desc[i].bg_inode_table, j=0;
-		     j < fs->inode_blocks_per_group ; j++, blk++)
+		     j < fs->inode_blocks_per_group ; j++, blk++) {
 			ext2fs_mark_block_bitmap(fs->block_map, blk);
+			if (ext2fs_test_block_bitmap(old_fs->block_map, blk) &&
+			    !ext2fs_test_block_bitmap(meta_bmap, blk))
+				ext2fs_mark_block_bitmap(rfs->move_blocks,
+							 blk);
+		}
+		
 		/*
 		 * Make sure the old inode table is reserved in the
 		 * block reservation bitmap.
@@ -383,7 +513,13 @@
 	next_group:
 		group_blk += rfs->new_fs->super->s_blocks_per_group;
 	}
-	return 0;
+	retval = 0;
+
+errout:
+	if (meta_bmap)
+		ext2fs_free_block_bitmap(meta_bmap);
+	
+	return retval;
 }
 
 
@@ -393,13 +529,15 @@
  * After this you have to use the rfs->new_fs file handle to read and
  * write inodes.
  */
-errcode_t move_itables(ext2_resize_t rfs)
+static errcode_t move_itables(ext2_resize_t rfs)
 {
 	int		i, n, num, max, size, diff;
 	ext2_filsys	fs = rfs->new_fs;
 	char		*cp;
 	blk_t		old, new;
 	errcode_t	retval, err;
+	ext2_sim_progmeter progress = 0;
+	int		to_move, moved;
 
 	max = fs->group_desc_count;
 	if (max > rfs->old_fs->group_desc_count)
@@ -411,6 +549,25 @@
 		if (!rfs->itable_buf)
 			return ENOMEM;
 	}
+
+	/*
+	 * Figure out how many inode tables we need to move
+	 */
+	to_move = moved = 0;
+	for (i=0; i < max; i++)
+		if (rfs->old_fs->group_desc[i].bg_inode_table !=
+		    fs->group_desc[i].bg_inode_table)
+			to_move++;
+
+	if (to_move == 0)
+		return 0;
+
+	if (rfs->flags & RESIZE_PERCENT_COMPLETE) {
+		retval = ext2fs_progress_init(&progress,
+		      "Moving inode table", 30, 40, to_move, 0);
+		if (retval)
+			return retval;
+	}
 	
 	for (i=0; i < max; i++) {
 		old = rfs->old_fs->group_desc[i].bg_inode_table;
@@ -419,7 +576,7 @@
 		
 		if (rfs->flags & RESIZE_DEBUG_ITABLEMOVE) 
 			printf("Itable move group %d block "
-			       "%ld->%ld (diff %d)\n", 
+			       "%u->%u (diff %d)\n", 
 			       i, old, new, diff);
 		
 		if (!diff)
@@ -458,20 +615,27 @@
 			      diff, rfs->itable_buf - fs->blocksize * diff);
 			if (retval)
 				goto backout;
-		} 
+		}
 		io_channel_flush(fs->io);
+		if (progress)
+			ext2fs_progress_update(progress, ++moved);
 	}
 	ext2fs_flush(rfs->new_fs);
+	io_channel_flush(fs->io);
 	if (rfs->flags & RESIZE_DEBUG_ITABLEMOVE) 
 		printf("Inode table move finished.\n");
+	if (progress)
+		ext2fs_progress_close(progress);
 	return 0;
 	
 backout:
+	if (progress)
+		ext2fs_progress_close(progress);
 	if (rfs->flags & RESIZE_DEBUG_ITABLEMOVE) 
 		printf("Error: %s; now backing out!\n", error_message(retval));
 	while (--i >= 0) {
 		if (rfs->flags & RESIZE_DEBUG_ITABLEMOVE) 
-			printf("Group %d block %ld->%ld\n", i, new, old);
+			printf("Group %d block %u->%u\n", i, new, old);
 		old = rfs->old_fs->group_desc[i].bg_inode_table;
 		new = fs->group_desc[i].bg_inode_table;
 		
@@ -554,7 +718,6 @@
 {
 	ext2_resize_t	rfs;
 	errcode_t	retval;
-	int		bmove_flags;
 
 	retval = ext2fs_read_bitmaps(fs);
 	if (retval)
@@ -579,24 +742,17 @@
 	if (retval)
 		goto errout;
 
-	retval = determine_relocations(rfs);
+	retval = blocks_to_move(rfs);
 	if (retval)
 		goto errout;
 
 	if (rfs->flags & RESIZE_DEBUG_BMOVE)
-		printf("Number of free blocks: %d, Needed: %d\n",
-		       fs->super->s_free_blocks_count, rfs->needed_blocks);
+		printf("Number of free blocks: %d/%d, Needed: %d\n",
+		       rfs->old_fs->super->s_free_blocks_count,
+		       rfs->new_fs->super->s_free_blocks_count,
+		       rfs->needed_blocks);
 	
-	if (rfs->needed_blocks > fs->super->s_free_blocks_count) {
-		retval = ENOSPC;
-		goto errout;
-	}
-
-	bmove_flags = EXT2_BMOVE_GET_DBLIST;
-	if (rfs->flags & RESIZE_DEBUG_BMOVE)
-		bmove_flags |= EXT2_BMOVE_DEBUG;
-	retval = ext2fs_move_blocks(rfs->old_fs, rfs->reserve_blocks,
-				    rfs->new_fs->block_map, bmove_flags);
+	retval = ext2fs_block_move(rfs);
 	if (retval)
 		goto errout;
 
diff --git a/resize/resize2fs.h b/resize/resize2fs.h
index 28297f6..32ddaa0 100644
--- a/resize/resize2fs.h
+++ b/resize/resize2fs.h
@@ -32,6 +32,16 @@
 #endif
 
 /*
+ * For the extent map
+ */
+typedef struct _ext2_extent *ext2_extent;
+
+/*
+ * For the simple progress meter
+ */
+typedef struct ext2_sim_progress *ext2_sim_progmeter;
+
+/*
  * Flags for the resizer; most are debugging flags only
  */
 #define RESIZE_DEBUG_IO			0x0001
@@ -40,6 +50,7 @@
 #define RESIZE_DEBUG_ITABLEMOVE		0x0008
 
 #define RESIZE_PERCENT_COMPLETE		0x0100
+#define RESIZE_VERBOSE			0x0200
 
 /*
  * The core state structure for the ext2 resizer
@@ -50,6 +61,7 @@
 	ext2_filsys	new_fs;
 	ext2_brel	block_relocate;
 	ext2fs_block_bitmap reserve_blocks;
+	ext2fs_block_bitmap move_blocks;
 	int		needed_blocks;
 	int		flags;
 	char		*itable_buf;
@@ -59,3 +71,27 @@
 
 /* prototypes */
 extern errcode_t resize_fs(ext2_filsys fs, blk_t new_size, int flags);
+extern errcode_t ext2fs_inode_move(ext2_resize_t rfs);
+extern errcode_t ext2fs_block_move(ext2_resize_t rfs);
+
+/* extent.c */
+extern errcode_t ext2fs_create_extent_table(ext2_extent *ret_extent,
+					    int size);
+extern void ext2fs_free_extent_table(ext2_extent extent);
+extern errcode_t ext2fs_add_extent_entry(ext2_extent extent,
+					 __u32 old, __u32 new);
+extern __u32 ext2fs_extent_translate(ext2_extent extent, __u32 old);
+extern void ext2fs_extent_dump(ext2_extent extent, FILE *out);
+extern errcode_t ext2fs_iterate_extent(ext2_extent extent, __u32 *old,
+				       __u32 *new, int *size);
+
+/* sim_progress.c */
+extern errcode_t ext2fs_progress_init(ext2_sim_progmeter *ret_prog,
+				      const char *label,
+				      int labelwidth, int barwidth,
+				      __u32 maxdone, int flags);
+extern void ext2fs_progress_update(ext2_sim_progmeter prog,
+					__u32 current);
+extern void ext2fs_progress_close(ext2_sim_progmeter prog);
+
+
diff --git a/resize/sim_progress.c b/resize/sim_progress.c
new file mode 100644
index 0000000..1e30d11
--- /dev/null
+++ b/resize/sim_progress.c
@@ -0,0 +1,106 @@
+/*
+ * sim_progress.c --- simple progress meter
+ */
+
+#include "resize2fs.h"
+
+struct ext2_sim_progress {
+	FILE	*f;
+	char	*label;
+	int	labelwidth;
+	int	barwidth;
+	__u32	maxdone;
+	__u32	current;
+	int	shown;
+	int	flags;
+};
+
+static errcode_t ext2fs_progress_display(ext2_sim_progmeter prog)
+{
+	int	i, width;
+
+	fputs(prog->label, prog->f);
+	width = prog->labelwidth - strlen(prog->label);
+	while (width-- > 0)
+		putc(' ', prog->f);
+	if (prog->labelwidth + prog->barwidth > 80) {
+		fputs("\n", prog->f);
+		for (width = prog->labelwidth; width > 0; width--)
+			putc(' ', prog->f);
+	}
+	for (i=0; i < prog->barwidth; i++)
+		putc('-', prog->f);
+	for (i=0; i < prog->barwidth; i++)
+		putc('\b', prog->f);
+	fflush(prog->f);
+	return 0;
+}
+
+
+void ext2fs_progress_update(ext2_sim_progmeter prog, __u32 current)
+{
+	int		old_level, level, num, i;
+
+	level = prog->barwidth * current / prog->maxdone;
+	old_level = prog->barwidth * prog->current / prog->maxdone;
+	prog->current = current;
+
+	num = level - old_level;
+	if (num == 0)
+		return;
+	
+	if (num > 0) {
+		for (i=0; i < num; i++)
+			putc('X', prog->f);
+	} else {
+		num = -num;
+		for (i=0; i < num; i++)
+			putc('\b', prog->f);
+		for (i=0; i < num; i++)
+			putc('-', prog->f);
+		for (i=0; i < num; i++)
+			putc('\b', prog->f);
+	}
+	fflush(prog->f);
+}
+
+errcode_t ext2fs_progress_init(ext2_sim_progmeter *ret_prog,
+			       const char *label,
+			       int labelwidth, int barwidth,
+			       __u32 maxdone, int flags)
+{
+	ext2_sim_progmeter prog;
+
+	prog = malloc(sizeof(struct ext2_sim_progress));
+	if (!prog)
+		return ENOMEM;
+	memset(prog, 0, sizeof(struct ext2_sim_progress));
+
+	prog->label = malloc(strlen(label)+1);
+	if (!prog->label) {
+		free(prog);
+		return ENOMEM;
+	}
+	strcpy(prog->label, label);
+	prog->labelwidth = labelwidth;
+	prog->barwidth = barwidth;
+	prog->flags = flags;
+	prog->maxdone = maxdone;
+	prog->current = 0;
+	prog->shown = 0;
+	prog->f = stdout;
+	
+	*ret_prog = prog;
+
+	return ext2fs_progress_display(prog);
+}
+
+void ext2fs_progress_close(ext2_sim_progmeter prog)
+{
+
+	if (prog->label)
+		free(prog->label);
+	free(prog);
+	printf("\n");
+	return;
+}
diff --git a/resize/test_extent.c b/resize/test_extent.c
new file mode 100644
index 0000000..dc3a584
--- /dev/null
+++ b/resize/test_extent.c
@@ -0,0 +1,113 @@
+/*
+ * test_extent.c --- tester for the extent abstraction
+ *
+ * Copyright (C) 1997 Theodore Ts'o
+ * 
+ * %Begin-Header%
+ * All rights reserved.
+ * %End-Header%
+ */
+
+#include "resize2fs.h"
+
+void do_test(FILE *in, FILE *out);
+
+void do_test(FILE *in, FILE *out)
+{
+	char		buf[128];
+	char		*cp, *cmd, *arg1, *arg2;
+	__u32		num1, num2;
+	int		size;
+	errcode_t	retval;
+	ext2_extent	extent = 0;
+	const char	*no_table = "# No extent table\n";
+	
+	while (!feof(in)) {
+		if (!fgets(buf, sizeof(buf), in))
+			break;
+		/*
+		 * Ignore comments
+		 */
+		if (buf[0] =='#')
+			continue;
+
+		/*
+		 * Echo command
+		 */
+		fputs(buf, out);
+
+		cp = strchr(buf, '\n');
+		if (cp)
+			*cp = '\0';
+
+		/*
+		 * Parse command line; simple, at most two arguments
+		 */
+		cmd = buf;
+		num1 = num2 = 0;
+		arg1 = arg2 = 0;
+		cp = strchr(buf, ' ');
+		if (cp) {
+			*cp++ = '\0';
+			arg1 = cp;
+			num1 = strtoul(arg1, 0, 0);
+			
+			cp = strchr(cp, ' ');
+		}
+		if (cp) {
+			*cp++ = '\0';
+			arg2 = cp;
+			num2 = strtoul(arg2, 0, 0);
+		}
+		
+		if (!strcmp(cmd, "create")) {
+			retval = ext2fs_create_extent_table(&extent, num1);
+			if (retval) {
+			handle_error:
+				fprintf(out, "# Error: %s\n",
+					error_message(retval));
+				continue;
+			}
+			continue;
+		}
+		if (!extent) {
+			fputs(no_table, out);
+			continue;
+		}		
+		if (!strcmp(cmd, "free")) {
+			ext2fs_free_extent_table(extent);
+			extent = 0;
+		} else if (!strcmp(cmd, "add")) {
+			retval = ext2fs_add_extent_entry(extent, num1, num2);
+			if (retval)
+				goto handle_error;
+		} else if (!strcmp(cmd, "lookup")) {
+			num2 = ext2fs_extent_translate(extent, num1);
+			fprintf(out, "# Answer: %u%s\n", num2,
+				num2 ? "" : " (not found)");
+		} else if (!strcmp(cmd, "dump")) {
+			ext2fs_extent_dump(extent, out);
+		} else if (!strcmp(cmd, "iter_test")) {
+			retval = ext2fs_iterate_extent(extent, 0, 0, 0);
+			if (retval)
+				goto handle_error;
+			while (1) {
+				retval = ext2fs_iterate_extent(extent,
+					       &num1, &num2, &size);
+				if (retval)
+					goto handle_error;
+				if (!size)
+					break;
+				fprintf(out, "# %u -> %u (%d)\n",
+					num1, num2, size);
+			}
+		} else 
+			fputs("# Syntax error\n", out);
+	}
+}
+
+int main(int argc, char **argv)
+{
+	do_test(stdin, stdout);
+	exit(0);
+}
diff --git a/resize/test_extent.in b/resize/test_extent.in
new file mode 100644
index 0000000..7edcc41
--- /dev/null
+++ b/resize/test_extent.in
@@ -0,0 +1,64 @@
+create 10
+add 10 20
+add 11 21
+add 12 22
+add 14 45
+add 16 50
+add 17 51
+dump
+# Extent dump:
+#	Num=3, Size=10, Cursor=0, Sorted=1
+#		 10 -> 20 (3)
+#		 14 -> 45 (1)
+#		 16 -> 50 (2)
+add 18 52
+dump
+# Extent dump:
+#	Num=3, Size=10, Cursor=0, Sorted=1
+#		 10 -> 20 (3)
+#		 14 -> 45 (1)
+#		 16 -> 50 (3)
+lookup 10
+# Answer: 20
+lookup 11
+# Answer: 21
+lookup 12
+# Answer: 22
+lookup 13
+# Answer: 0 (not found)
+lookup 14
+# Answer: 45
+lookup 15
+# Answer: 0 (not found)
+lookup 16
+# Answer: 50
+lookup 1
+# Answer: 0 (not found)
+lookup 50
+# Answer: 0 (not found)
+add 19 100
+add 13 5
+lookup 18
+# Answer: 52
+lookup 19
+# Answer: 100
+lookup 20
+# Answer: 0 (not found)
+lookup 12
+# Answer: 22
+lookup 13
+# Answer: 5
+dump
+# Extent dump:
+#	Num=5, Size=10, Cursor=0, Sorted=1
+#		 10 -> 20 (3)
+#		 13 -> 5 (1)
+#		 14 -> 45 (1)
+#		 16 -> 50 (3)
+#		 19 -> 100 (1)
+iter_test
+# 10 -> 20 (3)
+# 13 -> 5 (1)
+# 14 -> 45 (1)
+# 16 -> 50 (3)
+# 19 -> 100 (1)