2 * Copyright (c) 2009, 2010, 2013, 2014
3 * Phillip Lougher <phillip@squashfs.org.uk>
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.
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.
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.
21 * Support for ZLIB compression http://www.zlib.net
29 #include "squashfs_fs.h"
30 #include "gzip_wrapper.h"
31 #include "compressor.h"
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 },
42 static int strategy_count
= 0;
44 /* default compression level */
45 static int compression_level
= GZIP_DEFAULT_COMPRESSION_LEVEL
;
47 /* default window size */
48 static int window_size
= GZIP_DEFAULT_WINDOW_SIZE
;
51 * This function is called by the options parsing code in mksquashfs.c
52 * to parse any -X compressor option.
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)
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.
65 static int gzip_options(char *argv
[], int argc
)
67 if(strcmp(argv
[0], "-Xcompression-level") == 0) {
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");
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");
84 } else if(strcmp(argv
[0], "-Xwindow-size") == 0) {
86 fprintf(stderr
, "gzip: -Xwindow-size missing window "
88 fprintf(stderr
, "gzip: -Xwindow-size <window-size>\n");
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");
100 } else if(strcmp(argv
[0], "-Xstrategy") == 0) {
105 fprintf(stderr
, "gzip: -Xstrategy missing "
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) &&
117 if(strategy
[i
].selected
== 0) {
118 strategy
[i
].selected
= 1;
121 name
+= name
[n
] == ',' ? n
+ 1 : n
;
125 if(strategy
[i
].name
== NULL
) {
126 fprintf(stderr
, "gzip: -Xstrategy unrecognised "
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.
147 * This function returns 0 on successful post processing, or
150 static int gzip_options_post(int block_size
)
152 if(strategy_count
== 1 && strategy
[0].selected
) {
154 strategy
[0].selected
= 0;
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).
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
172 static void *gzip_dump_options(int block_size
, int *size
)
174 static struct gzip_comp_opts comp_opts
;
175 int i
, strategies
= 0;
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)
185 if(compression_level
== GZIP_DEFAULT_COMPRESSION_LEVEL
&&
186 window_size
== GZIP_DEFAULT_WINDOW_SIZE
&&
190 for(i
= 0; strategy
[i
].name
; i
++)
191 strategies
|= strategy
[i
].selected
<< i
;
193 comp_opts
.compression_level
= compression_level
;
194 comp_opts
.window_size
= window_size
;
195 comp_opts
.strategy
= strategies
;
197 SQUASHFS_INSWAP_COMP_OPTS(&comp_opts
);
199 *size
= sizeof(comp_opts
);
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
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.
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
221 * This function returns 0 on sucessful extraction of options, and
224 static int gzip_extract_options(int block_size
, void *buffer
, int size
)
226 struct gzip_comp_opts
*comp_opts
= buffer
;
230 /* Set default values */
231 compression_level
= GZIP_DEFAULT_COMPRESSION_LEVEL
;
232 window_size
= GZIP_DEFAULT_WINDOW_SIZE
;
237 /* we expect a comp_opts structure of sufficient size to be present */
238 if(size
< sizeof(*comp_opts
))
241 SQUASHFS_INSWAP_COMP_OPTS(comp_opts
);
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");
250 compression_level
= comp_opts
->compression_level
;
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");
258 window_size
= comp_opts
->window_size
;
261 for(i
= 0; strategy
[i
].name
; i
++) {
262 if((comp_opts
->strategy
>> i
) & 1) {
263 strategy
[i
].selected
= 1;
266 strategy
[i
].selected
= 0;
272 fprintf(stderr
, "gzip: error reading stored compressor options from "
279 static void gzip_display_options(void *buffer
, int size
)
281 struct gzip_comp_opts
*comp_opts
= buffer
;
284 /* we expect a comp_opts structure of sufficient size to be present */
285 if(size
< sizeof(*comp_opts
))
288 SQUASHFS_INSWAP_COMP_OPTS(comp_opts
);
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");
297 printf("\tcompression-level %d\n", comp_opts
->compression_level
);
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");
305 printf("\twindow-size %d\n", comp_opts
->window_size
);
307 for(i
= 0, printed
= 0; strategy
[i
].name
; i
++) {
308 if((comp_opts
->strategy
>> i
) & 1) {
312 printf("\tStrategies selected: ");
313 printf("%s", strategy
[i
].name
);
319 printf("\tStrategies selected: default\n");
326 fprintf(stderr
, "gzip: error reading stored compressor options from "
332 * This function is called by mksquashfs to initialise the
333 * compressor, before compress() is called.
335 * This function returns 0 on success, and
338 static int gzip_init(void **strm
, int block_size
, int datablock
)
341 struct gzip_stream
*stream
;
343 if(!datablock
|| !strategy_count
) {
344 stream
= malloc(sizeof(*stream
) + sizeof(struct gzip_strategy
));
348 stream
->strategies
= 1;
349 stream
->strategy
[0].strategy
= Z_DEFAULT_STRATEGY
;
351 stream
= malloc(sizeof(*stream
) +
352 sizeof(struct gzip_strategy
) * strategy_count
);
356 memset(stream
->strategy
, 0, sizeof(struct gzip_strategy
) *
359 stream
->strategies
= strategy_count
;
361 for(i
= 0, j
= 0; strategy
[i
].name
; i
++) {
362 if(!strategy
[i
].selected
)
365 stream
->strategy
[j
].strategy
= strategy
[i
].strategy
;
367 stream
->strategy
[j
].buffer
= malloc(block_size
);
368 if(stream
->strategy
[j
].buffer
== NULL
)
375 stream
->stream
.zalloc
= Z_NULL
;
376 stream
->stream
.zfree
= Z_NULL
;
377 stream
->stream
.opaque
= 0;
379 res
= deflateInit2(&stream
->stream
, compression_level
, Z_DEFLATED
,
380 window_size
, 8, stream
->strategy
[0].strategy
);
388 for(i
= 1; i
< stream
->strategies
; i
++)
389 free(stream
->strategy
[i
].buffer
);
396 static int gzip_compress(void *strm
, void *d
, void *s
, int size
, int block_size
,
400 struct gzip_stream
*stream
= strm
;
401 struct gzip_strategy
*selected
= NULL
;
403 stream
->strategy
[0].buffer
= d
;
405 for(i
= 0; i
< stream
->strategies
; i
++) {
406 struct gzip_strategy
*strategy
= &stream
->strategy
[i
];
408 res
= deflateReset(&stream
->stream
);
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
;
417 if(stream
->strategies
> 1) {
418 res
= deflateParams(&stream
->stream
,
419 compression_level
, strategy
->strategy
);
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
)
429 } else if(res
!= Z_OK
)
435 * Output buffer overflow. Return out of buffer space
439 if(selected
->buffer
!= d
)
440 memcpy(d
, selected
->buffer
, selected
->length
);
442 return (int) selected
->length
;
446 * All other errors return failure, with the compressor
447 * specific error code in *error
454 static int gzip_uncompress(void *d
, void *s
, int size
, int outsize
, int *error
)
457 unsigned long bytes
= outsize
;
459 res
= uncompress(d
, &bytes
, s
, size
);
470 static void gzip_usage()
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"
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");
487 struct compressor gzip_comp_ops
= {
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
,
497 .id
= ZLIB_COMPRESSION
,