Many files:
  Checked in e2fsprogs-1.07

diff --git a/lib/ext2fs/dll/jump.funcs b/lib/ext2fs/dll/jump.funcs
index 489bd62..825844c 100644
--- a/lib/ext2fs/dll/jump.funcs
+++ b/lib/ext2fs/dll/jump.funcs
@@ -71,7 +71,7 @@
 00000000 T _ext2fs_update_bb_inode 		libext2fs       bb_inode
 00000000 T _ext2fs_read_bb_FILE 		libext2fs       read_bb_file
 00000000 T _initialize_ext2_error_table 	libext2fs       ext2_err
-00000000 T _ext2_llseek              		libext2fs       llseek
+00000000 T _ext2fs_llseek              		libext2fs       llseek
 00000000 T _ext2fs_set_inode_callback 		libext2fs       inode
 00000000 T _ext2fs_compare_block_bitmap		libext2fs       cmp_bitmaps
 00000000 T _ext2fs_compare_inode_bitmap		libext2fs       cmp_bitmaps
@@ -93,3 +93,48 @@
 00000000 T _ext2fs_swap_group_desc 		libext2fs       swapfs
 00000000 T _ext2fs_get_device_size 		libext2fs       getsize
 00000000 T _ext2fs_check_if_mounted 		libext2fs       ismounted
+00000000 T _ext2fs_allocate_tables		libext2fs       alloc_tables
+00000000 T _ext2fs_allocate_generic_bitmap 	libext2fs       bitmaps
+00000000 T _ext2fs_warn_bitmap2 		libext2fs       bitops
+00000000 T _ext2fs_free_generic_bitmap 		libext2fs       freefs
+00000000 T _ext2fs_mark_generic_bitmap 		libext2fs       inline
+00000000 T _ext2fs_unmark_generic_bitmap 	libext2fs       inline
+00000000 T _ext2fs_test_generic_bitmap 		libext2fs       inline
+00000000 T _ext2fs_namei_follow 		libext2fs       namei
+00000000 T _ext2fs_follow_link			libext2fs       namei
+00000000 T _ext2fs_native_flag  		libext2fs       native
+00000000 T _ext2fs_swap_inode   		libext2fs       swapfs
+00000000 T _ext2fs_block_iterate2   		libext2fs       block
+00000000 T _ext2fs_inode_scan_goto_blockgroup	libext2fs       inode
+00000000 T _ext2fs_badblocks_list_create	libext2fs       badblocks
+00000000 T _ext2fs_badblocks_list_add		libext2fs       badblocks
+00000000 T _ext2fs_badblocks_list_test		libext2fs       badblocks
+00000000 T _ext2fs_badblocks_list_iterate_begin	libext2fs       badblocks
+00000000 T _ext2fs_badblocks_list_iterate	libext2fs       badblocks
+00000000 T _ext2fs_badblocks_list_iterate_end	libext2fs       badblocks
+00000000 T _ext2fs_brel_memarray_create		libext2fs       brel_ma
+00000000 T _ext2fs_badblocks_list_free		libext2fs       closefs
+00000000 T _ext2fs_free_dblist			libext2fs       closefs
+00000000 T _ext2fs_get_num_dirs			libext2fs       dblist
+00000000 T _ext2fs_init_dblist			libext2fs       dblist
+00000000 T _ext2fs_add_dir_block		libext2fs       dblist
+00000000 T _ext2fs_dblist_iterate		libext2fs       dblist
+00000000 T _ext2fs_dblist_dir_iterate		libext2fs       dblist_dir
+00000000 T _ext2fs_process_dir_block		libext2fs       dir_iterate
+00000000 T _ext2fs_test_block_bitmap_range	libext2fs       inline
+00000000 T _ext2fs_fast_test_block_bitmap_range	libext2fs       inline
+00000000 T _ext2fs_mark_block_bitmap_range	libext2fs       inline
+00000000 T _ext2fs_fast_mark_block_bitmap_range	libext2fs       inline
+00000000 T _ext2fs_unmark_block_bitmap_range	libext2fs       inline
+00000000 T _ext2fs_fast_unmark_block_bitmap_range libext2fs     inline
+00000000 T _ext2fs_inode_scan_flags		libext2fs       inode
+00000000 T _ext2fs_irel_memarray_create		libext2fs       irel_ma
+00000000 T _ext2fs_resize_generic_bitmap	libext2fs 	rs_bitmap
+00000000 T _ext2fs_inode_has_valid_blocks	libext2fs	valid_blk
+00000000 T _ext2fs_free_icount			libext2fs	icount
+00000000 T _ext2fs_create_icount		libext2fs	icount
+00000000 T _ext2fs_icount_fetch			libext2fs	icount
+00000000 T _ext2fs_icount_increment		libext2fs	icount
+00000000 T _ext2fs_icount_decrement		libext2fs	icount
+00000000 T _ext2fs_icount_store			libext2fs	icount
+00000000 T _ext2fs_get_icount_size		libext2fs	icount
diff --git a/lib/ext2fs/dll/jump.vars b/lib/ext2fs/dll/jump.vars
index edbbf7c..5f219d3 100644
--- a/lib/ext2fs/dll/jump.vars
+++ b/lib/ext2fs/dll/jump.vars
@@ -1 +1,6 @@
 00000004 D _unix_io_manager     libext2fs       unix_io
+00000004 D _test_io_manager     libext2fs       test_io
+00000004 D _test_io_backing_manager libext2fs   test_io
+00000004 D _test_io_cb_read_blk libext2fs       test_io
+00000004 D _test_io_cb_write_blk libext2fs      test_io
+00000004 D _test_io_cb_set_blksize libext2fs    test_io
diff --git a/lib/ext2fs/expanddir.c b/lib/ext2fs/expanddir.c
index b2597c9..852b40f 100644
--- a/lib/ext2fs/expanddir.c
+++ b/lib/ext2fs/expanddir.c
@@ -1,8 +1,12 @@
 /*
  * expand.c --- expand an ext2fs directory
  * 
- * Copyright (C) 1993 Theodore Ts'o.  This file may be redistributed
- * under the terms of the GNU Public License.
+ * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
  */
 
 #include <stdio.h>
diff --git a/lib/ext2fs/ext2_err.et.in b/lib/ext2fs/ext2_err.et.in
index 81e0c1e..075acee 100644
--- a/lib/ext2fs/ext2_err.et.in
+++ b/lib/ext2fs/ext2_err.et.in
@@ -1,6 +1,10 @@
 #
-# Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.  This file may be 
-# redistributed under the terms of the GNU Public License.
+# Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.  
+#
+# %Begin-Header%
+# This file may be redistributed under the terms of the GNU Public
+# License.
+# %End-Header%
 #
 	error_table ext2
 
@@ -37,14 +41,14 @@
 ec	EXT2_ET_MAGIC_GENERIC_BITMAP,
 	"Wrong magic number for generic_bitmap structure"
 
-ec	EXT2_ET_MAGIC_RESERVED_2,
-	"Wrong magic number --- RESERVED_2"
+ec	EXT2_ET_MAGIC_TEST_IO_CHANNEL,
+	"Wrong magic number for test io_channel structure"
 
-ec	EXT2_ET_MAGIC_RESERVED_3,
-	"Wrong magic number --- RESERVED_3"
+ec	EXT2_ET_MAGIC_DBLIST,
+	"Wrong magic number for directory block list structure"
 
-ec	EXT2_ET_MAGIC_RESERVED_4,
-	"Wrong magic number --- RESERVED_4"
+ec	EXT2_ET_MAGIC_ICOUNT,
+	"Wrong magic number for icount structure"
 
 ec	EXT2_ET_MAGIC_RESERVED_5,
 	"Wrong magic number --- RESERVED_5"
@@ -211,4 +215,13 @@
 ec	EXT2_ET_CALLBACK_NOTHANDLED,
 	"The callback function will not handle this case"
 
+ec	EXT2_ET_BAD_BLOCK_IN_INODE_TABLE,
+	"The inode is from a bad block in the inode table"
+
+ec	EXT2_ET_UNSUPP_FEATURE,
+	"Filesystem has unsupported feature(s)"
+
+ec	EXT2_ET_RO_UNSUPP_FEATURE,
+	"Filesystem has unsupported read-only feature(s)"
+
 	end
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index de74681..ce57e13 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -1,11 +1,22 @@
 /*
  * ext2fs.h --- ext2fs
  * 
- * Copyright (C) 1993 Theodore Ts'o.  This file may be redistributed
- * under the terms of the GNU Public License.
+ * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
  */
 
 /*
+ * Non-GNU C compilers won't necessarily understand inline
+ */
+#ifndef __GNUC__
+#define NO_INLINE_FUNCS
+#endif
+
+/*
  * Where the master copy of the superblock is located, and how big
  * superblocks are supposed to be.  We define SUPERBLOCK_SIZE because
  * the size of the superblock structure is not necessarily trustworthy
@@ -49,9 +60,7 @@
 #define EXT2FS_TEST_ERROR	2
 
 typedef struct ext2fs_struct_generic_bitmap *ext2fs_generic_bitmap;
-
 typedef struct ext2fs_struct_generic_bitmap *ext2fs_inode_bitmap;
-
 typedef struct ext2fs_struct_generic_bitmap *ext2fs_block_bitmap;
 
 #ifdef EXT2_DYNAMIC_REV
@@ -62,19 +71,46 @@
 #endif
 
 /*
+ * badblocks list definitions
+ */
+
+typedef struct ext2_struct_badblocks_list *ext2_badblocks_list;
+typedef struct ext2_struct_badblocks_iterate *ext2_badblocks_iterate;
+
+/* old */
+typedef struct ext2_struct_badblocks_list *badblocks_list;
+typedef struct ext2_struct_badblocks_iterate *badblocks_iterate;
+
+#define BADBLOCKS_FLAG_DIRTY	1
+
+/*
+ * ext2_dblist structure and abstractions (see dblist.c)
+ */
+struct ext2_db_entry {
+	ino_t	ino;
+	blk_t	blk;
+	int	blockcnt;
+};
+
+typedef struct ext2_struct_dblist *ext2_dblist;
+
+#define DBLIST_ABORT	1
+
+/*
  * Flags for the ext2_filsys structure
  */
 
-#define EXT2_FLAG_RW		0x01
-#define EXT2_FLAG_CHANGED	0x02
-#define EXT2_FLAG_DIRTY		0x04
-#define EXT2_FLAG_VALID		0x08
-#define EXT2_FLAG_IB_DIRTY	0x10
-#define EXT2_FLAG_BB_DIRTY	0x20
+#define EXT2_FLAG_RW			0x01
+#define EXT2_FLAG_CHANGED		0x02
+#define EXT2_FLAG_DIRTY			0x04
+#define EXT2_FLAG_VALID			0x08
+#define EXT2_FLAG_IB_DIRTY		0x10
+#define EXT2_FLAG_BB_DIRTY		0x20
 #define EXT2_FLAG_SWAP_BYTES		0x40
 #define EXT2_FLAG_SWAP_BYTES_READ	0x80
 #define EXT2_FLAG_SWAP_BYTES_WRITE	0x100
 #define EXT2_FLAG_MASTER_SB_ONLY	0x200
+#define EXT2_FLAG_FORCE			0x400
 
 /*
  * Special flag in the ext2 inode i_flag field that means that this is
@@ -103,7 +139,12 @@
 				struct ext2_inode *inode);
 	errcode_t (*write_inode)(ext2_filsys fs, ino_t ino,
 				struct ext2_inode *inode);
-	__u32				reserved[14];
+	badblocks_list			badblocks;
+	ext2_dblist			dblist;
+	/*
+	 * Reserved for future expansion
+	 */
+	__u32				reserved[12];
 
 	/*
 	 * Not used by ext2fs library; reserved for the use of the
@@ -112,32 +153,6 @@
 	void *				private; 
 };
 
-/*
- * badblocks list definitions
- */
-
-typedef struct struct_badblocks_list *badblocks_list;
-
-struct struct_badblocks_list {
-	int	magic;
-	int	num;
-	int	size;
-	blk_t	*list;
-	int	badblocks_flags;
-	int	reserved[8];
-};
-
-#define BADBLOCKS_FLAG_DIRTY	1
-
-typedef struct struct_badblocks_iterate *badblocks_iterate;
-
-struct struct_badblocks_iterate {
-	int		magic;
-	badblocks_list	bb;
-	int		ptr;
-	int	reserved[8];
-};
-
 #include "ext2fs/bitops.h"
 	
 /*
@@ -191,38 +206,30 @@
 
 #define DIRENT_FLAG_INCLUDE_EMPTY	1
 
+
+#define DIRENT_DOT_FILE		1
+#define DIRENT_DOT_DOT_FILE	2
+#define DIRENT_OTHER_FILE	3
+
 /*
  * Inode scan definitions
  */
 typedef struct ext2_struct_inode_scan *ext2_inode_scan;
 
-struct ext2_struct_inode_scan {
-	int			magic;
-	ext2_filsys		fs;
-	ino_t			current_inode;
-	blk_t			current_block;
-	dgrp_t			current_group;
-	int			inodes_left, blocks_left, groups_left;
-	int			inode_buffer_blocks;
-	char *			inode_buffer;
-	int			inode_size;
-	char *			ptr;
-	int			bytes_left;
-	char			*temp_buffer;
-	errcode_t		(*done_group)(ext2_filsys fs,
-					      ext2_inode_scan scan,
-					      dgrp_t group,
-					      void * private);
-	void *			done_group_data;
-	int			reserved[8];
-};
+/*
+ * ext2fs_scan flags
+ */
+#define EXT2_SF_CHK_BADBLOCKS	0x0001
+#define EXT2_SF_BAD_INODE_BLK	0x0002
+#define EXT2_SF_BAD_EXTRA_BYTES	0x0004
+#define EXT2_SF_SKIP_MISSING_ITABLE	0x0008
 
 /*
  * ext2fs_check_if_mounted flags
  */
 #define EXT2_MF_MOUNTED		1
 #define EXT2_MF_ISROOT		2
-#define EXT2_MF_READONLY	4 
+#define EXT2_MF_READONLY	4
 
 /*
  * Ext2/linux mode flags.  We define them here so that we don't need
@@ -265,6 +272,13 @@
 #define LINUX_S_ISSOCK(m)	(((m) & LINUX_S_IFMT) == LINUX_S_IFSOCK)
 
 /*
+ * ext2_icount_t abstraction
+ */
+#define EXT2_ICOUNT_OPT_INCREMENT	0x01
+
+typedef struct ext2_icount *ext2_icount_t;
+
+/*
  * For checking structure magic numbers...
  */
 
@@ -342,7 +356,25 @@
 					ext2fs_block_bitmap map,
 					blk_t *ret);
 
+/* allocate_tables.c */
+errcode_t ext2fs_allocate_tables(ext2_filsys fs);
+
 /* badblocks.c */
+extern errcode_t ext2fs_badblocks_list_create(ext2_badblocks_list *ret,
+					    int size);
+extern void ext2fs_badblocks_list_free(ext2_badblocks_list bb);
+extern errcode_t ext2fs_badblocks_list_add(ext2_badblocks_list bb,
+					   blk_t blk);
+extern int ext2fs_badblocks_list_test(ext2_badblocks_list bb,
+				    blk_t blk);
+extern errcode_t
+	ext2fs_badblocks_list_iterate_begin(ext2_badblocks_list bb,
+					    ext2_badblocks_iterate *ret);
+extern int ext2fs_badblocks_list_iterate(ext2_badblocks_iterate iter,
+					 blk_t *blk);
+extern void ext2fs_badblocks_list_iterate_end(ext2_badblocks_iterate iter);
+
+/* bb_compat */
 extern errcode_t badblocks_list_create(badblocks_list *ret, int size);
 extern void badblocks_list_free(badblocks_list bb);
 extern errcode_t badblocks_list_add(badblocks_list bb, blk_t blk);
