Files
2026-08-18 09:38:06 +02:00

186 lines
4.3 KiB
C

/* SPDX-License-Identifier: BSD-2-Clause */
/* Minimal DEFLATE decompressor for EROFS FreeBSD */
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <contrib/zlib/zlib.h>
#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",
};