]> git.baikalelectronics.ru Git - kernel.git/commitdiff
ext4: protect superblock modifications with a buffer lock
authorJan Kara <jack@suse.cz>
Wed, 16 Dec 2020 10:18:39 +0000 (11:18 +0100)
committerTheodore Ts'o <tytso@mit.edu>
Tue, 22 Dec 2020 18:08:46 +0000 (13:08 -0500)
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>
fs/ext4/ext4_jbd2.c
fs/ext4/file.c
fs/ext4/inode.c
fs/ext4/namei.c
fs/ext4/resize.c
fs/ext4/super.c
fs/ext4/xattr.c

index 1a0a827a7f34578984f6185c9d235d4248bae8a7..c7e410c4ab7d74619352c24fe29a0caa352f6884 100644 (file)
@@ -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)
index 3ed8c048fb12c547775850ba62d364bc08362c20..26907d5835d004a172b452052a52e557afdc5910 100644 (file)
@@ -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);
index 27946882d4ce45b7b544bce1b28a26eef006cd64..27de6f0a33c8a930231d3665c1f1d9d87a21408b 100644 (file)
@@ -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);
        }
index 7a890ff214f1a17a4247a6f002db82b05d2977af..40970a509dccc922aa1a1df8a78168b128907d22 100644 (file)
@@ -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);
@@ -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 {
index 928700d57eb67e5bc01340af2dd8826a6e9c6718..6155f2b9538ca24ecc56679fe57a5b901962d3d5 100644 (file)
@@ -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);
@@ -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) *
@@ -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,
@@ -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 */
@@ -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) {
index 8bf31003416a96ad7472ab8ad583615d79262b7a..2d653ad142005d0c9bed15462bfad85037c18365 100644 (file)
@@ -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
@@ -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
index 4e3b1f8c2e81eaaf5489ee96f2c63a5d6e8681da..1db7ca778d69e69dbbeade6a2c8543561b30d997 100644 (file)
@@ -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);
        }
 }