Many files:
  Checked in e2fsprogs 1.08.

diff --git a/lib/ext2fs/ChangeLog b/lib/ext2fs/ChangeLog
index dd6da6a..e191b7d 100644
--- a/lib/ext2fs/ChangeLog
+++ b/lib/ext2fs/ChangeLog
@@ -1,3 +1,73 @@
+Thu Apr 10 13:15:15 1997  Theodore Ts'o  <tytso@rsts-11.mit.edu>
+
+	* dblist.c (ext2fs_set_dir_block): New function which sets the
+ 		block of a dblist entry, given the directory inode and
+ 		blockcnt.
+
+Sat Apr  5 12:42:42 1997  Theodore Ts'o  <tytso@rsts-11.mit.edu>
+
+	* alloc_tables.c (ext2fs_allocate_tables): Allocate the bitmap and
+		inode bitmaps at staggered locations across the block
+		groups, to avoid concentrating the bitmaps on a small
+		number of disks when using striped RAID arrays.
+
+	* initialize.c (ext2fs_initialize): By default, choose the maximum
+		possible number of blocks per group (based on the size of
+		the bitmaps in the blocksize).
+
+Fri Apr  4 11:28:16 1997  Theodore Ts'o  <tytso@rsts-11.mit.edu>
+
+	* initialize.c (ext2fs_initialize): Add support for
+		EXT2_COMPAT_SPARSE_SUPER feature.
+
+	* closefs.c (ext2fs_bg_has_super): New function to determine
+		whether or a particular block group should have a
+		superblock and block group descriptor.  Used for the
+		EXT2_COMPAT_SPARSE_SUPER feature is turned on.
+		(ext2fs_flush):  Check ext2fs_bg_has_super to see whether
+		or not the superblock should be written out for the block
+		group. 
+
+	* ext2fs.h (EXT2_COMPAT_SPARSE_SUPER): Define compatibility flag
+		for sparse duplicate superblocks.
+
+	* version.c (ext2fs_get_library_version): New function which
+		returns the library version.
+
+	* version.c (ext2fs_parse_version_string): New function which
+		parses a version string and returns a version number,
+		so application programs can compare version numbers as
+		integers.
+
+Wed Mar 26 00:43:52 1997  Theodore Ts'o  <tytso@rsts-11.mit.edu>
+
+	* icount.c (ext2fs_create_icount): Change function so that it also
+		takes a new argument which contains a "hint" icount
+		structure.  This "hint" icount allows the create function
+		to set up the sorted list in advance.  This reduces
+		significantly the amount of data moving needed to insert
+		these inodes into the list later.
+	
+	* icount.c (ext2fs_icount_validate): New function which validates
+		that the icount structure's rep invariant.
+
+	* icount.c (get_icount_el): Completely revamped implementation
+		to subsume put_icount_el().  Put_icount_el() used to
+		use an O(N) implementation to insert in the middle
+		of the icount list.  It now uses a O(ln N) to search
+		for where the icount should be inserted, and then uses
+		a memcpy to move the list down (instead of a for loop).
+	
+	* icount.c (ext2fs_icount_fetch, ext2fs_icount_store,
+ 		ext2fs_icount_increment, ext2fs_icount_decrement): Check
+ 		to see if the inode is within bounds; if it isn't, return
+ 		EINVAL.
+
+	* bitops.h (ext2fs_test_generic_bitmap): Fix error message given
+ 		when a bad inode number is passed to test_generic_bitmap
+ 		to be EXT2FS_TEST_ERROR instead of the wrong
+ 		EXT2FS_UNMARK_ERROR.
+
 Wed Mar 12 13:32:05 1997  Theodore Y. Ts'o  <tytso@mit.edu>
 
 	* Release of E2fsprogs version 1.07
diff --git a/lib/ext2fs/Makefile.in b/lib/ext2fs/Makefile.in
index 2867dce..cea8490 100644
--- a/lib/ext2fs/Makefile.in
+++ b/lib/ext2fs/Makefile.in
@@ -50,7 +50,8 @@
 	test_io.o \
 	unix_io.o \
 	unlink.o \
