Home | History | Annotate | Download | only in squashfs-tools
      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