Commit 8991448e authored by Linus Torvalds's avatar Linus Torvalds
Browse files
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()
parents a0c83177 b17b79ff
Loading
Loading
Loading
Loading
+28 −0
Original line number Diff line number Diff line
@@ -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;
+36 −0
Original line number Diff line number Diff line
@@ -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.
+8 −3
Original line number Diff line number Diff line
@@ -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;
}

+17 −3
Original line number Diff line number Diff line
@@ -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;
+93 −5
Original line number Diff line number Diff line
@@ -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 @@ int btrfs_lookup_extent_info(struct btrfs_trans_handle *trans,
	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,10 +1974,17 @@ static int cleanup_ref_head(struct btrfs_trans_handle *trans,
			struct btrfs_root *csum_root;

			csum_root = btrfs_csum_root(fs_info, head->bytenr);
			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);
			}
		}
	}

	*bytes_released += btrfs_cleanup_ref_head_accounting(fs_info, delayed_refs, head);

@@ -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) {
Loading