-	valid_blk.o
+	valid_blk.o \
+	version.o
 
 SRCS= ext2_err.c \
 	$(srcdir)/alloc.c \
@@ -95,7 +96,8 @@
 	$(srcdir)/test_io.c \
 	$(srcdir)/unix_io.c \
 	$(srcdir)/unlink.c \
-	$(srcdir)/valid_blk.c
+	$(srcdir)/valid_blk.c \
+	$(srcdir)/version.c
 
 HFILES= bitops.h ext2fs.h io.h
 
@@ -105,21 +107,21 @@
 DLL_ADDRESS = 0x66900000
 DLL_JUMPSIZE = 0x1000
 DLL_GOTSIZE  = 0x1000
-DLL_VERSION = 1.1
+DLL_VERSION = 1.2
 DLL_IMAGE = libe2fs
 DLL_STUB = libext2fs
 DLL_LIBS = -L../.. -lcom_err
 DLL_MYDIR = ext2fs
 DLL_INSTALL_DIR = $(libdir)
 
-ELF_VERSION = 2.2
+ELF_VERSION = 2.3
 ELF_SO_VERSION = 2
 ELF_IMAGE = libext2fs
 ELF_MYDIR = ext2fs
 ELF_INSTALL_DIR = $(libdir)
 ELF_OTHER_LIBS = -lc -L../.. -lcom_err
 
-BSDLIB_VERSION = 2.0
+BSDLIB_VERSION = 2.1
 BSDLIB_IMAGE = libext2fs
 BSDLIB_MYDIR = ext2fs
 BSDLIB_INSTALL_DIR = $(libdir)
