This commit is contained in:
Igor Pavlov
2018-05-02 22:28:04 +01:00
committed by Kornel
parent f19b649c73
commit 18dc2b4161
121 changed files with 3523 additions and 1866 deletions

View File

@@ -549,9 +549,9 @@ private:
Byte *TempBuf;
UInt32 TempBufSize;
UInt32 TempBufWritten;
unsigned NumIdenticalFiles;
bool TempBufMode;
UInt32 m_BufStartFolderOffset;
unsigned m_StartIndex;
unsigned m_CurrentIndex;
@@ -575,7 +575,6 @@ private:
HRESULT OpenFile();
HRESULT CloseFileWithResOp(Int32 resOp);
HRESULT CloseFile();
HRESULT Write2(const void *data, UInt32 size, UInt32 *processedSize, bool isOK);
public:
HRESULT WriteEmptyFiles();
@@ -672,11 +671,11 @@ HRESULT CFolderOutStream::OpenFile()
FreeTempBuf();
TempBuf = (Byte *)MyAlloc(item.Size);
TempBufSize = item.Size;
if (TempBuf == NULL)
if (!TempBuf)
return E_OUTOFMEMORY;
}
TempBufMode = true;
m_BufStartFolderOffset = item.Offset;
TempBufWritten = 0;
}
else if (numExtractItems == 1)
{
@@ -725,8 +724,9 @@ HRESULT CFolderOutStream::WriteEmptyFiles()
}
HRESULT CFolderOutStream::Write2(const void *data, UInt32 size, UInt32 *processedSize, bool isOK)
HRESULT CFolderOutStream::Write(const void *data, UInt32 size, UInt32 *processedSize)
{
// (data == NULL) means Error_Data for solid folder flushing
COM_TRY_BEGIN
UInt32 realProcessed = 0;
@@ -741,21 +741,34 @@ HRESULT CFolderOutStream::Write2(const void *data, UInt32 size, UInt32 *processe
HRESULT res = S_OK;
if (numBytesToWrite != 0)
{
if (!isOK)
if (!data)
m_IsOk = false;
if (m_RealOutStream)
{
UInt32 processedSizeLocal = 0;
res = m_RealOutStream->Write((const Byte *)data, numBytesToWrite, &processedSizeLocal);
// 18.01 : we don't want ZEROs instead of missing data
if (data)
res = m_RealOutStream->Write((const Byte *)data, numBytesToWrite, &processedSizeLocal);
else
processedSizeLocal = numBytesToWrite;
numBytesToWrite = processedSizeLocal;
}
if (TempBufMode && TempBuf)
memcpy(TempBuf + (m_PosInFolder - m_BufStartFolderOffset), data, numBytesToWrite);
{
if (data)
{
memcpy(TempBuf + TempBufWritten, data, numBytesToWrite);
TempBufWritten += numBytesToWrite;
}
}
}
realProcessed += numBytesToWrite;
if (processedSize)
*processedSize = realProcessed;
data = (const void *)((const Byte *)data + numBytesToWrite);
if (data)
data = (const void *)((const Byte *)data + numBytesToWrite);
size -= numBytesToWrite;
m_RemainFileSize -= numBytesToWrite;
m_PosInFolder += numBytesToWrite;
@@ -773,7 +786,7 @@ HRESULT CFolderOutStream::Write2(const void *data, UInt32 size, UInt32 *processe
m_FileIsOpen = true;
m_CurrentIndex++;
if (result == S_OK && m_RealOutStream && TempBuf)
result = WriteStream(m_RealOutStream, TempBuf, (size_t)(m_PosInFolder - m_BufStartFolderOffset));
result = WriteStream(m_RealOutStream, TempBuf, TempBufWritten);
if (!TempBuf && TempBufMode && m_RealOutStream)
{
@@ -822,7 +835,8 @@ HRESULT CFolderOutStream::Write2(const void *data, UInt32 size, UInt32 *processe
realProcessed += numBytesToWrite;
if (processedSize)
*processedSize = realProcessed;
data = (const void *)((const Byte *)data + numBytesToWrite);
if (data)
data = (const void *)((const Byte *)data + numBytesToWrite);
size -= numBytesToWrite;
m_PosInFolder += numBytesToWrite;
}
@@ -843,12 +857,6 @@ HRESULT CFolderOutStream::Write2(const void *data, UInt32 size, UInt32 *processe
}
STDMETHODIMP CFolderOutStream::Write(const void *data, UInt32 size, UInt32 *processedSize)
{
return Write2(data, size, processedSize, true);
}
HRESULT CFolderOutStream::FlushCorrupted(unsigned folderIndex)
{
if (!NeedMoreWrite())
@@ -862,19 +870,16 @@ HRESULT CFolderOutStream::FlushCorrupted(unsigned folderIndex)
return S_OK;
}
const unsigned kBufSize = (1 << 12);
Byte buf[kBufSize];
for (unsigned i = 0; i < kBufSize; i++)
buf[i] = 0;
for (;;)
{
if (!NeedMoreWrite())
return S_OK;
UInt64 remain = GetRemain();
UInt32 size = (remain < kBufSize ? (UInt32)remain : (UInt32)kBufSize);
UInt32 size = (UInt32)1 << 20;
if (size > remain)
size = (UInt32)remain;
UInt32 processedSizeLocal = 0;
RINOK(Write2(buf, size, &processedSizeLocal, false));
RINOK(Write(NULL, size, &processedSizeLocal));
}
}

View File

@@ -219,6 +219,7 @@ class CHandler:
bool _masterCrcError;
bool _headersError;
UInt32 _dataStartOffset;
UInt64 _startPos;
UInt64 _phySize;
@@ -333,6 +334,7 @@ static const Byte kProps[] =
kpidCRC,
kpidComment,
kpidMethod
// kpidOffset
};
IMP_IInArchive_Props
@@ -631,17 +633,40 @@ bool CHandler::ParseBlob(const CByteBuffer &data)
HRESULT CHandler::Open2(IInStream *stream)
{
/*
- usual dmg contains Koly Header at the end:
- rare case dmg contains Koly Header at the start.
*/
_dataStartOffset = 0;
RINOK(stream->Seek(0, STREAM_SEEK_CUR, &_startPos));
UInt64 fileSize = 0;
RINOK(stream->Seek(0, STREAM_SEEK_END, &fileSize));
RINOK(stream->Seek(_startPos, STREAM_SEEK_SET, NULL));
Byte buf[HEADER_SIZE];
RINOK(ReadStream_FALSE(stream, buf, HEADER_SIZE));
UInt64 headerPos;
bool startKolyMode = false;
if (IsKoly(buf))
{
// it can be normal koly-at-the-end or koly-at-the-start
headerPos = _startPos;
if (_startPos <= (1 << 8))
{
// we want to support startKolyMode, even if there is
// some data before dmg file, like 128 bytes MacBin header
_dataStartOffset = HEADER_SIZE;
startKolyMode = true;
}
}
else
{
RINOK(stream->Seek(0, STREAM_SEEK_END, &headerPos));
// we check only koly-at-the-end
headerPos = fileSize;
if (headerPos < HEADER_SIZE)
return S_FALSE;
headerPos -= HEADER_SIZE;
@@ -667,24 +692,35 @@ HRESULT CHandler::Open2(IInStream *stream)
// CChecksum dataForkChecksum;
// dataForkChecksum.Parse(buf + 0x50);
_startPos = 0;
UInt64 top = 0;
if (!dataForkPair.UpdateTop(headerPos, top)) return S_FALSE;
if (!xmlPair.UpdateTop(headerPos, top)) return S_FALSE;
if (!rsrcPair.UpdateTop(headerPos, top)) return S_FALSE;
UInt64 limit = startKolyMode ? fileSize : headerPos;
if (!dataForkPair.UpdateTop(limit, top)) return S_FALSE;
if (!xmlPair.UpdateTop(limit, top)) return S_FALSE;
if (!rsrcPair.UpdateTop(limit, top)) return S_FALSE;
/* Some old dmg files contain garbage data in blobPair field.
So we need to ignore such garbage case;
And we still need to detect offset of start of archive for "parser" mode. */
bool useBlob = blobPair.UpdateTop(headerPos, top);
bool useBlob = blobPair.UpdateTop(limit, top);
_startPos = 0;
_phySize = headerPos + HEADER_SIZE;
if (top != headerPos)
if (startKolyMode)
_phySize = top;
else
{
_phySize = headerPos + HEADER_SIZE;
_startPos = 0;
if (top != headerPos)
{
/*
if expected absolute offset is not equal to real header offset,
2 cases are possible:
- additional (unknown) headers
- archive with offset.
So we try to read XML with absolute offset to select from these two ways.
*/
CForkPair xmlPair2 = xmlPair;
const char *sz = "<?xml version";
const unsigned len = (unsigned)strlen(sz);
@@ -694,9 +730,11 @@ HRESULT CHandler::Open2(IInStream *stream)
if (ReadData(stream, xmlPair2, buf2) != S_OK
|| memcmp(buf2, sz, len) != 0)
{
// if absolute offset is not OK, probably it's archive with offset
_startPos = headerPos - top;
_phySize = top + HEADER_SIZE;
}
}
}
// Byte reserved[0x78]
@@ -1041,6 +1079,14 @@ STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *val
break;
}
/*
case kpidOffset:
{
prop = item.StartPos;
break;
}
*/
case kpidMethod:
{
CMethods m;
@@ -1384,7 +1430,7 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,
break;
}
RINOK(_inStream->Seek(_startPos + item.StartPos + block.PackPos, STREAM_SEEK_SET, NULL));
RINOK(_inStream->Seek(_startPos + _dataStartOffset + item.StartPos + block.PackPos, STREAM_SEEK_SET, NULL));
streamSpec->Init(block.PackSize);
bool realMethod = true;
outStreamSpec->Init(block.UnpSize);
@@ -1781,7 +1827,7 @@ STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream)
spec->Stream = _inStream;
spec->Size = spec->File->Size;
RINOK(spec->InitAndSeek(_startPos));
RINOK(spec->InitAndSeek(_startPos + _dataStartOffset));
*stream = specStream.Detach();
return S_OK;

View File

@@ -567,6 +567,7 @@ static const char * const g_Machines[] =
static const CUInt32PCharPair g_MachinePairs[] =
{
{ 243, "RISC-V" },
{ 47787, "Xilinx MicroBlaze" }
// { 0x9026, "Alpha" }
};

View File

@@ -317,11 +317,15 @@ public:
// bool CaseSensetive;
UString ResFileName;
UInt64 SpecOffset;
UInt64 PhySize;
UInt64 PhySize2;
void Clear()
{
SpecOffset = 0;
PhySize = 0;
PhySize2 = 0;
HeadersError = false;
ThereAreAltStreams = false;
// CaseSensetive = false;
@@ -444,7 +448,7 @@ HRESULT CDatabase::ReadFile(const CFork &fork, CByteBuffer &buf, IInStream *inSt
e.NumBlocks > fork.NumBlocks - curBlock ||
e.NumBlocks > Header.NumBlocks - e.Pos)
return S_FALSE;
RINOK(inStream->Seek((UInt64)e.Pos << Header.BlockSizeLog, STREAM_SEEK_SET, NULL));
RINOK(inStream->Seek(SpecOffset + ((UInt64)e.Pos << Header.BlockSizeLog), STREAM_SEEK_SET, NULL));
RINOK(ReadStream_FALSE(inStream,
(Byte *)buf + ((size_t)curBlock << Header.BlockSizeLog),
(size_t)e.NumBlocks << Header.BlockSizeLog));
@@ -1154,13 +1158,36 @@ HRESULT CDatabase::LoadCatalog(const CFork &fork, const CObjectVector<CIdExtents
}
static const unsigned kHeaderPadSize = (1 << 10);
static const unsigned kMainHeaderSize = 512;
static const unsigned kHfsHeaderSize = kHeaderPadSize + kMainHeaderSize;
API_FUNC_static_IsArc IsArc_HFS(const Byte *p, size_t size)
{
if (size < kHfsHeaderSize)
return k_IsArc_Res_NEED_MORE;
p += kHeaderPadSize;
if (p[0] == 'B' && p[1] == 'D')
{
if (p[0x7C] != 'H' || p[0x7C + 1] != '+')
return k_IsArc_Res_NO;
}
else
{
if (p[0] != 'H' || (p[1] != '+' && p[1] != 'X'))
return k_IsArc_Res_NO;
UInt32 version = Get16(p + 2);
if (version < 4 || version > 5)
return k_IsArc_Res_NO;
}
return k_IsArc_Res_YES;
}
}
HRESULT CDatabase::Open2(IInStream *inStream, IArchiveOpenCallback *progress)
{
Clear();
static const unsigned kHeaderSize = kHeaderPadSize + 512;
Byte buf[kHeaderSize];
RINOK(ReadStream_FALSE(inStream, buf, kHeaderSize));
Byte buf[kHfsHeaderSize];
RINOK(ReadStream_FALSE(inStream, buf, kHfsHeaderSize));
{
for (unsigned i = 0; i < kHeaderPadSize; i++)
if (buf[i] != 0)
@@ -1171,6 +1198,67 @@ HRESULT CDatabase::Open2(IInStream *inStream, IArchiveOpenCallback *progress)
h.Header[0] = p[0];
h.Header[1] = p[1];
if (p[0] == 'B' && p[1] == 'D')
{
/*
It's header for old HFS format.
We don't support old HFS format, but we support
special HFS volume that contains embedded HFS+ volume
*/
if (p[0x7C] != 'H' || p[0x7C + 1] != '+')
return S_FALSE;
/*
h.CTime = Get32(p + 0x2);
h.MTime = Get32(p + 0x6);
h.NumFiles = Get32(p + 0x54);
h.NumFolders = Get32(p + 0x58);
if (h.NumFolders > ((UInt32)1 << 29) ||
h.NumFiles > ((UInt32)1 << 30))
return S_FALSE;
if (progress)
{
UInt64 numFiles = (UInt64)h.NumFiles + h.NumFolders + 1;
RINOK(progress->SetTotal(&numFiles, NULL));
}
h.NumFreeBlocks = Get16(p + 0x22);
*/
UInt32 blockSize = Get32(p + 0x14);
{
unsigned i;
for (i = 9; ((UInt32)1 << i) != blockSize; i++)
if (i == 31)
return S_FALSE;
h.BlockSizeLog = i;
}
h.NumBlocks = Get16(p + 0x12);
/*
we suppose that it has the follwing layout
{
start block with header
[h.NumBlocks]
end block with header
}
*/
PhySize2 = ((UInt64)h.NumBlocks + 2) << h.BlockSizeLog;
UInt32 startBlock = Get16(p + 0x7C + 2);
UInt32 blockCount = Get16(p + 0x7C + 4);
SpecOffset = (UInt64)(1 + startBlock) << h.BlockSizeLog;
UInt64 phy = SpecOffset + ((UInt64)blockCount << h.BlockSizeLog);
if (PhySize2 < phy)
PhySize2 = phy;
RINOK(inStream->Seek(SpecOffset, STREAM_SEEK_SET, NULL));
RINOK(ReadStream_FALSE(inStream, buf, kHfsHeaderSize));
}
if (p[0] != 'H' || (p[1] != '+' && p[1] != 'X'))
return S_FALSE;
h.Version = Get16(p + 2);
@@ -1330,7 +1418,14 @@ STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)
{
case kpidExtension: prop = Header.IsHfsX() ? "hfsx" : "hfs"; break;
case kpidMethod: prop = Header.IsHfsX() ? "HFSX" : "HFS+"; break;
case kpidPhySize: prop = PhySize; break;
case kpidPhySize:
{
UInt64 v = SpecOffset + PhySize;
if (v < PhySize2)
v = PhySize2;
prop = v;
break;
}
case kpidClusterSize: prop = (UInt32)1 << Header.BlockSizeLog; break;
case kpidFreeSpace: prop = (UInt64)Header.GetFreeSize(); break;
case kpidMTime: HfsTimeToProp(Header.MTime, prop); break;
@@ -1754,7 +1849,7 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,
if (fork.Size == pos)
break;
const CExtent &e = fork.Extents[extentIndex];
RINOK(_stream->Seek((UInt64)e.Pos << Header.BlockSizeLog, STREAM_SEEK_SET, NULL));
RINOK(_stream->Seek(SpecOffset + ((UInt64)e.Pos << Header.BlockSizeLog), STREAM_SEEK_SET, NULL));
UInt64 extentRem = (UInt64)e.NumBlocks << Header.BlockSizeLog;
while (extentRem != 0)
{
@@ -1865,6 +1960,7 @@ STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream)
}
static const Byte k_Signature[] = {
2, 'B', 'D',
4, 'H', '+', 0, 4,
4, 'H', 'X', 0, 5 };
@@ -1873,6 +1969,6 @@ REGISTER_ARC_I(
k_Signature,
kHeaderPadSize,
NArcInfoFlags::kMultiSignature,
NULL)
IsArc_HFS)
}}

