• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2013
3  * Phillip Lougher <phillip@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  */
lz4_options(char * argv[],int argc)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  */
lz4_dump_options(int block_size,int * size)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  */
lz4_extract_options(int block_size,void * buffer,int size)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  */
lz4_check_options(int block_size,void * buffer,int size)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 
lz4_display_options(void * buffer,int size)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 
lz4_compress(void * strm,void * dest,void * src,int size,int block_size,int * error)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_compress_HC(src, dest, size, block_size,
230 				      LZ4HC_CLEVEL_DEFAULT);
231 	else
232 		res = LZ4_compress_default(src, dest, size, block_size);
233 
234 	if(res == 0) {
235 		/*
236 	 	 * Output buffer overflow.  Return out of buffer space
237 	 	 */
238 		return 0;
239 	} else if(res < 0) {
240 		/*
241 	 	 * All other errors return failure, with the compressor
242 	 	 * specific error code in *error
243 	 	 */
244 		*error = res;
245 		return -1;
246 	}
247 
248 	return res;
249 }
250 
251 
lz4_uncompress(void * dest,void * src,int size,int outsize,int * error)252 static int lz4_uncompress(void *dest, void *src, int size, int outsize,
253 	int *error)
254 {
255 	int res = LZ4_decompress_safe(src, dest, size, outsize);
256 	if(res < 0) {
257 		*error = res;
258 		return -1;
259 	}
260 
261 	return res;
262 }
263 
264 
lz4_usage()265 void lz4_usage()
266 {
267 	fprintf(stderr, "\t  -Xhc\n");
268 	fprintf(stderr, "\t\tCompress using LZ4 High Compression\n");
269 }
270 
271 
272 struct compressor lz4_comp_ops = {
273 	.compress = lz4_compress,
274 	.uncompress = lz4_uncompress,
275 	.options = lz4_options,
276 	.dump_options = lz4_dump_options,
277 	.extract_options = lz4_extract_options,
278 	.check_options = lz4_check_options,
279 	.display_options = lz4_display_options,
280 	.usage = lz4_usage,
281 	.id = LZ4_COMPRESSION,
282 	.name = "lz4",
283 	.supported = 1
284 };
285