@@ -354,7 +386,7 @@
 
 /* bb_inode.c */
 extern errcode_t ext2fs_update_bb_inode(ext2_filsys fs,
-					badblocks_list bb_list);
+					ext2_badblocks_list bb_list);
 
 /* bitmaps.c */
 extern errcode_t ext2fs_write_inode_bitmap(ext2_filsys fs);
@@ -392,6 +424,18 @@
 						  void	*private),
 				      void *private);
 
+errcode_t ext2fs_block_iterate2(ext2_filsys fs,
+				ino_t	ino,
+				int	flags,
+				char *block_buf,
+				int (*func)(ext2_filsys fs,
+					    blk_t	*blocknr,
+					    int	blockcnt,
+					    blk_t	ref_blk,
+					    int		ref_offset,
+					    void	*private),
+				void *private);
+
 /* check_desc.c */
 extern errcode_t ext2fs_check_desc(ext2_filsys fs);
 
@@ -405,6 +449,32 @@
 extern errcode_t ext2fs_compare_inode_bitmap(ext2fs_inode_bitmap bm1,
 					     ext2fs_inode_bitmap bm2);
 
+/* dblist.c */
+
+errcode_t ext2fs_get_num_dirs(ext2_filsys fs, ino_t *ret_num_dirs);
+errcode_t ext2fs_init_dblist(ext2_filsys fs, ext2_dblist *ret_dblist);
+void ext2fs_free_dblist(ext2_dblist dblist);
+errcode_t ext2fs_add_dir_block(ext2_dblist dblist, ino_t ino, blk_t blk,
+			     int blockcnt);
+errcode_t ext2fs_dblist_iterate(ext2_dblist dblist,
+				int (*func)(ext2_filsys fs,
+					    struct ext2_db_entry *db_info,
+					    void	*private),
+				void *private);
+
+/* dblist_dir.c */
+extern errcode_t
+	ext2fs_dblist_dir_iterate(ext2_dblist dblist,
+				  int	flags,
+				  char	*block_buf,
+				  int (*func)(ino_t	dir,
+					      int		entry,
+					      struct ext2_dir_entry *dirent,
+					      int	offset,
+					      int	blocksize,
+					      char	*buf,
+					      void	*private),
+				  void *private);
 
 /* dirblock.c */
 extern errcode_t ext2fs_read_dir_block(ext2_filsys fs, blk_t block,
@@ -412,6 +482,24 @@
 extern errcode_t ext2fs_write_dir_block(ext2_filsys fs, blk_t block,
 					void *buf);
 
+/* dir_iterate.c */
+extern errcode_t ext2fs_dir_iterate(ext2_filsys fs, 
+			      ino_t dir,
+			      int flags,
+			      char *block_buf,
+			      int (*func)(struct ext2_dir_entry *dirent,
+					  int	offset,
+					  int	blocksize,
+					  char	*buf,
+					  void	*private),
+			      void *private);
+	/* private to library */
+extern int ext2fs_process_dir_block(ext2_filsys  	fs,
+				    blk_t		*blocknr,
+				    int		blockcnt,
+				    void		*private);
+
+
 /* expanddir.c */
 extern errcode_t ext2fs_expand_dir(ext2_filsys fs, ino_t dir);
 
@@ -436,39 +524,42 @@
 extern void ext2fs_close_inode_scan(ext2_inode_scan scan);
 extern errcode_t ext2fs_get_next_inode(ext2_inode_scan scan, ino_t *ino,
 			       struct ext2_inode *inode);
-void ext2fs_set_inode_callback(ext2_inode_scan scan,
-			       errcode_t (*done_group)(ext2_filsys fs,
-						       ext2_inode_scan scan,
-						       dgrp_t group,
-						       void * private),
-			       void *done_group_data);
-void ext2fs_set_inode_callback(ext2_inode_scan scan,
-			       errcode_t (*done_group)(ext2_filsys fs,
-						       ext2_inode_scan scan,
-						       dgrp_t group,
-						       void * private),
-			       void *done_group_data);
-extern errcode_t ext2fs_read_inode (ext2_filsys fs, unsigned long ino,
+extern errcode_t ext2fs_inode_scan_goto_blockgroup(ext2_inode_scan scan,
+						   int	group);
+extern void ext2fs_set_inode_callback
+	(ext2_inode_scan scan,
+	 errcode_t (*done_group)(ext2_filsys fs,
+				 ext2_inode_scan scan,
+				 dgrp_t group,
+				 void * private),
+	 void *done_group_data);
+extern int ext2fs_inode_scan_flags(ext2_inode_scan scan, int set_flags,
+				   int clear_flags);
+extern errcode_t ext2fs_read_inode (ext2_filsys fs, ino_t ino,
 			    struct ext2_inode * inode);
-extern errcode_t ext2fs_write_inode(ext2_filsys fs, unsigned long ino,
+extern errcode_t ext2fs_write_inode(ext2_filsys fs, ino_t ino,
 			    struct ext2_inode * inode);
 extern errcode_t ext2fs_get_blocks(ext2_filsys fs, ino_t ino, blk_t *blocks);
 extern errcode_t ext2fs_check_directory(ext2_filsys fs, ino_t ino);
 
+/* icount.c */
+extern void ext2fs_free_icount(ext2_icount_t icount);
+extern errcode_t ext2fs_create_icount(ext2_filsys fs, int flags, int size,
+				      ext2_icount_t *ret);
+extern errcode_t ext2fs_icount_fetch(ext2_icount_t icount, ino_t ino,
+				     __u16 *ret);
+extern errcode_t ext2fs_icount_increment(ext2_icount_t icount, ino_t ino,
+					 __u16 *ret);
+extern errcode_t ext2fs_icount_decrement(ext2_icount_t icount, ino_t ino,
+					 __u16 *ret);
+extern errcode_t ext2fs_icount_store(ext2_icount_t icount, ino_t ino,
+				     __u16 count);
+extern ino_t ext2fs_get_icount_size(ext2_icount_t icount);
+
 /* ismounted.c */
 extern errcode_t ext2fs_check_if_mounted(const char *file, int *mount_flags);
 
 /* namei.c */
-extern errcode_t ext2fs_dir_iterate(ext2_filsys fs, 
-			      ino_t dir,
-			      int flags,
-			      char *block_buf,
-			      int (*func)(struct ext2_dir_entry *dirent,
-					  int	offset,
-					  int	blocksize,
-					  char	*buf,
-					  void	*private),
-			      void *private);
 extern errcode_t ext2fs_lookup(ext2_filsys fs, ino_t dir, const char *name,
 			 int namelen, char *buf, ino_t *inode);
 extern errcode_t ext2fs_namei(ext2_filsys fs, ino_t root, ino_t cwd,
@@ -505,20 +596,32 @@
 			ino_t ino, int flags);
 
 /* read_bb.c */
-extern errcode_t ext2fs_read_bb_inode(ext2_filsys fs, badblocks_list *bb_list);
+extern errcode_t ext2fs_read_bb_inode(ext2_filsys fs,
+				      ext2_badblocks_list *bb_list);
 
 /* read_bb_file.c */
 extern errcode_t ext2fs_read_bb_FILE(ext2_filsys fs, FILE *f, 
-				     badblocks_list *bb_list,
+				     ext2_badblocks_list *bb_list,
 				     void (*invalid)(ext2_filsys fs,
 						     blk_t blk));
 
+/* rs_bitmap.c */
+extern errcode_t ext2fs_resize_generic_bitmap(__u32 new_end,
+					      __u32 new_real_end,
+					      ext2fs_generic_bitmap bmap);
+extern errcode_t ext2fs_resize_inode_bitmap(__u32 new_end, __u32 new_real_end,
+					    ext2fs_inode_bitmap bmap);
+extern errcode_t ext2fs_resize_block_bitmap(__u32 new_end, __u32 new_real_end,
+					    ext2fs_block_bitmap bmap);
+
 /* swapfs.c */
 extern void ext2fs_swap_super(struct ext2_super_block * super);
 extern void ext2fs_swap_group_desc(struct ext2_group_desc *gdp);
 extern void ext2fs_swap_inode(ext2_filsys fs,struct ext2_inode *t,
 			      struct ext2_inode *f, int hostorder);
 
+/* valid_blk.c */
+int ext2fs_inode_has_valid_blocks(struct ext2_inode *inode);
 
 /* inline functions */
 extern void ext2fs_mark_super_dirty(ext2_filsys fs);
diff --git a/lib/ext2fs/ext2fsP.h b/lib/ext2fs/ext2fsP.h
new file mode 100644
index 0000000..5cf83b5
--- /dev/null
+++ b/lib/ext2fs/ext2fsP.h
@@ -0,0 +1,74 @@
+/*
+ * ext2fsP.h --- private header file for ext2 library
+ * 
+ * Copyright (C) 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include "ext2fs.h"
+
+/*
+ * Badblocks list
+ */
+struct ext2_struct_badblocks_list {
+	int	magic;
+	int	num;
+	int	size;
+	blk_t	*list;
+	int	badblocks_flags;
+};
+
+struct ext2_struct_badblocks_iterate {
+	int		magic;
+	badblocks_list	bb;
+	int		ptr;
+};
+
+
+/*
+ * Directory block iterator definition
+ */
+struct ext2_struct_dblist {
+	int			magic;
+	ext2_filsys		fs;
+	ino_t			size;
+	ino_t			count;
+	int			sorted;
+	struct ext2_db_entry *	list;
+};
+
+/*
+ * For directory iterators
+ */
+struct dir_context {
+	ino_t		dir;
+	int		flags;
+	char		*buf;
+	int (*func)(struct ext2_dir_entry *dirent,
+		    int	offset,
+		    int	blocksize,
+		    char	*buf,
+		    void	*private);
+	int (*func2)(ino_t	dir,
+		     int	entry,
+		     struct ext2_dir_entry *dirent,
+		     int	offset,
+		     int	blocksize,
+		     char	*buf,
+		     void	*private);
+	void		*private;
+	errcode_t	errcode;
+};
+
+
+/* Function prototypes */
+
+extern int ext2_process_dir_block(ext2_filsys fs,
+				  blk_t	*blocknr,
+				  int	blockcnt,
+				  void	*private);
+
diff --git a/lib/ext2fs/freefs.c b/lib/ext2fs/freefs.c
index 5c70983..215d1fb 100644
--- a/lib/ext2fs/freefs.c
+++ b/lib/ext2fs/freefs.c
@@ -1,8 +1,12 @@
 /*
  * freefs.c --- free an ext2 filesystem
  * 
- * Copyright (C) 1993, 1994 Theodore Ts'o.  This file may be redistributed
- * under the terms of the GNU Public License.
+ * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
  */
 
 #include <stdio.h>
@@ -30,6 +34,16 @@
 		ext2fs_free_block_bitmap(fs->block_map);
 	if (fs->inode_map)
 		ext2fs_free_inode_bitmap(fs->inode_map);
+
+	if (fs->badblocks)
+		badblocks_list_free(fs->badblocks);
+	fs->badblocks = 0;
+
+	if (fs->dblist)
+		ext2fs_free_dblist(fs->dblist);
+	
+	fs->magic = 0;
+
 	free(fs);
 }
 
diff --git a/lib/ext2fs/get_pathname.c b/lib/ext2fs/get_pathname.c
index da6b249..8c94e93 100644
--- a/lib/ext2fs/get_pathname.c
+++ b/lib/ext2fs/get_pathname.c
@@ -1,8 +1,21 @@
 /*
  * get_pathname.c --- do directry/inode -> name translation
  * 
- * Copyright (C) 1993 Theodore Ts'o.  This file may be redistributed
- * under the terms of the GNU Public License.
+ * Copyright (C) 1993, 1994, 1995 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ *
+ * 	ext2fs_get_pathname(fs, dir, ino, name)
+ *
+ * 	This function translates takes two inode numbers into a
+ * 	string, placing the result in <name>.  <dir> is the containing
+ * 	directory inode, and <ino> is the inode number itself.  If
+ * 	<ino> is zero, then ext2fs_get_pathname will return pathname
+ * 	of the the directory <dir>.
+ * 
  */
 
 #include <stdio.h>
diff --git a/lib/ext2fs/getsize.c b/lib/ext2fs/getsize.c
index 86b12d1..c9fc00a 100644
--- a/lib/ext2fs/getsize.c
+++ b/lib/ext2fs/getsize.c
@@ -1,8 +1,12 @@
 /*
  * getsize.c --- get the size of a partition.
  * 
- * Copyright (C) 1995 Theodore Ts'o.  This file may be
- * redistributed under the terms of the GNU Public License.
+ * Copyright (C) 1995, 1995 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
  */
 
 #include <stdio.h>
@@ -35,7 +39,7 @@
 {
 	char ch;
 
-	if (ext2_llseek (fd, offset, 0) < 0)
+	if (ext2fs_llseek (fd, offset, 0) < 0)
 		return 0;
 	if (read (fd, &ch, 1) < 1)
 		return 0;
@@ -49,12 +53,13 @@
 				 blk_t *retblocks)
 {
 	int	fd;
-	int	size;
+	long	size;
 	ext2_loff_t high, low;
 #ifdef FDGETPRM
 	struct floppy_struct this_floppy;
 #endif
 #ifdef HAVE_SYS_DISKLABEL_H
+	int part;
 	struct disklabel lab;
 	struct partition *pp;
 	char ch;
@@ -79,18 +84,18 @@
 	}
 #endif
 #ifdef HAVE_SYS_DISKLABEL_H
-	size = strlen(file) - 1;
-	if (size >= 0) {
-		ch = file[size];
+	part = strlen(file) - 1;
+	if (part >= 0) {
+		ch = file[part];
 		if (isdigit(ch))
-			size = 0;
+			part = 0;
 		else if (ch >= 'a' && ch <= 'h')
-			size = ch - 'a';
+			part = ch - 'a';
 		else
-			size = -1;
+			part = -1;
 	}
-	if (size >= 0 && (ioctl(fd, DIOCGDINFO, (char *)&lab) >= 0)) {
-		pp = &lab.d_partitions[size];
+	if (part >= 0 && (ioctl(fd, DIOCGDINFO, (char *)&lab) >= 0)) {
+		pp = &lab.d_partitions[part];
 		if (pp->p_size) {
 			close(fd);
 			*retblocks = pp->p_size / (blocksize / 512);
@@ -101,7 +106,7 @@
 
 	/*
 	 * OK, we couldn't figure it out by using a specialized ioctl,
-	 * which is generally the besy way.  So do binary search to
+	 * which is generally the best way.  So do binary search to
 	 * find the size of the partition.
 	 */
 	low = 0;
diff --git a/lib/ext2fs/icount.c b/lib/ext2fs/icount.c
new file mode 100644
index 0000000..b9070a9
--- /dev/null
+++ b/lib/ext2fs/icount.c
@@ -0,0 +1,411 @@
+/*
+ * icount.c --- an efficient inode count abstraction
+ *
+ * Copyright (C) 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <et/com_err.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include <linux/ext2_fs.h>
+#include "ext2fs.h"
+
+/*
+ * The data storage strategy used by icount relies on the observation
+ * that most inode counts are either zero (for non-allocated inodes),
+ * one (for most files), and only a few that are two or more
+ * (directories and files that are linked to more than one directory).
+ *
+ * Also, e2fsck tends to load the icount data sequentially.
+ *
+ * So, we use an inode bitmap to indicate which inodes have a count of
+ * one, and then use a sorted list to store the counts for inodes
+ * which are greater than one.
+ *
+ * We also use an optional bitmap to indicate which inodes are already
+ * in the sorted list, to speed up the use of this abstraction by
+ * e2fsck's pass 2.  Pass 2 increments inode counts as it finds them,
+ * so this extra bitmap avoids searching the sorted list to see if a
+ * particular inode is on the sorted list already.
+ */
+
+struct ext2_icount_el {
+	ino_t	ino;
+	__u16	count;
+};
+
+struct ext2_icount {
+	int			magic;
+	ext2fs_inode_bitmap	single;
+	ext2fs_inode_bitmap	multiple;
+	ino_t			count;
+	ino_t			size;
+	ino_t			num_inodes;
+	int			cursor;
+	struct ext2_icount_el	*list;
+};
+
+void ext2fs_free_icount(ext2_icount_t icount)
+{
+	if (!icount)
+		return;
+
+	icount->magic = 0;
+	if (icount->list)
+		free(icount->list);
+	if (icount->single)
+		ext2fs_free_inode_bitmap(icount->single);
+	if (icount->multiple)
+		ext2fs_free_inode_bitmap(icount->multiple);
+	free(icount);
+}
+
+errcode_t ext2fs_create_icount(ext2_filsys fs, int flags, int size,
+			       ext2_icount_t *ret)
+{
+	ext2_icount_t	icount;
+	errcode_t	retval;
+	size_t		bytes;
+
+	icount = malloc(sizeof(struct ext2_icount));
+	if (!icount)
+		return ENOMEM;
+	memset(icount, 0, sizeof(struct ext2_icount));
+
+	retval = ext2fs_allocate_inode_bitmap(fs, 0, 
+					      &icount->single);
+	if (retval)
+		goto errout;
+
+	if (flags & EXT2_ICOUNT_OPT_INCREMENT) {
+		retval = ext2fs_allocate_inode_bitmap(fs, 0, 
+						      &icount->multiple);
+		if (retval)
+			goto errout;
+	} else
+		icount->multiple = 0;
+
+	if (size) {
+		icount->size = size;
+	} else {
+		/*
+		 * Figure out how many special case inode counts we will
+		 * have.  We know we will need one for each directory;
+		 * we also need to reserve some extra room for file links
+		 */
+		retval = ext2fs_get_num_dirs(fs, &icount->size);
+		if (retval)
+			goto errout;
+		icount->size += fs->super->s_inodes_count / 50;
+	}
+	
+	bytes = icount->size * sizeof(struct ext2_icount_el);
+#if 0
+	printf("Icount allocated %d entries, %d bytes.\n",
+	       icount->size, bytes);
+#endif
+	icount->list = malloc(bytes);
+	if (!icount->list)
+		goto errout;
+	memset(icount->list, 0, bytes);
+
+	icount->magic = EXT2_ET_MAGIC_ICOUNT;
+	icount->count = 0;
+	icount->cursor = 0;
+	icount->num_inodes = fs->super->s_inodes_count;
+
+	*ret = icount;
+
+	return 0;
+
+errout:
+	ext2fs_free_icount(icount);
+	return(retval);
+}
+
+/*
+ * get_icount_el() --- given an inode number, try to find icount
+ * 	information in the sorted list.  We use a binary search...
+ */
+static struct ext2_icount_el *get_icount_el(ext2_icount_t icount, ino_t ino)
+{
+	int	low, high, mid;
+
+	if (!icount || !icount->list || !icount->count)
+		return 0;
+
+	if (icount->multiple &&
+	    !ext2fs_test_inode_bitmap(icount->multiple, ino))
+		return 0;
+
+	low = 0;
+	high = icount->count-1;
+	if (ino == icount->list[low].ino) {
+		icount->cursor = low+1;
+		return &icount->list[low];
+	}
+	if  (ino == icount->list[high].ino) {
+		icount->cursor = 0;
+		return &icount->list[high];
+	}
+	if (icount->cursor >= icount->count)
+		icount->cursor = 0;
+	if (ino == icount->list[icount->cursor].ino)
+		return &icount->list[icount->cursor++];
+#if 0
+	printf("Non-cursor get_icount_el: %u\n", ino);
+#endif
+	
+	while (low < high) {
+		mid = (low+high)/2;
+		if (mid == low || mid == high)
+			break;
+		if (ino == icount->list[mid].ino) {
+			icount->cursor = mid;
+			return &icount->list[mid];
+		}
+		if (ino < icount->list[mid].ino)
+			high = mid;
+		else
+			low = mid;
+	}
+	return 0;
+}
+
+/*
+ * put_icount_el() --- given an inode number, create a new entry in
+ * 	the sorted list.  This function is optimized for adding values
+ * 	in ascending order.
+ */
+static struct ext2_icount_el *put_icount_el(ext2_icount_t icount, ino_t ino)
+{
+	struct ext2_icount_el *el, *new_list;
+	ino_t			new_size = 0;
+	int			i, j;
+
+	if (icount->count >= icount->size) {
+		if (icount->count) {
+			new_size = icount->list[icount->count-1].ino;
+			new_size = icount->count * 
+				((float) new_size / icount->num_inodes);
+		}
+		if (new_size < (icount->size + 100))
+			new_size = icount->size + 100;
+#if 0
+		printf("Reallocating icount %d entries...\n", new_size);
+#endif	
+		new_list = realloc(icount->list,
+			new_size * sizeof(struct ext2_icount_el));
+		if (!new_list)
+			return 0;
+		icount->size = new_size;
+		icount->list = new_list;
+	}
+
+	/*
+	 * Normally, get_icount_el is called with each inode in
+	 * sequential order; but once in a while (like when pass 3
+	 * needs to recreate the root directory or lost+found
+	 * directory) it is called out of order.
+	 */
+	if (icount->count && icount->list[icount->count-1].ino >= ino) {
+		for (i = icount->count-1; i > 0; i--)
+			if (icount->list[i-1].ino < ino)
+				break;
+		el = &icount->list[i];
+		if (el->ino != ino) {
+			for (j = icount->count++; j > i; j--)
+				icount->list[j] = icount->list[j-1];
+			el->count = 0;
+		}
+	} else {
+		el = &icount->list[icount->count++];
+		el->count = 0;
+	}
+	el->ino = ino;
+	return el;
+}
+	
+
+errcode_t ext2fs_icount_fetch(ext2_icount_t icount, ino_t ino, __u16 *ret)
+{
+	struct ext2_icount_el	*el;
+	
+	EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT);
+
+	if (ext2fs_test_inode_bitmap(icount->single, ino)) {
+		*ret = 1;
+		return 0;
+	}
+	el = get_icount_el(icount, ino);
+	if (!el) {
+		*ret = 0;
+		return 0;
+	}
+	*ret = el->count;
+	return 0;
+}
+
+errcode_t ext2fs_icount_increment(ext2_icount_t icount, ino_t ino,
+				  __u16 *ret)
+{
+	struct ext2_icount_el	*el;
+
+	EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT);
+
+	if (ext2fs_test_inode_bitmap(icount->single, ino)) {
+		/*
+		 * If the existing count is 1, then we know there is
+		 * no entry in the list, so use put_icount_el().
+		 */
+		el = put_icount_el(icount, ino);
+		if (!el)
+			return ENOMEM;
+	} else if (icount->multiple) {
+		/*
+		 * The count is either zero or greater than 1; if the
+		 * inode is set in icount->multiple, then there should
+		 * be an entry in the list, so find it using
+		 * get_icount_el().
+		 */
+		if (ext2fs_test_inode_bitmap(icount->multiple, ino)) {
+			el = get_icount_el(icount, ino);
+			if (!el) {
+				/* should never happen */
+				el = put_icount_el(icount, ino);
+				if (!el)
+					return ENOMEM;
+			}
+		} else {
+			/*
+			 * The count was zero; mark the single bitmap
+			 * and return.
+			 */
+		zero_count:
+			ext2fs_mark_inode_bitmap(icount->single, ino);
+			if (ret)
+				*ret = 1;
+			return 0;
+		}
+	} else {
+		/*
+		 * The count is either zero or greater than 1; try to
+		 * find an entry in the list to determine which.
+		 */
+		el = get_icount_el(icount, ino);
+		if (!el) {
+			/* No entry means the count was zero */
+			goto zero_count;
+		}
+		el = put_icount_el(icount, ino);
+		if (!el)
+			return ENOMEM;
+	}
+	if (ext2fs_test_inode_bitmap(icount->single, ino)) {
+		ext2fs_unmark_inode_bitmap(icount->single, ino);
+		el->count = 2;
+	} else
+		el->count++;
+	if (icount->multiple)
+		ext2fs_mark_inode_bitmap(icount->multiple, ino);
+	if (ret)
+		*ret = el->count;
+	return 0;
+}
+
+errcode_t ext2fs_icount_decrement(ext2_icount_t icount, ino_t ino,
+				  __u16 *ret)
+{
+	struct ext2_icount_el	*el;
+
+	EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT);
+
+	if (ext2fs_test_inode_bitmap(icount->single, ino)) {
+		ext2fs_unmark_inode_bitmap(icount->single, ino);
+		if (icount->multiple)
+			ext2fs_unmark_inode_bitmap(icount->multiple, ino);
+		else {
+			el = get_icount_el(icount, ino);
+			if (el)
+				el->count = 0;
+		}
+		if (ret)
+			*ret = 0;
+		return 0;
+	}
+
+	el = get_icount_el(icount, ino);
+	if (!el)
+		return EINVAL;
+
+	el->count--;
+	if (el->count == 1)
+		ext2fs_mark_inode_bitmap(icount->single, ino);
+	if ((el->count == 0) && icount->multiple)
+		ext2fs_unmark_inode_bitmap(icount->multiple, ino);
+
+	if (ret)
+		*ret = el->count;
+	return 0;
+}
+
+errcode_t ext2fs_icount_store(ext2_icount_t icount, ino_t ino,
+			      __u16 count)
+{
+	struct ext2_icount_el	*el;
+
+	EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT);
+
+	if (count == 1) {
+		ext2fs_mark_inode_bitmap(icount->single, ino);
+		if (icount->multiple)
+			ext2fs_unmark_inode_bitmap(icount->multiple, ino);
+		return 0;
+	}
+	if (count == 0) {
+		ext2fs_unmark_inode_bitmap(icount->single, ino);
+		if (icount->multiple) {
+			/*
+			 * If the icount->multiple bitmap is enabled,
+			 * we can just clear both bitmaps and we're done
+			 */
+			ext2fs_unmark_inode_bitmap(icount->multiple, ino);
+		} else {
+			el = get_icount_el(icount, ino);
+			if (el)
+				el->count = 0;
+		}
+		return 0;
+	}
+
+	/*
+	 * Get the icount element
+	 */
+	el = put_icount_el(icount, ino);
+	if (!el)
+		return ENOMEM;
+	el->count = count;
+	ext2fs_unmark_inode_bitmap(icount->single, ino);
+	if (icount->multiple)
+		ext2fs_mark_inode_bitmap(icount->multiple, ino);
+	return 0;
+}
+
+ino_t ext2fs_get_icount_size(ext2_icount_t icount)
+{
+	if (!icount || icount->magic != EXT2_ET_MAGIC_ICOUNT)
+		return 0;
+
+	return icount->size;
+}
diff --git a/lib/ext2fs/initialize.c b/lib/ext2fs/initialize.c
index 4349244..6db5a0c 100644
--- a/lib/ext2fs/initialize.c
+++ b/lib/ext2fs/initialize.c
@@ -4,8 +4,10 @@
  * 
  * Copyright (C) 1994, 1995, 1996 Theodore Ts'o.
  * 
+ * %Begin-Header%
  * This file may be redistributed under the terms of the GNU Public
  * License.
+ * %End-Header%
  */
 
 #include <stdio.h>
diff --git a/lib/ext2fs/inline.c b/lib/ext2fs/inline.c
index 599fceb..3451c35 100644
--- a/lib/ext2fs/inline.c
+++ b/lib/ext2fs/inline.c
@@ -3,8 +3,12 @@
  * 	files as standalone functions, in case the application program
  * 	is compiled with inlining turned off.
  * 
- * Copyright (C) 1993 Theodore Ts'o.  This file may be redistributed
- * under the terms of the GNU Public License.
+ * Copyright (C) 1993, 1994 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
  */
 
 
diff --git a/lib/ext2fs/inode.c b/lib/ext2fs/inode.c
index 8743476..8fd5e7e 100644
--- a/lib/ext2fs/inode.c
+++ b/lib/ext2fs/inode.c
@@ -1,8 +1,12 @@
 /*
  * inode.c --- utility routines to read and write inodes
  * 
- * Copyright (C) 1993 Theodore Ts'o.  This file may be redistributed
- * under the terms of the GNU Public License.
+ * 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>
@@ -17,15 +21,51 @@
 
 #include <linux/ext2_fs.h>
 
-#include "ext2fs.h"
+#include "ext2fsP.h"
+
+struct ext2_struct_inode_scan {
+	int			magic;
+	ext2_filsys		fs;
+	ino_t			current_inode;
+	blk_t			current_block;
+	dgrp_t			current_group;
+	int			inodes_left, blocks_left, groups_left;
+	int			inode_buffer_blocks;
+	char *			inode_buffer;
+	int			inode_size;
+	char *			ptr;
+	int			bytes_left;
+	char			*temp_buffer;
+	errcode_t		(*done_group)(ext2_filsys fs,
+					      ext2_inode_scan scan,
+					      dgrp_t group,
+					      void * private);
+	void *			done_group_data;
+	int			bad_block_ptr;
+	int			scan_flags;
+	int			reserved[6];
+};
 
 errcode_t ext2fs_open_inode_scan(ext2_filsys fs, int buffer_blocks,
 				 ext2_inode_scan *ret_scan)
 {
 	ext2_inode_scan	scan;
+	errcode_t	retval;
 
 	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
 
+	/*
+	 * If fs->badblocks isn't set, then set it --- since the inode
+	 * scanning functions require it.
+	 */
+	if (fs->badblocks == 0) {
+		retval = ext2fs_read_bb_inode(fs, &fs->badblocks);
+		if (retval && fs->badblocks) {
+			badblocks_list_free(fs->badblocks);
+			fs->badblocks = 0;
+		}
+	}
+
 	scan = (ext2_inode_scan) malloc(sizeof(struct ext2_struct_inode_scan));
 	if (!scan)
 		return ENOMEM;
@@ -41,6 +81,7 @@
 	scan->inode_buffer = malloc(scan->inode_buffer_blocks * fs->blocksize);
 	scan->done_group = 0;
 	scan->done_group_data = 0;
+	scan->bad_block_ptr = 0;
 	if (!scan->inode_buffer) {
 		free(scan);
 		return ENOMEM;
@@ -51,6 +92,8 @@
 		free(scan);
 		return ENOMEM;
 	}
+	if (scan->fs->badblocks && scan->fs->badblocks->num)
+		scan->scan_flags |= EXT2_SF_CHK_BADBLOCKS;
 	*ret_scan = scan;
 	return 0;
 }
@@ -82,11 +125,168 @@
 	scan->done_group_data = done_group_data;
 }
 
