Kaizen/external/unarr/zip/uncompress-zip.c

540 lines
19 KiB
C

/* Copyright 2015 the unarr project authors (see AUTHORS file).
License: LGPLv3 */
#include "zip.h"
#define ERR_UNCOMP UINT32_MAX
static bool zip_fill_input_buffer(ar_archive_zip *zip)
{
struct ar_archive_zip_uncomp *uncomp = &zip->uncomp;
size_t count;
if (uncomp->input.offset) {
memmove(&uncomp->input.data[0], &uncomp->input.data[uncomp->input.offset], uncomp->input.bytes_left);
uncomp->input.offset = 0;
}
count = sizeof(uncomp->input.data) - uncomp->input.bytes_left;
if (count > zip->progress.data_left)
count = zip->progress.data_left;
if (ar_read(zip->super.stream, &uncomp->input.data[uncomp->input.bytes_left], count) != count) {
warn("Unexpected EOF during decompression (invalid data size?)");
return false;
}
zip->progress.data_left -= count;
uncomp->input.bytes_left += (uint16_t)count;
uncomp->input.at_eof = !zip->progress.data_left;
return true;
}
/***** Deflate compression *****/
#ifdef HAVE_ZLIB
static void *gZlib_Alloc(void *opaque, uInt count, uInt size) { (void)opaque; return calloc(count, size); }
static void gZlib_Free(void *opaque, void *ptr) { (void)opaque; free(ptr); }
static bool zip_init_uncompress_deflate(struct ar_archive_zip_uncomp *uncomp)
{
int err;
uncomp->state.zstream.zalloc = gZlib_Alloc;
uncomp->state.zstream.zfree = gZlib_Free;
uncomp->state.zstream.opaque = NULL;
err = inflateInit2(&uncomp->state.zstream, -15);
return err == Z_OK;
}
static uint32_t zip_uncompress_data_deflate(struct ar_archive_zip_uncomp *uncomp, void *buffer, uint32_t buffer_size, bool is_last_chunk)
{
int err;
uncomp->state.zstream.next_in = &uncomp->input.data[uncomp->input.offset];
uncomp->state.zstream.avail_in = uncomp->input.bytes_left;
uncomp->state.zstream.next_out = buffer;
uncomp->state.zstream.avail_out = buffer_size;
err = inflate(&uncomp->state.zstream, Z_SYNC_FLUSH);
uncomp->input.offset += uncomp->input.bytes_left - (uint16_t)uncomp->state.zstream.avail_in;
uncomp->input.bytes_left = (uint16_t)uncomp->state.zstream.avail_in;
if (err != Z_OK && err != Z_STREAM_END) {
warn("Unexpected ZLIB error %d", err);
return ERR_UNCOMP;
}
if (err == Z_STREAM_END && (!is_last_chunk || uncomp->state.zstream.avail_out)) {
warn("Premature EOS in Deflate stream");
return ERR_UNCOMP;
}
return buffer_size - uncomp->state.zstream.avail_out;
}
static void zip_clear_uncompress_deflate(struct ar_archive_zip_uncomp *uncomp)
{
inflateEnd(&uncomp->state.zstream);
}
#endif
/***** Deflate(64) compression *****/
static bool zip_init_uncompress_deflate64(struct ar_archive_zip_uncomp *uncomp, bool deflate64)
{
uncomp->state.inflate = inflate_create(deflate64);
return uncomp->state.inflate != NULL;
}
static uint32_t zip_uncompress_data_deflate64(struct ar_archive_zip_uncomp *uncomp, void *buffer, uint32_t buffer_size, bool is_last_chunk)
{
size_t avail_in = uncomp->input.bytes_left;
size_t avail_out = buffer_size;
int result = inflate_process(uncomp->state.inflate, &uncomp->input.data[uncomp->input.offset], &avail_in, buffer, &avail_out);
uncomp->input.offset += uncomp->input.bytes_left - (uint16_t)avail_in;
uncomp->input.bytes_left = (uint16_t)avail_in;
if (result && result != EOF) {
warn("Unexpected Inflate error %d", result);
return ERR_UNCOMP;
}
if (result == EOF && (!is_last_chunk || avail_out)) {
warn("Premature EOS in Deflate stream");
return ERR_UNCOMP;
}
return buffer_size - (uint32_t)avail_out;
}
static void zip_clear_uncompress_deflate64(struct ar_archive_zip_uncomp *uncomp)
{
inflate_free(uncomp->state.inflate);
}
/***** BZIP2 compression *****/
#ifdef HAVE_BZIP2
static void *gBzip2_Alloc(void *opaque, int count, int size) { (void)opaque; return calloc(count, size); }
static void gBzip2_Free(void *opaque, void *ptr) { (void)opaque; free(ptr); }
static bool zip_init_uncompress_bzip2(struct ar_archive_zip_uncomp *uncomp)
{
int err;
uncomp->state.bstream.bzalloc = gBzip2_Alloc;
uncomp->state.bstream.bzfree = gBzip2_Free;
uncomp->state.bstream.opaque = NULL;
err = BZ2_bzDecompressInit(&uncomp->state.bstream, 0, 0);
return err == BZ_OK;
}
static uint32_t zip_uncompress_data_bzip2(struct ar_archive_zip_uncomp *uncomp, void *buffer, uint32_t buffer_size, bool is_last_chunk)
{
int err;
uncomp->state.bstream.next_in = (char *)&uncomp->input.data[uncomp->input.offset];
uncomp->state.bstream.avail_in = uncomp->input.bytes_left;
uncomp->state.bstream.next_out = (char *)buffer;
uncomp->state.bstream.avail_out = buffer_size;
err = BZ2_bzDecompress(&uncomp->state.bstream);
uncomp->input.offset += uncomp->input.bytes_left - (uint16_t)uncomp->state.bstream.avail_in;
uncomp->input.bytes_left = (uint16_t)uncomp->state.bstream.avail_in;
if (err != BZ_OK && err != BZ_STREAM_END) {
warn("Unexpected BZIP2 error %d", err);
return ERR_UNCOMP;
}
if (err == BZ_STREAM_END && (!is_last_chunk || uncomp->state.bstream.avail_out)) {
warn("Premature EOS in BZIP2 stream");
return ERR_UNCOMP;
}
return buffer_size - uncomp->state.bstream.avail_out;
}
static void zip_clear_uncompress_bzip2(struct ar_archive_zip_uncomp *uncomp)
{
BZ2_bzDecompressEnd(&uncomp->state.bstream);
}
#endif
/***** LZMA compression *****/
#ifdef HAVE_LIBLZMA
static void *gLzma_Alloc(void *opaque, size_t nmemb, size_t size)
{ (void)opaque; (void) nmemb; return malloc(size); }
static void gLzma_Free(void *opaque, void *ptr)
{ (void)opaque; free(ptr); }
static bool zip_init_uncompress_lzma(struct ar_archive_zip_uncomp *uncomp)
{
lzma_stream strm = LZMA_STREAM_INIT;
uncomp->state.lzmastream = strm;
#if LZMA_VERSION_MAJOR > 5 || (LZMA_VERSION_MAJOR == 5 && LZMA_VERSION_MINOR >= 2)
static const lzma_allocator allocator = { gLzma_Alloc, gLzma_Free, NULL };
#else
static lzma_allocator allocator = { gLzma_Alloc, gLzma_Free, NULL };
#endif
uncomp->state.lzmastream.allocator = &allocator;
return true;
}
static uint32_t zip_uncompress_data_lzma1(struct ar_archive_zip_uncomp *uncomp, void *buffer, uint32_t buffer_size, bool is_last_chunk)
{
int err;
if (uncomp->state.lzmastream.internal == NULL) {
uint8_t propsize;
propsize = uncomp->input.data[uncomp->input.offset + 2];
lzma_filter filters[2] = {{.id=LZMA_FILTER_LZMA1, .options=NULL},
{.id=LZMA_VLI_UNKNOWN, .options=NULL}};
err = lzma_properties_decode(
&filters[0], NULL,
&uncomp->input.data[uncomp->input.offset + 4], propsize);
if (err != LZMA_OK) {
warn("Properties error %d", err);
return ERR_UNCOMP;
}
err = lzma_raw_decoder(&uncomp->state.lzmastream, filters);
free(filters[0].options);
if (err != LZMA_OK) {
warn("Decoder init error %d", err);
return ERR_UNCOMP;
}
uncomp->input.offset += 4 + propsize;
uncomp->input.bytes_left -= 4 + propsize;
}
uncomp->state.lzmastream.next_in = &uncomp->input.data[uncomp->input.offset];
uncomp->state.lzmastream.avail_in = uncomp->input.bytes_left;
uncomp->state.lzmastream.next_out = buffer;
uncomp->state.lzmastream.avail_out = buffer_size;
err = lzma_code(&uncomp->state.lzmastream, LZMA_RUN);
uncomp->input.offset += (uint16_t)uncomp->input.bytes_left - (uint16_t)uncomp->state.lzmastream.avail_in;
uncomp->input.bytes_left = (uint16_t)uncomp->state.lzmastream.avail_in;
if (err != LZMA_OK && err != LZMA_STREAM_END) {
warn("Unexpected LZMA error %d", err);
warn("%d", buffer_size - uncomp->state.lzmastream.avail_out);
return ERR_UNCOMP;
}
if (err == LZMA_STREAM_END && (!is_last_chunk || uncomp->state.lzmastream.avail_out)) {
warn("Premature EOS in LZMA stream");
return ERR_UNCOMP;
}
return buffer_size - uncomp->state.lzmastream.avail_out;
}
static uint32_t zip_uncompress_data_xz(struct ar_archive_zip_uncomp *uncomp, void *buffer, uint32_t buffer_size, bool is_last_chunk)
{
int err;
if (uncomp->state.lzmastream.internal == NULL) {
/* restrict decoder memory usage to 100 MB */
err = lzma_stream_decoder(&uncomp->state.lzmastream, 100 << 20, 0);
if (err != LZMA_OK) {
warn("Unexpected LZMA Decoder init error %d", err);
return ERR_UNCOMP;
}
}
uncomp->state.lzmastream.next_in = &uncomp->input.data[uncomp->input.offset];
uncomp->state.lzmastream.avail_in = uncomp->input.bytes_left;
uncomp->state.lzmastream.next_out = buffer;
uncomp->state.lzmastream.avail_out = buffer_size;
err = lzma_code(&uncomp->state.lzmastream, LZMA_RUN);
uncomp->input.offset += (uint16_t)uncomp->input.bytes_left - (uint16_t)uncomp->state.lzmastream.avail_in;
uncomp->input.bytes_left = (uint16_t)uncomp->state.lzmastream.avail_in;
if (err != LZMA_OK && err != LZMA_STREAM_END) {
warn("Unexpected XZ error %d", err);
warn("%d", buffer_size - uncomp->state.lzmastream.avail_out);
return ERR_UNCOMP;
}
if (err == LZMA_STREAM_END && (!is_last_chunk || uncomp->state.lzmastream.avail_out)) {
warn("Premature EOS in XZ stream");
return ERR_UNCOMP;
}
return buffer_size - uncomp->state.lzmastream.avail_out;
}
static void zip_clear_uncompress_lzma(struct ar_archive_zip_uncomp *uncomp)
{
lzma_end(&uncomp->state.lzmastream);
}
#else //HAVE_LIBLZMA
static void *gLzma_Alloc(ISzAllocPtr self, size_t size) { (void)self; return malloc(size); }
static void gLzma_Free(ISzAllocPtr self, void *ptr) { (void)self; free(ptr); }
static bool zip_init_uncompress_lzma(struct ar_archive_zip_uncomp *uncomp, uint16_t flags)
{
uncomp->state.lzma.alloc.Alloc = gLzma_Alloc;
uncomp->state.lzma.alloc.Free = gLzma_Free;
uncomp->state.lzma.finish = (flags & (1 << 1)) ? LZMA_FINISH_END : LZMA_FINISH_ANY;
LzmaDec_Construct(&uncomp->state.lzma.dec);
return true;
}
static uint32_t zip_uncompress_data_lzma(struct ar_archive_zip_uncomp *uncomp, void *buffer, uint32_t buffer_size, bool is_last_chunk)
{
SizeT srclen, dstlen;
ELzmaStatus status;
ELzmaFinishMode finish;
SRes res;
if (!uncomp->state.lzma.dec.dic) {
uint8_t propsize;
if (uncomp->input.bytes_left < 9) {
warn("Insufficient data in compressed stream");
return ERR_UNCOMP;
}
propsize = uncomp->input.data[uncomp->input.offset + 2];
if (uncomp->input.data[uncomp->input.offset + 3] != 0 || uncomp->input.bytes_left < 4 + propsize) {
warn("Insufficient data in compressed stream");
return ERR_UNCOMP;
}
res = LzmaDec_Allocate(&uncomp->state.lzma.dec, &uncomp->input.data[uncomp->input.offset + 4], propsize, &uncomp->state.lzma.alloc);
uncomp->input.offset += 4 + propsize;
uncomp->input.bytes_left -= 4 + propsize;
if (res != SZ_OK)
return ERR_UNCOMP;
LzmaDec_Init(&uncomp->state.lzma.dec);
}
srclen = uncomp->input.bytes_left;
dstlen = buffer_size;
finish = uncomp->input.at_eof && is_last_chunk ? uncomp->state.lzma.finish : LZMA_FINISH_ANY;
res = LzmaDec_DecodeToBuf(&uncomp->state.lzma.dec, buffer, &dstlen, &uncomp->input.data[uncomp->input.offset], &srclen, finish, &status);
uncomp->input.offset += (uint16_t)srclen;
uncomp->input.bytes_left -= (uint16_t)srclen;
if (res != SZ_OK || (srclen == 0 && dstlen == 0)) {
warn("Unexpected LZMA error %d", res);
return ERR_UNCOMP;
}
if (status == LZMA_STATUS_FINISHED_WITH_MARK && (!is_last_chunk || dstlen != buffer_size)) {
warn("Premature EOS in LZMA stream");
return ERR_UNCOMP;
}
return (uint32_t)dstlen;
}
static void zip_clear_uncompress_lzma(struct ar_archive_zip_uncomp *uncomp)
{
LzmaDec_Free(&uncomp->state.lzma.dec, &uncomp->state.lzma.alloc);
}
#endif //HAVE_LIBLZMA
/***** PPMd compression *****/
static void *gPpmd_Alloc(ISzAllocPtr self, size_t size) { (void)self; return malloc(size); }
static void gPpmd_Free(ISzAllocPtr self, void *ptr) { (void)self; free(ptr); }
static Byte gPpmd_ByteIn_Read(const IByteIn *p)
{
struct ByteReader *self = (struct ByteReader *) p;
if (!self->input->bytes_left && (!self->zip->progress.data_left || !zip_fill_input_buffer(self->zip)))
return 0xFF;
self->input->bytes_left--;
return self->input->data[self->input->offset++];
}
static bool zip_init_uncompress_ppmd(ar_archive_zip *zip)
{
struct ar_archive_zip_uncomp *uncomp = &zip->uncomp;
uncomp->state.ppmd8.alloc.Alloc = gPpmd_Alloc;
uncomp->state.ppmd8.alloc.Free = gPpmd_Free;
uncomp->state.ppmd8.bytein.super.Read = gPpmd_ByteIn_Read;
uncomp->state.ppmd8.bytein.input = &uncomp->input;
uncomp->state.ppmd8.bytein.zip = zip;
uncomp->state.ppmd8.ctx.Stream.In = &uncomp->state.ppmd8.bytein.super;
Ppmd8_Construct(&uncomp->state.ppmd8.ctx);
return true;
}
static uint32_t zip_uncompress_data_ppmd(struct ar_archive_zip_uncomp *uncomp, void *buffer, uint32_t buffer_size, bool is_last_chunk)
{
uint32_t bytes_done = 0;
if (!uncomp->state.ppmd8.ctx.Base) {
uint8_t order, size, method;
if (uncomp->input.bytes_left < 2) {
warn("Insufficient data in compressed stream");
return ERR_UNCOMP;
}
order = (uncomp->input.data[uncomp->input.offset] & 0x0F) + 1;
size = ((uncomp->input.data[uncomp->input.offset] >> 4) | ((uncomp->input.data[uncomp->input.offset + 1] << 4) & 0xFF));
method = uncomp->input.data[uncomp->input.offset + 1] >> 4;
uncomp->input.bytes_left -= 2;
uncomp->input.offset += 2;
if (order < 2 || method > 2) {
warn("Invalid PPMd data stream");
return ERR_UNCOMP;
}
#ifndef PPMD8_FREEZE_SUPPORT
if (order == 2) {
warn("PPMd freeze method isn't supported");
return ERR_UNCOMP;
}
#endif
if (!Ppmd8_Alloc(&uncomp->state.ppmd8.ctx, (size + 1) << 20, &uncomp->state.ppmd8.alloc))
return ERR_UNCOMP;
if (!Ppmd8_Init_RangeDec(&uncomp->state.ppmd8.ctx))
return ERR_UNCOMP;
Ppmd8_Init(&uncomp->state.ppmd8.ctx, order, method);
}
while (bytes_done < buffer_size) {
int symbol = Ppmd8_DecodeSymbol(&uncomp->state.ppmd8.ctx);
if (symbol < 0) {
warn("Invalid PPMd data stream");
return ERR_UNCOMP;
}
((uint8_t *)buffer)[bytes_done++] = (uint8_t)symbol;
}
if (is_last_chunk) {
int symbol = Ppmd8_DecodeSymbol(&uncomp->state.ppmd8.ctx);
if (symbol != -1 || !Ppmd8_RangeDec_IsFinishedOK(&uncomp->state.ppmd8.ctx)) {
warn("Invalid PPMd data stream");
return ERR_UNCOMP;
}
}
return bytes_done;
}
static void zip_clear_uncompress_ppmd(struct ar_archive_zip_uncomp *uncomp)
{
Ppmd8_Free(&uncomp->state.ppmd8.ctx, &uncomp->state.ppmd8.alloc);
}
/***** common decompression handling *****/
static bool zip_init_uncompress(ar_archive_zip *zip)
{
struct ar_archive_zip_uncomp *uncomp = &zip->uncomp;
if (uncomp->initialized)
return true;
memset(uncomp, 0, sizeof(*uncomp));
if (zip->entry.method == METHOD_DEFLATE) {
#ifdef HAVE_ZLIB
if (zip_init_uncompress_deflate(uncomp)) {
uncomp->uncompress_data = zip_uncompress_data_deflate;
uncomp->clear_state = zip_clear_uncompress_deflate;
}
#else
if (zip_init_uncompress_deflate64(uncomp, false)) {
uncomp->uncompress_data = zip_uncompress_data_deflate64;
uncomp->clear_state = zip_clear_uncompress_deflate64;
}
#endif
}
else if (zip->entry.method == METHOD_DEFLATE64) {
if (zip_init_uncompress_deflate64(uncomp, true)) {
uncomp->uncompress_data = zip_uncompress_data_deflate64;
uncomp->clear_state = zip_clear_uncompress_deflate64;
}
}
else if (zip->entry.method == METHOD_BZIP2) {
#ifdef HAVE_BZIP2
if (zip_init_uncompress_bzip2(uncomp)) {
uncomp->uncompress_data = zip_uncompress_data_bzip2;
uncomp->clear_state = zip_clear_uncompress_bzip2;
}
#else
warn("BZIP2 support requires BZIP2 (define HAVE_BZIP2)");
#endif
}
#ifdef HAVE_LIBLZMA
else if (zip->entry.method == METHOD_LZMA) {
if (zip_init_uncompress_lzma(uncomp)) {
uncomp->uncompress_data = zip_uncompress_data_lzma1;
uncomp->clear_state = zip_clear_uncompress_lzma;
}
}
else if (zip->entry.method == METHOD_XZ) {
if (zip_init_uncompress_lzma(uncomp)) {
uncomp->uncompress_data = zip_uncompress_data_xz;
uncomp->clear_state = zip_clear_uncompress_lzma;
}
}
#else
else if (zip->entry.method == METHOD_LZMA) {
if (zip_init_uncompress_lzma(uncomp, zip->entry.flags)) {
uncomp->uncompress_data = zip_uncompress_data_lzma;
uncomp->clear_state = zip_clear_uncompress_lzma;
}
}
#endif // HAVE_LIBLZMA
else if (zip->entry.method == METHOD_PPMD) {
if (zip_init_uncompress_ppmd(zip)) {
uncomp->uncompress_data = zip_uncompress_data_ppmd;
uncomp->clear_state = zip_clear_uncompress_ppmd;
}
}
else
warn("Unsupported compression method %d", zip->entry.method);
uncomp->initialized = uncomp->uncompress_data != NULL && uncomp->clear_state != NULL;
return uncomp->initialized;
}
void zip_clear_uncompress(struct ar_archive_zip_uncomp *uncomp)
{
if (!uncomp->initialized)
return;
uncomp->clear_state(uncomp);
uncomp->initialized = false;
}
bool zip_uncompress_part(ar_archive_zip *zip, void *buffer, size_t buffer_size)
{
struct ar_archive_zip_uncomp *uncomp = &zip->uncomp;
uint32_t count;
if (!zip_init_uncompress(zip))
return false;
for (;;) {
if (buffer_size == 0)
return true;
if (uncomp->input.bytes_left < sizeof(uncomp->input.data) / 2 && zip->progress.data_left) {
if (!zip_fill_input_buffer(zip))
return false;
}
count = buffer_size >= UINT32_MAX ? UINT32_MAX - 1 : (uint32_t)buffer_size;
count = uncomp->uncompress_data(uncomp, buffer, count, zip->progress.bytes_done + count == zip->super.entry_size_uncompressed);
if (count == ERR_UNCOMP)
return false;
if (count == 0 && !zip->progress.data_left) {
warn("Insufficient data in compressed stream");
return false;
}
zip->progress.bytes_done += count;
buffer = (uint8_t *)buffer + count;
buffer_size -= count;
}
}