diff --git a/lib/ext2fs/alloc_tables.c b/lib/ext2fs/alloc_tables.c
index 97bceef..4bfc91d 100644
--- a/lib/ext2fs/alloc_tables.c
+++ b/lib/ext2fs/alloc_tables.c
@@ -29,37 +29,17 @@
 errcode_t ext2fs_allocate_tables(ext2_filsys fs)
 {
 	errcode_t	retval;
-	blk_t		group_blk, last_blk, new_blk, blk;
+	blk_t		group_blk, start_blk, last_blk, new_blk, blk;
 	int		i, j;
 
 	group_blk = fs->super->s_first_data_block;
 	for (i = 0; i < fs->group_desc_count; i++) {
 		last_blk = group_blk + fs->super->s_blocks_per_group;
-		
 		/*
-		 * Allocate the block bitmap
+		 * Allocate the inode table
 		 */
-		retval = ext2fs_get_free_blocks(fs, group_blk, last_blk,
-						1, fs->block_map, &new_blk);
-		if (retval)
-			return retval;
-		ext2fs_mark_block_bitmap(fs->block_map, new_blk);
-		fs->group_desc[i].bg_block_bitmap = new_blk;
-
-		/*
-		 * ... and the inode bitmap
-		 */
-		retval = ext2fs_get_free_blocks(fs, group_blk, last_blk,
-						1, fs->block_map, &new_blk);
-		if (retval)
-			return retval;
-		ext2fs_mark_block_bitmap(fs->block_map, new_blk);
-		fs->group_desc[i].bg_inode_bitmap = new_blk;
-
-		/*
-		 * Finally, allocate the inode table
-		 */
-		retval = ext2fs_get_free_blocks(fs, group_blk, last_blk,
+		start_blk = group_blk + 3 + fs->desc_blocks;
+		retval = ext2fs_get_free_blocks(fs, start_blk, last_blk,
 						fs->inode_blocks_per_group,
 						fs->block_map, &new_blk);
 		if (retval)
@@ -71,6 +51,25 @@
 		fs->group_desc[i].bg_inode_table = new_blk;
 
 		/*
+		 * Allocate the block and inode bitmaps
+		 */
+		start_blk += fs->inode_blocks_per_group +
+			((2 * i) % (last_blk - start_blk));
+		retval = ext2fs_get_free_blocks(fs, start_blk, last_blk,
+						1, fs->block_map, &new_blk);
+		if (retval)
+			return retval;
+		ext2fs_mark_block_bitmap(fs->block_map, new_blk);
+		fs->group_desc[i].bg_block_bitmap = new_blk;
+
+		retval = ext2fs_get_free_blocks(fs, start_blk, last_blk,
+						1, fs->block_map, &new_blk);
+		if (retval)
+			return retval;
+		ext2fs_mark_block_bitmap(fs->block_map, new_blk);
+		fs->group_desc[i].bg_inode_bitmap = new_blk;
+
+		/*
 		 * Increment the start of the block group
 		 */
 		group_blk += fs->super->s_blocks_per_group;
diff --git a/lib/ext2fs/bitops.h b/lib/ext2fs/bitops.h
index 24bd5a7..a20d395 100644
--- a/lib/ext2fs/bitops.h
+++ b/lib/ext2fs/bitops.h
@@ -353,7 +353,7 @@
 					blk_t bitno)
 {
 	if ((bitno < bitmap->start) || (bitno > bitmap->end)) {
-		ext2fs_warn_bitmap2(bitmap, EXT2FS_UNMARK_ERROR, bitno);
+		ext2fs_warn_bitmap2(bitmap, EXT2FS_TEST_ERROR, bitno);
 		return 0;
 	}
 	return ext2fs_test_bit(bitno - bitmap->start, bitmap->bitmap);
diff --git a/lib/ext2fs/closefs.c b/lib/ext2fs/closefs.c
index e24b6b6..a61f878 100644
--- a/lib/ext2fs/closefs.c
+++ b/lib/ext2fs/closefs.c
@@ -22,6 +22,38 @@
 
 #include "ext2fsP.h"
 
+static int test_root(int a, int b)
+{
+	if (a == 0)
+		return 1;
+	while (1) {
+		if (a == 1)
+			return 1;
+		if (a % b)
+			return 0;
+		a = a / b;
+	}
+}
+
+int ext2fs_bg_has_super(ext2_filsys fs, int group_block)
+{
+#ifdef EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER
+	struct ext2fs_sb	*s;
+
+	s = (struct ext2fs_sb *) fs->super;
+	if (!(s->s_feature_ro_compat & EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER))
+		return 1;
+
+	if (test_root(group_block, 3) || (test_root(group_block, 5)) ||
+	    test_root(group_block, 7))
+		return 1;
+	
+	return 0;
+#else
+	return 1;
+#endif
+}
+
 errcode_t ext2fs_flush(ext2_filsys fs)
 {
 	int		i,j,maxgroup;
@@ -92,6 +124,9 @@
 	maxgroup = (fs->flags & EXT2_FLAG_MASTER_SB_ONLY) ? 1 :
 		fs->group_desc_count;
 	for (i = 0; i < maxgroup; i++) {
+		if (!ext2fs_bg_has_super(fs, i))
+			goto next_group;
+
 		if (i !=0 ) {
 			retval = io_channel_write_blk(fs->io, group_block,
 						      -SUPERBLOCK_SIZE,
@@ -108,6 +143,7 @@
 				goto errout;
 			group_ptr += fs->blocksize;
 		}
+	next_group:
 		group_block += EXT2_BLOCKS_PER_GROUP(fs->super);
 	}
 
diff --git a/lib/ext2fs/dblist.c b/lib/ext2fs/dblist.c
index 72d5840..76c847f 100644
--- a/lib/ext2fs/dblist.c
+++ b/lib/ext2fs/dblist.c
@@ -128,6 +128,28 @@
 }
 
 /*
+ * Change the directory block to the directory block list
+ */
+errcode_t ext2fs_set_dir_block(ext2_dblist dblist, ino_t ino, blk_t blk,
+			       int blockcnt)
+{
+	struct ext2_db_entry 	*ent;
+	int			i;
+	
+	EXT2_CHECK_MAGIC(dblist, EXT2_ET_MAGIC_DBLIST);
+
+	for (i=0; i < dblist->count; i++) {
+		if ((dblist->list[i].ino != ino) ||
+		    (dblist->list[i].blockcnt != blockcnt))
+			continue;
+		dblist->list[i].blk = blk;
+		dblist->sorted = 0;
+		return 0;
+	}
+	return ENOENT;
+}
+
+/*
  * This function iterates over the directory block list
  */
 errcode_t ext2fs_dblist_iterate(ext2_dblist dblist,
@@ -151,7 +173,6 @@
 		if (ret & DBLIST_ABORT)
 			return 0;
 	}
-	
 	return 0;
 }
 
diff --git a/lib/ext2fs/dll/jump.funcs b/lib/ext2fs/dll/jump.funcs
index 825844c..6084372 100644
--- a/lib/ext2fs/dll/jump.funcs
+++ b/lib/ext2fs/dll/jump.funcs
@@ -138,3 +138,7 @@
 00000000 T _ext2fs_icount_decrement		libext2fs	icount
 00000000 T _ext2fs_icount_store			libext2fs	icount
 00000000 T _ext2fs_get_icount_size		libext2fs	icount
+00000000 T _ext2fs_create_icount2		libext2fs	icount
+00000000 T _ext2fs_get_library_version		libext2fs	version
+00000000 T _ext2fs_parse_version_string		libext2fs	version
+00000000 T _ext2fs_set_dir_block		libext2fs	dblist
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index ce57e13..4e14fd6 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -341,7 +341,19 @@
 	char	s_last_mounted[64]; 	/* directory where last mounted */
 	__u32	s_reserved[206];	/* Padding to the end of the block */
 };
-  
+
+/*
+ * Feature set definitions (that might not be in ext2_fs.h
+ * (was EXT2_COMPAT_SPARSE_SUPER)
+ */
+#ifndef EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER
+#define EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER	0x0001
+#endif
+
+#define EXT2_LIB_FEATURE_COMPAT_SUPP	0
+#define EXT2_LIB_FEATURE_INCOMPAT_SUPP	0
+#define EXT2_LIB_FEATURE_RO_COMPAT_SUPP	EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER
+
 /*
  * function prototypes
  */
@@ -442,6 +454,7 @@
 /* closefs.c */
 extern errcode_t ext2fs_close(ext2_filsys fs);
 extern errcode_t ext2fs_flush(ext2_filsys fs);
+extern int ext2fs_bg_has_super(ext2_filsys fs, int group_block);
 
 /* cmp_bitmaps.c */
 extern errcode_t ext2fs_compare_block_bitmap(ext2fs_block_bitmap bm1,
@@ -461,6 +474,8 @@
 					    struct ext2_db_entry *db_info,
 					    void	*private),
 				void *private);
+errcode_t ext2fs_set_dir_block(ext2_dblist dblist, ino_t ino, blk_t blk,
+			       int blockcnt);
 
 /* dblist_dir.c */
 extern errcode_t
@@ -544,6 +559,8 @@
 
 /* icount.c */
 extern void ext2fs_free_icount(ext2_icount_t icount);
+extern errcode_t ext2fs_create_icount2(ext2_filsys fs, int flags, int size,
+				       ext2_icount_t hint, ext2_icount_t *ret);
 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,
@@ -555,6 +572,7 @@
 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);
