diff -ru -X linux-2.0.38/exclude.lst linux-2.0.38.orig/CREDITS linux-2.0.38/CREDITS
--- linux-2.0.38.orig/CREDITS	Sun Jun 13 11:20:59 1999
+++ linux-2.0.38/CREDITS	Wed Sep 15 19:14:16 1999
@@ -337,6 +337,14 @@
 S: Warrendale, Pennsylvania 15086
 S: USA
 
+N: Andreas Dilger
+E: adilger@enel.ucalgary.ca
+W: http://www-mddsp.enel.ucalgary.ca/People/adilger/
+D: Ext2 filesystem online resize capability
+S: 630 Schooner Cove N.W.
+S: Calgary, AB
+S: Canada   T3L 1Z1
+
 N: Eddie C. Dost
 E: ecd@skynet.be
 D: Linux/Sparc kernel hacker
diff -ru -X linux-2.0.38/exclude.lst linux-2.0.38.orig/Documentation/Configure.help linux-2.0.38/Documentation/Configure.help
--- linux-2.0.38.orig/Documentation/Configure.help	Sun Jun 13 11:20:59 1999
+++ linux-2.0.38/Documentation/Configure.help	Tue Oct  5 22:45:41 1999
@@ -3612,6 +3626,21 @@
   short ext2fs-FAQ, available via FTP (user: anonymous) in
   sunsite.unc.edu:/pub/Linux/docs/faqs. This option will enlarge your
   kernel by about 41 kB. Default is Y.
