erofs: improve decompression error reporting

Change the return type of decompress() from `int` to `const char *` to
provide more informative error diagnostics:

 - A NULL return indicates successful decompression;

 - If IS_ERR(ptr) is true, the return value encodes a standard negative
   errno (e.g., -ENOMEM, -EOPNOTSUPP) identifying the specific error;

 - Otherwise, a non-NULL return points to a human-readable error string,
   and the corresponding error code should be treated as -EFSCORRUPTED.

Signed-off-by: Gao Xiang <hsiangkao@linux.alibaba.com>
This commit is contained in:
Gao Xiang 2025-11-27 15:31:19 +08:00
parent 9ae77198d4
commit 831faabed8
6 changed files with 40 additions and 37 deletions

View file

@ -23,8 +23,8 @@ 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,
struct page **pagepool);
const char *(*decompress)(struct z_erofs_decompress_req *rq,
struct page **pagepool);
int (*init)(void);
void (*exit)(void);
char *name;

View file

@ -235,8 +235,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;
@ -257,8 +255,8 @@ 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,
struct page **pagepool)
static const char *z_erofs_lz4_decompress(struct z_erofs_decompress_req *rq,
struct page **pagepool)
{
unsigned int dst_maptype;
void *dst;
@ -273,14 +271,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;
}
}
@ -289,11 +287,11 @@ 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,
struct page **pagepool)
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;
const unsigned int bs = rq->sb->s_blocksize;
@ -301,7 +299,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;
@ -341,7 +339,7 @@ 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,

View file

@ -157,8 +157,6 @@ again:
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;
break;
}
@ -178,8 +176,8 @@ failed_zinit:
return err;
}
static int z_erofs_deflate_decompress(struct z_erofs_decompress_req *rq,
struct page **pgpl)
static const char *z_erofs_deflate_decompress(struct z_erofs_decompress_req *rq,
struct page **pgpl)
{
#ifdef CONFIG_EROFS_FS_ZIP_ACCEL
int err;
@ -187,11 +185,11 @@ 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
return __z_erofs_deflate_decompress(rq, pgpl);
return ERR_PTR(__z_erofs_deflate_decompress(rq, pgpl));
}
const struct z_erofs_decompressor z_erofs_deflate_decomp = {

View file

@ -146,8 +146,8 @@ again:
return err;
}
static int z_erofs_lzma_decompress(struct z_erofs_decompress_req *rq,
struct page **pgpl)
static const char *z_erofs_lzma_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 };
@ -162,7 +162,7 @@ static int z_erofs_lzma_decompress(struct z_erofs_decompress_req *rq,
min(rq->inputsize, sb->s_blocksize - rq->pageofs_in));
if (err) {
kunmap_local(dctx.kin);
return err;
return ERR_PTR(err);
}
/* 2. get an available lzma context */
@ -207,8 +207,6 @@ again:
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;
break;
}
@ -223,7 +221,7 @@ again:
z_erofs_lzma_head = strm;
spin_unlock(&z_erofs_lzma_lock);
wake_up(&z_erofs_lzma_wq);
return err;
return ERR_PTR(err);
}
const struct z_erofs_decompressor z_erofs_lzma_decomp = {

View file

@ -135,8 +135,8 @@ static int z_erofs_load_zstd_config(struct super_block *sb,
return strm ? -ENOMEM : 0;
}
static int z_erofs_zstd_decompress(struct z_erofs_decompress_req *rq,
struct page **pgpl)
static const char *z_erofs_zstd_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 };
@ -152,7 +152,7 @@ static int z_erofs_zstd_decompress(struct z_erofs_decompress_req *rq,
min(rq->inputsize, sb->s_blocksize - rq->pageofs_in));
if (err) {
kunmap_local(dctx.kin);
return err;
return ERR_PTR(err);
}
/* 2. get an available ZSTD context */
@ -191,10 +191,6 @@ static int z_erofs_zstd_decompress(struct z_erofs_decompress_req *rq,
if (zstd_is_error(zerr) ||
((rq->outputsize + dctx.avail_out) && (!zerr || (zerr > 0 &&
!(rq->inputsize + in_buf.size - in_buf.pos))))) {
erofs_err(sb, "failed to decompress in[%u] out[%u]: %s",
rq->inputsize, rq->outputsize,
zstd_is_error(zerr) ? zstd_get_error_name(zerr) :
"unexpected end of stream");
err = -EFSCORRUPTED;
break;
}
@ -210,7 +206,7 @@ failed_zinit:
z_erofs_zstd_head = strm;
spin_unlock(&z_erofs_zstd_lock);
wake_up(&z_erofs_zstd_wq);
return err;
return ERR_PTR(err);
}
const struct z_erofs_decompressor z_erofs_zstd_decomp = {

View file

@ -1267,12 +1267,13 @@ static int z_erofs_decompress_pcluster(struct z_erofs_backend *be, int err)
struct erofs_sb_info *const sbi = EROFS_SB(be->sb);
struct z_erofs_pcluster *pcl = be->pcl;
unsigned int pclusterpages = z_erofs_pclusterpages(pcl);
const struct z_erofs_decompressor *decomp =
const struct z_erofs_decompressor *alg =
z_erofs_decomp[pcl->algorithmformat];
bool try_free = true;
int i, j, jtop, err2;
struct page *page;
bool overlapped;
bool try_free = true;
const char *reason;
mutex_lock(&pcl->lock);
be->nr_pages = PAGE_ALIGN(pcl->length + pcl->pageofs_out) >> PAGE_SHIFT;
@ -1304,8 +1305,8 @@ static int z_erofs_decompress_pcluster(struct z_erofs_backend *be, int err)
err2 = z_erofs_parse_in_bvecs(be, &overlapped);
if (err2)
err = err2;
if (!err)
err = decomp->decompress(&(struct z_erofs_decompress_req) {
if (!err) {
reason = alg->decompress(&(struct z_erofs_decompress_req) {
.sb = be->sb,
.in = be->compressed_pages,
.out = be->decompressed_pages,
@ -1322,6 +1323,18 @@ static int z_erofs_decompress_pcluster(struct z_erofs_backend *be, int err)
.gfp = pcl->besteffort ? GFP_KERNEL :
GFP_NOWAIT | __GFP_NORETRY
}, be->pagepool);
if (IS_ERR(reason)) {
erofs_err(be->sb, "failed to decompress (%s) %ld @ pa %llu size %u => %u",
alg->name, PTR_ERR(reason), pcl->pos,
pcl->pclustersize, pcl->length);
err = PTR_ERR(reason);
} else if (unlikely(reason)) {
erofs_err(be->sb, "failed to decompress (%s) %s @ pa %llu size %u => %u",
alg->name, reason, pcl->pos,
pcl->pclustersize, pcl->length);
err = -EFSCORRUPTED;
}
}
/* must handle all compressed pages before actual file pages */
if (pcl->from_meta) {