/* SPDX-License-Identifier: BSD-2-Clause */ /* Minimal DEFLATE decompressor for EROFS FreeBSD */ #include #include #include #include #include "compress.h" struct z_erofs_deflate_ctx { struct erofs_stream_ctx pool; z_stream stream; bool initialized; }; _Static_assert(sizeof(struct z_erofs_deflate_ctx) <= EROFS_STREAM_CTX_WRAPPER_SIZE, "Deflate stream wrapper exceeds UMA item"); static voidpf z_erofs_deflate_alloc(voidpf opaque, uInt items, uInt size) { struct erofs_stream_ctx *pool; size_t bytes; pool = opaque; if (__builtin_mul_overflow((size_t)items, (size_t)size, &bytes)) { pool->allocation_failed = true; return (NULL); } return (z_erofs_stream_ctx_alloc(pool, bytes)); } static void z_erofs_deflate_free(voidpf opaque, voidpf address) { z_erofs_stream_ctx_free(opaque, address); } static int z_erofs_load_deflate_config(struct erofs_sb_info *sbi, const struct erofs_super_block *dsb, const void *data, size_t size) { const struct z_erofs_deflate_cfgs *deflate; (void)dsb; if (size < sizeof(*deflate)) return (EINTEGRITY); deflate = data; if (deflate->windowbits < 8 || deflate->windowbits > 15) return (EOPNOTSUPP); sbi->deflate_windowbits = deflate->windowbits; return (0); } static int z_erofs_deflate_error(int ret) { switch (ret) { case Z_MEM_ERROR: return (ENOMEM); case Z_VERSION_ERROR: return (EOPNOTSUPP); case Z_NEED_DICT: case Z_DATA_ERROR: case Z_BUF_ERROR: return (EINTEGRITY); default: return (EIO); } } static int z_erofs_deflate_ctx_init(struct erofs_stream_ctx *pool) { struct z_erofs_deflate_ctx *ctx; int ret; ctx = (struct z_erofs_deflate_ctx *)pool; bzero(&ctx->stream, sizeof(ctx->stream)); ctx->stream.zalloc = z_erofs_deflate_alloc; ctx->stream.zfree = z_erofs_deflate_free; ctx->stream.opaque = pool; ret = inflateInit2(&ctx->stream, -pool->sbi->deflate_windowbits); if (ret != Z_OK) return (z_erofs_deflate_error(ret)); ctx->initialized = true; return (0); } static void z_erofs_deflate_ctx_fini(struct erofs_stream_ctx *pool) { struct z_erofs_deflate_ctx *ctx; ctx = (struct z_erofs_deflate_ctx *)pool; if (ctx->initialized) { (void)inflateEnd(&ctx->stream); ctx->initialized = false; } } static int z_erofs_deflate_finish(const struct z_erofs_decompress_req *rq, int ret, uInt avail_in) { if (rq->partial_decoding) return (0); if (ret != Z_STREAM_END || avail_in != 0) return (EINTEGRITY); return (0); } static int z_erofs_deflate_decompress(const struct z_erofs_decompress_req *rq) { struct erofs_stream_ctx *pool; struct z_erofs_deflate_ctx *ctx; z_stream *strm; uInt in_before, out_before; int error, ret; if (rq->sbi->deflate_windowbits < 8 || rq->sbi->deflate_windowbits > MAX_WBITS) return (EOPNOTSUPP); if (rq->inputsize > (size_t)(uInt)-1 || rq->outputsize > (size_t)(uInt)-1) return (EOVERFLOW); if (rq->outputsize == 0) return (EINTEGRITY); error = z_erofs_stream_ctx_get(rq->sbi, Z_EROFS_COMPRESSION_DEFLATE, sizeof(*ctx), z_erofs_deflate_ctx_init, z_erofs_deflate_ctx_fini, &pool); if (error != 0) return (error); ctx = (struct z_erofs_deflate_ctx *)pool; strm = &ctx->stream; pool->allocation_failed = false; ret = inflateReset2(strm, -rq->sbi->deflate_windowbits); if (ret != Z_OK) { error = z_erofs_deflate_error(ret); z_erofs_stream_ctx_put(pool, false); return (error); } strm->next_in = __DECONST(void *, rq->in); strm->avail_in = rq->inputsize; strm->next_out = rq->out; strm->avail_out = rq->outputsize; error = 0; ret = Z_OK; while (strm->avail_out != 0) { in_before = strm->avail_in; out_before = strm->avail_out; ret = inflate(strm, Z_SYNC_FLUSH); if (ret == Z_STREAM_END) break; if (ret != Z_OK) { error = z_erofs_deflate_error(ret); break; } if (strm->avail_in == in_before && strm->avail_out == out_before) { error = EINTEGRITY; break; } } if (error == 0 && strm->avail_out != 0) error = EINTEGRITY; else if (error == 0) error = z_erofs_deflate_finish(rq, ret, strm->avail_in); if (pool->allocation_failed) error = ENOMEM; z_erofs_stream_ctx_put(pool, error == 0); return (error); } const struct z_erofs_decompressor z_erofs_deflate_decomp = { .config = z_erofs_load_deflate_config, .decompress = z_erofs_deflate_decompress, .supports_subextent = 1, .name = "deflate", };