1 /* 2 * Copyright (c) 2010, 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 * lzma_xz_wrapper.c 20 * 21 * Support for LZMA1 compression using XZ Utils liblzma http://tukaani.org/xz/ 22 */ 23 24 #include <stdio.h> 25 #include <string.h> 26 #include <lzma.h> 27 28 #include "squashfs_fs.h" 29 #include "compressor.h" 30 31 #define LZMA_PROPS_SIZE 5 32 #define LZMA_UNCOMP_SIZE 8 33 #define LZMA_HEADER_SIZE (LZMA_PROPS_SIZE + LZMA_UNCOMP_SIZE) 34 35 #define LZMA_OPTIONS 5 36 #define MEMLIMIT (32 * 1024 * 1024) 37 38 static int lzma_compress(void *dummy, void *dest, void *src, int size, 39 int block_size, int *error) 40 { 41 unsigned char *d = (unsigned char *) dest; 42 lzma_options_lzma opt; 43 lzma_stream strm = LZMA_STREAM_INIT; 44 int res; 45 46 lzma_lzma_preset(&opt, LZMA_OPTIONS); 47 opt.dict_size = block_size; 48 49 res = lzma_alone_encoder(&strm, &opt); 50 if(res != LZMA_OK) { 51 lzma_end(&strm); 52 goto failed; 53 } 54 55 strm.next_out = dest; 56 strm.avail_out = block_size; 57 strm.next_in = src; 58 strm.avail_in = size; 59 60 res = lzma_code(&strm, LZMA_FINISH); 61 lzma_end(&strm); 62 63 if(res == LZMA_STREAM_END) { 64 /* 65 * Fill in the 8 byte little endian uncompressed size field in 66 * the LZMA header. 8 bytes is excessively large for squashfs 67 * but this is the standard LZMA header and which is expected by 68 * the kernel code 69 */ 70 71 d[LZMA_PROPS_SIZE] = size & 255; 72 d[LZMA_PROPS_SIZE + 1] = (size >> 8) & 255; 73 d[LZMA_PROPS_SIZE + 2] = (size >> 16) & 255; 74 d[LZMA_PROPS_SIZE + 3] = (size >> 24) & 255; 75 d[LZMA_PROPS_SIZE + 4] = 0; 76 d[LZMA_PROPS_SIZE + 5] = 0; 77 d[LZMA_PROPS_SIZE + 6] = 0; 78 d[LZMA_PROPS_SIZE + 7] = 0; 79 80 return (int) strm.total_out; 81 } 82 83 if(res == LZMA_OK) 84 /* 85 * Output buffer overflow. Return out of buffer space 86 */ 87 return 0; 88 89 failed: 90 /* 91 * All other errors return failure, with the compressor 92 * specific error code in *error 93 */ 94 *error = res; 95 return -1; 96 } 97 98 99 static int lzma_uncompress(void *dest, void *src, int size, int outsize, 100 int *error) 101 { 102 lzma_stream strm = LZMA_STREAM_INIT; 103 int uncompressed_size = 0, res; 104 unsigned char lzma_header[LZMA_HEADER_SIZE]; 105 106 res = lzma_alone_decoder(&strm, MEMLIMIT); 107 if(res != LZMA_OK) { 108 lzma_end(&strm); 109 goto failed; 110 } 111 112 memcpy(lzma_header, src, LZMA_HEADER_SIZE); 113 uncompressed_size = lzma_header[LZMA_PROPS_SIZE] | 114 (lzma_header[LZMA_PROPS_SIZE + 1] << 8) | 115 (lzma_header[LZMA_PROPS_SIZE + 2] << 16) | 116 (lzma_header[LZMA_PROPS_SIZE + 3] << 24); 117 118 if(uncompressed_size > outsize) { 119 res = 0; 120 goto failed; 121 } 122 123 memset(lzma_header + LZMA_PROPS_SIZE, 255, LZMA_UNCOMP_SIZE); 124 125 strm.next_out = dest; 126 strm.avail_out = outsize; 127 strm.next_in = lzma_header; 128 strm.avail_in = LZMA_HEADER_SIZE; 129 130 res = lzma_code(&strm, LZMA_RUN); 131 132 if(res != LZMA_OK || strm.avail_in != 0) { 133 lzma_end(&strm); 134 goto failed; 135 } 136 137 strm.next_in = src + LZMA_HEADER_SIZE; 138 strm.avail_in = size - LZMA_HEADER_SIZE; 139 140 res = lzma_code(&strm, LZMA_FINISH); 141 lzma_end(&strm); 142 143 if(res == LZMA_STREAM_END || (res == LZMA_OK && 144 strm.total_out >= uncompressed_size && strm.avail_in == 0)) 145 return uncompressed_size; 146 147 failed: 148 *error = res; 149 return -1; 150 } 151 152 153 struct compressor lzma_comp_ops = { 154 .init = NULL, 155 .compress = lzma_compress, 156 .uncompress = lzma_uncompress, 157 .options = NULL, 158 .usage = NULL, 159 .id = LZMA_COMPRESSION, 160 .name = "lzma", 161 .supported = 1 162 }; 163 164