+
+Online resize for ext2 filesystems
+CONFIG_EXT2_RESIZE
+  This option gives you the ability to increase the size of an ext2
+  filesystem while it is mounted (in use).  In order to do this, you
+  must also be able to resize the underlying disk partition, probably
+  via a Logical Volume Manager (LVM), metadevice (MD), or hardware
+  RAID device - none of that capability is included in this feature.
+  If you don't know what any of these things are, or you haven't
+  configured your kernel for them, you should probably say N here.
+  If you choose Y, then your kernel will be about 3k larger, and you
+  will need to download some additional software
+  (http://www-mddsp.enel.ucalgary.ca/People/adilger/online-ext2/) in
+  order to actually resize your filesystem, otherwise this feature will
+  just sit unused inside the kernel.
 
 xiafs filesystem support
 CONFIG_XIA_FS
diff -ru -X linux-2.0.38/exclude.lst linux-2.0.38.orig/fs/Config.in linux-2.0.38/fs/Config.in
--- linux-2.0.38.orig/fs/Config.in	Sun Jun 13 11:21:03 1999
+++ linux-2.0.38/fs/Config.in	Sat Sep 25 21:10:25 1999
@@ -8,6 +8,9 @@
 tristate 'Minix fs support' CONFIG_MINIX_FS
 tristate 'Extended fs support' CONFIG_EXT_FS
 tristate 'Second extended fs support' CONFIG_EXT2_FS
+if [ "$CONFIG_EXT2_FS" != "n" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then
+  bool 'Online ext2 resize support (experimental)' CONFIG_EXT2_RESIZE
+fi
 tristate 'xiafs filesystem support' CONFIG_XIA_FS
 
 tristate 'Native language support (Needed for FAT and ISO9660)' CONFIG_NLS
diff -ru -X linux-2.0.38/exclude.lst linux-2.0.38.orig/fs/ext2/balloc.c linux-2.0.38/fs/ext2/balloc.c
--- linux-2.0.38.orig/fs/ext2/balloc.c	Mon Jul 26 12:44:58 1999
+++ linux-2.0.38/fs/ext2/balloc.c	Sun Oct 10 19:54:46 1999
@@ -243,6 +243,79 @@
 	return slot;
 }
 
+#ifdef CONFIG_EXT2_RESIZE
+int ext2_update_group (struct super_block * sb, unsigned int block_group,
+		       unsigned int reserved, unsigned int resgdt)
+{
+	struct ext2_group_desc * gdp;
+	struct ext2_super_block * es;
+	int blocks, m_blocks;
+	int inodes;
+	int shrink = reserved > 100 ? 1 : 0;
+
+	lock_super (sb);
+	es = sb->u.ext2_sb.s_es;
+	gdp = get_group_desc (sb, block_group, NULL);
+
+	inodes = gdp->bg_free_inodes_count;
+	blocks = gdp->bg_free_blocks_count;
+	m_blocks = ((inodes * EXT2_INODE_SIZE(sb)) + EXT2_BLOCK_SIZE(sb) - 1) /
+		   EXT2_BLOCK_SIZE(sb) + 3 + sb->u.ext2_sb.s_db_per_group +
+		   resgdt + blocks;
+
+#ifdef CONFIG_EXT2_SHRINK
+	if (shrink) {	/* Can only free if all blocks/inodes in group unused */
+		int last = (es->s_blocks_count - es->s_first_data_block) %
+			   EXT2_BLOCKS_PER_GROUP(sb);
+		if (block_group != sb->u.ext2_sb.s_groups_count - 1) {
+			ext2_warning (sb, "ext2_update_group",
+				"unable to remove group %u - "
+				"it isn't the last group", block_group);
+			unlock_super (sb);
+			return -EBUSY;
+		}
+		if (inodes != es->s_inodes_per_group || m_blocks != last) {
+			ext2_warning (sb, "ext2_update_group",
+				"unable to remove group %u - "
+				"%u blocks, %u inodes in use", block_group,
+				es->s_inodes_per_group - inodes,
+				last - m_blocks);
+			unlock_super (sb);
+			return -EBUSY;
+		}
+		inodes = -inodes;
+		blocks = -blocks;
+		m_blocks = -m_blocks;
+		reserved -= 100;
+	}
+#endif /* CONFIG_EXT2_SHRINK */
+
+	es->s_free_inodes_count += inodes;
+	es->s_inodes_count += inodes;
+
+	es->s_free_blocks_count += blocks;
+	es->s_blocks_count += m_blocks;
+	es->s_r_blocks_count += m_blocks * reserved / 100;
+
+	mark_buffer_dirty (sb->u.ext2_sb.s_sbh, 1);
+	sb->s_dirt = 1;
+	unlock_super (sb);
+
+	if (test_opt (sb, DEBUG)) {
+		printk ("EXT2-fs: ext2_update_group: %s group %u: "
+			"%u inodes, %u blocks (%u free), %d (%d%%) reserved\n",
+			shrink ? "removing" : "adding", block_group, inodes,
+			m_blocks, blocks, m_blocks * reserved / 100, reserved);
+		printk ("EXT2-fs: ext2_update_group: %u inodes (%u free), "
+			"%u blocks (%u free)\n", es->s_inodes_count,
+			es->s_free_inodes_count, es->s_blocks_count,
+			es->s_free_blocks_count);
+	}
+
+	return 1;
+}
+#endif /* CONFIG_EXT2_RESIZE */
+
 void ext2_free_blocks (const struct inode * inode, unsigned long block,
 		       unsigned long count)
 {
@@ -353,11 +426,11 @@
 	struct super_block * sb;
 	struct ext2_group_desc * gdp;
 	struct ext2_super_block * es;
-
-	*err = -ENOSPC;
 #ifdef EXT2FS_DEBUG
 	static int goal_hits = 0, goal_attempts = 0;
 #endif
+
+	*err = -ENOSPC;
 	sb = inode->i_sb;
 	if (!sb) {
 		printk ("ext2_new_block: nonexistent device");
@@ -557,7 +630,7 @@
 		}	
 		gdp->bg_free_blocks_count -= *prealloc_count;
 		es->s_free_blocks_count -= *prealloc_count;
-		ext2_debug ("Preallocated a further %lu bits.\n",
+		ext2_debug ("Preallocated a further %u bits.\n",
 			    *prealloc_count);
 	}
 #endif
@@ -602,13 +675,13 @@
 
 unsigned long ext2_count_free_blocks (struct super_block * sb)
 {
-#ifdef EXT2FS_DEBUG
 	struct ext2_super_block * es;
 	unsigned long desc_count, bitmap_count, x;
 	int bitmap_nr;
 	struct ext2_group_desc * gdp;
 	int i;
 	
+	if (test_opt (sb, DEBUG) && test_opt (sb, CHECK_STRICT)) {
 	lock_super (sb);
 	es = sb->u.ext2_sb.s_es;
 	desc_count = 0;
@@ -628,13 +701,13 @@
 			i, gdp->bg_free_blocks_count, x);
 		bitmap_count += x;
 	}
-	printk("ext2_count_free_blocks: stored = %lu, computed = %lu, %lu\n",
-	       es->s_free_blocks_count, desc_count, bitmap_count);
+	printk ("ext2_count_free_blocks: sb = %u, gdt = %lu, bitmaps = %lu\n",
+		es->s_free_blocks_count, desc_count, bitmap_count);
 	unlock_super (sb);
 	return bitmap_count;
-#else
+	}
+	else
 	return sb->u.ext2_sb.s_es->s_free_blocks_count;
-#endif
 }
 
 static inline int block_in_use (unsigned long block,
@@ -701,14 +774,14 @@
 		if (gdp->bg_free_blocks_count != x)
 			ext2_error (sb, "ext2_check_blocks_bitmap",
 				    "Wrong free blocks count for group %d, "
-				    "stored = %d, counted = %lu", i,
+				    "descriptor = %d, bitmap = %lu", i,
 				    gdp->bg_free_blocks_count, x);
 		bitmap_count += x;
 	}
 	if (es->s_free_blocks_count != bitmap_count)
 		ext2_error (sb, "ext2_check_blocks_bitmap",
 			    "Wrong free blocks count in super block, "
-			    "stored = %lu, counted = %lu",
+			    "superblock = %lu, bitmap = %lu",
 			    (unsigned long) es->s_free_blocks_count, bitmap_count);
 	unlock_super (sb);
 }
