Blame view

lib/decompress_unlzo.c 7.11 KB
7dd65feb6   Albin Tonnerre   lib: add support ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
  /*
   * LZO decompressor for the Linux kernel. Code borrowed from the lzo
   * implementation by Markus Franz Xaver Johannes Oberhumer.
   *
   * Linux kernel adaptation:
   * Copyright (C) 2009
   * Albin Tonnerre, Free Electrons <albin.tonnerre@free-electrons.com>
   *
   * Original code:
   * Copyright (C) 1996-2005 Markus Franz Xaver Johannes Oberhumer
   * All Rights Reserved.
   *
   * lzop and the LZO library are free software; you can redistribute them
   * and/or modify them under the terms of the GNU General Public License as
   * published by the Free Software Foundation; either version 2 of
   * the License, or (at your option) any later version.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with this program; see the file COPYING.
   * If not, write to the Free Software Foundation, Inc.,
   * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
   *
   * Markus F.X.J. Oberhumer
   * <markus@oberhumer.com>
   * http://www.oberhumer.com/opensource/lzop/
   */
  
  #ifdef STATIC
2d3862d26   Yinghai Lu   lib/decompressors...
34
  #define PREBOOT
b6bec26ce   Markus F.X.J. Oberhumer   lib/lzo: Rename l...
35
  #include "lzo/lzo1x_decompress_safe.c"
7dd65feb6   Albin Tonnerre   lib: add support ...
36
  #else
7dd65feb6   Albin Tonnerre   lib: add support ...
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
  #include <linux/decompress/unlzo.h>
  #endif
  
  #include <linux/types.h>
  #include <linux/lzo.h>
  #include <linux/decompress/mm.h>
  
  #include <linux/compiler.h>
  #include <asm/unaligned.h>
  
  static const unsigned char lzop_magic[] = {
  	0x89, 0x4c, 0x5a, 0x4f, 0x00, 0x0d, 0x0a, 0x1a, 0x0a };
  
  #define LZO_BLOCK_SIZE        (256*1024l)
  #define HEADER_HAS_FILTER      0x00000800L
5a3f81a70   Lasse Collin   Decompressors: ch...
52
53
  #define HEADER_SIZE_MIN       (9 + 7     + 4 + 8     + 1       + 4)
  #define HEADER_SIZE_MAX       (9 + 7 + 1 + 8 + 8 + 4 + 1 + 255 + 4)
7dd65feb6   Albin Tonnerre   lib: add support ...
54

d97b07c54   Yinghai Lu   initramfs: suppor...
55
  STATIC inline long INIT parse_header(u8 *input, long *skip, long in_len)
7dd65feb6   Albin Tonnerre   lib: add support ...
56
57
58
  {
  	int l;
  	u8 *parse = input;
5a3f81a70   Lasse Collin   Decompressors: ch...
59
  	u8 *end = input + in_len;
7dd65feb6   Albin Tonnerre   lib: add support ...
60
61
  	u8 level = 0;
  	u16 version;
5a3f81a70   Lasse Collin   Decompressors: ch...
62
63
64
65
66
67
68
  	/*
  	 * Check that there's enough input to possibly have a valid header.
  	 * Then it is possible to parse several fields until the minimum
  	 * size may have been used.
  	 */
  	if (in_len < HEADER_SIZE_MIN)
  		return 0;
7dd65feb6   Albin Tonnerre   lib: add support ...
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
  	/* read magic: 9 first bits */
  	for (l = 0; l < 9; l++) {
  		if (*parse++ != lzop_magic[l])
  			return 0;
  	}
  	/* get version (2bytes), skip library version (2),
  	 * 'need to be extracted' version (2) and
  	 * method (1) */
  	version = get_unaligned_be16(parse);
  	parse += 7;
  	if (version >= 0x0940)
  		level = *parse++;
  	if (get_unaligned_be32(parse) & HEADER_HAS_FILTER)
  		parse += 8; /* flags + filter info */
  	else
  		parse += 4; /* flags */
5a3f81a70   Lasse Collin   Decompressors: ch...
85
86
87
88
89
90
91
92
  	/*
  	 * At least mode, mtime_low, filename length, and checksum must
  	 * be left to be parsed. If also mtime_high is present, it's OK
  	 * because the next input buffer check is after reading the
  	 * filename length.
  	 */
  	if (end - parse < 8 + 1 + 4)
  		return 0;
7dd65feb6   Albin Tonnerre   lib: add support ...
93
94
95
96
97
98
99
  	/* skip mode and mtime_low */
  	parse += 8;
  	if (version >= 0x0940)
  		parse += 4;	/* skip mtime_high */
  
  	l = *parse++;
  	/* don't care about the file name, and skip checksum */
5a3f81a70   Lasse Collin   Decompressors: ch...
100
101
  	if (end - parse < l + 4)
  		return 0;
7dd65feb6   Albin Tonnerre   lib: add support ...
102
103
104
105
106
  	parse += l + 4;
  
  	*skip = parse - input;
  	return 1;
  }
