When ext4_alloc_group_tables() cannot place the bitmaps and inode tables of the next flex group, ext4_resize_fs() just breaks out of its loop without recording the error. err is still 0 (from its initialization or from the previous successful ext4_flex_group_add()), so: - EXT4_IOC_RESIZE_FS returns success to resize2fs although the file system was not grown to the requested size, and - if the first pass was capped by the reserved GDT blocks (n_blocks_count_retry != 0), the "!err && n_blocks_count_retry" test jumps back to retry and carries on as if nothing had happened. Ignoring the failure was introduced to avoid a BUG_ON() when the last block group was too small for its metadata. That case has since been handled up front by trimming such a final group in ext4_resize_fs() (commit f0a459dec549 ("ext4: fix online resize's handling of a too-small final block group")), so a failure here is a real error that should be reported. Store the return value in err. Fixes: 03c1c29053f6 ("ext4: ignore last group w/o enough space when resizing instead of BUG'ing") Signed-off-by: Theodore Ts'o --- fs/ext4/resize-test.c | 24 ++++++++++++++++++++++++ fs/ext4/resize.c | 3 ++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/fs/ext4/resize-test.c b/fs/ext4/resize-test.c index 4a08a47b3481..0fd097d58a59 100644 --- a/fs/ext4/resize-test.c +++ b/fs/ext4/resize-test.c @@ -1408,6 +1408,29 @@ static void test_resize_fs_flex_group_straddles_first_meta_bg(struct kunit *test ext4_group_first_block_no(sb, 49) + 2)); } +/* + * Test 10: ext4_resize_fs() must report a failure of ext4_alloc_group_tables() + * instead of returning success without growing the file system. With one + * reserved GDT block, sparse group 3 has 1021 usable blocks, which cannot + * hold two bitmaps plus a 1020-block inode table. + */ +static void test_resize_fs_alloc_group_tables_enospc(struct kunit *test) +{ + struct resize_test_ctx *ctx = test->priv; + struct super_block *sb = ctx->sb; + ext4_fsblk_t o_blks, target_blks; + + resize_configure_fs(test, 3, 1, 0, RESIZE_TEST_BLOCKS_PER_GRP); + ctx->sbi.s_itb_per_group = 1020; + o_blks = ext4_blocks_count(ctx->es); + target_blks = RESIZE_TEST_FIRST_DATA_BLK + + 5ULL * RESIZE_TEST_BLOCKS_PER_GRP; + + KUNIT_EXPECT_EQ(test, ext4_resize_fs(sb, target_blks), -ENOSPC); + KUNIT_EXPECT_EQ(test, ctx->sbi.s_groups_count, 3U); + KUNIT_EXPECT_EQ(test, ext4_blocks_count(ctx->es), o_blks); +} + static struct kunit_case ext4_resize_test_cases[] = { KUNIT_CASE(test_resize_begin_and_end_guards), KUNIT_CASE(test_list_backups_and_verify_reserved_gdb), @@ -1418,6 +1441,7 @@ static struct kunit_case ext4_resize_test_cases[] = { KUNIT_CASE(test_convert_meta_bg_validation_and_conversion), KUNIT_CASE(test_resize_fs_across_resize_inode_to_meta_bg), KUNIT_CASE(test_resize_fs_flex_group_straddles_first_meta_bg), + KUNIT_CASE(test_resize_fs_alloc_group_tables_enospc), {} }; diff --git a/fs/ext4/resize.c b/fs/ext4/resize.c index 5e3b4e9f99f8..d3507b175382 100644 --- a/fs/ext4/resize.c +++ b/fs/ext4/resize.c @@ -2190,7 +2190,8 @@ int ext4_resize_fs(struct super_block *sb, ext4_fsblk_t n_blocks_count) ext4_blocks_count(es)); last_update_time = jiffies; } - if (ext4_alloc_group_tables(sb, flex_gd, flexbg_size) != 0) + err = ext4_alloc_group_tables(sb, flex_gd, flexbg_size); + if (err) break; err = ext4_flex_group_add(sb, resize_inode, flex_gd); if (unlikely(err)) -- 2.56.0.542.g3ef89b7020-goog