summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Brown <broonie@kernel.org>2026-07-06 13:00:28 +0100
committerMark Brown <broonie@kernel.org>2026-07-06 13:00:28 +0100
commit3bb6ddfe8c02f3acdacc0964715e832b821fea18 (patch)
treef99527273e385fe4e15e78b54e58f793ef66a6d8
parent8cdeaa50eae8dad34885515f62559ee83e7e8dda (diff)
parent6c893b948351d42cfc3761cc746ab5b3d03ee7f3 (diff)
downloadlinux-next-3bb6ddfe8c02f3acdacc0964715e832b821fea18.tar.gz
linux-next-3bb6ddfe8c02f3acdacc0964715e832b821fea18.zip
Merge branch 'next-fixes' of https://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux.git
-rw-r--r--fs/btrfs/file-item.c26
-rw-r--r--fs/btrfs/free-space-cache.c3
-rw-r--r--fs/btrfs/ioctl.c18
-rw-r--r--fs/btrfs/lzo.c15
-rw-r--r--fs/btrfs/print-tree.c8
-rw-r--r--fs/btrfs/props.c16
-rw-r--r--fs/btrfs/relocation.c15
-rw-r--r--fs/btrfs/tree-checker.c6
8 files changed, 82 insertions, 25 deletions
diff --git a/fs/btrfs/file-item.c b/fs/btrfs/file-item.c
index 9f6454e9db81..cf50fd623f41 100644
--- a/fs/btrfs/file-item.c
+++ b/fs/btrfs/file-item.c
@@ -358,6 +358,7 @@ int btrfs_lookup_bio_sums(struct btrfs_bio *bbio)
const unsigned int nblocks = orig_len >> fs_info->sectorsize_bits;
int ret = 0;
u32 bio_offset = 0;
+ bool using_commit_root = false;
if ((inode->flags & BTRFS_INODE_NODATASUM) ||
test_bit(BTRFS_FS_STATE_NO_DATA_CSUMS, &fs_info->fs_state))
@@ -431,6 +432,7 @@ int btrfs_lookup_bio_sums(struct btrfs_bio *bbio)
* from across transactions.
*/
if (bbio->csum_search_commit_root) {
+ using_commit_root = true;
path->search_commit_root = true;
path->skip_locking = true;
down_read(&fs_info->commit_root_sem);
@@ -463,6 +465,28 @@ int btrfs_lookup_bio_sums(struct btrfs_bio *bbio)
* assume this is the case.
*/
if (count == 0) {
+ /*
+ * If an extent is relocated in the current transaction
+ * then relocation writes a new csum without updating
+ * the extent map generation. Until the next commit, we
+ * will see a hole in that case, so we need to fallback
+ * to searching the transaction csum root.
+ *
+ * Note that a commit root lookup of a referenced extent can
+ * only miss, not return a stale csum. A freed extent's csum
+ * is deleted in the same transaction and its bytenr is not
+ * reusable until that transaction has committed and the
+ * extent is unpinned.
+ */
+ if (using_commit_root) {
+ up_read(&fs_info->commit_root_sem);
+ using_commit_root = false;
+ path->search_commit_root = false;
+ path->skip_locking = false;
+ btrfs_release_path(path);
+ continue;
+ }
+
memset(csum_dst, 0, csum_size);
count = 1;
@@ -481,7 +505,7 @@ int btrfs_lookup_bio_sums(struct btrfs_bio *bbio)
bio_offset += count * sectorsize;
}
- if (bbio->csum_search_commit_root)
+ if (using_commit_root)
up_read(&fs_info->commit_root_sem);
return ret;
}
diff --git a/fs/btrfs/free-space-cache.c b/fs/btrfs/free-space-cache.c
index 6009b1477232..e2af75a205ea 100644
--- a/fs/btrfs/free-space-cache.c
+++ b/fs/btrfs/free-space-cache.c
@@ -551,6 +551,9 @@ static int io_ctl_check_crc(struct btrfs_io_ctl *io_ctl, int index)
u32 crc = ~(u32)0;
unsigned offset = 0;
+ if (index >= io_ctl->num_pages)
+ return -EIO;
+
if (index == 0)
offset = sizeof(u32) * io_ctl->num_pages;
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 9d47d16394fc..81e87bc39828 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -289,6 +289,7 @@ int btrfs_fileattr_set(struct mnt_idmap *idmap,
int ret;
const char *comp = NULL;
u32 inode_flags;
+ bool prop_set = false;
if (btrfs_root_readonly(root))
return -EROFS;
@@ -401,16 +402,15 @@ int btrfs_fileattr_set(struct mnt_idmap *idmap,
if (comp) {
ret = btrfs_set_prop(trans, inode, "btrfs.compression",
comp, strlen(comp), 0);
- if (unlikely(ret)) {
- btrfs_abort_transaction(trans, ret);
+ if (ret)
goto out_end_trans;
- }
+ prop_set = true;
} else {
ret = btrfs_set_prop(trans, inode, "btrfs.compression", NULL, 0, 0);
- if (unlikely(ret && ret != -ENODATA)) {
- btrfs_abort_transaction(trans, ret);
+ prop_set = (ret == 0);
+ /* If ret == -ENODATA ignore and proceed to update inode item. */
+ if (ret && ret != -ENODATA)
goto out_end_trans;
- }
}
update_flags:
@@ -420,6 +420,12 @@ update_flags:
inode_inc_iversion(&inode->vfs_inode);
inode_set_ctime_current(&inode->vfs_inode);
ret = btrfs_update_inode(trans, inode);
+ /*
+ * If we set a property or deleted one, we must abort if we fail to
+ * update the inode, to avoid persisting an inconsistent state.
+ */
+ if (unlikely(ret && prop_set))
+ btrfs_abort_transaction(trans, ret);
out_end_trans:
btrfs_end_transaction(trans);
diff --git a/fs/btrfs/lzo.c b/fs/btrfs/lzo.c
index 6e4aa22853ab..1531adb117d1 100644
--- a/fs/btrfs/lzo.c
+++ b/fs/btrfs/lzo.c
@@ -552,17 +552,26 @@ int lzo_decompress(struct list_head *ws, const u8 *data_in,
size_t max_segment_len = workspace_buf_length(fs_info);
int ret;
- if (unlikely(srclen < LZO_LEN || srclen > max_segment_len + LZO_LEN * 2))
+ if (unlikely(srclen < LZO_LEN || srclen > max_segment_len + LZO_LEN * 2)) {
+ btrfs_err(fs_info, "invalid lzo header length, has %zu expect (%u, %zu)",
+ srclen, LZO_LEN, max_segment_len + LZO_LEN * 2);
return -EUCLEAN;
+ }
in_len = get_unaligned_le32(data_in);
- if (unlikely(in_len != srclen))
+ if (unlikely(in_len != srclen)) {
+ btrfs_err(fs_info, "invalid lzo header length, has %zu expect %zu",
+ in_len, srclen);
return -EUCLEAN;
+ }
data_in += LZO_LEN;
in_len = get_unaligned_le32(data_in);
- if (unlikely(in_len != srclen - LZO_LEN * 2))
+ if (unlikely(in_len != srclen - LZO_LEN * 2)) {
+ btrfs_err(fs_info, "invalid lzo segment length, has %zu expect %zu",
+ in_len, srclen - LZO_LEN * 2);
return -EUCLEAN;
+ }
data_in += LZO_LEN;
out_len = sectorsize;
diff --git a/fs/btrfs/print-tree.c b/fs/btrfs/print-tree.c
index 87e60a2d4bd8..53e726119ca7 100644
--- a/fs/btrfs/print-tree.c
+++ b/fs/btrfs/print-tree.c
@@ -449,9 +449,9 @@ void btrfs_print_leaf(const struct extent_buffer *l)
nr = btrfs_header_nritems(l);
btrfs_info(fs_info,
- "leaf %llu gen %llu total ptrs %d free space %d owner %llu",
+ "leaf %llu gen %llu total ptrs %d free space %d owner %lld",
btrfs_header_bytenr(l), btrfs_header_generation(l), nr,
- btrfs_leaf_free_space(l), btrfs_header_owner(l));
+ btrfs_leaf_free_space(l), (s64)btrfs_header_owner(l));
print_eb_refs_lock(l);
for (i = 0 ; i < nr ; i++) {
char key_buf[KEY_TYPE_BUF_SIZE];
@@ -600,10 +600,10 @@ void btrfs_print_tree(const struct extent_buffer *c, bool follow)
return;
}
btrfs_info(fs_info,
- "node %llu level %d gen %llu total ptrs %d free spc %u owner %llu",
+ "node %llu level %d gen %llu total ptrs %d free spc %u owner %lld",
btrfs_header_bytenr(c), level, btrfs_header_generation(c),
nr, (u32)BTRFS_NODEPTRS_PER_BLOCK(fs_info) - nr,
- btrfs_header_owner(c));
+ (s64)btrfs_header_owner(c));
print_eb_refs_lock(c);
for (i = 0; i < nr; i++) {
btrfs_node_key_to_cpu(c, &key, i);
diff --git a/fs/btrfs/props.c b/fs/btrfs/props.c
index adc956432d2f..bb77d46376d4 100644
--- a/fs/btrfs/props.c
+++ b/fs/btrfs/props.c
@@ -127,14 +127,24 @@ int btrfs_set_prop(struct btrfs_trans_handle *trans, struct btrfs_inode *inode,
return ret;
}
+ ret = handler->validate(inode, value, value_len);
+ if (ret)
+ return ret;
ret = btrfs_setxattr(trans, &inode->vfs_inode, handler->xattr_name, value,
value_len, flags);
if (ret)
return ret;
ret = handler->apply(inode, value, value_len);
- if (ret) {
- btrfs_setxattr(trans, &inode->vfs_inode, handler->xattr_name, NULL,
- 0, flags);
+ /* We validated before, so it should not fail here. */
+ ASSERT(ret == 0);
+ if (unlikely(ret)) {
+ int ret2;
+
+ /* Try to delete xattr, if not possible abort transaction. */
+ ret2 = btrfs_setxattr(trans, &inode->vfs_inode, handler->xattr_name,
+ NULL, 0, flags);
+ if (unlikely(ret2))
+ btrfs_abort_transaction(trans, ret2);
return ret;
}
diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c
index fb85bc8b345c..0d63d117db59 100644
--- a/fs/btrfs/relocation.c
+++ b/fs/btrfs/relocation.c
@@ -719,21 +719,19 @@ static struct btrfs_root *create_reloc_root(struct btrfs_trans_handle *trans,
ret = btrfs_insert_root(trans, fs_info->tree_root,
&root_key, root_item);
- if (ret)
- goto abort;
+ if (unlikely(ret)) {
+ btrfs_abort_transaction(trans, ret);
+ return ERR_PTR(ret);
+ }
reloc_root = btrfs_read_tree_root(fs_info->tree_root, &root_key);
if (IS_ERR(reloc_root)) {
- ret = PTR_ERR(reloc_root);
- goto abort;
+ btrfs_abort_transaction(trans, PTR_ERR(reloc_root));
+ return ERR_CAST(reloc_root);
}
set_bit(BTRFS_ROOT_SHAREABLE, &reloc_root->state);
btrfs_set_root_last_trans(reloc_root, trans->transid);
return reloc_root;
-
-abort:
- btrfs_abort_transaction(trans, ret);
- return ERR_PTR(ret);
}
/*
@@ -1912,6 +1910,7 @@ again:
* corruption, e.g. bad reloc tree key offset.
*/
ret = -EINVAL;
+ btrfs_put_root(root);
goto out;
}
ret = merge_reloc_root(rc, root);
diff --git a/fs/btrfs/tree-checker.c b/fs/btrfs/tree-checker.c
index cb3e676a81cc..0ce91396b517 100644
--- a/fs/btrfs/tree-checker.c
+++ b/fs/btrfs/tree-checker.c
@@ -1923,6 +1923,12 @@ static int check_inode_ref(struct extent_buffer *leaf,
iref = (struct btrfs_inode_ref *)ptr;
namelen = btrfs_inode_ref_name_len(leaf, iref);
+ if (unlikely(namelen == 0 || namelen > BTRFS_NAME_LEN)) {
+ inode_ref_err(leaf, slot,
+ "invalid inode ref name length, has %u expect [1, %u]",
+ namelen, BTRFS_NAME_LEN);
+ return -EUCLEAN;
+ }
if (unlikely(ptr + sizeof(*iref) + namelen > end)) {
inode_ref_err(leaf, slot,
"inode ref overflow, ptr %lu end %lu namelen %u",