mirror of
https://github.com/torvalds/linux.git
synced 2024-11-14 16:12:02 +00:00
ext4: check for inconsistent extents between index and leaf block
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>
This commit is contained in:
parent
8dd27feced
commit
9c6e071913
@ -354,7 +354,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;
|
||||
@ -368,6 +369,14 @@ static int ext4_valid_extent_entries(struct inode *inode,
|
||||
if (depth == 0) {
|
||||
/* leaf entries */
|
||||
struct ext4_extent *ext = EXT_FIRST_EXTENT(eh);
|
||||
|
||||
/*
|
||||
* 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;
|
||||
@ -384,6 +393,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;
|
||||
@ -404,7 +421,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;
|
||||
@ -430,7 +447,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;
|
||||
}
|
||||
@ -460,7 +477,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)
|
||||
{
|
||||
@ -493,16 +510,18 @@ 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;
|
||||
gfp_t gfp_flags = __GFP_MOVABLE | GFP_NOFS;
|
||||
ext4_fsblk_t pblk;
|
||||
|
||||
if (flags & EXT4_EX_NOFAIL)
|
||||
gfp_flags |= __GFP_NOFAIL;
|
||||
|
||||
pblk = ext4_idx_pblock(idx);
|
||||
bh = sb_getblk_gfp(inode->i_sb, pblk, gfp_flags);
|
||||
if (unlikely(!bh))
|
||||
return ERR_PTR(-ENOMEM);
|
||||
@ -515,8 +534,8 @@ __read_extent_tree_block(const char *function, unsigned int line,
|
||||
}
|
||||
if (buffer_verified(bh) && !(flags & EXT4_EX_FORCE_CACHE))
|
||||
return bh;
|
||||
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;
|
||||
set_buffer_verified(bh);
|
||||
@ -534,8 +553,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))
|
||||
|
||||
/*
|
||||
@ -585,8 +604,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)) {
|
||||
@ -893,8 +911,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;
|
||||
@ -1503,7 +1520,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;
|
||||
|
||||
@ -1570,20 +1586,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);
|
||||
@ -2962,9 +2975,9 @@ again:
|
||||
ext_debug(inode, "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…
Reference in New Issue
Block a user