ChangeLog, e2fsck.c, pass2.c, pass3.c, unix.c:
  pass2.c (e2fsck_pass2): Fix the progress accounting so that we get to
  	100%.
  pass3.c (e2fsck_pass3): Change progress accounting to be consistent
  	with the other e2fsck passes.
  e2fsck.c (e2fsck_run): At the end of each pass, call the progress
  	function with the pass number set to zero.
  unix.c (e2fsck_update_progress): If the pass number is zero, ignore
  	the call, since that indicates that we just want to deallocate any
  	progress structures.
emptydir.c:
  Commit partially done file.
ChangeLog, badblocks.c:
  badblocks.c (ext2fs_badblocks_list_add): Use a bigger increment than
  	10 blocks when we need to expand the size of the badblocks list.

diff --git a/e2fsck/emptydir.c b/e2fsck/emptydir.c
new file mode 100644
index 0000000..6229a6b
--- /dev/null
+++ b/e2fsck/emptydir.c
@@ -0,0 +1,192 @@
+/*
+ * emptydir.c --- clear empty directory blocks
+ * 
+ * Copyright (C) 1998 Theodore Ts'o
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ *
+ * This file has the necessary routines to search for empty directory
+ * blocks and get rid of them.
+ */
+
+#include "e2fsck.h"
+#include "problem.h"
+
+/*
+ * For e2fsck.h
+ */
+struct empty_dir_info_struct {
+	ext2_dblist empty_dblist;
+	ext2fs_block_bitmap empty_dir_blocks;
+	ext2fs_inode_bitmap dir_map;
+	char *block_buf;
+	ino_t	ino;
+	struct ext2_inode inode;
+	blk_t	logblk;
+	blk_t	freed_blocks;
+};
+
+typedef struct empty_dir_info_struct *empty_dir_info;
+
+extern empty_dir_info init_empty_dir(e2fsck_t ctx);
+extern void free_empty_dirblock(empty_dir_info edi);
+extern void add_empty_dirblock(empty_dir_info edi,
+			       struct ext2_db_entry *db);
+extern void process_empty_dirblock(e2fsck_t ctx, empty_dir_info edi);
+
+
+empty_dir_info init_empty_dir(e2fsck_t ctx)
+{
+	empty_dir_info	edi;
+	errcode_t	retval;
+
+	edi = malloc(sizeof(struct empty_dir_info_struct));
+	if (!edi)
+		return NULL;
+
+	memset(edi, 0, sizeof(struct empty_dir_info_struct));
+
+	retval = ext2fs_init_dblist(ctx->fs, &edi->empty_dblist);
+	if (retval)
+		goto errout;
+	
+	retval = ext2fs_allocate_block_bitmap(ctx->fs, "empty dirblocks",
+					      &edi->empty_dir_blocks);
+	if (retval)
+		goto errout;
+
+	retval = ext2fs_allocate_inode_bitmap(ctx->fs, "empty dir map",
+					      &edi->dir_map);
+	if (retval)
+		goto errout;
+
+	return (edi);
+
+errout:
+	free_empty_dirblock(edi);
+	return NULL;
+}
+
+void free_empty_dirblock(empty_dir_info edi)
+{
+	if (!edi)
+		return;
+	if (edi->empty_dblist)
+		ext2fs_free_dblist(edi->empty_dblist);
+	if (edi->empty_dir_blocks)
+		ext2fs_free_block_bitmap(edi->empty_dir_blocks);
+	if (edi->dir_map)
+		ext2fs_free_inode_bitmap(edi->dir_map);
+
+	memset(edi, 0, sizeof(struct empty_dir_info_struct));
+	free(edi);
+}
+
+void add_empty_dirblock(empty_dir_info edi,
+			struct ext2_db_entry *db)
+{
+	if (!edi || !db)
+		return;
+
+	if (db->ino == 11)
+		return;		/* Inode number 11 is usually lost+found */
+
+	printf("Empty directory block %d (#%d) in inode %d\n",
+	       db->blk, db->blockcnt, db->ino);
+
+	ext2fs_mark_block_bitmap(edi->empty_dir_blocks, db->blk);
+	if (ext2fs_test_inode_bitmap(edi->dir_map, db->ino))
+		return;
+	ext2fs_mark_inode_bitmap(edi->dir_map, db->ino);
+
+	ext2fs_add_dir_block(edi->empty_dblist, db->ino,
+			     db->blk, db->blockcnt);
+}
+
+/*
+ * Helper function used by fix_directory.
+ *
+ * XXX need to finish this.  General approach is to use bmap to
+ * iterate over all of the logical blocks using the bmap function, and
+ * copy the block reference as necessary.  Big question --- what do
+ * about error recovery?
+ *
+ * Also question --- how to free the indirect blocks.
+ */
+int empty_pass1(ext2_filsys fs, blk_t *block_nr, e2_blkcnt_t blockcnt,
+		blk_t ref_block, int ref_offset, void *priv_data)
+{
+	empty_dir_info edi = (empty_dir_info) priv_data;
+	blk_t	block, new_block;
+	errcode_t	retval;
+	
+	if (blockcnt < 0)
+		return 0;
+	block = *block_nr;
+	do {
+		retval = ext2fs_bmap(fs, edi->ino, &edi->inode,
+				     edi->block_buf, 0, edi->logblk,
+				     &new_block);
+		if (retval)
+			return DIRENT_ABORT;   /* XXX what to do? */
+		if (new_block == 0)
+			break;
+		edi->logblk++;
+	} while (ext2fs_test_block_bitmap(edi->empty_dir_blocks, new_block));
+
+	if (new_block == block)
+		return 0;
+	if (new_block == 0)
+		edi->freed_blocks++;
+	*block_nr = new_block;
+	return BLOCK_CHANGED;
+}
+
+static int fix_directory(ext2_filsys fs,
+			 struct ext2_db_entry *db,
+			 void *priv_data)
+{
+	errcode_t	retval;
+	
+	empty_dir_info edi = (empty_dir_info) priv_data;
+
+	edi->logblk = 0;
+	edi->freed_blocks = 0;
+	edi->ino = db->ino;
+
+	retval = ext2fs_read_inode(fs, db->ino, &edi->inode);
+	if (retval)
+		return 0;
+
+	retval = ext2fs_block_iterate2(fs, db->ino, 0, edi->block_buf,
+				       empty_pass1, edi);
+	if (retval)
+		return 0;
+
+	if (edi->freed_blocks) {
+		edi->inode.i_size -= edi->freed_blocks * fs->blocksize;
+		edi->inode.i_blocks -= edi->freed_blocks *
+			(fs->blocksize / 512);
+		(void) ext2fs_write_inode(fs, db->ino, &edi->inode);
+	}
+	return 0;
+}
+
+void process_empty_dirblock(e2fsck_t ctx, empty_dir_info edi)
+{
+	if (!edi)
+		return;
+
+	edi->block_buf = malloc(ctx->fs->blocksize * 3);
+
+	if (edi->block_buf) {
+		(void) ext2fs_dblist_iterate(edi->empty_dblist,
+					     fix_directory, &edi);
+	}
+	free(edi->block_buf);
+	free_empty_dirblock(edi);
+}
+