blob: e0e90ac6cdcb8ca31d07d37f79a3c80812aa21d9
1 | /* |
2 | This file is part of the lzop file compressor. |
3 | |
4 | Copyright (C) 1996..2003 Markus Franz Xaver Johannes Oberhumer |
5 | All Rights Reserved. |
6 | |
7 | Markus F.X.J. Oberhumer <markus@oberhumer.com> |
8 | http://www.oberhumer.com/opensource/lzop/ |
9 | |
10 | lzop and the LZO library are free software; you can redistribute them |
11 | and/or modify them under the terms of the GNU General Public License as |
12 | published by the Free Software Foundation; either version 2 of |
13 | the License, or (at your option) any later version. |
14 | |
15 | This program is distributed in the hope that it will be useful, |
16 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
18 | GNU General Public License for more details. |
19 | |
20 | You should have received a copy of the GNU General Public License |
21 | along with this program; see the file COPYING. |
22 | If not, write to the Free Software Foundation, Inc., |
23 | 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
24 | |
25 | "Minimalized" for busybox by Alain Knaff |
26 | */ |
27 | |
28 | //config:config LZOP |
29 | //config: bool "lzop" |
30 | //config: default y |
31 | //config: help |
32 | //config: Lzop compression/decompresion. |
33 | //config: |
34 | //config:config UNLZOP |
35 | //config: bool "unlzop" |
36 | //config: default y |
37 | //config: help |
38 | //config: Lzop decompresion. |
39 | //config: |
40 | //config:config LZOPCAT |
41 | //config: bool "lzopcat" |
42 | //config: default y |
43 | //config: help |
44 | //config: Alias to "unlzop -c". |
45 | //config: |
46 | //config:config LZOP_COMPR_HIGH |
47 | //config: bool "lzop compression levels 7,8,9 (not very useful)" |
48 | //config: default n |
49 | //config: depends on LZOP || UNLZOP || LZOPCAT |
50 | //config: help |
51 | //config: High levels (7,8,9) of lzop compression. These levels |
52 | //config: are actually slower than gzip at equivalent compression ratios |
53 | //config: and take up 3.2K of code. |
54 | |
55 | //applet:IF_LZOP(APPLET(lzop, BB_DIR_BIN, BB_SUID_DROP)) |
56 | //applet:IF_UNLZOP(APPLET_ODDNAME(unlzop, lzop, BB_DIR_USR_BIN, BB_SUID_DROP, unlzop)) |
57 | //applet:IF_LZOPCAT(APPLET_ODDNAME(lzopcat, lzop, BB_DIR_USR_BIN, BB_SUID_DROP, lzopcat)) |
58 | //kbuild:lib-$(CONFIG_LZOP) += lzop.o |
59 | //kbuild:lib-$(CONFIG_UNLZOP) += lzop.o |
60 | //kbuild:lib-$(CONFIG_LZOPCAT) += lzop.o |
61 | |
62 | //usage:#define lzop_trivial_usage |
63 | //usage: "[-cfvd123456789CF] [FILE]..." |
64 | //usage:#define lzop_full_usage "\n\n" |
65 | //usage: " -1..9 Compression level" |
66 | //usage: "\n -d Decompress" |
67 | //usage: "\n -c Write to stdout" |
68 | //usage: "\n -f Force" |
69 | //usage: "\n -v Verbose" |
70 | //usage: "\n -F Don't store or verify checksum" |
71 | //usage: "\n -C Also write checksum of compressed block" |
72 | //usage: |
73 | //usage:#define lzopcat_trivial_usage |
74 | //usage: "[-vF] [FILE]..." |
75 | //usage:#define lzopcat_full_usage "\n\n" |
76 | //usage: " -v Verbose" |
77 | //usage: "\n -F Don't verify checksum" |
78 | //usage: |
79 | //usage:#define unlzop_trivial_usage |
80 | //usage: "[-cfvF] [FILE]..." |
81 | //usage:#define unlzop_full_usage "\n\n" |
82 | //usage: " -c Write to stdout" |
83 | //usage: "\n -f Force" |
84 | //usage: "\n -v Verbose" |
85 | //usage: "\n -F Don't verify checksum" |
86 | |
87 | #include "libbb.h" |
88 | #include "common_bufsiz.h" |
89 | #include "bb_archive.h" |
90 | #include "liblzo_interface.h" |
91 | |
92 | /* lzo-2.03/src/lzo_ptr.h */ |
93 | #define pd(a,b) ((unsigned)((a)-(b))) |
94 | |
95 | #define lzo_version() LZO_VERSION |
96 | #define lzo_sizeof_dict_t (sizeof(uint8_t*)) |
97 | |
98 | /* lzo-2.03/include/lzo/lzo1x.h */ |
99 | #define LZO1X_1_MEM_COMPRESS (16384 * lzo_sizeof_dict_t) |
100 | #define LZO1X_1_15_MEM_COMPRESS (32768 * lzo_sizeof_dict_t) |
101 | #define LZO1X_999_MEM_COMPRESS (14 * 16384 * sizeof(short)) |
102 | |
103 | /* lzo-2.03/src/lzo1x_oo.c */ |
104 | #define NO_LIT UINT_MAX |
105 | |
106 | /**********************************************************************/ |
107 | static void copy2(uint8_t* ip, const uint8_t* m_pos, unsigned off) |
108 | { |
109 | ip[0] = m_pos[0]; |
110 | if (off == 1) |
111 | ip[1] = m_pos[0]; |
112 | else |
113 | ip[1] = m_pos[1]; |
114 | } |
115 | |
116 | static void copy3(uint8_t* ip, const uint8_t* m_pos, unsigned off) |
117 | { |
118 | ip[0] = m_pos[0]; |
119 | if (off == 1) { |
120 | ip[2] = ip[1] = m_pos[0]; |
121 | } |
122 | else if (off == 2) { |
123 | ip[1] = m_pos[1]; |
124 | ip[2] = m_pos[0]; |
125 | } |
126 | else { |
127 | ip[1] = m_pos[1]; |
128 | ip[2] = m_pos[2]; |
129 | } |
130 | } |
131 | |
132 | /**********************************************************************/ |
133 | // optimize a block of data. |
134 | /**********************************************************************/ |
135 | #define TEST_IP (ip < ip_end) |
136 | #define TEST_OP (op <= op_end) |
137 | |
138 | static NOINLINE int lzo1x_optimize(uint8_t *in, unsigned in_len, |
139 | uint8_t *out, unsigned *out_len, |
140 | void* wrkmem UNUSED_PARAM) |
141 | { |
142 | uint8_t* op; |
143 | uint8_t* ip; |
144 | unsigned t; |
145 | uint8_t* m_pos; |
146 | uint8_t* const ip_end = in + in_len; |
147 | uint8_t* const op_end = out + *out_len; |
148 | uint8_t* litp = NULL; |
149 | unsigned lit = 0; |
150 | unsigned next_lit = NO_LIT; |
151 | unsigned nl; |
152 | unsigned long o_m1_a = 0, o_m1_b = 0, o_m2 = 0, o_m3_a = 0, o_m3_b = 0; |
153 | |
154 | // LZO_UNUSED(wrkmem); |
155 | |
156 | *out_len = 0; |
157 | |
158 | op = out; |
159 | ip = in; |
160 | |
161 | if (*ip > 17) { |
162 | t = *ip++ - 17; |
163 | if (t < 4) |
164 | goto match_next; |
165 | goto first_literal_run; |
166 | } |
167 | |
168 | while (TEST_IP && TEST_OP) { |
169 | t = *ip++; |
170 | if (t >= 16) |
171 | goto match; |
172 | /* a literal run */ |
173 | litp = ip - 1; |
174 | if (t == 0) { |
175 | t = 15; |
176 | while (*ip == 0) |
177 | t += 255, ip++; |
178 | t += *ip++; |
179 | } |
180 | lit = t + 3; |
181 | /* copy literals */ |
182 | copy_literal_run: |
183 | *op++ = *ip++; |
184 | *op++ = *ip++; |
185 | *op++ = *ip++; |
186 | first_literal_run: |
187 | do *op++ = *ip++; while (--t > 0); |
188 | |
189 | t = *ip++; |
190 | |
191 | if (t >= 16) |
192 | goto match; |
193 | #if defined(LZO1X) |
194 | m_pos = op - 1 - 0x800; |
195 | #elif defined(LZO1Y) |
196 | m_pos = op - 1 - 0x400; |
197 | #endif |
198 | m_pos -= t >> 2; |
199 | m_pos -= *ip++ << 2; |
200 | *op++ = *m_pos++; |
201 | *op++ = *m_pos++; |
202 | *op++ = *m_pos++; |
203 | lit = 0; |
204 | goto match_done; |
205 | |
206 | |
207 | /* handle matches */ |
208 | do { |
209 | if (t < 16) { /* a M1 match */ |
210 | m_pos = op - 1; |
211 | m_pos -= t >> 2; |
212 | m_pos -= *ip++ << 2; |
213 | |
214 | if (litp == NULL) |
215 | goto copy_m1; |
216 | |
217 | nl = ip[-2] & 3; |
218 | /* test if a match follows */ |
219 | if (nl == 0 && lit == 1 && ip[0] >= 16) { |
220 | next_lit = nl; |
221 | /* adjust length of previous short run */ |
222 | lit += 2; |
223 | *litp = (unsigned char)((*litp & ~3) | lit); |
224 | /* copy over the 2 literals that replace the match */ |
225 | copy2(ip-2, m_pos, pd(op, m_pos)); |
226 | o_m1_a++; |
227 | } |
228 | /* test if a literal run follows */ |
229 | else |
230 | if (nl == 0 |
231 | && ip[0] < 16 |
232 | && ip[0] != 0 |
233 | && (lit + 2 + ip[0] < 16) |
234 | ) { |
235 | t = *ip++; |
236 | /* remove short run */ |
237 | *litp &= ~3; |
238 | /* copy over the 2 literals that replace the match */ |
239 | copy2(ip-3+1, m_pos, pd(op, m_pos)); |
240 | /* move literals 1 byte ahead */ |
241 | litp += 2; |
242 | if (lit > 0) |
243 | memmove(litp+1, litp, lit); |
244 | /* insert new length of long literal run */ |
245 | lit += 2 + t + 3; |
246 | *litp = (unsigned char)(lit - 3); |
247 | |
248 | o_m1_b++; |
249 | *op++ = *m_pos++; |
250 | *op++ = *m_pos++; |
251 | goto copy_literal_run; |
252 | } |
253 | copy_m1: |
254 | *op++ = *m_pos++; |
255 | *op++ = *m_pos++; |
256 | } else { |
257 | match: |
258 | if (t >= 64) { /* a M2 match */ |
259 | m_pos = op - 1; |
260 | #if defined(LZO1X) |
261 | m_pos -= (t >> 2) & 7; |
262 | m_pos -= *ip++ << 3; |
263 | t = (t >> 5) - 1; |
264 | #elif defined(LZO1Y) |
265 | m_pos -= (t >> 2) & 3; |
266 | m_pos -= *ip++ << 2; |
267 | t = (t >> 4) - 3; |
268 | #endif |
269 | if (litp == NULL) |
270 | goto copy_m; |
271 | |
272 | nl = ip[-2] & 3; |
273 | /* test if in beetween two long literal runs */ |
274 | if (t == 1 && lit > 3 && nl == 0 |
275 | && ip[0] < 16 && ip[0] != 0 && (lit + 3 + ip[0] < 16) |
276 | ) { |
277 | t = *ip++; |
278 | /* copy over the 3 literals that replace the match */ |
279 | copy3(ip-1-2, m_pos, pd(op, m_pos)); |
280 | /* set new length of previous literal run */ |
281 | lit += 3 + t + 3; |
282 | *litp = (unsigned char)(lit - 3); |
283 | o_m2++; |
284 | *op++ = *m_pos++; |
285 | *op++ = *m_pos++; |
286 | *op++ = *m_pos++; |
287 | goto copy_literal_run; |
288 | } |
289 | } else { |
290 | if (t >= 32) { /* a M3 match */ |
291 | t &= 31; |
292 | if (t == 0) { |
293 | t = 31; |
294 | while (*ip == 0) |
295 | t += 255, ip++; |
296 | t += *ip++; |
297 | } |
298 | m_pos = op - 1; |
299 | m_pos -= *ip++ >> 2; |
300 | m_pos -= *ip++ << 6; |
301 | } else { /* a M4 match */ |
302 | m_pos = op; |
303 | m_pos -= (t & 8) << 11; |
304 | t &= 7; |
305 | if (t == 0) { |
306 | t = 7; |
307 | while (*ip == 0) |
308 | t += 255, ip++; |
309 | t += *ip++; |
310 | } |
311 | m_pos -= *ip++ >> 2; |
312 | m_pos -= *ip++ << 6; |
313 | if (m_pos == op) |
314 | goto eof_found; |
315 | m_pos -= 0x4000; |
316 | } |
317 | if (litp == NULL) |
318 | goto copy_m; |
319 | |
320 | nl = ip[-2] & 3; |
321 | /* test if in beetween two matches */ |
322 | if (t == 1 && lit == 0 && nl == 0 && ip[0] >= 16) { |
323 | next_lit = nl; |
324 | /* make a previous short run */ |
325 | lit += 3; |
326 | *litp = (unsigned char)((*litp & ~3) | lit); |
327 | /* copy over the 3 literals that replace the match */ |
328 | copy3(ip-3, m_pos, pd(op, m_pos)); |
329 | o_m3_a++; |
330 | } |
331 | /* test if a literal run follows */ |
332 | else if (t == 1 && lit <= 3 && nl == 0 |
333 | && ip[0] < 16 && ip[0] != 0 && (lit + 3 + ip[0] < 16) |
334 | ) { |
335 | t = *ip++; |
336 | /* remove short run */ |
337 | *litp &= ~3; |
338 | /* copy over the 3 literals that replace the match */ |
339 | copy3(ip-4+1, m_pos, pd(op, m_pos)); |
340 | /* move literals 1 byte ahead */ |
341 | litp += 2; |
342 | if (lit > 0) |
343 | memmove(litp+1,litp,lit); |
344 | /* insert new length of long literal run */ |
345 | lit += 3 + t + 3; |
346 | *litp = (unsigned char)(lit - 3); |
347 | |
348 | o_m3_b++; |
349 | *op++ = *m_pos++; |
350 | *op++ = *m_pos++; |
351 | *op++ = *m_pos++; |
352 | goto copy_literal_run; |
353 | } |
354 | } |
355 | copy_m: |
356 | *op++ = *m_pos++; |
357 | *op++ = *m_pos++; |
358 | do *op++ = *m_pos++; while (--t > 0); |
359 | } |
360 | |
361 | match_done: |
362 | if (next_lit == NO_LIT) { |
363 | t = ip[-2] & 3; |
364 | lit = t; |
365 | litp = ip - 2; |
366 | } |
367 | else |
368 | t = next_lit; |
369 | next_lit = NO_LIT; |
370 | if (t == 0) |
371 | break; |
372 | /* copy literals */ |
373 | match_next: |
374 | do *op++ = *ip++; while (--t > 0); |
375 | t = *ip++; |
376 | } while (TEST_IP && TEST_OP); |
377 | } |
378 | |
379 | /* no EOF code was found */ |
380 | *out_len = pd(op, out); |
381 | return LZO_E_EOF_NOT_FOUND; |
382 | |
383 | eof_found: |
384 | // LZO_UNUSED(o_m1_a); LZO_UNUSED(o_m1_b); LZO_UNUSED(o_m2); |
385 | // LZO_UNUSED(o_m3_a); LZO_UNUSED(o_m3_b); |
386 | *out_len = pd(op, out); |
387 | return (ip == ip_end ? LZO_E_OK : |
388 | (ip < ip_end ? LZO_E_INPUT_NOT_CONSUMED : LZO_E_INPUT_OVERRUN)); |
389 | } |
390 | |
391 | /**********************************************************************/ |
392 | #define F_OS F_OS_UNIX |
393 | #define F_CS F_CS_NATIVE |
394 | |
395 | /**********************************************************************/ |
396 | #define ADLER32_INIT_VALUE 1 |
397 | #define CRC32_INIT_VALUE 0 |
398 | |
399 | /**********************************************************************/ |
400 | enum { |
401 | M_LZO1X_1 = 1, |
402 | M_LZO1X_1_15 = 2, |
403 | M_LZO1X_999 = 3, |
404 | }; |
405 | |
406 | /**********************************************************************/ |
407 | /* header flags */ |
408 | #define F_ADLER32_D 0x00000001L |
409 | #define F_ADLER32_C 0x00000002L |
410 | #define F_H_EXTRA_FIELD 0x00000040L |
411 | #define F_H_GMTDIFF 0x00000080L |
412 | #define F_CRC32_D 0x00000100L |
413 | #define F_CRC32_C 0x00000200L |
414 | #define F_H_FILTER 0x00000800L |
415 | #define F_H_CRC32 0x00001000L |
416 | #define F_MASK 0x00003FFFL |
417 | |
418 | /* operating system & file system that created the file [mostly unused] */ |
419 | #define F_OS_UNIX 0x03000000L |
420 | #define F_OS_SHIFT 24 |
421 | #define F_OS_MASK 0xff000000L |
422 | |
423 | /* character set for file name encoding [mostly unused] */ |
424 | #define F_CS_NATIVE 0x00000000L |
425 | #define F_CS_SHIFT 20 |
426 | #define F_CS_MASK 0x00f00000L |
427 | |
428 | /* these bits must be zero */ |
429 | #define F_RESERVED ((F_MASK | F_OS_MASK | F_CS_MASK) ^ 0xffffffffL) |
430 | |
431 | typedef struct chksum_t { |
432 | uint32_t f_adler32; |
433 | uint32_t f_crc32; |
434 | } chksum_t; |
435 | |
436 | typedef struct header_t { |
437 | unsigned version; |
438 | unsigned lib_version; |
439 | unsigned version_needed_to_extract; |
440 | uint32_t flags; |
441 | uint32_t mode; |
442 | uint32_t mtime; |
443 | uint32_t gmtdiff; |
444 | uint32_t header_checksum; |
445 | |
446 | uint32_t extra_field_len; |
447 | uint32_t extra_field_checksum; |
448 | |
449 | unsigned char method; |
450 | unsigned char level; |
451 | |
452 | /* info */ |
453 | char name[255+1]; |
454 | } header_t; |
455 | |
456 | struct globals { |
457 | /*const uint32_t *lzo_crc32_table;*/ |
458 | chksum_t chksum_in; |
459 | chksum_t chksum_out; |
460 | } FIX_ALIASING; |
461 | #define G (*(struct globals*)bb_common_bufsiz1) |
462 | #define INIT_G() do { setup_common_bufsiz(); } while (0) |
463 | //#define G (*ptr_to_globals) |
464 | //#define INIT_G() do { |
465 | // SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); |
466 | //} while (0) |
467 | |
468 | |
469 | /**********************************************************************/ |
470 | #define LZOP_VERSION 0x1010 |
471 | //#define LZOP_VERSION_STRING "1.01" |
472 | //#define LZOP_VERSION_DATE "Apr 27th 2003" |
473 | |
474 | #define OPTION_STRING "cfvqdt123456789CF" |
475 | |
476 | /* Note: must be kept in sync with archival/bbunzip.c */ |
477 | enum { |
478 | OPT_STDOUT = (1 << 0), |
479 | OPT_FORCE = (1 << 1), |
480 | OPT_VERBOSE = (1 << 2), |
481 | OPT_QUIET = (1 << 3), |
482 | OPT_DECOMPRESS = (1 << 4), |
483 | OPT_TEST = (1 << 5), |
484 | OPT_1 = (1 << 6), |
485 | OPT_2 = (1 << 7), |
486 | OPT_3 = (1 << 8), |
487 | OPT_4 = (1 << 9), |
488 | OPT_5 = (1 << 10), |
489 | OPT_6 = (1 << 11), |
490 | OPT_789 = (7 << 12), |
491 | OPT_7 = (1 << 13), |
492 | OPT_8 = (1 << 14), |
493 | OPT_C = (1 << 15), |
494 | OPT_F = (1 << 16), |
495 | }; |
496 | |
497 | /**********************************************************************/ |
498 | // adler32 checksum |
499 | // adapted from free code by Mark Adler <madler@alumni.caltech.edu> |
500 | // see http://www.zlib.org/ |
501 | /**********************************************************************/ |
502 | static FAST_FUNC uint32_t |
503 | lzo_adler32(uint32_t adler, const uint8_t* buf, unsigned len) |
504 | { |
505 | enum { |
506 | LZO_BASE = 65521, /* largest prime smaller than 65536 */ |
507 | /* NMAX is the largest n such that |
508 | * 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ |
509 | LZO_NMAX = 5552, |
510 | }; |
511 | uint32_t s1 = adler & 0xffff; |
512 | uint32_t s2 = (adler >> 16) & 0xffff; |
513 | unsigned k; |
514 | |
515 | if (buf == NULL) |
516 | return 1; |
517 | |
518 | while (len > 0) { |
519 | k = len < LZO_NMAX ? (unsigned) len : LZO_NMAX; |
520 | len -= k; |
521 | if (k != 0) do { |
522 | s1 += *buf++; |
523 | s2 += s1; |
524 | } while (--k > 0); |
525 | s1 %= LZO_BASE; |
526 | s2 %= LZO_BASE; |
527 | } |
528 | return (s2 << 16) | s1; |
529 | } |
530 | |
531 | static FAST_FUNC uint32_t |
532 | lzo_crc32(uint32_t c, const uint8_t* buf, unsigned len) |
533 | { |
534 | //if (buf == NULL) - impossible |
535 | // return 0; |
536 | |
537 | return ~crc32_block_endian0(~c, buf, len, global_crc32_table); |
538 | } |
539 | |
540 | /**********************************************************************/ |
541 | static void init_chksum(chksum_t *ct) |
542 | { |
543 | ct->f_adler32 = ADLER32_INIT_VALUE; |
544 | ct->f_crc32 = CRC32_INIT_VALUE; |
545 | } |
546 | |
547 | static void add_bytes_to_chksum(chksum_t *ct, const void* buf, int cnt) |
548 | { |
549 | /* We need to handle the two checksums at once, because at the |
550 | * beginning of the header, we don't know yet which one we'll |
551 | * eventually need */ |
552 | ct->f_adler32 = lzo_adler32(ct->f_adler32, (const uint8_t*)buf, cnt); |
553 | ct->f_crc32 = lzo_crc32(ct->f_crc32, (const uint8_t*)buf, cnt); |
554 | } |
555 | |
556 | static uint32_t chksum_getresult(chksum_t *ct, const header_t *h) |
557 | { |
558 | return (h->flags & F_H_CRC32) ? ct->f_crc32 : ct->f_adler32; |
559 | } |
560 | |
561 | /**********************************************************************/ |
562 | static uint32_t read32(void) |
563 | { |
564 | uint32_t v; |
565 | xread(0, &v, 4); |
566 | return ntohl(v); |
567 | } |
568 | |
569 | static void write32(uint32_t v) |
570 | { |
571 | v = htonl(v); |
572 | xwrite(1, &v, 4); |
573 | } |
574 | |
575 | static void f_write(const void* buf, int cnt) |
576 | { |
577 | xwrite(1, buf, cnt); |
578 | add_bytes_to_chksum(&G.chksum_out, buf, cnt); |
579 | } |
580 | |
581 | static void f_read(void* buf, int cnt) |
582 | { |
583 | xread(0, buf, cnt); |
584 | add_bytes_to_chksum(&G.chksum_in, buf, cnt); |
585 | } |
586 | |
587 | static int f_read8(void) |
588 | { |
589 | uint8_t v; |
590 | f_read(&v, 1); |
591 | return v; |
592 | } |
593 | |
594 | static void f_write8(uint8_t v) |
595 | { |
596 | f_write(&v, 1); |
597 | } |
598 | |
599 | static unsigned f_read16(void) |
600 | { |
601 | uint16_t v; |
602 | f_read(&v, 2); |
603 | return ntohs(v); |
604 | } |
605 | |
606 | static void f_write16(uint16_t v) |
607 | { |
608 | v = htons(v); |
609 | f_write(&v, 2); |
610 | } |
611 | |
612 | static uint32_t f_read32(void) |
613 | { |
614 | uint32_t v; |
615 | f_read(&v, 4); |
616 | return ntohl(v); |
617 | } |
618 | |
619 | static void f_write32(uint32_t v) |
620 | { |
621 | v = htonl(v); |
622 | f_write(&v, 4); |
623 | } |
624 | |
625 | /**********************************************************************/ |
626 | static int lzo_get_method(header_t *h) |
627 | { |
628 | /* check method */ |
629 | if (h->method == M_LZO1X_1) { |
630 | if (h->level == 0) |
631 | h->level = 3; |
632 | } else if (h->method == M_LZO1X_1_15) { |
633 | if (h->level == 0) |
634 | h->level = 1; |
635 | } else if (h->method == M_LZO1X_999) { |
636 | if (h->level == 0) |
637 | h->level = 9; |
638 | } else |
639 | return -1; /* not a LZO method */ |
640 | |
641 | /* check compression level */ |
642 | if (h->level < 1 || h->level > 9) |
643 | return 15; |
644 | |
645 | return 0; |
646 | } |
647 | |
648 | /**********************************************************************/ |
649 | #define LZO_BLOCK_SIZE (256 * 1024l) |
650 | #define MAX_BLOCK_SIZE (64 * 1024l * 1024l) /* DO NOT CHANGE */ |
651 | |
652 | /* LZO may expand uncompressible data by a small amount */ |
653 | #define MAX_COMPRESSED_SIZE(x) ((x) + (x) / 16 + 64 + 3) |
654 | |
655 | /**********************************************************************/ |
656 | // compress a file |
657 | /**********************************************************************/ |
658 | static NOINLINE int lzo_compress(const header_t *h) |
659 | { |
660 | unsigned block_size = LZO_BLOCK_SIZE; |
661 | int r = 0; /* LZO_E_OK */ |
662 | uint8_t *const b1 = xzalloc(block_size); |
663 | uint8_t *const b2 = xzalloc(MAX_COMPRESSED_SIZE(block_size)); |
664 | unsigned src_len = 0, dst_len = 0; |
665 | uint32_t d_adler32 = ADLER32_INIT_VALUE; |
666 | uint32_t d_crc32 = CRC32_INIT_VALUE; |
667 | int l; |
668 | uint8_t *wrk_mem = NULL; |
669 | |
670 | if (h->method == M_LZO1X_1) |
671 | wrk_mem = xzalloc(LZO1X_1_MEM_COMPRESS); |
672 | else if (h->method == M_LZO1X_1_15) |
673 | wrk_mem = xzalloc(LZO1X_1_15_MEM_COMPRESS); |
674 | else if (h->method == M_LZO1X_999) |
675 | wrk_mem = xzalloc(LZO1X_999_MEM_COMPRESS); |
676 | |
677 | for (;;) { |
678 | /* read a block */ |
679 | l = full_read(0, b1, block_size); |
680 | src_len = (l > 0 ? l : 0); |
681 | |
682 | /* write uncompressed block size */ |
683 | write32(src_len); |
684 | |
685 | /* exit if last block */ |
686 | if (src_len == 0) |
687 | break; |
688 | |
689 | /* compute checksum of uncompressed block */ |
690 | if (h->flags & F_ADLER32_D) |
691 | d_adler32 = lzo_adler32(ADLER32_INIT_VALUE, b1, src_len); |
692 | if (h->flags & F_CRC32_D) |
693 | d_crc32 = lzo_crc32(CRC32_INIT_VALUE, b1, src_len); |
694 | |
695 | /* compress */ |
696 | if (h->method == M_LZO1X_1) |
697 | r = lzo1x_1_compress(b1, src_len, b2, &dst_len, wrk_mem); |
698 | else if (h->method == M_LZO1X_1_15) |
699 | r = lzo1x_1_15_compress(b1, src_len, b2, &dst_len, wrk_mem); |
700 | #if ENABLE_LZOP_COMPR_HIGH |
701 | else if (h->method == M_LZO1X_999) |
702 | r = lzo1x_999_compress_level(b1, src_len, b2, &dst_len, |
703 | wrk_mem, h->level); |
704 | #endif |
705 | else |
706 | bb_error_msg_and_die("internal error"); |
707 | |
708 | if (r != 0) /* not LZO_E_OK */ |
709 | bb_error_msg_and_die("internal error - compression failed"); |
710 | |
711 | /* write compressed block size */ |
712 | if (dst_len < src_len) { |
713 | /* optimize */ |
714 | if (h->method == M_LZO1X_999) { |
715 | unsigned new_len = src_len; |
716 | r = lzo1x_optimize(b2, dst_len, b1, &new_len, NULL); |
717 | if (r != 0 /*LZO_E_OK*/ || new_len != src_len) |
718 | bb_error_msg_and_die("internal error - optimization failed"); |
719 | } |
720 | write32(dst_len); |
721 | } else { |
722 | /* data actually expanded => store data uncompressed */ |
723 | write32(src_len); |
724 | } |
725 | |
726 | /* write checksum of uncompressed block */ |
727 | if (h->flags & F_ADLER32_D) |
728 | write32(d_adler32); |
729 | if (h->flags & F_CRC32_D) |
730 | write32(d_crc32); |
731 | |
732 | if (dst_len < src_len) { |
733 | /* write checksum of compressed block */ |
734 | if (h->flags & F_ADLER32_C) |
735 | write32(lzo_adler32(ADLER32_INIT_VALUE, b2, dst_len)); |
736 | if (h->flags & F_CRC32_C) |
737 | write32(lzo_crc32(CRC32_INIT_VALUE, b2, dst_len)); |
738 | /* write compressed block data */ |
739 | xwrite(1, b2, dst_len); |
740 | } else { |
741 | /* write uncompressed block data */ |
742 | xwrite(1, b1, src_len); |
743 | } |
744 | } |
745 | |
746 | free(wrk_mem); |
747 | free(b1); |
748 | free(b2); |
749 | return 1; |
750 | } |
751 | |
752 | static FAST_FUNC void lzo_check( |
753 | uint32_t init, |
754 | uint8_t* buf, unsigned len, |
755 | uint32_t FAST_FUNC (*fn)(uint32_t, const uint8_t*, unsigned), |
756 | uint32_t ref) |
757 | { |
758 | /* This function, by having the same order of parameters |
759 | * as fn, and by being marked FAST_FUNC (same as fn), |
760 | * saves a dozen bytes of code. |
761 | */ |
762 | uint32_t c = fn(init, buf, len); |
763 | if (c != ref) |
764 | bb_error_msg_and_die("checksum error"); |
765 | } |
766 | |
767 | /**********************************************************************/ |
768 | // decompress a file |
769 | /**********************************************************************/ |
770 | static NOINLINE int lzo_decompress(const header_t *h) |
771 | { |
772 | unsigned block_size = LZO_BLOCK_SIZE; |
773 | int r; |
774 | uint32_t src_len, dst_len; |
775 | uint32_t c_adler32 = ADLER32_INIT_VALUE; |
776 | uint32_t d_adler32 = ADLER32_INIT_VALUE; |
777 | uint32_t c_crc32 = CRC32_INIT_VALUE, d_crc32 = CRC32_INIT_VALUE; |
778 | uint8_t *b1; |
779 | uint32_t mcs_block_size = MAX_COMPRESSED_SIZE(block_size); |
780 | uint8_t *b2 = NULL; |
781 | |
782 | for (;;) { |
783 | uint8_t *dst; |
784 | |
785 | /* read uncompressed block size */ |
786 | dst_len = read32(); |
787 | |
788 | /* exit if last block */ |
789 | if (dst_len == 0) |
790 | break; |
791 | |
792 | /* error if split file */ |
793 | if (dst_len == 0xffffffffL) |
794 | /* should not happen - not yet implemented */ |
795 | bb_error_msg_and_die("this file is a split lzop file"); |
796 | |
797 | if (dst_len > MAX_BLOCK_SIZE) |
798 | bb_error_msg_and_die("corrupted data"); |
799 | |
800 | /* read compressed block size */ |
801 | src_len = read32(); |
802 | if (src_len <= 0 || src_len > dst_len) |
803 | bb_error_msg_and_die("corrupted data"); |
804 | |
805 | if (dst_len > block_size) { |
806 | if (b2) { |
807 | free(b2); |
808 | b2 = NULL; |
809 | } |
810 | block_size = dst_len; |
811 | mcs_block_size = MAX_COMPRESSED_SIZE(block_size); |
812 | } |
813 | |
814 | /* read checksum of uncompressed block */ |
815 | if (h->flags & F_ADLER32_D) |
816 | d_adler32 = read32(); |
817 | if (h->flags & F_CRC32_D) |
818 | d_crc32 = read32(); |
819 | |
820 | /* read checksum of compressed block */ |
821 | if (src_len < dst_len) { |
822 | if (h->flags & F_ADLER32_C) |
823 | c_adler32 = read32(); |
824 | if (h->flags & F_CRC32_C) |
825 | c_crc32 = read32(); |
826 | } |
827 | |
828 | if (b2 == NULL) |
829 | b2 = xzalloc(mcs_block_size); |
830 | /* read the block into the end of our buffer */ |
831 | b1 = b2 + mcs_block_size - src_len; |
832 | xread(0, b1, src_len); |
833 | |
834 | if (src_len < dst_len) { |
835 | unsigned d = dst_len; |
836 | |
837 | if (!(option_mask32 & OPT_F)) { |
838 | /* verify checksum of compressed block */ |
839 | if (h->flags & F_ADLER32_C) |
840 | lzo_check(ADLER32_INIT_VALUE, |
841 | b1, src_len, |
842 | lzo_adler32, c_adler32); |
843 | if (h->flags & F_CRC32_C) |
844 | lzo_check(CRC32_INIT_VALUE, |
845 | b1, src_len, |
846 | lzo_crc32, c_crc32); |
847 | } |
848 | |
849 | /* decompress */ |
850 | // if (option_mask32 & OPT_F) |
851 | // r = lzo1x_decompress(b1, src_len, b2, &d, NULL); |
852 | // else |
853 | r = lzo1x_decompress_safe(b1, src_len, b2, &d, NULL); |
854 | |
855 | if (r != 0 /*LZO_E_OK*/ || dst_len != d) { |
856 | bb_error_msg_and_die("corrupted data"); |
857 | } |
858 | dst = b2; |
859 | } else { |
860 | /* "stored" block => no decompression */ |
861 | dst = b1; |
862 | } |
863 | |
864 | if (!(option_mask32 & OPT_F)) { |
865 | /* verify checksum of uncompressed block */ |
866 | if (h->flags & F_ADLER32_D) |
867 | lzo_check(ADLER32_INIT_VALUE, |
868 | dst, dst_len, |
869 | lzo_adler32, d_adler32); |
870 | if (h->flags & F_CRC32_D) |
871 | lzo_check(CRC32_INIT_VALUE, |
872 | dst, dst_len, |
873 | lzo_crc32, d_crc32); |
874 | } |
875 | |
876 | /* write uncompressed block data */ |
877 | xwrite(1, dst, dst_len); |
878 | } |
879 | |
880 | free(b2); |
881 | return 1; |
882 | } |
883 | |
884 | /**********************************************************************/ |
885 | // lzop file signature (shamelessly borrowed from PNG) |
886 | /**********************************************************************/ |
887 | /* |
888 | * The first nine bytes of a lzop file always contain the following values: |
889 | * |
890 | * 0 1 2 3 4 5 6 7 8 |
891 | * --- --- --- --- --- --- --- --- --- |
892 | * (hex) 89 4c 5a 4f 00 0d 0a 1a 0a |
893 | * (decimal) 137 76 90 79 0 13 10 26 10 |
894 | * (C notation - ASCII) \211 L Z O \0 \r \n \032 \n |
895 | */ |
896 | |
897 | /* (vda) comparison with lzop v1.02rc1 ("lzop -1 <FILE" cmd): |
898 | * Only slight differences in header: |
899 | * -00000000 89 4c 5a 4f 00 0d 0a 1a 0a 10 20 20 20 09 40 02 |
900 | * +00000000 89 4c 5a 4f 00 0d 0a 1a 0a 10 10 20 30 09 40 02 |
901 | * ^^^^^ ^^^^^ |
902 | * version lib_version |
903 | * -00000010 01 03 00 00 0d 00 00 81 a4 49 f7 a6 3f 00 00 00 |
904 | * +00000010 01 03 00 00 01 00 00 00 00 00 00 00 00 00 00 00 |
905 | * ^^^^^^^^^^^ ^^^^^^^^^^^ ^^^^^^^^^^^ |
906 | * flags mode mtime |
907 | * -00000020 00 00 2d 67 04 17 00 04 00 00 00 03 ed ec 9d 6d |
908 | * +00000020 00 00 10 5f 00 c1 00 04 00 00 00 03 ed ec 9d 6d |
909 | * ^^^^^^^^^^^ |
910 | * chksum_out |
911 | * The rest is identical. |
912 | */ |
913 | static const unsigned char lzop_magic[9] ALIGN1 = { |
914 | 0x89, 0x4c, 0x5a, 0x4f, 0x00, 0x0d, 0x0a, 0x1a, 0x0a |
915 | }; |
916 | |
917 | /* This coding is derived from Alexander Lehmann's pngcheck code. */ |
918 | static void check_magic(void) |
919 | { |
920 | unsigned char magic[sizeof(lzop_magic)]; |
921 | xread(0, magic, sizeof(magic)); |
922 | if (memcmp(magic, lzop_magic, sizeof(lzop_magic)) != 0) |
923 | bb_error_msg_and_die("bad magic number"); |
924 | } |
925 | |
926 | /**********************************************************************/ |
927 | // lzop file header |
928 | /**********************************************************************/ |
929 | static void write_header(const header_t *h) |
930 | { |
931 | int l; |
932 | |
933 | xwrite(1, lzop_magic, sizeof(lzop_magic)); |
934 | |
935 | init_chksum(&G.chksum_out); |
936 | |
937 | f_write16(h->version); |
938 | f_write16(h->lib_version); |
939 | f_write16(h->version_needed_to_extract); |
940 | f_write8(h->method); |
941 | f_write8(h->level); |
942 | f_write32(h->flags); |
943 | f_write32(h->mode); |
944 | f_write32(h->mtime); |
945 | f_write32(h->gmtdiff); |
946 | |
947 | l = (int) strlen(h->name); |
948 | f_write8(l); |
949 | if (l) |
950 | f_write(h->name, l); |
951 | |
952 | f_write32(chksum_getresult(&G.chksum_out, h)); |
953 | } |
954 | |
955 | static int read_header(header_t *h) |
956 | { |
957 | int r; |
958 | int l; |
959 | uint32_t checksum; |
960 | |
961 | memset(h, 0, sizeof(*h)); |
962 | h->version_needed_to_extract = 0x0900; /* first lzop version */ |
963 | h->level = 0; |
964 | |
965 | init_chksum(&G.chksum_in); |
966 | |
967 | h->version = f_read16(); |
968 | if (h->version < 0x0900) |
969 | return 3; |
970 | h->lib_version = f_read16(); |
971 | if (h->version >= 0x0940) { |
972 | h->version_needed_to_extract = f_read16(); |
973 | if (h->version_needed_to_extract > LZOP_VERSION) |
974 | return 16; |
975 | if (h->version_needed_to_extract < 0x0900) |
976 | return 3; |
977 | } |
978 | h->method = f_read8(); |
979 | if (h->version >= 0x0940) |
980 | h->level = f_read8(); |
981 | h->flags = f_read32(); |
982 | if (h->flags & F_H_FILTER) |
983 | return 16; /* filter not supported */ |
984 | h->mode = f_read32(); |
985 | h->mtime = f_read32(); |
986 | if (h->version >= 0x0940) |
987 | h->gmtdiff = f_read32(); |
988 | |
989 | l = f_read8(); |
990 | if (l > 0) |
991 | f_read(h->name, l); |
992 | h->name[l] = 0; |
993 | |
994 | checksum = chksum_getresult(&G.chksum_in, h); |
995 | h->header_checksum = f_read32(); |
996 | if (h->header_checksum != checksum) |
997 | return 2; |
998 | |
999 | if (h->method <= 0) |
1000 | return 14; |
1001 | r = lzo_get_method(h); |
1002 | if (r != 0) |
1003 | return r; |
1004 | |
1005 | /* check reserved flags */ |
1006 | if (h->flags & F_RESERVED) |
1007 | return -13; |
1008 | |
1009 | /* skip extra field [not used yet] */ |
1010 | if (h->flags & F_H_EXTRA_FIELD) { |
1011 | uint32_t k; |
1012 | |
1013 | /* note: the checksum also covers the length */ |
1014 | init_chksum(&G.chksum_in); |
1015 | h->extra_field_len = f_read32(); |
1016 | for (k = 0; k < h->extra_field_len; k++) |
1017 | f_read8(); |
1018 | checksum = chksum_getresult(&G.chksum_in, h); |
1019 | h->extra_field_checksum = f_read32(); |
1020 | if (h->extra_field_checksum != checksum) |
1021 | return 3; |
1022 | } |
1023 | |
1024 | return 0; |
1025 | } |
1026 | |
1027 | static void p_header(header_t *h) |
1028 | { |
1029 | int r; |
1030 | |
1031 | r = read_header(h); |
1032 | if (r == 0) |
1033 | return; |
1034 | bb_error_msg_and_die("header_error %d", r); |
1035 | } |
1036 | |
1037 | /**********************************************************************/ |
1038 | // compress |
1039 | /**********************************************************************/ |
1040 | static void lzo_set_method(header_t *h) |
1041 | { |
1042 | int level = 1; |
1043 | |
1044 | if (option_mask32 & OPT_1) { |
1045 | h->method = M_LZO1X_1_15; |
1046 | } else if (option_mask32 & OPT_789) { |
1047 | #if ENABLE_LZOP_COMPR_HIGH |
1048 | h->method = M_LZO1X_999; |
1049 | if (option_mask32 & OPT_7) |
1050 | level = 7; |
1051 | else if (option_mask32 & OPT_8) |
1052 | level = 8; |
1053 | else |
1054 | level = 9; |
1055 | #else |
1056 | bb_error_msg_and_die("high compression not compiled in"); |
1057 | #endif |
1058 | } else { /* levels 2..6 or none (defaults to level 3) */ |
1059 | h->method = M_LZO1X_1; |
1060 | level = 5; /* levels 2-6 are actually the same */ |
1061 | } |
1062 | |
1063 | h->level = level; |
1064 | } |
1065 | |
1066 | static int do_lzo_compress(void) |
1067 | { |
1068 | header_t header; |
1069 | |
1070 | #define h (&header) |
1071 | memset(h, 0, sizeof(*h)); |
1072 | |
1073 | lzo_set_method(h); |
1074 | |
1075 | h->version = (LZOP_VERSION & 0xffff); |
1076 | h->version_needed_to_extract = 0x0940; |
1077 | h->lib_version = lzo_version() & 0xffff; |
1078 | |
1079 | h->flags = (F_OS & F_OS_MASK) | (F_CS & F_CS_MASK); |
1080 | |
1081 | if (!(option_mask32 & OPT_F) || h->method == M_LZO1X_999) { |
1082 | h->flags |= F_ADLER32_D; |
1083 | if (option_mask32 & OPT_C) |
1084 | h->flags |= F_ADLER32_C; |
1085 | } |
1086 | write_header(h); |
1087 | return lzo_compress(h); |
1088 | #undef h |
1089 | } |
1090 | |
1091 | /**********************************************************************/ |
1092 | // decompress |
1093 | /**********************************************************************/ |
1094 | static int do_lzo_decompress(void) |
1095 | { |
1096 | header_t header; |
1097 | |
1098 | check_magic(); |
1099 | p_header(&header); |
1100 | return lzo_decompress(&header); |
1101 | } |
1102 | |
1103 | static char* FAST_FUNC make_new_name_lzop(char *filename, const char *expected_ext UNUSED_PARAM) |
1104 | { |
1105 | if (option_mask32 & OPT_DECOMPRESS) { |
1106 | char *extension = strrchr(filename, '.'); |
1107 | if (!extension || strcmp(extension + 1, "lzo") != 0) |
1108 | return xasprintf("%s.out", filename); |
1109 | *extension = '\0'; |
1110 | return filename; |
1111 | } |
1112 | return xasprintf("%s.lzo", filename); |
1113 | } |
1114 | |
1115 | static IF_DESKTOP(long long) int FAST_FUNC pack_lzop(transformer_state_t *xstate UNUSED_PARAM) |
1116 | { |
1117 | if (option_mask32 & OPT_DECOMPRESS) |
1118 | return do_lzo_decompress(); |
1119 | return do_lzo_compress(); |
1120 | } |
1121 | |
1122 | int lzop_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
1123 | int lzop_main(int argc UNUSED_PARAM, char **argv) |
1124 | { |
1125 | getopt32(argv, OPTION_STRING); |
1126 | argv += optind; |
1127 | /* lzopcat? */ |
1128 | if (ENABLE_LZOPCAT && applet_name[4] == 'c') |
1129 | option_mask32 |= (OPT_STDOUT | OPT_DECOMPRESS); |
1130 | /* unlzop? */ |
1131 | if (ENABLE_UNLZOP && applet_name[4] == 'o') |
1132 | option_mask32 |= OPT_DECOMPRESS; |
1133 | |
1134 | global_crc32_table = crc32_filltable(NULL, 0); |
1135 | return bbunpack(argv, pack_lzop, make_new_name_lzop, /*unused:*/ NULL); |
1136 | } |
1137 |