diff options
Diffstat (limited to 'CPP/7zip/Archive/Zip/ZipIn.cpp')
-rw-r--r-- | CPP/7zip/Archive/Zip/ZipIn.cpp | 1536 |
1 files changed, 1214 insertions, 322 deletions
diff --git a/CPP/7zip/Archive/Zip/ZipIn.cpp b/CPP/7zip/Archive/Zip/ZipIn.cpp index 2ba7e3fa..fe5200f7 100644 --- a/CPP/7zip/Archive/Zip/ZipIn.cpp +++ b/CPP/7zip/Archive/Zip/ZipIn.cpp @@ -5,8 +5,11 @@ // #include <stdio.h> #include "../../../Common/DynamicBuffer.h" +#include "../../../Common/IntToString.h" +#include "../../../Common/StringToInt.h" + +#include "../../../Windows/PropVariant.h" -#include "../../Common/LimitedStreams.h" #include "../../Common/StreamUtils.h" #include "../IArchive.h" @@ -17,100 +20,133 @@ #define Get32(p) GetUi32(p) #define Get64(p) GetUi64(p) +#define G16(offs, v) v = Get16(p + (offs)) +#define G32(offs, v) v = Get32(p + (offs)) +#define G64(offs, v) v = Get64(p + (offs)) + namespace NArchive { namespace NZip { struct CEcd { - UInt16 thisDiskNumber; - UInt16 startCDDiskNumber; - UInt16 numEntriesInCDOnThisDisk; - UInt16 numEntriesInCD; - UInt32 cdSize; - UInt32 cdStartOffset; - UInt16 commentSize; + UInt16 ThisDisk; + UInt16 CdDisk; + UInt16 NumEntries_in_ThisDisk; + UInt16 NumEntries; + UInt32 Size; + UInt32 Offset; + UInt16 CommentSize; - void Parse(const Byte *p); - - bool IsEmptyArc() + bool IsEmptyArc() const { - return thisDiskNumber == 0 && startCDDiskNumber == 0 && - numEntriesInCDOnThisDisk == 0 && numEntriesInCD == 0 && cdSize == 0 - && cdStartOffset == 0 // test it + return ThisDisk == 0 + && CdDisk == 0 + && NumEntries_in_ThisDisk == 0 + && NumEntries == 0 + && Size == 0 + && Offset == 0 // test it ; } + + void Parse(const Byte *p); // (p) doesn't include signature }; void CEcd::Parse(const Byte *p) { - thisDiskNumber = Get16(p); - startCDDiskNumber = Get16(p + 2); - numEntriesInCDOnThisDisk = Get16(p + 4); - numEntriesInCD = Get16(p + 6); - cdSize = Get32(p + 8); - cdStartOffset = Get32(p + 12); - commentSize = Get16(p + 16); + // (p) doesn't include signature + G16(0, ThisDisk); + G16(2, CdDisk); + G16(4, NumEntries_in_ThisDisk); + G16(6, NumEntries); + G32(8, Size); + G32(12, Offset); + G16(16, CommentSize); } -struct CEcd64 + +void CCdInfo::ParseEcd32(const Byte *p) { - UInt16 versionMade; - UInt16 versionNeedExtract; - UInt32 thisDiskNumber; - UInt32 startCDDiskNumber; - UInt64 numEntriesInCDOnThisDisk; - UInt64 numEntriesInCD; - UInt64 cdSize; - UInt64 cdStartOffset; - - void Parse(const Byte *p); - CEcd64() { memset(this, 0, sizeof(*this)); } -}; + // (p) includes signature + p += 4; + G16(0, ThisDisk); + G16(2, CdDisk); + G16(4, NumEntries_in_ThisDisk); + G16(6, NumEntries); + G32(8, Size); + G32(12, Offset); + G16(16, CommentSize); +} -void CEcd64::Parse(const Byte *p) +void CCdInfo::ParseEcd64e(const Byte *p) { - versionMade = Get16(p); - versionNeedExtract = Get16(p + 2); - thisDiskNumber = Get32(p + 4); - startCDDiskNumber = Get32(p + 8); - numEntriesInCDOnThisDisk = Get64(p + 12); - numEntriesInCD = Get64(p + 20); - cdSize = Get64(p + 28); - cdStartOffset = Get64(p + 36); + // (p) exclude signature + G16(0, VersionMade); + G16(2, VersionNeedExtract); + G32(4, ThisDisk); + G32(8, CdDisk); + + G64(12, NumEntries_in_ThisDisk); + G64(20, NumEntries); + G64(28, Size); + G64(36, Offset); } -HRESULT CInArchive::Open(IInStream *stream, const UInt64 *searchHeaderSizeLimit) + +struct CLocator { - _inBufMode = false; - Close(); - RINOK(stream->Seek(0, STREAM_SEEK_CUR, &m_Position)); - RINOK(stream->Seek(0, STREAM_SEEK_END, &ArcInfo.FileEndPos)); - RINOK(stream->Seek(m_Position, STREAM_SEEK_SET, NULL)); + UInt32 Ecd64Disk; + UInt32 NumDisks; + UInt64 Ecd64Offset; + + CLocator(): Ecd64Disk(0), NumDisks(0), Ecd64Offset(0) {} - // printf("\nOpen offset = %d", (int)m_Position); - RINOK(FindAndReadMarker(stream, searchHeaderSizeLimit)); - RINOK(stream->Seek(m_Position, STREAM_SEEK_SET, NULL)); - Stream = stream; - return S_OK; + void Parse(const Byte *p) + { + G32(0, Ecd64Disk); + G64(4, Ecd64Offset); + G32(12, NumDisks); + } +}; + + + + +void CInArchive::ClearRefs() +{ + StreamRef.Release(); + Stream = NULL; + StartStream = NULL; + Callback = NULL; + + Vols.Clear(); } void CInArchive::Close() { + _processedCnt = 0; IsArc = false; + IsArcOpen = false; + IsMultiVol = false; + UseDisk_in_SingleVol = false; + EcdVolIndex = 0; HeadersError = false; HeadersWarning = false; ExtraMinorError = false; UnexpectedEnd = false; NoCentralDir = false; IsZip64 = false; - Stream.Release(); + MarkerIsFound = false; + + ClearRefs(); } + HRESULT CInArchive::Seek(UInt64 offset) { return Stream->Seek(offset, STREAM_SEEK_SET, NULL); } + static bool CheckDosTime(UInt32 dosTime) { if (dosTime == 0) @@ -134,7 +170,8 @@ API_FUNC_IsArc IsArc_Zip(const Byte *p, size_t size) UInt32 value = Get32(p); - if (value == NSignature::kNoSpan) + if (value == NSignature::kNoSpan + || value == NSignature::kSpan) { p += 4; size -= 4; @@ -250,28 +287,35 @@ static UInt32 IsArc_Zip_2(const Byte *p, size_t size, bool isFinal) return res; } -HRESULT CInArchive::FindAndReadMarker(IInStream *stream, const UInt64 *searchLimit) + + +HRESULT CInArchive::FindMarker(IInStream *stream, const UInt64 *searchLimit) { - ArcInfo.Clear(); ArcInfo.MarkerPos = m_Position; ArcInfo.MarkerPos2 = m_Position; if (searchLimit && *searchLimit == 0) { - const unsigned kStartBufSize = kMarkerSize; - Byte startBuf[kStartBufSize]; - size_t processed = kStartBufSize; + Byte startBuf[kMarkerSize]; + size_t processed = kMarkerSize; RINOK(ReadStream(stream, startBuf, &processed)); m_Position += processed; if (processed < kMarkerSize) return S_FALSE; m_Signature = Get32(startBuf); + if (m_Signature != NSignature::kEcd && m_Signature != NSignature::kLocalFileHeader) { if (m_Signature != NSignature::kNoSpan) - return S_FALSE; - size_t processed = kStartBufSize; + { + if (m_Signature != NSignature::kSpan) + return S_FALSE; + if (m_Position != 4) // we don't support multivol archives with sfx stub + return S_FALSE; + ArcInfo.IsSpanMode = true; + } + size_t processed = kMarkerSize; RINOK(ReadStream(stream, startBuf, &processed)); m_Position += processed; if (processed < kMarkerSize) @@ -283,9 +327,8 @@ HRESULT CInArchive::FindAndReadMarker(IInStream *stream, const UInt64 *searchLim ArcInfo.MarkerPos2 += 4; } - // we use weak test in case of *searchLimit == 0) + // we use weak test in case of (*searchLimit == 0) // since error will be detected later in Open function - // m_Position = ArcInfo.MarkerPos2 + 4; return S_OK; // maybe we need to search backward. } @@ -302,9 +345,17 @@ HRESULT CInArchive::FindAndReadMarker(IInStream *stream, const UInt64 *searchLim RINOK(ReadStream(stream, buffer + numBytesInBuffer, &numReadBytes)); m_Position += numReadBytes; numBytesInBuffer += numReadBytes; - bool isFinished = (numBytesInBuffer != kBufSize); + const bool isFinished = (numBytesInBuffer != kBufSize); - size_t limit = (isFinished ? numBytesInBuffer : numBytesInBuffer - kCheckSize); + size_t limit = numBytesInBuffer;; + if (isFinished) + { + if (limit == 0) + break; + limit--; + } + else + limit -= kCheckSize; if (searchLimit && curScanPos + limit > *searchLimit) limit = (size_t)(*searchLimit - curScanPos + 1); @@ -328,7 +379,8 @@ HRESULT CInArchive::FindAndReadMarker(IInStream *stream, const UInt64 *searchLim m_Signature = Get32(buf + pos); ArcInfo.MarkerPos += curScanPos + pos; ArcInfo.MarkerPos2 = ArcInfo.MarkerPos; - if (m_Signature == NSignature::kNoSpan) + if (m_Signature == NSignature::kNoSpan + || m_Signature == NSignature::kSpan) { m_Signature = Get32(buf + pos + 4); ArcInfo.MarkerPos2 += 4; @@ -349,13 +401,86 @@ HRESULT CInArchive::FindAndReadMarker(IInStream *stream, const UInt64 *searchLim return S_FALSE; } -HRESULT CInArchive::IncreaseRealPosition(Int64 addValue) + +HRESULT CInArchive::IncreaseRealPosition(Int64 addValue, bool &isFinished) { - return Stream->Seek(addValue, STREAM_SEEK_CUR, &m_Position); + isFinished = false; + if (!IsMultiVol) + return Stream->Seek(addValue, STREAM_SEEK_CUR, &m_Position); + + for (;;) + { + if (addValue == 0) + return S_OK; + if (addValue > 0) + { + if (Vols.StreamIndex < 0) + return S_FALSE; + if ((unsigned)Vols.StreamIndex >= Vols.Streams.Size()) + { + isFinished = true; + return S_OK; + } + { + const CVols::CSubStreamInfo &s = Vols.Streams[Vols.StreamIndex]; + if (!s.Stream) + { + isFinished = true; + return S_OK; + } + if (m_Position > s.Size) + return S_FALSE; + UInt64 rem = s.Size - m_Position; + if ((UInt64)addValue <= rem) + return Stream->Seek(addValue, STREAM_SEEK_CUR, &m_Position); + RINOK(Stream->Seek(s.Size, STREAM_SEEK_SET, &m_Position)); + addValue -= rem; + Stream = NULL; + Vols.StreamIndex++; + if ((unsigned)Vols.StreamIndex >= Vols.Streams.Size()) + { + isFinished = true; + return S_OK; + } + } + const CVols::CSubStreamInfo &s2 = Vols.Streams[Vols.StreamIndex]; + if (!s2.Stream) + { + isFinished = true; + return S_OK; + } + Stream = s2.Stream; + m_Position = 0; + RINOK(Stream->Seek(0, STREAM_SEEK_SET, &m_Position)); + } + else + { + if (!Stream) + return S_FALSE; + { + if (m_Position >= (UInt64)(-addValue)) + return Stream->Seek(addValue, STREAM_SEEK_CUR, &m_Position); + addValue += m_Position; + RINOK(Stream->Seek(0, STREAM_SEEK_SET, &m_Position)); + m_Position = 0; + Stream = NULL; + if (--Vols.StreamIndex < 0) + return S_FALSE; + } + const CVols::CSubStreamInfo &s2 = Vols.Streams[Vols.StreamIndex]; + if (!s2.Stream) + return S_FALSE; + Stream = s2.Stream; + m_Position = s2.Size; + RINOK(Stream->Seek(s2.Size, STREAM_SEEK_SET, &m_Position)); + } + } } + class CUnexpectEnd {}; + HRESULT CInArchive::ReadBytes(void *data, UInt32 size, UInt32 *processedSize) { size_t realProcessedSize = size; @@ -376,18 +501,46 @@ HRESULT CInArchive::ReadBytes(void *data, UInt32 size, UInt32 *processedSize) void CInArchive::SafeReadBytes(void *data, unsigned size) { size_t processed = size; - if (_inBufMode) - { - processed = _inBuffer.ReadBytes((Byte *)data, size); - m_Position += processed; - } + + HRESULT result = S_OK; + + if (!_inBufMode) + result = ReadStream(Stream, data, &processed); else { - HRESULT result = ReadStream(Stream, data, &processed); - m_Position += processed; - if (result != S_OK) - throw CSystemException(result); + for (;;) + { + processed = _inBuffer.ReadBytes((Byte *)data, size); + if (processed != 0 + || IsMultiVol + || !CanStartNewVol + || Vols.StreamIndex < 0 + || (unsigned)Vols.StreamIndex >= Vols.Streams.Size()) + break; + Vols.StreamIndex++; + const CVols::CSubStreamInfo &s = Vols.Streams[Vols.StreamIndex]; + if (!s.Stream) + break; + // if (Vols.NeedSeek) + { + result = s.Stream->Seek(0, STREAM_SEEK_SET, NULL); + m_Position = 0; + if (result != S_OK) + break; + Vols.NeedSeek = false; + } + _inBuffer.SetStream(s.Stream); + _inBuffer.Init(); + } + CanStartNewVol = false; } + + m_Position += processed; + _processedCnt += processed; + + if (result != S_OK) + throw CSystemException(result); + if (processed != size) throw CUnexpectEnd(); } @@ -410,12 +563,15 @@ UInt16 CInArchive::ReadUInt16() { Byte buf[2]; SafeReadBytes(buf, 2); return Get UInt32 CInArchive::ReadUInt32() { Byte buf[4]; SafeReadBytes(buf, 4); return Get32(buf); } UInt64 CInArchive::ReadUInt64() { Byte buf[8]; SafeReadBytes(buf, 8); return Get64(buf); } +// we use Skip() inside headers only, so no need for stream change in multivol. + void CInArchive::Skip(unsigned num) { if (_inBufMode) { size_t skip = _inBuffer.Skip(num); m_Position += skip; + _processedCnt += skip; if (skip != num) throw CUnexpectEnd(); } @@ -440,16 +596,18 @@ void CInArchive::ReadFileName(unsigned size, AString &s) s.Empty(); return; } - char *p = s.GetBuf(size); - SafeReadBytes(p, size); + SafeReadBytes(s.GetBuf(size), size); s.ReleaseBuf_CalcLen(size); } + bool CInArchive::ReadExtra(unsigned extraSize, CExtraBlock &extraBlock, UInt64 &unpackSize, UInt64 &packSize, UInt64 &localHeaderOffset, UInt32 &diskStartNumber) { extraBlock.Clear(); + UInt32 remain = extraSize; + while (remain >= 4) { CExtraSubBlock subBlock; @@ -509,6 +667,7 @@ bool CInArchive::ReadExtra(unsigned extraSize, CExtraBlock &extraBlock, } remain -= dataSize; } + if (remain != 0) { ExtraMinorError = true; @@ -516,12 +675,17 @@ bool CInArchive::ReadExtra(unsigned extraSize, CExtraBlock &extraBlock, // so we don't return false, but just set warning flag // return false; } + Skip(remain); return true; } + bool CInArchive::ReadLocalItem(CItemEx &item) { + item.Disk = 0; + if (IsMultiVol && Vols.StreamIndex >= 0) + item.Disk = Vols.StreamIndex; const unsigned kPureHeaderSize = kLocalHeaderSize - 4; Byte p[kPureHeaderSize]; SafeReadBytes(p, kPureHeaderSize); @@ -534,14 +698,14 @@ bool CInArchive::ReadLocalItem(CItemEx &item) item.ExtractVersion.Version = p[0]; item.ExtractVersion.HostOS = p[1]; - item.Flags = Get16(p + 2); - item.Method = Get16(p + 4); - item.Time = Get32(p + 6); - item.Crc = Get32(p + 10); - item.PackSize = Get32(p + 14); - item.Size = Get32(p + 18); - unsigned nameSize = Get16(p + 22); - unsigned extraSize = Get16(p + 24); + G16(2, item.Flags); + G16(4, item.Method); + G32(6, item.Time); + G32(10, item.Crc); + G32(14, item.PackSize); + G32(18, item.Size); + const unsigned nameSize = Get16(p + 22); + const unsigned extraSize = Get16(p + 24); ReadFileName(nameSize, item.Name); item.LocalFullHeaderSize = kLocalHeaderSize + (UInt32)nameSize + extraSize; @@ -581,6 +745,7 @@ bool CInArchive::ReadLocalItem(CItemEx &item) return item.LocalFullHeaderSize <= ((UInt32)1 << 16); } + static bool FlagsAreSame(const CItem &i1, const CItem &i2) { if (i1.Method != i2.Method) @@ -597,9 +762,16 @@ static bool FlagsAreSame(const CItem &i1, const CItem &i2) if (i1.Method <= NFileHeader::NCompressionMethod::kImploded) mask = 0x7FFF; } + + // we can ignore utf8 flag, if name is ascii + if ((i1.Flags ^ i2.Flags) & NFileHeader::NFlags::kUtf8) + if (i1.Name.IsAscii() && i2.Name.IsAscii()) + mask &= ~NFileHeader::NFlags::kUtf8; + return ((i1.Flags & mask) == (i2.Flags & mask)); } + // #ifdef _WIN32 static bool AreEqualPaths_IgnoreSlashes(const char *s1, const char *s2) { @@ -623,6 +795,7 @@ static bool AreEqualPaths_IgnoreSlashes(const char *s1, const char *s2) } // #endif + static bool AreItemsEqual(const CItemEx &localItem, const CItemEx &cdItem) { if (!FlagsAreSame(cdItem, localItem)) @@ -670,16 +843,52 @@ static bool AreItemsEqual(const CItemEx &localItem, const CItemEx &cdItem) return true; } -HRESULT CInArchive::ReadLocalItemAfterCdItem(CItemEx &item) + +HRESULT CInArchive::ReadLocalItemAfterCdItem(CItemEx &item, bool &isAvail) { + isAvail = true; if (item.FromLocal) return S_OK; try { - UInt64 offset = ArcInfo.Base + item.LocalHeaderPos; - if (ArcInfo.Base < 0 && (Int64)offset < 0) - return S_FALSE; - RINOK(Seek(offset)); + UInt64 offset = item.LocalHeaderPos; + + if (IsMultiVol) + { + if (item.Disk >= Vols.Streams.Size()) + { + isAvail = false; + return S_FALSE; + } + IInStream *str2 = Vols.Streams[item.Disk].Stream; + if (!str2) + { + isAvail = false; + return S_FALSE; + } + RINOK(str2->Seek(offset, STREAM_SEEK_SET, NULL)); + Stream = str2; + Vols.StreamIndex = item.Disk; + } + else + { + if (UseDisk_in_SingleVol && item.Disk != EcdVolIndex) + { + isAvail = false; + return S_FALSE; + } + Stream = StreamRef; + + offset += ArcInfo.Base; + if (ArcInfo.Base < 0 && (Int64)offset < 0) + { + isAvail = false; + return S_FALSE; + } + RINOK(Seek(offset)); + } + + CItemEx localItem; if (ReadUInt32() != NSignature::kLocalFileHeader) return S_FALSE; @@ -694,6 +903,7 @@ HRESULT CInArchive::ReadLocalItemAfterCdItem(CItemEx &item) return S_OK; } + HRESULT CInArchive::ReadLocalItemDescriptor(CItemEx &item) { const unsigned kBufSize = (1 << 12); @@ -726,7 +936,8 @@ HRESULT CInArchive::ReadLocalItemDescriptor(CItemEx &item) item.Crc = Get32(buf + i + 4); item.PackSize = descriptorPackSize; item.Size = Get32(buf + i + 12); - return IncreaseRealPosition((Int64)(Int32)(0 - (numBytesInBuffer - i - kDataDescriptorSize))); + bool isFinished; + return IncreaseRealPosition((Int64)(Int32)(0 - (numBytesInBuffer - i - kDataDescriptorSize)), isFinished); } } } @@ -739,15 +950,18 @@ HRESULT CInArchive::ReadLocalItemDescriptor(CItemEx &item) } } + HRESULT CInArchive::ReadLocalItemAfterCdItemFull(CItemEx &item) { if (item.FromLocal) return S_OK; try { - RINOK(ReadLocalItemAfterCdItem(item)); + bool isAvail = true; + RINOK(ReadLocalItemAfterCdItem(item, isAvail)); if (item.HasDescriptor()) { + // pkzip's version without descriptor is not supported RINOK(Seek(ArcInfo.Base + item.GetDataPosition() + item.PackSize)); if (ReadUInt32() != NSignature::kDataDescriptor) return S_FALSE; @@ -775,6 +989,7 @@ HRESULT CInArchive::ReadLocalItemAfterCdItemFull(CItemEx &item) return S_OK; } + HRESULT CInArchive::ReadCdItem(CItemEx &item) { item.FromCentral = true; @@ -785,30 +1000,24 @@ HRESULT CInArchive::ReadCdItem(CItemEx &item) item.MadeByVersion.HostOS = p[1]; item.ExtractVersion.Version = p[2]; item.ExtractVersion.HostOS = p[3]; - item.Flags = Get16(p + 4); - item.Method = Get16(p + 6); - item.Time = Get32(p + 8); - item.Crc = Get32(p + 12); - item.PackSize = Get32(p + 16); - item.Size = Get32(p + 20); + G16(4, item.Flags); + G16(6, item.Method); + G32(8, item.Time); + G32(12, item.Crc); + G32(16, item.PackSize); + G32(20, item.Size); const unsigned nameSize = Get16(p + 24); const unsigned extraSize = Get16(p + 26); const unsigned commentSize = Get16(p + 28); - UInt32 diskNumberStart = Get16(p + 30); - item.InternalAttrib = Get16(p + 32); - item.ExternalAttrib = Get32(p + 34); - item.LocalHeaderPos = Get32(p + 38); + G16(30, item.Disk); + G16(32, item.InternalAttrib); + G32(34, item.ExternalAttrib); + G32(38, item.LocalHeaderPos); ReadFileName(nameSize, item.Name); if (extraSize > 0) - { - ReadExtra(extraSize, item.CentralExtra, item.Size, item.PackSize, - item.LocalHeaderPos, diskNumberStart); - } + ReadExtra(extraSize, item.CentralExtra, item.Size, item.PackSize, item.LocalHeaderPos, item.Disk); - if (diskNumberStart != 0) - return E_NOTIMPL; - // May be these strings must be deleted /* if (item.IsDir()) @@ -819,19 +1028,6 @@ HRESULT CInArchive::ReadCdItem(CItemEx &item) return S_OK; } -void CCdInfo::ParseEcd(const Byte *p) -{ - NumEntries = Get16(p + 10); - Size = Get32(p + 12); - Offset = Get32(p + 16); -} - -void CCdInfo::ParseEcd64(const Byte *p) -{ - NumEntries = Get64(p + 24); - Size = Get64(p + 40); - Offset = Get64(p + 48); -} HRESULT CInArchive::TryEcd64(UInt64 offset, CCdInfo &cdInfo) { @@ -847,155 +1043,256 @@ HRESULT CInArchive::TryEcd64(UInt64 offset, CCdInfo &cdInfo) UInt64 mainSize = Get64(buf + 4); if (mainSize < kEcd64_MainSize || mainSize > ((UInt64)1 << 32)) return S_FALSE; - cdInfo.ParseEcd64(buf); + cdInfo.ParseEcd64e(buf + 12); return S_OK; } -HRESULT CInArchive::FindCd(CCdInfo &cdInfo) + +HRESULT CInArchive::FindCd(bool checkOffsetMode) { - UInt64 endPosition; - RINOK(Stream->Seek(0, STREAM_SEEK_END, &endPosition)); + CCdInfo &cdInfo = Vols.ecd; + + UInt64 endPos; + + RINOK(Stream->Seek(0, STREAM_SEEK_END, &endPos)); const UInt32 kBufSizeMax = ((UInt32)1 << 16) + kEcdSize + kEcd64Locator_Size + kEcd64_FullSize; - UInt32 bufSize = (endPosition < kBufSizeMax) ? (UInt32)endPosition : kBufSizeMax; + const UInt32 bufSize = (endPos < kBufSizeMax) ? (UInt32)endPos : kBufSizeMax; if (bufSize < kEcdSize) return S_FALSE; CByteArr byteBuffer(bufSize); - UInt64 startPosition = endPosition - bufSize; - RINOK(Stream->Seek(startPosition, STREAM_SEEK_SET, &m_Position)); - if (m_Position != startPosition) + const UInt64 startPos = endPos - bufSize; + RINOK(Stream->Seek(startPos, STREAM_SEEK_SET, &m_Position)); + if (m_Position != startPos) return S_FALSE; RINOK(ReadStream_FALSE(Stream, byteBuffer, bufSize)); - const Byte *buf = byteBuffer; - for (UInt32 i = bufSize - kEcdSize;; i--) + for (UInt32 i = bufSize - kEcdSize + 1;;) { - if (buf[i] != 0x50) + if (i == 0) + return S_FALSE; + + const Byte *buf = byteBuffer; + + for (;;) { - if (i == 0) return S_FALSE; i--; - if (buf[i] != 0x50) - { - if (i == 0) return S_FALSE; - continue; - } + if (buf[i] == 0x50) + break; + if (i == 0) + return S_FALSE; } - if (Get32(buf + i) == NSignature::kEcd) + + if (Get32(buf + i) != NSignature::kEcd) + continue; + + cdInfo.ParseEcd32(buf + i); + + if (i >= kEcd64Locator_Size) { - if (i >= kEcd64_FullSize + kEcd64Locator_Size) + const Byte *locatorPtr = buf + i - kEcd64Locator_Size; + if (Get32(locatorPtr) == NSignature::kEcd64Locator) { - const Byte *locator = buf + i - kEcd64Locator_Size; - if (Get32(locator) == NSignature::kEcd64Locator && - Get32(locator + 4) == 0) // number of the disk with the start of the zip64 ECD + CLocator locator; + locator.Parse(locatorPtr + 4); + if ((cdInfo.ThisDisk == locator.NumDisks - 1 || cdInfo.ThisDisk == 0xFFFF) + && locator.Ecd64Disk < locator.NumDisks) { - // Most of the zip64 use fixed size Zip64 ECD + if (locator.Ecd64Disk != cdInfo.ThisDisk && cdInfo.ThisDisk != 0xFFFF) + return E_NOTIMPL; - UInt64 ecd64Offset = Get64(locator + 8); - UInt64 absEcd64 = endPosition - bufSize + i - (kEcd64Locator_Size + kEcd64_FullSize); + // Most of the zip64 use fixed size Zip64 ECD + // we try relative backward reading. + + UInt64 absEcd64 = endPos - bufSize + i - (kEcd64Locator_Size + kEcd64_FullSize); + if (checkOffsetMode || absEcd64 == locator.Ecd64Offset) { - const Byte *ecd64 = locator - kEcd64_FullSize; - if (Get32(ecd64) == NSignature::kEcd64 && - Get64(ecd64 + 4) == kEcd64_MainSize) + const Byte *ecd64 = locatorPtr - kEcd64_FullSize; + if (Get32(ecd64) == NSignature::kEcd64) { - cdInfo.ParseEcd64(ecd64); - ArcInfo.Base = absEcd64 - ecd64Offset; - return S_OK; + UInt64 mainEcd64Size = Get64(ecd64 + 4); + if (mainEcd64Size == kEcd64_MainSize) + { + cdInfo.ParseEcd64e(ecd64 + 12); + ArcInfo.Base = absEcd64 - locator.Ecd64Offset; + // ArcInfo.BaseVolIndex = cdInfo.ThisDisk; + return S_OK; + } } } - + // some zip64 use variable size Zip64 ECD. - // we try to find it - if (absEcd64 != ecd64Offset) + // we try to use absolute offset from locator. + + if (absEcd64 != locator.Ecd64Offset) { - if (TryEcd64(ecd64Offset, cdInfo) == S_OK) + if (TryEcd64(locator.Ecd64Offset, cdInfo) == S_OK) { ArcInfo.Base = 0; + // ArcInfo.BaseVolIndex = cdInfo.ThisDisk; return S_OK; } } - if (ArcInfo.MarkerPos != 0 && - ArcInfo.MarkerPos + ecd64Offset != absEcd64) + + // for variable Zip64 ECD with for archives with offset != 0. + + if (checkOffsetMode + && ArcInfo.MarkerPos != 0 + && ArcInfo.MarkerPos + locator.Ecd64Offset != absEcd64) { - if (TryEcd64(ArcInfo.MarkerPos + ecd64Offset, cdInfo) == S_OK) + if (TryEcd64(ArcInfo.MarkerPos + locator.Ecd64Offset, cdInfo) == S_OK) { ArcInfo.Base = ArcInfo.MarkerPos; + // ArcInfo.BaseVolIndex = cdInfo.ThisDisk; return S_OK; } } } } - if (Get32(buf + i + 4) == 0) // ThisDiskNumber, StartCentralDirectoryDiskNumber; + } + + // bool isVolMode = (Vols.EndVolIndex != -1); + // UInt32 searchDisk = (isVolMode ? Vols.EndVolIndex : 0); + + if (/* searchDisk == thisDisk && */ cdInfo.CdDisk <= cdInfo.ThisDisk) + { + // if (isVolMode) + { + if (cdInfo.CdDisk != cdInfo.ThisDisk) + return S_OK; + } + + UInt64 absEcdPos = endPos - bufSize + i; + UInt64 cdEnd = cdInfo.Size + cdInfo.Offset; + ArcInfo.Base = 0; + // ArcInfo.BaseVolIndex = cdInfo.ThisDisk; + if (absEcdPos != cdEnd) { - cdInfo.ParseEcd(buf + i); - UInt64 absEcdPos = endPosition - bufSize + i; - UInt64 cdEnd = cdInfo.Size + cdInfo.Offset; - ArcInfo.Base = 0; - if (absEcdPos != cdEnd) + /* + if (cdInfo.Offset <= 16 && cdInfo.Size != 0) { - /* - if (cdInfo.Offset <= 16 && cdInfo.Size != 0) - { - // here we support some rare ZIP files with Central directory at the start - ArcInfo.Base = 0; - } - else - */ - ArcInfo.Base = absEcdPos - cdEnd; + // here we support some rare ZIP files with Central directory at the start + ArcInfo.Base = 0; } - return S_OK; + else + */ + ArcInfo.Base = absEcdPos - cdEnd; } + return S_OK; } - if (i == 0) - return S_FALSE; } } -HRESULT CInArchive::TryReadCd(CObjectVector<CItemEx> &items, UInt64 cdOffset, UInt64 cdSize, CProgressVirt *progress) +HRESULT CInArchive::TryReadCd(CObjectVector<CItemEx> &items, const CCdInfo &cdInfo, UInt64 cdOffset, UInt64 cdSize) { items.Clear(); - RINOK(Stream->Seek(cdOffset, STREAM_SEEK_SET, &m_Position)); - if (m_Position != cdOffset) - return S_FALSE; + + ISequentialInStream *stream; + + if (!IsMultiVol) + { + stream = this->StartStream; + Vols.StreamIndex = -1; + RINOK(this->StartStream->Seek(cdOffset, STREAM_SEEK_SET, &m_Position)); + if (m_Position != cdOffset) + return S_FALSE; + } + else + { + if (cdInfo.CdDisk >= Vols.Streams.Size()) + return S_FALSE; + IInStream *str2 = Vols.Streams[cdInfo.CdDisk].Stream; + if (!str2) + return S_FALSE; + RINOK(str2->Seek(cdOffset, STREAM_SEEK_SET, NULL)); + stream = str2; + Vols.NeedSeek = false; + Vols.StreamIndex = cdInfo.CdDisk; + m_Position = cdOffset; + } + + _inBuffer.SetStream(stream); _inBuffer.Init(); _inBufMode = true; - while (m_Position - cdOffset < cdSize) + _processedCnt = 0; + + while (_processedCnt < cdSize) { + CanStartNewVol = true; if (ReadUInt32() != NSignature::kCentralFileHeader) return S_FALSE; - CItemEx cdItem; - RINOK(ReadCdItem(cdItem)); - items.Add(cdItem); - if (progress && (items.Size() & 0xFFF) == 0) - RINOK(progress->SetCompletedCD(items.Size())); + { + CItemEx cdItem; + RINOK(ReadCdItem(cdItem)); + items.Add(cdItem); + } + if (Callback && (items.Size() & 0xFFF) == 0) + { + const UInt64 numFiles = items.Size(); + RINOK(Callback->SetCompleted(&numFiles, NULL)); + } } - return (m_Position - cdOffset == cdSize) ? S_OK : S_FALSE; + + CanStartNewVol = true; + + return (_processedCnt == cdSize) ? S_OK : S_FALSE; } -HRESULT CInArchive::ReadCd(CObjectVector<CItemEx> &items, UInt64 &cdOffset, UInt64 &cdSize, CProgressVirt *progress) + +HRESULT CInArchive::ReadCd(CObjectVector<CItemEx> &items, UInt32 &cdDisk, UInt64 &cdOffset, UInt64 &cdSize) { - CCdInfo cdInfo; - RINOK(FindCd(cdInfo)); + bool checkOffsetMode = true; + + if (IsMultiVol) + { + if (Vols.EndVolIndex == -1) + return S_FALSE; + Stream = Vols.Streams[Vols.EndVolIndex].Stream; + if (!Vols.StartIsZip) + checkOffsetMode = false; + } + else + Stream = StartStream; + + if (!Vols.ecd_wasRead) + { + RINOK(FindCd(checkOffsetMode)); + } + + CCdInfo &cdInfo = Vols.ecd; + HRESULT res = S_FALSE; + cdSize = cdInfo.Size; cdOffset = cdInfo.Offset; - if (progress) - progress->SetTotalCD(cdInfo.NumEntries); - res = TryReadCd(items, ArcInfo.Base + cdOffset, cdSize, progress); - if (res == S_FALSE && ArcInfo.Base == 0) + cdDisk = cdInfo.CdDisk; + + if (Callback) { - res = TryReadCd(items, ArcInfo.MarkerPos + cdOffset, cdSize, progress); + RINOK(Callback->SetTotal(&cdInfo.NumEntries, NULL)); + } + + const UInt64 base = (IsMultiVol ? 0 : ArcInfo.Base); + res = TryReadCd(items, cdInfo, base + cdOffset, cdSize); + + if (res == S_FALSE && !IsMultiVol && base != ArcInfo.MarkerPos) + { + // do we need that additional attempt to read cd? + res = TryReadCd(items, cdInfo, ArcInfo.MarkerPos + cdOffset, cdSize); if (res == S_OK) ArcInfo.Base = ArcInfo.MarkerPos; } + return res; } -static HRESULT FindItem(const CObjectVector<CItemEx> &items, UInt64 offset) + +static int FindItem(const CObjectVector<CItemEx> &items, const CItemEx &item) { unsigned left = 0, right = items.Size(); for (;;) @@ -1003,42 +1300,66 @@ static HRESULT FindItem(const CObjectVector<CItemEx> &items, UInt64 offset) if (left >= right) return -1; unsigned index = (left + right) / 2; - UInt64 position = items[index].LocalHeaderPos; - if (offset == position) + const CItemEx &item2 = items[index]; + if (item.Disk < item2.Disk) + right = index; + else if (item.Disk > item2.Disk) + left = index + 1; + else if (item.LocalHeaderPos == item2.LocalHeaderPos) return index; - if (offset < position) + else if (item.LocalHeaderPos < item2.LocalHeaderPos) right = index; else left = index + 1; } } -bool IsStrangeItem(const CItem &item) +static bool IsStrangeItem(const CItem &item) { return item.Name.Len() > (1 << 14) || item.Method > (1 << 8); } -HRESULT CInArchive::ReadLocals( - CObjectVector<CItemEx> &items, CProgressVirt *progress) +HRESULT CInArchive::ReadLocals(CObjectVector<CItemEx> &items) { items.Clear(); + while (m_Signature == NSignature::kLocalFileHeader) { CItemEx item; - item.LocalHeaderPos = m_Position - 4 - ArcInfo.MarkerPos; + item.LocalHeaderPos = m_Position - 4; + if (!IsMultiVol) + item.LocalHeaderPos -= ArcInfo.MarkerPos; + // we write ralative LocalHeaderPos here. Later we can correct it to real Base. + try { ReadLocalItem(item); item.FromLocal = true; + bool isFinished = false; + if (item.HasDescriptor()) ReadLocalItemDescriptor(item); else { - RINOK(IncreaseRealPosition(item.PackSize)); + /* + if (IsMultiVol) + { + const int kStep = 10000; + RINOK(IncreaseRealPosition(-kStep, isFinished)); + RINOK(IncreaseRealPosition(item.PackSize + kStep, isFinished)); + } + else + */ + RINOK(IncreaseRealPosition(item.PackSize, isFinished)); } + items.Add(item); + + if (isFinished) + throw CUnexpectEnd(); + m_Signature = ReadUInt32(); } catch (CUnexpectEnd &) @@ -1048,34 +1369,364 @@ HRESULT CInArchive::ReadLocals( throw; } - if (progress && (items.Size() & 0xFF) == 0) - RINOK(progress->SetCompletedLocal(items.Size(), item.LocalHeaderPos)); + if (Callback && (items.Size() & 0xFF) == 0) + { + const UInt64 numFiles = items.Size(); + UInt64 numBytes = 0; + // if (!sMultiVol) + numBytes = item.LocalHeaderPos; + RINOK(Callback->SetCompleted(&numFiles, &numBytes)); + } } if (items.Size() == 1 && m_Signature != NSignature::kCentralFileHeader) if (IsStrangeItem(items[0])) return S_FALSE; + + return S_OK; +} + + + +HRESULT CVols::ParseArcName(IArchiveOpenVolumeCallback *volCallback) +{ + UString name; + { + NWindows::NCOM::CPropVariant prop; + RINOK(volCallback->GetProperty(kpidName, &prop)); + if (prop.vt != VT_BSTR) + return S_OK; + name = prop.bstrVal; + } + + UString base = name; + int dotPos = name.ReverseFind_Dot(); + + if (dotPos < 0) + return S_OK; + + base.DeleteFrom(dotPos + 1); + + const UString ext = name.Ptr(dotPos + 1); + StartVolIndex = (Int32)(-1); + + if (ext.IsEmpty()) + return S_OK; + else + { + wchar_t c = ext[0]; + IsUpperCase = (c >= 'A' && c <= 'Z'); + if (ext.IsEqualTo_Ascii_NoCase("zip")) + { + BaseName = base; + StartIsZ = true; + StartIsZip = true; + return S_OK; + } + else if (ext.IsEqualTo_Ascii_NoCase("exe")) + { + StartIsExe = true; + BaseName = base; + StartVolIndex = 0; + } + else if (ext[0] == 'z' || ext[0] == 'Z') + { + if (ext.Len() < 3) + return S_OK; + const wchar_t *end = NULL; + UInt32 volNum = ConvertStringToUInt32(ext.Ptr(1), &end); + if (*end != 0 || volNum < 1 || volNum > ((UInt32)1 << 30)) + return S_OK; + StartVolIndex = volNum - 1; + BaseName = base; + StartIsZ = true; + } + else + return S_OK; + } + + UString volName = BaseName; + volName.AddAscii(IsUpperCase ? "ZIP" : "zip"); + HRESULT result = volCallback->GetStream(volName, &ZipStream); + if (result == S_FALSE || !ZipStream) + { + if (MissingName.IsEmpty()) + MissingName = volName; + return S_OK; + } + + return result; +} + + +HRESULT CInArchive::ReadVols2(IArchiveOpenVolumeCallback *volCallback, + unsigned start, int lastDisk, int zipDisk, unsigned numMissingVolsMax, unsigned &numMissingVols) +{ + numMissingVols = 0; + + for (unsigned i = start;; i++) + { + if (lastDisk >= 0 && i >= (unsigned)lastDisk) + break; + + if (i < Vols.Streams.Size()) + if (Vols.Streams[i].Stream) + continue; + + CMyComPtr<IInStream> stream; + + if ((int)i == zipDisk) + { + stream = Vols.ZipStream; + } + else if ((int)i == Vols.StartVolIndex) + { + stream = StartStream; + } + else + { + UString volName = Vols.BaseName; + { + volName += (wchar_t)(Vols.IsUpperCase ? 'Z' : 'z'); + { + char s[32]; + ConvertUInt32ToString(i + 1, s); + unsigned len = (unsigned)strlen(s); + while (len < 2) + { + volName += (wchar_t)'0'; + len++; + } + volName.AddAscii(s); + } + } + + HRESULT result = volCallback->GetStream(volName, &stream); + if (result != S_OK && result != S_FALSE) + return result; + if (result == S_FALSE || !stream) + { + if (Vols.MissingName.IsEmpty()) + Vols.MissingName = volName; + numMissingVols++; + if (numMissingVols > numMissingVolsMax) + return S_OK; + if (lastDisk == -1 && numMissingVols != 0) + return S_OK; + continue; + } + } + + UInt64 size; + + UInt64 pos; + RINOK(stream->Seek(0, STREAM_SEEK_CUR, &pos)); + RINOK(stream->Seek(0, STREAM_SEEK_END, &size)); + RINOK(stream->Seek(pos, STREAM_SEEK_SET, NULL)); + + while (i >= Vols.Streams.Size()) + Vols.Streams.AddNew(); + + CVols::CSubStreamInfo &ss = Vols.Streams[i]; + Vols.NumVols++; + ss.Stream = stream; + ss.Size = size; + + if ((int)i == zipDisk) + { + Vols.EndVolIndex = Vols.Streams.Size() - 1; + break; + } + } + return S_OK; } +HRESULT CInArchive::ReadVols() +{ + CMyComPtr<IArchiveOpenVolumeCallback> volCallback; + + Callback->QueryInterface(IID_IArchiveOpenVolumeCallback, (void **)&volCallback); + if (!volCallback) + return S_OK; + + RINOK(Vols.ParseArcName(volCallback)); + + int startZIndex = Vols.StartVolIndex; + + if (!Vols.StartIsZ) + { + // if (!Vols.StartIsExe) + return S_OK; + } + + int zipDisk = -1; + int cdDisk = -1; + + if (Vols.StartIsZip) + Vols.ZipStream = StartStream; + + bool cdOK = false; + + if (Vols.ZipStream) + { + Stream = Vols.ZipStream; + HRESULT res = FindCd(true); + CCdInfo &ecd = Vols.ecd; + if (res == S_OK) + { + zipDisk = ecd.ThisDisk; + Vols.ecd_wasRead = true; + if (ecd.ThisDisk == 0 + || ecd.ThisDisk >= ((UInt32)1 << 30) + || ecd.ThisDisk < ecd.CdDisk) + return S_OK; + cdDisk = ecd.CdDisk; + if (Vols.StartVolIndex < 0) + Vols.StartVolIndex = ecd.ThisDisk; + // Vols.StartVolIndex = ecd.ThisDisk; + // Vols.EndVolIndex = ecd.ThisDisk; + unsigned numMissingVols; + if (cdDisk == zipDisk) + cdOK = true; + else + { + RINOK(ReadVols2(volCallback, cdDisk, zipDisk, zipDisk, 0, numMissingVols)); + if (numMissingVols == 0) + cdOK = false; + } + } + else if (res != S_FALSE) + return res; + } + + if (Vols.Streams.Size() > 0) + IsMultiVol = true; + + if (Vols.StartVolIndex < 0) + return S_OK; + + unsigned numMissingVols; + + if (cdDisk != 0) + { + RINOK(ReadVols2(volCallback, 0, cdDisk < 0 ? -1 : cdDisk, zipDisk, 1 << 10, numMissingVols)); + } + + if (Vols.ZipStream) + { + if (Vols.Streams.IsEmpty()) + if (zipDisk > (1 << 10)) + return S_OK; + RINOK(ReadVols2(volCallback, zipDisk, zipDisk + 1, zipDisk, 0, numMissingVols)); + } + + if (!Vols.Streams.IsEmpty()) + { + IsMultiVol = true; + /* + if (cdDisk) + IsMultiVol = true; + */ + if (startZIndex >= 0) + { + if (Vols.Streams.Size() >= (unsigned)startZIndex) + { + for (unsigned i = 0; i < (unsigned)startZIndex; i++) + if (!Vols.Streams[i].Stream) + { + Vols.StartParsingVol = startZIndex; + break; + } + } + } + } + + return S_OK; +} + + + + + + + +HRESULT CVols::Read(void *data, UInt32 size, UInt32 *processedSize) +{ + if (processedSize) + *processedSize = 0; + if (size == 0) + return S_OK; + + for (;;) + { + if (StreamIndex < 0) + return S_OK; + if ((unsigned)StreamIndex >= Streams.Size()) + return S_OK; + const CVols::CSubStreamInfo &s = Streams[StreamIndex]; + if (!s.Stream) + return S_FALSE; + if (NeedSeek) + { + RINOK(s.Stream->Seek(0, STREAM_SEEK_SET, NULL)); + NeedSeek = false; + } + UInt32 realProcessedSize = 0; + HRESULT res = s.Stream->Read(data, size, &realProcessedSize); + if (processedSize) + *processedSize = realProcessedSize; + if (res != S_OK) + return res; + if (realProcessedSize != 0) + return res; + StreamIndex++; + NeedSeek = true; + } +} + +STDMETHODIMP CVolStream::Read(void *data, UInt32 size, UInt32 *processedSize) +{ + return Vols->Read(data, size, processedSize); +} + + + + #define COPY_ECD_ITEM_16(n) if (!isZip64 || ecd. n != 0xFFFF) ecd64. n = ecd. n; #define COPY_ECD_ITEM_32(n) if (!isZip64 || ecd. n != 0xFFFFFFFF) ecd64. n = ecd. n; -HRESULT CInArchive::ReadHeaders2(CObjectVector<CItemEx> &items, CProgressVirt *progress) + +HRESULT CInArchive::ReadHeaders2(CObjectVector<CItemEx> &items) { - items.Clear(); + HRESULT res = S_OK; + + bool localsWereRead = false; + UInt64 cdSize = 0, cdRelatOffset = 0, cdAbsOffset = 0; + UInt32 cdDisk = 0; + if (!_inBuffer.Create(1 << 15)) + return E_OUTOFMEMORY; + + if (!MarkerIsFound) + { + IsArc = true; + res = ReadCd(items, cdDisk, cdRelatOffset, cdSize); + if (res == S_OK) + m_Signature = ReadUInt32(); + } + else + { + // m_Signature must be kLocalFileHeader or kEcd // m_Position points to next byte after signature RINOK(Stream->Seek(m_Position, STREAM_SEEK_SET, NULL)); - if (!_inBuffer.Create(1 << 15)) - return E_OUTOFMEMORY; _inBuffer.SetStream(Stream); bool needReadCd = true; - bool localsWereRead = false; + if (m_Signature == NSignature::kEcd) { // It must be empty archive or backware archive @@ -1097,9 +1748,6 @@ HRESULT CInArchive::ReadHeaders2(CObjectVector<CItemEx> &items, CProgressVirt *p RINOK(Stream->Seek(ArcInfo.MarkerPos2 + 4, STREAM_SEEK_SET, &m_Position)); } - UInt64 cdSize = 0, cdRelatOffset = 0, cdAbsOffset = 0; - HRESULT res = S_OK; - if (needReadCd) { CItemEx firstItem; @@ -1116,7 +1764,7 @@ HRESULT CInArchive::ReadHeaders2(CObjectVector<CItemEx> &items, CProgressVirt *p } IsArc = true; - res = ReadCd(items, cdRelatOffset, cdSize, progress); + res = ReadCd(items, cdDisk, cdRelatOffset, cdSize); if (res == S_OK) m_Signature = ReadUInt32(); } @@ -1130,16 +1778,27 @@ HRESULT CInArchive::ReadHeaders2(CObjectVector<CItemEx> &items, CProgressVirt *p if (res == S_OK) { // we can't read local items here to keep _inBufMode state - firstItem.LocalHeaderPos = ArcInfo.MarkerPos2 - ArcInfo.Base; - int index = FindItem(items, firstItem.LocalHeaderPos); - if (index == -1) - res = S_FALSE; - else if (!AreItemsEqual(firstItem, items[index])) + if ((Int64)ArcInfo.MarkerPos2 < ArcInfo.Base) res = S_FALSE; - ArcInfo.CdWasRead = true; - ArcInfo.FirstItemRelatOffset = items[0].LocalHeaderPos; + else + { + firstItem.LocalHeaderPos = ArcInfo.MarkerPos2 - ArcInfo.Base; + int index = FindItem(items, firstItem); + if (index == -1) + res = S_FALSE; + else if (!AreItemsEqual(firstItem, items[index])) + res = S_FALSE; + else + { + ArcInfo.CdWasRead = true; + ArcInfo.FirstItemRelatOffset = items[0].LocalHeaderPos; + } + } } } + } + + CObjectVector<CItemEx> cdItems; @@ -1148,19 +1807,32 @@ HRESULT CInArchive::ReadHeaders2(CObjectVector<CItemEx> &items, CProgressVirt *p if (res == S_FALSE) { - // CD doesn't match firstItem so we clear items and read Locals. + // CD doesn't match firstItem, + // so we clear items and read Locals. items.Clear(); localsWereRead = true; _inBufMode = false; ArcInfo.Base = ArcInfo.MarkerPos; + + if (IsMultiVol) + { + Vols.StreamIndex = Vols.StartParsingVol; + if (Vols.StartParsingVol >= (int)Vols.Streams.Size()) + return S_FALSE; + Stream = Vols.Streams[Vols.StartParsingVol].Stream; + if (!Stream) + return S_FALSE; + } + RINOK(Stream->Seek(ArcInfo.MarkerPos2, STREAM_SEEK_SET, &m_Position)); m_Signature = ReadUInt32(); - - RINOK(ReadLocals(items, progress)); + RINOK(ReadLocals(items)); + if (m_Signature != NSignature::kCentralFileHeader) { - m_Position -= 4; + // if (!UnexpectedEnd) + m_Position -= 4; NoCentralDir = true; HeadersError = true; return S_OK; @@ -1168,15 +1840,24 @@ HRESULT CInArchive::ReadHeaders2(CObjectVector<CItemEx> &items, CProgressVirt *p _inBufMode = true; _inBuffer.Init(); + cdAbsOffset = m_Position - 4; + cdDisk = Vols.StreamIndex; for (;;) { CItemEx cdItem; + CanStartNewVol = true; + RINOK(ReadCdItem(cdItem)); + cdItems.Add(cdItem); - if (progress && (cdItems.Size() & 0xFFF) == 0) - RINOK(progress->SetCompletedCD(items.Size())); + if (Callback && (cdItems.Size() & 0xFFF) == 0) + { + const UInt64 numFiles = items.Size(); + RINOK(Callback->SetCompleted(&numFiles, NULL)); + } + CanStartNewVol = true; m_Signature = ReadUInt32(); if (m_Signature != NSignature::kCentralFileHeader) break; @@ -1193,95 +1874,159 @@ HRESULT CInArchive::ReadHeaders2(CObjectVector<CItemEx> &items, CProgressVirt *p } } - CEcd64 ecd64; + + + CCdInfo ecd64; + CLocator locator; bool isZip64 = false; - UInt64 ecd64AbsOffset = m_Position - 4; + const UInt64 ecd64AbsOffset = m_Position - 4; + int ecd64Disk = -1; if (m_Signature == NSignature::kEcd64) { + ecd64Disk = Vols.StreamIndex; + IsZip64 = isZip64 = true; - UInt64 recordSize = ReadUInt64(); - const unsigned kBufSize = kEcd64_MainSize; - Byte buf[kBufSize]; - SafeReadBytes(buf, kBufSize); - ecd64.Parse(buf); + { + const UInt64 recordSize = ReadUInt64(); + if (recordSize < kEcd64_MainSize) + { + HeadersError = true; + return S_OK; + } + + { + const unsigned kBufSize = kEcd64_MainSize; + Byte buf[kBufSize]; + SafeReadBytes(buf, kBufSize); + ecd64.ParseEcd64e(buf); + } + + Skip64(recordSize - kEcd64_MainSize); + } - Skip64(recordSize - kEcd64_MainSize); - m_Signature = ReadUInt32(); - if (ecd64.thisDiskNumber != 0 || ecd64.startCDDiskNumber != 0) - return E_NOTIMPL; + m_Signature = ReadUInt32(); - if (needSetBase) + if (m_Signature != NSignature::kEcd64Locator) + { + HeadersError = true; + return S_OK; + } + { - ArcInfo.Base = cdAbsOffset - ecd64.cdStartOffset; - cdRelatOffset = ecd64.cdStartOffset; - needSetBase = false; + const unsigned kBufSize = 16; + Byte buf[kBufSize]; + SafeReadBytes(buf, kBufSize); + locator.Parse(buf); } - if (ecd64.numEntriesInCDOnThisDisk != numCdItems || - ecd64.numEntriesInCD != numCdItems || - ecd64.cdSize != cdSize || - (ecd64.cdStartOffset != cdRelatOffset && - (!items.IsEmpty()))) - return S_FALSE; + m_Signature = ReadUInt32(); } - if (m_Signature == NSignature::kEcd64Locator) + + if (m_Signature != NSignature::kEcd) { - if (!isZip64) - return S_FALSE; - /* UInt32 startEndCDDiskNumber = */ ReadUInt32(); - UInt64 ecd64RelatOffset = ReadUInt64(); - /* UInt32 numberOfDisks = */ ReadUInt32(); - if (ecd64AbsOffset != ArcInfo.Base + ecd64RelatOffset) - return S_FALSE; - m_Signature = ReadUInt32(); + HeadersError = true; + return S_OK; } + - if (m_Signature != NSignature::kEcd) - return S_FALSE; + // ---------- ECD ---------- - const unsigned kBufSize = kEcdSize - 4; - Byte buf[kBufSize]; - SafeReadBytes(buf, kBufSize); CEcd ecd; - ecd.Parse(buf); + { + const unsigned kBufSize = kEcdSize - 4; + Byte buf[kBufSize]; + SafeReadBytes(buf, kBufSize); + ecd.Parse(buf); + } - COPY_ECD_ITEM_16(thisDiskNumber); - COPY_ECD_ITEM_16(startCDDiskNumber); - COPY_ECD_ITEM_16(numEntriesInCDOnThisDisk); - COPY_ECD_ITEM_16(numEntriesInCD); - COPY_ECD_ITEM_32(cdSize); - COPY_ECD_ITEM_32(cdStartOffset); + COPY_ECD_ITEM_16(ThisDisk); + COPY_ECD_ITEM_16(CdDisk); + COPY_ECD_ITEM_16(NumEntries_in_ThisDisk); + COPY_ECD_ITEM_16(NumEntries); + COPY_ECD_ITEM_32(Size); + COPY_ECD_ITEM_32(Offset); - if (needSetBase) + if (IsMultiVol) { - ArcInfo.Base = cdAbsOffset - ecd64.cdStartOffset; - cdRelatOffset = ecd64.cdStartOffset; - needSetBase = false; + if (cdDisk != (int)ecd64.CdDisk) + HeadersError = true; } + else if (needSetBase) + { + if (isZip64) + { + if (ecd64Disk == Vols.StartVolIndex) + { + ArcInfo.Base = ecd64AbsOffset - locator.Ecd64Offset; + // cdRelatOffset = ecd64.Offset; + needSetBase = false; + } + } + else + { + if ((int)cdDisk == Vols.StartVolIndex) + { + ArcInfo.Base = cdAbsOffset - ecd64.Offset; + cdRelatOffset = ecd64.Offset; + needSetBase = false; + } + } + } + + EcdVolIndex = ecd64.ThisDisk; - if (localsWereRead && (UInt64)ArcInfo.Base != ArcInfo.MarkerPos) + if (!IsMultiVol) { - UInt64 delta = ArcInfo.MarkerPos - ArcInfo.Base; - for (unsigned i = 0; i < items.Size(); i++) - items[i].LocalHeaderPos += delta; + UseDisk_in_SingleVol = true; + + if (localsWereRead) + { + if ((UInt64)ArcInfo.Base != ArcInfo.MarkerPos) + { + const UInt64 delta = ArcInfo.MarkerPos - ArcInfo.Base; + FOR_VECTOR (i, items) + items[i].LocalHeaderPos += delta; + } + + if (EcdVolIndex != 0) + { + FOR_VECTOR (i, items) + items[i].Disk = EcdVolIndex; + } + } } + if (isZip64) + { + if (ecd64.ThisDisk == 0 && ecd64AbsOffset != ArcInfo.Base + locator.Ecd64Offset + // || ecd64.NumEntries_in_ThisDisk != numCdItems + || ecd64.NumEntries != numCdItems + || ecd64.Size != cdSize + || (ecd64.Offset != cdRelatOffset && !items.IsEmpty())) + { + HeadersError = true; + return S_OK; + } + } // ---------- merge Central Directory Items ---------- if (!cdItems.IsEmpty()) { - for (unsigned i = 0; i < cdItems.Size(); i++) + CObjectVector<CItemEx> items2; + + FOR_VECTOR (i, cdItems) { const CItemEx &cdItem = cdItems[i]; - int index = FindItem(items, cdItem.LocalHeaderPos); + int index = FindItem(items, cdItem); if (index == -1) { - items.Add(cdItem); + items2.Add(cdItem); + HeadersError = true; continue; } CItemEx &item = items[index]; @@ -1292,9 +2037,11 @@ HRESULT CInArchive::ReadHeaders2(CObjectVector<CItemEx> &items, CProgressVirt *p // item.ExtractVersion != cdItem.ExtractVersion || !FlagsAreSame(item, cdItem) || item.Crc != cdItem.Crc) + { + HeadersError = true; continue; + } - // item.LocalHeaderPos = cdItem.LocalHeaderPos; // item.Name = cdItem.Name; item.MadeByVersion = cdItem.MadeByVersion; item.CentralExtra = cdItem.CentralExtra; @@ -1303,33 +2050,47 @@ HRESULT CInArchive::ReadHeaders2(CObjectVector<CItemEx> &items, CProgressVirt *p item.Comment = cdItem.Comment; item.FromCentral = cdItem.FromCentral; } + + items += items2; } - if (ecd64.thisDiskNumber != 0 || ecd64.startCDDiskNumber != 0) - return E_NOTIMPL; - + + if (ecd.NumEntries < ecd.NumEntries_in_ThisDisk) + HeadersError = true; + + if (ecd.ThisDisk == 0) + { + // if (isZip64) + { + if (ecd.NumEntries != ecd.NumEntries_in_ThisDisk) + HeadersError = true; + } + } + + if (ecd.NumEntries > items.Size()) + HeadersError = true; + if (isZip64) { - if (ecd64.numEntriesInCDOnThisDisk != items.Size()) + if (ecd64.NumEntries != items.Size()) HeadersError = true; } else { // old 7-zip could store 32-bit number of CD items to 16-bit field. - if ((UInt16)ecd64.numEntriesInCDOnThisDisk != (UInt16)numCdItems || - (UInt16)ecd64.numEntriesInCDOnThisDisk != (UInt16)items.Size()) + /* + if ((UInt16)ecd64.NumEntries == (UInt16)items.Size()) HeadersError = true; + */ } - ReadBuffer(ArcInfo.Comment, ecd.commentSize); + ReadBuffer(ArcInfo.Comment, ecd.CommentSize); _inBufMode = false; _inBuffer.Free(); - if ( - (UInt16)ecd64.numEntriesInCD != ((UInt16)numCdItems) || - (UInt32)ecd64.cdSize != (UInt32)cdSize || - ((UInt32)(ecd64.cdStartOffset) != (UInt32)cdRelatOffset && - (!items.IsEmpty()))) + if ((UInt16)ecd64.NumEntries != (UInt16)numCdItems + || (UInt32)ecd64.Size != (UInt32)cdSize + || ((UInt32)ecd64.Offset != (UInt32)cdRelatOffset && !items.IsEmpty())) { // return S_FALSE; HeadersError = true; @@ -1340,39 +2101,170 @@ HRESULT CInArchive::ReadHeaders2(CObjectVector<CItemEx> &items, CProgressVirt *p } -HRESULT CInArchive::ReadHeaders(CObjectVector<CItemEx> &items, CProgressVirt *progress) + +HRESULT CInArchive::Open(IInStream *stream, const UInt64 *searchLimit, + IArchiveOpenCallback *callback, CObjectVector<CItemEx> &items) { - HRESULT res; - try + _inBufMode = false; + items.Clear(); + + Close(); + ArcInfo.Clear(); + + UInt64 startPos; + RINOK(stream->Seek(0, STREAM_SEEK_CUR, &startPos)); + RINOK(stream->Seek(0, STREAM_SEEK_END, &ArcInfo.FileEndPos)); + m_Position = ArcInfo.FileEndPos; + + StartStream = stream; + Callback = callback; + + bool volWasRequested = false; + + if (callback + && (startPos == 0 || !searchLimit || *searchLimit != 0)) { - res = ReadHeaders2(items, progress); + volWasRequested = true; + RINOK(ReadVols()); } - catch (const CInBufferException &e) { res = e.ErrorCode; } - catch (const CUnexpectEnd &) + + if (IsMultiVol && Vols.StartVolIndex != 0) { - if (items.IsEmpty()) - return S_FALSE; - UnexpectedEnd = true; - res = S_OK; + Stream = Vols.Streams[0].Stream; + if (Stream) + { + m_Position = 0; + RINOK(Stream->Seek(0, STREAM_SEEK_SET, NULL)); + UInt64 limit = 0; + HRESULT res = FindMarker(Stream, &limit); + if (res == S_OK) + MarkerIsFound = true; + else if (res != S_FALSE) + return res; + } + } + else + { + // printf("\nOpen offset = %u\n", (unsigned)startPos); + RINOK(stream->Seek(startPos, STREAM_SEEK_SET, NULL)); + m_Position = startPos; + HRESULT res = FindMarker(stream, searchLimit); + UInt64 curPos = m_Position; + if (res == S_OK) + MarkerIsFound = true; + else + { + // if (res != S_FALSE) + return res; + } + + MarkerIsFound = true; + + if (ArcInfo.IsSpanMode && !volWasRequested) + { + RINOK(ReadVols()); + } + + if (IsMultiVol && (unsigned)Vols.StartVolIndex < Vols.Streams.Size()) + { + Stream = Vols.Streams[Vols.StartVolIndex].Stream; + if (!Stream) + IsMultiVol = false; + else + { + RINOK(Stream->Seek(curPos, STREAM_SEEK_SET, NULL)); + m_Position = curPos; + } + } + else + IsMultiVol = false; + + if (!IsMultiVol) + { + RINOK(stream->Seek(curPos, STREAM_SEEK_SET, NULL)); + m_Position = curPos; + StreamRef = stream; + Stream = stream; + } } - catch (...) + + { + HRESULT res; + try + { + res = ReadHeaders2(items); + } + catch (const CInBufferException &e) { res = e.ErrorCode; } + catch (const CUnexpectEnd &) + { + if (items.IsEmpty()) + return S_FALSE; + UnexpectedEnd = true; + res = S_OK; + } + catch (...) + { + _inBufMode = false; + throw; + } + + if (IsMultiVol) + { + ArcInfo.FinishPos = ArcInfo.FileEndPos; + if ((unsigned)Vols.StreamIndex < Vols.Streams.Size()) + if (m_Position < Vols.Streams[Vols.StreamIndex].Size) + ArcInfo.ThereIsTail = true; + } + else + { + ArcInfo.FinishPos = m_Position; + ArcInfo.ThereIsTail = (ArcInfo.FileEndPos > m_Position); + } + _inBufMode = false; - throw; + IsArcOpen = true; + if (!IsMultiVol) + Vols.Streams.Clear(); + return res; } - ArcInfo.FinishPos = m_Position; - _inBufMode = false; - return res; } -ISequentialInStream* CInArchive::CreateLimitedStream(UInt64 position, UInt64 size) + +HRESULT CInArchive::GetItemStream(const CItemEx &item, bool seekPackData, CMyComPtr<ISequentialInStream> &stream) { - CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream; - CMyComPtr<ISequentialInStream> stream(streamSpec); - Stream->Seek(ArcInfo.Base + position, STREAM_SEEK_SET, NULL); - streamSpec->SetStream(Stream); - streamSpec->Init(size); - return stream.Detach(); + stream.Release(); + + UInt64 pos = item.LocalHeaderPos; + if (seekPackData) + pos += item.LocalFullHeaderSize; + + if (!IsMultiVol) + { + if (UseDisk_in_SingleVol && item.Disk != EcdVolIndex) + return S_OK; + pos += ArcInfo.Base; + RINOK(StreamRef->Seek(pos, STREAM_SEEK_SET, NULL)); + stream = StreamRef; + return S_OK; + } + + if (item.Disk >= Vols.Streams.Size()) + return S_OK; + + IInStream *str2 = Vols.Streams[item.Disk].Stream; + if (!str2) + return S_OK; + RINOK(str2->Seek(pos, STREAM_SEEK_SET, NULL)); + + Vols.NeedSeek = false; + Vols.StreamIndex = item.Disk; + + CVolStream *volsStreamSpec = new CVolStream; + volsStreamSpec->Vols = &Vols; + stream = volsStreamSpec; + + return S_OK; } }} |