diff -ru linux-2.2.10.orig/fs/ext2/balloc.c linux-2.2.10/fs/ext2/balloc.c
--- linux-2.2.10.orig/fs/ext2/balloc.c	Wed Oct 28 22:54:56 1998
+++ linux-2.2.10/fs/ext2/balloc.c	Tue Jul 27 18:45:58 1999
@@ -251,6 +251,42 @@
 	return slot;
 }
 
+void ext2_update_group (struct super_block * sb, unsigned int block_group,
+			unsigned int reserved)
+{
+	struct ext2_group_desc * gdp;
+	struct ext2_super_block * es;
+	unsigned int blocks;
+	unsigned int inodes;
+
+	lock_super(sb);
+	es = sb->u.ext2_sb.s_es;
+	gdp = get_group_desc (sb, block_group, NULL);
+
+	blocks = gdp->bg_free_blocks_count;
+	es->s_free_blocks_count =
+		cpu_to_le32(le32_to_cpu(es->s_free_blocks_count) + blocks);
+	es->s_blocks_count = cpu_to_le32(le32_to_cpu(es->s_blocks_count) +
+		blocks);
+	es->s_r_blocks_count = cpu_to_le32(le32_to_cpu(es->s_r_blocks_count) +
+		blocks * reserved >> 8);
+
+	inodes = gdp->bg_free_inodes_count;
+	es->s_free_inodes_count =
+		cpu_to_le32(le32_to_cpu(es->s_free_inodes_count) + inodes);
+	es->s_inodes_count = cpu_to_le32(le32_to_cpu(es->s_inodes_count) +
+		inodes);
+
+	if (test_opt (sb, DEBUG))
+		printk ("EXT2-fs: ext2_update_group: group %u: "
+			"%u blocks, %u inodes\n",
+			block_group, blocks, inodes);
+
+	mark_buffer_dirty(sb->u.ext2_sb.s_sbh, 1);
+	sb->s_dirt = 1;
+	unlock_super(sb);
+}
+
 void ext2_free_blocks (const struct inode * inode, unsigned long block,
 		       unsigned long count)
 {
@@ -630,13 +666,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;
@@ -661,9 +697,9 @@
 	       le32_to_cpu(es->s_free_blocks_count), desc_count, bitmap_count);
 	unlock_super (sb);
 	return bitmap_count;
-#else
+	}
+	else
 	return le32_to_cpu(sb->u.ext2_sb.s_es->s_free_blocks_count);
