Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f03a8a85 authored by Filipe Manana's avatar Filipe Manana Committed by Greg Kroah-Hartman
Browse files

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



[ 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: default avatarFilipe Manana <fdmanana@suse.com>
Reviewed-by: default avatarDavid Sterba <dsterba@suse.com>
Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
Signed-off-by: default avatarSasha Levin <sashal@kernel.org>
parent 4eda4cf2
Loading
Loading
Loading
Loading
+29 −18
Original line number Diff line number Diff line
@@ -881,9 +881,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,
@@ -892,29 +894,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;
}

/*
@@ -1421,10 +1429,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)),
		ret = inode_in_dir(root, path, btrfs_ino(BTRFS_I(dir)),
				   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
			 * metadata. if we find one we have to unlink that name
@@ -1483,6 +1493,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);