diff -ru -X linux-2.0.38/exclude.lst linux-2.0.38.orig/fs/ext2/ialloc.c linux-2.0.38/fs/ext2/ialloc.c
--- linux-2.0.38.orig/fs/ext2/ialloc.c	Sun Nov 15 11:33:12 1998
+++ linux-2.0.38/fs/ext2/ialloc.c	Sun Oct 10 19:56:49 1999
@@ -564,14 +564,14 @@
 		if (gdp->bg_free_inodes_count != x)
 			ext2_error (sb, "ext2_check_inodes_bitmap",
 				    "Wrong free inodes count in group %d, "
-				    "stored = %d, counted = %lu", i,
+				    "descriptor = %d, bitmap = %lu", i,
 				    gdp->bg_free_inodes_count, x);
 		bitmap_count += x;
 	}
 	if (es->s_free_inodes_count != bitmap_count)
 		ext2_error (sb, "ext2_check_inodes_bitmap",
 			    "Wrong free inodes count in super block, "
-			    "stored = %lu, counted = %lu",
+			    "superblock = %lu, inode = %lu",
 			    (unsigned long) es->s_free_inodes_count, bitmap_count);
 	unlock_super (sb);
 }
diff -ru -X linux-2.0.38/exclude.lst linux-2.0.38.orig/fs/ext2/super.c linux-2.0.38/fs/ext2/super.c
--- linux-2.0.38.orig/fs/ext2/super.c	Mon Jul 26 12:44:58 1999
+++ linux-2.0.38/fs/ext2/super.c	Mon Oct  4 21:37:49 1999
@@ -11,6 +11,8 @@
  *  linux/fs/minix/inode.c
  *
  *  Copyright (C) 1991, 1992  Linus Torvalds
+ *
+ *  Online resize by Andreas Dilger (adilger@enel.ucalgary.ca), July 1999
  */
 
 #include <linux/module.h>
@@ -32,6 +34,10 @@
 
 static char error_buf[1024];
 
