Add support for the meta_blockgroup filesystem format.

diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index 9849645..c685581 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -1793,7 +1793,7 @@
 {
 	ext2_filsys fs = ctx->fs;
 	blk_t	block, b;
-	int	i,j;
+	int	i, j, has_super, meta_bg, meta_bg_size;
 	struct problem_context pctx;
 	
 	clear_problem_context(&pctx);
@@ -1802,21 +1802,39 @@
 	for (i = 0; i < fs->group_desc_count; i++) {
 		pctx.group = i;
 
-		if (ext2fs_bg_has_super(fs, i)) {
+		has_super = ext2fs_bg_has_super(fs, i);
+		if (has_super)
 			/*
 			 * Mark this group's copy of the superblock
 			 */
 			ext2fs_mark_block_bitmap(ctx->block_found_map, block);
 		
-			/*
-			 * Mark this group's copy of the descriptors
-			 */
-			for (j = 0; j < fs->desc_blocks; j++) {
-				ext2fs_mark_block_bitmap(ctx->block_found_map,
+		meta_bg_size = (fs->blocksize /
+				sizeof (struct ext2_group_desc));
+		meta_bg = i / meta_bg_size;
+		if (!(fs->super->s_feature_incompat &
+		      EXT2_FEATURE_INCOMPAT_META_BG) ||
+		    (meta_bg < fs->super->s_first_meta_bg)) {
+			if (has_super) {
+				/*
+				 * Mark this group's copy of the descriptors
+				 */
+				for (j = 0; j < fs->desc_blocks; j++) {
+					ext2fs_mark_block_bitmap(ctx->block_found_map,
 							 block + j + 1);
+				}
 			}
+		} else {
+			if (has_super)
+				has_super = 1;
+			if (((i % meta_bg_size) == 0) ||
+			    ((i % meta_bg_size) == 1) ||
+			    ((i % meta_bg_size) == (meta_bg_size-1)))
+				ext2fs_mark_block_bitmap(ctx->block_found_map,
+						 block + has_super);
 		}
 		
+		
 		/*
 		 * Mark the blocks used for the inode table
 		 */
diff --git a/lib/e2p/ChangeLog b/lib/e2p/ChangeLog
index ba6c7f9..f3a9b70 100644
--- a/lib/e2p/ChangeLog
+++ b/lib/e2p/ChangeLog
@@ -1,3 +1,8 @@
+2002-10-20  Theodore Ts'o  <tytso@valinux.com>
+
+	* feature.c, ls.c (list_super2): Add support for the
+		meta_blockgroup filesystem format.
+
 2002-10-15    <tytso@snap.thunk.org>
 
 	* ls.c (print_mntopts, list_super2): Print any default mount
diff --git a/lib/e2p/feature.c b/lib/e2p/feature.c
index 2586727..83cf6f9 100644
--- a/lib/e2p/feature.c
+++ b/lib/e2p/feature.c
@@ -49,6 +49,8 @@
 			"needs_recovery" },
 	{	E2P_FEATURE_INCOMPAT, EXT3_FEATURE_INCOMPAT_JOURNAL_DEV,
 			"journal_dev" },
+	{	E2P_FEATURE_INCOMPAT, EXT2_FEATURE_INCOMPAT_META_BG,
+			"meta_bg" },
 	{	0, 0, 0 },
 };
 
diff --git a/lib/e2p/ls.c b/lib/e2p/ls.c
index c18e0ff..cab3ae1 100644
--- a/lib/e2p/ls.c
+++ b/lib/e2p/ls.c
@@ -202,6 +202,9 @@
 	fprintf(f, "Inodes per group:         %u\n", sb->s_inodes_per_group);
 	fprintf(f, "Inode blocks per group:   %u\n", inode_blocks_per_group);
 	tm = sb->s_mtime;
+	if (sb->s_first_meta_bg)
+		fprintf(f, "First meta block group:   %u\n",
+			sb->s_first_meta_bg);
 	fprintf(f, "Last mount time:          %s", ctime(&tm));
 	tm = sb->s_wtime;
 	fprintf(f, "Last write time:          %s", ctime(&tm));
diff --git a/lib/ext2fs/ChangeLog b/lib/ext2fs/ChangeLog
index a12a608..7d085e1 100644
--- a/lib/ext2fs/ChangeLog
+++ b/lib/ext2fs/ChangeLog
@@ -1,3 +1,10 @@
+2002-10-20  Theodore Ts'o  <tytso@valinux.com>
+
+	* closefs.c (ext2fs_flush, write_bgdesc), ext2_fs.h, ext2fs.h,
+	openfs.c (ext2fs_descriptor_block_loc, ext2fs_open), initialize.c
+	(ext2fs_initialize), swapfs.c (ext2fs_swap_super): Add support for
+	the meta_blockgroup filesystem format.
+
 2002-10-15    <tytso@snap.thunk.org>
 
 	* ext2_fs.h: Add new field in superblock for default mount options.
diff --git a/lib/ext2fs/closefs.c b/lib/ext2fs/closefs.c
index f52c343..dcf244b 100644
--- a/lib/ext2fs/closefs.c
+++ b/lib/ext2fs/closefs.c
@@ -113,9 +113,71 @@
 	/* other fields should be left alone */
 }
 
+/*
+ * This writes out the block group descriptors the original,
+ * old-fashioned way.
+ */
+static errcode_t write_bgdesc(ext2_filsys fs, dgrp_t group, blk_t group_block,
+			      struct ext2_group_desc *group_shadow)
+{
+	errcode_t	retval;
+	char		*group_ptr = (char *) group_shadow;
+	int		j;
+	int		has_super = ext2fs_bg_has_super(fs, group);
+	dgrp_t		meta_bg_size, meta_bg;
+	blk_t		blk;
+
+	meta_bg_size = (fs->blocksize / sizeof (struct ext2_group_desc));
+	meta_bg = group / meta_bg_size;
+	if (!(fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) ||
+	    (meta_bg < fs->super->s_first_meta_bg)) {
+		if (!has_super)
+			return 0;
+		for (j=0; j < fs->desc_blocks; j++) {
+			retval = io_channel_write_blk(fs->io,
+						      group_block+1+j, 1,
+						      group_ptr);
+			if (retval)
+				return retval;
+			group_ptr += fs->blocksize;
+		}
+	} else {
+		if (has_super)
+			group_block++;
+		if (((group % meta_bg_size) == 0) ||
+		    ((group % meta_bg_size) == 1) ||
+		    ((group % meta_bg_size) == (meta_bg_size-1))) {
+			return io_channel_write_blk(fs->io, group_block,
+				1, group_ptr + (meta_bg*fs->blocksize));
+		}
+	}
+	return 0;
+}
+
+
+static errcode_t write_backup_super(ext2_filsys fs, dgrp_t group,
+				    blk_t group_block,
+				    struct ext2_super_block *super_shadow)
+{
+	dgrp_t	sgrp = group;
+	
+	if (sgrp > ((1 << 16) - 1))
+		sgrp = (1 << 16) - 1;
+#ifdef EXT2FS_ENABLE_SWAPFS
+	if (fs->flags & EXT2_FLAG_SWAP_BYTES)
+		super_shadow->s_block_group_nr = ext2fs_swab16(sgrp);
+	else
+#endif
+		fs->super->s_block_group_nr = sgrp;
+
+	return io_channel_write_blk(fs->io, group_block, -SUPERBLOCK_SIZE, 
+				    super_shadow);
+}
+
+
 errcode_t ext2fs_flush(ext2_filsys fs)
 {
-	dgrp_t		i,j,maxgroup,sgrp;
+	dgrp_t		i,j,maxgroup;
 	blk_t		group_block;
 	errcode_t	retval;
 	char		*group_ptr;
@@ -204,36 +266,16 @@
 	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;
-
-		sgrp = i;
-		if (sgrp > ((1 << 16) - 1))
-			sgrp = (1 << 16) - 1;
-#ifdef EXT2FS_ENABLE_SWAPFS
-		if (fs->flags & EXT2_FLAG_SWAP_BYTES)
-			super_shadow->s_block_group_nr = ext2fs_swab16(sgrp);
-		else
-#endif
-			fs->super->s_block_group_nr = sgrp;
-
-		if (i !=0 ) {
-			retval = io_channel_write_blk(fs->io, group_block,
-						      -SUPERBLOCK_SIZE,
-						      super_shadow);
+		if (i && ext2fs_bg_has_super(fs, i)) {
+			retval = write_backup_super(fs, i, group_block,
+						    super_shadow);
 			if (retval)
 				goto errout;
 		}
-		if (fs->flags & EXT2_FLAG_SUPER_ONLY)
-			goto next_group;
-		group_ptr = (char *) group_shadow;
-		for (j=0; j < fs->desc_blocks; j++) {
-			retval = io_channel_write_blk(fs->io,
-						      group_block+1+j, 1,
-						      group_ptr);
-			if (retval)
+		if (!(fs->flags & EXT2_FLAG_SUPER_ONLY)) {
+			if ((retval = write_bgdesc(fs, i, group_block, 
+						   group_shadow)))
 				goto errout;
-			group_ptr += fs->blocksize;
 		}
 	next_group:
 		group_block += EXT2_BLOCKS_PER_GROUP(fs->super);
diff --git a/lib/ext2fs/ext2_fs.h b/lib/ext2fs/ext2_fs.h
index 56fabbf..2b11261 100644
--- a/lib/ext2fs/ext2_fs.h
+++ b/lib/ext2fs/ext2_fs.h
@@ -439,7 +439,8 @@
 	__u8	s_reserved_char_pad;
 	__u16	s_reserved_word_pad;
 	__u32	s_default_mount_opts;
-	__u32	s_reserved[191];	/* Padding to the end of the block */
+	__u32	s_first_meta_bg;	/* First metablock group */
+	__u32	s_reserved[190];	/* Padding to the end of the block */
 };
 
 /*
@@ -488,9 +489,10 @@
 #define EXT2_FEATURE_INCOMPAT_FILETYPE		0x0002
 #define EXT3_FEATURE_INCOMPAT_RECOVER		0x0004 /* Needs recovery */
 #define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV	0x0008 /* Journal device */
+#define EXT2_FEATURE_INCOMPAT_META_BG		0x0010
 
 #define EXT2_FEATURE_COMPAT_SUPP	0
-#define EXT2_FEATURE_INCOMPAT_SUPP	EXT2_FEATURE_INCOMPAT_FILETYPE
+#define EXT2_FEATURE_INCOMPAT_SUPP	(EXT2_FEATURE_INCOMPAT_FILETYPE)
 #define EXT2_FEATURE_RO_COMPAT_SUPP	(EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \
 					 EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \
 					 EXT2_FEATURE_RO_COMPAT_BTREE_DIR)
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index 6f5bc47..992fc74 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -432,10 +432,12 @@
 #define EXT2_LIB_FEATURE_INCOMPAT_SUPP	(EXT2_FEATURE_INCOMPAT_FILETYPE|\
 					 EXT2_FEATURE_INCOMPAT_COMPRESSION|\
 					 EXT3_FEATURE_INCOMPAT_JOURNAL_DEV|\
+					 EXT2_FEATURE_INCOMPAT_META_BG|\
 					 EXT3_FEATURE_INCOMPAT_RECOVER)
 #else
 #define EXT2_LIB_FEATURE_INCOMPAT_SUPP	(EXT2_FEATURE_INCOMPAT_FILETYPE|\
 					 EXT3_FEATURE_INCOMPAT_JOURNAL_DEV|\
+					 EXT2_FEATURE_INCOMPAT_META_BG|\
 					 EXT3_FEATURE_INCOMPAT_RECOVER)
 #endif
 #define EXT2_LIB_FEATURE_RO_COMPAT_SUPP	(EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER|\
@@ -808,6 +810,8 @@
 extern errcode_t ext2fs_open(const char *name, int flags, int superblock,
 			     int block_size, io_manager manager,
 			     ext2_filsys *ret_fs);
+extern blk_t ext2fs_descriptor_block_loc(ext2_filsys fs, blk_t group_block, 
+					 dgrp_t i);
 
 /* get_pathname.c */
 extern errcode_t ext2fs_get_pathname(ext2_filsys fs, ext2_ino_t dir, ext2_ino_t ino,
diff --git a/lib/ext2fs/initialize.c b/lib/ext2fs/initialize.c
index f132416..d9be945 100644
--- a/lib/ext2fs/initialize.c
+++ b/lib/ext2fs/initialize.c
@@ -67,6 +67,7 @@
 	int		i, j;
 	blk_t		numblocks;
 	char		*buf;
+	int		meta_bg_size, meta_bg, has_super;
 
 	if (!param || !param->s_blocks_count)
 		return EXT2_ET_INVALID_ARGUMENT;
@@ -113,6 +114,7 @@
 	set_field(s_feature_compat, 0);
 	set_field(s_feature_incompat, 0);
 	set_field(s_feature_ro_compat, 0);
+	set_field(s_first_meta_bg, 0);
 	if (super->s_feature_incompat & ~EXT2_LIB_FEATURE_INCOMPAT_SUPP)
 		return EXT2_ET_UNSUPP_FEATURE;
 	if (super->s_feature_ro_compat & ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP)
@@ -301,11 +303,35 @@
 		} else
 			numblocks = fs->super->s_blocks_per_group;
 
-		if (ext2fs_bg_has_super(fs, i)) {
-			for (j=0; j < fs->desc_blocks+1; j++)
+		has_super = ext2fs_bg_has_super(fs, i);
+
+		if (has_super) {
+			ext2fs_mark_block_bitmap(fs->block_map, group_block);
+			numblocks--;
+		}
+		meta_bg_size = (fs->blocksize /
+				sizeof (struct ext2_group_desc));
+		meta_bg = i / meta_bg_size;
+
+		if (!(fs->super->s_feature_incompat &
+		      EXT2_FEATURE_INCOMPAT_META_BG) ||
+		    (meta_bg < fs->super->s_first_meta_bg)) {
+			if (has_super) {
+				for (j=0; j < fs->desc_blocks; j++)
+					ext2fs_mark_block_bitmap(fs->block_map,
+							 group_block + j + 1);
+				numblocks -= fs->desc_blocks;
+			}
+		} else {
+			if (has_super)
+				has_super = 1;
+			if (((i % meta_bg_size) == 0) ||
+			    ((i % meta_bg_size) == 1) ||
+			    ((i % meta_bg_size) == (meta_bg_size-1))) {
 				ext2fs_mark_block_bitmap(fs->block_map,
-							 group_block + j);
-			numblocks -= 1 + fs->desc_blocks;
+					 group_block + has_super);
+				numblocks--;
+			}
 		}
 		
 		numblocks -= 2 + fs->inode_blocks_per_group;
diff --git a/lib/ext2fs/openfs.c b/lib/ext2fs/openfs.c
index d9021d6..4dcae98 100644
--- a/lib/ext2fs/openfs.c
+++ b/lib/ext2fs/openfs.c
@@ -24,9 +24,27 @@
 #endif
 
 #include "ext2_fs.h"
+
+
 #include "ext2fs.h"
 #include "e2image.h"
 
+blk_t ext2fs_descriptor_block_loc(ext2_filsys fs, blk_t group_block, dgrp_t i)
+{
+	int	bg;
+	int	has_super = 0;
+
+	if (!(fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) ||
+	    (i < fs->super->s_first_meta_bg))
+		return (group_block + i + 1);
+
+	bg = (fs->blocksize / sizeof (struct ext2_group_desc)) * i;
+	if (ext2fs_bg_has_super(fs, bg))
+		has_super = 1;
+	return (fs->super->s_first_data_block + has_super + 
+		(bg * fs->super->s_blocks_per_group));
+}
+
 /*
  *  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.
@@ -44,7 +62,7 @@
 	ext2_filsys	fs;
 	errcode_t	retval;
 	int		i, j, groups_per_block, blocks_per_group;
-	blk_t		group_block;
+	blk_t		group_block, blk;
 	char		*dest;
 	struct ext2_group_desc *gdp;
 	
@@ -214,18 +232,17 @@
 	if (retval)
 		goto cleanup;
 	if (!group_block)
-		group_block = fs->super->s_first_data_block + 1;
+		group_block = fs->super->s_first_data_block;
 	dest = (char *) fs->group_desc;
+	groups_per_block = fs->blocksize / sizeof(struct ext2_group_desc);
 	for (i=0 ; i < fs->desc_blocks; i++) {
-		retval = io_channel_read_blk(fs->io, group_block, 1, dest);
+		blk = ext2fs_descriptor_block_loc(fs, group_block, i);
+		retval = io_channel_read_blk(fs->io, blk, 1, dest);
 		if (retval)
 			goto cleanup;
-		group_block++;
 #ifdef EXT2FS_ENABLE_SWAPFS
 		if (fs->flags & EXT2_FLAG_SWAP_BYTES) {
 			gdp = (struct ext2_group_desc *) dest;
-			groups_per_block = fs->blocksize /
-				sizeof(struct ext2_group_desc);
 			for (j=0; j < groups_per_block; j++)
 				ext2fs_swap_group_desc(gdp++);
 		}
diff --git a/lib/ext2fs/swapfs.c b/lib/ext2fs/swapfs.c
index 14165cb..e375c63 100644
--- a/lib/ext2fs/swapfs.c
+++ b/lib/ext2fs/swapfs.c
@@ -57,6 +57,7 @@
 	sb->s_journal_inum = ext2fs_swab32(sb->s_journal_inum);
 	sb->s_journal_dev = ext2fs_swab32(sb->s_journal_dev);
 	sb->s_last_orphan = ext2fs_swab32(sb->s_last_orphan);
+	sb->s_first_meta_bg = ext2fs_swab32(sb->s_first_meta_bg);
 	for (i=0; i < 4; i++)
 		sb->s_hash_seed[i] = ext2fs_swab32(sb->s_hash_seed[i]);
 }
diff --git a/misc/ChangeLog b/misc/ChangeLog
index 038cb71..63f1d98 100644
--- a/misc/ChangeLog
+++ b/misc/ChangeLog
@@ -1,3 +1,9 @@
+2002-10-20  Theodore Ts'o  <tytso@valinux.com>
+
+	* dumpe2fs.c (list_desc), mke2fs.c (PRS), e2image.c
+		(mark_table_blocks): Add support for the meta_blockgroup
+		filesystem format.
+
 2002-10-15    <tytso@snap.thunk.org>
 
 	* tune2fs.8.in, tune2fs.c (update_mntopts, main): Add new option
diff --git a/misc/dumpe2fs.c b/misc/dumpe2fs.c
index 14931b0..b4c63a7 100644
--- a/misc/dumpe2fs.c
+++ b/misc/dumpe2fs.c
@@ -90,6 +90,7 @@
 	char * inode_bitmap = fs->inode_map->bitmap;
 	int inode_blocks_per_group;
 	int group_desc_blocks;
+	int meta_bg, meta_bg_size, has_super;
 
 	inode_blocks_per_group = ((fs->super->s_inodes_per_group *
 				   EXT2_INODE_SIZE(fs->super)) +
@@ -111,14 +112,35 @@
 		printf (_("Group %lu: (Blocks "), i);
 		printf(range_format, group_blk, next_blk - 1);
 		fputs(")\n", stdout);
-		if (ext2fs_bg_has_super (fs, i)) {
-			printf (_("  %s Superblock at "),
+		has_super = ext2fs_bg_has_super(fs, i);
+		if (has_super) {
+			printf (_("  %s superblock at "),
 				i == 0 ? _("Primary") : _("Backup"));
 			printf(num_format, group_blk);
-			printf(_(",  Group Descriptors at "));
+		}
+		meta_bg_size = (fs->blocksize /
+				sizeof (struct ext2_group_desc));
+		meta_bg = i / meta_bg_size;
+		if (!(fs->super->s_feature_incompat &
+		      EXT2_FEATURE_INCOMPAT_META_BG) ||
+		    (meta_bg < fs->super->s_first_meta_bg)) {
+			fputc(has_super ? ',' : ' ', stdout);
+			printf(_(" Group descriptors at "));
 			printf(range_format, group_blk+1,
 			       group_blk + group_desc_blocks);
 			fputc('\n', stdout);
+		} else {
+			if (has_super)
+				has_super = 1;
+			if (((i % meta_bg_size) == 0) ||
+			    ((i % meta_bg_size) == 1) ||
+			    ((i % meta_bg_size) == (meta_bg_size-1))) {
+				fputc(has_super ? ',' : ' ', stdout);
+				printf(_(" Group descriptor at "));
+				printf(num_format, group_blk + has_super);
+				fputc('\n', stdout);
+			} else if (has_super)
+				fputc('\n', stdout);
 		}
 		fputs(_("  Block bitmap at "), stdout);
 		printf(num_format, fs->group_desc[i].bg_block_bitmap);
diff --git a/misc/e2image.c b/misc/e2image.c
index 1996311..34b1e2c 100644
--- a/misc/e2image.c
+++ b/misc/e2image.c
@@ -240,7 +240,7 @@
 	 */
 	for (j = 0; j < fs->desc_blocks; j++) {
 		ext2fs_mark_block_bitmap(meta_block_map,
-					 block + j + 1);
+			 ext2fs_descriptor_block_loc(fs, block, j));
 	}
 
 	for (i = 0; i < fs->group_desc_count; i++) {
diff --git a/misc/mke2fs.c b/misc/mke2fs.c
index c37eecc..1c83d9f 100644
--- a/misc/mke2fs.c
+++ b/misc/mke2fs.c
@@ -780,7 +780,8 @@
 	EXT3_FEATURE_COMPAT_HAS_JOURNAL |
 		EXT2_FEATURE_COMPAT_DIR_INDEX,	/* Compat */
 	EXT2_FEATURE_INCOMPAT_FILETYPE|		/* Incompat */
-		EXT3_FEATURE_INCOMPAT_JOURNAL_DEV,
+		EXT3_FEATURE_INCOMPAT_JOURNAL_DEV|
+		EXT2_FEATURE_INCOMPAT_META_BG,
 	EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER	/* R/O compat */
 };
 
@@ -1164,6 +1165,12 @@
 	    !journal_size)
 		journal_size = -1;
 
+	/* Set first meta blockgroup via an environment variable */
+	/* (this is mostly for debugging purposes) */
+	if ((param.s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) &&
+	    ((tmp = getenv("MKE2FS_FIRST_META_BG"))))
+		param.s_first_meta_bg = atoi(tmp);
+
 	set_fs_defaults(fs_type, &param, blocksize, &inode_ratio);
 
 	if (param.s_blocks_per_group) {