Home | History | Annotate | Download | only in squashfs-tools
      1 /*
      2  * Copyright (c) 2009, 2010, 2013, 2014
      3  * Phillip Lougher <phillip (at) squashfs.org.uk>
      4  *
      5  * This program is free software; you can redistribute it and/or
      6  * modify it under the terms of the GNU General Public License
      7  * as published by the Free Software Foundation; either version 2,
      8  * or (at your option) any later version.
      9  *
     10  * This program is distributed in the hope that it will be useful,
     11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13  * GNU General Public License for more details.
     14  *
     15  * You should have received a copy of the GNU General Public License
     16  * along with this program; if not, write to the Free Software
     17  * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
     18  *
     19  * gzip_wrapper.c
     20  *
     21  * Support for ZLIB compression http://www.zlib.net
     22  */
     23 
     24 #include <stdio.h>
     25 #include <string.h>
     26 #include <stdlib.h>
     27 #include <zlib.h>
     28 
     29 #include "squashfs_fs.h"
     30 #include "gzip_wrapper.h"
     31 #include "compressor.h"
     32 
     33 static struct strategy strategy[] = {
     34 	{ "default", Z_DEFAULT_STRATEGY, 0 },
     35 	{ "filtered", Z_FILTERED, 0 },
     36 	{ "huffman_only", Z_HUFFMAN_ONLY, 0 },
     37 	{ "run_length_encoded", Z_RLE, 0 },
     38 	{ "fixed", Z_FIXED, 0 },
     39 	{ NULL, 0, 0 }
     40 };
     41 
     42 static int strategy_count = 0;
     43 
     44 /* default compression level */
     45 static int compression_level = GZIP_DEFAULT_COMPRESSION_LEVEL;
     46 
     47 /* default window size */
     48 static int window_size = GZIP_DEFAULT_WINDOW_SIZE;
     49 
     50 /*
     51  * This function is called by the options parsing code in mksquashfs.c
     52  * to parse any -X compressor option.
     53  *
     54  * This function returns:
     55  *	>=0 (number of additional args parsed) on success
     56  *	-1 if the option was unrecognised, or
     57  *	-2 if the option was recognised, but otherwise bad in
     58  *	   some way (e.g. invalid parameter)
     59  *
     60  * Note: this function sets internal compressor state, but does not
     61  * pass back the results of the parsing other than success/failure.
     62  * The gzip_dump_options() function is called later to get the options in
     63  * a format suitable for writing to the filesystem.
     64  */
     65 static int gzip_options(char *argv[], int argc)
     66 {
     67 	if(strcmp(argv[0], "-Xcompression-level") == 0) {
     68 		if(argc < 2) {
     69 			fprintf(stderr, "gzip: -Xcompression-level missing "
     70 				"compression level\n");
     71 			fprintf(stderr, "gzip: -Xcompression-level it "
     72 				"should be 1 >= n <= 9\n");
     73 			goto failed;
     74 		}
     75 
     76 		compression_level = atoi(argv[1]);
     77 		if(compression_level < 1 || compression_level > 9) {
     78 			fprintf(stderr, "gzip: -Xcompression-level invalid, it "
     79 				"should be 1 >= n <= 9\n");
     80 			goto failed;
     81 		}
     82 
     83 		return 1;
     84 	} else if(strcmp(argv[0], "-Xwindow-size") == 0) {
     85 		if(argc < 2) {
     86 			fprintf(stderr, "gzip: -Xwindow-size missing window "
     87 				"	size\n");
     88 			fprintf(stderr, "gzip: -Xwindow-size <window-size>\n");
     89 			goto failed;
     90 		}
     91 
     92 		window_size = atoi(argv[1]);
     93 		if(window_size < 8 || window_size > 15) {
     94 			fprintf(stderr, "gzip: -Xwindow-size invalid, it "
     95 				"should be 8 >= n <= 15\n");
     96 			goto failed;
     97 		}
     98 
     99 		return 1;
    100 	} else if(strcmp(argv[0], "-Xstrategy") == 0) {
    101 		char *name;
    102 		int i;
    103 
    104 		if(argc < 2) {
    105 			fprintf(stderr, "gzip: -Xstrategy missing "
    106 							"strategies\n");
    107 			goto failed;
    108 		}
    109 
    110 		name = argv[1];
    111 		while(name[0] != '\0') {
    112 			for(i = 0; strategy[i].name; i++) {
    113 				int n = strlen(strategy[i].name);
    114 				if((strncmp(name, strategy[i].name, n) == 0) &&
    115 						(name[n] == '\0' ||
    116 						 name[n] == ',')) {
    117 					if(strategy[i].selected == 0) {
    118 				 		strategy[i].selected = 1;
    119 						strategy_count++;
    120 					}
    121 					name += name[n] == ',' ? n + 1 : n;
    122 					break;
    123 				}
    124 			}
    125 			if(strategy[i].name == NULL) {
    126 				fprintf(stderr, "gzip: -Xstrategy unrecognised "
    127 					"strategy\n");
    128 				goto failed;
    129 			}
    130 		}
    131 
    132 		return 1;
    133 	}
    134 
    135 	return -1;
    136 
    137 failed:
    138 	return -2;
    139 }
    140 
    141 
    142 /*
    143  * This function is called after all options have been parsed.
    144  * It is used to do post-processing on the compressor options using
    145  * values that were not expected to be known at option parse time.
    146  *
    147  * This function returns 0 on successful post processing, or
    148  *			-1 on error
    149  */
    150 static int gzip_options_post(int block_size)
    151 {
    152 	if(strategy_count == 1 && strategy[0].selected) {
    153 		strategy_count = 0;
    154 		strategy[0].selected = 0;
    155 	}
    156 
    157 	return 0;
    158 }
    159 
    160 
    161 /*
    162  * This function is called by mksquashfs to dump the parsed
    163  * compressor options in a format suitable for writing to the
    164  * compressor options field in the filesystem (stored immediately
    165  * after the superblock).
    166  *
    167  * This function returns a pointer to the compression options structure
    168  * to be stored (and the size), or NULL if there are no compression
    169  * options
    170  *
    171  */
    172 static void *gzip_dump_options(int block_size, int *size)
    173 {
    174 	static struct gzip_comp_opts comp_opts;
    175 	int i, strategies = 0;
    176 
    177 	/*
    178 	 * If default compression options of:
    179 	 * compression-level: 8 and
    180 	 * window-size: 15 and
    181 	 * strategy_count == 0 then
    182 	 * don't store a compression options structure (this is compatible
    183 	 * with the legacy implementation of GZIP for Squashfs)
    184 	 */
    185 	if(compression_level == GZIP_DEFAULT_COMPRESSION_LEVEL &&
    186 				window_size == GZIP_DEFAULT_WINDOW_SIZE &&
    187 				strategy_count == 0)
    188 		return NULL;
    189 
    190 	for(i = 0; strategy[i].name; i++)
    191 		strategies |= strategy[i].selected << i;
    192 
    193 	comp_opts.compression_level = compression_level;
    194 	comp_opts.window_size = window_size;
    195 	comp_opts.strategy = strategies;
    196 
    197 	SQUASHFS_INSWAP_COMP_OPTS(&comp_opts);
    198 
    199 	*size = sizeof(comp_opts);
    200 	return &comp_opts;
    201 }
    202 
    203 
    204 /*
    205  * This function is a helper specifically for the append mode of
    206  * mksquashfs.  Its purpose is to set the internal compressor state
    207  * to the stored compressor options in the passed compressor options
    208  * structure.
    209  *
    210  * In effect this function sets up the compressor options
    211  * to the same state they were when the filesystem was originally
    212  * generated, this is to ensure on appending, the compressor uses
    213  * the same compression options that were used to generate the
    214  * original filesystem.
    215  *
    216  * Note, even if there are no compressor options, this function is still
    217  * called with an empty compressor structure (size == 0), to explicitly
    218  * set the default options, this is to ensure any user supplied
    219  * -X options on the appending mksquashfs command line are over-ridden
    220  *
    221  * This function returns 0 on sucessful extraction of options, and
    222  *			-1 on error
    223  */
    224 static int gzip_extract_options(int block_size, void *buffer, int size)
    225 {
    226 	struct gzip_comp_opts *comp_opts = buffer;
    227 	int i;
    228 
    229 	if(size == 0) {
    230 		/* Set default values */
    231 		compression_level = GZIP_DEFAULT_COMPRESSION_LEVEL;
    232 		window_size = GZIP_DEFAULT_WINDOW_SIZE;
    233 		strategy_count = 0;
    234 		return 0;
    235 	}
    236 
    237 	/* we expect a comp_opts structure of sufficient size to be present */
    238 	if(size < sizeof(*comp_opts))
    239 		goto failed;
    240 
    241 	SQUASHFS_INSWAP_COMP_OPTS(comp_opts);
    242 
    243 	/* Check comp_opts structure for correctness */
    244 	if(comp_opts->compression_level < 1 ||
    245 			comp_opts->compression_level > 9) {
    246 		fprintf(stderr, "gzip: bad compression level in "
    247 			"compression options structure\n");
    248 		goto failed;
    249 	}
    250 	compression_level = comp_opts->compression_level;
    251 
    252 	if(comp_opts->window_size < 8 ||
    253 			comp_opts->window_size > 15) {
    254 		fprintf(stderr, "gzip: bad window size in "
    255 			"compression options structure\n");
    256 		goto failed;
    257 	}
    258 	window_size = comp_opts->window_size;
    259 
    260 	strategy_count = 0;
    261 	for(i = 0; strategy[i].name; i++) {
    262 		if((comp_opts->strategy >> i) & 1) {
    263 			strategy[i].selected = 1;
    264 			strategy_count ++;
    265 		} else
    266 			strategy[i].selected = 0;
    267 	}
    268 
    269 	return 0;
    270 
    271 failed:
    272 	fprintf(stderr, "gzip: error reading stored compressor options from "
    273 		"filesystem!\n");
    274 
    275 	return -1;
    276 }
    277 
    278 
    279 void gzip_display_options(void *buffer, int size)
    280 {
    281 	struct gzip_comp_opts *comp_opts = buffer;
    282 	int i, printed;
    283 
    284 	/* we expect a comp_opts structure of sufficient size to be present */
    285 	if(size < sizeof(*comp_opts))
    286 		goto failed;
    287 
    288 	SQUASHFS_INSWAP_COMP_OPTS(comp_opts);
    289 
    290 	/* Check comp_opts structure for correctness */
    291 	if(comp_opts->compression_level < 1 ||
    292 			comp_opts->compression_level > 9) {
    293 		fprintf(stderr, "gzip: bad compression level in "
    294 			"compression options structure\n");
    295 		goto failed;
    296 	}
    297 	printf("\tcompression-level %d\n", comp_opts->compression_level);
    298 
    299 	if(comp_opts->window_size < 8 ||
    300 			comp_opts->window_size > 15) {
    301 		fprintf(stderr, "gzip: bad window size in "
    302 			"compression options structure\n");
    303 		goto failed;
    304 	}
    305 	printf("\twindow-size %d\n", comp_opts->window_size);
    306 
    307 	for(i = 0, printed = 0; strategy[i].name; i++) {
    308 		if((comp_opts->strategy >> i) & 1) {
    309 			if(printed)
    310 				printf(", ");
    311 			else
    312 				printf("\tStrategies selected: ");
    313 			printf("%s", strategy[i].name);
    314 			printed = 1;
    315 		}
    316 	}
    317 
    318 	if(!printed)
    319 		printf("\tStrategies selected: default\n");
    320 	else
    321 		printf("\n");
    322 
    323 	return;
    324 
    325 failed:
    326 	fprintf(stderr, "gzip: error reading stored compressor options from "
    327 		"filesystem!\n");
    328 }
    329 
    330 
    331 /*
    332  * This function is called by mksquashfs to initialise the
    333  * compressor, before compress() is called.
    334  *
    335  * This function returns 0 on success, and
    336  *			-1 on error
    337  */
    338 static int gzip_init(void **strm, int block_size, int datablock)
    339 {
    340 	int i, j, res;
    341 	struct gzip_stream *stream;
    342 
    343 	if(!datablock || !strategy_count) {
    344 		stream = malloc(sizeof(*stream) + sizeof(struct gzip_strategy));
    345 		if(stream == NULL)
    346 			goto failed;
    347 
    348 		stream->strategies = 1;
    349 		stream->strategy[0].strategy = Z_DEFAULT_STRATEGY;
    350 	} else {
    351 		stream = malloc(sizeof(*stream) +
    352 			sizeof(struct gzip_strategy) * strategy_count);
    353 		if(stream == NULL)
    354 			goto failed;
    355 
    356 		memset(stream->strategy, 0, sizeof(struct gzip_strategy) *
    357 			strategy_count);
    358 
    359 		stream->strategies = strategy_count;
    360 
    361 		for(i = 0, j = 0; strategy[i].name; i++) {
    362 			if(!strategy[i].selected)
    363 				continue;
    364 
    365 			stream->strategy[j].strategy = strategy[i].strategy;
    366 			if(j) {
    367 				stream->strategy[j].buffer = malloc(block_size);
    368 				if(stream->strategy[j].buffer == NULL)
    369 					goto failed2;
    370 			}
    371 			j++;
    372 		}
    373 	}
    374 
    375 	stream->stream.zalloc = Z_NULL;
    376 	stream->stream.zfree = Z_NULL;
    377 	stream->stream.opaque = 0;
    378 
    379 	res = deflateInit2(&stream->stream, compression_level, Z_DEFLATED,
    380 		window_size, 8, stream->strategy[0].strategy);
    381 	if(res != Z_OK)
    382 		goto failed2;
    383 
    384 	*strm = stream;
    385 	return 0;
    386 
    387 failed2:
    388 	for(i = 1; i < stream->strategies; i++)
    389 		free(stream->strategy[i].buffer);
    390 	free(stream);
    391 failed:
    392 	return -1;
    393 }
    394 
    395 
    396 static int gzip_compress(void *strm, void *d, void *s, int size, int block_size,
    397 		int *error)
    398 {
    399 	int i, res;
    400 	struct gzip_stream *stream = strm;
    401 	struct gzip_strategy *selected = NULL;
    402 
    403 	stream->strategy[0].buffer = d;
    404 
    405 	for(i = 0; i < stream->strategies; i++) {
    406 		struct gzip_strategy *strategy = &stream->strategy[i];
    407 
    408 		res = deflateReset(&stream->stream);
    409 		if(res != Z_OK)
    410 			goto failed;
    411 
    412 		stream->stream.next_in = s;
    413 		stream->stream.avail_in = size;
    414 		stream->stream.next_out = strategy->buffer;
    415 		stream->stream.avail_out = block_size;
    416 
    417 		if(stream->strategies > 1) {
    418 			res = deflateParams(&stream->stream,
    419 				compression_level, strategy->strategy);
    420 			if(res != Z_OK)
    421 				goto failed;
    422 		}
    423 
    424 		res = deflate(&stream->stream, Z_FINISH);
    425 		strategy->length = stream->stream.total_out;
    426 		if(res == Z_STREAM_END) {
    427 			if(!selected || selected->length > strategy->length)
    428 				selected = strategy;
    429 		} else if(res != Z_OK)
    430 			goto failed;
    431 	}
    432 
    433 	if(!selected)
    434 		/*
    435 		 * Output buffer overflow.  Return out of buffer space
    436 		 */
    437 		return 0;
    438 
    439 	if(selected->buffer != d)
    440 		memcpy(d, selected->buffer, selected->length);
    441 
    442 	return (int) selected->length;
    443 
    444 failed:
    445 	/*
    446 	 * All other errors return failure, with the compressor
    447 	 * specific error code in *error
    448 	 */
    449 	*error = res;
    450 	return -1;
    451 }
    452 
    453 
    454 static int gzip_uncompress(void *d, void *s, int size, int outsize, int *error)
    455 {
    456 	int res;
    457 	unsigned long bytes = outsize;
    458 
    459 	res = uncompress(d, &bytes, s, size);
    460 
    461 	if(res == Z_OK)
    462 		return (int) bytes;
    463 	else {
    464 		*error = res;
    465 		return -1;
    466 	}
    467 }
    468 
    469 
    470 void gzip_usage()
    471 {
    472 	fprintf(stderr, "\t  -Xcompression-level <compression-level>\n");
    473 	fprintf(stderr, "\t\t<compression-level> should be 1 .. 9 (default "
    474 		"%d)\n", GZIP_DEFAULT_COMPRESSION_LEVEL);
    475 	fprintf(stderr, "\t  -Xwindow-size <window-size>\n");
    476 	fprintf(stderr, "\t\t<window-size> should be 8 .. 15 (default "
    477 		"%d)\n", GZIP_DEFAULT_WINDOW_SIZE);
    478 	fprintf(stderr, "\t  -Xstrategy strategy1,strategy2,...,strategyN\n");
    479 	fprintf(stderr, "\t\tCompress using strategy1,strategy2,...,strategyN"
    480 		" in turn\n");
    481 	fprintf(stderr, "\t\tand choose the best compression.\n");
    482 	fprintf(stderr, "\t\tAvailable strategies: default, filtered, "
    483 		"huffman_only,\n\t\trun_length_encoded and fixed\n");
    484 }
    485 
    486 
    487 struct compressor gzip_comp_ops = {
    488 	.init = gzip_init,
    489 	.compress = gzip_compress,
    490 	.uncompress = gzip_uncompress,
    491 	.options = gzip_options,
    492 	.options_post = gzip_options_post,
    493 	.dump_options = gzip_dump_options,
    494 	.extract_options = gzip_extract_options,
    495 	.display_options = gzip_display_options,
    496 	.usage = gzip_usage,
    497 	.id = ZLIB_COMPRESSION,
    498 	.name = "gzip",
    499 	.supported = 1
    500 };
    501