diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-03-21 08:42:17 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-03-21 08:42:17 -0700 |
| commit | 8991448e56cb2118b561eeda193af53b4ff6b632 (patch) | |
| tree | a7ae3e706dda9f4a4a2a4802ee4a3af3eb6e5b4b | |
| parent | a0c83177734ab98623795e1ba2cf4b72c23de5e7 (diff) | |
| parent | b17b79ff896305fd74980a5f72afec370ee88ca4 (diff) | |
Merge tag 'for-7.0-rc4-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux
Pull btrfs fixes from David Sterba:
"Another batch of fixes for problems that have been identified by tools
analyzing code or by fuzzing. Most of them are short, two patches fix
the same thing in many places so the diffs are bigger.
- handle potential NULL pointer errors after attempting to read
extent and checksum trees
- prevent ENOSPC when creating many qgroups by ioctls in the same
transaction
- encoded write ioctl fixes (with 64K page and 4K block size):
- fix unexpected bio length
- do not let compressed bios and pages interfere with page cache
- compression fixes on setups with 64K page and 4K block size: fix
folio length assertions (zstd and lzo)
- remap tree fixes:
- make sure to hold block group reference while moving it
- handle early exit when moving block group to unused list
- handle deleted subvolumes with inconsistent state of deletion
progress"
* tag 'for-7.0-rc4-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux:
btrfs: reject root items with drop_progress and zero drop_level
btrfs: check block group before marking it unused in balance_remap_chunks()
btrfs: hold block group reference during entire move_existing_remap()
btrfs: fix an incorrect ASSERT() condition inside lzo_decompress_bio()
btrfs: fix an incorrect ASSERT() condition inside zstd_decompress_bio()
btrfs: do not touch page cache for encoded writes
btrfs: fix a bug that makes encoded write bio larger than expected
btrfs: reserve enough transaction items for qgroup ioctls
btrfs: check for NULL root after calls to btrfs_csum_root()
btrfs: check for NULL root after calls to btrfs_extent_root()
| -rw-r--r-- | fs/btrfs/backref.c | 28 | ||||
| -rw-r--r-- | fs/btrfs/block-group.c | 36 | ||||
| -rw-r--r-- | fs/btrfs/compression.c | 11 | ||||
| -rw-r--r-- | fs/btrfs/disk-io.c | 20 | ||||
| -rw-r--r-- | fs/btrfs/extent-tree.c | 98 | ||||
| -rw-r--r-- | fs/btrfs/file-item.c | 7 | ||||
| -rw-r--r-- | fs/btrfs/free-space-tree.c | 9 | ||||
| -rw-r--r-- | fs/btrfs/inode.c | 25 | ||||
| -rw-r--r-- | fs/btrfs/ioctl.c | 12 | ||||
| -rw-r--r-- | fs/btrfs/lzo.c | 4 | ||||
| -rw-r--r-- | fs/btrfs/qgroup.c | 8 | ||||
| -rw-r--r-- | fs/btrfs/raid56.c | 12 | ||||
| -rw-r--r-- | fs/btrfs/relocation.c | 39 | ||||
| -rw-r--r-- | fs/btrfs/tree-checker.c | 17 | ||||
| -rw-r--r-- | fs/btrfs/tree-log.c | 21 | ||||
| -rw-r--r-- | fs/btrfs/volumes.c | 25 | ||||
| -rw-r--r-- | fs/btrfs/zoned.c | 7 | ||||
| -rw-r--r-- | fs/btrfs/zstd.c | 2 |
18 files changed, 341 insertions, 40 deletions
diff --git a/fs/btrfs/backref.c b/fs/btrfs/backref.c index af98421e7922..0428557fd77b 100644 --- a/fs/btrfs/backref.c +++ b/fs/btrfs/backref.c @@ -1393,6 +1393,13 @@ static int find_parent_nodes(struct btrfs_backref_walk_ctx *ctx, .indirect_missing_keys = PREFTREE_INIT }; + if (unlikely(!root)) { + btrfs_err(ctx->fs_info, + "missing extent root for extent at bytenr %llu", + ctx->bytenr); + return -EUCLEAN; + } + /* Roots ulist is not needed when using a sharedness check context. */ if (sc) ASSERT(ctx->roots == NULL); @@ -2204,6 +2211,13 @@ int extent_from_logical(struct btrfs_fs_info *fs_info, u64 logical, struct btrfs_extent_item *ei; struct btrfs_key key; + if (unlikely(!extent_root)) { + btrfs_err(fs_info, + "missing extent root for extent at bytenr %llu", + logical); + return -EUCLEAN; + } + key.objectid = logical; if (btrfs_fs_incompat(fs_info, SKINNY_METADATA)) key.type = BTRFS_METADATA_ITEM_KEY; @@ -2851,6 +2865,13 @@ int btrfs_backref_iter_start(struct btrfs_backref_iter *iter, u64 bytenr) struct btrfs_key key; int ret; + if (unlikely(!extent_root)) { + btrfs_err(fs_info, + "missing extent root for extent at bytenr %llu", + bytenr); + return -EUCLEAN; + } + key.objectid = bytenr; key.type = BTRFS_METADATA_ITEM_KEY; key.offset = (u64)-1; @@ -2987,6 +3008,13 @@ int btrfs_backref_iter_next(struct btrfs_backref_iter *iter) /* We're at keyed items, there is no inline item, go to the next one */ extent_root = btrfs_extent_root(iter->fs_info, iter->bytenr); + if (unlikely(!extent_root)) { + btrfs_err(iter->fs_info, + "missing extent root for extent at bytenr %llu", + iter->bytenr); + return -EUCLEAN; + } + ret = btrfs_next_item(extent_root, iter->path); if (ret) return ret; diff --git a/fs/btrfs/block-group.c b/fs/btrfs/block-group.c index 2a886bece810..ebf5079096af 100644 --- a/fs/btrfs/block-group.c +++ b/fs/btrfs/block-group.c @@ -739,6 +739,12 @@ static int load_extent_tree_free(struct btrfs_caching_control *caching_ctl) last = max_t(u64, block_group->start, BTRFS_SUPER_INFO_OFFSET); extent_root = btrfs_extent_root(fs_info, last); + if (unlikely(!extent_root)) { + btrfs_err(fs_info, + "missing extent root for block group at offset %llu", + block_group->start); + return -EUCLEAN; + } #ifdef CONFIG_BTRFS_DEBUG /* @@ -1061,6 +1067,11 @@ static int remove_block_group_item(struct btrfs_trans_handle *trans, int ret; root = btrfs_block_group_root(fs_info); + if (unlikely(!root)) { + btrfs_err(fs_info, "missing block group root"); + return -EUCLEAN; + } + key.objectid = block_group->start; key.type = BTRFS_BLOCK_GROUP_ITEM_KEY; key.offset = block_group->length; @@ -1349,6 +1360,11 @@ struct btrfs_trans_handle *btrfs_start_trans_remove_block_group( struct btrfs_chunk_map *map; unsigned int num_items; + if (unlikely(!root)) { + btrfs_err(fs_info, "missing block group root"); + return ERR_PTR(-EUCLEAN); + } + map = btrfs_find_chunk_map(fs_info, chunk_offset, 1); ASSERT(map != NULL); ASSERT(map->start == chunk_offset); @@ -2140,6 +2156,11 @@ static int find_first_block_group(struct btrfs_fs_info *fs_info, int ret; struct btrfs_key found_key; + if (unlikely(!root)) { + btrfs_err(fs_info, "missing block group root"); + return -EUCLEAN; + } + btrfs_for_each_slot(root, key, &found_key, path, ret) { if (found_key.objectid >= key->objectid && found_key.type == BTRFS_BLOCK_GROUP_ITEM_KEY) { @@ -2713,6 +2734,11 @@ static int insert_block_group_item(struct btrfs_trans_handle *trans, size_t size; int ret; + if (unlikely(!root)) { + btrfs_err(fs_info, "missing block group root"); + return -EUCLEAN; + } + spin_lock(&block_group->lock); btrfs_set_stack_block_group_v2_used(&bgi, block_group->used); btrfs_set_stack_block_group_v2_chunk_objectid(&bgi, block_group->global_root_id); @@ -3048,6 +3074,11 @@ int btrfs_inc_block_group_ro(struct btrfs_block_group *cache, int ret; bool dirty_bg_running; + if (unlikely(!root)) { + btrfs_err(fs_info, "missing block group root"); + return -EUCLEAN; + } + /* * This can only happen when we are doing read-only scrub on read-only * mount. @@ -3192,6 +3223,11 @@ static int update_block_group_item(struct btrfs_trans_handle *trans, u64 used, remap_bytes; u32 identity_remap_count; + if (unlikely(!root)) { + btrfs_err(fs_info, "missing block group root"); + return -EUCLEAN; + } + /* * Block group items update can be triggered out of commit transaction * critical section, thus we need a consistent view of used bytes. diff --git a/fs/btrfs/compression.c b/fs/btrfs/compression.c index 790518a8c803..85199944c1eb 100644 --- a/fs/btrfs/compression.c +++ b/fs/btrfs/compression.c @@ -320,10 +320,16 @@ void btrfs_submit_compressed_write(struct btrfs_ordered_extent *ordered, ASSERT(IS_ALIGNED(ordered->file_offset, fs_info->sectorsize)); ASSERT(IS_ALIGNED(ordered->num_bytes, fs_info->sectorsize)); - ASSERT(cb->writeback); + /* + * This flag determines if we should clear the writeback flag from the + * page cache. But this function is only utilized by encoded writes, it + * never goes through the page cache. + */ + ASSERT(!cb->writeback); cb->start = ordered->file_offset; cb->len = ordered->num_bytes; + ASSERT(cb->bbio.bio.bi_iter.bi_size == ordered->disk_num_bytes); cb->compressed_len = ordered->disk_num_bytes; cb->bbio.bio.bi_iter.bi_sector = ordered->disk_bytenr >> SECTOR_SHIFT; cb->bbio.ordered = ordered; @@ -345,8 +351,7 @@ struct compressed_bio *btrfs_alloc_compressed_write(struct btrfs_inode *inode, cb = alloc_compressed_bio(inode, start, REQ_OP_WRITE, end_bbio_compressed_write); cb->start = start; cb->len = len; - cb->writeback = true; - + cb->writeback = false; return cb; } diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 407830d86d0d..01f2dbb69832 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -1591,7 +1591,7 @@ static int find_newest_super_backup(struct btrfs_fs_info *info) * this will bump the backup pointer by one when it is * done */ -static void backup_super_roots(struct btrfs_fs_info *info) +static int backup_super_roots(struct btrfs_fs_info *info) { const int next_backup = info->backup_root_index; struct btrfs_root_backup *root_backup; @@ -1623,6 +1623,15 @@ static void backup_super_roots(struct btrfs_fs_info *info) struct btrfs_root *extent_root = btrfs_extent_root(info, 0); struct btrfs_root *csum_root = btrfs_csum_root(info, 0); + if (unlikely(!extent_root)) { + btrfs_err(info, "missing extent root for extent at bytenr 0"); + return -EUCLEAN; + } + if (unlikely(!csum_root)) { + btrfs_err(info, "missing csum root for extent at bytenr 0"); + return -EUCLEAN; + } + btrfs_set_backup_extent_root(root_backup, extent_root->node->start); btrfs_set_backup_extent_root_gen(root_backup, @@ -1670,6 +1679,8 @@ static void backup_super_roots(struct btrfs_fs_info *info) memcpy(&info->super_copy->super_roots, &info->super_for_commit->super_roots, sizeof(*root_backup) * BTRFS_NUM_BACKUP_ROOTS); + + return 0; } /* @@ -4051,8 +4062,11 @@ int write_all_supers(struct btrfs_fs_info *fs_info, int max_mirrors) * not from fsync where the tree roots in fs_info have not * been consistent on disk. */ - if (max_mirrors == 0) - backup_super_roots(fs_info); + if (max_mirrors == 0) { + ret = backup_super_roots(fs_info); + if (ret < 0) + return ret; + } sb = fs_info->super_for_commit; dev_item = &sb->dev_item; diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index b0d9baf5b412..85ee5c79759d 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -75,6 +75,12 @@ int btrfs_lookup_data_extent(struct btrfs_fs_info *fs_info, u64 start, u64 len) struct btrfs_key key; BTRFS_PATH_AUTO_FREE(path); + if (unlikely(!root)) { + btrfs_err(fs_info, + "missing extent root for extent at bytenr %llu", start); + return -EUCLEAN; + } + path = btrfs_alloc_path(); if (!path) return -ENOMEM; @@ -131,6 +137,12 @@ search_again: key.offset = offset; extent_root = btrfs_extent_root(fs_info, bytenr); + if (unlikely(!extent_root)) { + btrfs_err(fs_info, + "missing extent root for extent at bytenr %llu", bytenr); + return -EUCLEAN; + } + ret = btrfs_search_slot(NULL, extent_root, &key, path, 0, 0); if (ret < 0) return ret; @@ -436,6 +448,12 @@ static noinline int lookup_extent_data_ref(struct btrfs_trans_handle *trans, int recow; int ret; + if (unlikely(!root)) { + btrfs_err(trans->fs_info, + "missing extent root for extent at bytenr %llu", bytenr); + return -EUCLEAN; + } + key.objectid = bytenr; if (parent) { key.type = BTRFS_SHARED_DATA_REF_KEY; @@ -510,6 +528,12 @@ static noinline int insert_extent_data_ref(struct btrfs_trans_handle *trans, u32 num_refs; int ret; + if (unlikely(!root)) { + btrfs_err(trans->fs_info, + "missing extent root for extent at bytenr %llu", bytenr); + return -EUCLEAN; + } + key.objectid = bytenr; if (node->parent) { key.type = BTRFS_SHARED_DATA_REF_KEY; @@ -668,6 +692,12 @@ static noinline int lookup_tree_block_ref(struct btrfs_trans_handle *trans, struct btrfs_key key; int ret; + if (unlikely(!root)) { + btrfs_err(trans->fs_info, + "missing extent root for extent at bytenr %llu", bytenr); + return -EUCLEAN; + } + key.objectid = bytenr; if (parent) { key.type = BTRFS_SHARED_BLOCK_REF_KEY; @@ -692,6 +722,12 @@ static noinline int insert_tree_block_ref(struct btrfs_trans_handle *trans, struct btrfs_key key; int ret; + if (unlikely(!root)) { + btrfs_err(trans->fs_info, + "missing extent root for extent at bytenr %llu", bytenr); + return -EUCLEAN; + } + key.objectid = bytenr; if (node->parent) { key.type = BTRFS_SHARED_BLOCK_REF_KEY; @@ -782,6 +818,12 @@ int lookup_inline_extent_backref(struct btrfs_trans_handle *trans, bool skinny_metadata = btrfs_fs_incompat(fs_info, SKINNY_METADATA); int needed; + if (unlikely(!root)) { + btrfs_err(fs_info, + "missing extent root for extent at bytenr %llu", bytenr); + return -EUCLEAN; + } + key.objectid = bytenr; key.type = BTRFS_EXTENT_ITEM_KEY; key.offset = num_bytes; @@ -1680,6 +1722,12 @@ static int run_delayed_extent_op(struct btrfs_trans_handle *trans, } root = btrfs_extent_root(fs_info, key.objectid); + if (unlikely(!root)) { + btrfs_err(fs_info, + "missing extent root for extent at bytenr %llu", + key.objectid); + return -EUCLEAN; + } again: ret = btrfs_search_slot(trans, root, &key, path, 0, 1); if (ret < 0) { @@ -1926,8 +1974,15 @@ static int cleanup_ref_head(struct btrfs_trans_handle *trans, struct btrfs_root *csum_root; csum_root = btrfs_csum_root(fs_info, head->bytenr); - ret = btrfs_del_csums(trans, csum_root, head->bytenr, - head->num_bytes); + if (unlikely(!csum_root)) { + btrfs_err(fs_info, + "missing csum root for extent at bytenr %llu", + head->bytenr); + ret = -EUCLEAN; + } else { + ret = btrfs_del_csums(trans, csum_root, head->bytenr, + head->num_bytes); + } } } @@ -2379,6 +2434,12 @@ static noinline int check_committed_ref(struct btrfs_inode *inode, int type; int ret; + if (unlikely(!extent_root)) { + btrfs_err(fs_info, + "missing extent root for extent at bytenr %llu", bytenr); + return -EUCLEAN; + } + key.objectid = bytenr; key.type = BTRFS_EXTENT_ITEM_KEY; key.offset = (u64)-1; @@ -3093,6 +3154,15 @@ static int do_free_extent_accounting(struct btrfs_trans_handle *trans, struct btrfs_root *csum_root; csum_root = btrfs_csum_root(trans->fs_info, bytenr); + if (unlikely(!csum_root)) { + ret = -EUCLEAN; + btrfs_abort_transaction(trans, ret); + btrfs_err(trans->fs_info, + "missing csum root for extent at bytenr %llu", + bytenr); + return ret; + } + ret = btrfs_del_csums(trans, csum_root, bytenr, num_bytes); if (unlikely(ret)) { btrfs_abort_transaction(trans, ret); @@ -3222,7 +3292,11 @@ static int __btrfs_free_extent(struct btrfs_trans_handle *trans, u64 delayed_ref_root = href->owning_root; extent_root = btrfs_extent_root(info, bytenr); - ASSERT(extent_root); + if (unlikely(!extent_root)) { + btrfs_err(info, + "missing extent root for extent at bytenr %llu", bytenr); + return -EUCLEAN; + } path = btrfs_alloc_path(); if (!path) @@ -4939,11 +5013,18 @@ static int alloc_reserved_file_extent(struct btrfs_trans_handle *trans, size += btrfs_extent_inline_ref_size(BTRFS_EXTENT_OWNER_REF_KEY); size += btrfs_extent_inline_ref_size(type); + extent_root = btrfs_extent_root(fs_info, ins->objectid); + if (unlikely(!extent_root)) { + btrfs_err(fs_info, + "missing extent root for extent at bytenr %llu", + ins->objectid); + return -EUCLEAN; + } + path = btrfs_alloc_path(); if (!path) return -ENOMEM; - extent_root = btrfs_extent_root(fs_info, ins->objectid); ret = btrfs_insert_empty_item(trans, extent_root, path, ins, size); if (ret) { btrfs_free_path(path); @@ -5019,11 +5100,18 @@ static int alloc_reserved_tree_block(struct btrfs_trans_handle *trans, size += sizeof(*block_info); } + extent_root = btrfs_extent_root(fs_info, extent_key.objectid); + if (unlikely(!extent_root)) { + btrfs_err(fs_info, + "missing extent root for extent at bytenr %llu", + extent_key.objectid); + return -EUCLEAN; + } + path = btrfs_alloc_path(); if (!path) return -ENOMEM; - extent_root = btrfs_extent_root(fs_info, extent_key.objectid); ret = btrfs_insert_empty_item(trans, extent_root, path, &extent_key, size); if (ret) { diff --git a/fs/btrfs/file-item.c b/fs/btrfs/file-item.c index 927324a2175a..ed8ecf44fbd0 100644 --- a/fs/btrfs/file-item.c +++ b/fs/btrfs/file-item.c @@ -308,6 +308,13 @@ static int search_csum_tree(struct btrfs_fs_info *fs_info, /* Current item doesn't contain the desired range, search again */ btrfs_release_path(path); csum_root = btrfs_csum_root(fs_info, disk_bytenr); + if (unlikely(!csum_root)) { + btrfs_err(fs_info, + "missing csum root for extent at bytenr %llu", + disk_bytenr); + return -EUCLEAN; + } + item = btrfs_lookup_csum(NULL, csum_root, path, disk_bytenr, 0); if (IS_ERR(item)) { ret = PTR_ERR(item); diff --git a/fs/btrfs/free-space-tree.c b/fs/btrfs/free-space-tree.c index ecddfca92b2b..9efd1ec90f03 100644 --- a/fs/btrfs/free-space-tree.c +++ b/fs/btrfs/free-space-tree.c @@ -1073,6 +1073,14 @@ static int populate_free_space_tree(struct btrfs_trans_handle *trans, if (ret) return ret; + extent_root = btrfs_extent_root(trans->fs_info, block_group->start); + if (unlikely(!extent_root)) { + btrfs_err(trans->fs_info, + "missing extent root for block group at offset %llu", + block_group->start); + return -EUCLEAN; + } + mutex_lock(&block_group->free_space_lock); /* @@ -1086,7 +1094,6 @@ static int populate_free_space_tree(struct btrfs_trans_handle *trans, key.type = BTRFS_EXTENT_ITEM_KEY; key.offset = 0; - extent_root = btrfs_extent_root(trans->fs_info, key.objectid); ret = btrfs_search_slot_for_read(extent_root, &key, path, 1, 0); if (ret < 0) goto out_locked; diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index a6da98435ef7..f643a0520872 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -2012,6 +2012,13 @@ static int can_nocow_file_extent(struct btrfs_path *path, */ csum_root = btrfs_csum_root(root->fs_info, io_start); + if (unlikely(!csum_root)) { + btrfs_err(root->fs_info, + "missing csum root for extent at bytenr %llu", io_start); + ret = -EUCLEAN; + goto out; + } + ret = btrfs_lookup_csums_list(csum_root, io_start, io_start + args->file_extent.num_bytes - 1, NULL, nowait); @@ -2749,10 +2756,17 @@ static int add_pending_csums(struct btrfs_trans_handle *trans, int ret; list_for_each_entry(sum, list, list) { - trans->adding_csums = true; - if (!csum_root) + if (!csum_root) { csum_root = btrfs_csum_root(trans->fs_info, sum->logical); + if (unlikely(!csum_root)) { + btrfs_err(trans->fs_info, + "missing csum root for extent at bytenr %llu", + sum->logical); + return -EUCLEAN; + } + } + trans->adding_csums = true; ret = btrfs_csum_file_blocks(trans, csum_root, sum); trans->adding_csums = false; if (ret) @@ -9874,6 +9888,7 @@ ssize_t btrfs_do_encoded_write(struct kiocb *iocb, struct iov_iter *from, int compression; size_t orig_count; const u32 min_folio_size = btrfs_min_folio_size(fs_info); + const u32 blocksize = fs_info->sectorsize; u64 start, end; u64 num_bytes, ram_bytes, disk_num_bytes; struct btrfs_key ins; @@ -9984,9 +9999,9 @@ ssize_t btrfs_do_encoded_write(struct kiocb *iocb, struct iov_iter *from, ret = -EFAULT; goto out_cb; } - if (bytes < min_folio_size) - folio_zero_range(folio, bytes, min_folio_size - bytes); - ret = bio_add_folio(&cb->bbio.bio, folio, folio_size(folio), 0); + if (!IS_ALIGNED(bytes, blocksize)) + folio_zero_range(folio, bytes, round_up(bytes, blocksize) - bytes); + ret = bio_add_folio(&cb->bbio.bio, folio, round_up(bytes, blocksize), 0); if (unlikely(!ret)) { folio_put(folio); ret = -EINVAL; diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index b805dd9227ef..d75d31b606e4 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -3617,7 +3617,8 @@ static long btrfs_ioctl_qgroup_assign(struct file *file, void __user *arg) } } - trans = btrfs_join_transaction(root); + /* 2 BTRFS_QGROUP_RELATION_KEY items. */ + trans = btrfs_start_transaction(root, 2); if (IS_ERR(trans)) { ret = PTR_ERR(trans); goto out; @@ -3689,7 +3690,11 @@ static long btrfs_ioctl_qgroup_create(struct file *file, void __user *arg) goto out; } - trans = btrfs_join_transaction(root); + /* + * 1 BTRFS_QGROUP_INFO_KEY item. + * 1 BTRFS_QGROUP_LIMIT_KEY item. + */ + trans = btrfs_start_transaction(root, 2); if (IS_ERR(trans)) { ret = PTR_ERR(trans); goto out; @@ -3738,7 +3743,8 @@ static long btrfs_ioctl_qgroup_limit(struct file *file, void __user *arg) goto drop_write; } - trans = btrfs_join_transaction(root); + /* 1 BTRFS_QGROUP_LIMIT_KEY item. */ + trans = btrfs_start_transaction(root, 1); if (IS_ERR(trans)) { ret = PTR_ERR(trans); goto out; diff --git a/fs/btrfs/lzo.c b/fs/btrfs/lzo.c index 049a940ba449..79642e02181b 100644 --- a/fs/btrfs/lzo.c +++ b/fs/btrfs/lzo.c @@ -429,7 +429,7 @@ static void copy_compressed_segment(struct compressed_bio *cb, int lzo_decompress_bio(struct list_head *ws, struct compressed_bio *cb) { struct workspace *workspace = list_entry(ws, struct workspace, list); - const struct btrfs_fs_info *fs_info = cb->bbio.inode->root->fs_info; + struct btrfs_fs_info *fs_info = cb->bbio.inode->root->fs_info; const u32 sectorsize = fs_info->sectorsize; struct folio_iter fi; char *kaddr; @@ -447,7 +447,7 @@ int lzo_decompress_bio(struct list_head *ws, struct compressed_bio *cb) /* There must be a compressed folio and matches the sectorsize. */ if (unlikely(!fi.folio)) return -EINVAL; - ASSERT(folio_size(fi.folio) == sectorsize); + ASSERT(folio_size(fi.folio) == btrfs_min_folio_size(fs_info)); kaddr = kmap_local_folio(fi.folio, 0); len_in = read_compress_length(kaddr); kunmap_local(kaddr); diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c index 3b2a6517d0b5..41589ce66371 100644 --- a/fs/btrfs/qgroup.c +++ b/fs/btrfs/qgroup.c @@ -3739,6 +3739,14 @@ static int qgroup_rescan_leaf(struct btrfs_trans_handle *trans, mutex_lock(&fs_info->qgroup_rescan_lock); extent_root = btrfs_extent_root(fs_info, fs_info->qgroup_rescan_progress.objectid); + if (unlikely(!extent_root)) { + btrfs_err(fs_info, + "missing extent root for extent at bytenr %llu", + fs_info->qgroup_rescan_progress.objectid); + mutex_unlock(&fs_info->qgroup_rescan_lock); + return -EUCLEAN; + } + ret = btrfs_search_slot_for_read(extent_root, &fs_info->qgroup_rescan_progress, path, 1, 0); diff --git a/fs/btrfs/raid56.c b/fs/btrfs/raid56.c index b4511f560e92..02105d68accb 100644 --- a/fs/btrfs/raid56.c +++ b/fs/btrfs/raid56.c @@ -2297,8 +2297,7 @@ void raid56_parity_recover(struct bio *bio, struct btrfs_io_context *bioc, static void fill_data_csums(struct btrfs_raid_bio *rbio) { struct btrfs_fs_info *fs_info = rbio->bioc->fs_info; - struct btrfs_root *csum_root = btrfs_csum_root(fs_info, - rbio->bioc->full_stripe_logical); + struct btrfs_root *csum_root; const u64 start = rbio->bioc->full_stripe_logical; const u32 len = (rbio->nr_data * rbio->stripe_nsectors) << fs_info->sectorsize_bits; @@ -2331,6 +2330,15 @@ static void fill_data_csums(struct btrfs_raid_bio *rbio) goto error; } + csum_root = btrfs_csum_root(fs_info, rbio->bioc->full_stripe_logical); + if (unlikely(!csum_root)) { + btrfs_err(fs_info, + "missing csum root for extent at bytenr %llu", + rbio->bioc->full_stripe_logical); + ret = -EUCLEAN; + goto error; + } + ret = btrfs_lookup_csums_bitmap(csum_root, NULL, start, start + len - 1, rbio->csum_buf, rbio->csum_bitmap); if (ret < 0) diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c index b2343aed7a5d..033f74fd6225 100644 --- a/fs/btrfs/relocation.c +++ b/fs/btrfs/relocation.c @@ -4185,6 +4185,8 @@ static int move_existing_remap(struct btrfs_fs_info *fs_info, dest_addr = ins.objectid; dest_length = ins.offset; + dest_bg = btrfs_lookup_block_group(fs_info, dest_addr); + if (!is_data && !IS_ALIGNED(dest_length, fs_info->nodesize)) { u64 new_length = ALIGN_DOWN(dest_length, fs_info->nodesize); @@ -4295,15 +4297,12 @@ static int move_existing_remap(struct btrfs_fs_info *fs_info, if (unlikely(ret)) goto end; - dest_bg = btrfs_lookup_block_group(fs_info, dest_addr); - adjust_block_group_remap_bytes(trans, dest_bg, dest_length); mutex_lock(&dest_bg->free_space_lock); bg_needs_free_space = test_bit(BLOCK_GROUP_FLAG_NEEDS_FREE_SPACE, &dest_bg->runtime_flags); mutex_unlock(&dest_bg->free_space_lock); - btrfs_put_block_group(dest_bg); if (bg_needs_free_space) { ret = btrfs_add_block_group_free_space(trans, dest_bg); @@ -4333,13 +4332,13 @@ end: btrfs_end_transaction(trans); } } else { - dest_bg = btrfs_lookup_block_group(fs_info, dest_addr); btrfs_free_reserved_bytes(dest_bg, dest_length, 0); - btrfs_put_block_group(dest_bg); ret = btrfs_commit_transaction(trans); } + btrfs_put_block_group(dest_bg); + return ret; } @@ -4954,6 +4953,12 @@ static int do_remap_reloc_trans(struct btrfs_fs_info *fs_info, struct btrfs_space_info *sinfo = src_bg->space_info; extent_root = btrfs_extent_root(fs_info, src_bg->start); + if (unlikely(!extent_root)) { + btrfs_err(fs_info, + "missing extent root for block group at offset %llu", + src_bg->start); + return -EUCLEAN; + } trans = btrfs_start_transaction(extent_root, 0); if (IS_ERR(trans)) @@ -5306,6 +5311,13 @@ int btrfs_relocate_block_group(struct btrfs_fs_info *fs_info, u64 group_start, int ret; bool bg_is_ro = false; + if (unlikely(!extent_root)) { + btrfs_err(fs_info, + "missing extent root for block group at offset %llu", + group_start); + return -EUCLEAN; + } + /* * This only gets set if we had a half-deleted snapshot on mount. We * cannot allow relocation to start while we're still trying to clean up @@ -5536,12 +5548,17 @@ int btrfs_recover_relocation(struct btrfs_fs_info *fs_info) goto out; } + rc->extent_root = btrfs_extent_root(fs_info, 0); + if (unlikely(!rc->extent_root)) { + btrfs_err(fs_info, "missing extent root for extent at bytenr 0"); + ret = -EUCLEAN; + goto out; + } + ret = reloc_chunk_start(fs_info); if (ret < 0) goto out_end; - rc->extent_root = btrfs_extent_root(fs_info, 0); - set_reloc_control(rc); trans = btrfs_join_transaction(rc->extent_root); @@ -5636,6 +5653,14 @@ int btrfs_reloc_clone_csums(struct btrfs_ordered_extent *ordered) LIST_HEAD(list); int ret; + if (unlikely(!csum_root)) { + btrfs_mark_ordered_extent_error(ordered); + btrfs_err(fs_info, + "missing csum root for extent at bytenr %llu", + disk_bytenr); + return -EUCLEAN; + } + ret = btrfs_lookup_csums_list(csum_root, disk_bytenr, disk_bytenr + ordered->num_bytes - 1, &list, false); diff --git a/fs/btrfs/tree-checker.c b/fs/btrfs/tree-checker.c index 516ef62c8f43..b4e114efff45 100644 --- a/fs/btrfs/tree-checker.c +++ b/fs/btrfs/tree-checker.c @@ -1288,6 +1288,23 @@ static int check_root_item(struct extent_buffer *leaf, struct btrfs_key *key, btrfs_root_drop_level(&ri), BTRFS_MAX_LEVEL - 1); return -EUCLEAN; } + /* + * If drop_progress.objectid is non-zero, a btrfs_drop_snapshot() was + * interrupted and the resume point was recorded in drop_progress and + * drop_level. In that case drop_level must be >= 1: level 0 is the + * leaf level and drop_snapshot never saves a checkpoint there (it + * only records checkpoints at internal node levels in DROP_REFERENCE + * stage). A zero drop_level combined with a non-zero drop_progress + * objectid indicates on-disk corruption and would cause a BUG_ON in + * merge_reloc_root() and btrfs_drop_snapshot() at mount time. + */ + if (unlikely(btrfs_disk_key_objectid(&ri.drop_progress) != 0 && + btrfs_root_drop_level(&ri) == 0)) { + generic_err(leaf, slot, + "invalid root drop_level 0 with non-zero drop_progress objectid %llu", + btrfs_disk_key_objectid(&ri.drop_progress)); + return -EUCLEAN; + } /* Flags check */ if (unlikely(btrfs_root_flags(&ri) & ~valid_root_flags)) { diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index 552fef3c385a..ab0d460c7139 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -984,6 +984,13 @@ static noinline int replay_one_extent(struct walk_control *wc) sums = list_first_entry(&ordered_sums, struct btrfs_ordered_sum, list); csum_root = btrfs_csum_root(fs_info, sums->logical); + if (unlikely(!csum_root)) { + btrfs_err(fs_info, + "missing csum root for extent at bytenr %llu", + sums->logical); + ret = -EUCLEAN; + } + if (!ret) { ret = btrfs_del_csums(trans, csum_root, sums->logical, sums->len); @@ -4890,6 +4897,13 @@ static noinline int copy_items(struct btrfs_trans_handle *trans, } csum_root = btrfs_csum_root(trans->fs_info, disk_bytenr); + if (unlikely(!csum_root)) { + btrfs_err(trans->fs_info, + "missing csum root for extent at bytenr %llu", + disk_bytenr); + return -EUCLEAN; + } + disk_bytenr += extent_offset; ret = btrfs_lookup_csums_list(csum_root, disk_bytenr, disk_bytenr + extent_num_bytes - 1, @@ -5086,6 +5100,13 @@ static int log_extent_csums(struct btrfs_trans_handle *trans, /* block start is already adjusted for the file extent offset. */ block_start = btrfs_extent_map_block_start(em); csum_root = btrfs_csum_root(trans->fs_info, block_start); + if (unlikely(!csum_root)) { + btrfs_err(trans->fs_info, + "missing csum root for extent at bytenr %llu", + block_start); + return -EUCLEAN; + } + ret = btrfs_lookup_csums_list(csum_root, block_start + csum_offset, block_start + csum_offset + csum_len - 1, &ordered_sums, false); diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index be8975ef8b24..ab51e6ecfdef 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -4277,20 +4277,29 @@ static int balance_remap_chunks(struct btrfs_fs_info *fs_info, struct btrfs_path end: while (!list_empty(chunks)) { bool is_unused; + struct btrfs_block_group *bg; rci = list_first_entry(chunks, struct remap_chunk_info, list); - spin_lock(&rci->bg->lock); - is_unused = !btrfs_is_block_group_used(rci->bg); - spin_unlock(&rci->bg->lock); + bg = rci->bg; + if (bg) { + /* + * This is a bit racy and the 'used' status can change + * but this is not a problem as later functions will + * verify it again. + */ + spin_lock(&bg->lock); + is_unused = !btrfs_is_block_group_used(bg); + spin_unlock(&bg->lock); - if (is_unused) - btrfs_mark_bg_unused(rci->bg); + if (is_unused) + btrfs_mark_bg_unused(bg); - if (rci->made_ro) - btrfs_dec_block_group_ro(rci->bg); + if (rci->made_ro) + btrfs_dec_block_group_ro(bg); - btrfs_put_block_group(rci->bg); + btrfs_put_block_group(bg); + } list_del(&rci->list); kfree(rci); diff --git a/fs/btrfs/zoned.c b/fs/btrfs/zoned.c index 817ca4fb9efa..0cd7fd3fcfa3 100644 --- a/fs/btrfs/zoned.c +++ b/fs/btrfs/zoned.c @@ -1261,6 +1261,13 @@ static int calculate_alloc_pointer(struct btrfs_block_group *cache, key.offset = 0; root = btrfs_extent_root(fs_info, key.objectid); + if (unlikely(!root)) { + btrfs_err(fs_info, + "missing extent root for extent at bytenr %llu", + key.objectid); + return -EUCLEAN; + } + ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); /* We should not find the exact match */ if (unlikely(!ret)) diff --git a/fs/btrfs/zstd.c b/fs/btrfs/zstd.c index 6f076fa8f3f9..3e847b91dae3 100644 --- a/fs/btrfs/zstd.c +++ b/fs/btrfs/zstd.c @@ -600,7 +600,7 @@ int zstd_decompress_bio(struct list_head *ws, struct compressed_bio *cb) bio_first_folio(&fi, &cb->bbio.bio, 0); if (unlikely(!fi.folio)) return -EINVAL; - ASSERT(folio_size(fi.folio) == blocksize); + ASSERT(folio_size(fi.folio) == min_folio_size); stream = zstd_init_dstream( ZSTD_BTRFS_MAX_INPUT, workspace->mem, workspace->size); |
