diff options
Diffstat (limited to 'lib/io/uncompress')
-rw-r--r-- | lib/io/uncompress/autodetect.c | 55 | ||||
-rw-r--r-- | lib/io/uncompress/bzip2.c | 118 | ||||
-rw-r--r-- | lib/io/uncompress/gzip.c | 106 | ||||
-rw-r--r-- | lib/io/uncompress/istream_compressor.c | 69 | ||||
-rw-r--r-- | lib/io/uncompress/xz.c | 96 | ||||
-rw-r--r-- | lib/io/uncompress/zstd.c | 81 |
6 files changed, 525 insertions, 0 deletions
diff --git a/lib/io/uncompress/autodetect.c b/lib/io/uncompress/autodetect.c new file mode 100644 index 0000000..dde33c8 --- /dev/null +++ b/lib/io/uncompress/autodetect.c @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * autodetect.c + * + * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at> + */ +#include "../internal.h" + +static const struct { + int id; + const sqfs_u8 *value; + size_t len; +} magic[] = { + { IO_COMPRESSOR_GZIP, (const sqfs_u8 *)"\x1F\x8B\x08", 3 }, + { IO_COMPRESSOR_XZ, (const sqfs_u8 *)("\xFD" "7zXZ"), 6 }, + { IO_COMPRESSOR_ZSTD, (const sqfs_u8 *)"\x28\xB5\x2F\xFD", 4 }, + { IO_COMPRESSOR_BZIP2, (const sqfs_u8 *)"BZh", 3 }, +}; + +int istream_detect_compressor(istream_t *strm, + int (*probe)(const sqfs_u8 *data, size_t size)) +{ + size_t i; + int ret; + + ret = istream_precache(strm); + if (ret != 0) + return ret; + + if (probe != NULL) { + ret = probe(strm->buffer + strm->buffer_offset, + strm->buffer_used - strm->buffer_offset); + if (ret < 0) + return ret; + + /* XXX: this means the data is uncompressed. We do this check + first since it might be perfectly OK for the uncompressed + data to contain a magic number from the table. */ + if (ret > 0) + return 0; + } + + for (i = 0; i < sizeof(magic) / sizeof(magic[0]); ++i) { + if ((strm->buffer_used - strm->buffer_offset) < magic[i].len) + continue; + + ret = memcmp(strm->buffer + strm->buffer_offset, + magic[i].value, magic[i].len); + + if (ret == 0) + return magic[i].id; + } + + return 0; +} diff --git a/lib/io/uncompress/bzip2.c b/lib/io/uncompress/bzip2.c new file mode 100644 index 0000000..3b44383 --- /dev/null +++ b/lib/io/uncompress/bzip2.c @@ -0,0 +1,118 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * bzip2.c + * + * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at> + */ +#include "../internal.h" + +#include <bzlib.h> + +typedef struct { + istream_comp_t base; + + bool initialized; + bz_stream strm; +} istream_bzip2_t; + +static int precache(istream_t *base) +{ + istream_bzip2_t *bzip2 = (istream_bzip2_t *)base; + istream_t *wrapped = ((istream_comp_t *)base)->wrapped; + size_t avail; + int ret; + + for (;;) { + if (!bzip2->initialized) { + if (BZ2_bzDecompressInit(&bzip2->strm, 0, 0) != BZ_OK) { + fprintf(stderr, "%s: error initializing " + "bzip2 decompressor.\n", + wrapped->get_filename(wrapped)); + return -1; + } + + bzip2->initialized = true; + } + + ret = istream_precache(wrapped); + if (ret != 0) + return ret; + + avail = wrapped->buffer_used; + if ((sizeof(size_t) > sizeof(unsigned int)) && + (avail > (size_t)UINT_MAX)) { + avail = UINT_MAX; + } + + bzip2->strm.next_in = (char *)wrapped->buffer; + bzip2->strm.avail_in = (unsigned int)avail; + + if (base->buffer_used > BUFSZ) + base->buffer_used = BUFSZ; + + avail = BUFSZ - base->buffer_used; + + if ((sizeof(size_t) > sizeof(unsigned int)) && + (avail > (size_t)UINT_MAX)) { + avail = UINT_MAX; + } + + bzip2->strm.next_out = (char *)base->buffer + base->buffer_used; + bzip2->strm.avail_out = (unsigned int)avail; + + if (bzip2->strm.avail_out < 1) + break; + + ret = BZ2_bzDecompress(&bzip2->strm); + + if (ret < 0) { + fprintf(stderr, "%s: internal error in bzip2 " + "decompressor.\n", + wrapped->get_filename(wrapped)); + return -1; + } + + base->buffer_used = BUFSZ - bzip2->strm.avail_out; + wrapped->buffer_offset = wrapped->buffer_used - + bzip2->strm.avail_in; + + if (ret == BZ_STREAM_END) { + if (istream_precache(wrapped)) + return -1; + + BZ2_bzDecompressEnd(&bzip2->strm); + bzip2->initialized = false; + + if (wrapped->buffer_used == 0) { + base->eof = true; + break; + } + } + } + + return 0; +} + +static void cleanup(istream_comp_t *base) +{ + istream_bzip2_t *bzip2 = (istream_bzip2_t *)base; + + if (bzip2->initialized) + BZ2_bzDecompressEnd(&bzip2->strm); +} + +istream_comp_t *istream_bzip2_create(const char *filename) +{ + istream_bzip2_t *bzip2 = calloc(1, sizeof(*bzip2)); + istream_comp_t *base = (istream_comp_t *)bzip2; + + if (bzip2 == NULL) { + fprintf(stderr, "%s: creating bzip2 compressor: %s.\n", + filename, strerror(errno)); + return NULL; + } + + ((istream_t *)base)->precache = precache; + base->cleanup = cleanup; + return base; +} diff --git a/lib/io/uncompress/gzip.c b/lib/io/uncompress/gzip.c new file mode 100644 index 0000000..1d6274c --- /dev/null +++ b/lib/io/uncompress/gzip.c @@ -0,0 +1,106 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * gzip.c + * + * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at> + */ +#include "../internal.h" + +#include <zlib.h> + +typedef struct { + istream_comp_t base; + + z_stream strm; +} istream_gzip_t; + +static int precache(istream_t *base) +{ + istream_t *wrapped = ((istream_comp_t *)base)->wrapped; + istream_gzip_t *gzip = (istream_gzip_t *)base; + size_t avail_in, avail_out; + int ret; + + for (;;) { + ret = istream_precache(wrapped); + if (ret != 0) + return ret; + + avail_in = wrapped->buffer_used; + avail_out = BUFSZ - base->buffer_used; + + if (sizeof(size_t) > sizeof(uInt)) { + gzip->strm.avail_in = ~((uInt)0U); + gzip->strm.avail_out = ~((uInt)0U); + + if ((size_t)gzip->strm.avail_in > avail_in) + gzip->strm.avail_in = (uInt)avail_in; + + if ((size_t)gzip->strm.avail_out > avail_out) + gzip->strm.avail_out = (uInt)avail_out; + } else { + gzip->strm.avail_in = (uInt)avail_in; + gzip->strm.avail_out = (uInt)avail_out; + } + + gzip->strm.next_in = wrapped->buffer; + gzip->strm.next_out = base->buffer + base->buffer_used; + + ret = inflate(&gzip->strm, Z_NO_FLUSH); + + wrapped->buffer_offset = wrapped->buffer_used - + gzip->strm.avail_in; + + base->buffer_used = BUFSZ - gzip->strm.avail_out; + + if (ret == Z_BUF_ERROR) + break; + + if (ret == Z_STREAM_END) { + base->eof = true; + break; + } + + if (ret != Z_OK) { + fprintf(stderr, + "%s: internal error in gzip decoder.\n", + wrapped->get_filename(wrapped)); + return -1; + } + } + + return 0; +} + +static void cleanup(istream_comp_t *base) +{ + istream_gzip_t *gzip = (istream_gzip_t *)base; + + inflateEnd(&gzip->strm); +} + +istream_comp_t *istream_gzip_create(const char *filename) +{ + istream_gzip_t *gzip = calloc(1, sizeof(*gzip)); + istream_comp_t *base = (istream_comp_t *)gzip; + int ret; + + if (gzip == NULL) { + fprintf(stderr, "%s: creating gzip decoder: %s.\n", + filename, strerror(errno)); + return NULL; + } + + ret = inflateInit2(&gzip->strm, 16 + 15); + if (ret != Z_OK) { + fprintf(stderr, + "%s: internal error creating gzip reader.\n", + filename); + free(gzip); + return NULL; + } + + ((istream_t *)base)->precache = precache; + base->cleanup = cleanup; + return base; +} diff --git a/lib/io/uncompress/istream_compressor.c b/lib/io/uncompress/istream_compressor.c new file mode 100644 index 0000000..ab9ad8b --- /dev/null +++ b/lib/io/uncompress/istream_compressor.c @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * istream_compressor.c + * + * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at> + */ +#include "../internal.h" + +static const char *comp_get_filename(istream_t *strm) +{ + istream_comp_t *comp = (istream_comp_t *)strm; + + return comp->wrapped->get_filename(comp->wrapped); +} + +static void comp_destroy(sqfs_object_t *obj) +{ + istream_comp_t *comp = (istream_comp_t *)obj; + + comp->cleanup(comp); + sqfs_destroy(comp->wrapped); + free(comp); +} + +istream_t *istream_compressor_create(istream_t *strm, int comp_id) +{ + istream_comp_t *comp = NULL; + sqfs_object_t *obj; + istream_t *base; + + switch (comp_id) { + case IO_COMPRESSOR_GZIP: +#ifdef WITH_GZIP + comp = istream_gzip_create(strm->get_filename(strm)); +#endif + break; + case IO_COMPRESSOR_XZ: +#ifdef WITH_XZ + comp = istream_xz_create(strm->get_filename(strm)); +#endif + break; + case IO_COMPRESSOR_ZSTD: +#if defined(WITH_ZSTD) && defined(HAVE_ZSTD_STREAM) + comp = istream_zstd_create(strm->get_filename(strm)); +#endif + break; + case IO_COMPRESSOR_BZIP2: +#ifdef WITH_BZIP2 + comp = istream_bzip2_create(strm->get_filename(strm)); +#endif + break; + default: + break; + } + + if (comp == NULL) + return NULL; + + comp->wrapped = strm; + + base = (istream_t *)comp; + base->get_filename = comp_get_filename; + base->buffer = comp->uncompressed; + base->eof = false; + + obj = (sqfs_object_t *)comp; + obj->destroy = comp_destroy; + return base; +} diff --git a/lib/io/uncompress/xz.c b/lib/io/uncompress/xz.c new file mode 100644 index 0000000..0fd9ce6 --- /dev/null +++ b/lib/io/uncompress/xz.c @@ -0,0 +1,96 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * xz.c + * + * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at> + */ +#include "../internal.h" + +#include <lzma.h> + +typedef struct { + istream_comp_t base; + + lzma_stream strm; +} istream_xz_t; + +static int precache(istream_t *base) +{ + istream_xz_t *xz = (istream_xz_t *)base; + istream_t *wrapped = ((istream_comp_t *)base)->wrapped; + lzma_action action; + lzma_ret ret_xz; + int ret; + + for (;;) { + ret = istream_precache(wrapped); + if (ret != 0) + return ret; + + action = wrapped->eof ? LZMA_FINISH : LZMA_RUN; + + xz->strm.avail_in = wrapped->buffer_used; + xz->strm.next_in = wrapped->buffer; + + xz->strm.avail_out = BUFSZ - base->buffer_used; + xz->strm.next_out = base->buffer + base->buffer_used; + + ret_xz = lzma_code(&xz->strm, action); + + base->buffer_used = BUFSZ - xz->strm.avail_out; + wrapped->buffer_offset = wrapped->buffer_used - + xz->strm.avail_in; + + if (ret_xz == LZMA_BUF_ERROR) + break; + + if (ret_xz == LZMA_STREAM_END) { + base->eof = true; + break; + } + + if (ret_xz != LZMA_OK) { + fprintf(stderr, + "%s: internal error in xz decoder.\n", + wrapped->get_filename(wrapped)); + return -1; + } + } + + return 0; +} + +static void cleanup(istream_comp_t *base) +{ + istream_xz_t *xz = (istream_xz_t *)base; + + lzma_end(&xz->strm); +} + +istream_comp_t *istream_xz_create(const char *filename) +{ + istream_xz_t *xz = calloc(1, sizeof(*xz)); + istream_comp_t *base = (istream_comp_t *)xz; + sqfs_u64 memlimit = 65 * 1024 * 1024; + lzma_ret ret_xz; + + if (xz == NULL) { + fprintf(stderr, "%s: creating xz decoder: %s.\n", + filename, strerror(errno)); + return NULL; + } + + ret_xz = lzma_stream_decoder(&xz->strm, memlimit, LZMA_CONCATENATED); + + if (ret_xz != LZMA_OK) { + fprintf(stderr, + "%s: error initializing xz decoder.\n", + filename); + free(xz); + return NULL; + } + + ((istream_t *)base)->precache = precache; + base->cleanup = cleanup; + return base; +} diff --git a/lib/io/uncompress/zstd.c b/lib/io/uncompress/zstd.c new file mode 100644 index 0000000..fd22cbf --- /dev/null +++ b/lib/io/uncompress/zstd.c @@ -0,0 +1,81 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * zstd.c + * + * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at> + */ +#include "../internal.h" + +#include <zstd.h> + +#ifdef HAVE_ZSTD_STREAM +typedef struct { + istream_comp_t base; + + ZSTD_DStream* strm; +} istream_zstd_t; + +static int precache(istream_t *base) +{ + istream_zstd_t *zstd = (istream_zstd_t *)base; + istream_t *wrapped = ((istream_comp_t *)base)->wrapped; + ZSTD_outBuffer out; + ZSTD_inBuffer in; + size_t ret; + + if (istream_precache(wrapped)) + return -1; + + memset(&in, 0, sizeof(in)); + memset(&out, 0, sizeof(out)); + + in.src = wrapped->buffer; + in.size = wrapped->buffer_used; + + out.dst = ((istream_comp_t *)base)->uncompressed + base->buffer_used; + out.size = BUFSZ - base->buffer_used; + + ret = ZSTD_decompressStream(zstd->strm, &out, &in); + + if (ZSTD_isError(ret)) { + fprintf(stderr, "%s: error in zstd decoder.\n", + wrapped->get_filename(wrapped)); + return -1; + } + + wrapped->buffer_offset = in.pos; + base->buffer_used += out.pos; + return 0; +} + +static void cleanup(istream_comp_t *base) +{ + istream_zstd_t *zstd = (istream_zstd_t *)base; + + ZSTD_freeDStream(zstd->strm); +} + +istream_comp_t *istream_zstd_create(const char *filename) +{ + istream_zstd_t *zstd = calloc(1, sizeof(*zstd)); + istream_comp_t *base = (istream_comp_t *)zstd; + + if (zstd == NULL) { + fprintf(stderr, "%s: creating zstd decoder: %s.\n", + filename, strerror(errno)); + return NULL; + } + + zstd->strm = ZSTD_createDStream(); + if (zstd->strm == NULL) { + fprintf(stderr, "%s: error creating zstd decoder.\n", + filename); + free(zstd); + return NULL; + } + + ((istream_t *)base)->precache = precache; + base->cleanup = cleanup; + return base; +} +#endif /* HAVE_ZSTD_STREAM */ |