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 { struct z_erofs_decompressor {
int (*config)(struct super_block *sb, struct erofs_super_block *dsb, int (*config)(struct super_block *sb, struct erofs_super_block *dsb,
void *data, int size); void *data, int size);
int (*decompress)(struct z_erofs_decompress_req *rq, const char *(*decompress)(struct z_erofs_decompress_req *rq,
struct page **pagepool); struct page **pagepool);
int (*init)(void); int (*init)(void);
void (*exit)(void); void (*exit)(void);
char *name; 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); rq->inputsize, rq->outputsize);
if (ret != 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) if (ret >= 0)
memset(out + ret, 0, rq->outputsize - ret); memset(out + ret, 0, rq->outputsize - ret);
ret = -EFSCORRUPTED; ret = -EFSCORRUPTED;
@ -257,8 +255,8 @@ static int z_erofs_lz4_decompress_mem(struct z_erofs_decompress_req *rq, u8 *dst
return ret; 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) struct page **pagepool)
{ {
unsigned int dst_maptype; unsigned int dst_maptype;
void *dst; 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 */ /* general decoding path which can be used for all cases */
ret = z_erofs_lz4_prepare_dstpages(rq, pagepool); ret = z_erofs_lz4_prepare_dstpages(rq, pagepool);
if (ret < 0) if (ret < 0)
return ret; return ERR_PTR(ret);
if (ret > 0) { if (ret > 0) {
dst = page_address(*rq->out); dst = page_address(*rq->out);
dst_maptype = 1; dst_maptype = 1;
} else { } else {
dst = erofs_vm_map_ram(rq->out, rq->outpages); dst = erofs_vm_map_ram(rq->out, rq->outpages);
if (!dst) if (!dst)
return -ENOMEM; return ERR_PTR(-ENOMEM);
dst_maptype = 2; dst_maptype = 2;
} }
} }
@ -289,11 +287,11 @@ static int z_erofs_lz4_decompress(struct z_erofs_decompress_req *rq,
kunmap_local(dst); kunmap_local(dst);
else if (dst_maptype == 2) else if (dst_maptype == 2)
vm_unmap_ram(dst, rq->outpages); 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) struct page **pagepool)
{ {
const unsigned int nrpages_in = rq->inpages, nrpages_out = rq->outpages; const unsigned int nrpages_in = rq->inpages, nrpages_out = rq->outpages;
const unsigned int bs = rq->sb->s_blocksize; 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; u8 *kin;
if (rq->outputsize > rq->inputsize) if (rq->outputsize > rq->inputsize)
return -EOPNOTSUPP; return ERR_PTR(-EOPNOTSUPP);
if (rq->alg == Z_EROFS_COMPRESSION_INTERLACED) { if (rq->alg == Z_EROFS_COMPRESSION_INTERLACED) {
cur = bs - (rq->pageofs_out & (bs - 1)); cur = bs - (rq->pageofs_out & (bs - 1));
pi = (rq->pageofs_in + rq->inputsize - cur) & ~PAGE_MASK; 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); kunmap_local(kin);
} }
DBG_BUGON(ni > nrpages_in); DBG_BUGON(ni > nrpages_in);
return 0; return NULL;
} }
int z_erofs_stream_switch_bufs(struct z_erofs_stream_dctx *dctx, void **dst, int z_erofs_stream_switch_bufs(struct z_erofs_stream_dctx *dctx, void **dst,

View File

@ -157,8 +157,6 @@ static int __z_erofs_deflate_decompress(struct z_erofs_decompress_req *rq,
break; break;
if (zerr == Z_STREAM_END && !rq->outputsize) if (zerr == Z_STREAM_END && !rq->outputsize)
break; break;
erofs_err(sb, "failed to decompress %d in[%u] out[%u]",
zerr, rq->inputsize, rq->outputsize);
err = -EFSCORRUPTED; err = -EFSCORRUPTED;
break; break;
} }
@ -178,8 +176,8 @@ static int __z_erofs_deflate_decompress(struct z_erofs_decompress_req *rq,
return err; return err;
} }
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 page **pgpl)
{ {
#ifdef CONFIG_EROFS_FS_ZIP_ACCEL #ifdef CONFIG_EROFS_FS_ZIP_ACCEL
int err; int err;
@ -187,11 +185,11 @@ static int z_erofs_deflate_decompress(struct z_erofs_decompress_req *rq,
if (!rq->partial_decoding) { if (!rq->partial_decoding) {
err = z_erofs_crypto_decompress(rq, pgpl); err = z_erofs_crypto_decompress(rq, pgpl);
if (err != -EOPNOTSUPP) if (err != -EOPNOTSUPP)
return err; return ERR_PTR(err);
} }
#endif #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 = { const struct z_erofs_decompressor z_erofs_deflate_decomp = {

View File

@ -146,8 +146,8 @@ static int z_erofs_load_lzma_config(struct super_block *sb,
return err; 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 page **pgpl)
{ {
struct super_block *sb = rq->sb; struct super_block *sb = rq->sb;
struct z_erofs_stream_dctx dctx = { .rq = rq, .no = -1, .ni = 0 }; 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)); min(rq->inputsize, sb->s_blocksize - rq->pageofs_in));
if (err) { if (err) {
kunmap_local(dctx.kin); kunmap_local(dctx.kin);
return err; return ERR_PTR(err);
} }
/* 2. get an available lzma context */ /* 2. get an available lzma context */
@ -207,8 +207,6 @@ static int z_erofs_lzma_decompress(struct z_erofs_decompress_req *rq,
if (xz_err != XZ_OK) { if (xz_err != XZ_OK) {
if (xz_err == XZ_STREAM_END && !rq->outputsize) if (xz_err == XZ_STREAM_END && !rq->outputsize)
break; break;
erofs_err(sb, "failed to decompress %d in[%u] out[%u]",
xz_err, rq->inputsize, rq->outputsize);
err = -EFSCORRUPTED; err = -EFSCORRUPTED;
break; break;
} }
@ -223,7 +221,7 @@ static int z_erofs_lzma_decompress(struct z_erofs_decompress_req *rq,
z_erofs_lzma_head = strm; z_erofs_lzma_head = strm;
spin_unlock(&z_erofs_lzma_lock); spin_unlock(&z_erofs_lzma_lock);
wake_up(&z_erofs_lzma_wq); wake_up(&z_erofs_lzma_wq);
return err; return ERR_PTR(err);
} }
const struct z_erofs_decompressor z_erofs_lzma_decomp = { 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; return strm ? -ENOMEM : 0;
} }
static int z_erofs_zstd_decompress(struct z_erofs_decompress_req *rq, static const char *z_erofs_zstd_decompress(struct z_erofs_decompress_req *rq,
struct page **pgpl) struct page **pgpl)
{ {
struct super_block *sb = rq->sb; struct super_block *sb = rq->sb;
struct z_erofs_stream_dctx dctx = { .rq = rq, .no = -1, .ni = 0 }; 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)); min(rq->inputsize, sb->s_blocksize - rq->pageofs_in));
if (err) { if (err) {
kunmap_local(dctx.kin); kunmap_local(dctx.kin);
return err; return ERR_PTR(err);
} }
/* 2. get an available ZSTD context */ /* 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) || if (zstd_is_error(zerr) ||
((rq->outputsize + dctx.avail_out) && (!zerr || (zerr > 0 && ((rq->outputsize + dctx.avail_out) && (!zerr || (zerr > 0 &&
!(rq->inputsize + in_buf.size - in_buf.pos))))) { !(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; err = -EFSCORRUPTED;
break; break;
} }
@ -210,7 +206,7 @@ static int z_erofs_zstd_decompress(struct z_erofs_decompress_req *rq,
z_erofs_zstd_head = strm; z_erofs_zstd_head = strm;
spin_unlock(&z_erofs_zstd_lock); spin_unlock(&z_erofs_zstd_lock);
wake_up(&z_erofs_zstd_wq); wake_up(&z_erofs_zstd_wq);
return err; return ERR_PTR(err);
} }
const struct z_erofs_decompressor z_erofs_zstd_decomp = { 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 erofs_sb_info *const sbi = EROFS_SB(be->sb);
struct z_erofs_pcluster *pcl = be->pcl; struct z_erofs_pcluster *pcl = be->pcl;
unsigned int pclusterpages = z_erofs_pclusterpages(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]; z_erofs_decomp[pcl->algorithmformat];
bool try_free = true;
int i, j, jtop, err2; int i, j, jtop, err2;
struct page *page; struct page *page;
bool overlapped; bool overlapped;
bool try_free = true; const char *reason;
mutex_lock(&pcl->lock); mutex_lock(&pcl->lock);
be->nr_pages = PAGE_ALIGN(pcl->length + pcl->pageofs_out) >> PAGE_SHIFT; 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); err2 = z_erofs_parse_in_bvecs(be, &overlapped);
if (err2) if (err2)
err = err2; err = err2;
if (!err) if (!err) {
err = decomp->decompress(&(struct z_erofs_decompress_req) { reason = alg->decompress(&(struct z_erofs_decompress_req) {
.sb = be->sb, .sb = be->sb,
.in = be->compressed_pages, .in = be->compressed_pages,
.out = be->decompressed_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 = pcl->besteffort ? GFP_KERNEL :
GFP_NOWAIT | __GFP_NORETRY GFP_NOWAIT | __GFP_NORETRY
}, be->pagepool); }, 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 */ /* must handle all compressed pages before actual file pages */
if (pcl->from_meta) { if (pcl->from_meta) {