Commit 77a5b9e3 authored by Filipe Manana's avatar Filipe Manana Committed by David Sterba

btrfs: deal with errors when checking if a dir entry exists during log replay

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: default avatarFilipe Manana <fdmanana@suse.com>
Reviewed-by: default avatarDavid Sterba <dsterba@suse.com>
Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
parent d175209b
...@@ -939,9 +939,11 @@ static noinline int drop_one_dir_item(struct btrfs_trans_handle *trans, ...@@ -939,9 +939,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 * See if a given name and sequence number found in an inode back reference are
* in an inode back reference are already in a directory and correctly * already in a directory and correctly point to this inode.
* 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, static noinline int inode_in_dir(struct btrfs_root *root,
struct btrfs_path *path, struct btrfs_path *path,
...@@ -950,29 +952,35 @@ static noinline int inode_in_dir(struct btrfs_root *root, ...@@ -950,29 +952,35 @@ static noinline int inode_in_dir(struct btrfs_root *root,
{ {
struct btrfs_dir_item *di; struct btrfs_dir_item *di;
struct btrfs_key location; struct btrfs_key location;
int match = 0; int ret = 0;
di = btrfs_lookup_dir_index_item(NULL, root, path, dirid, di = btrfs_lookup_dir_index_item(NULL, root, path, dirid,
index, name, name_len, 0); 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); btrfs_dir_item_key_to_cpu(path->nodes[0], di, &location);
if (location.objectid != objectid) if (location.objectid != objectid)
goto out; goto out;
} else } else {
goto out; goto out;
btrfs_release_path(path); }
btrfs_release_path(path);
di = btrfs_lookup_dir_item(NULL, root, path, dirid, name, name_len, 0); di = btrfs_lookup_dir_item(NULL, root, path, dirid, name, name_len, 0);
if (di && !IS_ERR(di)) { if (IS_ERR(di)) {
btrfs_dir_item_key_to_cpu(path->nodes[0], di, &location); ret = PTR_ERR(di);
if (location.objectid != objectid)
goto out;
} else
goto out; 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: out:
btrfs_release_path(path); btrfs_release_path(path);
return match; return ret;
} }
/* /*
...@@ -1517,10 +1525,12 @@ static noinline int add_inode_ref(struct btrfs_trans_handle *trans, ...@@ -1517,10 +1525,12 @@ static noinline int add_inode_ref(struct btrfs_trans_handle *trans,
if (ret) if (ret)
goto out; goto out;
/* if we already have a perfect match, we're done */ ret = inode_in_dir(root, path, btrfs_ino(BTRFS_I(dir)),
if (!inode_in_dir(root, path, btrfs_ino(BTRFS_I(dir)), btrfs_ino(BTRFS_I(inode)), ref_index,
btrfs_ino(BTRFS_I(inode)), ref_index, name, namelen);
name, namelen)) { if (ret < 0) {
goto out;
} else if (ret == 0) {
/* /*
* look for a conflicting back reference in the * look for a conflicting back reference in the
* metadata. if we find one we have to unlink that name * metadata. if we find one we have to unlink that name
...@@ -1580,6 +1590,7 @@ static noinline int add_inode_ref(struct btrfs_trans_handle *trans, ...@@ -1580,6 +1590,7 @@ static noinline int add_inode_ref(struct btrfs_trans_handle *trans,
if (ret) if (ret)
goto out; goto out;
} }
/* Else, ret == 1, we already have a perfect match, we're done. */
ref_ptr = (unsigned long)(ref_ptr + ref_struct_size) + namelen; ref_ptr = (unsigned long)(ref_ptr + ref_struct_size) + namelen;
kfree(name); kfree(name);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment