Blame view

lib/decompress_unlzo.c 6.41 KB
45a46873f   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
7dd65feb6   Albin Tonnerre   lib: add support ...
2
3
4
5
6
7
8
9
10
11
12
13
  /*
   * 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.
   *
7dd65feb6   Albin Tonnerre   lib: add support ...
14
15
16
17
18
19
   * Markus F.X.J. Oberhumer
   * <markus@oberhumer.com>
   * http://www.oberhumer.com/opensource/lzop/
   */
  
  #ifdef STATIC
2d3862d26   Yinghai Lu   lib/decompressors...
20
  #define PREBOOT
b6bec26ce   Markus F.X.J. Oberhumer   lib/lzo: Rename l...
21
  #include "lzo/lzo1x_decompress_safe.c"
7dd65feb6   Albin Tonnerre   lib: add support ...
22
  #else
7dd65feb6   Albin Tonnerre   lib: add support ...
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
  #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...
38
39
  #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 ...
40

d97b07c54   Yinghai Lu   initramfs: suppor...
41
  STATIC inline long INIT parse_header(u8 *input, long *skip, long in_len)
7dd65feb6   Albin Tonnerre   lib: add support ...
42
43
44
  {
  	int l;
  	u8 *parse = input;
5a3f81a70   Lasse Collin   Decompressors: ch...
45
  	u8 *end = input + in_len;
7dd65feb6   Albin Tonnerre   lib: add support ...
46
47
  	u8 level = 0;
  	u16 version;
5a3f81a70   Lasse Collin   Decompressors: ch...
48
49
50
51
52
53
54
  	/*
  	 * 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 ...
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
  	/* 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...
71
72
73
74
75
76
77
78
  	/*
  	 * 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 ...
79
80
81
82
83
84
85
  	/* 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...
86
87
  	if (end - parse < l + 4)
  		return 0;
7dd65feb6   Albin Tonnerre   lib: add support ...
88
89
90
91
92
  	parse += l + 4;
  
  	*skip = parse - input;
  	return 1;
  }
d97b07c54   Yinghai Lu   initramfs: suppor...
93
94
95
96
  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...
97
  				void (*error) (char *x))
7dd65feb6   Albin Tonnerre   lib: add support ...
98
  {
5a3f81a70   Lasse Collin   Decompressors: ch...
99
  	u8 r = 0;
d97b07c54   Yinghai Lu   initramfs: suppor...
100
  	long skip = 0;
7dd65feb6   Albin Tonnerre   lib: add support ...
101
102
103
  	u32 src_len, dst_len;
  	size_t tmp;
  	u8 *in_buf, *in_buf_save, *out_buf;
ccdb40048   Albin Tonnerre   lib: fix the use ...
104
  	int ret = -1;
7dd65feb6   Albin Tonnerre   lib: add support ...
105

7dd65feb6   Albin Tonnerre   lib: add support ...
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
  	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...
124
125
  	} else if (!fill) {
  		error("NULL input pointer and missing fill function");
7dd65feb6   Albin Tonnerre   lib: add support ...
126
127
128
129
130
131
132
133
134
135
136
137
  		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...
138
139
140
141
142
143
144
145
146
147
  	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 ...
148

fb7fa589f   Lasse Collin   Decompressors: fi...
149
  	if (!parse_header(in_buf, &skip, in_len)) {
7dd65feb6   Albin Tonnerre   lib: add support ...
150
151
152
153
  		error("invalid header");
  		goto exit_2;
  	}
  	in_buf += skip;
5a3f81a70   Lasse Collin   Decompressors: ch...
154
  	in_len -= skip;
7dd65feb6   Albin Tonnerre   lib: add support ...
155

fb7fa589f   Lasse Collin   Decompressors: fi...
156
157
158
159
160
  	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 ...
161
162
163
164
165
  	if (posp)
  		*posp = skip;
  
  	for (;;) {
  		/* read uncompressed block size */
fb7fa589f   Lasse Collin   Decompressors: fi...
166
167
168
169
170
  		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...
171
172
173
174
  		if (in_len < 4) {
  			error("file corrupted");
  			goto exit_2;
  		}
7dd65feb6   Albin Tonnerre   lib: add support ...
175
176
  		dst_len = get_unaligned_be32(in_buf);
  		in_buf += 4;
5a3f81a70   Lasse Collin   Decompressors: ch...
177
  		in_len -= 4;
7dd65feb6   Albin Tonnerre   lib: add support ...
178
179
180
181
182
183
184
185
186
187
188
189
190
191
  
  		/* 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...
192
193
194
195
196
  		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...
197
198
199
200
  		if (in_len < 8) {
  			error("file corrupted");
  			goto exit_2;
  		}
7dd65feb6   Albin Tonnerre   lib: add support ...
201
202
  		src_len = get_unaligned_be32(in_buf);
  		in_buf += 8;
5a3f81a70   Lasse Collin   Decompressors: ch...
203
  		in_len -= 8;
7dd65feb6   Albin Tonnerre   lib: add support ...
204

fb7fa589f   Lasse Collin   Decompressors: fi...
205
  		if (src_len <= 0 || src_len > dst_len) {
7dd65feb6   Albin Tonnerre   lib: add support ...
206
207
208
209
210
  			error("file corrupted");
  			goto exit_2;
  		}
  
  		/* decompress */
fb7fa589f   Lasse Collin   Decompressors: fi...
211
212
213
214
215
216
217
218
219
  		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 ...
220
  		tmp = dst_len;
ccdb40048   Albin Tonnerre   lib: fix the use ...
221
222
223
224
225
226
227
228
  
  		/* 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 ...
229
  						out_buf, &tmp);
ccdb40048   Albin Tonnerre   lib: fix the use ...
230
231
232
233
  			if (r != LZO_E_OK || dst_len != tmp) {
  				error("Compressed data violation");
  				goto exit_2;
  			}
7dd65feb6   Albin Tonnerre   lib: add support ...
234
  		}
8f9b54a35   Lasse Collin   Decompressors: ch...
235
236
  		if (flush && flush(out_buf, dst_len) != dst_len)
  			goto exit_2;
7dd65feb6   Albin Tonnerre   lib: add support ...
237
238
239
240
  		if (output)
  			out_buf += dst_len;
  		if (posp)
  			*posp += src_len + 12;
fb7fa589f   Lasse Collin   Decompressors: fi...
241
242
243
  
  		in_buf += src_len;
  		in_len -= src_len;
7dd65feb6   Albin Tonnerre   lib: add support ...
244
  		if (fill) {
fb7fa589f   Lasse Collin   Decompressors: fi...
245
246
247
248
249
250
251
252
  			/*
  			 * 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 ...
253
  			in_buf = in_buf_save;
5a3f81a70   Lasse Collin   Decompressors: ch...
254
  		}
7dd65feb6   Albin Tonnerre   lib: add support ...
255
  	}
ccdb40048   Albin Tonnerre   lib: fix the use ...
256
  	ret = 0;
7dd65feb6   Albin Tonnerre   lib: add support ...
257
258
  exit_2:
  	if (!input)
35f152684   Sascha Hauer   unlzo: fix input ...
259
  		free(in_buf_save);
7dd65feb6   Albin Tonnerre   lib: add support ...
260
261
262
263
  exit_1:
  	if (!output)
  		free(out_buf);
  exit:
ccdb40048   Albin Tonnerre   lib: fix the use ...
264
  	return ret;
7dd65feb6   Albin Tonnerre   lib: add support ...
265
  }
2d3862d26   Yinghai Lu   lib/decompressors...
266
267
268
269
270
271
272
273
274
275
276
  #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