diff mbox series

[3/3] ext4: fix corruption when online resizing a 1K bigalloc fs

Message ID 20221115121638.192349-4-libaokun1@huawei.com
State Superseded
Headers show
Series fix some bugs in online resize | expand

Commit Message

Baokun Li Nov. 15, 2022, 12:16 p.m. UTC
When a backup superblock is updated in update_backups(), the primary
superblock's offset in the group (that is, sbi->s_sbh->b_blocknr) is used
as the backup superblock's offset in its group. However, when the block
size is 1K and bigalloc is enabled, the two offsets are not equal. This
causes the backup group descriptors to be overwritten by the superblock
in update_backups(). Moreover, if meta_bg is enabled, the file system will
be corrupted because this feature uses backup group descriptors.

To solve this issue, we use a more accurate s_first_data_block as the
offset of the backup superblock in its group.

Fixes: d77147ff443b ("ext4: add support for online resizing with bigalloc")
Signed-off-by: Baokun Li <libaokun1@huawei.com>
---
 fs/ext4/resize.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

Comments

kernel test robot Nov. 16, 2022, 1:08 a.m. UTC | #1
Hi Baokun,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on tytso-ext4/dev]
[also build test WARNING on linus/master v6.1-rc5 next-20221115]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Baokun-Li/fix-some-bugs-in-online-resize/20221115-195625
base:   https://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4.git dev
patch link:    https://lore.kernel.org/r/20221115121638.192349-4-libaokun1%40huawei.com
patch subject: [PATCH 3/3] ext4: fix corruption when online resizing a 1K bigalloc fs
config: m68k-randconfig-s053-20221115
compiler: m68k-linux-gcc (GCC) 12.1.0
reproduce:
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # apt-get install sparse
        # sparse version: v0.6.4-39-gce1a6720-dirty
        # https://github.com/intel-lab-lkp/linux/commit/72fcaa65f215267f43525ac8cfe4ddb563c9db34
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Baokun-Li/fix-some-bugs-in-online-resize/20221115-195625
        git checkout 72fcaa65f215267f43525ac8cfe4ddb563c9db34
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__' O=build_dir ARCH=m68k SHELL=/bin/bash fs/ext4/

If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <lkp@intel.com>

sparse warnings: (new ones prefixed by >>)
>> fs/ext4/resize.c:1594:38: sparse: sparse: incorrect type in argument 2 (different base types) @@     expected unsigned long long [usertype] blk_off @@     got restricted __le32 [usertype] s_first_data_block @@
   fs/ext4/resize.c:1594:38: sparse:     expected unsigned long long [usertype] blk_off
   fs/ext4/resize.c:1594:38: sparse:     got restricted __le32 [usertype] s_first_data_block
   fs/ext4/resize.c:1806:38: sparse: sparse: incorrect type in argument 2 (different base types) @@     expected unsigned long long [usertype] blk_off @@     got restricted __le32 [usertype] s_first_data_block @@
   fs/ext4/resize.c:1806:38: sparse:     expected unsigned long long [usertype] blk_off
   fs/ext4/resize.c:1806:38: sparse:     got restricted __le32 [usertype] s_first_data_block

