// 7zIn.cpp #include "StdAfx.h" #include "7zIn.h" #include "7zDecode.h" #include "../../Common/StreamObjects.h" #include "../../Common/StreamUtils.h" extern "C" { #include "../../../../C/7zCrc.h" } // 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 { 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); HRESULT 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()); } HRESULT CStreamSwitch::Set(CInArchive *archive, const CObjectVector *dataVector) { Remove(); Byte external; RINOK(archive->ReadByte(external)); if (external != 0) { CNum dataIndex; RINOK(archive->ReadNum(dataIndex)); Set(archive, (*dataVector)[dataIndex]); } return S_OK; } CInArchiveException::CInArchiveException(CCauseType cause): Cause(cause) {} HRESULT CInArchive::ReadDirect(IInStream *stream, void *data, UInt32 size, UInt32 *processedSize) { UInt32 realProcessedSize; HRESULT result = ReadStream(stream, data, size, &realProcessedSize); if(processedSize != NULL) *processedSize = realProcessedSize; _position += realProcessedSize; return result; } HRESULT CInArchive::ReadDirect(void *data, UInt32 size, UInt32 *processedSize) { return ReadDirect(_stream, data, size, processedSize); } HRESULT CInArchive::SafeReadDirect(void *data, UInt32 size) { UInt32 realProcessedSize; RINOK(ReadDirect(data, size, &realProcessedSize)); if (realProcessedSize != size) throw CInArchiveException(CInArchiveException::kUnexpectedEndOfArchive); return S_OK; } HRESULT CInArchive::SafeReadDirectByte(Byte &b) { return SafeReadDirect(&b, 1); } HRESULT CInArchive::SafeReadDirectUInt32(UInt32 &value, UInt32 &crc) { value = 0; for (int i = 0; i < 4; i++) { Byte b; RINOK(SafeReadDirectByte(b)); value |= (UInt32(b) << (8 * i)); crc = CRC_UPDATE_BYTE(crc, b); } return S_OK; } HRESULT CInArchive::SafeReadDirectUInt64(UInt64 &value, UInt32 &crc) { value = 0; for (int i = 0; i < 8; i++) { Byte b; RINOK(SafeReadDirectByte(b)); value |= (UInt64(b) << (8 * i)); crc = CRC_UPDATE_BYTE(crc, b); } return S_OK; } HRESULT CInArchive::ReadNumber(UInt64 &value) { Byte firstByte; RINOK(ReadByte(firstByte)); Byte mask = 0x80; value = 0; for (int i = 0; i < 8; i++) { if ((firstByte & mask) == 0) { UInt64 highPart = firstByte & (mask - 1); value += (highPart << (i * 8)); return S_OK; } Byte b; RINOK(ReadByte(b)); value |= (UInt64(b) << (8 * i)); mask >>= 1; } return S_OK; } HRESULT CInArchive::ReadNum(CNum &value) { UInt64 value64; RINOK(ReadNumber(value64)); if (value64 > kNumMax) return E_FAIL; value = (CNum)value64; return S_OK; } HRESULT CInArchive::ReadUInt32(UInt32 &value) { value = 0; for (int i = 0; i < 4; i++) { Byte b; RINOK(ReadByte(b)); value |= (UInt32(b) << (8 * i)); } return S_OK; } HRESULT CInArchive::ReadUInt64(UInt64 &value) { value = 0; for (int i = 0; i < 8; i++) { Byte b; RINOK(ReadByte(b)); value |= (UInt64(b) << (8 * i)); } return S_OK; } static inline bool TestSignatureCandidate(const void *testBytes) { for (int i = 0; i < kSignatureSize; i++) if (((const Byte *)testBytes)[i] != kSignature[i]) return false; return true; } #ifdef _7Z_VOL static inline bool TestFinishSignatureCandidate(const void *testBytes) { for (int i = 0; i < kSignatureSize; i++) if (((const Byte *)testBytes)[i] != kFinishSignature[i]) return false; return true; } #endif HRESULT CInArchive::FindAndReadSignature(IInStream *stream, const UInt64 *searchHeaderSizeLimit) { _position = _arhiveBeginStreamPosition; RINOK(stream->Seek(_arhiveBeginStreamPosition, STREAM_SEEK_SET, NULL)); Byte signature[kSignatureSize]; UInt32 processedSize; RINOK(ReadDirect(stream, signature, kSignatureSize, &processedSize)); if(processedSize != kSignatureSize) return S_FALSE; if (TestSignatureCandidate(signature)) return S_OK; CByteBuffer byteBuffer; const UInt32 kBufferSize = (1 << 16); byteBuffer.SetCapacity(kBufferSize); Byte *buffer = byteBuffer; UInt32 numPrevBytes = kSignatureSize - 1; memmove(buffer, signature + 1, numPrevBytes); UInt64 curTestPos = _arhiveBeginStreamPosition + 1; for (;;) { if (searchHeaderSizeLimit != NULL) if (curTestPos - _arhiveBeginStreamPosition > *searchHeaderSizeLimit) break; UInt32 numReadBytes = kBufferSize - numPrevBytes; RINOK(ReadDirect(stream, buffer + numPrevBytes, numReadBytes, &processedSize)); UInt32 numBytesInBuffer = numPrevBytes + processedSize; if (numBytesInBuffer < kSignatureSize) break; UInt32 numTests = numBytesInBuffer - kSignatureSize + 1; for(UInt32 pos = 0; pos < numTests; pos++, curTestPos++) { if (TestSignatureCandidate(buffer + pos)) { _arhiveBeginStreamPosition = curTestPos; _position = curTestPos + kSignatureSize; return stream->Seek(_position, STREAM_SEEK_SET, NULL); } } numPrevBytes = numBytesInBuffer - numTests; memmove(buffer, buffer + numTests, numPrevBytes); } return S_FALSE; } // Out: _position must point to end of signature #ifdef _7Z_VOL HRESULT CInArchive::FindFinishSignature(IInStream *stream, const UInt64 *searchHeaderSizeLimit) { RINOK(stream->Seek(0, STREAM_SEEK_END, &_position)); if (_position < kSignatureSize) return S_FALSE; CByteBuffer byteBuffer; const UInt32 kBufferSize = (1 << 18); byteBuffer.SetCapacity(kBufferSize); Byte *buffer = byteBuffer; UInt32 numPrevBytes = 0; UInt64 limitPos = 0; if (searchHeaderSizeLimit != NULL) if (*searchHeaderSizeLimit < _position) limitPos = _position - *searchHeaderSizeLimit; while(_position >= limitPos) { UInt32 numReadBytes = kBufferSize - numPrevBytes; if (numReadBytes > _position) numReadBytes = (UInt32)_position; UInt32 numBytesInBuffer = numPrevBytes + numReadBytes; if (numBytesInBuffer < kSignatureSize) return S_FALSE; _position -= numReadBytes; RINOK(stream->Seek(_position, STREAM_SEEK_SET, &_position)); UInt32 startPos = kBufferSize - numBytesInBuffer; UInt32 processedSize; RINOK(ReadDirect(stream, buffer + startPos, numReadBytes, &processedSize)); if (processedSize != numReadBytes) return S_FALSE; _position -= processedSize; for(UInt32 pos = kBufferSize; pos >= startPos + kSignatureSize; pos--) { if (TestFinishSignatureCandidate(buffer + pos - kSignatureSize)) { _position += pos - startPos; return stream->Seek(_position, STREAM_SEEK_SET, NULL); } } numPrevBytes = kSignatureSize - 1; memmove(buffer + kBufferSize - numPrevBytes, buffer + startPos + 1, numPrevBytes); } return S_FALSE; } #endif // S_FALSE means that file is not archive HRESULT CInArchive::Open(IInStream *stream, const UInt64 *searchHeaderSizeLimit) { Close(); RINOK(stream->Seek(0, STREAM_SEEK_CUR, &_arhiveBeginStreamPosition)) _position = _arhiveBeginStreamPosition; #ifdef _7Z_VOL HRESULT result = FindFinishSignature(stream, searchHeaderSizeLimit); if (result == S_OK) _finishSignature = true; else { if (result != S_FALSE) return result; _finishSignature = false; RINOK(FindAndReadSignature(stream, searchHeaderSizeLimit)); } #else RINOK(FindAndReadSignature(stream, searchHeaderSizeLimit)); #endif _stream = stream; return S_OK; } void CInArchive::Close() { _stream.Release(); } HRESULT CInArchive::SkeepData(UInt64 size) { for (UInt64 i = 0; i < size; i++) { Byte temp; RINOK(ReadByte(temp)); } return S_OK; } HRESULT CInArchive::SkeepData() { UInt64 size; RINOK(ReadNumber(size)); return SkeepData(size); } HRESULT CInArchive::ReadArchiveProperties(CInArchiveInfo & /* archiveInfo */) { for (;;) { UInt64 type; RINOK(ReadID(type)); if (type == NID::kEnd) break; SkeepData(); } return S_OK; } HRESULT CInArchive::GetNextFolderItem(CFolder &folder) { CNum numCoders; RINOK(ReadNum(numCoders)); 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 = 0; RINOK(ReadByte(mainByte)); int idSize = (mainByte & 0xF); BYTE longID[15]; RINOK(ReadBytes(longID, idSize)); if (idSize > 8) return S_FALSE; 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) { RINOK(ReadNum(coder.NumInStreams)); RINOK(ReadNum(coder.NumOutStreams)); } else { coder.NumInStreams = 1; coder.NumOutStreams = 1; } if ((mainByte & 0x20) != 0) { CNum propertiesSize = 0; RINOK(ReadNum(propertiesSize)); coder.Properties.SetCapacity((size_t)propertiesSize); RINOK(ReadBytes((Byte *)coder.Properties, (size_t)propertiesSize)); } if ((mainByte & 0x80) != 0) return S_FALSE; } numInStreams += coder.NumInStreams; numOutStreams += coder.NumOutStreams; } CNum numBindPairs; // RINOK(ReadNumber(numBindPairs)); numBindPairs = numOutStreams - 1; folder.BindPairs.Clear(); folder.BindPairs.Reserve(numBindPairs); for (i = 0; i < numBindPairs; i++) { CBindPair bindPair; RINOK(ReadNum(bindPair.InIndex)); RINOK(ReadNum(bindPair.OutIndex)); folder.BindPairs.Add(bindPair); } CNum numPackedStreams = numInStreams - numBindPairs; folder.PackStreams.Reserve(numPackedStreams); if (numPackedStreams == 1) { for (CNum j = 0; j < numInStreams; j++) if (folder.FindBindPairForInStream(j) < 0) { folder.PackStreams.Add(j); break; } } else for(i = 0; i < numPackedStreams; i++) { CNum packStreamInfo; RINOK(ReadNum(packStreamInfo)); folder.PackStreams.Add(packStreamInfo); } return S_OK; } HRESULT CInArchive::WaitAttribute(UInt64 attribute) { for (;;) { UInt64 type; RINOK(ReadID(type)); if (type == attribute) return S_OK; if (type == NID::kEnd) return S_FALSE; RINOK(SkeepData()); } } HRESULT CInArchive::ReadHashDigests(int numItems, CRecordVector &digestsDefined, CRecordVector &digests) { RINOK(ReadBoolVector2(numItems, digestsDefined)); digests.Clear(); digests.Reserve(numItems); for(int i = 0; i < numItems; i++) { UInt32 crc = 0; if (digestsDefined[i]) RINOK(ReadUInt32(crc)); digests.Add(crc); } return S_OK; } HRESULT CInArchive::ReadPackInfo( UInt64 &dataOffset, CRecordVector &packSizes, CRecordVector &packCRCsDefined, CRecordVector &packCRCs) { RINOK(ReadNumber(dataOffset)); CNum numPackStreams; RINOK(ReadNum(numPackStreams)); RINOK(WaitAttribute(NID::kSize)); packSizes.Clear(); packSizes.Reserve(numPackStreams); for(CNum i = 0; i < numPackStreams; i++) { UInt64 size; RINOK(ReadNumber(size)); packSizes.Add(size); } UInt64 type; for (;;) { RINOK(ReadID(type)); if (type == NID::kEnd) break; if (type == NID::kCRC) { RINOK(ReadHashDigests(numPackStreams, packCRCsDefined, packCRCs)); continue; } RINOK(SkeepData()); } if (packCRCsDefined.IsEmpty()) { packCRCsDefined.Reserve(numPackStreams); packCRCsDefined.Clear(); packCRCs.Reserve(numPackStreams); packCRCs.Clear(); for(CNum i = 0; i < numPackStreams; i++) { packCRCsDefined.Add(false); packCRCs.Add(0); } } return S_OK; } HRESULT CInArchive::ReadUnPackInfo( const CObjectVector *dataVector, CObjectVector &folders) { RINOK(WaitAttribute(NID::kFolder)); CNum numFolders; RINOK(ReadNum(numFolders)); { CStreamSwitch streamSwitch; RINOK(streamSwitch.Set(this, dataVector)); folders.Clear(); folders.Reserve((UInt32)numFolders); for(CNum i = 0; i < numFolders; i++) { folders.Add(CFolder()); RINOK(GetNextFolderItem(folders.Back())); } } RINOK(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++) { UInt64 unPackSize; RINOK(ReadNumber(unPackSize)); folder.UnPackSizes.Add(unPackSize); } } for (;;) { UInt64 type; RINOK(ReadID(type)); if (type == NID::kEnd) return S_OK; if (type == NID::kCRC) { CRecordVector crcsDefined; CRecordVector crcs; RINOK(ReadHashDigests(numFolders, crcsDefined, crcs)); for(i = 0; i < numFolders; i++) { CFolder &folder = folders[i]; folder.UnPackCRCDefined = crcsDefined[i]; folder.UnPackCRC = crcs[i]; } continue; } RINOK(SkeepData()); } } HRESULT CInArchive::ReadSubStreamsInfo( const CObjectVector &folders, CRecordVector &numUnPackStreamsInFolders, CRecordVector &unPackSizes, CRecordVector &digestsDefined, CRecordVector &digests) { numUnPackStreamsInFolders.Clear(); numUnPackStreamsInFolders.Reserve(folders.Size()); UInt64 type; for (;;) { RINOK(ReadID(type)); if (type == NID::kNumUnPackStream) { for(int i = 0; i < folders.Size(); i++) { CNum value; RINOK(ReadNum(value)); numUnPackStreamsInFolders.Add(value); } continue; } if (type == NID::kCRC || type == NID::kSize) break; if (type == NID::kEnd) break; RINOK(SkeepData()); } 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++) { UInt64 size; if (type == NID::kSize) { RINOK(ReadNumber(size)); unPackSizes.Add(size); sum += size; } } unPackSizes.Add(folders[i].GetUnPackSize() - sum); } if (type == NID::kSize) { RINOK(ReadID(type)); } 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) { CRecordVector digestsDefined2; CRecordVector digests2; RINOK(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()) { digestsDefined.Clear(); digests.Clear(); for (int i = 0; i < numDigestsTotal; i++) { digestsDefined.Add(false); digests.Add(0); } } return S_OK; } else { RINOK(SkeepData()); } RINOK(ReadID(type)); } } HRESULT CInArchive::ReadStreamsInfo( const CObjectVector *dataVector, UInt64 &dataOffset, CRecordVector &packSizes, CRecordVector &packCRCsDefined, CRecordVector &packCRCs, CObjectVector &folders, CRecordVector &numUnPackStreamsInFolders, CRecordVector &unPackSizes, CRecordVector &digestsDefined, CRecordVector &digests) { for (;;) { UInt64 type; RINOK(ReadID(type)); switch(type) { case NID::kEnd: return S_OK; case NID::kPackInfo: { RINOK(ReadPackInfo(dataOffset, packSizes, packCRCsDefined, packCRCs)); break; } case NID::kUnPackInfo: { RINOK(ReadUnPackInfo(dataVector, folders)); break; } case NID::kSubStreamsInfo: { RINOK(ReadSubStreamsInfo(folders, numUnPackStreamsInFolders, unPackSizes, digestsDefined, digests)); break; } } } } HRESULT CInArchive::ReadFileNames(CObjectVector &files) { for(int i = 0; i < files.Size(); i++) { UString &name = files[i].Name; name.Empty(); for (;;) { wchar_t c; RINOK(ReadWideCharLE(c)); if (c == L'\0') break; name += c; } } return S_OK; } HRESULT 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) { RINOK(ReadByte(b)); mask = 0x80; } v.Add((b & mask) != 0); mask >>= 1; } return S_OK; } HRESULT CInArchive::ReadBoolVector2(int numItems, CBoolVector &v) { Byte allAreDefined; RINOK(ReadByte(allAreDefined)); if (allAreDefined == 0) return ReadBoolVector(numItems, v); v.Clear(); v.Reserve(numItems); for (int i = 0; i < numItems; i++) v.Add(true); return S_OK; } HRESULT CInArchive::ReadTime(const CObjectVector &dataVector, CObjectVector &files, UInt64 type) { CBoolVector boolVector; RINOK(ReadBoolVector2(files.Size(), boolVector)) CStreamSwitch streamSwitch; RINOK(streamSwitch.Set(this, &dataVector)); for(int i = 0; i < files.Size(); i++) { CFileItem &file = files[i]; CArchiveFileTime fileTime; fileTime.dwLowDateTime = 0; fileTime.dwHighDateTime = 0; bool defined = boolVector[i]; if (defined) { UInt32 low, high; RINOK(ReadUInt32(low)); RINOK(ReadUInt32(high)); fileTime.dwLowDateTime = low; fileTime.dwHighDateTime = high; } switch(type) { case NID::kCreationTime: file.IsCreationTimeDefined = defined; if (defined) file.CreationTime = fileTime; break; case NID::kLastWriteTime: file.IsLastWriteTimeDefined = defined; if (defined) file.LastWriteTime = fileTime; break; case NID::kLastAccessTime: file.IsLastAccessTimeDefined = defined; if (defined) file.LastAccessTime = fileTime; break; } } return S_OK; } HRESULT CInArchive::ReadAndDecodePackedStreams( DECL_EXTERNAL_CODECS_LOC_VARS UInt64 baseOffset, UInt64 &dataOffset, CObjectVector &dataVector #ifndef _NO_CRYPTO , ICryptoGetTextPassword *getTextPassword #endif ) { CRecordVector packSizes; CRecordVector packCRCsDefined; CRecordVector packCRCs; CObjectVector folders; CRecordVector numUnPackStreamsInFolders; CRecordVector unPackSizes; CRecordVector digestsDefined; CRecordVector digests; RINOK(ReadStreamsInfo(NULL, dataOffset, packSizes, packCRCsDefined, packCRCs, folders, numUnPackStreamsInFolders, unPackSizes, digestsDefined, digests)); // database.ArchiveInfo.DataStartPosition2 += database.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 unPackSize = folder.GetUnPackSize(); if (unPackSize > kNumMax) return E_FAIL; if (unPackSize > 0xFFFFFFFF) return E_FAIL; data.SetCapacity((size_t)unPackSize); CSequentialOutStreamImp2 *outStreamSpec = new CSequentialOutStreamImp2; CMyComPtr outStream = outStreamSpec; outStreamSpec->Init(data, (size_t)unPackSize); HRESULT result = decoder.Decode( EXTERNAL_CODECS_LOC_VARS _stream, dataStartPos, &packSizes[packIndex], folder, outStream, NULL #ifndef _NO_CRYPTO , getTextPassword #endif #ifdef COMPRESS_MT , false, 1 #endif ); RINOK(result); if (folder.UnPackCRCDefined) if (CrcCalc(data, (UInt32)unPackSize) != folder.UnPackCRC) throw CInArchiveException(CInArchiveException::kIncorrectHeader); for (int j = 0; j < folder.PackStreams.Size(); j++) dataStartPos += packSizes[packIndex++]; } return S_OK; } HRESULT CInArchive::ReadHeader( DECL_EXTERNAL_CODECS_LOC_VARS CArchiveDatabaseEx &database #ifndef _NO_CRYPTO , ICryptoGetTextPassword *getTextPassword #endif ) { UInt64 type; RINOK(ReadID(type)); if (type == NID::kArchiveProperties) { RINOK(ReadArchiveProperties(database.ArchiveInfo)); RINOK(ReadID(type)); } CObjectVector dataVector; if (type == NID::kAdditionalStreamsInfo) { HRESULT result = ReadAndDecodePackedStreams( EXTERNAL_CODECS_LOC_VARS database.ArchiveInfo.StartPositionAfterHeader, database.ArchiveInfo.DataStartPosition2, dataVector #ifndef _NO_CRYPTO , getTextPassword #endif ); RINOK(result); database.ArchiveInfo.DataStartPosition2 += database.ArchiveInfo.StartPositionAfterHeader; RINOK(ReadID(type)); } CRecordVector unPackSizes; CRecordVector digestsDefined; CRecordVector digests; if (type == NID::kMainStreamsInfo) { RINOK(ReadStreamsInfo(&dataVector, database.ArchiveInfo.DataStartPosition, database.PackSizes, database.PackCRCsDefined, database.PackCRCs, database.Folders, database.NumUnPackStreamsVector, unPackSizes, digestsDefined, digests)); database.ArchiveInfo.DataStartPosition += database.ArchiveInfo.StartPositionAfterHeader; RINOK(ReadID(type)); } else { for(int i = 0; i < database.Folders.Size(); i++) { database.NumUnPackStreamsVector.Add(1); CFolder &folder = database.Folders[i]; unPackSizes.Add(folder.GetUnPackSize()); digestsDefined.Add(folder.UnPackCRCDefined); digests.Add(folder.UnPackCRC); } } database.Files.Clear(); if (type == NID::kEnd) return S_OK; if (type != NID::kFilesInfo) throw CInArchiveException(CInArchiveException::kIncorrectHeader); CNum numFiles; RINOK(ReadNum(numFiles)); database.Files.Reserve(numFiles); CNum i; for(i = 0; i < numFiles; i++) database.Files.Add(CFileItem()); database.ArchiveInfo.FileInfoPopIDs.Add(NID::kSize); if (!database.PackSizes.IsEmpty()) database.ArchiveInfo.FileInfoPopIDs.Add(NID::kPackInfo); if (numFiles > 0 && !digests.IsEmpty()) database.ArchiveInfo.FileInfoPopIDs.Add(NID::kCRC); CBoolVector emptyStreamVector; emptyStreamVector.Reserve((int)numFiles); for(i = 0; i < numFiles; i++) emptyStreamVector.Add(false); CBoolVector emptyFileVector; CBoolVector antiFileVector; CNum numEmptyStreams = 0; // int sizePrev = -1; // int posPrev = 0; for (;;) { /* if (sizePrev >= 0) if (sizePrev != _inByteBack->GetProcessedSize() - posPrev) throw 2; */ UInt64 type; RINOK(ReadID(type)); if (type == NID::kEnd) break; UInt64 size; RINOK(ReadNumber(size)); // sizePrev = size; // posPrev = _inByteBack->GetProcessedSize(); database.ArchiveInfo.FileInfoPopIDs.Add(type); switch(type) { case NID::kName: { CStreamSwitch streamSwitch; RINOK(streamSwitch.Set(this, &dataVector)); RINOK(ReadFileNames(database.Files)) break; } case NID::kWinAttributes: { CBoolVector boolVector; RINOK(ReadBoolVector2(database.Files.Size(), boolVector)) CStreamSwitch streamSwitch; RINOK(streamSwitch.Set(this, &dataVector)); for(i = 0; i < numFiles; i++) { CFileItem &file = database.Files[i]; file.AreAttributesDefined = boolVector[i]; if (file.AreAttributesDefined) { RINOK(ReadUInt32(file.Attributes)); } } break; } case NID::kStartPos: { CBoolVector boolVector; RINOK(ReadBoolVector2(database.Files.Size(), boolVector)) CStreamSwitch streamSwitch; RINOK(streamSwitch.Set(this, &dataVector)); for(i = 0; i < numFiles; i++) { CFileItem &file = database.Files[i]; file.IsStartPosDefined = boolVector[i]; if (file.IsStartPosDefined) { RINOK(ReadUInt64(file.StartPos)); } } break; } case NID::kEmptyStream: { RINOK(ReadBoolVector(numFiles, emptyStreamVector)) for (i = 0; i < (CNum)emptyStreamVector.Size(); i++) if (emptyStreamVector[i]) numEmptyStreams++; emptyFileVector.Reserve(numEmptyStreams); antiFileVector.Reserve(numEmptyStreams); for (i = 0; i < numEmptyStreams; i++) { emptyFileVector.Add(false); antiFileVector.Add(false); } break; } case NID::kEmptyFile: { RINOK(ReadBoolVector(numEmptyStreams, emptyFileVector)) break; } case NID::kAnti: { RINOK(ReadBoolVector(numEmptyStreams, antiFileVector)) break; } case NID::kCreationTime: case NID::kLastWriteTime: case NID::kLastAccessTime: { RINOK(ReadTime(dataVector, database.Files, type)) break; } default: { database.ArchiveInfo.FileInfoPopIDs.DeleteBack(); RINOK(SkeepData(size)); } } } CNum emptyFileIndex = 0; CNum sizeIndex = 0; for(i = 0; i < numFiles; i++) { CFileItem &file = database.Files[i]; file.HasStream = !emptyStreamVector[i]; if(file.HasStream) { file.IsDirectory = false; file.IsAnti = false; file.UnPackSize = unPackSizes[sizeIndex]; file.FileCRC = digests[sizeIndex]; file.IsFileCRCDefined = digestsDefined[sizeIndex]; sizeIndex++; } else { file.IsDirectory = !emptyFileVector[emptyFileIndex]; file.IsAnti = antiFileVector[emptyFileIndex]; emptyFileIndex++; file.UnPackSize = 0; file.IsFileCRCDefined = false; } } 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()) throw CInArchiveException(CInArchiveException::kIncorrectHeader); 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::ReadDatabase( DECL_EXTERNAL_CODECS_LOC_VARS CArchiveDatabaseEx &database #ifndef _NO_CRYPTO , ICryptoGetTextPassword *getTextPassword #endif ) { database.Clear(); database.ArchiveInfo.StartPosition = _arhiveBeginStreamPosition; RINOK(SafeReadDirect(&database.ArchiveInfo.Version.Major, 1)); RINOK(SafeReadDirect(&database.ArchiveInfo.Version.Minor, 1)); if (database.ArchiveInfo.Version.Major != kMajorVersion) throw CInArchiveException(CInArchiveException::kUnsupportedVersion); #ifdef _7Z_VOL if (_finishSignature) { RINOK(_stream->Seek(_position - (4 + kFinishHeaderSize) - (kSignatureSize + 2), STREAM_SEEK_SET, &_position)); } #endif UInt32 crcFromArchive; UInt64 nextHeaderOffset; UInt64 nextHeaderSize; UInt32 nextHeaderCRC; UInt32 crc = CRC_INIT_VAL; UInt32 temp = 0; RINOK(SafeReadDirectUInt32(crcFromArchive, temp)); RINOK(SafeReadDirectUInt64(nextHeaderOffset, crc)); RINOK(SafeReadDirectUInt64(nextHeaderSize, crc)); RINOK(SafeReadDirectUInt32(nextHeaderCRC, crc)); #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)); UInt32 realProcessedSize; RINOK(ReadDirect(buf, (UInt32)kCheckSize, &realProcessedSize)); int i; for (i = (int)realProcessedSize - 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 = realProcessedSize - i; nextHeaderOffset = cur2 - cur + i; nextHeaderCRC = CrcCalc(buf + i, (size_t)nextHeaderSize); RINOK(_stream->Seek(cur, STREAM_SEEK_SET, &_position)); } #endif #ifdef FORMAT_7Z_RECOVERY crcFromArchive = CRC_GET_DIGEST(crc); #endif #ifdef _7Z_VOL UInt64 archiveStartOffset; // data offset from end if that struct UInt64 additionalStartBlockSize; // start signature & start header size if (_finishSignature) { RINOK(SafeReadDirectUInt64(archiveStartOffset)); crc.UpdateUInt64(archiveStartOffset); RINOK(SafeReadDirectUInt64(additionalStartBlockSize)); crc.UpdateUInt64(additionalStartBlockSize); database.ArchiveInfo.StartPositionAfterHeader = _position + archiveStartOffset; } else #endif { database.ArchiveInfo.StartPositionAfterHeader = _position; } if (CRC_GET_DIGEST(crc) != crcFromArchive) throw CInArchiveException(CInArchiveException::kIncorrectHeader); if (nextHeaderSize == 0) return S_OK; if (nextHeaderSize >= 0xFFFFFFFF) return E_FAIL; RINOK(_stream->Seek(nextHeaderOffset, STREAM_SEEK_CUR, &_position)); CByteBuffer buffer2; buffer2.SetCapacity((size_t)nextHeaderSize); RINOK(SafeReadDirect(buffer2, (UInt32)nextHeaderSize)); if (CrcCalc(buffer2, (UInt32)nextHeaderSize) != nextHeaderCRC) throw CInArchiveException(CInArchiveException::kIncorrectHeader); CStreamSwitch streamSwitch; streamSwitch.Set(this, buffer2); CObjectVector dataVector; for (;;) { UInt64 type; RINOK(ReadID(type)); if (type == NID::kHeader) break; if (type != NID::kEncodedHeader) throw CInArchiveException(CInArchiveException::kIncorrectHeader); HRESULT result = ReadAndDecodePackedStreams( EXTERNAL_CODECS_LOC_VARS database.ArchiveInfo.StartPositionAfterHeader, database.ArchiveInfo.DataStartPosition2, dataVector #ifndef _NO_CRYPTO , getTextPassword #endif ); RINOK(result); if (dataVector.Size() == 0) return S_OK; if (dataVector.Size() > 1) throw CInArchiveException(CInArchiveException::kIncorrectHeader); streamSwitch.Remove(); streamSwitch.Set(this, dataVector.Front()); } return ReadHeader( EXTERNAL_CODECS_LOC_VARS database #ifndef _NO_CRYPTO , getTextPassword #endif ); } }}