1 /*
2 * LZO decompressor for the Linux kernel. Code borrowed from the lzo
3 * implementation by Markus Franz Xaver Johannes Oberhumer.
4 *
5 * Linux kernel adaptation:
6 * Copyright (C) 2009
7 * Albin Tonnerre, Free Electrons <albin.tonnerre@free-electrons.com>
8 *
9 * Original code:
10 * Copyright (C) 1996-2005 Markus Franz Xaver Johannes Oberhumer
11 * All Rights Reserved.
12 *
13 * lzop and the LZO library are free software; you can redistribute them
14 * and/or modify them under the terms of the GNU General Public License as
15 * published by the Free Software Foundation; either version 2 of
16 * the License, or (at your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; see the file COPYING.
25 * If not, see <http://www.gnu.org/licenses/>.
26 *
27 * Markus F.X.J. Oberhumer
28 * <markus@oberhumer.com>
29 * http://www.oberhumer.com/opensource/lzop/
30 */
31
32 #include "decompress.h"
33 #include <xen/lzo.h>
34
35 #ifdef __XEN__
36 #include <asm/byteorder.h>
37 #endif
38
39 #if 1 /* ndef CONFIG_??? */
get_unaligned_be16(void * p)40 static inline u16 INIT get_unaligned_be16(void *p)
41 {
42 return be16_to_cpup(p);
43 }
44
get_unaligned_be32(void * p)45 static inline u32 INIT get_unaligned_be32(void *p)
46 {
47 return be32_to_cpup(p);
48 }
49 #else
50 #include <asm/unaligned.h>
51
get_unaligned_be16(void * p)52 static inline u16 INIT get_unaligned_be16(void *p)
53 {
54 return be16_to_cpu(__get_unaligned(p, 2));
55 }
56
get_unaligned_be32(void * p)57 static inline u32 INIT get_unaligned_be32(void *p)
58 {
59 return be32_to_cpu(__get_unaligned(p, 4));
60 }
61 #endif
62
63 static const unsigned char lzop_magic[] = {
64 0x89, 0x4c, 0x5a, 0x4f, 0x00, 0x0d, 0x0a, 0x1a, 0x0a };
65
66 #define LZO_BLOCK_SIZE (256*1024l)
67 #define HEADER_HAS_FILTER 0x00000800L
68 #define HEADER_SIZE_MIN (9 + 7 + 4 + 8 + 1 + 4)
69 #define HEADER_SIZE_MAX (9 + 7 + 1 + 8 + 8 + 4 + 1 + 255 + 4)
70
parse_header(u8 * input,int * skip,int in_len)71 static int INIT parse_header(u8 *input, int *skip, int in_len)
72 {
73 int l;
74 u8 *parse = input;
75 u8 *end = input + in_len;
76 u8 level = 0;
77 u16 version;
78
79 /*
80 * Check that there's enough input to possibly have a valid header.
81 * Then it is possible to parse several fields until the minimum
82 * size may have been used.
83 */
84 if (in_len < HEADER_SIZE_MIN)
85 return 0;
86
87 /* read magic: 9 first bits */
88 for (l = 0; l < 9; l++) {
89 if (*parse++ != lzop_magic[l])
90 return 0;
91 }
92 /* get version (2bytes), skip library version (2),
93 * 'need to be extracted' version (2) and
94 * method (1) */
95 version = get_unaligned_be16(parse);
96 parse += 7;
97 if (version >= 0x0940)
98 level = *parse++;
99 if (get_unaligned_be32(parse) & HEADER_HAS_FILTER)
100 parse += 8; /* flags + filter info */
101 else
102 parse += 4; /* flags */
103
104 /*
105 * At least mode, mtime_low, filename length, and checksum must
106 * be left to be parsed. If also mtime_high is present, it's OK
107 * because the next input buffer check is after reading the
108 * filename length.
109 */
110 if (end - parse < 8 + 1 + 4)
111 return 0;
112
113 /* skip mode and mtime_low */
114 parse += 8;
115 if (version >= 0x0940)
116 parse += 4; /* skip mtime_high */
117
118 l = *parse++;
119 /* don't care about the file name, and skip checksum */
120 if (end - parse < l + 4)
121 return 0;
122 parse += l + 4;
123
124 *skip = parse - input;
125 return 1;
126 }
127
unlzo(u8 * input,unsigned int in_len,int (* fill)(void *,unsigned int),int (* flush)(void *,unsigned int),u8 * output,unsigned int * posp,void (* error)(const char * x))128 STATIC int INIT unlzo(u8 *input, unsigned int in_len,
129 int (*fill) (void *, unsigned int),
130 int (*flush) (void *, unsigned int),
131 u8 *output, unsigned int *posp,
132 void (*error) (const char *x))
133 {
134 u8 r = 0;
135 int skip = 0;
136 u32 src_len, dst_len;
137 size_t tmp;
138 u8 *in_buf, *in_buf_save, *out_buf;
139 int ret = -1;
140
141 if (output) {
142 out_buf = output;
143 } else if (!flush) {
144 error("NULL output pointer and no flush function provided");
145 goto exit;
146 } else {
147 out_buf = malloc(LZO_BLOCK_SIZE);
148 if (!out_buf) {
149 error("Could not allocate output buffer");
150 goto exit;
151 }
152 }
153
154 if (input && fill) {
155 error("Both input pointer and fill function provided, don't know what to do");
156 goto exit_1;
157 } else if (input) {
158 in_buf = input;
159 } else if (!fill || !posp) {
160 error("NULL input pointer and missing position pointer or fill function");
161 goto exit_1;
162 } else {
163 in_buf = malloc(lzo1x_worst_compress(LZO_BLOCK_SIZE));
164 if (!in_buf) {
165 error("Could not allocate input buffer");
166 goto exit_1;
167 }
168 }
169 in_buf_save = in_buf;
170
171 if (posp)
172 *posp = 0;
173
174 if (fill)
175 fill(in_buf, lzo1x_worst_compress(LZO_BLOCK_SIZE));
176
177 if (!parse_header(input, &skip, in_len)) {
178 error("invalid header");
179 goto exit_2;
180 }
181 in_buf += skip;
182 in_len -= skip;
183
184 if (posp)
185 *posp = skip;
186
187 for (;;) {
188 /* read uncompressed block size */
189 if (in_len < 4) {
190 error("file corrupted");
191 goto exit_2;
192 }
193 dst_len = get_unaligned_be32(in_buf);
194 in_buf += 4;
195 in_len -= 4;
196
197 /* exit if last block */
198 if (dst_len == 0) {
199 if (posp)
200 *posp += 4;
201 break;
202 }
203
204 if (dst_len > LZO_BLOCK_SIZE) {
205 error("dest len longer than block size");
206 goto exit_2;
207 }
208
209 /* read compressed block size, and skip block checksum info */
210 if (in_len < 8) {
211 error("file corrupted");
212 goto exit_2;
213 }
214 src_len = get_unaligned_be32(in_buf);
215 in_buf += 8;
216 in_len -= 8;
217
218 if (src_len <= 0 || src_len > dst_len || src_len > in_len) {
219 error("file corrupted");
220 goto exit_2;
221 }
222
223 /* decompress */
224 tmp = dst_len;
225
226 /* When the input data is not compressed at all,
227 * lzo1x_decompress_safe will fail, so call memcpy()
228 * instead */
229 if (unlikely(dst_len == src_len))
230 memcpy(out_buf, in_buf, src_len);
231 else {
232 r = lzo1x_decompress_safe(in_buf, src_len,
233 out_buf, &tmp);
234
235 if (r != LZO_E_OK || dst_len != tmp) {
236 error("Compressed data violation");
237 goto exit_2;
238 }
239 }
240
241 if (flush && flush(out_buf, dst_len) != dst_len)
242 goto exit_2;
243 if (output)
244 out_buf += dst_len;
245 if (posp)
246 *posp += src_len + 12;
247 if (fill) {
248 in_buf = in_buf_save;
249 fill(in_buf, lzo1x_worst_compress(LZO_BLOCK_SIZE));
250 } else {
251 in_buf += src_len;
252 in_len -= src_len;
253 }
254 }
255
256 ret = 0;
257 exit_2:
258 if (!input)
259 free(in_buf_save);
260 exit_1:
261 if (!output)
262 free(out_buf);
263 exit:
264 return ret;
265 }
266