Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
stenzek
GitHub Repository: stenzek/duckstation
Path: blob/master/dep/lzma/src/XzIn.c
4253 views
1
/* XzIn.c - Xz input
2
2023-09-07 : 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, ISeqInStreamPtr inStream)
19
{
20
Byte sig[XZ_STREAM_HEADER_SIZE];
21
size_t processedSize = XZ_STREAM_HEADER_SIZE;
22
RINOK(SeqInStream_ReadMax(inStream, sig, &processedSize))
23
if (processedSize != XZ_STREAM_HEADER_SIZE
24
|| memcmp(sig, XZ_SIG, XZ_SIG_SIZE) != 0)
25
return SZ_ERROR_NO_ARCHIVE;
26
return Xz_ParseHeader(p, sig);
27
}
28
29
#define READ_VARINT_AND_CHECK(buf, pos, size, res) \
30
{ const unsigned s = Xz_ReadVarInt(buf + pos, size - pos, res); \
31
if (s == 0) return SZ_ERROR_ARCHIVE; \
32
pos += s; }
33
34
SRes XzBlock_ReadHeader(CXzBlock *p, ISeqInStreamPtr inStream, BoolInt *isIndex, UInt32 *headerSizeRes)
35
{
36
Byte header[XZ_BLOCK_HEADER_SIZE_MAX];
37
unsigned headerSize;
38
*headerSizeRes = 0;
39
RINOK(SeqInStream_ReadByte(inStream, &header[0]))
40
headerSize = header[0];
41
if (headerSize == 0)
42
{
43
*headerSizeRes = 1;
44
*isIndex = True;
45
return SZ_OK;
46
}
47
48
*isIndex = False;
49
headerSize = (headerSize << 2) + 4;
50
*headerSizeRes = (UInt32)headerSize;
51
{
52
size_t processedSize = headerSize - 1;
53
RINOK(SeqInStream_ReadMax(inStream, header + 1, &processedSize))
54
if (processedSize != headerSize - 1)
55
return SZ_ERROR_INPUT_EOF;
56
}
57
return XzBlock_Parse(p, header);
58
}
59
60
#define ADD_SIZE_CHECK(size, val) \
61
{ const UInt64 newSize = size + (val); if (newSize < size) return XZ_SIZE_OVERFLOW; size = newSize; }
62
63
UInt64 Xz_GetUnpackSize(const CXzStream *p)
64
{
65
UInt64 size = 0;
66
size_t i;
67
for (i = 0; i < p->numBlocks; i++)
68
{
69
ADD_SIZE_CHECK(size, p->blocks[i].unpackSize)
70
}
71
return size;
72
}
73
74
UInt64 Xz_GetPackSize(const CXzStream *p)
75
{
76
UInt64 size = 0;
77
size_t i;
78
for (i = 0; i < p->numBlocks; i++)
79
{
80
ADD_SIZE_CHECK(size, (p->blocks[i].totalSize + 3) & ~(UInt64)3)
81
}
82
return size;
83
}
84
85
/*
86
SRes XzBlock_ReadFooter(CXzBlock *p, CXzStreamFlags f, ISeqInStreamPtr inStream)
87
{
88
return SeqInStream_Read(inStream, p->check, XzFlags_GetCheckSize(f));
89
}
90
*/
91
92
static SRes Xz_ReadIndex2(CXzStream *p, const Byte *buf, size_t size, ISzAllocPtr alloc)
93
{
94
size_t numBlocks, pos = 1;
95
UInt32 crc;
96
97
if (size < 5 || buf[0] != 0)
98
return SZ_ERROR_ARCHIVE;
99
100
size -= 4;
101
crc = CrcCalc(buf, size);
102
if (crc != GetUi32(buf + size))
103
return SZ_ERROR_ARCHIVE;
104
105
{
106
UInt64 numBlocks64;
107
READ_VARINT_AND_CHECK(buf, pos, size, &numBlocks64)
108
numBlocks = (size_t)numBlocks64;
109
if (numBlocks != numBlocks64 || numBlocks * 2 > size)
110
return SZ_ERROR_ARCHIVE;
111
}
112
113
Xz_Free(p, alloc);
114
if (numBlocks != 0)
115
{
116
size_t i;
117
p->numBlocks = numBlocks;
118
p->blocks = (CXzBlockSizes *)ISzAlloc_Alloc(alloc, sizeof(CXzBlockSizes) * numBlocks);
119
if (!p->blocks)
120
return SZ_ERROR_MEM;
121
for (i = 0; i < numBlocks; i++)
122
{
123
CXzBlockSizes *block = &p->blocks[i];
124
READ_VARINT_AND_CHECK(buf, pos, size, &block->totalSize)
125
READ_VARINT_AND_CHECK(buf, pos, size, &block->unpackSize)
126
if (block->totalSize == 0)
127
return SZ_ERROR_ARCHIVE;
128
}
129
}
130
while ((pos & 3) != 0)
131
if (buf[pos++] != 0)
132
return SZ_ERROR_ARCHIVE;
133
return (pos == size) ? SZ_OK : SZ_ERROR_ARCHIVE;
134
}
135
136
static SRes Xz_ReadIndex(CXzStream *p, ILookInStreamPtr stream, UInt64 indexSize, ISzAllocPtr alloc)
137
{
138
SRes res;
139
size_t size;
140
Byte *buf;
141
if (indexSize > ((UInt32)1 << 31))
142
return SZ_ERROR_UNSUPPORTED;
143
size = (size_t)indexSize;
144
if (size != indexSize)
145
return SZ_ERROR_UNSUPPORTED;
146
buf = (Byte *)ISzAlloc_Alloc(alloc, size);
147
if (!buf)
148
return SZ_ERROR_MEM;
149
res = LookInStream_Read2(stream, buf, size, SZ_ERROR_UNSUPPORTED);
150
if (res == SZ_OK)
151
res = Xz_ReadIndex2(p, buf, size, alloc);
152
ISzAlloc_Free(alloc, buf);
153
return res;
154
}
155
156
static SRes LookInStream_SeekRead_ForArc(ILookInStreamPtr stream, UInt64 offset, void *buf, size_t size)
157
{
158
RINOK(LookInStream_SeekTo(stream, offset))
159
return LookInStream_Read(stream, buf, size);
160
/* return LookInStream_Read2(stream, buf, size, SZ_ERROR_NO_ARCHIVE); */
161
}
162
163
static SRes Xz_ReadBackward(CXzStream *p, ILookInStreamPtr stream, Int64 *startOffset, ISzAllocPtr alloc)
164
{
165
UInt64 indexSize;
166
Byte buf[XZ_STREAM_FOOTER_SIZE];
167
UInt64 pos = (UInt64)*startOffset;
168
169
if ((pos & 3) != 0 || pos < XZ_STREAM_FOOTER_SIZE)
170
return SZ_ERROR_NO_ARCHIVE;
171
172
pos -= XZ_STREAM_FOOTER_SIZE;
173
RINOK(LookInStream_SeekRead_ForArc(stream, pos, buf, XZ_STREAM_FOOTER_SIZE))
174
175
if (!XZ_FOOTER_SIG_CHECK(buf + 10))
176
{
177
UInt32 total = 0;
178
pos += XZ_STREAM_FOOTER_SIZE;
179
180
for (;;)
181
{
182
size_t i;
183
#define TEMP_BUF_SIZE (1 << 10)
184
Byte temp[TEMP_BUF_SIZE];
185
186
i = (pos > TEMP_BUF_SIZE) ? TEMP_BUF_SIZE : (size_t)pos;
187
pos -= i;
188
RINOK(LookInStream_SeekRead_ForArc(stream, pos, temp, i))
189
total += (UInt32)i;
190
for (; i != 0; i--)
191
if (temp[i - 1] != 0)
192
break;
193
if (i != 0)
194
{
195
if ((i & 3) != 0)
196
return SZ_ERROR_NO_ARCHIVE;
197
pos += i;
198
break;
199
}
200
if (pos < XZ_STREAM_FOOTER_SIZE || total > (1 << 16))
201
return SZ_ERROR_NO_ARCHIVE;
202
}
203
204
if (pos < XZ_STREAM_FOOTER_SIZE)
205
return SZ_ERROR_NO_ARCHIVE;
206
pos -= XZ_STREAM_FOOTER_SIZE;
207
RINOK(LookInStream_SeekRead_ForArc(stream, pos, buf, XZ_STREAM_FOOTER_SIZE))
208
if (!XZ_FOOTER_SIG_CHECK(buf + 10))
209
return SZ_ERROR_NO_ARCHIVE;
210
}
211
212
p->flags = (CXzStreamFlags)GetBe16(buf + 8);
213
214
if (!XzFlags_IsSupported(p->flags))
215
return SZ_ERROR_UNSUPPORTED;
216
217
{
218
/* to eliminate GCC 6.3 warning:
219
dereferencing type-punned pointer will break strict-aliasing rules */
220
const Byte *buf_ptr = buf;
221
if (GetUi32(buf_ptr) != CrcCalc(buf + 4, 6))
222
return SZ_ERROR_ARCHIVE;
223
}
224
225
indexSize = ((UInt64)GetUi32(buf + 4) + 1) << 2;
226
227
if (pos < indexSize)
228
return SZ_ERROR_ARCHIVE;
229
230
pos -= indexSize;
231
RINOK(LookInStream_SeekTo(stream, pos))
232
RINOK(Xz_ReadIndex(p, stream, indexSize, alloc))
233
234
{
235
UInt64 totalSize = Xz_GetPackSize(p);
236
if (totalSize == XZ_SIZE_OVERFLOW
237
|| totalSize >= ((UInt64)1 << 63)
238
|| pos < totalSize + XZ_STREAM_HEADER_SIZE)
239
return SZ_ERROR_ARCHIVE;
240
pos -= (totalSize + XZ_STREAM_HEADER_SIZE);
241
RINOK(LookInStream_SeekTo(stream, pos))
242
*startOffset = (Int64)pos;
243
}
244
{
245
CXzStreamFlags headerFlags;
246
CSecToRead secToRead;
247
SecToRead_CreateVTable(&secToRead);
248
secToRead.realStream = stream;
249
250
RINOK(Xz_ReadHeader(&headerFlags, &secToRead.vt))
251
return (p->flags == headerFlags) ? SZ_OK : SZ_ERROR_ARCHIVE;
252
}
253
}
254
255
256
/* ---------- Xz Streams ---------- */
257
258
void Xzs_Construct(CXzs *p)
259
{
260
p->num = p->numAllocated = 0;
261
p->streams = 0;
262
}
263
264
void Xzs_Free(CXzs *p, ISzAllocPtr alloc)
265
{
266
size_t i;
267
for (i = 0; i < p->num; i++)
268
Xz_Free(&p->streams[i], alloc);
269
ISzAlloc_Free(alloc, p->streams);
270
p->num = p->numAllocated = 0;
271
p->streams = 0;
272
}
273
274
UInt64 Xzs_GetNumBlocks(const CXzs *p)
275
{
276
UInt64 num = 0;
277
size_t i;
278
for (i = 0; i < p->num; i++)
279
num += p->streams[i].numBlocks;
280
return num;
281
}
282
283
UInt64 Xzs_GetUnpackSize(const CXzs *p)
284
{
285
UInt64 size = 0;
286
size_t i;
287
for (i = 0; i < p->num; i++)
288
{
289
ADD_SIZE_CHECK(size, Xz_GetUnpackSize(&p->streams[i]))
290
}
291
return size;
292
}
293
294
/*
295
UInt64 Xzs_GetPackSize(const CXzs *p)
296
{
297
UInt64 size = 0;
298
size_t i;
299
for (i = 0; i < p->num; i++)
300
{
301
ADD_SIZE_CHECK(size, Xz_GetTotalSize(&p->streams[i]))
302
}
303
return size;
304
}
305
*/
306
307
SRes Xzs_ReadBackward(CXzs *p, ILookInStreamPtr stream, Int64 *startOffset, ICompressProgressPtr progress, ISzAllocPtr alloc)
308
{
309
Int64 endOffset = 0;
310
RINOK(ILookInStream_Seek(stream, &endOffset, SZ_SEEK_END))
311
*startOffset = endOffset;
312
for (;;)
313
{
314
CXzStream st;
315
SRes res;
316
Xz_Construct(&st);
317
res = Xz_ReadBackward(&st, stream, startOffset, alloc);
318
st.startOffset = (UInt64)*startOffset;
319
RINOK(res)
320
if (p->num == p->numAllocated)
321
{
322
const size_t newNum = p->num + p->num / 4 + 1;
323
void *data = ISzAlloc_Alloc(alloc, newNum * sizeof(CXzStream));
324
if (!data)
325
return SZ_ERROR_MEM;
326
p->numAllocated = newNum;
327
if (p->num != 0)
328
memcpy(data, p->streams, p->num * sizeof(CXzStream));
329
ISzAlloc_Free(alloc, p->streams);
330
p->streams = (CXzStream *)data;
331
}
332
p->streams[p->num++] = st;
333
if (*startOffset == 0)
334
break;
335
RINOK(LookInStream_SeekTo(stream, (UInt64)*startOffset))
336
if (progress && ICompressProgress_Progress(progress, (UInt64)(endOffset - *startOffset), (UInt64)(Int64)-1) != SZ_OK)
337
return SZ_ERROR_PROGRESS;
338
}
339
return SZ_OK;
340
}
341
342