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