dir_iterate.c (ext2fs_dir_iterate2, ext2fs_process_dir_block):
	Add support for a new flag, DIRENT_FLAG_INCLUDE_REMOVED,
	which will return deleted directory entries.
	ext2fs_dir_iterate2 takes a new callback function which
	is identical with the one used by
	ext2fs_dblist_dir_iterate().  If the directory entry is
	deleted, the callback function will be called with the
	entry paraemter set to DIRENT_DELETED_FILE.

Makefile.in, alloc_stats.c (ext2fs_inode_alloc_stats,
	ext2fs_block_alloc_stats): New functions which update
	block/inode allocation statistics in the bitmaps, block
	group descriptors, and superblock.

mkjournal.c (mkjournal_proc), mkdir.c (ext2fs_mkdir),
expanddir.c (expand_dir_proc), bb_inode.c
	(clear_bad_block_proc, set_bad_block_proc,
	ext2fs_update_bb_inode), alloc.c (ext2fs_alloc_block):
	Update to use new block/inode allocation statistics.

diff --git a/lib/ext2fs/ChangeLog b/lib/ext2fs/ChangeLog
index 05dbd9a..0319a71 100644
--- a/lib/ext2fs/ChangeLog
+++ b/lib/ext2fs/ChangeLog
@@ -1,3 +1,25 @@
+2002-01-03  Theodore Tso  <tytso@valinux.com>
+
+	* dir_iterate.c (ext2fs_dir_iterate2, ext2fs_process_dir_block):
+		Add support for a new flag, DIRENT_FLAG_INCLUDE_REMOVED,
+		which will return deleted directory entries.
+		ext2fs_dir_iterate2 takes a new callback function which
+		is identical with the one used by
+		ext2fs_dblist_dir_iterate().  If the directory entry is
+		deleted, the callback function will be called with the
+		entry paraemter set to DIRENT_DELETED_FILE.
+
+	* Makefile.in, alloc_stats.c (ext2fs_inode_alloc_stats,
+		ext2fs_block_alloc_stats): New functions which update
+		block/inode allocation statistics in the bitmaps, block
+		group descriptors, and superblock.
+
+	* mkjournal.c (mkjournal_proc), mkdir.c (ext2fs_mkdir),
+		expanddir.c (expand_dir_proc), bb_inode.c
+		(clear_bad_block_proc, set_bad_block_proc,
+		ext2fs_update_bb_inode), alloc.c (ext2fs_alloc_block):
+		Update to use new block/inode allocation statistics.
+
 2001-12-24  Theodore Tso  <tytso@valinux.com>
 
 	* ismounted.c (is_swap_device): New function used by
diff --git a/lib/ext2fs/Makefile.in b/lib/ext2fs/Makefile.in
index bb04813..98a8b9c 100644
--- a/lib/ext2fs/Makefile.in
+++ b/lib/ext2fs/Makefile.in
@@ -17,6 +17,7 @@
 OBJS= $(DEBUGFS_LIB_OBJS) $(RESIZE_LIB_OBJS) $(E2IMAGE_LIB_OBJS) \
 	ext2_err.o \
 	alloc.o \
+	alloc_stats.o \
 	alloc_tables.o \
 	badblocks.o \
 	bb_inode.o \
@@ -62,6 +63,7 @@
 
 SRCS= ext2_err.c \
 	$(srcdir)/alloc.c \
+	$(srcdir)/alloc_stats.c \
 	$(srcdir)/alloc_tables.c \
 	$(srcdir)/badblocks.c \
 	$(srcdir)/bb_compat.c \