+int ext2fs_inode_scan_flags(ext2_inode_scan scan, int set_flags,
+			    int clear_flags)
+{
+	int	old_flags;
+
+	if (!scan || (scan->magic != EXT2_ET_MAGIC_INODE_SCAN))
+		return 0;
+
+	old_flags = scan->scan_flags;
+	scan->scan_flags &= ~clear_flags;
+	scan->scan_flags |= set_flags;
+	return old_flags;
+}
+
+/*
+ * This function is called by ext2fs_get_next_inode when it needs to
+ * get ready to read in a new blockgroup.
+ */
+static errcode_t get_next_blockgroup(ext2_inode_scan scan)
+{
+	scan->current_group++;
+	scan->groups_left--;
+			
+	scan->current_block = scan->fs->
+		group_desc[scan->current_group].bg_inode_table;
+
+	scan->bytes_left = 0;
+	scan->inodes_left = EXT2_INODES_PER_GROUP(scan->fs->super);
+	scan->blocks_left = scan->fs->inode_blocks_per_group;
+	return 0;
+}
+
+errcode_t ext2fs_inode_scan_goto_blockgroup(ext2_inode_scan scan,
+					    int	group)
+{
+	scan->current_group = group - 1;
+	scan->groups_left = scan->fs->group_desc_count - group;
+	return get_next_blockgroup(scan);
+}
+
+/*
+ * This function is called by get_next_blocks() to check for bad
+ * blocks in the inode table.
+ *
+ * This function assumes that badblocks_list->list is sorted in
+ * increasing order.
+ */
+static errcode_t check_for_inode_bad_blocks(ext2_inode_scan scan,
+					    int *num_blocks)
+{
+	blk_t	blk = scan->current_block;
+	badblocks_list	bb = scan->fs->badblocks;
+
+	/*
+	 * If the inode table is missing, then obviously there are no
+	 * bad blocks.  :-)
+	 */
+	if (blk == 0)
+		return 0;
+
+	/*
+	 * If the current block is greater than the bad block listed
+	 * in the bad block list, then advance the pointer until this
+	 * is no longer the case.  If we run out of bad blocks, then
+	 * we don't need to do any more checking!
+	 */
+	while (blk > bb->list[scan->bad_block_ptr]) {
+		if (++scan->bad_block_ptr >= bb->num) {
+			scan->scan_flags &= ~EXT2_SF_CHK_BADBLOCKS;
+			return 0;
+		}
+	}
+
+	/*
+	 * If the current block is equal to the bad block listed in
+	 * the bad block list, then handle that one block specially.
+	 * (We could try to handle runs of bad blocks, but that
+	 * only increases CPU efficiency by a small amount, at the
+	 * expense of a huge expense of code complexity, and for an
+	 * uncommon case at that.)
+	 */
+	if (blk == bb->list[scan->bad_block_ptr]) {
+		scan->scan_flags |= EXT2_SF_BAD_INODE_BLK;
+		*num_blocks = 1;
+		if (++scan->bad_block_ptr >= bb->num)
+			scan->scan_flags &= ~EXT2_SF_CHK_BADBLOCKS;
+		return 0;
+	}
+
+	/*
+	 * If there is a bad block in the range that we're about to
+	 * read in, adjust the number of blocks to read so that we we
+	 * don't read in the bad block.  (Then the next block to read
+	 * will be the bad block, which is handled in the above case.)
+	 */
+	if ((blk + *num_blocks) > bb->list[scan->bad_block_ptr])
+		*num_blocks = bb->list[scan->bad_block_ptr] - blk;
+
+	return 0;
+}
+
+/*
+ * This function is called by ext2fs_get_next_inode when it needs to
+ * read in more blocks from the current blockgroup's inode table.
+ */
+static errcode_t get_next_blocks(ext2_inode_scan scan)
+{
+	int		num_blocks;
+	errcode_t	retval;
+
+	/*
+	 * Figure out how many blocks to read; we read at most
+	 * inode_buffer_blocks, and perhaps less if there aren't that
+	 * many blocks left to read.
+	 */
+	num_blocks = scan->inode_buffer_blocks;
+	if (num_blocks > scan->blocks_left)
+		num_blocks = scan->blocks_left;
+
+	/*
+	 * If the past block "read" was a bad block, then mark the
+	 * left-over extra bytes as also being bad.
+	 */
+	if (scan->scan_flags & EXT2_SF_BAD_INODE_BLK) {
+		if (scan->bytes_left)
+			scan->scan_flags |= EXT2_SF_BAD_EXTRA_BYTES;
+		scan->scan_flags &= ~EXT2_SF_BAD_INODE_BLK;
+	}
+
+	/*
+	 * Do inode bad block processing, if necessary.
+	 */
+	if (scan->scan_flags & EXT2_SF_CHK_BADBLOCKS) {
+		retval = check_for_inode_bad_blocks(scan, &num_blocks);
+		if (retval)
+			return retval;
+	}
+		
+	if ((scan->scan_flags & EXT2_SF_BAD_INODE_BLK) ||
+	    (scan->current_block == 0)) {
+		memset(scan->inode_buffer, 0,
+		       num_blocks * scan->fs->blocksize);
+	} else {
+		retval = io_channel_read_blk(scan->fs->io,
+					     scan->current_block,
+					     num_blocks, scan->inode_buffer);
+		if (retval)
+			return EXT2_ET_NEXT_INODE_READ;
+	}
+	scan->ptr = scan->inode_buffer;
+	scan->bytes_left = num_blocks * scan->fs->blocksize;
+
+	scan->blocks_left -= num_blocks;
+	if (scan->current_block)
+		scan->current_block += num_blocks;
+	return 0;
+}
+
 errcode_t ext2fs_get_next_inode(ext2_inode_scan scan, ino_t *ino,
 				struct ext2_inode *inode)
 {
 	errcode_t	retval;
-	int		num_blocks;
 	int		extra_bytes = 0;
 	
 	EXT2_CHECK_MAGIC(scan, EXT2_ET_MAGIC_INODE_SCAN);
@@ -95,10 +295,10 @@
 	 * Do we need to start reading a new block group?
 	 */
 	if (scan->inodes_left <= 0) {
+	retry:
 		if (scan->done_group) {
 			retval = (scan->done_group)
-				(scan->fs, scan,
-				 scan->current_group,
+				(scan->fs, scan, scan->current_group,
 				 scan->done_group_data);
 			if (retval)
 				return retval;
@@ -107,17 +307,15 @@
 			*ino = 0;
 			return 0;
 		}
-		scan->current_group++;
-		scan->groups_left--;
-			
-		scan->current_block = scan->fs->
-			group_desc[scan->current_group].bg_inode_table;
-
-		if (scan->current_block == 0)
-			return EXT2_ET_MISSING_INODE_TABLE;
-		scan->bytes_left = 0;
-		scan->inodes_left = EXT2_INODES_PER_GROUP(scan->fs->super);
-		scan->blocks_left = scan->fs->inode_blocks_per_group;
+		retval = get_next_blockgroup(scan);
+		if (retval)
+			return retval;
+		if (scan->current_block == 0) {
+			if (scan->scan_flags & EXT2_SF_SKIP_MISSING_ITABLE) {
+				goto retry;
+			} else
+				return EXT2_ET_MISSING_INODE_TABLE;
+		}
 	}
 
 	/*
@@ -127,22 +325,13 @@
 	if (scan->bytes_left < scan->inode_size) {
 		memcpy(scan->temp_buffer, scan->ptr, scan->bytes_left);
 		extra_bytes = scan->bytes_left;
-		
-		scan->blocks_left -= scan->inode_buffer_blocks;
-		num_blocks = scan->inode_buffer_blocks;
-		if (scan->blocks_left < 0)
-			num_blocks += scan->blocks_left;
-		
-		retval = io_channel_read_blk(scan->fs->io, scan->current_block,
-					     num_blocks, scan->inode_buffer);
+
+		retval = get_next_blocks(scan);
 		if (retval)
-			return EXT2_ET_NEXT_INODE_READ;
-		scan->ptr = scan->inode_buffer;
-		scan->bytes_left = num_blocks * scan->fs->blocksize;
-	
-		scan->current_block += scan->inode_buffer_blocks;
+			return retval;
 	}
 
+	retval = 0;
 	if (extra_bytes) {
 		memcpy(scan->temp_buffer+extra_bytes, scan->ptr,
 		       scan->inode_size - extra_bytes);
@@ -155,6 +344,9 @@
 				 (struct ext2_inode *) scan->temp_buffer, 0);
 		else
 			*inode = *((struct ext2_inode *) scan->temp_buffer);
+		if (scan->scan_flags & EXT2_SF_BAD_EXTRA_BYTES)
+			retval = EXT2_ET_BAD_BLOCK_IN_INODE_TABLE;
+		scan->scan_flags &= ~EXT2_SF_BAD_EXTRA_BYTES;
 	} else {
 		if ((scan->fs->flags & EXT2_FLAG_SWAP_BYTES) ||
 		    (scan->fs->flags & EXT2_FLAG_SWAP_BYTES_READ))
@@ -164,12 +356,14 @@
 			*inode = *((struct ext2_inode *) scan->ptr);
 		scan->ptr += scan->inode_size;
 		scan->bytes_left -= scan->inode_size;
+		if (scan->scan_flags & EXT2_SF_BAD_INODE_BLK)
+			retval = EXT2_ET_BAD_BLOCK_IN_INODE_TABLE;
 	}
 
 	scan->inodes_left--;
 	scan->current_inode++;
 	*ino = scan->current_inode;
-	return 0;
+	return retval;
 }
 
 /*
@@ -188,7 +382,7 @@
 #endif
 
 
-errcode_t ext2fs_read_inode (ext2_filsys fs, unsigned long ino,
+errcode_t ext2fs_read_inode (ext2_filsys fs, ino_t ino,
 			     struct ext2_inode * inode)
 {
 	unsigned long 	group, block, block_nr, offset;
@@ -234,6 +428,8 @@
 	offset = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) *
 		EXT2_INODE_SIZE(fs->super);
 	block = offset >> EXT2_BLOCK_SIZE_BITS(fs->super);
+	if (!fs->group_desc[group].bg_inode_table)
+		return EXT2_ET_MISSING_INODE_TABLE;
 	block_nr = fs->group_desc[group].bg_inode_table + block;
 	if (block_nr != inode_buffer_block) {
 		retval = io_channel_read_blk(fs->io, block_nr, 1,
@@ -280,8 +476,8 @@
 	return 0;
 }
 
-errcode_t ext2fs_write_inode(ext2_filsys fs, unsigned long ino,
-		     struct ext2_inode * inode)
+errcode_t ext2fs_write_inode(ext2_filsys fs, ino_t ino,
+			     struct ext2_inode * inode)
 {
 	unsigned long group, block, block_nr, offset;
 	errcode_t	retval;
@@ -332,11 +528,14 @@
 	offset = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) *
 		EXT2_INODE_SIZE(fs->super);
 	block = offset >> EXT2_BLOCK_SIZE_BITS(fs->super);
+	if (!fs->group_desc[group].bg_inode_table)
+		return EXT2_ET_MISSING_INODE_TABLE;
 	block_nr = fs->group_desc[group].bg_inode_table + block;
 	offset &= (EXT2_BLOCK_SIZE(fs->super) - 1);
 	ptr = (char *) inode_buffer + offset;
 
 	length = EXT2_INODE_SIZE(fs->super);
+	clen = length;
 	if (length > sizeof(struct ext2_inode))
 		length = sizeof(struct ext2_inode);
 	
@@ -350,12 +549,11 @@
 	
 	if ((offset + length) > EXT2_BLOCK_SIZE(fs->super)) {
 		clen = EXT2_BLOCK_SIZE(fs->super) - offset;
-		memcpy(ptr, &temp_inode, clen);
 		length -= clen;
 	} else {
-		memcpy(ptr, &temp_inode, length);
 		length = 0;
 	}
+	memcpy(ptr, &temp_inode, clen);
 	retval = io_channel_write_blk(fs->io, block_nr, 1, inode_buffer);
 	if (retval)
 		return retval;
@@ -419,7 +617,7 @@
 	if (retval)
 		return retval;
 	if (!LINUX_S_ISDIR(inode.i_mode))
-	return ENOTDIR;
+		return ENOTDIR;
 	return 0;
 }
 
diff --git a/lib/ext2fs/io.h b/lib/ext2fs/io.h
index 7a44a11..6b28fee 100644
--- a/lib/ext2fs/io.h
+++ b/lib/ext2fs/io.h
@@ -1,8 +1,12 @@
 /*
  * io.h --- the I/O manager abstraction
  * 
- * Copyright (C) 1993 Theodore Ts'o.  This file may be redistributed
- * under the terms of the GNU Public License.
+ * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
  */
 
 /*
@@ -15,7 +19,7 @@
 #endif
 
 /* llseek.c */
-ext2_loff_t ext2_llseek (unsigned int, ext2_loff_t, unsigned int);
+ext2_loff_t ext2fs_llseek (unsigned int, ext2_loff_t, unsigned int);
 
 typedef struct struct_io_manager *io_manager;
 typedef struct struct_io_channel *io_channel;
@@ -68,8 +72,14 @@
 #define io_channel_write_blk(c,b,n,d)	((c)->manager->write_blk((c),b,n,d))
 #define io_channel_flush(c) 		((c)->manager->flush((c)))
 	
+/* unix_io.c */
 extern io_manager unix_io_manager;
 
-
-
-
+/* test_io.c */
+extern io_manager test_io_manager, test_io_backing_manager;
+extern void (*test_io_cb_read_blk)
+	(unsigned long block, int count, errcode_t err);
+extern void (*test_io_cb_write_blk)
+	(unsigned long block, int count, errcode_t err);
+extern void (*test_io_cb_set_blksize)
+	(int blksize, errcode_t err);
diff --git a/lib/ext2fs/irel.h b/lib/ext2fs/irel.h
new file mode 100644
index 0000000..9c7529a
--- /dev/null
+++ b/lib/ext2fs/irel.h
@@ -0,0 +1,114 @@
+/*
+ * irel.h
+ * 
+ * Copyright (C) 1996, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+struct ext2_inode_reference {
+	blk_t	block;
+	__u16 offset;
+};
+
+struct ext2_inode_relocate_entry {
+	ino_t	new;
+	ino_t	orig;
+	__u16	flags;
+	__u16	max_refs;
+};
+
+typedef struct ext2_inode_relocation_table *ext2_irel;
+
+struct ext2_inode_relocation_table {
+	__u32	magic;
+	char	*name;
+	ino_t	current;
+	void	*private;
+
+	/*
+	 * Add an inode relocation entry.
+	 */
+	errcode_t (*put)(ext2_irel irel, ino_t old,
+			      struct ext2_inode_relocate_entry *ent);
+	/*
+	 * Get an inode relocation entry.
+	 */
+	errcode_t (*get)(ext2_irel irel, ino_t old,
+			      struct ext2_inode_relocate_entry *ent);
+
+	/*
+	 * Get an inode relocation entry by its original inode number
+	 */
+	errcode_t (*get_by_orig)(ext2_irel irel, ino_t orig, ino_t *old,
+				 struct ext2_inode_relocate_entry *ent);
+
+	/*
+	 * Initialize for iterating over the inode relocation entries.
+	 */
+	errcode_t (*start_iter)(ext2_irel irel);
+
+	/*
+	 * The iterator function for the inode relocation entries.
+	 * Returns an inode number of 0 when out of entries.
+	 */
+	errcode_t (*next)(ext2_irel irel, ino_t *old,
+			  struct ext2_inode_relocate_entry *ent);
+
+	/*
+	 * Add an inode reference (i.e., note the fact that a
+	 * particular block/offset contains a reference to an inode)
+	 */
+	errcode_t (*add_ref)(ext2_irel irel, ino_t ino,
+			     struct ext2_inode_reference *ref);
+
+	/*
+	 * Initialize for iterating over the inode references for a
+	 * particular inode.
+	 */
+	errcode_t (*start_iter_ref)(ext2_irel irel, ino_t ino);
+
+	/*
+	 * The iterator function for the inode references for an
+	 * inode.  The references for only one inode can be interator
+	 * over at a time, as the iterator state is stored in ext2_irel.
+	 */
+	errcode_t (*next_ref)(ext2_irel irel,
+			      struct ext2_inode_reference *ref);
+
+	/*
+	 * Move the inode relocation table from one inode number to
+	 * another.  Note that the inode references also must move.
+	 */
+	errcode_t (*move)(ext2_irel irel, ino_t old, ino_t new);
+
+	/*
+	 * Remove an inode relocation entry, along with all of the
+	 * inode references.
+	 */
+	errcode_t (*delete)(ext2_irel irel, ino_t old);
+
+	/*
+	 * Free the inode relocation table.
+	 */
+	errcode_t (*free)(ext2_irel irel);
+};
+
+errcode_t ext2fs_irel_memarray_create(char *name, ino_t max_inode,
+				    ext2_irel *irel);
+
+#define ext2fs_irel_put(irel, old, ent) ((irel)->put((irel), old, ent))
+#define ext2fs_irel_get(irel, old, ent) ((irel)->get((irel), old, ent))
+#define ext2fs_irel_get_by_orig(irel, orig, old, ent) \
+			((irel)->get_by_orig((irel), orig, old, ent))
+#define ext2fs_irel_start_iter(irel) ((irel)->start_iter((irel)))
+#define ext2fs_irel_next(irel, old, ent) ((irel)->next((irel), old, ent))
+#define ext2fs_irel_add_ref(irel, ino, ref) ((irel)->add_ref((irel), ino, ref))
+#define ext2fs_irel_start_iter_ref(irel, ino) ((irel)->start_iter_ref((irel), ino))
+#define ext2fs_irel_next_ref(irel, ref) ((irel)->next_ref((irel), ref))
+#define ext2fs_irel_move(irel, old, new) ((irel)->move((irel), old, new))
+#define ext2fs_irel_delete(irel, old) ((irel)->delete((irel), old))
+#define ext2fs_irel_free(irel) ((irel)->free((irel)))
diff --git a/lib/ext2fs/irel_ma.c b/lib/ext2fs/irel_ma.c
new file mode 100644
index 0000000..1bb3af6
--- /dev/null
+++ b/lib/ext2fs/irel_ma.c
@@ -0,0 +1,364 @@
+/*
+ * irel_ma.c
+ * 
+ * Copyright (C) 1996, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <linux/ext2_fs.h>
+
+#include "ext2fs.h"
+#include "irel.h"
+
+static errcode_t ima_put(ext2_irel irel, ino_t old,
+			 struct ext2_inode_relocate_entry *ent);
+static errcode_t ima_get(ext2_irel irel, ino_t old,
+			 struct ext2_inode_relocate_entry *ent);
+static errcode_t ima_get_by_orig(ext2_irel irel, ino_t orig, ino_t *old,
+				 struct ext2_inode_relocate_entry *ent);
+static errcode_t ima_start_iter(ext2_irel irel);
+static errcode_t ima_next(ext2_irel irel, ino_t *old,
+			  struct ext2_inode_relocate_entry *ent);
+static errcode_t ima_add_ref(ext2_irel irel, ino_t ino,
+			     struct ext2_inode_reference *ref);
+static errcode_t ima_start_iter_ref(ext2_irel irel, ino_t ino);
+static errcode_t ima_next_ref(ext2_irel irel, struct ext2_inode_reference *ref);
+static errcode_t ima_move(ext2_irel irel, ino_t old, ino_t new);
+static errcode_t ima_delete(ext2_irel irel, ino_t old);
+static errcode_t ima_free(ext2_irel irel);
+
+/*
+ * This data structure stores the array of inode references; there is
+ * a structure for each inode.
+ */
+struct inode_reference_entry {
+	__u16 num;
+	struct ext2_inode_reference *refs;
+};
+
+struct irel_ma {
+	__u32 magic;
+	ino_t max_inode;
+	ino_t ref_current;
+	int   ref_iter;
+	ino_t	*orig_map;
+	struct ext2_inode_relocate_entry *entries;
+	struct inode_reference_entry *ref_entries;
+};
+
+errcode_t ext2fs_irel_memarray_create(char *name, ino_t max_inode,
+				      ext2_irel *new_irel)
+{
+	ext2_irel		irel = 0;
+	errcode_t	retval;
+	struct irel_ma 	*ma = 0;
+	size_t		size;
+
+	*new_irel = 0;
+
+	/*
+	 * Allocate memory structures
+	 */
+	retval = ENOMEM;
+	irel = malloc(sizeof(struct ext2_inode_relocation_table));
+	if (!irel)
+		goto errout;
+	memset(irel, 0, sizeof(struct ext2_inode_relocation_table));
+	
+	irel->name = malloc(strlen(name)+1);
+	if (!irel->name)
+		goto errout;
+	strcpy(irel->name, name);
+	
+	ma = malloc(sizeof(struct irel_ma));
+	if (!ma)
+		goto errout;
+	memset(ma, 0, sizeof(struct irel_ma));
+	irel->private = ma;
+	
+	size = sizeof(ino_t) * (max_inode+1);
+	ma->orig_map = malloc(size);
+	if (!ma->orig_map)
+		goto errout;
+	memset(ma->orig_map, 0, size);
+
+	size = sizeof(struct ext2_inode_relocate_entry) * (max_inode+1);
+	ma->entries = malloc(size);
+	if (!ma->entries)
+		goto errout;
+	memset(ma->entries, 0, size);
+
+	size = sizeof(struct inode_reference_entry) * (max_inode+1);
+	ma->ref_entries = malloc(size);
+	if (!ma->ref_entries)
+		goto errout;
+	memset(ma->ref_entries, 0, size);
+	ma->max_inode = max_inode;
+
+	/*
+	 * Fill in the irel data structure
+	 */
+	irel->put = ima_put;
+	irel->get = ima_get;
+	irel->get_by_orig = ima_get_by_orig;
+	irel->start_iter = ima_start_iter;
+	irel->next = ima_next;
+	irel->add_ref = ima_add_ref;
+	irel->start_iter_ref = ima_start_iter_ref;
+	irel->next_ref = ima_next_ref;
+	irel->move = ima_move;
+	irel->delete = ima_delete;
+	irel->free = ima_free;
+	
+	*new_irel = irel;
+	return 0;
+
+errout:
+	ima_free(irel);
+	return retval;
+}
+
+static errcode_t ima_put(ext2_irel irel, ino_t old,
+			struct ext2_inode_relocate_entry *ent)
+{
+	struct irel_ma 	*ma;
+	struct inode_reference_entry *ref_ent;
+	struct ext2_inode_reference *new_refs;
+	int size;
+
+	ma = irel->private;
+	if (old > ma->max_inode)
+		return EINVAL;
+
+	/*
+	 * Force the orig field to the correct value; the application
+	 * program shouldn't be messing with this field.
+	 */
+	if (ma->entries[old].new == 0)
+		ent->orig = old;
+	else
+		ent->orig = ma->entries[old].orig;
+	
+	/*
+	 * If max_refs has changed, reallocate the refs array
+	 */
+	ref_ent = ma->ref_entries + old;
+	if (ref_ent->refs && ent->max_refs != ma->entries[old].max_refs) {
+		size = (sizeof(struct ext2_inode_reference) * ent->max_refs);
+		new_refs = realloc(ref_ent->refs, size);
+		if (!new_refs)
+			return ENOMEM;
+		ref_ent->refs = new_refs;
+	}
+
+	ma->entries[old] = *ent;
+	ma->orig_map[ent->orig] = old;
+	return 0;
+}
+
+static errcode_t ima_get(ext2_irel irel, ino_t old,
+			struct ext2_inode_relocate_entry *ent)
+{
+	struct irel_ma 	*ma;
+
+	ma = irel->private;
+	if (old > ma->max_inode)
+		return EINVAL;
+	if (ma->entries[old].new == 0)
+		return ENOENT;
+	*ent = ma->entries[old];
+	return 0;
+}
+
+static errcode_t ima_get_by_orig(ext2_irel irel, ino_t orig, ino_t *old,
+			struct ext2_inode_relocate_entry *ent)
+{
+	struct irel_ma 	*ma;
+	ino_t	ino;
+
+	ma = irel->private;
+	if (orig > ma->max_inode)
+		return EINVAL;
+	ino = ma->orig_map[orig];
+	if (ino == 0)
+		return ENOENT;
+	*old = ino;
+	*ent = ma->entries[ino];
+	return 0;
+}
+
+static errcode_t ima_start_iter(ext2_irel irel)
+{
+	irel->current = 0;
+	return 0;
+}
+
+static errcode_t ima_next(ext2_irel irel, ino_t *old,
+			 struct ext2_inode_relocate_entry *ent)
+{
+	struct irel_ma 	*ma;
+
+	ma = irel->private;
+	while (++irel->current < ma->max_inode) {
+		if (ma->entries[irel->current].new == 0)
+			continue;
+		*old = irel->current;
+		*ent = ma->entries[irel->current];
+		return 0;
+	}
+	*old = 0;
+	return 0;
+}
+
+static errcode_t ima_add_ref(ext2_irel irel, ino_t ino,
+			     struct ext2_inode_reference *ref)
+{
+	struct irel_ma 	*ma;
+	size_t		size;
+	struct inode_reference_entry *ref_ent;
+	struct ext2_inode_relocate_entry *ent;
+
+	ma = irel->private;
+	if (ino > ma->max_inode)
+		return EINVAL;
+
+	ref_ent = ma->ref_entries + ino;
+	ent = ma->entries + ino;
+	
+	/*
+	 * If the inode reference array doesn't exist, create it.
+	 */
+	if (ref_ent->refs == 0) {
+		size = (sizeof(struct ext2_inode_reference) * ent->max_refs);
+		ref_ent->refs = malloc(size);
+		if (ref_ent->refs == 0)
+			return ENOMEM;
+		memset(ref_ent->refs, 0, size);
+		ref_ent->num = 0;
+	}
+
+	if (ref_ent->num >= ent->max_refs)
+		return ENOSPC;
+
+	ref_ent->refs[ref_ent->num++] = *ref;
+	return 0;
+}
+
+static errcode_t ima_start_iter_ref(ext2_irel irel, ino_t ino)
+{
+	struct irel_ma 	*ma;
+
+	ma = irel->private;
+	if (ino > ma->max_inode)
+		return EINVAL;
+	if (ma->entries[ino].new == 0)
+		return ENOENT;
+	ma->ref_current = ino;
+	ma->ref_iter = 0;
+	return 0;
+}
+
+static errcode_t ima_next_ref(ext2_irel irel,
+			      struct ext2_inode_reference *ref)
+{
+	struct irel_ma 	*ma;
+	struct inode_reference_entry *ref_ent;
+
+	ma = irel->private;
+	
+	ref_ent = ma->ref_entries + ma->ref_current;
+
+	if ((ref_ent->refs == NULL) ||
+	    (ma->ref_iter >= ref_ent->num)) {
+		ref->block = 0;
+		ref->offset = 0;
+		return 0;
+	}
+	*ref = ref_ent->refs[ma->ref_iter++];
+	return 0;
+}
+
+
+static errcode_t ima_move(ext2_irel irel, ino_t old, ino_t new)
+{
+	struct irel_ma 	*ma;
+
+	ma = irel->private;
+	if ((old > ma->max_inode) || (new > ma->max_inode))
+		return EINVAL;
+	if (ma->entries[old].new == 0)
+		return ENOENT;
+	
+	ma->entries[new] = ma->entries[old];
+	if (ma->ref_entries[new].refs)
+		free(ma->ref_entries[new].refs);
+	ma->ref_entries[new] = ma->ref_entries[old];
+	
+	ma->entries[old].new = 0;
+	ma->ref_entries[old].num = 0;
+	ma->ref_entries[old].refs = 0;
+
+	ma->orig_map[ma->entries[new].orig] = new;
+	return 0;
+}
+
+static errcode_t ima_delete(ext2_irel irel, ino_t old)
+{
+	struct irel_ma 	*ma;
+
+	ma = irel->private;
+	if (old > ma->max_inode)
+		return EINVAL;
+	if (ma->entries[old].new == 0)
+		return ENOENT;
+	
+	ma->entries[old].new = 0;
+	if (ma->ref_entries[old].refs)
+		free(ma->ref_entries[old].refs);
+	ma->orig_map[ma->entries[old].orig] = 0;
+	
+	ma->ref_entries[old].num = 0;
+	ma->ref_entries[old].refs = 0;
+	return 0;
+}
+
+static errcode_t ima_free(ext2_irel irel)
+{
+	struct irel_ma 	*ma;
+	ino_t	ino;
+
+	if (!irel)
+		return 0;
+
+	ma = irel->private;
+
+	if (ma) {
+		if (ma->orig_map)
+			free (ma->orig_map);
+		if (ma->entries)
+			free (ma->entries);
+		if (ma->ref_entries) {
+			for (ino = 0; ino <= ma->max_inode; ino++) {
+				if (ma->ref_entries[ino].refs)
+					free(ma->ref_entries[ino].refs);
+			}
+			free(ma->ref_entries);
+		}
+		free(ma);
+	}
+	if (irel->name)
+		free(irel->name);
+	free (irel);
+	return 0;
+}
diff --git a/lib/ext2fs/ismounted.c b/lib/ext2fs/ismounted.c
index 781bfdf..9c2593c 100644
--- a/lib/ext2fs/ismounted.c
+++ b/lib/ext2fs/ismounted.c
@@ -1,8 +1,12 @@
 /*
- * getsize.c --- get the size of a partition.
+ * ismounted.c --- Check to see if the filesystem was mounted
  * 
- * Copyright (C) 1995 Theodore Ts'o.  This file may be
- * redistributed under the terms of the GNU Public License.
+ * Copyright (C) 1995 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
  */
 
 #include <stdio.h>
diff --git a/lib/ext2fs/link.c b/lib/ext2fs/link.c
index 042183b..933dfa8 100644
--- a/lib/ext2fs/link.c
+++ b/lib/ext2fs/link.c
@@ -1,8 +1,12 @@
 /*
- * link.c --- create or delete links in a ext2fs directory
+ * link.c --- create links in a ext2fs directory
  * 
- * Copyright (C) 1993 Theodore Ts'o.  This file may be redistributed
- * under the terms of the GNU Public License.
+ * Copyright (C) 1993, 1994 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
  */
 
 #include <stdio.h>
@@ -104,48 +108,3 @@
 
 	return (ls.done) ? 0 : EXT2_ET_DIR_NO_SPACE;
 }
