Commit b8a49ae1 authored by Josef Bacik's avatar Josef Bacik Committed by David Sterba
Browse files

btrfs: hold a ref on the root in btrfs_search_path_in_tree_user



We can wander into a different root, so grab a ref on the root we look
up.  Later on we make root = fs_info->tree_root so we need this separate
out label to make sure we do the right cleanup only in the case we're
looking up a different root.

Signed-off-by: default avatarJosef Bacik <josef@toxicpanda.com>
Reviewed-by: default avatarDavid Sterba <dsterba@suse.com>
Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
parent 88234012
Loading
Loading
Loading
Loading
+20 −13
Original line number Diff line number Diff line
@@ -2401,7 +2401,7 @@ static int btrfs_search_path_in_tree_user(struct inode *inode,
	unsigned long item_len;
	struct btrfs_inode_ref *iref;
	struct btrfs_root_ref *rref;
	struct btrfs_root *root;
	struct btrfs_root *root = NULL;
	struct btrfs_path *path;
	struct btrfs_key key, key2;
	struct extent_buffer *leaf;
@@ -2431,6 +2431,10 @@ static int btrfs_search_path_in_tree_user(struct inode *inode,
			ret = PTR_ERR(root);
			goto out;
		}
		if (!btrfs_grab_fs_root(root)) {
			ret = -ENOENT;
			goto out;
		}

		key.objectid = dirid;
		key.type = BTRFS_INODE_REF_KEY;
@@ -2438,15 +2442,15 @@ static int btrfs_search_path_in_tree_user(struct inode *inode,
		while (1) {
			ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
			if (ret < 0) {
				goto out;
				goto out_put;
			} else if (ret > 0) {
				ret = btrfs_previous_item(root, path, dirid,
							  BTRFS_INODE_REF_KEY);
				if (ret < 0) {
					goto out;
					goto out_put;
				} else if (ret > 0) {
					ret = -ENOENT;
					goto out;
					goto out_put;
				}
			}

@@ -2460,7 +2464,7 @@ static int btrfs_search_path_in_tree_user(struct inode *inode,
			total_len += len + 1;
			if (ptr < args->path) {
				ret = -ENAMETOOLONG;
				goto out;
				goto out_put;
			}

			*(ptr + len) = '/';
@@ -2471,10 +2475,10 @@ static int btrfs_search_path_in_tree_user(struct inode *inode,
			ret = btrfs_previous_item(root, path, dirid,
						  BTRFS_INODE_ITEM_KEY);
			if (ret < 0) {
				goto out;
				goto out_put;
			} else if (ret > 0) {
				ret = -ENOENT;
				goto out;
				goto out_put;
			}

			leaf = path->nodes[0];
@@ -2482,26 +2486,26 @@ static int btrfs_search_path_in_tree_user(struct inode *inode,
			btrfs_item_key_to_cpu(leaf, &key2, slot);
			if (key2.objectid != dirid) {
				ret = -ENOENT;
				goto out;
				goto out_put;
			}

			temp_inode = btrfs_iget(sb, &key2, root);
			if (IS_ERR(temp_inode)) {
				ret = PTR_ERR(temp_inode);
				goto out;
				goto out_put;
			}
			ret = inode_permission(temp_inode, MAY_READ | MAY_EXEC);
			iput(temp_inode);
			if (ret) {
				ret = -EACCES;
				goto out;
				goto out_put;
			}

			if (key.offset == upper_limit.objectid)
				break;
			if (key.objectid == BTRFS_FIRST_FREE_OBJECTID) {
				ret = -EACCES;
				goto out;
				goto out_put;
			}

			btrfs_release_path(path);
@@ -2512,15 +2516,16 @@ static int btrfs_search_path_in_tree_user(struct inode *inode,

		memmove(args->path, ptr, total_len);
		args->path[total_len] = '\0';
		btrfs_put_fs_root(root);
		root = NULL;
		btrfs_release_path(path);
	}

	/* Get the bottom subvolume's name from ROOT_REF */
	root = fs_info->tree_root;
	key.objectid = treeid;
	key.type = BTRFS_ROOT_REF_KEY;
	key.offset = args->treeid;
	ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
	ret = btrfs_search_slot(NULL, fs_info->tree_root, &key, path, 0, 0);
	if (ret < 0) {
		goto out;
	} else if (ret > 0) {
@@ -2547,6 +2552,8 @@ static int btrfs_search_path_in_tree_user(struct inode *inode,
	read_extent_buffer(leaf, args->name, item_off, item_len);
	args->name[item_len] = 0;

out_put:
	btrfs_put_fs_root(root);
out:
	btrfs_free_path(path);
	return ret;