summaryrefslogtreecommitdiffstats
path: root/squashfs-tools/gzip_wrapper.c
diff options
context:
space:
mode:
Diffstat (limited to 'squashfs-tools/gzip_wrapper.c')
-rw-r--r--squashfs-tools/gzip_wrapper.c513
1 files changed, 513 insertions, 0 deletions
diff --git a/squashfs-tools/gzip_wrapper.c b/squashfs-tools/gzip_wrapper.c
new file mode 100644
index 0000000..3cb1cc0
--- /dev/null
+++ b/squashfs-tools/gzip_wrapper.c
@@ -0,0 +1,513 @@
+/*
+ * Copyright (c) 2009, 2010, 2013, 2014, 2021, 2022
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * gzip_wrapper.c
+ *
+ * Support for ZLIB compression http://www.zlib.net
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <zlib.h>
+
+#include "squashfs_fs.h"
+#include "gzip_wrapper.h"
+#include "compressor.h"
+
+static struct strategy strategy[] = {
+ { "default", Z_DEFAULT_STRATEGY, 0 },
+ { "filtered", Z_FILTERED, 0 },
+ { "huffman_only", Z_HUFFMAN_ONLY, 0 },
+ { "run_length_encoded", Z_RLE, 0 },
+ { "fixed", Z_FIXED, 0 },
+ { NULL, 0, 0 }
+};
+
+static int strategy_count = 0;
+
+/* default compression level */
+static int compression_level = GZIP_DEFAULT_COMPRESSION_LEVEL;
+
+/* default window size */
+static int window_size = GZIP_DEFAULT_WINDOW_SIZE;
+
+/*
+ * This function is called by the options parsing code in mksquashfs.c
+ * to parse any -X compressor option.
+ *
+ * This function returns:
+ * >=0 (number of additional args parsed) on success
+ * -1 if the option was unrecognised, or
+ * -2 if the option was recognised, but otherwise bad in
+ * some way (e.g. invalid parameter)
+ *
+ * Note: this function sets internal compressor state, but does not
+ * pass back the results of the parsing other than success/failure.
+ * The gzip_dump_options() function is called later to get the options in
+ * a format suitable for writing to the filesystem.
+ */
+static int gzip_options(char *argv[], int argc)
+{
+ if(strcmp(argv[0], "-Xcompression-level") == 0) {
+ if(argc < 2) {
+ fprintf(stderr, "gzip: -Xcompression-level missing "
+ "compression level\n");
+ fprintf(stderr, "gzip: -Xcompression-level it "
+ "should be 1 >= n <= 9\n");
+ goto failed;
+ }
+
+ compression_level = atoi(argv[1]);
+ if(compression_level < 1 || compression_level > 9) {
+ fprintf(stderr, "gzip: -Xcompression-level invalid, it "
+ "should be 1 >= n <= 9\n");
+ goto failed;
+ }
+
+ return 1;
+ } else if(strcmp(argv[0], "-Xwindow-size") == 0) {
+ if(argc < 2) {
+ fprintf(stderr, "gzip: -Xwindow-size missing window "
+ " size\n");
+ fprintf(stderr, "gzip: -Xwindow-size <window-size>\n");
+ goto failed;
+ }
+
+ window_size = atoi(argv[1]);
+ if(window_size < 8 || window_size > 15) {
+ fprintf(stderr, "gzip: -Xwindow-size invalid, it "
+ "should be 8 >= n <= 15\n");
+ goto failed;
+ }
+
+ return 1;
+ } else if(strcmp(argv[0], "-Xstrategy") == 0) {
+ char *name;
+ int i;
+
+ if(argc < 2) {
+ fprintf(stderr, "gzip: -Xstrategy missing "
+ "strategies\n");
+ goto failed;
+ }
+
+ name = argv[1];
+ while(name[0] != '\0') {
+ for(i = 0; strategy[i].name; i++) {
+ int n = strlen(strategy[i].name);
+ if((strncmp(name, strategy[i].name, n) == 0) &&
+ (name[n] == '\0' ||
+ name[n] == ',')) {
+ if(strategy[i].selected == 0) {
+ strategy[i].selected = 1;
+ strategy_count++;
+ }
+ name += name[n] == ',' ? n + 1 : n;
+ break;
+ }
+ }
+ if(strategy[i].name == NULL) {
+ fprintf(stderr, "gzip: -Xstrategy unrecognised "
+ "strategy\n");
+ goto failed;
+ }
+ }
+
+ return 1;
+ }
+
+ return -1;
+
+failed:
+ return -2;
+}
+
+
+/*
+ * This function is called after all options have been parsed.
+ * It is used to do post-processing on the compressor options using
+ * values that were not expected to be known at option parse time.
+ *
+ * This function returns 0 on successful post processing, or
+ * -1 on error
+ */
+static int gzip_options_post(int block_size)
+{
+ if(strategy_count == 1 && strategy[0].selected) {
+ strategy_count = 0;
+ strategy[0].selected = 0;
+ }
+
+ return 0;
+}
+
+
+/*
+ * This function is called by mksquashfs to dump the parsed
+ * compressor options in a format suitable for writing to the
+ * compressor options field in the filesystem (stored immediately
+ * after the superblock).
+ *
+ * This function returns a pointer to the compression options structure
+ * to be stored (and the size), or NULL if there are no compression
+ * options
+ *
+ */
+static void *gzip_dump_options(int block_size, int *size)
+{
+ static struct gzip_comp_opts comp_opts;
+ int i, strategies = 0;
+
+ /*
+ * If default compression options of:
+ * compression-level: 8 and
+ * window-size: 15 and
+ * strategy_count == 0 then
+ * don't store a compression options structure (this is compatible
+ * with the legacy implementation of GZIP for Squashfs)
+ */
+ if(compression_level == GZIP_DEFAULT_COMPRESSION_LEVEL &&
+ window_size == GZIP_DEFAULT_WINDOW_SIZE &&
+ strategy_count == 0)
+ return NULL;
+
+ for(i = 0; strategy[i].name; i++)
+ strategies |= strategy[i].selected << i;
+
+ comp_opts.compression_level = compression_level;
+ comp_opts.window_size = window_size;
+ comp_opts.strategy = strategies;
+
+ SQUASHFS_INSWAP_COMP_OPTS(&comp_opts);
+
+ *size = sizeof(comp_opts);
+ return &comp_opts;
+}
+
+
+/*
+ * This function is a helper specifically for the append mode of
+ * mksquashfs. Its purpose is to set the internal compressor state
+ * to the stored compressor options in the passed compressor options
+ * structure.
+ *
+ * In effect this function sets up the compressor options
+ * to the same state they were when the filesystem was originally
+ * generated, this is to ensure on appending, the compressor uses
+ * the same compression options that were used to generate the
+ * original filesystem.
+ *
+ * Note, even if there are no compressor options, this function is still
+ * called with an empty compressor structure (size == 0), to explicitly
+ * set the default options, this is to ensure any user supplied
+ * -X options on the appending mksquashfs command line are over-ridden
+ *
+ * This function returns 0 on sucessful extraction of options, and
+ * -1 on error
+ */
+static int gzip_extract_options(int block_size, void *buffer, int size)
+{
+ struct gzip_comp_opts *comp_opts = buffer;
+ int i;
+
+ if(size == 0) {
+ /* Set default values */
+ compression_level = GZIP_DEFAULT_COMPRESSION_LEVEL;
+ window_size = GZIP_DEFAULT_WINDOW_SIZE;
+ strategy_count = 0;
+ return 0;
+ }
+
+ /* we expect a comp_opts structure of sufficient size to be present */
+ if(size < sizeof(*comp_opts))
+ goto failed;
+
+ SQUASHFS_INSWAP_COMP_OPTS(comp_opts);
+
+ /* Check comp_opts structure for correctness */
+ if(comp_opts->compression_level < 1 ||
+ comp_opts->compression_level > 9) {
+ fprintf(stderr, "gzip: bad compression level in "
+ "compression options structure\n");
+ goto failed;
+ }
+ compression_level = comp_opts->compression_level;
+
+ if(comp_opts->window_size < 8 ||
+ comp_opts->window_size > 15) {
+ fprintf(stderr, "gzip: bad window size in "
+ "compression options structure\n");
+ goto failed;
+ }
+ window_size = comp_opts->window_size;
+
+ strategy_count = 0;
+ for(i = 0; strategy[i].name; i++) {
+ if((comp_opts->strategy >> i) & 1) {
+ strategy[i].selected = 1;
+ strategy_count ++;
+ } else
+ strategy[i].selected = 0;
+ }
+
+ return 0;
+
+failed:
+ fprintf(stderr, "gzip: error reading stored compressor options from "
+ "filesystem!\n");
+
+ return -1;
+}
+
+
+static void gzip_display_options(void *buffer, int size)
+{
+ struct gzip_comp_opts *comp_opts = buffer;
+ int i, printed;
+
+ /* we expect a comp_opts structure of sufficient size to be present */
+ if(size < sizeof(*comp_opts))
+ goto failed;
+
+ SQUASHFS_INSWAP_COMP_OPTS(comp_opts);
+
+ /* Check comp_opts structure for correctness */
+ if(comp_opts->compression_level < 1 ||
+ comp_opts->compression_level > 9) {
+ fprintf(stderr, "gzip: bad compression level in "
+ "compression options structure\n");
+ goto failed;
+ }
+ printf("\tcompression-level %d\n", comp_opts->compression_level);
+
+ if(comp_opts->window_size < 8 ||
+ comp_opts->window_size > 15) {
+ fprintf(stderr, "gzip: bad window size in "
+ "compression options structure\n");
+ goto failed;
+ }
+ printf("\twindow-size %d\n", comp_opts->window_size);
+
+ for(i = 0, printed = 0; strategy[i].name; i++) {
+ if((comp_opts->strategy >> i) & 1) {
+ if(printed)
+ printf(", ");
+ else
+ printf("\tStrategies selected: ");
+ printf("%s", strategy[i].name);
+ printed = 1;
+ }
+ }
+
+ if(!printed)
+ printf("\tStrategies selected: default\n");
+ else
+ printf("\n");
+
+ return;
+
+failed:
+ fprintf(stderr, "gzip: error reading stored compressor options from "
+ "filesystem!\n");
+}
+
+
+/*
+ * This function is called by mksquashfs to initialise the
+ * compressor, before compress() is called.
+ *
+ * This function returns 0 on success, and
+ * -1 on error
+ */
+static int gzip_init(void **strm, int block_size, int datablock)
+{
+ int i, j, res;
+ struct gzip_stream *stream;
+
+ if(!datablock || !strategy_count) {
+ stream = malloc(sizeof(*stream) + sizeof(struct gzip_strategy));
+ if(stream == NULL)
+ goto failed;
+
+ stream->strategies = 1;
+ stream->strategy[0].strategy = Z_DEFAULT_STRATEGY;
+ } else {
+ stream = malloc(sizeof(*stream) +
+ sizeof(struct gzip_strategy) * strategy_count);
+ if(stream == NULL)
+ goto failed;
+
+ memset(stream->strategy, 0, sizeof(struct gzip_strategy) *
+ strategy_count);
+
+ stream->strategies = strategy_count;
+
+ for(i = 0, j = 0; strategy[i].name; i++) {
+ if(!strategy[i].selected)
+ continue;
+
+ stream->strategy[j].strategy = strategy[i].strategy;
+ if(j) {
+ stream->strategy[j].buffer = malloc(block_size);
+ if(stream->strategy[j].buffer == NULL)
+ goto failed2;
+ }
+ j++;
+ }
+ }
+
+ stream->stream.zalloc = Z_NULL;
+ stream->stream.zfree = Z_NULL;
+ stream->stream.opaque = 0;
+
+ res = deflateInit2(&stream->stream, compression_level, Z_DEFLATED,
+ window_size, 8, stream->strategy[0].strategy);
+ if(res != Z_OK)
+ goto failed2;
+
+ *strm = stream;
+ return 0;
+
+failed2:
+ for(i = 1; i < stream->strategies; i++)
+ free(stream->strategy[i].buffer);
+ free(stream);
+failed:
+ return -1;
+}
+
+
+static int gzip_compress(void *strm, void *d, void *s, int size, int block_size,
+ int *error)
+{
+ int i, res;
+ struct gzip_stream *stream = strm;
+ struct gzip_strategy *selected = NULL;
+
+ stream->strategy[0].buffer = d;
+
+ for(i = 0; i < stream->strategies; i++) {
+ struct gzip_strategy *strategy = &stream->strategy[i];
+
+ res = deflateReset(&stream->stream);
+ if(res != Z_OK)
+ goto failed;
+
+ stream->stream.next_in = s;
+ stream->stream.avail_in = size;
+ stream->stream.next_out = strategy->buffer;
+ stream->stream.avail_out = block_size;
+
+ if(stream->strategies > 1) {
+ res = deflateParams(&stream->stream,
+ compression_level, strategy->strategy);
+ if(res != Z_OK)
+ goto failed;
+ }
+
+ stream->stream.total_out = 0;
+ res = deflate(&stream->stream, Z_FINISH);
+ strategy->length = stream->stream.total_out;
+ if(res == Z_STREAM_END) {
+ if(!selected || selected->length > strategy->length)
+ selected = strategy;
+ } else if(res != Z_OK)
+ goto failed;
+ }
+
+ if(!selected)
+ /*
+ * Output buffer overflow. Return out of buffer space
+ */
+ return 0;
+
+ if(selected->buffer != d)
+ memcpy(d, selected->buffer, selected->length);
+
+ return (int) selected->length;
+
+failed:
+ /*
+ * All other errors return failure, with the compressor
+ * specific error code in *error
+ */
+ *error = res;
+ return -1;
+}
+
+
+static int gzip_uncompress(void *d, void *s, int size, int outsize, int *error)
+{
+ int res;
+ unsigned long bytes = outsize;
+
+ res = uncompress(d, &bytes, s, size);
+
+ if(res == Z_OK)
+ return (int) bytes;
+ else {
+ *error = res;
+ return -1;
+ }
+}
+
+
+static void gzip_usage(FILE *stream)
+{
+ fprintf(stream, "\t -Xcompression-level <compression-level>\n");
+ fprintf(stream, "\t\t<compression-level> should be 1 .. 9 (default "
+ "%d)\n", GZIP_DEFAULT_COMPRESSION_LEVEL);
+ fprintf(stream, "\t -Xwindow-size <window-size>\n");
+ fprintf(stream, "\t\t<window-size> should be 8 .. 15 (default "
+ "%d)\n", GZIP_DEFAULT_WINDOW_SIZE);
+ fprintf(stream, "\t -Xstrategy strategy1,strategy2,...,strategyN\n");
+ fprintf(stream, "\t\tCompress using strategy1,strategy2,...,strategyN"
+ " in turn\n");
+ fprintf(stream, "\t\tand choose the best compression.\n");
+ fprintf(stream, "\t\tAvailable strategies: default, filtered, "
+ "huffman_only,\n\t\trun_length_encoded and fixed\n");
+}
+
+
+static int option_args(char *option)
+{
+ if(strcmp(option, "-Xcompression-level") == 0 ||
+ strcmp(option, "-Xwindow-size") == 0 ||
+ strcmp(option, "-Xstrategy") == 0)
+ return 1;
+
+ return 0;
+}
+
+
+struct compressor gzip_comp_ops = {
+ .init = gzip_init,
+ .compress = gzip_compress,
+ .uncompress = gzip_uncompress,
+ .options = gzip_options,
+ .options_post = gzip_options_post,
+ .dump_options = gzip_dump_options,
+ .extract_options = gzip_extract_options,
+ .display_options = gzip_display_options,
+ .usage = gzip_usage,
+ .option_args = option_args,
+ .id = ZLIB_COMPRESSION,
+ .name = "gzip",
+ .supported = 1
+};