From cdccc69c62579b0c13b35fad0728079652b8f3c9 Mon Sep 17 00:00:00 2001 From: David Oberhollenzer Date: Tue, 31 Jan 2023 11:21:30 +0100 Subject: Move library source into src sub-directory Signed-off-by: David Oberhollenzer --- lib/sqfs/src/comp/compressor.c | 218 +++++++++++++++++++++++++++ lib/sqfs/src/comp/gzip.c | 307 ++++++++++++++++++++++++++++++++++++++ lib/sqfs/src/comp/internal.h | 46 ++++++ lib/sqfs/src/comp/lz4.c | 172 ++++++++++++++++++++++ lib/sqfs/src/comp/lzma.c | 281 +++++++++++++++++++++++++++++++++++ lib/sqfs/src/comp/xz.c | 324 +++++++++++++++++++++++++++++++++++++++++ lib/sqfs/src/comp/zstd.c | 172 ++++++++++++++++++++++ 7 files changed, 1520 insertions(+) create mode 100644 lib/sqfs/src/comp/compressor.c create mode 100644 lib/sqfs/src/comp/gzip.c create mode 100644 lib/sqfs/src/comp/internal.h create mode 100644 lib/sqfs/src/comp/lz4.c create mode 100644 lib/sqfs/src/comp/lzma.c create mode 100644 lib/sqfs/src/comp/xz.c create mode 100644 lib/sqfs/src/comp/zstd.c (limited to 'lib/sqfs/src/comp') diff --git a/lib/sqfs/src/comp/compressor.c b/lib/sqfs/src/comp/compressor.c new file mode 100644 index 0000000..4c4d73c --- /dev/null +++ b/lib/sqfs/src/comp/compressor.c @@ -0,0 +1,218 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * compressor.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "config.h" + +#include +#include + +#include "internal.h" + +typedef int (*compressor_fun_t)(const sqfs_compressor_config_t *cfg, + sqfs_compressor_t **out); + +static compressor_fun_t compressors[SQFS_COMP_MAX + 1] = { +#ifdef WITH_GZIP + [SQFS_COMP_GZIP] = gzip_compressor_create, +#endif +#ifdef WITH_XZ + [SQFS_COMP_XZ] = xz_compressor_create, + [SQFS_COMP_LZMA] = lzma_compressor_create, +#endif +#ifdef WITH_LZ4 + [SQFS_COMP_LZ4] = lz4_compressor_create, +#endif +#ifdef WITH_ZSTD + [SQFS_COMP_ZSTD] = zstd_compressor_create, +#endif +}; + +static const char *names[] = { + [SQFS_COMP_GZIP] = "gzip", + [SQFS_COMP_LZMA] = "lzma", + [SQFS_COMP_LZO] = "lzo", + [SQFS_COMP_XZ] = "xz", + [SQFS_COMP_LZ4] = "lz4", + [SQFS_COMP_ZSTD] = "zstd", +}; + +int sqfs_generic_write_options(sqfs_file_t *file, const void *data, size_t size) +{ + sqfs_u8 buffer[64]; + sqfs_u16 header; + int ret; + + /* XXX: options for all known compressors should fit into this */ + if (size >= (sizeof(buffer) - sizeof(header))) + return SQFS_ERROR_INTERNAL; + + header = htole16(0x8000 | size); + memcpy(buffer, &header, sizeof(header)); + memcpy(buffer + sizeof(header), data, size); + + ret = file->write_at(file, sizeof(sqfs_super_t), + buffer, sizeof(header) + size); + if (ret) + return ret; + + return sizeof(header) + size; +} + +int sqfs_generic_read_options(sqfs_file_t *file, void *data, size_t size) +{ + sqfs_u8 buffer[64]; + sqfs_u16 header; + int ret; + + /* XXX: options for all known compressors should fit into this */ + if (size >= (sizeof(buffer) - sizeof(header))) + return SQFS_ERROR_INTERNAL; + + ret = file->read_at(file, sizeof(sqfs_super_t), + buffer, sizeof(header) + size); + if (ret) + return ret; + + memcpy(&header, buffer, sizeof(header)); + + if (le16toh(header) != (0x8000 | size)) + return SQFS_ERROR_CORRUPTED; + + memcpy(data, buffer + 2, size); + return 0; +} + +int sqfs_compressor_create(const sqfs_compressor_config_t *cfg, + sqfs_compressor_t **out) +{ + sqfs_u8 padd0[sizeof(cfg->opt)]; + int ret; + + *out = NULL; + + /* check compressor ID */ + if (cfg == NULL) + return SQFS_ERROR_ARG_INVALID; + + if (cfg->id < SQFS_COMP_MIN || cfg->id > SQFS_COMP_MAX) + return SQFS_ERROR_UNSUPPORTED; + + if (compressors[cfg->id] == NULL) + return SQFS_ERROR_UNSUPPORTED; + + /* make sure the padding bytes are cleared, so we could theoretically + turn them into option fields in the future and remain compatible */ + memset(padd0, 0, sizeof(padd0)); + + switch (cfg->id) { + case SQFS_COMP_XZ: + ret = memcmp(cfg->opt.xz.padd0, padd0, + sizeof(cfg->opt.xz.padd0)); + break; + case SQFS_COMP_LZMA: + ret = memcmp(cfg->opt.lzma.padd0, padd0, + sizeof(cfg->opt.lzma.padd0)); + break; + case SQFS_COMP_LZO: + ret = memcmp(cfg->opt.lzo.padd0, padd0, + sizeof(cfg->opt.lzo.padd0)); + break; + case SQFS_COMP_GZIP: + ret = memcmp(cfg->opt.gzip.padd0, padd0, + sizeof(cfg->opt.gzip.padd0)); + break; + default: + ret = memcmp(cfg->opt.padd0, padd0, sizeof(cfg->opt.padd0)); + break; + } + + if (ret != 0) + return SQFS_ERROR_ARG_INVALID; + + return compressors[cfg->id](cfg, out); +} + +const char *sqfs_compressor_name_from_id(SQFS_COMPRESSOR id) +{ + if (id < 0 || (size_t)id >= sizeof(names) / sizeof(names[0])) + return NULL; + + return names[id]; +} + +int sqfs_compressor_id_from_name(const char *name) +{ + size_t i; + + for (i = 0; i < sizeof(names) / sizeof(names[0]); ++i) { + if (names[i] != NULL && strcmp(names[i], name) == 0) + return i; + } + + return SQFS_ERROR_UNSUPPORTED; +} + +int sqfs_compressor_config_init(sqfs_compressor_config_t *cfg, + SQFS_COMPRESSOR id, + size_t block_size, sqfs_u16 flags) +{ + sqfs_u32 flag_mask = SQFS_COMP_FLAG_GENERIC_ALL; + + memset(cfg, 0, sizeof(*cfg)); + + switch (id) { + case SQFS_COMP_GZIP: + flag_mask |= SQFS_COMP_FLAG_GZIP_ALL; + cfg->level = SQFS_GZIP_DEFAULT_LEVEL; + cfg->opt.gzip.window_size = SQFS_GZIP_DEFAULT_WINDOW; + break; + case SQFS_COMP_LZO: + cfg->opt.lzo.algorithm = SQFS_LZO_DEFAULT_ALG; + cfg->level = SQFS_LZO_DEFAULT_LEVEL; + break; + case SQFS_COMP_ZSTD: + cfg->level = SQFS_ZSTD_DEFAULT_LEVEL; + break; + case SQFS_COMP_XZ: + flag_mask |= SQFS_COMP_FLAG_XZ_ALL; + cfg->level = SQFS_XZ_DEFAULT_LEVEL; + cfg->opt.xz.dict_size = block_size; + cfg->opt.xz.lc = SQFS_XZ_DEFAULT_LC; + cfg->opt.xz.lp = SQFS_XZ_DEFAULT_LP; + cfg->opt.xz.pb = SQFS_XZ_DEFAULT_PB; + + if (block_size < SQFS_XZ_MIN_DICT_SIZE) + cfg->opt.xz.dict_size = SQFS_XZ_MIN_DICT_SIZE; + break; + case SQFS_COMP_LZMA: + flag_mask |= SQFS_COMP_FLAG_LZMA_ALL; + cfg->level = SQFS_LZMA_DEFAULT_LEVEL; + cfg->opt.lzma.dict_size = block_size; + cfg->opt.lzma.lc = SQFS_LZMA_DEFAULT_LC; + cfg->opt.lzma.lp = SQFS_LZMA_DEFAULT_LP; + cfg->opt.lzma.pb = SQFS_LZMA_DEFAULT_PB; + + if (block_size < SQFS_LZMA_MIN_DICT_SIZE) + cfg->opt.lzma.dict_size = SQFS_LZMA_MIN_DICT_SIZE; + break; + case SQFS_COMP_LZ4: + flag_mask |= SQFS_COMP_FLAG_LZ4_ALL; + break; + default: + return SQFS_ERROR_UNSUPPORTED; + } + + if (flags & ~flag_mask) { + memset(cfg, 0, sizeof(*cfg)); + return SQFS_ERROR_UNSUPPORTED; + } + + cfg->id = id; + cfg->flags = flags; + cfg->block_size = block_size; + return 0; +} diff --git a/lib/sqfs/src/comp/gzip.c b/lib/sqfs/src/comp/gzip.c new file mode 100644 index 0000000..beacfb8 --- /dev/null +++ b/lib/sqfs/src/comp/gzip.c @@ -0,0 +1,307 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * gzip.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "config.h" + +#include +#include +#include +#include +#include + +#include "internal.h" + +typedef struct { + sqfs_u32 level; + sqfs_u16 window; + sqfs_u16 strategies; +} gzip_options_t; + +typedef struct { + sqfs_compressor_t base; + + z_stream strm; + bool compress; + + size_t block_size; + gzip_options_t opt; +} gzip_compressor_t; + +static void gzip_destroy(sqfs_object_t *base) +{ + gzip_compressor_t *gzip = (gzip_compressor_t *)base; + + if (gzip->compress) { + deflateEnd(&gzip->strm); + } else { + inflateEnd(&gzip->strm); + } + + free(gzip); +} + +static void gzip_get_configuration(const sqfs_compressor_t *base, + sqfs_compressor_config_t *cfg) +{ + const gzip_compressor_t *gzip = (const gzip_compressor_t *)base; + + memset(cfg, 0, sizeof(*cfg)); + cfg->id = SQFS_COMP_GZIP; + cfg->flags = gzip->opt.strategies; + cfg->block_size = gzip->block_size; + cfg->level = gzip->opt.level; + cfg->opt.gzip.window_size = gzip->opt.window; + + if (!gzip->compress) + cfg->flags |= SQFS_COMP_FLAG_UNCOMPRESS; +} + +static int gzip_write_options(sqfs_compressor_t *base, sqfs_file_t *file) +{ + gzip_compressor_t *gzip = (gzip_compressor_t *)base; + gzip_options_t opt; + + if (gzip->opt.level == SQFS_GZIP_DEFAULT_LEVEL && + gzip->opt.window == SQFS_GZIP_DEFAULT_WINDOW && + gzip->opt.strategies == 0) { + return 0; + } + + opt.level = htole32(gzip->opt.level); + opt.window = htole16(gzip->opt.window); + opt.strategies = htole16(gzip->opt.strategies); + + return sqfs_generic_write_options(file, &opt, sizeof(opt)); +} + +static int gzip_read_options(sqfs_compressor_t *base, sqfs_file_t *file) +{ + gzip_compressor_t *gzip = (gzip_compressor_t *)base; + gzip_options_t opt; + int ret; + + ret = sqfs_generic_read_options(file, &opt, sizeof(opt)); + if (ret) + return ret; + + gzip->opt.level = le32toh(opt.level); + gzip->opt.window = le16toh(opt.window); + gzip->opt.strategies = le16toh(opt.strategies); + + if (gzip->opt.level < 1 || gzip->opt.level > 9) + return SQFS_ERROR_UNSUPPORTED; + + if (gzip->opt.window < 8 || gzip->opt.window > 15) + return SQFS_ERROR_UNSUPPORTED; + + if (gzip->opt.strategies & ~SQFS_COMP_FLAG_GZIP_ALL) + return SQFS_ERROR_UNSUPPORTED; + + return 0; +} + +static int flag_to_zlib_strategy(int flag) +{ + switch (flag) { + case SQFS_COMP_FLAG_GZIP_DEFAULT: + return Z_DEFAULT_STRATEGY; + case SQFS_COMP_FLAG_GZIP_FILTERED: + return Z_FILTERED; + case SQFS_COMP_FLAG_GZIP_HUFFMAN: + return Z_HUFFMAN_ONLY; + case SQFS_COMP_FLAG_GZIP_RLE: + return Z_RLE; + case SQFS_COMP_FLAG_GZIP_FIXED: + return Z_FIXED; + default: + break; + } + + return 0; +} + +static int find_strategy(gzip_compressor_t *gzip, const sqfs_u8 *in, + sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) +{ + int ret, strategy, selected = Z_DEFAULT_STRATEGY; + size_t i, length, minlength = 0; + + for (i = 0x01; i & SQFS_COMP_FLAG_GZIP_ALL; i <<= 1) { + if ((gzip->opt.strategies & i) == 0) + continue; + + ret = deflateReset(&gzip->strm); + if (ret != Z_OK) + return SQFS_ERROR_COMPRESSOR; + + strategy = flag_to_zlib_strategy(i); + + gzip->strm.next_in = (z_const Bytef *)in; + gzip->strm.avail_in = size; + gzip->strm.next_out = out; + gzip->strm.avail_out = outsize; + + ret = deflateParams(&gzip->strm, gzip->opt.level, strategy); + if (ret != Z_OK) + return SQFS_ERROR_COMPRESSOR; + + ret = deflate(&gzip->strm, Z_FINISH); + + if (ret == Z_STREAM_END) { + length = gzip->strm.total_out; + + if (minlength == 0 || length < minlength) { + minlength = length; + selected = strategy; + } + } else if (ret != Z_OK && ret != Z_BUF_ERROR) { + return SQFS_ERROR_COMPRESSOR; + } + } + + return selected; +} + +static sqfs_s32 gzip_do_block(sqfs_compressor_t *base, const sqfs_u8 *in, + sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) +{ + gzip_compressor_t *gzip = (gzip_compressor_t *)base; + int ret, strategy = 0; + size_t written; + + if (size >= 0x7FFFFFFF) + return SQFS_ERROR_ARG_INVALID; + + if (gzip->compress && gzip->opt.strategies != 0) { + strategy = find_strategy(gzip, in, size, out, outsize); + if (strategy < 0) + return strategy; + } + + if (gzip->compress) { + ret = deflateReset(&gzip->strm); + } else { + ret = inflateReset(&gzip->strm); + } + + if (ret != Z_OK) + return SQFS_ERROR_COMPRESSOR; + + gzip->strm.next_in = (const void *)in; + gzip->strm.avail_in = size; + gzip->strm.next_out = out; + gzip->strm.avail_out = outsize; + + if (gzip->compress && gzip->opt.strategies != 0) { + ret = deflateParams(&gzip->strm, gzip->opt.level, strategy); + if (ret != Z_OK) + return SQFS_ERROR_COMPRESSOR; + } + + if (gzip->compress) { + ret = deflate(&gzip->strm, Z_FINISH); + } else { + ret = inflate(&gzip->strm, Z_FINISH); + } + + if (ret == Z_STREAM_END) { + written = gzip->strm.total_out; + + if (gzip->compress && written >= size) + return 0; + + return written; + } + + if (ret != Z_OK && ret != Z_BUF_ERROR) + return SQFS_ERROR_COMPRESSOR; + + return 0; +} + +static sqfs_object_t *gzip_create_copy(const sqfs_object_t *cmp) +{ + gzip_compressor_t *gzip = malloc(sizeof(*gzip)); + int ret; + + if (gzip == NULL) + return NULL; + + memcpy(gzip, cmp, sizeof(*gzip)); + memset(&gzip->strm, 0, sizeof(gzip->strm)); + + if (gzip->compress) { + ret = deflateInit2(&gzip->strm, gzip->opt.level, Z_DEFLATED, + gzip->opt.window, 8, Z_DEFAULT_STRATEGY); + } else { + ret = inflateInit(&gzip->strm); + } + + if (ret != Z_OK) { + free(gzip); + return NULL; + } + + return (sqfs_object_t *)gzip; +} + +int gzip_compressor_create(const sqfs_compressor_config_t *cfg, + sqfs_compressor_t **out) +{ + gzip_compressor_t *gzip; + sqfs_compressor_t *base; + int ret; + + if (cfg->flags & ~(SQFS_COMP_FLAG_GZIP_ALL | + SQFS_COMP_FLAG_GENERIC_ALL)) { + return SQFS_ERROR_UNSUPPORTED; + } + + if (cfg->level < SQFS_GZIP_MIN_LEVEL || + cfg->level > SQFS_GZIP_MAX_LEVEL) { + return SQFS_ERROR_UNSUPPORTED; + } + + if (cfg->opt.gzip.window_size < SQFS_GZIP_MIN_WINDOW || + cfg->opt.gzip.window_size > SQFS_GZIP_MAX_WINDOW) { + return SQFS_ERROR_UNSUPPORTED; + } + + gzip = calloc(1, sizeof(*gzip)); + base = (sqfs_compressor_t *)gzip; + + if (gzip == NULL) + return SQFS_ERROR_ALLOC; + + sqfs_object_init(gzip, gzip_destroy, gzip_create_copy); + + gzip->opt.level = cfg->level; + gzip->opt.window = cfg->opt.gzip.window_size; + gzip->opt.strategies = cfg->flags & SQFS_COMP_FLAG_GZIP_ALL; + gzip->compress = (cfg->flags & SQFS_COMP_FLAG_UNCOMPRESS) == 0; + gzip->block_size = cfg->block_size; + base->get_configuration = gzip_get_configuration; + base->do_block = gzip_do_block; + base->write_options = gzip_write_options; + base->read_options = gzip_read_options; + + if (gzip->compress) { + ret = deflateInit2(&gzip->strm, cfg->level, + Z_DEFLATED, cfg->opt.gzip.window_size, 8, + Z_DEFAULT_STRATEGY); + } else { + ret = inflateInit(&gzip->strm); + } + + if (ret != Z_OK) { + free(gzip); + return SQFS_ERROR_COMPRESSOR; + } + + *out = base; + return 0; +} diff --git a/lib/sqfs/src/comp/internal.h b/lib/sqfs/src/comp/internal.h new file mode 100644 index 0000000..e4c3dd8 --- /dev/null +++ b/lib/sqfs/src/comp/internal.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * internal.h + * + * Copyright (C) 2019 David Oberhollenzer + */ +#ifndef INTERNAL_H +#define INTERNAL_H + +#include "config.h" + +#include "sqfs/predef.h" +#include "sqfs/compressor.h" +#include "sqfs/error.h" +#include "sqfs/block.h" +#include "sqfs/io.h" +#include "util/util.h" + +SQFS_INTERNAL +int sqfs_generic_write_options(sqfs_file_t *file, const void *data, + size_t size); + +SQFS_INTERNAL +int sqfs_generic_read_options(sqfs_file_t *file, void *data, size_t size); + +SQFS_INTERNAL +int xz_compressor_create(const sqfs_compressor_config_t *cfg, + sqfs_compressor_t **out); + +SQFS_INTERNAL +int gzip_compressor_create(const sqfs_compressor_config_t *cfg, + sqfs_compressor_t **out); + +SQFS_INTERNAL +int lz4_compressor_create(const sqfs_compressor_config_t *cfg, + sqfs_compressor_t **out); + +SQFS_INTERNAL +int zstd_compressor_create(const sqfs_compressor_config_t *cfg, + sqfs_compressor_t **out); + +SQFS_INTERNAL +int lzma_compressor_create(const sqfs_compressor_config_t *cfg, + sqfs_compressor_t **out); + +#endif /* INTERNAL_H */ diff --git a/lib/sqfs/src/comp/lz4.c b/lib/sqfs/src/comp/lz4.c new file mode 100644 index 0000000..77f4a6e --- /dev/null +++ b/lib/sqfs/src/comp/lz4.c @@ -0,0 +1,172 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * lz4.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "config.h" + +#include +#include +#include + +#include +#include + +#include "internal.h" + +typedef struct { + sqfs_compressor_t base; + size_t block_size; + bool high_compression; +} lz4_compressor_t; + +typedef struct { + sqfs_u32 version; + sqfs_u32 flags; +} lz4_options; + +#define LZ4LEGACY 1 + +/* old verions of liblz4 don't have this */ +#ifndef LZ4HC_CLEVEL_MAX +#define LZ4HC_CLEVEL_MAX 12 +#endif + +static int lz4_write_options(sqfs_compressor_t *base, sqfs_file_t *file) +{ + lz4_compressor_t *lz4 = (lz4_compressor_t *)base; + lz4_options opt = { + .version = htole32(LZ4LEGACY), + .flags = htole32(lz4->high_compression ? + SQFS_COMP_FLAG_LZ4_HC : 0), + }; + + return sqfs_generic_write_options(file, &opt, sizeof(opt)); +} + +static int lz4_read_options(sqfs_compressor_t *base, sqfs_file_t *file) +{ + lz4_options opt; + int ret; + (void)base; + + ret = sqfs_generic_read_options(file, &opt, sizeof(opt)); + if (ret) + return ret; + + opt.version = le32toh(opt.version); + opt.flags = le32toh(opt.flags); + + if (opt.version != LZ4LEGACY) + return SQFS_ERROR_UNSUPPORTED; + + return 0; +} + +static sqfs_s32 lz4_comp_block(sqfs_compressor_t *base, const sqfs_u8 *in, + sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) +{ + lz4_compressor_t *lz4 = (lz4_compressor_t *)base; + int ret; + + if (size >= 0x7FFFFFFF) + return SQFS_ERROR_ARG_INVALID; + + if (lz4->high_compression) { + ret = LZ4_compress_HC((const void *)in, (void *)out, + size, outsize, LZ4HC_CLEVEL_MAX); + } else { + ret = LZ4_compress_default((const void *)in, (void *)out, + size, outsize); + } + + if (ret < 0) + return SQFS_ERROR_COMPRESSOR; + + return ret; +} + +static sqfs_s32 lz4_uncomp_block(sqfs_compressor_t *base, const sqfs_u8 *in, + sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) +{ + int ret; + (void)base; + + if (outsize >= 0x7FFFFFFF) + return SQFS_ERROR_ARG_INVALID; + + ret = LZ4_decompress_safe((const void *)in, (void *)out, size, outsize); + + if (ret < 0) + return SQFS_ERROR_COMPRESSOR; + + return ret; +} + +static void lz4_get_configuration(const sqfs_compressor_t *base, + sqfs_compressor_config_t *cfg) +{ + const lz4_compressor_t *lz4 = (const lz4_compressor_t *)base; + + memset(cfg, 0, sizeof(*cfg)); + cfg->id = SQFS_COMP_LZ4; + cfg->block_size = lz4->block_size; + + if (lz4->high_compression) + cfg->flags |= SQFS_COMP_FLAG_LZ4_HC; + + if (base->do_block == lz4_uncomp_block) + cfg->flags |= SQFS_COMP_FLAG_UNCOMPRESS; +} + +static sqfs_object_t *lz4_create_copy(const sqfs_object_t *cmp) +{ + lz4_compressor_t *lz4 = malloc(sizeof(*lz4)); + + if (lz4 == NULL) + return NULL; + + memcpy(lz4, cmp, sizeof(*lz4)); + return (sqfs_object_t *)lz4; +} + +static void lz4_destroy(sqfs_object_t *base) +{ + free(base); +} + +int lz4_compressor_create(const sqfs_compressor_config_t *cfg, + sqfs_compressor_t **out) +{ + sqfs_compressor_t *base; + lz4_compressor_t *lz4; + + if (cfg->flags & ~(SQFS_COMP_FLAG_LZ4_ALL | + SQFS_COMP_FLAG_GENERIC_ALL)) { + return SQFS_ERROR_UNSUPPORTED; + } + + if (cfg->level != 0) + return SQFS_ERROR_UNSUPPORTED; + + lz4 = calloc(1, sizeof(*lz4)); + base = (sqfs_compressor_t *)lz4; + if (lz4 == NULL) + return SQFS_ERROR_ALLOC; + + sqfs_object_init(lz4, lz4_destroy, lz4_create_copy); + + lz4->high_compression = (cfg->flags & SQFS_COMP_FLAG_LZ4_HC) != 0; + lz4->block_size = cfg->block_size; + + base->get_configuration = lz4_get_configuration; + base->do_block = (cfg->flags & SQFS_COMP_FLAG_UNCOMPRESS) ? + lz4_uncomp_block : lz4_comp_block; + base->write_options = lz4_write_options; + base->read_options = lz4_read_options; + + *out = base; + return 0; +} diff --git a/lib/sqfs/src/comp/lzma.c b/lib/sqfs/src/comp/lzma.c new file mode 100644 index 0000000..5456603 --- /dev/null +++ b/lib/sqfs/src/comp/lzma.c @@ -0,0 +1,281 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * lzma.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "config.h" + +#include +#include +#include +#include + +#include "internal.h" + +#define LZMA_SIZE_OFFSET (5) +#define LZMA_SIZE_BYTES (8) +#define LZMA_HEADER_SIZE (13) + +#define MEMLIMIT (64 * 1024 * 1024) + +typedef struct { + sqfs_compressor_t base; + size_t block_size; + size_t dict_size; + + sqfs_u32 flags; + sqfs_u8 level; + sqfs_u8 lc; + sqfs_u8 lp; + sqfs_u8 pb; +} lzma_compressor_t; + +static int lzma_write_options(sqfs_compressor_t *base, sqfs_file_t *file) +{ + (void)base; (void)file; + return 0; +} + +static int lzma_read_options(sqfs_compressor_t *base, sqfs_file_t *file) +{ + (void)base; + (void)file; + return SQFS_ERROR_UNSUPPORTED; +} + +static sqfs_s32 try_compress(lzma_compressor_t *lzma, sqfs_u32 preset, + const sqfs_u8 *in, size_t size, + sqfs_u8 *out, size_t outsize) +{ + lzma_stream strm = LZMA_STREAM_INIT; + lzma_options_lzma opt; + int ret; + + lzma_lzma_preset(&opt, preset); + opt.dict_size = lzma->block_size; + opt.lc = lzma->lc; + opt.lp = lzma->lp; + opt.pb = lzma->pb; + + if (lzma_alone_encoder(&strm, &opt) != LZMA_OK) { + lzma_end(&strm); + return SQFS_ERROR_COMPRESSOR; + } + + strm.next_out = out; + strm.avail_out = outsize; + strm.next_in = in; + strm.avail_in = size; + + ret = lzma_code(&strm, LZMA_FINISH); + lzma_end(&strm); + + if (ret != LZMA_STREAM_END) + return ret == LZMA_OK ? 0 : SQFS_ERROR_COMPRESSOR; + + if (strm.total_out > size) + return 0; + + out[LZMA_SIZE_OFFSET ] = size & 0xFF; + out[LZMA_SIZE_OFFSET + 1] = (size >> 8) & 0xFF; + out[LZMA_SIZE_OFFSET + 2] = (size >> 16) & 0xFF; + out[LZMA_SIZE_OFFSET + 3] = (size >> 24) & 0xFF; + out[LZMA_SIZE_OFFSET + 4] = 0; + out[LZMA_SIZE_OFFSET + 5] = 0; + out[LZMA_SIZE_OFFSET + 6] = 0; + out[LZMA_SIZE_OFFSET + 7] = 0; + return strm.total_out; +} + +static sqfs_s32 lzma_comp_block(sqfs_compressor_t *base, const sqfs_u8 *in, + sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) +{ + lzma_compressor_t *lzma = (lzma_compressor_t *)base; + sqfs_s32 ret, smallest; + sqfs_u32 preset; + + if (outsize < LZMA_HEADER_SIZE || size >= 0x7FFFFFFF) + return SQFS_ERROR_ARG_INVALID; + + preset = lzma->level; + ret = try_compress(lzma, preset, in, size, out, outsize); + if (ret < 0 || !(lzma->flags & SQFS_COMP_FLAG_LZMA_EXTREME)) + return ret; + + preset |= LZMA_PRESET_EXTREME; + smallest = ret; + + ret = try_compress(lzma, preset, in, size, out, outsize); + if (ret < 0 || (ret > 0 && (smallest == 0 || ret < smallest))) + return ret; + + preset &= ~LZMA_PRESET_EXTREME; + return smallest == 0 ? 0 : + try_compress(lzma, preset, in, size, out, outsize); +} + +static sqfs_s32 lzma_uncomp_block(sqfs_compressor_t *base, const sqfs_u8 *in, + sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) +{ + sqfs_u8 lzma_header[LZMA_HEADER_SIZE]; + lzma_stream strm = LZMA_STREAM_INIT; + size_t hdrsize; + int ret; + (void)base; + + if (size >= 0x7FFFFFFF) + return SQFS_ERROR_ARG_INVALID; + + if (size < sizeof(lzma_header)) + return SQFS_ERROR_CORRUPTED; + + hdrsize = (size_t)in[LZMA_SIZE_OFFSET] | + ((size_t)in[LZMA_SIZE_OFFSET + 1] << 8) | + ((size_t)in[LZMA_SIZE_OFFSET + 2] << 16) | + ((size_t)in[LZMA_SIZE_OFFSET + 3] << 24); + + if (hdrsize > outsize) + return 0; + + if (lzma_alone_decoder(&strm, MEMLIMIT) != LZMA_OK) { + lzma_end(&strm); + return SQFS_ERROR_COMPRESSOR; + } + + memcpy(lzma_header, in, sizeof(lzma_header)); + memset(lzma_header + LZMA_SIZE_OFFSET, 0xFF, LZMA_SIZE_BYTES); + + strm.next_out = out; + strm.avail_out = outsize; + strm.next_in = lzma_header; + strm.avail_in = sizeof(lzma_header); + + ret = lzma_code(&strm, LZMA_RUN); + + if (ret != LZMA_OK || strm.avail_in != 0) { + lzma_end(&strm); + return SQFS_ERROR_COMPRESSOR; + } + + strm.next_in = in + sizeof(lzma_header); + strm.avail_in = size - sizeof(lzma_header); + + ret = lzma_code(&strm, LZMA_FINISH); + lzma_end(&strm); + + if (ret != LZMA_STREAM_END && ret != LZMA_OK) + return SQFS_ERROR_COMPRESSOR; + + if (ret == LZMA_OK) { + if (strm.total_out < hdrsize || strm.avail_in != 0) + return 0; + } + + return hdrsize; +} + +static void lzma_get_configuration(const sqfs_compressor_t *base, + sqfs_compressor_config_t *cfg) +{ + const lzma_compressor_t *lzma = (const lzma_compressor_t *)base; + + memset(cfg, 0, sizeof(*cfg)); + cfg->id = SQFS_COMP_LZMA; + cfg->block_size = lzma->block_size; + cfg->flags = lzma->flags; + cfg->level = lzma->level; + cfg->opt.lzma.dict_size = lzma->dict_size; + cfg->opt.lzma.lc = lzma->lc; + cfg->opt.lzma.lp = lzma->lp; + cfg->opt.lzma.pb = lzma->pb; +} + +static sqfs_object_t *lzma_create_copy(const sqfs_object_t *cmp) +{ + lzma_compressor_t *copy = malloc(sizeof(*copy)); + + if (copy != NULL) + memcpy(copy, cmp, sizeof(*copy)); + + return (sqfs_object_t *)copy; +} + +static void lzma_destroy(sqfs_object_t *base) +{ + free(base); +} + +int lzma_compressor_create(const sqfs_compressor_config_t *cfg, + sqfs_compressor_t **out) +{ + sqfs_compressor_t *base; + lzma_compressor_t *lzma; + sqfs_u32 mask; + + mask = SQFS_COMP_FLAG_GENERIC_ALL | SQFS_COMP_FLAG_LZMA_ALL; + + if (cfg->flags & ~mask) + return SQFS_ERROR_UNSUPPORTED; + + /* XXX: values are unsigned and minimum is 0 */ + if (cfg->level > SQFS_LZMA_MAX_LEVEL) + return SQFS_ERROR_UNSUPPORTED; + + if (cfg->opt.lzma.lc > SQFS_LZMA_MAX_LC) + return SQFS_ERROR_UNSUPPORTED; + + if (cfg->opt.lzma.lp > SQFS_LZMA_MAX_LP) + return SQFS_ERROR_UNSUPPORTED; + + if (cfg->opt.lzma.pb > SQFS_LZMA_MAX_PB) + return SQFS_ERROR_UNSUPPORTED; + + if (cfg->opt.lzma.lc + cfg->opt.lzma.lp > 4) + return SQFS_ERROR_UNSUPPORTED; + + if (cfg->opt.lzma.dict_size == 0) + return SQFS_ERROR_UNSUPPORTED; + + if (cfg->opt.lzma.dict_size < SQFS_LZMA_MIN_DICT_SIZE) + return SQFS_ERROR_UNSUPPORTED; + + if (cfg->opt.lzma.dict_size > SQFS_LZMA_MAX_DICT_SIZE) + return SQFS_ERROR_UNSUPPORTED; + + mask = cfg->opt.lzma.dict_size; + mask &= mask - 1; + + if (mask != 0) { + if ((mask & (mask - 1)) != 0) + return SQFS_ERROR_UNSUPPORTED; + + if (cfg->opt.lzma.dict_size != (mask | mask >> 1)) + return SQFS_ERROR_UNSUPPORTED; + } + + lzma = calloc(1, sizeof(*lzma)); + base = (sqfs_compressor_t *)lzma; + if (lzma == NULL) + return SQFS_ERROR_ALLOC; + + sqfs_object_init(lzma, lzma_destroy, lzma_create_copy); + + lzma->block_size = cfg->block_size; + lzma->flags = cfg->flags; + lzma->level = cfg->level; + lzma->dict_size = cfg->opt.lzma.dict_size; + lzma->lc = cfg->opt.lzma.lc; + lzma->lp = cfg->opt.lzma.lp; + lzma->pb = cfg->opt.lzma.pb; + + base->get_configuration = lzma_get_configuration; + base->do_block = (cfg->flags & SQFS_COMP_FLAG_UNCOMPRESS) ? + lzma_uncomp_block : lzma_comp_block; + base->write_options = lzma_write_options; + base->read_options = lzma_read_options; + + *out = base; + return 0; +} diff --git a/lib/sqfs/src/comp/xz.c b/lib/sqfs/src/comp/xz.c new file mode 100644 index 0000000..13545ed --- /dev/null +++ b/lib/sqfs/src/comp/xz.c @@ -0,0 +1,324 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * xz.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "config.h" + +#include +#include +#include +#include + +#include "internal.h" + +typedef struct { + sqfs_compressor_t base; + size_t block_size; + size_t dict_size; + + sqfs_u8 level; + sqfs_u8 lc; + sqfs_u8 lp; + sqfs_u8 pb; + + int flags; +} xz_compressor_t; + +typedef struct { + sqfs_u32 dict_size; + sqfs_u32 flags; +} xz_options_t; + +static bool is_dict_size_valid(size_t size) +{ + size_t x = size & (size - 1); + + if (x == 0) + return true; + + return size == (x | (x >> 1)); +} + +static int xz_write_options(sqfs_compressor_t *base, sqfs_file_t *file) +{ + xz_compressor_t *xz = (xz_compressor_t *)base; + xz_options_t opt; + sqfs_u32 flags; + + if (xz->flags == 0 && xz->dict_size == xz->block_size) + return 0; + + flags = xz->flags & SQFS_COMP_FLAG_XZ_ALL; + flags &= ~SQFS_COMP_FLAG_XZ_EXTREME; + + opt.dict_size = htole32(xz->dict_size); + opt.flags = htole32(flags); + + return sqfs_generic_write_options(file, &opt, sizeof(opt)); +} + +static int xz_read_options(sqfs_compressor_t *base, sqfs_file_t *file) +{ + xz_compressor_t *xz = (xz_compressor_t *)base; + xz_options_t opt; + int ret; + + ret = sqfs_generic_read_options(file, &opt, sizeof(opt)); + if (ret) + return ret; + + opt.dict_size = le32toh(opt.dict_size); + opt.flags = le32toh(opt.flags); + + if (!is_dict_size_valid(opt.dict_size)) + return SQFS_ERROR_CORRUPTED; + + if (opt.flags & ~SQFS_COMP_FLAG_XZ_ALL) + return SQFS_ERROR_UNSUPPORTED; + + xz->flags = opt.flags; + xz->dict_size = opt.dict_size; + return 0; +} + +static sqfs_s32 compress(xz_compressor_t *xz, lzma_vli filter, + const sqfs_u8 *in, sqfs_u32 size, + sqfs_u8 *out, sqfs_u32 outsize, + sqfs_u32 presets) +{ + lzma_filter filters[5]; + lzma_options_lzma opt; + size_t written = 0; + lzma_ret ret; + int i = 0; + + if (lzma_lzma_preset(&opt, presets)) + return SQFS_ERROR_COMPRESSOR; + + opt.lc = xz->lc; + opt.lp = xz->lp; + opt.pb = xz->pb; + opt.dict_size = xz->dict_size; + + if (filter != LZMA_VLI_UNKNOWN) { + filters[i].id = filter; + filters[i].options = NULL; + ++i; + } + + filters[i].id = LZMA_FILTER_LZMA2; + filters[i].options = &opt; + ++i; + + filters[i].id = LZMA_VLI_UNKNOWN; + filters[i].options = NULL; + ++i; + + ret = lzma_stream_buffer_encode(filters, LZMA_CHECK_CRC32, NULL, + in, size, out, &written, outsize); + + if (ret == LZMA_OK) + return (written >= size) ? 0 : written; + + if (ret != LZMA_BUF_ERROR) + return SQFS_ERROR_COMPRESSOR; + + return 0; +} + +static lzma_vli flag_to_vli(int flag) +{ + switch (flag) { + case SQFS_COMP_FLAG_XZ_X86: + return LZMA_FILTER_X86; + case SQFS_COMP_FLAG_XZ_POWERPC: + return LZMA_FILTER_POWERPC; + case SQFS_COMP_FLAG_XZ_IA64: + return LZMA_FILTER_IA64; + case SQFS_COMP_FLAG_XZ_ARM: + return LZMA_FILTER_ARM; + case SQFS_COMP_FLAG_XZ_ARMTHUMB: + return LZMA_FILTER_ARMTHUMB; + case SQFS_COMP_FLAG_XZ_SPARC: + return LZMA_FILTER_SPARC; + default: + break; + } + + return LZMA_VLI_UNKNOWN; +} + +static sqfs_s32 xz_comp_block(sqfs_compressor_t *base, const sqfs_u8 *in, + sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) +{ + xz_compressor_t *xz = (xz_compressor_t *)base; + lzma_vli filter, selected = LZMA_VLI_UNKNOWN; + sqfs_s32 ret, smallest; + bool extreme; + size_t i; + + if (size >= 0x7FFFFFFF) + return SQFS_ERROR_ARG_INVALID; + + ret = compress(xz, LZMA_VLI_UNKNOWN, in, size, out, + outsize, xz->level); + if (ret < 0 || xz->flags == 0) + return ret; + + smallest = ret; + extreme = false; + + if (xz->flags & SQFS_COMP_FLAG_XZ_EXTREME) { + ret = compress(xz, LZMA_VLI_UNKNOWN, in, size, out, outsize, + xz->level | LZMA_PRESET_EXTREME); + + if (ret > 0 && (smallest == 0 || ret < smallest)) { + smallest = ret; + extreme = true; + } + } + + for (i = 1; i & SQFS_COMP_FLAG_XZ_ALL; i <<= 1) { + if ((i & SQFS_COMP_FLAG_XZ_EXTREME) || (xz->flags & i) == 0) + continue; + + filter = flag_to_vli(i); + + ret = compress(xz, filter, in, size, out, outsize, xz->level); + if (ret > 0 && (smallest == 0 || ret < smallest)) { + smallest = ret; + selected = filter; + extreme = false; + } + + if (xz->flags & SQFS_COMP_FLAG_XZ_EXTREME) { + ret = compress(xz, filter, in, size, out, outsize, + xz->level | LZMA_PRESET_EXTREME); + + if (ret > 0 && (smallest == 0 || ret < smallest)) { + smallest = ret; + selected = filter; + extreme = true; + } + } + } + + if (smallest == 0) + return 0; + + return compress(xz, selected, in, size, out, outsize, + xz->level | (extreme ? LZMA_PRESET_EXTREME : 0)); +} + +static sqfs_s32 xz_uncomp_block(sqfs_compressor_t *base, const sqfs_u8 *in, + sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) +{ + sqfs_u64 memlimit = 65 * 1024 * 1024; + size_t dest_pos = 0; + size_t src_pos = 0; + lzma_ret ret; + (void)base; + + if (outsize >= 0x7FFFFFFF) + return SQFS_ERROR_ARG_INVALID; + + ret = lzma_stream_buffer_decode(&memlimit, 0, NULL, + in, &src_pos, size, + out, &dest_pos, outsize); + + if (ret == LZMA_OK && size == src_pos) + return dest_pos; + + return SQFS_ERROR_COMPRESSOR; +} + +static void xz_get_configuration(const sqfs_compressor_t *base, + sqfs_compressor_config_t *cfg) +{ + const xz_compressor_t *xz = (const xz_compressor_t *)base; + + memset(cfg, 0, sizeof(*cfg)); + cfg->id = SQFS_COMP_XZ; + cfg->flags = xz->flags; + cfg->block_size = xz->block_size; + cfg->level = xz->level; + cfg->opt.xz.dict_size = xz->dict_size; + cfg->opt.xz.lc = xz->lc; + cfg->opt.xz.lp = xz->lp; + cfg->opt.xz.pb = xz->pb; + + if (base->do_block == xz_uncomp_block) + cfg->flags |= SQFS_COMP_FLAG_UNCOMPRESS; +} + +static sqfs_object_t *xz_create_copy(const sqfs_object_t *cmp) +{ + xz_compressor_t *xz = malloc(sizeof(*xz)); + + if (xz == NULL) + return NULL; + + memcpy(xz, cmp, sizeof(*xz)); + return (sqfs_object_t *)xz; +} + +static void xz_destroy(sqfs_object_t *base) +{ + free(base); +} + +int xz_compressor_create(const sqfs_compressor_config_t *cfg, + sqfs_compressor_t **out) +{ + sqfs_compressor_t *base; + xz_compressor_t *xz; + + if (cfg->flags & ~(SQFS_COMP_FLAG_GENERIC_ALL | + SQFS_COMP_FLAG_XZ_ALL)) { + return SQFS_ERROR_UNSUPPORTED; + } + + if (!is_dict_size_valid(cfg->opt.xz.dict_size)) + return SQFS_ERROR_UNSUPPORTED; + + if (cfg->opt.xz.lc + cfg->opt.xz.lp > 4) + return SQFS_ERROR_UNSUPPORTED; + + if (cfg->opt.xz.pb > SQFS_XZ_MAX_PB) + return SQFS_ERROR_UNSUPPORTED; + + if (cfg->level > SQFS_XZ_MAX_LEVEL) + return SQFS_ERROR_UNSUPPORTED; + + if (cfg->opt.xz.dict_size < SQFS_XZ_MIN_DICT_SIZE) + return SQFS_ERROR_UNSUPPORTED; + + if (cfg->opt.xz.dict_size > SQFS_XZ_MAX_DICT_SIZE) + return SQFS_ERROR_UNSUPPORTED; + + xz = calloc(1, sizeof(*xz)); + base = (sqfs_compressor_t *)xz; + if (xz == NULL) + return SQFS_ERROR_ALLOC; + + sqfs_object_init(xz, xz_destroy, xz_create_copy); + + xz->flags = cfg->flags; + xz->dict_size = cfg->opt.xz.dict_size; + xz->block_size = cfg->block_size; + xz->lc = cfg->opt.xz.lc; + xz->lp = cfg->opt.xz.lp; + xz->pb = cfg->opt.xz.pb; + xz->level = cfg->level; + base->get_configuration = xz_get_configuration; + base->do_block = (cfg->flags & SQFS_COMP_FLAG_UNCOMPRESS) ? + xz_uncomp_block : xz_comp_block; + base->write_options = xz_write_options; + base->read_options = xz_read_options; + + *out = base; + return 0; +} diff --git a/lib/sqfs/src/comp/zstd.c b/lib/sqfs/src/comp/zstd.c new file mode 100644 index 0000000..a6d7975 --- /dev/null +++ b/lib/sqfs/src/comp/zstd.c @@ -0,0 +1,172 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * zstd.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "config.h" + +#include +#include +#include + +#include +#include + +#include "internal.h" + +typedef struct { + sqfs_compressor_t base; + size_t block_size; + ZSTD_CCtx *zctx; + int level; +} zstd_compressor_t; + +typedef struct { + sqfs_u32 level; +} zstd_options_t; + +static int zstd_write_options(sqfs_compressor_t *base, sqfs_file_t *file) +{ + zstd_compressor_t *zstd = (zstd_compressor_t *)base; + zstd_options_t opt; + + if (zstd->level == SQFS_ZSTD_DEFAULT_LEVEL) + return 0; + + opt.level = htole32(zstd->level); + return sqfs_generic_write_options(file, &opt, sizeof(opt)); +} + +static int zstd_read_options(sqfs_compressor_t *base, sqfs_file_t *file) +{ + zstd_options_t opt; + int ret; + (void)base; + + ret = sqfs_generic_read_options(file, &opt, sizeof(opt)); + if (ret) + return ret; + + opt.level = le32toh(opt.level); + return 0; +} + +static sqfs_s32 zstd_comp_block(sqfs_compressor_t *base, const sqfs_u8 *in, + sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) +{ + zstd_compressor_t *zstd = (zstd_compressor_t *)base; + size_t ret; + + if (size >= 0x7FFFFFFF) + return SQFS_ERROR_ARG_INVALID; + + ret = ZSTD_compressCCtx(zstd->zctx, out, outsize, in, size, + zstd->level); + + if (ZSTD_isError(ret)) { + if (ZSTD_getErrorCode(ret) == ZSTD_error_dstSize_tooSmall) + return 0; + + return SQFS_ERROR_COMPRESSOR; + } + + return ret < size ? ret : 0; +} + +static sqfs_s32 zstd_uncomp_block(sqfs_compressor_t *base, const sqfs_u8 *in, + sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) +{ + size_t ret; + (void)base; + + if (outsize >= 0x7FFFFFFF) + return SQFS_ERROR_ARG_INVALID; + + ret = ZSTD_decompress(out, outsize, in, size); + + if (ZSTD_isError(ret)) + return SQFS_ERROR_COMPRESSOR; + + return ret; +} + +static void zstd_get_configuration(const sqfs_compressor_t *base, + sqfs_compressor_config_t *cfg) +{ + const zstd_compressor_t *zstd = (const zstd_compressor_t *)base; + + memset(cfg, 0, sizeof(*cfg)); + cfg->id = SQFS_COMP_ZSTD; + + cfg->block_size = zstd->block_size; + cfg->level = zstd->level; + + if (base->do_block == zstd_uncomp_block) + cfg->flags |= SQFS_COMP_FLAG_UNCOMPRESS; +} + +static sqfs_object_t *zstd_create_copy(const sqfs_object_t *cmp) +{ + zstd_compressor_t *zstd = malloc(sizeof(*zstd)); + + if (zstd == NULL) + return NULL; + + memcpy(zstd, cmp, sizeof(*zstd)); + + zstd->zctx = ZSTD_createCCtx(); + + if (zstd->zctx == NULL) { + free(zstd); + return NULL; + } + + return (sqfs_object_t *)zstd; +} + +static void zstd_destroy(sqfs_object_t *base) +{ + zstd_compressor_t *zstd = (zstd_compressor_t *)base; + + ZSTD_freeCCtx(zstd->zctx); + free(zstd); +} + +int zstd_compressor_create(const sqfs_compressor_config_t *cfg, + sqfs_compressor_t **out) +{ + zstd_compressor_t *zstd; + sqfs_compressor_t *base; + + if (cfg->flags & ~SQFS_COMP_FLAG_GENERIC_ALL) + return SQFS_ERROR_UNSUPPORTED; + + if (cfg->level < 1 || cfg->level > (unsigned int)ZSTD_maxCLevel()) + return SQFS_ERROR_UNSUPPORTED; + + zstd = calloc(1, sizeof(*zstd)); + base = (sqfs_compressor_t *)zstd; + if (zstd == NULL) + return SQFS_ERROR_ALLOC; + + sqfs_object_init(zstd, zstd_destroy, zstd_create_copy); + + zstd->block_size = cfg->block_size; + zstd->level = cfg->level; + zstd->zctx = ZSTD_createCCtx(); + if (zstd->zctx == NULL) { + free(zstd); + return SQFS_ERROR_COMPRESSOR; + } + + base->get_configuration = zstd_get_configuration; + base->do_block = cfg->flags & SQFS_COMP_FLAG_UNCOMPRESS ? + zstd_uncomp_block : zstd_comp_block; + base->write_options = zstd_write_options; + base->read_options = zstd_read_options; + + *out = base; + return 0; +} -- cgit v1.2.3