blob: 5a85507c19eb6223be2653c116911a903eafea22 [file] [log] [blame]
// 7zIn.cpp
#include "StdAfx.h"
#ifdef _WIN32
#include <wchar.h>
#else
#include <ctype.h>
#endif
#include "../../../../C/7zCrc.h"
#include "../../../../C/CpuArch.h"
#include "../../Common/StreamObjects.h"
#include "../../Common/StreamUtils.h"
#include "7zDecode.h"
#include "7zIn.h"
#define Get16(p) GetUi16(p)
#define Get32(p) GetUi32(p)
#define Get64(p) GetUi64(p)
// define FORMAT_7Z_RECOVERY if you want to recover multivolume archives with empty StartHeader
#ifndef _SFX
#define FORMAT_7Z_RECOVERY
#endif
using namespace NWindows;
using namespace NCOM;
namespace NArchive {
namespace N7z {
static const UInt32 k_LZMA2 = 0x21;
static const UInt32 k_LZMA = 0x030101;
static void BoolVector_Fill_False(CBoolVector &v, unsigned size)
{
v.ClearAndSetSize(size);
bool *p = &v[0];
for (unsigned i = 0; i < size; i++)
p[i] = false;
}
static bool BoolVector_GetAndSet(CBoolVector &v, UInt32 index)
{
if (index >= (UInt32)v.Size())
return true;
bool res = v[index];
v[index] = true;
return res;
}
bool CFolder::CheckStructure(unsigned numUnpackSizes) const
{
const unsigned kNumCodersMax = sizeof(UInt32) * 8; // don't change it
const unsigned kMaskSize = sizeof(UInt32) * 8; // it must be >= kNumCodersMax
const unsigned kNumBindsMax = 32;
if (Coders.Size() > kNumCodersMax || BindPairs.Size() > kNumBindsMax)
return false;
{
CBoolVector v;
BoolVector_Fill_False(v, BindPairs.Size() + PackStreams.Size());
unsigned i;
for (i = 0; i < BindPairs.Size(); i++)
if (BoolVector_GetAndSet(v, BindPairs[i].InIndex))
return false;
for (i = 0; i < PackStreams.Size(); i++)
if (BoolVector_GetAndSet(v, PackStreams[i]))
return false;
BoolVector_Fill_False(v, numUnpackSizes);
for (i = 0; i < BindPairs.Size(); i++)
if (BoolVector_GetAndSet(v, BindPairs[i].OutIndex))
return false;
}
UInt32 mask[kMaskSize];
unsigned i;
for (i = 0; i < kMaskSize; i++)
mask[i] = 0;
{
CUIntVector inStreamToCoder, outStreamToCoder;
for (i = 0; i < Coders.Size(); i++)
{
CNum j;
const CCoderInfo &coder = Coders[i];
for (j = 0; j < coder.NumInStreams; j++)
inStreamToCoder.Add(i);
for (j = 0; j < coder.NumOutStreams; j++)
outStreamToCoder.Add(i);
}
for (i = 0; i < BindPairs.Size(); i++)
{
const CBindPair &bp = BindPairs[i];
mask[inStreamToCoder[bp.InIndex]] |= (1 << outStreamToCoder[bp.OutIndex]);
}
}
for (i = 0; i < kMaskSize; i++)
for (unsigned j = 0; j < kMaskSize; j++)
if (((1 << j) & mask[i]) != 0)
mask[i] |= mask[j];
for (i = 0; i < kMaskSize; i++)
if (((1 << i) & mask[i]) != 0)
return false;
return true;
}
class CInArchiveException {};
class CUnsupportedFeatureException: public CInArchiveException {};
static void ThrowException() { throw CInArchiveException(); }
static inline void ThrowEndOfData() { ThrowException(); }
static inline void ThrowUnsupported() { throw CUnsupportedFeatureException(); }
static inline void ThrowIncorrect() { ThrowException(); }
class CStreamSwitch
{
CInArchive *_archive;
bool _needRemove;
bool _needUpdatePos;
public:
CStreamSwitch(): _needRemove(false), _needUpdatePos(false) {}
~CStreamSwitch() { Remove(); }
void Remove();
void Set(CInArchive *archive, const Byte *data, size_t size, bool needUpdatePos);
void Set(CInArchive *archive, const CByteBuffer &byteBuffer);
void Set(CInArchive *archive, const CObjectVector<CByteBuffer> *dataVector);
};
void CStreamSwitch::Remove()
{
if (_needRemove)
{
if (_archive->_inByteBack->GetRem() != 0)
_archive->ThereIsHeaderError = true;
_archive->DeleteByteStream(_needUpdatePos);
_needRemove = false;
}
}
void CStreamSwitch::Set(CInArchive *archive, const Byte *data, size_t size, bool needUpdatePos)
{
Remove();
_archive = archive;
_archive->AddByteStream(data, size);
_needRemove = true;
_needUpdatePos = needUpdatePos;
}
void CStreamSwitch::Set(CInArchive *archive, const CByteBuffer &byteBuffer)
{
Set(archive, byteBuffer, byteBuffer.Size(), false);
}
void CStreamSwitch::Set(CInArchive *archive, const CObjectVector<CByteBuffer> *dataVector)
{
Remove();
Byte external = archive->ReadByte();
if (external != 0)
{
CNum dataIndex = archive->ReadNum();
if (dataIndex >= dataVector->Size())
ThrowIncorrect();
Set(archive, (*dataVector)[dataIndex]);
}
}
void CInArchive::AddByteStream(const Byte *buf, size_t size)
{
if (_numInByteBufs == kNumBufLevelsMax)
ThrowIncorrect();
_inByteBack = &_inByteVector[_numInByteBufs++];
_inByteBack->Init(buf, size);
}
Byte CInByte2::ReadByte()
{
if (_pos >= _size)
ThrowEndOfData();
return _buffer[_pos++];
}
void CInByte2::ReadBytes(Byte *data, size_t size)
{
if (size > _size - _pos)
ThrowEndOfData();
memcpy(data, _buffer + _pos, size);
_pos += size;
}
void CInByte2::SkipData(UInt64 size)
{
if (size > _size - _pos)
ThrowEndOfData();
_pos += (size_t)size;
}
void CInByte2::SkipData()
{
SkipData(ReadNumber());
}
static UInt64 ReadNumberSpec(const Byte *p, size_t size, size_t &processed)
{
if (size == 0)
{
processed = 0;
return 0;
}
Byte firstByte = *p++;
size--;
if ((firstByte & 0x80) == 0)
{
processed = 1;
return firstByte;
}
Byte mask = 0x40;
if (size == 0)
{
processed = 0;
return 0;
}
UInt64 value = (UInt64)*p;
p++;
size--;
for (unsigned i = 1; i < 8; i++)
{
if ((firstByte & mask) == 0)
{
UInt64 highPart = firstByte & (mask - 1);
value += (highPart << (i * 8));
processed = i + 1;
return value;
}
if (size == 0)
{
processed = 0;
return 0;
}
value |= ((UInt64)*p << (i * 8));
p++;
size--;
mask >>= 1;
}
processed = 9;
return value;
}
UInt64 CInByte2::ReadNumber()
{
size_t processed;
UInt64 res = ReadNumberSpec(_buffer + _pos, _size - _pos, processed);
if (processed == 0)
ThrowEndOfData();
_pos += processed;
return res;
}
CNum CInByte2::ReadNum()
{
/*
if (_pos < _size)
{
Byte val = _buffer[_pos];
if ((unsigned)val < 0x80)
{
_pos++;
return (unsigned)val;
}
}
*/
UInt64 value = ReadNumber();
if (value > kNumMax)
ThrowUnsupported();
return (CNum)value;
}
UInt32 CInByte2::ReadUInt32()
{
if (_pos + 4 > _size)
ThrowEndOfData();
UInt32 res = Get32(_buffer + _pos);
_pos += 4;
return res;
}
UInt64 CInByte2::ReadUInt64()
{
if (_pos + 8 > _size)
ThrowEndOfData();
UInt64 res = Get64(_buffer + _pos);
_pos += 8;
return res;
}
#define CHECK_SIGNATURE if (p[0] != '7' || p[1] != 'z' || p[2] != 0xBC || p[3] != 0xAF || p[4] != 0x27 || p[5] != 0x1C) return false;
static inline bool TestSignature(const Byte *p)
{
CHECK_SIGNATURE
return CrcCalc(p + 12, 20) == Get32(p + 8);
}
#ifdef FORMAT_7Z_RECOVERY
static inline bool TestSignature2(const Byte *p)
{
CHECK_SIGNATURE;
if (CrcCalc(p + 12, 20) == Get32(p + 8))
return true;
for (unsigned i = 8; i < kHeaderSize; i++)
if (p[i] != 0)
return false;
return (p[6] != 0 || p[7] != 0);
}
#else
#define TestSignature2(p) TestSignature(p)
#endif
HRESULT CInArchive::FindAndReadSignature(IInStream *stream, const UInt64 *searchHeaderSizeLimit)
{
RINOK(ReadStream_FALSE(stream, _header, kHeaderSize));
if (TestSignature2(_header))
return S_OK;
if (searchHeaderSizeLimit && *searchHeaderSizeLimit == 0)
return S_FALSE;
const UInt32 kBufSize = 1 << 15;
CByteArr buf(kBufSize);
memcpy(buf, _header, kHeaderSize);
UInt64 offset = 0;
for (;;)
{
UInt32 readSize = kBufSize - kHeaderSize;
{
UInt64 rem = *searchHeaderSizeLimit - offset;
if (readSize > rem)
readSize = (UInt32)rem;
if (readSize == 0)
return S_FALSE;
}
UInt32 processed = 0;
RINOK(stream->Read(buf + kHeaderSize, readSize, &processed));
if (processed == 0)
return S_FALSE;
for (UInt32 pos = 0;;)
{
const Byte *p = buf + pos + 1;
const Byte *lim = buf + processed;
for (; p <= lim; p += 4)
{
if (p[0] == '7') break;
if (p[1] == '7') { p += 1; break; }
if (p[2] == '7') { p += 2; break; }
if (p[3] == '7') { p += 3; break; }
};
if (p > lim)
break;
pos = (UInt32)(p - buf);
if (TestSignature(p))
{
memcpy(_header, p, kHeaderSize);
_arhiveBeginStreamPosition += offset + pos;
return stream->Seek(_arhiveBeginStreamPosition + kHeaderSize, STREAM_SEEK_SET, NULL);
}
}
offset += processed;
memmove(buf, buf + processed, kHeaderSize);
}
}
// S_FALSE means that file is not archive
HRESULT CInArchive::Open(IInStream *stream, const UInt64 *searchHeaderSizeLimit)
{
HeadersSize = 0;
Close();
RINOK(stream->Seek(0, STREAM_SEEK_CUR, &_arhiveBeginStreamPosition))
RINOK(stream->Seek(0, STREAM_SEEK_END, &_fileEndPosition))
RINOK(stream->Seek(_arhiveBeginStreamPosition, STREAM_SEEK_SET, NULL))
RINOK(FindAndReadSignature(stream, searchHeaderSizeLimit));
_stream = stream;
return S_OK;
}
void CInArchive::Close()
{
_numInByteBufs = 0;
_stream.Release();
ThereIsHeaderError = false;
}
void CInArchive::ReadArchiveProperties(CInArchiveInfo & /* archiveInfo */)
{
for (;;)
{
if (ReadID() == NID::kEnd)
break;
SkipData();
}
}
// CFolder &folder can be non empty. So we must set all fields
void CInByte2::ParseFolder(CFolder &folder)
{
CNum numCoders = ReadNum();
folder.Coders.SetSize(numCoders);
CNum numInStreams = 0;
CNum numOutStreams = 0;
CNum i;
for (i = 0; i < numCoders; i++)
{
CCoderInfo &coder = folder.Coders[i];
{
Byte mainByte = ReadByte();
if ((mainByte & 0xC0) != 0)
ThrowUnsupported();
unsigned idSize = (mainByte & 0xF);
if (idSize > 8 || idSize > GetRem())
ThrowUnsupported();
const Byte *longID = GetPtr();
UInt64 id = 0;
for (unsigned j = 0; j < idSize; j++)
id = ((id << 8) | longID[j]);
SkipDataNoCheck(idSize);
coder.MethodID = id;
if ((mainByte & 0x10) != 0)
{
coder.NumInStreams = ReadNum();
coder.NumOutStreams = ReadNum();
}
else
{
coder.NumInStreams = 1;
coder.NumOutStreams = 1;
}
if ((mainByte & 0x20) != 0)
{
CNum propsSize = ReadNum();
coder.Props.Alloc((size_t)propsSize);
ReadBytes((Byte *)coder.Props, (size_t)propsSize);
}
else
coder.Props.Free();
}
numInStreams += coder.NumInStreams;
numOutStreams += coder.NumOutStreams;
}
CNum numBindPairs = numOutStreams - 1;
folder.BindPairs.SetSize(numBindPairs);
for (i = 0; i < numBindPairs; i++)
{
CBindPair &bp = folder.BindPairs[i];
bp.InIndex = ReadNum();
bp.OutIndex = ReadNum();
}
if (numInStreams < numBindPairs)
ThrowUnsupported();
CNum numPackStreams = numInStreams - numBindPairs;
folder.PackStreams.SetSize(numPackStreams);
if (numPackStreams == 1)
{
for (i = 0; i < numInStreams; i++)
if (folder.FindBindPairForInStream(i) < 0)
{
folder.PackStreams[0] = i;
break;
}
if (i == numInStreams)
ThrowUnsupported();
}
else
for (i = 0; i < numPackStreams; i++)
folder.PackStreams[i] = ReadNum();
}
void CFolders::ParseFolderInfo(unsigned folderIndex, CFolder &folder) const
{
size_t startPos = FoCodersDataOffset[folderIndex];
CInByte2 inByte;
inByte.Init(CodersData + startPos, FoCodersDataOffset[folderIndex + 1] - startPos);
inByte.ParseFolder(folder);
if (inByte.GetRem() != 0)
throw 20120424;
}
void CDatabase::GetPath(unsigned index, UString &path) const
{
path.Empty();
if (!NameOffsets || !NamesBuf)
return;
size_t offset = NameOffsets[index];
size_t size = NameOffsets[index + 1] - offset - 1;
if (size >= (1 << 20))
return;
wchar_t *s = path.GetBuffer((unsigned)size);
const Byte *p = ((const Byte *)NamesBuf + offset * 2);
#if defined(_WIN32) && defined(MY_CPU_LE)
wmemcpy(s, (const wchar_t *)p, size);
#else
for (size_t i = 0; i < size; i++)
{
*s = Get16(p);
p += 2;
s++;
}
#endif
path.ReleaseBuffer((unsigned)size);
}
HRESULT CDatabase::GetPath_Prop(unsigned index, PROPVARIANT *path) const throw()
{
PropVariant_Clear(path);
if (!NameOffsets || !NamesBuf)
return S_OK;
size_t offset = NameOffsets[index];
size_t size = NameOffsets[index + 1] - offset;
if (size >= (1 << 14))
return S_OK;
RINOK(PropVarEm_Alloc_Bstr(path, (unsigned)size - 1));
wchar_t *s = path->bstrVal;
const Byte *p = ((const Byte *)NamesBuf + offset * 2);
for (size_t i = 0; i < size; i++)
{
wchar_t c = Get16(p);
p += 2;
#if WCHAR_PATH_SEPARATOR != L'/'
if (c == L'/')
c = WCHAR_PATH_SEPARATOR;
#endif
*s++ = c;
}
return S_OK;
/*
unsigned cur = index;
unsigned size = 0;
for (int i = 0;; i++)
{
size_t len = NameOffsets[cur + 1] - NameOffsets[cur];
size += (unsigned)len;
if (i > 256 || len > (1 << 14) || size > (1 << 14))
return PropVarEm_Set_Str(path, "[TOO-LONG]");
cur = Files[cur].Parent;
if (cur < 0)
break;
}
size--;
RINOK(PropVarEm_Alloc_Bstr(path, size));
wchar_t *s = path->bstrVal;
s += size;
*s = 0;
cur = index;
for (;;)
{
unsigned len = (unsigned)(NameOffsets[cur + 1] - NameOffsets[cur] - 1);
const Byte *p = (const Byte *)NamesBuf + (NameOffsets[cur + 1] * 2) - 2;
do
{
p -= 2;
--s;
wchar_t c = Get16(p);
if (c == '/')
c = WCHAR_PATH_SEPARATOR;
*s = c;
}
while (--len);
const CFileItem &file = Files[cur];
cur = file.Parent;
if (cur < 0)
return S_OK;
*(--s) = (file.IsAltStream ? ':' : WCHAR_PATH_SEPARATOR);
}
*/
}
void CInArchive::WaitId(UInt64 id)
{
for (;;)
{
UInt64 type = ReadID();
if (type == id)
return;
if (type == NID::kEnd)
ThrowIncorrect();
SkipData();
}
}
void CInArchive::ReadHashDigests(unsigned numItems, CUInt32DefVector &crcs)
{
ReadBoolVector2(numItems, crcs.Defs);
crcs.Vals.ClearAndSetSize(numItems);
UInt32 *p = &crcs.Vals[0];
const bool *defs = &crcs.Defs[0];
for (unsigned i = 0; i < numItems; i++)
{
UInt32 crc = 0;
if (defs[i])
crc = ReadUInt32();
p[i] = crc;
}
}
void CInArchive::ReadPackInfo(CFolders &f)
{
CNum numPackStreams = ReadNum();
WaitId(NID::kSize);
f.PackPositions.Alloc(numPackStreams + 1);
f.NumPackStreams = numPackStreams;
UInt64 sum = 0;
for (CNum i = 0; i < numPackStreams; i++)
{
f.PackPositions[i] = sum;
UInt64 packSize = ReadNumber();
sum += packSize;
if (sum < packSize)
ThrowIncorrect();
}
f.PackPositions[numPackStreams] = sum;
UInt64 type;
for (;;)
{
type = ReadID();
if (type == NID::kEnd)
return;
if (type == NID::kCRC)
{
CUInt32DefVector PackCRCs;
ReadHashDigests(numPackStreams, PackCRCs);
continue;
}
SkipData();
}
}
void CInArchive::ReadUnpackInfo(
const CObjectVector<CByteBuffer> *dataVector,
CFolders &folders)
{
WaitId(NID::kFolder);
CNum numFolders = ReadNum();
CNum numCodersOutStreams = 0;
{
CStreamSwitch streamSwitch;
streamSwitch.Set(this, dataVector);
const Byte *startBufPtr = _inByteBack->GetPtr();
folders.NumFolders = numFolders;
folders.FoStartPackStreamIndex.Alloc(numFolders + 1);
folders.FoToMainUnpackSizeIndex.Alloc(numFolders);
folders.FoCodersDataOffset.Alloc(numFolders + 1);
folders.FoToCoderUnpackSizes.Alloc(numFolders + 1);
CRecordVector<bool> InStreamUsed;
CRecordVector<bool> OutStreamUsed;
CNum packStreamIndex = 0;
CNum fo;
CInByte2 *inByte = _inByteBack;
for (fo = 0; fo < numFolders; fo++)
{
UInt32 numOutStreams = 0;
UInt32 indexOfMainStream = 0;
UInt32 numPackStreams = 0;
folders.FoCodersDataOffset[fo] = _inByteBack->GetPtr() - startBufPtr;
numOutStreams = 0;
CNum numInStreams = 0;
CNum numCoders = inByte->ReadNum();
for (CNum ci = 0; ci < numCoders; ci++)
{
Byte mainByte = inByte->ReadByte();
if ((mainByte & 0xC0) != 0)
ThrowUnsupported();
unsigned idSize = (mainByte & 0xF);
if (idSize > 8)
ThrowUnsupported();
if (idSize > inByte->GetRem())
ThrowEndOfData();
const Byte *longID = inByte->GetPtr();
UInt64 id = 0;
for (unsigned j = 0; j < idSize; j++)
id = ((id << 8) | longID[j]);
inByte->SkipDataNoCheck(idSize);
if (folders.ParsedMethods.IDs.Size() < 128)
folders.ParsedMethods.IDs.AddToUniqueSorted(id);
CNum coderInStreams = 1;
CNum coderOutStreams = 1;
if ((mainByte & 0x10) != 0)
{
coderInStreams = inByte->ReadNum();
coderOutStreams = inByte->ReadNum();
}
numInStreams += coderInStreams;
if (numInStreams < coderInStreams)
ThrowUnsupported();
numOutStreams += coderOutStreams;
if (numOutStreams < coderOutStreams)
ThrowUnsupported();
if ((mainByte & 0x20) != 0)
{
CNum propsSize = inByte->ReadNum();
if (propsSize > inByte->GetRem())
ThrowEndOfData();
if (id == k_LZMA2 && propsSize == 1)
{
Byte v = *_inByteBack->GetPtr();
if (folders.ParsedMethods.Lzma2Prop < v)
folders.ParsedMethods.Lzma2Prop = v;
}
else if (id == k_LZMA && propsSize == 5)
{
UInt32 dicSize = GetUi32(_inByteBack->GetPtr() + 1);
if (folders.ParsedMethods.LzmaDic < dicSize)
folders.ParsedMethods.LzmaDic = dicSize;
}
inByte->SkipDataNoCheck((size_t)propsSize);
}
}
if (numOutStreams == 1 && numInStreams == 1)
{
indexOfMainStream = 0;
numPackStreams = 1;
}
else
{
UInt32 i;
if (numOutStreams == 0)
ThrowUnsupported();
CNum numBindPairs = numOutStreams - 1;
if (numInStreams < numBindPairs)
ThrowUnsupported();
if (numInStreams >= 256 || numOutStreams >= 256)
ThrowUnsupported();
InStreamUsed.ClearAndSetSize(numInStreams);
for (i = 0; i < numInStreams; i++)
InStreamUsed[i] = false;
OutStreamUsed.ClearAndSetSize(numOutStreams);
for (i = 0; i < numOutStreams; i++)
OutStreamUsed[i] = false;
for (i = 0; i < numBindPairs; i++)
{
CNum index = ReadNum();
if (index >= numInStreams || InStreamUsed[index])
ThrowUnsupported();
InStreamUsed[index] = true;
index = ReadNum();
if (index >= numOutStreams || OutStreamUsed[index])
ThrowUnsupported();
OutStreamUsed[index] = true;
}
numPackStreams = numInStreams - numBindPairs;
if (numPackStreams != 1)
for (i = 0; i < numPackStreams; i++)
inByte->ReadNum(); // PackStreams
for (i = 0; i < numOutStreams; i++)
if (!OutStreamUsed[i])
{
indexOfMainStream = i;
break;
}
if (i == numOutStreams)
ThrowUnsupported();
}
folders.FoToCoderUnpackSizes[fo] = numCodersOutStreams;
numCodersOutStreams += numOutStreams;
folders.FoStartPackStreamIndex[fo] = packStreamIndex;
packStreamIndex += numPackStreams;
folders.FoToMainUnpackSizeIndex[fo] = (Byte)indexOfMainStream;
}
size_t dataSize = _inByteBack->GetPtr() - startBufPtr;
folders.FoToCoderUnpackSizes[fo] = numCodersOutStreams;
folders.FoStartPackStreamIndex[fo] = packStreamIndex;
folders.FoCodersDataOffset[fo] = _inByteBack->GetPtr() - startBufPtr;
folders.CodersData.CopyFrom(startBufPtr, dataSize);
}
WaitId(NID::kCodersUnpackSize);
folders.CoderUnpackSizes.Alloc(numCodersOutStreams);
for (CNum i = 0; i < numCodersOutStreams; i++)
folders.CoderUnpackSizes[i] = ReadNumber();
for (;;)
{
UInt64 type = ReadID();
if (type == NID::kEnd)
return;
if (type == NID::kCRC)
{
ReadHashDigests(numFolders, folders.FolderCRCs);
continue;
}
SkipData();
}
}
void CInArchive::ReadSubStreamsInfo(
CFolders &folders,
CRecordVector<UInt64> &unpackSizes,
CUInt32DefVector &digests)
{
folders.NumUnpackStreamsVector.Alloc(folders.NumFolders);
CNum i;
for (i = 0; i < folders.NumFolders; i++)
folders.NumUnpackStreamsVector[i] = 1;
UInt64 type;
for (;;)
{
type = ReadID();
if (type == NID::kNumUnpackStream)
{
for (i = 0; i < folders.NumFolders; i++)
folders.NumUnpackStreamsVector[i] = ReadNum();
continue;
}
if (type == NID::kCRC || type == NID::kSize || type == NID::kEnd)
break;
SkipData();
}
if (type == NID::kSize)
{
for (i = 0; i < folders.NumFolders; i++)
{
// v3.13 incorrectly worked with empty folders
// v4.07: we check that folder is empty
CNum numSubstreams = folders.NumUnpackStreamsVector[i];
if (numSubstreams == 0)
continue;
UInt64 sum = 0;
for (CNum j = 1; j < numSubstreams; j++)
{
UInt64 size = ReadNumber();
unpackSizes.Add(size);
sum += size;
if (sum < size)
ThrowIncorrect();
}
UInt64 folderUnpackSize = folders.GetFolderUnpackSize(i);
if (folderUnpackSize < sum)
ThrowIncorrect();
unpackSizes.Add(folderUnpackSize - sum);
}
type = ReadID();
}
else
{
for (i = 0; i < folders.NumFolders; i++)
{
/* v9.26 - v9.29 incorrectly worked:
if (folders.NumUnpackStreamsVector[i] == 0), it threw error */
CNum val = folders.NumUnpackStreamsVector[i];
if (val > 1)
ThrowIncorrect();
if (val == 1)
unpackSizes.Add(folders.GetFolderUnpackSize(i));
}
}
unsigned numDigests = 0;
for (i = 0; i < folders.NumFolders; i++)
{
CNum numSubstreams = folders.NumUnpackStreamsVector[i];
if (numSubstreams != 1 || !folders.FolderCRCs.ValidAndDefined(i))
numDigests += numSubstreams;
}
for (;;)
{
if (type == NID::kEnd)
break;
if (type == NID::kCRC)
{
// CUInt32DefVector digests2;
// ReadHashDigests(numDigests, digests2);
CBoolVector digests2;
ReadBoolVector2(numDigests, digests2);
digests.ClearAndSetSize(unpackSizes.Size());
unsigned k = 0;
unsigned k2 = 0;
for (i = 0; i < folders.NumFolders; i++)
{
CNum numSubstreams = folders.NumUnpackStreamsVector[i];
if (numSubstreams == 1 && folders.FolderCRCs.ValidAndDefined(i))
{
digests.Defs[k] = true;
digests.Vals[k] = folders.FolderCRCs.Vals[i];
k++;
}
else for (CNum j = 0; j < numSubstreams; j++)
{
bool defined = digests2[k2++];
digests.Defs[k] = defined;
UInt32 crc = 0;
if (defined)
crc = ReadUInt32();
digests.Vals[k] = crc;
k++;
}
}
// if (k != unpackSizes.Size()) throw 1234567;
}
else
SkipData();
type = ReadID();
}
if (digests.Defs.Size() != unpackSizes.Size())
{
digests.ClearAndSetSize(unpackSizes.Size());
unsigned k = 0;
for (i = 0; i < folders.NumFolders; i++)
{
CNum numSubstreams = folders.NumUnpackStreamsVector[i];
if (numSubstreams == 1 && folders.FolderCRCs.ValidAndDefined(i))
{
digests.Defs[k] = true;
digests.Vals[k] = folders.FolderCRCs.Vals[i];
k++;
}
else for (CNum j = 0; j < numSubstreams; j++)
{
digests.Defs[k] = false;
digests.Vals[k] = 0;
k++;
}
}
}
}
void CInArchive::ReadStreamsInfo(
const CObjectVector<CByteBuffer> *dataVector,
UInt64 &dataOffset,
CFolders &folders,
CRecordVector<UInt64> &unpackSizes,
CUInt32DefVector &digests)
{
UInt64 type = ReadID();
if (type == NID::kPackInfo)
{
dataOffset = ReadNumber();
ReadPackInfo(folders);
type = ReadID();
}
if (type == NID::kUnpackInfo)
{
ReadUnpackInfo(dataVector, folders);
type = ReadID();
}
if (folders.NumFolders != 0 && !folders.PackPositions)
{
// if there are folders, we need PackPositions also
folders.PackPositions.Alloc(1);
folders.PackPositions[0] = 0;
}
if (type == NID::kSubStreamsInfo)
{
ReadSubStreamsInfo(folders, unpackSizes, digests);
type = ReadID();
}
else
{
folders.NumUnpackStreamsVector.Alloc(folders.NumFolders);
/* If digests.Defs.Size() == 0, it means that there are no crcs.
So we don't need to fill digests with values. */
// digests.Vals.ClearAndSetSize(folders.NumFolders);
// BoolVector_Fill_False(digests.Defs, folders.NumFolders);
for (CNum i = 0; i < folders.NumFolders; i++)
{
folders.NumUnpackStreamsVector[i] = 1;
unpackSizes.Add(folders.GetFolderUnpackSize(i));
// digests.Vals[i] = 0;
}
}
if (type != NID::kEnd)
ThrowIncorrect();
}
void CInArchive::ReadBoolVector(unsigned numItems, CBoolVector &v)
{
v.ClearAndSetSize(numItems);
Byte b = 0;
Byte mask = 0;
bool *p = &v[0];
for (unsigned i = 0; i < numItems; i++)
{
if (mask == 0)
{
b = ReadByte();
mask = 0x80;
}
p[i] = ((b & mask) != 0);
mask >>= 1;
}
}
void CInArchive::ReadBoolVector2(unsigned numItems, CBoolVector &v)
{
Byte allAreDefined = ReadByte();
if (allAreDefined == 0)
{
ReadBoolVector(numItems, v);
return;
}
v.ClearAndSetSize(numItems);
bool *p = &v[0];
for (unsigned i = 0; i < numItems; i++)
p[i] = true;
}
void CInArchive::ReadUInt64DefVector(const CObjectVector<CByteBuffer> &dataVector,
CUInt64DefVector &v, unsigned numItems)
{
ReadBoolVector2(numItems, v.Defs);
CStreamSwitch streamSwitch;
streamSwitch.Set(this, &dataVector);
v.Vals.ClearAndSetSize(numItems);
UInt64 *p = &v.Vals[0];
const bool *defs = &v.Defs[0];
for (unsigned i = 0; i < numItems; i++)
{
UInt64 t = 0;
if (defs[i])
t = ReadUInt64();
p[i] = t;
}
}
HRESULT CInArchive::ReadAndDecodePackedStreams(
DECL_EXTERNAL_CODECS_LOC_VARS
UInt64 baseOffset,
UInt64 &dataOffset, CObjectVector<CByteBuffer> &dataVector
_7Z_DECODER_CRYPRO_VARS_DECL
)
{
CFolders folders;
CRecordVector<UInt64> unpackSizes;
CUInt32DefVector digests;
ReadStreamsInfo(NULL,
dataOffset,
folders,
unpackSizes,
digests);
CDecoder decoder(
#ifdef _ST_MODE
false
#else
true
#endif
);
for (CNum i = 0; i < folders.NumFolders; i++)
{
CByteBuffer &data = dataVector.AddNew();
UInt64 unpackSize64 = folders.GetFolderUnpackSize(i);
size_t unpackSize = (size_t)unpackSize64;
if (unpackSize != unpackSize64)
ThrowUnsupported();
data.Alloc(unpackSize);
CBufPtrSeqOutStream *outStreamSpec = new CBufPtrSeqOutStream;
CMyComPtr<ISequentialOutStream> outStream = outStreamSpec;
outStreamSpec->Init(data, unpackSize);
HRESULT result = decoder.Decode(
EXTERNAL_CODECS_LOC_VARS
_stream, baseOffset + dataOffset,
folders, i,
outStream, NULL
_7Z_DECODER_CRYPRO_VARS
#if !defined(_7ZIP_ST) && !defined(_SFX)
, false, 1
#endif
);
RINOK(result);
if (folders.FolderCRCs.ValidAndDefined(i))
if (CrcCalc(data, unpackSize) != folders.FolderCRCs.Vals[i])
ThrowIncorrect();
}
HeadersSize += folders.PackPositions[folders.NumPackStreams];
return S_OK;
}
HRESULT CInArchive::ReadHeader(
DECL_EXTERNAL_CODECS_LOC_VARS
CDbEx &db
_7Z_DECODER_CRYPRO_VARS_DECL
)
{
UInt64 type = ReadID();
if (type == NID::kArchiveProperties)
{
ReadArchiveProperties(db.ArcInfo);
type = ReadID();
}
CObjectVector<CByteBuffer> dataVector;
if (type == NID::kAdditionalStreamsInfo)
{
HRESULT result = ReadAndDecodePackedStreams(
EXTERNAL_CODECS_LOC_VARS
db.ArcInfo.StartPositionAfterHeader,
db.ArcInfo.DataStartPosition2,
dataVector
_7Z_DECODER_CRYPRO_VARS
);
RINOK(result);
db.ArcInfo.DataStartPosition2 += db.ArcInfo.StartPositionAfterHeader;
type = ReadID();
}
CRecordVector<UInt64> unpackSizes;
CUInt32DefVector digests;
if (type == NID::kMainStreamsInfo)
{
ReadStreamsInfo(&dataVector,
db.ArcInfo.DataStartPosition,
(CFolders &)db,
unpackSizes,
digests);
db.ArcInfo.DataStartPosition += db.ArcInfo.StartPositionAfterHeader;
type = ReadID();
}
db.Files.Clear();
if (type == NID::kFilesInfo)
{
CNum numFiles = ReadNum();
db.Files.ClearAndSetSize(numFiles);
CNum i;
/*
db.Files.Reserve(numFiles);
CNum i;
for (i = 0; i < numFiles; i++)
db.Files.Add(CFileItem());
*/
db.ArcInfo.FileInfoPopIDs.Add(NID::kSize);
// if (!db.PackSizes.IsEmpty())
db.ArcInfo.FileInfoPopIDs.Add(NID::kPackInfo);
if (numFiles > 0 && !digests.Defs.IsEmpty())
db.ArcInfo.FileInfoPopIDs.Add(NID::kCRC);
CBoolVector emptyStreamVector;
BoolVector_Fill_False(emptyStreamVector, (unsigned)numFiles);
CBoolVector emptyFileVector;
CBoolVector antiFileVector;
CNum numEmptyStreams = 0;
for (;;)
{
UInt64 type = ReadID();
if (type == NID::kEnd)
break;
UInt64 size = ReadNumber();
if (size > _inByteBack->GetRem())
ThrowIncorrect();
CStreamSwitch switchProp;
switchProp.Set(this, _inByteBack->GetPtr(), (size_t)size, true);
bool addPropIdToList = true;
bool isKnownType = true;
if (type > ((UInt32)1 << 30))
isKnownType = false;
else switch((UInt32)type)
{
case NID::kName:
{
CStreamSwitch streamSwitch;
streamSwitch.Set(this, &dataVector);
size_t rem = _inByteBack->GetRem();
db.NamesBuf.Alloc(rem);
ReadBytes(db.NamesBuf, rem);
db.NameOffsets.Alloc(db.Files.Size() + 1);
size_t pos = 0;
unsigned i;
for (i = 0; i < db.Files.Size(); i++)
{
size_t curRem = (rem - pos) / 2;
const UInt16 *buf = (const UInt16 *)(db.NamesBuf + pos);
size_t j;
for (j = 0; j < curRem && buf[j] != 0; j++);
if (j == curRem)
ThrowEndOfData();
db.NameOffsets[i] = pos / 2;
pos += j * 2 + 2;
}
db.NameOffsets[i] = pos / 2;
if (pos != rem)
ThereIsHeaderError = true;
break;
}
case NID::kWinAttrib:
{
CBoolVector boolVector;
ReadBoolVector2(db.Files.Size(), boolVector);
CStreamSwitch streamSwitch;
streamSwitch.Set(this, &dataVector);
for (i = 0; i < numFiles; i++)
{
CFileItem &file = db.Files[i];
file.AttribDefined = boolVector[i];
if (file.AttribDefined)
file.Attrib = ReadUInt32();
}
break;
}
/*
case NID::kIsAux:
{
ReadBoolVector(db.Files.Size(), db.IsAux);
break;
}
case NID::kParent:
{
db.IsTree = true;
// CBoolVector boolVector;
// ReadBoolVector2(db.Files.Size(), boolVector);
// CStreamSwitch streamSwitch;
// streamSwitch.Set(this, &dataVector);
CBoolVector boolVector;
ReadBoolVector2(db.Files.Size(), boolVector);
db.ThereAreAltStreams = false;
for (i = 0; i < numFiles; i++)
{
CFileItem &file = db.Files[i];
// file.Parent = -1;
// if (boolVector[i])
file.Parent = (int)ReadUInt32();
file.IsAltStream = !boolVector[i];
if (file.IsAltStream)
db.ThereAreAltStreams = true;
}
break;
}
*/
case NID::kEmptyStream:
{
ReadBoolVector(numFiles, emptyStreamVector);
numEmptyStreams = 0;
for (i = 0; i < (CNum)emptyStreamVector.Size(); i++)
if (emptyStreamVector[i])
numEmptyStreams++;
BoolVector_Fill_False(emptyFileVector, numEmptyStreams);
BoolVector_Fill_False(antiFileVector, numEmptyStreams);
break;
}
case NID::kEmptyFile: ReadBoolVector(numEmptyStreams, emptyFileVector); break;
case NID::kAnti: ReadBoolVector(numEmptyStreams, antiFileVector); break;
case NID::kStartPos: ReadUInt64DefVector(dataVector, db.StartPos, (unsigned)numFiles); break;
case NID::kCTime: ReadUInt64DefVector(dataVector, db.CTime, (unsigned)numFiles); break;
case NID::kATime: ReadUInt64DefVector(dataVector, db.ATime, (unsigned)numFiles); break;
case NID::kMTime: ReadUInt64DefVector(dataVector, db.MTime, (unsigned)numFiles); break;
case NID::kDummy:
{
for (UInt64 j = 0; j < size; j++)
if (ReadByte() != 0)
ThereIsHeaderError = true;
addPropIdToList = false;
break;
}
/*
case NID::kNtSecure:
{
try
{
{
CStreamSwitch streamSwitch;
streamSwitch.Set(this, &dataVector);
UInt32 numDescriptors = ReadUInt32();
size_t offset = 0;
db.SecureOffsets.Clear();
for (i = 0; i < numDescriptors; i++)
{
UInt32 size = ReadUInt32();
db.SecureOffsets.Add(offset);
offset += size;
}
// ThrowIncorrect();;
db.SecureOffsets.Add(offset);
db.SecureBuf.SetCapacity(offset);
for (i = 0; i < numDescriptors; i++)
{
offset = db.SecureOffsets[i];
ReadBytes(db.SecureBuf + offset, db.SecureOffsets[i + 1] - offset);
}
db.SecureIDs.Clear();
for (unsigned i = 0; i < db.Files.Size(); i++)
{
db.SecureIDs.Add(ReadNum());
// db.SecureIDs.Add(ReadUInt32());
}
// ReadUInt32();
if (_inByteBack->GetRem() != 0)
ThrowIncorrect();;
}
}
catch(CInArchiveException &)
{
ThereIsHeaderError = true;
addPropIdToList = isKnownType = false;
db.ClearSecure();
}
break;
}
*/
default:
addPropIdToList = isKnownType = false;
}
if (isKnownType)
{
if (addPropIdToList)
db.ArcInfo.FileInfoPopIDs.Add(type);
}
else
{
db.UnsupportedFeatureWarning = true;
_inByteBack->SkipRem();
}
// SkipData worked incorrectly in some versions before v4.59 (7zVer <= 00.02)
if (_inByteBack->GetRem() != 0)
ThrowIncorrect();
}
type = ReadID(); // Read (NID::kEnd) end of headers
CNum emptyFileIndex = 0;
CNum sizeIndex = 0;
CNum numAntiItems = 0;
for (i = 0; i < numEmptyStreams; i++)
if (antiFileVector[i])
numAntiItems++;
for (i = 0; i < numFiles; i++)
{
CFileItem &file = db.Files[i];
bool isAnti;
file.HasStream = !emptyStreamVector[i];
file.Crc = 0;
if (file.HasStream)
{
file.IsDir = false;
isAnti = false;
file.Size = unpackSizes[sizeIndex];
file.CrcDefined = digests.ValidAndDefined(sizeIndex);
if (file.CrcDefined)
file.Crc = digests.Vals[sizeIndex];
sizeIndex++;
}
else
{
file.IsDir = !emptyFileVector[emptyFileIndex];
isAnti = antiFileVector[emptyFileIndex];
emptyFileIndex++;
file.Size = 0;
file.CrcDefined = false;
}
if (numAntiItems != 0)
db.IsAnti.Add(isAnti);
}
}
db.FillLinks();
/*
if (type != NID::kEnd)
ThrowIncorrect();
if (_inByteBack->GetRem() != 0)
ThrowIncorrect();
*/
return S_OK;
}
void CDbEx::FillLinks()
{
FolderStartFileIndex.ClearAndSetSize(NumFolders);
FileIndexToFolderIndexMap.ClearAndSetSize(Files.Size());
CNum folderIndex = 0;
CNum indexInFolder = 0;
unsigned i;
for (i = 0; i < Files.Size(); i++)
{
bool emptyStream = !Files[i].HasStream;
if (indexInFolder == 0)
{
if (emptyStream)
{
FileIndexToFolderIndexMap[i] = kNumNoIndex;
continue;
}
// v3.13 incorrectly worked with empty folders
// v4.07: we skip empty folders
for (;;)
{
if (folderIndex >= NumFolders)
ThrowIncorrect();
FolderStartFileIndex[folderIndex] = i;
if (NumUnpackStreamsVector[folderIndex] != 0)
break;
folderIndex++;
}
}
FileIndexToFolderIndexMap[i] = folderIndex;
if (emptyStream)
continue;
if (++indexInFolder >= NumUnpackStreamsVector[folderIndex])
{
folderIndex++;
indexInFolder = 0;
}
}
if (indexInFolder != 0)
folderIndex++;
/*
if (indexInFolder != 0)
ThrowIncorrect();
*/
for (;;)
{
if (folderIndex >= NumFolders)
return;
FolderStartFileIndex[folderIndex] = i;
/*
if (NumUnpackStreamsVector[folderIndex] != 0)
ThrowIncorrect();;
*/
folderIndex++;
}
}
HRESULT CInArchive::ReadDatabase2(
DECL_EXTERNAL_CODECS_LOC_VARS
CDbEx &db
_7Z_DECODER_CRYPRO_VARS_DECL
)
{
db.Clear();
db.ArcInfo.StartPosition = _arhiveBeginStreamPosition;
db.ArcInfo.Version.Major = _header[6];
db.ArcInfo.Version.Minor = _header[7];
if (db.ArcInfo.Version.Major != kMajorVersion)
{
// db.UnsupportedVersion = true;
return S_FALSE;
}
UInt64 nextHeaderOffset = Get64(_header + 12);
UInt64 nextHeaderSize = Get64(_header + 20);
UInt32 nextHeaderCRC = Get32(_header + 28);
#ifdef FORMAT_7Z_RECOVERY
UInt32 crcFromArc = Get32(_header + 8);
if (crcFromArc == 0 && nextHeaderOffset == 0 && nextHeaderSize == 0 && nextHeaderCRC == 0)
{
UInt64 cur, fileSize;
RINOK(_stream->Seek(0, STREAM_SEEK_CUR, &cur));
const unsigned kCheckSize = 512;
Byte buf[kCheckSize];
RINOK(_stream->Seek(0, STREAM_SEEK_END, &fileSize));
UInt64 rem = fileSize - cur;
unsigned checkSize = kCheckSize;
if (rem < kCheckSize)
checkSize = (unsigned)(rem);
if (checkSize < 3)
return S_FALSE;
RINOK(_stream->Seek(fileSize - checkSize, STREAM_SEEK_SET, NULL));
RINOK(ReadStream_FALSE(_stream, buf, (size_t)checkSize));
if (buf[checkSize - 1] != 0)
return S_FALSE;
unsigned i;
for (i = checkSize - 2;; i--)
{
if (buf[i] == NID::kEncodedHeader && buf[i + 1] == NID::kPackInfo ||
buf[i] == NID::kHeader && buf[i + 1] == NID::kMainStreamsInfo)
break;
if (i == 0)
return S_FALSE;
}
nextHeaderSize = checkSize - i;
nextHeaderOffset = rem - nextHeaderSize;
nextHeaderCRC = CrcCalc(buf + i, (size_t)nextHeaderSize);
RINOK(_stream->Seek(cur, STREAM_SEEK_SET, NULL));
db.StartHeaderWasRecovered = true;
}
else
#endif
{
// Crc was tested already at signature check
// if (CrcCalc(_header + 12, 20) != crcFromArchive) ThrowIncorrect();
}
db.ArcInfo.StartPositionAfterHeader = _arhiveBeginStreamPosition + kHeaderSize;
db.PhySize = kHeaderSize;
db.IsArc = false;
if ((Int64)nextHeaderOffset < 0 ||
nextHeaderSize > ((UInt64)1 << 62))
return S_FALSE;
if (nextHeaderSize == 0)
{
if (nextHeaderOffset != 0)
return S_FALSE;
db.IsArc = true;
return S_OK;
}
if (!db.StartHeaderWasRecovered)
db.IsArc = true;
HeadersSize += kHeaderSize + nextHeaderSize;
db.PhySize = kHeaderSize + nextHeaderOffset + nextHeaderSize;
if (_fileEndPosition - db.ArcInfo.StartPositionAfterHeader < nextHeaderOffset + nextHeaderSize)
{
db.UnexpectedEnd = true;
return S_FALSE;
}
RINOK(_stream->Seek(nextHeaderOffset, STREAM_SEEK_CUR, NULL));
size_t nextHeaderSize_t = (size_t)nextHeaderSize;
if (nextHeaderSize_t != nextHeaderSize)
return E_OUTOFMEMORY;
CByteBuffer buffer2(nextHeaderSize_t);
RINOK(ReadStream_FALSE(_stream, buffer2, nextHeaderSize_t));
if (CrcCalc(buffer2, nextHeaderSize_t) != nextHeaderCRC)
ThrowIncorrect();
if (!db.StartHeaderWasRecovered)
db.PhySizeWasConfirmed = true;
CStreamSwitch streamSwitch;
streamSwitch.Set(this, buffer2);
CObjectVector<CByteBuffer> dataVector;
UInt64 type = ReadID();
if (type != NID::kHeader)
{
if (type != NID::kEncodedHeader)
ThrowIncorrect();
HRESULT result = ReadAndDecodePackedStreams(
EXTERNAL_CODECS_LOC_VARS
db.ArcInfo.StartPositionAfterHeader,
db.ArcInfo.DataStartPosition2,
dataVector
_7Z_DECODER_CRYPRO_VARS
);
RINOK(result);
if (dataVector.Size() == 0)
return S_OK;
if (dataVector.Size() > 1)
ThrowIncorrect();
streamSwitch.Remove();
streamSwitch.Set(this, dataVector.Front());
if (ReadID() != NID::kHeader)
ThrowIncorrect();
}
db.IsArc = true;
db.HeadersSize = HeadersSize;
return ReadHeader(
EXTERNAL_CODECS_LOC_VARS
db
_7Z_DECODER_CRYPRO_VARS
);
}
HRESULT CInArchive::ReadDatabase(
DECL_EXTERNAL_CODECS_LOC_VARS
CDbEx &db
_7Z_DECODER_CRYPRO_VARS_DECL
)
{
try
{
HRESULT res = ReadDatabase2(
EXTERNAL_CODECS_LOC_VARS db
_7Z_DECODER_CRYPRO_VARS
);
if (ThereIsHeaderError)
db.ThereIsHeaderError = true;
if (res == E_NOTIMPL)
ThrowUnsupported();
return res;
}
catch(CUnsupportedFeatureException &)
{
db.UnsupportedFeatureError = true;
return S_FALSE;
}
catch(CInArchiveException &)
{
db.ThereIsHeaderError = true;
return S_FALSE;
}
}
}}