1/* 2 * Copyright (c) 2010 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 * 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 38static 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 89failed: 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 99static int lzma_uncompress(void *dest, void *src, int size, int block_size, 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 memset(lzma_header + LZMA_PROPS_SIZE, 255, LZMA_UNCOMP_SIZE); 118 119 strm.next_out = dest; 120 strm.avail_out = block_size; 121 strm.next_in = lzma_header; 122 strm.avail_in = LZMA_HEADER_SIZE; 123 124 res = lzma_code(&strm, LZMA_RUN); 125 126 if(res != LZMA_OK || strm.avail_in != 0) { 127 lzma_end(&strm); 128 goto failed; 129 } 130 131 strm.next_in = src + LZMA_HEADER_SIZE; 132 strm.avail_in = size - LZMA_HEADER_SIZE; 133 134 res = lzma_code(&strm, LZMA_FINISH); 135 lzma_end(&strm); 136 137 if(res == LZMA_STREAM_END || (res == LZMA_OK && 138 strm.total_out >= uncompressed_size && strm.avail_in == 0)) 139 return uncompressed_size; 140 141failed: 142 *error = res; 143 return -1; 144} 145 146 147struct compressor lzma_comp_ops = { 148 .init = NULL, 149 .compress = lzma_compress, 150 .uncompress = lzma_uncompress, 151 .options = NULL, 152 .usage = NULL, 153 .id = LZMA_COMPRESSION, 154 .name = "lzma", 155 .supported = 1 156}; 157