| From ce911a5a7e47ac44b5f701ecbc2a808dcd75bdb7 Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Fri, 1 Oct 2021 13:52:30 +0100 |
| Subject: btrfs: deal with errors when checking if a dir entry exists during |
| log replay |
| |
| From: Filipe Manana <fdmanana@suse.com> |
| |
| [ Upstream commit 77a5b9e3d14cbce49ceed2766b2003c034c066dc ] |
| |
| 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> |
| --- |
| fs/btrfs/tree-log.c | 47 ++++++++++++++++++++++++++++----------------- |
| 1 file changed, 29 insertions(+), 18 deletions(-) |
| |
| diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c |
| index c3bb5c4375ab..3b93a98fd544 100644 |
| --- a/fs/btrfs/tree-log.c |
| +++ b/fs/btrfs/tree-log.c |
| @@ -894,9 +894,11 @@ out: |
| } |
| |
| /* |
| - * 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, |
| @@ -905,29 +907,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; |
| } |
| |
| /* |
| @@ -1477,10 +1485,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 |
| @@ -1538,6 +1548,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); |
| -- |
| 2.33.0 |
| |