diff options
Diffstat (limited to 'CPP/7zip/Archive/VhdHandler.cpp')
-rw-r--r--[-rwxr-xr-x] | CPP/7zip/Archive/VhdHandler.cpp | 517 |
1 files changed, 384 insertions, 133 deletions
diff --git a/CPP/7zip/Archive/VhdHandler.cpp b/CPP/7zip/Archive/VhdHandler.cpp index 9d1c928e..5268bd0b 100755..100644 --- a/CPP/7zip/Archive/VhdHandler.cpp +++ b/CPP/7zip/Archive/VhdHandler.cpp @@ -4,12 +4,12 @@ #include "../../../C/CpuArch.h" -#include "Common/Buffer.h" -#include "Common/ComTry.h" -#include "Common/IntToString.h" -#include "Common/MyString.h" +#include "../../Common/ComTry.h" +#include "../../Common/IntToString.h" +#include "../../Common/MyBuffer.h" +#include "../../Common/MyString.h" -#include "Windows/PropVariant.h" +#include "../../Windows/PropVariant.h" #include "../Common/LimitedStreams.h" #include "../Common/ProgressUtils.h" @@ -22,14 +22,19 @@ #define Get32(p) GetBe32(p) #define Get64(p) GetBe64(p) -#define G32(p, dest) dest = Get32(p); -#define G64(p, dest) dest = Get64(p); +#define G32(_offs_, dest) dest = Get32(p + (_offs_)); +#define G64(_offs_, dest) dest = Get64(p + (_offs_)); using namespace NWindows; namespace NArchive { namespace NVhd { +#define SIGNATURE { 'c', 'o', 'n', 'e', 'c', 't', 'i', 'x', 0, 0 } + +static const unsigned kSignatureSize = 10; +static const Byte kSignature[kSignatureSize] = SIGNATURE; + static const UInt32 kUnusedBlock = 0xFFFFFFFF; static const UInt32 kDiskType_Fixed = 2; @@ -38,11 +43,11 @@ static const UInt32 kDiskType_Diff = 4; static const char *kDiskTypes[] = { - "0", - "1", - "Fixed", - "Dynamic", - "Differencing" + "0" + , "1" + , "Fixed" + , "Dynamic" + , "Differencing" }; struct CFooter @@ -73,7 +78,7 @@ struct CFooter AString CFooter::GetTypeString() const { - if (Type < sizeof(kDiskTypes) / sizeof(kDiskTypes[0])) + if (Type < ARRAY_SIZE(kDiskTypes)) return kDiskTypes[Type]; char s[16]; ConvertUInt32ToString(Type, s); @@ -96,27 +101,35 @@ static bool CheckBlock(const Byte *p, unsigned size, unsigned checkSumOffset, un return true; } +static const unsigned kSectorSize_Log = 9; +static const unsigned kSectorSize = 1 << kSectorSize_Log; +static const unsigned kHeaderSize = 512; + bool CFooter::Parse(const Byte *p) { - if (memcmp(p, "conectix", 8) != 0) + if (memcmp(p, kSignature, kSignatureSize) != 0) + return false; + // G32(0x08, Features); + // G32(0x0C, FormatVersion); + G64(0x10, DataOffset); + G32(0x18, CTime); + G32(0x1C, CreatorApp); + G32(0x20, CreatorVersion); + G32(0x24, CreatorHostOS); + // G64(0x28, OriginalSize); + G64(0x30, CurrentSize); + G32(0x38, DiskGeometry); + G32(0x3C, Type); + if (Type < kDiskType_Fixed || + Type > kDiskType_Diff) return false; - // G32(p + 0x08, Features); - // G32(p + 0x0C, FormatVersion); - G64(p + 0x10, DataOffset); - G32(p + 0x18, CTime); - G32(p + 0x1C, CreatorApp); - G32(p + 0x20, CreatorVersion); - G32(p + 0x24, CreatorHostOS); - // G64(p + 0x28, OriginalSize); - G64(p + 0x30, CurrentSize); - G32(p + 0x38, DiskGeometry); - G32(p + 0x3C, Type); memcpy(Id, p + 0x44, 16); SavedState = p[0x54]; - return CheckBlock(p, 512, 0x40, 0x55); + // if (DataOffset > ((UInt64)1 << 62)) return false; + // if (CurrentSize > ((UInt64)1 << 62)) return false; + return CheckBlock(p, kHeaderSize, 0x40, 0x55); } -/* struct CParentLocatorEntry { UInt32 Code; @@ -124,17 +137,15 @@ struct CParentLocatorEntry UInt32 DataLen; UInt64 DataOffset; - bool Parse(const Byte *p); + bool Parse(const Byte *p) + { + G32(0x00, Code); + G32(0x04, DataSpace); + G32(0x08, DataLen); + G64(0x10, DataOffset); + return Get32(p + 0x0C) == 0; // Reserved + } }; -bool CParentLocatorEntry::Parse(const Byte *p) -{ - G32(p + 0x00, Code); - G32(p + 0x04, DataSpace); - G32(p + 0x08, DataLen); - G32(p + 0x10, DataOffset); - return (Get32(p + 0x0C) == 0); // Resrved -} -*/ struct CDynHeader { @@ -142,56 +153,63 @@ struct CDynHeader UInt64 TableOffset; // UInt32 HeaderVersion; UInt32 NumBlocks; - int BlockSizeLog; + unsigned BlockSizeLog; UInt32 ParentTime; Byte ParentId[16]; + bool RelativeNameWasUsed; UString ParentName; - // CParentLocatorEntry ParentLocators[8]; + UString RelativeParentNameFromLocator; + CParentLocatorEntry ParentLocators[8]; bool Parse(const Byte *p); UInt32 NumBitMapSectors() const { - UInt32 numSectorsInBlock = (1 << (BlockSizeLog - 9)); - return (numSectorsInBlock + 512 * 8 - 1) / (512 * 8); + UInt32 numSectorsInBlock = (1 << (BlockSizeLog - kSectorSize_Log)); + return (numSectorsInBlock + kSectorSize * 8 - 1) / (kSectorSize * 8); + } + void Clear() + { + RelativeNameWasUsed = false; + ParentName.Empty(); + RelativeParentNameFromLocator.Empty(); } }; -static int GetLog(UInt32 num) -{ - for (int i = 0; i < 31; i++) - if (((UInt32)1 << i) == num) - return i; - return -1; -} - bool CDynHeader::Parse(const Byte *p) { if (memcmp(p, "cxsparse", 8) != 0) return false; - // G64(p + 0x08, DataOffset); - G64(p + 0x10, TableOffset); - // G32(p + 0x18, HeaderVersion); - G32(p + 0x1C, NumBlocks); - BlockSizeLog = GetLog(Get32(p + 0x20)); - if (BlockSizeLog < 9 || BlockSizeLog > 30) - return false; - G32(p + 0x38, ParentTime); + // G64(0x08, DataOffset); + G64(0x10, TableOffset); + // G32(0x18, HeaderVersion); + G32(0x1C, NumBlocks); + { + UInt32 blockSize = Get32(p + 0x20); + unsigned i; + for (i = kSectorSize_Log;; i++) + { + if (i > 31) + return false; + if (((UInt32)1 << i) == blockSize) + break; + } + BlockSizeLog = i; + } + G32(0x38, ParentTime); if (Get32(p + 0x3C) != 0) // reserved return false; memcpy(ParentId, p + 0x28, 16); { - const int kNameLength = 256; - wchar_t *s = ParentName.GetBuffer(kNameLength); - for (unsigned i = 0; i < kNameLength; i++) + const unsigned kNameLen = 256; + wchar_t *s = ParentName.GetBuffer(kNameLen); + for (unsigned i = 0; i < kNameLen; i++) s[i] = Get16(p + 0x40 + i * 2); - s[kNameLength] = 0; + s[kNameLen] = 0; ParentName.ReleaseBuffer(); } - /* - for (int i = 0; i < 8; i++) + for (unsigned i = 0; i < 8; i++) if (!ParentLocators[i].Parse(p + 0x240 + i * 24)) return false; - */ return CheckBlock(p, 1024, 0x24, 0x240 + 8 * 24); } @@ -202,8 +220,10 @@ class CHandler: public CMyUnknownImp { UInt64 _virtPos; - UInt64 _phyPos; - UInt64 _phyLimit; + UInt64 _posInArc; + UInt64 _posInArcLimit; + UInt64 _startOffset; + UInt64 _phySize; CFooter Footer; CDynHeader Dyn; @@ -214,7 +234,22 @@ class CHandler: CMyComPtr<IInStream> Stream; CMyComPtr<IInStream> ParentStream; CHandler *Parent; + UString _errorMessage; + // bool _unexpectedEnd; + void AddErrorMessage(const wchar_t *s) + { + if (!_errorMessage.IsEmpty()) + _errorMessage += L'\n'; + _errorMessage += s; + } + void UpdatePhySize(UInt64 value) + { + if (_phySize < value) + _phySize = value; + } + + void Reset_PosInArc() { _posInArc = (UInt64)0 - 1; } HRESULT Seek(UInt64 offset); HRESULT InitAndSeek(); HRESULT ReadPhy(UInt64 offset, void *data, UInt32 size); @@ -223,7 +258,7 @@ class CHandler: UInt64 GetPackSize() const { return Footer.ThereIsDynamic() ? ((UInt64)NumUsedBlocks << Dyn.BlockSizeLog) : Footer.CurrentSize; } - UString GetParentName() const + UString GetParentSequence() const { const CHandler *p = this; UString res; @@ -231,26 +266,45 @@ class CHandler: { if (!res.IsEmpty()) res += L" -> "; - res += p->Dyn.ParentName; + UString mainName; + UString anotherName; + if (Dyn.RelativeNameWasUsed) + { + mainName = p->Dyn.RelativeParentNameFromLocator; + anotherName = p->Dyn.ParentName; + } + else + { + mainName = p->Dyn.ParentName; + anotherName = p->Dyn.RelativeParentNameFromLocator; + } + res += mainName; + if (mainName != anotherName && !anotherName.IsEmpty()) + { + res += L' '; + res += L'('; + res += anotherName; + res += L')'; + } p = p->Parent; } return res; } - bool IsOK() const + bool AreParentsOK() const { const CHandler *p = this; while (p->NeedParent()) { p = p->Parent; - if (p == 0) + if (!p) return false; } return true; } HRESULT Open3(); - HRESULT Open2(IInStream *stream, CHandler *child, IArchiveOpenCallback *openArchiveCallback, int level); + HRESULT Open2(IInStream *stream, CHandler *child, IArchiveOpenCallback *openArchiveCallback, unsigned level); public: MY_UNKNOWN_IMP3(IInArchive, IInArchiveGetStream, IInStream) @@ -261,7 +315,7 @@ public: STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition); }; -HRESULT CHandler::Seek(UInt64 offset) { return Stream->Seek(offset, STREAM_SEEK_SET, NULL); } +HRESULT CHandler::Seek(UInt64 offset) { return Stream->Seek(_startOffset + offset, STREAM_SEEK_SET, NULL); } HRESULT CHandler::InitAndSeek() { @@ -269,50 +323,122 @@ HRESULT CHandler::InitAndSeek() { RINOK(Parent->InitAndSeek()); } - _virtPos = _phyPos = 0; + _virtPos = _posInArc = 0; BitMapTag = kUnusedBlock; - BitMap.SetCapacity(Dyn.NumBitMapSectors() << 9); + BitMap.Alloc(Dyn.NumBitMapSectors() << kSectorSize_Log); return Seek(0); } HRESULT CHandler::ReadPhy(UInt64 offset, void *data, UInt32 size) { - if (offset + size > _phyLimit) + if (offset + size > _posInArcLimit) return S_FALSE; - if (offset != _phyPos) + if (offset != _posInArc) { - _phyPos = offset; + _posInArc = offset; RINOK(Seek(offset)); } HRESULT res = ReadStream_FALSE(Stream, data, size); - _phyPos += size; + if (res == S_OK) + _posInArc += size; + else + Reset_PosInArc(); return res; } HRESULT CHandler::Open3() { - RINOK(Stream->Seek(0, STREAM_SEEK_END, &_phyPos)); - if (_phyPos < 512) + // Fixed archive uses only footer + + UInt64 startPos; + RINOK(Stream->Seek(0, STREAM_SEEK_CUR, &startPos)); + _startOffset = startPos; + Byte header[kHeaderSize]; + RINOK(ReadStream_FALSE(Stream, header, kHeaderSize)); + bool headerIsOK = Footer.Parse(header); + + if (headerIsOK && !Footer.ThereIsDynamic()) + { + // fixed archive + if (startPos < Footer.CurrentSize) + return S_FALSE; + _posInArcLimit = Footer.CurrentSize; + _phySize = Footer.CurrentSize + kHeaderSize; + _startOffset = startPos - Footer.CurrentSize; + _posInArc = _phySize; + return S_OK; + } + + UInt64 fileSize; + RINOK(Stream->Seek(0, STREAM_SEEK_END, &fileSize)); + if (fileSize < kHeaderSize) return S_FALSE; + const UInt32 kDynSize = 1024; Byte buf[kDynSize]; - _phyLimit = _phyPos; - RINOK(ReadPhy(_phyLimit - 512, buf, 512)); - if (!Footer.Parse(buf)) - return S_FALSE; - _phyLimit -= 512; + RINOK(Stream->Seek(fileSize - kHeaderSize, STREAM_SEEK_SET, NULL)); + RINOK(ReadStream_FALSE(Stream, buf, kHeaderSize)); - if (!Footer.ThereIsDynamic()) + if (!headerIsOK) + { + if (!Footer.Parse(buf)) + return S_FALSE; + if (Footer.ThereIsDynamic()) + return S_FALSE; // we can't open Dynamic Archive backward. + _posInArcLimit = Footer.CurrentSize; + _phySize = Footer.CurrentSize + kHeaderSize; + _startOffset = fileSize - kHeaderSize - Footer.CurrentSize; + _posInArc = _phySize; return S_OK; + } - RINOK(ReadPhy(0, buf + 512, 512)); - if (memcmp(buf, buf + 512, 512) != 0) - return S_FALSE; + _phySize = kHeaderSize; + _posInArc = fileSize - startPos; + _posInArcLimit = _posInArc - kHeaderSize; + + bool headerAndFooterAreEqual = false; + if (memcmp(header, buf, kHeaderSize) == 0) + { + headerAndFooterAreEqual = true; + _phySize = fileSize - _startOffset; + } RINOK(ReadPhy(Footer.DataOffset, buf, kDynSize)); if (!Dyn.Parse(buf)) return S_FALSE; + + UpdatePhySize(Footer.DataOffset + kDynSize); + + for (int i = 0; i < 8; i++) + { + const CParentLocatorEntry &locator = Dyn.ParentLocators[i]; + const UInt32 kNameBufSizeMax = 1024; + if (locator.DataLen < kNameBufSizeMax && + locator.DataOffset < _posInArcLimit && + locator.DataOffset + locator.DataLen <= _posInArcLimit) + { + if (locator.Code == 0x57327275 && (locator.DataLen & 1) == 0) + { + // "W2ru" locator + // Path is encoded as little-endian UTF-16 + Byte nameBuf[kNameBufSizeMax]; + UString tempString; + unsigned len = (locator.DataLen >> 1); + wchar_t *s = tempString.GetBuffer(len); + RINOK(ReadPhy(locator.DataOffset, nameBuf, locator.DataLen)); + for (unsigned j = 0; j < len; j++) + s[j] = GetUi16(nameBuf + j * 2); + s[len] = 0; + tempString.ReleaseBuffer(); + if (tempString[0] == L'.' && tempString[1] == L'\\') + tempString.DeleteFrontal(2); + Dyn.RelativeParentNameFromLocator = tempString; + } + } + if (locator.DataLen != 0) + UpdatePhySize(locator.DataOffset + locator.DataLen); + } if (Dyn.NumBlocks >= (UInt32)1 << 31) return S_FALSE; @@ -324,20 +450,69 @@ HRESULT CHandler::Open3() else if (((Footer.CurrentSize - 1) >> Dyn.BlockSizeLog) + 1 != Dyn.NumBlocks) return S_FALSE; - Bat.Reserve(Dyn.NumBlocks); + Bat.ClearAndReserve(Dyn.NumBlocks); + + UInt32 bitmapSize = Dyn.NumBitMapSectors() << kSectorSize_Log; + while ((UInt32)Bat.Size() < Dyn.NumBlocks) { - RINOK(ReadPhy(Dyn.TableOffset + (UInt64)Bat.Size() * 4, buf, 512)); - for (UInt32 j = 0; j < 512; j += 4) + RINOK(ReadPhy(Dyn.TableOffset + (UInt64)Bat.Size() * 4, buf, kSectorSize)); + UpdatePhySize(Dyn.TableOffset + kSectorSize); + for (UInt32 j = 0; j < kSectorSize; j += 4) { UInt32 v = Get32(buf + j); if (v != kUnusedBlock) + { + UInt32 blockSize = (UInt32)1 << Dyn.BlockSizeLog; + UpdatePhySize(((UInt64)v << kSectorSize_Log) + bitmapSize + blockSize); NumUsedBlocks++; - Bat.Add(v); + } + Bat.AddInReserved(v); if ((UInt32)Bat.Size() >= Dyn.NumBlocks) break; } } + + if (headerAndFooterAreEqual) + return S_OK; + + if (_startOffset + _phySize + kHeaderSize > fileSize) + { + // _unexpectedEnd = true; + _posInArcLimit = _phySize; + _phySize += kHeaderSize; + return S_OK; + } + + RINOK(ReadPhy(_phySize, buf, kHeaderSize)); + if (memcmp(header, buf, kHeaderSize) == 0) + { + _posInArcLimit = _phySize; + _phySize += kHeaderSize; + return S_OK; + } + + if (_phySize == 0x800) + { + /* WHY does empty archive contain additional empty sector? + We skip that sector and check footer again. */ + unsigned i; + for (i = 0; i < kSectorSize && buf[i] == 0; i++); + if (i == kSectorSize) + { + RINOK(ReadPhy(_phySize + kSectorSize, buf, kHeaderSize)); + if (memcmp(header, buf, kHeaderSize) == 0) + { + _phySize += kSectorSize; + _posInArcLimit = _phySize; + _phySize += kHeaderSize; + return S_OK; + } + } + } + _posInArcLimit = _phySize; + _phySize += kHeaderSize; + AddErrorMessage(L"Can't find footer"); return S_OK; } @@ -371,17 +546,17 @@ STDMETHODIMP CHandler::Read(void *data, UInt32 size, UInt32 *processedSize) } else { - UInt64 newPos = (UInt64)blockSectIndex << 9; + UInt64 newPos = (UInt64)blockSectIndex << kSectorSize_Log; if (BitMapTag != blockIndex) { - RINOK(ReadPhy(newPos, BitMap, (UInt32)BitMap.GetCapacity())); + RINOK(ReadPhy(newPos, BitMap, (UInt32)BitMap.Size())); BitMapTag = blockIndex; } - RINOK(ReadPhy(newPos + BitMap.GetCapacity() + offsetInBlock, data, size)); + RINOK(ReadPhy(newPos + BitMap.Size() + offsetInBlock, data, size)); for (UInt32 cur = 0; cur < size;) { UInt32 rem = MyMin(0x200 - (offsetInBlock & 0x1FF), size - cur); - UInt32 bmi = offsetInBlock >> 9; + UInt32 bmi = offsetInBlock >> kSectorSize_Log; if (((BitMap[bmi >> 3] >> (7 - (bmi & 7))) & 1) == 0) { if (ParentStream) @@ -409,15 +584,18 @@ STDMETHODIMP CHandler::Read(void *data, UInt32 size, UInt32 *processedSize) STDMETHODIMP CHandler::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition) { - switch(seekOrigin) + switch (seekOrigin) { - case STREAM_SEEK_SET: _virtPos = offset; break; - case STREAM_SEEK_CUR: _virtPos += offset; break; - case STREAM_SEEK_END: _virtPos = Footer.CurrentSize + offset; break; + 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 = _virtPos; + *newPosition = offset; return S_OK; } @@ -427,9 +605,10 @@ enum kpidSavedState }; -STATPROPSTG kArcProps[] = +static const STATPROPSTG kArcProps[] = { { NULL, kpidSize, VT_UI8}, + { NULL, kpidOffset, VT_UI8}, { NULL, kpidCTime, VT_FILETIME}, { NULL, kpidClusterSize, VT_UI8}, { NULL, kpidMethod, VT_BSTR}, @@ -440,16 +619,16 @@ STATPROPSTG kArcProps[] = { NULL, kpidId, VT_BSTR} }; -STATPROPSTG kProps[] = +static const Byte kProps[] = { - { NULL, kpidSize, VT_UI8}, - { NULL, kpidPackSize, VT_UI8}, - { NULL, kpidCTime, VT_FILETIME} + kpidSize, + kpidPackSize, + kpidCTime /* - { NULL, kpidNumCyls, VT_UI4}, - { NULL, kpidNumHeads, VT_UI4}, - { NULL, kpidSectorsPerTrack, VT_UI4} + { kpidNumCyls, VT_UI4}, + { kpidNumHeads, VT_UI4}, + { kpidSectorsPerTrack, VT_UI4} */ }; @@ -470,11 +649,11 @@ static void VhdTimeToFileTime(UInt32 vhdTime, NCOM::CPropVariant &prop) prop = utc; } -static void StringToAString(char *dest, UInt32 s) +static void StringToAString(char *dest, UInt32 val) { for (int i = 24; i >= 0; i -= 8) { - Byte b = (Byte)((s >> i) & 0xFF); + Byte b = (Byte)((val >> i) & 0xFF); if (b < 0x20 || b > 0x7F) break; *dest++ = b; @@ -496,11 +675,12 @@ STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) { COM_TRY_BEGIN NCOM::CPropVariant prop; - switch(propID) + switch (propID) { case kpidMainSubfile: prop = (UInt32)0; break; case kpidCTime: VhdTimeToFileTime(Footer.CTime, prop); break; case kpidClusterSize: if (Footer.ThereIsDynamic()) prop = (UInt32)1 << Dyn.BlockSizeLog; break; + case kpidShortComment: case kpidMethod: { AString s = Footer.GetTypeString(); @@ -535,7 +715,7 @@ STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) } case kpidHostOS: { - if (Footer.CreatorHostOS == 0x5769326b) + if (Footer.CreatorHostOS == 0x5769326B) prop = "Windows"; else { @@ -555,18 +735,32 @@ STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) break; } case kpidSavedState: prop = Footer.SavedState ? true : false; break; - case kpidParent: if (NeedParent()) prop = GetParentName(); break; + case kpidParent: if (NeedParent()) prop = GetParentSequence(); break; + case kpidOffset: prop = _startOffset; break; + case kpidPhySize: prop = _phySize; break; + /* + case kpidErrorFlags: + { + UInt32 flags = 0; + if (_unexpectedEnd) + flags |= kpv_ErrorFlags_UnexpectedEndOfArc; + if (flags != 0) + prop = flags; + break; + } + */ + case kpidError: if (!_errorMessage.IsEmpty()) prop = _errorMessage; break; } prop.Detach(value); return S_OK; COM_TRY_END } -HRESULT CHandler::Open2(IInStream *stream, CHandler *child, IArchiveOpenCallback *openArchiveCallback, int level) +HRESULT CHandler::Open2(IInStream *stream, CHandler *child, IArchiveOpenCallback *openArchiveCallback, unsigned level) { Close(); Stream = stream; - if (level > 32) + if (level > (1 << 12)) // Maybe we need to increase that limit return S_FALSE; RINOK(Open3()); if (child && memcmp(child->Dyn.ParentId, Footer.Id, 16) != 0) @@ -575,16 +769,65 @@ HRESULT CHandler::Open2(IInStream *stream, CHandler *child, IArchiveOpenCallback return S_OK; CMyComPtr<IArchiveOpenVolumeCallback> openVolumeCallback; if (openArchiveCallback->QueryInterface(IID_IArchiveOpenVolumeCallback, (void **)&openVolumeCallback) != S_OK) - return S_FALSE; + { + // return S_FALSE; + } CMyComPtr<IInStream> nextStream; - HRESULT res = openVolumeCallback->GetStream(Dyn.ParentName, &nextStream); - if (res == S_FALSE) - return S_OK; - RINOK(res); - Parent = new CHandler; - ParentStream = Parent; - return Parent->Open2(nextStream, this, openArchiveCallback, level + 1); + bool useRelative; + UString name; + if (!Dyn.RelativeParentNameFromLocator.IsEmpty()) + { + useRelative = true; + name = Dyn.RelativeParentNameFromLocator; + } + else + { + useRelative = false; + name = Dyn.ParentName; + } + Dyn.RelativeNameWasUsed = useRelative; + + if (openVolumeCallback) + { + HRESULT res = openVolumeCallback->GetStream(name, &nextStream); + if (res == S_FALSE) + { + if (useRelative && Dyn.ParentName != Dyn.RelativeParentNameFromLocator) + { + res = openVolumeCallback->GetStream(Dyn.ParentName, &nextStream); + if (res == S_OK) + Dyn.RelativeNameWasUsed = false; + } + if (res == S_FALSE) + return S_OK; + } + RINOK(res); + + Parent = new CHandler; + ParentStream = Parent; + + res = Parent->Open2(nextStream, this, openArchiveCallback, level + 1); + if (res == S_FALSE) + { + Parent = NULL; + ParentStream.Release(); + } + } + { + const CHandler *p = this; + while (p->NeedParent()) + { + p = p->Parent; + if (p == 0) + { + AddErrorMessage(L"Can't open parent VHD file:"); + AddErrorMessage(Dyn.ParentName); + break; + } + } + } + return S_OK; } STDMETHODIMP CHandler::Open(IInStream *stream, @@ -613,11 +856,15 @@ STDMETHODIMP CHandler::Open(IInStream *stream, STDMETHODIMP CHandler::Close() { + _phySize = 0; Bat.Clear(); NumUsedBlocks = 0; Parent = 0; Stream.Release(); ParentStream.Release(); + Dyn.Clear(); + _errorMessage.Empty(); + // _unexpectedEnd = false; return S_OK; } @@ -629,8 +876,8 @@ STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) STDMETHODIMP CHandler::GetProperty(UInt32 /* index */, PROPID propID, PROPVARIANT *value) { - COM_TRY_BEGIN - NWindows::NCOM::CPropVariant prop; + // COM_TRY_BEGIN + NCOM::CPropVariant prop; switch(propID) { @@ -645,7 +892,7 @@ STDMETHODIMP CHandler::GetProperty(UInt32 /* index */, PROPID propID, PROPVARIAN } prop.Detach(value); return S_OK; - COM_TRY_END + // COM_TRY_END } STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, @@ -654,7 +901,7 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, COM_TRY_BEGIN if (numItems == 0) return S_OK; - if (numItems != (UInt32)-1 && (numItems != 1 || indices[0] != 0)) + if (numItems != (UInt32)(Int32)-1 && (numItems != 1 || indices[0] != 0)) return E_INVALIDARG; RINOK(extractCallback->SetTotal(Footer.CurrentSize)); @@ -678,7 +925,7 @@ STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems, CMyComPtr<ISequentialInStream> inStream; HRESULT hres = GetStream(0, &inStream); if (hres == S_FALSE) - res = NExtract::NOperationResult::kUnSupportedMethod; + res = NExtract::NOperationResult::kUnsupportedMethod; else { RINOK(hres); @@ -715,7 +962,7 @@ STDMETHODIMP CHandler::GetStream(UInt32 /* index */, ISequentialInStream **strea *stream = streamTemp.Detach(); return S_OK; } - if (!Footer.ThereIsDynamic() || !IsOK()) + if (!Footer.ThereIsDynamic() || !AreParentsOK()) return S_FALSE; CMyComPtr<ISequentialInStream> streamTemp = this; RINOK(InitAndSeek()); @@ -724,10 +971,14 @@ STDMETHODIMP CHandler::GetStream(UInt32 /* index */, ISequentialInStream **strea COM_TRY_END } -static IInArchive *CreateArc() { return new CHandler; } +IMP_CreateArcIn static CArcInfo g_ArcInfo = - { L"VHD", L"vhd", L".mbr", 0xDC, { 'c', 'o', 'n', 'e', 'c', 't', 'i', 'x', 0, 0 }, 10, false, CreateArc, 0 }; + { "VHD", "vhd", ".mbr", 0xDC, + kSignatureSize, SIGNATURE, + 0, + NArcInfoFlags::kUseGlobalOffset, + CreateArc }; REGISTER_ARC(Vhd) |