+errcode_t ext2fs_icount_validate(ext2_icount_t icount, FILE *);
 
 /* ismounted.c */
 extern errcode_t ext2fs_check_if_mounted(const char *file, int *mount_flags);
@@ -621,7 +639,12 @@
 			      struct ext2_inode *f, int hostorder);
 
 /* valid_blk.c */
-int ext2fs_inode_has_valid_blocks(struct ext2_inode *inode);
+extern int ext2fs_inode_has_valid_blocks(struct ext2_inode *inode);
+
+/* version.c */
+extern int ext2fs_parse_version_string(const char *ver_string);
+extern int ext2fs_get_library_version(const char **ver_string,
+				      const char **date_string);
 
 /* inline functions */
 extern void ext2fs_mark_super_dirty(ext2_filsys fs);
diff --git a/lib/ext2fs/icount.c b/lib/ext2fs/icount.c
index b9070a9..7c68680 100644
--- a/lib/ext2fs/icount.c
+++ b/lib/ext2fs/icount.c
@@ -71,13 +71,20 @@
 	free(icount);
 }
 
-errcode_t ext2fs_create_icount(ext2_filsys fs, int flags, int size,
-			       ext2_icount_t *ret)
+errcode_t ext2fs_create_icount2(ext2_filsys fs, int flags, int size,
+				ext2_icount_t hint, ext2_icount_t *ret)
 {
 	ext2_icount_t	icount;
 	errcode_t	retval;
 	size_t		bytes;
+	int		i;
 
+	if (hint) {
+		EXT2_CHECK_MAGIC(hint, EXT2_ET_MAGIC_ICOUNT);
+		if (hint->size > size)
+			size = hint->size;
+	}
+	
 	icount = malloc(sizeof(struct ext2_icount));
 	if (!icount)
 		return ENOMEM;
@@ -125,8 +132,18 @@
 	icount->cursor = 0;
 	icount->num_inodes = fs->super->s_inodes_count;
 
-	*ret = icount;
+	/*
+	 * Populate the sorted list with those entries which were
+	 * found in the hint icount (since those are ones which will
+	 * likely need to be in the sorted list this time around).
+	 */
+	if (hint) {
+		for (i=0; i < hint->count; i++)
+			icount->list[i].ino = hint->list[i].ino;
+		icount->count = hint->count;
+	}
 
+	*ret = icount;
 	return 0;
 
 errout:
@@ -134,65 +151,22 @@
 	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)