vim +1594 fs/ext4/resize.c

  1516	
  1517	/* Add a flex group to an fs. Ensure we handle all possible error conditions
  1518	 * _before_ we start modifying the filesystem, because we cannot abort the
  1519	 * transaction and not have it write the data to disk.
  1520	 */
  1521	static int ext4_flex_group_add(struct super_block *sb,
  1522				       struct inode *resize_inode,
  1523				       struct ext4_new_flex_group_data *flex_gd)
  1524	{
  1525		struct ext4_sb_info *sbi = EXT4_SB(sb);
  1526		struct ext4_super_block *es = sbi->s_es;
  1527		ext4_fsblk_t o_blocks_count;
  1528		ext4_grpblk_t last;
  1529		ext4_group_t group;
  1530		handle_t *handle;
  1531		unsigned reserved_gdb;
  1532		int err = 0, err2 = 0, credit;
  1533	
  1534		BUG_ON(!flex_gd->count || !flex_gd->groups || !flex_gd->bg_flags);
  1535	
  1536		reserved_gdb = le16_to_cpu(es->s_reserved_gdt_blocks);
  1537		o_blocks_count = ext4_blocks_count(es);
  1538		ext4_get_group_no_and_offset(sb, o_blocks_count, &group, &last);
  1539		BUG_ON(last);
  1540	
  1541		err = setup_new_flex_group_blocks(sb, flex_gd);
  1542		if (err)
  1543			goto exit;
  1544		/*
  1545		 * We will always be modifying at least the superblock and  GDT
  1546		 * blocks.  If we are adding a group past the last current GDT block,
  1547		 * we will also modify the inode and the dindirect block.  If we
  1548		 * are adding a group with superblock/GDT backups  we will also
  1549		 * modify each of the reserved GDT dindirect blocks.
  1550		 */
  1551		credit = 3;	/* sb, resize inode, resize inode dindirect */
  1552		/* GDT blocks */
  1553		credit += 1 + DIV_ROUND_UP(flex_gd->count, EXT4_DESC_PER_BLOCK(sb));
  1554		credit += reserved_gdb;	/* Reserved GDT dindirect blocks */
  1555		handle = ext4_journal_start_sb(sb, EXT4_HT_RESIZE, credit);
  1556		if (IS_ERR(handle)) {
  1557			err = PTR_ERR(handle);
  1558			goto exit;
  1559		}
  1560	
  1561		BUFFER_TRACE(sbi->s_sbh, "get_write_access");
  1562		err = ext4_journal_get_write_access(handle, sb, sbi->s_sbh,
  1563						    EXT4_JTR_NONE);
  1564		if (err)
  1565			goto exit_journal;
  1566	
  1567		group = flex_gd->groups[0].group;
  1568		BUG_ON(group != sbi->s_groups_count);
  1569		err = ext4_add_new_descs(handle, sb, group,
  1570					resize_inode, flex_gd->count);
  1571		if (err)
  1572			goto exit_journal;
  1573	
  1574		err = ext4_setup_new_descs(handle, sb, flex_gd);
  1575		if (err)
  1576			goto exit_journal;
  1577	
  1578		ext4_update_super(sb, flex_gd);
  1579	
  1580		err = ext4_handle_dirty_metadata(handle, NULL, sbi->s_sbh);
  1581	
  1582	exit_journal:
  1583		err2 = ext4_journal_stop(handle);
  1584		if (!err)
  1585			err = err2;
  1586	
  1587		if (!err) {
  1588			int gdb_num = group / EXT4_DESC_PER_BLOCK(sb);
  1589			int gdb_num_end = ((group + flex_gd->count - 1) /
  1590					   EXT4_DESC_PER_BLOCK(sb));
  1591			int meta_bg = ext4_has_feature_meta_bg(sb);
  1592			sector_t old_gdb = 0;
  1593	
> 1594			update_backups(sb, es->s_first_data_block, (char *)es,
  1595				       sizeof(struct ext4_super_block), 0);
  1596			for (; gdb_num <= gdb_num_end; gdb_num++) {
  1597				struct buffer_head *gdb_bh;
  1598	
  1599				gdb_bh = sbi_array_rcu_deref(sbi, s_group_desc,
  1600							     gdb_num);
  1601				if (old_gdb == gdb_bh->b_blocknr)
  1602					continue;
  1603				update_backups(sb, gdb_bh->b_blocknr, gdb_bh->b_data,
  1604					       gdb_bh->b_size, meta_bg);
  1605				old_gdb = gdb_bh->b_blocknr;
  1606			}
  1607		}
  1608	exit:
  1609		return err;
  1610	}
  1611
diff mbox series

Patch

diff --git a/fs/ext4/resize.c b/fs/ext4/resize.c
index 32fbfc173571..cf75fdd3729d 100644
--- a/fs/ext4/resize.c
+++ b/fs/ext4/resize.c
@@ -1591,7 +1591,7 @@  static int ext4_flex_group_add(struct super_block *sb,
 		int meta_bg = ext4_has_feature_meta_bg(sb);
 		sector_t old_gdb = 0;
 
-		update_backups(sb, sbi->s_sbh->b_blocknr, (char *)es,
+		update_backups(sb, es->s_first_data_block, (char *)es,
 			       sizeof(struct ext4_super_block), 0);
 		for (; gdb_num <= gdb_num_end; gdb_num++) {
 			struct buffer_head *gdb_bh;
@@ -1803,8 +1803,8 @@  static int ext4_group_extend_no_check(struct super_block *sb,
 		if (test_opt(sb, DEBUG))
 			printk(KERN_DEBUG "EXT4-fs: extended group to %llu "
 			       "blocks\n", ext4_blocks_count(es));
-		update_backups(sb, EXT4_SB(sb)->s_sbh->b_blocknr,
-			       (char *)es, sizeof(struct ext4_super_block), 0);
+		update_backups(sb, es->s_first_data_block, (char *)es,
+			       sizeof(struct ext4_super_block), 0);
 	}
 	return err;
 }