aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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;