+errcode_t ext2fs_create_icount(ext2_filsys fs, int flags, int size,
+			       ext2_icount_t *ret)
 {
-	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;
+	return ext2fs_create_icount2(fs, flags, size, 0, ret);
 }
 
 /*
- * 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.
+ * insert_icount_el() --- Insert a new entry into the sorted list at a
+ * 	specified position.
  */
-static struct ext2_icount_el *put_icount_el(ext2_icount_t icount, ino_t ino)
+static struct ext2_icount_el *insert_icount_el(ext2_icount_t icount,
+					    ino_t ino, int pos)
 {
 	struct ext2_icount_el *el, *new_list;
 	ino_t			new_size = 0;
-	int			i, j;
+	int			num;
 
 	if (icount->count >= icount->size) {
 		if (icount->count) {
@@ -212,31 +186,112 @@
 		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;
+	num = icount->count - pos;
+	if (num < 0)
+		return 0;	/* should never happen */
+	if (num) {
+		memmove(&icount->list[pos+1], &icount->list[pos],
+			sizeof(struct ext2_icount_el) * num);
 	}
+	icount->count++;
+	el = &icount->list[pos];
+	el->count = 0;
 	el->ino = ino;
 	return el;
 }
+
+/*
+ * get_icount_el() --- given an inode number, try to find icount
+ * 	information in the sorted list.  If the create flag is set,
+ * 	and we can't find an entry, create one in the sorted list.
+ */
+static struct ext2_icount_el *get_icount_el(ext2_icount_t icount,
+					    ino_t ino, int create)
+{
+	float	range;
+	int	low, high, mid;
+	ino_t	lowval, highval;
+
+	if (!icount || !icount->list)
+		return 0;
+
+	if (create && ((icount->count == 0) ||
+		       (ino > icount->list[icount->count-1].ino))) {
+		return insert_icount_el(icount, ino, icount->count);
+	}
+	if (icount->count == 0)
+		return 0;
 	
+	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
+	low = 0;
+	high = icount->count-1;
+	while (low <= high) {
+#if 0
+		mid = (low+high)/2;
+#else
+		if (low == high)
+			mid = low;
+		else {
+			/* Interpolate for efficiency */
+			lowval = icount->list[low].ino;
+			highval = icount->list[high].ino;
+
+			if (ino < lowval)
+				range = 0;
+			else if (ino > highval)
+				range = 1;
+			else 
+				range = ((float) (ino - lowval)) /
+					(highval - lowval);
+			mid = low + ((int) (range * (high-low)));
+		}
+#endif
+		if (ino == icount->list[mid].ino) {
+			icount->cursor = mid+1;
+			return &icount->list[mid];
+		}
+		if (ino < icount->list[mid].ino)
+			high = mid-1;
+		else
+			low = mid+1;
+	}
+	/*
+	 * If we need to create a new entry, it should be right at
+	 * low (where high will be left at low-1).
+	 */
+	if (create)
+		return insert_icount_el(icount, ino, low);
+	return 0;
+}
+
+errcode_t ext2fs_icount_validate(ext2_icount_t icount, FILE *out)
+{
+	errcode_t	ret = 0;
+	int		i;
+	const char *bad = "bad icount";
+	
+	EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT);
+
+	if (icount->count > icount->size) {
+		fprintf(out, "%s: count > size\n", bad);
+		return EINVAL;
+	}
+	for (i=1; i < icount->count; i++) {
+		if (icount->list[i-1].ino >= icount->list[i].ino) {
+			fprintf(out, "%s: list[%d].ino=%u, list[%d].ino=%u\n",
+				bad, i-1, icount->list[i-1].ino,
+				i, icount->list[i].ino);
+			ret = EINVAL;
+		}
+	}
+	return ret;
+}
 
 errcode_t ext2fs_icount_fetch(ext2_icount_t icount, ino_t ino, __u16 *ret)
 {
@@ -244,11 +299,19 @@
 	
 	EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT);
 
+	if (!ino || (ino > icount->num_inodes))
+		return EINVAL;
+
 	if (ext2fs_test_inode_bitmap(icount->single, ino)) {
 		*ret = 1;
 		return 0;
 	}
-	el = get_icount_el(icount, ino);
+	if (icount->multiple &&
+	    !ext2fs_test_inode_bitmap(icount->multiple, ino)) {
+		*ret = 0;
+		return 0;
+	}
+	el = get_icount_el(icount, ino, 0);
 	if (!el) {
 		*ret = 0;
 		return 0;
@@ -264,14 +327,19 @@
 
 	EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT);
 
+	if (!ino || (ino > icount->num_inodes))
+		return EINVAL;
+
 	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().
+		 * no entry in the list.
 		 */
-		el = put_icount_el(icount, ino);
+		el = get_icount_el(icount, ino, 1);
 		if (!el)
 			return ENOMEM;
+		ext2fs_unmark_inode_bitmap(icount->single, ino);
+		el->count = 2;
 	} else if (icount->multiple) {
 		/*
 		 * The count is either zero or greater than 1; if the
@@ -280,13 +348,10 @@
 		 * 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;
-			}
+			el = get_icount_el(icount, ino, 1);
+			if (!el)
+				return ENOMEM;
+			el->count++;
 		} else {
 			/*
 			 * The count was zero; mark the single bitmap
@@ -303,20 +368,16 @@
 		 * 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);
+		el = get_icount_el(icount, ino, 0);
 		if (!el) {
 			/* No entry means the count was zero */
 			goto zero_count;
 		}
