// 7zIn.cpp #include "StdAfx.h" #ifdef _WIN32 #include #else #include #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 *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 *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 *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 InStreamUsed; CRecordVector 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 &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 *dataVector, UInt64 &dataOffset, CFolders &folders, CRecordVector &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 &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 &dataVector _7Z_DECODER_CRYPRO_VARS_DECL ) { CFolders folders; CRecordVector 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 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 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 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 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; } } }}