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,
|
static int ext4_valid_extent_entries(struct inode *inode,
|
||||||
struct ext4_extent_header *eh,
|
struct ext4_extent_header *eh,
|
||||||
ext4_fsblk_t *pblk, int depth)
|
ext4_lblk_t lblk, ext4_fsblk_t *pblk,
|
||||||
|
int depth)
|
||||||
{
|
{
|
||||||
unsigned short entries;
|
unsigned short entries;
|
||||||
ext4_lblk_t lblock = 0;
|
ext4_lblk_t lblock = 0;
|
||||||
@ -368,6 +369,14 @@ static int ext4_valid_extent_entries(struct inode *inode,
|
|||||||
if (depth == 0) {
|
if (depth == 0) {
|
||||||
/* leaf entries */
|
/* leaf entries */
|
||||||
struct ext4_extent *ext = EXT_FIRST_EXTENT(eh);
|
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) {
|
while (entries) {
|
||||||
if (!ext4_valid_extent(inode, ext))
|
if (!ext4_valid_extent(inode, ext))
|
||||||
return 0;
|
return 0;
|
||||||
@ -384,6 +393,14 @@ static int ext4_valid_extent_entries(struct inode *inode,
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
struct ext4_extent_idx *ext_idx = EXT_FIRST_INDEX(eh);
|
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) {
|
while (entries) {
|
||||||
if (!ext4_valid_extent_idx(inode, ext_idx))
|
if (!ext4_valid_extent_idx(inode, ext_idx))
|
||||||
return 0;
|
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,
|
static int __ext4_ext_check(const char *function, unsigned int line,
|
||||||
struct inode *inode, struct ext4_extent_header *eh,
|
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;
|
const char *error_msg;
|
||||||
int max = 0, err = -EFSCORRUPTED;
|
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";
|
error_msg = "invalid eh_entries";
|
||||||
goto corrupted;
|
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";
|
error_msg = "invalid extent entries";
|
||||||
goto corrupted;
|
goto corrupted;
|
||||||
}
|
}
|
||||||
@ -460,7 +477,7 @@ corrupted:
|
|||||||
}
|
}
|
||||||
|
|
||||||
#define ext4_ext_check(inode, eh, depth, pblk) \
|
#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)
|
int ext4_ext_check_inode(struct inode *inode)
|
||||||
{
|
{
|
||||||
@ -493,16 +510,18 @@ static void ext4_cache_extents(struct inode *inode,
|
|||||||
|
|
||||||
static struct buffer_head *
|
static struct buffer_head *
|
||||||
__read_extent_tree_block(const char *function, unsigned int line,
|
__read_extent_tree_block(const char *function, unsigned int line,
|
||||||
struct inode *inode, ext4_fsblk_t pblk, int depth,
|
struct inode *inode, struct ext4_extent_idx *idx,
|
||||||
int flags)
|
int depth, int flags)
|
||||||
{
|
{
|
||||||
struct buffer_head *bh;
|
struct buffer_head *bh;
|
||||||
int err;
|
int err;
|
||||||
gfp_t gfp_flags = __GFP_MOVABLE | GFP_NOFS;
|
gfp_t gfp_flags = __GFP_MOVABLE | GFP_NOFS;
|
||||||
|
ext4_fsblk_t pblk;
|
||||||
|
|
||||||
if (flags & EXT4_EX_NOFAIL)
|
if (flags & EXT4_EX_NOFAIL)
|
||||||
gfp_flags |= __GFP_NOFAIL;
|
gfp_flags |= __GFP_NOFAIL;
|
||||||
|
|
||||||
|
pblk = ext4_idx_pblock(idx);
|
||||||
bh = sb_getblk_gfp(inode->i_sb, pblk, gfp_flags);
|
bh = sb_getblk_gfp(inode->i_sb, pblk, gfp_flags);
|
||||||
if (unlikely(!bh))
|
if (unlikely(!bh))
|
||||||
return ERR_PTR(-ENOMEM);
|
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))
|
if (buffer_verified(bh) && !(flags & EXT4_EX_FORCE_CACHE))
|
||||||
return bh;
|
return bh;
|
||||||
err = __ext4_ext_check(function, line, inode,
|
err = __ext4_ext_check(function, line, inode, ext_block_hdr(bh),
|
||||||
ext_block_hdr(bh), depth, pblk);
|
depth, pblk, le32_to_cpu(idx->ei_block));
|
||||||
if (err)
|
if (err)
|
||||||
goto errout;
|
goto errout;
|
||||||
set_buffer_verified(bh);
|
set_buffer_verified(bh);
|
||||||
@ -534,8 +553,8 @@ errout:
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#define read_extent_tree_block(inode, pblk, depth, flags) \
|
#define read_extent_tree_block(inode, idx, depth, flags) \
|
||||||
__read_extent_tree_block(__func__, __LINE__, (inode), (pblk), \
|
__read_extent_tree_block(__func__, __LINE__, (inode), (idx), \
|
||||||
(depth), (flags))
|
(depth), (flags))
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -585,8 +604,7 @@ int ext4_ext_precache(struct inode *inode)
|
|||||||
i--;
|
i--;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
bh = read_extent_tree_block(inode,
|
bh = read_extent_tree_block(inode, path[i].p_idx++,
|
||||||
ext4_idx_pblock(path[i].p_idx++),
|
|
||||||
depth - i - 1,
|
depth - i - 1,
|
||||||
EXT4_EX_FORCE_CACHE);
|
EXT4_EX_FORCE_CACHE);
|
||||||
if (IS_ERR(bh)) {
|
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_depth = i;
|
||||||
path[ppos].p_ext = NULL;
|
path[ppos].p_ext = NULL;
|
||||||
|
|
||||||
bh = read_extent_tree_block(inode, path[ppos].p_block, --i,
|
bh = read_extent_tree_block(inode, path[ppos].p_idx, --i, flags);
|
||||||
flags);
|
|
||||||
if (IS_ERR(bh)) {
|
if (IS_ERR(bh)) {
|
||||||
ret = PTR_ERR(bh);
|
ret = PTR_ERR(bh);
|
||||||
goto err;
|
goto err;
|
||||||
@ -1503,7 +1520,6 @@ static int ext4_ext_search_right(struct inode *inode,
|
|||||||
struct ext4_extent_header *eh;
|
struct ext4_extent_header *eh;
|
||||||
struct ext4_extent_idx *ix;
|
struct ext4_extent_idx *ix;
|
||||||
struct ext4_extent *ex;
|
struct ext4_extent *ex;
|
||||||
ext4_fsblk_t block;
|
|
||||||
int depth; /* Note, NOT eh_depth; depth from top of tree */
|
int depth; /* Note, NOT eh_depth; depth from top of tree */
|
||||||
int ee_len;
|
int ee_len;
|
||||||
|
|
||||||
@ -1570,20 +1586,17 @@ got_index:
|
|||||||
* follow it and find the closest allocated
|
* follow it and find the closest allocated
|
||||||
* block to the right */
|
* block to the right */
|
||||||
ix++;
|
ix++;
|
||||||
block = ext4_idx_pblock(ix);
|
|
||||||
while (++depth < path->p_depth) {
|
while (++depth < path->p_depth) {
|
||||||
/* subtract from p_depth to get proper eh_depth */
|
/* subtract from p_depth to get proper eh_depth */
|
||||||
bh = read_extent_tree_block(inode, block,
|
bh = read_extent_tree_block(inode, ix, path->p_depth - depth, 0);
|
||||||
path->p_depth - depth, 0);
|
|
||||||
if (IS_ERR(bh))
|
if (IS_ERR(bh))
|
||||||
return PTR_ERR(bh);
|
return PTR_ERR(bh);
|
||||||
eh = ext_block_hdr(bh);
|
eh = ext_block_hdr(bh);
|
||||||
ix = EXT_FIRST_INDEX(eh);
|
ix = EXT_FIRST_INDEX(eh);
|
||||||
block = ext4_idx_pblock(ix);
|
|
||||||
put_bh(bh);
|
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))
|
if (IS_ERR(bh))
|
||||||
return PTR_ERR(bh);
|
return PTR_ERR(bh);
|
||||||
eh = ext_block_hdr(bh);
|
eh = ext_block_hdr(bh);
|
||||||
@ -2962,9 +2975,9 @@ again:
|
|||||||
ext_debug(inode, "move to level %d (block %llu)\n",
|
ext_debug(inode, "move to level %d (block %llu)\n",
|
||||||
i + 1, ext4_idx_pblock(path[i].p_idx));
|
i + 1, ext4_idx_pblock(path[i].p_idx));
|
||||||
memset(path + i + 1, 0, sizeof(*path));
|
memset(path + i + 1, 0, sizeof(*path));
|
||||||
bh = read_extent_tree_block(inode,
|
bh = read_extent_tree_block(inode, path[i].p_idx,
|
||||||
ext4_idx_pblock(path[i].p_idx), depth - i - 1,
|
depth - i - 1,
|
||||||
EXT4_EX_NOCACHE);
|
EXT4_EX_NOCACHE);
|
||||||
if (IS_ERR(bh)) {
|
if (IS_ERR(bh)) {
|
||||||
/* should we reset i_size? */
|
/* should we reset i_size? */
|
||||||
err = PTR_ERR(bh);
|
err = PTR_ERR(bh);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user