// 7zIn.cpp #include "StdAfx.h" #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 namespace NArchive { namespace N7z { static void BoolVector_Fill_False(CBoolVector &v, int size) { v.Clear(); v.Reserve(size); for (int i = 0; i < size; i++) v.Add(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() const { const int kNumCodersMax = sizeof(UInt32) * 8; // don't change it const int kMaskSize = sizeof(UInt32) * 8; // it must be >= kNumCodersMax const int kNumBindsMax = 32; if (Coders.Size() > kNumCodersMax || BindPairs.Size() > kNumBindsMax) return false; { CBoolVector v; BoolVector_Fill_False(v, BindPairs.Size() + PackStreams.Size()); int 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, UnpackSizes.Size()); for (i = 0; i < BindPairs.Size(); i++) if (BoolVector_GetAndSet(v, BindPairs[i].OutIndex)) return false; } UInt32 mask[kMaskSize]; int i; for (i = 0; i < kMaskSize; i++) mask[i] = 0; { CIntVector 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 (int 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 {}; static void ThrowException() { throw CInArchiveException(); } static inline void ThrowEndOfData() { ThrowException(); } static inline void ThrowUnsupported() { ThrowException(); } static inline void ThrowIncorrect() { ThrowException(); } static inline void ThrowUnsupportedVersion() { ThrowException(); } /* class CInArchiveException { public: enum CCauseType { kUnsupportedVersion = 0, kUnsupported, kIncorrect, kEndOfData } Cause; CInArchiveException(CCauseType cause): Cause(cause) {}; }; static void ThrowException(CInArchiveException::CCauseType c) { throw CInArchiveException(c); } static void ThrowEndOfData() { ThrowException(CInArchiveException::kEndOfData); } static void ThrowUnsupported() { ThrowException(CInArchiveException::kUnsupported); } static void ThrowIncorrect() { ThrowException(CInArchiveException::kIncorrect); } static void ThrowUnsupportedVersion() { ThrowException(CInArchiveException::kUnsupportedVersion); } */ class CStreamSwitch { CInArchive *_archive; bool _needRemove; public: CStreamSwitch(): _needRemove(false) {} ~CStreamSwitch() { Remove(); } void Remove(); void Set(CInArchive *archive, const Byte *data, size_t size); void Set(CInArchive *archive, const CByteBuffer &byteBuffer); void Set(CInArchive *archive, const CObjectVector *dataVector); }; void CStreamSwitch::Remove() { if (_needRemove) { _archive->DeleteByteStream(); _needRemove = false; } } void CStreamSwitch::Set(CInArchive *archive, const Byte *data, size_t size) { Remove(); _archive = archive; _archive->AddByteStream(data, size); _needRemove = true; } void CStreamSwitch::Set(CInArchive *archive, const CByteBuffer &byteBuffer) { Set(archive, byteBuffer, byteBuffer.GetCapacity()); } void CStreamSwitch::Set(CInArchive *archive, const CObjectVector *dataVector) { Remove(); Byte external = archive->ReadByte(); if (external != 0) { int dataIndex = (int)archive->ReadNum(); if (dataIndex < 0 || dataIndex >= dataVector->Size()) ThrowIncorrect(); Set(archive, (*dataVector)[dataIndex]); } } Byte CInByte2::ReadByte() { if (_pos >= _size) ThrowEndOfData(); return _buffer[_pos++]; } void CInByte2::ReadBytes(Byte *data, size_t size) { if (size > _size - _pos) ThrowEndOfData(); for (size_t i = 0; i < size; i++) data[i] = _buffer[_pos++]; } void CInByte2::SkipData(UInt64 size) { if (size > _size - _pos) ThrowEndOfData(); _pos += (size_t)size; } void CInByte2::SkipData() { SkipData(ReadNumber()); } UInt64 CInByte2::ReadNumber() { if (_pos >= _size) ThrowEndOfData(); Byte firstByte = _buffer[_pos++]; Byte mask = 0x80; UInt64 value = 0; for (int i = 0; i < 8; i++) { if ((firstByte & mask) == 0) { UInt64 highPart = firstByte & (mask - 1); value += (highPart << (i * 8)); return value; } if (_pos >= _size) ThrowEndOfData(); value |= ((UInt64)_buffer[_pos++] << (8 * i)); mask >>= 1; } return value; } CNum CInByte2::ReadNum() { 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; } void CInByte2::ReadString(UString &s) { const Byte *buf = _buffer + _pos; size_t rem = (_size - _pos) / 2 * 2; { size_t i; for (i = 0; i < rem; i += 2) if (buf[i] == 0 && buf[i + 1] == 0) break; if (i == rem) ThrowEndOfData(); rem = i; } int len = (int)(rem / 2); if (len < 0 || (size_t)len * 2 != rem) ThrowUnsupported(); wchar_t *p = s.GetBuffer(len); int i; for (i = 0; i < len; i++, buf += 2) p[i] = (wchar_t)Get16(buf); s.ReleaseBuffer(len); _pos += rem + 2; } static inline bool TestSignature(const Byte *p) { for (int i = 0; i < kSignatureSize; i++) if (p[i] != kSignature[i]) return false; return CrcCalc(p + 12, 20) == GetUi32(p + 8); } #ifdef FORMAT_7Z_RECOVERY static inline bool TestSignature2(const Byte *p) { int i; for (i = 0; i < kSignatureSize; i++) if (p[i] != kSignature[i]) return false; if (CrcCalc(p + 12, 20) == GetUi32(p + 8)) return true; for (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; CByteBuffer byteBuffer; const UInt32 kBufferSize = (1 << 16); byteBuffer.SetCapacity(kBufferSize); Byte *buffer = byteBuffer; UInt32 numPrevBytes = kHeaderSize; memcpy(buffer, _header, kHeaderSize); UInt64 curTestPos = _arhiveBeginStreamPosition; for (;;) { if (searchHeaderSizeLimit != NULL) if (curTestPos - _arhiveBeginStreamPosition > *searchHeaderSizeLimit) break; do { UInt32 numReadBytes = kBufferSize - numPrevBytes; UInt32 processedSize; RINOK(stream->Read(buffer + numPrevBytes, numReadBytes, &processedSize)); numPrevBytes += processedSize; if (processedSize == 0) return S_FALSE; } while (numPrevBytes <= kHeaderSize); UInt32 numTests = numPrevBytes - kHeaderSize; for (UInt32 pos = 0; pos < numTests; pos++) { for (; buffer[pos] != '7' && pos < numTests; pos++) {} if (pos == numTests) break; if (TestSignature(buffer + pos)) { memcpy(_header, buffer + pos, kHeaderSize); curTestPos += pos; _arhiveBeginStreamPosition = curTestPos; return stream->Seek(curTestPos + kHeaderSize, STREAM_SEEK_SET, NULL); } } curTestPos += numTests; numPrevBytes -= numTests; memmove(buffer, buffer + numTests, numPrevBytes); } return S_FALSE; } // 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(FindAndReadSignature(stream, searchHeaderSizeLimit)); _stream = stream; return S_OK; } void CInArchive::Close() { _stream.Release(); } void CInArchive::ReadArchiveProperties(CInArchiveInfo & /* archiveInfo */) { for (;;) { if (ReadID() == NID::kEnd) break; SkipData(); } } void CInArchive::GetNextFolderItem(CFolder &folder) { CNum numCoders = ReadNum(); folder.Coders.Clear(); folder.Coders.Reserve((int)numCoders); CNum numInStreams = 0; CNum numOutStreams = 0; CNum i; for (i = 0; i < numCoders; i++) { folder.Coders.Add(CCoderInfo()); CCoderInfo &coder = folder.Coders.Back(); { Byte mainByte = ReadByte(); int idSize = (mainByte & 0xF); Byte longID[15]; ReadBytes(longID, idSize); if (idSize > 8) ThrowUnsupported(); UInt64 id = 0; for (int j = 0; j < idSize; j++) id |= (UInt64)longID[idSize - 1 - j] << (8 * j); 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.SetCapacity((size_t)propsSize); ReadBytes((Byte *)coder.Props, (size_t)propsSize); } if ((mainByte & 0x80) != 0) ThrowUnsupported(); } numInStreams += coder.NumInStreams; numOutStreams += coder.NumOutStreams; } CNum numBindPairs = numOutStreams - 1; folder.BindPairs.Clear(); folder.BindPairs.Reserve(numBindPairs); for (i = 0; i < numBindPairs; i++) { CBindPair bp; bp.InIndex = ReadNum(); bp.OutIndex = ReadNum(); folder.BindPairs.Add(bp); } if (numInStreams < numBindPairs) ThrowUnsupported(); CNum numPackStreams = numInStreams - numBindPairs; folder.PackStreams.Reserve(numPackStreams); if (numPackStreams == 1) { for (i = 0; i < numInStreams; i++) if (folder.FindBindPairForInStream(i) < 0) { folder.PackStreams.Add(i); break; } if (folder.PackStreams.Size() != 1) ThrowUnsupported(); } else for (i = 0; i < numPackStreams; i++) folder.PackStreams.Add(ReadNum()); } void CInArchive::WaitAttribute(UInt64 attribute) { for (;;) { UInt64 type = ReadID(); if (type == attribute) return; if (type == NID::kEnd) ThrowIncorrect(); SkipData(); } } void CInArchive::ReadHashDigests(int numItems, CBoolVector &digestsDefined, CRecordVector &digests) { ReadBoolVector2(numItems, digestsDefined); digests.Clear(); digests.Reserve(numItems); for (int i = 0; i < numItems; i++) { UInt32 crc = 0; if (digestsDefined[i]) crc = ReadUInt32(); digests.Add(crc); } } void CInArchive::ReadPackInfo( UInt64 &dataOffset, CRecordVector &packSizes, CBoolVector &packCRCsDefined, CRecordVector &packCRCs) { dataOffset = ReadNumber(); CNum numPackStreams = ReadNum(); WaitAttribute(NID::kSize); packSizes.Clear(); packSizes.Reserve(numPackStreams); for (CNum i = 0; i < numPackStreams; i++) packSizes.Add(ReadNumber()); UInt64 type; for (;;) { type = ReadID(); if (type == NID::kEnd) break; if (type == NID::kCRC) { ReadHashDigests(numPackStreams, packCRCsDefined, packCRCs); continue; } SkipData(); } if (packCRCsDefined.IsEmpty()) { BoolVector_Fill_False(packCRCsDefined, numPackStreams); packCRCs.Reserve(numPackStreams); packCRCs.Clear(); for (CNum i = 0; i < numPackStreams; i++) packCRCs.Add(0); } } void CInArchive::ReadUnpackInfo( const CObjectVector *dataVector, CObjectVector &folders) { WaitAttribute(NID::kFolder); CNum numFolders = ReadNum(); { CStreamSwitch streamSwitch; streamSwitch.Set(this, dataVector); folders.Clear(); folders.Reserve(numFolders); for (CNum i = 0; i < numFolders; i++) { folders.Add(CFolder()); GetNextFolderItem(folders.Back()); } } WaitAttribute(NID::kCodersUnpackSize); CNum i; for (i = 0; i < numFolders; i++) { CFolder &folder = folders[i]; CNum numOutStreams = folder.GetNumOutStreams(); folder.UnpackSizes.Reserve(numOutStreams); for (CNum j = 0; j < numOutStreams; j++) folder.UnpackSizes.Add(ReadNumber()); } for (;;) { UInt64 type = ReadID(); if (type == NID::kEnd) return; if (type == NID::kCRC) { CBoolVector crcsDefined; CRecordVector crcs; ReadHashDigests(numFolders, crcsDefined, crcs); for (i = 0; i < numFolders; i++) { CFolder &folder = folders[i]; folder.UnpackCRCDefined = crcsDefined[i]; folder.UnpackCRC = crcs[i]; } continue; } SkipData(); } } void CInArchive::ReadSubStreamsInfo( const CObjectVector &folders, CRecordVector &numUnpackStreamsInFolders, CRecordVector &unpackSizes, CBoolVector &digestsDefined, CRecordVector &digests) { numUnpackStreamsInFolders.Clear(); numUnpackStreamsInFolders.Reserve(folders.Size()); UInt64 type; for (;;) { type = ReadID(); if (type == NID::kNumUnpackStream) { for (int i = 0; i < folders.Size(); i++) numUnpackStreamsInFolders.Add(ReadNum()); continue; } if (type == NID::kCRC || type == NID::kSize) break; if (type == NID::kEnd) break; SkipData(); } if (numUnpackStreamsInFolders.IsEmpty()) for (int i = 0; i < folders.Size(); i++) numUnpackStreamsInFolders.Add(1); int i; for (i = 0; i < numUnpackStreamsInFolders.Size(); i++) { // v3.13 incorrectly worked with empty folders // v4.07: we check that folder is empty CNum numSubstreams = numUnpackStreamsInFolders[i]; if (numSubstreams == 0) continue; UInt64 sum = 0; for (CNum j = 1; j < numSubstreams; j++) if (type == NID::kSize) { UInt64 size = ReadNumber(); unpackSizes.Add(size); sum += size; } unpackSizes.Add(folders[i].GetUnpackSize() - sum); } if (type == NID::kSize) type = ReadID(); int numDigests = 0; int numDigestsTotal = 0; for (i = 0; i < folders.Size(); i++) { CNum numSubstreams = numUnpackStreamsInFolders[i]; if (numSubstreams != 1 || !folders[i].UnpackCRCDefined) numDigests += numSubstreams; numDigestsTotal += numSubstreams; } for (;;) { if (type == NID::kCRC) { CBoolVector digestsDefined2; CRecordVector digests2; ReadHashDigests(numDigests, digestsDefined2, digests2); int digestIndex = 0; for (i = 0; i < folders.Size(); i++) { CNum numSubstreams = numUnpackStreamsInFolders[i]; const CFolder &folder = folders[i]; if (numSubstreams == 1 && folder.UnpackCRCDefined) { digestsDefined.Add(true); digests.Add(folder.UnpackCRC); } else for (CNum j = 0; j < numSubstreams; j++, digestIndex++) { digestsDefined.Add(digestsDefined2[digestIndex]); digests.Add(digests2[digestIndex]); } } } else if (type == NID::kEnd) { if (digestsDefined.IsEmpty()) { BoolVector_Fill_False(digestsDefined, numDigestsTotal); digests.Clear(); for (int i = 0; i < numDigestsTotal; i++) digests.Add(0); } return; } else SkipData(); type = ReadID(); } } void CInArchive::ReadStreamsInfo( const CObjectVector *dataVector, UInt64 &dataOffset, CRecordVector &packSizes, CBoolVector &packCRCsDefined, CRecordVector &packCRCs, CObjectVector &folders, CRecordVector &numUnpackStreamsInFolders, CRecordVector &unpackSizes, CBoolVector &digestsDefined, CRecordVector &digests) { for (;;) { UInt64 type = ReadID(); if (type > ((UInt32)1 << 30)) ThrowIncorrect(); switch((UInt32)type) { case NID::kEnd: return; case NID::kPackInfo: { ReadPackInfo(dataOffset, packSizes, packCRCsDefined, packCRCs); break; } case NID::kUnpackInfo: { ReadUnpackInfo(dataVector, folders); break; } case NID::kSubStreamsInfo: { ReadSubStreamsInfo(folders, numUnpackStreamsInFolders, unpackSizes, digestsDefined, digests); break; } default: ThrowIncorrect(); } } } void CInArchive::ReadBoolVector(int numItems, CBoolVector &v) { v.Clear(); v.Reserve(numItems); Byte b = 0; Byte mask = 0; for (int i = 0; i < numItems; i++) { if (mask == 0) { b = ReadByte(); mask = 0x80; } v.Add((b & mask) != 0); mask >>= 1; } } void CInArchive::ReadBoolVector2(int numItems, CBoolVector &v) { Byte allAreDefined = ReadByte(); if (allAreDefined == 0) { ReadBoolVector(numItems, v); return; } v.Clear(); v.Reserve(numItems); for (int i = 0; i < numItems; i++) v.Add(true); } void CInArchive::ReadUInt64DefVector(const CObjectVector &dataVector, CUInt64DefVector &v, int numFiles) { ReadBoolVector2(numFiles, v.Defined); CStreamSwitch streamSwitch; streamSwitch.Set(this, &dataVector); v.Values.Reserve(numFiles); for (int i = 0; i < numFiles; i++) { UInt64 t = 0; if (v.Defined[i]) t = ReadUInt64(); v.Values.Add(t); } } HRESULT CInArchive::ReadAndDecodePackedStreams( DECL_EXTERNAL_CODECS_LOC_VARS UInt64 baseOffset, UInt64 &dataOffset, CObjectVector &dataVector #ifndef _NO_CRYPTO , ICryptoGetTextPassword *getTextPassword, bool &passwordIsDefined #endif ) { CRecordVector packSizes; CBoolVector packCRCsDefined; CRecordVector packCRCs; CObjectVector folders; CRecordVector numUnpackStreamsInFolders; CRecordVector unpackSizes; CBoolVector digestsDefined; CRecordVector digests; ReadStreamsInfo(NULL, dataOffset, packSizes, packCRCsDefined, packCRCs, folders, numUnpackStreamsInFolders, unpackSizes, digestsDefined, digests); // db.ArchiveInfo.DataStartPosition2 += db.ArchiveInfo.StartPositionAfterHeader; CNum packIndex = 0; CDecoder decoder( #ifdef _ST_MODE false #else true #endif ); UInt64 dataStartPos = baseOffset + dataOffset; for (int i = 0; i < folders.Size(); i++) { const CFolder &folder = folders[i]; dataVector.Add(CByteBuffer()); CByteBuffer &data = dataVector.Back(); UInt64 unpackSize64 = folder.GetUnpackSize(); size_t unpackSize = (size_t)unpackSize64; if (unpackSize != unpackSize64) ThrowUnsupported(); data.SetCapacity(unpackSize); CBufPtrSeqOutStream *outStreamSpec = new CBufPtrSeqOutStream; CMyComPtr outStream = outStreamSpec; outStreamSpec->Init(data, unpackSize); HRESULT result = decoder.Decode( EXTERNAL_CODECS_LOC_VARS _stream, dataStartPos, &packSizes[packIndex], folder, outStream, NULL #ifndef _NO_CRYPTO , getTextPassword, passwordIsDefined #endif #if !defined(_7ZIP_ST) && !defined(_SFX) , false, 1 #endif ); RINOK(result); if (folder.UnpackCRCDefined) if (CrcCalc(data, unpackSize) != folder.UnpackCRC) ThrowIncorrect(); for (int j = 0; j < folder.PackStreams.Size(); j++) { UInt64 packSize = packSizes[packIndex++]; dataStartPos += packSize; HeadersSize += packSize; } } return S_OK; } HRESULT CInArchive::ReadHeader( DECL_EXTERNAL_CODECS_LOC_VARS CArchiveDatabaseEx &db #ifndef _NO_CRYPTO , ICryptoGetTextPassword *getTextPassword, bool &passwordIsDefined #endif ) { UInt64 type = ReadID(); if (type == NID::kArchiveProperties) { ReadArchiveProperties(db.ArchiveInfo); type = ReadID(); } CObjectVector dataVector; if (type == NID::kAdditionalStreamsInfo) { HRESULT result = ReadAndDecodePackedStreams( EXTERNAL_CODECS_LOC_VARS db.ArchiveInfo.StartPositionAfterHeader, db.ArchiveInfo.DataStartPosition2, dataVector #ifndef _NO_CRYPTO , getTextPassword, passwordIsDefined #endif ); RINOK(result); db.ArchiveInfo.DataStartPosition2 += db.ArchiveInfo.StartPositionAfterHeader; type = ReadID(); } CRecordVector unpackSizes; CBoolVector digestsDefined; CRecordVector digests; if (type == NID::kMainStreamsInfo) { ReadStreamsInfo(&dataVector, db.ArchiveInfo.DataStartPosition, db.PackSizes, db.PackCRCsDefined, db.PackCRCs, db.Folders, db.NumUnpackStreamsVector, unpackSizes, digestsDefined, digests); db.ArchiveInfo.DataStartPosition += db.ArchiveInfo.StartPositionAfterHeader; type = ReadID(); } else { for (int i = 0; i < db.Folders.Size(); i++) { db.NumUnpackStreamsVector.Add(1); CFolder &folder = db.Folders[i]; unpackSizes.Add(folder.GetUnpackSize()); digestsDefined.Add(folder.UnpackCRCDefined); digests.Add(folder.UnpackCRC); } } db.Files.Clear(); if (type == NID::kEnd) return S_OK; if (type != NID::kFilesInfo) ThrowIncorrect(); CNum numFiles = ReadNum(); db.Files.Reserve(numFiles); CNum i; for (i = 0; i < numFiles; i++) db.Files.Add(CFileItem()); db.ArchiveInfo.FileInfoPopIDs.Add(NID::kSize); if (!db.PackSizes.IsEmpty()) db.ArchiveInfo.FileInfoPopIDs.Add(NID::kPackInfo); if (numFiles > 0 && !digests.IsEmpty()) db.ArchiveInfo.FileInfoPopIDs.Add(NID::kCRC); CBoolVector emptyStreamVector; BoolVector_Fill_False(emptyStreamVector, (int)numFiles); CBoolVector emptyFileVector; CBoolVector antiFileVector; CNum numEmptyStreams = 0; for (;;) { UInt64 type = ReadID(); if (type == NID::kEnd) break; UInt64 size = ReadNumber(); size_t ppp = _inByteBack->_pos; 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); for (int i = 0; i < db.Files.Size(); i++) _inByteBack->ReadString(db.Files[i].Name); break; } case NID::kWinAttributes: { 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::kEmptyStream: { ReadBoolVector(numFiles, emptyStreamVector); 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, (int)numFiles); break; case NID::kCTime: ReadUInt64DefVector(dataVector, db.CTime, (int)numFiles); break; case NID::kATime: ReadUInt64DefVector(dataVector, db.ATime, (int)numFiles); break; case NID::kMTime: ReadUInt64DefVector(dataVector, db.MTime, (int)numFiles); break; case NID::kDummy: { for (UInt64 j = 0; j < size; j++) if (ReadByte() != 0) ThrowIncorrect(); addPropIdToList = false; break; } default: addPropIdToList = isKnownType = false; } if (isKnownType) { if(addPropIdToList) db.ArchiveInfo.FileInfoPopIDs.Add(type); } else SkipData(size); bool checkRecordsSize = (db.ArchiveInfo.Version.Major > 0 || db.ArchiveInfo.Version.Minor > 2); if (checkRecordsSize && _inByteBack->_pos - ppp != size) ThrowIncorrect(); } 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]; if (file.HasStream) { file.IsDir = false; isAnti = false; file.Size = unpackSizes[sizeIndex]; file.Crc = digests[sizeIndex]; file.CrcDefined = digestsDefined[sizeIndex]; sizeIndex++; } else { file.IsDir = !emptyFileVector[emptyFileIndex]; isAnti = antiFileVector[emptyFileIndex]; emptyFileIndex++; file.Size = 0; file.CrcDefined = false; } if (numAntiItems != 0) db.IsAnti.Add(isAnti); } return S_OK; } void CArchiveDatabaseEx::FillFolderStartPackStream() { FolderStartPackStreamIndex.Clear(); FolderStartPackStreamIndex.Reserve(Folders.Size()); CNum startPos = 0; for (int i = 0; i < Folders.Size(); i++) { FolderStartPackStreamIndex.Add(startPos); startPos += (CNum)Folders[i].PackStreams.Size(); } } void CArchiveDatabaseEx::FillStartPos() { PackStreamStartPositions.Clear(); PackStreamStartPositions.Reserve(PackSizes.Size()); UInt64 startPos = 0; for (int i = 0; i < PackSizes.Size(); i++) { PackStreamStartPositions.Add(startPos); startPos += PackSizes[i]; } } void CArchiveDatabaseEx::FillFolderStartFileIndex() { FolderStartFileIndex.Clear(); FolderStartFileIndex.Reserve(Folders.Size()); FileIndexToFolderIndexMap.Clear(); FileIndexToFolderIndexMap.Reserve(Files.Size()); int folderIndex = 0; CNum indexInFolder = 0; for (int i = 0; i < Files.Size(); i++) { const CFileItem &file = Files[i]; bool emptyStream = !file.HasStream; if (emptyStream && indexInFolder == 0) { FileIndexToFolderIndexMap.Add(kNumNoIndex); continue; } if (indexInFolder == 0) { // v3.13 incorrectly worked with empty folders // v4.07: Loop for skipping empty folders for (;;) { if (folderIndex >= Folders.Size()) ThrowIncorrect(); FolderStartFileIndex.Add(i); // check it if (NumUnpackStreamsVector[folderIndex] != 0) break; folderIndex++; } } FileIndexToFolderIndexMap.Add(folderIndex); if (emptyStream) continue; indexInFolder++; if (indexInFolder >= NumUnpackStreamsVector[folderIndex]) { folderIndex++; indexInFolder = 0; } } } HRESULT CInArchive::ReadDatabase2( DECL_EXTERNAL_CODECS_LOC_VARS CArchiveDatabaseEx &db #ifndef _NO_CRYPTO , ICryptoGetTextPassword *getTextPassword, bool &passwordIsDefined #endif ) { db.Clear(); db.ArchiveInfo.StartPosition = _arhiveBeginStreamPosition; db.ArchiveInfo.Version.Major = _header[6]; db.ArchiveInfo.Version.Minor = _header[7]; if (db.ArchiveInfo.Version.Major != kMajorVersion) ThrowUnsupportedVersion(); UInt32 crcFromArchive = Get32(_header + 8); UInt64 nextHeaderOffset = Get64(_header + 0xC); UInt64 nextHeaderSize = Get64(_header + 0x14); UInt32 nextHeaderCRC = Get32(_header + 0x1C); UInt32 crc = CrcCalc(_header + 0xC, 20); #ifdef FORMAT_7Z_RECOVERY if (crcFromArchive == 0 && nextHeaderOffset == 0 && nextHeaderSize == 0 && nextHeaderCRC == 0) { UInt64 cur, cur2; RINOK(_stream->Seek(0, STREAM_SEEK_CUR, &cur)); const int kCheckSize = 500; Byte buf[kCheckSize]; RINOK(_stream->Seek(0, STREAM_SEEK_END, &cur2)); int checkSize = kCheckSize; if (cur2 - cur < kCheckSize) checkSize = (int)(cur2 - cur); RINOK(_stream->Seek(-checkSize, STREAM_SEEK_END, &cur2)); RINOK(ReadStream_FALSE(_stream, buf, (size_t)checkSize)); int i; for (i = (int)checkSize - 2; i >= 0; i--) if (buf[i] == 0x17 && buf[i + 1] == 0x6 || buf[i] == 0x01 && buf[i + 1] == 0x04) break; if (i < 0) return S_FALSE; nextHeaderSize = checkSize - i; nextHeaderOffset = cur2 - cur + i; nextHeaderCRC = CrcCalc(buf + i, (size_t)nextHeaderSize); RINOK(_stream->Seek(cur, STREAM_SEEK_SET, NULL)); } else #endif { if (crc != crcFromArchive) ThrowIncorrect(); } db.ArchiveInfo.StartPositionAfterHeader = _arhiveBeginStreamPosition + kHeaderSize; if (nextHeaderSize == 0) return S_OK; if (nextHeaderSize > (UInt64)0xFFFFFFFF) return S_FALSE; if ((Int64)nextHeaderOffset < 0) return S_FALSE; RINOK(_stream->Seek(nextHeaderOffset, STREAM_SEEK_CUR, NULL)); CByteBuffer buffer2; buffer2.SetCapacity((size_t)nextHeaderSize); RINOK(ReadStream_FALSE(_stream, buffer2, (size_t)nextHeaderSize)); HeadersSize += kHeaderSize + nextHeaderSize; db.PhySize = kHeaderSize + nextHeaderOffset + nextHeaderSize; if (CrcCalc(buffer2, (UInt32)nextHeaderSize) != nextHeaderCRC) ThrowIncorrect(); 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.ArchiveInfo.StartPositionAfterHeader, db.ArchiveInfo.DataStartPosition2, dataVector #ifndef _NO_CRYPTO , getTextPassword, passwordIsDefined #endif ); 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.HeadersSize = HeadersSize; return ReadHeader( EXTERNAL_CODECS_LOC_VARS db #ifndef _NO_CRYPTO , getTextPassword, passwordIsDefined #endif ); } HRESULT CInArchive::ReadDatabase( DECL_EXTERNAL_CODECS_LOC_VARS CArchiveDatabaseEx &db #ifndef _NO_CRYPTO , ICryptoGetTextPassword *getTextPassword, bool &passwordIsDefined #endif ) { try { return ReadDatabase2( EXTERNAL_CODECS_LOC_VARS db #ifndef _NO_CRYPTO , getTextPassword, passwordIsDefined #endif ); } catch(CInArchiveException &) { return S_FALSE; } } }}