Skip to content

Commit

Permalink
ext4: protect superblock modifications with a buffer lock
Browse files Browse the repository at this point in the history
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: Jan Kara <jack@suse.cz>
Link: https://lore.kernel.org/r/20201216101844.22917-4-jack@suse.cz
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
  • Loading branch information
Jan Kara authored and Theodore Ts'o committed Dec 22, 2020
1 parent 4392fbc commit 05c2c00
Show file tree
Hide file tree
Showing 7 changed files with 28 additions and 2 deletions.
1 change: 0 additions & 1 deletion fs/ext4/ext4_jbd2.c
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,6 @@ int __ext4_handle_dirty_super(const char *where, unsigned int line,
struct buffer_head *bh = EXT4_SB(sb)->s_sbh;
int err = 0;

ext4_superblock_csum_set(sb);
if (ext4_handle_valid(handle)) {
err = jbd2_journal_dirty_metadata(handle, bh);
if (err)
Expand Down
3 changes: 3 additions & 0 deletions fs/ext4/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -809,8 +809,11 @@ static int ext4_sample_last_mounted(struct super_block *sb,
err = ext4_journal_get_write_access(handle, sbi->s_sbh);
if (err)
goto out_journal;
lock_buffer(sbi->s_sbh);
strlcpy(sbi->s_es->s_last_mounted, cp,
sizeof(sbi->s_es->s_last_mounted));
ext4_superblock_csum_set(sb);
unlock_buffer(sbi->s_sbh);
ext4_handle_dirty_super(handle, sb);
out_journal:
ext4_journal_stop(handle);
Expand Down
3 changes: 3 additions & 0 deletions fs/ext4/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
if (err)
goto out_brelse;
lock_buffer(EXT4_SB(sb)->s_sbh);
ext4_set_feature_large_file(sb);
ext4_superblock_csum_set(sb);
unlock_buffer(EXT4_SB(sb)->s_sbh);
ext4_handle_sync(handle);
err = ext4_handle_dirty_super(handle, sb);
}
Expand Down
6 changes: 6 additions & 0 deletions fs/ext4/namei.c
Original file line number Diff line number Diff line change
Expand Up @@ -2978,7 +2978,10 @@ int ext4_orphan_add(handle_t *handle, struct inode *inode)
(le32_to_cpu(sbi->s_es->s_inodes_count))) {
/* Insert this inode at the head of the on-disk orphan list */
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);
ext4_superblock_csum_set(sb);
unlock_buffer(sbi->s_sbh);
dirty = true;
}
list_add(&EXT4_I(inode)->i_orphan, &sbi->s_orphan);
Expand Down Expand Up @@ -3061,7 +3064,10 @@ int ext4_orphan_del(handle_t *handle, struct inode *inode)
mutex_unlock(&sbi->s_orphan_lock);
goto out_brelse;
}
lock_buffer(sbi->s_sbh);
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);
err = ext4_handle_dirty_super(handle, inode->i_sb);
} else {
Expand Down
12 changes: 12 additions & 0 deletions fs/ext4/resize.c
Original file line number Diff line number Diff line change
Expand Up @@ -899,7 +899,10 @@ static int add_new_gdb(handle_t *handle, struct inode *inode,
EXT4_SB(sb)->s_gdb_count++;
ext4_kvfree_array_rcu(o_group_desc);

lock_buffer(EXT4_SB(sb)->s_sbh);
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);
if (err)
ext4_std_error(sb, err);
Expand Down Expand Up @@ -1384,6 +1387,7 @@ static void ext4_update_super(struct super_block *sb,
reserved_blocks *= blocks_count;
do_div(reserved_blocks, 100);

lock_buffer(sbi->s_sbh);
ext4_blocks_count_set(es, ext4_blocks_count(es) + blocks_count);
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) *
Expand Down Expand Up @@ -1421,6 +1425,8 @@ static void ext4_update_super(struct super_block *sb,
* active. */
ext4_r_blocks_count_set(es, ext4_r_blocks_count(es) +
reserved_blocks);
ext4_superblock_csum_set(sb);
unlock_buffer(sbi->s_sbh);

/* Update the free space counts */
percpu_counter_add(&sbi->s_freeclusters_counter,
Expand Down Expand Up @@ -1717,8 +1723,11 @@ static int ext4_group_extend_no_check(struct super_block *sb,
goto errout;
}

lock_buffer(EXT4_SB(sb)->s_sbh);
ext4_blocks_count_set(es, o_blocks_count + 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,
o_blocks_count + add);
/* We add the blocks to the bitmap and set the group need init bit */
Expand Down Expand Up @@ -1874,10 +1883,13 @@ static int ext4_convert_meta_bg(struct super_block *sb, struct inode *inode)
if (err)
goto errout;

lock_buffer(sbi->s_sbh);
ext4_clear_feature_resize_inode(sb);
ext4_set_feature_meta_bg(sb);
sbi->s_es->s_first_meta_bg =
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);
if (err) {
Expand Down
2 changes: 1 addition & 1 deletion fs/ext4/super.c
Original file line number Diff line number Diff line change
Expand Up @@ -5444,6 +5444,7 @@ static int ext4_commit_super(struct super_block *sb)
if (!sbh || block_device_ejected(sb))
return error;

lock_buffer(sbh);
/*
* If the file system is mounted read-only, don't update the
* superblock write time. This avoids updating the superblock
Expand Down Expand Up @@ -5515,7 +5516,6 @@ static int ext4_commit_super(struct super_block *sb)

BUFFER_TRACE(sbh, "marking dirty");
ext4_superblock_csum_set(sb);
lock_buffer(sbh);
if (buffer_write_io_error(sbh) || !buffer_uptodate(sbh)) {
/*
* Oh, dear. A previous attempt to write the
Expand Down
3 changes: 3 additions & 0 deletions fs/ext4/xattr.c
Original file line number Diff line number Diff line change
Expand Up @@ -792,7 +792,10 @@ static void ext4_xattr_update_super_block(handle_t *handle,

BUFFER_TRACE(EXT4_SB(sb)->s_sbh, "get_write_access");
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_superblock_csum_set(sb);
unlock_buffer(EXT4_SB(sb)->s_sbh);
ext4_handle_dirty_super(handle, sb);
}
}
Expand Down

0 comments on commit 05c2c00

Please sign in to comment.