summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Oberhollenzer <david.oberhollenzer@sigma-star.at>2019-09-01 22:42:49 +0200
committerDavid Oberhollenzer <david.oberhollenzer@sigma-star.at>2019-09-01 22:42:49 +0200
commit307107ecd2fc3ffbf6fe91497daf767700f3572f (patch)
tree87c2c5993ab10cd4aa791a4e6d34f251db208ed2
parent2e28c45601a57b1d23e9cad33d2bdcc59e8a3f4f (diff)
Move command line processing stuff out of compressor code
This commit moves stuff like printing help text, command line option processing and enumerating available processors on stdout out of the generic compressor code. The option string is replaced with a structure that directly exposese the tweakable parameters for all compressors. A function for parsing the command line arguments into this structure is added in sqfshelper. Signed-off-by: David Oberhollenzer <david.oberhollenzer@sigma-star.at>
-rw-r--r--include/highlevel.h9
-rw-r--r--include/sqfs/compress.h91
-rw-r--r--lib/Makemodule.am1
-rw-r--r--lib/sqfs/comp/compressor.c100
-rw-r--r--lib/sqfs/comp/gzip.c210
-rw-r--r--lib/sqfs/comp/internal.h25
-rw-r--r--lib/sqfs/comp/lz4.c42
-rw-r--r--lib/sqfs/comp/lzo.c176
-rw-r--r--lib/sqfs/comp/xz.c216
-rw-r--r--lib/sqfs/comp/zstd.c64
-rw-r--r--lib/sqfshelper/comp_opt.c380
-rw-r--r--lib/sqfshelper/compress.c33
-rw-r--r--lib/sqfshelper/sqfs_reader.c9
-rw-r--r--mkfs/mkfs.c9
-rw-r--r--tar/tar2sqfs.c8
15 files changed, 752 insertions, 621 deletions
diff --git a/include/highlevel.h b/include/highlevel.h
index b9d9339..6b8e2e2 100644
--- a/include/highlevel.h
+++ b/include/highlevel.h
@@ -141,4 +141,13 @@ int meta_writer_write_dir(meta_writer_t *dm, dir_info_t *dir,
int meta_writer_write_inode(fstree_t *fs, id_table_t *idtbl, meta_writer_t *im,
meta_writer_t *dm, tree_node_t *node);
+void compressor_print_available(void);
+
+E_SQFS_COMPRESSOR compressor_get_default(void);
+
+int compressor_cfg_init_options(compressor_config_t *cfg, E_SQFS_COMPRESSOR id,
+ size_t block_size, char *options);
+
+void compressor_print_help(E_SQFS_COMPRESSOR id);
+
#endif /* HIGHLEVEL_H */
diff --git a/include/sqfs/compress.h b/include/sqfs/compress.h
index 3860353..cb3994f 100644
--- a/include/sqfs/compress.h
+++ b/include/sqfs/compress.h
@@ -51,22 +51,93 @@ struct compressor_t {
void (*destroy)(compressor_t *stream);
};
+typedef struct {
+ uint16_t id;
+ uint16_t flags;
+ uint32_t block_size;
+
+ union {
+ struct {
+ uint16_t level;
+ uint16_t window_size;
+ } gzip;
+
+ struct {
+ uint16_t level;
+ } zstd;
+
+ struct {
+ uint16_t algorithm;
+ uint16_t level;
+ } lzo;
+
+ struct {
+ uint32_t dict_size;
+ } xz;
+ } opt;
+} compressor_config_t;
+
+typedef enum {
+ SQFS_COMP_FLAG_LZ4_HC = 0x0001,
+ SQFS_COMP_FLAG_LZ4_ALL = 0x0001,
+
+ SQFS_COMP_FLAG_XZ_X86 = 0x0001,
+ SQFS_COMP_FLAG_XZ_POWERPC = 0x0002,
+ SQFS_COMP_FLAG_XZ_IA64 = 0x0004,
+ SQFS_COMP_FLAG_XZ_ARM = 0x0008,
+ SQFS_COMP_FLAG_XZ_ARMTHUMB = 0x0010,
+ SQFS_COMP_FLAG_XZ_SPARC = 0x0020,
+ SQFS_COMP_FLAG_XZ_ALL = 0x003F,
+
+ SQFS_COMP_FLAG_GZIP_DEFAULT = 0x0001,
+ SQFS_COMP_FLAG_GZIP_FILTERED = 0x0002,
+ SQFS_COMP_FLAG_GZIP_HUFFMAN = 0x0004,
+ SQFS_COMP_FLAG_GZIP_RLE = 0x0008,
+ SQFS_COMP_FLAG_GZIP_FIXED = 0x0010,
+ SQFS_COMP_FLAG_GZIP_ALL = 0x001F,
+
+ SQFS_COMP_FLAG_UNCOMPRESS = 0x8000,
+ SQFS_COMP_FLAG_GENERIC_ALL = 0x8000,
+} SQFS_COMP_FLAG;
+
+typedef enum {
+ SQFS_LZO1X_1 = 0,
+ SQFS_LZO1X_1_11 = 1,
+ SQFS_LZO1X_1_12 = 2,
+ SQFS_LZO1X_1_15 = 3,
+ SQFS_LZO1X_999 = 4,
+} SQFS_LZO_ALGORITHM;
+
+#define SQFS_GZIP_DEFAULT_LEVEL (9)
+#define SQFS_GZIP_DEFAULT_WINDOW (15)
+
+#define SQFS_LZO_DEFAULT_ALG SQFS_LZO1X_999
+#define SQFS_LZO_DEFAULT_LEVEL (8)
+
+#define SQFS_ZSTD_DEFAULT_LEVEL (15)
+
+#define SQFS_GZIP_MIN_LEVEL (1)
+#define SQFS_GZIP_MAX_LEVEL (9)
+
+#define SQFS_LZO_MIN_LEVEL (0)
+#define SQFS_LZO_MAX_LEVEL (9)
+
+#define SQFS_ZSTD_MIN_LEVEL (1)
+#define SQFS_ZSTD_MAX_LEVEL (22)
+
+#define SQFS_GZIP_MIN_WINDOW (8)
+#define SQFS_GZIP_MAX_WINDOW (15)
+
#ifdef __cplusplus
extern "C" {
#endif
-bool compressor_exists(E_SQFS_COMPRESSOR id);
-
-/* block_size is the configured block size for the SquashFS image. Needed
- by some compressors to set internal defaults. */
-compressor_t *compressor_create(E_SQFS_COMPRESSOR id, bool compress,
- size_t block_size, char *options);
+int compressor_config_init(compressor_config_t *cfg, E_SQFS_COMPRESSOR id,
+ size_t block_size, uint16_t flags);
-void compressor_print_help(E_SQFS_COMPRESSOR id);
-
-void compressor_print_available(void);
+bool compressor_exists(E_SQFS_COMPRESSOR id);
-E_SQFS_COMPRESSOR compressor_get_default(void);
+compressor_t *compressor_create(const compressor_config_t *cfg);
const char *compressor_name_from_id(E_SQFS_COMPRESSOR id);
diff --git a/lib/Makemodule.am b/lib/Makemodule.am
index bf2e965..3681344 100644
--- a/lib/Makemodule.am
+++ b/lib/Makemodule.am
@@ -29,6 +29,7 @@ libsqfshelper_a_SOURCES += lib/sqfshelper/write_inode.c
libsqfshelper_a_SOURCES += lib/sqfshelper/write_export_table.c
libsqfshelper_a_SOURCES += lib/sqfshelper/xattr_reader.c
libsqfshelper_a_SOURCES += lib/sqfshelper/print_version.c
+libsqfshelper_a_SOURCES += lib/sqfshelper/compress.c lib/sqfshelper/comp_opt.c
libsqfshelper_a_SOURCES += include/data_reader.h lib/sqfshelper/data_reader.c
libsqfshelper_a_SOURCES += include/data_writer.h lib/sqfshelper/data_writer.c
libsqfshelper_a_SOURCES += include/xattr_reader.h lib/sqfshelper/write_xattr.c
diff --git a/lib/sqfs/comp/compressor.c b/lib/sqfs/comp/compressor.c
index e59d948..d436100 100644
--- a/lib/sqfs/comp/compressor.c
+++ b/lib/sqfs/comp/compressor.c
@@ -13,10 +13,7 @@
#include "internal.h"
#include "util.h"
-typedef compressor_t *(*compressor_fun_t)(bool compress, size_t block_size,
- char *options);
-
-typedef void (*compressor_help_fun_t)(void);
+typedef compressor_t *(*compressor_fun_t)(const compressor_config_t *cfg);
static compressor_fun_t compressors[SQFS_COMP_MAX + 1] = {
#ifdef WITH_GZIP
@@ -36,24 +33,6 @@ static compressor_fun_t compressors[SQFS_COMP_MAX + 1] = {
#endif
};
-static const compressor_help_fun_t helpfuns[SQFS_COMP_MAX + 1] = {
-#ifdef WITH_GZIP
- [SQFS_COMP_GZIP] = compressor_gzip_print_help,
-#endif
-#ifdef WITH_XZ
- [SQFS_COMP_XZ] = compressor_xz_print_help,
-#endif
-#ifdef WITH_LZO
- [SQFS_COMP_LZO] = compressor_lzo_print_help,
-#endif
-#ifdef WITH_LZ4
- [SQFS_COMP_LZ4] = compressor_lz4_print_help,
-#endif
-#ifdef WITH_ZSTD
- [SQFS_COMP_ZSTD] = compressor_zstd_print_help,
-#endif
-};
-
static const char *names[] = {
[SQFS_COMP_GZIP] = "gzip",
[SQFS_COMP_LZMA] = "lzma",
@@ -105,41 +84,15 @@ bool compressor_exists(E_SQFS_COMPRESSOR id)
return (compressors[id] != NULL);
}
-compressor_t *compressor_create(E_SQFS_COMPRESSOR id, bool compress,
- size_t block_size, char *options)
+compressor_t *compressor_create(const compressor_config_t *cfg)
{
- if (id < SQFS_COMP_MIN || id > SQFS_COMP_MAX)
+ if (cfg == NULL || cfg->id < SQFS_COMP_MIN || cfg->id > SQFS_COMP_MAX)
return NULL;
- if (compressors[id] == NULL)
+ if (compressors[cfg->id] == NULL)
return NULL;
- return compressors[id](compress, block_size, options);
-}
-
-void compressor_print_help(E_SQFS_COMPRESSOR id)
-{
- if (id < SQFS_COMP_MIN || id > SQFS_COMP_MAX)
- return;
-
- if (compressors[id] == NULL)
- return;
-
- helpfuns[id]();
-}
-
-void compressor_print_available(void)
-{
- size_t i;
-
- fputs("Available compressors:\n", stdout);
-
- for (i = 0; i < sizeof(names) / sizeof(names[0]); ++i) {
- if (compressor_exists(i))
- printf("\t%s\n", names[i]);
- }
-
- printf("\nDefault compressor: %s\n", names[compressor_get_default()]);
+ return compressors[cfg->id](cfg);
}
const char *compressor_name_from_id(E_SQFS_COMPRESSOR id)
@@ -164,20 +117,33 @@ int compressor_id_from_name(const char *name, E_SQFS_COMPRESSOR *out)
return -1;
}
-E_SQFS_COMPRESSOR compressor_get_default(void)
+int compressor_config_init(compressor_config_t *cfg, E_SQFS_COMPRESSOR id,
+ size_t block_size, uint16_t flags)
{
-#if defined(WITH_XZ)
- return SQFS_COMP_XZ;
-#elif defined(WITH_ZSTD)
- return SQFS_COMP_ZSTD;
-#elif defined(WITH_GZIP)
- return SQFS_COMP_GZIP;
-#elif defined(WITH_LZO)
- return SQFS_COMP_LZO;
-#elif defined(WITH_LZ4)
- return SQFS_COMP_LZ4;
-#else
- fputs("No compressor implementation available!\n", stderr);
- exit(EXIT_FAILURE);
-#endif
+ memset(cfg, 0, sizeof(*cfg));
+
+ cfg->id = id;
+ cfg->flags = flags;
+ cfg->block_size = block_size;
+
+ switch (id) {
+ case SQFS_COMP_GZIP:
+ cfg->opt.gzip.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->opt.lzo.level = SQFS_LZO_DEFAULT_LEVEL;
+ break;
+ case SQFS_COMP_ZSTD:
+ cfg->opt.zstd.level = SQFS_ZSTD_DEFAULT_LEVEL;
+ break;
+ case SQFS_COMP_XZ:
+ cfg->opt.xz.dict_size = block_size;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
}
diff --git a/lib/sqfs/comp/gzip.c b/lib/sqfs/comp/gzip.c
index 1fdc051..bbe1e4f 100644
--- a/lib/sqfs/comp/gzip.c
+++ b/lib/sqfs/comp/gzip.c
@@ -15,32 +15,6 @@
#include "internal.h"
-#define GZIP_DEFAULT_LEVEL 9
-#define GZIP_DEFAULT_WINDOW 15
-#define GZIP_NUM_STRATEGIES (sizeof(strategies) / sizeof(strategies[0]))
-
-typedef enum {
- GZIP_STRATEGY_DEFAULT = 0x01,
- GZIP_STRATEGY_FILTERED = 0x02,
- GZIP_STRATEGY_HUFFMAN = 0x04,
- GZIP_STARTEGY_RLE = 0x08,
- GZIP_STRATEGY_FIXED = 0x10,
-
- GZIP_ALL_STRATEGIES = 0x1F,
-} GZIP_STRATEGIES;
-
-static const struct {
- const char *name;
- int flag;
- int zlib;
-} strategies[] = {
- { "default", GZIP_STRATEGY_DEFAULT, Z_DEFAULT_STRATEGY },
- { "filtered", GZIP_STRATEGY_FILTERED, Z_FILTERED },
- { "huffman", GZIP_STRATEGY_HUFFMAN, Z_HUFFMAN_ONLY },
- { "rle", GZIP_STARTEGY_RLE, Z_RLE },
- { "fixed", GZIP_STRATEGY_FIXED, Z_FIXED },
-};
-
typedef struct {
uint32_t level;
uint16_t window;
@@ -75,8 +49,8 @@ static int gzip_write_options(compressor_t *base, int fd)
gzip_compressor_t *gzip = (gzip_compressor_t *)base;
gzip_options_t opt;
- if (gzip->opt.level == GZIP_DEFAULT_LEVEL &&
- gzip->opt.window == GZIP_DEFAULT_WINDOW &&
+ if (gzip->opt.level == SQFS_GZIP_DEFAULT_LEVEL &&
+ gzip->opt.window == SQFS_GZIP_DEFAULT_WINDOW &&
gzip->opt.strategies == 0) {
return 0;
}
@@ -112,7 +86,7 @@ static int gzip_read_options(compressor_t *base, int fd)
return -1;
}
- if (gzip->opt.strategies & ~GZIP_ALL_STRATEGIES) {
+ if (gzip->opt.strategies & ~SQFS_COMP_FLAG_GZIP_ALL) {
fputs("Unknown gzip strategies selected.\n", stderr);
return -1;
}
@@ -120,14 +94,32 @@ static int gzip_read_options(compressor_t *base, int fd)
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;
+ }
+
+ return 0;
+}
+
static int find_strategy(gzip_compressor_t *gzip, const uint8_t *in,
size_t size, uint8_t *out, size_t outsize)
{
- int ret, selected = Z_DEFAULT_STRATEGY;
+ int ret, strategy, selected = Z_DEFAULT_STRATEGY;
size_t i, length, minlength = 0;
- for (i = 0; i < GZIP_NUM_STRATEGIES; ++i) {
- if (!(strategies[i].flag & gzip->opt.strategies))
+ for (i = 0x01; i & SQFS_COMP_FLAG_GZIP_ALL; i <<= 1) {
+ if ((gzip->opt.strategies & i) == 0)
continue;
ret = deflateReset(&gzip->strm);
@@ -137,13 +129,14 @@ static int find_strategy(gzip_compressor_t *gzip, const uint8_t *in,
return -1;
}
+ strategy = flag_to_zlib_strategy(i);
+
gzip->strm.next_in = (void *)in;
gzip->strm.avail_in = size;
gzip->strm.next_out = out;
gzip->strm.avail_out = outsize;
- ret = deflateParams(&gzip->strm, gzip->opt.level,
- strategies[i].zlib);
+ ret = deflateParams(&gzip->strm, gzip->opt.level, strategy);
if (ret != Z_OK) {
fputs("setting deflate parameters failed\n",
stderr);
@@ -157,7 +150,7 @@ static int find_strategy(gzip_compressor_t *gzip, const uint8_t *in,
if (minlength == 0 || length < minlength) {
minlength = length;
- selected = strategies[i].zlib;
+ selected = strategy;
}
} else if (ret != Z_OK && ret != Z_BUF_ERROR) {
fputs("gzip block processing failed\n", stderr);
@@ -229,85 +222,6 @@ static ssize_t gzip_do_block(compressor_t *base, const uint8_t *in,
return 0;
}
-static int process_options(char *options, int *level, int *window, int *flags)
-{
- enum {
- OPT_WINDOW = 0,
- OPT_LEVEL,
- };
- char *const token[] = {
- [OPT_WINDOW] = (char *)"window",
- [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_WINDOW:
- if (value == NULL)
- goto fail_value;
-
- for (i = 0; isdigit(value[i]); ++i)
- ;
-
- if (i < 1 || i > 3 || value[i] != '\0')
- goto fail_window;
-
- *window = atoi(value);
-
- if (*window < 8 || *window > 15)
- goto fail_window;
- 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:
- for (i = 0; i < GZIP_NUM_STRATEGIES; ++i) {
- if (strcmp(value, strategies[i].name) == 0) {
- *flags |= strategies[i].flag;
- break;
- }
- }
- if (i == GZIP_NUM_STRATEGIES)
- goto fail_opt;
- break;
- }
- }
-
- return 0;
-fail_window:
- fputs("Window size must be a number between 8 and 15.\n", stderr);
- return -1;
-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;
-}
-
static compressor_t *gzip_create_copy(compressor_t *cmp)
{
gzip_compressor_t *gzip = malloc(sizeof(*gzip));
@@ -338,19 +252,33 @@ static compressor_t *gzip_create_copy(compressor_t *cmp)
return (compressor_t *)gzip;
}
-compressor_t *create_gzip_compressor(bool compress, size_t block_size,
- char *options)
+compressor_t *create_gzip_compressor(const compressor_config_t *cfg)
{
- int window = GZIP_DEFAULT_WINDOW;
- int level = GZIP_DEFAULT_LEVEL;
gzip_compressor_t *gzip;
compressor_t *base;
- int flags = 0;
int ret;
- if (options != NULL) {
- if (process_options(options, &level, &window, &flags))
- return NULL;
+ if (cfg->flags & ~(SQFS_COMP_FLAG_GZIP_ALL |
+ SQFS_COMP_FLAG_GENERIC_ALL)) {
+ fputs("creating gzip compressor: unknown compressor flags\n",
+ stderr);
+ return NULL;
+ }
+
+ if (cfg->opt.gzip.level < SQFS_GZIP_MIN_LEVEL ||
+ cfg->opt.gzip.level > SQFS_GZIP_MAX_LEVEL) {
+ fprintf(stderr, "creating gzip compressor: compression level"
+ "must be between %d and %d inclusive\n",
+ SQFS_GZIP_MIN_LEVEL, SQFS_GZIP_MAX_LEVEL);
+ return NULL;
+ }
+
+ if (cfg->opt.gzip.window_size < SQFS_GZIP_MIN_WINDOW ||
+ cfg->opt.gzip.window_size > SQFS_GZIP_MAX_WINDOW) {
+ fprintf(stderr, "creating gzip compressor: window size"
+ "must be between %d and %d inclusive\n",
+ SQFS_GZIP_MIN_WINDOW, SQFS_GZIP_MAX_WINDOW);
+ return NULL;
}
gzip = calloc(1, sizeof(*gzip));
@@ -361,19 +289,20 @@ compressor_t *create_gzip_compressor(bool compress, size_t block_size,
return NULL;
}
- gzip->opt.level = level;
- gzip->opt.window = window;
- gzip->opt.strategies = flags;
- gzip->compress = compress;
- gzip->block_size = block_size;
+ gzip->opt.level = cfg->opt.gzip.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->do_block = gzip_do_block;
base->destroy = gzip_destroy;
base->write_options = gzip_write_options;
base->read_options = gzip_read_options;
base->create_copy = gzip_create_copy;
- if (compress) {
- ret = deflateInit2(&gzip->strm, level, Z_DEFLATED, window, 8,
+ if (gzip->compress) {
+ ret = deflateInit2(&gzip->strm, cfg->opt.gzip.level,
+ Z_DEFLATED, cfg->opt.gzip.window_size, 8,
Z_DEFAULT_STRATEGY);
} else {
ret = inflateInit(&gzip->strm);
@@ -387,26 +316,3 @@ compressor_t *create_gzip_compressor(bool compress, size_t block_size,
return base;
}
-
-void compressor_gzip_print_help(void)
-{
- size_t i;
-
- printf(
-"Available options for gzip compressor:\n"
-"\n"
-" level=<value> Compression level. Value from 1 to 9.\n"
-" Defaults to %d.\n"
-" window=<size> Deflate compression window size. Value from 8 to 15.\n"
-" Defaults to %d.\n"
-"\n"
-"In additon to the options, one or more strategies can be specified.\n"
-"If multiple stratgies are provided, the one yielding the best compression\n"
-"ratio will be used.\n"
-"\n"
-"The following strategies are available:\n",
- GZIP_DEFAULT_LEVEL, GZIP_DEFAULT_WINDOW);
-
- for (i = 0; i < GZIP_NUM_STRATEGIES; ++i)
- printf("\t%s\n", strategies[i].name);
-}
diff --git a/lib/sqfs/comp/internal.h b/lib/sqfs/comp/internal.h
index 19ef5cd..229772f 100644
--- a/lib/sqfs/comp/internal.h
+++ b/lib/sqfs/comp/internal.h
@@ -16,29 +16,14 @@ int generic_write_options(int fd, const void *data, size_t size);
int generic_read_options(int fd, void *data, size_t size);
-compressor_t *create_xz_compressor(bool compress, size_t block_size,
- char *options);
+compressor_t *create_xz_compressor(const compressor_config_t *cfg);
-compressor_t *create_gzip_compressor(bool compress, size_t block_size,
- char *options);
+compressor_t *create_gzip_compressor(const compressor_config_t *cfg);
-compressor_t *create_lzo_compressor(bool compress, size_t block_size,
- char *options);
+compressor_t *create_lzo_compressor(const compressor_config_t *cfg);
-compressor_t *create_lz4_compressor(bool compress, size_t block_size,
- char *options);
+compressor_t *create_lz4_compressor(const compressor_config_t *cfg);
-compressor_t *create_zstd_compressor(bool compress, size_t block_size,
- char *options);
-
-void compressor_xz_print_help(void);
-
-void compressor_gzip_print_help(void);
-
-void compressor_lzo_print_help(void);
-
-void compressor_lz4_print_help(void);
-
-void compressor_zstd_print_help(void);
+compressor_t *create_zstd_compressor(const compressor_config_t *cfg);
#endif /* INTERNAL_H */
diff --git a/lib/sqfs/comp/lz4.c b/lib/sqfs/comp/lz4.c
index abb6c5c..4a15198 100644
--- a/lib/sqfs/comp/lz4.c
+++ b/lib/sqfs/comp/lz4.c
@@ -27,14 +27,14 @@ typedef struct {
} lz4_options;
#define LZ4LEGACY 1
-#define LZ4_FLAG_HC 0x01
static int lz4_write_options(compressor_t *base, int fd)
{
lz4_compressor_t *lz4 = (lz4_compressor_t *)base;
lz4_options opt = {
.version = htole32(LZ4LEGACY),
- .flags = htole32(lz4->high_compression ? LZ4_FLAG_HC : 0),
+ .flags = htole32(lz4->high_compression ?
+ SQFS_COMP_FLAG_LZ4_HC : 0),
};
return generic_write_options(fd, &opt, sizeof(opt));
@@ -115,45 +115,31 @@ static void lz4_destroy(compressor_t *base)
free(base);
}
-compressor_t *create_lz4_compressor(bool compress, size_t block_size,
- char *options)
+compressor_t *create_lz4_compressor(const compressor_config_t *cfg)
{
lz4_compressor_t *lz4 = calloc(1, sizeof(*lz4));
compressor_t *base = (compressor_t *)lz4;
- (void)block_size;
+ if (cfg->flags & ~(SQFS_COMP_FLAG_LZ4_ALL |
+ SQFS_COMP_FLAG_GENERIC_ALL)) {
+ fputs("creating lz4 compressor: unknown compressor flags\n",
+ stderr);
+ }
+
+ lz4 = calloc(1, sizeof(*lz4));
+ base = (compressor_t *)lz4;
if (lz4 == NULL) {
perror("creating lz4 compressor");
return NULL;
}
- lz4->high_compression = false;
-
- if (options != NULL) {
- if (strcmp(options, "hc") == 0) {
- lz4->high_compression = true;
- } else {
- fputs("Unsupported extra options for lz4 "
- "compressor.\n", stderr);
- free(lz4);
- return NULL;
- }
- }
+ lz4->high_compression = (cfg->flags & SQFS_COMP_FLAG_LZ4_HC) != 0;
base->destroy = lz4_destroy;
- base->do_block = compress ? lz4_comp_block : lz4_uncomp_block;
+ 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;
base->create_copy = lz4_create_copy;
return base;
}
-
-void compressor_lz4_print_help(void)
-{
- fputs("Available options for lz4 compressor:\n"
- "\n"
- " hc If present, use slower but better compressing\n"
- " variant of lz4.\n"
- "\n",
- stdout);
-}
diff --git a/lib/sqfs/comp/lzo.c b/lib/sqfs/comp/lzo.c
index 09ef75c..3293af1 100644
--- a/lib/sqfs/comp/lzo.c
+++ b/lib/sqfs/comp/lzo.c
@@ -16,49 +16,32 @@
#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",
+ [SQFS_LZO1X_1] = {
.compress = lzo1x_1_compress,
.bufsize = LZO1X_1_MEM_COMPRESS,
},
- [LZO_ALGORITHM_LZO1X_1_11] = {
- .name = "lzo1x_1_11",
+ [SQFS_LZO1X_1_11] = {
.compress = lzo1x_1_11_compress,
.bufsize = LZO1X_1_11_MEM_COMPRESS,
},
- [LZO_ALGORITHM_LZO1X_1_12] = {
- .name = "lzo1x_1_12",
+ [SQFS_LZO1X_1_12] = {
.compress = lzo1x_1_12_compress,
.bufsize = LZO1X_1_12_MEM_COMPRESS,
},
- [LZO_ALGORITHM_LZO1X_1_15] = {
- .name = "lzo1x_1_15",
+ [SQFS_LZO1X_1_15] = {
.compress = lzo1x_1_15_compress,
.bufsize = LZO1X_1_15_MEM_COMPRESS,
},
- [LZO_ALGORITHM_LZO1X_999] = {
- .name = "lzo1x_999",
+ [SQFS_LZO1X_999] = {
.compress = lzo1x_999_compress,
.bufsize = LZO1X_999_MEM_COMPRESS,
},
@@ -82,14 +65,14 @@ 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) {
+ if (lzo->algorithm == SQFS_LZO_DEFAULT_ALG &&
+ lzo->level == SQFS_LZO_DEFAULT_LEVEL) {
return 0;
}
opt.algorithm = htole32(lzo->algorithm);
- if (lzo->algorithm == LZO_ALGORITHM_LZO1X_999) {
+ if (lzo->algorithm == SQFS_LZO1X_999) {
opt.level = htole32(lzo->level);
} else {
opt.level = 0;
@@ -110,14 +93,14 @@ static int lzo_read_options(compressor_t *base, int fd)
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:
+ case SQFS_LZO1X_1:
+ case SQFS_LZO1X_1_11:
+ case SQFS_LZO1X_1_12:
+ case SQFS_LZO1X_1_15:
if (lzo->level != 0)
goto fail_level;
break;
- case LZO_ALGORITHM_LZO1X_999:
+ case SQFS_LZO1X_999:
if (lzo->level < 1 || lzo->level > 9)
goto fail_level;
break;
@@ -139,8 +122,8 @@ static ssize_t lzo_comp_block(compressor_t *base, const uint8_t *in,
lzo_uint len = outsize;
int ret;
- if (lzo->algorithm == LZO_ALGORITHM_LZO1X_999 &&
- lzo->level != LZO_DEFAULT_LEVEL) {
+ 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);
@@ -198,91 +181,38 @@ 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)
+compressor_t *create_lzo_compressor(const compressor_config_t *cfg)
{
lzo_compressor_t *lzo;
compressor_t *base;
- int level, alg;
- (void)block_size;
- alg = LZO_DEFAULT_ALG;
- level = LZO_DEFAULT_LEVEL;
+ if (cfg->flags & ~SQFS_COMP_FLAG_GENERIC_ALL) {
+ fputs("creating lzo compressor: unknown compressor flags\n",
+ stderr);
+ return NULL;
+ }
- if (options != NULL && process_options(options, &alg, &level) != 0)
+ if (cfg->opt.lzo.algorithm > LZO_NUM_ALGS ||
+ lzo_algs[cfg->opt.lzo.algorithm].compress == NULL) {
+ fputs("creating lzo compressor: unknown LZO variant\n",
+ stderr);
return NULL;
+ }
- lzo = alloc_flex(sizeof(*lzo), 1, lzo_algs[alg].bufsize);
+ if (cfg->opt.lzo.algorithm == SQFS_LZO1X_999) {
+ if (cfg->opt.lzo.level > SQFS_LZO_MAX_LEVEL) {
+ fputs("creating lzo compressor: compression level "
+ "must be between 0 and 9 inclusive\n", stderr);
+ return NULL;
+ }
+ } else if (cfg->opt.lzo.level != 0) {
+ fputs("creating lzo compressor: level argument "
+ "only supported by lzo1x 999\n", stderr);
+ return NULL;
+ }
+
+ lzo = alloc_flex(sizeof(*lzo), 1,
+ lzo_algs[cfg->opt.lzo.algorithm].bufsize);
base = (compressor_t *)lzo;
if (lzo == NULL) {
@@ -290,32 +220,14 @@ compressor_t *create_lzo_compressor(bool compress, size_t block_size,
return NULL;
}
- lzo->algorithm = alg;
- lzo->level = level;
+ lzo->algorithm = cfg->opt.lzo.algorithm;
+ lzo->level = cfg->opt.lzo.level;
base->destroy = lzo_destroy;
- base->do_block = compress ? lzo_comp_block : lzo_uncomp_block;
+ 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;
}
-
-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);
-}
diff --git a/lib/sqfs/comp/xz.c b/lib/sqfs/comp/xz.c
index d38aab6..4f67b1c 100644
--- a/lib/sqfs/comp/xz.c
+++ b/lib/sqfs/comp/xz.c
@@ -15,17 +15,6 @@
#include "internal.h"
-typedef enum {
- XZ_FILTER_X86 = 0x01,
- XZ_FILTER_POWERPC = 0x02,
- XZ_FILTER_IA64 = 0x04,
- XZ_FILTER_ARM = 0x08,
- XZ_FILTER_ARMTHUMB = 0x10,
- XZ_FILTER_SPARC = 0x20,
-
- XZ_FILTER_ALL = 0x3F,
-} XZ_FILTER_FLAG;
-
typedef struct {
compressor_t base;
size_t block_size;
@@ -38,20 +27,15 @@ typedef struct {
uint32_t flags;
} xz_options_t;
-static const struct {
- const char *name;
- lzma_vli filter;
- int flag;
-} xz_filters[] = {
- { "x86", LZMA_FILTER_X86, XZ_FILTER_X86 },
- { "powerpc", LZMA_FILTER_POWERPC, XZ_FILTER_POWERPC },
- { "ia64", LZMA_FILTER_IA64, XZ_FILTER_IA64 },
- { "arm", LZMA_FILTER_ARM, XZ_FILTER_ARM },
- { "armthumb", LZMA_FILTER_ARMTHUMB, XZ_FILTER_ARMTHUMB },
- { "sparc", LZMA_FILTER_SPARC, XZ_FILTER_SPARC },
-};
-
-#define XZ_NUM_FILTERS (sizeof(xz_filters) / sizeof(xz_filters[0]))
+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(compressor_t *base, int fd)
{
@@ -71,7 +55,6 @@ static int xz_read_options(compressor_t *base, int fd)
{
xz_compressor_t *xz = (xz_compressor_t *)base;
xz_options_t opt;
- uint32_t mask;
if (generic_read_options(fd, &opt, sizeof(opt)))
return -1;
@@ -79,14 +62,12 @@ static int xz_read_options(compressor_t *base, int fd)
opt.dict_size = le32toh(opt.dict_size);
opt.flags = le32toh(opt.flags);
- mask = opt.dict_size & (opt.dict_size - 1);
-
- if (mask != 0 && ((mask & (mask - 1)) != 0)) {
+ if (!is_dict_size_valid(opt.dict_size)) {
fputs("Invalid lzma dictionary size.\n", stderr);
return -1;
}
- if (opt.flags & ~XZ_FILTER_ALL) {
+ if (opt.flags & ~SQFS_COMP_FLAG_XZ_ALL) {
fputs("Unknown BCJ filter used.\n", stderr);
return -1;
}
@@ -141,11 +122,31 @@ static ssize_t compress(xz_compressor_t *xz, lzma_vli filter,
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;
+ }
+
+ return LZMA_VLI_UNKNOWN;
+}
+
static ssize_t xz_comp_block(compressor_t *base, const uint8_t *in,
size_t size, uint8_t *out, size_t outsize)
{
xz_compressor_t *xz = (xz_compressor_t *)base;
- lzma_vli selected = LZMA_VLI_UNKNOWN;
+ lzma_vli filter, selected = LZMA_VLI_UNKNOWN;
size_t i, smallest;
ssize_t ret;
@@ -155,17 +156,19 @@ static ssize_t xz_comp_block(compressor_t *base, const uint8_t *in,
smallest = ret;
- for (i = 0; i < XZ_NUM_FILTERS; ++i) {
- if (!(xz->flags & xz_filters[i].flag))
+ for (i = 0; i & SQFS_COMP_FLAG_XZ_ALL; i <<= 1) {
+ if ((xz->flags & i) == 0)
continue;
- ret = compress(xz, xz_filters[i].filter, in, size, out, outsize);
+ filter = flag_to_vli(i);
+
+ ret = compress(xz, filter, in, size, out, outsize);
if (ret < 0)
return -1;
if (ret > 0 && (smallest == 0 || (size_t)ret < smallest)) {
smallest = ret;
- selected = xz_filters[i].filter;
+ selected = filter;
}
}
@@ -213,108 +216,22 @@ static void xz_destroy(compressor_t *base)
free(base);
}
-static int process_options(char *options, size_t blocksize,
- int *flags, uint64_t *dictsize)
-{
- enum {
- OPT_DICT = 0,
- };
- char *const token[] = {
- [OPT_DICT] = (char *)"dictsize",
- NULL
- };
- char *subopts, *value;
- uint64_t mask;
- size_t i;
- int opt;
-
- subopts = options;
-
- while (*subopts != '\0') {
- opt = getsubopt(&subopts, token, &value);
-
- switch (opt) {
- case OPT_DICT:
- if (value == NULL)
- goto fail_value;
-
- for (i = 0; isdigit(value[i]); ++i)
- ;
-
- if (i < 1 || i > 9)
- goto fail_dict;
-
- *dictsize = atol(value);
-
- switch (value[i]) {
- case '\0':
- break;
- case 'm':
- case 'M':
- *dictsize <<= 20;
- break;
- case 'k':
- case 'K':
- *dictsize <<= 10;
- break;
- case '%':
- *dictsize = ((*dictsize) * blocksize) / 100;
- break;
- default:
- goto fail_dict;
- }
-
- if (*dictsize > 0x0FFFFFFFFUL)
- goto fail_dict_ov;
-
- mask = *dictsize & (*dictsize - 1);
-
- if (mask != 0 && ((mask & (mask - 1)) != 0))
- goto fail_dict_pot;
- break;
- default:
- for (i = 0; i < XZ_NUM_FILTERS; ++i) {
- if (strcmp(value, xz_filters[i].name) == 0) {
- *flags |= xz_filters[i].flag;
- break;
- }
- }
- if (i == XZ_NUM_FILTERS)
- goto fail_opt;
- break;
- }
- }
-
- return 0;
-fail_dict_pot:
- fputs("dictionary size must be either 2^n or 2^n + 2^(n-1)\n", stderr);
- return -1;
-fail_dict_ov:
- fputs("dictionary size too large.\n", stderr);
- return -1;
-fail_dict:
- fputs("dictionary size must be a number with the optional "
- "suffix 'm','k' or '%'.\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_xz_compressor(bool compress, size_t block_size,
- char *options)
+compressor_t *create_xz_compressor(const compressor_config_t *cfg)
{
- uint64_t dictsize = block_size;
xz_compressor_t *xz;
compressor_t *base;
- int flags = 0;
- if (options != NULL) {
- if (process_options(options, block_size, &flags, &dictsize))
- return NULL;
+ if (cfg->flags & ~(SQFS_COMP_FLAG_GENERIC_ALL |
+ SQFS_COMP_FLAG_XZ_ALL)) {
+ fputs("creating xz compressor: unknown compressor flags\n",
+ stderr);
+ return NULL;
+ }
+
+ if (!is_dict_size_valid(cfg->opt.xz.dict_size)) {
+ fputs("creating xz compressor: invalid dictionary size\n",
+ stderr);
+ return NULL;
}
xz = calloc(1, sizeof(*xz));
@@ -324,37 +241,14 @@ compressor_t *create_xz_compressor(bool compress, size_t block_size,
return NULL;
}
- xz->flags = flags;
- xz->dict_size = dictsize;
- xz->block_size = block_size;
+ xz->flags = cfg->flags;
+ xz->dict_size = cfg->opt.xz.dict_size;
+ xz->block_size = cfg->block_size;
base->destroy = xz_destroy;
- base->do_block = compress ? xz_comp_block : xz_uncomp_block;
+ 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;
base->create_copy = xz_create_copy;
return base;
}
-
-void compressor_xz_print_help(void)
-{
- size_t i;
-
- fputs(
-"Available options for xz compressor:\n"
-"\n"
-" dictsize=<value> Dictionary size. Either a value in bytes or a\n"
-" percentage of the block size. Defaults to 100%.\n"
-" The suffix '%' indicates a percentage. 'K' and 'M'\n"
-" can also be used for kibi and mebi bytes\n"
-" respecitively.\n"
-"\n"
-"In additon to the options, one or more bcj filters can be specified.\n"
-"If multiple filters are provided, the one yielding the best compression\n"
-"ratio will be used.\n"
-"\n"
-"The following filters are available:\n",
- stdout);
-
- for (i = 0; i < XZ_NUM_FILTERS; ++i)
- printf("\t%s\n", xz_filters[i].name);
-}
diff --git a/lib/sqfs/comp/zstd.c b/lib/sqfs/comp/zstd.c
index e206338..c805524 100644
--- a/lib/sqfs/comp/zstd.c
+++ b/lib/sqfs/comp/zstd.c
@@ -16,8 +16,6 @@
#include "internal.h"
-#define ZSTD_DEFAULT_COMPRESSION_LEVEL 15
-
typedef struct {
compressor_t base;
ZSTD_CCtx *zctx;
@@ -34,7 +32,7 @@ static int zstd_write_options(compressor_t *base, int fd)
zstd_options_t opt;
(void)fd;
- if (zstd->level == ZSTD_DEFAULT_COMPRESSION_LEVEL)
+ if (zstd->level == SQFS_ZSTD_DEFAULT_LEVEL)
return 0;
opt.level = htole32(zstd->level);
@@ -119,38 +117,26 @@ static void zstd_destroy(compressor_t *base)
free(zstd);
}
-compressor_t *create_zstd_compressor(bool compress, size_t block_size,
- char *options)
+compressor_t *create_zstd_compressor(const compressor_config_t *cfg)
{
- zstd_compressor_t *zstd = calloc(1, sizeof(*zstd));
- compressor_t *base = (compressor_t *)zstd;
- size_t i;
- (void)block_size;
+ zstd_compressor_t *zstd;
+ compressor_t *base;
- if (zstd == NULL) {
- perror("creating zstd compressor");
- return NULL;
+ if (cfg->flags & ~SQFS_COMP_FLAG_GENERIC_ALL) {
+ fputs("creating zstd compressor: unknown compressor flags\n",
+ stderr);
}
- zstd->level = ZSTD_DEFAULT_COMPRESSION_LEVEL;
-
- if (options != NULL) {
- if (strncmp(options, "level=", 6) == 0) {
- options += 6;
-
- for (i = 0; isdigit(options[i]); ++i)
- ;
-
- if (i == 0 || options[i] != '\0' || i > 6)
- goto fail_level;
-
- zstd->level = atoi(options);
+ if (cfg->opt.zstd.level < 1 ||
+ cfg->opt.zstd.level > ZSTD_maxCLevel()) {
+ goto fail_level;
+ }
- if (zstd->level < 1 || zstd->level > ZSTD_maxCLevel())
- goto fail_level;
- } else {
- goto fail_opt;
- }
+ zstd = calloc(1, sizeof(*zstd));
+ base = (compressor_t *)zstd;
+ if (zstd == NULL) {
+ perror("creating zstd compressor");
+ return NULL;
}
zstd->zctx = ZSTD_createCCtx();
@@ -161,7 +147,8 @@ compressor_t *create_zstd_compressor(bool compress, size_t block_size,
}
base->destroy = zstd_destroy;
- base->do_block = compress ? zstd_comp_block : zstd_uncomp_block;
+ 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;
base->create_copy = zstd_create_copy;
@@ -169,20 +156,5 @@ compressor_t *create_zstd_compressor(bool compress, size_t block_size,
fail_level:
fprintf(stderr, "zstd compression level must be a number in the range "
"1...%d\n", ZSTD_maxCLevel());
- free(zstd);
return NULL;
-fail_opt:
- fputs("Unsupported extra options for zstd compressor\n", stderr);
- free(zstd);
- return NULL;
-}
-
-void compressor_zstd_print_help(void)
-{
- printf("Available options for zstd compressor:\n"
- "\n"
- " level=<value> Set compression level. Defaults to %d.\n"
- " Maximum is %d.\n"
- "\n",
- ZSTD_DEFAULT_COMPRESSION_LEVEL, ZSTD_maxCLevel());
}
diff --git a/lib/sqfshelper/comp_opt.c b/lib/sqfshelper/comp_opt.c
new file mode 100644
index 0000000..1f3fabf
--- /dev/null
+++ b/lib/sqfshelper/comp_opt.c
@@ -0,0 +1,380 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * comp_opt.c
+ *
+ * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include "config.h"
+
+#include "highlevel.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <ctype.h>
+
+typedef struct {
+ const char *name;
+ uint16_t flag;
+} flag_t;
+
+static const flag_t gzip_flags[] = {
+ { "default", SQFS_COMP_FLAG_GZIP_DEFAULT },
+ { "filtered", SQFS_COMP_FLAG_GZIP_FILTERED },
+ { "huffman", SQFS_COMP_FLAG_GZIP_HUFFMAN },
+ { "rle", SQFS_COMP_FLAG_GZIP_RLE },
+ { "fixed", SQFS_COMP_FLAG_GZIP_FIXED },
+};
+
+static const flag_t xz_flags[] = {
+ { "x86", SQFS_COMP_FLAG_XZ_X86 },
+ { "powerpc", SQFS_COMP_FLAG_XZ_POWERPC },
+ { "ia64", SQFS_COMP_FLAG_XZ_IA64 },
+ { "arm", SQFS_COMP_FLAG_XZ_ARM },
+ { "armthumb", SQFS_COMP_FLAG_XZ_ARMTHUMB },
+ { "sparc", SQFS_COMP_FLAG_XZ_SPARC },
+};
+
+static const flag_t lz4_flags[] = {
+ { "hc", SQFS_COMP_FLAG_LZ4_HC },
+};
+
+static const char *lzo_algs[] = {
+ [SQFS_LZO1X_1] = "lzo1x_1",
+ [SQFS_LZO1X_1_11] = "lzo1x_1_11",
+ [SQFS_LZO1X_1_12] = "lzo1x_1_12",
+ [SQFS_LZO1X_1_15] = "lzo1x_1_15",
+ [SQFS_LZO1X_999] = "lzo1x_999",
+};
+
+static int set_flag(compressor_config_t *cfg, const char *name,
+ const flag_t *flags, size_t num_flags)
+{
+ size_t i;
+
+ for (i = 0; i < num_flags; ++i) {
+ if (strcmp(flags[i].name, name) == 0) {
+ cfg->flags |= flags[i].flag;
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+static int find_lzo_alg(compressor_config_t *cfg, const char *name)
+{
+ size_t i;
+
+ for (i = 0; i < sizeof(lzo_algs) / sizeof(lzo_algs[0]); ++i) {
+ if (strcmp(lzo_algs[i], name) == 0) {
+ cfg->opt.lzo.algorithm = i;
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+static int get_size_value(const char *value, uint32_t *out, uint32_t block_size)
+{
+ int i;
+
+ for (i = 0; isdigit(value[i]) && i < 9; ++i)
+ ;
+
+ if (i < 1 || i > 9)
+ return -1;
+
+ *out = atol(value);
+
+ switch (value[i]) {
+ case '\0':
+ break;
+ case 'm':
+ case 'M':
+ *out <<= 20;
+ break;
+ case 'k':
+ case 'K':
+ *out <<= 10;
+ break;
+ case '%':
+ *out = (*out * block_size) / 100;
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+enum {
+ OPT_WINDOW = 0,
+ OPT_LEVEL,
+ OPT_ALG,
+ OPT_DICT,
+};
+static char *const token[] = {
+ [OPT_WINDOW] = (char *)"window",
+ [OPT_LEVEL] = (char *)"level",
+ [OPT_ALG] = (char *)"algorithm",
+ [OPT_DICT] = (char *)"dictsize",
+ NULL
+};
+
+int compressor_cfg_init_options(compressor_config_t *cfg, E_SQFS_COMPRESSOR id,
+ size_t block_size, char *options)
+{
+ size_t num_flags = 0, min_level = 0, max_level = 0, level;
+ const flag_t *flags = NULL;
+ char *subopts, *value;
+ int i, opt;
+
+ if (compressor_config_init(cfg, id, block_size, 0))
+ return -1;
+
+ if (options == NULL)
+ return 0;
+
+ switch (cfg->id) {
+ case SQFS_COMP_GZIP:
+ min_level = SQFS_GZIP_MIN_LEVEL;
+ max_level = SQFS_GZIP_MAX_LEVEL;
+ flags = gzip_flags;
+ num_flags = sizeof(gzip_flags) / sizeof(gzip_flags[0]);
+ break;
+ case SQFS_COMP_LZO:
+ min_level = SQFS_LZO_MIN_LEVEL;
+ max_level = SQFS_LZO_MAX_LEVEL;
+ break;
+ case SQFS_COMP_ZSTD:
+ min_level = SQFS_ZSTD_MIN_LEVEL;
+ max_level = SQFS_ZSTD_MAX_LEVEL;
+ break;
+ case SQFS_COMP_XZ:
+ flags = xz_flags;
+ num_flags = sizeof(xz_flags) / sizeof(xz_flags[0]);
+ break;
+ case SQFS_COMP_LZ4:
+ flags = lz4_flags;
+ num_flags = sizeof(lz4_flags) / sizeof(lz4_flags[0]);
+ break;
+ default:
+ break;
+ }
+
+ subopts = options;
+
+ while (*subopts != '\0') {
+ opt = getsubopt(&subopts, token, &value);
+
+ switch (opt) {
+ case OPT_WINDOW:
+ if (cfg->id != SQFS_COMP_GZIP)
+ goto fail_opt;
+
+ if (value == NULL)
+ goto fail_value;
+
+ for (i = 0; isdigit(value[i]); ++i)
+ ;
+
+ if (i < 1 || i > 3 || value[i] != '\0')
+ goto fail_window;
+
+ cfg->opt.gzip.window_size = atoi(value);
+
+ if (cfg->opt.gzip.window_size < SQFS_GZIP_MIN_WINDOW ||
+ cfg->opt.gzip.window_size > SQFS_GZIP_MAX_WINDOW)
+ goto fail_window;
+ break;
+ case OPT_LEVEL:
+ if (value == NULL)
+ goto fail_value;
+
+ for (i = 0; isdigit(value[i]) && i < 3; ++i)
+ ;
+
+ if (i < 1 || i > 3 || value[i] != '\0')
+ goto fail_level;
+
+ level = atoi(value);
+
+ if (level < min_level || level > max_level)
+ goto fail_level;
+
+ switch (cfg->id) {
+ case SQFS_COMP_GZIP:
+ cfg->opt.gzip.level = level;
+ break;
+ case SQFS_COMP_LZO:
+ cfg->opt.lzo.level = level;
+ break;
+ case SQFS_COMP_ZSTD:
+ cfg->opt.zstd.level = level;
+ break;
+ default:
+ goto fail_opt;
+ }
+ break;
+ case OPT_ALG:
+ if (cfg->id != SQFS_COMP_LZO)
+ goto fail_opt;
+
+ if (value == NULL)
+ goto fail_value;
+
+ if (find_lzo_alg(cfg, value))
+ goto fail_lzo_alg;
+ break;
+ case OPT_DICT:
+ if (cfg->id != SQFS_COMP_XZ)
+ goto fail_opt;
+
+ if (value == NULL)
+ goto fail_value;
+
+ if (get_size_value(value, &cfg->opt.xz.dict_size,
+ cfg->block_size)) {
+ goto fail_dict;
+ }
+ break;
+ default:
+ if (set_flag(cfg, value, flags, num_flags))
+ goto fail_opt;
+ break;
+ }
+ }
+
+ return 0;
+fail_lzo_alg:
+ fprintf(stderr, "Unknown lzo variant '%s'.\n", value);
+ return -1;
+fail_window:
+ fputs("Window size must be a number between 8 and 15.\n", stderr);
+ return -1;
+fail_level:
+ fprintf(stderr,
+ "Compression level must be a number between %zu and %zu.\n",
+ min_level, max_level);
+ return -1;
+fail_opt:
+ fprintf(stderr, "Unknown compressor option '%s'.\n", value);
+ return -1;
+fail_value:
+ fprintf(stderr, "Missing value for compressor option '%s'.\n",
+ token[opt]);
+ return -1;
+fail_dict:
+ fputs("Dictionary size must be a number with the optional "
+ "suffix 'm','k' or '%'.\n", stderr);
+ return -1;
+}
+
+typedef void (*compressor_help_fun_t)(void);
+
+static void gzip_print_help(void)
+{
+ size_t i;
+
+ printf(
+"Available options for gzip compressor:\n"
+"\n"
+" level=<value> Compression level. Value from 1 to 9.\n"
+" Defaults to %d.\n"
+" window=<size> Deflate compression window size. Value from 8 to 15.\n"
+" Defaults to %d.\n"
+"\n"
+"In additon to the options, one or more strategies can be specified.\n"
+"If multiple stratgies are provided, the one yielding the best compression\n"
+"ratio will be used.\n"
+"\n"
+"The following strategies are available:\n",
+ SQFS_GZIP_DEFAULT_LEVEL, SQFS_GZIP_DEFAULT_WINDOW);
+
+ for (i = 0; i < sizeof(gzip_flags) / sizeof(gzip_flags[0]); ++i)
+ printf("\t%s\n", gzip_flags[i].name);
+}
+
+static void lz4_print_help(void)
+{
+ fputs("Available options for lz4 compressor:\n"
+ "\n"
+ " hc If present, use slower but better compressing\n"
+ " variant of lz4.\n"
+ "\n",
+ stdout);
+}
+
+static void 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 < sizeof(lzo_algs) / sizeof(lzo_algs[0]); ++i)
+ printf("\t%s\n", lzo_algs[i]);
+}
+
+static void xz_print_help(void)
+{
+ size_t i;
+
+ fputs(
+"Available options for xz compressor:\n"
+"\n"
+" dictsize=<value> Dictionary size. Either a value in bytes or a\n"
+" percentage of the block size. Defaults to 100%.\n"
+" The suffix '%' indicates a percentage. 'K' and 'M'\n"
+" can also be used for kibi and mebi bytes\n"
+" respecitively.\n"
+"\n"
+"In additon to the options, one or more bcj filters can be specified.\n"
+"If multiple filters are provided, the one yielding the best compression\n"
+"ratio will be used.\n"
+"\n"
+"The following filters are available:\n",
+ stdout);
+
+ for (i = 0; i < sizeof(xz_flags) / sizeof(xz_flags[0]); ++i)
+ printf("\t%s\n", xz_flags[i].name);
+}
+
+static void zstd_print_help(void)
+{
+ printf("Available options for zstd compressor:\n"
+ "\n"
+ " level=<value> Set compression level. Defaults to %d.\n"
+ " Maximum is %d.\n"
+ "\n",
+ SQFS_ZSTD_DEFAULT_LEVEL, SQFS_ZSTD_MAX_LEVEL);
+}
+
+static const compressor_help_fun_t helpfuns[SQFS_COMP_MAX + 1] = {
+ [SQFS_COMP_GZIP] = gzip_print_help,
+ [SQFS_COMP_XZ] = xz_print_help,
+ [SQFS_COMP_LZO] = lzo_print_help,
+ [SQFS_COMP_LZ4] = lz4_print_help,
+ [SQFS_COMP_ZSTD] = zstd_print_help,
+};
+
+void compressor_print_help(E_SQFS_COMPRESSOR id)
+{
+ if (id < SQFS_COMP_MIN || id > SQFS_COMP_MAX)
+ return;
+
+ if (helpfuns[id] == NULL)
+ return;
+
+ helpfuns[id]();
+}
diff --git a/lib/sqfshelper/compress.c b/lib/sqfshelper/compress.c
new file mode 100644
index 0000000..3d53e9e
--- /dev/null
+++ b/lib/sqfshelper/compress.c
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later */
+/*
+ * compress.c
+ *
+ * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at>
+ */
+#include "highlevel.h"
+
+E_SQFS_COMPRESSOR compressor_get_default(void)
+{
+ if (compressor_exists(SQFS_COMP_XZ))
+ return SQFS_COMP_XZ;
+
+ if (compressor_exists(SQFS_COMP_ZSTD))
+ return SQFS_COMP_ZSTD;
+
+ return SQFS_COMP_GZIP;
+}
+
+void compressor_print_available(void)
+{
+ int i;
+
+ fputs("Available compressors:\n", stdout);
+
+ for (i = SQFS_COMP_MIN; i <= SQFS_COMP_MAX; ++i) {
+ if (compressor_exists(i))
+ printf("\t%s\n", compressor_name_from_id(i));
+ }
+
+ printf("\nDefault compressor: %s\n",
+ compressor_name_from_id(compressor_get_default()));
+}
diff --git a/lib/sqfshelper/sqfs_reader.c b/lib/sqfshelper/sqfs_reader.c
index 197e08f..49e91f5 100644
--- a/lib/sqfshelper/sqfs_reader.c
+++ b/lib/sqfshelper/sqfs_reader.c
@@ -14,6 +14,8 @@
int sqfs_reader_open(sqfs_reader_t *rd, const char *filename, int rdtree_flags)
{
+ compressor_config_t cfg;
+
memset(rd, 0, sizeof(*rd));
rd->sqfsfd = open(filename, O_RDONLY);
@@ -30,8 +32,11 @@ int sqfs_reader_open(sqfs_reader_t *rd, const char *filename, int rdtree_flags)
goto fail_fd;
}
- rd->cmp = compressor_create(rd->super.compression_id, false,
- rd->super.block_size, NULL);
+ compressor_config_init(&cfg, rd->super.compression_id,
+ rd->super.block_size,
+ SQFS_COMP_FLAG_UNCOMPRESS);
+
+ rd->cmp = compressor_create(&cfg);
if (rd->cmp == NULL)
goto fail_fd;
diff --git a/mkfs/mkfs.c b/mkfs/mkfs.c
index 5759744..512b30b 100644
--- a/mkfs/mkfs.c
+++ b/mkfs/mkfs.c
@@ -93,6 +93,7 @@ static int read_fstree(fstree_t *fs, options_t *opt)
int main(int argc, char **argv)
{
int status = EXIT_FAILURE, ret;
+ compressor_config_t cfg;
data_writer_t *data;
sqfs_super_t super;
compressor_t *cmp;
@@ -103,6 +104,11 @@ int main(int argc, char **argv)
process_command_line(&opt, argc, argv);
+ if (compressor_cfg_init_options(&cfg, opt.compressor,
+ opt.blksz, opt.comp_extra)) {
+ return EXIT_FAILURE;
+ }
+
if (fstree_init(&fs, opt.blksz, opt.fs_defaults))
return EXIT_FAILURE;
@@ -144,8 +150,7 @@ int main(int argc, char **argv)
fstree_xattr_deduplicate(&fs);
- cmp = compressor_create(super.compression_id, true, super.block_size,
- opt.comp_extra);
+ cmp = compressor_create(&cfg);
if (cmp == NULL) {
fputs("Error creating compressor\n", stderr);
goto out_outfd;
diff --git a/tar/tar2sqfs.c b/tar/tar2sqfs.c
index 1e70562..c0f8a14 100644
--- a/tar/tar2sqfs.c
+++ b/tar/tar2sqfs.c
@@ -355,6 +355,7 @@ fail:
int main(int argc, char **argv)
{
int outfd, status = EXIT_SUCCESS;
+ compressor_config_t cfg;
data_writer_t *data;
sqfs_super_t super;
compressor_t *cmp;
@@ -364,6 +365,11 @@ int main(int argc, char **argv)
process_args(argc, argv);
+ if (compressor_cfg_init_options(&cfg, comp_id,
+ block_size, comp_extra)) {
+ return EXIT_FAILURE;
+ }
+
outfd = open(filename, outmode, 0644);
if (outfd < 0) {
perror(filename);
@@ -373,7 +379,7 @@ int main(int argc, char **argv)
if (fstree_init(&fs, block_size, fs_defaults))
goto out_fd;
- cmp = compressor_create(comp_id, true, block_size, comp_extra);
+ cmp = compressor_create(&cfg);
if (cmp == NULL) {
fputs("Error creating compressor\n", stderr);
goto out_fs;