#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: deflate.c,v 1.26 2026/07/05 15:33:44 riastradh Exp $");
#include <sys/param.h>
#include <sys/types.h>
#include <net/zlib.h>
#include <sys/malloc.h>
#include <sys/systm.h>
#include <opencrypto/cryptodev.h>
#include <opencrypto/deflate.h>
#define ZBUF 10
struct deflate_buf {
uint8_t *out;
uint32_t size;
};
int window_inflate = -1 * MAX_WBITS;
int window_deflate = -12;
static void *
ocf_zalloc(void *nil, uint32_t type, uint32_t size)
{
void *ptr;
ptr = malloc(type *size, M_CRYPTO_DATA, M_NOWAIT);
return ptr;
}
static void
ocf_zfree(void *nil, void *ptr)
{
free(ptr, M_CRYPTO_DATA);
}
uint32_t
deflate_global(uint8_t *data, uint32_t size, int decomp, uint8_t **out,
int size_hint)
{
z_stream zbuf;
uint8_t *output;
uint32_t count, result, tocopy;
int error, i, j;
struct deflate_buf buf[ZBUF];
DPRINTF("size %u\n", size);
memset(&zbuf, 0, sizeof(z_stream));
zbuf.next_in = data;
zbuf.zalloc = ocf_zalloc;
zbuf.zfree = ocf_zfree;
zbuf.opaque = Z_NULL;
zbuf.avail_in = size;
if (!decomp) {
buf[0].size = size;
} else {
buf[0].size = MAX(size * 4, size_hint);
}
buf[0].out = malloc(buf[0].size, M_CRYPTO_DATA, M_NOWAIT);
if (buf[0].out == NULL)
return 0;
i = 1;
zbuf.next_out = buf[0].out;
zbuf.avail_out = buf[0].size;
error = decomp ? inflateInit2(&zbuf, window_inflate) :
deflateInit2(&zbuf, Z_DEFAULT_COMPRESSION, Z_METHOD,
window_deflate, Z_MEMLEVEL, Z_DEFAULT_STRATEGY);
if (error != Z_OK)
goto bad2;
for (;;) {
error = decomp ? inflate(&zbuf, Z_SYNC_FLUSH) :
deflate(&zbuf, Z_FINISH);
if (error == Z_STREAM_END)
break;
else if (decomp && (error == Z_OK || error == Z_BUF_ERROR)
&& zbuf.avail_in == 0 && zbuf.avail_out != 0)
break;
else if (error != Z_OK)
goto bad;
else if (zbuf.avail_out == 0) {
int nextsize = buf[i-1].size * 2;
if (i == ZBUF || nextsize > 1000000)
goto bad;
buf[i].out = malloc(nextsize, M_CRYPTO_DATA, M_NOWAIT);
if (buf[i].out == NULL)
goto bad;
zbuf.next_out = buf[i].out;
zbuf.avail_out = buf[i].size = nextsize;
i++;
}
}
result = count = zbuf.total_out;
if (i != 1) {
output = malloc(result, M_CRYPTO_DATA, M_NOWAIT);
if (output == NULL)
goto bad;
*out = output;
for (j = 0; j < i; j++) {
tocopy = MIN(count, buf[j].size);
KASSERT(tocopy || j == (i - 1));
memcpy(output, buf[j].out, tocopy);
output += tocopy;
free(buf[j].out, M_CRYPTO_DATA);
count -= tocopy;
}
KASSERT(count == 0);
} else {
*out = buf[0].out;
}
if (decomp)
inflateEnd(&zbuf);
else
deflateEnd(&zbuf);
return result;
bad:
if (decomp)
inflateEnd(&zbuf);
else
deflateEnd(&zbuf);
bad2:
for (j = 0; j < i; j++)
free(buf[j].out, M_CRYPTO_DATA);
return 0;
}
static const char gzip_header[10] = {
0x1f, 0x8b,
Z_DEFLATED,
0,
0, 0, 0, 0,
0,
0x03
};
#define GZIP_TAIL_SIZE 8
uint32_t
gzip_global(uint8_t *data, uint32_t size,
int decomp, uint8_t **out, int size_hint)
{
z_stream zbuf;
uint8_t *output;
uint32_t count, result;
int error, i, j;
struct deflate_buf buf[ZBUF];
uint32_t crc;
uint32_t isize = 0, icrc = 0;
DPRINTF("decomp %d, size %u\n", decomp, size);
memset(&zbuf, 0, sizeof(z_stream));
zbuf.zalloc = ocf_zalloc;
zbuf.zfree = ocf_zfree;
zbuf.opaque = Z_NULL;
if (!decomp) {
DPRINTF("compress malloc %u + %zu + %u = %zu\n",
size, sizeof(gzip_header), GZIP_TAIL_SIZE,
size + sizeof(gzip_header) + GZIP_TAIL_SIZE);
buf[0].size = size;
crc = crc32(0, data, size);
DPRINTF("size %u, crc 0x%x\n", size, crc);
zbuf.avail_in = size;
zbuf.next_in = data;
} else {
if (size <= sizeof(gzip_header) + GZIP_TAIL_SIZE) {
DPRINTF("not enough data (%u)\n", size);
return 0;
}
if (memcmp(data, gzip_header, sizeof(gzip_header)) != 0) {
DPRINTF("unsupported gzip header (%02x%02x)\n",
data[0], data[1]);
return 0;
} else {
DPRINTF("%d: gzip header ok\n",__LINE__);
}
memcpy(&isize, &data[size-sizeof(uint32_t)], sizeof(uint32_t));
LE32TOH(isize);
memcpy(&icrc, &data[size-2*sizeof(uint32_t)], sizeof(uint32_t));
LE32TOH(icrc);
DPRINTF("isize = %u (%02x %02x %02x %02x)\n",
isize,
data[size-4],
data[size-3],
data[size-2],
data[size-1]);
buf[0].size = isize;
crc = crc32(0, NULL, 0);
zbuf.next_in = data + sizeof(gzip_header);
zbuf.avail_in = size - sizeof(gzip_header) - GZIP_TAIL_SIZE;
}
buf[0].out = malloc(buf[0].size, M_CRYPTO_DATA, M_NOWAIT);
if (buf[0].out == NULL)
return 0;
zbuf.next_out = buf[0].out;
zbuf.avail_out = buf[0].size;
DPRINTF("zbuf avail_in %u, avail_out %u\n",
zbuf.avail_in, zbuf.avail_out);
i = 1;
error = decomp ? inflateInit2(&zbuf, window_inflate) :
deflateInit2(&zbuf, Z_DEFAULT_COMPRESSION, Z_METHOD,
window_deflate, Z_MEMLEVEL, Z_DEFAULT_STRATEGY);
if (error != Z_OK) {
printf("deflateInit2() failed\n");
goto bad2;
}
for (;;) {
DPRINTF("pre: %s in:%u out:%u\n", decomp ? "deflate()" : "inflate()",
zbuf.avail_in, zbuf.avail_out);
error = decomp ? inflate(&zbuf, Z_SYNC_FLUSH) :
deflate(&zbuf, Z_FINISH);
DPRINTF("post: %s in:%u out:%u\n", decomp ? "deflate()" : "inflate()",
zbuf.avail_in, zbuf.avail_out);
if (error == Z_STREAM_END)
break;
else if (decomp && error == Z_BUF_ERROR
&& zbuf.avail_in == 0 && zbuf.avail_out != 0)
break;
else if (error != Z_OK)
goto bad;
else if (zbuf.avail_out == 0) {
int nextsize = buf[i-1].size * 2;
if (i == ZBUF || nextsize > 1000000)
goto bad;
buf[i].out = malloc(nextsize, M_CRYPTO_DATA, M_NOWAIT);
if (buf[i].out == NULL)
goto bad;
zbuf.next_out = buf[i].out;
zbuf.avail_out = buf[i].size = nextsize;
i++;
}
}
if (decomp) {
count = result = zbuf.total_out;
} else {
result = zbuf.total_out + sizeof(gzip_header) + GZIP_TAIL_SIZE;
count = zbuf.total_out;
}
DPRINTF("in %u -> out %u\n", size, result);
*out = malloc(result, M_CRYPTO_DATA, M_NOWAIT);
if (*out == NULL)
goto bad;
output = *out;
if (decomp)
inflateEnd(&zbuf);
else {
deflateEnd(&zbuf);
memcpy(output, gzip_header, sizeof(gzip_header));
output += sizeof(gzip_header);
}
for (j = 0; j < i; j++) {
if (decomp) {
crc = crc32(crc, buf[j].out, MIN(count, buf[j].size));
}
if (count > buf[j].size) {
memcpy(output, buf[j].out, buf[j].size);
output += buf[j].size;
free(buf[j].out, M_CRYPTO_DATA);
count -= buf[j].size;
} else {
memcpy(output, buf[j].out, count);
output += count;
free(buf[j].out, M_CRYPTO_DATA);
count = 0;
}
}
if (!decomp) {
HTOLE32(crc);
memcpy(output, &crc, sizeof(uint32_t));
HTOLE32(size);
memcpy(output + sizeof(uint32_t), &size, sizeof(uint32_t));
DPRINTF("size = 0x%x (%02x %02x %02x %02x)\n",
size,
output[7],
output[3],
output[5],
output[4]);
} else {
if (crc != icrc || result != isize) {
DPRINTF("crc/size mismatch\n");
free(*out, M_CRYPTO_DATA);
*out = NULL;
return 0;
}
}
return result;
bad:
if (decomp)
inflateEnd(&zbuf);
else
deflateEnd(&zbuf);
bad2:
*out = NULL;
for (j = 0; j < i; j++)
free(buf[j].out, M_CRYPTO_DATA);
return 0;
}