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 Original line Diff line number Diff line
@@ -2401,7 +2401,7 @@ static int btrfs_search_path_in_tree_user(struct inode *inode,
	unsigned long item_len;
	unsigned long item_len;
	struct btrfs_inode_ref *iref;
	struct btrfs_inode_ref *iref;
	struct btrfs_root_ref *rref;
	struct btrfs_root_ref *rref;
	struct btrfs_root *root;
	struct btrfs_root *root = NULL;
	struct btrfs_path *path;
	struct btrfs_path *path;
	struct btrfs_key key, key2;
	struct btrfs_key key, key2;
	struct extent_buffer *leaf;
	struct extent_buffer *leaf;
@@ -2431,6 +2431,10 @@ static int btrfs_search_path_in_tree_user(struct inode *inode,
			ret = PTR_ERR(root);
			ret = PTR_ERR(root);
			goto out;
			goto out;
		}
		}
		if (!btrfs_grab_fs_root(root)) {
			ret = -ENOENT;
			goto out;
		}


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


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


			leaf = path->nodes[0];
			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);
			btrfs_item_key_to_cpu(leaf, &key2, slot);
			if (key2.objectid != dirid) {
			if (key2.objectid != dirid) {
				ret = -ENOENT;
				ret = -ENOENT;
				goto out;
				goto out_put;
			}
			}


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


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


			btrfs_release_path(path);
			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);
		memmove(args->path, ptr, total_len);
		args->path[total_len] = '\0';
		args->path[total_len] = '\0';
		btrfs_put_fs_root(root);
		root = NULL;
		btrfs_release_path(path);
		btrfs_release_path(path);
	}
	}


	/* Get the bottom subvolume's name from ROOT_REF */
	/* Get the bottom subvolume's name from ROOT_REF */
	root = fs_info->tree_root;
	key.objectid = treeid;
	key.objectid = treeid;
	key.type = BTRFS_ROOT_REF_KEY;
	key.type = BTRFS_ROOT_REF_KEY;
	key.offset = args->treeid;
	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) {
	if (ret < 0) {
		goto out;
		goto out;
	} else if (ret > 0) {
	} 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);
	read_extent_buffer(leaf, args->name, item_off, item_len);
	args->name[item_len] = 0;
	args->name[item_len] = 0;


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