1 /*
2  * This file uses XZ Embedded library code which is written
3  * by Lasse Collin <lasse.collin@tukaani.org>
4  * and Igor Pavlov <http://7-zip.org/>
5  *
6  * See README file in unxz/ directory for more information.
7  *
8  * This file is:
9  * Copyright (C) 2010 Denys Vlasenko <vda.linux@googlemail.com>
10  * Licensed under GPLv2, see file LICENSE in this source tree.
11  */
12 #include "libbb.h"
13 #include "bb_archive.h"
14 
15 #define XZ_FUNC FAST_FUNC
16 #define XZ_EXTERN static
17 
18 #define XZ_DEC_DYNALLOC
19 
20 /* Skip check (rather than fail) of unsupported hash functions */
21 #define XZ_DEC_ANY_CHECK  1
22 
23 /* We use our own crc32 function */
24 #define XZ_INTERNAL_CRC32 0
xz_crc32(const uint8_t * buf,size_t size,uint32_t crc)25 static uint32_t xz_crc32(const uint8_t *buf, size_t size, uint32_t crc)
26 {
27 	return ~crc32_block_endian0(~crc, buf, size, global_crc32_table);
28 }
29 
30 /* We use arch-optimized unaligned fixed-endian accessors.
31  * They have been moved to libbb (proved to be useful elsewhere as well),
32  * just check that we have them defined:
33  */
34 #if !defined(get_unaligned_le32) \
35  || !defined(get_unaligned_be32) \
36  || !defined(put_unaligned_le32) \
37  || !defined(put_unaligned_be32)
38 # error get_unaligned_le32 accessors are not defined
39 #endif
40 
41 #include "unxz/xz_dec_bcj.c"
42 #include "unxz/xz_dec_lzma2.c"
43 #include "unxz/xz_dec_stream.c"
44 
IF_DESKTOP(long long)45 IF_DESKTOP(long long) int FAST_FUNC
46 unpack_xz_stream(transformer_state_t *xstate)
47 {
48 	enum xz_ret xz_result;
49 	struct xz_buf iobuf;
50 	struct xz_dec *state;
51 	unsigned char *membuf;
52 	IF_DESKTOP(long long) int total = 0;
53 
54 	if (!global_crc32_table)
55 		global_crc32_new_table_le();
56 
57 	memset(&iobuf, 0, sizeof(iobuf));
58 	membuf = xmalloc(2 * BUFSIZ);
59 	iobuf.in = membuf;
60 	iobuf.out = membuf + BUFSIZ;
61 	iobuf.out_size = BUFSIZ;
62 
63 	if (!xstate || xstate->signature_skipped) {
64 		/* Preload XZ file signature */
65 		strcpy((char*)membuf, HEADER_MAGIC);
66 		iobuf.in_size = HEADER_MAGIC_SIZE;
67 	} /* else: let xz code read & check it */
68 
69 	/* Limit memory usage to about 64 MiB. */
70 	state = xz_dec_init(XZ_DYNALLOC, 64*1024*1024);
71 
72 	xz_result = X_OK;
73 	while (1) {
74 		if (iobuf.in_pos == iobuf.in_size) {
75 			int rd = safe_read(xstate->src_fd, membuf, BUFSIZ);
76 			if (rd < 0) {
77 				bb_simple_error_msg(bb_msg_read_error);
78 				total = -1;
79 				break;
80 			}
81 			if (rd == 0 && xz_result == XZ_STREAM_END)
82 				break;
83 			iobuf.in_size = rd;
84 			iobuf.in_pos = 0;
85 		}
86 		if (xz_result == XZ_STREAM_END) {
87 			/*
88 			 * Try to start decoding next concatenated stream.
89 			 * Stream padding must always be a multiple of four
90 			 * bytes to preserve four-byte alignment. To keep the
91 			 * code slightly smaller, we aren't as strict here as
92 			 * the .xz spec requires. We just skip all zero-bytes
93 			 * without checking the alignment and thus can accept
94 			 * files that aren't valid, e.g. the XZ utils test
95 			 * files bad-0pad-empty.xz and bad-0catpad-empty.xz.
96 			 */
97 			do {
98 				if (membuf[iobuf.in_pos] != 0) {
99 					/* There is more data, but is it XZ data?
100 					 * Example: dpkg-deb -f busybox_1.30.1-4_amd64.deb
101 					 * reads control.tar.xz "control" file
102 					 * inside the ar archive, but tar.xz
103 					 * extraction code reaches end of xz data,
104 					 * reached this code and reads the beginning
105 					 * of data.tar.xz's ar header, which isn't xz data,
106 					 * and prints "corrupted data".
107 					 * The correct solution is to not read
108 					 * past nested archive (to simulate EOF).
109 					 * This is a workaround:
110 					 */
111 					if (membuf[iobuf.in_pos] != 0xfd) {
112 						/* It's definitely not a xz signature
113 						 * (which is 0xfd,"7zXZ",0x00).
114 						 */
115 						goto end;
116 					}
117 					xz_dec_reset(state);
118 					goto do_run;
119 				}
120 				iobuf.in_pos++;
121 			} while (iobuf.in_pos < iobuf.in_size);
122 		}
123  do_run:
124 //		bb_error_msg(">in pos:%d size:%d out pos:%d size:%d",
125 //				iobuf.in_pos, iobuf.in_size, iobuf.out_pos, iobuf.out_size);
126 		xz_result = xz_dec_run(state, &iobuf);
127 //		bb_error_msg("<in pos:%d size:%d out pos:%d size:%d r:%d",
128 //				iobuf.in_pos, iobuf.in_size, iobuf.out_pos, iobuf.out_size, xz_result);
129 		if (iobuf.out_pos) {
130 			xtransformer_write(xstate, iobuf.out, iobuf.out_pos);
131 			IF_DESKTOP(total += iobuf.out_pos;)
132 			iobuf.out_pos = 0;
133 		}
134 		if (xz_result == XZ_STREAM_END) {
135 			/*
136 			 * Can just "break;" here, if not for concatenated
137 			 * .xz streams.
138 			 * Checking for padding may require buffer
139 			 * replenishment. Can't do it here.
140 			 */
141 			continue;
142 		}
143 		if (xz_result != XZ_OK && xz_result != XZ_UNSUPPORTED_CHECK) {
144 			bb_simple_error_msg("corrupted data");
145 			total = -1;
146 			break;
147 		}
148 	}
149  end:
150 	xz_dec_end(state);
151 	free(membuf);
152 
153 	return total;
154 }
155