diff options
Diffstat (limited to 'lib/common')
-rw-r--r-- | lib/common/Makemodule.am | 5 | ||||
-rw-r--r-- | lib/common/comp_lzo.c | 229 | ||||
-rw-r--r-- | lib/common/compress.c | 10 | ||||
-rw-r--r-- | lib/common/writer.c | 10 |
4 files changed, 253 insertions, 1 deletions
diff --git a/lib/common/Makemodule.am b/lib/common/Makemodule.am index 2e5a812..7e1ec63 100644 --- a/lib/common/Makemodule.am +++ b/lib/common/Makemodule.am @@ -6,5 +6,10 @@ libcommon_a_SOURCES += lib/common/data_writer.c include/common.h libcommon_a_SOURCES += lib/common/get_path.c lib/common/io_stdin.c libcommon_a_SOURCES += lib/common/writer.c lib/common/perror.c libcommon_a_SOURCES += lib/common/mkdir_p.c lib/common/filename_sane.c +libcommon_a_CFLAGS = $(AM_CFLAGS) $(LZO_CFLAGS) + +if WITH_LZO +libcommon_a_SOURCES += lib/common/comp_lzo.c +endif noinst_LIBRARIES += libcommon.a diff --git a/lib/common/comp_lzo.c b/lib/common/comp_lzo.c new file mode 100644 index 0000000..473b76f --- /dev/null +++ b/lib/common/comp_lzo.c @@ -0,0 +1,229 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* + * comp_lzo.c + * + * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at> + */ +#include "config.h" +#include "common.h" + +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> + +#include <lzo/lzo1x.h> + +#define LZO_NUM_ALGS (sizeof(lzo_algs) / sizeof(lzo_algs[0])) + +typedef int (*lzo_cb_t)(const lzo_bytep src, lzo_uint src_len, lzo_bytep dst, + lzo_uintp dst_len, lzo_voidp wrkmem); + +static const struct { + lzo_cb_t compress; + size_t bufsize; +} lzo_algs[] = { + [SQFS_LZO1X_1] = { + .compress = lzo1x_1_compress, + .bufsize = LZO1X_1_MEM_COMPRESS, + }, + [SQFS_LZO1X_1_11] = { + .compress = lzo1x_1_11_compress, + .bufsize = LZO1X_1_11_MEM_COMPRESS, + }, + [SQFS_LZO1X_1_12] = { + .compress = lzo1x_1_12_compress, + .bufsize = LZO1X_1_12_MEM_COMPRESS, + }, + [SQFS_LZO1X_1_15] = { + .compress = lzo1x_1_15_compress, + .bufsize = LZO1X_1_15_MEM_COMPRESS, + }, + [SQFS_LZO1X_999] = { + .compress = lzo1x_999_compress, + .bufsize = LZO1X_999_MEM_COMPRESS, + }, +}; + +typedef struct { + sqfs_compressor_t base; + int algorithm; + int level; + + sqfs_u8 buffer[]; +} lzo_compressor_t; + +typedef struct { + sqfs_u32 algorithm; + sqfs_u32 level; +} lzo_options_t; + +static int lzo_write_options(sqfs_compressor_t *base, sqfs_file_t *file) +{ + lzo_compressor_t *lzo = (lzo_compressor_t *)base; + sqfs_u8 buffer[sizeof(lzo_options_t) + 2]; + lzo_options_t opt; + int ret; + + if (lzo->algorithm == SQFS_LZO_DEFAULT_ALG && + lzo->level == SQFS_LZO_DEFAULT_LEVEL) { + return 0; + } + + opt.algorithm = htole32(lzo->algorithm); + + if (lzo->algorithm == SQFS_LZO1X_999) { + opt.level = htole32(lzo->level); + } else { + opt.level = 0; + } + + *((sqfs_u16 *)buffer) = htole16(0x8000 | sizeof(opt)); + memcpy(buffer + 2, &opt, sizeof(opt)); + + ret = file->write_at(file, sizeof(sqfs_super_t), + buffer, sizeof(buffer)); + + return ret ? ret : (int)sizeof(buffer); +} + +static int lzo_read_options(sqfs_compressor_t *base, sqfs_file_t *file) +{ + lzo_compressor_t *lzo = (lzo_compressor_t *)base; + sqfs_u8 buffer[sizeof(lzo_options_t) + 2]; + lzo_options_t opt; + int ret; + + ret = file->read_at(file, sizeof(sqfs_super_t), + buffer, sizeof(buffer)); + if (ret) + return ret; + + if (le16toh(*((sqfs_u16 *)buffer)) != (0x8000 | sizeof(opt))) + return SQFS_ERROR_CORRUPTED; + + memcpy(&opt, buffer + 2, sizeof(opt)); + lzo->algorithm = le32toh(opt.algorithm); + lzo->level = le32toh(opt.level); + + switch(lzo->algorithm) { + case SQFS_LZO1X_1: + case SQFS_LZO1X_1_11: + case SQFS_LZO1X_1_12: + case SQFS_LZO1X_1_15: + if (lzo->level != 0) + return SQFS_ERROR_UNSUPPORTED; + break; + case SQFS_LZO1X_999: + if (lzo->level < 1 || lzo->level > 9) + return SQFS_ERROR_UNSUPPORTED; + break; + default: + return SQFS_ERROR_UNSUPPORTED; + } + + return 0; +} + +static sqfs_s32 lzo_comp_block(sqfs_compressor_t *base, const sqfs_u8 *in, + sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) +{ + lzo_compressor_t *lzo = (lzo_compressor_t *)base; + lzo_uint len = outsize; + int ret; + + if (size >= 0x7FFFFFFF) + return 0; + + if (lzo->algorithm == SQFS_LZO1X_999 && + lzo->level != SQFS_LZO_DEFAULT_LEVEL) { + ret = lzo1x_999_compress_level(in, size, out, &len, + lzo->buffer, NULL, 0, 0, + lzo->level); + } else { + ret = lzo_algs[lzo->algorithm].compress(in, size, out, + &len, lzo->buffer); + } + + if (ret != LZO_E_OK) + return SQFS_ERROR_COMPRESSOR; + + if (len < size) + return len; + + return 0; +} + +static sqfs_s32 lzo_uncomp_block(sqfs_compressor_t *base, const sqfs_u8 *in, + sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) +{ + lzo_compressor_t *lzo = (lzo_compressor_t *)base; + lzo_uint len = outsize; + int ret; + + if (outsize >= 0x7FFFFFFF) + return 0; + + ret = lzo1x_decompress_safe(in, size, out, &len, lzo->buffer); + + if (ret != LZO_E_OK) + return SQFS_ERROR_COMPRESSOR; + + return len; +} + +static sqfs_compressor_t *lzo_create_copy(sqfs_compressor_t *cmp) +{ + lzo_compressor_t *other = (lzo_compressor_t *)cmp; + lzo_compressor_t *lzo; + + lzo = alloc_flex(sizeof(*lzo), 1, lzo_algs[other->algorithm].bufsize); + if (lzo == NULL) + return NULL; + + memcpy(lzo, other, sizeof(*lzo)); + return (sqfs_compressor_t *)lzo; +} + +static void lzo_destroy(sqfs_compressor_t *base) +{ + free(base); +} + +sqfs_compressor_t *lzo_compressor_create(const sqfs_compressor_config_t *cfg) +{ + sqfs_compressor_t *base; + lzo_compressor_t *lzo; + + if (cfg->flags & ~SQFS_COMP_FLAG_GENERIC_ALL) + return NULL; + + if (cfg->opt.lzo.algorithm >= LZO_NUM_ALGS || + lzo_algs[cfg->opt.lzo.algorithm].compress == NULL) { + return NULL; + } + + if (cfg->opt.lzo.algorithm == SQFS_LZO1X_999) { + if (cfg->opt.lzo.level > SQFS_LZO_MAX_LEVEL) + return NULL; + } else if (cfg->opt.lzo.level != 0) { + return NULL; + } + + lzo = alloc_flex(sizeof(*lzo), 1, + lzo_algs[cfg->opt.lzo.algorithm].bufsize); + base = (sqfs_compressor_t *)lzo; + + if (lzo == NULL) + return NULL; + + lzo->algorithm = cfg->opt.lzo.algorithm; + lzo->level = cfg->opt.lzo.level; + + base->destroy = lzo_destroy; + base->do_block = (cfg->flags & SQFS_COMP_FLAG_UNCOMPRESS) ? + lzo_uncomp_block : lzo_comp_block; + base->write_options = lzo_write_options; + base->read_options = lzo_read_options; + base->create_copy = lzo_create_copy; + return base; +} diff --git a/lib/common/compress.c b/lib/common/compress.c index 04e1f40..a2f53c2 100644 --- a/lib/common/compress.c +++ b/lib/common/compress.c @@ -19,12 +19,20 @@ E_SQFS_COMPRESSOR compressor_get_default(void) void compressor_print_available(void) { + bool have_compressor; int i; fputs("Available compressors:\n", stdout); for (i = SQFS_COMP_MIN; i <= SQFS_COMP_MAX; ++i) { - if (sqfs_compressor_exists(i)) + have_compressor = sqfs_compressor_exists(i); + +#ifdef WITH_LZO + if (i == SQFS_COMP_LZO) + have_compressor = true; +#endif + + if (have_compressor) printf("\t%s\n", sqfs_compressor_name_from_id(i)); } diff --git a/lib/common/writer.c b/lib/common/writer.c index 1221358..d67f76b 100644 --- a/lib/common/writer.c +++ b/lib/common/writer.c @@ -86,6 +86,16 @@ int sqfs_writer_init(sqfs_writer_t *sqfs, const sqfs_writer_cfg_t *wrcfg) goto fail_file; sqfs->cmp = sqfs_compressor_create(&cfg); + +#ifdef WITH_LZO + if (cfg.id == SQFS_COMP_LZO) { + if (sqfs->cmp != NULL) + sqfs->cmp->destroy(sqfs->cmp); + + sqfs->cmp = lzo_compressor_create(&cfg); + } +#endif + if (sqfs->cmp == NULL) { fputs("Error creating compressor\n", stderr); goto fail_fs; |