1/* 2 * Copyright (c) 2010, 2011 3 * Phillip Lougher <phillip@lougher.demon.co.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 * xz_wrapper.c 20 * 21 * Support for XZ (LZMA2) compression using XZ Utils liblzma 22 * http://tukaani.org/xz/ 23 */ 24 25#include <stdio.h> 26#include <string.h> 27#include <stdlib.h> 28#include <lzma.h> 29 30#include "squashfs_fs.h" 31#include "xz_wrapper.h" 32#include "compressor.h" 33 34static struct bcj bcj[] = { 35 { "x86", LZMA_FILTER_X86, 0 }, 36 { "powerpc", LZMA_FILTER_POWERPC, 0 }, 37 { "ia64", LZMA_FILTER_IA64, 0 }, 38 { "arm", LZMA_FILTER_ARM, 0 }, 39 { "armthumb", LZMA_FILTER_ARMTHUMB, 0 }, 40 { "sparc", LZMA_FILTER_SPARC, 0 }, 41 { NULL, LZMA_VLI_UNKNOWN, 0 } 42}; 43 44static struct comp_opts comp_opts; 45 46static int filter_count = 1; 47static int dictionary_size = 0; 48static float dictionary_percent = 0; 49 50 51static int xz_options(char *argv[], int argc) 52{ 53 int i; 54 char *name; 55 56 if(strcmp(argv[0], "-Xbcj") == 0) { 57 if(argc < 2) { 58 fprintf(stderr, "xz: -Xbcj missing filter\n"); 59 goto failed; 60 } 61 62 name = argv[1]; 63 while(name[0] != '\0') { 64 for(i = 0; bcj[i].name; i++) { 65 int n = strlen(bcj[i].name); 66 if((strncmp(name, bcj[i].name, n) == 0) && 67 (name[n] == '\0' || 68 name[n] == ',')) { 69 if(bcj[i].selected == 0) { 70 bcj[i].selected = 1; 71 filter_count++; 72 } 73 name += name[n] == ',' ? n + 1 : n; 74 break; 75 } 76 } 77 if(bcj[i].name == NULL) { 78 fprintf(stderr, "xz: -Xbcj unrecognised " 79 "filter\n"); 80 goto failed; 81 } 82 } 83 84 return 1; 85 } else if(strcmp(argv[0], "-Xdict-size") == 0) { 86 char *b; 87 float size; 88 89 if(argc < 2) { 90 fprintf(stderr, "xz: -Xdict-size missing dict-size\n"); 91 goto failed; 92 } 93 94 size = strtof(argv[1], &b); 95 if(*b == '%') { 96 if(size <= 0 || size > 100) { 97 fprintf(stderr, "xz: -Xdict-size percentage " 98 "should be 0 < dict-size <= 100\n"); 99 goto failed; 100 } 101 102 dictionary_percent = size; 103 dictionary_size = 0; 104 } else { 105 if((float) ((int) size) != size) { 106 fprintf(stderr, "xz: -Xdict-size can't be " 107 "fractional unless a percentage of the" 108 " block size\n"); 109 goto failed; 110 } 111 112 dictionary_percent = 0; 113 dictionary_size = (int) size; 114 115 if(*b == 'k' || *b == 'K') 116 dictionary_size *= 1024; 117 else if(*b == 'm' || *b == 'M') 118 dictionary_size *= 1024 * 1024; 119 else if(*b != '\0') { 120 fprintf(stderr, "xz: -Xdict-size invalid " 121 "dict-size\n"); 122 goto failed; 123 } 124 } 125 126 return 1; 127 } 128 129 return -1; 130 131failed: 132 return -2; 133} 134 135 136static int xz_options_post(int block_size) 137{ 138 /* 139 * if -Xdict-size has been specified use this to compute the datablock 140 * dictionary size 141 */ 142 if(dictionary_size || dictionary_percent) { 143 int n; 144 145 if(dictionary_size) { 146 if(dictionary_size > block_size) { 147 fprintf(stderr, "xz: -Xdict-size is larger than" 148 " block_size\n"); 149 goto failed; 150 } 151 } else 152 dictionary_size = block_size * dictionary_percent / 100; 153 154 if(dictionary_size < 8192) { 155 fprintf(stderr, "xz: -Xdict-size should be 8192 bytes " 156 "or larger\n"); 157 goto failed; 158 } 159 160 /* 161 * dictionary_size must be storable in xz header as either 162 * 2^n or as 2^n+2^(n+1) 163 */ 164 n = ffs(dictionary_size) - 1; 165 if(dictionary_size != (1 << n) && 166 dictionary_size != ((1 << n) + (1 << (n + 1)))) { 167 fprintf(stderr, "xz: -Xdict-size is an unsupported " 168 "value, dict-size must be storable in xz " 169 "header\n"); 170 fprintf(stderr, "as either 2^n or as 2^n+2^(n+1). " 171 "Example dict-sizes are 75%%, 50%%, 37.5%%, " 172 "25%%,\n"); 173 fprintf(stderr, "or 32K, 16K, 8K etc.\n"); 174 goto failed; 175 } 176 177 } else 178 /* No -Xdict-size specified, use defaults */ 179 dictionary_size = block_size; 180 181 return 0; 182 183failed: 184 return -1; 185} 186 187 188static void *xz_dump_options(int block_size, int *size) 189{ 190 int flags = 0, i; 191 192 /* 193 * don't store compressor specific options in file system if the 194 * default options are being used - no compressor options in the 195 * file system means the default options are always assumed 196 * 197 * Defaults are: 198 * metadata dictionary size: SQUASHFS_METADATA_SIZE 199 * datablock dictionary size: block_size 200 * 1 filter 201 */ 202 if(dictionary_size == block_size && filter_count == 1) 203 return NULL; 204 205 for(i = 0; bcj[i].name; i++) 206 flags |= bcj[i].selected << i; 207 208 comp_opts.dictionary_size = dictionary_size; 209 comp_opts.flags = flags; 210 211 SQUASHFS_INSWAP_COMP_OPTS(&comp_opts); 212 213 *size = sizeof(comp_opts); 214 return &comp_opts; 215} 216 217 218static int xz_extract_options(int block_size, void *buffer, int size) 219{ 220 struct comp_opts *comp_opts = buffer; 221 int flags, i, n; 222 223 if(size == 0) { 224 /* set defaults */ 225 dictionary_size = block_size; 226 flags = 0; 227 } else { 228 /* check passed comp opts struct is of the correct length */ 229 if(size != sizeof(struct comp_opts)) 230 goto failed; 231 232 SQUASHFS_INSWAP_COMP_OPTS(comp_opts); 233 234 dictionary_size = comp_opts->dictionary_size; 235 flags = comp_opts->flags; 236 237 /* 238 * check that the dictionary size seems correct - the dictionary 239 * size should 2^n or 2^n+2^(n+1) 240 */ 241 n = ffs(dictionary_size) - 1; 242 if(dictionary_size != (1 << n) && 243 dictionary_size != ((1 << n) + (1 << (n + 1)))) 244 goto failed; 245 } 246 247 filter_count = 1; 248 for(i = 0; bcj[i].name; i++) { 249 if((flags >> i) & 1) { 250 bcj[i].selected = 1; 251 filter_count ++; 252 } else 253 bcj[i].selected = 0; 254 } 255 256 return 0; 257 258failed: 259 fprintf(stderr, "xz: error reading stored compressor options from " 260 "filesystem!\n"); 261 262 return -1; 263} 264 265 266static int xz_init(void **strm, int block_size, int datablock) 267{ 268 int i, j, filters = datablock ? filter_count : 1; 269 struct filter *filter = malloc(filters * sizeof(struct filter)); 270 struct xz_stream *stream; 271 272 if(filter == NULL) 273 goto failed; 274 275 stream = *strm = malloc(sizeof(struct xz_stream)); 276 if(stream == NULL) 277 goto failed2; 278 279 stream->filter = filter; 280 stream->filters = filters; 281 282 memset(filter, 0, filters * sizeof(struct filter)); 283 284 stream->dictionary_size = datablock ? dictionary_size : 285 SQUASHFS_METADATA_SIZE; 286 287 filter[0].filter[0].id = LZMA_FILTER_LZMA2; 288 filter[0].filter[0].options = &stream->opt; 289 filter[0].filter[1].id = LZMA_VLI_UNKNOWN; 290 291 for(i = 0, j = 1; datablock && bcj[i].name; i++) { 292 if(bcj[i].selected) { 293 filter[j].buffer = malloc(block_size); 294 if(filter[j].buffer == NULL) 295 goto failed3; 296 filter[j].filter[0].id = bcj[i].id; 297 filter[j].filter[1].id = LZMA_FILTER_LZMA2; 298 filter[j].filter[1].options = &stream->opt; 299 filter[j].filter[2].id = LZMA_VLI_UNKNOWN; 300 j++; 301 } 302 } 303 304 return 0; 305 306failed3: 307 for(i = 1; i < filters; i++) 308 free(filter[i].buffer); 309 free(stream); 310 311failed2: 312 free(filter); 313 314failed: 315 return -1; 316} 317 318 319static int xz_compress(void *strm, void *dest, void *src, int size, 320 int block_size, int *error) 321{ 322 int i; 323 lzma_ret res = 0; 324 struct xz_stream *stream = strm; 325 struct filter *selected = NULL; 326 327 stream->filter[0].buffer = dest; 328 329 for(i = 0; i < stream->filters; i++) { 330 struct filter *filter = &stream->filter[i]; 331 332 if(lzma_lzma_preset(&stream->opt, LZMA_PRESET_DEFAULT)) 333 goto failed; 334 335 stream->opt.dict_size = stream->dictionary_size; 336 337 filter->length = 0; 338 res = lzma_stream_buffer_encode(filter->filter, 339 LZMA_CHECK_CRC32, NULL, src, size, filter->buffer, 340 &filter->length, block_size); 341 342 if(res == LZMA_OK) { 343 if(!selected || selected->length > filter->length) 344 selected = filter; 345 } else if(res != LZMA_BUF_ERROR) 346 goto failed; 347 } 348 349 if(!selected) 350 /* 351 * Output buffer overflow. Return out of buffer space 352 */ 353 return 0; 354 355 if(selected->buffer != dest) 356 memcpy(dest, selected->buffer, selected->length); 357 358 return (int) selected->length; 359 360failed: 361 /* 362 * All other errors return failure, with the compressor 363 * specific error code in *error 364 */ 365 *error = res; 366 return -1; 367} 368 369 370static int xz_uncompress(void *dest, void *src, int size, int block_size, 371 int *error) 372{ 373 size_t src_pos = 0; 374 size_t dest_pos = 0; 375 uint64_t memlimit = MEMLIMIT; 376 377 lzma_ret res = lzma_stream_buffer_decode(&memlimit, 0, NULL, 378 src, &src_pos, size, dest, &dest_pos, block_size); 379 380 *error = res; 381 return res == LZMA_OK && size == (int) src_pos ? (int) dest_pos : -1; 382} 383 384 385void xz_usage() 386{ 387 fprintf(stderr, "\t -Xbcj filter1,filter2,...,filterN\n"); 388 fprintf(stderr, "\t\tCompress using filter1,filter2,...,filterN in"); 389 fprintf(stderr, " turn\n\t\t(in addition to no filter), and choose"); 390 fprintf(stderr, " the best compression.\n"); 391 fprintf(stderr, "\t\tAvailable filters: x86, arm, armthumb,"); 392 fprintf(stderr, " powerpc, sparc, ia64\n"); 393 fprintf(stderr, "\t -Xdict-size <dict-size>\n"); 394 fprintf(stderr, "\t\tUse <dict-size> as the XZ dictionary size. The"); 395 fprintf(stderr, " dictionary size\n\t\tcan be specified as a"); 396 fprintf(stderr, " percentage of the block size, or as an\n\t\t"); 397 fprintf(stderr, "absolute value. The dictionary size must be less"); 398 fprintf(stderr, " than or equal\n\t\tto the block size and 8192 bytes"); 399 fprintf(stderr, " or larger. It must also be\n\t\tstorable in the xz"); 400 fprintf(stderr, " header as either 2^n or as 2^n+2^(n+1).\n\t\t"); 401 fprintf(stderr, "Example dict-sizes are 75%%, 50%%, 37.5%%, 25%%, or"); 402 fprintf(stderr, " 32K, 16K, 8K\n\t\tetc.\n"); 403} 404 405 406struct compressor xz_comp_ops = { 407 .init = xz_init, 408 .compress = xz_compress, 409 .uncompress = xz_uncompress, 410 .options = xz_options, 411 .options_post = xz_options_post, 412 .dump_options = xz_dump_options, 413 .extract_options = xz_extract_options, 414 .usage = xz_usage, 415 .id = XZ_COMPRESSION, 416 .name = "xz", 417 .supported = 1 418}; 419