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

ext4: update the backup superblock's at the end of the online resize

When expanding a file system using online resize, various fields in
the superblock (e.g., s_blocks_count, s_inodes_count, etc.) change.
To update the backup superblocks, the online resize uses the function
update_backups() in fs/ext4/resize.c. This function was not updating
the checksum field in the backup superblocks. This wasn't a big deal
previously, because e2fsck didn't care about the checksum field in the
backup superblock. (And indeed, update_backups() goes all the way
back to the ext3 days, well before we had support for metadata
checksums.)

However, there is an alternate, more general way of updating
superblock fields, ext4_update_primary_sb() in fs/ext4/ioctl.c. This
function does check the checksum of the backup superblock, and if it
doesn't match will mark the file system as corrupted. That was
clearly not the intent, so avoid to aborting the resize when a bad
superblock is found.

In addition, teach update_backups() to properly update the checksum in
the backup superblocks. We will eventually want to unify
updapte_backups() with the infrasture in ext4_update_primary_sb(), but
that's for another day.

Note: The problem has been around for a while; it just didn't really
matter until ext4_update_primary_sb() was added by commit bbc605cdb1e1
("ext4: implement support for get/set fs label"). And it became
trivially easy to reproduce after commit 827891a38acc ("ext4: update
the s_overhead_clusters in the backup sb's when resizing") in v6.0.

Cc: stable@kernel.org # 5.17+
Fixes: bbc605cdb1e1 ("ext4: implement support for get/set fs label")
Signed-off-by: Theodore Ts'o <tytso@mit.edu>

+6 -2
+1 -2
fs/ext4/ioctl.c
··· 145 145 if (ext4_has_metadata_csum(sb) && 146 146 es->s_checksum != ext4_superblock_csum(sb, es)) { 147 147 ext4_msg(sb, KERN_ERR, "Invalid checksum for backup " 148 - "superblock %llu\n", sb_block); 148 + "superblock %llu", sb_block); 149 149 unlock_buffer(bh); 150 - err = -EFSBADCRC; 151 150 goto out_bh; 152 151 } 153 152 func(es, arg);
+5
fs/ext4/resize.c
··· 1158 1158 while (group < sbi->s_groups_count) { 1159 1159 struct buffer_head *bh; 1160 1160 ext4_fsblk_t backup_block; 1161 + struct ext4_super_block *es; 1161 1162 1162 1163 /* Out of journal space, and can't get more - abort - so sad */ 1163 1164 err = ext4_resize_ensure_credits_batch(handle, 1); ··· 1187 1186 memcpy(bh->b_data, data, size); 1188 1187 if (rest) 1189 1188 memset(bh->b_data + size, 0, rest); 1189 + es = (struct ext4_super_block *) bh->b_data; 1190 + es->s_block_group_nr = cpu_to_le16(group); 1191 + if (ext4_has_metadata_csum(sb)) 1192 + es->s_checksum = ext4_superblock_csum(sb, es); 1190 1193 set_buffer_uptodate(bh); 1191 1194 unlock_buffer(bh); 1192 1195 err = ext4_handle_dirty_metadata(handle, NULL, bh);