mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-13 14:14:37 +00:00
btrfs: deal with errors when checking if a dir entry exists during log replay
[ Upstream commit 77a5b9e3d1
]
Currently inode_in_dir() ignores errors returned from
btrfs_lookup_dir_index_item() and from btrfs_lookup_dir_item(), treating
any errors as if the directory entry does not exists in the fs/subvolume
tree, which is obviously not correct, as we can get errors such as -EIO
when reading extent buffers while searching the fs/subvolume's tree.
Fix that by making inode_in_dir() return the errors and making its only
caller, add_inode_ref(), deal with returned errors as well.
Signed-off-by: Filipe Manana <fdmanana@suse.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
parent
d49a293b94
commit
044fa2afd6
1 changed files with 29 additions and 18 deletions
|
@ -900,9 +900,11 @@ static noinline int drop_one_dir_item(struct btrfs_trans_handle *trans,
|
|||
}
|
||||
|
||||
/*
|
||||
* helper function to see if a given name and sequence number found
|
||||
* in an inode back reference are already in a directory and correctly
|
||||
* point to this inode
|
||||
* See if a given name and sequence number found in an inode back reference are
|
||||
* already in a directory and correctly point to this inode.
|
||||
*
|
||||
* Returns: < 0 on error, 0 if the directory entry does not exists and 1 if it
|
||||
* exists.
|
||||
*/
|
||||
static noinline int inode_in_dir(struct btrfs_root *root,
|
||||
struct btrfs_path *path,
|
||||
|
@ -911,29 +913,35 @@ static noinline int inode_in_dir(struct btrfs_root *root,
|
|||
{
|
||||
struct btrfs_dir_item *di;
|
||||
struct btrfs_key location;
|
||||
int match = 0;
|
||||
int ret = 0;
|
||||
|
||||
di = btrfs_lookup_dir_index_item(NULL, root, path, dirid,
|
||||
index, name, name_len, 0);
|
||||
if (di && !IS_ERR(di)) {
|
||||
if (IS_ERR(di)) {
|
||||
if (PTR_ERR(di) != -ENOENT)
|
||||
ret = PTR_ERR(di);
|
||||
goto out;
|
||||
} else if (di) {
|
||||
btrfs_dir_item_key_to_cpu(path->nodes[0], di, &location);
|
||||
if (location.objectid != objectid)
|
||||
goto out;
|
||||
} else
|
||||
} else {
|
||||
goto out;
|
||||
btrfs_release_path(path);
|
||||
}
|
||||
|
||||
btrfs_release_path(path);
|
||||
di = btrfs_lookup_dir_item(NULL, root, path, dirid, name, name_len, 0);
|
||||
if (di && !IS_ERR(di)) {
|
||||
btrfs_dir_item_key_to_cpu(path->nodes[0], di, &location);
|
||||
if (location.objectid != objectid)
|
||||
goto out;
|
||||
} else
|
||||
if (IS_ERR(di)) {
|
||||
ret = PTR_ERR(di);
|
||||
goto out;
|
||||
match = 1;
|
||||
} else if (di) {
|
||||
btrfs_dir_item_key_to_cpu(path->nodes[0], di, &location);
|
||||
if (location.objectid == objectid)
|
||||
ret = 1;
|
||||
}
|
||||
out:
|
||||
btrfs_release_path(path);
|
||||
return match;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1500,10 +1508,12 @@ static noinline int add_inode_ref(struct btrfs_trans_handle *trans,
|
|||
if (ret)
|
||||
goto out;
|
||||
|
||||
/* if we already have a perfect match, we're done */
|
||||
if (!inode_in_dir(root, path, btrfs_ino(BTRFS_I(dir)),
|
||||
btrfs_ino(BTRFS_I(inode)), ref_index,
|
||||
name, namelen)) {
|
||||
ret = inode_in_dir(root, path, btrfs_ino(BTRFS_I(dir)),
|
||||
btrfs_ino(BTRFS_I(inode)), ref_index,
|
||||
name, namelen);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
} else if (ret == 0) {
|
||||
/*
|
||||
* look for a conflicting back reference in the
|
||||
* metadata. if we find one we have to unlink that name
|
||||
|
@ -1561,6 +1571,7 @@ static noinline int add_inode_ref(struct btrfs_trans_handle *trans,
|
|||
|
||||
btrfs_update_inode(trans, root, inode);
|
||||
}
|
||||
/* Else, ret == 1, we already have a perfect match, we're done. */
|
||||
|
||||
ref_ptr = (unsigned long)(ref_ptr + ref_struct_size) + namelen;
|
||||
kfree(name);
|
||||
|
|
Loading…
Reference in a new issue