From 66ac98bb02ac0fadd2a0e3266ea39279984580a3 Mon Sep 17 00:00:00 2001 From: Igor Pavlov Date: Tue, 10 May 2016 00:00:00 +0000 Subject: 16.00 --- CPP/7zip/Archive/Zip/ZipHandler.cpp | 134 ++- CPP/7zip/Archive/Zip/ZipHeader.h | 5 +- CPP/7zip/Archive/Zip/ZipIn.cpp | 1566 ++++++++++++++++++++++++++-------- CPP/7zip/Archive/Zip/ZipIn.h | 261 ++++-- CPP/7zip/Archive/Zip/ZipItem.h | 2 + CPP/7zip/Archive/Zip/ZipRegister.cpp | 3 +- CPP/7zip/Archive/Zip/ZipUpdate.cpp | 51 +- 7 files changed, 1564 insertions(+), 458 deletions(-) (limited to 'CPP/7zip/Archive/Zip') diff --git a/CPP/7zip/Archive/Zip/ZipHandler.cpp b/CPP/7zip/Archive/Zip/ZipHandler.cpp index 092dcbc9..75034de0 100644 --- a/CPP/7zip/Archive/Zip/ZipHandler.cpp +++ b/CPP/7zip/Archive/Zip/ZipHandler.cpp @@ -12,6 +12,7 @@ #include "../../IPassword.h" #include "../../Common/FilterCoder.h" +#include "../../Common/LimitedStreams.h" #include "../../Common/ProgressUtils.h" #include "../../Common/StreamObjects.h" #include "../../Common/StreamUtils.h" @@ -142,14 +143,19 @@ static const Byte kProps[] = kpidCRC, kpidMethod, kpidHostOS, - kpidUnpackVer + kpidUnpackVer, + kpidVolumeIndex }; static const Byte kArcProps[] = { kpidEmbeddedStubSize, kpidBit64, - kpidComment + kpidComment, + kpidTotalPhySize, + kpidIsVolume, + kpidVolumeIndex, + kpidNumVolumes }; CHandler::CHandler() @@ -175,18 +181,23 @@ STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) { case kpidBit64: if (m_Archive.IsZip64) prop = m_Archive.IsZip64; break; case kpidComment: if (m_Archive.ArcInfo.Comment.Size() != 0) prop = MultiByteToUnicodeString(BytesToString(m_Archive.ArcInfo.Comment), CP_ACP); break; - case kpidPhySize: prop = m_Archive.ArcInfo.GetPhySize(); break; - case kpidOffset: /* if (m_Archive.ArcInfo.Base != 0) */ - prop = m_Archive.ArcInfo.Base; break; + + case kpidPhySize: prop = m_Archive.GetPhySize(); break; + case kpidOffset: prop = m_Archive.GetOffset(); break; case kpidEmbeddedStubSize: { - UInt64 stubSize = m_Archive.ArcInfo.GetEmbeddedStubSize(); + UInt64 stubSize = m_Archive.GetEmbeddedStubSize(); if (stubSize != 0) prop = stubSize; break; } + case kpidTotalPhySize: if (m_Archive.IsMultiVol) prop = m_Archive.Vols.GetTotalSize(); break; + case kpidVolumeIndex: if (m_Archive.IsMultiVol) prop = (UInt32)m_Archive.Vols.StartVolIndex; break; + case kpidIsVolume: if (m_Archive.IsMultiVol) prop = true; break; + case kpidNumVolumes: if (m_Archive.IsMultiVol) prop = (UInt32)m_Archive.Vols.Streams.Size(); break; + case kpidWarningFlags: { UInt32 v = 0; @@ -197,6 +208,18 @@ STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) break; } + case kpidError: + { + if (!m_Archive.Vols.MissingName.IsEmpty()) + { + UString s; + s.SetFromAscii("Missing volume : "); + s += m_Archive.Vols.MissingName; + prop = s; + } + break; + } + case kpidErrorFlags: { UInt32 v = 0; @@ -209,7 +232,7 @@ STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) but the stream has access only to zip part. In that case we ignore UnavailableStart error. maybe we must show warning in that case. */ - UInt64 stubSize = m_Archive.ArcInfo.GetEmbeddedStubSize(); + UInt64 stubSize = m_Archive.GetEmbeddedStubSize(); if (stubSize < (UInt64)-m_Archive.ArcInfo.Base) v |= kpv_ErrorFlags_UnavailableStart; } @@ -429,6 +452,10 @@ STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *val case kpidUnpackVer: prop = (UInt32)item.ExtractVersion.Version; break; + + case kpidVolumeIndex: + prop = item.Disk; + break; } prop.Detach(value); @@ -436,30 +463,6 @@ STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *val COM_TRY_END } -class CProgressImp: public CProgressVirt -{ - CMyComPtr _callback; -public: - virtual HRESULT SetCompletedLocal(UInt64 numFiles, UInt64 numBytes); - virtual HRESULT SetTotalCD(UInt64 numFiles); - virtual HRESULT SetCompletedCD(UInt64 numFiles); - CProgressImp(IArchiveOpenCallback *callback): _callback(callback) {} -}; - -HRESULT CProgressImp::SetCompletedLocal(UInt64 numFiles, UInt64 numBytes) -{ - return _callback->SetCompleted(&numFiles, &numBytes); -} - -HRESULT CProgressImp::SetTotalCD(UInt64 numFiles) -{ - return _callback->SetTotal(&numFiles, NULL); -} - -HRESULT CProgressImp::SetCompletedCD(UInt64 numFiles) -{ - return _callback->SetCompleted(&numFiles, NULL); -} STDMETHODIMP CHandler::Open(IInStream *inStream, const UInt64 *maxCheckStartPosition, IArchiveOpenCallback *callback) @@ -468,9 +471,13 @@ STDMETHODIMP CHandler::Open(IInStream *inStream, try { Close(); - RINOK(m_Archive.Open(inStream, maxCheckStartPosition)); - CProgressImp progressImp(callback); - return m_Archive.ReadHeaders(m_Items, callback ? &progressImp : NULL); + HRESULT res = m_Archive.Open(inStream, maxCheckStartPosition, callback, m_Items); + if (res != S_OK) + { + m_Items.Clear(); + m_Archive.ClearRefs(); + } + return res; } catch(...) { Close(); throw; } COM_TRY_END @@ -483,8 +490,6 @@ STDMETHODIMP CHandler::Close() return S_OK; } -////////////////////////////////////// -// CHandler::DecompressItems class CLzmaDecoder: public ICompressCoder, @@ -550,6 +555,8 @@ struct CMethodItem CMyComPtr Coder; }; + + class CZipDecoder { NCrypto::NZip::CDecoder *_zipCryptoDecoderSpec; @@ -584,6 +591,24 @@ public: Int32 &res); }; + +static HRESULT SkipStreamData(ISequentialInStream *stream, UInt64 size) +{ + const size_t kBufSize = 1 << 12; + Byte buf[kBufSize]; + for (;;) + { + if (size == 0) + return S_OK; + size_t curSize = kBufSize; + if (curSize > size) + curSize = (size_t)size; + RINOK(ReadStream_FALSE(stream, buf, curSize)); + size -= curSize; + } +} + + HRESULT CZipDecoder::Decode( DECL_EXTERNAL_CODECS_LOC_VARS CInArchive &archive, const CItemEx &item, @@ -634,9 +659,11 @@ HRESULT CZipDecoder::Decode( outStreamSpec->SetStream(realOutStream); outStreamSpec->Init(needCRC); - UInt64 authenticationPos; - - CMyComPtr inStream; + CMyComPtr packStream; + + CLimitedSequentialInStream *limitedStreamSpec = new CLimitedSequentialInStream; + CMyComPtr inStream(limitedStreamSpec); + { UInt64 packSize = item.PackSize; if (wzAesMode) @@ -645,9 +672,14 @@ HRESULT CZipDecoder::Decode( return S_OK; packSize -= NCrypto::NWzAes::kMacSize; } - UInt64 dataPos = item.GetDataPosition(); - inStream.Attach(archive.CreateLimitedStream(dataPos, packSize)); - authenticationPos = dataPos + packSize; + RINOK(archive.GetItemStream(item, true, packStream)); + if (!packStream) + { + res = NExtract::NOperationResult::kUnavailable; + return S_OK; + } + limitedStreamSpec->SetStream(packStream); + limitedStreamSpec->Init(packSize); } CMyComPtr cryptoFilter; @@ -912,9 +944,15 @@ HRESULT CZipDecoder::Decode( bool authOk = true; if (needCRC) crcOK = (outStreamSpec->GetCRC() == item.Crc); + if (wzAesMode) { - inStream.Attach(archive.CreateLimitedStream(authenticationPos, NCrypto::NWzAes::kMacSize)); + const UInt64 rem = limitedStreamSpec->GetRem(); + if (rem != 0) + if (SkipStreamData(inStream, rem) != S_OK) + authOk = false; + + limitedStreamSpec->Init(NCrypto::NWzAes::kMacSize); if (_wzAesDecoderSpec->CheckMac(inStream, authOk) != S_OK) authOk = false; } @@ -988,16 +1026,21 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kUnavailable)); continue; } + if (!item.FromLocal) { - HRESULT res = m_Archive.ReadLocalItemAfterCdItem(item); + bool isAvail = true; + HRESULT res = m_Archive.ReadLocalItemAfterCdItem(item, isAvail); if (res == S_FALSE) { if (item.IsDir() || realOutStream || testMode) { RINOK(extractCallback->PrepareOperation(askMode)); realOutStream.Release(); - RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kHeadersError)); + RINOK(extractCallback->SetOperationResult( + isAvail ? + NExtract::NOperationResult::kHeadersError : + NExtract::NOperationResult::kUnavailable)); } continue; } @@ -1034,6 +1077,7 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, RINOK(extractCallback->SetOperationResult(res)) } + lps->InSize = currentTotalPacked; lps->OutSize = currentTotalUnPacked; return lps->SetCur(); diff --git a/CPP/7zip/Archive/Zip/ZipHeader.h b/CPP/7zip/Archive/Zip/ZipHeader.h index 82e46eb6..fead0192 100644 --- a/CPP/7zip/Archive/Zip/ZipHeader.h +++ b/CPP/7zip/Archive/Zip/ZipHeader.h @@ -18,9 +18,8 @@ namespace NSignature const UInt32 kEcd = 0x06054B50; const UInt32 kEcd64 = 0x06064B50; const UInt32 kEcd64Locator = 0x07064B50; - - // const UInt32 kSpan = 0x08074B50; - const UInt32 kNoSpan = 0x30304b50; // PK00, replaces kSpan, if there is only 1 segment + const UInt32 kSpan = 0x08074B50; + const UInt32 kNoSpan = 0x30304B50; // PK00, replaces kSpan, if there is only 1 segment } const unsigned kLocalHeaderSize = 4 + 26; // including signature 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 #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 &items, UInt64 cdOffset, UInt64 cdSize, CProgressVirt *progress) +HRESULT CInArchive::TryReadCd(CObjectVector &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 &items, UInt64 &cdOffset, UInt64 &cdSize, CProgressVirt *progress) + +HRESULT CInArchive::ReadCd(CObjectVector &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) + { + 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) { - res = TryReadCd(items, ArcInfo.MarkerPos + cdOffset, cdSize, progress); + // 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 &items, UInt64 offset) + +static int FindItem(const CObjectVector &items, const CItemEx &item) { unsigned left = 0, right = items.Size(); for (;;) @@ -1003,42 +1300,66 @@ static HRESULT FindItem(const CObjectVector &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 &items, CProgressVirt *progress) +HRESULT CInArchive::ReadLocals(CObjectVector &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,43 +1369,373 @@ 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; } -#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 &items, CProgressVirt *progress) +HRESULT CVols::ParseArcName(IArchiveOpenVolumeCallback *volCallback) { - items.Clear(); + UString name; + { + NWindows::NCOM::CPropVariant prop; + RINOK(volCallback->GetProperty(kpidName, &prop)); + if (prop.vt != VT_BSTR) + return S_OK; + name = prop.bstrVal; + } - // m_Signature must be kLocalFileHeader or kEcd - // m_Position points to next byte after signature - RINOK(Stream->Seek(m_Position, STREAM_SEEK_SET, NULL)); + UString base = name; + int dotPos = name.ReverseFind_Dot(); - if (!_inBuffer.Create(1 << 15)) - return E_OUTOFMEMORY; - _inBuffer.SetStream(Stream); + if (dotPos < 0) + return S_OK; - bool needReadCd = true; - bool localsWereRead = false; - if (m_Signature == NSignature::kEcd) + base.DeleteFrom(dotPos + 1); + + const UString ext = name.Ptr(dotPos + 1); + StartVolIndex = (Int32)(-1); + + if (ext.IsEmpty()) + return S_OK; + else { - // It must be empty archive or backware archive - // we don't support backware archive still - - const unsigned kBufSize = kEcdSize - 4; - Byte buf[kBufSize]; - SafeReadBytes(buf, kBufSize); - CEcd ecd; + 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 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 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 &items) +{ + 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)); + + _inBuffer.SetStream(Stream); + + bool needReadCd = true; + + if (m_Signature == NSignature::kEcd) + { + // It must be empty archive or backware archive + // we don't support backware archive still + + const unsigned kBufSize = kEcdSize - 4; + Byte buf[kBufSize]; + SafeReadBytes(buf, kBufSize); + CEcd ecd; ecd.Parse(buf); // if (ecd.cdSize != 0) // Do we need also to support the case where empty zip archive with PK00 uses cdOffset = 4 ?? @@ -1097,9 +1748,6 @@ HRESULT CInArchive::ReadHeaders2(CObjectVector &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 &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 &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) + if ((Int64)ArcInfo.MarkerPos2 < ArcInfo.Base) res = S_FALSE; - else if (!AreItemsEqual(firstItem, items[index])) - 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 cdItems; @@ -1148,19 +1807,32 @@ HRESULT CInArchive::ReadHeaders2(CObjectVector &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 &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 &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) + { + if (cdDisk != (int)ecd64.CdDisk) + HeadersError = true; + } + else if (needSetBase) { - ArcInfo.Base = cdAbsOffset - ecd64.cdStartOffset; - cdRelatOffset = ecd64.cdStartOffset; - needSetBase = false; + 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; + } + } } - if (localsWereRead && (UInt64)ArcInfo.Base != ArcInfo.MarkerPos) + EcdVolIndex = ecd64.ThisDisk; + + 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 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 &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 &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 &items, CProgressVirt *p } -HRESULT CInArchive::ReadHeaders(CObjectVector &items, CProgressVirt *progress) + +HRESULT CInArchive::Open(IInStream *stream, const UInt64 *searchLimit, + IArchiveOpenCallback *callback, CObjectVector &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 &stream) { - CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream; - CMyComPtr 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; } }} diff --git a/CPP/7zip/Archive/Zip/ZipIn.h b/CPP/7zip/Archive/Zip/ZipIn.h index 734d3bcb..9b10905e 100644 --- a/CPP/7zip/Archive/Zip/ZipIn.h +++ b/CPP/7zip/Archive/Zip/ZipIn.h @@ -28,6 +28,7 @@ public: { return LocalHeaderPos + LocalFullHeaderSize; } }; + struct CInArchiveInfo { Int64 Base; /* Base offset of start of archive in stream. @@ -40,74 +41,195 @@ struct CInArchiveInfo Base = ArcInfo.MarkerPos; */ /* The following *Pos variables contain absolute offsets in Stream */ - UInt64 MarkerPos; /* Pos of first signature, it can point to PK00 signature + + UInt64 MarkerPos; /* Pos of first signature, it can point to kSpan/kNoSpan signature = MarkerPos2 in most archives - = MarkerPos2 - 4 if there is PK00 signature */ + = MarkerPos2 - 4 if there is kSpan/kNoSpan signature */ UInt64 MarkerPos2; // Pos of first local item signature in stream - UInt64 FinishPos; // Finish pos of archive data + UInt64 FinishPos; // Finish pos of archive data in starting volume UInt64 FileEndPos; // Finish pos of stream UInt64 FirstItemRelatOffset; /* Relative offset of first local (read from cd) (relative to Base). = 0 in most archives = size of stub for some SFXs */ bool CdWasRead; + bool IsSpanMode; + bool ThereIsTail; + + // UInt32 BaseVolIndex; CByteBuffer Comment; - CInArchiveInfo(): Base(0), MarkerPos(0), MarkerPos2(0), FinishPos(0), FileEndPos(0), - FirstItemRelatOffset(0), CdWasRead(false) {} - - UInt64 GetPhySize() const { return FinishPos - Base; } - UInt64 GetEmbeddedStubSize() const - { - if (CdWasRead) - return FirstItemRelatOffset; - return MarkerPos2 - Base; - } - bool ThereIsTail() const { return FileEndPos > FinishPos; } + CInArchiveInfo(): + Base(0), + MarkerPos(0), + MarkerPos2(0), + FinishPos(0), + FileEndPos(0), + FirstItemRelatOffset(0), + CdWasRead(false), + IsSpanMode(false), + ThereIsTail(false) + // BaseVolIndex(0) + {} + void Clear() { + // BaseVolIndex = 0; Base = 0; MarkerPos = 0; MarkerPos2 = 0; FinishPos = 0; FileEndPos = 0; + ThereIsTail = false; FirstItemRelatOffset = 0; + CdWasRead = false; + IsSpanMode = false; Comment.Free(); } }; -struct CProgressVirt -{ - virtual HRESULT SetCompletedLocal(UInt64 numFiles, UInt64 numBytes) = 0; - virtual HRESULT SetTotalCD(UInt64 numFiles) = 0; - virtual HRESULT SetCompletedCD(UInt64 numFiles) = 0; -}; struct CCdInfo { + // 64 + UInt16 VersionMade; + UInt16 VersionNeedExtract; + + // old zip + UInt32 ThisDisk; + UInt32 CdDisk; + UInt64 NumEntries_in_ThisDisk; UInt64 NumEntries; UInt64 Size; UInt64 Offset; - void ParseEcd(const Byte *p); - void ParseEcd64(const Byte *p); + UInt16 CommentSize; + + CCdInfo() { memset(this, 0, sizeof(*this)); } + + void ParseEcd32(const Byte *p); // (p) includes signature + void ParseEcd64e(const Byte *p); // (p) exclude signature }; + +class CVols +{ +public: + + struct CSubStreamInfo + { + CMyComPtr Stream; + UInt64 Size; + + CSubStreamInfo(): Size(0) {} + }; + + CObjectVector Streams; + int StreamIndex; + bool NeedSeek; + + CMyComPtr ZipStream; + + bool StartIsExe; // is .exe + bool StartIsZ; // is .zip or .zNN + bool StartIsZip; // is .zip + bool IsUpperCase; + Int32 StartVolIndex; // = (NN - 1), if StartStream is .zNN + + Int32 StartParsingVol; // if we need local parsing, we must use that stream + unsigned NumVols; + + int EndVolIndex; // index of last volume (ecd volume), + // -1, if is not multivol + + UString BaseName; // including '.' + + UString MissingName; + + CCdInfo ecd; + bool ecd_wasRead; + + void Clear() + { + StreamIndex = -1; + NeedSeek = false; + + + StartIsExe = false; + StartIsZ = false; + StartIsZip = false; + IsUpperCase = false; + + StartVolIndex = -1; + StartParsingVol = 0; + NumVols = 0; + EndVolIndex = -1; + + BaseName.Empty(); + MissingName.Empty(); + + ecd_wasRead = false; + + Streams.Clear(); + ZipStream.Release(); + } + + HRESULT ParseArcName(IArchiveOpenVolumeCallback *volCallback); + + HRESULT Read(void *data, UInt32 size, UInt32 *processedSize); + + UInt64 GetTotalSize() const + { + UInt64 total = 0; + FOR_VECTOR (i, Streams) + total += Streams[i].Size; + return total; + } +}; + + +class CVolStream: + public ISequentialInStream, + public CMyUnknownImp +{ +public: + CVols *Vols; + + MY_UNKNOWN_IMP1(ISequentialInStream) + + STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); +}; + + class CInArchive { CInBuffer _inBuffer; bool _inBufMode; UInt32 m_Signature; UInt64 m_Position; + + UInt64 _processedCnt; + bool CanStartNewVol; + + CMyComPtr StreamRef; + IInStream *Stream; + IInStream *StartStream; + + bool IsArcOpen; + + HRESULT ReadVols2(IArchiveOpenVolumeCallback *volCallback, + unsigned start, int lastDisk, int zipDisk, unsigned numMissingVolsMax, unsigned &numMissingVols); + HRESULT ReadVols(); + HRESULT Seek(UInt64 offset); - HRESULT FindAndReadMarker(IInStream *stream, const UInt64 *searchHeaderSizeLimit); - HRESULT IncreaseRealPosition(Int64 addValue); + HRESULT FindMarker(IInStream *stream, const UInt64 *searchLimit); + HRESULT IncreaseRealPosition(Int64 addValue, bool &isFinished); HRESULT ReadBytes(void *data, UInt32 size, UInt32 *processedSize); void SafeReadBytes(void *data, unsigned size); @@ -126,12 +248,14 @@ class CInArchive HRESULT ReadLocalItemDescriptor(CItemEx &item); HRESULT ReadCdItem(CItemEx &item); HRESULT TryEcd64(UInt64 offset, CCdInfo &cdInfo); - HRESULT FindCd(CCdInfo &cdInfo); - HRESULT TryReadCd(CObjectVector &items, UInt64 cdOffset, UInt64 cdSize, CProgressVirt *progress); - HRESULT ReadCd(CObjectVector &items, UInt64 &cdOffset, UInt64 &cdSize, CProgressVirt *progress); - HRESULT ReadLocals(CObjectVector &localItems, CProgressVirt *progress); + HRESULT FindCd(bool checkOffsetMode); + HRESULT TryReadCd(CObjectVector &items, const CCdInfo &cdInfo, UInt64 cdOffset, UInt64 cdSize); + HRESULT ReadCd(CObjectVector &items, UInt32 &cdDisk, UInt64 &cdOffset, UInt64 &cdSize); + HRESULT ReadLocals(CObjectVector &localItems); - HRESULT ReadHeaders2(CObjectVector &items, CProgressVirt *progress); + HRESULT ReadHeaders2(CObjectVector &items); + + HRESULT GetVolStream(unsigned vol, UInt64 pos, CMyComPtr &stream); public: CInArchiveInfo ArcInfo; @@ -142,46 +266,87 @@ public: bool ExtraMinorError; bool UnexpectedEnd; bool NoCentralDir; + + bool MarkerIsFound; + + bool IsMultiVol; + bool UseDisk_in_SingleVol; + UInt32 EcdVolIndex; + + CVols Vols; + + IArchiveOpenCallback *Callback; + + CInArchive(): Stream(NULL), Callback(NULL), IsArcOpen(false) {} + + UInt64 GetPhySize() const + { + if (IsMultiVol) + return ArcInfo.FinishPos; + else + return ArcInfo.FinishPos - ArcInfo.Base; + } + + UInt64 GetOffset() const + { + if (IsMultiVol) + return 0; + else + return ArcInfo.Base; + } + - CMyComPtr Stream; - + void ClearRefs(); void Close(); - HRESULT Open(IInStream *stream, const UInt64 *searchHeaderSizeLimit); - HRESULT ReadHeaders(CObjectVector &items, CProgressVirt *progress); + HRESULT Open(IInStream *stream, const UInt64 *searchLimit, IArchiveOpenCallback *callback, CObjectVector &items); + HRESULT ReadHeaders(CObjectVector &items); - bool IsOpen() const { return Stream != NULL; } - bool AreThereErrors() const { return HeadersError || UnexpectedEnd; } + bool IsOpen() const { return IsArcOpen; } + + bool AreThereErrors() const + { + return HeadersError + || UnexpectedEnd + || !Vols.MissingName.IsEmpty(); + } bool IsLocalOffsetOK(const CItemEx &item) const { if (item.FromLocal) return true; - return /* ArcInfo.Base >= 0 || */ ArcInfo.Base + (Int64)item.LocalHeaderPos >= 0; + return (Int64)GetOffset() + (Int64)item.LocalHeaderPos >= 0; + } + + UInt64 GetEmbeddedStubSize() const + { + if (ArcInfo.CdWasRead) + return ArcInfo.FirstItemRelatOffset; + if (IsMultiVol) + return 0; + return ArcInfo.MarkerPos2 - ArcInfo.Base; } - HRESULT ReadLocalItemAfterCdItem(CItemEx &item); + + HRESULT ReadLocalItemAfterCdItem(CItemEx &item, bool &isAvail); HRESULT ReadLocalItemAfterCdItemFull(CItemEx &item); - ISequentialInStream *CreateLimitedStream(UInt64 position, UInt64 size); + HRESULT GetItemStream(const CItemEx &item, bool seekPackData, CMyComPtr &stream); - UInt64 GetOffsetInStream(UInt64 offsetFromArc) const { return ArcInfo.Base + offsetFromArc; } + IInStream *GetBaseStream() { return StreamRef; } bool CanUpdate() const { - if (AreThereErrors()) - return false; - if (ArcInfo.Base < 0) - return false; - if ((Int64)ArcInfo.MarkerPos2 < ArcInfo.Base) + if (AreThereErrors() + || IsMultiVol + || ArcInfo.Base < 0 + || (Int64)ArcInfo.MarkerPos2 < ArcInfo.Base + || ArcInfo.ThereIsTail + || GetEmbeddedStubSize() != 0) return false; // 7-zip probably can update archives with embedded stubs. // we just disable that feature for more safety. - if (ArcInfo.GetEmbeddedStubSize() != 0) - return false; - if (ArcInfo.ThereIsTail()) - return false; return true; } }; diff --git a/CPP/7zip/Archive/Zip/ZipItem.h b/CPP/7zip/Archive/Zip/ZipItem.h index 5f078b60..c134ec79 100644 --- a/CPP/7zip/Archive/Zip/ZipItem.h +++ b/CPP/7zip/Archive/Zip/ZipItem.h @@ -199,6 +199,8 @@ public: UInt64 PackSize; UInt32 Time; UInt32 Crc; + + UInt32 Disk; AString Name; diff --git a/CPP/7zip/Archive/Zip/ZipRegister.cpp b/CPP/7zip/Archive/Zip/ZipRegister.cpp index 2be783e6..6674189f 100644 --- a/CPP/7zip/Archive/Zip/ZipRegister.cpp +++ b/CPP/7zip/Archive/Zip/ZipRegister.cpp @@ -12,10 +12,11 @@ namespace NZip { static const Byte k_Signature[] = { 4, 0x50, 0x4B, 0x03, 0x04, 4, 0x50, 0x4B, 0x05, 0x06, + 6, 0x50, 0x4B, 0x07, 0x08, 0x50, 0x4B, 6, 0x50, 0x4B, 0x30, 0x30, 0x50, 0x4B }; REGISTER_ARC_IO( - "zip", "zip zipx jar xpi odt ods docx xlsx epub", 0, 1, + "zip", "zip z01 zipx jar xpi odt ods docx xlsx epub", 0, 1, k_Signature, 0, NArcInfoFlags::kFindSignature | diff --git a/CPP/7zip/Archive/Zip/ZipUpdate.cpp b/CPP/7zip/Archive/Zip/ZipUpdate.cpp index 3128939e..1546a2af 100644 --- a/CPP/7zip/Archive/Zip/ZipUpdate.cpp +++ b/CPP/7zip/Archive/Zip/ZipUpdate.cpp @@ -52,16 +52,6 @@ static HRESULT CopyBlockToArchive(ISequentialInStream *inStream, UInt64 size, return NCompress::CopyStream_ExactSize(inStream, outStream, size, progress); } -static HRESULT WriteRange(IInStream *inStream, COutArchive &outArchive, - const CUpdateRange &range, ICompressProgressInfo *progress) -{ - UInt64 position; - RINOK(inStream->Seek(range.Position, STREAM_SEEK_SET, &position)); - RINOK(CopyBlockToArchive(inStream, range.Size, outArchive, progress)); - return progress->SetRatioInfo(&range.Size, &range.Size); -} - - static void SetFileHeader( COutArchive &archive, const CCompressionMethodMode &options, @@ -358,9 +348,12 @@ static HRESULT UpdateItemOldData( return E_NOTIMPL; // use old name size. - // CUpdateRange range(item.GetLocalExtraPosition(), item.LocalExtraSize + item.PackSize); - CUpdateRange range(inArchive->GetOffsetInStream(itemEx.GetDataPosition()), itemEx.PackSize); + CMyComPtr packStream; + RINOK(inArchive->GetItemStream(itemEx, true, packStream)); + if (!packStream) + return E_NOTIMPL; + // we keep ExternalAttrib and some another properties from old archive // item.ExternalAttrib = ui.Attrib; @@ -378,19 +371,27 @@ static HRESULT UpdateItemOldData( archive.PrepareWriteCompressedData2(item.Name.Len(), item.Size, item.PackSize, item.LocalExtra.HasWzAes()); archive.WriteLocalHeader(item); - RINOK(WriteRange(inArchive->Stream, archive, range, progress)); - complexity += range.Size; + + RINOK(CopyBlockToArchive(packStream, itemEx.PackSize, archive, progress)); + + complexity += itemEx.PackSize; } else { - CUpdateRange range(inArchive->GetOffsetInStream(itemEx.LocalHeaderPos), itemEx.GetLocalFullSize()); + CMyComPtr packStream; + RINOK(inArchive->GetItemStream(itemEx, false, packStream)); + if (!packStream) + return E_NOTIMPL; // set new header position item.LocalHeaderPos = archive.GetCurPos(); - RINOK(WriteRange(inArchive->Stream, archive, range, progress)); - complexity += range.Size; - archive.MoveCurPos(range.Size); + const UInt64 rangeSize = itemEx.GetLocalFullSize(); + + RINOK(CopyBlockToArchive(packStream, rangeSize, archive, progress)); + + complexity += rangeSize; + archive.MoveCurPos(rangeSize); } return S_OK; @@ -1191,10 +1192,11 @@ HRESULT Update( if (inArchive) { - if (inArchive->ArcInfo.Base > 0 && !removeSfx) + if (!inArchive->IsMultiVol && inArchive->ArcInfo.Base > 0 && !removeSfx) { - RINOK(inArchive->Stream->Seek(0, STREAM_SEEK_SET, NULL)); - RINOK(NCompress::CopyStream_ExactSize(inArchive->Stream, outStreamReal, inArchive->ArcInfo.Base, NULL)); + IInStream *baseStream = inArchive->GetBaseStream(); + RINOK(baseStream->Seek(0, STREAM_SEEK_SET, NULL)); + RINOK(NCompress::CopyStream_ExactSize(baseStream, outStreamReal, inArchive->ArcInfo.Base, NULL)); } } @@ -1210,11 +1212,12 @@ HRESULT Update( if (inArchive) { - if ((Int64)inArchive->ArcInfo.MarkerPos2 > inArchive->ArcInfo.Base) + if (!inArchive->IsMultiVol && (Int64)inArchive->ArcInfo.MarkerPos2 > inArchive->ArcInfo.Base) { - RINOK(inArchive->Stream->Seek(inArchive->ArcInfo.Base, STREAM_SEEK_SET, NULL)); + IInStream *baseStream = inArchive->GetBaseStream(); + RINOK(baseStream->Seek(inArchive->ArcInfo.Base, STREAM_SEEK_SET, NULL)); UInt64 embStubSize = inArchive->ArcInfo.MarkerPos2 - inArchive->ArcInfo.Base; - RINOK(NCompress::CopyStream_ExactSize(inArchive->Stream, outStream, embStubSize, NULL)); + RINOK(NCompress::CopyStream_ExactSize(baseStream, outStream, embStubSize, NULL)); outArchive.MoveCurPos(embStubSize); } } -- cgit v1.2.3