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