Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/kornelski/7z.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'CPP/7zip/Archive/Zip/ZipIn.cpp')
-rw-r--r--[-rwxr-xr-x]CPP/7zip/Archive/Zip/ZipIn.cpp1323
1 files changed, 867 insertions, 456 deletions
diff --git a/CPP/7zip/Archive/Zip/ZipIn.cpp b/CPP/7zip/Archive/Zip/ZipIn.cpp
index e930488f..345fbf56 100755..100644
--- a/CPP/7zip/Archive/Zip/ZipIn.cpp
+++ b/CPP/7zip/Archive/Zip/ZipIn.cpp
@@ -2,13 +2,15 @@
#include "StdAfx.h"
-#include "../../../../C/CpuArch.h"
+// #include <stdio.h>
-#include "Common/DynamicBuffer.h"
+#include "../../../Common/DynamicBuffer.h"
#include "../../Common/LimitedStreams.h"
#include "../../Common/StreamUtils.h"
+#include "../IArchive.h"
+
#include "ZipIn.h"
#define Get16(p) GetUi16(p)
@@ -17,100 +19,342 @@
namespace NArchive {
namespace NZip {
-
+
+struct CEcd
+{
+ UInt16 thisDiskNumber;
+ UInt16 startCDDiskNumber;
+ UInt16 numEntriesInCDOnThisDisk;
+ UInt16 numEntriesInCD;
+ UInt32 cdSize;
+ UInt32 cdStartOffset;
+ UInt16 commentSize;
+
+ void Parse(const Byte *p);
+
+ bool IsEmptyArc()
+ {
+ return thisDiskNumber == 0 && startCDDiskNumber == 0 &&
+ numEntriesInCDOnThisDisk == 0 && numEntriesInCD == 0 && cdSize == 0
+ && cdStartOffset == 0 // test it
+ ;
+ }
+};
+
+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);
+}
+
+struct CEcd64
+{
+ 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)); }
+};
+
+void CEcd64::Parse(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);
+}
+
HRESULT CInArchive::Open(IInStream *stream, const UInt64 *searchHeaderSizeLimit)
{
_inBufMode = false;
Close();
- RINOK(stream->Seek(0, STREAM_SEEK_CUR, &m_StreamStartPosition));
- m_Position = m_StreamStartPosition;
+ 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));
+
+ // printf("\nOpen offset = %d", (int)m_Position);
RINOK(FindAndReadMarker(stream, searchHeaderSizeLimit));
RINOK(stream->Seek(m_Position, STREAM_SEEK_SET, NULL));
- m_Stream = stream;
+ Stream = stream;
return S_OK;
}
void CInArchive::Close()
{
- _inBuffer.ReleaseStream();
- m_Stream.Release();
+ IsArc = false;
+ HeadersError = false;
+ HeadersWarning = false;
+ ExtraMinorError = false;
+ UnexpectedEnd = false;
+ NoCentralDir = false;
+ IsZip64 = false;
+ Stream.Release();
}
HRESULT CInArchive::Seek(UInt64 offset)
{
- return m_Stream->Seek(offset, STREAM_SEEK_SET, NULL);
+ return Stream->Seek(offset, STREAM_SEEK_SET, NULL);
}
-//////////////////////////////////////
-// Markers
+static bool CheckDosTime(UInt32 dosTime)
+{
+ if (dosTime == 0)
+ return true;
+ unsigned month = (dosTime >> 21) & 0xF;
+ unsigned day = (dosTime >> 16) & 0x1F;
+ unsigned hour = (dosTime >> 11) & 0x1F;
+ unsigned min = (dosTime >> 5) & 0x3F;
+ unsigned sec = (dosTime & 0x1F) * 2;
+ if (month < 1 || month > 12 || day < 1 || day > 31 || hour > 23 || min > 59 || sec > 59)
+ return false;
+ return true;
+}
-static inline bool TestMarkerCandidate(const Byte *p, UInt32 &value)
+API_FUNC_IsArc IsArc_Zip(const Byte *p, size_t size)
{
+ if (size < 8)
+ return k_IsArc_Res_NEED_MORE;
+ if (p[0] != 'P')
+ return k_IsArc_Res_NO;
+
+ UInt32 value = Get32(p);
+
+ if (value == NSignature::kNoSpan)
+ {
+ p += 4;
+ size -= 4;
+ }
+
value = Get32(p);
- return
- (value == NSignature::kLocalFileHeader) ||
- (value == NSignature::kEndOfCentralDir);
+
+ if (value == NSignature::kEcd)
+ {
+ if (size < kEcdSize)
+ return k_IsArc_Res_NEED_MORE;
+ CEcd ecd;
+ ecd.Parse(p + 4);
+ // if (ecd.cdSize != 0)
+ if (!ecd.IsEmptyArc())
+ return k_IsArc_Res_NO;
+ return k_IsArc_Res_YES; // k_IsArc_Res_YES_2;
+ }
+
+ if (value != NSignature::kLocalFileHeader)
+ return k_IsArc_Res_NO;
+
+ if (size < kLocalHeaderSize)
+ return k_IsArc_Res_NEED_MORE;
+
+ p += 4;
+
+ {
+ const unsigned kPureHeaderSize = kLocalHeaderSize - 4;
+ unsigned i;
+ for (i = 0; i < kPureHeaderSize && p[i] == 0; i++);
+ if (i == kPureHeaderSize)
+ return k_IsArc_Res_NEED_MORE;
+ }
+
+ /*
+ if (p[0] >= 128) // ExtractVersion.Version;
+ return k_IsArc_Res_NO;
+ */
+
+ // ExtractVersion.Version = p[0];
+ // ExtractVersion.HostOS = p[1];
+ // Flags = Get16(p + 2);
+ // Method = Get16(p + 4);
+ /*
+ // 9.33: some zip archives contain incorrect value in timestamp. So we don't check it now
+ UInt32 dosTime = Get32(p + 6);
+ if (!CheckDosTime(dosTime))
+ return k_IsArc_Res_NO;
+ */
+ // Crc = Get32(p + 10);
+ // PackSize = Get32(p + 14);
+ // Size = Get32(p + 18);
+ unsigned nameSize = Get16(p + 22);
+ unsigned extraSize = Get16(p + 24);
+ UInt32 extraOffset = kLocalHeaderSize + (UInt32)nameSize;
+ if (extraOffset + extraSize > (1 << 16))
+ return k_IsArc_Res_NO;
+
+ p -= 4;
+
+ {
+ size_t rem = size - kLocalHeaderSize;
+ if (rem > nameSize)
+ rem = nameSize;
+ const Byte *p2 = p + kLocalHeaderSize;
+ for (size_t i = 0; i < rem; i++)
+ if (p2[i] == 0)
+ return k_IsArc_Res_NO;
+ }
+
+ if (size < extraOffset)
+ return k_IsArc_Res_NEED_MORE;
+
+ if (extraSize > 0)
+ {
+ p += extraOffset;
+ size -= extraOffset;
+ while (extraSize != 0)
+ {
+ if (extraSize < 4)
+ {
+ // 7-Zip before 9.31 created incorrect WsAES Extra in folder's local headers.
+ // so we return k_IsArc_Res_YES to support such archives.
+ // return k_IsArc_Res_NO; // do we need to support such extra ?
+ return k_IsArc_Res_YES;
+ }
+ if (size < 4)
+ return k_IsArc_Res_NEED_MORE;
+ unsigned dataSize = Get16(p + 2);
+ size -= 4;
+ extraSize -= 4;
+ p += 4;
+ if (dataSize > extraSize)
+ return k_IsArc_Res_NO;
+ if (dataSize > size)
+ return k_IsArc_Res_NEED_MORE;
+ size -= dataSize;
+ extraSize -= dataSize;
+ p += dataSize;
+ }
+ }
+
+ return k_IsArc_Res_YES;
}
-static const UInt32 kNumMarkerAddtionalBytes = 2;
-static inline bool TestMarkerCandidate2(const Byte *p, UInt32 &value)
+static UInt32 IsArc_Zip_2(const Byte *p, size_t size, bool isFinal)
{
- value = Get32(p);
- if (value == NSignature::kEndOfCentralDir)
- return (Get16(p + 4) == 0);
- return (value == NSignature::kLocalFileHeader && p[4] < 128);
+ UInt32 res = IsArc_Zip(p, size);
+ if (res == k_IsArc_Res_NEED_MORE && isFinal)
+ return k_IsArc_Res_NO;
+ return res;
}
-HRESULT CInArchive::FindAndReadMarker(IInStream *stream, const UInt64 *searchHeaderSizeLimit)
+HRESULT CInArchive::FindAndReadMarker(IInStream *stream, const UInt64 *searchLimit)
{
ArcInfo.Clear();
- m_Position = m_StreamStartPosition;
+ ArcInfo.MarkerPos = m_Position;
+ ArcInfo.MarkerPos2 = m_Position;
- Byte marker[NSignature::kMarkerSize];
- RINOK(ReadStream_FALSE(stream, marker, NSignature::kMarkerSize));
- m_Position += NSignature::kMarkerSize;
- if (TestMarkerCandidate(marker, m_Signature))
- return S_OK;
+ if (searchLimit && *searchLimit == 0)
+ {
+ const unsigned kStartBufSize = kMarkerSize;
+ Byte startBuf[kStartBufSize];
+ size_t processed = kStartBufSize;
+ 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;
+ 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)
+ return S_FALSE;
+ ArcInfo.MarkerPos2 += 4;
+ }
+
+ // 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.
+ }
+
+ const size_t kBufSize = (size_t)1 << 18; // must be larger than kCheckSize
+ const size_t kCheckSize = (size_t)1 << 16; // must be smaller than kBufSize
+ CByteArr buffer(kBufSize);
+
+ size_t numBytesInBuffer = 0;
+ UInt64 curScanPos = 0;
- CByteDynamicBuffer dynamicBuffer;
- const UInt32 kSearchMarkerBufferSize = 0x10000;
- dynamicBuffer.EnsureCapacity(kSearchMarkerBufferSize);
- Byte *buffer = dynamicBuffer;
- UInt32 numBytesPrev = NSignature::kMarkerSize - 1;
- memcpy(buffer, marker + 1, numBytesPrev);
- UInt64 curTestPos = m_StreamStartPosition + 1;
for (;;)
{
- if (searchHeaderSizeLimit != NULL)
- if (curTestPos - m_StreamStartPosition > *searchHeaderSizeLimit)
- break;
- size_t numReadBytes = kSearchMarkerBufferSize - numBytesPrev;
- RINOK(ReadStream(stream, buffer + numBytesPrev, &numReadBytes));
+ size_t numReadBytes = kBufSize - numBytesInBuffer;
+ RINOK(ReadStream(stream, buffer + numBytesInBuffer, &numReadBytes));
m_Position += numReadBytes;
- UInt32 numBytesInBuffer = numBytesPrev + (UInt32)numReadBytes;
- const UInt32 kMarker2Size = NSignature::kMarkerSize + kNumMarkerAddtionalBytes;
- if (numBytesInBuffer < kMarker2Size)
+ numBytesInBuffer += numReadBytes;
+ bool isFinished = (numBytesInBuffer != kBufSize);
+
+ size_t limit = (isFinished ? numBytesInBuffer : numBytesInBuffer - kCheckSize);
+
+ if (searchLimit && curScanPos + limit > *searchLimit)
+ limit = (size_t)(*searchLimit - curScanPos + 1);
+
+ if (limit < 1)
break;
- UInt32 numTests = numBytesInBuffer - kMarker2Size + 1;
- for (UInt32 pos = 0; pos < numTests; pos++)
+
+ const Byte *buf = buffer;
+ for (size_t pos = 0; pos < limit; pos++)
{
- if (buffer[pos] != 0x50)
+ if (buf[pos] != 0x50)
+ continue;
+ if (buf[pos + 1] != 0x4B)
continue;
- if (TestMarkerCandidate2(buffer + pos, m_Signature))
+ size_t rem = numBytesInBuffer - pos;
+ UInt32 res = IsArc_Zip_2(buf + pos, rem, isFinished);
+ if (res != k_IsArc_Res_NO)
{
- curTestPos += pos;
- ArcInfo.StartPosition = curTestPos;
- m_Position = curTestPos + NSignature::kMarkerSize;
+ if (rem < kMarkerSize)
+ return S_FALSE;
+ m_Signature = Get32(buf + pos);
+ ArcInfo.MarkerPos += curScanPos + pos;
+ ArcInfo.MarkerPos2 = ArcInfo.MarkerPos;
+ if (m_Signature == NSignature::kNoSpan)
+ {
+ m_Signature = Get32(buf + pos + 4);
+ ArcInfo.MarkerPos2 += 4;
+ }
+ m_Position = ArcInfo.MarkerPos2 + kMarkerSize;
return S_OK;
}
}
- curTestPos += numTests;
- numBytesPrev = numBytesInBuffer - numTests;
- memmove(buffer, buffer + numTests, numBytesPrev);
+
+ if (isFinished)
+ break;
+
+ curScanPos += limit;
+ numBytesInBuffer -= limit;
+ memmove(buffer, buffer + limit, numBytesInBuffer);
}
+
return S_FALSE;
}
+HRESULT CInArchive::IncreaseRealPosition(UInt64 addValue)
+{
+ return Stream->Seek(addValue, STREAM_SEEK_CUR, &m_Position);
+}
+
+class CUnexpectEnd {};
+
HRESULT CInArchive::ReadBytes(void *data, UInt32 size, UInt32 *processedSize)
{
size_t realProcessedSize = size;
@@ -121,42 +365,35 @@ HRESULT CInArchive::ReadBytes(void *data, UInt32 size, UInt32 *processedSize)
catch (const CInBufferException &e) { return e.ErrorCode; }
}
else
- result = ReadStream(m_Stream, data, &realProcessedSize);
- if (processedSize != NULL)
+ result = ReadStream(Stream, data, &realProcessedSize);
+ if (processedSize)
*processedSize = (UInt32)realProcessedSize;
m_Position += realProcessedSize;
return result;
}
-void CInArchive::Skip(UInt64 num)
-{
- for (UInt64 i = 0; i < num; i++)
- ReadByte();
-}
-
-void CInArchive::IncreaseRealPosition(UInt64 addValue)
-{
- if (m_Stream->Seek(addValue, STREAM_SEEK_CUR, &m_Position) != S_OK)
- throw CInArchiveException(CInArchiveException::kSeekStreamError);
-}
-
-bool CInArchive::ReadBytesAndTestSize(void *data, UInt32 size)
-{
- UInt32 realProcessedSize;
- if (ReadBytes(data, size, &realProcessedSize) != S_OK)
- throw CInArchiveException(CInArchiveException::kReadStreamError);
- return (realProcessedSize == size);
-}
-
-void CInArchive::SafeReadBytes(void *data, UInt32 size)
+void CInArchive::SafeReadBytes(void *data, unsigned size)
{
- if (!ReadBytesAndTestSize(data, size))
- throw CInArchiveException(CInArchiveException::kUnexpectedEndOfArchive);
+ size_t processed = size;
+ if (_inBufMode)
+ {
+ processed = _inBuffer.ReadBytes((Byte *)data, size);
+ m_Position += processed;
+ }
+ else
+ {
+ HRESULT result = ReadStream(Stream, data, &processed);
+ m_Position += processed;
+ if (result != S_OK)
+ throw CSystemException(result);
+ }
+ if (processed != size)
+ throw CUnexpectEnd();
}
-void CInArchive::ReadBuffer(CByteBuffer &buffer, UInt32 size)
+void CInArchive::ReadBuffer(CByteBuffer &buffer, unsigned size)
{
- buffer.SetCapacity(size);
+ buffer.Alloc(size);
if (size > 0)
SafeReadBytes(buffer, size);
}
@@ -168,65 +405,73 @@ Byte CInArchive::ReadByte()
return b;
}
-UInt16 CInArchive::ReadUInt16()
-{
- Byte buf[2];
- SafeReadBytes(buf, 2);
- return Get16(buf);
-}
+UInt16 CInArchive::ReadUInt16() { Byte buf[2]; SafeReadBytes(buf, 2); return Get16(buf); }
+UInt32 CInArchive::ReadUInt32() { Byte buf[4]; SafeReadBytes(buf, 4); return Get32(buf); }
+UInt64 CInArchive::ReadUInt64() { Byte buf[8]; SafeReadBytes(buf, 8); return Get64(buf); }
-UInt32 CInArchive::ReadUInt32()
+void CInArchive::Skip(unsigned num)
{
- Byte buf[4];
- SafeReadBytes(buf, 4);
- return Get32(buf);
+ if (_inBufMode)
+ {
+ size_t skip = _inBuffer.Skip(num);
+ m_Position += skip;
+ if (skip != num)
+ throw CUnexpectEnd();
+ }
+ else
+ {
+ for (unsigned i = 0; i < num; i++)
+ ReadByte();
+ }
}
-UInt64 CInArchive::ReadUInt64()
+void CInArchive::Skip64(UInt64 num)
{
- Byte buf[8];
- SafeReadBytes(buf, 8);
- return Get64(buf);
+ for (UInt64 i = 0; i < num; i++)
+ ReadByte();
}
-bool CInArchive::ReadUInt32(UInt32 &value)
-{
- Byte buf[4];
- if (!ReadBytesAndTestSize(buf, 4))
- return false;
- value = Get32(buf);
- return true;
-}
-void CInArchive::ReadFileName(UInt32 nameSize, AString &dest)
+void CInArchive::ReadFileName(unsigned size, AString &s)
{
- if (nameSize == 0)
- dest.Empty();
- char *p = dest.GetBuffer((int)nameSize);
- SafeReadBytes(p, nameSize);
- p[nameSize] = 0;
- dest.ReleaseBuffer();
+ if (size == 0)
+ {
+ s.Empty();
+ return;
+ }
+ char *p = s.GetBuffer(size);
+ SafeReadBytes(p, size);
+ p[size] = 0;
+ s.ReleaseBuffer();
}
-void CInArchive::ReadExtra(UInt32 extraSize, CExtraBlock &extraBlock,
+bool CInArchive::ReadExtra(unsigned extraSize, CExtraBlock &extraBlock,
UInt64 &unpackSize, UInt64 &packSize, UInt64 &localHeaderOffset, UInt32 &diskStartNumber)
{
extraBlock.Clear();
UInt32 remain = extraSize;
- while(remain >= 4)
+ while (remain >= 4)
{
CExtraSubBlock subBlock;
subBlock.ID = ReadUInt16();
- UInt32 dataSize = ReadUInt16();
+ unsigned dataSize = ReadUInt16();
remain -= 4;
if (dataSize > remain) // it's bug
- dataSize = remain;
+ {
+ HeadersWarning = true;
+ Skip(remain);
+ return false;
+ }
if (subBlock.ID == NFileHeader::NExtraID::kZip64)
{
if (unpackSize == 0xFFFFFFFF)
{
if (dataSize < 8)
- break;
+ {
+ HeadersWarning = true;
+ Skip(remain);
+ return false;
+ }
unpackSize = ReadUInt64();
remain -= 8;
dataSize -= 8;
@@ -255,8 +500,7 @@ void CInArchive::ReadExtra(UInt32 extraSize, CExtraBlock &extraBlock,
remain -= 4;
dataSize -= 4;
}
- for (UInt32 i = 0; i < dataSize; i++)
- ReadByte();
+ Skip(dataSize);
}
else
{
@@ -265,88 +509,125 @@ void CInArchive::ReadExtra(UInt32 extraSize, CExtraBlock &extraBlock,
}
remain -= dataSize;
}
+ if (remain != 0)
+ {
+ ExtraMinorError = true;
+ // 7-Zip before 9.31 created incorrect WsAES Extra in folder's local headers.
+ // so we don't return false, but just set warning flag
+ // return false;
+ }
Skip(remain);
+ return true;
}
-HRESULT CInArchive::ReadLocalItem(CItemEx &item)
+bool CInArchive::ReadLocalItem(CItemEx &item)
{
- const int kBufSize = 26;
- Byte p[kBufSize];
- SafeReadBytes(p, kBufSize);
+ const unsigned kPureHeaderSize = kLocalHeaderSize - 4;
+ Byte p[kPureHeaderSize];
+ SafeReadBytes(p, kPureHeaderSize);
+ {
+ unsigned i;
+ for (i = 0; i < kPureHeaderSize && p[i] == 0; i++);
+ if (i == kPureHeaderSize)
+ return false;
+ }
item.ExtractVersion.Version = p[0];
item.ExtractVersion.HostOS = p[1];
item.Flags = Get16(p + 2);
- item.CompressionMethod = Get16(p + 4);
+ item.Method = Get16(p + 4);
item.Time = Get32(p + 6);
- item.FileCRC = Get32(p + 10);
+ item.Crc = Get32(p + 10);
item.PackSize = Get32(p + 14);
- item.UnPackSize = Get32(p + 18);
- UInt32 fileNameSize = Get16(p + 22);
- item.LocalExtraSize = Get16(p + 24);
- ReadFileName(fileNameSize, item.Name);
- item.FileHeaderWithNameSize = 4 + NFileHeader::kLocalBlockSize + fileNameSize;
- if (item.LocalExtraSize > 0)
+ item.Size = Get32(p + 18);
+ unsigned nameSize = Get16(p + 22);
+ unsigned extraSize = Get16(p + 24);
+ ReadFileName(nameSize, item.Name);
+ item.LocalFullHeaderSize = kLocalHeaderSize + (UInt32)nameSize + extraSize;
+
+ /*
+ if (item.IsDir())
+ item.Size = 0; // check It
+ */
+
+ if (extraSize > 0)
{
UInt64 localHeaderOffset = 0;
UInt32 diskStartNumber = 0;
- ReadExtra(item.LocalExtraSize, item.LocalExtra, item.UnPackSize, item.PackSize,
- localHeaderOffset, diskStartNumber);
+ if (!ReadExtra(extraSize, item.LocalExtra, item.Size, item.PackSize,
+ localHeaderOffset, diskStartNumber))
+ return false;
}
- /*
- if (item.IsDir())
- item.UnPackSize = 0; // check It
- */
- return S_OK;
+ if (!CheckDosTime(item.Time))
+ {
+ HeadersWarning = true;
+ // return false;
+ }
+ if (item.Name.Len() != nameSize)
+ return false;
+ return item.LocalFullHeaderSize <= ((UInt32)1 << 16);
}
-static bool FlagsAreSame(CItem &i1, CItem &i2)
+static bool FlagsAreSame(const CItem &i1, const CItem &i2)
{
- if (i1.CompressionMethod != i2.CompressionMethod)
+ if (i1.Method != i2.Method)
return false;
- // i1.Time
-
if (i1.Flags == i2.Flags)
return true;
UInt32 mask = 0xFFFF;
- switch(i1.CompressionMethod)
+ switch(i1.Method)
{
case NFileHeader::NCompressionMethod::kDeflated:
mask = 0x7FF9;
break;
default:
- if (i1.CompressionMethod <= NFileHeader::NCompressionMethod::kImploded)
+ if (i1.Method <= NFileHeader::NCompressionMethod::kImploded)
mask = 0x7FFF;
}
return ((i1.Flags & mask) == (i2.Flags & mask));
}
+static bool AreItemsEqual(const CItemEx &localItem, const CItemEx &cdItem)
+{
+ if (!FlagsAreSame(cdItem, localItem))
+ return false;
+ if (!localItem.HasDescriptor())
+ {
+ if (cdItem.Crc != localItem.Crc ||
+ cdItem.PackSize != localItem.PackSize ||
+ cdItem.Size != localItem.Size)
+ return false;
+ }
+ /* pkzip 2.50 creates incorrect archives. It uses
+ - WIN encoding for name in local header
+ - OEM encoding for name in central header
+ We don't support these strange items. */
+
+ /* if (cdItem.Name.Len() != localItem.Name.Len())
+ return false;
+ */
+ if (cdItem.Name != localItem.Name)
+ return false;
+ return true;
+}
+
HRESULT CInArchive::ReadLocalItemAfterCdItem(CItemEx &item)
{
if (item.FromLocal)
return S_OK;
try
{
- RINOK(Seek(ArcInfo.Base + item.LocalHeaderPosition));
+ UInt64 offset = ArcInfo.Base + item.LocalHeaderPos;
+ if (ArcInfo.Base < 0 && (Int64)offset < 0)
+ return S_FALSE;
+ RINOK(Seek(offset));
CItemEx localItem;
if (ReadUInt32() != NSignature::kLocalFileHeader)
return S_FALSE;
- RINOK(ReadLocalItem(localItem));
- if (!FlagsAreSame(item, localItem))
+ ReadLocalItem(localItem);
+ if (!AreItemsEqual(localItem, item))
return S_FALSE;
-
- if ((!localItem.HasDescriptor() &&
- (
- item.FileCRC != localItem.FileCRC ||
- item.PackSize != localItem.PackSize ||
- item.UnPackSize != localItem.UnPackSize
- )
- ) ||
- item.Name.Length() != localItem.Name.Length()
- )
- return S_FALSE;
- item.FileHeaderWithNameSize = localItem.FileHeaderWithNameSize;
- item.LocalExtraSize = localItem.LocalExtraSize;
+ item.LocalFullHeaderSize = localItem.LocalFullHeaderSize;
item.LocalExtra = localItem.LocalExtra;
item.FromLocal = true;
}
@@ -356,53 +637,45 @@ HRESULT CInArchive::ReadLocalItemAfterCdItem(CItemEx &item)
HRESULT CInArchive::ReadLocalItemDescriptor(CItemEx &item)
{
- if (item.HasDescriptor())
+ const unsigned kBufSize = (1 << 12);
+ Byte buf[kBufSize];
+
+ UInt32 numBytesInBuffer = 0;
+ UInt32 packedSize = 0;
+
+ for (;;)
{
- const int kBufferSize = (1 << 12);
- Byte buffer[kBufferSize];
-
- UInt32 numBytesInBuffer = 0;
- UInt32 packedSize = 0;
-
- bool descriptorWasFound = false;
- for (;;)
+ UInt32 processedSize;
+ RINOK(ReadBytes(buf + numBytesInBuffer, kBufSize - numBytesInBuffer, &processedSize));
+ numBytesInBuffer += processedSize;
+ if (numBytesInBuffer < kDataDescriptorSize)
+ return S_FALSE;
+ UInt32 i;
+ for (i = 0; i <= numBytesInBuffer - kDataDescriptorSize; i++)
{
- UInt32 processedSize;
- RINOK(ReadBytes(buffer + numBytesInBuffer, kBufferSize - numBytesInBuffer, &processedSize));
- numBytesInBuffer += processedSize;
- if (numBytesInBuffer < NFileHeader::kDataDescriptorSize)
- return S_FALSE;
- UInt32 i;
- for (i = 0; i <= numBytesInBuffer - NFileHeader::kDataDescriptorSize; i++)
+ // descriptor signature field is Info-ZIP's extension to pkware Zip specification.
+ // New ZIP specification also allows descriptorSignature.
+ if (buf[i] != 0x50)
+ continue;
+ // !!!! It must be fixed for Zip64 archives
+ if (Get32(buf + i) == NSignature::kDataDescriptor)
{
- // descriptorSignature field is Info-ZIP's extension
- // to Zip specification.
- UInt32 descriptorSignature = Get32(buffer + i);
-
- // !!!! It must be fixed for Zip64 archives
- UInt32 descriptorPackSize = Get32(buffer + i + 8);
- if (descriptorSignature== NSignature::kDataDescriptor && descriptorPackSize == packedSize + i)
+ UInt32 descriptorPackSize = Get32(buf + i + 8);
+ if (descriptorPackSize == packedSize + i)
{
- descriptorWasFound = true;
- item.FileCRC = Get32(buffer + i + 4);
+ item.Crc = Get32(buf + i + 4);
item.PackSize = descriptorPackSize;
- item.UnPackSize = Get32(buffer + i + 12);
- IncreaseRealPosition(Int64(Int32(0 - (numBytesInBuffer - i - NFileHeader::kDataDescriptorSize))));
- break;
+ item.Size = Get32(buf + i + 12);
+ return IncreaseRealPosition(Int64(Int32(0 - (numBytesInBuffer - i - kDataDescriptorSize))));
}
}
- if (descriptorWasFound)
- break;
- packedSize += i;
- int j;
- for (j = 0; i < numBytesInBuffer; i++, j++)
- buffer[j] = buffer[i];
- numBytesInBuffer = j;
}
+ packedSize += i;
+ unsigned j;
+ for (j = 0; i < numBytesInBuffer; i++, j++)
+ buf[j] = buf[i];
+ numBytesInBuffer = j;
}
- else
- IncreaseRealPosition(item.PackSize);
- return S_OK;
}
HRESULT CInArchive::ReadLocalItemAfterCdItemFull(CItemEx &item)
@@ -433,7 +706,7 @@ HRESULT CInArchive::ReadLocalItemAfterCdItemFull(CItemEx &item)
unpackSize = ReadUInt32();
}
- if (crc != item.FileCRC || item.PackSize != packSize || item.UnPackSize != unpackSize)
+ if (crc != item.Crc || item.PackSize != packSize || item.Size != unpackSize)
return S_FALSE;
}
}
@@ -444,106 +717,161 @@ HRESULT CInArchive::ReadLocalItemAfterCdItemFull(CItemEx &item)
HRESULT CInArchive::ReadCdItem(CItemEx &item)
{
item.FromCentral = true;
- const int kBufSize = 42;
- Byte p[kBufSize];
- SafeReadBytes(p, kBufSize);
+ Byte p[kCentralHeaderSize - 4];
+ SafeReadBytes(p, kCentralHeaderSize - 4);
+
item.MadeByVersion.Version = p[0];
item.MadeByVersion.HostOS = p[1];
item.ExtractVersion.Version = p[2];
item.ExtractVersion.HostOS = p[3];
item.Flags = Get16(p + 4);
- item.CompressionMethod = Get16(p + 6);
+ item.Method = Get16(p + 6);
item.Time = Get32(p + 8);
- item.FileCRC = Get32(p + 12);
+ item.Crc = Get32(p + 12);
item.PackSize = Get32(p + 16);
- item.UnPackSize = Get32(p + 20);
- UInt16 headerNameSize = Get16(p + 24);
- UInt16 headerExtraSize = Get16(p + 26);
- UInt16 headerCommentSize = Get16(p + 28);
- UInt32 headerDiskNumberStart = Get16(p + 30);
- item.InternalAttributes = Get16(p + 32);
- item.ExternalAttributes = Get32(p + 34);
- item.LocalHeaderPosition = Get32(p + 38);
- ReadFileName(headerNameSize, item.Name);
+ item.Size = Get32(p + 20);
+ unsigned nameSize = Get16(p + 24);
+ UInt16 extraSize = Get16(p + 26);
+ UInt16 commentSize = Get16(p + 28);
+ UInt32 diskNumberStart = Get16(p + 30);
+ item.InternalAttrib = Get16(p + 32);
+ item.ExternalAttrib = Get32(p + 34);
+ item.LocalHeaderPos = Get32(p + 38);
+ ReadFileName(nameSize, item.Name);
- if (headerExtraSize > 0)
+ if (extraSize > 0)
{
- ReadExtra(headerExtraSize, item.CentralExtra, item.UnPackSize, item.PackSize,
- item.LocalHeaderPosition, headerDiskNumberStart);
+ ReadExtra(extraSize, item.CentralExtra, item.Size, item.PackSize,
+ item.LocalHeaderPos, diskNumberStart);
}
- if (headerDiskNumberStart != 0)
- throw CInArchiveException(CInArchiveException::kMultiVolumeArchiveAreNotSupported);
+ if (diskNumberStart != 0)
+ return E_NOTIMPL;
// May be these strings must be deleted
/*
if (item.IsDir())
- item.UnPackSize = 0;
+ item.Size = 0;
*/
- ReadBuffer(item.Comment, headerCommentSize);
+ ReadBuffer(item.Comment, commentSize);
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)
{
+ if (offset >= ((UInt64)1 << 63))
+ return S_FALSE;
RINOK(Seek(offset));
- const UInt32 kEcd64Size = 56;
- Byte buf[kEcd64Size];
- if (!ReadBytesAndTestSize(buf, kEcd64Size))
+ Byte buf[kEcd64_FullSize];
+
+ RINOK(ReadStream_FALSE(Stream, buf, kEcd64_FullSize));
+
+ if (Get32(buf) != NSignature::kEcd64)
return S_FALSE;
- if (Get32(buf) != NSignature::kZip64EndOfCentralDir)
+ UInt64 mainSize = Get64(buf + 4);
+ if (mainSize < kEcd64_MainSize || mainSize > ((UInt64)1 << 32))
return S_FALSE;
- // cdInfo.NumEntries = Get64(buf + 24);
- cdInfo.Size = Get64(buf + 40);
- cdInfo.Offset = Get64(buf + 48);
+ cdInfo.ParseEcd64(buf);
return S_OK;
}
HRESULT CInArchive::FindCd(CCdInfo &cdInfo)
{
UInt64 endPosition;
- RINOK(m_Stream->Seek(0, STREAM_SEEK_END, &endPosition));
- const UInt32 kBufSizeMax = (1 << 16) + kEcdSize + kZip64EcdLocatorSize;
- CByteBuffer byteBuffer;
- byteBuffer.SetCapacity(kBufSizeMax);
- Byte *buf = byteBuffer;
+ RINOK(Stream->Seek(0, STREAM_SEEK_END, &endPosition));
+
+ const UInt32 kBufSizeMax = ((UInt32)1 << 16) + kEcdSize + kEcd64Locator_Size + kEcd64_FullSize;
UInt32 bufSize = (endPosition < kBufSizeMax) ? (UInt32)endPosition : kBufSizeMax;
if (bufSize < kEcdSize)
return S_FALSE;
+ CByteArr byteBuffer(bufSize);
+
UInt64 startPosition = endPosition - bufSize;
- RINOK(m_Stream->Seek(startPosition, STREAM_SEEK_SET, &m_Position));
+ RINOK(Stream->Seek(startPosition, STREAM_SEEK_SET, &m_Position));
if (m_Position != startPosition)
return S_FALSE;
- if (!ReadBytesAndTestSize(buf, bufSize))
- return S_FALSE;
- for (int i = (int)(bufSize - kEcdSize); i >= 0; i--)
+
+ RINOK(ReadStream_FALSE(Stream, byteBuffer, bufSize));
+
+ const Byte *buf = byteBuffer;
+ for (UInt32 i = bufSize - kEcdSize;; i--)
{
- if (Get32(buf + i) == NSignature::kEndOfCentralDir)
+ if (buf[i] != 0x50)
{
- if (i >= kZip64EcdLocatorSize)
+ if (i == 0) return S_FALSE;
+ i--;
+ if (buf[i] != 0x50)
{
- const Byte *locator = buf + i - kZip64EcdLocatorSize;
- if (Get32(locator) == NSignature::kZip64EndOfCentralDirLocator)
+ if (i == 0) return S_FALSE;
+ continue;
+ }
+ }
+ if (Get32(buf + i) == NSignature::kEcd)
+ {
+ if (i >= kEcd64_FullSize + kEcd64Locator_Size)
+ {
+ 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
{
+ // Most of the zip64 use fixed size Zip64 ECD
+
UInt64 ecd64Offset = Get64(locator + 8);
- if (TryEcd64(ecd64Offset, cdInfo) == S_OK)
- return S_OK;
- if (TryEcd64(ArcInfo.StartPosition + ecd64Offset, cdInfo) == S_OK)
+ UInt64 absEcd64 = endPosition - bufSize + i - (kEcd64Locator_Size + kEcd64_FullSize);
+ {
+ const Byte *ecd64 = locator - kEcd64_FullSize;
+ if (Get32(ecd64) == NSignature::kEcd64 &&
+ Get64(ecd64 + 4) == kEcd64_MainSize)
+ {
+ cdInfo.ParseEcd64(ecd64);
+ ArcInfo.Base = absEcd64 - ecd64Offset;
+ return S_OK;
+ }
+ }
+
+ // some zip64 use variable size Zip64 ECD.
+ // we try to find it
+ if (absEcd64 != ecd64Offset)
+ {
+ if (TryEcd64(ecd64Offset, cdInfo) == S_OK)
+ {
+ ArcInfo.Base = 0;
+ return S_OK;
+ }
+ }
+ if (ArcInfo.MarkerPos != 0 &&
+ ArcInfo.MarkerPos + ecd64Offset != absEcd64)
{
- ArcInfo.Base = ArcInfo.StartPosition;
- return S_OK;
+ if (TryEcd64(ArcInfo.MarkerPos + ecd64Offset, cdInfo) == S_OK)
+ {
+ ArcInfo.Base = ArcInfo.MarkerPos;
+ return S_OK;
+ }
}
}
}
- if (Get32(buf + i + 4) == 0)
+ if (Get32(buf + i + 4) == 0) // ThisDiskNumber, StartCentralDirectoryDiskNumber;
{
- // cdInfo.NumEntries = GetUInt16(buf + i + 10);
- cdInfo.Size = Get32(buf + i + 12);
- cdInfo.Offset = Get32(buf + i + 16);
- UInt64 curPos = endPosition - bufSize + i;
+ cdInfo.ParseEcd(buf + i);
+ UInt64 absEcdPos = endPosition - bufSize + i;
UInt64 cdEnd = cdInfo.Size + cdInfo.Offset;
- if (curPos != cdEnd)
+ ArcInfo.Base = 0;
+ if (absEcdPos != cdEnd)
{
/*
if (cdInfo.Offset <= 16 && cdInfo.Size != 0)
@@ -553,290 +881,301 @@ HRESULT CInArchive::FindCd(CCdInfo &cdInfo)
}
else
*/
- ArcInfo.Base = curPos - cdEnd;
+ ArcInfo.Base = absEcdPos - cdEnd;
}
return S_OK;
}
}
+ if (i == 0)
+ return S_FALSE;
}
- return S_FALSE;
}
+
HRESULT CInArchive::TryReadCd(CObjectVector<CItemEx> &items, UInt64 cdOffset, UInt64 cdSize, CProgressVirt *progress)
{
items.Clear();
- RINOK(m_Stream->Seek(cdOffset, STREAM_SEEK_SET, &m_Position));
+ RINOK(Stream->Seek(cdOffset, STREAM_SEEK_SET, &m_Position));
if (m_Position != cdOffset)
return S_FALSE;
- if (!_inBuffer.Create(1 << 15))
- return E_OUTOFMEMORY;
- _inBuffer.SetStream(m_Stream);
_inBuffer.Init();
_inBufMode = true;
- while(m_Position - cdOffset < cdSize)
+ while (m_Position - cdOffset < cdSize)
{
if (ReadUInt32() != NSignature::kCentralFileHeader)
return S_FALSE;
CItemEx cdItem;
RINOK(ReadCdItem(cdItem));
items.Add(cdItem);
- if (progress && items.Size() % 1000 == 0)
- RINOK(progress->SetCompleted(items.Size()));
+ if (progress && items.Size() % 1 == 0)
+ RINOK(progress->SetCompletedCD(items.Size()));
}
return (m_Position - cdOffset == cdSize) ? S_OK : S_FALSE;
}
HRESULT CInArchive::ReadCd(CObjectVector<CItemEx> &items, UInt64 &cdOffset, UInt64 &cdSize, CProgressVirt *progress)
{
- ArcInfo.Base = 0;
CCdInfo cdInfo;
RINOK(FindCd(cdInfo));
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)
{
- res = TryReadCd(items, cdInfo.Offset + ArcInfo.StartPosition, cdSize, progress);
+ res = TryReadCd(items, ArcInfo.MarkerPos + cdOffset, cdSize, progress);
if (res == S_OK)
- ArcInfo.Base = ArcInfo.StartPosition;
+ ArcInfo.Base = ArcInfo.MarkerPos;
}
- if (!ReadUInt32(m_Signature))
- return S_FALSE;
return res;
}
-HRESULT CInArchive::ReadLocalsAndCd(CObjectVector<CItemEx> &items, CProgressVirt *progress, UInt64 &cdOffset, int &numCdItems)
+static HRESULT FindItem(const CObjectVector<CItemEx> &items, UInt64 offset)
{
- items.Clear();
- numCdItems = 0;
- while (m_Signature == NSignature::kLocalFileHeader)
+ unsigned left = 0, right = items.Size();
+ for (;;)
{
- // FSeek points to next byte after signature
- // NFileHeader::CLocalBlock localHeader;
- CItemEx item;
- item.LocalHeaderPosition = m_Position - m_StreamStartPosition - 4; // points to signature;
- RINOK(ReadLocalItem(item));
- item.FromLocal = true;
- ReadLocalItemDescriptor(item);
- items.Add(item);
- if (progress && items.Size() % 100 == 0)
- RINOK(progress->SetCompleted(items.Size()));
- if (!ReadUInt32(m_Signature))
- break;
+ if (left >= right)
+ return -1;
+ unsigned index = (left + right) / 2;
+ UInt64 position = items[index].LocalHeaderPos;
+ if (offset == position)
+ return index;
+ if (offset < position)
+ right = index;
+ else
+ left = index + 1;
}
- cdOffset = m_Position - 4;
- int i;
- for (i = 0; i < items.Size(); i++, numCdItems++)
- {
- if (progress && i % 1000 == 0)
- RINOK(progress->SetCompleted(items.Size()));
- if (m_Signature == NSignature::kEndOfCentralDir)
- break;
-
- if (m_Signature != NSignature::kCentralFileHeader)
- return S_FALSE;
+}
- CItemEx cdItem;
- RINOK(ReadCdItem(cdItem));
+bool IsStrangeItem(const CItem &item)
+{
+ return item.Name.Len() > (1 << 14) || item.Method > (1 << 8);
+}
- if (i == 0)
+HRESULT CInArchive::ReadLocals(
+ CObjectVector<CItemEx> &items, CProgressVirt *progress)
+{
+ items.Clear();
+ while (m_Signature == NSignature::kLocalFileHeader)
+ {
+ CItemEx item;
+ item.LocalHeaderPos = m_Position - 4 - ArcInfo.MarkerPos;
+ // we write ralative LocalHeaderPos here. Later we can correct it to real Base.
+ try
{
- int j;
- for (j = 0; j < items.Size(); j++)
+ ReadLocalItem(item);
+ item.FromLocal = true;
+ if (item.HasDescriptor())
+ ReadLocalItemDescriptor(item);
+ else
{
- CItemEx &item = items[j];
- if (item.Name == cdItem.Name)
- {
- ArcInfo.Base = item.LocalHeaderPosition - cdItem.LocalHeaderPosition;
- break;
- }
+ RINOK(IncreaseRealPosition(item.PackSize));
}
- if (j == items.Size())
- return S_FALSE;
+ items.Add(item);
+ m_Signature = ReadUInt32();
}
-
- int index;
- int left = 0, right = items.Size();
- for (;;)
+ catch (CUnexpectEnd &)
{
- if (left >= right)
+ if (items.IsEmpty() || items.Size() == 1 && IsStrangeItem(items[0]))
return S_FALSE;
- index = (left + right) / 2;
- UInt64 position = items[index].LocalHeaderPosition - ArcInfo.Base;
- if (cdItem.LocalHeaderPosition == position)
- break;
- if (cdItem.LocalHeaderPosition < position)
- right = index;
- else
- left = index + 1;
+ throw;
}
- CItemEx &item = items[index];
- // item.LocalHeaderPosition = cdItem.LocalHeaderPosition;
- item.MadeByVersion = cdItem.MadeByVersion;
- item.CentralExtra = cdItem.CentralExtra;
-
- if (
- // item.ExtractVersion != cdItem.ExtractVersion ||
- !FlagsAreSame(item, cdItem) ||
- item.FileCRC != cdItem.FileCRC)
- return S_FALSE;
+ if (progress && items.Size() % 1 == 0)
+ RINOK(progress->SetCompletedLocal(items.Size(), item.LocalHeaderPos));
+ }
- if (item.Name.Length() != cdItem.Name.Length() ||
- item.PackSize != cdItem.PackSize ||
- item.UnPackSize != cdItem.UnPackSize
- )
- return S_FALSE;
- item.Name = cdItem.Name;
- item.InternalAttributes = cdItem.InternalAttributes;
- item.ExternalAttributes = cdItem.ExternalAttributes;
- item.Comment = cdItem.Comment;
- item.FromCentral = cdItem.FromCentral;
- if (!ReadUInt32(m_Signature))
+ if (items.Size() == 1 && m_Signature != NSignature::kCentralFileHeader)
+ if (IsStrangeItem(items[0]))
return S_FALSE;
- }
- for (i = 0; i < items.Size(); i++)
- items[i].LocalHeaderPosition -= ArcInfo.Base;
return S_OK;
}
-struct CEcd
-{
- UInt16 thisDiskNumber;
- UInt16 startCDDiskNumber;
- UInt16 numEntriesInCDOnThisDisk;
- UInt16 numEntriesInCD;
- UInt32 cdSize;
- UInt32 cdStartOffset;
- UInt16 commentSize;
- void Parse(const Byte *p);
-};
-
-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);
-}
-
-struct CEcd64
-{
- 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)); }
-};
-
-void CEcd64::Parse(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);
-}
#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::ReadHeaders(CObjectVector<CItemEx> &items, CProgressVirt *progress)
+HRESULT CInArchive::ReadHeaders2(CObjectVector<CItemEx> &items, CProgressVirt *progress)
{
- IsOkHeaders = true;
- // m_Signature must be kLocalFileHeaderSignature or
- // kEndOfCentralDirSignature
+ items.Clear();
+
+ // m_Signature must be kLocalFileHeader or kEcd
// m_Position points to next byte after signature
+ RINOK(Stream->Seek(m_Position, STREAM_SEEK_SET, NULL));
- IsZip64 = false;
- items.Clear();
+ if (!_inBuffer.Create(1 << 15))
+ return E_OUTOFMEMORY;
+ _inBuffer.SetStream(Stream);
- UInt64 cdSize, cdStartOffset;
- HRESULT res;
- try
+ bool needReadCd = true;
+ bool localsWereRead = false;
+ if (m_Signature == NSignature::kEcd)
{
- res = ReadCd(items, cdStartOffset, cdSize, progress);
+ // 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 ??
+ if (!ecd.IsEmptyArc())
+ return S_FALSE;
+
+ ArcInfo.Base = ArcInfo.MarkerPos;
+ needReadCd = false;
+ IsArc = true; // check it: we need more tests?
+ RINOK(Stream->Seek(ArcInfo.MarkerPos2 + 4, STREAM_SEEK_SET, &m_Position));
}
- catch(CInArchiveException &)
+
+ UInt64 cdSize = 0, cdRelatOffset = 0, cdAbsOffset = 0;
+ HRESULT res = S_OK;
+
+ if (needReadCd)
{
- res = S_FALSE;
+ CItemEx firstItem;
+ // try
+ {
+ try
+ {
+ if (!ReadLocalItem(firstItem))
+ return S_FALSE;
+ }
+ catch(CUnexpectEnd &)
+ {
+ return S_FALSE;
+ }
+
+ IsArc = true;
+ res = ReadCd(items, cdRelatOffset, cdSize, progress);
+ if (res == S_OK)
+ m_Signature = ReadUInt32();
+ }
+ // catch() { res = S_FALSE; }
+ if (res != S_FALSE && res != S_OK)
+ return res;
+
+ if (res == S_OK && items.Size() == 0)
+ res = S_FALSE;
+
+ 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]))
+ res = S_FALSE;
+ ArcInfo.CdWasRead = true;
+ ArcInfo.FirstItemRelatOffset = items[0].LocalHeaderPos;
+ }
}
- if (res != S_FALSE && res != S_OK)
- return res;
- /*
- if (res != S_OK)
- return res;
- res = S_FALSE;
- */
+ CObjectVector<CItemEx> cdItems;
- int numCdItems = items.Size();
+ bool needSetBase = false;
+ unsigned numCdItems = items.Size();
+
if (res == S_FALSE)
{
+ // CD doesn't match firstItem so we clear items and read Locals.
+ items.Clear();
+ localsWereRead = true;
_inBufMode = false;
- ArcInfo.Base = 0;
- RINOK(m_Stream->Seek(ArcInfo.StartPosition, STREAM_SEEK_SET, &m_Position));
- if (m_Position != ArcInfo.StartPosition)
- return S_FALSE;
- if (!ReadUInt32(m_Signature))
- return S_FALSE;
- RINOK(ReadLocalsAndCd(items, progress, cdStartOffset, numCdItems));
- cdSize = (m_Position - 4) - cdStartOffset;
- cdStartOffset -= ArcInfo.Base;
+ ArcInfo.Base = ArcInfo.MarkerPos;
+ RINOK(Stream->Seek(ArcInfo.MarkerPos2, STREAM_SEEK_SET, &m_Position));
+ m_Signature = ReadUInt32();
+
+ RINOK(ReadLocals(items, progress));
+
+ if (m_Signature != NSignature::kCentralFileHeader)
+ {
+ m_Position -= 4;
+ NoCentralDir = true;
+ HeadersError = true;
+ return S_OK;
+ }
+ _inBufMode = true;
+ _inBuffer.Init();
+ cdAbsOffset = m_Position - 4;
+ for (;;)
+ {
+ CItemEx cdItem;
+ RINOK(ReadCdItem(cdItem));
+ cdItems.Add(cdItem);
+ if (progress && cdItems.Size() % 1 == 0)
+ RINOK(progress->SetCompletedCD(items.Size()));
+ m_Signature = ReadUInt32();
+ if (m_Signature != NSignature::kCentralFileHeader)
+ break;
+ }
+
+ cdSize = (m_Position - 4) - cdAbsOffset;
+ needSetBase = true;
+ numCdItems = cdItems.Size();
+
+ if (!cdItems.IsEmpty())
+ {
+ ArcInfo.CdWasRead = true;
+ ArcInfo.FirstItemRelatOffset = cdItems[0].LocalHeaderPos;
+ }
}
CEcd64 ecd64;
bool isZip64 = false;
- UInt64 zip64EcdStartOffset = m_Position - 4 - ArcInfo.Base;
- if (m_Signature == NSignature::kZip64EndOfCentralDir)
+ UInt64 ecd64AbsOffset = m_Position - 4;
+ if (m_Signature == NSignature::kEcd64)
{
IsZip64 = isZip64 = true;
UInt64 recordSize = ReadUInt64();
- const int kBufSize = kZip64EcdSize;
+ const unsigned kBufSize = kEcd64_MainSize;
Byte buf[kBufSize];
SafeReadBytes(buf, kBufSize);
ecd64.Parse(buf);
- Skip(recordSize - kZip64EcdSize);
- if (!ReadUInt32(m_Signature))
- return S_FALSE;
+ Skip64(recordSize - kEcd64_MainSize);
+ m_Signature = ReadUInt32();
+
if (ecd64.thisDiskNumber != 0 || ecd64.startCDDiskNumber != 0)
- throw CInArchiveException(CInArchiveException::kMultiVolumeArchiveAreNotSupported);
+ return E_NOTIMPL;
+
+ if (needSetBase)
+ {
+ ArcInfo.Base = cdAbsOffset - ecd64.cdStartOffset;
+ cdRelatOffset = ecd64.cdStartOffset;
+ needSetBase = false;
+ }
+
if (ecd64.numEntriesInCDOnThisDisk != numCdItems ||
ecd64.numEntriesInCD != numCdItems ||
ecd64.cdSize != cdSize ||
- (ecd64.cdStartOffset != cdStartOffset &&
+ (ecd64.cdStartOffset != cdRelatOffset &&
(!items.IsEmpty())))
return S_FALSE;
}
- if (m_Signature == NSignature::kZip64EndOfCentralDirLocator)
+ if (m_Signature == NSignature::kEcd64Locator)
{
+ if (!isZip64)
+ return S_FALSE;
/* UInt32 startEndCDDiskNumber = */ ReadUInt32();
- UInt64 endCDStartOffset = ReadUInt64();
+ UInt64 ecd64RelatOffset = ReadUInt64();
/* UInt32 numberOfDisks = */ ReadUInt32();
- if (zip64EcdStartOffset != endCDStartOffset)
- return S_FALSE;
- if (!ReadUInt32(m_Signature))
+ if (ecd64AbsOffset != ArcInfo.Base + ecd64RelatOffset)
return S_FALSE;
+ m_Signature = ReadUInt32();
}
- if (m_Signature != NSignature::kEndOfCentralDir)
+ if (m_Signature != NSignature::kEcd)
return S_FALSE;
- const int kBufSize = kEcdSize - 4;
+ const unsigned kBufSize = kEcdSize - 4;
Byte buf[kBufSize];
SafeReadBytes(buf, kBufSize);
CEcd ecd;
@@ -849,47 +1188,119 @@ HRESULT CInArchive::ReadHeaders(CObjectVector<CItemEx> &items, CProgressVirt *pr
COPY_ECD_ITEM_32(cdSize);
COPY_ECD_ITEM_32(cdStartOffset);
- ReadBuffer(ArcInfo.Comment, ecd.commentSize);
+ if (needSetBase)
+ {
+ ArcInfo.Base = cdAbsOffset - ecd64.cdStartOffset;
+ cdRelatOffset = ecd64.cdStartOffset;
+ needSetBase = false;
+ }
+
+ if (localsWereRead && (UInt64)ArcInfo.Base != ArcInfo.MarkerPos)
+ {
+ UInt64 delta = ArcInfo.MarkerPos - ArcInfo.Base;
+ for (unsigned i = 0; i < items.Size(); i++)
+ items[i].LocalHeaderPos += delta;
+ }
+
+
+ // ---------- merge Central Directory Items ----------
+
+ if (!cdItems.IsEmpty())
+ {
+ for (unsigned i = 0; i < cdItems.Size(); i++)
+ {
+ const CItemEx &cdItem = cdItems[i];
+ int index = FindItem(items, cdItem.LocalHeaderPos);
+ if (index == -1)
+ {
+ items.Add(cdItem);
+ continue;
+ }
+ CItemEx &item = items[index];
+ if (item.Name != cdItem.Name
+ // || item.Name.Len() != cdItem.Name.Len()
+ || item.PackSize != cdItem.PackSize
+ || item.Size != cdItem.Size
+ // item.ExtractVersion != cdItem.ExtractVersion
+ || !FlagsAreSame(item, cdItem)
+ || item.Crc != cdItem.Crc)
+ continue;
+
+ // item.LocalHeaderPos = cdItem.LocalHeaderPos;
+ // item.Name = cdItem.Name;
+ item.MadeByVersion = cdItem.MadeByVersion;
+ item.CentralExtra = cdItem.CentralExtra;
+ item.InternalAttrib = cdItem.InternalAttrib;
+ item.ExternalAttrib = cdItem.ExternalAttrib;
+ item.Comment = cdItem.Comment;
+ item.FromCentral = cdItem.FromCentral;
+ }
+ }
if (ecd64.thisDiskNumber != 0 || ecd64.startCDDiskNumber != 0)
- throw CInArchiveException(CInArchiveException::kMultiVolumeArchiveAreNotSupported);
- if (numCdItems != items.Size())
- IsOkHeaders = false;
- if ((UInt16)ecd64.numEntriesInCDOnThisDisk != ((UInt16)numCdItems) ||
+ return E_NOTIMPL;
+
+ if (isZip64)
+ {
+ if (ecd64.numEntriesInCDOnThisDisk != 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())
+ HeadersError = true;
+ }
+
+ ReadBuffer(ArcInfo.Comment, ecd.commentSize);
+ _inBufMode = false;
+ _inBuffer.Free();
+
+ if (
(UInt16)ecd64.numEntriesInCD != ((UInt16)numCdItems) ||
(UInt32)ecd64.cdSize != (UInt32)cdSize ||
- ((UInt32)(ecd64.cdStartOffset) != (UInt32)cdStartOffset &&
+ ((UInt32)(ecd64.cdStartOffset) != (UInt32)cdRelatOffset &&
(!items.IsEmpty())))
return S_FALSE;
- _inBufMode = false;
- _inBuffer.Free();
- ArcInfo.FinishPosition = m_Position;
+ // printf("\nOpen OK");
return S_OK;
}
+HRESULT CInArchive::ReadHeaders(CObjectVector<CItemEx> &items, CProgressVirt *progress)
+{
+ HRESULT res;
+ try
+ {
+ res = ReadHeaders2(items, progress);
+ }
+ 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;
+ }
+ ArcInfo.FinishPos = m_Position;
+ _inBufMode = false;
+ return res;
+}
+
ISequentialInStream* CInArchive::CreateLimitedStream(UInt64 position, UInt64 size)
{
CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream;
CMyComPtr<ISequentialInStream> stream(streamSpec);
- SeekInArchive(ArcInfo.Base + position);
- streamSpec->SetStream(m_Stream);
+ Stream->Seek(ArcInfo.Base + position, STREAM_SEEK_SET, NULL);
+ streamSpec->SetStream(Stream);
streamSpec->Init(size);
return stream.Detach();
}
-IInStream* CInArchive::CreateStream()
-{
- CMyComPtr<IInStream> stream = m_Stream;
- return stream.Detach();
-}
-
-bool CInArchive::SeekInArchive(UInt64 position)
-{
- UInt64 newPosition;
- if (m_Stream->Seek(position, STREAM_SEEK_SET, &newPosition) != S_OK)
- return false;
- return (newPosition == position);
-}
-
}}