d97b07c54   Yinghai Lu   initramfs: suppor...
107
108
109
110
  STATIC int INIT unlzo(u8 *input, long in_len,
  				long (*fill)(void *, unsigned long),
  				long (*flush)(void *, unsigned long),
  				u8 *output, long *posp,
93685ad24   Lasse Collin   Decompressors: ge...
111
  				void (*error) (char *x))
7dd65feb6   Albin Tonnerre   lib: add support ...
112
  {
5a3f81a70   Lasse Collin   Decompressors: ch...
113
  	u8 r = 0;
d97b07c54   Yinghai Lu   initramfs: suppor...
114
  	long skip = 0;
7dd65feb6   Albin Tonnerre   lib: add support ...
115
116
117
  	u32 src_len, dst_len;
  	size_t tmp;
  	u8 *in_buf, *in_buf_save, *out_buf;
ccdb40048   Albin Tonnerre   lib: fix the use ...
118
  	int ret = -1;
7dd65feb6   Albin Tonnerre   lib: add support ...
119

7dd65feb6   Albin Tonnerre   lib: add support ...
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
  	if (output) {
  		out_buf = output;
  	} else if (!flush) {
  		error("NULL output pointer and no flush function provided");
  		goto exit;
  	} else {
  		out_buf = malloc(LZO_BLOCK_SIZE);
  		if (!out_buf) {
  			error("Could not allocate output buffer");
  			goto exit;
  		}
  	}
  
  	if (input && fill) {
  		error("Both input pointer and fill function provided, don't know what to do");
  		goto exit_1;
  	} else if (input) {
  		in_buf = input;
fb7fa589f   Lasse Collin   Decompressors: fi...
138
139
  	} else if (!fill) {
  		error("NULL input pointer and missing fill function");
7dd65feb6   Albin Tonnerre   lib: add support ...
140
141
142
143
144
145
146
147
148
149
150
151
  		goto exit_1;
  	} else {
  		in_buf = malloc(lzo1x_worst_compress(LZO_BLOCK_SIZE));
  		if (!in_buf) {
  			error("Could not allocate input buffer");
  			goto exit_1;
  		}
  	}
  	in_buf_save = in_buf;
  
  	if (posp)
  		*posp = 0;
fb7fa589f   Lasse Collin   Decompressors: fi...
152
153
154
155
156
157
158
159
160
161
  	if (fill) {
  		/*
  		 * Start from in_buf + HEADER_SIZE_MAX to make it possible
  		 * to use memcpy() to copy the unused data to the beginning
  		 * of the buffer. This way memmove() isn't needed which
  		 * is missing from pre-boot environments of most archs.
  		 */
  		in_buf += HEADER_SIZE_MAX;
  		in_len = fill(in_buf, HEADER_SIZE_MAX);
  	}
7dd65feb6   Albin Tonnerre   lib: add support ...
162

fb7fa589f   Lasse Collin   Decompressors: fi...
163
  	if (!parse_header(in_buf, &skip, in_len)) {
7dd65feb6   Albin Tonnerre   lib: add support ...
164
165
166
167
  		error("invalid header");
  		goto exit_2;
  	}
  	in_buf += skip;
5a3f81a70   Lasse Collin   Decompressors: ch...
168
  	in_len -= skip;
7dd65feb6   Albin Tonnerre   lib: add support ...
169

fb7fa589f   Lasse Collin   Decompressors: fi...
170
171
172
173
174
  	if (fill) {
  		/* Move the unused data to the beginning of the buffer. */
  		memcpy(in_buf_save, in_buf, in_len);
  		in_buf = in_buf_save;
  	}
7dd65feb6   Albin Tonnerre   lib: add support ...
175
176
177
178
179
  	if (posp)
  		*posp = skip;
  
  	for (;;) {
  		/* read uncompressed block size */
fb7fa589f   Lasse Collin   Decompressors: fi...
180
181
182
183
184
  		if (fill && in_len < 4) {
  			skip = fill(in_buf + in_len, 4 - in_len);
  			if (skip > 0)
  				in_len += skip;
  		}
5a3f81a70   Lasse Collin   Decompressors: ch...
185
186
187
188
  		if (in_len < 4) {
  			error("file corrupted");
  			goto exit_2;
  		}
7dd65feb6   Albin Tonnerre   lib: add support ...
189
190
  		dst_len = get_unaligned_be32(in_buf);
  		in_buf += 4;
5a3f81a70   Lasse Collin   Decompressors: ch...
191
  		in_len -= 4;
7dd65feb6   Albin Tonnerre   lib: add support ...
192
193
194
195
196
197
198
199
200
201
202
203
204
205
  
  		/* exit if last block */
  		if (dst_len == 0) {
  			if (posp)
  				*posp += 4;
  			break;
  		}
  
  		if (dst_len > LZO_BLOCK_SIZE) {
  			error("dest len longer than block size");
  			goto exit_2;
  		}
  
  		/* read compressed block size, and skip block checksum info */
fb7fa589f   Lasse Collin   Decompressors: fi...
206
207
208
209
210
  		if (fill && in_len < 8) {
  			skip = fill(in_buf + in_len, 8 - in_len);
  			if (skip > 0)
  				in_len += skip;
  		}
5a3f81a70   Lasse Collin   Decompressors: ch...
211
212
213
214
  		if (in_len < 8) {
  			error("file corrupted");
  			goto exit_2;
  		}
7dd65feb6   Albin Tonnerre   lib: add support ...
215
216
  		src_len = get_unaligned_be32(in_buf);
  		in_buf += 8;
5a3f81a70   Lasse Collin   Decompressors: ch...
217
  		in_len -= 8;
7dd65feb6   Albin Tonnerre   lib: add support ...
218

fb7fa589f   Lasse Collin   Decompressors: fi...
219
  		if (src_len <= 0 || src_len > dst_len) {
7dd65feb6   Albin Tonnerre   lib: add support ...
220
221
222
223
224
  			error("file corrupted");
  			goto exit_2;
  		}
  
  		/* decompress */
fb7fa589f   Lasse Collin   Decompressors: fi...
225
226
227
228
229
230
231
232
233
  		if (fill && in_len < src_len) {
  			skip = fill(in_buf + in_len, src_len - in_len);
  			if (skip > 0)
  				in_len += skip;
  		}
  		if (in_len < src_len) {
  			error("file corrupted");
  			goto exit_2;
  		}
7dd65feb6   Albin Tonnerre   lib: add support ...
234
  		tmp = dst_len;
ccdb40048   Albin Tonnerre   lib: fix the use ...
235
236
237
238
239
240
241
242
  
  		/* When the input data is not compressed at all,
  		 * lzo1x_decompress_safe will fail, so call memcpy()
  		 * instead */
  		if (unlikely(dst_len == src_len))
  			memcpy(out_buf, in_buf, src_len);
  		else {
  			r = lzo1x_decompress_safe((u8 *) in_buf, src_len,
7dd65feb6   Albin Tonnerre   lib: add support ...
243
  						out_buf, &tmp);
ccdb40048   Albin Tonnerre   lib: fix the use ...
244
245
246
247
  			if (r != LZO_E_OK || dst_len != tmp) {
  				error("Compressed data violation");
  				goto exit_2;
  			}
7dd65feb6   Albin Tonnerre   lib: add support ...
248
  		}
8f9b54a35   Lasse Collin   Decompressors: ch...
249
250
  		if (flush && flush(out_buf, dst_len) != dst_len)
  			goto exit_2;
7dd65feb6   Albin Tonnerre   lib: add support ...
251
252
253
254
  		if (output)
  			out_buf += dst_len;
  		if (posp)
  			*posp += src_len + 12;
fb7fa589f   Lasse Collin   Decompressors: fi...
255
256
257
  
  		in_buf += src_len;
  		in_len -= src_len;
7dd65feb6   Albin Tonnerre   lib: add support ...
258
  		if (fill) {
fb7fa589f   Lasse Collin   Decompressors: fi...
259
260
261
262
263
264
265
266
  			/*
  			 * If there happens to still be unused data left in
  			 * in_buf, move it to the beginning of the buffer.
  			 * Use a loop to avoid memmove() dependency.
  			 */
  			if (in_len > 0)
  				for (skip = 0; skip < in_len; ++skip)
  					in_buf_save[skip] = in_buf[skip];
7dd65feb6   Albin Tonnerre   lib: add support ...
267
  			in_buf = in_buf_save;
5a3f81a70   Lasse Collin   Decompressors: ch...
268
  		}
7dd65feb6   Albin Tonnerre   lib: add support ...
269
  	}
ccdb40048   Albin Tonnerre   lib: fix the use ...
270
  	ret = 0;
7dd65feb6   Albin Tonnerre   lib: add support ...
271
272
  exit_2:
  	if (!input)
35f152684   Sascha Hauer   unlzo: fix input ...
273
  		free(in_buf_save);
7dd65feb6   Albin Tonnerre   lib: add support ...
274
275
276
277
  exit_1:
  	if (!output)
  		free(out_buf);
  exit:
ccdb40048   Albin Tonnerre   lib: fix the use ...
278
  	return ret;
7dd65feb6   Albin Tonnerre   lib: add support ...
279
  }
2d3862d26   Yinghai Lu   lib/decompressors...
280
281
282
283
284
285
286
287
288
289
290
  #ifdef PREBOOT
  STATIC int INIT __decompress(unsigned char *buf, long len,
  			   long (*fill)(void*, unsigned long),
  			   long (*flush)(void*, unsigned long),
  			   unsigned char *out_buf, long olen,
  			   long *pos,
  			   void (*error)(char *x))
  {
  	return unlzo(buf, len, fill, flush, out_buf, pos, error);
  }
  #endif