diff --git a/lib/ext2fs/alloc.c b/lib/ext2fs/alloc.c
index 2de05bc..2dc4fc7 100644
--- a/lib/ext2fs/alloc.c
+++ b/lib/ext2fs/alloc.c
@@ -107,7 +107,6 @@
 {
 	errcode_t	retval;
 	blk_t		block;
-	int		group;
 	char		*buf = 0;
 
 	if (!block_buf) {
@@ -132,12 +131,7 @@
 	if (retval)
 		goto fail;
 	
-	fs->super->s_free_blocks_count--;
-	group = ext2fs_group_of_blk(fs, block);
-	fs->group_desc[group].bg_free_blocks_count--;
-	ext2fs_mark_block_bitmap(fs->block_map, block);
-	ext2fs_mark_super_dirty(fs);
-	ext2fs_mark_bb_dirty(fs);
+	ext2fs_block_alloc_stats(fs, block, +1);
 	*ret = block;
 	return 0;
 
diff --git a/lib/ext2fs/alloc_stats.c b/lib/ext2fs/alloc_stats.c
new file mode 100644
index 0000000..986f459
--- /dev/null
+++ b/lib/ext2fs/alloc_stats.c
@@ -0,0 +1,44 @@
+/*
+ * alloc_stats.c --- Update allocation statistics for ext2fs
+ *
+ * Copyright (C) 2001 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ * 
+ */
+
+#include <stdio.h>
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+void ext2fs_inode_alloc_stats(ext2_filsys fs, ext2_ino_t ino, int inuse)
+{
+	int	group = ext2fs_group_of_ino(fs, ino);
+
+	if (inuse > 0)
+		ext2fs_mark_inode_bitmap(fs->inode_map, ino);
+	else
+		ext2fs_unmark_inode_bitmap(fs->inode_map, ino);
+	fs->group_desc[group].bg_free_inodes_count -= inuse;
+	fs->super->s_free_inodes_count -= inuse;
+	ext2fs_mark_super_dirty(fs);
+	ext2fs_mark_ib_dirty(fs);
+}
+
+void ext2fs_block_alloc_stats(ext2_filsys fs, blk_t blk, int inuse)
+{
+	int	group = ext2fs_group_of_blk(fs, blk);
+
+	if (inuse > 0)
+		ext2fs_mark_block_bitmap(fs->block_map, blk);
+	else
+		ext2fs_unmark_block_bitmap(fs->block_map, blk);
+	fs->group_desc[group].bg_free_blocks_count -= inuse;
+	fs->super->s_free_blocks_count -= inuse;
+	ext2fs_mark_super_dirty(fs);
+	ext2fs_mark_bb_dirty(fs);
+}
diff --git a/lib/ext2fs/bb_inode.c b/lib/ext2fs/bb_inode.c
index 34c4955..4c575cf 100644
--- a/lib/ext2fs/bb_inode.c
+++ b/lib/ext2fs/bb_inode.c
@@ -59,7 +59,6 @@
 	errcode_t			retval;
 	struct set_badblock_record 	rec;
 	struct ext2_inode		inode;
-	blk_t				blk;
 	
 	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
 
@@ -105,16 +104,6 @@
 							     &rec.bb_iter);
 		if (retval)
 			goto cleanup;
-		while (ext2fs_badblocks_list_iterate(rec.bb_iter, &blk)) {
-			ext2fs_mark_block_bitmap(fs->block_map, blk); 
-		}
-		ext2fs_badblocks_list_iterate_end(rec.bb_iter);
-		ext2fs_mark_bb_dirty(fs);
-		
-		retval = ext2fs_badblocks_list_iterate_begin(bb_list,
-							     &rec.bb_iter);
-		if (retval)
-			goto cleanup;
 		retval = ext2fs_block_iterate2(fs, EXT2_BAD_INO,
 					       BLOCK_FLAG_APPEND, 0,
 					       set_bad_block_proc, &rec);
@@ -168,7 +157,6 @@
 	struct set_badblock_record *rec = (struct set_badblock_record *)
 		priv_data;
 	errcode_t	retval;
-	int		group;
 	unsigned long 	old_size;
 
 	if (!*block_nr)
@@ -202,12 +190,7 @@
 	/*
 	 * Mark the block as unused, and update accounting information
 	 */
-	ext2fs_unmark_block_bitmap(fs->block_map, *block_nr);
-	ext2fs_mark_bb_dirty(fs);
-	group = ext2fs_group_of_blk(fs, *block_nr);
-	fs->group_desc[group].bg_free_blocks_count++;
-	fs->super->s_free_blocks_count++;
-	ext2fs_mark_super_dirty(fs);
+	ext2fs_block_alloc_stats(fs, *block_nr, -1);
 	
 	*block_nr = 0;
 	return BLOCK_CHANGED;
@@ -230,7 +213,6 @@
 		priv_data;
 	errcode_t	retval;
 	blk_t		blk;
-	int		group;
 
 	if (blockcnt >= 0) {
 		/*
@@ -264,17 +246,12 @@
 			rec->err = retval;
 			return BLOCK_ABORT;
 		}
-		ext2fs_mark_block_bitmap(fs->block_map, blk); 
-		ext2fs_mark_bb_dirty(fs);
 	}
 	
 	/*
 	 * Update block counts
 	 */
-	group = ext2fs_group_of_blk(fs, blk);
-	fs->group_desc[group].bg_free_blocks_count--;
-	fs->super->s_free_blocks_count--;
-	ext2fs_mark_super_dirty(fs);
+	ext2fs_block_alloc_stats(fs, blk, +1);
 	
 	*block_nr = blk;
 	return BLOCK_CHANGED;
diff --git a/lib/ext2fs/dblist_dir.c b/lib/ext2fs/dblist_dir.c
index c4ea584..f0e4a26 100644
--- a/lib/ext2fs/dblist_dir.c
+++ b/lib/ext2fs/dblist_dir.c
@@ -50,8 +50,7 @@
 		if (retval)
 			return retval;
 	}
-	ctx.func = 0;
-	ctx.func2 = func;
+	ctx.func = func;
 	ctx.priv_data = priv_data;
 	ctx.errcode = 0;
 
diff --git a/lib/ext2fs/dir_iterate.c b/lib/ext2fs/dir_iterate.c
index ae97d82..35fae07 100644
--- a/lib/ext2fs/dir_iterate.c
+++ b/lib/ext2fs/dir_iterate.c
@@ -21,16 +21,41 @@
 #include "ext2_fs.h"
 #include "ext2fsP.h"
 
-errcode_t ext2fs_dir_iterate(ext2_filsys fs,
-			     ext2_ino_t dir,
-			     int flags,
-			     char *block_buf,
-			     int (*func)(struct ext2_dir_entry *dirent,
-					 int	offset,
-					 int	blocksize,
-					 char	*buf,
-					 void	*priv_data),
-			     void *priv_data)
+/*
+ * This function checks to see whether or not a potential deleted
+ * directory entry looks valid.  What we do is check the deleted entry
+ * and each successive entry to make sure that they all look valid and
+ * that the last deleted entry ends at the beginning of the next
+ * undeleted entry.  Returns 1 if the deleted entry looks valid, zero
+ * if not valid.
+ */
+static int ext2fs_validate_entry(char *buf, int offset, int final_offset)
+{
+	struct ext2_dir_entry *dirent;
+	
+	while (offset < final_offset) {
+		dirent = (struct ext2_dir_entry *)(buf + offset);
+		offset += dirent->rec_len;
+		if ((dirent->rec_len < 8) ||
+		    ((dirent->rec_len % 4) != 0) ||
+		    (((dirent->name_len & 0xFF)+8) > dirent->rec_len))
+			return 0;
+	}
+	return (offset == final_offset);
+}
+
+errcode_t ext2fs_dir_iterate2(ext2_filsys fs,
+			      ext2_ino_t dir,
+			      int flags,
+			      char *block_buf,
+			      int (*func)(ext2_ino_t	dir,
+					  int		entry,
+					  struct ext2_dir_entry *dirent,
+					  int	offset,
+					  int	blocksize,
+					  char	*buf,
+					  void	*priv_data),
+			      void *priv_data)
 {
 	struct		dir_context	ctx;
 	errcode_t	retval;
@@ -51,7 +76,6 @@
 			return retval;
 	}
 	ctx.func = func;
-	ctx.func2 = 0;
 	ctx.priv_data = priv_data;
 	ctx.errcode = 0;
 	retval = ext2fs_block_iterate2(fs, dir, 0, 0,
@@ -63,6 +87,45 @@
 	return ctx.errcode;
 }
 
+struct xlate {
+	int (*func)(struct ext2_dir_entry *dirent,
+		    int		offset,
+		    int		blocksize,
+		    char	*buf,
+		    void	*priv_data);
+	void *real_private;
+};
+
+static int xlate_func(ext2_ino_t dir, int entry,
+		      struct ext2_dir_entry *dirent, int offset,
+		      int blocksize, char *buf, void *priv_data)
+{
+	struct xlate *xl = (struct xlate *) priv_data;
+
+	return (*xl->func)(dirent, offset, blocksize, buf, xl->real_private);
+}
+
+extern errcode_t ext2fs_dir_iterate(ext2_filsys fs, 
+			      ext2_ino_t dir,
+			      int flags,
+			      char *block_buf,
+			      int (*func)(struct ext2_dir_entry *dirent,
+					  int	offset,
+					  int	blocksize,
+					  char	*buf,
+					  void	*priv_data),
+			      void *priv_data)
+{
+	struct xlate xl;
+	
+	xl.real_private = priv_data;
+	xl.func = func;
+
+	return ext2fs_dir_iterate2(fs, dir, flags, block_buf,
+				   xlate_func, &xl);
+}
+
+
 /*
  * Helper function which is private to this module.  Used by
  * ext2fs_dir_iterate() and ext2fs_dblist_dir_iterate()
@@ -76,10 +139,11 @@
 {
 	struct dir_context *ctx = (struct dir_context *) priv_data;
 	int		offset = 0;
+	int		next_real_entry = 0;
 	int		ret = 0;
 	int		changed = 0;
 	int		do_abort = 0;
-	int		entry;
+	int		entry, size;
 	struct ext2_dir_entry *dirent;
 
 	if (blockcnt < 0)
@@ -104,16 +168,14 @@
 		    !(ctx->flags & DIRENT_FLAG_INCLUDE_EMPTY))
 			goto next;
 
-		if (ctx->func)
-			ret = (ctx->func)(dirent, offset, fs->blocksize,
-					  ctx->buf, ctx->priv_data);
-		else if (ctx->func2) {
-			ret = (ctx->func2)(ctx->dir, entry, dirent, offset,
-					   fs->blocksize, ctx->buf,
-					   ctx->priv_data);
-			if (entry < DIRENT_OTHER_FILE)
-				entry++;
-		}
+		ret = (ctx->func)(ctx->dir,
+				  (next_real_entry > offset) ?
+				  DIRENT_DELETED_FILE : entry,
+				  dirent, offset,
+				  fs->blocksize, ctx->buf,
+				  ctx->priv_data);
+		if (entry < DIRENT_OTHER_FILE)
+			entry++;
 			
 		if (ret & DIRENT_CHANGED)
 			changed++;
@@ -122,6 +184,24 @@
 			break;
 		}
 next:		
+ 		if (next_real_entry == offset)
+			next_real_entry += dirent->rec_len;
+ 
+ 		if (ctx->flags & DIRENT_FLAG_INCLUDE_REMOVED) {
+			size = ((dirent->name_len & 0xFF) + 11) & ~3;
+
+			if (dirent->rec_len != size)  {
+				int final_offset = offset + dirent->rec_len;
+ 			
+				offset += size;
+				while (offset < final_offset &&
+				       !ext2fs_validate_entry(ctx->buf,
+							      offset,
+							      final_offset))
+					offset += 4;
+				continue;
+			}
+		}
 		offset += dirent->rec_len;
 	}
 
diff --git a/lib/ext2fs/expanddir.c b/lib/ext2fs/expanddir.c
index 112dedc..efc2b22 100644
--- a/lib/ext2fs/expanddir.c
+++ b/lib/ext2fs/expanddir.c
@@ -36,7 +36,6 @@
 	static blk_t	last_blk = 0;
 	char		*block;
 	errcode_t	retval;
-	int		group;
 	
 	if (*blocknr) {
 		last_blk = *blocknr;
@@ -70,12 +69,7 @@
 	}
 	ext2fs_free_mem((void **) &block);
 	*blocknr = new_blk;
-	ext2fs_mark_block_bitmap(fs->block_map, new_blk);
-	ext2fs_mark_bb_dirty(fs);
-	group = ext2fs_group_of_blk(fs, new_blk);
-	fs->group_desc[group].bg_free_blocks_count--;
-	fs->super->s_free_blocks_count--;
-	ext2fs_mark_super_dirty(fs);
+	ext2fs_block_alloc_stats(fs, new_blk, +1);
 	es->newblocks++;
 
 	if (es->done)
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index 3ddd452..bfca032 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -300,11 +300,12 @@
  */
 
 #define DIRENT_FLAG_INCLUDE_EMPTY	1
-
+#define DIRENT_FLAG_INCLUDE_REMOVED	2
 
 #define DIRENT_DOT_FILE		1
 #define DIRENT_DOT_DOT_FILE	2
 #define DIRENT_OTHER_FILE	3
+#define DIRENT_DELETED_FILE	4
 
 /*
  * Inode scan definitions
@@ -442,6 +443,10 @@
 extern errcode_t ext2fs_alloc_block(ext2_filsys fs, blk_t goal,
 				    char *block_buf, blk_t *ret);
 
+/* alloc_stats.c */
+void ext2fs_inode_alloc_stats(ext2_filsys fs, ext2_ino_t ino, int inuse);
+void ext2fs_block_alloc_stats(ext2_filsys fs, blk_t blk, int inuse);
+
 /* alloc_tables.c */
 extern errcode_t ext2fs_allocate_tables(ext2_filsys fs);
 extern errcode_t ext2fs_allocate_group_table(ext2_filsys fs, dgrp_t group,
@@ -603,6 +608,18 @@
 					  char	*buf,
 					  void	*priv_data),
 			      void *priv_data);