-
-static int unlink_proc(struct ext2_dir_entry *dirent,
-		     int	offset,
-		     int	blocksize,
-		     char	*buf,
-		     void	*private)
-{
-	struct link_struct *ls = (struct link_struct *) private;
-
-	if (ls->name && (dirent->name_len != ls->namelen))
-		return 0;
-	if (ls->name && strncmp(ls->name, dirent->name, dirent->name_len))
-		return 0;
-	if (ls->inode && (dirent->inode != ls->inode))
-		return 0;
-
-	dirent->inode = 0;
-	ls->done++;
-	return DIRENT_ABORT|DIRENT_CHANGED;
-}
-
-errcode_t ext2fs_unlink(ext2_filsys fs, ino_t dir, const char *name, ino_t ino,
-			int flags)
-{
-	errcode_t	retval;
-	struct link_struct ls;
-
-	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
-
-	if (!(fs->flags & EXT2_FLAG_RW))
-		return EXT2_ET_RO_FILSYS;
-
-	ls.name = name;
-	ls.namelen = name ? strlen(name) : 0;
-	ls.inode = ino;
-	ls.flags = 0;
-	ls.done = 0;
-
-	retval = ext2fs_dir_iterate(fs, dir, 0, 0, unlink_proc, &ls);
-	if (retval)
-		return retval;
-
-	return (ls.done) ? 0 : EXT2_ET_DIR_NO_SPACE;
-}
-
diff --git a/lib/ext2fs/llseek.c b/lib/ext2fs/llseek.c
index 02e0ede..71a9d91 100644
--- a/lib/ext2fs/llseek.c
+++ b/lib/ext2fs/llseek.c
@@ -1,8 +1,12 @@
 /*
  * llseek.c -- stub calling the llseek system call
  *
- * Copyright (C) 1994 Remy Card.  This file may be redistributed
- * under the terms of the GNU Public License.
+ * Copyright (C) 1994, 1995, 1996 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
  */
 
 #include <sys/types.h>
