ce188d4d |
1 | /* XzIn.c - Xz input\r |
2 | 2015-11-08 : Igor Pavlov : Public domain */\r |
3 | \r |
4 | #include "Precomp.h"\r |
5 | \r |
6 | #include <string.h>\r |
7 | \r |
8 | #include "7zCrc.h"\r |
9 | #include "CpuArch.h"\r |
10 | #include "Xz.h"\r |
11 | \r |
12 | SRes Xz_ReadHeader(CXzStreamFlags *p, ISeqInStream *inStream)\r |
13 | {\r |
14 | Byte sig[XZ_STREAM_HEADER_SIZE];\r |
15 | RINOK(SeqInStream_Read2(inStream, sig, XZ_STREAM_HEADER_SIZE, SZ_ERROR_NO_ARCHIVE));\r |
16 | if (memcmp(sig, XZ_SIG, XZ_SIG_SIZE) != 0)\r |
17 | return SZ_ERROR_NO_ARCHIVE;\r |
18 | return Xz_ParseHeader(p, sig);\r |
19 | }\r |
20 | \r |
21 | #define READ_VARINT_AND_CHECK(buf, pos, size, res) \\r |
22 | { unsigned s = Xz_ReadVarInt(buf + pos, size - pos, res); \\r |
23 | if (s == 0) return SZ_ERROR_ARCHIVE; pos += s; }\r |
24 | \r |
25 | SRes XzBlock_ReadHeader(CXzBlock *p, ISeqInStream *inStream, Bool *isIndex, UInt32 *headerSizeRes)\r |
26 | {\r |
27 | Byte header[XZ_BLOCK_HEADER_SIZE_MAX];\r |
28 | unsigned headerSize;\r |
29 | *headerSizeRes = 0;\r |
30 | RINOK(SeqInStream_ReadByte(inStream, &header[0]));\r |
31 | headerSize = ((unsigned)header[0] << 2) + 4;\r |
32 | if (headerSize == 0)\r |
33 | {\r |
34 | *headerSizeRes = 1;\r |
35 | *isIndex = True;\r |
36 | return SZ_OK;\r |
37 | }\r |
38 | \r |
39 | *isIndex = False;\r |
40 | *headerSizeRes = headerSize;\r |
41 | RINOK(SeqInStream_Read(inStream, header + 1, headerSize - 1));\r |
42 | return XzBlock_Parse(p, header);\r |
43 | }\r |
44 | \r |
45 | #define ADD_SIZE_CHECH(size, val) \\r |
46 | { UInt64 newSize = size + (val); if (newSize < size) return XZ_SIZE_OVERFLOW; size = newSize; }\r |
47 | \r |
48 | UInt64 Xz_GetUnpackSize(const CXzStream *p)\r |
49 | {\r |
50 | UInt64 size = 0;\r |
51 | size_t i;\r |
52 | for (i = 0; i < p->numBlocks; i++)\r |
53 | ADD_SIZE_CHECH(size, p->blocks[i].unpackSize);\r |
54 | return size;\r |
55 | }\r |
56 | \r |
57 | UInt64 Xz_GetPackSize(const CXzStream *p)\r |
58 | {\r |
59 | UInt64 size = 0;\r |
60 | size_t i;\r |
61 | for (i = 0; i < p->numBlocks; i++)\r |
62 | ADD_SIZE_CHECH(size, (p->blocks[i].totalSize + 3) & ~(UInt64)3);\r |
63 | return size;\r |
64 | }\r |
65 | \r |
66 | /*\r |
67 | SRes XzBlock_ReadFooter(CXzBlock *p, CXzStreamFlags f, ISeqInStream *inStream)\r |
68 | {\r |
69 | return SeqInStream_Read(inStream, p->check, XzFlags_GetCheckSize(f));\r |
70 | }\r |
71 | */\r |
72 | \r |
73 | static SRes Xz_ReadIndex2(CXzStream *p, const Byte *buf, size_t size, ISzAlloc *alloc)\r |
74 | {\r |
75 | size_t numBlocks, pos = 1;\r |
76 | UInt32 crc;\r |
77 | \r |
78 | if (size < 5 || buf[0] != 0)\r |
79 | return SZ_ERROR_ARCHIVE;\r |
80 | \r |
81 | size -= 4;\r |
82 | crc = CrcCalc(buf, size);\r |
83 | if (crc != GetUi32(buf + size))\r |
84 | return SZ_ERROR_ARCHIVE;\r |
85 | \r |
86 | {\r |
87 | UInt64 numBlocks64;\r |
88 | READ_VARINT_AND_CHECK(buf, pos, size, &numBlocks64);\r |
89 | numBlocks = (size_t)numBlocks64;\r |
90 | if (numBlocks != numBlocks64 || numBlocks * 2 > size)\r |
91 | return SZ_ERROR_ARCHIVE;\r |
92 | }\r |
93 | \r |
94 | Xz_Free(p, alloc);\r |
95 | if (numBlocks != 0)\r |
96 | {\r |
97 | size_t i;\r |
98 | p->numBlocks = numBlocks;\r |
99 | p->numBlocksAllocated = numBlocks;\r |
100 | p->blocks = alloc->Alloc(alloc, sizeof(CXzBlockSizes) * numBlocks);\r |
101 | if (p->blocks == 0)\r |
102 | return SZ_ERROR_MEM;\r |
103 | for (i = 0; i < numBlocks; i++)\r |
104 | {\r |
105 | CXzBlockSizes *block = &p->blocks[i];\r |
106 | READ_VARINT_AND_CHECK(buf, pos, size, &block->totalSize);\r |
107 | READ_VARINT_AND_CHECK(buf, pos, size, &block->unpackSize);\r |
108 | if (block->totalSize == 0)\r |
109 | return SZ_ERROR_ARCHIVE;\r |
110 | }\r |
111 | }\r |
112 | while ((pos & 3) != 0)\r |
113 | if (buf[pos++] != 0)\r |
114 | return SZ_ERROR_ARCHIVE;\r |
115 | return (pos == size) ? SZ_OK : SZ_ERROR_ARCHIVE;\r |
116 | }\r |
117 | \r |
118 | static SRes Xz_ReadIndex(CXzStream *p, ILookInStream *stream, UInt64 indexSize, ISzAlloc *alloc)\r |
119 | {\r |
120 | SRes res;\r |
121 | size_t size;\r |
122 | Byte *buf;\r |
123 | if (indexSize > ((UInt32)1 << 31))\r |
124 | return SZ_ERROR_UNSUPPORTED;\r |
125 | size = (size_t)indexSize;\r |
126 | if (size != indexSize)\r |
127 | return SZ_ERROR_UNSUPPORTED;\r |
128 | buf = alloc->Alloc(alloc, size);\r |
129 | if (buf == 0)\r |
130 | return SZ_ERROR_MEM;\r |
131 | res = LookInStream_Read2(stream, buf, size, SZ_ERROR_UNSUPPORTED);\r |
132 | if (res == SZ_OK)\r |
133 | res = Xz_ReadIndex2(p, buf, size, alloc);\r |
134 | alloc->Free(alloc, buf);\r |
135 | return res;\r |
136 | }\r |
137 | \r |
138 | static SRes LookInStream_SeekRead_ForArc(ILookInStream *stream, UInt64 offset, void *buf, size_t size)\r |
139 | {\r |
140 | RINOK(LookInStream_SeekTo(stream, offset));\r |
141 | return LookInStream_Read(stream, buf, size);\r |
142 | /* return LookInStream_Read2(stream, buf, size, SZ_ERROR_NO_ARCHIVE); */\r |
143 | }\r |
144 | \r |
145 | static SRes Xz_ReadBackward(CXzStream *p, ILookInStream *stream, Int64 *startOffset, ISzAlloc *alloc)\r |
146 | {\r |
147 | UInt64 indexSize;\r |
148 | Byte buf[XZ_STREAM_FOOTER_SIZE];\r |
149 | UInt64 pos = *startOffset;\r |
150 | \r |
151 | if ((pos & 3) != 0 || pos < XZ_STREAM_FOOTER_SIZE)\r |
152 | return SZ_ERROR_NO_ARCHIVE;\r |
153 | \r |
154 | pos -= XZ_STREAM_FOOTER_SIZE;\r |
155 | RINOK(LookInStream_SeekRead_ForArc(stream, pos, buf, XZ_STREAM_FOOTER_SIZE));\r |
156 | \r |
157 | if (memcmp(buf + 10, XZ_FOOTER_SIG, XZ_FOOTER_SIG_SIZE) != 0)\r |
158 | {\r |
159 | UInt32 total = 0;\r |
160 | pos += XZ_STREAM_FOOTER_SIZE;\r |
161 | \r |
162 | for (;;)\r |
163 | {\r |
164 | size_t i;\r |
165 | #define TEMP_BUF_SIZE (1 << 10)\r |
166 | Byte temp[TEMP_BUF_SIZE];\r |
167 | \r |
168 | i = (pos > TEMP_BUF_SIZE) ? TEMP_BUF_SIZE : (size_t)pos;\r |
169 | pos -= i;\r |
170 | RINOK(LookInStream_SeekRead_ForArc(stream, pos, temp, i));\r |
171 | total += (UInt32)i;\r |
172 | for (; i != 0; i--)\r |
173 | if (temp[i - 1] != 0)\r |
174 | break;\r |
175 | if (i != 0)\r |
176 | {\r |
177 | if ((i & 3) != 0)\r |
178 | return SZ_ERROR_NO_ARCHIVE;\r |
179 | pos += i;\r |
180 | break;\r |
181 | }\r |
182 | if (pos < XZ_STREAM_FOOTER_SIZE || total > (1 << 16))\r |
183 | return SZ_ERROR_NO_ARCHIVE;\r |
184 | }\r |
185 | \r |
186 | if (pos < XZ_STREAM_FOOTER_SIZE)\r |
187 | return SZ_ERROR_NO_ARCHIVE;\r |
188 | pos -= XZ_STREAM_FOOTER_SIZE;\r |
189 | RINOK(LookInStream_SeekRead_ForArc(stream, pos, buf, XZ_STREAM_FOOTER_SIZE));\r |
190 | if (memcmp(buf + 10, XZ_FOOTER_SIG, XZ_FOOTER_SIG_SIZE) != 0)\r |
191 | return SZ_ERROR_NO_ARCHIVE;\r |
192 | }\r |
193 | \r |
194 | p->flags = (CXzStreamFlags)GetBe16(buf + 8);\r |
195 | \r |
196 | if (!XzFlags_IsSupported(p->flags))\r |
197 | return SZ_ERROR_UNSUPPORTED;\r |
198 | \r |
199 | if (GetUi32(buf) != CrcCalc(buf + 4, 6))\r |
200 | return SZ_ERROR_ARCHIVE;\r |
201 | \r |
202 | indexSize = ((UInt64)GetUi32(buf + 4) + 1) << 2;\r |
203 | \r |
204 | if (pos < indexSize)\r |
205 | return SZ_ERROR_ARCHIVE;\r |
206 | \r |
207 | pos -= indexSize;\r |
208 | RINOK(LookInStream_SeekTo(stream, pos));\r |
209 | RINOK(Xz_ReadIndex(p, stream, indexSize, alloc));\r |
210 | \r |
211 | {\r |
212 | UInt64 totalSize = Xz_GetPackSize(p);\r |
213 | if (totalSize == XZ_SIZE_OVERFLOW\r |
214 | || totalSize >= ((UInt64)1 << 63)\r |
215 | || pos < totalSize + XZ_STREAM_HEADER_SIZE)\r |
216 | return SZ_ERROR_ARCHIVE;\r |
217 | pos -= (totalSize + XZ_STREAM_HEADER_SIZE);\r |
218 | RINOK(LookInStream_SeekTo(stream, pos));\r |
219 | *startOffset = pos;\r |
220 | }\r |
221 | {\r |
222 | CXzStreamFlags headerFlags;\r |
223 | CSecToRead secToRead;\r |
224 | SecToRead_CreateVTable(&secToRead);\r |
225 | secToRead.realStream = stream;\r |
226 | \r |
227 | RINOK(Xz_ReadHeader(&headerFlags, &secToRead.s));\r |
228 | return (p->flags == headerFlags) ? SZ_OK : SZ_ERROR_ARCHIVE;\r |
229 | }\r |
230 | }\r |
231 | \r |
232 | \r |
233 | /* ---------- Xz Streams ---------- */\r |
234 | \r |
235 | void Xzs_Construct(CXzs *p)\r |
236 | {\r |
237 | p->num = p->numAllocated = 0;\r |
238 | p->streams = 0;\r |
239 | }\r |
240 | \r |
241 | void Xzs_Free(CXzs *p, ISzAlloc *alloc)\r |
242 | {\r |
243 | size_t i;\r |
244 | for (i = 0; i < p->num; i++)\r |
245 | Xz_Free(&p->streams[i], alloc);\r |
246 | alloc->Free(alloc, p->streams);\r |
247 | p->num = p->numAllocated = 0;\r |
248 | p->streams = 0;\r |
249 | }\r |
250 | \r |
251 | UInt64 Xzs_GetNumBlocks(const CXzs *p)\r |
252 | {\r |
253 | UInt64 num = 0;\r |
254 | size_t i;\r |
255 | for (i = 0; i < p->num; i++)\r |
256 | num += p->streams[i].numBlocks;\r |
257 | return num;\r |
258 | }\r |
259 | \r |
260 | UInt64 Xzs_GetUnpackSize(const CXzs *p)\r |
261 | {\r |
262 | UInt64 size = 0;\r |
263 | size_t i;\r |
264 | for (i = 0; i < p->num; i++)\r |
265 | ADD_SIZE_CHECH(size, Xz_GetUnpackSize(&p->streams[i]));\r |
266 | return size;\r |
267 | }\r |
268 | \r |
269 | /*\r |
270 | UInt64 Xzs_GetPackSize(const CXzs *p)\r |
271 | {\r |
272 | UInt64 size = 0;\r |
273 | size_t i;\r |
274 | for (i = 0; i < p->num; i++)\r |
275 | ADD_SIZE_CHECH(size, Xz_GetTotalSize(&p->streams[i]));\r |
276 | return size;\r |
277 | }\r |
278 | */\r |
279 | \r |
280 | SRes Xzs_ReadBackward(CXzs *p, ILookInStream *stream, Int64 *startOffset, ICompressProgress *progress, ISzAlloc *alloc)\r |
281 | {\r |
282 | Int64 endOffset = 0;\r |
283 | RINOK(stream->Seek(stream, &endOffset, SZ_SEEK_END));\r |
284 | *startOffset = endOffset;\r |
285 | for (;;)\r |
286 | {\r |
287 | CXzStream st;\r |
288 | SRes res;\r |
289 | Xz_Construct(&st);\r |
290 | res = Xz_ReadBackward(&st, stream, startOffset, alloc);\r |
291 | st.startOffset = *startOffset;\r |
292 | RINOK(res);\r |
293 | if (p->num == p->numAllocated)\r |
294 | {\r |
295 | size_t newNum = p->num + p->num / 4 + 1;\r |
296 | Byte *data = (Byte *)alloc->Alloc(alloc, newNum * sizeof(CXzStream));\r |
297 | if (data == 0)\r |
298 | return SZ_ERROR_MEM;\r |
299 | p->numAllocated = newNum;\r |
300 | if (p->num != 0)\r |
301 | memcpy(data, p->streams, p->num * sizeof(CXzStream));\r |
302 | alloc->Free(alloc, p->streams);\r |
303 | p->streams = (CXzStream *)data;\r |
304 | }\r |
305 | p->streams[p->num++] = st;\r |
306 | if (*startOffset == 0)\r |
307 | break;\r |
308 | RINOK(LookInStream_SeekTo(stream, *startOffset));\r |
309 | if (progress && progress->Progress(progress, endOffset - *startOffset, (UInt64)(Int64)-1) != SZ_OK)\r |
310 | return SZ_ERROR_PROGRESS;\r |
311 | }\r |
312 | return SZ_OK;\r |
313 | }\r |