libclamav/7z/XzIn.c
b3840d2f
 /* XzIn.c - Xz input
 2009-06-19 : Igor Pavlov : Public domain */
 
 #include <string.h>
 
 #if defined(_WIN32)
 #include <WinSock2.h>
 #include <Windows.h>
 #endif
 
60d8d2c3
 #include "clamav.h"
b3840d2f
 #include "7zCrc.h"
 #include "CpuArch.h"
 #include "Xz.h"
 
 SRes Xz_ReadHeader(CXzStreamFlags *p, ISeqInStream *inStream)
 {
   Byte sig[XZ_STREAM_HEADER_SIZE];
   RINOK(SeqInStream_Read2(inStream, sig, XZ_STREAM_HEADER_SIZE, SZ_ERROR_NO_ARCHIVE));
   if (memcmp(sig, XZ_SIG, XZ_SIG_SIZE) != 0)
     return SZ_ERROR_NO_ARCHIVE;
   return Xz_ParseHeader(p, sig);
 }
 
 #define READ_VARINT_AND_CHECK(buf, pos, size, res) \
   { unsigned s = Xz_ReadVarInt(buf + pos, size - pos, res); \
   if (s == 0) return SZ_ERROR_ARCHIVE; pos += s; }
 
 SRes XzBlock_ReadHeader(CXzBlock *p, ISeqInStream *inStream, Bool *isIndex, UInt32 *headerSizeRes)
 {
   Byte header[XZ_BLOCK_HEADER_SIZE_MAX];
   unsigned headerSize;
   *headerSizeRes = 0;
   RINOK(SeqInStream_ReadByte(inStream, &header[0]));
   headerSize = ((unsigned)header[0] << 2) + 4;
   if (headerSize == 0)
   {
     *headerSizeRes = 1;
     *isIndex = True;
     return SZ_OK;
   }
 
   *isIndex = False;
   *headerSizeRes = headerSize;
   RINOK(SeqInStream_Read(inStream, header + 1, headerSize - 1));
   return XzBlock_Parse(p, header);
 }
 
 #define ADD_SIZE_CHECH(size, val) \
   { UInt64 newSize = size + (val); if (newSize < size) return XZ_SIZE_OVERFLOW; size = newSize; }
 
 UInt64 Xz_GetUnpackSize(const CXzStream *p)
 {
   UInt64 size = 0;
   size_t i;
   for (i = 0; i < p->numBlocks; i++)
     ADD_SIZE_CHECH(size, p->blocks[i].unpackSize);
   return size;
 }
 
 UInt64 Xz_GetPackSize(const CXzStream *p)
 {
   UInt64 size = 0;
   size_t i;
   for (i = 0; i < p->numBlocks; i++)
     ADD_SIZE_CHECH(size, (p->blocks[i].totalSize + 3) & ~(UInt64)3);
   return size;
 }
 
 /*
 SRes XzBlock_ReadFooter(CXzBlock *p, CXzStreamFlags f, ISeqInStream *inStream)
 {
   return SeqInStream_Read(inStream, p->check, XzFlags_GetCheckSize(f));
 }
 */
 
 static SRes Xz_ReadIndex2(CXzStream *p, const Byte *buf, size_t size, ISzAlloc *alloc)
 {
   size_t i, numBlocks, crcStartPos, pos = 1;
   UInt32 crc;
 
   if (size < 5 || buf[0] != 0)
     return SZ_ERROR_ARCHIVE;
 
   size -= 4;
   crc = CrcCalc(buf, size);
   if (crc != GetUi32(buf + size))
     return SZ_ERROR_ARCHIVE;
 
   {
     UInt64 numBlocks64;
     READ_VARINT_AND_CHECK(buf, pos, size, &numBlocks64);
     numBlocks = (size_t)numBlocks64;
     if (numBlocks != numBlocks64 || numBlocks * 2 > size)
       return SZ_ERROR_ARCHIVE;
   }
   
   crcStartPos = pos;
   Xz_Free(p, alloc);
   if (numBlocks != 0)
   {
     p->numBlocks = numBlocks;
     p->numBlocksAllocated = numBlocks;
     p->blocks = alloc->Alloc(alloc, sizeof(CXzBlockSizes) * numBlocks);
     if (p->blocks == 0)
       return SZ_ERROR_MEM;
     for (i = 0; i < numBlocks; i++)
     {
       CXzBlockSizes *block = &p->blocks[i];
       READ_VARINT_AND_CHECK(buf, pos, size, &block->totalSize);
       READ_VARINT_AND_CHECK(buf, pos, size, &block->unpackSize);
       if (block->totalSize == 0)
         return SZ_ERROR_ARCHIVE;
     }
   }
   while ((pos & 3) != 0)
     if (buf[pos++] != 0)
       return SZ_ERROR_ARCHIVE;
   return (pos == size) ? SZ_OK : SZ_ERROR_ARCHIVE;
 }
 
 static SRes Xz_ReadIndex(CXzStream *p, ILookInStream *stream, UInt64 indexSize, ISzAlloc *alloc)
 {
   SRes res;
   size_t size;
   Byte *buf;
   if (indexSize > ((UInt32)1 << 31))
     return SZ_ERROR_UNSUPPORTED;
   size = (size_t)indexSize;
   if (size != indexSize)
     return SZ_ERROR_UNSUPPORTED;
   buf = alloc->Alloc(alloc, size);
   if (buf == 0)
     return SZ_ERROR_MEM;
   res = LookInStream_Read2(stream, buf, size, SZ_ERROR_UNSUPPORTED);
   if (res == SZ_OK)
     res = Xz_ReadIndex2(p, buf, size, alloc);
   alloc->Free(alloc, buf);
   return res;
 }
 
 static SRes SeekFromCur(ILookInStream *inStream, Int64 *res)
 {
   return inStream->Seek(inStream, res, SZ_SEEK_CUR);
 }
 
 static SRes Xz_ReadBackward(CXzStream *p, ILookInStream *stream, Int64 *startOffset, ISzAlloc *alloc)
 {
   UInt64 indexSize;
   Byte buf[XZ_STREAM_FOOTER_SIZE];
 
   if ((*startOffset & 3) != 0 || *startOffset < XZ_STREAM_FOOTER_SIZE)
     return SZ_ERROR_NO_ARCHIVE;
   *startOffset = -XZ_STREAM_FOOTER_SIZE;
   RINOK(SeekFromCur(stream, startOffset));
 
   RINOK(LookInStream_Read2(stream, buf, XZ_STREAM_FOOTER_SIZE, SZ_ERROR_NO_ARCHIVE));
   
   if (memcmp(buf + 10, XZ_FOOTER_SIG, XZ_FOOTER_SIG_SIZE) != 0)
   {
     Int64 i = 0;
     *startOffset += XZ_STREAM_FOOTER_SIZE;
     for (;;)
     {
       int j;
       size_t processedSize;
       #define TEMP_BUF_SIZE (1 << 10)
       Byte tempBuf[TEMP_BUF_SIZE];
       if (*startOffset < XZ_STREAM_FOOTER_SIZE || i > (1 << 16))
         return SZ_ERROR_NO_ARCHIVE;
       processedSize = (*startOffset > TEMP_BUF_SIZE) ? TEMP_BUF_SIZE : (size_t)*startOffset;
       i += processedSize;
       *startOffset = -(Int64)processedSize;
       RINOK(SeekFromCur(stream, startOffset));
       RINOK(LookInStream_Read2(stream, tempBuf, processedSize, SZ_ERROR_NO_ARCHIVE));
       for (j = (int)processedSize; j >= 0; j--)
         if (tempBuf[j -1] != 0)
           break;
       if (j != 0)
       {
         if ((j & 3) != 0)
           return SZ_ERROR_NO_ARCHIVE;
         *startOffset += j;
         if (*startOffset < XZ_STREAM_FOOTER_SIZE)
           return SZ_ERROR_NO_ARCHIVE;
         *startOffset -= XZ_STREAM_FOOTER_SIZE;
         RINOK(stream->Seek(stream, startOffset, SZ_SEEK_SET));
         RINOK(LookInStream_Read2(stream, buf, XZ_STREAM_FOOTER_SIZE, SZ_ERROR_NO_ARCHIVE));
         if (memcmp(buf + 10, XZ_FOOTER_SIG, XZ_FOOTER_SIG_SIZE) != 0)
           return SZ_ERROR_NO_ARCHIVE;
         break;
       }
     }
   }
   
   p->flags = (CXzStreamFlags)GetBe16(buf + 8);
 
   if (!XzFlags_IsSupported(p->flags))
     return SZ_ERROR_UNSUPPORTED;
 
   if (GetUi32(buf) != CrcCalc(buf + 4, 6))
     return SZ_ERROR_ARCHIVE;
 
   indexSize = ((UInt64)GetUi32(buf + 4) + 1) << 2;
 
   *startOffset = -(Int64)(indexSize + XZ_STREAM_FOOTER_SIZE);
   RINOK(SeekFromCur(stream, startOffset));
 
   RINOK(Xz_ReadIndex(p, stream, indexSize, alloc));
 
   {
     UInt64 totalSize = Xz_GetPackSize(p);
     UInt64 sum = XZ_STREAM_HEADER_SIZE + totalSize + indexSize;
     if (totalSize == XZ_SIZE_OVERFLOW ||
       sum >= ((UInt64)1 << 63) ||
       totalSize >= ((UInt64)1 << 63))
       return SZ_ERROR_ARCHIVE;
     *startOffset = -(Int64)sum;
     RINOK(SeekFromCur(stream, startOffset));
   }
   {
     CXzStreamFlags headerFlags;
     CSecToRead secToRead;
     SecToRead_CreateVTable(&secToRead);
     secToRead.realStream = stream;
 
     RINOK(Xz_ReadHeader(&headerFlags, &secToRead.s));
     return (p->flags == headerFlags) ? SZ_OK : SZ_ERROR_ARCHIVE;
   }
 }
 
 
 /* ---------- Xz Streams ---------- */
 
 void Xzs_Construct(CXzs *p)
 {
   p->num = p->numAllocated = 0;
   p->streams = 0;
 }
 
 void Xzs_Free(CXzs *p, ISzAlloc *alloc)
 {
   size_t i;
   for (i = 0; i < p->num; i++)
     Xz_Free(&p->streams[i], alloc);
   alloc->Free(alloc, p->streams);
   p->num = p->numAllocated = 0;
   p->streams = 0;
 }
 
 UInt64 Xzs_GetNumBlocks(const CXzs *p)
 {
   UInt64 num = 0;
   size_t i;
   for (i = 0; i < p->num; i++)
     num += p->streams[i].numBlocks;
   return num;
 }
 
 UInt64 Xzs_GetUnpackSize(const CXzs *p)
 {
   UInt64 size = 0;
   size_t i;
   for (i = 0; i < p->num; i++)
     ADD_SIZE_CHECH(size, Xz_GetUnpackSize(&p->streams[i]));
   return size;
 }
 
 /*
 UInt64 Xzs_GetPackSize(const CXzs *p)
 {
   UInt64 size = 0;
   size_t i;
   for (i = 0; i < p->num; i++)
     ADD_SIZE_CHECH(size, Xz_GetTotalSize(&p->streams[i]));
   return size;
 }
 */
 
 SRes Xzs_ReadBackward(CXzs *p, ILookInStream *stream, Int64 *startOffset, ICompressProgress *progress, ISzAlloc *alloc)
 {
   Int64 endOffset = 0;
   RINOK(stream->Seek(stream, &endOffset, SZ_SEEK_END));
   *startOffset = endOffset;
   for (;;)
   {
     CXzStream st;
     SRes res;
     Xz_Construct(&st);
     res = Xz_ReadBackward(&st, stream, startOffset, alloc);
     st.startOffset = *startOffset;
     RINOK(res);
     if (p->num == p->numAllocated)
     {
       size_t newNum = p->num + p->num / 4 + 1;
       Byte *data = (Byte *)alloc->Alloc(alloc, newNum * sizeof(CXzStream));
       if (data == 0)
         return SZ_ERROR_MEM;
       p->numAllocated = newNum;
       memcpy(data, p->streams, p->num * sizeof(CXzStream));
       alloc->Free(alloc, p->streams);
       p->streams = (CXzStream *)data;
     }
     p->streams[p->num++] = st;
     if (*startOffset == 0)
       break;
     RINOK(stream->Seek(stream, startOffset, SZ_SEEK_SET));
     if (progress && progress->Progress(progress, endOffset - *startOffset, (UInt64)(Int64)-1) != SZ_OK)
       return SZ_ERROR_PROGRESS;
   }
   return SZ_OK;
 }