@@ -55,7 +59,7 @@
 
 #endif /* __alpha__ */
 
-ext2_loff_t ext2_llseek (unsigned int fd, ext2_loff_t offset,
+ext2_loff_t ext2fs_llseek (unsigned int fd, ext2_loff_t offset,
 			 unsigned int origin)
 {
 	ext2_loff_t result;
@@ -84,7 +88,7 @@
 
 #else /* !linux */
 
-ext2_loff_t ext2_llseek (unsigned int fd, ext2_loff_t offset,
+ext2_loff_t ext2fs_llseek (unsigned int fd, ext2_loff_t offset,
 			 unsigned int origin)
 {
 	if ((sizeof(off_t) < sizeof(ext2_loff_t)) &&
diff --git a/lib/ext2fs/lookup.c b/lib/ext2fs/lookup.c
new file mode 100644
index 0000000..5cded5f
--- /dev/null
+++ b/lib/ext2fs/lookup.c
@@ -0,0 +1,69 @@
+/*
+ * lookup.c --- ext2fs directory lookup operations
+ * 
+ * Copyright (C) 1993, 1994, 1994, 1995 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include <linux/ext2_fs.h>
+
+#include "ext2fs.h"
+
+struct lookup_struct  {
+	const char	*name;
+	int		len;
+	ino_t		*inode;
+	int		found;
+};	
+
+static int lookup_proc(struct ext2_dir_entry *dirent,
+		       int	offset,
+		       int	blocksize,
+		       char	*buf,
+		       void	*private)
+{
+	struct lookup_struct *ls = (struct lookup_struct *) private;
+
+	if (ls->len != dirent->name_len)
+		return 0;
+	if (strncmp(ls->name, dirent->name, dirent->name_len))
+		return 0;
+	*ls->inode = dirent->inode;
+	ls->found++;
+	return DIRENT_ABORT;
+}
+
+
+errcode_t ext2fs_lookup(ext2_filsys fs, ino_t dir, const char *name,
+			int namelen, char *buf, ino_t *inode)
+{
+	errcode_t	retval;
+	struct lookup_struct ls;
+
+	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+	ls.name = name;
+	ls.len = namelen;
+	ls.inode = inode;
+	ls.found = 0;
+
+	retval = ext2fs_dir_iterate(fs, dir, 0, buf, lookup_proc, &ls);
+	if (retval)
+		return retval;
+
+	return (ls.found) ? 0 : ENOENT;
+}
+
+
diff --git a/lib/ext2fs/mkdir.c b/lib/ext2fs/mkdir.c
index af3b9b7..67a2046 100644
--- a/lib/ext2fs/mkdir.c
+++ b/lib/ext2fs/mkdir.c
@@ -1,8 +1,12 @@
 /*
  * mkdir.c --- make a directory in the filesystem
  * 
- * Copyright (C) 1994 Theodore Ts'o.  This file may be redistributed
- * under the terms of the GNU Public License.
+ * Copyright (C) 1994, 1995 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
  */
 
 #include <stdio.h>
diff --git a/lib/ext2fs/namei.c b/lib/ext2fs/namei.c
index 8fc71b0..ae39eec 100644
--- a/lib/ext2fs/namei.c
+++ b/lib/ext2fs/namei.c
@@ -1,8 +1,12 @@
 /*
  * namei.c --- ext2fs directory lookup operations
  * 
- * Copyright (C) 1993, 1994 Theodore Ts'o.  This file may be
- * redistributed under the terms of the GNU Public License.
+ * Copyright (C) 1993, 1994, 1994, 1995 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
  */
 
 #include <stdio.h>
@@ -19,163 +23,6 @@
 
 #include "ext2fs.h"
 
-struct dir_context {
-	ino_t		dir;
-	int		flags;
-	char		*buf;
-	int (*func)(struct ext2_dir_entry *dirent,
-		    int	offset,
-		    int	blocksize,
-		    char	*buf,
-		    void	*private);
-	void		*private;
-	errcode_t	errcode;
-};
-
-static int process_dir_block(ext2_filsys fs,
-			     blk_t	*blocknr,
-			     int	blockcnt,
-			     void	*private);
-
-errcode_t ext2fs_dir_iterate(ext2_filsys fs,
-			     ino_t dir,
-			     int flags,
-			     char *block_buf,
-			     int (*func)(struct ext2_dir_entry *dirent,
-					 int	offset,
-					 int	blocksize,
-					 char	*buf,
-					 void	*private),
-			     void *private)
-{
-	struct		dir_context	ctx;
-	errcode_t	retval;
-	
-	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
-
-	retval = ext2fs_check_directory(fs, dir);
-	if (retval)
-		return retval;
-	
-	ctx.dir = dir;
-	ctx.flags = flags;
-	if (block_buf)
-		ctx.buf = block_buf;
-	else {
-		ctx.buf = malloc(fs->blocksize);
-		if (!ctx.buf)
-			return ENOMEM;
-	}
-	ctx.func = func;
-	ctx.private = private;
-	ctx.errcode = 0;
-	retval = ext2fs_block_iterate(fs, dir, 0, 0, process_dir_block, &ctx);
-	if (!block_buf)
-		free(ctx.buf);
-	if (retval)
-		return retval;
-	return ctx.errcode;
-}
-
-static int process_dir_block(ext2_filsys  fs,
-			     blk_t	*blocknr,
-			     int	blockcnt,
-			     void	*private)
-{
-	struct dir_context *ctx = (struct dir_context *) private;
-	int		offset = 0;
-	int		ret;
-	int		changed = 0;
-	int		do_abort = 0;
-	struct ext2_dir_entry *dirent;
-
-	if (blockcnt < 0)
-		return 0;
-
-	ctx->errcode = ext2fs_read_dir_block(fs, *blocknr, ctx->buf);
-	if (ctx->errcode)
-		return BLOCK_ABORT;
-	
-	while (offset < fs->blocksize) {
-		dirent = (struct ext2_dir_entry *) (ctx->buf + offset);
-		if (!dirent->inode &&
-		    !(ctx->flags & DIRENT_FLAG_INCLUDE_EMPTY))
-			goto next;
-
-		ret = (ctx->func)(dirent, offset, fs->blocksize,
-				  ctx->buf, ctx->private);
-		if (ret & DIRENT_CHANGED)
-			changed++;
-		if (ret & DIRENT_ABORT) {
-			do_abort++;
-			break;
-		}
-next:		
-		if (((offset + dirent->rec_len) > fs->blocksize) ||
-		    (dirent->rec_len < 8) ||
-		    ((dirent->name_len+8) > dirent->rec_len)) {
-			ctx->errcode = EXT2_ET_DIR_CORRUPTED;
-			return BLOCK_ABORT;
-		}
-		offset += dirent->rec_len;
-	}
-
-	if (changed) {
-		ctx->errcode = ext2fs_write_dir_block(fs, *blocknr, ctx->buf);
-		if (ctx->errcode)
-			return BLOCK_ABORT;
-	}
-	if (do_abort)
-		return BLOCK_ABORT;
-	return 0;
-}
-
-struct lookup_struct  {
-	const char	*name;
-	int		len;
-	ino_t		*inode;
-	int		found;
-};	
-
-static int lookup_proc(struct ext2_dir_entry *dirent,
-		       int	offset,
-		       int	blocksize,
-		       char	*buf,
-		       void	*private)
-{
-	struct lookup_struct *ls = (struct lookup_struct *) private;
-
-	if (ls->len != dirent->name_len)
-		return 0;
-	if (strncmp(ls->name, dirent->name, dirent->name_len))
-		return 0;
-	*ls->inode = dirent->inode;
-	ls->found++;
-	return DIRENT_ABORT;
-}
-
-
-errcode_t ext2fs_lookup(ext2_filsys fs, ino_t dir, const char *name,
-			int namelen, char *buf, ino_t *inode)
-{
-	errcode_t	retval;
-	struct lookup_struct ls;
-
-	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
-
-	ls.name = name;
-	ls.len = namelen;
-	ls.inode = inode;
-	ls.found = 0;
-
-	retval = ext2fs_dir_iterate(fs, dir, 0, buf, lookup_proc, &ls);
-	if (retval)
-		return retval;
-
-	return (ls.found) ? 0 : ENOENT;
-}
-
-
 static errcode_t open_namei(ext2_filsys fs, ino_t root, ino_t base,
 			    const char *pathname, int pathlen, int follow,
 			    int link_count, char *buf, ino_t *res_inode);
diff --git a/lib/ext2fs/native.c b/lib/ext2fs/native.c
index 2f84bf7..3fdc4a0 100644
--- a/lib/ext2fs/native.c
+++ b/lib/ext2fs/native.c
@@ -3,8 +3,10 @@
  * 
  * Copyright (C) 1996 Theodore Ts'o.
  * 
+ * %Begin-Header%
  * This file may be redistributed under the terms of the GNU Public
  * License.
+ * %End-Header%
  */
 
 #include <stdio.h>
diff --git a/lib/ext2fs/newdir.c b/lib/ext2fs/newdir.c
index 2254726..863960f 100644
--- a/lib/ext2fs/newdir.c
+++ b/lib/ext2fs/newdir.c
@@ -1,8 +1,12 @@
 /*
  * newdir.c --- create a new directory block
  * 
- * Copyright (C) 1994 Theodore Ts'o.  This file may be redistributed
- * under the terms of the GNU Public License.
+ * Copyright (C) 1994, 1995 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
  */
 
 #include <stdio.h>
diff --git a/lib/ext2fs/openfs.c b/lib/ext2fs/openfs.c
index 389bfc7..b2e2393 100644
--- a/lib/ext2fs/openfs.c
+++ b/lib/ext2fs/openfs.c
@@ -1,10 +1,12 @@
 /*
  * openfs.c --- open an ext2 filesystem
  * 
- * Copyright (C) 1993, 1994, 1995 Theodore Ts'o.
+ * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.
  * 
+ * %Begin-Header%
  * This file may be redistributed under the terms of the GNU Public
  * License.
+ * %End-Header%
  */
 
 #include <stdio.h>
@@ -30,6 +32,12 @@
 /*
  *  Note: if superblock is non-zero, block-size must also be non-zero.
  * 	Superblock and block_size can be zero to use the default size.
+ *
+ * Valid flags for ext2fs_open()
+ * 
+ * 	EXT2_FLAG_RW	- Open the filesystem for read/write.
+ * 	EXT2_FLAG_FORCE - Open the filesystem even if some of the
+ *  				features aren't supported.
  */
 errcode_t ext2fs_open(const char *name, int flags, int superblock,
 		      int block_size, io_manager manager, ext2_filsys *ret_fs)
@@ -39,6 +47,7 @@
 	int		i, j, group_block, groups_per_block;
 	char		*dest;
 	struct ext2_group_desc *gdp;
+	struct ext2fs_sb	*s;
 	
 	EXT2_CHECK_MAGIC(manager, EXT2_ET_MAGIC_IO_MANAGER);
 	
@@ -112,6 +121,22 @@
 	}
 #endif
 #endif
+	/*
+	 * Check for feature set incompatibility
+	 */
+	if (!(flags & EXT2_FLAG_FORCE)) {
+		s = (struct ext2fs_sb *) fs->super;
+		if (s->s_feature_incompat) {
+			retval = EXT2_ET_UNSUPP_FEATURE;
+			goto cleanup;
+		}
+		if ((flags & EXT2_FLAG_RW) &&
+		    s->s_feature_ro_compat) {
+			retval = EXT2_ET_RO_UNSUPP_FEATURE;
+			goto cleanup;
+		}
+	}
+	
 	fs->blocksize = EXT2_BLOCK_SIZE(fs->super);
 	if (fs->blocksize == 0) {
 		retval = EXT2_ET_CORRUPT_SUPERBLOCK;
diff --git a/lib/ext2fs/read_bb.c b/lib/ext2fs/read_bb.c
index 1a9f774..3fabddd 100644
--- a/lib/ext2fs/read_bb.c
+++ b/lib/ext2fs/read_bb.c
@@ -1,8 +1,12 @@
 /*
  * read_bb --- read the bad blocks inode
  *
- * Copyright (C) 1994 Theodore Ts'o.  This file may be redistributed
- * under the terms of the GNU Public License.
+ * Copyright (C) 1994 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
  */
 
 #include <stdio.h>
@@ -19,7 +23,7 @@
 #include "ext2fs.h"
 
 struct read_bb_record {
-	badblocks_list	bb_list;
+	ext2_badblocks_list	bb_list;
 	errcode_t	err;
 };
 
@@ -34,7 +38,7 @@
 	if (blockcnt < 0)
 		return 0;
 	
-	rb->err = badblocks_list_add(rb->bb_list, *block_nr);
+	rb->err = ext2fs_badblocks_list_add(rb->bb_list, *block_nr);
 	if (rb->err)
 		return BLOCK_ABORT;
 	return 0;
@@ -43,7 +47,7 @@
 /*
  * Reads the current bad blocks from the bad blocks inode.
  */
-errcode_t ext2fs_read_bb_inode(ext2_filsys fs, badblocks_list *bb_list)
+errcode_t ext2fs_read_bb_inode(ext2_filsys fs, ext2_badblocks_list *bb_list)
 {
 	errcode_t	retval;
 	struct read_bb_record rb;
@@ -57,7 +61,7 @@
 		if (retval)
 			return retval;
 		numblocks = (inode.i_blocks / (fs->blocksize / 512)) + 20;
-		retval = badblocks_list_create(bb_list, numblocks);
+		retval = ext2fs_badblocks_list_create(bb_list, numblocks);
 		if (retval)
 			return retval;
 	}
diff --git a/lib/ext2fs/read_bb_file.c b/lib/ext2fs/read_bb_file.c
index 04e1581..16c2e0b 100644
--- a/lib/ext2fs/read_bb_file.c
+++ b/lib/ext2fs/read_bb_file.c
@@ -1,8 +1,12 @@
 /*
  * read_bb_file.c --- read a list of bad blocks for a FILE *
  *
- * Copyright (C) 1994 Theodore Ts'o.  This file may be redistributed
- * under the terms of the GNU Public License.
+ * Copyright (C) 1994, 1995 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
  */
 
 #include <stdio.h>
@@ -22,7 +26,7 @@
  * Reads a list of bad blocks from  a FILE *
  */
 errcode_t ext2fs_read_bb_FILE(ext2_filsys fs, FILE *f, 
-			      badblocks_list *bb_list,
+			      ext2_badblocks_list *bb_list,
 			      void (*invalid)(ext2_filsys fs, blk_t blk))
 {
 	errcode_t	retval;
@@ -33,7 +37,7 @@
 	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
 
 	if (!*bb_list) {
-		retval = badblocks_list_create(bb_list, 10);
+		retval = ext2fs_badblocks_list_create(bb_list, 10);
 		if (retval)
 			return retval;
 	}
@@ -50,7 +54,7 @@
 				(invalid)(fs, blockno);
 			continue;
 		}
-		retval = badblocks_list_add(*bb_list, blockno);
+		retval = ext2fs_badblocks_list_add(*bb_list, blockno);
 		if (retval)
 			return retval;
 	}
diff --git a/lib/ext2fs/rs_bitmap.c b/lib/ext2fs/rs_bitmap.c
new file mode 100644
index 0000000..9017853
--- /dev/null
+++ b/lib/ext2fs/rs_bitmap.c
@@ -0,0 +1,92 @@
+/*
+ * rs_bitmap.c --- routine for changing the size of a bitmap
+ *
+ * Copyright (C) 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>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <time.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include <linux/ext2_fs.h>
+
+#include "ext2fs.h"
+
+errcode_t ext2fs_resize_generic_bitmap(__u32 new_end, __u32 new_real_end,
+				       ext2fs_generic_bitmap bmap)
+{
+	size_t	size, new_size;
+	char 	*new_bitmap;
+
+	if (!bmap)
+		return EINVAL;
+
+	EXT2_CHECK_MAGIC(bmap, EXT2_ET_MAGIC_GENERIC_BITMAP);
+	
+	if (new_real_end == bmap->real_end) {
+		bmap->end = new_end;
+		return 0;
+	}
+	
+	size = ((bmap->real_end - bmap->start) / 8) + 1;
+	new_size = ((new_real_end - bmap->start) / 8) + 1;
+
+	new_bitmap = realloc(bmap->bitmap, new_size);
+	if (!new_bitmap)
+		return ENOMEM;
+	if (new_size > size)
+		memset(new_bitmap + size, 0, new_size - size);
+
+	bmap->bitmap = new_bitmap;
+	bmap->end = new_end;
+	bmap->real_end = new_real_end;
+	return 0;
+}
+
+errcode_t ext2fs_resize_inode_bitmap(__u32 new_end, __u32 new_real_end,
+				     ext2fs_inode_bitmap bmap)
+{
+	errcode_t	retval;
+	
+	if (!bmap)
+		return EINVAL;
+
+	EXT2_CHECK_MAGIC(bmap, EXT2_ET_MAGIC_INODE_BITMAP);
+
+	bmap->magic = EXT2_ET_MAGIC_GENERIC_BITMAP;
+	retval = ext2fs_resize_generic_bitmap(new_end, new_real_end,
+					      bmap);
+	bmap->magic = EXT2_ET_MAGIC_INODE_BITMAP;
+	return retval;
+}
+
+errcode_t ext2fs_resize_block_bitmap(__u32 new_end, __u32 new_real_end,
+				     ext2fs_block_bitmap bmap)
+{
+	errcode_t	retval;
+	
+	if (!bmap)
+		return EINVAL;
+
+	EXT2_CHECK_MAGIC(bmap, EXT2_ET_MAGIC_BLOCK_BITMAP);
+
+	bmap->magic = EXT2_ET_MAGIC_GENERIC_BITMAP;
+	retval = ext2fs_resize_generic_bitmap(new_end, new_real_end,
+					      bmap);
+	bmap->magic = EXT2_ET_MAGIC_BLOCK_BITMAP;
+	return retval;
+}
+
diff --git a/lib/ext2fs/rw_bitmaps.c b/lib/ext2fs/rw_bitmaps.c
index 3a3f51f..e9bb58c 100644
--- a/lib/ext2fs/rw_bitmaps.c
+++ b/lib/ext2fs/rw_bitmaps.c
@@ -1,8 +1,12 @@
 /*
  * rw_bitmaps.c --- routines to read and write the  inode and block bitmaps.
  *
- * Copyright (C) 1993,1994 Theodore Ts'o.  This file may be redistributed
- * under the terms of the GNU Public License.
+ * Copyright (C) 1993, 1994, 1994, 1996 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
  */
 
 #include <stdio.h>
@@ -28,6 +32,7 @@
 	errcode_t	retval;
 	char * inode_bitmap = fs->inode_map->bitmap;
 	char * bitmap_block = NULL;
+	blk_t		blk;
 
 	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
 
@@ -43,11 +48,13 @@
 	memset(bitmap_block, 0xff, fs->blocksize);
 	for (i = 0; i < fs->group_desc_count; i++) {
 		memcpy(bitmap_block, inode_bitmap, nbytes);
-		retval = io_channel_write_blk(fs->io,
-		      fs->group_desc[i].bg_inode_bitmap, 1,
-					      bitmap_block);
-		if (retval)
-			return EXT2_ET_INODE_BITMAP_WRITE;
+		blk = fs->group_desc[i].bg_inode_bitmap;
+		if (blk) {
+			retval = io_channel_write_blk(fs->io, blk, 1,
+						      bitmap_block);
+			if (retval)
+				return EXT2_ET_INODE_BITMAP_WRITE;
+		}
 		inode_bitmap += nbytes;
 	}
 	fs->flags |= EXT2_FLAG_CHANGED;
@@ -65,6 +72,7 @@
 	errcode_t	retval;
 	char * block_bitmap = fs->block_map->bitmap;
 	char * bitmap_block = NULL;
+	blk_t		blk;
 
 	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
 
@@ -88,11 +96,13 @@
 				for (j = nbits; j < fs->blocksize * 8; j++)
 					ext2fs_set_bit(j, bitmap_block);
 		}
-		retval = io_channel_write_blk(fs->io,
-		      fs->group_desc[i].bg_block_bitmap, 1,
-					      bitmap_block);
-		if (retval)
-			return EXT2_ET_BLOCK_BITMAP_WRITE;
+		blk = fs->group_desc[i].bg_block_bitmap;
+		if (blk) {
+			retval = io_channel_write_blk(fs->io, blk, 1,
+						      bitmap_block);
+			if (retval)
+				return EXT2_ET_BLOCK_BITMAP_WRITE;
+		}
 		block_bitmap += nbytes;
 	}
 	fs->flags |= EXT2_FLAG_CHANGED;
@@ -109,6 +119,7 @@
 	errcode_t retval;
 	int block_nbytes = EXT2_BLOCKS_PER_GROUP(fs->super) / 8;
 	int inode_nbytes = EXT2_INODES_PER_GROUP(fs->super) / 8;
+	blk_t	blk;
 
 	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
 
@@ -137,25 +148,29 @@
 
 	for (i = 0; i < fs->group_desc_count; i++) {
 		if (block_bitmap) {
-			retval = io_channel_read_blk
-				(fs->io,
-				 fs->group_desc[i].bg_block_bitmap,
-				 -block_nbytes, block_bitmap);
-			if (retval) {
-				retval = EXT2_ET_BLOCK_BITMAP_READ;
-				goto cleanup;
-			}
+			blk = fs->group_desc[i].bg_block_bitmap;
+			if (blk) {
+				retval = io_channel_read_blk(fs->io, blk,
+					     -block_nbytes, block_bitmap);
+				if (retval) {
+					retval = EXT2_ET_BLOCK_BITMAP_READ;
+					goto cleanup;
+				}
+			} else
+				memset(block_bitmap, 0, block_nbytes);
 			block_bitmap += block_nbytes;
 		}
 		if (inode_bitmap) {
-			retval = io_channel_read_blk
-				(fs->io,
-				 fs->group_desc[i].bg_inode_bitmap,
-				 -inode_nbytes, inode_bitmap);
-			if (retval) {
-				retval = EXT2_ET_INODE_BITMAP_READ;
-				goto cleanup;
-			}
+			blk = fs->group_desc[i].bg_inode_bitmap;
+			if (blk) {
+				retval = io_channel_read_blk(fs->io, blk,
+					     -inode_nbytes, inode_bitmap);
+				if (retval) {
+					retval = EXT2_ET_INODE_BITMAP_READ;
+					goto cleanup;
+				}
+			} else
+				memset(inode_bitmap, 0, inode_nbytes);
 			inode_bitmap += inode_nbytes;
 		}
 	}
diff --git a/lib/ext2fs/swapfs.c b/lib/ext2fs/swapfs.c
index 968f41c..de347ac 100644
--- a/lib/ext2fs/swapfs.c
+++ b/lib/ext2fs/swapfs.c
@@ -1,8 +1,12 @@
 /*
  * swapfs.c --- swap ext2 filesystem data structures
  * 
- * Copyright (C) 1995 Theodore Ts'o.  This file may be redistributed
- * under the terms of the GNU Public License.
+ * Copyright (C) 1995, 1996 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
  */
 
 #include <stdio.h>
@@ -98,9 +102,15 @@
 
 	switch (fs->super->s_creator_os) {
 	case EXT2_OS_LINUX:
+		t->osd1.linux1.l_i_reserved1 =
+			ext2fs_swab32(f->osd1.linux1.l_i_reserved1);
 		t->osd2.linux2.l_i_frag = f->osd2.linux2.l_i_frag;
 		t->osd2.linux2.l_i_fsize = f->osd2.linux2.l_i_fsize;
 		t->osd2.linux2.i_pad1 = ext2fs_swab16(f->osd2.linux2.i_pad1);
+		t->osd2.linux2.l_i_reserved2[0] =
+			ext2fs_swab32(f->osd2.linux2.l_i_reserved2[0]);
+		t->osd2.linux2.l_i_reserved2[1] =
+			ext2fs_swab32(f->osd2.linux2.l_i_reserved2[1]);
 		break;
 	case EXT2_OS_HURD:
 		t->osd1.hurd1.h_i_translator =
@@ -117,9 +127,15 @@
 		  ext2fs_swab32 (f->osd2.hurd2.h_i_author);
 		break;
 	case EXT2_OS_MASIX:
+		t->osd1.masix1.m_i_reserved1 =
+			ext2fs_swab32(f->osd1.masix1.m_i_reserved1);
 		t->osd2.masix2.m_i_frag = f->osd2.masix2.m_i_frag;
 		t->osd2.masix2.m_i_fsize = f->osd2.masix2.m_i_fsize;
 		t->osd2.masix2.m_pad1 = ext2fs_swab16(f->osd2.masix2.m_pad1);
+		t->osd2.masix2.m_i_reserved2[0] =
+			ext2fs_swab32(f->osd2.masix2.m_i_reserved2[0]);
+		t->osd2.masix2.m_i_reserved2[1] =
+			ext2fs_swab32(f->osd2.masix2.m_i_reserved2[1]);
 		break;
 	}
 }
diff --git a/lib/ext2fs/test_io.c b/lib/ext2fs/test_io.c
new file mode 100644
index 0000000..1bd09b1
--- /dev/null
+++ b/lib/ext2fs/test_io.c
@@ -0,0 +1,227 @@
+/*
+ * test_io.c --- This is the Test I/O interface.
+ *
+ * Copyright (C) 1996 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <time.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "et/com_err.h"
+#include "ext2fs/ext2_err.h"
+#include "io.h"
+
+/*
+ * For checking structure magic numbers...
+ */
+
+#define EXT2_CHECK_MAGIC(struct, code) \
+	  if ((struct)->magic != (code)) return (code)
+  
+struct test_private_data {
+	int	magic;
+	io_channel real;
+	void (*read_blk)(unsigned long block, int count, errcode_t err);
+	void (*write_blk)(unsigned long block, int count, errcode_t err);
+	void (*set_blksize)(int blksize, errcode_t err);
+};
+
+static errcode_t test_open(const char *name, int flags, io_channel *channel);
+static errcode_t test_close(io_channel channel);
+static errcode_t test_set_blksize(io_channel channel, int blksize);
+static errcode_t test_read_blk(io_channel channel, unsigned long block,
+			       int count, void *data);
+static errcode_t test_write_blk(io_channel channel, unsigned long block,
+				int count, const void *data);
+static errcode_t test_flush(io_channel channel);
+
+static struct struct_io_manager struct_test_manager = {
+	EXT2_ET_MAGIC_IO_MANAGER,
+	"Test I/O Manager",
+	test_open,
+	test_close,
+	test_set_blksize,
+	test_read_blk,
+	test_write_blk,
+	test_flush
+};
+
+io_manager test_io_manager = &struct_test_manager;
+
+/*
+ * These global variable can be set by the test program as
+ * necessary *before* calling test_open
+ */
+io_manager test_io_backing_manager = 0;
+void (*test_io_cb_read_blk)
+	(unsigned long block, int count, errcode_t err) = 0;
+void (*test_io_cb_write_blk)
+	(unsigned long block, int count, errcode_t err) = 0;
+void (*test_io_cb_set_blksize)
+	(int blksize, errcode_t err) = 0;
+
+static errcode_t test_open(const char *name, int flags, io_channel *channel)
+{
+	io_channel	io = NULL;
+	struct test_private_data *data = NULL;
+	errcode_t	retval;
+
+	if (name == 0)
+		return EXT2_ET_BAD_DEVICE_NAME;
+	io = (io_channel) malloc(sizeof(struct struct_io_channel));
+	if (!io)
+		return ENOMEM;
+	memset(io, 0, sizeof(struct struct_io_channel));
+	io->magic = EXT2_ET_MAGIC_IO_CHANNEL;
+	data = (struct test_private_data *)
+		malloc(sizeof(struct test_private_data));
+	if (!data) {
+		retval = ENOMEM;
+		goto cleanup;
+	}
+	io->manager = test_io_manager;
+	io->name = malloc(strlen(name)+1);
+	if (!io->name) {
+		retval = ENOMEM;
+		goto cleanup;
+	}
+	strcpy(io->name, name);
+	io->private_data = data;
+	io->block_size = 1024;
+	io->read_error = 0;
+	io->write_error = 0;
+
+	memset(data, 0, sizeof(struct test_private_data));
+	data->magic = EXT2_ET_MAGIC_TEST_IO_CHANNEL;
+	if (test_io_backing_manager) {
+		retval = test_io_backing_manager->open(name, flags,
+						       &data->real);
+		if (retval)
+			goto cleanup;
+	} else
+		data->real = 0;
+	data->read_blk = 	test_io_cb_read_blk;
+	data->write_blk = 	test_io_cb_write_blk;
+	data->set_blksize = 	test_io_cb_set_blksize;
+	
+	*channel = io;
+	return 0;
+
+cleanup:
+	if (io)
+		free(io);
+	if (data)
+		free(data);
+	return retval;
+}
+
+static errcode_t test_close(io_channel channel)
+{
+	struct test_private_data *data;
+	errcode_t	retval = 0;
+
+	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+	data = (struct test_private_data *) channel->private_data;
+	EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
+
+	if (data->real)
+		retval = io_channel_close(data->real);
+	
+	if (channel->private_data)
+		free(channel->private_data);
+	if (channel->name)
+		free(channel->name);
+	free(channel);
+	return retval;
+}
+
+static errcode_t test_set_blksize(io_channel channel, int blksize)
+{
+	struct test_private_data *data;
+	errcode_t	retval = 0;
+
+	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+	data = (struct test_private_data *) channel->private_data;
+	EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
+
+	if (data->real)
+		retval = io_channel_set_blksize(data->real, blksize);
+	if (data->set_blksize)
+		data->set_blksize(blksize, retval);
+	else
+		printf("Test_io: set_blksize(%d) returned %s\n",
+		       blksize, retval ? error_message(retval) : "OK");
+	return retval;
+}
+
+
+static errcode_t test_read_blk(io_channel channel, unsigned long block,
+			       int count, void *buf)
+{
+	struct test_private_data *data;
+	errcode_t	retval = 0;
+
+	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+	data = (struct test_private_data *) channel->private_data;
+	EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
+
+	if (data->real)
+		retval = io_channel_read_blk(data->real, block, count, buf);
+	if (data->read_blk)
+		data->read_blk(block, count, retval);
+	else
+		printf("Test_io: read_blk(%lu, %d) returned %s\n",
+		       block, count, retval ? error_message(retval) : "OK");
+	return retval;
+}
+
+static errcode_t test_write_blk(io_channel channel, unsigned long block,
+			       int count, const void *buf)
+{
+	struct test_private_data *data;
+	errcode_t	retval = 0;
+
+	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+	data = (struct test_private_data *) channel->private_data;
+	EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
+
+	if (data->real)
+		retval = io_channel_write_blk(data->real, block, count, buf);
+	if (data->write_blk)
+		data->write_blk(block, count, retval);
+	else
+		printf("Test_io: write_blk(%lu, %d) returned %s\n",
+		       block, count, retval ? error_message(retval) : "OK");
+	return retval;
+}
+
+/*
+ * Flush data buffers to disk.
+ */
+static errcode_t test_flush(io_channel channel)
+{
+	struct test_private_data *data;
+	
+	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+	data = (struct test_private_data *) channel->private_data;
+	EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
+
+	if (data->real)
+		return io_channel_flush(data->real);
+	return 0;
+}
+
diff --git a/lib/ext2fs/tst_badblocks.c b/lib/ext2fs/tst_badblocks.c
new file mode 100644
index 0000000..d071406
--- /dev/null
+++ b/lib/ext2fs/tst_badblocks.c
@@ -0,0 +1,162 @@
+/*
+ * This testing program makes sure the badblocks implementation works.
+ *
+ * Copyright (C) 1996 by Theodore Ts'o.
+ * 
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <time.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include <linux/ext2_fs.h>
+
+#include "ext2fs.h"
+
+blk_t test1[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0 };
+blk_t test2[] = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
+blk_t test3[] = { 3, 1, 4, 5, 9, 2, 7, 10, 6, 8, 0 };
+blk_t test4[] = { 20, 50, 12, 17, 13, 2, 66, 23, 56, 0 };
+blk_t test4a[] = {
+ 	20, 1,
+	50, 1,
+	3, 0,
+	17, 1,
+	18, 0,
+	16, 0,
+	11, 0,
+	12, 1,
+	13, 1,
+	14, 0, 
+	80, 0,
+	45, 0,
+	66, 1,
+	0 };
+
+static int test_fail = 0;
+
+static errcode_t create_test_list(blk_t *vec, badblocks_list *ret)
+{
+	errcode_t	retval;
+	badblocks_list	bb;
+	int		i;
+	
+	retval = badblocks_list_create(&bb, 5);
+	if (retval) {
+		com_err("create_test_list", retval, "while creating list");
+		return retval;
+	}
+	for (i=0; vec[i]; i++) {
+		retval = badblocks_list_add(bb, vec[i]);
+		if (retval) {
+			com_err("create_test_list", retval,
+				"while adding test vector %d", i);
+			badblocks_list_free(bb);
+			return retval;
+		}
+	}
+	*ret = bb;
+	return 0;
+}
+
+static void print_list(badblocks_list bb, int verify)
+{
+	errcode_t	retval;
+	badblocks_iterate	iter;
+	blk_t			blk;
+	int			i, ok;
+	
+	retval = badblocks_list_iterate_begin(bb, &iter);
+	if (retval) {
+		com_err("print_list", retval, "while setting up iterator");
+		return;
+	}
+	ok = i = 1;
+	while (badblocks_list_iterate(iter, &blk)) {
+		printf("%d ", blk);
+		if (i++ != blk)
+			ok = 0;
+	}
+	badblocks_list_iterate_end(iter);
+	if (verify) {
+		if (ok)
+			printf("--- OK");
+		else {
+			printf("--- NOT OK");
+			test_fail++;
+		}
+	}
+}
+
+static void validate_test_seq(badblocks_list bb, blk_t *vec)
+{
+	int	i, match, ok;
+
+	for (i = 0; vec[i]; i += 2) {
+		match = badblocks_list_test(bb, vec[i]);
+		if (match == vec[i+1])
+			ok = 1;
+		else {
+			ok = 0;
+			test_fail++;
+		}
+		printf("\tblock %d is %s --- %s\n", vec[i],
+		       match ? "present" : "absent",
+		       ok ? "OK" : "NOT OK");
+	}
+}
+
+int main(int argc, char *argv)
+{
+	badblocks_list bb;
+	errcode_t	retval;
+
+	printf("test1: ");
+	retval = create_test_list(test1, &bb);
+	if (retval == 0) {
+		print_list(bb, 1);
+		badblocks_list_free(bb);
+	}
+	printf("\n");
+	
+	printf("test2: ");
+	retval = create_test_list(test2, &bb);
+	if (retval == 0) {
+		print_list(bb, 1);
+		badblocks_list_free(bb);
+	}
+	printf("\n");
+
+	printf("test3: ");
+	retval = create_test_list(test3, &bb);
+	if (retval == 0) {
+		print_list(bb, 1);
+		badblocks_list_free(bb);
+	}
+	printf("\n");
+	
+	printf("test4: ");
+	retval = create_test_list(test4, &bb);
+	if (retval == 0) {
+		print_list(bb, 0);
+		printf("\n");
+		validate_test_seq(bb, test4a);
+		badblocks_list_free(bb);
+	}
+	printf("\n");
+	if (test_fail == 0)
+		printf("ext2fs library badblocks tests checks out OK!\n");
+	return test_fail;
+}
diff --git a/lib/ext2fs/tst_iscan.c b/lib/ext2fs/tst_iscan.c
new file mode 100644
index 0000000..dbf0813
--- /dev/null
+++ b/lib/ext2fs/tst_iscan.c
@@ -0,0 +1,218 @@
+/*
+ * tst_inode.c --- this function tests the inode scan function
+ * 
+ * Copyright (C) 1996 by Theodore Ts'o.
+ * 
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <time.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include <linux/ext2_fs.h>
+
+#include "ext2fs.h"
+
+blk_t test_vec[] = { 8, 12, 24, 34, 43, 44, 100, 0 };
+
+ext2_filsys	test_fs;
+ext2fs_block_bitmap bad_block_map, touched_map;
+ext2fs_inode_bitmap bad_inode_map;
+badblocks_list	test_badblocks;
+
+int first_no_comma = 1;
+int failed = 0;
+
+static void test_read_blk(unsigned long block, int count, errcode_t err)
+{
+	int	i;
+
+	if (first_no_comma)
+		first_no_comma = 0;
+	else
+		printf(", ");
+
+	if (count > 1)
+		printf("%lu-%lu", block, block+count-1);
+	else
+		printf("%lu", block);
+
+	for (i=0; i < count; i++, block++) {
+		if (ext2fs_test_block_bitmap(touched_map, block)) {
+			printf("\nDuplicate block?!? --- %lu\n", block);
+			failed++;
+			first_no_comma = 1;
+		}
+		ext2fs_mark_block_bitmap(touched_map, block);
+	}
+}
+
+/*
+ * Setup the variables for doing the inode scan test.
+ */
+static void setup(void)
+{
+	errcode_t	retval;
+	int		i;
+	struct ext2_super_block param;
+
+	initialize_ext2_error_table();
+
+	memset(&param, 0, sizeof(param));
+	param.s_blocks_count = 12000;
+
+
+	test_io_cb_read_blk = test_read_blk;
+	
+	retval = ext2fs_initialize("test fs", 0, &param,
+				   test_io_manager, &test_fs);
+	if (retval) {
+		com_err("setup", retval,
+			"While initializing filesystem");
+		exit(1);
+	}
+	retval = ext2fs_allocate_tables(test_fs);
+	if (retval) {
+		com_err("setup", retval,
+			"While allocating tables for test filesystem");
+		exit(1);
+	}
+	retval = ext2fs_allocate_block_bitmap(test_fs, "bad block map",
+					      &bad_block_map);
+	if (retval) {
+		com_err("setup", retval,
+			"While allocating bad_block bitmap");
+		exit(1);
+	}
+	retval = ext2fs_allocate_block_bitmap(test_fs, "touched map",
+					      &touched_map);
+	if (retval) {
+		com_err("setup", retval,
+			"While allocating touched block bitmap");
+		exit(1);
+	}
+	retval = ext2fs_allocate_inode_bitmap(test_fs, "bad inode map",
+					      &bad_inode_map);
+	if (retval) {
+		com_err("setup", retval,
+			"While allocating bad inode bitmap");
+		exit(1);
+	}
+	
+	retval = badblocks_list_create(&test_badblocks, 5);
+	if (retval) {
+		com_err("setup", retval, "while creating badblocks list");
+		exit(1);
+	}
+	for (i=0; test_vec[i]; i++) {
+		retval = badblocks_list_add(test_badblocks, test_vec[i]);
+		if (retval) {
+			com_err("setup", retval,
+				"while adding test vector %d", i);
+			exit(1);
+		}
+		ext2fs_mark_block_bitmap(bad_block_map, test_vec[i]);
+	}
+	test_fs->badblocks = test_badblocks;
+}
+
+/*
+ * Iterate using inode_scan
+ */
+static void iterate(void)
+{
+	struct ext2_inode inode;
+	ext2_inode_scan	scan;
+	errcode_t	retval;
+	ino_t		ino;
+
+	retval = ext2fs_open_inode_scan(test_fs, 8, &scan);
+	if (retval) {
+		com_err("iterate", retval, "While opening inode scan");
+		exit(1);
+	}
+	printf("Reading blocks: ");
+	retval = ext2fs_get_next_inode(scan, &ino, &inode);
+	if (retval) {
+		com_err("iterate", retval, "while reading first inode");
+		exit(1);
+	}
+	while (ino) {
+		retval = ext2fs_get_next_inode(scan, &ino, &inode);
+		if (retval == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE) {
+			ext2fs_mark_inode_bitmap(bad_inode_map, ino);
+			continue;
+		}
+		if (retval) {
+			com_err("iterate", retval,
+				"while getting next inode");
+			exit(1);
+		}
+	}
+	printf("\n");
+	ext2fs_close_inode_scan(scan);
+}
+
+/*
+ * Verify the touched map
+ */
+static void check_map(void)
+{
+	int	i, j, first=1;
+	unsigned long	blk;
+
+	for (i=0; test_vec[i]; i++) {
+		if (ext2fs_test_block_bitmap(touched_map, test_vec[i])) {
+			printf("Bad block was touched --- %d\n", test_vec[i]);
+			failed++;
+			first_no_comma = 1;
+		}
+		ext2fs_mark_block_bitmap(touched_map, test_vec[i]);
+	}
+	for (i = 0; i < test_fs->group_desc_count; i++) {
+		for (j=0, blk = test_fs->group_desc[i].bg_inode_table;
+		     j < test_fs->inode_blocks_per_group;
+		     j++, blk++) {
+			if (!ext2fs_test_block_bitmap(touched_map, blk) &&
+			    !ext2fs_test_block_bitmap(bad_block_map, blk)) {
+				printf("Missing block --- %lu\n", blk);
+				failed++;
+			}
+		}
+	}
+	printf("Bad inodes: ");
+	for (i=1; i <= test_fs->super->s_inodes_count; i++) {
+		if (ext2fs_test_inode_bitmap(bad_inode_map, i)) {
+			if (first)
+				first = 0;
+			else
+				printf(", ");
+			printf("%d", i);
+		}
+	}
+	printf("\n");
+}
+
+
+int main(int argc, char **argv)
+{
+	setup();
+	iterate();
+	check_map();
+	if (!failed)
+		printf("Inode scan tested OK!\n");
+	return failed;
+}
+
diff --git a/lib/ext2fs/unix_io.c b/lib/ext2fs/unix_io.c
index b995515..5083771 100644
--- a/lib/ext2fs/unix_io.c
+++ b/lib/ext2fs/unix_io.c
@@ -3,8 +3,12 @@
  *
  * Implements a one-block write-through cache.
  *
- * Copyright (C) 1993 Theodore Ts'o.  This file may be redistributed
- * under the terms of the GNU Public License.
+ * Copyright (C) 1993, 1994, 1995 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
  */
 
 #include <stdio.h>
@@ -184,7 +188,7 @@
 #endif
 	size = (count < 0) ? -count : count * channel->block_size;
 	location = (ext2_loff_t) block * channel->block_size;
-	if (ext2_llseek(data->dev, location, SEEK_SET) != location) {
+	if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) {
 		retval = errno;
 		goto error_out;
 	}
@@ -233,7 +237,7 @@
 	} 
 
 	location = (ext2_loff_t) block * channel->block_size;
