Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

ext4: fix corrupt backup group descriptors after online resize

In commit 9a8c5b0d0615 ("ext4: update the backup superblock's at the end
of the online resize"), it is assumed that update_backups() only updates
backup superblocks, so each b_data is treated as a backupsuper block to
update its s_block_group_nr and s_checksum. However, update_backups()
also updates the backup group descriptors, which causes the backup group
descriptors to be corrupted.

The above commit fixes the problem of invalid checksum of the backup
superblock. The root cause of this problem is that the checksum of
ext4_update_super() is not set correctly. This problem has been fixed
in the previous patch ("ext4: fix bad checksum after online resize").

However, we do need to set block_group_nr for the backup superblock in
update_backups(). When a block is in a group that contains a backup
superblock, and the block is the first block in the group, the block is
definitely a superblock. We add a helper function that includes setting
s_block_group_nr and updating checksum, and then call it only when the
above conditions are met to prevent the backup group descriptors from
being incorrectly modified.

Fixes: 9a8c5b0d0615 ("ext4: update the backup superblock's at the end of the online resize")
Signed-off-by: Baokun Li <libaokun1@huawei.com>
Reviewed-by: Jan Kara <jack@suse.cz>
Cc: stable@kernel.org
Link: https://lore.kernel.org/r/20221117040341.1380702-3-libaokun1@huawei.com
Signed-off-by: Theodore Ts'o <tytso@mit.edu>

authored by

Baokun Li and committed by
Theodore Ts'o
8f49ec60 a408f33e

+15 -7
+15 -7
fs/ext4/resize.c
··· 1110 1110 return err; 1111 1111 } 1112 1112 1113 + static inline void ext4_set_block_group_nr(struct super_block *sb, char *data, 1114 + ext4_group_t group) 1115 + { 1116 + struct ext4_super_block *es = (struct ext4_super_block *) data; 1117 + 1118 + es->s_block_group_nr = cpu_to_le16(group); 1119 + if (ext4_has_metadata_csum(sb)) 1120 + es->s_checksum = ext4_superblock_csum(sb, es); 1121 + } 1122 + 1113 1123 /* 1114 1124 * Update the backup copies of the ext4 metadata. These don't need to be part 1115 1125 * of the main resize transaction, because e2fsck will re-write them if there ··· 1168 1158 while (group < sbi->s_groups_count) { 1169 1159 struct buffer_head *bh; 1170 1160 ext4_fsblk_t backup_block; 1171 - struct ext4_super_block *es; 1161 + int has_super = ext4_bg_has_super(sb, group); 1162 + ext4_fsblk_t first_block = ext4_group_first_block_no(sb, group); 1172 1163 1173 1164 /* Out of journal space, and can't get more - abort - so sad */ 1174 1165 err = ext4_resize_ensure_credits_batch(handle, 1); ··· 1179 1168 if (meta_bg == 0) 1180 1169 backup_block = ((ext4_fsblk_t)group) * bpg + blk_off; 1181 1170 else 1182 - backup_block = (ext4_group_first_block_no(sb, group) + 1183 - ext4_bg_has_super(sb, group)); 1171 + backup_block = first_block + has_super; 1184 1172 1185 1173 bh = sb_getblk(sb, backup_block); 1186 1174 if (unlikely(!bh)) { ··· 1197 1187 memcpy(bh->b_data, data, size); 1198 1188 if (rest) 1199 1189 memset(bh->b_data + size, 0, rest); 1200 - es = (struct ext4_super_block *) bh->b_data; 1201 - es->s_block_group_nr = cpu_to_le16(group); 1202 - if (ext4_has_metadata_csum(sb)) 1203 - es->s_checksum = ext4_superblock_csum(sb, es); 1190 + if (has_super && (backup_block == first_block)) 1191 + ext4_set_block_group_nr(sb, bh->b_data, group); 1204 1192 set_buffer_uptodate(bh); 1205 1193 unlock_buffer(bh); 1206 1194 err = ext4_handle_dirty_metadata(handle, NULL, bh);