+#ifndef min
+# define min(m,n) ((m) < (n) ? (m) : (n))
+#endif
+
 void ext2_error (struct super_block * sb, const char * function,
 		 const char * fmt, ...)
 {
@@ -140,7 +146,9 @@
  */
 static int parse_options (char * options, unsigned long * sb_block,
 			  unsigned short *resuid, unsigned short * resgid,
-			  unsigned long * mount_options)
+			  unsigned long * mount_options,
+			  unsigned long * n_blocks_count,
+			  unsigned long * resgdt)
 {
 	char * this_char;
 	char * value;
@@ -214,6 +222,26 @@
 		else if (!strcmp (this_char, "nogrpid") ||
 			 !strcmp (this_char, "sysvgroups"))
 			clear_opt (*mount_options, GRPID);
+#ifdef CONFIG_EXT2_RESIZE
+		else if (!strcmp (this_char, "resize")) {
+			printk ("EXT2-fs: parse_options: resize=%s\n", value);
+			if (!value || !*value){
+				printk ("EXT2-fs: resize requires number of "
+					"blocks\n");
+				return 0;
+			}
+			*n_blocks_count = simple_strtoul (value, &value, 0);
+			if (*value == ':') {
+				value++;
+				*resgdt = simple_strtoul (value, &value, 0);
+			}
+			if (*value) {
+				printk ("EXT2-fs: invalid resize option: %s\n",
+					value);
+				return 0;
+			}
+		}
+#endif /* CONFIG_EXT2_RESIZE */
 		else if (!strcmp (this_char, "resgid")) {
 			if (!value || !*value) {
 				printk ("EXT2-fs: the resgid option requires "
@@ -298,10 +326,10 @@
 		mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1);
 		sb->s_dirt = 1;
 		if (test_opt (sb, DEBUG))
-			printk ("[EXT II FS %s, %s, bs=%lu, fs=%lu, gc=%lu, "
+			printk ("[EXT II FS %s, %s, bs=%lu, bc=%lu, gc=%lu, "
 				"bpg=%lu, ipg=%lu, mo=%04lx]\n",
 				EXT2FS_VERSION, EXT2FS_DATE, sb->s_blocksize,
-				sb->u.ext2_sb.s_frag_size,
+				es->s_blocks_count,
 				sb->u.ext2_sb.s_groups_count,
 				EXT2_BLOCKS_PER_GROUP(sb),
 				EXT2_INODES_PER_GROUP(sb),
@@ -329,7 +357,7 @@
 		if (gdp->bg_block_bitmap < block ||
 		    gdp->bg_block_bitmap >= block + EXT2_BLOCKS_PER_GROUP(sb))
 		{
-			ext2_error (sb, "ext2_check_descriptors",
+			ext2_warning (sb, "ext2_check_descriptors",
 				    "Block bitmap for group %d"
 				    " not in group (block %lu)!",
 				    i, (unsigned long) gdp->bg_block_bitmap);
@@ -338,7 +366,7 @@
 		if (gdp->bg_inode_bitmap < block ||
 		    gdp->bg_inode_bitmap >= block + EXT2_BLOCKS_PER_GROUP(sb))
 		{
-			ext2_error (sb, "ext2_check_descriptors",
+			ext2_warning (sb, "ext2_check_descriptors",
 				    "Inode bitmap for group %d"
 				    " not in group (block %lu)!",
 				    i, (unsigned long) gdp->bg_inode_bitmap);
@@ -348,7 +376,7 @@
 		    gdp->bg_inode_table + sb->u.ext2_sb.s_itb_per_group >=
 		    block + EXT2_BLOCKS_PER_GROUP(sb))
 		{
-			ext2_error (sb, "ext2_check_descriptors",
+			ext2_warning (sb, "ext2_check_descriptors",
 				    "Inode table for group %d"
 				    " not in group (block %lu)!",
 				    i, (unsigned long) gdp->bg_inode_table);
@@ -360,6 +388,210 @@
 	return 1;
 }
 
+static int ext2_read_descriptors (struct super_block * sb,
+				  unsigned long logic_sb_block,
+				  unsigned long blocks_count)
+{
+	int db_start;
+	int db_count;
+	unsigned long groups_count;
+	unsigned long o_groups_count;
+	struct buffer_head ** group_desc = NULL;
+	struct buffer_head ** o_group_desc;
+	int i, j;
+
+	o_group_desc = sb->u.ext2_sb.s_group_desc;
+	o_groups_count = sb->u.ext2_sb.s_groups_count;
+
+	/* If we already have some groups loaded, start on the next one. */
+	db_start = sb->u.ext2_sb.s_db_per_group + o_groups_count ? 1 : 0;
+	groups_count = (blocks_count - sb->u.ext2_sb.s_es->s_first_data_block +
+			EXT2_BLOCKS_PER_GROUP(sb) - 1) /
+			EXT2_BLOCKS_PER_GROUP(sb);
+	db_count = (groups_count + EXT2_DESC_PER_BLOCK(sb) - 1) /
+		   EXT2_DESC_PER_BLOCK(sb);
+	if (test_opt (sb, DEBUG)) {
+		printk ("EXT2-fs: ext2_read_descriptors: o_groups_count=%lu, "
+			"groups_count=%lu, blocks_count=%lu\n", o_groups_count,
+			groups_count, blocks_count);
+		printk ("EXT2-fs: ext2_read_descriptors: db_orig=%lu, "
+			"db_start=%d, db_count=%d\n",
+			sb->u.ext2_sb.s_db_per_group, db_start, db_count);
+	}
+
+	if (db_start != db_count) {
+		group_desc = (struct buffer_head **) kmalloc (db_count *
+				sizeof (struct buffer_head *), GFP_KERNEL);
+		if (group_desc == NULL) {
+			ext2_warning (sb, "ext2_read_descriptors",
+				      "not enough memory for %d groups\n",
+				      db_count);
+			return 0;
+		}
+		for (i = db_start; i < db_count; i++) {	/* Add group blocks */
+			group_desc[i] = bread (sb->s_dev,
+					       logic_sb_block + i + 1,
+					       sb->s_blocksize);
+			if (!group_desc[i]) {
+				for (j = db_start; j < i; j++)
+					brelse (group_desc[j]);
+				kfree_s (group_desc, db_count *
+					 sizeof (struct buffer_head *));
+				ext2_warning (sb, "ext2_read_descriptors",
+					      "unable to read group block %d\n",
+					      i);
+				return 0;
+			}
+		}
+#ifdef CONFIG_EXT2_SHRINK
+		for (i = db_count; i < db_start; i++) /* Remove group blocks */
+			brelse (o_group_desc[i]);
+#endif /* CONFIG_EXT2_SHRINK */
+
+		/*
+		 * Copy over old descriptor blocks previously loaded, if any.
+		 * We need to set s_group_desc and s_groups_count now so that
+		 * ext2_check_descriptors() will check the new groups as well.
+		 * FIXME: is it ok to memcpy() 0 bytes to a NULL pointer?
+		 */
+		memcpy (group_desc, o_group_desc, 
+			min(sb->u.ext2_sb.s_db_per_group, db_count) *
+			sizeof (struct buffer_head *));
+		sb->u.ext2_sb.s_group_desc = group_desc;
+	}
+	sb->u.ext2_sb.s_groups_count = groups_count;
+
+	if (!ext2_check_descriptors (sb)) {
+		sb->u.ext2_sb.s_groups_count = o_groups_count;
+		sb->u.ext2_sb.s_group_desc = o_group_desc;
+		for (j = db_start; j < db_count; j++)
+			brelse (group_desc[j]);
+		if (group_desc != NULL)
+			kfree_s (group_desc,
+				 db_count * sizeof (struct buffer_head *));
+		return 0;
+	}
+
+	if (o_group_desc && o_group_desc != sb->u.ext2_sb.s_group_desc)
+		kfree_s (o_group_desc, sb->u.ext2_sb.s_db_per_group *
+			 sizeof (struct buffer_head *));
+
+	sb->u.ext2_sb.s_db_per_group = db_count;
+	
+	return 1;
+}
+
+#ifdef CONFIG_EXT2_RESIZE
+static int ext2_resize_fs (struct super_block * sb,
+			   struct ext2_super_block * es,
+			   unsigned long n_blocks_count,
+			   unsigned long resgdt)
+{
+	unsigned long o_blocks_count;
+	unsigned long o_groups_count;
+	unsigned long last, add;
+	unsigned long reserved;
+	struct buffer_head * bh;
+	struct inode inode;
+	unsigned long i;
+	int stat = 1;
+
+	o_blocks_count = es->s_blocks_count;
+	o_groups_count = sb->u.ext2_sb.s_groups_count;
+
+	if (test_opt (sb, DEBUG))
+		printk ("EXT2-fs: ext2_resize_fs: from %lu to %lu blocks\n",
+			o_blocks_count, n_blocks_count);
+	
+	if (n_blocks_count == o_blocks_count)
+		return 1;
+
+	/* See if the device is actually as big as what was requested */
+	bh = bread (sb->s_dev, n_blocks_count - 1, EXT2_BLOCK_SIZE(sb));
+	if (!bh) {
+		ext2_warning (sb, "ext2_resize_fs",
+			      "unable to read last block, resize aborted");
+		return -ENOSPC;
+	}
+	brelse (bh);
+
+	/* For reserved percentage calculation, we avoid 32-bit overflow. */
+	reserved = o_blocks_count > 10000000 ?
+		(es->s_r_blocks_count + o_blocks_count / 200) / (o_blocks_count / 100) :
+		(es->s_r_blocks_count * 100 + o_blocks_count / 2) / o_blocks_count;
+
+#ifdef CONFIG_EXT2_SHRINK
+	/* Free only whole groups for now, it is easier */
+	while (n_blocks_count < es->s_blocks_count &&
+	       (stat = ext2_update_group (sb, o_groups_count - 1,
+					  reserved + 100, resgdt)) > 0)
+		--o_groups_count;
+	if (stat < 0)
+		return stat;
+#endif /* CONFIG_EXT2_SHRINK */
+
+	lock_super (sb);
+	if (!ext2_read_descriptors (sb, sb->u.ext2_sb.s_sbh->b_blocknr,
+				   n_blocks_count)) {
+		ext2_warning (sb, "ext2_resize_fs",
+			      "group descriptor error, resize aborted");
+		unlock_super (sb);
+		return -EINVAL;
+	}
+
+	/* Handle the remaining blocks in the last partial group. */
+	last = (o_blocks_count - es->s_first_data_block) %
+		EXT2_BLOCKS_PER_GROUP(sb);
+	if (last != 0) {	/* The last group isn't full yet */
+		/* Shouldn't happen because we currently free only whole
+		 * groups in ext2_update_groups()
+		 */
+		if (n_blocks_count < o_blocks_count) {
+			ext2_error (sb, "ext2_resize_fs",
+				"shrinking FS, but last group has blocks!");
+			unlock_super (sb);
+			return -EBUSY;
+		}
+		add = EXT2_BLOCKS_PER_GROUP(sb) - last;
+		if (add + o_blocks_count > n_blocks_count)
+			add = n_blocks_count - o_blocks_count;
+		es->s_blocks_count += add;
+		es->s_r_blocks_count += add * reserved / 100;
+		mark_buffer_dirty (sb->u.ext2_sb.s_sbh, 1);
+		sb->s_dirt = 1;
+		unlock_super (sb);
+		/*
+		 * Fake out an inode enough to "free" the new blocks in this
+		 * group.  Turn off quotas for this inode so it doesn't get
+		 * confused by freeing blocks that don't really exist yet.
+		 */
+		inode.i_sb = sb;
+		for (i = 0; i < MAXQUOTAS; i++)
+			inode.i_dquot[i] = NODQUOT;
+		ext2_free_blocks (&inode, o_blocks_count, add);
+		if (test_opt (sb, DEBUG))
+			printk ("EXT2-fs: ext2_resize_fs: added %lu new blocks "
+				"to %lu current blocks\n",
+				add, o_blocks_count);
+	}
+	else
+		unlock_super (sb);
+
+	/*
+	 * Update superblock with remaining new group block/inode counts
+	 */
+	for (i = o_groups_count;
+	     i < sb->u.ext2_sb.s_groups_count &&
+	     (stat = ext2_update_group (sb, i, reserved, resgdt)) > 0; i++)
+		/* empty loop */;
+	if (es->s_blocks_count != n_blocks_count)
+		ext2_warning (sb, "ext2_resize_fs",
+			      "new block count does not match specified size"
+			      "(%u != %lu)", es->s_blocks_count,n_blocks_count);
+	return stat;
+}
+#endif /* CONFIG_EXT2_RESIZE */
+
 #define log2(n) ffz(~(n))
 
 struct super_block * ext2_read_super (struct super_block * sb, void * data,
@@ -371,14 +603,15 @@
 	unsigned short resuid = EXT2_DEF_RESUID;
 	unsigned short resgid = EXT2_DEF_RESGID;
 	unsigned long logic_sb_block = 1;
+	unsigned long n_blocks_count = 0;
+	unsigned long resgdt = 0;
 	kdev_t dev = sb->s_dev;
-	int db_count;
-	int i, j;
+	int i;
 
 	sb->u.ext2_sb.s_mount_opt = 0;
 	set_opt (sb->u.ext2_sb.s_mount_opt, CHECK_NORMAL);
 	if (!parse_options ((char *) data, &sb_block, &resuid, &resgid,
-	    &sb->u.ext2_sb.s_mount_opt)) {
+	    &sb->u.ext2_sb.s_mount_opt, &n_blocks_count, &resgdt)) {
 		sb->s_dev = 0;
 		return NULL;
 	}
@@ -531,35 +764,12 @@
 		goto failed_mount;
 	}
 
-	sb->u.ext2_sb.s_groups_count = (es->s_blocks_count -
-				        es->s_first_data_block +
-				       EXT2_BLOCKS_PER_GROUP(sb) - 1) /
-				       EXT2_BLOCKS_PER_GROUP(sb);
-	db_count = (sb->u.ext2_sb.s_groups_count + EXT2_DESC_PER_BLOCK(sb) - 1) /
-		   EXT2_DESC_PER_BLOCK(sb);
-	sb->u.ext2_sb.s_group_desc = kmalloc (db_count * sizeof (struct buffer_head *), GFP_KERNEL);
-	if (sb->u.ext2_sb.s_group_desc == NULL) {
-		printk ("EXT2-fs: not enough memory\n");
-		goto failed_mount;
-	}
-	for (i = 0; i < db_count; i++) {
-		sb->u.ext2_sb.s_group_desc[i] = bread (dev, logic_sb_block + i + 1,
-						       sb->s_blocksize);
-		if (!sb->u.ext2_sb.s_group_desc[i]) {
-			for (j = 0; j < i; j++)
-				brelse (sb->u.ext2_sb.s_group_desc[j]);
-			kfree_s (sb->u.ext2_sb.s_group_desc,
-				 db_count * sizeof (struct buffer_head *));
-			printk ("EXT2-fs: unable to read group descriptors\n");
-			goto failed_mount;
-		}
-	}
-	if (!ext2_check_descriptors (sb)) {
-		for (j = 0; j < db_count; j++)
-			brelse (sb->u.ext2_sb.s_group_desc[j]);
-		kfree_s (sb->u.ext2_sb.s_group_desc,
-			 db_count * sizeof (struct buffer_head *));
-		printk ("EXT2-fs: group descriptors corrupted !\n");
+	sb->u.ext2_sb.s_db_per_group = 0;
+	sb->u.ext2_sb.s_groups_count = 0;
+	sb->u.ext2_sb.s_group_desc = NULL;
+	if (!ext2_read_descriptors (sb, logic_sb_block, es->s_blocks_count)) {
+		ext2_error (sb, "ext2_read_super",
+			    "group descriptor error, unable to mount");
 		goto failed_mount;
 	}
 	for (i = 0; i < EXT2_MAX_GROUP_LOADED; i++) {
@@ -570,7 +780,6 @@
 	}
 	sb->u.ext2_sb.s_loaded_inode_bitmaps = 0;
 	sb->u.ext2_sb.s_loaded_block_bitmaps = 0;
-	sb->u.ext2_sb.s_db_per_group = db_count;
 	unlock_super (sb);
 	/*
 	 * set up enough so that it can read an inode
@@ -579,16 +788,23 @@
 	sb->s_op = &ext2_sops;
 	if (!(sb->s_mounted = iget (sb, EXT2_ROOT_INO))) {
 		sb->s_dev = 0;
-		for (i = 0; i < db_count; i++)
+		for (i = 0; i < sb->u.ext2_sb.s_db_per_group; i++)
 			if (sb->u.ext2_sb.s_group_desc[i])
 				brelse (sb->u.ext2_sb.s_group_desc[i]);
 		kfree_s (sb->u.ext2_sb.s_group_desc,
-			 db_count * sizeof (struct buffer_head *));
+			 sb->u.ext2_sb.s_db_per_group *
+			 sizeof (struct buffer_head *));
 		brelse (bh);
 		printk ("EXT2-fs: get root inode failed\n");
 		MOD_DEC_USE_COUNT;
 		return NULL;
 	}
+#ifdef CONFIG_EXT2_RESIZE
+	if (n_blocks_count != 0 &&
+	    !ext2_resize_fs (sb, es, n_blocks_count, resgdt))
+		ext2_warning (sb, "ext2_read_super",
+			      "filesystem resize failed\n");
+#endif /* CONFIG_EXT2_RESIZE */
 	ext2_setup_super (sb, es);
 	return sb;
 }
@@ -635,6 +851,8 @@
 	struct ext2_super_block * es;
 	unsigned short resuid = sb->u.ext2_sb.s_resuid;
 	unsigned short resgid = sb->u.ext2_sb.s_resgid;
+	unsigned long n_blocks_count = 0;
+	unsigned long resgdt = 0;
 	unsigned long new_mount_opt;
 	unsigned long tmp;
 
@@ -643,14 +861,15 @@
 	 */
 	new_mount_opt = EXT2_MOUNT_CHECK_NORMAL;
 	if (!parse_options (data, &tmp, &resuid, &resgid,
-			    &new_mount_opt))
+			    &new_mount_opt, &n_blocks_count, &resgdt))
 		return -EINVAL;
 
 	sb->u.ext2_sb.s_mount_opt = new_mount_opt;
 	sb->u.ext2_sb.s_resuid = resuid;
 	sb->u.ext2_sb.s_resgid = resgid;
 	es = sb->u.ext2_sb.s_es;
-	if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY))
+	if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY) &&
+	    n_blocks_count == 0)
 		return 0;
 	if (*flags & MS_RDONLY) {
 		if (es->s_state & EXT2_VALID_FS ||
@@ -674,6 +893,19 @@
 		 */
 		sb->u.ext2_sb.s_mount_state = es->s_state;
 		sb->s_flags &= ~MS_RDONLY;
+#ifdef CONFIG_EXT2_RESIZE
+		if (n_blocks_count != 0) {
+			int stat;
+			sb->u.ext2_sb.s_mount_state |= EXT2_VALID_FS;
+			if ((stat = ext2_resize_fs (sb, es, n_blocks_count,
+						    resgdt)) < 0) {
+				ext2_warning (sb, "ext2_remount",
+					"filesystem resize failed\n");
+				sb->u.ext2_sb.s_mount_state &= ~EXT2_VALID_FS;
+				return stat;
+			}
+		}
+#endif /* CONFIG_EXT2_RESIZE */
 		ext2_setup_super (sb, es);
 	}
 	return 0;
diff -ru -X linux-2.0.38/exclude.lst linux-2.0.38.orig/include/linux/ext2_fs.h linux-2.0.38/include/linux/ext2_fs.h
--- linux-2.0.38.orig/include/linux/ext2_fs.h	Mon Jul 26 12:44:58 1999
+++ linux-2.0.38/include/linux/ext2_fs.h	Sun Sep 26 00:47:44 1999
@@ -60,6 +60,7 @@
 #define EXT2_ACL_DATA_INO	 4	/* ACL inode */
 #define EXT2_BOOT_LOADER_INO	 5	/* Boot loader inode */
 #define EXT2_UNDEL_DIR_INO	 6	/* Undelete directory inode */
+#define EXT2_RESIZE_INO		 7	/* Reserved group descriptors inode */
 
 /* First non-reserved inode for old ext2 filesystems */
 #define EXT2_GOOD_OLD_FIRST_INO	11
@@ -444,6 +444,8 @@
 			   __u32 *, __u32 *, int *);
 extern void ext2_free_blocks (const struct inode *, unsigned long,
 			      unsigned long);
+extern int ext2_update_group (struct super_block *, unsigned int,
+			       unsigned int, unsigned int);
 extern unsigned long ext2_count_free_blocks (struct super_block *);
 extern void ext2_check_blocks_bitmap (struct super_block *);
 
