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