2 2018-07-04 : Igor Pavlov : Public domain */
13 #define XZ_FOOTER_SIG_CHECK(p) (memcmp((p), XZ_FOOTER_SIG, XZ_FOOTER_SIG_SIZE) == 0)
15 #define XZ_FOOTER_SIG_CHECK(p) ((p)[0] == XZ_FOOTER_SIG_0 && (p)[1] == XZ_FOOTER_SIG_1)
18 SRes
Xz_ReadHeader(CXzStreamFlags
*p
, ISeqInStream
*inStream
)
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
);
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; pos += s; }
31 SRes
XzBlock_ReadHeader(CXzBlock
*p
, ISeqInStream
*inStream
, BoolInt
*isIndex
, UInt32
*headerSizeRes
)
33 Byte header
[XZ_BLOCK_HEADER_SIZE_MAX
];
36 RINOK(SeqInStream_ReadByte(inStream
, &header
[0]));
37 headerSize
= (unsigned)header
[0];
46 headerSize
= (headerSize
<< 2) + 4;
47 *headerSizeRes
= headerSize
;
48 RINOK(SeqInStream_Read(inStream
, header
+ 1, headerSize
- 1));
49 return XzBlock_Parse(p
, header
);
52 #define ADD_SIZE_CHECK(size, val) \
53 { UInt64 newSize = size + (val); if (newSize < size) return XZ_SIZE_OVERFLOW; size = newSize; }
55 UInt64
Xz_GetUnpackSize(const CXzStream
*p
)
59 for (i
= 0; i
< p
->numBlocks
; i
++)
60 ADD_SIZE_CHECK(size
, p
->blocks
[i
].unpackSize
);
64 UInt64
Xz_GetPackSize(const CXzStream
*p
)
68 for (i
= 0; i
< p
->numBlocks
; i
++)
69 ADD_SIZE_CHECK(size
, (p
->blocks
[i
].totalSize
+ 3) & ~(UInt64
)3);
74 SRes XzBlock_ReadFooter(CXzBlock *p, CXzStreamFlags f, ISeqInStream *inStream)
76 return SeqInStream_Read(inStream, p->check, XzFlags_GetCheckSize(f));
80 static SRes
Xz_ReadIndex2(CXzStream
*p
, const Byte
*buf
, size_t size
, ISzAllocPtr alloc
)
82 size_t numBlocks
, pos
= 1;
85 if (size
< 5 || buf
[0] != 0)
86 return SZ_ERROR_ARCHIVE
;
89 crc
= CrcCalc(buf
, size
);
90 if (crc
!= GetUi32(buf
+ size
))
91 return SZ_ERROR_ARCHIVE
;
95 READ_VARINT_AND_CHECK(buf
, pos
, size
, &numBlocks64
);
96 numBlocks
= (size_t)numBlocks64
;
97 if (numBlocks
!= numBlocks64
|| numBlocks
* 2 > size
)
98 return SZ_ERROR_ARCHIVE
;
105 p
->numBlocks
= numBlocks
;
106 p
->blocks
= (CXzBlockSizes
*)ISzAlloc_Alloc(alloc
, sizeof(CXzBlockSizes
) * numBlocks
);
109 for (i
= 0; i
< numBlocks
; i
++)
111 CXzBlockSizes
*block
= &p
->blocks
[i
];
112 READ_VARINT_AND_CHECK(buf
, pos
, size
, &block
->totalSize
);
113 READ_VARINT_AND_CHECK(buf
, pos
, size
, &block
->unpackSize
);
114 if (block
->totalSize
== 0)
115 return SZ_ERROR_ARCHIVE
;
118 while ((pos
& 3) != 0)
120 return SZ_ERROR_ARCHIVE
;
121 return (pos
== size
) ? SZ_OK
: SZ_ERROR_ARCHIVE
;
124 static SRes
Xz_ReadIndex(CXzStream
*p
, ILookInStream
*stream
, UInt64 indexSize
, ISzAllocPtr alloc
)
129 if (indexSize
> ((UInt32
)1 << 31))
130 return SZ_ERROR_UNSUPPORTED
;
131 size
= (size_t)indexSize
;
132 if (size
!= indexSize
)
133 return SZ_ERROR_UNSUPPORTED
;
134 buf
= (Byte
*)ISzAlloc_Alloc(alloc
, size
);
137 res
= LookInStream_Read2(stream
, buf
, size
, SZ_ERROR_UNSUPPORTED
);
139 res
= Xz_ReadIndex2(p
, buf
, size
, alloc
);
140 ISzAlloc_Free(alloc
, buf
);
144 static SRes
LookInStream_SeekRead_ForArc(ILookInStream
*stream
, UInt64 offset
, void *buf
, size_t size
)
146 RINOK(LookInStream_SeekTo(stream
, offset
));
147 return LookInStream_Read(stream
, buf
, size
);
148 /* return LookInStream_Read2(stream, buf, size, SZ_ERROR_NO_ARCHIVE); */
151 static SRes
Xz_ReadBackward(CXzStream
*p
, ILookInStream
*stream
, Int64
*startOffset
, ISzAllocPtr alloc
)
154 Byte buf
[XZ_STREAM_FOOTER_SIZE
];
155 UInt64 pos
= *startOffset
;
157 if ((pos
& 3) != 0 || pos
< XZ_STREAM_FOOTER_SIZE
)
158 return SZ_ERROR_NO_ARCHIVE
;
160 pos
-= XZ_STREAM_FOOTER_SIZE
;
161 RINOK(LookInStream_SeekRead_ForArc(stream
, pos
, buf
, XZ_STREAM_FOOTER_SIZE
));
163 if (!XZ_FOOTER_SIG_CHECK(buf
+ 10))
166 pos
+= XZ_STREAM_FOOTER_SIZE
;
171 #define TEMP_BUF_SIZE (1 << 10)
172 Byte temp
[TEMP_BUF_SIZE
];
174 i
= (pos
> TEMP_BUF_SIZE
) ? TEMP_BUF_SIZE
: (size_t)pos
;
176 RINOK(LookInStream_SeekRead_ForArc(stream
, pos
, temp
, i
));
179 if (temp
[i
- 1] != 0)
184 return SZ_ERROR_NO_ARCHIVE
;
188 if (pos
< XZ_STREAM_FOOTER_SIZE
|| total
> (1 << 16))
189 return SZ_ERROR_NO_ARCHIVE
;
192 if (pos
< XZ_STREAM_FOOTER_SIZE
)
193 return SZ_ERROR_NO_ARCHIVE
;
194 pos
-= XZ_STREAM_FOOTER_SIZE
;
195 RINOK(LookInStream_SeekRead_ForArc(stream
, pos
, buf
, XZ_STREAM_FOOTER_SIZE
));
196 if (!XZ_FOOTER_SIG_CHECK(buf
+ 10))
197 return SZ_ERROR_NO_ARCHIVE
;
200 p
->flags
= (CXzStreamFlags
)GetBe16(buf
+ 8);
202 if (!XzFlags_IsSupported(p
->flags
))
203 return SZ_ERROR_UNSUPPORTED
;
205 if (GetUi32(buf
) != CrcCalc(buf
+ 4, 6))
206 return SZ_ERROR_ARCHIVE
;
208 indexSize
= ((UInt64
)GetUi32(buf
+ 4) + 1) << 2;
211 return SZ_ERROR_ARCHIVE
;
214 RINOK(LookInStream_SeekTo(stream
, pos
));
215 RINOK(Xz_ReadIndex(p
, stream
, indexSize
, alloc
));
218 UInt64 totalSize
= Xz_GetPackSize(p
);
219 if (totalSize
== XZ_SIZE_OVERFLOW
220 || totalSize
>= ((UInt64
)1 << 63)
221 || pos
< totalSize
+ XZ_STREAM_HEADER_SIZE
)
222 return SZ_ERROR_ARCHIVE
;
223 pos
-= (totalSize
+ XZ_STREAM_HEADER_SIZE
);
224 RINOK(LookInStream_SeekTo(stream
, pos
));
228 CXzStreamFlags headerFlags
;
229 CSecToRead secToRead
;
230 SecToRead_CreateVTable(&secToRead
);
231 secToRead
.realStream
= stream
;
233 RINOK(Xz_ReadHeader(&headerFlags
, &secToRead
.vt
));
234 return (p
->flags
== headerFlags
) ? SZ_OK
: SZ_ERROR_ARCHIVE
;
239 /* ---------- Xz Streams ---------- */
241 void Xzs_Construct(CXzs
*p
)
243 p
->num
= p
->numAllocated
= 0;
247 void Xzs_Free(CXzs
*p
, ISzAllocPtr alloc
)
250 for (i
= 0; i
< p
->num
; i
++)
251 Xz_Free(&p
->streams
[i
], alloc
);
252 ISzAlloc_Free(alloc
, p
->streams
);
253 p
->num
= p
->numAllocated
= 0;
257 UInt64
Xzs_GetNumBlocks(const CXzs
*p
)
261 for (i
= 0; i
< p
->num
; i
++)
262 num
+= p
->streams
[i
].numBlocks
;
266 UInt64
Xzs_GetUnpackSize(const CXzs
*p
)
270 for (i
= 0; i
< p
->num
; i
++)
271 ADD_SIZE_CHECK(size
, Xz_GetUnpackSize(&p
->streams
[i
]));
276 UInt64 Xzs_GetPackSize(const CXzs *p)
280 for (i = 0; i < p->num; i++)
281 ADD_SIZE_CHECK(size, Xz_GetTotalSize(&p->streams[i]));
286 SRes
Xzs_ReadBackward(CXzs
*p
, ILookInStream
*stream
, Int64
*startOffset
, ICompressProgress
*progress
, ISzAllocPtr alloc
)
289 RINOK(ILookInStream_Seek(stream
, &endOffset
, SZ_SEEK_END
));
290 *startOffset
= endOffset
;
296 res
= Xz_ReadBackward(&st
, stream
, startOffset
, alloc
);
297 st
.startOffset
= *startOffset
;
299 if (p
->num
== p
->numAllocated
)
301 size_t newNum
= p
->num
+ p
->num
/ 4 + 1;
302 Byte
*data
= (Byte
*)ISzAlloc_Alloc(alloc
, newNum
* sizeof(CXzStream
));
305 p
->numAllocated
= newNum
;
307 memcpy(data
, p
->streams
, p
->num
* sizeof(CXzStream
));
308 ISzAlloc_Free(alloc
, p
->streams
);
309 p
->streams
= (CXzStream
*)data
;
311 p
->streams
[p
->num
++] = st
;
312 if (*startOffset
== 0)
314 RINOK(LookInStream_SeekTo(stream
, *startOffset
));
315 if (progress
&& ICompressProgress_Progress(progress
, endOffset
- *startOffset
, (UInt64
)(Int64
)-1) != SZ_OK
)
316 return SZ_ERROR_PROGRESS
;