Home | History | Annotate | Download | only in squashfs-tools
      1 /*
      2  * Copyright (c) 2013
      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  * lz4_wrapper.c
     20  *
     21  * Support for LZ4 compression http://fastcompression.blogspot.com/p/lz4.html
     22  */
     23 
     24 #include <stdio.h>
     25 #include <string.h>
     26 #include <stdlib.h>
     27 #include <lz4.h>
     28 #include <lz4hc.h>
     29 
     30 #include "squashfs_fs.h"
     31 #include "lz4_wrapper.h"
     32 #include "compressor.h"
     33 
     34 static int hc = 0;
     35 
     36 /*
     37  * This function is called by the options parsing code in mksquashfs.c
     38  * to parse any -X compressor option.
     39  *
     40  * This function returns:
     41  *	>=0 (number of additional args parsed) on success
     42  *	-1 if the option was unrecognised, or
     43  *	-2 if the option was recognised, but otherwise bad in
     44  *	   some way (e.g. invalid parameter)
     45  *
     46  * Note: this function sets internal compressor state, but does not
     47  * pass back the results of the parsing other than success/failure.
     48  * The lz4_dump_options() function is called later to get the options in
     49  * a format suitable for writing to the filesystem.
     50  */
     51 static int lz4_options(char *argv[], int argc)
     52 {
     53 	if(strcmp(argv[0], "-Xhc") == 0) {
     54 		hc = 1;
     55 		return 0;
     56 	}
     57 
     58 	return -1;
     59 }
     60 
     61 
     62 /*
     63  * This function is called by mksquashfs to dump the parsed
     64  * compressor options in a format suitable for writing to the
     65  * compressor options field in the filesystem (stored immediately
     66  * after the superblock).
     67  *
     68  * This function returns a pointer to the compression options structure
     69  * to be stored (and the size), or NULL if there are no compression
     70  * options
     71  *
     72  * Currently LZ4 always returns a comp_opts structure, with
     73  * the version indicating LZ4_LEGACY stream fomat.  This is to
     74  * easily accomodate changes in the kernel code to different
     75  * stream formats
     76  */
     77 static void *lz4_dump_options(int block_size, int *size)
     78 {
     79 	static struct lz4_comp_opts comp_opts;
     80 
     81 	comp_opts.version = LZ4_LEGACY;
     82 	comp_opts.flags = hc ? LZ4_HC : 0;
     83 	SQUASHFS_INSWAP_COMP_OPTS(&comp_opts);
     84 
     85 	*size = sizeof(comp_opts);
     86 	return &comp_opts;
     87 }
     88 
     89 
     90 /*
     91  * This function is a helper specifically for the append mode of
     92  * mksquashfs.  Its purpose is to set the internal compressor state
     93  * to the stored compressor options in the passed compressor options
     94  * structure.
     95  *
     96  * In effect this function sets up the compressor options
     97  * to the same state they were when the filesystem was originally
     98  * generated, this is to ensure on appending, the compressor uses
     99  * the same compression options that were used to generate the
    100  * original filesystem.
    101  *
    102  * Note, even if there are no compressor options, this function is still
    103  * called with an empty compressor structure (size == 0), to explicitly
    104  * set the default options, this is to ensure any user supplied
    105  * -X options on the appending mksquashfs command line are over-ridden
    106  *
    107  * This function returns 0 on sucessful extraction of options, and
    108  *			-1 on error
    109  */
    110 static int lz4_extract_options(int block_size, void *buffer, int size)
    111 {
    112 	struct lz4_comp_opts *comp_opts = buffer;
    113 
    114 	/* we expect a comp_opts structure to be present */
    115 	if(size < sizeof(*comp_opts))
    116 		goto failed;
    117 
    118 	SQUASHFS_INSWAP_COMP_OPTS(comp_opts);
    119 
    120 	/* we expect the stream format to be LZ4_LEGACY */
    121 	if(comp_opts->version != LZ4_LEGACY) {
    122 		fprintf(stderr, "lz4: unknown LZ4 version\n");
    123 		goto failed;
    124 	}
    125 
    126 	/*
    127 	 * Check compression flags, currently only LZ4_HC ("high compression")
    128 	 * can be set.
    129 	 */
    130 	if(comp_opts->flags == LZ4_HC)
    131 		hc = 1;
    132 	else if(comp_opts->flags != 0) {
    133 		fprintf(stderr, "lz4: unknown LZ4 flags\n");
    134 		goto failed;
    135 	}
    136 
    137 	return 0;
    138 
    139 failed:
    140 	fprintf(stderr, "lz4: error reading stored compressor options from "
    141 		"filesystem!\n");
    142 
    143 	return -1;
    144 }
    145 
    146 
    147 /*
    148  * This function is a helper specifically for unsquashfs.
    149  * Its purpose is to check that the compression options are
    150  * understood by this version of LZ4.
    151  *
    152  * This is important for LZ4 because the format understood by the
    153  * Linux kernel may change from the already obsolete legacy format
    154  * currently supported.
    155  *
    156  * If this does happen, then this version of LZ4 will not be able to decode
    157  * the newer format.  So we need to check for this.
    158  *
    159  * This function returns 0 on sucessful checking of options, and
    160  *			-1 on error
    161  */
    162 static int lz4_check_options(int block_size, void *buffer, int size)
    163 {
    164 	struct lz4_comp_opts *comp_opts = buffer;
    165 
    166 	/* we expect a comp_opts structure to be present */
    167 	if(size < sizeof(*comp_opts))
    168 		goto failed;
    169 
    170 	SQUASHFS_INSWAP_COMP_OPTS(comp_opts);
    171 
    172 	/* we expect the stream format to be LZ4_LEGACY */
    173 	if(comp_opts->version != LZ4_LEGACY) {
    174 		fprintf(stderr, "lz4: unknown LZ4 version\n");
    175 		goto failed;
    176 	}
    177 
    178 	return 0;
    179 
    180 failed:
    181 	fprintf(stderr, "lz4: error reading stored compressor options from "
    182 		"filesystem!\n");
    183 	return -1;
    184 }
    185 
    186 
    187 void lz4_display_options(void *buffer, int size)
    188 {
    189 	struct lz4_comp_opts *comp_opts = buffer;
    190 
    191 	/* check passed comp opts struct is of the correct length */
    192 	if(size < sizeof(*comp_opts))
    193 		goto failed;
    194 
    195 	SQUASHFS_INSWAP_COMP_OPTS(comp_opts);
    196 
    197 	/* we expect the stream format to be LZ4_LEGACY */
    198 	if(comp_opts->version != LZ4_LEGACY) {
    199 		fprintf(stderr, "lz4: unknown LZ4 version\n");
    200 		goto failed;
    201 	}
    202 
    203 	/*
    204 	 * Check compression flags, currently only LZ4_HC ("high compression")
    205 	 * can be set.
    206 	 */
    207 	if(comp_opts->flags & ~LZ4_FLAGS_MASK) {
    208 		fprintf(stderr, "lz4: unknown LZ4 flags\n");
    209 		goto failed;
    210 	}
    211 
    212 	if(comp_opts->flags & LZ4_HC)
    213 		printf("\tHigh Compression option specified (-Xhc)\n");
    214 
    215 	return;
    216 
    217 failed:
    218 	fprintf(stderr, "lz4: error reading stored compressor options from "
    219 		"filesystem!\n");
    220 }
    221 
    222 
    223 static int lz4_compress(void *strm, void *dest, void *src,  int size,
    224 	int block_size, int *error)
    225 {
    226 	int res;
    227 
    228 	if(hc)
    229 		res = LZ4_compressHC_limitedOutput(src, dest, size, block_size);
    230 	else
    231 		res = LZ4_compress_limitedOutput(src, dest, size, block_size);
    232 
    233 	if(res == 0) {
    234 		/*
    235 	 	 * Output buffer overflow.  Return out of buffer space
    236 	 	 */
    237 		return 0;
    238 	} else if(res < 0) {
    239 		/*
    240 	 	 * All other errors return failure, with the compressor
    241 	 	 * specific error code in *error
    242 	 	 */
    243 		*error = res;
    244 		return -1;
    245 	}
    246 
    247 	return res;
    248 }
    249 
    250 
    251 static int lz4_uncompress(void *dest, void *src, int size, int outsize,
    252 	int *error)
    253 {
    254 	int res = LZ4_decompress_safe(src, dest, size, outsize);
    255 	if(res < 0) {
    256 		*error = res;
    257 		return -1;
    258 	}
    259 
    260 	return res;
    261 }
    262 
    263 
    264 void lz4_usage()
    265 {
    266 	fprintf(stderr, "\t  -Xhc\n");
    267 	fprintf(stderr, "\t\tCompress using LZ4 High Compression\n");
    268 }
    269 
    270 
    271 struct compressor lz4_comp_ops = {
    272 	.compress = lz4_compress,
    273 	.uncompress = lz4_uncompress,
    274 	.options = lz4_options,
    275 	.dump_options = lz4_dump_options,
    276 	.extract_options = lz4_extract_options,
    277 	.check_options = lz4_check_options,
    278 	.display_options = lz4_display_options,
    279 	.usage = lz4_usage,
    280 	.id = LZ4_COMPRESSION,
    281 	.name = "lz4",
    282 	.supported = 1
    283 };
    284