diff options
Diffstat (limited to 'lib/sqfshelper')
| -rw-r--r-- | lib/sqfshelper/comp_opt.c | 380 | ||||
| -rw-r--r-- | lib/sqfshelper/compress.c | 33 | ||||
| -rw-r--r-- | lib/sqfshelper/sqfs_reader.c | 9 | 
3 files changed, 420 insertions, 2 deletions
| 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; | 
