aboutsummaryrefslogtreecommitdiff
path: root/lib/sqfs/comp/lzo.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/sqfs/comp/lzo.c')
-rw-r--r--lib/sqfs/comp/lzo.c321
1 files changed, 321 insertions, 0 deletions
diff --git a/lib/sqfs/comp/lzo.c b/lib/sqfs/comp/lzo.c
new file mode 100644
index 0000000..09ef75c
--- /dev/null
+++ b/lib/sqfs/comp/lzo.c
@@ -0,0 +1,321 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * lzo.c
+ *
+ * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include "config.h"
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include <lzo/lzo1x.h>
+
+#include "internal.h"
+
+
+typedef enum {
+ LZO_ALGORITHM_LZO1X_1 = 0,
+ LZO_ALGORITHM_LZO1X_1_11 = 1,
+ LZO_ALGORITHM_LZO1X_1_12 = 2,
+ LZO_ALGORITHM_LZO1X_1_15 = 3,
+ LZO_ALGORITHM_LZO1X_999 = 4,
+} LZO_ALGORITHM;
+
+#define LZO_DEFAULT_ALG LZO_ALGORITHM_LZO1X_999
+#define LZO_DEFAULT_LEVEL 8
+#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 {
+ const char *name;
+ lzo_cb_t compress;
+ size_t bufsize;
+} lzo_algs[] = {
+ [LZO_ALGORITHM_LZO1X_1] = {
+ .name = "lzo1x_1",
+ .compress = lzo1x_1_compress,
+ .bufsize = LZO1X_1_MEM_COMPRESS,
+ },
+ [LZO_ALGORITHM_LZO1X_1_11] = {
+ .name = "lzo1x_1_11",
+ .compress = lzo1x_1_11_compress,
+ .bufsize = LZO1X_1_11_MEM_COMPRESS,
+ },
+ [LZO_ALGORITHM_LZO1X_1_12] = {
+ .name = "lzo1x_1_12",
+ .compress = lzo1x_1_12_compress,
+ .bufsize = LZO1X_1_12_MEM_COMPRESS,
+ },
+ [LZO_ALGORITHM_LZO1X_1_15] = {
+ .name = "lzo1x_1_15",
+ .compress = lzo1x_1_15_compress,
+ .bufsize = LZO1X_1_15_MEM_COMPRESS,
+ },
+ [LZO_ALGORITHM_LZO1X_999] = {
+ .name = "lzo1x_999",
+ .compress = lzo1x_999_compress,
+ .bufsize = LZO1X_999_MEM_COMPRESS,
+ },
+};
+
+typedef struct {
+ compressor_t base;
+ int algorithm;
+ int level;
+
+ uint8_t buffer[];
+} lzo_compressor_t;
+
+typedef struct {
+ uint32_t algorithm;
+ uint32_t level;
+} lzo_options_t;
+
+static int lzo_write_options(compressor_t *base, int fd)
+{
+ lzo_compressor_t *lzo = (lzo_compressor_t *)base;
+ lzo_options_t opt;
+
+ if (lzo->algorithm == LZO_DEFAULT_ALG &&
+ lzo->level == LZO_DEFAULT_LEVEL) {
+ return 0;
+ }
+
+ opt.algorithm = htole32(lzo->algorithm);
+
+ if (lzo->algorithm == LZO_ALGORITHM_LZO1X_999) {
+ opt.level = htole32(lzo->level);
+ } else {
+ opt.level = 0;
+ }
+
+ return generic_write_options(fd, &opt, sizeof(opt));
+}
+
+static int lzo_read_options(compressor_t *base, int fd)
+{
+ lzo_compressor_t *lzo = (lzo_compressor_t *)base;
+ lzo_options_t opt;
+
+ if (generic_read_options(fd, &opt, sizeof(opt)))
+ return -1;
+
+ lzo->algorithm = le32toh(opt.algorithm);
+ lzo->level = le32toh(opt.level);
+
+ switch(lzo->algorithm) {
+ case LZO_ALGORITHM_LZO1X_1:
+ case LZO_ALGORITHM_LZO1X_1_11:
+ case LZO_ALGORITHM_LZO1X_1_12:
+ case LZO_ALGORITHM_LZO1X_1_15:
+ if (lzo->level != 0)
+ goto fail_level;
+ break;
+ case LZO_ALGORITHM_LZO1X_999:
+ if (lzo->level < 1 || lzo->level > 9)
+ goto fail_level;
+ break;
+ default:
+ fputs("Unsupported LZO variant specified.\n", stderr);
+ return -1;
+ }
+
+ return 0;
+fail_level:
+ fputs("Unsupported LZO compression level specified.\n", stderr);
+ return -1;
+}
+
+static ssize_t lzo_comp_block(compressor_t *base, const uint8_t *in,
+ size_t size, uint8_t *out, size_t outsize)
+{
+ lzo_compressor_t *lzo = (lzo_compressor_t *)base;
+ lzo_uint len = outsize;
+ int ret;
+
+ if (lzo->algorithm == LZO_ALGORITHM_LZO1X_999 &&
+ lzo->level != 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) {
+ fputs("LZO compression failed.\n", stderr);
+ return -1;
+ }
+
+ if (len < size)
+ return len;
+
+ return 0;
+}
+
+static ssize_t lzo_uncomp_block(compressor_t *base, const uint8_t *in,
+ size_t size, uint8_t *out, size_t outsize)
+{
+ lzo_compressor_t *lzo = (lzo_compressor_t *)base;
+ lzo_uint len = outsize;
+ int ret;
+
+ ret = lzo1x_decompress_safe(in, size, out, &len, lzo->buffer);
+
+ if (ret != LZO_E_OK) {
+ fputs("lzo decompress: input data is corrupted\n", stderr);
+ return -1;
+ }
+
+ return len;
+}
+
+static compressor_t *lzo_create_copy(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) {
+ perror("creating additional lzo compressor");
+ return NULL;
+ }
+
+ memcpy(lzo, other, sizeof(*lzo));
+ return (compressor_t *)lzo;
+}
+
+static void lzo_destroy(compressor_t *base)
+{
+ free(base);
+}
+
+static int process_options(char *options, int *algorithm, int *level)
+{
+ enum {
+ OPT_ALG = 0,
+ OPT_LEVEL,
+ };
+ char *const token[] = {
+ [OPT_ALG] = (char *)"algorithm",
+ [OPT_LEVEL] = (char *)"level",
+ NULL
+ };
+ char *subopts, *value;
+ size_t i;
+ int opt;
+
+ subopts = options;
+
+ while (*subopts != '\0') {
+ opt = getsubopt(&subopts, token, &value);
+
+ switch (opt) {
+ case OPT_ALG:
+ if (value == NULL)
+ goto fail_value;
+
+ for (i = 0; i < LZO_NUM_ALGS; ++i) {
+ if (strcmp(lzo_algs[i].name, value) == 0) {
+ *algorithm = i;
+ break;
+ }
+ }
+
+ if (i == LZO_NUM_ALGS) {
+ fprintf(stderr, "Unknown lzo variant '%s'.\n",
+ value);
+ return -1;
+ }
+ break;
+ case OPT_LEVEL:
+ if (value == NULL)
+ goto fail_value;
+
+ for (i = 0; isdigit(value[i]); ++i)
+ ;
+
+ if (i < 1 || i > 3 || value[i] != '\0')
+ goto fail_level;
+
+ *level = atoi(value);
+
+ if (*level < 1 || *level > 9)
+ goto fail_level;
+ break;
+ default:
+ goto fail_opt;
+ }
+ }
+
+ return 0;
+fail_level:
+ fputs("Compression level must be a number between 1 and 9.\n", stderr);
+ return -1;
+fail_opt:
+ fprintf(stderr, "Unknown option '%s'.\n", value);
+ return -1;
+fail_value:
+ fprintf(stderr, "Missing value for '%s'.\n", token[opt]);
+ return -1;
+}
+
+compressor_t *create_lzo_compressor(bool compress, size_t block_size,
+ char *options)
+{
+ lzo_compressor_t *lzo;
+ compressor_t *base;
+ int level, alg;
+ (void)block_size;
+
+ alg = LZO_DEFAULT_ALG;
+ level = LZO_DEFAULT_LEVEL;
+
+ if (options != NULL && process_options(options, &alg, &level) != 0)
+ return NULL;
+
+ lzo = alloc_flex(sizeof(*lzo), 1, lzo_algs[alg].bufsize);
+ base = (compressor_t *)lzo;
+
+ if (lzo == NULL) {
+ perror("creating lzo compressor");
+ return NULL;
+ }
+
+ lzo->algorithm = alg;
+ lzo->level = level;
+
+ base->destroy = lzo_destroy;
+ base->do_block = compress ? lzo_comp_block : lzo_uncomp_block;
+ base->write_options = lzo_write_options;
+ base->read_options = lzo_read_options;
+ base->create_copy = lzo_create_copy;
+ return base;
+}
+
+void compressor_lzo_print_help(void)
+{
+ size_t i;
+
+ fputs("Available options for lzo compressor:\n"
+ "\n"
+ " algorithm=<name> Specify the variant of lzo to use.\n"
+ " Defaults to 'lzo1x_999'.\n"
+ " level=<value> For lzo1x_999, the compression level.\n"
+ " Value from 1 to 9. Defaults to 8.\n"
+ " Ignored if algorithm is not lzo1x_999.\n"
+ "\n"
+ "Available algorithms:\n",
+ stdout);
+
+ for (i = 0; i < LZO_NUM_ALGS; ++i)
+ printf("\t%s\n", lzo_algs[i].name);
+}