Commit f975f08c authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull a few more btrfs fixes from David Sterba:

 - in tree-checker, fix wrong size of check for inode ref item

 - in ref-verify, handle combination of mount options that allow
   partially damaged extent tree (reported by syzbot)

 - additional validation of compression mount option to catch invalid
   string as level

* tag 'for-6.17-rc6-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux:
  btrfs: reject invalid compression level
  btrfs: ref-verify: handle damaged extent root tree
  btrfs: tree-checker: fix the incorrect inode ref size check
parents fce24200 b98b2083
Loading
Loading
Loading
Loading
+13 −9
Original line number Diff line number Diff line
@@ -1616,25 +1616,29 @@ int btrfs_compress_heuristic(struct btrfs_inode *inode, u64 start, u64 end)
}

/*
 * Convert the compression suffix (eg. after "zlib" starting with ":") to
 * level, unrecognized string will set the default level. Negative level
 * numbers are allowed.
 * Convert the compression suffix (eg. after "zlib" starting with ":") to level.
 *
 * If the resulting level exceeds the algo's supported levels, it will be clamped.
 *
 * Return <0 if no valid string can be found.
 * Return 0 if everything is fine.
 */
int btrfs_compress_str2level(unsigned int type, const char *str)
int btrfs_compress_str2level(unsigned int type, const char *str, int *level_ret)
{
	int level = 0;
	int ret;

	if (!type)
	if (!type) {
		*level_ret = btrfs_compress_set_level(type, level);
		return 0;
	}

	if (str[0] == ':') {
		ret = kstrtoint(str + 1, 10, &level);
		if (ret)
			level = 0;
			return ret;
	}

	level = btrfs_compress_set_level(type, level);

	return level;
	*level_ret = btrfs_compress_set_level(type, level);
	return 0;
}
+1 −1
Original line number Diff line number Diff line
@@ -102,7 +102,7 @@ void btrfs_submit_compressed_write(struct btrfs_ordered_extent *ordered,
				   bool writeback);
void btrfs_submit_compressed_read(struct btrfs_bio *bbio);

int btrfs_compress_str2level(unsigned int type, const char *str);
int btrfs_compress_str2level(unsigned int type, const char *str, int *level_ret);

struct folio *btrfs_alloc_compr_folio(void);
void btrfs_free_compr_folio(struct folio *folio);
+8 −1
Original line number Diff line number Diff line
@@ -980,11 +980,18 @@ int btrfs_build_ref_tree(struct btrfs_fs_info *fs_info)
	if (!btrfs_test_opt(fs_info, REF_VERIFY))
		return 0;

	extent_root = btrfs_extent_root(fs_info, 0);
	/* If the extent tree is damaged we cannot ignore it (IGNOREBADROOTS). */
	if (IS_ERR(extent_root)) {
		btrfs_warn(fs_info, "ref-verify: extent tree not available, disabling");
		btrfs_clear_opt(fs_info->mount_opt, REF_VERIFY);
		return 0;
	}

	path = btrfs_alloc_path();
	if (!path)
		return -ENOMEM;

	extent_root = btrfs_extent_root(fs_info, 0);
	eb = btrfs_read_lock_root_node(extent_root);
	level = btrfs_header_level(eb);
	path->nodes[level] = eb;
+19 −8
Original line number Diff line number Diff line
@@ -276,6 +276,7 @@ static int btrfs_parse_compress(struct btrfs_fs_context *ctx,
				const struct fs_parameter *param, int opt)
{
	const char *string = param->string;
	int ret;

	/*
	 * Provide the same semantics as older kernels that don't use fs
@@ -294,15 +295,19 @@ static int btrfs_parse_compress(struct btrfs_fs_context *ctx,
		btrfs_clear_opt(ctx->mount_opt, NODATASUM);
	} else if (btrfs_match_compress_type(string, "zlib", true)) {
		ctx->compress_type = BTRFS_COMPRESS_ZLIB;
		ctx->compress_level = btrfs_compress_str2level(BTRFS_COMPRESS_ZLIB,
							       string + 4);
		ret = btrfs_compress_str2level(BTRFS_COMPRESS_ZLIB, string + 4,
					       &ctx->compress_level);
		if (ret < 0)
			goto error;
		btrfs_set_opt(ctx->mount_opt, COMPRESS);
		btrfs_clear_opt(ctx->mount_opt, NODATACOW);
		btrfs_clear_opt(ctx->mount_opt, NODATASUM);
	} else if (btrfs_match_compress_type(string, "lzo", true)) {
		ctx->compress_type = BTRFS_COMPRESS_LZO;
		ctx->compress_level = btrfs_compress_str2level(BTRFS_COMPRESS_LZO,
							       string + 3);
		ret = btrfs_compress_str2level(BTRFS_COMPRESS_LZO, string + 3,
					       &ctx->compress_level);
		if (ret < 0)
			goto error;
		if (string[3] == ':' && string[4])
			btrfs_warn(NULL, "Compression level ignored for LZO");
		btrfs_set_opt(ctx->mount_opt, COMPRESS);
@@ -310,8 +315,10 @@ static int btrfs_parse_compress(struct btrfs_fs_context *ctx,
		btrfs_clear_opt(ctx->mount_opt, NODATASUM);
	} else if (btrfs_match_compress_type(string, "zstd", true)) {
		ctx->compress_type = BTRFS_COMPRESS_ZSTD;
		ctx->compress_level = btrfs_compress_str2level(BTRFS_COMPRESS_ZSTD,
							       string + 4);
		ret = btrfs_compress_str2level(BTRFS_COMPRESS_ZSTD, string + 4,
					       &ctx->compress_level);
		if (ret < 0)
			goto error;
		btrfs_set_opt(ctx->mount_opt, COMPRESS);
		btrfs_clear_opt(ctx->mount_opt, NODATACOW);
		btrfs_clear_opt(ctx->mount_opt, NODATASUM);
@@ -322,10 +329,14 @@ static int btrfs_parse_compress(struct btrfs_fs_context *ctx,
		btrfs_clear_opt(ctx->mount_opt, COMPRESS);
		btrfs_clear_opt(ctx->mount_opt, FORCE_COMPRESS);
	} else {
		btrfs_err(NULL, "unrecognized compression value %s", string);
		return -EINVAL;
		ret = -EINVAL;
		goto error;
	}
	return 0;
error:
	btrfs_err(NULL, "failed to parse compression option '%s'", string);
	return ret;

}

static int btrfs_parse_param(struct fs_context *fc, struct fs_parameter *param)
+2 −2
Original line number Diff line number Diff line
@@ -1756,10 +1756,10 @@ static int check_inode_ref(struct extent_buffer *leaf,
	while (ptr < end) {
		u16 namelen;

		if (unlikely(ptr + sizeof(iref) > end)) {
		if (unlikely(ptr + sizeof(*iref) > end)) {
			inode_ref_err(leaf, slot,
			"inode ref overflow, ptr %lu end %lu inode_ref_size %zu",
				ptr, end, sizeof(iref));
				ptr, end, sizeof(*iref));
			return -EUCLEAN;
		}