diff options
author | Igor Pavlov <ipavlov@users.sourceforge.net> | 2015-09-22 03:00:00 +0300 |
---|---|---|
committer | Kornel LesiĆski <kornel@geekhood.net> | 2016-05-28 02:16:55 +0300 |
commit | f6444c32568553e0261ca0105083658f12be6284 (patch) | |
tree | 8f4eb80f6accd2a9d0759e2564fd6a2b00836e02 /CPP/7zip/Archive | |
parent | cba375916fb18db8b9101aedf4fa079e019311b3 (diff) |
15.0715.07
Diffstat (limited to 'CPP/7zip/Archive')
34 files changed, 4630 insertions, 1223 deletions
diff --git a/CPP/7zip/Archive/7z/7zHandlerOut.cpp b/CPP/7zip/Archive/7z/7zHandlerOut.cpp index 7ece4c68..41bd6520 100644 --- a/CPP/7zip/Archive/7z/7zHandlerOut.cpp +++ b/CPP/7zip/Archive/7z/7zHandlerOut.cpp @@ -282,7 +282,8 @@ STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outStream, UInt32 numIt bool need_CTime = (Write_CTime.Def && Write_CTime.Val); bool need_ATime = (Write_ATime.Def && Write_ATime.Val); bool need_MTime = (Write_MTime.Def && Write_MTime.Val || !Write_MTime.Def); - if (db) + + if (db && !db->Files.IsEmpty()) { if (!Write_CTime.Def) need_CTime = !db->CTime.Defs.IsEmpty(); if (!Write_ATime.Def) need_ATime = !db->ATime.Defs.IsEmpty(); diff --git a/CPP/7zip/Archive/7z/7zUpdate.cpp b/CPP/7zip/Archive/7z/7zUpdate.cpp index 345cd627..c2478a29 100644 --- a/CPP/7zip/Archive/7z/7zUpdate.cpp +++ b/CPP/7zip/Archive/7z/7zUpdate.cpp @@ -118,11 +118,11 @@ static int Parse_EXE(const Byte *buf, size_t size, CFilterMode *filterMode) #define ELF_SIG 0x464C457F -#define ELF_CLASS_32 1 -#define ELF_CLASS_64 2 +#define ELF_CLASS_32 1 +#define ELF_CLASS_64 2 -#define ELF_DATA_2LSB 1 -#define ELF_DATA_2MSB 2 +#define ELF_DATA_2LSB 1 +#define ELF_DATA_2MSB 2 static UInt16 Get16(const Byte *p, Bool be) { if (be) return (UInt16)GetBe16(p); return (UInt16)GetUi16(p); } static UInt32 Get32(const Byte *p, Bool be) { if (be) return GetBe32(p); return GetUi32(p); } diff --git a/CPP/7zip/Archive/ApmHandler.cpp b/CPP/7zip/Archive/ApmHandler.cpp index d5a111e9..fcd686aa 100644 --- a/CPP/7zip/Archive/ApmHandler.cpp +++ b/CPP/7zip/Archive/ApmHandler.cpp @@ -7,16 +7,13 @@ #include "../../Common/ComTry.h" #include "../../Common/Defs.h" #include "../../Common/IntToString.h" -#include "../../Common/MyString.h" #include "../../Windows/PropVariant.h" -#include "../Common/LimitedStreams.h" -#include "../Common/ProgressUtils.h" #include "../Common/RegisterArc.h" #include "../Common/StreamUtils.h" -#include "../Compress/CopyCoder.h" +#include "HandlerCont.h" #define Get16(p) GetBe16(p) #define Get32(p) GetBe32(p) @@ -75,13 +72,9 @@ struct CItem } }; -class CHandler: - public IInArchive, - public IInArchiveGetStream, - public CMyUnknownImp +class CHandler: public CHandlerCont { CRecordVector<CItem> _items; - CMyComPtr<IInStream> _stream; unsigned _blockSizeLog; UInt32 _numBlocks; UInt64 _phySize; @@ -89,11 +82,11 @@ class CHandler: HRESULT ReadTables(IInStream *stream); UInt64 BlocksToBytes(UInt32 i) const { return (UInt64)i << _blockSizeLog; } - UInt64 GetItemSize(const CItem &item) const { return BlocksToBytes(item.NumBlocks); } + + virtual UInt64 GetItemPos(UInt32 index) const { return BlocksToBytes(_items[index].StartBlock); } + virtual UInt64 GetItemSize(UInt32 index) const { return BlocksToBytes(_items[index].NumBlocks); } public: - MY_UNKNOWN_IMP2(IInArchive, IInArchiveGetStream) - INTERFACE_IInArchive(;) - STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream); + INTERFACE_IInArchive_Cont(;) }; static const UInt32 kSectorSize = 512; @@ -300,7 +293,7 @@ STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *val } case kpidSize: case kpidPackSize: - prop = GetItemSize(item); + prop = BlocksToBytes(item.NumBlocks); break; case kpidOffset: prop = BlocksToBytes(item.StartBlock); break; } @@ -309,73 +302,6 @@ STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *val COM_TRY_END } -STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, - Int32 testMode, IArchiveExtractCallback *extractCallback) -{ - COM_TRY_BEGIN - bool allFilesMode = (numItems == (UInt32)(Int32)-1); - if (allFilesMode) - numItems = _items.Size(); - if (numItems == 0) - return S_OK; - UInt64 totalSize = 0; - UInt32 i; - for (i = 0; i < numItems; i++) - totalSize += GetItemSize(_items[allFilesMode ? i : indices[i]]); - extractCallback->SetTotal(totalSize); - - totalSize = 0; - - NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder(); - CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec; - - CLocalProgress *lps = new CLocalProgress; - CMyComPtr<ICompressProgressInfo> progress = lps; - lps->Init(extractCallback, false); - - CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream; - CMyComPtr<ISequentialInStream> inStream(streamSpec); - streamSpec->SetStream(_stream); - - for (i = 0; i < numItems; i++) - { - lps->InSize = totalSize; - lps->OutSize = totalSize; - RINOK(lps->SetCur()); - CMyComPtr<ISequentialOutStream> outStream; - Int32 askMode = testMode ? - NExtract::NAskMode::kTest : - NExtract::NAskMode::kExtract; - Int32 index = allFilesMode ? i : indices[i]; - const CItem &item = _items[index]; - - RINOK(extractCallback->GetStream(index, &outStream, askMode)); - UInt64 size = GetItemSize(item); - totalSize += size; - if (!testMode && !outStream) - continue; - RINOK(extractCallback->PrepareOperation(askMode)); - - RINOK(_stream->Seek(BlocksToBytes(item.StartBlock), STREAM_SEEK_SET, NULL)); - streamSpec->Init(size); - RINOK(copyCoder->Code(inStream, outStream, NULL, NULL, progress)); - outStream.Release(); - RINOK(extractCallback->SetOperationResult(copyCoderSpec->TotalSize == size ? - NExtract::NOperationResult::kOK: - NExtract::NOperationResult::kDataError)); - } - return S_OK; - COM_TRY_END -} - -STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream) -{ - COM_TRY_BEGIN - const CItem &item = _items[index]; - return CreateLimitedInStream(_stream, BlocksToBytes(item.StartBlock), GetItemSize(item), stream); - COM_TRY_END -} - static const Byte k_Signature[] = { kSig0, kSig1 }; REGISTER_ARC_I( diff --git a/CPP/7zip/Archive/Cab/CabBlockInStream.cpp b/CPP/7zip/Archive/Cab/CabBlockInStream.cpp index 625276f3..c193434f 100644 --- a/CPP/7zip/Archive/Cab/CabBlockInStream.cpp +++ b/CPP/7zip/Archive/Cab/CabBlockInStream.cpp @@ -29,15 +29,24 @@ CCabBlockInStream::~CCabBlockInStream() static UInt32 CheckSum(const Byte *p, UInt32 size) { UInt32 sum = 0; - for (UInt32 i = size >> 2; i != 0; i--) + + for (; size >= 8; size -= 8) + { + sum ^= GetUi32(p) ^ GetUi32(p + 4); + p += 8; + } + + if (size >= 4) { sum ^= GetUi32(p); p += 4; } + size &= 3; if (size > 2) sum ^= (UInt32)(*p++) << 16; if (size > 1) sum ^= (UInt32)(*p++) << 8; if (size > 0) sum ^= (UInt32)(*p++); + return sum; } diff --git a/CPP/7zip/Archive/Cab/CabBlockInStream.h b/CPP/7zip/Archive/Cab/CabBlockInStream.h index b795ed97..af89abb6 100644 --- a/CPP/7zip/Archive/Cab/CabBlockInStream.h +++ b/CPP/7zip/Archive/Cab/CabBlockInStream.h @@ -25,10 +25,16 @@ public: CCabBlockInStream(): _buf(0), ReservedSize(0), MsZip(false) {} ~CCabBlockInStream(); + bool Create(); + void InitForNewBlock() { _size = 0; _pos = 0; } + HRESULT PreRead(ISequentialInStream *stream, UInt32 &packSize, UInt32 &unpackSize); + UInt32 GetPackSizeAvail() const { return _size - _pos; } + const Byte *GetData() const { return _buf + _pos; } + STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); }; diff --git a/CPP/7zip/Archive/Cab/CabHandler.cpp b/CPP/7zip/Archive/Cab/CabHandler.cpp index 4235ec34..711bfdf7 100644 --- a/CPP/7zip/Archive/Cab/CabHandler.cpp +++ b/CPP/7zip/Archive/Cab/CabHandler.cpp @@ -569,13 +569,14 @@ public: UInt64 folderSize, IArchiveExtractCallback *extractCallback, bool testMode); - HRESULT FlushCorrupted(); + HRESULT FlushCorrupted(unsigned folderIndex); HRESULT Unsupported(); UInt64 GetRemain() const { return m_FolderSize - m_PosInFolder; } UInt64 GetPosInFolder() const { return m_PosInFolder; } }; + void CFolderOutStream::Init( const CMvDatabaseEx *database, const CRecordVector<bool> *extractStatuses, @@ -600,6 +601,7 @@ void CFolderOutStream::Init( NumIdenticalFiles = 0; } + HRESULT CFolderOutStream::CloseFileWithResOp(Int32 resOp) { m_RealOutStream.Release(); @@ -608,6 +610,7 @@ HRESULT CFolderOutStream::CloseFileWithResOp(Int32 resOp) return m_ExtractCallback->SetOperationResult(resOp); } + HRESULT CFolderOutStream::CloseFile() { return CloseFileWithResOp(m_IsOk ? @@ -615,6 +618,7 @@ HRESULT CFolderOutStream::CloseFile() NExtract::NOperationResult::kDataError); } + HRESULT CFolderOutStream::OpenFile() { if (NumIdenticalFiles == 0) @@ -680,6 +684,7 @@ HRESULT CFolderOutStream::OpenFile() return m_ExtractCallback->PrepareOperation(askMode); } + HRESULT CFolderOutStream::WriteEmptyFiles() { if (m_FileIsOpen) @@ -699,13 +704,15 @@ HRESULT CFolderOutStream::WriteEmptyFiles() return S_OK; } -// This is Write function + HRESULT CFolderOutStream::Write2(const void *data, UInt32 size, UInt32 *processedSize, bool isOK) { COM_TRY_BEGIN + UInt32 realProcessed = 0; if (processedSize) *processedSize = 0; + while (size != 0) { if (m_FileIsOpen) @@ -732,8 +739,10 @@ HRESULT CFolderOutStream::Write2(const void *data, UInt32 size, UInt32 *processe size -= numBytesToWrite; m_RemainFileSize -= numBytesToWrite; m_PosInFolder += numBytesToWrite; + if (res != S_OK) return res; + if (m_RemainFileSize == 0) { RINOK(CloseFile()); @@ -754,17 +763,27 @@ HRESULT CFolderOutStream::Write2(const void *data, UInt32 size, UInt32 *processe { RINOK(CloseFile()); } + RINOK(result); } + TempBufMode = false; } + if (realProcessed > 0) break; // with this break this function works as Write-Part } else { if (m_CurrentIndex >= m_ExtractStatuses->Size()) - return E_FAIL; + { + // we ignore extra data; + realProcessed += size; + if (processedSize) + *processedSize = realProcessed; + return S_OK; + // return E_FAIL; + } const CMvItem &mvItem = m_Database->Items[m_StartIndex + m_CurrentIndex]; const CItem &item = m_Database->Volumes[mvItem.VolumeIndex].Items[mvItem.ItemIndex]; @@ -772,8 +791,10 @@ HRESULT CFolderOutStream::Write2(const void *data, UInt32 size, UInt32 *processe m_RemainFileSize = item.Size; UInt32 fileOffset = item.Offset; + if (fileOffset < m_PosInFolder) return E_FAIL; + if (fileOffset > m_PosInFolder) { UInt32 numBytesToWrite = MyMin(fileOffset - (UInt32)m_PosInFolder, size); @@ -784,6 +805,7 @@ HRESULT CFolderOutStream::Write2(const void *data, UInt32 size, UInt32 *processe size -= numBytesToWrite; m_PosInFolder += numBytesToWrite; } + if (fileOffset == m_PosInFolder) { RINOK(OpenFile()); @@ -793,21 +815,39 @@ HRESULT CFolderOutStream::Write2(const void *data, UInt32 size, UInt32 *processe } } } + return WriteEmptyFiles(); + COM_TRY_END } + STDMETHODIMP CFolderOutStream::Write(const void *data, UInt32 size, UInt32 *processedSize) { return Write2(data, size, processedSize, true); } -HRESULT CFolderOutStream::FlushCorrupted() + +HRESULT CFolderOutStream::FlushCorrupted(unsigned folderIndex) { + UInt64 remain = GetRemain(); + + if (remain == 0) + { + CMyComPtr<IArchiveExtractCallbackMessage> callbackMessage; + m_ExtractCallback.QueryInterface(IID_IArchiveExtractCallbackMessage, &callbackMessage); + if (callbackMessage) + { + RINOK(callbackMessage->ReportExtractResult(NEventIndexType::kBlockIndex, folderIndex, NExtract::NOperationResult::kDataError)); + } + return S_OK; + } + const unsigned kBufSize = (1 << 10); Byte buf[kBufSize]; for (unsigned i = 0; i < kBufSize; i++) buf[i] = 0; + for (;;) { UInt64 remain = GetRemain(); @@ -819,6 +859,7 @@ HRESULT CFolderOutStream::FlushCorrupted() } } + HRESULT CFolderOutStream::Unsupported() { while (m_CurrentIndex < m_ExtractStatuses->Size()) @@ -838,6 +879,7 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, Int32 testModeSpec, IArchiveExtractCallback *extractCallback) { COM_TRY_BEGIN + bool allFilesMode = (numItems == (UInt32)(Int32)-1); if (allFilesMode) numItems = m_Database.Items.Size(); @@ -883,10 +925,10 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, CMyComPtr<ICompressCoder> deflateDecoder; NCompress::NLzx::CDecoder *lzxDecoderSpec = NULL; - CMyComPtr<ICompressCoder> lzxDecoder; + CMyComPtr<IUnknown> lzxDecoder; NCompress::NQuantum::CDecoder *quantumDecoderSpec = NULL; - CMyComPtr<ICompressCoder> quantumDecoder; + CMyComPtr<IUnknown> quantumDecoder; CCabBlockInStream *cabBlockInStreamSpec = new CCabBlockInStream(); CMyComPtr<ISequentialInStream> cabBlockInStream = cabBlockInStreamSpec; @@ -968,7 +1010,8 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, CFolderOutStream *cabFolderOutStream = new CFolderOutStream; CMyComPtr<ISequentialOutStream> outStream(cabFolderOutStream); - const CFolder &folder = db.Folders[item.GetFolderIndex(db.Folders.Size())]; + unsigned folderIndex2 = item.GetFolderIndex(db.Folders.Size()); + const CFolder &folder = db.Folders[folderIndex2]; cabFolderOutStream->Init(&m_Database, &extractStatuses, startIndex2, curUnpack, extractCallback, testMode); @@ -980,6 +1023,7 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, { case NHeader::NMethod::kNone: break; + case NHeader::NMethod::kMSZip: if (!deflateDecoder) { @@ -988,14 +1032,16 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, } cabBlockInStreamSpec->MsZip = true; break; + case NHeader::NMethod::kLZX: if (!lzxDecoder) { lzxDecoderSpec = new NCompress::NLzx::CDecoder; lzxDecoder = lzxDecoderSpec; } - res = lzxDecoderSpec->SetParams(folder.MethodMinor); + res = lzxDecoderSpec->SetParams_and_Alloc(folder.MethodMinor); break; + case NHeader::NMethod::kQuantum: if (!quantumDecoder) { @@ -1004,6 +1050,7 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, } res = quantumDecoderSpec->SetParams(folder.MethodMinor); break; + default: res = E_INVALIDARG; break; @@ -1022,6 +1069,7 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, int locFolderIndex = item.GetFolderIndex(db.Folders.Size()); bool keepHistory = false; bool keepInputBuffer = false; + bool thereWasNotAlignedChunk = false; for (UInt32 bl = 0; cabFolderOutStream->GetRemain() != 0;) { @@ -1058,6 +1106,7 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, continue; } } + bl++; if (!keepInputBuffer) @@ -1079,19 +1128,39 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, lps->InSize = totalPacked; RINOK(lps->SetCur()); - UInt64 unpackRemain = cabFolderOutStream->GetRemain(); - const UInt32 kBlockSizeMax = (1 << 15); - if (unpackRemain > kBlockSizeMax) - unpackRemain = kBlockSizeMax; - if (unpackRemain > unpackSize) - unpackRemain = unpackSize; + + /* We don't try to reduce last block. + Note that LZX converts data with x86 filter. + and filter needs larger input data than reduced size. + It's simpler to decompress full chunk here. + also we need full block for quantum for more integrity checks */ + + if (unpackSize > kBlockSizeMax) + { + res = S_FALSE; + break; + } + + if (unpackSize != kBlockSizeMax) + { + if (thereWasNotAlignedChunk) + { + res = S_FALSE; + break; + } + thereWasNotAlignedChunk = true; + } + + UInt64 unpackSize64 = unpackSize; + UInt32 packSizeChunk = cabBlockInStreamSpec->GetPackSizeAvail(); switch (folder.GetMethod()) { case NHeader::NMethod::kNone: - res = copyCoder->Code(cabBlockInStream, outStream, NULL, &unpackRemain, NULL); + res = copyCoder->Code(cabBlockInStream, outStream, NULL, &unpackSize64, NULL); break; + case NHeader::NMethod::kMSZip: deflateDecoderSpec->Set_KeepHistory(keepHistory); /* v9.31: now we follow MSZIP specification that requires to finish deflate stream at the end of each block. @@ -1100,7 +1169,7 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, Maybe we also should ignore that error? Or we should extract full file and show the warning? */ deflateDecoderSpec->Set_NeedFinishInput(true); - res = deflateDecoder->Code(cabBlockInStream, outStream, NULL, &unpackRemain, NULL); + res = deflateDecoder->Code(cabBlockInStream, outStream, NULL, &unpackSize64, NULL); if (res == S_OK) { if (!deflateDecoderSpec->IsFinished()) @@ -1108,16 +1177,24 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, if (!deflateDecoderSpec->IsFinalBlock()) res = S_FALSE; } - break; + case NHeader::NMethod::kLZX: lzxDecoderSpec->SetKeepHistory(keepHistory); - res = lzxDecoder->Code(cabBlockInStream, outStream, NULL, &unpackRemain, NULL); + lzxDecoderSpec->KeepHistoryForNext = true; + + res = lzxDecoderSpec->Code(cabBlockInStreamSpec->GetData(), packSizeChunk, unpackSize); + + if (res == S_OK) + res = WriteStream(outStream, + lzxDecoderSpec->GetUnpackData(), + lzxDecoderSpec->GetUnpackSize()); break; + case NHeader::NMethod::kQuantum: - quantumDecoderSpec->SetKeepHistory(keepHistory); - res = quantumDecoder->Code(cabBlockInStream, outStream, NULL, &unpackRemain, NULL); - break; + res = quantumDecoderSpec->Code(cabBlockInStreamSpec->GetData(), + packSizeChunk, outStream, unpackSize, keepHistory); + break; } if (res != S_OK) @@ -1135,17 +1212,21 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, RINOK(cabFolderOutStream->WriteEmptyFiles()); } } + if (res != S_OK || cabFolderOutStream->GetRemain() != 0) { - RINOK(cabFolderOutStream->FlushCorrupted()); + RINOK(cabFolderOutStream->FlushCorrupted(folderIndex2)); } + totalUnPacked += curUnpack; } return S_OK; + COM_TRY_END } + STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) { *numItems = m_Database.Items.Size(); diff --git a/CPP/7zip/Archive/Chm/ChmHandler.cpp b/CPP/7zip/Archive/Chm/ChmHandler.cpp index 3035ef9e..b1ab2996 100644 --- a/CPP/7zip/Archive/Chm/ChmHandler.cpp +++ b/CPP/7zip/Archive/Chm/ChmHandler.cpp @@ -144,7 +144,7 @@ STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *val if (item.Section == 0) prop = "Copy"; else if (item.Section < m_Database.Sections.Size()) - prop = m_Database.Sections[(int)item.Section].GetMethodName(); + prop = m_Database.Sections[(unsigned)item.Section].GetMethodName(); break; } case kpidBlock: @@ -315,8 +315,9 @@ HRESULT CChmFolderOutStream::WriteEmptyFiles() HRESULT CChmFolderOutStream::Write2(const void *data, UInt32 size, UInt32 *processedSize, bool isOK) { UInt32 realProcessed = 0; - if (processedSize != NULL) + if (processedSize) *processedSize = 0; + while(size != 0) { if (m_FileIsOpen) @@ -335,7 +336,7 @@ HRESULT CChmFolderOutStream::Write2(const void *data, UInt32 size, UInt32 *proce } } realProcessed += numBytesToWrite; - if (processedSize != NULL) + if (processedSize) *processedSize = realProcessed; data = (const void *)((const Byte *)data + numBytesToWrite); size -= numBytesToWrite; @@ -359,23 +360,32 @@ HRESULT CChmFolderOutStream::Write2(const void *data, UInt32 size, UInt32 *proce else { if (m_CurrentIndex >= m_NumFiles) - return E_FAIL; + { + realProcessed += size; + if (processedSize) + *processedSize = realProcessed; + return S_OK; + // return E_FAIL; + } + int fullIndex = m_StartIndex + m_CurrentIndex; m_RemainFileSize = m_Database->GetFileSize(fullIndex); UInt64 fileOffset = m_Database->GetFileOffset(fullIndex); if (fileOffset < m_PosInSection) return E_FAIL; + if (fileOffset > m_PosInSection) { UInt32 numBytesToWrite = (UInt32)MyMin(fileOffset - m_PosInSection, UInt64(size)); realProcessed += numBytesToWrite; - if (processedSize != NULL) + if (processedSize) *processedSize = realProcessed; data = (const void *)((const Byte *)data + numBytesToWrite); size -= numBytesToWrite; m_PosInSection += numBytesToWrite; m_PosInFolder += numBytesToWrite; } + if (fileOffset == m_PosInSection) { RINOK(OpenFile()); @@ -385,6 +395,7 @@ HRESULT CChmFolderOutStream::Write2(const void *data, UInt32 size, UInt32 *proce } } } + return WriteEmptyFiles(); } @@ -430,7 +441,7 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, UInt64 currentTotalSize = 0; - NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder(); + NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder; CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec; UInt32 i; @@ -446,11 +457,13 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, { UInt64 currentItemSize = 0; UInt64 totalSize = 0; + if (m_Database.NewFormat) totalSize = m_Database.NewFormatString.Len(); else for (i = 0; i < numItems; i++) totalSize += m_Database.Items[allFilesMode ? i : indices[i]].Size; + extractCallback->SetTotal(totalSize); for (i = 0; i < numItems; i++, currentTotalSize += currentItemSize) @@ -481,6 +494,7 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK)); continue; } + const CItem &item = m_Database.Items[index]; currentItemSize = item.Size; @@ -513,6 +527,7 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, } UInt64 lastFolderIndex = ((UInt64)0 - 1); + for (i = 0; i < numItems; i++) { UInt32 index = allFilesMode ? i : indices[i]; @@ -526,7 +541,7 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, currentTotalSize += item.Size; continue; } - const CSectionInfo §ion = m_Database.Sections[(int)item.Section]; + const CSectionInfo §ion = m_Database.Sections[(unsigned)item.Section]; if (section.IsLzx()) { const CLzxInfo &lzxInfo = section.Methods[0].LzxInfo; @@ -541,14 +556,17 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, RINOK(extractCallback->SetTotal(currentTotalSize)); - NCompress::NLzx::CDecoder *lzxDecoderSpec = 0; - CMyComPtr<ICompressCoder> lzxDecoder; + NCompress::NLzx::CDecoder *lzxDecoderSpec = NULL; + CMyComPtr<IUnknown> lzxDecoder; CChmFolderOutStream *chmFolderOutStream = 0; CMyComPtr<ISequentialOutStream> outStream; currentTotalSize = 0; CRecordVector<bool> extractStatuses; + + CByteBuffer packBuf; + for (i = 0; i < numItems;) { RINOK(extractCallback->SetCompleted(¤tTotalSize)); @@ -560,6 +578,7 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, Int32 askMode= testMode ? NExtract::NAskMode::kTest : NExtract::NAskMode::kExtract; + if (item.IsDir()) { CMyComPtr<ISequentialOutStream> realOutStream; @@ -595,7 +614,7 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, continue; } - const CSectionInfo §ion = m_Database.Sections[(int)sectionIndex]; + const CSectionInfo §ion = m_Database.Sections[(unsigned)sectionIndex]; if (!section.IsLzx()) { @@ -610,7 +629,7 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, const CLzxInfo &lzxInfo = section.Methods[0].LzxInfo; - if (chmFolderOutStream == 0) + if (!chmFolderOutStream) { chmFolderOutStream = new CChmFolderOutStream; outStream = chmFolderOutStream; @@ -618,7 +637,7 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, chmFolderOutStream->Init(&m_Database, extractCallback, testMode); - if (lzxDecoderSpec == NULL) + if (!lzxDecoderSpec) { lzxDecoderSpec = new NCompress::NLzx::CDecoder; lzxDecoder = lzxDecoderSpec; @@ -627,8 +646,7 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, UInt64 folderIndex = m_Database.GetFolder(index); UInt64 compressedPos = m_Database.ContentOffset + section.Offset; - UInt32 numDictBits = lzxInfo.GetNumDictBits(); - RINOK(lzxDecoderSpec->SetParams(numDictBits)); + RINOK(lzxDecoderSpec->SetParams_and_Alloc(lzxInfo.GetNumDictBits())); const CItem *lastItem = &item; extractStatuses.Clear(); @@ -645,10 +663,12 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, lastFolderIndex = m_Database.GetLastFolder(index); UInt64 folderSize = lzxInfo.GetFolderSize(); UInt64 unPackSize = folderSize; + if (extractStatuses.IsEmpty()) chmFolderOutStream->m_StartIndex = index + 1; else chmFolderOutStream->m_StartIndex = index; + if (limitFolderIndex == folderIndex) { for (; i < numItems; i++) @@ -671,6 +691,7 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, lastFolderIndex = m_Database.GetLastFolder(index); } } + unPackSize = MyMin(finishPos - startPos, unPackSize); chmFolderOutStream->m_FolderSize = folderSize; @@ -679,11 +700,13 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, chmFolderOutStream->m_ExtractStatuses = &extractStatuses; chmFolderOutStream->m_NumFiles = extractStatuses.Size(); chmFolderOutStream->m_CurrentIndex = 0; + try { UInt64 startBlock = lzxInfo.GetBlockIndexFromFolderIndex(folderIndex); const CResetTable &rt = lzxInfo.ResetTable; UInt32 numBlocks = (UInt32)rt.GetNumBlocks(unPackSize); + for (UInt32 b = 0; b < numBlocks; b++) { UInt64 completedSize = currentTotalSize + chmFolderOutStream->m_PosInSection - startPos; @@ -691,17 +714,35 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, UInt64 bCur = startBlock + b; if (bCur >= rt.ResetOffsets.Size()) return E_FAIL; - UInt64 offset = rt.ResetOffsets[(int)bCur]; + UInt64 offset = rt.ResetOffsets[(unsigned)bCur]; UInt64 compressedSize; rt.GetCompressedSizeOfBlock(bCur, compressedSize); - UInt64 rem = finishPos - chmFolderOutStream->m_PosInSection; - if (rem > rt.BlockSize) - rem = rt.BlockSize; + + // chm writes full blocks. So we don't need to use reduced size for last block + RINOK(m_Stream->Seek(compressedPos + offset, STREAM_SEEK_SET, NULL)); streamSpec->SetStream(m_Stream); streamSpec->Init(compressedSize); + lzxDecoderSpec->SetKeepHistory(b > 0); - HRESULT res = lzxDecoder->Code(inStream, outStream, NULL, &rem, NULL); + + size_t compressedSizeT = (size_t)compressedSize; + if (compressedSizeT != compressedSize) + throw 2; + packBuf.AllocAtLeast(compressedSizeT); + + HRESULT res = ReadStream_FALSE(inStream, packBuf, compressedSizeT); + + if (res == S_OK) + { + lzxDecoderSpec->KeepHistoryForNext = true; + res = lzxDecoderSpec->Code(packBuf, compressedSizeT, kBlockSize); // rt.BlockSize; + if (res == S_OK) + res = WriteStream(chmFolderOutStream, + lzxDecoderSpec->GetUnpackData(), + lzxDecoderSpec->GetUnpackSize()); + } + if (res != S_OK) { if (res != S_FALSE) @@ -714,6 +755,7 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, { RINOK(chmFolderOutStream->FlushCorrupted(unPackSize)); } + currentTotalSize += folderSize; if (folderIndex == lastFolderIndex) break; diff --git a/CPP/7zip/Archive/Chm/ChmIn.cpp b/CPP/7zip/Archive/Chm/ChmIn.cpp index e8da227d..d7556b89 100644 --- a/CPP/7zip/Archive/Chm/ChmIn.cpp +++ b/CPP/7zip/Archive/Chm/ChmIn.cpp @@ -4,6 +4,8 @@ // #include <stdio.h> +#include "../../../../C/CpuArch.h" + #include "../../../Common/IntToString.h" #include "../../../Common/UTFConvert.h" @@ -11,6 +13,10 @@ #include "ChmIn.h" +#define Get16(p) GetUi16(p) +#define Get32(p) GetUi32(p) +#define Get64(p) GetUi64(p) + namespace NArchive { namespace NChm { @@ -168,38 +174,36 @@ Byte CInArchive::ReadByte() void CInArchive::Skip(size_t size) { - while (size-- != 0) - ReadByte(); + if (_inBuffer.Skip(size) != size) + throw CEnexpectedEndException(); } void CInArchive::ReadBytes(Byte *data, UInt32 size) { - for (UInt32 i = 0; i < size; i++) - data[i] = ReadByte(); + if (_inBuffer.ReadBytes(data, size) != size) + throw CEnexpectedEndException(); } UInt16 CInArchive::ReadUInt16() { - UInt16 val = 0; - for (int i = 0; i < 2; i++) - val |= ((UInt16)(ReadByte()) << (8 * i)); - return val; + Byte b0, b1; + if (!_inBuffer.ReadByte(b0)) throw CEnexpectedEndException(); + if (!_inBuffer.ReadByte(b1)) throw CEnexpectedEndException(); + return (UInt16)(((UInt16)b1 << 8) | b0); } UInt32 CInArchive::ReadUInt32() { - UInt32 val = 0; - for (int i = 0; i < 4; i++) - val |= ((UInt32)(ReadByte()) << (8 * i)); - return val; + Byte p[4]; + ReadBytes(p, 4); + return Get32(p); } UInt64 CInArchive::ReadUInt64() { - UInt64 val = 0; - for (int i = 0; i < 8; i++) - val |= ((UInt64)(ReadByte()) << (8 * i)); - return val; + Byte p[8]; + ReadBytes(p, 8); + return Get64(p); } UInt64 CInArchive::ReadEncInt() @@ -227,15 +231,10 @@ void CInArchive::ReadGUID(GUID &g) void CInArchive::ReadString(unsigned size, AString &s) { s.Empty(); - while (size-- != 0) + if (size != 0) { - char c = (char)ReadByte(); - if (c == 0) - { - Skip(size); - return; - } - s += c; + ReadBytes((Byte *)s.GetBuf(size), size); + s.ReleaseBuf_CalcLen(size); } } @@ -380,6 +379,7 @@ HRESULT CInArchive::OpenChm(IInStream *inStream, CDatabase &database) ReadUInt32(); // Chunk number of next listing chunk when reading // directory in sequence (-1 if this is the last listing chunk) unsigned numItems = 0; + for (;;) { UInt64 offset = _inBuffer.GetProcessedSize() - chunkPos; @@ -391,9 +391,16 @@ HRESULT CInArchive::OpenChm(IInStream *inStream, CDatabase &database) RINOK(ReadDirEntry(database)); numItems++; } + Skip(quickrefLength - 2); - if (ReadUInt16() != numItems) - return S_FALSE; + + unsigned rrr = ReadUInt16(); + if (rrr != numItems) + { + // Lazarus 9-26-2 chm contains 0 here. + if (rrr != 0) + return S_FALSE; + } } else Skip(dirChunkSize - 4); @@ -709,6 +716,14 @@ bool CFilesDatabase::Check() return true; } +static int inline GetLog(UInt32 num) +{ + for (int i = 0; i < 32; i++) + if (((UInt32)1 << i) == num) + return i; + return -1; +} + HRESULT CInArchive::OpenHighLevel(IInStream *inStream, CFilesDatabase &database) { { @@ -771,6 +786,7 @@ HRESULT CInArchive::OpenHighLevel(IInStream *inStream, CFilesDatabase &database) { // Control Data RINOK(DecompressStream(inStream, database, sectionPrefix + kControlData)); + FOR_VECTOR (mi, section.Methods) { CMethodInfo &method = section.Methods[mi]; @@ -785,27 +801,22 @@ HRESULT CInArchive::OpenHighLevel(IInStream *inStream, CFilesDatabase &database) li.Version = ReadUInt32(); if (li.Version != 2 && li.Version != 3) return S_FALSE; - li.ResetInterval = ReadUInt32(); - li.WindowSize = ReadUInt32(); + + { + int n = GetLog(ReadUInt32()); + if (n < 0 || n > 16) + return S_FALSE; + li.ResetIntervalBits = n; + } + + { + int n = GetLog(ReadUInt32()); + if (n < 0 || n > 16) + return S_FALSE; + li.WindowSizeBits = n; + } + li.CacheSize = ReadUInt32(); - if ( - li.ResetInterval != 1 && - li.ResetInterval != 2 && - li.ResetInterval != 4 && - li.ResetInterval != 8 && - li.ResetInterval != 16 && - li.ResetInterval != 32 && - li.ResetInterval != 64) - return S_FALSE; - if ( - li.WindowSize != 1 && - li.WindowSize != 2 && - li.WindowSize != 4 && - li.WindowSize != 8 && - li.WindowSize != 16 && - li.WindowSize != 32 && - li.WindowSize != 64) - return S_FALSE; numDWORDS -= 5; while (numDWORDS-- != 0) ReadUInt32(); @@ -835,6 +846,7 @@ HRESULT CInArchive::OpenHighLevel(IInStream *inStream, CFilesDatabase &database) RINOK(DecompressStream(inStream, database, transformPrefix + method.GetGuidString() + kResetTable)); CResetTable &rt = method.LzxInfo.ResetTable; + if (_chunkSize < 4) { if (_chunkSize != 0) @@ -844,7 +856,7 @@ HRESULT CInArchive::OpenHighLevel(IInStream *inStream, CFilesDatabase &database) return S_FALSE; rt.UncompressedSize = 0; rt.CompressedSize = 0; - rt.BlockSize = 0; + // rt.BlockSize = 0; } else { @@ -852,18 +864,45 @@ HRESULT CInArchive::OpenHighLevel(IInStream *inStream, CFilesDatabase &database) if (ver != 2 && ver != 3) return S_FALSE; UInt32 numEntries = ReadUInt32(); - if (ReadUInt32() != 8) // Size of table entry (bytes) + const unsigned kEntrySize = 8; + if (ReadUInt32() != kEntrySize) return S_FALSE; - if (ReadUInt32() != 0x28) // Len of table header + const unsigned kRtHeaderSize = 4 * 4 + 8 * 3; + if (ReadUInt32() != kRtHeaderSize) return S_FALSE; + if (kRtHeaderSize + kEntrySize * (UInt64)numEntries != _chunkSize) + return S_FALSE; + rt.UncompressedSize = ReadUInt64(); rt.CompressedSize = ReadUInt64(); - rt.BlockSize = ReadUInt64(); // 0x8000 block size for locations below - if (rt.BlockSize != 0x8000) + UInt64 blockSize = ReadUInt64(); + if (blockSize != kBlockSize) + return S_FALSE; + UInt64 numBlocks = (rt.UncompressedSize + kBlockSize + 1) / kBlockSize; + if (numEntries != numBlocks && + numEntries != numBlocks + 1) return S_FALSE; + rt.ResetOffsets.ClearAndReserve(numEntries); + for (UInt32 i = 0; i < numEntries; i++) - rt.ResetOffsets.AddInReserved(ReadUInt64()); + { + UInt64 v = ReadUInt64(); + if (i != 0 && v < rt.ResetOffsets[i - 1]) + return S_FALSE; + rt.ResetOffsets.AddInReserved(v); + } + + if (numEntries != 0) + if (rt.ResetOffsets[0] != 0) + return S_FALSE; + + if (numEntries == numBlocks + 1) + { + // Lazarus 9-26-2 chm contains additional entty + if (rt.ResetOffsets.Back() != rt.CompressedSize) + return S_FALSE; + } } } } @@ -896,14 +935,16 @@ HRESULT CInArchive::Open2(IInStream *inStream, if (_help2) { - const int kSignatureSize = 8; - UInt64 signature = ((UInt64)kSignature_ITLS << 32)| kSignature_ITOL; + const unsigned kSignatureSize = 8; + const UInt64 signature = ((UInt64)kSignature_ITLS << 32) | kSignature_ITOL; UInt64 limit = 1 << 18; + if (searchHeaderSizeLimit) if (limit > *searchHeaderSizeLimit) limit = *searchHeaderSizeLimit; UInt64 val = 0; + for (;;) { Byte b; @@ -919,6 +960,7 @@ HRESULT CInArchive::Open2(IInStream *inStream, return S_FALSE; } } + database.StartPosition += _inBuffer.GetProcessedSize() - kSignatureSize; RINOK(OpenHelp2(inStream, database)); if (database.NewFormat) diff --git a/CPP/7zip/Archive/Chm/ChmIn.h b/CPP/7zip/Archive/Chm/ChmIn.h index c4ce83ed..dc5e8263 100644 --- a/CPP/7zip/Archive/Chm/ChmIn.h +++ b/CPP/7zip/Archive/Chm/ChmIn.h @@ -36,12 +36,13 @@ struct CItem bool IsDir() const { - if (Name.Len() == 0) + if (Name.IsEmpty()) return false; return (Name.Back() == '/'); } }; + struct CDatabase { UInt64 StartPosition; @@ -73,11 +74,14 @@ struct CDatabase } }; + +const UInt32 kBlockSize = 1 << 15; + struct CResetTable { UInt64 UncompressedSize; UInt64 CompressedSize; - UInt64 BlockSize; + // unsigned BlockSizeBits; CRecordVector<UInt64> ResetOffsets; bool GetCompressedSizeOfBlocks(UInt64 blockIndex, UInt32 numBlocks, UInt64 &size) const @@ -91,39 +95,41 @@ struct CResetTable size = ResetOffsets[(unsigned)(blockIndex + numBlocks)] - startPos; return true; } + bool GetCompressedSizeOfBlock(UInt64 blockIndex, UInt64 &size) const { return GetCompressedSizeOfBlocks(blockIndex, 1, size); } + UInt64 GetNumBlocks(UInt64 size) const { - return (size + BlockSize - 1) / BlockSize; + return (size + kBlockSize - 1) / kBlockSize; } }; + struct CLzxInfo { UInt32 Version; - UInt32 ResetInterval; - UInt32 WindowSize; + + unsigned ResetIntervalBits; + unsigned WindowSizeBits; UInt32 CacheSize; + CResetTable ResetTable; - UInt32 GetNumDictBits() const + unsigned GetNumDictBits() const { if (Version == 2 || Version == 3) - { - for (unsigned i = 0; i <= 31; i++) - if (((UInt32)1 << i) >= WindowSize) - return 15 + i; - } + return 15 + WindowSizeBits; return 0; } - UInt64 GetFolderSize() const { return ResetTable.BlockSize * ResetInterval; } + UInt64 GetFolderSize() const { return kBlockSize << ResetIntervalBits; } UInt64 GetFolder(UInt64 offset) const { return offset / GetFolderSize(); } UInt64 GetFolderPos(UInt64 folderIndex) const { return folderIndex * GetFolderSize(); } - UInt64 GetBlockIndexFromFolderIndex(UInt64 folderIndex) const { return folderIndex * ResetInterval; } + UInt64 GetBlockIndexFromFolderIndex(UInt64 folderIndex) const { return folderIndex << ResetIntervalBits; } + bool GetOffsetOfFolder(UInt64 folderIndex, UInt64 &offset) const { UInt64 blockIndex = GetBlockIndexFromFolderIndex(folderIndex); @@ -132,24 +138,28 @@ struct CLzxInfo offset = ResetTable.ResetOffsets[(unsigned)blockIndex]; return true; } + bool GetCompressedSizeOfFolder(UInt64 folderIndex, UInt64 &size) const { UInt64 blockIndex = GetBlockIndexFromFolderIndex(folderIndex); - return ResetTable.GetCompressedSizeOfBlocks(blockIndex, ResetInterval, size); + return ResetTable.GetCompressedSizeOfBlocks(blockIndex, (UInt32)1 << ResetIntervalBits, size); } }; + struct CMethodInfo { GUID Guid; CByteBuffer ControlData; CLzxInfo LzxInfo; + bool IsLzx() const; bool IsDes() const; AString GetGuidString() const; UString GetName() const; }; + struct CSectionInfo { UInt64 Offset; @@ -203,19 +213,12 @@ public: CDatabase::Clear(); HighLevelClear(); } + void SetIndices(); void Sort(); bool Check(); }; -/* -class CProgressVirt -{ -public: - STDMETHOD(SetTotal)(const UInt64 *numFiles) PURE; - STDMETHOD(SetCompleted)(const UInt64 *numFiles) PURE; -}; -*/ class CInArchive { diff --git a/CPP/7zip/Archive/DmgHandler.cpp b/CPP/7zip/Archive/DmgHandler.cpp index 208f2a82..2a80e255 100644 --- a/CPP/7zip/Archive/DmgHandler.cpp +++ b/CPP/7zip/Archive/DmgHandler.cpp @@ -545,8 +545,8 @@ HRESULT CHandler::Open2(IInStream *stream) CChecksum masterChecksum; masterChecksum.Parse(buf + 0x160); - // UInt32 imageVariant = Get32(buf + 0x1E8); - // UInt64 numSectors = Get64(buf + 0x1EC); + // UInt32 imageVariant = Get32(buf + 0x1E8); + // UInt64 numSectors = Get64(buf + 0x1EC); // Byte reserved[0x12] const UInt32 RSRC_HEAD_SIZE = 0x100; diff --git a/CPP/7zip/Archive/ElfHandler.cpp b/CPP/7zip/Archive/ElfHandler.cpp index 089d1023..beca8036 100644 --- a/CPP/7zip/Archive/ElfHandler.cpp +++ b/CPP/7zip/Archive/ElfHandler.cpp @@ -226,25 +226,25 @@ void CSegment::Parse(const Byte *p, bool mode64, bool be) // Section types -#define SHT_NULL 0 -#define SHT_PROGBITS 1 -#define SHT_SYMTAB 2 -#define SHT_STRTAB 3 -#define SHT_RELA 4 -#define SHT_HASH 5 -#define SHT_DYNAMIC 6 -#define SHT_NOTE 7 -#define SHT_NOBITS 8 -#define SHT_REL 9 -#define SHT_SHLIB 10 -#define SHT_DYNSYM 11 -#define SHT_UNKNOWN12 12 -#define SHT_UNKNOWN13 13 -#define SHT_INIT_ARRAY 14 -#define SHT_FINI_ARRAY 15 -#define SHT_PREINIT_ARRAY 16 -#define SHT_GROUP 17 -#define SHT_SYMTAB_SHNDX 18 +#define SHT_NULL 0 +#define SHT_PROGBITS 1 +#define SHT_SYMTAB 2 +#define SHT_STRTAB 3 +#define SHT_RELA 4 +#define SHT_HASH 5 +#define SHT_DYNAMIC 6 +#define SHT_NOTE 7 +#define SHT_NOBITS 8 +#define SHT_REL 9 +#define SHT_SHLIB 10 +#define SHT_DYNSYM 11 +#define SHT_UNKNOWN12 12 +#define SHT_UNKNOWN13 13 +#define SHT_INIT_ARRAY 14 +#define SHT_FINI_ARRAY 15 +#define SHT_PREINIT_ARRAY 16 +#define SHT_GROUP 17 +#define SHT_SYMTAB_SHNDX 18 static const CUInt32PCharPair g_SectTypes[] = @@ -554,11 +554,11 @@ static const CUInt32PCharPair g_OS[] = { 255, "Standalone" } }; -#define ET_NONE 0 -#define ET_REL 1 -#define ET_EXEC 2 -#define ET_DYN 3 -#define ET_CORE 4 +#define ET_NONE 0 +#define ET_REL 1 +#define ET_EXEC 2 +#define ET_DYN 3 +#define ET_CORE 4 static const char *g_Types[] = { diff --git a/CPP/7zip/Archive/GptHandler.cpp b/CPP/7zip/Archive/GptHandler.cpp new file mode 100644 index 00000000..2e0e7a57 --- /dev/null +++ b/CPP/7zip/Archive/GptHandler.cpp @@ -0,0 +1,386 @@ +// GptHandler.cpp + +#include "StdAfx.h" + +#include "../../../C/7zCrc.h" +#include "../../../C/CpuArch.h" + +#include "../../Common/ComTry.h" +#include "../../Common/IntToString.h" +#include "../../Common/MyBuffer.h" + +#include "../../Windows/PropVariantUtils.h" + +#include "../Common/RegisterArc.h" +#include "../Common/StreamUtils.h" + +#include "HandlerCont.h" + +#define Get16(p) GetUi16(p) +#define Get32(p) GetUi32(p) +#define Get64(p) GetUi64(p) + +using namespace NWindows; + +namespace NArchive { +namespace NGpt { + +#define SIGNATURE { 'E', 'F', 'I', ' ', 'P', 'A', 'R', 'T', 0, 0, 1, 0 } + +static const unsigned k_SignatureSize = 12; +static const Byte k_Signature[k_SignatureSize] = SIGNATURE; + +static const UInt32 kSectorSize = 512; + +static const CUInt32PCharPair g_PartitionFlags[] = +{ + { 0, "Sys" }, + { 1, "Ignore" }, + { 2, "Legacy" }, + { 60, "Win-Read-only" }, + { 62, "Win-Hidden" }, + { 63, "Win-Not-Automount" } +}; + +static const unsigned kNameLen = 36; + +struct CPartition +{ + Byte Type[16]; + Byte Id[16]; + UInt64 FirstLba; + UInt64 LastLba; + UInt64 Flags; + Byte Name[kNameLen * 2]; + + bool IsUnused() const + { + for (unsigned i = 0; i < 16; i++) + if (Type[i] != 0) + return false; + return true; + } + + UInt64 GetSize() const { return (LastLba - FirstLba + 1) * kSectorSize; } + UInt64 GetPos() const { return FirstLba * kSectorSize; } + UInt64 GetEnd() const { return (LastLba + 1) * kSectorSize; } + + void Parse(const Byte *p) + { + memcpy(Type, p, 16); + memcpy(Id, p + 16, 16); + FirstLba = Get64(p + 32); + LastLba = Get64(p + 40); + Flags = Get64(p + 48); + memcpy(Name, p + 56, kNameLen * 2); + } +}; + + +struct CPartType +{ + UInt32 Id; + const char *Ext; + const char *Type; +}; + +static const CPartType kPartTypes[] = +{ + // { 0x0, 0, "Unused" }, + { 0xC12A7328, 0, "EFI System" }, + { 0x024DEE41, 0, "MBR" }, + + { 0xE3C9E316, 0, "Windows MSR" }, + { 0xEBD0A0A2, 0, "Windows BDP" }, + { 0x5808C8AA, 0, "Windows LDM Metadata" }, + { 0xAF9B60A0, 0, "Windows LDM Data" }, + { 0xDE94BBA4, 0, "Windows Recovery" }, + // { 0x37AFFC90, 0, "IBM GPFS" }, + // { 0xE75CAF8F, 0, "Windows Storage Spaces" }, + + { 0x83BD6B9D, 0, "FreeBSD Boot" }, + { 0x516E7CB4, 0, "FreeBSD Data" }, + { 0x516E7CB5, 0, "FreeBSD Swap" }, + { 0x516E7CB6, "ufs", "FreeBSD UFS" }, + { 0x516E7CB8, 0, "FreeBSD Vinum" }, + { 0x516E7CB8, "zfs", "FreeBSD ZFS" }, + + { 0x48465300, "hfsx", "HFS+" }, +}; + +static int FindPartType(const Byte *guid) +{ + UInt32 val = Get32(guid); + for (unsigned i = 0; i < ARRAY_SIZE(kPartTypes); i++) + if (kPartTypes[i].Id == val) + return i; + return -1; +} + +static inline char GetHex(unsigned t) { return (char)(((t < 10) ? ('0' + t) : ('A' + (t - 10)))); } + +static void PrintHex(unsigned v, char *s) +{ + s[0] = GetHex((v >> 4) & 0xF); + s[1] = GetHex(v & 0xF); +} + +static void ConvertUInt16ToHex4Digits(UInt32 val, char *s) throw() +{ + PrintHex(val >> 8, s); + PrintHex(val & 0xFF, s + 2); +} + +static void GuidToString(const Byte *g, char *s) +{ + ConvertUInt32ToHex8Digits(Get32(g ), s); s += 8; *s++ = '-'; + ConvertUInt16ToHex4Digits(Get16(g + 4), s); s += 4; *s++ = '-'; + ConvertUInt16ToHex4Digits(Get16(g + 6), s); s += 4; *s++ = '-'; + for (unsigned i = 0; i < 8; i++) + { + if (i == 2) + *s++ = '-'; + PrintHex(g[8 + i], s); + s += 2; + } + *s = 0; +} + + +class CHandler: public CHandlerCont +{ + CRecordVector<CPartition> _items; + UInt64 _totalSize; + Byte Guid[16]; + + CByteBuffer _buffer; + + HRESULT Open2(IInStream *stream); + virtual UInt64 GetItemPos(UInt32 index) const { return _items[index].GetPos(); } + virtual UInt64 GetItemSize(UInt32 index) const { return _items[index].GetSize(); } +public: + INTERFACE_IInArchive_Cont(;) +}; + + +HRESULT CHandler::Open2(IInStream *stream) +{ + _buffer.Alloc(kSectorSize * 2); + RINOK(ReadStream_FALSE(stream, _buffer, kSectorSize * 2)); + + const Byte *buf = _buffer; + if (buf[0x1FE] != 0x55 || buf[0x1FF] != 0xAA) + return S_FALSE; + + buf += kSectorSize; + if (memcmp(buf, k_Signature, k_SignatureSize) != 0) + return S_FALSE; + { + // if (Get32(buf + 8) != 0x10000) return S_FALSE; // revision + UInt32 headerSize = Get32(buf + 12); // = 0x5C usually + if (headerSize > kSectorSize) + return S_FALSE; + UInt32 crc = Get32(buf + 0x10); + SetUi32(_buffer + kSectorSize + 0x10, 0); + if (CrcCalc(_buffer + kSectorSize, headerSize) != crc) + return S_FALSE; + } + // UInt32 reserved = Get32(buf + 0x14); + UInt64 curLba = Get64(buf + 0x18); + if (curLba != 1) + return S_FALSE; + UInt64 backupLba = Get64(buf + 0x20); + // UInt64 firstUsableLba = Get64(buf + 0x28); + // UInt64 lastUsableLba = Get64(buf + 0x30); + memcpy(Guid, buf + 0x38, 16); + UInt64 tableLba = Get64(buf + 0x48); + if (tableLba < 2) + return S_FALSE; + UInt32 numEntries = Get32(buf + 0x50); + UInt32 entrySize = Get32(buf + 0x54); // = 128 usually + UInt32 entriesCrc = Get32(buf + 0x58); + + if (entrySize < 128 + || entrySize > (1 << 12) + || numEntries > (1 << 16) + || tableLba < 2 + || tableLba >= ((UInt64)1 << (64 - 10))) + return S_FALSE; + + UInt32 tableSize = entrySize * numEntries; + UInt32 tableSizeAligned = (tableSize + kSectorSize - 1) & ~(kSectorSize - 1); + _buffer.Alloc(tableSizeAligned); + UInt64 tableOffset = tableLba * kSectorSize; + RINOK(stream->Seek(tableOffset, STREAM_SEEK_SET, NULL)); + RINOK(ReadStream_FALSE(stream, _buffer, tableSizeAligned)); + + if (CrcCalc(_buffer, tableSize) != entriesCrc) + return S_FALSE; + + _totalSize = tableOffset + tableSizeAligned; + + for (UInt32 i = 0; i < numEntries; i++) + { + CPartition item; + item.Parse(_buffer + i * entrySize); + if (item.IsUnused()) + continue; + UInt64 endPos = item.GetEnd(); + if (_totalSize < endPos) + _totalSize = endPos; + _items.Add(item); + } + + UInt64 end = (backupLba + 1) * kSectorSize; + if (_totalSize < end) + _totalSize = end; + + return S_OK; +} + +STDMETHODIMP CHandler::Open(IInStream *stream, + const UInt64 * /* maxCheckStartPosition */, + IArchiveOpenCallback * /* openArchiveCallback */) +{ + COM_TRY_BEGIN + Close(); + RINOK(Open2(stream)); + _stream = stream; + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::Close() +{ + _totalSize = 0; + memset(Guid, 0, sizeof(Guid)); + _items.Clear(); + _stream.Release(); + return S_OK; +} + +static const Byte kProps[] = +{ + kpidPath, + kpidSize, + kpidFileSystem, + kpidCharacts, + kpidOffset, + kpidId +}; + +static const Byte kArcProps[] = +{ + kpidId +}; + +IMP_IInArchive_Props +IMP_IInArchive_ArcProps + +STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NCOM::CPropVariant prop; + switch (propID) + { + case kpidMainSubfile: + { + if (_items.Size() == 1) + prop = (UInt32)0; + break; + } + case kpidPhySize: prop = _totalSize; break; + case kpidId: + { + char s[48]; + GuidToString(Guid, s); + prop = s; + break; + } + } + prop.Detach(value); + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) +{ + *numItems = _items.Size(); + return S_OK; +} + +STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NCOM::CPropVariant prop; + + const CPartition &item = _items[index]; + + switch (propID) + { + case kpidPath: + { + UString s; + for (unsigned i = 0; i < kNameLen; i++) + { + wchar_t c = (wchar_t)Get16(item.Name + i * 2); + if (c == 0) + break; + s += c; + } + { + int typeIndex = FindPartType(item.Type); + s += L'.'; + const char *ext = "img"; + if (typeIndex >= 0 && kPartTypes[(unsigned)typeIndex].Ext) + ext = kPartTypes[(unsigned)typeIndex].Ext; + s.AddAscii(ext); + } + prop = s; + break; + } + + case kpidSize: + case kpidPackSize: prop = item.GetSize(); break; + case kpidOffset: prop = item.GetPos(); break; + + case kpidFileSystem: + { + char s[48]; + const char *res; + int typeIndex = FindPartType(item.Type); + if (typeIndex >= 0 && kPartTypes[(unsigned)typeIndex].Type) + res = kPartTypes[(unsigned)typeIndex].Type; + else + { + GuidToString(item.Type, s); + res = s; + } + prop = res; + break; + } + + case kpidId: + { + char s[48]; + GuidToString(item.Id, s); + prop = s; + break; + } + + case kpidCharacts: FLAGS64_TO_PROP(g_PartitionFlags, item.Flags, prop); break; + } + + prop.Detach(value); + return S_OK; + COM_TRY_END +} + +REGISTER_ARC_I( + "GPT", "gpt mbr", NULL, 0xCB, + k_Signature, + kSectorSize, + 0, + NULL) + +}} diff --git a/CPP/7zip/Archive/HandlerCont.cpp b/CPP/7zip/Archive/HandlerCont.cpp new file mode 100644 index 00000000..23a184f2 --- /dev/null +++ b/CPP/7zip/Archive/HandlerCont.cpp @@ -0,0 +1,230 @@ +// HandlerCont.cpp + +#include "StdAfx.h" + +#include "../../Common/ComTry.h" + +#include "../Common/LimitedStreams.h" +#include "../Common/ProgressUtils.h" +#include "../Common/StreamUtils.h" + +#include "../Compress/CopyCoder.h" + +#include "HandlerCont.h" + +namespace NArchive { + +STDMETHODIMP CHandlerCont::Extract(const UInt32 *indices, UInt32 numItems, + Int32 testMode, IArchiveExtractCallback *extractCallback) +{ + COM_TRY_BEGIN + bool allFilesMode = (numItems == (UInt32)(Int32)-1); + if (allFilesMode) + { + RINOK(GetNumberOfItems(&numItems)); + } + if (numItems == 0) + return S_OK; + UInt64 totalSize = 0; + UInt32 i; + for (i = 0; i < numItems; i++) + totalSize += GetItemSize(allFilesMode ? i : indices[i]); + extractCallback->SetTotal(totalSize); + + totalSize = 0; + + NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder(); + CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec; + + CLocalProgress *lps = new CLocalProgress; + CMyComPtr<ICompressProgressInfo> progress = lps; + lps->Init(extractCallback, false); + + CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream; + CMyComPtr<ISequentialInStream> inStream(streamSpec); + streamSpec->SetStream(_stream); + + for (i = 0; i < numItems; i++) + { + lps->InSize = totalSize; + lps->OutSize = totalSize; + RINOK(lps->SetCur()); + CMyComPtr<ISequentialOutStream> outStream; + Int32 askMode = testMode ? + NExtract::NAskMode::kTest : + NExtract::NAskMode::kExtract; + Int32 index = allFilesMode ? i : indices[i]; + + RINOK(extractCallback->GetStream(index, &outStream, askMode)); + UInt64 size = GetItemSize(index); + totalSize += size; + if (!testMode && !outStream) + continue; + RINOK(extractCallback->PrepareOperation(askMode)); + + RINOK(_stream->Seek(GetItemPos(index), STREAM_SEEK_SET, NULL)); + streamSpec->Init(size); + RINOK(copyCoder->Code(inStream, outStream, NULL, NULL, progress)); + outStream.Release(); + int opRes = NExtract::NOperationResult::kDataError; + if (copyCoderSpec->TotalSize == size) + opRes = NExtract::NOperationResult::kOK; + else if (copyCoderSpec->TotalSize < size) + opRes = NExtract::NOperationResult::kUnexpectedEnd; + RINOK(extractCallback->SetOperationResult(opRes)); + } + + return S_OK; + COM_TRY_END +} + +STDMETHODIMP CHandlerCont::GetStream(UInt32 index, ISequentialInStream **stream) +{ + COM_TRY_BEGIN + // const CPartition &item = _items[index]; + return CreateLimitedInStream(_stream, GetItemPos(index), GetItemSize(index), stream); + COM_TRY_END +} + + + +STDMETHODIMP CHandlerImg::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition) +{ + switch (seekOrigin) + { + case STREAM_SEEK_SET: break; + case STREAM_SEEK_CUR: offset += _virtPos; break; + case STREAM_SEEK_END: offset += _size; break; + default: return STG_E_INVALIDFUNCTION; + } + if (offset < 0) + return HRESULT_WIN32_ERROR_NEGATIVE_SEEK; + _virtPos = offset; + if (newPosition) + *newPosition = offset; + return S_OK; +} + +static const Byte k_GDP_Signature[] = { 'E', 'F', 'I', ' ', 'P', 'A', 'R', 'T' }; + +static const char *GetImgExt(ISequentialInStream *stream) +{ + const size_t kHeaderSize = 1 << 10; + Byte buf[kHeaderSize]; + if (ReadStream_FAIL(stream, buf, kHeaderSize) == S_OK) + { + if (buf[0x1FE] == 0x55 && buf[0x1FF] == 0xAA) + { + if (memcmp(buf + 512, k_GDP_Signature, sizeof(k_GDP_Signature)) == 0) + return "gpt"; + return "mbr"; + } + } + return NULL; +} + +void CHandlerImg::CloseAtError() +{ + Stream.Release(); +} + +STDMETHODIMP CHandlerImg::Open(IInStream *stream, + const UInt64 * /* maxCheckStartPosition */, + IArchiveOpenCallback * openCallback) +{ + COM_TRY_BEGIN + { + Close(); + HRESULT res; + try + { + res = Open2(stream, openCallback); + if (res == S_OK) + { + CMyComPtr<ISequentialInStream> inStream; + HRESULT res2 = GetStream(0, &inStream); + if (res2 == S_OK && inStream) + _imgExt = GetImgExt(inStream); + return S_OK; + } + } + catch(...) + { + CloseAtError(); + throw; + } + CloseAtError(); + return res; + } + COM_TRY_END +} + +STDMETHODIMP CHandlerImg::GetNumberOfItems(UInt32 *numItems) +{ + *numItems = 1; + return S_OK; +} + +STDMETHODIMP CHandlerImg::Extract(const UInt32 *indices, UInt32 numItems, + Int32 testMode, IArchiveExtractCallback *extractCallback) +{ + COM_TRY_BEGIN + if (numItems == 0) + return S_OK; + if (numItems != (UInt32)(Int32)-1 && (numItems != 1 || indices[0] != 0)) + return E_INVALIDARG; + + RINOK(extractCallback->SetTotal(_size)); + CMyComPtr<ISequentialOutStream> outStream; + Int32 askMode = testMode ? + NExtract::NAskMode::kTest : + NExtract::NAskMode::kExtract; + RINOK(extractCallback->GetStream(0, &outStream, askMode)); + if (!testMode && !outStream) + return S_OK; + RINOK(extractCallback->PrepareOperation(askMode)); + + CLocalProgress *lps = new CLocalProgress; + CMyComPtr<ICompressProgressInfo> progress = lps; + lps->Init(extractCallback, false); + + int opRes = NExtract::NOperationResult::kDataError; + + CMyComPtr<ISequentialInStream> inStream; + HRESULT hres = GetStream(0, &inStream); + if (hres == S_FALSE) + hres = E_NOTIMPL; + + if (hres == S_OK && inStream) + { + NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder(); + CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec; + + hres = copyCoder->Code(inStream, outStream, NULL, &_size, progress); + if (hres == S_OK) + { + if (copyCoderSpec->TotalSize == _size) + opRes = NExtract::NOperationResult::kOK; + else if (copyCoderSpec->TotalSize < _size) + opRes = NExtract::NOperationResult::kUnexpectedEnd; + } + } + + inStream.Release(); + outStream.Release(); + + if (hres != S_OK) + { + if (hres == S_FALSE) + opRes = NExtract::NOperationResult::kDataError; + else if (hres == E_NOTIMPL) + opRes = NExtract::NOperationResult::kUnsupportedMethod; + else + return hres; + } + + return extractCallback->SetOperationResult(opRes); + COM_TRY_END +} + +} diff --git a/CPP/7zip/Archive/HandlerCont.h b/CPP/7zip/Archive/HandlerCont.h new file mode 100644 index 00000000..603a6511 --- /dev/null +++ b/CPP/7zip/Archive/HandlerCont.h @@ -0,0 +1,90 @@ +// HandlerCont.h + +#ifndef __HANDLER_CONT_H +#define __HANDLER_CONT_H + +#include "../../Common/MyCom.h" + +#include "IArchive.h" + +namespace NArchive { + +#define INTERFACE_IInArchive_Cont(x) \ + STDMETHOD(Open)(IInStream *stream, const UInt64 *maxCheckStartPosition, IArchiveOpenCallback *openCallback) MY_NO_THROW_DECL_ONLY x; \ + STDMETHOD(Close)() MY_NO_THROW_DECL_ONLY x; \ + STDMETHOD(GetNumberOfItems)(UInt32 *numItems) MY_NO_THROW_DECL_ONLY x; \ + STDMETHOD(GetProperty)(UInt32 index, PROPID propID, PROPVARIANT *value) MY_NO_THROW_DECL_ONLY x; \ + /* STDMETHOD(Extract)(const UInt32* indices, UInt32 numItems, Int32 testMode, IArchiveExtractCallback *extractCallback) MY_NO_THROW_DECL_ONLY x; */ \ + STDMETHOD(GetArchiveProperty)(PROPID propID, PROPVARIANT *value) MY_NO_THROW_DECL_ONLY x; \ + STDMETHOD(GetNumberOfProperties)(UInt32 *numProps) MY_NO_THROW_DECL_ONLY x; \ + STDMETHOD(GetPropertyInfo)(UInt32 index, BSTR *name, PROPID *propID, VARTYPE *varType) MY_NO_THROW_DECL_ONLY x; \ + STDMETHOD(GetNumberOfArchiveProperties)(UInt32 *numProps) MY_NO_THROW_DECL_ONLY x; \ + STDMETHOD(GetArchivePropertyInfo)(UInt32 index, BSTR *name, PROPID *propID, VARTYPE *varType) MY_NO_THROW_DECL_ONLY x; \ + + +class CHandlerCont: + public IInArchive, + public IInArchiveGetStream, + public CMyUnknownImp +{ +protected: + CMyComPtr<IInStream> _stream; + + virtual UInt64 GetItemPos(UInt32 index) const = 0; + virtual UInt64 GetItemSize(UInt32 index) const = 0; +public: + MY_UNKNOWN_IMP2(IInArchive, IInArchiveGetStream) + INTERFACE_IInArchive_Cont(PURE) + + STDMETHOD(Extract)(const UInt32* indices, UInt32 numItems, Int32 testMode, IArchiveExtractCallback *extractCallback) MY_NO_THROW_DECL_ONLY; + + STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream); +}; + + + +#define INTERFACE_IInArchive_Img(x) \ + /* STDMETHOD(Open)(IInStream *stream, const UInt64 *maxCheckStartPosition, IArchiveOpenCallback *openCallback) MY_NO_THROW_DECL_ONLY x; */ \ + STDMETHOD(Close)() MY_NO_THROW_DECL_ONLY x; \ + /* STDMETHOD(GetNumberOfItems)(UInt32 *numItems) MY_NO_THROW_DECL_ONLY x; */ \ + STDMETHOD(GetProperty)(UInt32 index, PROPID propID, PROPVARIANT *value) MY_NO_THROW_DECL_ONLY x; \ + /* STDMETHOD(Extract)(const UInt32* indices, UInt32 numItems, Int32 testMode, IArchiveExtractCallback *extractCallback) MY_NO_THROW_DECL_ONLY x; */ \ + STDMETHOD(GetArchiveProperty)(PROPID propID, PROPVARIANT *value) MY_NO_THROW_DECL_ONLY x; \ + STDMETHOD(GetNumberOfProperties)(UInt32 *numProps) MY_NO_THROW_DECL_ONLY x; \ + STDMETHOD(GetPropertyInfo)(UInt32 index, BSTR *name, PROPID *propID, VARTYPE *varType) MY_NO_THROW_DECL_ONLY x; \ + STDMETHOD(GetNumberOfArchiveProperties)(UInt32 *numProps) MY_NO_THROW_DECL_ONLY x; \ + STDMETHOD(GetArchivePropertyInfo)(UInt32 index, BSTR *name, PROPID *propID, VARTYPE *varType) MY_NO_THROW_DECL_ONLY x; \ + + +class CHandlerImg: + public IInStream, + public IInArchive, + public IInArchiveGetStream, + public CMyUnknownImp +{ +protected: + UInt64 _virtPos; + UInt64 _posInArc; + UInt64 _size; + CMyComPtr<IInStream> Stream; + const char *_imgExt; + + virtual HRESULT Open2(IInStream *stream, IArchiveOpenCallback *openCallback) = 0; + virtual void CloseAtError(); +public: + MY_UNKNOWN_IMP3(IInArchive, IInArchiveGetStream, IInStream) + INTERFACE_IInArchive_Img(PURE) + + STDMETHOD(Open)(IInStream *stream, const UInt64 *maxCheckStartPosition, IArchiveOpenCallback *openCallback); + STDMETHOD(GetNumberOfItems)(UInt32 *numItems); + STDMETHOD(Extract)(const UInt32* indices, UInt32 numItems, Int32 testMode, IArchiveExtractCallback *extractCallback); + + STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream) = 0; + + STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize) = 0; + STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition); +}; + +} + +#endif diff --git a/CPP/7zip/Archive/MachoHandler.cpp b/CPP/7zip/Archive/MachoHandler.cpp index 00dc571b..9dae1e70 100644 --- a/CPP/7zip/Archive/MachoHandler.cpp +++ b/CPP/7zip/Archive/MachoHandler.cpp @@ -40,7 +40,7 @@ namespace NMacho { #define CPU_SUBTYPE_LIB64 (1 << 31) -#define CPU_SUBTYPE_POWERPC_970 100 +#define CPU_SUBTYPE_POWERPC_970 100 static const char * const k_PowerPc_SubTypes[] = { diff --git a/CPP/7zip/Archive/MbrHandler.cpp b/CPP/7zip/Archive/MbrHandler.cpp index c4e4cc60..5135e5f8 100644 --- a/CPP/7zip/Archive/MbrHandler.cpp +++ b/CPP/7zip/Archive/MbrHandler.cpp @@ -13,16 +13,13 @@ #include "../../Common/ComTry.h" #include "../../Common/IntToString.h" #include "../../Common/MyBuffer.h" -#include "../../Common/MyString.h" #include "../../Windows/PropVariant.h" -#include "../Common/LimitedStreams.h" -#include "../Common/ProgressUtils.h" #include "../Common/RegisterArc.h" #include "../Common/StreamUtils.h" -#include "../Compress/CopyCoder.h" +#include "HandlerCont.h" #ifdef SHOW_DEBUG_INFO #define PRF(x) x @@ -56,12 +53,15 @@ struct CChs #define RINOZ(x) { int __tt = (x); if (__tt != 0) return __tt; } +// Chs in some MBRs contains only low bits of "Cyl number". So we disable check. +/* static int CompareChs(const CChs &c1, const CChs &c2) { RINOZ(MyCompare(c1.GetCyl(), c2.GetCyl())); RINOZ(MyCompare(c1.Head, c2.Head)); return MyCompare(c1.GetSector(), c2.GetSector()); } +*/ static void AddUIntToString(UInt32 val, AString &res) { @@ -112,12 +112,11 @@ struct CPartition return true; if (Status != 0 && Status != 0x80) return false; - return - BeginChs.Check() && - EndChs.Check() && - CompareChs(BeginChs, EndChs) <= 0 && - NumBlocks > 0 && - CheckLbaLimits(); + return BeginChs.Check() + && EndChs.Check() + // && CompareChs(BeginChs, EndChs) <= 0 + && NumBlocks > 0 + && CheckLbaLimits(); } #ifdef SHOW_DEBUG_INFO @@ -159,11 +158,12 @@ static const CPartType kPartTypes[] = { 0x1E, kFat, "FAT16-LBA-WIN95-Hidden" }, { 0x82, 0, "Solaris x86 / Linux swap" }, { 0x83, 0, "Linux" }, + { 0xA5, 0, "BSD slice" }, { 0xBE, 0, "Solaris 8 boot" }, { 0xBF, 0, "New Solaris x86" }, { 0xC2, 0, "Linux-Hidden" }, { 0xC3, 0, "Linux swap-Hidden" }, - { 0xEE, 0, "EFI-MBR" }, + { 0xEE, 0, "GPT" }, { 0xEE, 0, "EFI" } }; @@ -183,21 +183,17 @@ struct CItem CPartition Part; }; -class CHandler: - public IInArchive, - public IInArchiveGetStream, - public CMyUnknownImp +class CHandler: public CHandlerCont { - CMyComPtr<IInStream> _stream; CObjectVector<CItem> _items; UInt64 _totalSize; CByteBuffer _buffer; + virtual UInt64 GetItemPos(UInt32 index) const { return _items[index].Part.GetPos(); } + virtual UInt64 GetItemSize(UInt32 index) const { return _items[index].Size; } HRESULT ReadTables(IInStream *stream, UInt32 baseLba, UInt32 lba, unsigned level); public: - MY_UNKNOWN_IMP2(IInArchive, IInArchiveGetStream) - INTERFACE_IInArchive(;) - STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream); + INTERFACE_IInArchive_Cont(;) }; HRESULT CHandler::ReadTables(IInStream *stream, UInt32 baseLba, UInt32 lba, unsigned level) @@ -391,7 +387,7 @@ STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *val const CItem &item = _items[index]; const CPartition &part = item.Part; - switch(propID) + switch (propID) { case kpidPath: { @@ -421,7 +417,7 @@ STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *val prop = res; } break; - case kpidSize: prop = item.Size; break;; + case kpidSize: case kpidPackSize: prop = item.Size; break; case kpidOffset: prop = part.GetPos(); break; case kpidPrimary: if (item.IsReal) prop = item.IsPrim; break; @@ -433,72 +429,6 @@ STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *val COM_TRY_END } -STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, - Int32 testMode, IArchiveExtractCallback *extractCallback) -{ - COM_TRY_BEGIN - bool allFilesMode = (numItems == (UInt32)(Int32)-1); - if (allFilesMode) - numItems = _items.Size(); - if (numItems == 0) - return S_OK; - UInt64 totalSize = 0; - UInt32 i; - for (i = 0; i < numItems; i++) - totalSize += _items[allFilesMode ? i : indices[i]].Size; - extractCallback->SetTotal(totalSize); - - totalSize = 0; - - NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder(); - CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec; - - CLocalProgress *lps = new CLocalProgress; - CMyComPtr<ICompressProgressInfo> progress = lps; - lps->Init(extractCallback, false); - - CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream; - CMyComPtr<ISequentialInStream> inStream(streamSpec); - streamSpec->SetStream(_stream); - - for (i = 0; i < numItems; i++) - { - lps->InSize = totalSize; - lps->OutSize = totalSize; - RINOK(lps->SetCur()); - CMyComPtr<ISequentialOutStream> outStream; - Int32 askMode = testMode ? - NExtract::NAskMode::kTest : - NExtract::NAskMode::kExtract; - Int32 index = allFilesMode ? i : indices[i]; - const CItem &item = _items[index]; - const CPartition &part = item.Part; - RINOK(extractCallback->GetStream(index, &outStream, askMode)); - totalSize += item.Size; - if (!testMode && !outStream) - continue; - RINOK(extractCallback->PrepareOperation(askMode)); - - RINOK(_stream->Seek(part.GetPos(), STREAM_SEEK_SET, NULL)); - streamSpec->Init(item.Size); - RINOK(copyCoder->Code(inStream, outStream, NULL, NULL, progress)); - outStream.Release(); - RINOK(extractCallback->SetOperationResult(copyCoderSpec->TotalSize == item.Size ? - NExtract::NOperationResult::kOK: - NExtract::NOperationResult::kDataError)); - } - return S_OK; - COM_TRY_END -} - -STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream) -{ - COM_TRY_BEGIN - const CItem &item = _items[index]; - return CreateLimitedInStream(_stream, item.Part.GetPos(), item.Size, stream); - COM_TRY_END -} - // 3, { 1, 1, 0 }, // 2, { 0x55, 0x1FF }, diff --git a/CPP/7zip/Archive/MubHandler.cpp b/CPP/7zip/Archive/MubHandler.cpp index a84c3120..05ea4d9f 100644 --- a/CPP/7zip/Archive/MubHandler.cpp +++ b/CPP/7zip/Archive/MubHandler.cpp @@ -10,12 +10,10 @@ #include "../../Windows/PropVariant.h" -#include "../Common/LimitedStreams.h" -#include "../Common/ProgressUtils.h" #include "../Common/RegisterArc.h" #include "../Common/StreamUtils.h" -#include "../Compress/CopyCoder.h" +#include "HandlerCont.h" static UInt32 Get32(const Byte *p, bool be) { if (be) return GetBe32(p); return GetUi32(p); } @@ -36,7 +34,7 @@ namespace NMub { #define MACH_CPU_SUBTYPE_LIB64 (1 << 31) -#define MACH_CPU_SUBTYPE_I386_ALL 3 +#define MACH_CPU_SUBTYPE_I386_ALL 3 struct CItem { @@ -49,12 +47,8 @@ struct CItem static const UInt32 kNumFilesMax = 10; -class CHandler: - public IInArchive, - public IInArchiveGetStream, - public CMyUnknownImp +class CHandler: public CHandlerCont { - CMyComPtr<IInStream> _stream; // UInt64 _startPos; UInt64 _phySize; UInt32 _numItems; @@ -62,10 +56,10 @@ class CHandler: CItem _items[kNumFilesMax]; HRESULT Open2(IInStream *stream); + virtual UInt64 GetItemPos(UInt32 index) const { return _items[index].Offset; } + virtual UInt64 GetItemSize(UInt32 index) const { return _items[index].Size; } public: - MY_UNKNOWN_IMP2(IInArchive, IInArchiveGetStream) - INTERFACE_IInArchive(;) - STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream); + INTERFACE_IInArchive_Cont(;) }; static const Byte kArcProps[] = @@ -223,75 +217,6 @@ STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) return S_OK; } -STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, - Int32 testMode, IArchiveExtractCallback *extractCallback) -{ - COM_TRY_BEGIN - bool allFilesMode = (numItems == (UInt32)(Int32)-1); - if (allFilesMode) - numItems = _numItems; - if (numItems == 0) - return S_OK; - UInt64 totalSize = 0; - UInt32 i; - for (i = 0; i < numItems; i++) - totalSize += _items[allFilesMode ? i : indices[i]].Size; - extractCallback->SetTotal(totalSize); - - UInt64 currentTotalSize = 0; - - NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder(); - CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec; - - CLocalProgress *lps = new CLocalProgress; - CMyComPtr<ICompressProgressInfo> progress = lps; - lps->Init(extractCallback, false); - - CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream; - CMyComPtr<ISequentialInStream> inStream(streamSpec); - streamSpec->SetStream(_stream); - - for (i = 0; i < numItems; i++) - { - lps->InSize = lps->OutSize = currentTotalSize; - RINOK(lps->SetCur()); - CMyComPtr<ISequentialOutStream> realOutStream; - Int32 askMode = testMode ? - NExtract::NAskMode::kTest : - NExtract::NAskMode::kExtract; - UInt32 index = allFilesMode ? i : indices[i]; - const CItem &item = _items[index]; - RINOK(extractCallback->GetStream(index, &realOutStream, askMode)); - currentTotalSize += item.Size; - - if (!testMode && !realOutStream) - continue; - RINOK(extractCallback->PrepareOperation(askMode)); - if (testMode) - { - RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK)); - continue; - } - RINOK(_stream->Seek(/* _startPos + */ item.Offset, STREAM_SEEK_SET, NULL)); - streamSpec->Init(item.Size); - RINOK(copyCoder->Code(inStream, realOutStream, NULL, NULL, progress)); - realOutStream.Release(); - RINOK(extractCallback->SetOperationResult((copyCoderSpec->TotalSize == item.Size) ? - NExtract::NOperationResult::kOK: - NExtract::NOperationResult::kDataError)); - } - return S_OK; - COM_TRY_END -} - -STDMETHODIMP CHandler::GetStream(UInt32 index, ISequentialInStream **stream) -{ - COM_TRY_BEGIN - const CItem &item = _items[index]; - return CreateLimitedInStream(_stream, /* _startPos + */ item.Offset, item.Size, stream); - COM_TRY_END -} - namespace NBe { static const Byte k_Signature[] = { diff --git a/CPP/7zip/Archive/NtfsHandler.cpp b/CPP/7zip/Archive/NtfsHandler.cpp index ee630a66..67cd385f 100644 --- a/CPP/7zip/Archive/NtfsHandler.cpp +++ b/CPP/7zip/Archive/NtfsHandler.cpp @@ -2514,8 +2514,8 @@ STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *val attrib |= FILE_ATTRIBUTE_DIRECTORY; /* some system entries can contain extra flags (Index View). - // 0x10000000 (Directory) - // 0x20000000 FILE_ATTR_VIEW_INDEX_PRESENT MFT_RECORD_IS_VIEW_INDEX (Index View) + // 0x10000000 (Directory) + // 0x20000000 FILE_ATTR_VIEW_INDEX_PRESENT MFT_RECORD_IS_VIEW_INDEX (Index View) But we don't need them */ attrib &= 0xFFFF; diff --git a/CPP/7zip/Archive/QcowHandler.cpp b/CPP/7zip/Archive/QcowHandler.cpp new file mode 100644 index 00000000..42c0a511 --- /dev/null +++ b/CPP/7zip/Archive/QcowHandler.cpp @@ -0,0 +1,611 @@ +// QcowHandler.cpp + +#include "StdAfx.h" + +// #include <stdio.h> + +#include "../../../C/CpuArch.h" + +#include "../../Common/ComTry.h" +#include "../../Common/IntToString.h" + +#include "../../Windows/PropVariant.h" + +#include "../Common/RegisterArc.h" +#include "../Common/StreamObjects.h" +#include "../Common/StreamUtils.h" + +#include "../Compress/DeflateDecoder.h" + +#include "HandlerCont.h" + +#define Get32(p) GetBe32(p) +#define Get64(p) GetBe64(p) + +using namespace NWindows; + +namespace NArchive { +namespace NQcow { + +#define SIGNATURE { 'Q', 'F', 'I', 0xFB, 0, 0, 0 } + +static const Byte k_Signature[] = SIGNATURE; + +class CHandler: public CHandlerImg +{ + unsigned _clusterBits; + unsigned _numMidBits; + UInt64 _compressedFlag; + + CObjectVector<CByteBuffer> _tables; + UInt64 _cacheCluster; + CByteBuffer _cache; + CByteBuffer _cacheCompressed; + + UInt64 _comprPos; + size_t _comprSize; + + UInt64 _phySize; + + CBufInStream *_bufInStreamSpec; + CMyComPtr<ISequentialInStream> _bufInStream; + + CBufPtrSeqOutStream *_bufOutStreamSpec; + CMyComPtr<ISequentialOutStream> _bufOutStream; + + NCompress::NDeflate::NDecoder::CCOMCoder *_deflateDecoderSpec; + CMyComPtr<ICompressCoder> _deflateDecoder; + + bool _needDeflate; + bool _isArc; + bool _unsupported; + + UInt32 _version; + UInt32 _cryptMethod; + + HRESULT Seek(UInt64 offset) + { + _posInArc = offset; + return Stream->Seek(offset, STREAM_SEEK_SET, NULL); + } + + HRESULT InitAndSeek() + { + _virtPos = 0; + return Seek(0); + } + + HRESULT Open2(IInStream *stream, IArchiveOpenCallback *openCallback); + +public: + INTERFACE_IInArchive_Img(;) + + STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream); + STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); +}; + + +STDMETHODIMP CHandler::Read(void *data, UInt32 size, UInt32 *processedSize) +{ + if (processedSize) + *processedSize = 0; + if (_virtPos >= _size) + return S_OK; + { + UInt64 rem = _size - _virtPos; + if (size > rem) + size = (UInt32)rem; + if (size == 0) + return S_OK; + } + + for (;;) + { + UInt64 cluster = _virtPos >> _clusterBits; + size_t clusterSize = (size_t)1 << _clusterBits; + size_t lowBits = (size_t)_virtPos & (clusterSize - 1); + { + size_t rem = clusterSize - lowBits; + if (size > rem) + size = (UInt32)rem; + } + + if (cluster == _cacheCluster) + { + memcpy(data, _cache + lowBits, size); + _virtPos += size; + if (processedSize) + *processedSize = size; + return S_OK; + } + + UInt64 high = cluster >> _numMidBits; + + if (high < _tables.Size()) + { + const CByteBuffer &buffer = _tables[(unsigned)high]; + + if (buffer.Size() != 0) + { + size_t midBits = (size_t)cluster & (((size_t)1 << _numMidBits) - 1); + const Byte *p = (const Byte *)buffer + (midBits << 3); + UInt64 v = Get64(p); + + if (v != 0) + { + if ((v & _compressedFlag) != 0) + { + if (_version <= 1) + return E_FAIL; + unsigned numOffsetBits = (62 - (_clusterBits - 8)); + UInt64 offset = v & (((UInt64)1 << 62) - 1); + const size_t dataSize = ((size_t)(offset >> numOffsetBits) + 1) << 9; + offset &= ((UInt64)1 << numOffsetBits) - 1; + UInt64 sectorOffset = offset >> 9 << 9; + UInt64 offset2inCache = sectorOffset - _comprPos; + + if (sectorOffset >= _comprPos && offset2inCache < _comprSize) + { + if (offset2inCache != 0) + { + _comprSize -= (size_t)offset2inCache; + memmove(_cacheCompressed, _cacheCompressed + offset2inCache, _comprSize); + _comprPos = sectorOffset; + } + sectorOffset += _comprSize; + } + else + { + _comprPos = sectorOffset; + _comprSize = 0; + } + + // printf("\nDeflate"); + if (sectorOffset != _posInArc) + { + // printf("\nDeflate %12I64x %12I64x\n", sectorOffset, sectorOffset - _posInArc); + RINOK(Seek(sectorOffset)); + } + + if (_cacheCompressed.Size() < dataSize) + return E_FAIL; + size_t dataSize3 = dataSize - _comprSize; + size_t dataSize2 = dataSize3; + RINOK(ReadStream(Stream, _cacheCompressed + _comprSize, &dataSize2)); + _posInArc += dataSize2; + if (dataSize2 != dataSize3) + return E_FAIL; + _comprSize += dataSize2; + + const size_t kSectorMask = (1 << 9) - 1; + size_t offsetInSector = ((size_t)offset & kSectorMask); + _bufInStreamSpec->Init(_cacheCompressed + offsetInSector, dataSize - offsetInSector); + + _cacheCluster = (UInt64)(Int64)-1; + if (_cache.Size() < clusterSize) + return E_FAIL; + _bufOutStreamSpec->Init(_cache, clusterSize); + + // Do we need to use smaller block than clusterSize for last cluster? + UInt64 blockSize64 = clusterSize; + HRESULT res = _deflateDecoderSpec->Code(_bufInStream, _bufOutStream, NULL, &blockSize64, NULL); + + /* + if (_bufOutStreamSpec->GetPos() != clusterSize) + memset(_cache + _bufOutStreamSpec->GetPos(), 0, clusterSize - _bufOutStreamSpec->GetPos()); + */ + + if (res == S_OK) + if (!_deflateDecoderSpec->IsFinished() + || _bufOutStreamSpec->GetPos() != clusterSize) + res = S_FALSE; + + RINOK(res); + _cacheCluster = cluster; + + continue; + /* + memcpy(data, _cache + lowBits, size); + _virtPos += size; + if (processedSize) + *processedSize = size; + return S_OK; + */ + } + + // version 3 support zero clusters + if (((UInt32)v & 511) != 1) + { + v &= (_compressedFlag - 1); + v += lowBits; + if (v != _posInArc) + { + // printf("\n%12I64x\n", v - _posInArc); + RINOK(Seek(v)); + } + HRESULT res = Stream->Read(data, size, &size); + _posInArc += size; + _virtPos += size; + if (processedSize) + *processedSize = size; + return res; + } + } + } + } + + memset(data, 0, size); + _virtPos += size; + if (processedSize) + *processedSize = size; + return S_OK; + } +} + + +static const Byte kProps[] = +{ + kpidSize, + kpidPackSize +}; + +static const Byte kArcProps[] = +{ + kpidClusterSize, + kpidUnpackVer, + kpidMethod +}; + +IMP_IInArchive_Props +IMP_IInArchive_ArcProps + +STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NCOM::CPropVariant prop; + + switch (propID) + { + case kpidMainSubfile: prop = (UInt32)0; break; + case kpidClusterSize: prop = (UInt32)1 << _clusterBits; break; + case kpidPhySize: if (_phySize != 0) prop = _phySize; break; + case kpidUnpackVer: prop = _version; break; + + case kpidMethod: + { + AString s; + + if (_needDeflate) + s = "Deflate"; + + if (_cryptMethod != 0) + { + s.Add_Space_if_NotEmpty(); + if (_cryptMethod == 1) + s += "AES"; + else + { + char temp[16]; + ConvertUInt32ToString(_cryptMethod, temp); + s += temp; + } + } + + if (!s.IsEmpty()) + prop = s; + + break; + } + + case kpidErrorFlags: + { + UInt32 v = 0; + if (!_isArc) v |= kpv_ErrorFlags_IsNotArc;; + if (_unsupported) v |= kpv_ErrorFlags_UnsupportedMethod; + // if (_headerError) v |= kpv_ErrorFlags_HeadersError; + if (!Stream && v == 0 && _isArc) + v = kpv_ErrorFlags_HeadersError; + if (v != 0) + prop = v; + break; + } + } + + prop.Detach(value); + return S_OK; + COM_TRY_END +} + + +STDMETHODIMP CHandler::GetProperty(UInt32 /* index */, PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NCOM::CPropVariant prop; + + switch (propID) + { + case kpidSize: prop = _size; break; + case kpidPackSize: prop = _phySize; break; + case kpidExtension: prop = (_imgExt ? _imgExt : "img"); break; + } + + prop.Detach(value); + return S_OK; + COM_TRY_END +} + + +HRESULT CHandler::Open2(IInStream *stream, IArchiveOpenCallback *openCallback) +{ + const unsigned kHeaderSize = 18 * 4; + Byte buf[kHeaderSize]; + RINOK(ReadStream_FALSE(stream, buf, kHeaderSize)); + + if (memcmp(buf, k_Signature, 4) != 0) + return S_FALSE; + + _version = Get32(buf + 4); + if (_version < 1 || _version > 3) + return S_FALSE; + + const UInt64 backOffset = Get64(buf + 8); + // UInt32 backSize = Get32(buf + 0x10); + + UInt64 l1Offset = 0; + UInt32 l1Size = 0; + + if (_version == 1) + { + // _mTime = Get32(buf + 0x14); // is unused im most images + _size = Get64(buf + 0x18); + _clusterBits = buf[0x20]; + _numMidBits = buf[0x21]; + if (_clusterBits < 9 || _clusterBits > 30) + return S_FALSE; + if (_numMidBits < 1 || _numMidBits > 28) + return S_FALSE; + _cryptMethod = Get32(buf + 0x24); + l1Offset = Get64(buf + 0x28); + if (l1Offset < 0x30) + return S_FALSE; + unsigned numBits2 = (_clusterBits + _numMidBits); + UInt64 l1Size64 = (_size + (((UInt64)1 << numBits2) - 1)) >> numBits2; + if (l1Size64 > ((UInt32)1 << 31)) + return S_FALSE; + l1Size = (UInt32)l1Size64; + } + else + { + _clusterBits = Get32(buf + 0x14); + if (_clusterBits < 9 || _clusterBits > 30) + return S_FALSE; + _numMidBits = _clusterBits - 3; + _size = Get64(buf + 0x18); + _cryptMethod = Get32(buf + 0x20); + l1Size = Get32(buf + 0x24); + l1Offset = Get64(buf + 0x28); // must be aligned for cluster + + UInt64 refOffset = Get64(buf + 0x30); // must be aligned for cluster + UInt32 refClusters = Get32(buf + 0x38); + + // UInt32 numSnapshots = Get32(buf + 0x3C); + // UInt64 snapshotsOffset = Get64(buf + 0x40); // must be aligned for cluster + /* + if (numSnapshots != 0) + return S_FALSE; + */ + + if (refClusters != 0) + { + size_t numBytes = refClusters << _clusterBits; + /* + CByteBuffer refs; + refs.Alloc(numBytes); + RINOK(stream->Seek(refOffset, STREAM_SEEK_SET, NULL)); + RINOK(ReadStream_FALSE(stream, refs, numBytes)); + */ + UInt64 end = refOffset + numBytes; + if (_phySize < end) + _phySize = end; + /* + for (size_t i = 0; i < numBytes; i += 2) + { + UInt32 v = GetBe16((const Byte *)refs + (size_t)i); + if (v == 0) + continue; + } + */ + } + } + + _isArc = true; + + if (backOffset != 0) + { + _unsupported = true; + return S_FALSE; + } + + const size_t clusterSize = (size_t)1 << _clusterBits; + + CByteBuffer table; + { + size_t t1SizeBytes = (size_t)l1Size << 3; + if ((t1SizeBytes >> 3) != l1Size) + return S_FALSE; + table.Alloc(t1SizeBytes); + RINOK(stream->Seek(l1Offset, STREAM_SEEK_SET, NULL)); + RINOK(ReadStream_FALSE(stream, table, t1SizeBytes)); + + { + UInt64 end = l1Offset + t1SizeBytes; + // we need to uses align end for empty qcow files + end = (end + clusterSize - 1) >> _clusterBits << _clusterBits; + if (_phySize < end) + _phySize = end; + } + } + + if (openCallback) + { + UInt64 totalBytes = (UInt64)l1Size << (_numMidBits + 3); + RINOK(openCallback->SetTotal(NULL, &totalBytes)); + } + + _compressedFlag = (_version <= 1) ? ((UInt64)1 << 63) : ((UInt64)1 << 62); + const UInt64 offsetMask = _compressedFlag - 1; + + for (UInt32 i = 0; i < l1Size; i++) + { + if (openCallback) + { + UInt64 numBytes = (UInt64)i << (_numMidBits + 3); + RINOK(openCallback->SetCompleted(NULL, &numBytes)); + } + + UInt64 v = Get64((const Byte *)table + (size_t)i * 8); + v &= offsetMask; + CByteBuffer &buf = _tables.AddNew(); + if (v == 0) + continue; + + buf.Alloc((size_t)1 << (_numMidBits + 3)); + RINOK(stream->Seek(v, STREAM_SEEK_SET, NULL)); + RINOK(ReadStream_FALSE(stream, buf, clusterSize)); + UInt64 end = v + clusterSize; + if (_phySize < end) + _phySize = end; + + for (size_t k = 0; k < clusterSize; k += 8) + { + UInt64 v = Get64((const Byte *)buf + (size_t)k); + if (v == 0) + continue; + UInt64 offset = v & offsetMask; + size_t dataSize = clusterSize; + + if ((v & _compressedFlag) != 0) + { + if (_version <= 1) + { + unsigned numOffsetBits = (63 - _clusterBits); + dataSize = ((size_t)(offset >> numOffsetBits) + 1) << 9; + offset &= ((UInt64)1 << numOffsetBits) - 1; + dataSize = 0; + // offset >>= 9; + // offset <<= 9; + } + else + { + unsigned numOffsetBits = (62 - (_clusterBits - 8)); + dataSize = ((size_t)(offset >> numOffsetBits) + 1) << 9; + offset &= ((UInt64)1 << numOffsetBits) - 1; + offset >>= 9; + offset <<= 9; + } + _needDeflate = true; + } + else + { + UInt32 low = (UInt32)v & 511; + if (low != 0) + { + // version 3 support zero clusters + if (_version < 3 || low != 1) + { + _unsupported = true; + return S_FALSE; + } + } + } + + UInt64 end = offset + dataSize; + if (_phySize < end) + _phySize = end; + } + } + + if (_cryptMethod != 0) + _unsupported = true; + + if (_needDeflate && _version <= 1) // that case was not implemented + _unsupported = true; + + Stream = stream; + return S_OK; +} + + +STDMETHODIMP CHandler::Close() +{ + _tables.Clear(); + _phySize = 0; + _size = 0; + + _cacheCluster = (UInt64)(Int64)-1; + _comprPos = 0; + _comprSize = 0; + _needDeflate = false; + + _isArc = false; + _unsupported = false; + + _imgExt = NULL; + Stream.Release(); + return S_OK; +} + + +STDMETHODIMP CHandler::GetStream(UInt32 /* index */, ISequentialInStream **stream) +{ + COM_TRY_BEGIN + *stream = NULL; + + if (_unsupported) + return S_FALSE; + + if (_needDeflate) + { + if (_version <= 1) + return S_FALSE; + + if (!_bufInStream) + { + _bufInStreamSpec = new CBufInStream; + _bufInStream = _bufInStreamSpec; + } + + if (!_bufOutStream) + { + _bufOutStreamSpec = new CBufPtrSeqOutStream(); + _bufOutStream = _bufOutStreamSpec; + } + + if (!_deflateDecoder) + { + _deflateDecoderSpec = new NCompress::NDeflate::NDecoder::CCOMCoder(); + _deflateDecoder = _deflateDecoderSpec; + _deflateDecoderSpec->Set_NeedFinishInput(true); + } + + size_t clusterSize = (size_t)1 << _clusterBits; + _cache.AllocAtLeast(clusterSize); + _cacheCompressed.AllocAtLeast(clusterSize * 2); + } + + CMyComPtr<ISequentialInStream> streamTemp = this; + RINOK(InitAndSeek()); + *stream = streamTemp.Detach(); + return S_OK; + COM_TRY_END +} + + +REGISTER_ARC_I( + "QCOW", "qcow qcow2 qcow2c", NULL, 0xCA, + k_Signature, + 0, + 0, + NULL) + +}} diff --git a/CPP/7zip/Archive/Rar/RarVol.h b/CPP/7zip/Archive/Rar/RarVol.h index d0f91de6..2d2ce473 100644 --- a/CPP/7zip/Archive/Rar/RarVol.h +++ b/CPP/7zip/Archive/Rar/RarVol.h @@ -17,110 +17,110 @@ inline bool IsDigit(wchar_t c) class CVolumeName { - bool _first; - bool _newStyle; - UString _unchangedPart; - UString _changedPart; - UString _afterPart; + bool _needChangeForNext; + UString _before; + UString _changed; + UString _after; public: - CVolumeName(): _newStyle(true) {}; + CVolumeName(): _needChangeForNext(true) {}; bool InitName(const UString &name, bool newStyle = true) { - _first = true; - _newStyle = newStyle; + _needChangeForNext = true; + _after.Empty(); + UString base = name; int dotPos = name.ReverseFind_Dot(); - UString basePart = name; if (dotPos >= 0) { - UString ext = name.Ptr(dotPos + 1); + const UString ext = name.Ptr(dotPos + 1); if (ext.IsEqualTo_Ascii_NoCase("rar")) { - _afterPart = name.Ptr(dotPos); - basePart = name.Left(dotPos); + _after = name.Ptr(dotPos); + base.DeleteFrom(dotPos); } else if (ext.IsEqualTo_Ascii_NoCase("exe")) { - _afterPart.SetFromAscii(".rar"); - basePart = name.Left(dotPos); + _after.SetFromAscii(".rar"); + base.DeleteFrom(dotPos); } - else if (!_newStyle) + else if (!newStyle) { if (ext.IsEqualTo_Ascii_NoCase("000") || ext.IsEqualTo_Ascii_NoCase("001") || ext.IsEqualTo_Ascii_NoCase("r00") || ext.IsEqualTo_Ascii_NoCase("r01")) { - _afterPart.Empty(); - _first = false; - _changedPart = ext; - _unchangedPart = name.Left(dotPos + 1); + _changed = ext; + _before = name.Left(dotPos + 1); return true; } } } - if (!_newStyle) + if (newStyle) { - _afterPart.Empty(); - _unchangedPart = basePart; - _unchangedPart += L'.'; - _changedPart.SetFromAscii("r00"); - return true; - } + unsigned i = base.Len(); - if (basePart.IsEmpty()) - return false; - unsigned i = basePart.Len(); - - do - if (!IsDigit(basePart[i - 1])) - break; - while (--i); + for (; i != 0; i--) + if (!IsDigit(base[i - 1])) + break; + + if (i != base.Len()) + { + _before = base.Left(i); + _changed = base.Ptr(i); + return true; + } + } - _unchangedPart = basePart.Left(i); - _changedPart = basePart.Ptr(i); + _after.Empty(); + _before = base; + _before += L'.'; + _changed.SetFromAscii("r00"); + _needChangeForNext = false; return true; } /* void MakeBeforeFirstName() { - unsigned len = _changedPart.Len(); - _changedPart.Empty(); + unsigned len = _changed.Len(); + _changed.Empty(); for (unsigned i = 0; i < len; i++) - _changedPart += L'0'; + _changed += L'0'; } */ UString GetNextName() { - if (_newStyle || !_first) + if (_needChangeForNext) { - unsigned i = _changedPart.Len(); + unsigned i = _changed.Len(); + if (i == 0) + return UString(); for (;;) { - wchar_t c = _changedPart[--i]; + wchar_t c = _changed[--i]; if (c == L'9') { c = L'0'; - _changedPart.ReplaceOneCharAtPos(i, c); + _changed.ReplaceOneCharAtPos(i, c); if (i == 0) { - _changedPart.InsertAtFront(L'1'); + _changed.InsertAtFront(L'1'); break; } continue; } c++; - _changedPart.ReplaceOneCharAtPos(i, c); + _changed.ReplaceOneCharAtPos(i, c); break; } } - _first = false; - return _unchangedPart + _changedPart + _afterPart; + _needChangeForNext = true; + return _before + _changed + _after; } }; diff --git a/CPP/7zip/Archive/RpmHandler.cpp b/CPP/7zip/Archive/RpmHandler.cpp index 268e0837..f3ae78aa 100644 --- a/CPP/7zip/Archive/RpmHandler.cpp +++ b/CPP/7zip/Archive/RpmHandler.cpp @@ -4,21 +4,19 @@ #include "../../../C/CpuArch.h" +#include "../../Common/MyBuffer.h" #include "../../Common/ComTry.h" #include "../../Common/IntToString.h" -#include "../../Common/MyString.h" #include "../../Common/StringConvert.h" #include "../../Common/UTFConvert.h" #include "../../Windows/PropVariant.h" #include "../../Windows/TimeUtils.h" -#include "../Common/LimitedStreams.h" -#include "../Common/ProgressUtils.h" #include "../Common/RegisterArc.h" #include "../Common/StreamUtils.h" -#include "../Compress/CopyCoder.h" +#include "HandlerCont.h" // #define _SHOW_RPM_METADATA @@ -169,13 +167,8 @@ struct CEntry } }; -class CHandler: - public IInArchive, - public IInArchiveGetStream, - public CMyUnknownImp +class CHandler: public CHandlerCont { - CMyComPtr<IInStream> _stream; - UInt64 _headersSize; // is equal to start offset of payload data UInt64 _payloadSize; UInt64 _size; @@ -232,10 +225,11 @@ class CHandler: HRESULT ReadHeader(ISequentialInStream *stream, bool isMainHeader); HRESULT Open2(ISequentialInStream *stream); + + virtual UInt64 GetItemPos(UInt32) const { return _headersSize; } + virtual UInt64 GetItemSize(UInt32) const { return _size; } public: - MY_UNKNOWN_IMP2(IInArchive, IInArchiveGetStream) - INTERFACE_IInArchive(;) - STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream); + INTERFACE_IInArchive_Cont(;) }; static const Byte kArcProps[] = @@ -728,50 +722,6 @@ STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) return S_OK; } -STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, - Int32 testMode, IArchiveExtractCallback *extractCallback) -{ - COM_TRY_BEGIN - if (numItems == 0) - return S_OK; - if (numItems != (UInt32)(Int32)-1 && (numItems != 1 || indices[0] != 0)) - return E_INVALIDARG; - - RINOK(extractCallback->SetTotal(_size)); - - CMyComPtr<ISequentialOutStream> outStream; - Int32 askMode = testMode ? - NExtract::NAskMode::kTest : - NExtract::NAskMode::kExtract; - RINOK(extractCallback->GetStream(0, &outStream, askMode)); - if (!testMode && !outStream) - return S_OK; - RINOK(extractCallback->PrepareOperation(askMode)); - - NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder; - CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec; - - CLocalProgress *lps = new CLocalProgress; - CMyComPtr<ICompressProgressInfo> progress = lps; - lps->Init(extractCallback, false); - - RINOK(_stream->Seek(_headersSize, STREAM_SEEK_SET, NULL)); - RINOK(copyCoder->Code(_stream, outStream, NULL, &_size, progress)); - outStream.Release(); - Int32 opRes = NExtract::NOperationResult::kOK; - if (copyCoderSpec->TotalSize < _size) - opRes = NExtract::NOperationResult::kUnexpectedEnd; - return extractCallback->SetOperationResult(opRes); - COM_TRY_END -} - -STDMETHODIMP CHandler::GetStream(UInt32 /* index */, ISequentialInStream **stream) -{ - COM_TRY_BEGIN - return CreateLimitedInStream(_stream, _headersSize, _size, stream); - COM_TRY_END -} - static const Byte k_Signature[] = { 0xED, 0xAB, 0xEE, 0xDB}; REGISTER_ARC_I( diff --git a/CPP/7zip/Archive/Tar/TarRegister.cpp b/CPP/7zip/Archive/Tar/TarRegister.cpp index f7b256df..5014f04d 100644 --- a/CPP/7zip/Archive/Tar/TarRegister.cpp +++ b/CPP/7zip/Archive/Tar/TarRegister.cpp @@ -12,7 +12,7 @@ namespace NTar { static const Byte k_Signature[] = { 'u', 's', 't', 'a', 'r' }; REGISTER_ARC_IO( - "tar", "tar", 0, 0xEE, + "tar", "tar ova", 0, 0xEE, k_Signature, NFileHeader::kUstarMagic_Offset, NArcInfoFlags::kStartOpen | diff --git a/CPP/7zip/Archive/VdiHandler.cpp b/CPP/7zip/Archive/VdiHandler.cpp new file mode 100644 index 00000000..a8d3fe36 --- /dev/null +++ b/CPP/7zip/Archive/VdiHandler.cpp @@ -0,0 +1,362 @@ +// VdiHandler.cpp + +#include "StdAfx.h" + +// #include <stdio.h> + +#include "../../../C/CpuArch.h" + +#include "../../Common/ComTry.h" +#include "../../Common/IntToString.h" +#include "../../Common/MyBuffer.h" + +#include "../../Windows/PropVariant.h" + +#include "../Common/RegisterArc.h" +#include "../Common/StreamUtils.h" + +#include "HandlerCont.h" + +#define Get32(p) GetUi32(p) +#define Get64(p) GetUi64(p) + +using namespace NWindows; + +namespace NArchive { +namespace NVdi { + +#define SIGNATURE { 0x7F, 0x10, 0xDA, 0xBE } + +static const Byte k_Signature[] = SIGNATURE; + +static const unsigned k_ClusterBits = 20; +static const UInt32 k_ClusterSize = (UInt32)1 << k_ClusterBits; +static const UInt32 k_UnusedCluster = 0xFFFFFFFF; + +// static const UInt32 kDiskType_Dynamic = 1; +// static const UInt32 kDiskType_Static = 2; + +static const char * const kDiskTypes[] = +{ + "0" + , "Dynamic" + , "Static" +}; + +class CHandler: public CHandlerImg +{ + UInt32 _dataOffset; + CByteBuffer _table; + UInt64 _phySize; + UInt32 _imageType; + bool _isArc; + bool _unsupported; + + HRESULT Seek(UInt64 offset) + { + _posInArc = offset; + return Stream->Seek(offset, STREAM_SEEK_SET, NULL); + } + + HRESULT InitAndSeek() + { + _virtPos = 0; + return Seek(0); + } + + HRESULT Open2(IInStream *stream, IArchiveOpenCallback *openCallback); + +public: + INTERFACE_IInArchive_Img(;) + + STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream); + STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); +}; + + +STDMETHODIMP CHandler::Read(void *data, UInt32 size, UInt32 *processedSize) +{ + if (processedSize) + *processedSize = 0; + if (_virtPos >= _size) + return S_OK; + { + UInt64 rem = _size - _virtPos; + if (size > rem) + size = (UInt32)rem; + if (size == 0) + return S_OK; + } + + { + UInt64 cluster = _virtPos >> k_ClusterBits; + UInt32 lowBits = (UInt32)_virtPos & (k_ClusterSize - 1); + { + UInt32 rem = k_ClusterSize - lowBits; + if (size > rem) + size = rem; + } + + cluster <<= 2; + if (cluster < _table.Size()) + { + const Byte *p = (const Byte *)_table + (size_t)cluster; + UInt32 v = Get32(p); + if (v != k_UnusedCluster) + { + UInt64 offset = _dataOffset + ((UInt64)v << k_ClusterBits); + offset += lowBits; + if (offset != _posInArc) + { + RINOK(Seek(offset)); + } + HRESULT res = Stream->Read(data, size, &size); + _posInArc += size; + _virtPos += size; + if (processedSize) + *processedSize = size; + return res; + } + } + + memset(data, 0, size); + _virtPos += size; + if (processedSize) + *processedSize = size; + return S_OK; + } +} + + +static const Byte kProps[] = +{ + kpidSize, + kpidPackSize +}; + +static const Byte kArcProps[] = +{ + kpidHeadersSize, + kpidMethod +}; + +IMP_IInArchive_Props +IMP_IInArchive_ArcProps + +STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NCOM::CPropVariant prop; + + switch (propID) + { + case kpidMainSubfile: prop = (UInt32)0; break; + case kpidPhySize: if (_phySize != 0) prop = _phySize; break; + case kpidHeadersSize: prop = _dataOffset; break; + + case kpidMethod: + { + char s[16]; + const char *ptr; + if (_imageType < ARRAY_SIZE(kDiskTypes)) + ptr = kDiskTypes[_imageType]; + else + { + ConvertUInt32ToString(_imageType, s); + ptr = s; + } + prop = ptr; + break; + } + + case kpidErrorFlags: + { + UInt32 v = 0; + if (!_isArc) v |= kpv_ErrorFlags_IsNotArc;; + if (_unsupported) v |= kpv_ErrorFlags_UnsupportedMethod; + // if (_headerError) v |= kpv_ErrorFlags_HeadersError; + if (!Stream && v == 0 && _isArc) + v = kpv_ErrorFlags_HeadersError; + if (v != 0) + prop = v; + break; + } + } + + prop.Detach(value); + return S_OK; + COM_TRY_END +} + + +STDMETHODIMP CHandler::GetProperty(UInt32 /* index */, PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NCOM::CPropVariant prop; + + switch (propID) + { + case kpidSize: prop = _size; break; + case kpidPackSize: prop = _phySize - _dataOffset; break; + case kpidExtension: prop = (_imgExt ? _imgExt : "img"); break; + } + + prop.Detach(value); + return S_OK; + COM_TRY_END +} + + +static bool IsEmptyGuid(const Byte *data) +{ + for (unsigned i = 0; i < 16; i++) + if (data[i] != 0) + return false; + return true; +} + + +HRESULT CHandler::Open2(IInStream *stream, IArchiveOpenCallback * /* openCallback */) +{ + const unsigned kHeaderSize = 512; + Byte buf[kHeaderSize]; + RINOK(ReadStream_FALSE(stream, buf, kHeaderSize)); + + if (memcmp(buf + 0x40, k_Signature, sizeof(k_Signature)) != 0) + return S_FALSE; + + UInt32 version = Get32(buf + 0x44); + if (version >= 0x20000) + return S_FALSE; + + UInt32 headerSize = Get32(buf + 0x48); + if (headerSize < 0x140 || headerSize > 0x1B8) + return S_FALSE; + + _imageType = Get32(buf + 0x4C); + _dataOffset = Get32(buf + 0x158); + + UInt32 tableOffset = Get32(buf + 0x154); + if (tableOffset < 0x200) + return S_FALSE; + + UInt32 sectorSize = Get32(buf + 0x168); + if (sectorSize != 0x200) + return S_FALSE; + + _size = Get64(buf + 0x170); + _isArc = true; + + if (_imageType > 2) + { + _unsupported = true; + return S_FALSE; + } + + if (_dataOffset < tableOffset) + return S_FALSE; + + UInt32 blockSize = Get32(buf + 0x178); + if (blockSize != ((UInt32)1 << k_ClusterBits)) + { + _unsupported = true; + return S_FALSE; + } + + UInt32 totalBlocks = Get32(buf + 0x180); + + { + UInt64 size2 = (UInt64)totalBlocks << k_ClusterBits; + if (size2 < _size) + { + _unsupported = true; + return S_FALSE; + } + /* + if (size2 > _size) + _size = size2; + */ + } + + if (headerSize >= 0x180) + { + if (!IsEmptyGuid(buf + 0x1A8) || + !IsEmptyGuid(buf + 0x1B8)) + { + _unsupported = true; + return S_FALSE; + } + } + + UInt32 numAllocatedBlocks = Get32(buf + 0x184); + + { + UInt32 tableReserved = _dataOffset - tableOffset; + if ((tableReserved >> 2) < totalBlocks) + return S_FALSE; + } + + _phySize = _dataOffset + ((UInt64)numAllocatedBlocks << k_ClusterBits); + + size_t numBytes = (size_t)totalBlocks * 4; + if ((numBytes >> 2) != totalBlocks) + { + _unsupported = true; + return S_FALSE; + } + + _table.Alloc(numBytes); + RINOK(stream->Seek(tableOffset, STREAM_SEEK_SET, NULL)); + RINOK(ReadStream_FALSE(stream, _table, numBytes)); + + const Byte *data = _table; + for (UInt32 i = 0; i < totalBlocks; i++) + { + UInt32 v = Get32(data + (size_t)i * 4); + if (v == k_UnusedCluster) + continue; + if (v >= numAllocatedBlocks) + return S_FALSE; + } + + Stream = stream; + return S_OK; +} + + +STDMETHODIMP CHandler::Close() +{ + _table.Free(); + _phySize = 0; + _size = 0; + _isArc = false; + _unsupported = false; + + _imgExt = NULL; + Stream.Release(); + return S_OK; +} + + +STDMETHODIMP CHandler::GetStream(UInt32 /* index */, ISequentialInStream **stream) +{ + COM_TRY_BEGIN + *stream = NULL; + if (_unsupported) + return S_FALSE; + CMyComPtr<ISequentialInStream> streamTemp = this; + RINOK(InitAndSeek()); + *stream = streamTemp.Detach(); + return S_OK; + COM_TRY_END +} + + +REGISTER_ARC_I( + "VDI", "vdi", NULL, 0xC9, + k_Signature, + 0x40, + 0, + NULL) + +}} diff --git a/CPP/7zip/Archive/VhdHandler.cpp b/CPP/7zip/Archive/VhdHandler.cpp index 7508adfa..d7c3ca95 100644 --- a/CPP/7zip/Archive/VhdHandler.cpp +++ b/CPP/7zip/Archive/VhdHandler.cpp @@ -10,11 +10,10 @@ #include "../../Windows/PropVariant.h" #include "../Common/LimitedStreams.h" -#include "../Common/ProgressUtils.h" #include "../Common/RegisterArc.h" #include "../Common/StreamUtils.h" -#include "../Compress/CopyCoder.h" +#include "HandlerCont.h" #define Get16(p) GetBe16(p) #define Get32(p) GetBe32(p) @@ -217,14 +216,8 @@ bool CDynHeader::Parse(const Byte *p) return CheckBlock(p, 1024, 0x24, 0x240 + 8 * 24); } -class CHandler: - public IInStream, - public IInArchive, - public IInArchiveGetStream, - public CMyUnknownImp +class CHandler: public CHandlerImg { - UInt64 _virtPos; - UInt64 _posInArc; UInt64 _posInArcLimit; UInt64 _startOffset; UInt64 _phySize; @@ -235,7 +228,7 @@ class CHandler: CByteBuffer BitMap; UInt32 BitMapTag; UInt32 NumUsedBlocks; - CMyComPtr<IInStream> Stream; + // CMyComPtr<IInStream> Stream; CMyComPtr<IInStream> ParentStream; CHandler *Parent; UString _errorMessage; @@ -309,14 +302,17 @@ class CHandler: HRESULT Open3(); HRESULT Open2(IInStream *stream, CHandler *child, IArchiveOpenCallback *openArchiveCallback, unsigned level); + HRESULT Open2(IInStream *stream, IArchiveOpenCallback *openArchiveCallback) + { + return Open2(stream, NULL, openArchiveCallback, 0); + } + void CloseAtError(); public: - MY_UNKNOWN_IMP3(IInArchive, IInArchiveGetStream, IInStream) + INTERFACE_IInArchive_Img(;) - INTERFACE_IInArchive(;) STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream); STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); - STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition); }; HRESULT CHandler::Seek(UInt64 offset) { return Stream->Seek(_startOffset + offset, STREAM_SEEK_SET, NULL); } @@ -360,6 +356,7 @@ HRESULT CHandler::Open3() Byte header[kHeaderSize]; RINOK(ReadStream_FALSE(Stream, header, kHeaderSize)); bool headerIsOK = Footer.Parse(header); + _size = Footer.CurrentSize; if (headerIsOK && !Footer.ThereIsDynamic()) { @@ -388,6 +385,7 @@ HRESULT CHandler::Open3() { if (!Footer.Parse(buf)) return S_FALSE; + _size = Footer.CurrentSize; if (Footer.ThereIsDynamic()) return S_FALSE; // we can't open Dynamic Archive backward. _posInArcLimit = Footer.CurrentSize; @@ -594,22 +592,6 @@ STDMETHODIMP CHandler::Read(void *data, UInt32 size, UInt32 *processedSize) return res; } -STDMETHODIMP CHandler::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition) -{ - switch (seekOrigin) - { - case STREAM_SEEK_SET: break; - case STREAM_SEEK_CUR: offset += _virtPos; break; - case STREAM_SEEK_END: offset += Footer.CurrentSize; break; - default: return STG_E_INVALIDFUNCTION; - } - if (offset < 0) - return HRESULT_WIN32_ERROR_NEGATIVE_SEEK; - _virtPos = offset; - if (newPosition) - *newPosition = offset; - return S_OK; -} enum { @@ -842,31 +824,7 @@ HRESULT CHandler::Open2(IInStream *stream, CHandler *child, IArchiveOpenCallback return S_OK; } -STDMETHODIMP CHandler::Open(IInStream *stream, - const UInt64 * /* maxCheckStartPosition */, - IArchiveOpenCallback * openArchiveCallback) -{ - COM_TRY_BEGIN - { - HRESULT res; - try - { - res = Open2(stream, NULL, openArchiveCallback, 0); - if (res == S_OK) - return S_OK; - } - catch(...) - { - Close(); - throw; - } - Close(); - return res; - } - COM_TRY_END -} - -STDMETHODIMP CHandler::Close() +void CHandler::CloseAtError() { _phySize = 0; Bat.Clear(); @@ -877,89 +835,40 @@ STDMETHODIMP CHandler::Close() Dyn.Clear(); _errorMessage.Empty(); // _unexpectedEnd = false; - return S_OK; + _imgExt = NULL; } -STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) +STDMETHODIMP CHandler::Close() { - *numItems = 1; + CloseAtError(); return S_OK; } STDMETHODIMP CHandler::GetProperty(UInt32 /* index */, PROPID propID, PROPVARIANT *value) { - // COM_TRY_BEGIN + COM_TRY_BEGIN NCOM::CPropVariant prop; - switch(propID) + switch (propID) { case kpidSize: prop = Footer.CurrentSize; break; case kpidPackSize: prop = GetPackSize(); break; case kpidCTime: VhdTimeToFileTime(Footer.CTime, prop); break; + case kpidExtension: prop = (_imgExt ? _imgExt : "img"); break; + /* case kpidNumCyls: prop = Footer.NumCyls(); break; case kpidNumHeads: prop = Footer.NumHeads(); break; case kpidSectorsPerTrack: prop = Footer.NumSectorsPerTrack(); break; */ } + prop.Detach(value); return S_OK; - // COM_TRY_END -} - -STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, - Int32 testMode, IArchiveExtractCallback *extractCallback) -{ - COM_TRY_BEGIN - if (numItems == 0) - return S_OK; - if (numItems != (UInt32)(Int32)-1 && (numItems != 1 || indices[0] != 0)) - return E_INVALIDARG; - - RINOK(extractCallback->SetTotal(Footer.CurrentSize)); - CMyComPtr<ISequentialOutStream> outStream; - Int32 askMode = testMode ? - NExtract::NAskMode::kTest : - NExtract::NAskMode::kExtract; - RINOK(extractCallback->GetStream(0, &outStream, askMode)); - if (!testMode && !outStream) - return S_OK; - RINOK(extractCallback->PrepareOperation(askMode)); - - NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder(); - CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec; - - CLocalProgress *lps = new CLocalProgress; - CMyComPtr<ICompressProgressInfo> progress = lps; - lps->Init(extractCallback, false); - - int res = NExtract::NOperationResult::kDataError; - CMyComPtr<ISequentialInStream> inStream; - HRESULT hres = GetStream(0, &inStream); - if (hres == S_FALSE) - res = NExtract::NOperationResult::kUnsupportedMethod; - else - { - RINOK(hres); - HRESULT hres = copyCoder->Code(inStream, outStream, NULL, NULL, progress); - if (hres == S_OK) - { - if (copyCoderSpec->TotalSize == Footer.CurrentSize) - res = NExtract::NOperationResult::kOK; - } - else - { - if (hres != S_FALSE) - { - RINOK(hres); - } - } - } - outStream.Release(); - return extractCallback->SetOperationResult(res); COM_TRY_END } + STDMETHODIMP CHandler::GetStream(UInt32 /* index */, ISequentialInStream **stream) { COM_TRY_BEGIN @@ -984,7 +893,7 @@ STDMETHODIMP CHandler::GetStream(UInt32 /* index */, ISequentialInStream **strea } REGISTER_ARC_I( - "VHD", "vhd", ".mbr", 0xDC, + "VHD", "vhd", NULL, 0xDC, kSignature, 0, NArcInfoFlags::kUseGlobalOffset, diff --git a/CPP/7zip/Archive/VmdkHandler.cpp b/CPP/7zip/Archive/VmdkHandler.cpp new file mode 100644 index 00000000..4b404e14 --- /dev/null +++ b/CPP/7zip/Archive/VmdkHandler.cpp @@ -0,0 +1,890 @@ +// VmdkHandler.cpp + +#include "StdAfx.h" + +// #include <stdio.h> + +#include "../../../C/CpuArch.h" + +#include "../../Common/ComTry.h" +#include "../../Common/IntToString.h" + +#include "../../Windows/PropVariant.h" + +#include "../Common/RegisterArc.h" +#include "../Common/StreamObjects.h" +#include "../Common/StreamUtils.h" + +#include "../Compress/ZlibDecoder.h" + +#include "HandlerCont.h" + +#define Get16(p) GetUi16(p) +#define Get32(p) GetUi32(p) +#define Get64(p) GetUi64(p) + +using namespace NWindows; + +namespace NArchive { +namespace NVmdk { + +#define SIGNATURE { 'K', 'D', 'M', 'V' } + +static const Byte k_Signature[] = SIGNATURE; + +static const UInt32 k_Flags_NL = (UInt32)1 << 0; +static const UInt32 k_Flags_RGD = (UInt32)1 << 1; +static const UInt32 k_Flags_ZeroGrain = (UInt32)1 << 2; +static const UInt32 k_Flags_Compressed = (UInt32)1 << 16; +static const UInt32 k_Flags_Marker = (UInt32)1 << 17; + +static const unsigned k_NumMidBits = 9; // num bits for index in Grain Table + +struct CHeader +{ + UInt32 flags; + UInt32 version; + + UInt64 capacity; + UInt64 grainSize; + UInt64 descriptorOffset; + UInt64 descriptorSize; + + UInt32 numGTEsPerGT; + UInt16 algo; + // Byte uncleanShutdown; + // UInt64 rgdOffset; + UInt64 gdOffset; + UInt64 overHead; + + bool Is_NL() const { return (flags & k_Flags_NL) != 0; }; + bool Is_ZeroGrain() const { return (flags & k_Flags_ZeroGrain) != 0; }; + bool Is_Compressed() const { return (flags & k_Flags_Compressed) != 0; }; + bool Is_Marker() const { return (flags & k_Flags_Marker) != 0; }; + + bool Parse(const Byte *buf); + + bool IsSameImageFor(const CHeader &h) const + { + return flags == h.flags + && version == h.version + && capacity == h.capacity + && grainSize == h.grainSize + && algo == h.algo; + } +}; + +bool CHeader::Parse(const Byte *buf) +{ + if (memcmp(buf, k_Signature, sizeof(k_Signature)) != 0) + return false; + + version = Get32(buf + 0x4); + flags = Get32(buf + 0x8); + capacity = Get64(buf + 0xC); + grainSize = Get64(buf + 0x14); + descriptorOffset = Get64(buf + 0x1C); + descriptorSize = Get64(buf + 0x24); + numGTEsPerGT = Get32(buf + 0x2C); + // rgdOffset = Get64(buf + 0x30); + gdOffset = Get64(buf + 0x38); + overHead = Get64(buf + 0x40); + // uncleanShutdown = buf[0x48]; + algo = Get16(buf + 0x4D); + + if (Is_NL() && Get32(buf + 0x49) != 0x0A0D200A) // do we need Is_NL() check here? + return false; + + return (numGTEsPerGT == (1 << k_NumMidBits)) && (version <= 3); +} + + +enum +{ + k_Marker_END_OF_STREAM = 0, + k_Marker_GRAIN_TABLE = 1, + k_Marker_GRAIN_DIR = 2, + k_Marker_FOOTER = 3 +}; + +struct CMarker +{ + UInt64 NumSectors; + UInt32 SpecSize; // = 0 for metadata sectors + UInt32 Type; + + void Parse(const Byte *p) + { + NumSectors = Get64(p); + SpecSize = Get32(p + 8); + Type = Get32(p + 12); + } +}; + + +struct CDescriptor +{ + AString CID; + AString parentCID; + AString createType; + + AStringVector Extents; + + void Clear() + { + CID.Empty(); + parentCID.Empty(); + createType.Empty(); + Extents.Clear(); + } + + void Parse(const Byte *p, size_t size); +}; + +static bool Str_to_ValName(const AString &s, AString &name, AString &val) +{ + name.Empty(); + val.Empty(); + int qu = s.Find('"'); + int eq = s.Find('='); + if (eq < 0 || (qu >= 0 && eq > qu)) + return false; + name = s.Left(eq); + name.Trim(); + val = s.Ptr(eq + 1); + val.Trim(); + return true; +} + +void CDescriptor::Parse(const Byte *p, size_t size) +{ + Clear(); + + AString s; + AString name; + AString val; + + for (size_t i = 0;; i++) + { + char c = p[i]; + if (i == size || c == 0 || c == 0xA || c == 0xD) + { + if (!s.IsEmpty() && s[0] != '#') + { + if (Str_to_ValName(s, name, val)) + { + if (name.IsEqualTo_Ascii_NoCase("CID")) + CID = val; + else if (name.IsEqualTo_Ascii_NoCase("parentCID")) + parentCID = val; + else if (name.IsEqualTo_Ascii_NoCase("createType")) + createType = val; + } + else + Extents.Add(s); + } + s.Empty(); + if (c == 0 || i >= size) + break; + } + else + s += (char)c; + } +} + + +class CHandler: public CHandlerImg +{ + unsigned _clusterBits; + + CObjectVector<CByteBuffer> _tables; + UInt64 _cacheCluster; + CByteBuffer _cache; + CByteBuffer _cacheCompressed; + + UInt64 _phySize; + + UInt32 _zeroSector; + bool _needDeflate; + bool _isArc; + bool _unsupported; + // bool _headerError; + + CBufInStream *_bufInStreamSpec; + CMyComPtr<ISequentialInStream> _bufInStream; + + CBufPtrSeqOutStream *_bufOutStreamSpec; + CMyComPtr<ISequentialOutStream> _bufOutStream; + + NCompress::NZlib::CDecoder *_zlibDecoderSpec; + CMyComPtr<ICompressCoder> _zlibDecoder; + + CByteBuffer _descriptorBuf; + CDescriptor _descriptor; + + CHeader h; + + + HRESULT Seek(UInt64 offset) + { + _posInArc = offset; + return Stream->Seek(offset, STREAM_SEEK_SET, NULL); + } + + HRESULT InitAndSeek() + { + _virtPos = 0; + return Seek(0); + } + + HRESULT ReadForHeader(IInStream *stream, UInt64 sector, void *data, size_t numSectors); + virtual HRESULT Open2(IInStream *stream, IArchiveOpenCallback *openCallback); + +public: + INTERFACE_IInArchive_Img(;) + + STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream); + STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize); +}; + + +STDMETHODIMP CHandler::Read(void *data, UInt32 size, UInt32 *processedSize) +{ + if (processedSize) + *processedSize = 0; + if (_virtPos >= _size) + return S_OK; + { + UInt64 rem = _size - _virtPos; + if (size > rem) + size = (UInt32)rem; + if (size == 0) + return S_OK; + } + + for (;;) + { + const UInt64 cluster = _virtPos >> _clusterBits; + const size_t clusterSize = (size_t)1 << _clusterBits; + const size_t lowBits = (size_t)_virtPos & (clusterSize - 1); + { + size_t rem = clusterSize - lowBits; + if (size > rem) + size = (UInt32)rem; + } + + if (cluster == _cacheCluster) + { + memcpy(data, _cache + lowBits, size); + _virtPos += size; + if (processedSize) + *processedSize = size; + return S_OK; + } + + const UInt64 high = cluster >> k_NumMidBits; + + if (high < _tables.Size()) + { + const CByteBuffer &table = _tables[(unsigned)high]; + + if (table.Size() != 0) + { + const size_t midBits = (size_t)cluster & ((1 << k_NumMidBits) - 1); + const Byte *p = (const Byte *)table + (midBits << 2); + const UInt32 v = Get32(p); + + if (v != 0 && v != _zeroSector) + { + UInt64 offset = (UInt64)v << 9; + if (_needDeflate) + { + if (offset != _posInArc) + { + // printf("\n%12x %12x\n", (unsigned)offset, (unsigned)(offset - _posInArc)); + RINOK(Seek(offset)); + } + + const size_t kStartSize = 1 << 9; + { + size_t curSize = kStartSize; + HRESULT res = ReadStream(Stream, _cacheCompressed, &curSize); + _posInArc += curSize; + RINOK(res); + if (curSize != kStartSize) + return S_FALSE; + } + + if (Get64(_cacheCompressed) != (cluster << (_clusterBits - 9))) + return S_FALSE; + + UInt32 dataSize = Get32(_cacheCompressed + 8); + if (dataSize > ((UInt32)1 << 31)) + return S_FALSE; + + size_t dataSize2 = (size_t)dataSize + 12; + + if (dataSize2 > kStartSize) + { + dataSize2 = (dataSize2 + 511) & ~(size_t)511; + if (dataSize2 > _cacheCompressed.Size()) + return S_FALSE; + size_t curSize = dataSize2 - kStartSize; + const size_t curSize2 = curSize; + HRESULT res = ReadStream(Stream, _cacheCompressed + kStartSize, &curSize); + _posInArc += curSize; + RINOK(res); + if (curSize != curSize2) + return S_FALSE; + } + + _bufInStreamSpec->Init(_cacheCompressed + 12, dataSize); + + _cacheCluster = (UInt64)(Int64)-1; + if (_cache.Size() < clusterSize) + return E_FAIL; + _bufOutStreamSpec->Init(_cache, clusterSize); + + // Do we need to use smaller block than clusterSize for last cluster? + UInt64 blockSize64 = clusterSize; + HRESULT res = _zlibDecoderSpec->Code(_bufInStream, _bufOutStream, NULL, &blockSize64, NULL); + + // if (_bufOutStreamSpec->GetPos() != clusterSize) + // memset(_cache + _bufOutStreamSpec->GetPos(), 0, clusterSize - _bufOutStreamSpec->GetPos()); + + if (res == S_OK) + if (_bufOutStreamSpec->GetPos() != clusterSize + || _zlibDecoderSpec->GetInputProcessedSize() != dataSize) + res = S_FALSE; + + RINOK(res); + _cacheCluster = cluster; + + continue; + /* + memcpy(data, _cache + lowBits, size); + _virtPos += size; + if (processedSize) + *processedSize = size; + return S_OK; + */ + } + { + offset += lowBits; + if (offset != _posInArc) + { + // printf("\n%12x %12x\n", (unsigned)offset, (unsigned)(offset - _posInArc)); + RINOK(Seek(offset)); + } + HRESULT res = Stream->Read(data, size, &size); + _posInArc += size; + _virtPos += size; + if (processedSize) + *processedSize = size; + return res; + } + } + } + } + + memset(data, 0, size); + _virtPos += size; + if (processedSize) + *processedSize = size; + return S_OK; + } +} + + +static const Byte kProps[] = +{ + kpidSize, + kpidPackSize +}; + +static const Byte kArcProps[] = +{ + kpidClusterSize, + kpidHeadersSize, + kpidMethod, + kpidId, + kpidName, + kpidComment +}; + +IMP_IInArchive_Props +IMP_IInArchive_ArcProps + + +STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NCOM::CPropVariant prop; + + switch (propID) + { + case kpidMainSubfile: prop = (UInt32)0; break; + case kpidPhySize: if (_phySize != 0) prop = _phySize; break; + case kpidClusterSize: prop = (UInt32)1 << _clusterBits; break; + case kpidHeadersSize: prop = (h.overHead << 9); break; + case kpidMethod: + { + AString s; + + if (!_descriptor.createType.IsEmpty()) + s = _descriptor.createType; + + if (h.algo != 0) + { + s.Add_Space_if_NotEmpty(); + if (h.algo == 1) + s += "zlib"; + else + { + char temp[16]; + ConvertUInt32ToString(h.algo, temp); + s += temp; + } + } + + if (h.Is_Marker()) + { + s.Add_Space_if_NotEmpty(); + s += "Marker"; + } + + if (!s.IsEmpty()) + prop = s; + break; + } + + case kpidComment: + { + if (_descriptorBuf.Size() != 0) + { + AString s; + s.SetFrom_CalcLen((const char *)(const Byte *)_descriptorBuf, (unsigned)_descriptorBuf.Size()); + if (!s.IsEmpty() && s.Len() <= (1 << 16)) + prop = s; + } + break; + } + + case kpidId: + if (!_descriptor.CID.IsEmpty()) + { + prop = _descriptor.CID; + break; + } + + case kpidName: + { + if (_descriptor.Extents.Size() == 1) + { + const AString &s = _descriptor.Extents[0]; + if (!s.IsEmpty()) + { + if (s.Back() == '"') + { + AString s2 = s; + s2.DeleteBack(); + if (s2.Len() > 5 && StringsAreEqualNoCase_Ascii(s2.RightPtr(5), ".vmdk")) + { + int pos = s2.ReverseFind('"'); + if (pos >= 0) + { + s2.DeleteFrontal(pos + 1); + prop = s2; + } + } + } + } + } + break; + } + + case kpidErrorFlags: + { + UInt32 v = 0; + if (!_isArc) v |= kpv_ErrorFlags_IsNotArc;; + if (_unsupported) v |= kpv_ErrorFlags_UnsupportedMethod; + // if (_headerError) v |= kpv_ErrorFlags_HeadersError; + if (!Stream && v == 0 && _isArc) + v = kpv_ErrorFlags_HeadersError; + if (v != 0) + prop = v; + break; + } + } + + prop.Detach(value); + return S_OK; + COM_TRY_END +} + + +STDMETHODIMP CHandler::GetProperty(UInt32 /* index */, PROPID propID, PROPVARIANT *value) +{ + COM_TRY_BEGIN + NCOM::CPropVariant prop; + + switch (propID) + { + case kpidSize: prop = _size; break; + case kpidPackSize: + { + UInt64 ov = (h.overHead << 9); + if (_phySize >= ov) + prop = _phySize - ov; + break; + } + case kpidExtension: prop = (_imgExt ? _imgExt : "img"); break; + } + + prop.Detach(value); + return S_OK; + COM_TRY_END +} + + +static int inline GetLog(UInt64 num) +{ + for (int i = 0; i < 64; i++) + if (((UInt64)1 << i) == num) + return i; + return -1; +} + + +HRESULT CHandler::ReadForHeader(IInStream *stream, UInt64 sector, void *data, size_t numSectors) +{ + sector <<= 9; + RINOK(stream->Seek(sector, STREAM_SEEK_SET, NULL)); + size_t size = numSectors << 9; + RINOK(ReadStream_FALSE(stream, data, size)); + UInt64 end = sector + size; + if (_phySize < end) + _phySize = end; + return S_OK; +} + + +HRESULT CHandler::Open2(IInStream *stream, IArchiveOpenCallback *openCallback) +{ + const unsigned kSectoreSize = 512; + Byte buf[kSectoreSize]; + size_t headerSize = kSectoreSize; + RINOK(ReadStream(stream, buf, &headerSize)); + + if (headerSize < sizeof(k_Signature)) + return S_FALSE; + if (memcmp(buf, k_Signature, sizeof(k_Signature)) != 0) + { + const char *kSignature_Descriptor = "# Disk DescriptorFile"; + size_t k_SigDesc_Size = strlen(kSignature_Descriptor); + if (headerSize >= k_SigDesc_Size) + if (memcmp(buf, kSignature_Descriptor, k_SigDesc_Size) == 0) + { + _unsupported = true; + _isArc = true; + // return E_NOTIMPL; + } + return S_FALSE; + } + + if (headerSize != kSectoreSize) + return S_FALSE; + + // CHeader h; + + if (!h.Parse(buf)) + return S_FALSE; + + if (h.descriptorSize != 0) + { + if (h.descriptorOffset < 1) + return S_FALSE; + if (h.descriptorSize > (1 << 20)) + return S_FALSE; + size_t numBytes = (size_t)h.descriptorSize << 9; + _descriptorBuf.Alloc(numBytes); + RINOK(ReadForHeader(stream, h.descriptorOffset, _descriptorBuf, (size_t)h.descriptorSize)); + if (h.descriptorOffset == 1 && h.Is_Marker() && Get64(_descriptorBuf) == 0) + { + // We check data as end marker. + // and if probably it's footer's copy of header, we don't want to open it. + return S_FALSE; + } + } + + if (h.gdOffset == (UInt64)(Int64)-1) + { + // Grain Dir is at end of file + UInt64 endPos; + RINOK(stream->Seek(0, STREAM_SEEK_END, &endPos)); + if ((endPos & 511) != 0) + return S_FALSE; + + const size_t kEndSize = 512 * 3; + Byte buf2[kEndSize]; + if (endPos < kEndSize) + return S_FALSE; + RINOK(stream->Seek(endPos - kEndSize, STREAM_SEEK_SET, NULL)); + RINOK(ReadStream_FALSE(stream, buf2, kEndSize)); + + CHeader h2; + if (!h2.Parse(buf2 + 512)) + return S_FALSE; + if (!h.IsSameImageFor(h2)) + return S_FALSE; + + h = h2; + + CMarker m; + m.Parse(buf2); + if (m.NumSectors != 1 || m.SpecSize != 0 || m.Type != k_Marker_FOOTER) + return S_FALSE; + m.Parse(buf2 + 512 * 2); + if (m.NumSectors != 0 || m.SpecSize != 0 || m.Type != k_Marker_END_OF_STREAM) + return S_FALSE; + _phySize = endPos; + } + + int grainSize_Log = GetLog(h.grainSize); + if (grainSize_Log < 3 || grainSize_Log > 30 - 9) // grain size must be >= 4 KB + return S_FALSE; + if (h.capacity >= ((UInt64)1 << (63 - 9))) + return S_FALSE; + if (h.overHead >= ((UInt64)1 << (63 - 9))) + return S_FALSE; + + _isArc = true; + _clusterBits = (9 + grainSize_Log); + _size = h.capacity << 9; + _needDeflate = (h.algo >= 1); + + if (h.Is_Compressed() ? (h.algo > 1 || !h.Is_Marker()) : (h.algo != 0)) + { + _unsupported = true; + _phySize = 0; + return S_FALSE; + } + + { + UInt64 overHeadBytes = h.overHead << 9; + if (_phySize < overHeadBytes) + _phySize = overHeadBytes; + } + + _zeroSector = 0; + if (h.Is_ZeroGrain()) + _zeroSector = 1; + + const UInt64 numSectorsPerGde = (UInt64)1 << (grainSize_Log + k_NumMidBits); + const UInt64 numGdeEntries = (h.capacity + numSectorsPerGde - 1) >> (grainSize_Log + k_NumMidBits); + CByteBuffer table; + + if (numGdeEntries != 0) + { + if (h.gdOffset == 0) + return S_FALSE; + + size_t numSectors = (size_t)((numGdeEntries + ((1 << (9 - 2)) - 1)) >> (9 - 2)); + size_t t1SizeBytes = numSectors << 9; + if ((t1SizeBytes >> 2) < numGdeEntries) + return S_FALSE; + table.Alloc(t1SizeBytes); + + if (h.Is_Marker()) + { + Byte buf2[1 << 9]; + if (ReadForHeader(stream, h.gdOffset - 1, buf2, 1) != S_OK) + return S_FALSE; + { + CMarker m; + m.Parse(buf2); + if (m.Type != k_Marker_GRAIN_DIR + || m.NumSectors != numSectors + || m.SpecSize != 0) + return S_FALSE; + } + } + + RINOK(ReadForHeader(stream, h.gdOffset, table, numSectors)); + } + + size_t clusterSize = (size_t)1 << _clusterBits; + + if (openCallback) + { + UInt64 totalBytes = (UInt64)numGdeEntries << (k_NumMidBits + 2); + RINOK(openCallback->SetTotal(NULL, &totalBytes)); + } + + UInt64 lastSector = 0; + UInt64 lastVirtCluster = 0; + size_t numProcessed_Prev = 0; + + for (size_t i = 0; i < numGdeEntries; i++) + { + UInt32 v = Get32((const Byte *)table + (size_t)i * 4); + CByteBuffer &buf = _tables.AddNew(); + if (v == 0 || v == _zeroSector) + continue; + + if (openCallback && ((i - numProcessed_Prev) & 0xFFF) == 0) + { + UInt64 numBytes = (UInt64)i << (k_NumMidBits + 2); + RINOK(openCallback->SetCompleted(NULL, &numBytes)); + numProcessed_Prev = i; + } + + const size_t k_NumSectors = (size_t)1 << (k_NumMidBits - 9 + 2); + + if (h.Is_Marker()) + { + Byte buf2[1 << 9]; + if (ReadForHeader(stream, v - 1, buf2, 1) != S_OK) + return S_FALSE; + { + CMarker m; + m.Parse(buf2); + if (m.Type != k_Marker_GRAIN_TABLE + || m.NumSectors != k_NumSectors + || m.SpecSize != 0) + return S_FALSE; + } + } + + const size_t k_NumMidItems = (size_t)1 << k_NumMidBits; + + buf.Alloc(k_NumMidItems * 4); + RINOK(ReadForHeader(stream, v, buf, k_NumSectors)); + + for (size_t k = 0; k < k_NumMidItems; k++) + { + UInt32 v = Get32((const Byte *)buf + (size_t)k * 4); + if (v == 0 || v == _zeroSector) + continue; + if (v < h.overHead) + return S_FALSE; + if (lastSector < v) + { + lastSector = v; + if (_needDeflate) + lastVirtCluster = ((UInt64)i << k_NumMidBits) + k; + } + } + } + + + if (!_needDeflate) + { + UInt64 end = ((UInt64)lastSector << 9) + clusterSize; + if (_phySize < end) + _phySize = end; + } + else if (lastSector != 0) + { + Byte buf[1 << 9]; + if (ReadForHeader(stream, lastSector, buf, 1) == S_OK) + { + UInt64 lba = Get64(buf); + if (lba == (lastVirtCluster << (_clusterBits - 9))) + { + UInt32 dataSize = Get32(buf + 8); + size_t dataSize2 = (size_t)dataSize + 12; + dataSize2 = (dataSize2 + 511) & ~(size_t)511; + UInt64 end = ((UInt64)lastSector << 9) + dataSize2; + if (_phySize < end) + _phySize = end; + } + } + } + + if (_descriptorBuf.Size() != 0) + { + _descriptor.Parse(_descriptorBuf, _descriptorBuf.Size()); + if (!_descriptor.parentCID.IsEmpty()) + if (!_descriptor.parentCID.IsEqualTo_Ascii_NoCase("ffffffff")) + _unsupported = true; + } + + Stream = stream; + return S_OK; +} + + +STDMETHODIMP CHandler::Close() +{ + _phySize = 0; + _size = 0; + _cacheCluster = (UInt64)(Int64)-1; + _zeroSector = 0; + _clusterBits = 0; + + _needDeflate = false; + _isArc = false; + _unsupported = false; + // _headerError = false; + + _tables.Clear(); + _descriptorBuf.Free(); + _descriptor.Clear(); + + _imgExt = NULL; + Stream.Release(); + return S_OK; +} + + +STDMETHODIMP CHandler::GetStream(UInt32 /* index */, ISequentialInStream **stream) +{ + COM_TRY_BEGIN + *stream = 0; + + if (_unsupported) + return S_FALSE; + + + if (_needDeflate) + { + if (!_bufInStream) + { + _bufInStreamSpec = new CBufInStream; + _bufInStream = _bufInStreamSpec; + } + + if (!_bufOutStream) + { + _bufOutStreamSpec = new CBufPtrSeqOutStream(); + _bufOutStream = _bufOutStreamSpec; + } + + if (!_zlibDecoder) + { + _zlibDecoderSpec = new NCompress::NZlib::CDecoder; + _zlibDecoder = _zlibDecoderSpec; + } + + size_t clusterSize = (size_t)1 << _clusterBits; + _cache.AllocAtLeast(clusterSize); + _cacheCompressed.AllocAtLeast(clusterSize * 2); + } + + CMyComPtr<ISequentialInStream> streamTemp = this; + RINOK(InitAndSeek()); + *stream = streamTemp.Detach(); + return S_OK; + COM_TRY_END +} + + +REGISTER_ARC_I( + "VMDK", "vmdk", NULL, 0xC8, + k_Signature, + 0, + 0, + NULL) + +}} diff --git a/CPP/7zip/Archive/Wim/WimHandler.cpp b/CPP/7zip/Archive/Wim/WimHandler.cpp index bca551a6..0c635eae 100644 --- a/CPP/7zip/Archive/Wim/WimHandler.cpp +++ b/CPP/7zip/Archive/Wim/WimHandler.cpp @@ -22,6 +22,8 @@ using namespace NWindows; namespace NArchive { namespace NWim { +#define FILES_DIR_NAME "[DELETED]" + // #define WIM_DETAILS static const Byte kProps[] = @@ -35,6 +37,7 @@ static const Byte kProps[] = kpidATime, kpidAttrib, kpidMethod, + kpidSolid, kpidShortName, kpidINode, kpidLinks, @@ -58,6 +61,7 @@ static const STATPROPSTG kArcProps[] = { NULL, kpidSize, VT_UI8}, { NULL, kpidPackSize, VT_UI8}, { NULL, kpidMethod, VT_BSTR}, + { NULL, kpidClusterSize, VT_UI4}, { NULL, kpidCTime, VT_FILETIME}, { NULL, kpidMTime, VT_FILETIME}, { NULL, kpidComment, VT_BSTR}, @@ -69,9 +73,16 @@ static const STATPROPSTG kArcProps[] = { (LPOLESTR)L"Boot Image", kpidBootImage, VT_UI4} }; -static const char *kMethodLZX = "LZX"; -static const char *kMethodXpress = "XPress"; -static const char *kMethodCopy = "Copy"; + +static const char * const k_Methods[] = +{ + "Copy" + , "XPress" + , "LZX" + , "LZMS" +}; + + IMP_IInArchive_Props IMP_IInArchive_ArcProps_WITH_NAME @@ -199,6 +210,18 @@ STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) } break; case kpidNumVolumes: if (_volumes.Size() > 0) prop = (UInt32)(_volumes.Size() - 1); break; + + case kpidClusterSize: + if (_xmls.Size() > 0) + { + UInt16 volIndex = _xmls[0].VolIndex; + if (volIndex < _volumes.Size()) + { + const CHeader &h = _volumes[volIndex].Header; + prop = (UInt32)1 << h.ChunkSizeBits; + } + } + break; case kpidName: if (_firstVolumeIndex >= 0) @@ -252,36 +275,61 @@ STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) case kpidNumImages: prop = (UInt32)_db.Images.Size(); break; case kpidBootImage: if (_bootIndex != 0) prop = (UInt32)_bootIndex; break; + case kpidMethod: { - bool lzx = false, xpress = false, copy = false; - FOR_VECTOR (i, _xmls) + UInt32 methodUnknown = 0; + UInt32 methodMask = 0; + unsigned chunkSizeBits = 0; + { - const CHeader &header = _volumes[_xmls[i].VolIndex].Header; - if (header.IsCompressed()) - if (header.IsLzxMode()) - lzx = true; + FOR_VECTOR (i, _xmls) + { + const CHeader &header = _volumes[_xmls[i].VolIndex].Header; + unsigned method = header.GetMethod(); + if (method < ARRAY_SIZE(k_Methods)) + methodMask |= ((UInt32)1 << method); else - xpress = true; - else - copy = true; + methodUnknown = method; + if (chunkSizeBits < header.ChunkSizeBits) + chunkSizeBits = header.ChunkSizeBits; + } } + AString res; - if (lzx) - res = kMethodLZX; - if (xpress) + + bool numMethods = 0; + for (unsigned i = 0; i < ARRAY_SIZE(k_Methods); i++) { - res.Add_Space_if_NotEmpty(); - res += kMethodXpress; + if (methodMask & ((UInt32)1 << i)) + { + res.Add_Space_if_NotEmpty(); + res += k_Methods[i]; + numMethods++; + } } - if (copy) + + if (methodUnknown != 0) { + char temp[32]; + ConvertUInt32ToString(methodUnknown, temp); res.Add_Space_if_NotEmpty(); - res += kMethodCopy; + res += temp; + numMethods++; + } + + if (numMethods == 1 && chunkSizeBits != 0) + { + char temp[32]; + temp[0] = ':'; + ConvertUInt32ToString((UInt32)chunkSizeBits, temp + 1); + res += temp; } + prop = res; break; } + case kpidIsTree: prop = true; break; case kpidIsAltStream: prop = _db.ThereAreAltStreams; break; case kpidIsAux: prop = true; break; @@ -293,8 +341,8 @@ STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) { UInt32 flags = 0; if (!_isArc) flags |= kpv_ErrorFlags_IsNotArc; - // if (HeadersError) flags |= kpv_ErrorFlags_HeadersError; - // if (UnexpectedEnd) flags |= kpv_ErrorFlags_UnexpectedEndOfArc; + if (_db.HeadersError) flags |= kpv_ErrorFlags_HeadersError; + if (_unsupported) flags |= kpv_ErrorFlags_UnsupportedMethod; prop = flags; break; } @@ -313,23 +361,13 @@ STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) case kpidReadOnly: { - bool readOnly = false; - if (ThereIsError()) - readOnly = true; - else if (_volumes.Size() != 0) - { - if (_version != kWimVersion - || _volumes.Size() != 2 - || _volumes[0].Stream - // || _db.Images.Size() > kNumImagesMax - ) - readOnly = true; - } + bool readOnly = !IsUpdateSupported(); if (readOnly) prop = readOnly; break; } } + prop.Detach(value); return S_OK; COM_TRY_END @@ -342,7 +380,29 @@ void GetFileTime(const Byte *p, NCOM::CPropVariant &prop) prop.filetime.dwHighDateTime = Get32(p + 4); } -#define FILES_DIR_NAME "[Files]" + +static void MethodToProp(int method, int chunksSizeBits, NCOM::CPropVariant &prop) +{ + if (method >= 0) + { + char temp[32]; + + if ((unsigned)method < ARRAY_SIZE(k_Methods)) + strcpy(temp, k_Methods[method]); + else + ConvertUInt32ToString((unsigned)method, temp); + + if (chunksSizeBits >= 0) + { + size_t pos = strlen(temp); + temp[pos++] = ':'; + ConvertUInt32ToString((unsigned)chunksSizeBits, temp + pos); + } + + prop = temp; + } +} + STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value) { @@ -413,8 +473,59 @@ STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *val _db.GetShortName(realIndex, prop); break; - case kpidPackSize: prop = (UInt64)(si ? si->Resource.PackSize : 0); break; - case kpidSize: prop = (UInt64)(si ? si->Resource.UnpackSize : 0); break; + case kpidPackSize: + { + UInt64 size = 0; + if (si) + { + if (!si->Resource.IsSolidSmall()) + { + size = si->Resource.PackSize; + prop = size; + } + else + { + if (si->Resource.SolidIndex >= 0) + { + const CSolid &ss = _db.Solids[(unsigned)si->Resource.SolidIndex]; + if (ss.FirstSmallStream == item.StreamIndex) + prop = _db.DataStreams[ss.StreamIndex].Resource.PackSize; + } + } + } + break; + } + + case kpidSize: + { + UInt64 size = 0; + if (si) + { + if (si->Resource.IsSolid()) + { + if (si->Resource.IsSolidBig()) + { + if (si->Resource.SolidIndex >= 0) + { + CSolid &ss = _db.Solids[(unsigned)si->Resource.SolidIndex]; + prop = ss.UnpackSize; + } + } + else + { + size = si->Resource.PackSize; + prop = size; + } + } + else + { + size = si->Resource.UnpackSize; + prop = size; + } + } + break; + } + case kpidIsDir: prop = item.IsDir; break; case kpidIsAltStream: prop = item.IsAltStream; break; case kpidNumAltStreams: @@ -467,8 +578,33 @@ STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *val prop = (UInt32)item.StreamIndex; break; - case kpidMethod: if (si) prop = si->Resource.IsCompressed() ? - (vol->Header.IsLzxMode() ? kMethodLZX : kMethodXpress) : kMethodCopy; break; + case kpidMethod: + if (si) + { + const CResource &r = si->Resource; + if (r.IsSolid()) + { + if (r.SolidIndex >= 0) + { + CSolid &ss = _db.Solids[r.SolidIndex]; + MethodToProp(ss.Method, ss.ChunkSizeBits, prop); + } + } + else + { + int method = 0; + int chunkSizeBits = -1; + if (r.IsCompressed()) + { + method = vol->Header.GetMethod(); + chunkSizeBits = vol->Header.ChunkSizeBits; + } + MethodToProp(method, chunkSizeBits, prop); + } + } + break; + + case kpidSolid: if (si) prop = si->Resource.IsSolid(); break; case kpidLinks: if (si) prop = (UInt32)si->RefCount; break; #ifdef WIM_DETAILS case kpidVolume: if (si) prop = (UInt32)si->PartNumber; break; @@ -488,7 +624,7 @@ STDMETHODIMP CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *val case kpidIsDir: prop = false; break; case kpidPackSize: case kpidSize: prop = (UInt64)_xmls[index].Data.Size(); break; - case kpidMethod: prop = kMethodCopy; break; + case kpidMethod: /* prop = k_Method_Copy; */ break; } } else @@ -629,14 +765,6 @@ STDMETHODIMP CHandler::GetParent(UInt32 index, UInt32 *parent, UInt32 *parentTyp return S_OK; } -static bool IsEmptySha(const Byte *data) -{ - for (int i = 0; i < kHashSize; i++) - if (data[i] != 0) - return false; - return true; -} - STDMETHODIMP CHandler::GetRawProp(UInt32 index, PROPID propID, const void **data, UInt32 *dataSize, UInt32 *propType) { *data = NULL; @@ -773,6 +901,7 @@ STDMETHODIMP CHandler::Open(IInStream *inStream, const UInt64 *, IArchiveOpenCal for (UInt32 i = 1; i <= numVolumes; i++) { CMyComPtr<IInStream> curStream; + if (i == 1) curStream = inStream; else @@ -786,14 +915,17 @@ STDMETHODIMP CHandler::Open(IInStream *inStream, const UInt64 *, IArchiveOpenCal if (!curStream) break; } + CHeader header; HRESULT res = NWim::ReadHeader(curStream, header, _phySize); + if (res != S_OK) { if (i != 1 && res == S_FALSE) continue; return res; } + _isArc = true; _bootIndex = header.BootIndex; _version = header.Version; @@ -806,17 +938,25 @@ STDMETHODIMP CHandler::Open(IInStream *inStream, const UInt64 *, IArchiveOpenCal CWimXml xml; xml.VolIndex = header.PartNumber; res = _db.OpenXml(curStream, header, xml.Data); + if (res == S_OK) { if (!xml.Parse()) _xmlError = true; + if (xml.IsEncrypted) + { + _unsupported = true; + return S_FALSE; + } + UInt64 totalFiles = xml.GetTotalFilesAndDirs() + xml.Images.Size(); totalFiles += 16 + xml.Images.Size() * 4; // we reserve some additional items if (totalFiles >= ((UInt32)1 << 30)) totalFiles = 0; res = _db.Open(curStream, header, (unsigned)totalFiles, callback); } + if (res != S_OK) { if (i != 1 && res == S_FALSE) @@ -859,7 +999,7 @@ STDMETHODIMP CHandler::Open(IInStream *inStream, const UInt64 *, IArchiveOpenCal } } - RINOK(_db.FillAndCheck()); + RINOK(_db.FillAndCheck(_volumes)); int defaultImageIndex = (int)_defaultImageNumber - 1; bool showImageNumber = (_db.Images.Size() != 1 && defaultImageIndex < 0); @@ -901,9 +1041,11 @@ STDMETHODIMP CHandler::Close() _numIgnoreItems = 0; _xmlError = false; _isArc = false; + _unsupported = false; return S_OK; } + STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, Int32 testMode, IArchiveExtractCallback *extractCallback) { @@ -917,6 +1059,7 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, UInt32 i; UInt64 totalSize = 0; + for (i = 0; i < numItems; i++) { UInt32 index = allFilesMode ? i : indices[i]; @@ -926,7 +1069,7 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, if (streamIndex >= 0) { const CStreamInfo &si = _db.DataStreams[streamIndex]; - totalSize += si.Resource.UnpackSize; + totalSize += _db.Get_UnpackSize_of_Resource(si.Resource); } } else @@ -939,9 +1082,8 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, RINOK(extractCallback->SetTotal(totalSize)); - UInt64 currentTotalPacked = 0; UInt64 currentTotalUnPacked = 0; - UInt64 currentItemUnPacked, currentItemPacked; + UInt64 currentItemUnPacked; int prevSuccessStreamIndex = -1; @@ -951,13 +1093,12 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, CMyComPtr<ICompressProgressInfo> progress = lps; lps->Init(extractCallback, false); - for (i = 0; i < numItems; currentTotalUnPacked += currentItemUnPacked, - currentTotalPacked += currentItemPacked) + for (i = 0; i < numItems; + currentTotalUnPacked += currentItemUnPacked) { currentItemUnPacked = 0; - currentItemPacked = 0; - lps->InSize = currentTotalPacked; + lps->InSize = unpacker.TotalPacked; lps->OutSize = currentTotalUnPacked; RINOK(lps->SetCur()); @@ -1005,8 +1146,8 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, } const CStreamInfo &si = _db.DataStreams[streamIndex]; - currentItemUnPacked = si.Resource.UnpackSize; - currentItemPacked = si.Resource.PackSize; + currentItemUnPacked = _db.Get_UnpackSize_of_Resource(si.Resource); + // currentItemPacked = _db.Get_PackSize_of_Resource(streamIndex); if (!testMode && !realOutStream) continue; @@ -1016,17 +1157,20 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, { Byte digest[kHashSize]; const CVolume &vol = _volumes[si.PartNumber]; - HRESULT res = unpacker.Unpack(vol.Stream, si.Resource, vol.Header.IsLzxMode(), - realOutStream, progress, digest); + bool needDigest = !si.IsEmptyHash(); + HRESULT res = unpacker.Unpack(vol.Stream, si.Resource, vol.Header, &_db, + realOutStream, progress, needDigest ? digest : NULL); if (res == S_OK) { - if (memcmp(digest, si.Hash, kHashSize) == 0) + if (!needDigest || memcmp(digest, si.Hash, kHashSize) == 0) prevSuccessStreamIndex = streamIndex; else opRes = NExtract::NOperationResult::kCRCError; } else if (res == S_FALSE) opRes = NExtract::NOperationResult::kDataError; + else if (res == E_NOTIMPL) + opRes = NExtract::NOperationResult::kUnsupportedMethod; else return res; } diff --git a/CPP/7zip/Archive/Wim/WimHandler.h b/CPP/7zip/Archive/Wim/WimHandler.h index 00de1b87..3ba88d2b 100644 --- a/CPP/7zip/Archive/Wim/WimHandler.h +++ b/CPP/7zip/Archive/Wim/WimHandler.h @@ -10,6 +10,8 @@ namespace NArchive { namespace NWim { +static const Int32 kNumImagesMaxUpdate = (1 << 10); + class CHandler: public IInArchive, public IArchiveGetRawProps, @@ -34,6 +36,7 @@ class CHandler: bool _xmlError; bool _isArc; + bool _unsupported; bool _set_use_ShowImageNumber; bool _set_showImageNumber; @@ -53,6 +56,26 @@ class CHandler: _defaultImageNumber = -1; } + bool IsUpdateSupported() const + { + if (ThereIsError()) return false; + if (_db.Images.Size() > kNumImagesMaxUpdate) return false; + + // Solid format is complicated. So we disable updating now. + if (!_db.Solids.IsEmpty()) return false; + + if (_volumes.Size() == 0) + return true; + + if (_volumes.Size() != 2) return false; + if (_volumes[0].Stream) return false; + if (_version != k_Version_NonSolid + // && _version != k_Version_Solid + ) return false; + + return true; + } + bool ThereIsError() const { return _xmlError || _db.ThereIsError(); } HRESULT GetSecurity(UInt32 realIndex, const void **data, UInt32 *dataSize, UInt32 *propType); diff --git a/CPP/7zip/Archive/Wim/WimHandlerOut.cpp b/CPP/7zip/Archive/Wim/WimHandlerOut.cpp index 145ede42..2eb6c94b 100644 --- a/CPP/7zip/Archive/Wim/WimHandlerOut.cpp +++ b/CPP/7zip/Archive/Wim/WimHandlerOut.cpp @@ -2,10 +2,6 @@ #include "StdAfx.h" -// #include <stdio.h> - -#include "../../../../C/CpuArch.h" - #include "../../../Common/ComTry.h" #include "../../../Common/IntToString.h" #include "../../../Common/StringToInt.h" @@ -30,49 +26,36 @@ using namespace NWindows; namespace NArchive { namespace NWim { -static const Int32 kNumImagesMax = (1 << 10); - -struct CSha1Hash +static int AddUniqHash(const CStreamInfo *streams, CUIntVector &sorted, const Byte *h, int streamIndexForInsert) { - Byte Hash[kHashSize]; -}; - -class CHashList -{ - CUIntVector Sorted; -public: - CRecordVector<CSha1Hash> Digests; - - int AddUniq(const Byte *h); -}; - -// returns -1 : if it's new HASH - -int CHashList::AddUniq(const Byte *h) -{ - unsigned left = 0, right = Sorted.Size(); + unsigned left = 0, right = sorted.Size(); while (left != right) { unsigned mid = (left + right) / 2; - unsigned index = Sorted[mid]; - const Byte *hash2 = Digests[index].Hash; + unsigned index = sorted[mid]; + const Byte *hash2 = streams[index].Hash; + unsigned i; for (i = 0; i < kHashSize; i++) if (h[i] != hash2[i]) break; + if (i == kHashSize) return index; + if (h[i] < hash2[i]) right = mid; else left = mid + 1; } - CSha1Hash h2; - memcpy(h2.Hash, h, kHashSize); - Sorted.Insert(left, Digests.Add(h2)); + + if (streamIndexForInsert >= 0) + sorted.Insert(left, streamIndexForInsert); + return -1; } + struct CAltStream { int UpdateIndex; @@ -84,6 +67,7 @@ struct CAltStream CAltStream(): UpdateIndex(-1), HashIndex(-1), Skip(false) {} }; + struct CMetaItem { int UpdateIndex; @@ -114,6 +98,7 @@ struct CMetaItem Skip(false), NumSkipAltStreams(0) {} }; + static int Compare_HardLink_MetaItems(const CMetaItem &a1, const CMetaItem &a2) { if (a1.VolID < a2.VolID) return -1; @@ -125,6 +110,7 @@ static int Compare_HardLink_MetaItems(const CMetaItem &a1, const CMetaItem &a2) return ::CompareFileTime(&a1.MTime, &a2.MTime); } + static int AddToHardLinkList(const CObjectVector<CMetaItem> &metaItems, unsigned indexOfItem, CUIntVector &indexes) { const CMetaItem &mi = metaItems[indexOfItem]; @@ -145,6 +131,7 @@ static int AddToHardLinkList(const CObjectVector<CMetaItem> &metaItems, unsigned return -1; } + struct CUpdateItem { unsigned CallbackIndex; // index in callback @@ -160,6 +147,7 @@ struct CUpdateItem CUpdateItem(): MetaIndex(-1), AltStreamIndex(-1), InArcIndex(-1) {} }; + struct CDir { int MetaIndex; @@ -224,12 +212,14 @@ bool CDir::FindDir(const CObjectVector<CMetaItem> &items, const UString &name, u return false; } + STDMETHODIMP CHandler::GetFileTimeType(UInt32 *type) { *type = NFileTimeType::kWindows; return S_OK; } + HRESULT CHandler::GetOutProperty(IArchiveUpdateCallback *callback, UInt32 callbackIndex, Int32 arcIndex, PROPID propID, PROPVARIANT *value) { if (arcIndex >= 0) @@ -237,6 +227,7 @@ HRESULT CHandler::GetOutProperty(IArchiveUpdateCallback *callback, UInt32 callba return callback->GetProperty(callbackIndex, propID, value); } + HRESULT CHandler::GetTime(IArchiveUpdateCallback *callback, UInt32 callbackIndex, Int32 arcIndex, PROPID propID, FILETIME &ft) { ft.dwLowDateTime = ft.dwHighDateTime = 0; @@ -249,6 +240,7 @@ HRESULT CHandler::GetTime(IArchiveUpdateCallback *callback, UInt32 callbackIndex return S_OK; } + static HRESULT GetRootTime( IArchiveGetRootProps *callback, IArchiveGetRootProps *arcRoot, @@ -292,6 +284,7 @@ void CResource::WriteTo(Byte *p) const Set64(p + 16, UnpackSize); } + void CHeader::WriteTo(Byte *p) const { memcpy(p, kSignature, kSignatureSize); @@ -311,6 +304,7 @@ void CHeader::WriteTo(Byte *p) const memset(p + 0x94, 0, 60); } + void CStreamInfo::WriteTo(Byte *p) const { Resource.WriteTo(p); @@ -319,6 +313,7 @@ void CStreamInfo::WriteTo(Byte *p) const memcpy(p + 0x1E, Hash, kHashSize); } + class CInStreamWithSha1: public ISequentialInStream, public CMyUnknownImp @@ -352,6 +347,7 @@ STDMETHODIMP CInStreamWithSha1::Read(void *data, UInt32 size, UInt32 *processedS return result; } + static void SetFileTimeToMem(Byte *p, const FILETIME &ft) { Set32(p, ft.dwLowDateTime); @@ -391,7 +387,8 @@ static size_t WriteItem_Dummy(const CMetaItem &item) return totalLen; } -static size_t WriteItem(const CRecordVector<CSha1Hash> &digests, const CMetaItem &item, Byte *p) + +static size_t WriteItem(const CStreamInfo *streams, const CMetaItem &item, Byte *p) { if (item.Skip) return 0; @@ -437,7 +434,7 @@ static size_t WriteItem(const CRecordVector<CSha1Hash> &digests, const CMetaItem if (item.GetNumAltStreams() == 0) { if (item.HashIndex >= 0) - memcpy(p + 0x40, digests[item.HashIndex].Hash, kHashSize); + memcpy(p + 0x40, streams[item.HashIndex].Hash, kHashSize); } else { @@ -450,7 +447,7 @@ static size_t WriteItem(const CRecordVector<CSha1Hash> &digests, const CMetaItem memset(p, 0, curLen); Set64(p, curLen); if (item.HashIndex >= 0) - memcpy(p + 0x10, digests[item.HashIndex].Hash, kHashSize); + memcpy(p + 0x10, streams[item.HashIndex].Hash, kHashSize); totalLen += curLen; p += curLen; } @@ -468,7 +465,7 @@ static size_t WriteItem(const CRecordVector<CSha1Hash> &digests, const CMetaItem Set64(p, curLen); if (ss.HashIndex >= 0) - memcpy(p + 0x10, digests[ss.HashIndex].Hash, kHashSize); + memcpy(p + 0x10, streams[ss.HashIndex].Hash, kHashSize); Set16(p + 0x24, (UInt16)fileNameLen); for (i = 0; i * 2 < fileNameLen; i++) Set16(p + 0x26 + i * 2, ss.Name[i]); @@ -480,10 +477,11 @@ static size_t WriteItem(const CRecordVector<CSha1Hash> &digests, const CMetaItem return totalLen; } + struct CDb { CMetaItem DefaultDirItem; - const CRecordVector<CSha1Hash> *Hashes; + const CStreamInfo *Hashes; CObjectVector<CMetaItem> MetaItems; CRecordVector<CUpdateItem> UpdateItems; CUIntVector UpdateIndexes; /* indexes in UpdateItems in order of writing data streams @@ -494,6 +492,7 @@ struct CDb void WriteOrderList(const CDir &tree); }; + size_t CDb::WriteTree_Dummy(const CDir &tree) const { unsigned i; @@ -509,11 +508,12 @@ size_t CDb::WriteTree_Dummy(const CDir &tree) const return pos + 8; } + void CDb::WriteTree(const CDir &tree, Byte *dest, size_t &pos) const { unsigned i; for (i = 0; i < tree.Files.Size(); i++) - pos += WriteItem(*Hashes, MetaItems[tree.Files[i]], dest + pos); + pos += WriteItem(Hashes, MetaItems[tree.Files[i]], dest + pos); size_t posStart = pos; for (i = 0; i < tree.Dirs.Size(); i++) @@ -530,7 +530,7 @@ void CDb::WriteTree(const CDir &tree, Byte *dest, size_t &pos) const bool needCreateTree = (metaItem.Reparse.Size() == 0) || !subDir.Files.IsEmpty() || !subDir.Dirs.IsEmpty(); - size_t len = WriteItem(*Hashes, metaItem, dest + posStart); + size_t len = WriteItem(Hashes, metaItem, dest + posStart); posStart += len; if (needCreateTree) { @@ -540,6 +540,7 @@ void CDb::WriteTree(const CDir &tree, Byte *dest, size_t &pos) const } } + void CDb::WriteOrderList(const CDir &tree) { if (tree.MetaIndex >= 0) @@ -564,6 +565,7 @@ void CDb::WriteOrderList(const CDir &tree) WriteOrderList(tree.Dirs[i]); } + static void AddTag_ToString(AString &s, const char *name, const char *value) { s += '<'; @@ -576,6 +578,7 @@ static void AddTag_ToString(AString &s, const char *name, const char *value) s += '>'; } + static void AddTagUInt64_ToString(AString &s, const char *name, UInt64 value) { char temp[32]; @@ -583,6 +586,7 @@ static void AddTagUInt64_ToString(AString &s, const char *name, UInt64 value) AddTag_ToString(s, name, temp); } + static CXmlItem &AddUniqueTag(CXmlItem &parentItem, const char *name) { int index = parentItem.FindSubTag(name); @@ -598,6 +602,7 @@ static CXmlItem &AddUniqueTag(CXmlItem &parentItem, const char *name) return subItem; } + static void AddTag_UInt64_2(CXmlItem &item, UInt64 value) { CXmlItem &subItem = item.SubItems.AddNew(); @@ -607,11 +612,13 @@ static void AddTag_UInt64_2(CXmlItem &item, UInt64 value) subItem.Name = temp; } + static void AddTag_UInt64(CXmlItem &parentItem, const char *name, UInt64 value) { AddTag_UInt64_2(AddUniqueTag(parentItem, name), value); } + static void AddTag_Hex(CXmlItem &item, const char *name, UInt32 value) { item.IsTag = true; @@ -625,17 +632,20 @@ static void AddTag_Hex(CXmlItem &item, const char *name, UInt32 value) subItem.Name = temp; } + static void AddTag_Time_2(CXmlItem &item, const FILETIME &ft) { AddTag_Hex(item.SubItems.AddNew(), "HIGHPART", ft.dwHighDateTime); AddTag_Hex(item.SubItems.AddNew(), "LOWPART", ft.dwLowDateTime); } + static void AddTag_Time(CXmlItem &parentItem, const char *name, const FILETIME &ft) { AddTag_Time_2(AddUniqueTag(parentItem, name), ft); } + static void AddTag_String_IfEmpty(CXmlItem &parentItem, const char *name, const char *value) { int index = parentItem.FindSubTag(name); @@ -649,15 +659,17 @@ static void AddTag_String_IfEmpty(CXmlItem &parentItem, const char *name, const subItem.Name = value; } + void CHeader::SetDefaultFields(bool useLZX) { - Version = kWimVersion; + Version = k_Version_NonSolid; Flags = NHeaderFlags::kReparsePointFixup; ChunkSize = 0; if (useLZX) { Flags |= NHeaderFlags::kCompression | NHeaderFlags::kLZX; ChunkSize = kChunkSize; + ChunkSizeBits = kChunkSizeBits; } g_RandomGenerator.Generate(Guid, 16); PartNumber = 1; @@ -670,19 +682,23 @@ void CHeader::SetDefaultFields(bool useLZX) IntegrityResource.Clear(); } + static void AddTrees(CObjectVector<CDir> &trees, CObjectVector<CMetaItem> &metaItems, const CMetaItem &ri, int curTreeIndex) { while (curTreeIndex >= (int)trees.Size()) trees.AddNew().Dirs.AddNew().MetaIndex = metaItems.Add(ri); } + #define IS_LETTER_CHAR(c) ((c) >= 'a' && (c) <= 'z' || (c) >= 'A' && (c) <= 'Z') + + STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outSeqStream, UInt32 numItems, IArchiveUpdateCallback *callback) { COM_TRY_BEGIN - if (ThereIsError()) + if (!IsUpdateSupported()) return E_NOTIMPL; bool isUpdate = (_volumes.Size() != 0); @@ -692,14 +708,8 @@ STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outSeqStream, UInt32 nu if (isUpdate) { showImageNumber = _showImageNumber; - if (_version != kWimVersion) - return E_NOTIMPL; - if (_volumes.Size() != 2 || _volumes[0].Stream) - return E_NOTIMPL; if (!showImageNumber) defaultImageIndex = _db.IndexOfUserImage; - if (_db.Images.Size() > kNumImagesMax) - return E_NOTIMPL; } else { @@ -708,7 +718,7 @@ STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outSeqStream, UInt32 nu defaultImageIndex = 0; } - if (defaultImageIndex >= kNumImagesMax) + if (defaultImageIndex >= kNumImagesMaxUpdate) return E_NOTIMPL; CMyComPtr<IOutStream> outStream; @@ -783,7 +793,7 @@ STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outSeqStream, UInt32 nu UInt64 val = ConvertStringToUInt64(path, &end); if (end == path) return E_INVALIDARG; - if (val == 0 || val > kNumImagesMax) + if (val == 0 || val > kNumImagesMaxUpdate) return E_INVALIDARG; wchar_t c = *end; if (c != 0 && c != ':' && c != L'/' && c != WCHAR_PATH_SEPARATOR) @@ -889,6 +899,7 @@ STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outSeqStream, UInt32 nu UInt32 indexInArchive; Int32 newData, newProps; RINOK(callback->GetUpdateItemInfo(i, &newData, &newProps, &indexInArchive)); + if (newData == 0 || newProps == 0) { if (indexInArchive >= _db.SortedItems.Size()) @@ -910,7 +921,14 @@ STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outSeqStream, UInt32 nu // if deleted item was not renamed, we just skip it if (newProps == 0) continue; + if (item.StreamIndex >= 0) + { + // we don't support property change for SolidBig streams + if (_db.DataStreams[item.StreamIndex].Resource.IsSolidBig()) + return E_NOTIMPL; + } } + if (newData == 0) ui.InArcIndex = indexInArchive; } @@ -955,6 +973,7 @@ STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outSeqStream, UInt32 nu } NCOM::CPropVariant prop; + if (newData) { RINOK(callback->GetProperty(i, kpidSize, &prop)); @@ -963,6 +982,7 @@ STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outSeqStream, UInt32 nu { RINOK(GetProperty(indexInArchive, kpidSize, &prop)); } + if (prop.vt == VT_UI8) size = prop.uhVal.QuadPart; else if (prop.vt != VT_EMPTY) @@ -999,7 +1019,7 @@ STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outSeqStream, UInt32 nu UInt64 val = ConvertStringToUInt64(path, &end); if (end == path) return E_INVALIDARG; - if (val == 0 || val > kNumImagesMax) + if (val == 0 || val > kNumImagesMaxUpdate) return E_INVALIDARG; imageIndex = (int)val - 1; @@ -1111,6 +1131,7 @@ STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outSeqStream, UInt32 nu ui.MetaIndex = db.MetaItems.Size(); db.MetaItems.AddNew(); } + CMetaItem &mi = db.MetaItems[ui.MetaIndex]; mi.Size = size; mi.IsDir = isDir; @@ -1163,6 +1184,7 @@ STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outSeqStream, UInt32 nu { getRawProps->GetRawProp(i, kpidNtSecure, &data, &dataSize, &propType); } + if (dataSize != 0) { if (propType != NPropDataType::kRaw) @@ -1173,6 +1195,7 @@ STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outSeqStream, UInt32 nu data = NULL; dataSize = 0; propType = 0; + if (arcIndex >= 0) { GetRawProp(arcIndex, kpidNtReparse, &data, &dataSize, &propType); @@ -1181,6 +1204,7 @@ STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outSeqStream, UInt32 nu { getRawProps->GetRawProp(i, kpidNtReparse, &data, &dataSize, &propType); } + if (dataSize != 0) { if (propType != NPropDataType::kRaw) @@ -1228,7 +1252,7 @@ STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outSeqStream, UInt32 nu UInt64 complexity = 0; unsigned numDataStreams = _db.DataStreams.Size(); - CIntArr streamsRefs(numDataStreams); + CUIntArr streamsRefs(numDataStreams); for (i = 0; i < numDataStreams; i++) streamsRefs[i] = 0; @@ -1249,11 +1273,13 @@ STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outSeqStream, UInt32 nu } } + // ---------- Update Streams Refs Counts in changed images for (i = 0; i < db.UpdateIndexes.Size(); i++) { const CUpdateItem &ui = db.UpdateItems[db.UpdateIndexes[i]]; + if (ui.InArcIndex >= 0) { if ((unsigned)ui.InArcIndex >= _db.SortedItems.Size()) @@ -1274,12 +1300,32 @@ STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outSeqStream, UInt32 nu } } + // Clear ref counts for SolidBig streams + for (i = 0; i < _db.DataStreams.Size(); i++) - if (streamsRefs[i] != 0) - complexity += _db.DataStreams[i].Resource.PackSize; + if (_db.DataStreams[i].Resource.IsSolidBig()) + streamsRefs[i] = 0; + + // Set ref counts for SolidBig streams - RINOK(callback->SetTotal(complexity)); + for (i = 0; i < _db.DataStreams.Size(); i++) + if (streamsRefs[i] != 0) + { + const CResource &rs = _db.DataStreams[i].Resource; + if (rs.IsSolidSmall()) + streamsRefs[_db.Solids[rs.SolidIndex].StreamIndex] = 1; + } + for (i = 0; i < _db.DataStreams.Size(); i++) + if (streamsRefs[i] != 0) + { + const CResource &rs = _db.DataStreams[i].Resource; + if (!rs.IsSolidSmall()) + complexity += rs.PackSize; + } + + RINOK(callback->SetTotal(complexity)); + UInt64 totalComplexity = complexity; NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder; CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec; @@ -1300,7 +1346,9 @@ STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outSeqStream, UInt32 nu { const CHeader &srcHeader = _volumes[1].Header; header.Flags = srcHeader.Flags; + header.Version = srcHeader.Version; header.ChunkSize = srcHeader.ChunkSize; + header.ChunkSizeBits = srcHeader.ChunkSizeBits; } Byte buf[kHeaderSizeMax]; @@ -1322,40 +1370,84 @@ STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outSeqStream, UInt32 nu } - // these two lists have same sizes and same hashes in same order. - CHashList hashes; - CObjectVector<CStreamInfo> streams; - + CRecordVector<CStreamInfo> streams; + CUIntVector sortedHashes; // indexes to streams, sorted by SHA1 // ---------- Copy unchanged data streams ---------- + UInt64 solidRunOffset = 0; + UInt64 curSolidSize = 0; + for (i = 0; i < _db.DataStreams.Size(); i++) { - if (streamsRefs[i] == 0) - continue; + const CStreamInfo &siOld = _db.DataStreams[i]; + const CResource &rs = siOld.Resource; + + unsigned numRefs = streamsRefs[i]; + + if (numRefs == 0) + { + if (!rs.IsSolidSmall()) + continue; + if (streamsRefs[_db.Solids[rs.SolidIndex].StreamIndex] == 0) + continue; + } lps->InSize = lps->OutSize = complexity; RINOK(lps->SetCur()); - const CStreamInfo &siOld = _db.DataStreams[i]; - if (hashes.AddUniq(siOld.Hash) >= 0) - return E_FAIL; // two streams with same SHA-1 - - RINOK(_volumes[siOld.PartNumber].Stream->Seek(siOld.Resource.Offset, STREAM_SEEK_SET, NULL)); - inStreamLimitedSpec->Init(siOld.Resource.PackSize); - RINOK(copyCoder->Code(inStreamLimited, outStream, NULL, NULL, progress)); - if (copyCoderSpec->TotalSize != siOld.Resource.PackSize) - return E_FAIL; - - CStreamInfo &s = streams.AddNew(); - s.Resource = siOld.Resource; - s.Resource.Offset = curPos; + int streamIndex = streams.Size(); + CStreamInfo s; + s.Resource = rs; s.PartNumber = 1; - s.RefCount = streamsRefs[i]; + s.RefCount = numRefs; + memcpy(s.Hash, siOld.Hash, kHashSize); - curPos += s.Resource.PackSize; - lps->ProgressOffset += s.Resource.PackSize; + if (rs.IsSolid()) + { + CSolid &ss = _db.Solids[rs.SolidIndex]; + if (rs.IsSolidSmall()) + { + UInt64 oldOffset = ss.SolidOffset; + if (rs.Offset < oldOffset) + return E_FAIL; + UInt64 relatOffset = rs.Offset - oldOffset; + s.Resource.Offset = solidRunOffset + relatOffset; + } + else + { + // IsSolidBig + solidRunOffset += curSolidSize; + curSolidSize = ss.UnpackSize; + } + } + else + { + solidRunOffset = 0; + curSolidSize = 0; + } + + if (!rs.IsSolid() || rs.IsSolidSmall()) + { + int find = AddUniqHash(&streams.Front(), sortedHashes, siOld.Hash, streamIndex); + if (find >= 0) + return E_FAIL; // two streams with same SHA-1 + } + + if (!rs.IsSolid() || rs.IsSolidBig()) + { + RINOK(_volumes[siOld.PartNumber].Stream->Seek(rs.Offset, STREAM_SEEK_SET, NULL)); + inStreamLimitedSpec->Init(rs.PackSize); + RINOK(copyCoder->Code(inStreamLimited, outStream, NULL, NULL, progress)); + if (copyCoderSpec->TotalSize != rs.PackSize) + return E_FAIL; + s.Resource.Offset = curPos; + curPos += rs.PackSize; + lps->ProgressOffset += rs.PackSize; + } + + streams.Add(s); } @@ -1367,7 +1459,6 @@ STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outSeqStream, UInt32 nu { lps->InSize = lps->OutSize = complexity; RINOK(lps->SetCur()); - const CUpdateItem &ui = db.UpdateItems[db.UpdateIndexes[i]]; CMetaItem &mi = db.MetaItems[ui.MetaIndex]; UInt64 size = 0; @@ -1396,7 +1487,9 @@ STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outSeqStream, UInt32 nu if ((unsigned)ui.InArcIndex >= _db.SortedItems.Size()) return E_FAIL; + const CItem &item = _db.Items[_db.SortedItems[ui.InArcIndex]]; + if (item.StreamIndex < 0) { if (size == 0) @@ -1408,19 +1501,23 @@ STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outSeqStream, UInt32 nu // We support empty file (size = 0, but with stream and SHA-1) from old archive const CStreamInfo &siOld = _db.DataStreams[item.StreamIndex]; + + int index = AddUniqHash(&streams.Front(), sortedHashes, siOld.Hash, -1); // we must have written that stream already - int index = hashes.AddUniq(siOld.Hash); if (index < 0) return E_FAIL; + if (ui.AltStreamIndex < 0) mi.HashIndex = index; else mi.AltStreams[ui.AltStreamIndex].HashIndex = index; + continue; } CMyComPtr<ISequentialInStream> fileInStream; HRESULT res = callback->GetStream(ui.CallbackIndex, &fileInStream); + if (res == S_FALSE) { if (ui.AltStreamIndex >= 0) @@ -1452,8 +1549,6 @@ STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outSeqStream, UInt32 nu if (getProps2->GetProps2(&props) == S_OK) { mi.Attrib = props.Attrib; - mi.Size = props.Size; - size = props.Size; mi.CTime = props.CTime; mi.ATime = props.ATime; mi.MTime = props.MTime; @@ -1463,6 +1558,19 @@ STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outSeqStream, UInt32 nu mi.VolID = props.VolID; if (mi.FileID != 0) miIndex = AddToHardLinkList(db.MetaItems, ui.MetaIndex, hlIndexes); + + if (props.Size != size && props.Size != (UInt64)(Int64)-1) + { + Int64 delta = (Int64)props.Size - (Int64)size; + Int64 newComplexity = totalComplexity + delta; + if (newComplexity > 0) + { + totalComplexity = newComplexity; + callback->SetTotal(totalComplexity); + } + mi.Size = props.Size; + size = props.Size; + } } } } @@ -1484,14 +1592,16 @@ STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outSeqStream, UInt32 nu sha1.Update((const Byte *)mi.Reparse + 8, packSize); Byte hash[kHashSize]; sha1.Final(hash); - int index = hashes.AddUniq(hash); + + int index = AddUniqHash(&streams.Front(), sortedHashes, hash, streams.Size()); + if (index >= 0) streams[index].RefCount++; else { - RINOK(WriteStream(outStream, (const Byte *)mi.Reparse + 8, packSize)); index = streams.Size(); - CStreamInfo &s = streams.AddNew(); + RINOK(WriteStream(outStream, (const Byte *)mi.Reparse + 8, packSize)); + CStreamInfo s; s.Resource.PackSize = packSize; s.Resource.Offset = curPos; s.Resource.UnpackSize = packSize; @@ -1504,7 +1614,10 @@ STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outSeqStream, UInt32 nu s.RefCount = 1; memcpy(s.Hash, hash, kHashSize); curPos += packSize; + + streams.Add(s); } + mi.HashIndex = index; } else @@ -1534,8 +1647,9 @@ STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outSeqStream, UInt32 nu Byte hash[kHashSize]; UInt64 packSize = offsetBlockSize + size; inShaStreamSpec->Final(hash); - int index = hashes.AddUniq(hash); - + + int index = AddUniqHash(&streams.Front(), sortedHashes, hash, streams.Size()); + if (index >= 0) { streams[index].RefCount++; @@ -1545,7 +1659,7 @@ STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outSeqStream, UInt32 nu else { index = streams.Size(); - CStreamInfo &s = streams.AddNew(); + CStreamInfo s; s.Resource.PackSize = packSize; s.Resource.Offset = curPos; s.Resource.UnpackSize = size; @@ -1558,6 +1672,8 @@ STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outSeqStream, UInt32 nu s.RefCount = 1; memcpy(s.Hash, hash, kHashSize); curPos += packSize; + + streams.Add(s); } if (ui.AltStreamIndex < 0) @@ -1658,15 +1774,16 @@ STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outSeqStream, UInt32 nu Set32((Byte *)meta, (UInt32)pos); // size of security data } - db.Hashes = &hashes.Digests; + db.Hashes = &streams.Front(); db.WriteTree(tree, (Byte *)meta, pos); { NCrypto::NSha1::CContext sha; sha.Init(); sha.Update((const Byte *)meta, pos); - CSha1Hash digest; - sha.Final(digest.Hash); + + Byte digest[kHashSize]; + sha.Final(digest); CStreamInfo s; s.Resource.PackSize = pos; @@ -1675,7 +1792,7 @@ STDMETHODIMP CHandler::UpdateItems(ISequentialOutStream *outSeqStream, UInt32 nu s.Resource.Flags = NResourceFlags::kMetadata; s.PartNumber = 1; s.RefCount = 1; - memcpy(s.Hash, digest.Hash, kHashSize); + memcpy(s.Hash, digest, kHashSize); streams.Add(s); if (_bootIndex != 0 && _bootIndex == (UInt32)i + 1) diff --git a/CPP/7zip/Archive/Wim/WimIn.cpp b/CPP/7zip/Archive/Wim/WimIn.cpp index 12b8525c..2c1ec5de 100644 --- a/CPP/7zip/Archive/Wim/WimIn.cpp +++ b/CPP/7zip/Archive/Wim/WimIn.cpp @@ -2,7 +2,14 @@ #include "StdAfx.h" -// #include <stdio.h> +// #define SHOW_DEBUG_INFO + +#ifdef SHOW_DEBUG_INFO +#include <stdio.h> +#define PRF(x) x +#else +#define PRF(x) +#endif #include "../../../../C/CpuArch.h" @@ -14,6 +21,8 @@ #include "../../Common/StreamObjects.h" #include "../../Common/StreamUtils.h" +#include "../../Compress/XPressDecoder.h" + #include "../Common/OutStreamWithSha1.h" #include "WimIn.h" @@ -25,245 +34,397 @@ namespace NArchive { namespace NWim { -namespace NXpress { +static int inline GetLog(UInt32 num) +{ + for (int i = 0; i < 32; i++) + if (((UInt32)1 << i) == num) + return i; + return -1; +} + -class CDecoderFlusher +CUnpacker::~CUnpacker() { - CDecoder *m_Decoder; -public: - bool NeedFlush; - CDecoderFlusher(CDecoder *decoder): m_Decoder(decoder), NeedFlush(true) {} - ~CDecoderFlusher() - { - if (NeedFlush) - m_Decoder->Flush(); - } -}; + if (lzmsDecoder) + delete lzmsDecoder; +} + -HRESULT CDecoder::CodeSpec(UInt32 outSize) +HRESULT CUnpacker::UnpackChunk( + ISequentialInStream *inStream, + unsigned method, unsigned chunkSizeBits, + size_t inSize, size_t outSize, + ISequentialOutStream *outStream) { + if (inSize == outSize) + { + } + else if (method == NMethod::kXPRESS) + { + } + else if (method == NMethod::kLZX) { - Byte levels[kMainTableSize]; - for (unsigned i = 0; i < kMainTableSize; i += 2) + if (!lzxDecoder) { - Byte b = m_InBitStream.DirectReadByte(); - levels[i] = (Byte)(b & 0xF); - levels[i + 1] = (Byte)(b >> 4); + lzxDecoderSpec = new NCompress::NLzx::CDecoder(true); + lzxDecoder = lzxDecoderSpec; } - if (!m_MainDecoder.SetCodeLengths(levels)) - return S_FALSE; } + else if (method == NMethod::kLZMS) + { + if (!lzmsDecoder) + lzmsDecoder = new NCompress::NLzms::CDecoder(); + } + else + return E_NOTIMPL; - while (outSize > 0) + const size_t chunkSize = (size_t)1 << chunkSizeBits; + + unpackBuf.EnsureCapacity(chunkSize); + if (!unpackBuf.Data) + return E_OUTOFMEMORY; + + HRESULT res = S_FALSE; + size_t unpackedSize = 0; + + if (inSize == outSize) + { + unpackedSize = outSize; + res = ReadStream(inStream, unpackBuf.Data, &unpackedSize); + TotalPacked += unpackedSize; + } + else if (inSize < chunkSize) { - UInt32 number = m_MainDecoder.DecodeSymbol(&m_InBitStream); - if (number < 256) + packBuf.EnsureCapacity(chunkSize); + if (!packBuf.Data) + return E_OUTOFMEMORY; + + RINOK(ReadStream_FALSE(inStream, packBuf.Data, inSize)); + + TotalPacked += inSize; + + if (method == NMethod::kXPRESS) + { + res = NCompress::NXpress::Decode(packBuf.Data, inSize, unpackBuf.Data, outSize); + if (res == S_OK) + unpackedSize = outSize; + } + else if (method == NMethod::kLZX) { - m_OutWindowStream.PutByte((Byte)number); - outSize--; + lzxDecoderSpec->SetExternalWindow(unpackBuf.Data, chunkSizeBits); + lzxDecoderSpec->KeepHistoryForNext = false; + lzxDecoderSpec->SetKeepHistory(false); + res = lzxDecoderSpec->Code(packBuf.Data, inSize, (UInt32)outSize); + unpackedSize = lzxDecoderSpec->GetUnpackSize(); + if (res == S_OK && !lzxDecoderSpec->WasBlockFinished()) + res = S_FALSE; } else { - if (number >= kMainTableSize) - return S_FALSE; - UInt32 posLenSlot = number - 256; - UInt32 posSlot = posLenSlot / kNumLenSlots; - UInt32 len = posLenSlot % kNumLenSlots; - UInt32 distance = (1 << posSlot) - 1 + m_InBitStream.ReadBits(posSlot); - - if (len == kNumLenSlots - 1) - { - len = m_InBitStream.DirectReadByte(); - if (len == 0xFF) - { - len = m_InBitStream.DirectReadByte(); - len |= (UInt32)m_InBitStream.DirectReadByte() << 8; - } - else - len += kNumLenSlots - 1; - } - - len += kMatchMinLen; - UInt32 locLen = (len <= outSize ? len : outSize); - - if (!m_OutWindowStream.CopyBlock(distance, locLen)) - return S_FALSE; - - len -= locLen; - outSize -= locLen; - if (len != 0) - return S_FALSE; + res = lzmsDecoder->Code(packBuf.Data, inSize, unpackBuf.Data, outSize); + unpackedSize = lzmsDecoder->GetUnpackSize();; } } - return S_OK; + + if (unpackedSize != outSize) + { + if (res == S_OK) + res = S_FALSE; + + if (unpackedSize > outSize) + res = S_FALSE; + else + memset(unpackBuf.Data + unpackedSize, 0, outSize - unpackedSize); + } + + if (outStream) + { + RINOK(WriteStream(outStream, unpackBuf.Data, outSize)); + } + + return res; } -const UInt32 kDictSize = (1 << kNumPosSlots); -HRESULT CDecoder::CodeReal(ISequentialInStream *inStream, ISequentialOutStream *outStream, UInt32 outSize) +HRESULT CUnpacker::Unpack2( + IInStream *inStream, + const CResource &resource, + const CHeader &header, + const CDatabase *db, + ISequentialOutStream *outStream, + ICompressProgressInfo *progress) { - if (!m_OutWindowStream.Create(kDictSize) || !m_InBitStream.Create(1 << 16)) - return E_OUTOFMEMORY; + if (!resource.IsCompressed() && !resource.IsSolid()) + { + if (!copyCoder) + { + copyCoderSpec = new NCompress::CCopyCoder; + copyCoder = copyCoderSpec; + } - CDecoderFlusher flusher(this); + CLimitedSequentialInStream *limitedStreamSpec = new CLimitedSequentialInStream(); + CMyComPtr<ISequentialInStream> limitedStream = limitedStreamSpec; + limitedStreamSpec->SetStream(inStream); + + RINOK(inStream->Seek(resource.Offset, STREAM_SEEK_SET, NULL)); + if (resource.PackSize != resource.UnpackSize) + return S_FALSE; - m_InBitStream.SetStream(inStream); - m_OutWindowStream.SetStream(outStream); - m_InBitStream.Init(); - m_OutWindowStream.Init(false); + limitedStreamSpec->Init(resource.PackSize); + TotalPacked += resource.PackSize; + + HRESULT res = copyCoder->Code(limitedStream, outStream, NULL, NULL, progress); + + if (res == S_OK && copyCoderSpec->TotalSize != resource.UnpackSize) + res = S_FALSE; + return res; + } + + if (resource.IsSolid()) + { + if (!db || resource.SolidIndex < 0) + return E_NOTIMPL; + if (resource.IsCompressed()) + return E_NOTIMPL; - RINOK(CodeSpec(outSize)); + const CSolid &ss = db->Solids[resource.SolidIndex]; + + const unsigned chunkSizeBits = ss.ChunkSizeBits; + const size_t chunkSize = (size_t)1 << chunkSizeBits; + + size_t chunkIndex = 0; + UInt64 rem = ss.UnpackSize; + size_t offsetInChunk = 0; + + if (resource.IsSolidSmall()) + { + UInt64 offs = resource.Offset; + if (offs < ss.SolidOffset) + return E_NOTIMPL; + offs -= ss.SolidOffset; + if (offs > ss.UnpackSize) + return E_NOTIMPL; + rem = resource.PackSize; + if (rem > ss.UnpackSize - offs) + return E_NOTIMPL; + chunkIndex = (size_t)(offs >> chunkSizeBits); + offsetInChunk = (size_t)offs & (chunkSize - 1); + } + + UInt64 packProcessed = 0; + UInt64 outProcessed = 0; + + if (_solidIndex == resource.SolidIndex && _unpackedChunkIndex == chunkIndex) + { + size_t cur = chunkSize - offsetInChunk; + if (cur > rem) + cur = (size_t)rem; + RINOK(WriteStream(outStream, unpackBuf.Data + offsetInChunk, cur)); + outProcessed += cur; + rem -= cur; + offsetInChunk = 0; + chunkIndex++; + } + + for (;;) + { + if (rem == 0) + return S_OK; + + UInt64 offset = ss.Chunks[chunkIndex]; + UInt64 packSize = ss.GetChunkPackSize(chunkIndex); + const CResource &rs = db->DataStreams[ss.StreamIndex].Resource; + RINOK(inStream->Seek(rs.Offset + ss.HeadersSize + offset, STREAM_SEEK_SET, NULL)); + + size_t cur = chunkSize; + UInt64 unpackRem = ss.UnpackSize - ((UInt64)chunkIndex << chunkSizeBits); + if (cur > unpackRem) + cur = (size_t)unpackRem; + + _solidIndex = -1; + _unpackedChunkIndex = 0; + + HRESULT res = UnpackChunk(inStream, ss.Method, chunkSizeBits, (size_t)packSize, cur, NULL); + + if (res != S_OK) + { + // We ignore data errors in solid stream. SHA will show what files are bad. + if (res != S_FALSE) + return res; + } + + _solidIndex = resource.SolidIndex; + _unpackedChunkIndex = chunkIndex; + + if (cur > rem) + cur = (size_t)rem; + + RINOK(WriteStream(outStream, unpackBuf.Data + offsetInChunk, cur)); + + if (progress) + { + RINOK(progress->SetRatioInfo(&packProcessed, &outProcessed)); + packProcessed += packSize; + outProcessed += cur; + } + + rem -= cur; + offsetInChunk = 0; + chunkIndex++; + } + } - flusher.NeedFlush = false; - return Flush(); -} -HRESULT CDecoder::Code(ISequentialInStream *inStream, ISequentialOutStream *outStream, UInt32 outSize) -{ - try { return CodeReal(inStream, outStream, outSize); } - catch(const CInBufferException &e) { return e.ErrorCode; } \ - catch(const CLzOutWindowException &e) { return e.ErrorCode; } - catch(...) { return S_FALSE; } -} + // ---------- NON Solid ---------- -} + const UInt64 unpackSize = resource.UnpackSize; + if (unpackSize == 0) + { + if (resource.PackSize == 0) + return S_OK; + return S_FALSE; + } -HRESULT CUnpacker::Unpack(IInStream *inStream, const CResource &resource, bool lzxMode, - ISequentialOutStream *outStream, ICompressProgressInfo *progress) -{ - RINOK(inStream->Seek(resource.Offset, STREAM_SEEK_SET, NULL)); + if (unpackSize > ((UInt64)1 << 63)) + return E_NOTIMPL; - CLimitedSequentialInStream *limitedStreamSpec = new CLimitedSequentialInStream(); - CMyComPtr<ISequentialInStream> limitedStream = limitedStreamSpec; - limitedStreamSpec->SetStream(inStream); + const unsigned chunkSizeBits = header.ChunkSizeBits; + const unsigned entrySizeShifts = (resource.UnpackSize < ((UInt64)1 << 32) ? 2 : 3); - if (!copyCoder) - { - copyCoderSpec = new NCompress::CCopyCoder; - copyCoder = copyCoderSpec; - } - if (!resource.IsCompressed()) + UInt64 baseOffset = resource.Offset; + UInt64 packDataSize; + size_t numChunks; { - if (resource.PackSize != resource.UnpackSize) + UInt64 numChunks64 = (unpackSize + (((UInt32)1 << chunkSizeBits) - 1)) >> chunkSizeBits; + UInt64 sizesBufSize64 = (numChunks64 - 1) << entrySizeShifts; + if (sizesBufSize64 > resource.PackSize) return S_FALSE; - limitedStreamSpec->Init(resource.PackSize); - return copyCoder->Code(limitedStreamSpec, outStream, NULL, NULL, progress); - } - if (resource.UnpackSize == 0) - return S_OK; - UInt64 numChunks = (resource.UnpackSize + kChunkSize - 1) >> kChunkSizeBits; - unsigned entrySize = ((resource.UnpackSize > (UInt64)1 << 32) ? 8 : 4); - UInt64 sizesBufSize64 = entrySize * (numChunks - 1); - size_t sizesBufSize = (size_t)sizesBufSize64; - if (sizesBufSize != sizesBufSize64) - return E_OUTOFMEMORY; - sizesBuf.AllocAtLeast(sizesBufSize); - RINOK(ReadStream_FALSE(inStream, (Byte *)sizesBuf, sizesBufSize)); - const Byte *p = (const Byte *)sizesBuf; - - if (lzxMode && !lzxDecoder) - { - lzxDecoderSpec = new NCompress::NLzx::CDecoder(true); - lzxDecoder = lzxDecoderSpec; - RINOK(lzxDecoderSpec->SetParams(kChunkSizeBits)); + packDataSize = resource.PackSize - sizesBufSize64; + size_t sizesBufSize = (size_t)sizesBufSize64; + if (sizesBufSize != sizesBufSize64) + return E_OUTOFMEMORY; + sizesBuf.AllocAtLeast(sizesBufSize); + RINOK(inStream->Seek(baseOffset, STREAM_SEEK_SET, NULL)); + RINOK(ReadStream_FALSE(inStream, sizesBuf, sizesBufSize)); + baseOffset += sizesBufSize64; + numChunks = (size_t)numChunks64; } - - UInt64 baseOffset = resource.Offset + sizesBufSize64; + + _solidIndex = -1; + _unpackedChunkIndex = 0; + UInt64 outProcessed = 0; - for (UInt32 i = 0; i < (UInt32)numChunks; i++) + UInt64 offset = 0; + + for (size_t i = 0; i < numChunks; i++) { - UInt64 offset = 0; - if (i > 0) + UInt64 nextOffset = packDataSize; + + if (i + 1 < numChunks) { - offset = (entrySize == 4) ? Get32(p): Get64(p); - p += entrySize; + const Byte *p = (const Byte *)sizesBuf + (i << entrySizeShifts); + nextOffset = (entrySizeShifts == 2) ? Get32(p): Get64(p); } - UInt64 nextOffset = resource.PackSize - sizesBufSize64; - if (i + 1 < (UInt32)numChunks) - nextOffset = (entrySize == 4) ? Get32(p): Get64(p); + if (nextOffset < offset) return S_FALSE; + UInt64 inSize64 = nextOffset - offset; + size_t inSize = (size_t)inSize64; + if (inSize != inSize64) + return S_FALSE; + RINOK(inStream->Seek(baseOffset + offset, STREAM_SEEK_SET, NULL)); - UInt64 inSize = nextOffset - offset; - limitedStreamSpec->Init(inSize); if (progress) { RINOK(progress->SetRatioInfo(&offset, &outProcessed)); } - UInt32 outSize = kChunkSize; - if (outProcessed + outSize > resource.UnpackSize) - outSize = (UInt32)(resource.UnpackSize - outProcessed); - UInt64 outSize64 = outSize; - if (inSize == outSize) - { - RINOK(copyCoder->Code(limitedStreamSpec, outStream, NULL, &outSize64, NULL)); - } - else - { - if (lzxMode) - { - lzxDecoderSpec->SetKeepHistory(false); - RINOK(lzxDecoder->Code(limitedStreamSpec, outStream, NULL, &outSize64, NULL)); - } - else - { - RINOK(xpressDecoder.Code(limitedStreamSpec, outStream, outSize)); - } - } + size_t outSize = (size_t)1 << chunkSizeBits; + const UInt64 rem = unpackSize - outProcessed; + if (outSize > rem) + outSize = (size_t)rem; + + RINOK(UnpackChunk(inStream, header.GetMethod(), chunkSizeBits, inSize, outSize, outStream)); + outProcessed += outSize; + offset = nextOffset; } + return S_OK; } -HRESULT CUnpacker::Unpack(IInStream *inStream, const CResource &resource, bool lzxMode, + +HRESULT CUnpacker::Unpack(IInStream *inStream, const CResource &resource, const CHeader &header, const CDatabase *db, ISequentialOutStream *outStream, ICompressProgressInfo *progress, Byte *digest) { - COutStreamWithSha1 *shaStreamSpec = new COutStreamWithSha1(); - CMyComPtr<ISequentialOutStream> shaStream = shaStreamSpec; - shaStreamSpec->SetStream(outStream); - shaStreamSpec->Init(digest != NULL); - HRESULT result = Unpack(inStream, resource, lzxMode, shaStream, progress); + COutStreamWithSha1 *shaStreamSpec = NULL; + CMyComPtr<ISequentialOutStream> shaStream; + + // outStream can be NULL, so we use COutStreamWithSha1 even if sha1 is not required + // if (digest) + { + shaStreamSpec = new COutStreamWithSha1(); + shaStream = shaStreamSpec; + shaStreamSpec->SetStream(outStream); + shaStreamSpec->Init(digest != NULL); + outStream = shaStream; + } + + HRESULT res = Unpack2(inStream, resource, header, db, outStream, progress); + if (digest) shaStreamSpec->Final(digest); - return result; + + return res; } -static HRESULT UnpackData(IInStream *inStream, const CResource &resource, bool lzxMode, CByteBuffer &buf, Byte *digest) + +HRESULT CUnpacker::UnpackData(IInStream *inStream, + const CResource &resource, const CHeader &header, + const CDatabase *db, + CByteBuffer &buf, Byte *digest) { - size_t size = (size_t)resource.UnpackSize; - if (size != resource.UnpackSize) + // if (resource.IsSolid()) return E_NOTIMPL; + + UInt64 unpackSize64 = resource.UnpackSize; + if (db) + unpackSize64 = db->Get_UnpackSize_of_Resource(resource); + + size_t size = (size_t)unpackSize64; + if (size != unpackSize64) return E_OUTOFMEMORY; + buf.Alloc(size); CBufPtrSeqOutStream *outStreamSpec = new CBufPtrSeqOutStream(); CMyComPtr<ISequentialOutStream> outStream = outStreamSpec; outStreamSpec->Init((Byte *)buf, size); - CUnpacker unpacker; - return unpacker.Unpack(inStream, resource, lzxMode, outStream, NULL, digest); + return Unpack(inStream, resource, header, db, outStream, NULL, digest); } + void CResource::Parse(const Byte *p) { Flags = p[7]; PackSize = Get64(p) & (((UInt64)1 << 56) - 1); Offset = Get64(p + 8); UnpackSize = Get64(p + 16); + KeepSolid = false; + SolidIndex = -1; } #define GET_RESOURCE(_p_, res) res.ParseAndUpdatePhySize(_p_, phySize) -static void GetStream(bool oldVersion, const Byte *p, CStreamInfo &s) +static inline void ParseStream(bool oldVersion, const Byte *p, CStreamInfo &s) { s.Resource.Parse(p); if (oldVersion) { s.PartNumber = 1; s.Id = Get32(p + 24); - // printf("\n%d", s.Id); p += 28; } else @@ -300,6 +461,7 @@ void CDatabase::GetShortName(unsigned index, NWindows::NCOM::CPropVariant &name) // empty shortName has no ZERO at the end ? } + void CDatabase::GetItemName(unsigned index, NWindows::NCOM::CPropVariant &name) const { const CItem &item = Items[index]; @@ -321,6 +483,7 @@ void CDatabase::GetItemName(unsigned index, NWindows::NCOM::CPropVariant &name) s[i] = Get16(meta + i * 2); } + void CDatabase::GetItemPath(unsigned index1, bool showImageNumber, NWindows::NCOM::CPropVariant &path) const { unsigned size = 0; @@ -403,22 +566,16 @@ void CDatabase::GetItemPath(unsigned index1, bool showImageNumber, NWindows::NCO } } -static bool IsEmptySha(const Byte *data) -{ - for (unsigned i = 0; i < kHashSize; i++) - if (data[i] != 0) - return false; - return true; -} -// Root folders in OLD archives (ver = 1.10) conatin real items. -// Root folders in NEW archives (ver > 1.11) contain only one folder with empty name. +// if (ver <= 1.10), root folder contains real items. +// if (ver >= 1.12), root folder contains only one folder with empty name. HRESULT CDatabase::ParseDirItem(size_t pos, int parent) { - if ((pos & 7) != 0) + const unsigned align = GetDirAlignMask(); + if ((pos & align) != 0) return S_FALSE; - + for (unsigned numItems = 0;; numItems++) { if (OpenCallback && (Items.Size() & 0xFFFF) == 0) @@ -426,25 +583,28 @@ HRESULT CDatabase::ParseDirItem(size_t pos, int parent) UInt64 numFiles = Items.Size(); RINOK(OpenCallback->SetCompleted(&numFiles, NULL)); } + size_t rem = DirSize - pos; if (pos < DirStartOffset || pos > DirSize || rem < 8) return S_FALSE; + const Byte *p = DirData + pos; + UInt64 len = Get64(p); if (len == 0) { - if (parent < 0 && numItems != 1) - Images.Back().NumEmptyRootItems = 0; DirProcessed += 8; return S_OK; } - if ((len & 7) != 0 || rem < len) + + if ((len & align) != 0 || rem < len) return S_FALSE; + DirProcessed += (size_t)len; if (DirProcessed > DirSize) return S_FALSE; - UInt32 dirRecordSize = IsOldVersion ? kDirRecordSizeOld : kDirRecordSize; + const unsigned dirRecordSize = IsOldVersion ? kDirRecordSizeOld : kDirRecordSize; if (len < dirRecordSize) return S_FALSE; @@ -452,14 +612,15 @@ HRESULT CDatabase::ParseDirItem(size_t pos, int parent) UInt32 attrib = Get32(p + 8); item.IsDir = ((attrib & 0x10) != 0); UInt64 subdirOffset = Get64(p + 0x10); - UInt32 numAltStreams = Get16(p + dirRecordSize - 6); - UInt32 shortNameLen = Get16(p + dirRecordSize - 4); - UInt32 fileNameLen = Get16(p + dirRecordSize - 2); + + const UInt32 numAltStreams = Get16(p + dirRecordSize - 6); + const UInt32 shortNameLen = Get16(p + dirRecordSize - 4); + const UInt32 fileNameLen = Get16(p + dirRecordSize - 2); if ((shortNameLen & 1) != 0 || (fileNameLen & 1) != 0) return S_FALSE; - UInt32 shortNameLen2 = (shortNameLen == 0 ? shortNameLen : shortNameLen + 2); - UInt32 fileNameLen2 = (fileNameLen == 0 ? fileNameLen : fileNameLen + 2); - if (((dirRecordSize + fileNameLen2 + shortNameLen2 + 6) & ~7) > len) + const UInt32 shortNameLen2 = (shortNameLen == 0 ? shortNameLen : shortNameLen + 2); + const UInt32 fileNameLen2 = (fileNameLen == 0 ? fileNameLen : fileNameLen + 2); + if (((dirRecordSize + fileNameLen2 + shortNameLen2 + align) & ~align) > len) return S_FALSE; p += dirRecordSize; @@ -471,6 +632,9 @@ HRESULT CDatabase::ParseDirItem(size_t pos, int parent) if (*(const UInt16 *)(p + j) == 0) return S_FALSE; } + + // PRF(printf("\n%S", p)); + if (shortNameLen != 0) { // empty shortName has no ZERO at the end ? @@ -485,36 +649,27 @@ HRESULT CDatabase::ParseDirItem(size_t pos, int parent) item.Offset = pos; item.Parent = parent; item.ImageIndex = Images.Size() - 1; - unsigned prevIndex = Items.Add(item); + + const unsigned prevIndex = Items.Add(item); pos += (size_t)len; - unsigned numItems2 = Items.Size(); - for (UInt32 i = 0; i < numAltStreams; i++) { - size_t rem = DirSize - pos; + const size_t rem = DirSize - pos; if (pos < DirStartOffset || pos > DirSize || rem < 8) return S_FALSE; const Byte *p = DirData + pos; - UInt64 len = Get64(p); - if (len == 0) + const UInt64 len = Get64(p); + if ((len & align) != 0 || rem < len || len < (IsOldVersion ? 0x18 : 0x28)) return S_FALSE; - if ((len & 7) != 0 || rem < len) - return S_FALSE; - if (IsOldVersion) - { - if (len < 0x18) - return S_FALSE; - } - else - if (len < 0x28) - return S_FALSE; + DirProcessed += (size_t)len; if (DirProcessed > DirSize) return S_FALSE; unsigned extraOffset = 0; + if (IsOldVersion) extraOffset = 0x10; else @@ -523,13 +678,14 @@ HRESULT CDatabase::ParseDirItem(size_t pos, int parent) return S_FALSE; extraOffset = 0x24; } + UInt32 fileNameLen = Get16(p + extraOffset); if ((fileNameLen & 1) != 0) return S_FALSE; /* Probably different versions of ImageX can use different number of additional ZEROs. So we don't use exact check. */ UInt32 fileNameLen2 = (fileNameLen == 0 ? fileNameLen : fileNameLen + 2); - if (((extraOffset + 2 + fileNameLen2 + 6) & ~7) > len) + if (((extraOffset + 2 + fileNameLen2 + align) & ~align) > len) return S_FALSE; { @@ -539,21 +695,30 @@ HRESULT CDatabase::ParseDirItem(size_t pos, int parent) for (UInt32 j = 0; j < fileNameLen; j += 2) if (*(const UInt16 *)(p2 + j) == 0) return S_FALSE; + + // PRF(printf("\n %S", p2)); } /* wim uses alt sreams list, if there is at least one alt stream. - And alt stream without name is main stream. */ + And alt stream without name is main stream. */ + + // Why wimlib writes two alt streams for REPARSE_POINT, with empty second alt stream? + Byte *prevMeta = DirData + item.Offset; + if (fileNameLen == 0 && - (attrib & FILE_ATTRIBUTE_REPARSE_POINT - || !item.IsDir /* && (IsOldVersion || IsEmptySha(prevMeta + 0x40)) */ )) + ((attrib & FILE_ATTRIBUTE_REPARSE_POINT) || !item.IsDir) + && (IsOldVersion || IsEmptySha(prevMeta + 0x40))) { - Byte *prevMeta = DirData + item.Offset; if (IsOldVersion) memcpy(prevMeta + 0x10, p + 8, 4); // It's 32-bit Id - else - memcpy(prevMeta + 0x40, p + 0x10, kHashSize); + else if (!IsEmptySha(p + 0x10)) + { + // if (IsEmptySha(prevMeta + 0x40)) + memcpy(prevMeta + 0x40, p + 0x10, kHashSize); + // else HeadersError = true; + } } else { @@ -565,13 +730,31 @@ HRESULT CDatabase::ParseDirItem(size_t pos, int parent) item2.ImageIndex = Images.Size() - 1; Items.Add(item2); } + pos += (size_t)len; } if (parent < 0 && numItems == 0 && shortNameLen == 0 && fileNameLen == 0 && item.IsDir) { - CImage &image = Images.Back(); - image.NumEmptyRootItems = numItems2 - image.StartItem; // Items.Size() + const Byte *p2 = DirData + pos; + if (DirSize - pos >= 8 && Get64(p2) == 0) + { + CImage &image = Images.Back(); + image.NumEmptyRootItems = 1; + + if (subdirOffset != 0 + && DirSize - pos >= 16 + && Get64(p2 + 8) != 0 + && pos + 8 < subdirOffset) + { + // Longhorn.4093 contains hidden files after empty root folder and before items of next folder. Why? + // That code shows them. If we want to ignore them, we need to update DirProcessed. + // DirProcessed += (size_t)(subdirOffset - (pos + 8)); + // printf("\ndirOffset = %5d hiddenOffset = %5d\n", (int)subdirOffset, (int)pos + 8); + subdirOffset = pos + 8; + // return S_FALSE; + } + } } if (item.IsDir && subdirOffset != 0) @@ -581,6 +764,7 @@ HRESULT CDatabase::ParseDirItem(size_t pos, int parent) } } + HRESULT CDatabase::ParseImageDirs(CByteBuffer &buf, int parent) { DirData = buf; @@ -593,29 +777,37 @@ HRESULT CDatabase::ParseImageDirs(CByteBuffer &buf, int parent) if (IsOldVersion) { - // there is no specification about that code - UInt32 sum = 0; - image.SecurOffsets.Add(0); - for (;;) + UInt32 numEntries = Get32(p + 4); + + if (numEntries > (1 << 28) || + numEntries > (DirSize >> 3)) + return S_FALSE; + + UInt32 sum = 8; + if (numEntries != 0) + sum = numEntries * 8; + + image.SecurOffsets.ClearAndReserve(numEntries + 1); + image.SecurOffsets.AddInReserved(sum); + + for (UInt32 i = 0; i < numEntries; i++) { - if (pos + 8 > DirSize) + const Byte *pp = p + (size_t)i * 8; + UInt32 len = Get32(pp); + if (i != 0 && Get32(pp + 4) != 0) return S_FALSE; - UInt32 len = Get32(p + pos); if (len > DirSize - sum) return S_FALSE; sum += len; - image.SecurOffsets.Add(sum); - UInt32 n = Get32(p + pos + 4); // what does this field mean? - pos += 8; - if (n == 0) - break; + if (sum < len) + return S_FALSE; + image.SecurOffsets.AddInReserved(sum); } - if (sum > DirSize - pos) - return S_FALSE; - FOR_VECTOR (i, image.SecurOffsets) - image.SecurOffsets[i] += (UInt32)pos; - pos += sum; - pos = (pos + 7) & ~(size_t)7; + + pos = sum; + + const size_t align = GetDirAlignMask(); + pos = (pos + align) & ~(size_t)align; } else { @@ -633,6 +825,7 @@ HRESULT CDatabase::ParseImageDirs(CByteBuffer &buf, int parent) UInt32 sum = (UInt32)pos + numEntries * 8; image.SecurOffsets.ClearAndReserve(numEntries + 1); image.SecurOffsets.AddInReserved(sum); + for (UInt32 i = 0; i < numEntries; i++, pos += 8) { UInt64 len = Get64(p + pos); @@ -641,6 +834,7 @@ HRESULT CDatabase::ParseImageDirs(CByteBuffer &buf, int parent) sum += (UInt32)len; image.SecurOffsets.AddInReserved(sum); } + pos = sum; pos = (pos + 7) & ~(size_t)7; if (pos != (((size_t)totalLen + 7) & ~(size_t)7)) @@ -650,21 +844,27 @@ HRESULT CDatabase::ParseImageDirs(CByteBuffer &buf, int parent) if (pos > DirSize) return S_FALSE; + DirStartOffset = DirProcessed = pos; image.StartItem = Items.Size(); + RINOK(ParseDirItem(pos, parent)); + image.NumItems = Items.Size() - image.StartItem; if (DirProcessed == DirSize) return S_OK; + /* Original program writes additional 8 bytes (END_OF_ROOT_FOLDER), but the reference to that folder is empty */ // we can't use DirProcessed - DirStartOffset == 112 check if there is alt stream in root - if (DirProcessed == DirSize - 8 && Get64(p + DirSize - 8) == 0) + if (DirProcessed == DirSize - 8 && Get64(p + DirSize - 8) != 0) return S_OK; + return S_FALSE; } + HRESULT CHeader::Parse(const Byte *p, UInt64 &phySize) { UInt32 headerSize = Get32(p + 8); @@ -673,10 +873,37 @@ HRESULT CHeader::Parse(const Byte *p, UInt64 &phySize) Flags = Get32(p + 0x10); if (!IsSupported()) return S_FALSE; - ChunkSize = Get32(p + 0x14); - if (ChunkSize != kChunkSize && ChunkSize != 0) - return S_FALSE; + + { + ChunkSize = Get32(p + 0x14); + ChunkSizeBits = kChunkSizeBits; + if (ChunkSize != 0) + { + int log = GetLog(ChunkSize); + if (log < 12) + return S_FALSE; + ChunkSizeBits = log; + } + } + + _IsOldVersion = false; + _IsNewVersion = false; + + if (IsSolidVersion()) + _IsNewVersion = true; + else + { + if (Version < 0x010900) + return S_FALSE; + _IsOldVersion = (Version <= 0x010A00); + // We don't know details about 1.11 version. So we use headerSize to guess exact features. + if (Version == 0x010B00 && headerSize == 0x60) + _IsOldVersion = true; + _IsNewVersion = (Version >= 0x010D00); + } + unsigned offset; + if (IsOldVersion()) { if (headerSize != 0x60) @@ -693,21 +920,26 @@ HRESULT CHeader::Parse(const Byte *p, UInt64 &phySize) memcpy(Guid, p + 0x18, 16); PartNumber = Get16(p + 0x28); NumParts = Get16(p + 0x2A); + if (PartNumber == 0 || PartNumber > NumParts) + return S_FALSE; offset = 0x2C; if (IsNewVersion()) { + // if (headerSize < 0xD0) + if (headerSize != 0xD0) + return S_FALSE; NumImages = Get32(p + offset); offset += 4; } } + GET_RESOURCE(p + offset , OffsetResource); GET_RESOURCE(p + offset + 0x18, XmlResource); GET_RESOURCE(p + offset + 0x30, MetadataResource); BootIndex = 0; + if (IsNewVersion()) { - if (headerSize < 0xD0) - return S_FALSE; BootIndex = Get32(p + offset + 0x48); GET_RESOURCE(p + offset + 0x4C, IntegrityResource); } @@ -715,6 +947,7 @@ HRESULT CHeader::Parse(const Byte *p, UInt64 &phySize) return S_OK; } + const Byte kSignature[kSignatureSize] = { 'M', 'S', 'W', 'I', 'M', 0, 0, 0 }; HRESULT ReadHeader(IInStream *inStream, CHeader &h, UInt64 &phySize) @@ -726,40 +959,85 @@ HRESULT ReadHeader(IInStream *inStream, CHeader &h, UInt64 &phySize) return h.Parse(p, phySize); } + static HRESULT ReadStreams(IInStream *inStream, const CHeader &h, CDatabase &db) { CByteBuffer offsetBuf; - RINOK(UnpackData(inStream, h.OffsetResource, h.IsLzxMode(), offsetBuf, NULL)); - size_t i; - size_t streamInfoSize = h.IsOldVersion() ? kStreamInfoSize + 2 : kStreamInfoSize; - for (i = 0; offsetBuf.Size() - i >= streamInfoSize; i += streamInfoSize) + + CUnpacker unpacker; + RINOK(unpacker.UnpackData(inStream, h.OffsetResource, h, NULL, offsetBuf, NULL)); + + const size_t streamInfoSize = h.IsOldVersion() ? kStreamInfoSize + 2 : kStreamInfoSize; + unsigned numItems = (unsigned)(offsetBuf.Size() / streamInfoSize); + if ((size_t)numItems * streamInfoSize != offsetBuf.Size()) + return S_FALSE; + db.DataStreams.Reserve(numItems); + + bool keepSolid = false; + + for (size_t i = 0; i < offsetBuf.Size(); i += streamInfoSize) { CStreamInfo s; - GetStream(h.IsOldVersion(), (const Byte *)offsetBuf + i, s); - if (s.PartNumber == h.PartNumber) + ParseStream(h.IsOldVersion(), (const Byte *)offsetBuf + i, s); + + PRF(printf("\n")); + PRF(printf(s.Resource.IsMetadata() ? "### META" : " DATA")); + PRF(printf(" %2X", s.Resource.Flags)); + PRF(printf(" %9I64X", s.Resource.Offset)); + PRF(printf(" %9I64X", s.Resource.PackSize)); + PRF(printf(" %9I64X", s.Resource.UnpackSize)); + PRF(printf(" %d", s.RefCount)); + + if (s.PartNumber != h.PartNumber) + continue; + + if (s.Resource.IsSolid()) { - if (s.Resource.IsMetadata()) + s.Resource.KeepSolid = keepSolid; + keepSolid = true; + } + else + { + s.Resource.KeepSolid = false; + keepSolid = false; + } + + if (!s.Resource.IsMetadata()) + db.DataStreams.AddInReserved(s); + else + { + if (s.Resource.IsSolid()) + return E_NOTIMPL; + if (s.RefCount == 0) { - if (s.RefCount == 0) - return S_FALSE; - if (s.RefCount > 1) - { - s.RefCount--; - db.DataStreams.Add(s); - } - s.RefCount = 1; - db.MetaStreams.Add(s); + // some wims have such (deleted?) metadata stream. + // examples: boot.wim in VistaBeta2, WinPE.wim from WAIK. + // db.DataStreams.Add(s); + // we can show these delete images, if we comment "continue" command; + continue; } - else - db.DataStreams.Add(s); + + if (s.RefCount > 1) + { + return S_FALSE; + // s.RefCount--; + // db.DataStreams.Add(s); + } + + db.MetaStreams.Add(s); } } - return (i == offsetBuf.Size()) ? S_OK : S_FALSE; + + PRF(printf("\n")); + + return S_OK; } + HRESULT CDatabase::OpenXml(IInStream *inStream, const CHeader &h, CByteBuffer &xml) { - return UnpackData(inStream, h.XmlResource, h.IsLzxMode(), xml, NULL); + CUnpacker unpacker; + return unpacker.UnpackData(inStream, h.XmlResource, h, this, xml, NULL); } static void SetRootNames(CImage &image, unsigned value) @@ -777,66 +1055,63 @@ static void SetRootNames(CImage &image, unsigned value) } } + HRESULT CDatabase::Open(IInStream *inStream, const CHeader &h, unsigned numItemsReserve, IArchiveOpenCallback *openCallback) { OpenCallback = openCallback; IsOldVersion = h.IsOldVersion(); + IsOldVersion9 = (h.Version == 0x10900); + RINOK(ReadStreams(inStream, h, *this)); - // printf("\nh.PartNumber = %02d", (unsigned)h.PartNumber); bool needBootMetadata = !h.MetadataResource.IsEmpty(); + unsigned numNonDeletedImages = 0; + + CUnpacker unpacker; FOR_VECTOR (i, MetaStreams) { const CStreamInfo &si = MetaStreams[i]; - /* - printf("\ni = %5d" - " Refs = %3d" - " Part = %1d" - " Offs = %7X" - " PackSize = %7X" - " Size = %7X" - " Flags = %d " - , - i, - si.RefCount, - (unsigned)si.PartNumber, - (unsigned)si.Resource.Offset, - (unsigned)si.Resource.PackSize, - (unsigned)si.Resource.UnpackSize, - (unsigned)si.Resource.Flags - ); - for (unsigned y = 0; y < 2; y++) - printf("%02X", (unsigned)si.Hash[y]); - */ if (h.PartNumber != 1 || si.PartNumber != h.PartNumber) continue; - Byte hash[kHashSize]; + const int userImage = Images.Size() + GetStartImageIndex(); CImage &image = Images.AddNew(); - SetRootNames(image, Images.Size()); + SetRootNames(image, userImage); + CByteBuffer &metadata = image.Meta; - RINOK(UnpackData(inStream, si.Resource, h.IsLzxMode(), metadata, hash)); + Byte hash[kHashSize]; + + RINOK(unpacker.UnpackData(inStream, si.Resource, h, this, metadata, hash)); + if (memcmp(hash, si.Hash, kHashSize) != 0 && !(h.IsOldVersion() && IsEmptySha(si.Hash))) return S_FALSE; + image.NumEmptyRootItems = 0; + if (Items.IsEmpty()) Items.ClearAndReserve(numItemsReserve); + RINOK(ParseImageDirs(metadata, -1)); + if (needBootMetadata) { bool sameRes = (h.MetadataResource.Offset == si.Resource.Offset); if (sameRes) needBootMetadata = false; - bool isBootIndex = (h.BootIndex == (UInt32)Images.Size()); if (h.IsNewVersion()) { - if (sameRes && !isBootIndex) - return S_FALSE; - if (isBootIndex && !sameRes) - return S_FALSE; + if (si.RefCount == 1) + { + numNonDeletedImages++; + bool isBootIndex = (h.BootIndex == numNonDeletedImages); + if (sameRes && !isBootIndex) + return S_FALSE; + if (isBootIndex && !sameRes) + return S_FALSE; + } } } } @@ -847,6 +1122,25 @@ HRESULT CDatabase::Open(IInStream *inStream, const CHeader &h, unsigned numItems } +bool CDatabase::ItemHasStream(const CItem &item) const +{ + if (item.ImageIndex < 0) + return true; + const Byte *meta = Images[item.ImageIndex].Meta + item.Offset; + if (IsOldVersion) + { + // old wim use same field for file_id and dir_offset; + if (item.IsDir) + return false; + meta += (item.IsAltStream ? 0x8 : 0x10); + UInt32 id = GetUi32(meta); + return id != 0; + } + meta += (item.IsAltStream ? 0x10 : 0x40); + return !IsEmptySha(meta); +} + + #define RINOZ(x) { int __tt = (x); if (__tt != 0) return __tt; } static int CompareStreamsByPos(const CStreamInfo *p1, const CStreamInfo *p2, void * /* param */) @@ -858,24 +1152,23 @@ static int CompareStreamsByPos(const CStreamInfo *p1, const CStreamInfo *p2, voi static int CompareIDs(const unsigned *p1, const unsigned *p2, void *param) { - const CRecordVector<CStreamInfo> &streams = *(const CRecordVector<CStreamInfo> *)param; + const CStreamInfo *streams = (const CStreamInfo *)param; return MyCompare(streams[*p1].Id, streams[*p2].Id); } static int CompareHashRefs(const unsigned *p1, const unsigned *p2, void *param) { - const CRecordVector<CStreamInfo> &streams = *(const CRecordVector<CStreamInfo> *)param; + const CStreamInfo *streams = (const CStreamInfo *)param; return memcmp(streams[*p1].Hash, streams[*p2].Hash, kHashSize); } -static int FindId(const CRecordVector<CStreamInfo> &streams, - const CUIntVector &sortedByHash, UInt32 id) +static int FindId(const CStreamInfo *streams, const CUIntVector &sorted, UInt32 id) { - unsigned left = 0, right = streams.Size(); + unsigned left = 0, right = sorted.Size(); while (left != right) { unsigned mid = (left + right) / 2; - unsigned streamIndex = sortedByHash[mid]; + unsigned streamIndex = sorted[mid]; UInt32 id2 = streams[streamIndex].Id; if (id == id2) return streamIndex; @@ -887,14 +1180,13 @@ static int FindId(const CRecordVector<CStreamInfo> &streams, return -1; } -static int FindHash(const CRecordVector<CStreamInfo> &streams, - const CUIntVector &sortedByHash, const Byte *hash) +static int FindHash(const CStreamInfo *streams, const CUIntVector &sorted, const Byte *hash) { - unsigned left = 0, right = streams.Size(); + unsigned left = 0, right = sorted.Size(); while (left != right) { unsigned mid = (left + right) / 2; - unsigned streamIndex = sortedByHash[mid]; + unsigned streamIndex = sorted[mid]; const Byte *hash2 = streams[streamIndex].Hash; unsigned i; for (i = 0; i < kHashSize; i++) @@ -910,27 +1202,6 @@ static int FindHash(const CRecordVector<CStreamInfo> &streams, return -1; } -bool CDatabase::ItemHasStream(const CItem &item) const -{ - if (item.ImageIndex < 0) - return true; - const Byte *meta = Images[item.ImageIndex].Meta + item.Offset; - if (IsOldVersion) - { - // old wim use same field for file_id and dir_offset; - if (item.IsDir) - return false; - meta += (item.IsAltStream ? 0x8 : 0x10); - UInt32 id = GetUi32(meta); - return id != 0; - } - meta += (item.IsAltStream ? 0x10 : 0x40); - for (unsigned i = 0; i < kHashSize; i++) - if (meta[i] != 0) - return true; - return false; -} - static int CompareItems(const unsigned *a1, const unsigned *a2, void *param) { const CRecordVector<CItem> &items = ((CDatabase *)param)->Items; @@ -946,10 +1217,190 @@ static int CompareItems(const unsigned *a1, const unsigned *a2, void *param) return MyCompare(i1.Offset, i2.Offset); } -HRESULT CDatabase::FillAndCheck() + +HRESULT CDatabase::FillAndCheck(const CObjectVector<CVolume> &volumes) { + CUIntVector sortedByHash; + sortedByHash.Reserve(DataStreams.Size()); { - DataStreams.Sort(CompareStreamsByPos, NULL); + CByteBuffer sizesBuf; + + for (unsigned iii = 0; iii < DataStreams.Size();) + { + { + const CResource &r = DataStreams[iii].Resource; + if (!r.IsSolid()) + { + sortedByHash.AddInReserved(iii++); + continue; + } + } + + UInt64 solidRunOffset = 0; + unsigned k; + unsigned numSolidsStart = Solids.Size(); + + for (k = iii; k < DataStreams.Size(); k++) + { + CStreamInfo &si = DataStreams[k]; + CResource &r = si.Resource; + + if (!r.IsSolid()) + break; + if (!r.KeepSolid && k != iii) + break; + + if (r.Flags != NResourceFlags::kSolid) + return S_FALSE; + + if (!r.IsSolidBig()) + continue; + + if (!si.IsEmptyHash()) + return S_FALSE; + if (si.RefCount != 1) + return S_FALSE; + + r.SolidIndex = Solids.Size(); + + CSolid &ss = Solids.AddNew(); + ss.StreamIndex = k; + ss.SolidOffset = solidRunOffset; + { + const size_t kSolidHeaderSize = 8 + 4 + 4; + Byte header[kSolidHeaderSize]; + + if (si.PartNumber >= volumes.Size()) + return S_FALSE; + + const CVolume &vol = volumes[si.PartNumber]; + IInStream *inStream = vol.Stream; + RINOK(inStream->Seek(r.Offset, STREAM_SEEK_SET, NULL)); + RINOK(ReadStream_FALSE(inStream, (Byte *)header, kSolidHeaderSize)); + + ss.UnpackSize = GetUi64(header); + + if (ss.UnpackSize > ((UInt64)1 << 63)) + return S_FALSE; + + solidRunOffset += ss.UnpackSize; + if (solidRunOffset < ss.UnpackSize) + return S_FALSE; + + const UInt32 solidChunkSize = GetUi32(header + 8); + int log = GetLog(solidChunkSize); + if (log < 8 || log > 31) + return S_FALSE; + ss.ChunkSizeBits = log; + ss.Method = GetUi32(header + 12); + + UInt64 numChunks64 = (ss.UnpackSize + (((UInt32)1 << ss.ChunkSizeBits) - 1)) >> ss.ChunkSizeBits; + UInt64 sizesBufSize64 = 4 * numChunks64; + ss.HeadersSize = kSolidHeaderSize + sizesBufSize64; + size_t sizesBufSize = (size_t)sizesBufSize64; + if (sizesBufSize != sizesBufSize64) + return E_OUTOFMEMORY; + sizesBuf.AllocAtLeast(sizesBufSize); + + RINOK(ReadStream_FALSE(inStream, sizesBuf, sizesBufSize)); + + size_t numChunks = (size_t)numChunks64; + ss.Chunks.Alloc(numChunks + 1); + + UInt64 offset = 0; + + size_t c; + for (c = 0; c < numChunks; c++) + { + ss.Chunks[c] = offset; + UInt32 packSize = GetUi32((const Byte *)sizesBuf + c * 4); + offset += packSize; + if (offset < packSize) + return S_FALSE; + } + ss.Chunks[c] = offset; + + if (ss.Chunks[0] != 0) + return S_FALSE; + if (ss.HeadersSize + offset != r.PackSize) + return S_FALSE; + } + } + + unsigned solidLim = k; + + for (k = iii; k < solidLim; k++) + { + CStreamInfo &si = DataStreams[k]; + CResource &r = si.Resource; + + if (!r.IsSolidSmall()) + continue; + + if (si.IsEmptyHash()) + return S_FALSE; + + unsigned solidIndex; + { + UInt64 offset = r.Offset; + for (solidIndex = numSolidsStart;; solidIndex++) + { + if (solidIndex == Solids.Size()) + return S_FALSE; + UInt64 unpackSize = Solids[solidIndex].UnpackSize; + if (offset < unpackSize) + break; + offset -= unpackSize; + } + } + CSolid &ss = Solids[solidIndex]; + if (r.Offset < ss.SolidOffset) + return S_FALSE; + UInt64 relat = r.Offset - ss.SolidOffset; + if (relat > ss.UnpackSize) + return S_FALSE; + if (r.PackSize > ss.UnpackSize - relat) + return S_FALSE; + r.SolidIndex = solidIndex; + if (ss.FirstSmallStream < 0) + ss.FirstSmallStream = k; + + sortedByHash.AddInReserved(k); + // ss.NumRefs++; + } + + iii = solidLim; + } + } + + if (Solids.IsEmpty()) + { + /* We want to check that streams layout is OK. + So we need resources sorted by offset. + Another code can work with non-sorted streams. + NOTE: all WIM programs probably create wim archives with + sorted data streams. So it doesn't call Sort() here. */ + + { + unsigned i; + for (i = 1; i < DataStreams.Size(); i++) + { + const CStreamInfo &s0 = DataStreams[i - 1]; + const CStreamInfo &s1 = DataStreams[i]; + if (s0.PartNumber < s1.PartNumber) continue; + if (s0.PartNumber > s1.PartNumber) break; + if (s0.Resource.Offset < s1.Resource.Offset) continue; + if (s0.Resource.Offset > s1.Resource.Offset) break; + if (s0.Resource.PackSize > s1.Resource.PackSize) break; + } + + if (i < DataStreams.Size()) + { + // return E_FAIL; + DataStreams.Sort(CompareStreamsByPos, NULL); + } + } + for (unsigned i = 1; i < DataStreams.Size(); i++) { const CStreamInfo &s0 = DataStreams[i - 1]; @@ -961,17 +1412,35 @@ HRESULT CDatabase::FillAndCheck() } { - CUIntVector sortedByHash; { - unsigned num = DataStreams.Size(); - sortedByHash.ClearAndSetSize(num); - unsigned *vals = &sortedByHash[0]; - for (unsigned i = 0; i < num; i++) - vals[i] = i; + const CStreamInfo *streams = &DataStreams.Front(); + if (IsOldVersion) - sortedByHash.Sort(CompareIDs, &DataStreams); + { + sortedByHash.Sort(CompareIDs, (void *)streams); + + for (unsigned i = 1; i < sortedByHash.Size(); i++) + if (streams[sortedByHash[i - 1]].Id >= + streams[sortedByHash[i]].Id) + return S_FALSE; + } else - sortedByHash.Sort(CompareHashRefs, &DataStreams); + { + sortedByHash.Sort(CompareHashRefs, (void *)streams); + + if (!sortedByHash.IsEmpty()) + { + if (IsEmptySha(streams[sortedByHash[0]].Hash)) + HeadersError = true; + + for (unsigned i = 1; i < sortedByHash.Size(); i++) + if (memcmp( + streams[sortedByHash[i - 1]].Hash, + streams[sortedByHash[i]].Hash, + kHashSize) >= 0) + return S_FALSE; + } + } } FOR_VECTOR (i, Items) @@ -986,7 +1455,7 @@ HRESULT CDatabase::FillAndCheck() hash += (item.IsAltStream ? 0x8 : 0x10); UInt32 id = GetUi32(hash); if (id != 0) - item.StreamIndex = FindId(DataStreams, sortedByHash, id); + item.StreamIndex = FindId(&DataStreams.Front(), sortedByHash, id); } } /* @@ -998,13 +1467,9 @@ HRESULT CDatabase::FillAndCheck() else { hash += (item.IsAltStream ? 0x10 : 0x40); - unsigned hi; - for (hi = 0; hi < kHashSize; hi++) - if (hash[hi] != 0) - break; - if (hi != kHashSize) + if (!IsEmptySha(hash)) { - item.StreamIndex = FindHash(DataStreams, sortedByHash, hash); + item.StreamIndex = FindHash(&DataStreams.Front(), sortedByHash, hash); } } } @@ -1035,29 +1500,36 @@ HRESULT CDatabase::FillAndCheck() for (i = 0; i < DataStreams.Size(); i++) { const CStreamInfo &s = DataStreams[i]; - if (s.RefCount != refCounts[i]) + if (s.RefCount != refCounts[i] + && !s.Resource.IsSolidBig()) { /* - printf("\ni = %5d si.Refcount = %d realRefs = %d size = %6d offset = %6x id = %4d ", + printf("\ni=%5d si.Ref=%2d realRefs=%2d size=%8d offset=%8x id=%4d ", i, s.RefCount, refCounts[i], (unsigned)s.Resource.UnpackSize, (unsigned)s.Resource.Offset, s.Id); */ - // return S_FALSE; RefCountError = true; } + if (refCounts[i] == 0) { - CItem item; - item.Offset = 0; - item.StreamIndex = i; - item.ImageIndex = -1; - ThereAreDeletedStreams = true; - Items.Add(item); + const CResource &r = DataStreams[i].Resource; + if (!r.IsSolidBig() || Solids[r.SolidIndex].FirstSmallStream < 0) + { + CItem item; + item.Offset = 0; + item.StreamIndex = i; + item.ImageIndex = -1; + Items.Add(item); + ThereAreDeletedStreams = true; + } } } } + return S_OK; } + HRESULT CDatabase::GenerateSortedItems(int imageIndex, bool showImageNumber) { SortedItems.Clear(); @@ -1117,9 +1589,11 @@ HRESULT CDatabase::GenerateSortedItems(int imageIndex, bool showImageNumber) image.VirtualRootIndex = VirtualRoots.Size(); VirtualRoots.Add(i); } + return S_OK; } + static void IntVector_SetMinusOne_IfNeed(CIntVector &v, unsigned size) { if (v.Size() == size) @@ -1130,6 +1604,7 @@ static void IntVector_SetMinusOne_IfNeed(CIntVector &v, unsigned size) vals[i] = -1; } + HRESULT CDatabase::ExtractReparseStreams(const CObjectVector<CVolume> &volumes, IArchiveOpenCallback *openCallback) { ItemToReparse.Clear(); @@ -1140,11 +1615,14 @@ HRESULT CDatabase::ExtractReparseStreams(const CObjectVector<CVolume> &volumes, return S_OK; CIntVector streamToReparse; + CUnpacker unpacker; + UInt64 totalPackedPrev = 0; - FOR_VECTOR(i, Items) + FOR_VECTOR(indexInSorted, SortedItems) { - // maybe it's better to use sorted items for faster access? - const CItem &item = Items[i]; + // we use sorted items for faster access + unsigned itemIndex = SortedItems[indexInSorted]; + const CItem &item = Items[itemIndex]; if (!item.HasMetadata() || item.IsAltStream) continue; @@ -1173,10 +1651,14 @@ HRESULT CDatabase::ExtractReparseStreams(const CObjectVector<CVolume> &volumes, int reparseIndex = streamToReparse[item.StreamIndex]; CByteBuffer buf; - if (openCallback && (i & 0xFFFF) == 0) + if (openCallback) { - UInt64 numFiles = Items.Size(); - RINOK(openCallback->SetCompleted(&numFiles, NULL)); + if ((unpacker.TotalPacked - totalPackedPrev) >= ((UInt32)1 << 16)) + { + UInt64 numFiles = Items.Size(); + RINOK(openCallback->SetCompleted(&numFiles, &unpacker.TotalPacked)); + totalPackedPrev = unpacker.TotalPacked; + } } if (reparseIndex >= 0) @@ -1184,7 +1666,7 @@ HRESULT CDatabase::ExtractReparseStreams(const CObjectVector<CVolume> &volumes, const CByteBuffer &reparse = ReparseItems[reparseIndex]; if (tag == Get32(reparse)) { - ItemToReparse[i] = reparseIndex; + ItemToReparse[itemIndex] = reparseIndex; continue; } buf = reparse; @@ -1203,7 +1685,7 @@ HRESULT CDatabase::ExtractReparseStreams(const CObjectVector<CVolume> &volumes, */ Byte digest[kHashSize]; - HRESULT res = UnpackData(vol.Stream, si.Resource, vol.Header.IsLzxMode(), buf, digest); + HRESULT res = unpacker.UnpackData(vol.Stream, si.Resource, vol.Header, this, buf, digest); if (res == S_FALSE) continue; @@ -1226,7 +1708,7 @@ HRESULT CDatabase::ExtractReparseStreams(const CObjectVector<CVolume> &volumes, SetUi32(dest + 4, (UInt32)buf.Size()); if (buf.Size() != 0) memcpy(dest + 8, buf, buf.Size()); - ItemToReparse[i] = ReparseItems.Size() - 1; + ItemToReparse[itemIndex] = ReparseItems.Size() - 1; } return S_OK; @@ -1252,6 +1734,7 @@ static bool ParseNumber64(const AString &s, UInt64 &res) return *end == 0; } + static bool ParseNumber32(const AString &s, UInt32 &res) { UInt64 res64; @@ -1261,6 +1744,7 @@ static bool ParseNumber32(const AString &s, UInt32 &res) return true; } + static bool ParseTime(const CXmlItem &item, FILETIME &ft, const char *tag) { int index = item.FindSubTag(tag); @@ -1279,6 +1763,7 @@ static bool ParseTime(const CXmlItem &item, FILETIME &ft, const char *tag) return false; } + void CImageInfo::Parse(const CXmlItem &item) { CTimeDefined = ParseTime(item, CTime, "CREATIONTIME"); @@ -1310,8 +1795,10 @@ void CWimXml::ToUnicode(UString &s) s.ReleaseBuf_SetLen((unsigned)(chars - (const wchar_t *)s)); } + bool CWimXml::Parse() { + IsEncrypted = false; AString utf; { UString s; @@ -1328,16 +1815,36 @@ bool CWimXml::Parse() FOR_VECTOR (i, Xml.Root.SubItems) { const CXmlItem &item = Xml.Root.SubItems[i]; + if (item.IsTagged("IMAGE")) { CImageInfo imageInfo; imageInfo.Parse(item); - if (!imageInfo.IndexDefined || imageInfo.Index != (UInt32)Images.Size() + 1) + if (!imageInfo.IndexDefined) return false; + + if (imageInfo.Index != (UInt32)Images.Size() + 1) + { + // old wim (1.09) uses zero based image index + if (imageInfo.Index != (UInt32)Images.Size()) + return false; + } + imageInfo.ItemIndexInXml = i; Images.Add(imageInfo); } + + if (item.IsTagged("ESD")) + { + FOR_VECTOR (k, item.SubItems) + { + const CXmlItem &item2 = item.SubItems[k]; + if (item2.IsTagged("ENCRYPTED")) + IsEncrypted = true; + } + } } + return true; } diff --git a/CPP/7zip/Archive/Wim/WimIn.h b/CPP/7zip/Archive/Wim/WimIn.h index c3b93a8d..ac4a2bd8 100644 --- a/CPP/7zip/Archive/Wim/WimIn.h +++ b/CPP/7zip/Archive/Wim/WimIn.h @@ -3,12 +3,15 @@ #ifndef __ARCHIVE_WIM_IN_H #define __ARCHIVE_WIM_IN_H +#include "../../../../C/Alloc.h" + #include "../../../Common/MyBuffer.h" #include "../../../Common/MyXml.h" #include "../../../Windows/PropVariant.h" #include "../../Compress/CopyCoder.h" +#include "../../Compress/LzmsDecoder.h" #include "../../Compress/LzxDecoder.h" #include "../IArchive.h" @@ -16,8 +19,20 @@ namespace NArchive { namespace NWim { -const UInt32 kDirRecordSizeOld = 62; -const UInt32 kDirRecordSize = 102; +/* +WIM versions: +hexVer : headerSize : ver + : 1.07.01 - 1.08.01 : Longhorn.4001-4015 - another header, no signature, CAB compression +10900 : 60 : 1.09 : Longhorn.4029-4039 (2003) +10A00 : 60 : 1.10 : Longhorn.4083 (2004) image starting from 1 +10B00 : ?? : 1.11 : ?? +10C00 : 74 : 1.12 : Longhorn.4093 - VistaBeta1.5112 (2005) - (Multi-Part, SHA1) +10D00 : D0 : 1.13 : VistaBeta2 - Win10, (NumImages, BootIndex, IntegrityResource) +00E00 : D0 : 0.14 : LZMS, solid, esd, dism +*/ + +const unsigned kDirRecordSizeOld = 62; +const unsigned kDirRecordSize = 102; /* There is error in WIM specification about dwReparseTag, dwReparseReserved and liHardLink fields. @@ -114,85 +129,26 @@ const UInt32 kDirRecordSize = 102; */ -namespace NXpress { - -class CBitStream -{ - CInBuffer m_Stream; - UInt32 m_Value; - unsigned m_BitPos; -public: - bool Create(UInt32 bufferSize) { return m_Stream.Create(bufferSize); } - void SetStream(ISequentialInStream *s) { m_Stream.SetStream(s); } - - void Init() { m_Stream.Init(); m_BitPos = 0; } - // UInt64 GetProcessedSize() const { return m_Stream.GetProcessedSize() - m_BitPos / 8; } - Byte DirectReadByte() { return m_Stream.ReadByte(); } - - void Normalize() - { - if (m_BitPos < 16) - { - Byte b0 = m_Stream.ReadByte(); - Byte b1 = m_Stream.ReadByte(); - m_Value = (m_Value << 8) | b1; - m_Value = (m_Value << 8) | b0; - m_BitPos += 16; - } - } - - UInt32 GetValue(unsigned numBits) - { - Normalize(); - return (m_Value >> (m_BitPos - numBits)) & ((1 << numBits) - 1); - } - - void MovePos(unsigned numBits) { m_BitPos -= numBits; } - - UInt32 ReadBits(unsigned numBits) - { - UInt32 res = GetValue(numBits); - m_BitPos -= numBits; - return res; - } -}; - -const unsigned kNumHuffmanBits = 16; -const UInt32 kMatchMinLen = 3; -const UInt32 kNumLenSlots = 16; -const UInt32 kNumPosSlots = 16; -const UInt32 kNumPosLenSlots = kNumPosSlots * kNumLenSlots; -const UInt32 kMainTableSize = 256 + kNumPosLenSlots; - -class CDecoder -{ - CBitStream m_InBitStream; - CLzOutWindow m_OutWindowStream; - NCompress::NHuffman::CDecoder<kNumHuffmanBits, kMainTableSize> m_MainDecoder; - - HRESULT CodeSpec(UInt32 size); - HRESULT CodeReal(ISequentialInStream *inStream, ISequentialOutStream *outStream, UInt32 outSize); -public: - HRESULT Flush() { return m_OutWindowStream.Flush(); } - HRESULT Code(ISequentialInStream *inStream, ISequentialOutStream *outStream, UInt32 outSize); -}; - -} namespace NResourceFlags { - const Byte kFree = 1; - const Byte kMetadata = 2; - const Byte Compressed = 4; - // const Byte Spanned = 4; + // const Byte kFree = 1 << 0; + const Byte kMetadata = 1 << 1; + const Byte kCompressed = 1 << 2; + // const Byte kSpanned = 1 << 3; + const Byte kSolid = 1 << 4; } +const UInt64 k_SolidBig_Resource_Marker = (UInt64)1 << 32; + struct CResource { UInt64 PackSize; UInt64 Offset; UInt64 UnpackSize; Byte Flags; + bool KeepSolid; + int SolidIndex; void Clear() { @@ -200,7 +156,10 @@ struct CResource Offset = 0; UnpackSize = 0; Flags = 0; + KeepSolid = false; + SolidIndex = -1; } + UInt64 GetEndLimit() const { return Offset + PackSize; } void Parse(const Byte *p); void ParseAndUpdatePhySize(const Byte *p, UInt64 &phySize) @@ -212,59 +171,130 @@ struct CResource } void WriteTo(Byte *p) const; - bool IsFree() const { return (Flags & NResourceFlags::kFree) != 0; } + bool IsMetadata() const { return (Flags & NResourceFlags::kMetadata) != 0; } - bool IsCompressed() const { return (Flags & NResourceFlags::Compressed) != 0; } + bool IsCompressed() const { return (Flags & NResourceFlags::kCompressed) != 0; } + bool IsSolid() const { return (Flags & NResourceFlags::kSolid) != 0; } + bool IsSolidBig() const { return IsSolid() && UnpackSize == k_SolidBig_Resource_Marker; } + bool IsSolidSmall() const { return IsSolid() && UnpackSize == 0; } + bool IsEmpty() const { return (UnpackSize == 0); } }; + +struct CSolid +{ + unsigned StreamIndex; + // unsigned NumRefs; + int FirstSmallStream; + + UInt64 SolidOffset; + + UInt64 UnpackSize; + int Method; + int ChunkSizeBits; + + UInt64 HeadersSize; + // size_t NumChunks; + CObjArray<UInt64> Chunks; // [NumChunks + 1] (start offset) + + UInt64 GetChunkPackSize(size_t chunkIndex) const { return Chunks[chunkIndex + 1] - Chunks[chunkIndex]; } + + CSolid(): + FirstSmallStream(-1), + // NumRefs(0), + Method(-1) + {} +}; + + namespace NHeaderFlags { - const UInt32 kCompression = 2; - const UInt32 kReadOnly = 4; - const UInt32 kSpanned = 8; - const UInt32 kResourceOnly = 0x10; - const UInt32 kMetadataOnly = 0x20; - const UInt32 kWriteInProgress = 0x40; - const UInt32 kReparsePointFixup = 0x80; - const UInt32 kXPRESS = 0x20000; - const UInt32 kLZX = 0x40000; + const UInt32 kCompression = 1 << 1; + const UInt32 kReadOnly = 1 << 2; + const UInt32 kSpanned = 1 << 3; + const UInt32 kResourceOnly = 1 << 4; + const UInt32 kMetadataOnly = 1 << 5; + const UInt32 kWriteInProgress = 1 << 6; + const UInt32 kReparsePointFixup = 1 << 7; + + const UInt32 kXPRESS = (UInt32)1 << 17; + const UInt32 kLZX = (UInt32)1 << 18; + const UInt32 kLZMS = (UInt32)1 << 19; + + const UInt32 kMethodMask = 0xFFFE0000; } -const UInt32 kWimVersion = 0x010D00; + +namespace NMethod +{ + const UInt32 kXPRESS = 1; + const UInt32 kLZX = 2; + const UInt32 kLZMS = 3; +} + + +const UInt32 k_Version_NonSolid = 0x10D00; +const UInt32 k_Version_Solid = 0xE00; const unsigned kHeaderSizeMax = 0xD0; const unsigned kSignatureSize = 8; extern const Byte kSignature[kSignatureSize]; + const unsigned kChunkSizeBits = 15; -const UInt32 kChunkSize = (1 << kChunkSizeBits); +const UInt32 kChunkSize = (UInt32)1 << kChunkSizeBits; + struct CHeader { UInt32 Version; UInt32 Flags; UInt32 ChunkSize; + unsigned ChunkSizeBits; Byte Guid[16]; UInt16 PartNumber; UInt16 NumParts; UInt32 NumImages; - + UInt32 BootIndex; + + bool _IsOldVersion; // 1.10- + bool _IsNewVersion; // 1.13+ or 0.14 + CResource OffsetResource; CResource XmlResource; CResource MetadataResource; CResource IntegrityResource; - UInt32 BootIndex; void SetDefaultFields(bool useLZX); void WriteTo(Byte *p) const; HRESULT Parse(const Byte *p, UInt64 &phySize); + bool IsCompressed() const { return (Flags & NHeaderFlags::kCompression) != 0; } - bool IsSupported() const { return (!IsCompressed() || (Flags & NHeaderFlags::kLZX) != 0 || (Flags & NHeaderFlags::kXPRESS) != 0 ) ; } - bool IsLzxMode() const { return (Flags & NHeaderFlags::kLZX) != 0; } - bool IsSpanned() const { return (!IsCompressed() || (Flags & NHeaderFlags::kSpanned) != 0); } - bool IsOldVersion() const { return (Version <= 0x010A00); } - bool IsNewVersion() const { return (Version > 0x010C00); } + + bool IsSupported() const + { + return (!IsCompressed() + || (Flags & NHeaderFlags::kLZX) != 0 + || (Flags & NHeaderFlags::kXPRESS) != 0 + || (Flags & NHeaderFlags::kLZMS) != 0); + } + + unsigned GetMethod() const + { + if (!IsCompressed()) + return 0; + UInt32 mask = (Flags & NHeaderFlags::kMethodMask); + if (mask == 0) return 0; + if (mask == NHeaderFlags::kXPRESS) return NMethod::kXPRESS; + if (mask == NHeaderFlags::kLZX) return NMethod::kLZX; + if (mask == NHeaderFlags::kLZMS) return NMethod::kLZMS; + return mask; + } + + bool IsOldVersion() const { return _IsOldVersion; } + bool IsNewVersion() const { return _IsNewVersion; } + bool IsSolidVersion() const { return (Version == k_Version_Solid); } bool AreFromOnArchive(const CHeader &h) { @@ -272,7 +302,17 @@ struct CHeader } }; + const unsigned kHashSize = 20; + +inline bool IsEmptySha(const Byte *data) +{ + for (unsigned i = 0; i < kHashSize; i++) + if (data[i] != 0) + return false; + return true; +} + const unsigned kStreamInfoSize = 24 + 2 + 4 + kHashSize; struct CStreamInfo @@ -283,9 +323,12 @@ struct CStreamInfo UInt32 Id; // for OLD WIM format Byte Hash[kHashSize]; + bool IsEmptyHash() const { return IsEmptySha(Hash); } + void WriteTo(Byte *p) const; }; + struct CItem { size_t Offset; @@ -321,6 +364,7 @@ struct CImage CImage(): VirtualRootIndex(-1) {} }; + struct CImageInfo { bool CTimeDefined; @@ -345,6 +389,7 @@ struct CImageInfo void Parse(const CXmlItem &item); }; + struct CWimXml { CByteBuffer Data; @@ -354,6 +399,7 @@ struct CWimXml CObjectVector<CImageInfo> Images; UString FileName; + bool IsEncrypted; UInt64 GetTotalFilesAndDirs() const { @@ -365,14 +411,18 @@ struct CWimXml void ToUnicode(UString &s); bool Parse(); + + CWimXml(): IsEncrypted(false) {} }; + struct CVolume { CHeader Header; CMyComPtr<IInStream> Stream; }; + class CDatabase { Byte *DirData; @@ -386,9 +436,9 @@ class CDatabase public: CRecordVector<CStreamInfo> DataStreams; - - CRecordVector<CStreamInfo> MetaStreams; + + CObjectVector<CSolid> Solids; CRecordVector<CItem> Items; CObjectVector<CByteBuffer> ReparseItems; @@ -397,10 +447,15 @@ public: CObjectVector<CImage> Images; + bool IsOldVersion9; bool IsOldVersion; bool ThereAreDeletedStreams; bool ThereAreAltStreams; bool RefCountError; + bool HeadersError; + + bool GetStartImageIndex() const { return IsOldVersion9 ? 0 : 1; } + unsigned GetDirAlignMask() const { return IsOldVersion9 ? 3 : 7; } // User Items can contain all images or just one image from all. CUIntVector SortedItems; @@ -423,6 +478,31 @@ public: bool ItemHasStream(const CItem &item) const; + UInt64 Get_UnpackSize_of_Resource(const CResource &r) const + { + if (!r.IsSolid()) + return r.UnpackSize; + if (r.IsSolidSmall()) + return r.PackSize; + if (r.IsSolidBig() && r.SolidIndex >= 0) + return Solids[(unsigned)r.SolidIndex].UnpackSize; + return 0; + } + + UInt64 Get_PackSize_of_Resource(unsigned streamIndex) const + { + const CResource &r = DataStreams[streamIndex].Resource; + if (!r.IsSolidSmall()) + return r.PackSize; + if (r.SolidIndex >= 0) + { + const CSolid &ss = Solids[(unsigned)r.SolidIndex]; + if (ss.FirstSmallStream == (int)streamIndex) + return DataStreams[ss.StreamIndex].Resource.PackSize; + } + return 0; + } + UInt64 GetUnpackSize() const { UInt64 res = 0; @@ -442,8 +522,8 @@ public: void Clear() { DataStreams.Clear(); - MetaStreams.Clear(); + Solids.Clear(); Items.Clear(); ReparseItems.Clear(); @@ -458,6 +538,7 @@ public: ThereAreDeletedStreams = false; ThereAreAltStreams = false; RefCountError = false; + HeadersError = false; } CDatabase(): RefCountError(false) {} @@ -468,7 +549,7 @@ public: HRESULT OpenXml(IInStream *inStream, const CHeader &h, CByteBuffer &xml); HRESULT Open(IInStream *inStream, const CHeader &h, unsigned numItemsReserve, IArchiveOpenCallback *openCallback); - HRESULT FillAndCheck(); + HRESULT FillAndCheck(const CObjectVector<CVolume> &volumes); /* imageIndex showImageNumber NumImages @@ -484,23 +565,87 @@ public: HRESULT ReadHeader(IInStream *inStream, CHeader &header, UInt64 &phySize); + +struct CMidBuf +{ + Byte *Data; + size_t _size; + + CMidBuf(): Data(NULL), _size(0) {} + + void EnsureCapacity(size_t size) + { + if (size > _size) + { + ::MidFree(Data); + _size = 0; + Data = (Byte *)::MidAlloc(size); + if (Data) + _size = size; + } + } + + ~CMidBuf() { ::MidFree(Data); } +}; + + class CUnpacker { NCompress::CCopyCoder *copyCoderSpec; CMyComPtr<ICompressCoder> copyCoder; NCompress::NLzx::CDecoder *lzxDecoderSpec; - CMyComPtr<ICompressCoder> lzxDecoder; + CMyComPtr<IUnknown> lzxDecoder; - NXpress::CDecoder xpressDecoder; + NCompress::NLzms::CDecoder *lzmsDecoder; CByteBuffer sizesBuf; - HRESULT Unpack(IInStream *inStream, const CResource &res, bool lzxMode, - ISequentialOutStream *outStream, ICompressProgressInfo *progress); + CMidBuf packBuf; + CMidBuf unpackBuf; + + // solid resource + int _solidIndex; + size_t _unpackedChunkIndex; + + HRESULT UnpackChunk( + ISequentialInStream *inStream, + unsigned method, unsigned chunkSizeBits, + size_t inSize, size_t outSize, + ISequentialOutStream *outStream); + + HRESULT Unpack2( + IInStream *inStream, + const CResource &res, + const CHeader &header, + const CDatabase *db, + ISequentialOutStream *outStream, + ICompressProgressInfo *progress); + public: - HRESULT Unpack(IInStream *inStream, const CResource &res, bool lzxMode, - ISequentialOutStream *outStream, ICompressProgressInfo *progress, Byte *digest); + UInt64 TotalPacked; + + CUnpacker(): + lzmsDecoder(NULL), + _solidIndex(-1), + _unpackedChunkIndex(0), + TotalPacked(0) + {} + ~CUnpacker(); + + HRESULT Unpack( + IInStream *inStream, + const CResource &res, + const CHeader &header, + const CDatabase *db, + ISequentialOutStream *outStream, + ICompressProgressInfo *progress, + Byte *digest); + + HRESULT UnpackData(IInStream *inStream, + const CResource &resource, const CHeader &header, + const CDatabase *db, + CByteBuffer &buf, Byte *digest); }; }} diff --git a/CPP/7zip/Archive/Wim/WimRegister.cpp b/CPP/7zip/Archive/Wim/WimRegister.cpp index 3063dec4..6ad28acc 100644 --- a/CPP/7zip/Archive/Wim/WimRegister.cpp +++ b/CPP/7zip/Archive/Wim/WimRegister.cpp @@ -10,7 +10,7 @@ namespace NArchive { namespace NWim { REGISTER_ARC_IO( - "wim", "wim swm", 0, 0xE6, + "wim", "wim swm esd", 0, 0xE6, kSignature, 0, NArcInfoFlags::kAltStreams | diff --git a/CPP/7zip/Archive/XzHandler.cpp b/CPP/7zip/Archive/XzHandler.cpp index ada14fbf..11f4b444 100644 --- a/CPP/7zip/Archive/XzHandler.cpp +++ b/CPP/7zip/Archive/XzHandler.cpp @@ -351,7 +351,8 @@ struct COpenCallbackWrap static SRes OpenCallbackProgress(void *pp, UInt64 inSize, UInt64 /* outSize */) { COpenCallbackWrap *p = (COpenCallbackWrap *)pp; - p->Res = p->OpenCallback->SetCompleted(NULL, &inSize); + if (p->OpenCallback) + p->Res = p->OpenCallback->SetCompleted(NULL, &inSize); return (SRes)p->Res; } @@ -414,7 +415,10 @@ HRESULT CHandler::Open2(IInStream *inStream, /* UInt32 flags, */ IArchiveOpenCal } RINOK(inStream->Seek(0, STREAM_SEEK_END, &_stat.PhySize)); - RINOK(callback->SetTotal(NULL, &_stat.PhySize)); + if (callback) + { + RINOK(callback->SetTotal(NULL, &_stat.PhySize)); + } CSeekInStreamWrap inStreamImp(inStream); @@ -466,7 +470,7 @@ STDMETHODIMP CHandler::Open(IInStream *inStream, const UInt64 *, IArchiveOpenCal COM_TRY_BEGIN { Close(); - return Open2(inStream, /* 0, */ callback); + return Open2(inStream, callback); } COM_TRY_END } diff --git a/CPP/7zip/Archive/Zip/ZipUpdate.cpp b/CPP/7zip/Archive/Zip/ZipUpdate.cpp index 7d03056c..d4b321dc 100644 --- a/CPP/7zip/Archive/Zip/ZipUpdate.cpp +++ b/CPP/7zip/Archive/Zip/ZipUpdate.cpp @@ -8,7 +8,7 @@ #include "../../../Common/Defs.h" #include "../../../Common/StringConvert.h" -#include "../../../Windows/Defs.h" +#include "../../../Windows/TimeUtils.h" #include "../../../Windows/Thread.h" #include "../../Common/CreateCoder.h" @@ -61,6 +61,7 @@ static HRESULT WriteRange(IInStream *inStream, COutArchive &outArchive, return progress->SetRatioInfo(&range.Size, &range.Size); } + static void SetFileHeader( COutArchive &archive, const CCompressionMethodMode &options, @@ -108,6 +109,7 @@ static void SetFileHeader( } } + static void SetItemInfoFromCompressingResult(const CCompressingResult &compressingResult, bool isAesMode, Byte aesKeyMode, CItem &item) { @@ -134,6 +136,7 @@ static void SetItemInfoFromCompressingResult(const CCompressingResult &compressi } } + #ifndef _7ZIP_ST static THREAD_FUNC_DECL CoderThread(void *threadCoderInfo); @@ -188,7 +191,6 @@ struct CThreadInfo Thread.Wait(); Thread.Close(); } - }; void CThreadInfo::WaitAndCode() @@ -390,9 +392,11 @@ static HRESULT UpdateItemOldData( complexity += range.Size; archive.MoveCurPos(range.Size); } + return S_OK; } + static void WriteDirHeader(COutArchive &archive, const CCompressionMethodMode *options, const CUpdateItem &ui, CItemOut &item) { @@ -404,15 +408,65 @@ static void WriteDirHeader(COutArchive &archive, const CCompressionMethodMode *o archive.WriteLocalHeader_And_SeekToNextFile(item); } + +static inline bool IsZero_FILETIME(const FILETIME &ft) +{ + return (ft.dwHighDateTime == 0 && ft.dwLowDateTime == 0); +} + +static void UpdatePropsFromStream(CUpdateItem &item, ISequentialInStream *fileInStream, + IArchiveUpdateCallback *updateCallback, UInt64 &totalComplexity) +{ + CMyComPtr<IStreamGetProps> getProps; + fileInStream->QueryInterface(IID_IStreamGetProps, (void **)&getProps); + if (!getProps) + return; + + FILETIME cTime, aTime, mTime; + UInt64 size; + // UInt32 attrib; + if (getProps->GetProps(&size, &cTime, &aTime, &mTime, NULL) != S_OK) + return; + + if (size != item.Size && size != (UInt64)(Int64)-1) + { + Int64 newComplexity = totalComplexity + ((Int64)size - (Int64)item.Size); + if (newComplexity > 0) + { + totalComplexity = newComplexity; + updateCallback->SetTotal(totalComplexity); + } + item.Size = size; + } + + if (!IsZero_FILETIME(mTime)) + { + item.Ntfs_MTime = mTime; + FILETIME loc = { 0, 0 }; + if (FileTimeToLocalFileTime(&mTime, &loc)) + { + item.Time = 0; + NTime::FileTimeToDosTime(loc, item.Time); + } + } + + if (!IsZero_FILETIME(cTime)) item.Ntfs_CTime = cTime; + if (!IsZero_FILETIME(aTime)) item.Ntfs_ATime = aTime; + + // item.Attrib = attrib; +} + + static HRESULT Update2St( DECL_EXTERNAL_CODECS_LOC_VARS COutArchive &archive, CInArchive *inArchive, const CObjectVector<CItemEx> &inputItems, - const CObjectVector<CUpdateItem> &updateItems, + CObjectVector<CUpdateItem> &updateItems, const CCompressionMethodMode *options, const CByteBuffer *comment, IArchiveUpdateCallback *updateCallback, + UInt64 &totalComplexity, IArchiveUpdateCallbackFile *opCallback) { CLocalProgress *lps = new CLocalProgress; @@ -429,7 +483,7 @@ static HRESULT Update2St( lps->InSize = unpackSizeTotal; lps->OutSize = packSizeTotal; RINOK(lps->SetCur()); - const CUpdateItem &ui = updateItems[itemIndex]; + CUpdateItem &ui = updateItems[itemIndex]; CItemEx itemEx; CItemOut item; @@ -471,8 +525,10 @@ static HRESULT Update2St( } */ - // file Size can be 64-bit !!! + UpdatePropsFromStream(ui, fileInStream, updateCallback, totalComplexity); SetFileHeader(archive, *options, ui, item); + + // file Size can be 64-bit !!! archive.PrepareWriteCompressedData(item.Name.Len(), ui.Size, options->IsRealAesMode()); CCompressingResult compressingResult; CMyComPtr<IOutStream> outStream; @@ -508,6 +564,7 @@ static HRESULT Update2St( lps->SendRatio = true; lps->ProgressOffset += complexity; } + items.Add(item); lps->ProgressOffset += kLocalHeaderSize; } @@ -519,12 +576,13 @@ static HRESULT Update2St( return S_OK; } + static HRESULT Update2( DECL_EXTERNAL_CODECS_LOC_VARS COutArchive &archive, CInArchive *inArchive, const CObjectVector<CItemEx> &inputItems, - const CObjectVector<CUpdateItem> &updateItems, + CObjectVector<CUpdateItem> &updateItems, const CCompressionMethodMode *options, const CByteBuffer *comment, IArchiveUpdateCallback *updateCallback) @@ -567,6 +625,8 @@ static HRESULT Update2( complexity++; // end of central updateCallback->SetTotal(complexity); + UInt64 totalComplexity = complexity; + CAddCommon compressor(*options); complexity = 0; @@ -596,6 +656,7 @@ static HRESULT Update2( mtMode = false; Byte method = options->MethodSequence.Front(); + if (!mtMode) { if (options2.MethodInfo.FindProp(NCoderPropID::kNumThreads) < 0) @@ -643,7 +704,7 @@ static HRESULT Update2( return Update2St( EXTERNAL_CODECS_LOC_VARS archive, inArchive, - inputItems, updateItems, &options2, comment, updateCallback, opCallback); + inputItems, updateItems, &options2, comment, updateCallback, totalComplexity, opCallback); #ifndef _7ZIP_ST @@ -691,8 +752,8 @@ static HRESULT Update2( RINOK(threadInfo.CreateThread()); } } - unsigned mtItemIndex = 0; + unsigned mtItemIndex = 0; unsigned itemIndex = 0; int lastRealStreamItemIndex = -1; @@ -700,11 +761,12 @@ static HRESULT Update2( { if ((UInt32)threadIndices.Size() < numThreads && mtItemIndex < updateItems.Size()) { - const CUpdateItem &ui = updateItems[mtItemIndex++]; + CUpdateItem &ui = updateItems[mtItemIndex++]; if (!ui.NewData) continue; CItemEx itemEx; CItemOut item; + if (ui.NewProps) { if (ui.IsDir) @@ -719,7 +781,9 @@ static HRESULT Update2( if (item.IsDir()) continue; } + CMyComPtr<ISequentialInStream> fileInStream; + { NWindows::NSynchronization::CCriticalSectionLock lock(mtProgressMixerSpec->Mixer2->CriticalSection); HRESULT res = updateCallback->GetStream(ui.IndexInClient, &fileInStream); @@ -735,6 +799,7 @@ static HRESULT Update2( RINOK(res); if (!fileInStream) return E_INVALIDARG; + UpdatePropsFromStream(ui, fileInStream, updateCallback, totalComplexity); RINOK(updateCallback->SetOperationResult(NArchive::NUpdate::NOperationResult::kOK)); } @@ -760,6 +825,7 @@ static HRESULT Update2( break; } } + continue; } @@ -773,6 +839,7 @@ static HRESULT Update2( CItemEx itemEx; CItemOut item; + if (!ui.NewProps || !ui.NewData) { itemEx = inputItems[ui.IndexInArc]; @@ -784,6 +851,7 @@ static HRESULT Update2( if (ui.NewData) { bool isDir = ((ui.NewProps) ? ui.IsDir : item.IsDir()); + if (isDir) { WriteDirHeader(archive, options, ui, item); @@ -799,6 +867,7 @@ static HRESULT Update2( } CMemBlocks2 &memRef = refs.Refs[itemIndex]; + if (memRef.Defined) { CMyComPtr<IOutStream> outStream; @@ -834,6 +903,7 @@ static HRESULT Update2( DWORD lastError = GetLastError(); return lastError != 0 ? lastError : E_FAIL; } + unsigned t = (unsigned)(result - WAIT_OBJECT_0); if (t >= compressingCompletedEvents.Size()) return E_FAIL; @@ -844,6 +914,7 @@ static HRESULT Update2( RINOK(threadInfo.Result); threadIndices.Delete(t); compressingCompletedEvents.Delete(t); + if (t == 0) { RINOK(threadInfo.OutStreamSpec->WriteToRealStream()); @@ -868,17 +939,20 @@ static HRESULT Update2( { RINOK(UpdateItemOldData(archive, inArchive, itemEx, ui, item, progress, opCallback, complexity)); } + items.Add(item); complexity += kLocalHeaderSize; mtProgressMixerSpec->Mixer2->SetProgressOffset(complexity); itemIndex++; } + RINOK(mtCompressProgressMixer.SetRatioInfo(0, NULL, NULL)); archive.WriteCentralDir(items, comment); return S_OK; #endif } + static const size_t kCacheBlockSize = (1 << 20); static const size_t kCacheSize = (kCacheBlockSize << 2); static const size_t kCacheMask = (kCacheSize - 1); @@ -1095,7 +1169,7 @@ STDMETHODIMP CCacheOutStream::SetSize(UInt64 newSize) HRESULT Update( DECL_EXTERNAL_CODECS_LOC_VARS const CObjectVector<CItemEx> &inputItems, - const CObjectVector<CUpdateItem> &updateItems, + CObjectVector<CUpdateItem> &updateItems, ISequentialOutStream *seqOutStream, CInArchive *inArchive, bool removeSfx, CCompressionMethodMode *compressionMethodMode, diff --git a/CPP/7zip/Archive/Zip/ZipUpdate.h b/CPP/7zip/Archive/Zip/ZipUpdate.h index 747c07bc..054db668 100644 --- a/CPP/7zip/Archive/Zip/ZipUpdate.h +++ b/CPP/7zip/Archive/Zip/ZipUpdate.h @@ -48,7 +48,7 @@ struct CUpdateItem HRESULT Update( DECL_EXTERNAL_CODECS_LOC_VARS const CObjectVector<CItemEx> &inputItems, - const CObjectVector<CUpdateItem> &updateItems, + CObjectVector<CUpdateItem> &updateItems, ISequentialOutStream *seqOutStream, CInArchive *inArchive, bool removeSfx, CCompressionMethodMode *compressionMethodMode, |