View File

@@ -2201,6 +2201,11 @@ bool CHeader::ParseCoff(const Byte *p)
return false;
if (OptHeaderSize != 0 && OptHeaderSize < k_OptHeader32_Size_MIN)
return false;
// 18.04: we reduce false detections
if (NumSections == 0 && OptHeaderSize == 0)
return false;
for (unsigned i = 0; i < ARRAY_SIZE(g_MachinePairs); i++)
if (Machine == g_MachinePairs[i].Value)
return true;

View File

@@ -139,11 +139,10 @@ static unsigned ReadVarInt(const Byte *p, size_t maxSize, UInt64 *val)
{
*val = 0;
for (unsigned i = 0; i < maxSize;)
for (unsigned i = 0; i < maxSize && i < 10;)
{
Byte b = p[i];
if (i < 10)
*val |= (UInt64)(b & 0x7F) << (7 * i);
*val |= (UInt64)(b & 0x7F) << (7 * i);
i++;
if ((b & 0x80) == 0)
return i;
@@ -1363,7 +1362,9 @@ static const Byte kProps[] =
kpidCharacts,
kpidSymLink,
kpidHardLink,
kpidCopyLink
kpidCopyLink,
kpidVolumeIndex
};
@@ -1794,6 +1795,18 @@ STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *val
case kpidSplitBefore: prop = item.IsSplitBefore(); break;
case kpidSplitAfter: prop = lastItem.IsSplitAfter(); break;
case kpidVolumeIndex:
{
if (item.VolIndex < _arcs.Size())
{
const CInArcInfo &arcInfo = _arcs[item.VolIndex].Info;
if (arcInfo.IsVolume())
prop = (UInt64)arcInfo.GetVolIndex();
}
break;
}
case kpidCRC:
{
const CItem *item2 = (lastItem.IsSplitAfter() ? &item : &lastItem);

View File

@@ -180,7 +180,7 @@ struct CItem
AString Name;
int VolIndex;
unsigned VolIndex;
int NextItem;
UInt32 UnixMTime;

View File

@@ -777,7 +777,9 @@ static const Byte kProps[] =
kpidCRC,
kpidHostOS,
kpidMethod,
kpidUnpackVer
kpidUnpackVer,
kpidVolumeIndex
};
static const Byte kArcProps[] =
@@ -995,6 +997,12 @@ STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *val
case kpidCommented: prop = item.IsCommented(); break;
case kpidSplitBefore: prop = item.IsSplitBefore(); break;
case kpidSplitAfter: prop = _items[refItem.ItemIndex + refItem.NumItems - 1].IsSplitAfter(); break;
case kpidVolumeIndex:
if (_arcInfo.Is_VolNumber_Defined())
prop = (UInt32)(_arcInfo.VolNumber + refItem.VolumeIndex);
break;
case kpidCRC:
{
prop = ((lastItem.IsSplitAfter()) ? item.FileCRC : lastItem.FileCRC);

View File

@@ -26,7 +26,7 @@ struct CInArcInfo
UInt32 DataCRC;
bool EndOfArchive_was_Read;
CInArcInfo(): EndFlags(0), EndOfArchive_was_Read(false) {}
CInArcInfo(): EndFlags(0), EndOfArchive_was_Read(false), VolNumber(0) {}
UInt64 GetPhySize() const { return EndPos - StartPos; }

View File

@@ -94,7 +94,9 @@ static bool ParseInt64(const char *p, Int64 &val)
static bool ParseInt64_MTime(const char *p, Int64 &val)
{
// rare case tar contains spaces instead of MTime
// rare case tar : ZEROs in Docker-Windows TARs
// rare case tar : spaces
if (GetUi32(p) != 0)
for (unsigned i = 0; i < 12; i++)
if (p[i] != ' ')
return ParseInt64(p, val);

View File

@@ -1993,6 +1993,13 @@ HRESULT CVols::ParseArcName(IArchiveOpenVolumeCallback *volCallback)
}
else if (ext.IsEqualTo_Ascii_NoCase("exe"))
{
/* possible cases:
- exe with zip inside
- sfx: a.exe, a.z02, a.z03,... , a.zip
a.exe is start volume.
- zip renamed to exe
*/
StartIsExe = true;
BaseName = name;
StartVolIndex = 0;
@@ -2000,7 +2007,22 @@ HRESULT CVols::ParseArcName(IArchiveOpenVolumeCallback *volCallback)
We can open arc.zip, if it was requesed to open arc.exe.
But it's possible that arc.exe and arc.zip are not parts of same archive.
So we can disable such operation */
return S_FALSE; // don't open arc.zip instead of arc.exe
// 18.04: we still want to open zip renamed to exe.
/*
{
UString volName = name;
volName += IsUpperCase ? "Z01" : "z01";
{
CMyComPtr<IInStream> stream;
HRESULT res2 = volCallback->GetStream(volName, &stream);
if (res2 == S_OK)
DisableVolsSearch = true;
}
}
*/
DisableVolsSearch = true;
return S_OK;
}
else if (ext[0] == 'z' || ext[0] == 'Z')
{
@@ -2040,6 +2062,9 @@ HRESULT CVols::ParseArcName(IArchiveOpenVolumeCallback *volCallback)
HRESULT CInArchive::ReadVols2(IArchiveOpenVolumeCallback *volCallback,
unsigned start, int lastDisk, int zipDisk, unsigned numMissingVolsMax, unsigned &numMissingVols)
{
if (Vols.DisableVolsSearch)
return S_OK;
numMissingVols = 0;
for (unsigned i = start;; i++)
@@ -2090,6 +2115,8 @@ HRESULT CInArchive::ReadVols2(IArchiveOpenVolumeCallback *volCallback,
}
if (res == S_FALSE || !stream)
{
if (i == 1 && Vols.StartIsExe)
return S_OK;
if (Vols.MissingName.IsEmpty())
Vols.MissingName = volName;
numMissingVols++;

View File

@@ -162,6 +162,7 @@ struct CVols
bool NeedSeek;
bool DisableVolsSearch;
bool StartIsExe; // is .exe
bool StartIsZ; // is .zip or .zNN
bool StartIsZip; // is .zip
@@ -201,6 +202,7 @@ struct CVols
StreamIndex = -1;
NeedSeek = false;
DisableVolsSearch = false;
StartIsExe = false;
StartIsZ = false;
StartIsZip = false;