Commit 477e31fd authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull erofs updates from Gao Xiang:

 - Fix a WARNING caused by a recent FSDAX misdetection regression

 - Fix the filesystem stacking limit for file-backed mounts

 - Print more informative diagnostics on decompression errors

 - Switch the on-disk definition `erofs_fs.h` to the MIT license

 - Minor cleanups

* tag 'erofs-for-6.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/xiang/erofs:
  erofs: switch on-disk header `erofs_fs.h` to MIT license
  erofs: get rid of raw bi_end_io() usage
  erofs: enable error reporting for z_erofs_fixup_insize()
  erofs: enable error reporting for z_erofs_stream_switch_bufs()
  erofs: improve Zstd, LZMA and DEFLATE error strings
  erofs: improve decompression error reporting
  erofs: tidy up z_erofs_lz4_handle_overlap()
  erofs: limit the level of fs stacking for file-backed mounts
  erofs: correct FSDAX detection
parents ca010e2e 0bdbf89a
Loading
Loading
Loading
Loading
+6 −6
Original line number Diff line number Diff line
@@ -23,7 +23,7 @@ struct z_erofs_decompress_req {
struct z_erofs_decompressor {
	int (*config)(struct super_block *sb, struct erofs_super_block *dsb,
		      void *data, int size);
	int (*decompress)(struct z_erofs_decompress_req *rq,
	const char *(*decompress)(struct z_erofs_decompress_req *rq,
				  struct page **pagepool);
	int (*init)(void);
	void (*exit)(void);
@@ -70,10 +70,10 @@ struct z_erofs_stream_dctx {
	bool bounced;			/* is the bounce buffer used now? */
};

int z_erofs_stream_switch_bufs(struct z_erofs_stream_dctx *dctx, void **dst,
			       void **src, struct page **pgpl);
int z_erofs_fixup_insize(struct z_erofs_decompress_req *rq, const char *padbuf,
			 unsigned int padbufsize);
const char *z_erofs_stream_switch_bufs(struct z_erofs_stream_dctx *dctx,
				void **dst, void **src, struct page **pgpl);
const char *z_erofs_fixup_insize(struct z_erofs_decompress_req *rq,
				 const char *padbuf, unsigned int padbufsize);
int __init z_erofs_init_decompressor(void);
void z_erofs_exit_decompressor(void);
int z_erofs_crypto_decompress(struct z_erofs_decompress_req *rq,
+75 −74
Original line number Diff line number Diff line
@@ -105,30 +105,20 @@ static int z_erofs_lz4_prepare_dstpages(struct z_erofs_decompress_req *rq,
	return kaddr ? 1 : 0;
}

static void *z_erofs_lz4_handle_overlap(struct z_erofs_decompress_req *rq,
static void *z_erofs_lz4_handle_overlap(const struct z_erofs_decompress_req *rq,
			void *inpage, void *out, unsigned int *inputmargin,
			int *maptype, bool may_inplace)
{
	unsigned int oend, omargin, total, i;
	unsigned int oend, omargin, cnt, i;
	struct page **in;
	void *src, *tmp;

	if (rq->inplace_io) {
		oend = rq->pageofs_out + rq->outputsize;
		omargin = PAGE_ALIGN(oend) - oend;
		if (rq->partial_decoding || !may_inplace ||
		    omargin < LZ4_DECOMPRESS_INPLACE_MARGIN(rq->inputsize))
			goto docopy;

		for (i = 0; i < rq->inpages; ++i)
			if (rq->out[rq->outpages - rq->inpages + i] !=
			    rq->in[i])
				goto docopy;
		kunmap_local(inpage);
		*maptype = 3;
		return out + ((rq->outpages - rq->inpages) << PAGE_SHIFT);
	}
	void *src;

	/*
	 * If in-place I/O isn't used, for example, the bounce compressed cache
	 * can hold data for incomplete read requests. Just map the compressed
	 * buffer as well and decompress directly.
	 */
	if (!rq->inplace_io) {
		if (rq->inpages <= 1) {
			*maptype = 0;
			return inpage;
@@ -139,10 +129,34 @@ static void *z_erofs_lz4_handle_overlap(struct z_erofs_decompress_req *rq,
			return ERR_PTR(-ENOMEM);
		*maptype = 1;
		return src;

docopy:
	/* Or copy compressed data which can be overlapped to per-CPU buffer */
	in = rq->in;
	}
	/*
	 * Then, deal with in-place I/Os. The reasons why in-place I/O is useful
	 * are: (1) It minimizes memory footprint during the I/O submission,
	 * which is useful for slow storage (including network devices and
	 * low-end HDDs/eMMCs) but with a lot inflight I/Os; (2) If in-place
	 * decompression can also be applied, it will reuse the unique buffer so
	 * that no extra CPU D-cache is polluted with temporary compressed data
	 * for extreme performance.
	 */
	oend = rq->pageofs_out + rq->outputsize;
	omargin = PAGE_ALIGN(oend) - oend;
	if (!rq->partial_decoding && may_inplace &&
	    omargin >= LZ4_DECOMPRESS_INPLACE_MARGIN(rq->inputsize)) {
		for (i = 0; i < rq->inpages; ++i)
			if (rq->out[rq->outpages - rq->inpages + i] !=
			    rq->in[i])
				break;
		if (i >= rq->inpages) {
			kunmap_local(inpage);
			*maptype = 3;
			return out + ((rq->outpages - rq->inpages) << PAGE_SHIFT);
		}
	}
	/*
	 * If in-place decompression can't be applied, copy compressed data that
	 * may potentially overlap during decompression to a per-CPU buffer.
	 */
	src = z_erofs_get_gbuf(rq->inpages);
	if (!src) {
		DBG_BUGON(1);
@@ -150,20 +164,13 @@ static void *z_erofs_lz4_handle_overlap(struct z_erofs_decompress_req *rq,
		return ERR_PTR(-EFAULT);
	}

	tmp = src;
	total = rq->inputsize;
	while (total) {
		unsigned int page_copycnt =
			min_t(unsigned int, total, PAGE_SIZE - *inputmargin);

	for (i = 0, in = rq->in; i < rq->inputsize; i += cnt, ++in) {
		cnt = min_t(u32, rq->inputsize - i, PAGE_SIZE - *inputmargin);
		if (!inpage)
			inpage = kmap_local_page(*in);
		memcpy(tmp, inpage + *inputmargin, page_copycnt);
		memcpy(src + i, inpage + *inputmargin, cnt);
		kunmap_local(inpage);
		inpage = NULL;
		tmp += page_copycnt;
		total -= page_copycnt;
		++in;
		*inputmargin = 0;
	}
	*maptype = 2;
@@ -171,21 +178,21 @@ static void *z_erofs_lz4_handle_overlap(struct z_erofs_decompress_req *rq,
}

/*
 * Get the exact inputsize with zero_padding feature.
 *  - For LZ4, it should work if zero_padding feature is on (5.3+);
 *  - For MicroLZMA, it'd be enabled all the time.
 * Get the exact on-disk size of the compressed data:
 *  - For LZ4, it should apply if the zero_padding feature is on (5.3+);
 *  - For others, zero_padding is enabled all the time.
 */
int z_erofs_fixup_insize(struct z_erofs_decompress_req *rq, const char *padbuf,
			 unsigned int padbufsize)
const char *z_erofs_fixup_insize(struct z_erofs_decompress_req *rq,
				 const char *padbuf, unsigned int padbufsize)
{
	const char *padend;

	padend = memchr_inv(padbuf, 0, padbufsize);
	if (!padend)
		return -EFSCORRUPTED;
		return "compressed data start not found";
	rq->inputsize -= padend - padbuf;
	rq->pageofs_in += padend - padbuf;
	return 0;
	return NULL;
}

static int z_erofs_lz4_decompress_mem(struct z_erofs_decompress_req *rq, u8 *dst)
@@ -193,6 +200,7 @@ static int z_erofs_lz4_decompress_mem(struct z_erofs_decompress_req *rq, u8 *dst
	bool support_0padding = false, may_inplace = false;
	unsigned int inputmargin;
	u8 *out, *headpage, *src;
	const char *reason;
	int ret, maptype;

	DBG_BUGON(*rq->in == NULL);
@@ -201,12 +209,12 @@ static int z_erofs_lz4_decompress_mem(struct z_erofs_decompress_req *rq, u8 *dst
	/* LZ4 decompression inplace is only safe if zero_padding is enabled */
	if (erofs_sb_has_zero_padding(EROFS_SB(rq->sb))) {
		support_0padding = true;
		ret = z_erofs_fixup_insize(rq, headpage + rq->pageofs_in,
		reason = z_erofs_fixup_insize(rq, headpage + rq->pageofs_in,
				min_t(unsigned int, rq->inputsize,
				      rq->sb->s_blocksize - rq->pageofs_in));
		if (ret) {
		if (reason) {
			kunmap_local(headpage);
			return ret;
			return IS_ERR(reason) ? PTR_ERR(reason) : -EFSCORRUPTED;
		}
		may_inplace = !((rq->pageofs_in + rq->inputsize) &
				(rq->sb->s_blocksize - 1));
@@ -228,8 +236,6 @@ static int z_erofs_lz4_decompress_mem(struct z_erofs_decompress_req *rq, u8 *dst
					  rq->inputsize, rq->outputsize);

	if (ret != rq->outputsize) {
		erofs_err(rq->sb, "failed to decompress %d in[%u, %u] out[%u]",
			  ret, rq->inputsize, inputmargin, rq->outputsize);
		if (ret >= 0)
			memset(out + ret, 0, rq->outputsize - ret);
		ret = -EFSCORRUPTED;
@@ -250,7 +256,7 @@ static int z_erofs_lz4_decompress_mem(struct z_erofs_decompress_req *rq, u8 *dst
	return ret;
}

static int z_erofs_lz4_decompress(struct z_erofs_decompress_req *rq,
static const char *z_erofs_lz4_decompress(struct z_erofs_decompress_req *rq,
					  struct page **pagepool)
{
	unsigned int dst_maptype;
@@ -266,14 +272,14 @@ static int z_erofs_lz4_decompress(struct z_erofs_decompress_req *rq,
		/* general decoding path which can be used for all cases */
		ret = z_erofs_lz4_prepare_dstpages(rq, pagepool);
		if (ret < 0)
			return ret;
			return ERR_PTR(ret);
		if (ret > 0) {
			dst = page_address(*rq->out);
			dst_maptype = 1;
		} else {
			dst = erofs_vm_map_ram(rq->out, rq->outpages);
			if (!dst)
				return -ENOMEM;
				return ERR_PTR(-ENOMEM);
			dst_maptype = 2;
		}
	}
@@ -282,10 +288,10 @@ static int z_erofs_lz4_decompress(struct z_erofs_decompress_req *rq,
		kunmap_local(dst);
	else if (dst_maptype == 2)
		vm_unmap_ram(dst, rq->outpages);
	return ret;
	return ERR_PTR(ret);
}

static int z_erofs_transform_plain(struct z_erofs_decompress_req *rq,
static const char *z_erofs_transform_plain(struct z_erofs_decompress_req *rq,
					   struct page **pagepool)
{
	const unsigned int nrpages_in = rq->inpages, nrpages_out = rq->outpages;
@@ -294,7 +300,7 @@ static int z_erofs_transform_plain(struct z_erofs_decompress_req *rq,
	u8 *kin;

	if (rq->outputsize > rq->inputsize)
		return -EOPNOTSUPP;
		return ERR_PTR(-EOPNOTSUPP);
	if (rq->alg == Z_EROFS_COMPRESSION_INTERLACED) {
		cur = bs - (rq->pageofs_out & (bs - 1));
		pi = (rq->pageofs_in + rq->inputsize - cur) & ~PAGE_MASK;
@@ -334,22 +340,19 @@ static int z_erofs_transform_plain(struct z_erofs_decompress_req *rq,
		kunmap_local(kin);
	}
	DBG_BUGON(ni > nrpages_in);
	return 0;
	return NULL;
}

int z_erofs_stream_switch_bufs(struct z_erofs_stream_dctx *dctx, void **dst,
			       void **src, struct page **pgpl)
const char *z_erofs_stream_switch_bufs(struct z_erofs_stream_dctx *dctx,
				void **dst, void **src, struct page **pgpl)
{
	struct z_erofs_decompress_req *rq = dctx->rq;
	struct super_block *sb = rq->sb;
	struct page **pgo, *tmppage;
	unsigned int j;

	if (!dctx->avail_out) {
		if (++dctx->no >= rq->outpages || !rq->outputsize) {
			erofs_err(sb, "insufficient space for decompressed data");
			return -EFSCORRUPTED;
		}
		if (++dctx->no >= rq->outpages || !rq->outputsize)
			return "insufficient space for decompressed data";

		if (dctx->kout)
			kunmap_local(dctx->kout);
@@ -360,7 +363,7 @@ int z_erofs_stream_switch_bufs(struct z_erofs_stream_dctx *dctx, void **dst,
			*pgo = erofs_allocpage(pgpl, rq->gfp);
			if (!*pgo) {
				dctx->kout = NULL;
				return -ENOMEM;
				return ERR_PTR(-ENOMEM);
			}
			set_page_private(*pgo, Z_EROFS_SHORTLIVED_PAGE);
		}
@@ -374,10 +377,8 @@ int z_erofs_stream_switch_bufs(struct z_erofs_stream_dctx *dctx, void **dst,
	}

	if (dctx->inbuf_pos == dctx->inbuf_sz && rq->inputsize) {
		if (++dctx->ni >= rq->inpages) {
			erofs_err(sb, "invalid compressed data");
			return -EFSCORRUPTED;
		}
		if (++dctx->ni >= rq->inpages)
			return "invalid compressed data";
		if (dctx->kout) /* unlike kmap(), take care of the orders */
			kunmap_local(dctx->kout);
		kunmap_local(dctx->kin);
@@ -412,12 +413,12 @@ int z_erofs_stream_switch_bufs(struct z_erofs_stream_dctx *dctx, void **dst,
			continue;
		tmppage = erofs_allocpage(pgpl, rq->gfp);
		if (!tmppage)
			return -ENOMEM;
			return ERR_PTR(-ENOMEM);
		set_page_private(tmppage, Z_EROFS_SHORTLIVED_PAGE);
		copy_highpage(tmppage, rq->in[j]);
		rq->in[j] = tmppage;
	}
	return 0;
	return NULL;
}

const struct z_erofs_decompressor *z_erofs_decomp[] = {
+4 −3
Original line number Diff line number Diff line
@@ -9,16 +9,17 @@ static int __z_erofs_crypto_decompress(struct z_erofs_decompress_req *rq,
	struct sg_table st_src, st_dst;
	struct acomp_req *req;
	struct crypto_wait wait;
	const char *reason;
	u8 *headpage;
	int ret;

	headpage = kmap_local_page(*rq->in);
	ret = z_erofs_fixup_insize(rq, headpage + rq->pageofs_in,
	reason = z_erofs_fixup_insize(rq, headpage + rq->pageofs_in,
				min_t(unsigned int, rq->inputsize,
				      rq->sb->s_blocksize - rq->pageofs_in));
	kunmap_local(headpage);
	if (ret)
		return ret;
	if (reason)
		return IS_ERR(reason) ? PTR_ERR(reason) : -EFSCORRUPTED;

	req = acomp_request_alloc(tfm);
	if (!req)
+19 −18
Original line number Diff line number Diff line
@@ -97,21 +97,22 @@ static int z_erofs_load_deflate_config(struct super_block *sb,
	return -ENOMEM;
}

static int __z_erofs_deflate_decompress(struct z_erofs_decompress_req *rq,
static const char *__z_erofs_deflate_decompress(struct z_erofs_decompress_req *rq,
						struct page **pgpl)
{
	struct super_block *sb = rq->sb;
	struct z_erofs_stream_dctx dctx = { .rq = rq, .no = -1, .ni = 0 };
	struct z_erofs_deflate *strm;
	int zerr, err;
	const char *reason;
	int zerr;

	/* 1. get the exact DEFLATE compressed size */
	dctx.kin = kmap_local_page(*rq->in);
	err = z_erofs_fixup_insize(rq, dctx.kin + rq->pageofs_in,
	reason = z_erofs_fixup_insize(rq, dctx.kin + rq->pageofs_in,
			min(rq->inputsize, sb->s_blocksize - rq->pageofs_in));
	if (err) {
	if (reason) {
		kunmap_local(dctx.kin);
		return err;
		return reason;
	}

	/* 2. get an available DEFLATE context */
@@ -129,7 +130,7 @@ static int __z_erofs_deflate_decompress(struct z_erofs_decompress_req *rq,
	/* 3. multi-call decompress */
	zerr = zlib_inflateInit2(&strm->z, -MAX_WBITS);
	if (zerr != Z_OK) {
		err = -EIO;
		reason = ERR_PTR(-EINVAL);
		goto failed_zinit;
	}

@@ -143,10 +144,10 @@ static int __z_erofs_deflate_decompress(struct z_erofs_decompress_req *rq,
	while (1) {
		dctx.avail_out = strm->z.avail_out;
		dctx.inbuf_sz = strm->z.avail_in;
		err = z_erofs_stream_switch_bufs(&dctx,
		reason = z_erofs_stream_switch_bufs(&dctx,
					(void **)&strm->z.next_out,
					(void **)&strm->z.next_in, pgpl);
		if (err)
		if (reason)
			break;
		strm->z.avail_out = dctx.avail_out;
		strm->z.avail_in = dctx.inbuf_sz;
@@ -157,14 +158,14 @@ static int __z_erofs_deflate_decompress(struct z_erofs_decompress_req *rq,
				break;
			if (zerr == Z_STREAM_END && !rq->outputsize)
				break;
			erofs_err(sb, "failed to decompress %d in[%u] out[%u]",
				  zerr, rq->inputsize, rq->outputsize);
			err = -EFSCORRUPTED;
			reason = (zerr == Z_DATA_ERROR ?
				"corrupted compressed data" :
				"unexpected end of stream");
			break;
		}
	}
	if (zlib_inflateEnd(&strm->z) != Z_OK && !err)
		err = -EIO;
	if (zlib_inflateEnd(&strm->z) != Z_OK && !reason)
		reason = ERR_PTR(-EIO);
	if (dctx.kout)
		kunmap_local(dctx.kout);
failed_zinit:
@@ -175,10 +176,10 @@ static int __z_erofs_deflate_decompress(struct z_erofs_decompress_req *rq,
	z_erofs_deflate_head = strm;
	spin_unlock(&z_erofs_deflate_lock);
	wake_up(&z_erofs_deflate_wq);
	return err;
	return reason;
}

static int z_erofs_deflate_decompress(struct z_erofs_decompress_req *rq,
static const char *z_erofs_deflate_decompress(struct z_erofs_decompress_req *rq,
					      struct page **pgpl)
{
#ifdef CONFIG_EROFS_FS_ZIP_ACCEL
@@ -187,7 +188,7 @@ static int z_erofs_deflate_decompress(struct z_erofs_decompress_req *rq,
	if (!rq->partial_decoding) {
		err = z_erofs_crypto_decompress(rq, pgpl);
		if (err != -EOPNOTSUPP)
			return err;
			return ERR_PTR(err);

	}
#endif
+13 −13
Original line number Diff line number Diff line
@@ -146,7 +146,7 @@ static int z_erofs_load_lzma_config(struct super_block *sb,
	return err;
}

static int z_erofs_lzma_decompress(struct z_erofs_decompress_req *rq,
static const char *z_erofs_lzma_decompress(struct z_erofs_decompress_req *rq,
					   struct page **pgpl)
{
	struct super_block *sb = rq->sb;
@@ -154,15 +154,15 @@ static int z_erofs_lzma_decompress(struct z_erofs_decompress_req *rq,
	struct xz_buf buf = {};
	struct z_erofs_lzma *strm;
	enum xz_ret xz_err;
	int err;
	const char *reason;

	/* 1. get the exact LZMA compressed size */
	dctx.kin = kmap_local_page(*rq->in);
	err = z_erofs_fixup_insize(rq, dctx.kin + rq->pageofs_in,
	reason = z_erofs_fixup_insize(rq, dctx.kin + rq->pageofs_in,
			min(rq->inputsize, sb->s_blocksize - rq->pageofs_in));
	if (err) {
	if (reason) {
		kunmap_local(dctx.kin);
		return err;
		return reason;
	}

	/* 2. get an available lzma context */
@@ -188,9 +188,9 @@ static int z_erofs_lzma_decompress(struct z_erofs_decompress_req *rq,
		dctx.avail_out = buf.out_size - buf.out_pos;
		dctx.inbuf_sz = buf.in_size;
		dctx.inbuf_pos = buf.in_pos;
		err = z_erofs_stream_switch_bufs(&dctx, (void **)&buf.out,
		reason = z_erofs_stream_switch_bufs(&dctx, (void **)&buf.out,
						    (void **)&buf.in, pgpl);
		if (err)
		if (reason)
			break;

		if (buf.out_size == buf.out_pos) {
@@ -207,9 +207,9 @@ static int z_erofs_lzma_decompress(struct z_erofs_decompress_req *rq,
		if (xz_err != XZ_OK) {
			if (xz_err == XZ_STREAM_END && !rq->outputsize)
				break;
			erofs_err(sb, "failed to decompress %d in[%u] out[%u]",
				  xz_err, rq->inputsize, rq->outputsize);
			err = -EFSCORRUPTED;
			reason = (xz_err == XZ_DATA_ERROR ?
				"corrupted compressed data" :
				"unexpected end of stream");
			break;
		}
	} while (1);
@@ -223,7 +223,7 @@ static int z_erofs_lzma_decompress(struct z_erofs_decompress_req *rq,
	z_erofs_lzma_head = strm;
	spin_unlock(&z_erofs_lzma_lock);
	wake_up(&z_erofs_lzma_wq);
	return err;
	return reason;
}

const struct z_erofs_decompressor z_erofs_lzma_decomp = {
Loading