1370b324cSopenharmony_ci/* XzIn.c - Xz input 2370b324cSopenharmony_ci2023-04-02 : Igor Pavlov : Public domain */ 3370b324cSopenharmony_ci 4370b324cSopenharmony_ci#include "Precomp.h" 5370b324cSopenharmony_ci 6370b324cSopenharmony_ci#include <string.h> 7370b324cSopenharmony_ci 8370b324cSopenharmony_ci#include "7zCrc.h" 9370b324cSopenharmony_ci#include "CpuArch.h" 10370b324cSopenharmony_ci#include "Xz.h" 11370b324cSopenharmony_ci 12370b324cSopenharmony_ci/* 13370b324cSopenharmony_ci#define XZ_FOOTER_SIG_CHECK(p) (memcmp((p), XZ_FOOTER_SIG, XZ_FOOTER_SIG_SIZE) == 0) 14370b324cSopenharmony_ci*/ 15370b324cSopenharmony_ci#define XZ_FOOTER_SIG_CHECK(p) ((p)[0] == XZ_FOOTER_SIG_0 && (p)[1] == XZ_FOOTER_SIG_1) 16370b324cSopenharmony_ci 17370b324cSopenharmony_ci 18370b324cSopenharmony_ciSRes Xz_ReadHeader(CXzStreamFlags *p, ISeqInStreamPtr inStream) 19370b324cSopenharmony_ci{ 20370b324cSopenharmony_ci Byte sig[XZ_STREAM_HEADER_SIZE]; 21370b324cSopenharmony_ci size_t processedSize = XZ_STREAM_HEADER_SIZE; 22370b324cSopenharmony_ci RINOK(SeqInStream_ReadMax(inStream, sig, &processedSize)) 23370b324cSopenharmony_ci if (processedSize != XZ_STREAM_HEADER_SIZE 24370b324cSopenharmony_ci || memcmp(sig, XZ_SIG, XZ_SIG_SIZE) != 0) 25370b324cSopenharmony_ci return SZ_ERROR_NO_ARCHIVE; 26370b324cSopenharmony_ci return Xz_ParseHeader(p, sig); 27370b324cSopenharmony_ci} 28370b324cSopenharmony_ci 29370b324cSopenharmony_ci#define READ_VARINT_AND_CHECK(buf, pos, size, res) \ 30370b324cSopenharmony_ci { unsigned s = Xz_ReadVarInt(buf + pos, size - pos, res); \ 31370b324cSopenharmony_ci if (s == 0) return SZ_ERROR_ARCHIVE; \ 32370b324cSopenharmony_ci pos += s; } 33370b324cSopenharmony_ci 34370b324cSopenharmony_ciSRes XzBlock_ReadHeader(CXzBlock *p, ISeqInStreamPtr inStream, BoolInt *isIndex, UInt32 *headerSizeRes) 35370b324cSopenharmony_ci{ 36370b324cSopenharmony_ci Byte header[XZ_BLOCK_HEADER_SIZE_MAX]; 37370b324cSopenharmony_ci unsigned headerSize; 38370b324cSopenharmony_ci *headerSizeRes = 0; 39370b324cSopenharmony_ci RINOK(SeqInStream_ReadByte(inStream, &header[0])) 40370b324cSopenharmony_ci headerSize = (unsigned)header[0]; 41370b324cSopenharmony_ci if (headerSize == 0) 42370b324cSopenharmony_ci { 43370b324cSopenharmony_ci *headerSizeRes = 1; 44370b324cSopenharmony_ci *isIndex = True; 45370b324cSopenharmony_ci return SZ_OK; 46370b324cSopenharmony_ci } 47370b324cSopenharmony_ci 48370b324cSopenharmony_ci *isIndex = False; 49370b324cSopenharmony_ci headerSize = (headerSize << 2) + 4; 50370b324cSopenharmony_ci *headerSizeRes = headerSize; 51370b324cSopenharmony_ci { 52370b324cSopenharmony_ci size_t processedSize = headerSize - 1; 53370b324cSopenharmony_ci RINOK(SeqInStream_ReadMax(inStream, header + 1, &processedSize)) 54370b324cSopenharmony_ci if (processedSize != headerSize - 1) 55370b324cSopenharmony_ci return SZ_ERROR_INPUT_EOF; 56370b324cSopenharmony_ci } 57370b324cSopenharmony_ci return XzBlock_Parse(p, header); 58370b324cSopenharmony_ci} 59370b324cSopenharmony_ci 60370b324cSopenharmony_ci#define ADD_SIZE_CHECK(size, val) \ 61370b324cSopenharmony_ci { UInt64 newSize = size + (val); if (newSize < size) return XZ_SIZE_OVERFLOW; size = newSize; } 62370b324cSopenharmony_ci 63370b324cSopenharmony_ciUInt64 Xz_GetUnpackSize(const CXzStream *p) 64370b324cSopenharmony_ci{ 65370b324cSopenharmony_ci UInt64 size = 0; 66370b324cSopenharmony_ci size_t i; 67370b324cSopenharmony_ci for (i = 0; i < p->numBlocks; i++) 68370b324cSopenharmony_ci { 69370b324cSopenharmony_ci ADD_SIZE_CHECK(size, p->blocks[i].unpackSize) 70370b324cSopenharmony_ci } 71370b324cSopenharmony_ci return size; 72370b324cSopenharmony_ci} 73370b324cSopenharmony_ci 74370b324cSopenharmony_ciUInt64 Xz_GetPackSize(const CXzStream *p) 75370b324cSopenharmony_ci{ 76370b324cSopenharmony_ci UInt64 size = 0; 77370b324cSopenharmony_ci size_t i; 78370b324cSopenharmony_ci for (i = 0; i < p->numBlocks; i++) 79370b324cSopenharmony_ci { 80370b324cSopenharmony_ci ADD_SIZE_CHECK(size, (p->blocks[i].totalSize + 3) & ~(UInt64)3) 81370b324cSopenharmony_ci } 82370b324cSopenharmony_ci return size; 83370b324cSopenharmony_ci} 84370b324cSopenharmony_ci 85370b324cSopenharmony_ci/* 86370b324cSopenharmony_ciSRes XzBlock_ReadFooter(CXzBlock *p, CXzStreamFlags f, ISeqInStreamPtr inStream) 87370b324cSopenharmony_ci{ 88370b324cSopenharmony_ci return SeqInStream_Read(inStream, p->check, XzFlags_GetCheckSize(f)); 89370b324cSopenharmony_ci} 90370b324cSopenharmony_ci*/ 91370b324cSopenharmony_ci 92370b324cSopenharmony_cistatic SRes Xz_ReadIndex2(CXzStream *p, const Byte *buf, size_t size, ISzAllocPtr alloc) 93370b324cSopenharmony_ci{ 94370b324cSopenharmony_ci size_t numBlocks, pos = 1; 95370b324cSopenharmony_ci UInt32 crc; 96370b324cSopenharmony_ci 97370b324cSopenharmony_ci if (size < 5 || buf[0] != 0) 98370b324cSopenharmony_ci return SZ_ERROR_ARCHIVE; 99370b324cSopenharmony_ci 100370b324cSopenharmony_ci size -= 4; 101370b324cSopenharmony_ci crc = CrcCalc(buf, size); 102370b324cSopenharmony_ci if (crc != GetUi32(buf + size)) 103370b324cSopenharmony_ci return SZ_ERROR_ARCHIVE; 104370b324cSopenharmony_ci 105370b324cSopenharmony_ci { 106370b324cSopenharmony_ci UInt64 numBlocks64; 107370b324cSopenharmony_ci READ_VARINT_AND_CHECK(buf, pos, size, &numBlocks64) 108370b324cSopenharmony_ci numBlocks = (size_t)numBlocks64; 109370b324cSopenharmony_ci if (numBlocks != numBlocks64 || numBlocks * 2 > size) 110370b324cSopenharmony_ci return SZ_ERROR_ARCHIVE; 111370b324cSopenharmony_ci } 112370b324cSopenharmony_ci 113370b324cSopenharmony_ci Xz_Free(p, alloc); 114370b324cSopenharmony_ci if (numBlocks != 0) 115370b324cSopenharmony_ci { 116370b324cSopenharmony_ci size_t i; 117370b324cSopenharmony_ci p->numBlocks = numBlocks; 118370b324cSopenharmony_ci p->blocks = (CXzBlockSizes *)ISzAlloc_Alloc(alloc, sizeof(CXzBlockSizes) * numBlocks); 119370b324cSopenharmony_ci if (!p->blocks) 120370b324cSopenharmony_ci return SZ_ERROR_MEM; 121370b324cSopenharmony_ci for (i = 0; i < numBlocks; i++) 122370b324cSopenharmony_ci { 123370b324cSopenharmony_ci CXzBlockSizes *block = &p->blocks[i]; 124370b324cSopenharmony_ci READ_VARINT_AND_CHECK(buf, pos, size, &block->totalSize) 125370b324cSopenharmony_ci READ_VARINT_AND_CHECK(buf, pos, size, &block->unpackSize) 126370b324cSopenharmony_ci if (block->totalSize == 0) 127370b324cSopenharmony_ci return SZ_ERROR_ARCHIVE; 128370b324cSopenharmony_ci } 129370b324cSopenharmony_ci } 130370b324cSopenharmony_ci while ((pos & 3) != 0) 131370b324cSopenharmony_ci if (buf[pos++] != 0) 132370b324cSopenharmony_ci return SZ_ERROR_ARCHIVE; 133370b324cSopenharmony_ci return (pos == size) ? SZ_OK : SZ_ERROR_ARCHIVE; 134370b324cSopenharmony_ci} 135370b324cSopenharmony_ci 136370b324cSopenharmony_cistatic SRes Xz_ReadIndex(CXzStream *p, ILookInStreamPtr stream, UInt64 indexSize, ISzAllocPtr alloc) 137370b324cSopenharmony_ci{ 138370b324cSopenharmony_ci SRes res; 139370b324cSopenharmony_ci size_t size; 140370b324cSopenharmony_ci Byte *buf; 141370b324cSopenharmony_ci if (indexSize > ((UInt32)1 << 31)) 142370b324cSopenharmony_ci return SZ_ERROR_UNSUPPORTED; 143370b324cSopenharmony_ci size = (size_t)indexSize; 144370b324cSopenharmony_ci if (size != indexSize) 145370b324cSopenharmony_ci return SZ_ERROR_UNSUPPORTED; 146370b324cSopenharmony_ci buf = (Byte *)ISzAlloc_Alloc(alloc, size); 147370b324cSopenharmony_ci if (!buf) 148370b324cSopenharmony_ci return SZ_ERROR_MEM; 149370b324cSopenharmony_ci res = LookInStream_Read2(stream, buf, size, SZ_ERROR_UNSUPPORTED); 150370b324cSopenharmony_ci if (res == SZ_OK) 151370b324cSopenharmony_ci res = Xz_ReadIndex2(p, buf, size, alloc); 152370b324cSopenharmony_ci ISzAlloc_Free(alloc, buf); 153370b324cSopenharmony_ci return res; 154370b324cSopenharmony_ci} 155370b324cSopenharmony_ci 156370b324cSopenharmony_cistatic SRes LookInStream_SeekRead_ForArc(ILookInStreamPtr stream, UInt64 offset, void *buf, size_t size) 157370b324cSopenharmony_ci{ 158370b324cSopenharmony_ci RINOK(LookInStream_SeekTo(stream, offset)) 159370b324cSopenharmony_ci return LookInStream_Read(stream, buf, size); 160370b324cSopenharmony_ci /* return LookInStream_Read2(stream, buf, size, SZ_ERROR_NO_ARCHIVE); */ 161370b324cSopenharmony_ci} 162370b324cSopenharmony_ci 163370b324cSopenharmony_cistatic SRes Xz_ReadBackward(CXzStream *p, ILookInStreamPtr stream, Int64 *startOffset, ISzAllocPtr alloc) 164370b324cSopenharmony_ci{ 165370b324cSopenharmony_ci UInt64 indexSize; 166370b324cSopenharmony_ci Byte buf[XZ_STREAM_FOOTER_SIZE]; 167370b324cSopenharmony_ci UInt64 pos = (UInt64)*startOffset; 168370b324cSopenharmony_ci 169370b324cSopenharmony_ci if ((pos & 3) != 0 || pos < XZ_STREAM_FOOTER_SIZE) 170370b324cSopenharmony_ci return SZ_ERROR_NO_ARCHIVE; 171370b324cSopenharmony_ci 172370b324cSopenharmony_ci pos -= XZ_STREAM_FOOTER_SIZE; 173370b324cSopenharmony_ci RINOK(LookInStream_SeekRead_ForArc(stream, pos, buf, XZ_STREAM_FOOTER_SIZE)) 174370b324cSopenharmony_ci 175370b324cSopenharmony_ci if (!XZ_FOOTER_SIG_CHECK(buf + 10)) 176370b324cSopenharmony_ci { 177370b324cSopenharmony_ci UInt32 total = 0; 178370b324cSopenharmony_ci pos += XZ_STREAM_FOOTER_SIZE; 179370b324cSopenharmony_ci 180370b324cSopenharmony_ci for (;;) 181370b324cSopenharmony_ci { 182370b324cSopenharmony_ci size_t i; 183370b324cSopenharmony_ci #define TEMP_BUF_SIZE (1 << 10) 184370b324cSopenharmony_ci Byte temp[TEMP_BUF_SIZE]; 185370b324cSopenharmony_ci 186370b324cSopenharmony_ci i = (pos > TEMP_BUF_SIZE) ? TEMP_BUF_SIZE : (size_t)pos; 187370b324cSopenharmony_ci pos -= i; 188370b324cSopenharmony_ci RINOK(LookInStream_SeekRead_ForArc(stream, pos, temp, i)) 189370b324cSopenharmony_ci total += (UInt32)i; 190370b324cSopenharmony_ci for (; i != 0; i--) 191370b324cSopenharmony_ci if (temp[i - 1] != 0) 192370b324cSopenharmony_ci break; 193370b324cSopenharmony_ci if (i != 0) 194370b324cSopenharmony_ci { 195370b324cSopenharmony_ci if ((i & 3) != 0) 196370b324cSopenharmony_ci return SZ_ERROR_NO_ARCHIVE; 197370b324cSopenharmony_ci pos += i; 198370b324cSopenharmony_ci break; 199370b324cSopenharmony_ci } 200370b324cSopenharmony_ci if (pos < XZ_STREAM_FOOTER_SIZE || total > (1 << 16)) 201370b324cSopenharmony_ci return SZ_ERROR_NO_ARCHIVE; 202370b324cSopenharmony_ci } 203370b324cSopenharmony_ci 204370b324cSopenharmony_ci if (pos < XZ_STREAM_FOOTER_SIZE) 205370b324cSopenharmony_ci return SZ_ERROR_NO_ARCHIVE; 206370b324cSopenharmony_ci pos -= XZ_STREAM_FOOTER_SIZE; 207370b324cSopenharmony_ci RINOK(LookInStream_SeekRead_ForArc(stream, pos, buf, XZ_STREAM_FOOTER_SIZE)) 208370b324cSopenharmony_ci if (!XZ_FOOTER_SIG_CHECK(buf + 10)) 209370b324cSopenharmony_ci return SZ_ERROR_NO_ARCHIVE; 210370b324cSopenharmony_ci } 211370b324cSopenharmony_ci 212370b324cSopenharmony_ci p->flags = (CXzStreamFlags)GetBe16(buf + 8); 213370b324cSopenharmony_ci 214370b324cSopenharmony_ci if (!XzFlags_IsSupported(p->flags)) 215370b324cSopenharmony_ci return SZ_ERROR_UNSUPPORTED; 216370b324cSopenharmony_ci 217370b324cSopenharmony_ci { 218370b324cSopenharmony_ci /* to eliminate GCC 6.3 warning: 219370b324cSopenharmony_ci dereferencing type-punned pointer will break strict-aliasing rules */ 220370b324cSopenharmony_ci const Byte *buf_ptr = buf; 221370b324cSopenharmony_ci if (GetUi32(buf_ptr) != CrcCalc(buf + 4, 6)) 222370b324cSopenharmony_ci return SZ_ERROR_ARCHIVE; 223370b324cSopenharmony_ci } 224370b324cSopenharmony_ci 225370b324cSopenharmony_ci indexSize = ((UInt64)GetUi32(buf + 4) + 1) << 2; 226370b324cSopenharmony_ci 227370b324cSopenharmony_ci if (pos < indexSize) 228370b324cSopenharmony_ci return SZ_ERROR_ARCHIVE; 229370b324cSopenharmony_ci 230370b324cSopenharmony_ci pos -= indexSize; 231370b324cSopenharmony_ci RINOK(LookInStream_SeekTo(stream, pos)) 232370b324cSopenharmony_ci RINOK(Xz_ReadIndex(p, stream, indexSize, alloc)) 233370b324cSopenharmony_ci 234370b324cSopenharmony_ci { 235370b324cSopenharmony_ci UInt64 totalSize = Xz_GetPackSize(p); 236370b324cSopenharmony_ci if (totalSize == XZ_SIZE_OVERFLOW 237370b324cSopenharmony_ci || totalSize >= ((UInt64)1 << 63) 238370b324cSopenharmony_ci || pos < totalSize + XZ_STREAM_HEADER_SIZE) 239370b324cSopenharmony_ci return SZ_ERROR_ARCHIVE; 240370b324cSopenharmony_ci pos -= (totalSize + XZ_STREAM_HEADER_SIZE); 241370b324cSopenharmony_ci RINOK(LookInStream_SeekTo(stream, pos)) 242370b324cSopenharmony_ci *startOffset = (Int64)pos; 243370b324cSopenharmony_ci } 244370b324cSopenharmony_ci { 245370b324cSopenharmony_ci CXzStreamFlags headerFlags; 246370b324cSopenharmony_ci CSecToRead secToRead; 247370b324cSopenharmony_ci SecToRead_CreateVTable(&secToRead); 248370b324cSopenharmony_ci secToRead.realStream = stream; 249370b324cSopenharmony_ci 250370b324cSopenharmony_ci RINOK(Xz_ReadHeader(&headerFlags, &secToRead.vt)) 251370b324cSopenharmony_ci return (p->flags == headerFlags) ? SZ_OK : SZ_ERROR_ARCHIVE; 252370b324cSopenharmony_ci } 253370b324cSopenharmony_ci} 254370b324cSopenharmony_ci 255370b324cSopenharmony_ci 256370b324cSopenharmony_ci/* ---------- Xz Streams ---------- */ 257370b324cSopenharmony_ci 258370b324cSopenharmony_civoid Xzs_Construct(CXzs *p) 259370b324cSopenharmony_ci{ 260370b324cSopenharmony_ci p->num = p->numAllocated = 0; 261370b324cSopenharmony_ci p->streams = 0; 262370b324cSopenharmony_ci} 263370b324cSopenharmony_ci 264370b324cSopenharmony_civoid Xzs_Free(CXzs *p, ISzAllocPtr alloc) 265370b324cSopenharmony_ci{ 266370b324cSopenharmony_ci size_t i; 267370b324cSopenharmony_ci for (i = 0; i < p->num; i++) 268370b324cSopenharmony_ci Xz_Free(&p->streams[i], alloc); 269370b324cSopenharmony_ci ISzAlloc_Free(alloc, p->streams); 270370b324cSopenharmony_ci p->num = p->numAllocated = 0; 271370b324cSopenharmony_ci p->streams = 0; 272370b324cSopenharmony_ci} 273370b324cSopenharmony_ci 274370b324cSopenharmony_ciUInt64 Xzs_GetNumBlocks(const CXzs *p) 275370b324cSopenharmony_ci{ 276370b324cSopenharmony_ci UInt64 num = 0; 277370b324cSopenharmony_ci size_t i; 278370b324cSopenharmony_ci for (i = 0; i < p->num; i++) 279370b324cSopenharmony_ci num += p->streams[i].numBlocks; 280370b324cSopenharmony_ci return num; 281370b324cSopenharmony_ci} 282370b324cSopenharmony_ci 283370b324cSopenharmony_ciUInt64 Xzs_GetUnpackSize(const CXzs *p) 284370b324cSopenharmony_ci{ 285370b324cSopenharmony_ci UInt64 size = 0; 286370b324cSopenharmony_ci size_t i; 287370b324cSopenharmony_ci for (i = 0; i < p->num; i++) 288370b324cSopenharmony_ci { 289370b324cSopenharmony_ci ADD_SIZE_CHECK(size, Xz_GetUnpackSize(&p->streams[i])) 290370b324cSopenharmony_ci } 291370b324cSopenharmony_ci return size; 292370b324cSopenharmony_ci} 293370b324cSopenharmony_ci 294370b324cSopenharmony_ci/* 295370b324cSopenharmony_ciUInt64 Xzs_GetPackSize(const CXzs *p) 296370b324cSopenharmony_ci{ 297370b324cSopenharmony_ci UInt64 size = 0; 298370b324cSopenharmony_ci size_t i; 299370b324cSopenharmony_ci for (i = 0; i < p->num; i++) 300370b324cSopenharmony_ci { 301370b324cSopenharmony_ci ADD_SIZE_CHECK(size, Xz_GetTotalSize(&p->streams[i])) 302370b324cSopenharmony_ci } 303370b324cSopenharmony_ci return size; 304370b324cSopenharmony_ci} 305370b324cSopenharmony_ci*/ 306370b324cSopenharmony_ci 307370b324cSopenharmony_ciSRes Xzs_ReadBackward(CXzs *p, ILookInStreamPtr stream, Int64 *startOffset, ICompressProgressPtr progress, ISzAllocPtr alloc) 308370b324cSopenharmony_ci{ 309370b324cSopenharmony_ci Int64 endOffset = 0; 310370b324cSopenharmony_ci RINOK(ILookInStream_Seek(stream, &endOffset, SZ_SEEK_END)) 311370b324cSopenharmony_ci *startOffset = endOffset; 312370b324cSopenharmony_ci for (;;) 313370b324cSopenharmony_ci { 314370b324cSopenharmony_ci CXzStream st; 315370b324cSopenharmony_ci SRes res; 316370b324cSopenharmony_ci Xz_Construct(&st); 317370b324cSopenharmony_ci res = Xz_ReadBackward(&st, stream, startOffset, alloc); 318370b324cSopenharmony_ci st.startOffset = (UInt64)*startOffset; 319370b324cSopenharmony_ci RINOK(res) 320370b324cSopenharmony_ci if (p->num == p->numAllocated) 321370b324cSopenharmony_ci { 322370b324cSopenharmony_ci const size_t newNum = p->num + p->num / 4 + 1; 323370b324cSopenharmony_ci void *data = ISzAlloc_Alloc(alloc, newNum * sizeof(CXzStream)); 324370b324cSopenharmony_ci if (!data) 325370b324cSopenharmony_ci return SZ_ERROR_MEM; 326370b324cSopenharmony_ci p->numAllocated = newNum; 327370b324cSopenharmony_ci if (p->num != 0) 328370b324cSopenharmony_ci memcpy(data, p->streams, p->num * sizeof(CXzStream)); 329370b324cSopenharmony_ci ISzAlloc_Free(alloc, p->streams); 330370b324cSopenharmony_ci p->streams = (CXzStream *)data; 331370b324cSopenharmony_ci } 332370b324cSopenharmony_ci p->streams[p->num++] = st; 333370b324cSopenharmony_ci if (*startOffset == 0) 334370b324cSopenharmony_ci break; 335370b324cSopenharmony_ci RINOK(LookInStream_SeekTo(stream, (UInt64)*startOffset)) 336370b324cSopenharmony_ci if (progress && ICompressProgress_Progress(progress, (UInt64)(endOffset - *startOffset), (UInt64)(Int64)-1) != SZ_OK) 337370b324cSopenharmony_ci return SZ_ERROR_PROGRESS; 338370b324cSopenharmony_ci } 339370b324cSopenharmony_ci return SZ_OK; 340370b324cSopenharmony_ci} 341