+extern errcode_t ext2fs_dir_iterate2(ext2_filsys fs, 
+			      ext2_ino_t dir,
+			      int flags,
+			      char *block_buf,
+			      int (*func)(ext2_ino_t	dir,
+					  int	entry,
+					  struct ext2_dir_entry *dirent,
+					  int	offset,
+					  int	blocksize,
+					  char	*buf,
+					  void	*priv_data),
+			      void *priv_data);
 
 /* dupfs.c */
 extern errcode_t ext2fs_dup_handle(ext2_filsys src, ext2_filsys *dest);
diff --git a/lib/ext2fs/ext2fsP.h b/lib/ext2fs/ext2fsP.h
index aedee15..5783354 100644
--- a/lib/ext2fs/ext2fsP.h
+++ b/lib/ext2fs/ext2fsP.h
@@ -48,18 +48,13 @@
 	ext2_ino_t		dir;
 	int		flags;
 	char		*buf;
-	int (*func)(struct ext2_dir_entry *dirent,
+	int (*func)(ext2_ino_t	dir,
+		    int	entry,
+		    struct ext2_dir_entry *dirent,
 		    int	offset,
 		    int	blocksize,
 		    char	*buf,
 		    void	*priv_data);
-	int (*func2)(ext2_ino_t	dir,
-		     int	entry,
-		     struct ext2_dir_entry *dirent,
-		     int	offset,
-		     int	blocksize,
-		     char	*buf,
-		     void	*priv_data);
 	void		*priv_data;
 	errcode_t	errcode;
 };
