dae3cd5
From a0ade1deb86d2325aecc36272bb4505a6eec9235 Mon Sep 17 00:00:00 2001
dae3cd5
From: Lukas Czerner <lczerner@redhat.com>
dae3cd5
Date: Mon, 20 Feb 2012 23:02:06 -0500
dae3cd5
Subject: [PATCH] ext4: fix resize when resizing within single group
dae3cd5
dae3cd5
When resizing file system in the way that the new size of the file
dae3cd5
system is still in the same group (no new groups are added), then we can
dae3cd5
hit a BUG_ON in ext4_alloc_group_tables()
dae3cd5
dae3cd5
BUG_ON(flex_gd->count == 0 || group_data == NULL);
dae3cd5
dae3cd5
because flex_gd->count is zero. The reason is the missing check for such
dae3cd5
case, so the code always extend the last group fully and then attempt to
dae3cd5
add more groups, but at that time n_blocks_count is actually smaller
dae3cd5
than o_blocks_count.
dae3cd5
dae3cd5
It can be easily reproduced like this:
dae3cd5
dae3cd5
mkfs.ext4 -b 4096 /dev/sda 30M
dae3cd5
mount /dev/sda /mnt/test
dae3cd5
resize2fs /dev/sda 50M
dae3cd5
dae3cd5
Fix this by checking whether the resize happens within the singe group
dae3cd5
and only add that many blocks into the last group to satisfy user
dae3cd5
request. Then o_blocks_count == n_blocks_count and the resize will exit
dae3cd5
successfully without and attempt to add more groups into the fs.
dae3cd5
dae3cd5
Also fix mixing together block number and blocks count which might be
dae3cd5
confusing and can easily lead to off-by-one errors (but it is actually
dae3cd5
not the case here since the two occurrence of this mix-up will cancel
dae3cd5
each other).
dae3cd5
dae3cd5
Signed-off-by: Lukas Czerner <lczerner@redhat.com>
dae3cd5
Reported-by: Milan Broz <mbroz@redhat.com>
dae3cd5
Reviewed-by: Eric Sandeen <sandeen@redhat.com>
dae3cd5
Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
dae3cd5
---
dae3cd5
 fs/ext4/resize.c |   14 ++++++++------
dae3cd5
 1 files changed, 8 insertions(+), 6 deletions(-)
dae3cd5
dae3cd5
diff --git a/fs/ext4/resize.c b/fs/ext4/resize.c
dae3cd5
index f9d948f..3fed79d 100644
dae3cd5
--- a/fs/ext4/resize.c
dae3cd5
+++ b/fs/ext4/resize.c
dae3cd5
@@ -1582,7 +1582,7 @@ int ext4_resize_fs(struct super_block *sb, ext4_fsblk_t n_blocks_count)
dae3cd5
 	ext4_fsblk_t o_blocks_count;
dae3cd5
 	ext4_group_t o_group;
dae3cd5
 	ext4_group_t n_group;
dae3cd5
-	ext4_grpblk_t offset;
dae3cd5
+	ext4_grpblk_t offset, add;
dae3cd5
 	unsigned long n_desc_blocks;
dae3cd5
 	unsigned long o_desc_blocks;
dae3cd5
 	unsigned long desc_blocks;
dae3cd5
@@ -1605,7 +1605,7 @@ int ext4_resize_fs(struct super_block *sb, ext4_fsblk_t n_blocks_count)
dae3cd5
 		return 0;
dae3cd5
 
dae3cd5
 	ext4_get_group_no_and_offset(sb, n_blocks_count - 1, &n_group, &offset);
dae3cd5
-	ext4_get_group_no_and_offset(sb, o_blocks_count, &o_group, &offset);
dae3cd5
+	ext4_get_group_no_and_offset(sb, o_blocks_count - 1, &o_group, &offset);
dae3cd5
 
dae3cd5
 	n_desc_blocks = (n_group + EXT4_DESC_PER_BLOCK(sb)) /
dae3cd5
 			EXT4_DESC_PER_BLOCK(sb);
dae3cd5
@@ -1634,10 +1634,12 @@ int ext4_resize_fs(struct super_block *sb, ext4_fsblk_t n_blocks_count)
dae3cd5
 	}
dae3cd5
 	brelse(bh);
dae3cd5
 
dae3cd5
-	if (offset != 0) {
dae3cd5
-		/* extend the last group */
dae3cd5
-		ext4_grpblk_t add;
dae3cd5
-		add = EXT4_BLOCKS_PER_GROUP(sb) - offset;
dae3cd5
+	/* extend the last group */
dae3cd5
+	if (n_group == o_group)
dae3cd5
+		add = n_blocks_count - o_blocks_count;
dae3cd5
+	else
dae3cd5
+		add = EXT4_BLOCKS_PER_GROUP(sb) - (offset + 1);
dae3cd5
+	if (add > 0) {
dae3cd5
 		err = ext4_group_extend_no_check(sb, o_blocks_count, add);
dae3cd5
 		if (err)
dae3cd5
 			goto out;
dae3cd5
-- 
dae3cd5
1.7.6.5
dae3cd5