Commit 1986c10a authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'for-5.15-rc5-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux

Pull btrfs fixes from David Sterba:
 "A few more error handling fixes, stemming from code inspection, error
  injection or fuzzing"

* tag 'for-5.15-rc5-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux:
  btrfs: fix abort logic in btrfs_replace_file_extents
  btrfs: check for error when looking up inode during dir entry replay
  btrfs: unify lookup return value when dir entry is missing
  btrfs: deal with errors when adding inode reference during log replay
  btrfs: deal with errors when replaying dir entry during log replay
  btrfs: deal with errors when checking if a dir entry exists during log replay
  btrfs: update refs for any root except tree log roots
  btrfs: unlock newly allocated extent buffer after error
parents 64570fbc 4afb912f
......@@ -3030,7 +3030,7 @@ struct btrfs_dir_item *
btrfs_lookup_dir_index_item(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct btrfs_path *path, u64 dir,
u64 objectid, const char *name, int name_len,
u64 index, const char *name, int name_len,
int mod);
struct btrfs_dir_item *
btrfs_search_dir_index_item(struct btrfs_root *root,
......
......@@ -190,9 +190,20 @@ static struct btrfs_dir_item *btrfs_lookup_match_dir(
}
/*
* lookup a directory item based on name. 'dir' is the objectid
* we're searching in, and 'mod' tells us if you plan on deleting the
* item (use mod < 0) or changing the options (use mod > 0)
* Lookup for a directory item by name.
*
* @trans: The transaction handle to use. Can be NULL if @mod is 0.
* @root: The root of the target tree.
* @path: Path to use for the search.
* @dir: The inode number (objectid) of the directory.
* @name: The name associated to the directory entry we are looking for.
* @name_len: The length of the name.
* @mod: Used to indicate if the tree search is meant for a read only
* lookup, for a modification lookup or for a deletion lookup, so
* its value should be 0, 1 or -1, respectively.
*
* Returns: NULL if the dir item does not exists, an error pointer if an error
* happened, or a pointer to a dir item if a dir item exists for the given name.
*/
struct btrfs_dir_item *btrfs_lookup_dir_item(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
......@@ -273,27 +284,42 @@ int btrfs_check_dir_item_collision(struct btrfs_root *root, u64 dir,
}
/*
* lookup a directory item based on index. 'dir' is the objectid
* we're searching in, and 'mod' tells us if you plan on deleting the
* item (use mod < 0) or changing the options (use mod > 0)
* Lookup for a directory index item by name and index number.
*
* The name is used to make sure the index really points to the name you were
* looking for.
* @trans: The transaction handle to use. Can be NULL if @mod is 0.
* @root: The root of the target tree.
* @path: Path to use for the search.
* @dir: The inode number (objectid) of the directory.
* @index: The index number.
* @name: The name associated to the directory entry we are looking for.
* @name_len: The length of the name.
* @mod: Used to indicate if the tree search is meant for a read only
* lookup, for a modification lookup or for a deletion lookup, so
* its value should be 0, 1 or -1, respectively.
*
* Returns: NULL if the dir index item does not exists, an error pointer if an
* error happened, or a pointer to a dir item if the dir index item exists and
* matches the criteria (name and index number).
*/
struct btrfs_dir_item *
btrfs_lookup_dir_index_item(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct btrfs_path *path, u64 dir,
u64 objectid, const char *name, int name_len,
u64 index, const char *name, int name_len,
int mod)
{
struct btrfs_dir_item *di;
struct btrfs_key key;
key.objectid = dir;
key.type = BTRFS_DIR_INDEX_KEY;
key.offset = objectid;
key.offset = index;
return btrfs_lookup_match_dir(trans, root, path, &key, name, name_len, mod);
di = btrfs_lookup_match_dir(trans, root, path, &key, name, name_len, mod);
if (di == ERR_PTR(-ENOENT))
return NULL;
return di;
}
struct btrfs_dir_item *
......
......@@ -4859,6 +4859,7 @@ struct extent_buffer *btrfs_alloc_tree_block(struct btrfs_trans_handle *trans,
out_free_delayed:
btrfs_free_delayed_extent_op(extent_op);
out_free_buf:
btrfs_tree_unlock(buf);
free_extent_buffer(buf);
out_free_reserved:
btrfs_free_reserved_extent(fs_info, ins.objectid, ins.offset, 0);
......
......@@ -734,8 +734,7 @@ int btrfs_drop_extents(struct btrfs_trans_handle *trans,
if (args->start >= inode->disk_i_size && !args->replace_extent)
modify_tree = 0;
update_refs = (test_bit(BTRFS_ROOT_SHAREABLE, &root->state) ||
root == fs_info->tree_root);
update_refs = (root->root_key.objectid != BTRFS_TREE_LOG_OBJECTID);
while (1) {
recow = 0;
ret = btrfs_lookup_file_extent(trans, root, path, ino,
......@@ -2704,14 +2703,16 @@ int btrfs_replace_file_extents(struct btrfs_inode *inode,
drop_args.bytes_found);
if (ret != -ENOSPC) {
/*
* When cloning we want to avoid transaction aborts when
* nothing was done and we are attempting to clone parts
* of inline extents, in such cases -EOPNOTSUPP is
* returned by __btrfs_drop_extents() without having
* changed anything in the file.
* The only time we don't want to abort is if we are
* attempting to clone a partial inline extent, in which
* case we'll get EOPNOTSUPP. However if we aren't
* clone we need to abort no matter what, because if we
* got EOPNOTSUPP via prealloc then we messed up and
* need to abort.
*/
if (extent_info && !extent_info->is_new_extent &&
ret && ret != -EOPNOTSUPP)
if (ret &&
(ret != -EOPNOTSUPP ||
(extent_info && extent_info->is_new_extent)))
btrfs_abort_transaction(trans, ret);
break;
}
......
......@@ -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
* 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,
......@@ -950,29 +952,34 @@ 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)) {
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;
}
/*
......@@ -1182,7 +1189,9 @@ static inline int __add_inode_ref(struct btrfs_trans_handle *trans,
/* look for a conflicting sequence number */
di = btrfs_lookup_dir_index_item(trans, root, path, btrfs_ino(dir),
ref_index, name, namelen, 0);
if (di && !IS_ERR(di)) {
if (IS_ERR(di)) {
return PTR_ERR(di);
} else if (di) {
ret = drop_one_dir_item(trans, root, path, dir, di);
if (ret)
return ret;
......@@ -1192,7 +1201,9 @@ static inline int __add_inode_ref(struct btrfs_trans_handle *trans,
/* look for a conflicting name */
di = btrfs_lookup_dir_item(trans, root, path, btrfs_ino(dir),
name, namelen, 0);
if (di && !IS_ERR(di)) {
if (IS_ERR(di)) {
return PTR_ERR(di);
} else if (di) {
ret = drop_one_dir_item(trans, root, path, dir, di);
if (ret)
return ret;
......@@ -1517,10 +1528,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
......@@ -1580,6 +1593,7 @@ static noinline int add_inode_ref(struct btrfs_trans_handle *trans,
if (ret)
goto out;
}
/* Else, ret == 1, we already have a perfect match, we're done. */
ref_ptr = (unsigned long)(ref_ptr + ref_struct_size) + namelen;
kfree(name);
......@@ -1936,8 +1950,8 @@ static noinline int replay_one_name(struct btrfs_trans_handle *trans,
struct btrfs_key log_key;
struct inode *dir;
u8 log_type;
int exists;
int ret = 0;
bool exists;
int ret;
bool update_size = (key->type == BTRFS_DIR_INDEX_KEY);
bool name_added = false;
......@@ -1957,12 +1971,12 @@ static noinline int replay_one_name(struct btrfs_trans_handle *trans,
name_len);
btrfs_dir_item_key_to_cpu(eb, di, &log_key);
exists = btrfs_lookup_inode(trans, root, path, &log_key, 0);
if (exists == 0)
exists = 1;
else
exists = 0;
ret = btrfs_lookup_inode(trans, root, path, &log_key, 0);
btrfs_release_path(path);
if (ret < 0)
goto out;
exists = (ret == 0);
ret = 0;
if (key->type == BTRFS_DIR_ITEM_KEY) {
dst_di = btrfs_lookup_dir_item(trans, root, path, key->objectid,
......@@ -1977,7 +1991,11 @@ static noinline int replay_one_name(struct btrfs_trans_handle *trans,
ret = -EINVAL;
goto out;
}
if (IS_ERR_OR_NULL(dst_di)) {
if (IS_ERR(dst_di)) {
ret = PTR_ERR(dst_di);
goto out;
} else if (!dst_di) {
/* we need a sequence number to insert, so we only
* do inserts for the BTRFS_DIR_INDEX_KEY types
*/
......@@ -2281,7 +2299,7 @@ static noinline int check_item_in_log(struct btrfs_trans_handle *trans,
dir_key->offset,
name, name_len, 0);
}
if (!log_di || log_di == ERR_PTR(-ENOENT)) {
if (!log_di) {
btrfs_dir_item_key_to_cpu(eb, di, &location);
btrfs_release_path(path);
btrfs_release_path(log_path);
......@@ -3540,8 +3558,7 @@ int btrfs_del_dir_entries_in_log(struct btrfs_trans_handle *trans,
if (err == -ENOSPC) {
btrfs_set_log_full_commit(trans);
err = 0;
} else if (err < 0 && err != -ENOENT) {
/* ENOENT can be returned if the entry hasn't been fsynced yet */
} else if (err < 0) {
btrfs_abort_transaction(trans, err);
}
......
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