diff --git a/lib/ext2fs/mkdir.c b/lib/ext2fs/mkdir.c
index 98f0496..4fa711d 100644
--- a/lib/ext2fs/mkdir.c
+++ b/lib/ext2fs/mkdir.c
@@ -39,7 +39,6 @@
 	ext2_ino_t		scratch_ino;
 	blk_t			blk;
 	char			*block = 0;
-	int			group;
 
 	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
 
@@ -130,20 +129,9 @@
 	/*
 	 * Update accounting....
 	 */
-	ext2fs_mark_block_bitmap(fs->block_map, blk);
-	ext2fs_mark_bb_dirty(fs);
-	ext2fs_mark_inode_bitmap(fs->inode_map, ino);
-	ext2fs_mark_ib_dirty(fs);
+	ext2fs_block_alloc_stats(fs, blk, +1);
+	ext2fs_inode_alloc_stats(fs, ino, +1);
 
-	group = ext2fs_group_of_blk(fs, blk);
-	fs->group_desc[group].bg_free_blocks_count--;
-	group = ext2fs_group_of_ino(fs, ino);
-	fs->group_desc[group].bg_free_inodes_count--;
-	fs->group_desc[group].bg_used_dirs_count++;
-	fs->super->s_free_blocks_count--;
-	fs->super->s_free_inodes_count--;
-	ext2fs_mark_super_dirty(fs);
-	
 cleanup:
 	if (block)
 		ext2fs_free_mem((void **) &block);
diff --git a/lib/ext2fs/mkjournal.c b/lib/ext2fs/mkjournal.c
index ae25841..eb35ba8 100644
--- a/lib/ext2fs/mkjournal.c
+++ b/lib/ext2fs/mkjournal.c
@@ -154,7 +154,6 @@
 	blk_t	new_blk;
 	static blk_t	last_blk = 0;
 	errcode_t	retval;
-	int		group;
 	
 	if (*blocknr) {
 		last_blk = *blocknr;
@@ -180,12 +179,7 @@
 	}
 	*blocknr = new_blk;
 	last_blk = new_blk;
-	ext2fs_mark_block_bitmap(fs->block_map, new_blk);
-	ext2fs_mark_bb_dirty(fs);
-	group = ext2fs_group_of_blk(fs, new_blk);
-	fs->group_desc[group].bg_free_blocks_count--;
-	fs->super->s_free_blocks_count--;
-	ext2fs_mark_super_dirty(fs);
+	ext2fs_block_alloc_stats(fs, new_blk, +1);
 
 	if (es->num_blocks == 0)
 		return (BLOCK_CHANGED | BLOCK_ABORT);