-		el = put_icount_el(icount, ino);
+		el = get_icount_el(icount, ino, 1);
 		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)
@@ -329,6 +390,9 @@
 {
 	struct ext2_icount_el	*el;
 
+	if (!ino || (ino > icount->num_inodes))
+		return EINVAL;
+
 	EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT);
 
 	if (ext2fs_test_inode_bitmap(icount->single, ino)) {
@@ -336,7 +400,7 @@
 		if (icount->multiple)
 			ext2fs_unmark_inode_bitmap(icount->multiple, ino);
 		else {
-			el = get_icount_el(icount, ino);
+			el = get_icount_el(icount, ino, 0);
 			if (el)
 				el->count = 0;
 		}
@@ -345,8 +409,12 @@
 		return 0;
 	}
 
-	el = get_icount_el(icount, ino);
-	if (!el)
+	if (icount->multiple &&
+	    !ext2fs_test_inode_bitmap(icount->multiple, ino))
+		return EINVAL;
+	
+	el = get_icount_el(icount, ino, 0);
+	if (!el || el->count == 0)
 		return EINVAL;
 
 	el->count--;
@@ -365,6 +433,9 @@
 {
 	struct ext2_icount_el	*el;
 
+	if (!ino || (ino > icount->num_inodes))
+		return EINVAL;
+
 	EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT);
 
 	if (count == 1) {
@@ -382,7 +453,7 @@
 			 */
 			ext2fs_unmark_inode_bitmap(icount->multiple, ino);
 		} else {
-			el = get_icount_el(icount, ino);
+			el = get_icount_el(icount, ino, 0);
 			if (el)
 				el->count = 0;
 		}