-	if (ext2_llseek(data->dev, location, SEEK_SET) != location) {
+	if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) {
 		retval = errno;
 		goto error_out;
 	}
diff --git a/lib/ext2fs/unlink.c b/lib/ext2fs/unlink.c
new file mode 100644
index 0000000..e0309aa
--- /dev/null
+++ b/lib/ext2fs/unlink.c
@@ -0,0 +1,72 @@
+/*
+ * unlink.c --- delete links in a ext2fs directory
+ * 
+ * Copyright (C) 1993, 1994, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include <linux/ext2_fs.h>
+
+#include "ext2fs.h"
+
+struct link_struct  {
+	const char	*name;
+	int		namelen;
+	ino_t		inode;
+	int		flags;
+	int		done;
+};	
+
+static int unlink_proc(struct ext2_dir_entry *dirent,
+		     int	offset,
+		     int	blocksize,
+		     char	*buf,
+		     void	*private)
+{
+	struct link_struct *ls = (struct link_struct *) private;
+
+	if (ls->name && (dirent->name_len != ls->namelen))
+		return 0;
+	if (ls->name && strncmp(ls->name, dirent->name, dirent->name_len))
+		return 0;
+	if (ls->inode && (dirent->inode != ls->inode))
+		return 0;
+
+	dirent->inode = 0;
+	ls->done++;
+	return DIRENT_ABORT|DIRENT_CHANGED;
+}
+
+errcode_t ext2fs_unlink(ext2_filsys fs, ino_t dir, const char *name, ino_t ino,
+			int flags)
+{
+	errcode_t	retval;
+	struct link_struct ls;
+
+	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+	if (!(fs->flags & EXT2_FLAG_RW))
+		return EXT2_ET_RO_FILSYS;
+
+	ls.name = name;
+	ls.namelen = name ? strlen(name) : 0;
+	ls.inode = ino;
+	ls.flags = 0;
+	ls.done = 0;
+
+	retval = ext2fs_dir_iterate(fs, dir, 0, 0, unlink_proc, &ls);
+	if (retval)
+		return retval;
+
+	return (ls.done) ? 0 : EXT2_ET_DIR_NO_SPACE;
+}
+
diff --git a/lib/ext2fs/valid_blk.c b/lib/ext2fs/valid_blk.c
new file mode 100644
index 0000000..3a0cb3e
--- /dev/null
+++ b/lib/ext2fs/valid_blk.c
@@ -0,0 +1,49 @@
+/*
+ * valid_blk.c --- does the inode have valid blocks?
+ *
+ * Copyright 1997 by Theodore Ts'o
+ * 
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ * 
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include <linux/ext2_fs.h>
+
+#include "ext2fs.h"
+
+/*
+ * This function returns 1 if the inode's block entries actually
+ * contain block entries.
+ */
+int ext2fs_inode_has_valid_blocks(struct ext2_inode *inode)
+{
+	/*
+	 * Only directories, regular files, and some symbolic links
+	 * have valid block entries.
+	 */
+	if (!LINUX_S_ISDIR(inode->i_mode) && !LINUX_S_ISREG(inode->i_mode) &&
+	    !LINUX_S_ISLNK(inode->i_mode))
+		return 0;
+	
+	/*
+	 * If the symbolic link is a "fast symlink", then the symlink
+	 * target is stored in the block entries.
+	 */
+	if (LINUX_S_ISLNK (inode->i_mode) && inode->i_blocks == 0 &&
+	    inode->i_size < EXT2_N_BLOCKS * sizeof (unsigned long))
+		return 0;
+
+	return 1;
+}