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 * ...@@ -3030,7 +3030,7 @@ struct btrfs_dir_item *
btrfs_lookup_dir_index_item(struct btrfs_trans_handle *trans, btrfs_lookup_dir_index_item(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct btrfs_root *root,
struct btrfs_path *path, u64 dir, struct btrfs_path *path, u64 dir,
u64 objectid, const char *name, int name_len, u64 index, const char *name, int name_len,
int mod); int mod);
struct btrfs_dir_item * struct btrfs_dir_item *
btrfs_search_dir_index_item(struct btrfs_root *root, btrfs_search_dir_index_item(struct btrfs_root *root,
......
...@@ -190,9 +190,20 @@ static struct btrfs_dir_item *btrfs_lookup_match_dir( ...@@ -190,9 +190,20 @@ static struct btrfs_dir_item *btrfs_lookup_match_dir(
} }
/* /*
* lookup a directory item based on name. 'dir' is the objectid * Lookup for a directory item by name.
* 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) * @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_dir_item *btrfs_lookup_dir_item(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct btrfs_root *root,
...@@ -273,27 +284,42 @@ int btrfs_check_dir_item_collision(struct btrfs_root *root, u64 dir, ...@@ -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 * Lookup for a directory index item by name and index number.
* 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)
* *
* The name is used to make sure the index really points to the name you were * @trans: The transaction handle to use. Can be NULL if @mod is 0.
* looking for. * @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 * struct btrfs_dir_item *
btrfs_lookup_dir_index_item(struct btrfs_trans_handle *trans, btrfs_lookup_dir_index_item(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct btrfs_root *root,
struct btrfs_path *path, u64 dir, struct btrfs_path *path, u64 dir,
u64 objectid, const char *name, int name_len, u64 index, const char *name, int name_len,
int mod) int mod)
{ {
struct btrfs_dir_item *di;
struct btrfs_key key; struct btrfs_key key;
key.objectid = dir; key.objectid = dir;
key.type = BTRFS_DIR_INDEX_KEY; 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 * struct btrfs_dir_item *
......
...@@ -4859,6 +4859,7 @@ struct extent_buffer *btrfs_alloc_tree_block(struct btrfs_trans_handle *trans, ...@@ -4859,6 +4859,7 @@ struct extent_buffer *btrfs_alloc_tree_block(struct btrfs_trans_handle *trans,
out_free_delayed: out_free_delayed:
btrfs_free_delayed_extent_op(extent_op); btrfs_free_delayed_extent_op(extent_op);
out_free_buf: out_free_buf:
btrfs_tree_unlock(buf);
free_extent_buffer(buf); free_extent_buffer(buf);
out_free_reserved: out_free_reserved:
btrfs_free_reserved_extent(fs_info, ins.objectid, ins.offset, 0); btrfs_free_reserved_extent(fs_info, ins.objectid, ins.offset, 0);
......
...@@ -734,8 +734,7 @@ int btrfs_drop_extents(struct btrfs_trans_handle *trans, ...@@ -734,8 +734,7 @@ int btrfs_drop_extents(struct btrfs_trans_handle *trans,
if (args->start >= inode->disk_i_size && !args->replace_extent) if (args->start >= inode->disk_i_size && !args->replace_extent)
modify_tree = 0; modify_tree = 0;
update_refs = (test_bit(BTRFS_ROOT_SHAREABLE, &root->state) || update_refs = (root->root_key.objectid != BTRFS_TREE_LOG_OBJECTID);
root == fs_info->tree_root);
while (1) { while (1) {
recow = 0; recow = 0;
ret = btrfs_lookup_file_extent(trans, root, path, ino, ret = btrfs_lookup_file_extent(trans, root, path, ino,
...@@ -2704,14 +2703,16 @@ int btrfs_replace_file_extents(struct btrfs_inode *inode, ...@@ -2704,14 +2703,16 @@ int btrfs_replace_file_extents(struct btrfs_inode *inode,
drop_args.bytes_found); drop_args.bytes_found);
if (ret != -ENOSPC) { if (ret != -ENOSPC) {
/* /*
* When cloning we want to avoid transaction aborts when * The only time we don't want to abort is if we are
* nothing was done and we are attempting to clone parts * attempting to clone a partial inline extent, in which
* of inline extents, in such cases -EOPNOTSUPP is * case we'll get EOPNOTSUPP. However if we aren't
* returned by __btrfs_drop_extents() without having * clone we need to abort no matter what, because if we
* changed anything in the file. * got EOPNOTSUPP via prealloc then we messed up and
* need to abort.
*/ */
if (extent_info && !extent_info->is_new_extent && if (ret &&
ret && ret != -EOPNOTSUPP) (ret != -EOPNOTSUPP ||
(extent_info && extent_info->is_new_extent)))
btrfs_abort_transaction(trans, ret); btrfs_abort_transaction(trans, ret);
break; break;
} }
......
...@@ -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,34 @@ static noinline int inode_in_dir(struct btrfs_root *root, ...@@ -950,29 +952,34 @@ 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)) {
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;
} }
/* /*
...@@ -1182,7 +1189,9 @@ static inline int __add_inode_ref(struct btrfs_trans_handle *trans, ...@@ -1182,7 +1189,9 @@ static inline int __add_inode_ref(struct btrfs_trans_handle *trans,
/* look for a conflicting sequence number */ /* look for a conflicting sequence number */
di = btrfs_lookup_dir_index_item(trans, root, path, btrfs_ino(dir), di = btrfs_lookup_dir_index_item(trans, root, path, btrfs_ino(dir),
ref_index, name, namelen, 0); 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); ret = drop_one_dir_item(trans, root, path, dir, di);
if (ret) if (ret)
return ret; return ret;
...@@ -1192,7 +1201,9 @@ static inline int __add_inode_ref(struct btrfs_trans_handle *trans, ...@@ -1192,7 +1201,9 @@ static inline int __add_inode_ref(struct btrfs_trans_handle *trans,
/* look for a conflicting name */ /* look for a conflicting name */
di = btrfs_lookup_dir_item(trans, root, path, btrfs_ino(dir), di = btrfs_lookup_dir_item(trans, root, path, btrfs_ino(dir),
name, namelen, 0); 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); ret = drop_one_dir_item(trans, root, path, dir, di);
if (ret) if (ret)
return ret; return ret;
...@@ -1517,10 +1528,12 @@ static noinline int add_inode_ref(struct btrfs_trans_handle *trans, ...@@ -1517,10 +1528,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 +1593,7 @@ static noinline int add_inode_ref(struct btrfs_trans_handle *trans, ...@@ -1580,6 +1593,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);
...@@ -1936,8 +1950,8 @@ static noinline int replay_one_name(struct btrfs_trans_handle *trans, ...@@ -1936,8 +1950,8 @@ static noinline int replay_one_name(struct btrfs_trans_handle *trans,
struct btrfs_key log_key; struct btrfs_key log_key;
struct inode *dir; struct inode *dir;
u8 log_type; u8 log_type;
int exists; bool exists;
int ret = 0; int ret;
bool update_size = (key->type == BTRFS_DIR_INDEX_KEY); bool update_size = (key->type == BTRFS_DIR_INDEX_KEY);
bool name_added = false; bool name_added = false;
...@@ -1957,12 +1971,12 @@ static noinline int replay_one_name(struct btrfs_trans_handle *trans, ...@@ -1957,12 +1971,12 @@ static noinline int replay_one_name(struct btrfs_trans_handle *trans,
name_len); name_len);
btrfs_dir_item_key_to_cpu(eb, di, &log_key); btrfs_dir_item_key_to_cpu(eb, di, &log_key);
exists = btrfs_lookup_inode(trans, root, path, &log_key, 0); ret = btrfs_lookup_inode(trans, root, path, &log_key, 0);
if (exists == 0)
exists = 1;
else
exists = 0;
btrfs_release_path(path); btrfs_release_path(path);
if (ret < 0)
goto out;
exists = (ret == 0);
ret = 0;
if (key->type == BTRFS_DIR_ITEM_KEY) { if (key->type == BTRFS_DIR_ITEM_KEY) {
dst_di = btrfs_lookup_dir_item(trans, root, path, key->objectid, 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, ...@@ -1977,7 +1991,11 @@ static noinline int replay_one_name(struct btrfs_trans_handle *trans,
ret = -EINVAL; ret = -EINVAL;
goto out; 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 /* we need a sequence number to insert, so we only
* do inserts for the BTRFS_DIR_INDEX_KEY types * 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, ...@@ -2281,7 +2299,7 @@ static noinline int check_item_in_log(struct btrfs_trans_handle *trans,
dir_key->offset, dir_key->offset,
name, name_len, 0); 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_dir_item_key_to_cpu(eb, di, &location);
btrfs_release_path(path); btrfs_release_path(path);
btrfs_release_path(log_path); btrfs_release_path(log_path);
...@@ -3540,8 +3558,7 @@ int btrfs_del_dir_entries_in_log(struct btrfs_trans_handle *trans, ...@@ -3540,8 +3558,7 @@ int btrfs_del_dir_entries_in_log(struct btrfs_trans_handle *trans,
if (err == -ENOSPC) { if (err == -ENOSPC) {
btrfs_set_log_full_commit(trans); btrfs_set_log_full_commit(trans);
err = 0; err = 0;
} else if (err < 0 && err != -ENOENT) { } else if (err < 0) {
/* ENOENT can be returned if the entry hasn't been fsynced yet */
btrfs_abort_transaction(trans, err); 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