@@ -392,7 +463,7 @@
 	/*
 	 * Get the icount element
 	 */
-	el = put_icount_el(icount, ino);
+	el = get_icount_el(icount, ino, 1);
 	if (!el)
 		return ENOMEM;
 	el->count = count;
diff --git a/lib/ext2fs/initialize.c b/lib/ext2fs/initialize.c
index 6db5a0c..72f40ea 100644
--- a/lib/ext2fs/initialize.c
+++ b/lib/ext2fs/initialize.c
@@ -58,6 +58,7 @@
 	ext2_filsys	fs;
 	errcode_t	retval;
 	struct ext2_super_block *super;
+	struct ext2fs_sb	*s;
 	int		frags_per_block;
 	int		rem;
 	int		overhead = 0;
@@ -91,9 +92,12 @@
 		goto cleanup;
 	}
 	memset(super, 0, SUPERBLOCK_SIZE);
+	s = (struct ext2fs_sb *) super;
 
 #define set_field(field, default) (super->field = param->field ? \
 				   param->field : (default))
+#define set_ext2_field(field, default) (s->field = param->field ? \
+					param->field : (default))
 
 	super->s_magic = EXT2_SUPER_MAGIC;
 	super->s_state = EXT2_VALID_FS;
@@ -103,7 +107,13 @@
 	set_field(s_first_data_block, super->s_log_block_size ? 0 : 1);
 	set_field(s_max_mnt_count, EXT2_DFL_MAX_MNT_COUNT);
 	set_field(s_errors, EXT2_ERRORS_DEFAULT);
-
+	set_ext2_field(s_feature_compat, 0);
+	set_ext2_field(s_feature_incompat, 0);
+	set_ext2_field(s_feature_ro_compat, 0);
+	if (s->s_feature_incompat & ~EXT2_LIB_FEATURE_INCOMPAT_SUPP)
+		return EXT2_ET_UNSUPP_FEATURE;
+	if (s->s_feature_ro_compat & ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP)
+		return EXT2_ET_RO_UNSUPP_FEATURE;
 
 #ifdef EXT2_DYNAMIC_REV
 	set_field(s_rev_level, EXT2_GOOD_OLD_REV);
@@ -122,7 +132,8 @@
 	fs->fragsize = EXT2_FRAG_SIZE(super);
 	frags_per_block = fs->blocksize / fs->fragsize;
 	
-	set_field(s_blocks_per_group, 8192); /* default: 8192 blocks/group */
+	/* default: (fs->blocksize*8) blocks/group */
+	set_field(s_blocks_per_group, fs->blocksize*8); 
 	super->s_frags_per_group = super->s_blocks_per_group * frags_per_block;
 	
 	super->s_blocks_count = param->s_blocks_count;
@@ -188,16 +199,6 @@
 	super->s_free_inodes_count = super->s_inodes_count;
 
 	/*
-	 * Overhead is the number of bookkeeping blocks per group.  It
-	 * includes the superblock backup, the group descriptor
-	 * backups, the inode bitmap, the block bitmap, and the inode
-	 * table.
-	 */
-	overhead = 3 + fs->desc_blocks + fs->inode_blocks_per_group;
-	super->s_free_blocks_count = super->s_blocks_count -
-		super->s_first_data_block - (overhead*fs->group_desc_count);
-	
-	/*
 	 * See if the last group is big enough to support the
 	 * necessary data structures.  If not, we need to get rid of
 	 * it.
@@ -250,11 +251,8 @@
 	 * by this routine), they are accounted for nevertheless.
 	 */
 	group_block = super->s_first_data_block;
