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