-#endif
 }
 
 static inline int block_in_use (unsigned long block,
diff -ru linux-2.2.10.orig/fs/ext2/super.c linux-2.2.10/fs/ext2/super.c
--- linux-2.2.10.orig/fs/ext2/super.c	Mon Apr 12 11:03:45 1999
+++ linux-2.2.10/fs/ext2/super.c	Tue Jul 27 18:45:58 1999
@@ -14,6 +14,7 @@
  *
  *  Big-endian to little-endian byte-swapping/bitmaps by
  *        David S. Miller (davem@caip.rutgers.edu), 1995
+ *  Online resize by Andreas Dilger (adilger@enel.ucalgary.ca), July 1999
  */
 
 #include <linux/module.h>
@@ -146,7 +147,8 @@
  */
 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)
 {
 	char * this_char;
 	char * value;
@@ -220,6 +222,20 @@
 		else if (!strcmp (this_char, "nogrpid") ||
 			 !strcmp (this_char, "sysvgroups"))
 			clear_opt (*mount_options, GRPID);
+		else if (!strcmp (this_char, "resize")){
+			printk ("EXT2-fs: resize value: %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) {
+				printk ("EXT2-fs: invalid resize option: %s\n",
+					value);
+				return 0;
+			}
+		}
 		else if (!strcmp (this_char, "resgid")) {
 			if (!value || !*value) {
 				printk ("EXT2-fs: the resgid option requires "
@@ -343,7 +359,7 @@
 		if (le32_to_cpu(gdp->bg_block_bitmap) < block ||
 		    le32_to_cpu(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) le32_to_cpu(gdp->bg_block_bitmap));
@@ -352,7 +368,7 @@
 		if (le32_to_cpu(gdp->bg_inode_bitmap) < block ||
 		    le32_to_cpu(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) le32_to_cpu(gdp->bg_inode_bitmap));
@@ -362,7 +378,7 @@
 		    le32_to_cpu(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) le32_to_cpu(gdp->bg_inode_table));
@@ -374,6 +390,181 @@
 	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;
+
+	db_start = sb->u.ext2_sb.s_db_per_group + o_groups_count ? 1 : 0;
+	groups_count = (blocks_count -
+			le32_to_cpu(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: db_orig=%lu, db_start=%d, db_count=%d "
+			"logic_sb_block=%lu, blocks_count=%lu\n",
+			sb->u.ext2_sb.s_db_per_group,
+			db_start, db_count, logic_sb_block, blocks_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++) {
+			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;
+			}
+		}
+
+		/*
+		 * 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.
+		 * We SHOULD be safe if o_group_desc == NULL, because we copy 0
+		 * bytes.  Need to check with someone who knows...
+		 */
+		memcpy (group_desc, o_group_desc, sb->u.ext2_sb.s_db_per_group *
+			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;
+}
+
+static void ext2_resize_fs (struct super_block * sb,
+			    struct ext2_super_block * es,
+			    unsigned long n_blocks_count)
+{
+	unsigned long o_blocks_count;
+	unsigned long o_groups_count;
+	unsigned long last, add;
+	unsigned long reserved;
+	struct buffer_head * bh;
+	struct dquot_operations * dq_op_save = sb->dq_op;
+	struct inode inode;
+	unsigned long i;
+
+	o_blocks_count = le32_to_cpu(es->s_blocks_count);
+	o_groups_count = sb->u.ext2_sb.s_groups_count;
+
+	if (test_opt (sb, DEBUG))
+		printk ("EXT2-fs: attempting resize from %lu to %lu blocks\n",
+			o_blocks_count, n_blocks_count);
+	
+	if (n_blocks_count < o_blocks_count) {
+		ext2_warning (sb, "ext2_resize_fs",
+			      "unable to shrink filesystem, resize aborted");
+		return;
+	}
+	else if (n_blocks_count == o_blocks_count)
+		return;
+
+	/* See if the device is actually as big as what was requested */
+	bh = bread (sb->s_dev, n_blocks_count, EXT2_BLOCK_SIZE(sb));
+	if (!bh) {
+		ext2_warning (sb, "ext2_resize_fs",
+			      "unable to read last block, resize aborted");
+		return;
+	}
+	brelse (bh);
+
+	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;
+	}
+	unlock_super (sb);
+
+	/*
+	 * Handle the remaining blocks in the last partial group.  For
+	 * reserved calculation, we avoid 32-bit overflow.  If you change
+	 * the reserved scaling, also fix the scale in ext2_update_group().
+	 */
+	o_blocks_count = le32_to_cpu(es->s_blocks_count);
+	reserved = o_blocks_count > 1<<24 ?
+		le32_to_cpu(es->s_r_blocks_count) / (o_blocks_count >> 8) :
+		(le32_to_cpu(es->s_r_blocks_count) << 8 + 128) / o_blocks_count;
+	last = (o_blocks_count - 1) % EXT2_BLOCKS_PER_GROUP(sb);
+	add = last + EXT2_BLOCKS_PER_GROUP(sb) > n_blocks_count ?
+		n_blocks_count - o_blocks_count :
+		EXT2_BLOCKS_PER_GROUP(sb) - last;
+	es->s_blocks_count = cpu_to_le32(le32_to_cpu(es->s_blocks_count) + add);
+	es->s_r_blocks_count = cpu_to_le32(le32_to_cpu(es->s_r_blocks_count) +
+					   add * reserved >> 8);
+	/*
+	 * Fake out an inode enough to "free" the new blocks in this group.
+	 * FIXME: There is a race condition with dq_op = NULL, if another
+	 * process gets the superblock between the time dq_op is set NULL,
+	 * ext2_free_blocks() gets the lock, and dq_op is restored, then quotas
+	 * will not work on this FS.  However, resize will not happen very
+	 * often, and it is a VERY short time...
+	 *
+	 */
+	if (test_opt (sb, DEBUG))
+		printk ("EXT2-fs: ext2_resize_fs: freeing %lu blocks\n", add);
+	inode.i_sb = sb;
+	sb->dq_op = NULL;
+	ext2_free_blocks (&inode, o_blocks_count, add);
+	sb->dq_op = dq_op_save;
+
+	/*
+	 * Update superblock with remaining group block/inode counts
+	 */
+	for (i = o_groups_count; i < sb->u.ext2_sb.s_groups_count; i++)
+		ext2_update_group (sb, i, reserved);
+	if (le32_to_cpu(es->s_blocks_count) != n_blocks_count)
+		ext2_warning (sb, "ext2_resize_fs",
+			      "new block count does not match specified size"
+			      "(%lu != %lu)", o_blocks_count, n_blocks_count);
+}
+
 #define log2(n) ffz(~(n))
 
 struct super_block * ext2_read_super (struct super_block * sb, void * data,
@@ -385,12 +576,12 @@
 	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 offset = 0;
	kdev_t dev = sb->s_dev;
 	int blocksize = BLOCK_SIZE;
 	int hblock;
-	int db_count;
-	int i, j;
+	int i;
 
 	/*
 	 * See what the current blocksize for the device is, and
@@ -408,7 +599,7 @@
 	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)) {
 		sb->s_dev = 0;
 		return NULL;
 	}
@@ -583,35 +774,13 @@
 		goto failed_mount;
 	}
 
-	sb->u.ext2_sb.s_groups_count = (le32_to_cpu(es->s_blocks_count) -
-				        le32_to_cpu(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,
+				    le32_to_cpu(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++) {
@@ -622,7 +792,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
@@ -632,16 +801,19 @@
 	sb->s_root = d_alloc_root(iget(sb, EXT2_ROOT_INO), NULL);
 	if (!sb->s_root) {
 		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;
 	}
+	if (n_blocks_count != 0)
+		ext2_resize_fs (sb, es, n_blocks_count);
 	ext2_setup_super (sb, es);
 	return sb;
 }
@@ -688,6 +860,7 @@
 	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 new_mount_opt;
 	unsigned long tmp;
 
@@ -696,14 +869,15 @@
 	 */
 	new_mount_opt = EXT2_MOUNT_CHECK_NORMAL;
 	if (!parse_options (data, &tmp, &resuid, &resgid,
-			    &new_mount_opt))
+			    &new_mount_opt, &n_blocks_count))
 		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 (le16_to_cpu(es->s_state) & EXT2_VALID_FS ||
@@ -727,6 +901,10 @@
 		 */
 		sb->u.ext2_sb.s_mount_state = le16_to_cpu(es->s_state);
 		sb->s_flags &= ~MS_RDONLY;
+		if (n_blocks_count != 0) {
+			ext2_resize_fs (sb, es, n_blocks_count);
+			sb->u.ext2_sb.s_mount_state |= EXT2_VALID_FS;
+		}
 		ext2_setup_super (sb, es);
 	}
 	return 0;
diff -ru linux-2.2.10.orig/include/linux/ext2_fs.h linux-2.2.10/include/linux/ext2_fs.h
--- linux-2.2.10.orig/include/linux/ext2_fs.h	Fri Apr 23 22:20:38 1999
+++ linux-2.2.10/include/linux/ext2_fs.h	Tue Jul 27 18:45:58 1999
@@ -525,6 +525,8 @@
 			   __u32 *, __u32 *, int *);
 extern void ext2_free_blocks (const struct inode *, unsigned long,
 			      unsigned long);
+extern void ext2_update_group (struct super_block *, unsigned int,
+			       unsigned int);
 extern unsigned long ext2_count_free_blocks (struct super_block *);
 extern void ext2_check_blocks_bitmap (struct super_block *);
 extern struct ext2_group_desc * ext2_get_group_desc(struct super_block * sb,