+	super->s_free_blocks_count = 0;
 	for (i = 0; i < fs->group_desc_count; i++) {
-		for (j=0; j < fs->desc_blocks+1; j++)
-			ext2fs_mark_block_bitmap(fs->block_map,
-						 group_block + j);
-
 		if (i == fs->group_desc_count-1) {
 			numblocks = (fs->super->s_blocks_count -
 				     fs->super->s_first_data_block) %
@@ -263,8 +261,17 @@
 				numblocks = fs->super->s_blocks_per_group;
 		} else
 			numblocks = fs->super->s_blocks_per_group;
-		numblocks -= 3 + fs->desc_blocks + fs->inode_blocks_per_group;
+
+		if (ext2fs_bg_has_super(fs, i)) {
+			for (j=0; j < fs->desc_blocks+1; j++)
+				ext2fs_mark_block_bitmap(fs->block_map,
+							 group_block + j);
+			numblocks -= 1 + fs->desc_blocks;
+		}
 		
+		numblocks -= 2 + fs->inode_blocks_per_group;
+		
+		super->s_free_blocks_count += numblocks;
 		fs->group_desc[i].bg_free_blocks_count = numblocks;
 		fs->group_desc[i].bg_free_inodes_count =
 			fs->super->s_inodes_per_group;
diff --git a/lib/ext2fs/inode.c b/lib/ext2fs/inode.c
index 8fd5e7e..7194fb0 100644
--- a/lib/ext2fs/inode.c
+++ b/lib/ext2fs/inode.c
@@ -51,6 +51,7 @@
 {
 	ext2_inode_scan	scan;
 	errcode_t	retval;
+	errcode_t (*save_get_blocks)(ext2_filsys fs, ino_t ino, blk_t *blocks);
 
 	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
 
@@ -59,11 +60,18 @@
 	 * scanning functions require it.
 	 */
 	if (fs->badblocks == 0) {
+		/*
+		 * Temporarly save fs->get_blocks and set it to zero,
+		 * for compatibility with old e2fsck's.
+		 */
+		save_get_blocks = fs->get_blocks;
+		fs->get_blocks = 0;
 		retval = ext2fs_read_bb_inode(fs, &fs->badblocks);
 		if (retval && fs->badblocks) {
 			badblocks_list_free(fs->badblocks);
 			fs->badblocks = 0;
 		}
+		fs->get_blocks = save_get_blocks;
 	}
 
 	scan = (ext2_inode_scan) malloc(sizeof(struct ext2_struct_inode_scan));
diff --git a/lib/ext2fs/openfs.c b/lib/ext2fs/openfs.c
index b2e2393..a1c0d5b 100644
--- a/lib/ext2fs/openfs.c
+++ b/lib/ext2fs/openfs.c
@@ -126,12 +126,13 @@
 	 */
 	if (!(flags & EXT2_FLAG_FORCE)) {
 		s = (struct ext2fs_sb *) fs->super;
-		if (s->s_feature_incompat) {
+		if (s->s_feature_incompat & ~EXT2_LIB_FEATURE_INCOMPAT_SUPP) {
 			retval = EXT2_ET_UNSUPP_FEATURE;
 			goto cleanup;
 		}
 		if ((flags & EXT2_FLAG_RW) &&
-		    s->s_feature_ro_compat) {
+		    (s->s_feature_ro_compat &
+		     ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP)) {
 			retval = EXT2_ET_RO_UNSUPP_FEATURE;
 			goto cleanup;
 		}
diff --git a/lib/ext2fs/version.c b/lib/ext2fs/version.c
new file mode 100644
index 0000000..df7236e
--- /dev/null
+++ b/lib/ext2fs/version.c
@@ -0,0 +1,53 @@
+/*
+ * version.c --- Return the version of the 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 <et/com_err.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include <linux/ext2_fs.h>
+#include "ext2fs.h"
+
+#include "../../version.h"
+
+static const char *lib_version = E2FSPROGS_VERSION;
+static const char *lib_date = E2FSPROGS_DATE;
+
+int ext2fs_parse_version_string(const char *ver_string)
+{
+	const char *cp;
+	int version = 0;
+
+	for (cp = lib_version; *cp; cp++) {
+		if (!isdigit(*cp))
+			continue;
+		version = (version * 10) + (*cp - '0');
+	}
+	return version;
+}
+
+
+int ext2fs_get_library_version(const char **ver_string,
+			       const char **date_string)
+{
+	if (ver_string)
+		*ver_string = lib_version;
+	if (date_string)
+		*date_string = lib_date;
+
+	return ext2fs_parse_version_string(lib_version);
+}