ext4: check for inconsistent extents between index and leaf block
commit 9c6e071913792d80894cd0be98cc3c4b770e26d3 upstream. Now that we can check out overlapping extents in leaf block and out-of-order index extents in index block. But the .ee_block in the first extent of one leaf block should equal to the .ei_block in it's parent index extent entry. This patch add a check to verify such inconsistent between the index and leaf block. Signed-off-by: Zhang Yi <yi.zhang@huawei.com> Link: https://lore.kernel.org/r/20210908120850.4012324-3-yi.zhang@huawei.com Signed-off-by: Theodore Ts'o <tytso@mit.edu> Signed-off-by: Leah Rumancik <leah.rumancik@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
6a332d095c
commit
d57fcf0d91
@ -390,7 +390,8 @@ static int ext4_valid_extent_idx(struct inode *inode,
|
||||
|
||||
static int ext4_valid_extent_entries(struct inode *inode,
|
||||
struct ext4_extent_header *eh,
|
||||
ext4_fsblk_t *pblk, int depth)
|
||||
ext4_lblk_t lblk, ext4_fsblk_t *pblk,
|
||||
int depth)
|
||||
{
|
||||
unsigned short entries;
|
||||
ext4_lblk_t lblock = 0;
|
||||
@ -406,6 +407,14 @@ static int ext4_valid_extent_entries(struct inode *inode,
|
||||
struct ext4_extent *ext = EXT_FIRST_EXTENT(eh);
|
||||
struct ext4_super_block *es = EXT4_SB(inode->i_sb)->s_es;
|
||||
ext4_fsblk_t pblock = 0;
|
||||
|
||||
/*
|
||||
* The logical block in the first entry should equal to
|
||||
* the number in the index block.
|
||||
*/
|
||||
if (depth != ext_depth(inode) &&
|
||||
lblk != le32_to_cpu(ext->ee_block))
|
||||
return 0;
|
||||
while (entries) {
|
||||
if (!ext4_valid_extent(inode, ext))
|
||||
return 0;
|
||||
@ -423,6 +432,14 @@ static int ext4_valid_extent_entries(struct inode *inode,
|
||||
}
|
||||
} else {
|
||||
struct ext4_extent_idx *ext_idx = EXT_FIRST_INDEX(eh);
|
||||
|
||||
/*
|
||||
* The logical block in the first entry should equal to
|
||||
* the number in the parent index block.
|
||||
*/
|
||||
if (depth != ext_depth(inode) &&
|
||||
lblk != le32_to_cpu(ext_idx->ei_block))
|
||||
return 0;
|
||||
while (entries) {
|
||||
if (!ext4_valid_extent_idx(inode, ext_idx))
|
||||
return 0;
|
||||
@ -443,7 +460,7 @@ static int ext4_valid_extent_entries(struct inode *inode,
|
||||
|
||||
static int __ext4_ext_check(const char *function, unsigned int line,
|
||||
struct inode *inode, struct ext4_extent_header *eh,
|
||||
int depth, ext4_fsblk_t pblk)
|
||||
int depth, ext4_fsblk_t pblk, ext4_lblk_t lblk)
|
||||
{
|
||||
const char *error_msg;
|
||||
int max = 0, err = -EFSCORRUPTED;
|
||||
@ -469,7 +486,7 @@ static int __ext4_ext_check(const char *function, unsigned int line,
|
||||
error_msg = "invalid eh_entries";
|
||||
goto corrupted;
|
||||
}
|
||||
if (!ext4_valid_extent_entries(inode, eh, &pblk, depth)) {
|
||||
if (!ext4_valid_extent_entries(inode, eh, lblk, &pblk, depth)) {
|
||||
error_msg = "invalid extent entries";
|
||||
goto corrupted;
|
||||
}
|
||||
@ -498,7 +515,7 @@ corrupted:
|
||||
}
|
||||
|
||||
#define ext4_ext_check(inode, eh, depth, pblk) \
|
||||
__ext4_ext_check(__func__, __LINE__, (inode), (eh), (depth), (pblk))
|
||||
__ext4_ext_check(__func__, __LINE__, (inode), (eh), (depth), (pblk), 0)
|
||||
|
||||
int ext4_ext_check_inode(struct inode *inode)
|
||||
{
|
||||
@ -531,12 +548,14 @@ static void ext4_cache_extents(struct inode *inode,
|
||||
|
||||
static struct buffer_head *
|
||||
__read_extent_tree_block(const char *function, unsigned int line,
|
||||
struct inode *inode, ext4_fsblk_t pblk, int depth,
|
||||
int flags)
|
||||
struct inode *inode, struct ext4_extent_idx *idx,
|
||||
int depth, int flags)
|
||||
{
|
||||
struct buffer_head *bh;
|
||||
int err;
|
||||
ext4_fsblk_t pblk;
|
||||
|
||||
pblk = ext4_idx_pblock(idx);
|
||||
bh = sb_getblk_gfp(inode->i_sb, pblk, __GFP_MOVABLE | GFP_NOFS);
|
||||
if (unlikely(!bh))
|
||||
return ERR_PTR(-ENOMEM);
|
||||
@ -552,8 +571,8 @@ __read_extent_tree_block(const char *function, unsigned int line,
|
||||
if (!ext4_has_feature_journal(inode->i_sb) ||
|
||||
(inode->i_ino !=
|
||||
le32_to_cpu(EXT4_SB(inode->i_sb)->s_es->s_journal_inum))) {
|
||||
err = __ext4_ext_check(function, line, inode,
|
||||
ext_block_hdr(bh), depth, pblk);
|
||||
err = __ext4_ext_check(function, line, inode, ext_block_hdr(bh),
|
||||
depth, pblk, le32_to_cpu(idx->ei_block));
|
||||
if (err)
|
||||
goto errout;
|
||||
}
|
||||
@ -572,8 +591,8 @@ errout:
|
||||
|
||||
}
|
||||
|
||||
#define read_extent_tree_block(inode, pblk, depth, flags) \
|
||||
__read_extent_tree_block(__func__, __LINE__, (inode), (pblk), \
|
||||
#define read_extent_tree_block(inode, idx, depth, flags) \
|
||||
__read_extent_tree_block(__func__, __LINE__, (inode), (idx), \
|
||||
(depth), (flags))
|
||||
|
||||
/*
|
||||
@ -620,8 +639,7 @@ int ext4_ext_precache(struct inode *inode)
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
bh = read_extent_tree_block(inode,
|
||||
ext4_idx_pblock(path[i].p_idx++),
|
||||
bh = read_extent_tree_block(inode, path[i].p_idx++,
|
||||
depth - i - 1,
|
||||
EXT4_EX_FORCE_CACHE);
|
||||
if (IS_ERR(bh)) {
|
||||
@ -924,8 +942,7 @@ ext4_find_extent(struct inode *inode, ext4_lblk_t block,
|
||||
path[ppos].p_depth = i;
|
||||
path[ppos].p_ext = NULL;
|
||||
|
||||
bh = read_extent_tree_block(inode, path[ppos].p_block, --i,
|
||||
flags);
|
||||
bh = read_extent_tree_block(inode, path[ppos].p_idx, --i, flags);
|
||||
if (IS_ERR(bh)) {
|
||||
ret = PTR_ERR(bh);
|
||||
goto err;
|
||||
@ -1524,7 +1541,6 @@ static int ext4_ext_search_right(struct inode *inode,
|
||||
struct ext4_extent_header *eh;
|
||||
struct ext4_extent_idx *ix;
|
||||
struct ext4_extent *ex;
|
||||
ext4_fsblk_t block;
|
||||
int depth; /* Note, NOT eh_depth; depth from top of tree */
|
||||
int ee_len;
|
||||
|
||||
@ -1591,20 +1607,17 @@ got_index:
|
||||
* follow it and find the closest allocated
|
||||
* block to the right */
|
||||
ix++;
|
||||
block = ext4_idx_pblock(ix);
|
||||
while (++depth < path->p_depth) {
|
||||
/* subtract from p_depth to get proper eh_depth */
|
||||
bh = read_extent_tree_block(inode, block,
|
||||
path->p_depth - depth, 0);
|
||||
bh = read_extent_tree_block(inode, ix, path->p_depth - depth, 0);
|
||||
if (IS_ERR(bh))
|
||||
return PTR_ERR(bh);
|
||||
eh = ext_block_hdr(bh);
|
||||
ix = EXT_FIRST_INDEX(eh);
|
||||
block = ext4_idx_pblock(ix);
|
||||
put_bh(bh);
|
||||
}
|
||||
|
||||
bh = read_extent_tree_block(inode, block, path->p_depth - depth, 0);
|
||||
bh = read_extent_tree_block(inode, ix, path->p_depth - depth, 0);
|
||||
if (IS_ERR(bh))
|
||||
return PTR_ERR(bh);
|
||||
eh = ext_block_hdr(bh);
|
||||
@ -3126,9 +3139,9 @@ again:
|
||||
ext_debug("move to level %d (block %llu)\n",
|
||||
i + 1, ext4_idx_pblock(path[i].p_idx));
|
||||
memset(path + i + 1, 0, sizeof(*path));
|
||||
bh = read_extent_tree_block(inode,
|
||||
ext4_idx_pblock(path[i].p_idx), depth - i - 1,
|
||||
EXT4_EX_NOCACHE);
|
||||
bh = read_extent_tree_block(inode, path[i].p_idx,
|
||||
depth - i - 1,
|
||||
EXT4_EX_NOCACHE);
|
||||
if (IS_ERR(bh)) {
|
||||
/* should we reset i_size? */
|
||||
err = PTR_ERR(bh);
|
||||
|
Loading…
x
Reference in New Issue
Block a user