Commit 05c2c00f authored by Jan Kara's avatar Jan Kara Committed by Theodore Ts'o

ext4: protect superblock modifications with a buffer lock

Protect all superblock modifications (including checksum computation)
with a superblock buffer lock. That way we are sure computed checksum
matches current superblock contents (a mismatch could cause checksum
failures in nojournal mode or if an unjournalled superblock update races
with a journalled one). Also we avoid modifying superblock contents
while it is being written out (which can cause DIF/DIX failures if we
are running in nojournal mode).
Signed-off-by: default avatarJan Kara <jack@suse.cz>
Link: https://lore.kernel.org/r/20201216101844.22917-4-jack@suse.czSigned-off-by: default avatarTheodore Ts'o <tytso@mit.edu>
parent 4392fbc4
...@@ -379,7 +379,6 @@ int __ext4_handle_dirty_super(const char *where, unsigned int line, ...@@ -379,7 +379,6 @@ int __ext4_handle_dirty_super(const char *where, unsigned int line,
struct buffer_head *bh = EXT4_SB(sb)->s_sbh; struct buffer_head *bh = EXT4_SB(sb)->s_sbh;
int err = 0; int err = 0;
ext4_superblock_csum_set(sb);
if (ext4_handle_valid(handle)) { if (ext4_handle_valid(handle)) {
err = jbd2_journal_dirty_metadata(handle, bh); err = jbd2_journal_dirty_metadata(handle, bh);
if (err) if (err)
......
...@@ -809,8 +809,11 @@ static int ext4_sample_last_mounted(struct super_block *sb, ...@@ -809,8 +809,11 @@ static int ext4_sample_last_mounted(struct super_block *sb,
err = ext4_journal_get_write_access(handle, sbi->s_sbh); err = ext4_journal_get_write_access(handle, sbi->s_sbh);
if (err) if (err)
goto out_journal; goto out_journal;
lock_buffer(sbi->s_sbh);
strlcpy(sbi->s_es->s_last_mounted, cp, strlcpy(sbi->s_es->s_last_mounted, cp,
sizeof(sbi->s_es->s_last_mounted)); sizeof(sbi->s_es->s_last_mounted));
ext4_superblock_csum_set(sb);
unlock_buffer(sbi->s_sbh);
ext4_handle_dirty_super(handle, sb); ext4_handle_dirty_super(handle, sb);
out_journal: out_journal:
ext4_journal_stop(handle); ext4_journal_stop(handle);
......
...@@ -5150,7 +5150,10 @@ static int ext4_do_update_inode(handle_t *handle, ...@@ -5150,7 +5150,10 @@ static int ext4_do_update_inode(handle_t *handle,
err = ext4_journal_get_write_access(handle, EXT4_SB(sb)->s_sbh); err = ext4_journal_get_write_access(handle, EXT4_SB(sb)->s_sbh);
if (err) if (err)
goto out_brelse; goto out_brelse;
lock_buffer(EXT4_SB(sb)->s_sbh);
ext4_set_feature_large_file(sb); ext4_set_feature_large_file(sb);
ext4_superblock_csum_set(sb);
unlock_buffer(EXT4_SB(sb)->s_sbh);
ext4_handle_sync(handle); ext4_handle_sync(handle);
err = ext4_handle_dirty_super(handle, sb); err = ext4_handle_dirty_super(handle, sb);
} }
......
...@@ -2978,7 +2978,10 @@ int ext4_orphan_add(handle_t *handle, struct inode *inode) ...@@ -2978,7 +2978,10 @@ int ext4_orphan_add(handle_t *handle, struct inode *inode)
(le32_to_cpu(sbi->s_es->s_inodes_count))) { (le32_to_cpu(sbi->s_es->s_inodes_count))) {
/* Insert this inode at the head of the on-disk orphan list */ /* Insert this inode at the head of the on-disk orphan list */
NEXT_ORPHAN(inode) = le32_to_cpu(sbi->s_es->s_last_orphan); NEXT_ORPHAN(inode) = le32_to_cpu(sbi->s_es->s_last_orphan);
lock_buffer(sbi->s_sbh);
sbi->s_es->s_last_orphan = cpu_to_le32(inode->i_ino); sbi->s_es->s_last_orphan = cpu_to_le32(inode->i_ino);
ext4_superblock_csum_set(sb);
unlock_buffer(sbi->s_sbh);
dirty = true; dirty = true;
} }
list_add(&EXT4_I(inode)->i_orphan, &sbi->s_orphan); list_add(&EXT4_I(inode)->i_orphan, &sbi->s_orphan);
...@@ -3061,7 +3064,10 @@ int ext4_orphan_del(handle_t *handle, struct inode *inode) ...@@ -3061,7 +3064,10 @@ int ext4_orphan_del(handle_t *handle, struct inode *inode)
mutex_unlock(&sbi->s_orphan_lock); mutex_unlock(&sbi->s_orphan_lock);
goto out_brelse; goto out_brelse;
} }
lock_buffer(sbi->s_sbh);
sbi->s_es->s_last_orphan = cpu_to_le32(ino_next); sbi->s_es->s_last_orphan = cpu_to_le32(ino_next);
ext4_superblock_csum_set(inode->i_sb);
unlock_buffer(sbi->s_sbh);
mutex_unlock(&sbi->s_orphan_lock); mutex_unlock(&sbi->s_orphan_lock);
err = ext4_handle_dirty_super(handle, inode->i_sb); err = ext4_handle_dirty_super(handle, inode->i_sb);
} else { } else {
......
...@@ -899,7 +899,10 @@ static int add_new_gdb(handle_t *handle, struct inode *inode, ...@@ -899,7 +899,10 @@ static int add_new_gdb(handle_t *handle, struct inode *inode,
EXT4_SB(sb)->s_gdb_count++; EXT4_SB(sb)->s_gdb_count++;
ext4_kvfree_array_rcu(o_group_desc); ext4_kvfree_array_rcu(o_group_desc);
lock_buffer(EXT4_SB(sb)->s_sbh);
le16_add_cpu(&es->s_reserved_gdt_blocks, -1); le16_add_cpu(&es->s_reserved_gdt_blocks, -1);
ext4_superblock_csum_set(sb);
unlock_buffer(EXT4_SB(sb)->s_sbh);
err = ext4_handle_dirty_super(handle, sb); err = ext4_handle_dirty_super(handle, sb);
if (err) if (err)
ext4_std_error(sb, err); ext4_std_error(sb, err);
...@@ -1384,6 +1387,7 @@ static void ext4_update_super(struct super_block *sb, ...@@ -1384,6 +1387,7 @@ static void ext4_update_super(struct super_block *sb,
reserved_blocks *= blocks_count; reserved_blocks *= blocks_count;
do_div(reserved_blocks, 100); do_div(reserved_blocks, 100);
lock_buffer(sbi->s_sbh);
ext4_blocks_count_set(es, ext4_blocks_count(es) + blocks_count); ext4_blocks_count_set(es, ext4_blocks_count(es) + blocks_count);
ext4_free_blocks_count_set(es, ext4_free_blocks_count(es) + free_blocks); ext4_free_blocks_count_set(es, ext4_free_blocks_count(es) + free_blocks);
le32_add_cpu(&es->s_inodes_count, EXT4_INODES_PER_GROUP(sb) * le32_add_cpu(&es->s_inodes_count, EXT4_INODES_PER_GROUP(sb) *
...@@ -1421,6 +1425,8 @@ static void ext4_update_super(struct super_block *sb, ...@@ -1421,6 +1425,8 @@ static void ext4_update_super(struct super_block *sb,
* active. */ * active. */
ext4_r_blocks_count_set(es, ext4_r_blocks_count(es) + ext4_r_blocks_count_set(es, ext4_r_blocks_count(es) +
reserved_blocks); reserved_blocks);
ext4_superblock_csum_set(sb);
unlock_buffer(sbi->s_sbh);
/* Update the free space counts */ /* Update the free space counts */
percpu_counter_add(&sbi->s_freeclusters_counter, percpu_counter_add(&sbi->s_freeclusters_counter,
...@@ -1717,8 +1723,11 @@ static int ext4_group_extend_no_check(struct super_block *sb, ...@@ -1717,8 +1723,11 @@ static int ext4_group_extend_no_check(struct super_block *sb,
goto errout; goto errout;
} }
lock_buffer(EXT4_SB(sb)->s_sbh);
ext4_blocks_count_set(es, o_blocks_count + add); ext4_blocks_count_set(es, o_blocks_count + add);
ext4_free_blocks_count_set(es, ext4_free_blocks_count(es) + add); ext4_free_blocks_count_set(es, ext4_free_blocks_count(es) + add);
ext4_superblock_csum_set(sb);
unlock_buffer(EXT4_SB(sb)->s_sbh);
ext4_debug("freeing blocks %llu through %llu\n", o_blocks_count, ext4_debug("freeing blocks %llu through %llu\n", o_blocks_count,
o_blocks_count + add); o_blocks_count + add);
/* We add the blocks to the bitmap and set the group need init bit */ /* We add the blocks to the bitmap and set the group need init bit */
...@@ -1874,10 +1883,13 @@ static int ext4_convert_meta_bg(struct super_block *sb, struct inode *inode) ...@@ -1874,10 +1883,13 @@ static int ext4_convert_meta_bg(struct super_block *sb, struct inode *inode)
if (err) if (err)
goto errout; goto errout;
lock_buffer(sbi->s_sbh);
ext4_clear_feature_resize_inode(sb); ext4_clear_feature_resize_inode(sb);
ext4_set_feature_meta_bg(sb); ext4_set_feature_meta_bg(sb);
sbi->s_es->s_first_meta_bg = sbi->s_es->s_first_meta_bg =
cpu_to_le32(num_desc_blocks(sb, sbi->s_groups_count)); cpu_to_le32(num_desc_blocks(sb, sbi->s_groups_count));
ext4_superblock_csum_set(sb);
unlock_buffer(sbi->s_sbh);
err = ext4_handle_dirty_super(handle, sb); err = ext4_handle_dirty_super(handle, sb);
if (err) { if (err) {
......
...@@ -5444,6 +5444,7 @@ static int ext4_commit_super(struct super_block *sb) ...@@ -5444,6 +5444,7 @@ static int ext4_commit_super(struct super_block *sb)
if (!sbh || block_device_ejected(sb)) if (!sbh || block_device_ejected(sb))
return error; return error;
lock_buffer(sbh);
/* /*
* If the file system is mounted read-only, don't update the * If the file system is mounted read-only, don't update the
* superblock write time. This avoids updating the superblock * superblock write time. This avoids updating the superblock
...@@ -5515,7 +5516,6 @@ static int ext4_commit_super(struct super_block *sb) ...@@ -5515,7 +5516,6 @@ static int ext4_commit_super(struct super_block *sb)
BUFFER_TRACE(sbh, "marking dirty"); BUFFER_TRACE(sbh, "marking dirty");
ext4_superblock_csum_set(sb); ext4_superblock_csum_set(sb);
lock_buffer(sbh);
if (buffer_write_io_error(sbh) || !buffer_uptodate(sbh)) { if (buffer_write_io_error(sbh) || !buffer_uptodate(sbh)) {
/* /*
* Oh, dear. A previous attempt to write the * Oh, dear. A previous attempt to write the
......
...@@ -792,7 +792,10 @@ static void ext4_xattr_update_super_block(handle_t *handle, ...@@ -792,7 +792,10 @@ static void ext4_xattr_update_super_block(handle_t *handle,
BUFFER_TRACE(EXT4_SB(sb)->s_sbh, "get_write_access"); BUFFER_TRACE(EXT4_SB(sb)->s_sbh, "get_write_access");
if (ext4_journal_get_write_access(handle, EXT4_SB(sb)->s_sbh) == 0) { if (ext4_journal_get_write_access(handle, EXT4_SB(sb)->s_sbh) == 0) {
lock_buffer(EXT4_SB(sb)->s_sbh);
ext4_set_feature_xattr(sb); ext4_set_feature_xattr(sb);
ext4_superblock_csum_set(sb);
unlock_buffer(EXT4_SB(sb)->s_sbh);
ext4_handle_dirty_super(handle, sb); ext4_handle_dirty_super(handle, sb);
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment