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