• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2010, 2013
3  * Phillip Lougher <phillip@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 
lzma_compress(void * dummy,void * dest,void * src,int size,int block_size,int * error)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 
lzma_uncompress(void * dest,void * src,int size,int outsize,int * error)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