Skip to content

Commit d29fa1a

Browse files
Zhihao Chenggregkh
authored andcommitted
ext4: fix dir corruption when ext4_dx_add_entry() fails
commit 7177dd0 upstream. Following process may lead to fs corruption: 1. ext4_create(dir/foo) ext4_add_nondir ext4_add_entry ext4_dx_add_entry a. add_dirent_to_buf ext4_mark_inode_dirty ext4_handle_dirty_metadata // dir inode bh is recorded into journal b. ext4_append // dx_get_count(entries) == dx_get_limit(entries) ext4_bread(EXT4_GET_BLOCKS_CREATE) ext4_getblk ext4_map_blocks ext4_ext_map_blocks ext4_mb_new_blocks dquot_alloc_block dquot_alloc_space_nodirty inode_add_bytes // update dir's i_blocks ext4_ext_insert_extent ext4_ext_dirty // record extent bh into journal ext4_handle_dirty_metadata(bh) // record new block into journal inode->i_size += inode->i_sb->s_blocksize // new size(in mem) c. ext4_handle_dirty_dx_node(bh2) // record dir's new block(dx_node) into journal d. ext4_handle_dirty_dx_node((frame - 1)->bh) e. ext4_handle_dirty_dx_node(frame->bh) f. do_split // ret err! g. add_dirent_to_buf ext4_mark_inode_dirty(dir) // update raw_inode on disk(skipped) 2. fsck -a /dev/sdb drop last block(dx_node) which beyonds dir's i_size. /dev/sdb: recovering journal /dev/sdb contains a file system with errors, check forced. /dev/sdb: Inode 12, end of extent exceeds allowed value (logical block 128, physical block 3938, len 1) 3. fsck -fn /dev/sdb dx_node->entry[i].blk > dir->i_size Pass 2: Checking directory structure Problem in HTREE directory inode 12 (/dir): bad block number 128. Clear HTree index? no Problem in HTREE directory inode 12: block #3 has invalid depth (2) Problem in HTREE directory inode 12: block #3 has bad max hash Problem in HTREE directory inode 12: block #3 not referenced Fix it by marking inode dirty directly inside ext4_append(). Fetch a reproducer in [Link]. Link: https://bugzilla.kernel.org/show_bug.cgi?id=216466 Cc: [email protected] Signed-off-by: Zhihao Cheng <[email protected]> Reviewed-by: Jan Kara <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Theodore Ts'o <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent d12471b commit d29fa1a

File tree

1 file changed

+10
-5
lines changed

1 file changed

+10
-5
lines changed

fs/ext4/namei.c

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -85,15 +85,20 @@ static struct buffer_head *ext4_append(handle_t *handle,
8585
return bh;
8686
inode->i_size += inode->i_sb->s_blocksize;
8787
EXT4_I(inode)->i_disksize = inode->i_size;
88+
err = ext4_mark_inode_dirty(handle, inode);
89+
if (err)
90+
goto out;
8891
BUFFER_TRACE(bh, "get_write_access");
8992
err = ext4_journal_get_write_access(handle, inode->i_sb, bh,
9093
EXT4_JTR_NONE);
91-
if (err) {
92-
brelse(bh);
93-
ext4_std_error(inode->i_sb, err);
94-
return ERR_PTR(err);
95-
}
94+
if (err)
95+
goto out;
9696
return bh;
97+
98+
out:
99+
brelse(bh);
100+
ext4_std_error(inode->i_sb, err);
101+
return ERR_PTR(err);
97102
}
98103

99104
static int ext4_dx_csum_verify(struct inode *inode,

0 commit comments

Comments
 (0)