diff options
Diffstat (limited to 'CPP/7zip/Archive/VmdkHandler.cpp')
-rw-r--r-- | CPP/7zip/Archive/VmdkHandler.cpp | 1034 |
1 files changed, 822 insertions, 212 deletions
diff --git a/CPP/7zip/Archive/VmdkHandler.cpp b/CPP/7zip/Archive/VmdkHandler.cpp index 4b404e14..83e38d02 100644 --- a/CPP/7zip/Archive/VmdkHandler.cpp +++ b/CPP/7zip/Archive/VmdkHandler.cpp @@ -8,6 +8,9 @@ #include "../../Common/ComTry.h" #include "../../Common/IntToString.h" +#include "../../Common/StringConvert.h" +#include "../../Common/StringToInt.h" +#include "../../Common/UTFConvert.h" #include "../../Windows/PropVariant.h" @@ -19,24 +22,29 @@ #include "HandlerCont.h" +using namespace NWindows; + +namespace NArchive { +namespace NVmdk { + #define Get16(p) GetUi16(p) #define Get32(p) GetUi32(p) #define Get64(p) GetUi64(p) -using namespace NWindows; +#define LE_16(offs, dest) dest = Get16(p + (offs)); +#define LE_32(offs, dest) dest = Get32(p + (offs)); +#define LE_64(offs, dest) dest = Get64(p + (offs)); -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_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 UInt32 k_Flags_Marker = (UInt32)1 << 17; static const unsigned k_NumMidBits = 9; // num bits for index in Grain Table @@ -57,12 +65,12 @@ struct CHeader 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_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 Is_Marker() const { return (flags & k_Flags_Marker) != 0; }; - bool Parse(const Byte *buf); + bool Parse(const Byte *p); bool IsSameImageFor(const CHeader &h) const { @@ -74,25 +82,25 @@ struct CHeader } }; -bool CHeader::Parse(const Byte *buf) +bool CHeader::Parse(const Byte *p) { - if (memcmp(buf, k_Signature, sizeof(k_Signature)) != 0) + if (memcmp(p, 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); + LE_32 (0x04, version); + LE_32 (0x08, flags); + LE_64 (0x0C, capacity); + LE_64 (0x14, grainSize); + LE_64 (0x1C, descriptorOffset); + LE_64 (0x24, descriptorSize); + LE_32 (0x2C, numGTEsPerGT); + // LE_64 (0x30, rgdOffset); + LE_64 (0x38, gdOffset); + LE_64 (0x40, overHead); // uncleanShutdown = buf[0x48]; - algo = Get16(buf + 0x4D); + LE_16(0x4D, algo); - if (Is_NL() && Get32(buf + 0x49) != 0x0A0D200A) // do we need Is_NL() check here? + if (Is_NL() && Get32(p + 0x49) != 0x0A0D200A) // do we need Is_NL() check here? return false; return (numGTEsPerGT == (1 << k_NumMidBits)) && (version <= 3); @@ -115,21 +123,154 @@ struct CMarker void Parse(const Byte *p) { - NumSectors = Get64(p); - SpecSize = Get32(p + 8); - Type = Get32(p + 12); + LE_64 (0, NumSectors); + LE_32 (8, SpecSize); + LE_32 (12, Type); + } +}; + + +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; +} + +static inline bool IsSpaceChar(char c) +{ + return (c == ' ' || c == '\t'); +} + +static const char *SkipSpaces(const char *s) +{ + for (;; s++) + { + char c = *s; + if (c == 0 || !IsSpaceChar(c)) + return s; } +} + +#define SKIP_SPACES(s) s = SkipSpaces(s); + +static const char *GetNextWord(const char *s, AString &dest) +{ + dest.Empty(); + SKIP_SPACES(s); + const char *start = s; + for (;; s++) + { + char c = *s; + if (c == 0 || IsSpaceChar(c)) + { + dest.SetFrom(start, (unsigned)(s - start)); + return s; + } + } +} + +static const char *GetNextNumber(const char *s, UInt64 &val) +{ + SKIP_SPACES(s); + if (*s == 0) + return s; + const char *end; + val = ConvertStringToUInt64(s, &end); + char c = *end; + if (c != 0 && !IsSpaceChar(c)) + return NULL; + return end; +} + + +struct CExtentInfo +{ + AString Access; // RW, RDONLY, or NOACCESS + UInt64 NumSectors; // 512 bytes sectors + AString Type; // FLAT, SPARSE, ZERO, VMFS, VMFSSPARSE, VMFSRDM, VMFSRAW + AString FileName; + UInt64 StartSector; // used for FLAT + + // for VMWare Player 9: + // PartitionUUID + // DeviceIdentifier + + bool IsType_ZERO() const { return Type == "ZERO"; } + // bool IsType_FLAT() const { return Type == "FLAT"; } + bool IsType_Flat() const { return Type == "FLAT" || Type == "VMFS" || Type == "VMFSRAW"; } + + bool Parse(const char *s); }; +bool CExtentInfo::Parse(const char *s) +{ + NumSectors = 0; + StartSector = 0; + Access.Empty(); + Type.Empty(); + FileName.Empty(); + + s = GetNextWord(s, Access); + s = GetNextNumber(s, NumSectors); + if (!s) + return false; + s = GetNextWord(s, Type); + + if (Type.IsEmpty()) + return false; + + SKIP_SPACES(s); + + if (IsType_ZERO()) + return (*s == 0); + + if (*s != '\"') + return false; + s++; + { + const char *s2 = strchr(s, '\"'); + if (!s2) + return false; + FileName.SetFrom(s, (unsigned)(s2 - s)); + s = s2 + 1; + } + SKIP_SPACES(s); + if (*s == 0) + return true; + + s = GetNextNumber(s, StartSector); + if (!s) + return false; + return true; + // SKIP_SPACES(s); + // return (*s == 0); +} + struct CDescriptor { AString CID; AString parentCID; AString createType; + // AString encoding; // UTF-8, windows-1252 - default is UTF-8 + + CObjectVector<CExtentInfo> Extents; + + static void GetUnicodeName(const AString &s, UString &res) + { + if (!ConvertUTF8ToUnicode(s, res)) + MultiByteToUnicodeString2(res, s); + } - AStringVector Extents; - void Clear() { CID.Empty(); @@ -138,25 +279,16 @@ struct CDescriptor Extents.Clear(); } - void Parse(const Byte *p, size_t size); + bool IsThere_Parent() const + { + return !parentCID.IsEmpty() && !parentCID.IsEqualTo_Ascii_NoCase("ffffffff"); + } + + bool 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) +bool CDescriptor::Parse(const Byte *p, size_t size) { Clear(); @@ -166,7 +298,7 @@ void CDescriptor::Parse(const Byte *p, size_t size) for (size_t i = 0;; i++) { - char c = p[i]; + const char c = p[i]; if (i == size || c == 0 || c == 0xA || c == 0xD) { if (!s.IsEmpty() && s[0] != '#') @@ -181,8 +313,14 @@ void CDescriptor::Parse(const Byte *p, size_t size) createType = val; } else - Extents.Add(s); + { + CExtentInfo ei; + if (!ei.Parse(s)) + return false; + Extents.Add(ei); + } } + s.Empty(); if (c == 0 || i >= size) break; @@ -190,25 +328,116 @@ void CDescriptor::Parse(const Byte *p, size_t size) else s += (char)c; } + + return true; } +struct CExtent +{ + bool IsOK; + bool IsArc; + bool NeedDeflate; + bool Unsupported; + bool IsZero; + bool IsFlat; + bool DescriptorOK; + bool HeadersError; + + unsigned ClusterBits; + UInt32 ZeroSector; + + CObjectVector<CByteBuffer> Tables; + + CMyComPtr<IInStream> Stream; + UInt64 PosInArc; + + UInt64 PhySize; + UInt64 VirtSize; // from vmdk header of volume + + UInt64 StartOffset; // virtual offset of this extent + UInt64 NumBytes; // from main descriptor, if multi-vol + UInt64 FlatOffset; // in Stream + + CByteBuffer DescriptorBuf; + CDescriptor Descriptor; + + CHeader h; + + UInt64 GetEndOffset() const { return StartOffset + NumBytes; } + + bool IsVmdk() const { return !IsZero && !IsFlat; }; + // if (IsOK && IsVmdk()), then VMDK header of this extent was read + + CExtent(): + IsOK(false), + IsArc(false), + NeedDeflate(false), + Unsupported(false), + IsZero(false), + IsFlat(false), + DescriptorOK(false), + HeadersError(false), + + ClusterBits(0), + ZeroSector(0), + + PosInArc(0), + + PhySize(0), + VirtSize(0), + + StartOffset(0), + NumBytes(0), + FlatOffset(0) + {} + + + HRESULT ReadForHeader(IInStream *stream, UInt64 sector, void *data, size_t numSectors); + HRESULT Open3(IInStream *stream, IArchiveOpenCallback *openCallback, + unsigned numVols, unsigned volIndex, UInt64 &complexity); + + HRESULT Seek(UInt64 offset) + { + PosInArc = offset; + return Stream->Seek(offset, STREAM_SEEK_SET, NULL); + } + + HRESULT InitAndSeek() + { + if (Stream) + return Seek(0); + return S_OK; + } + + HRESULT Read(void *data, size_t *size) + { + HRESULT res = ReadStream(Stream, data, size); + PosInArc += *size; + return res; + } +}; + + class CHandler: public CHandlerImg { - unsigned _clusterBits; + bool _isArc; + bool _unsupported; + bool _unsupportedSome; + bool _headerError; + bool _missingVol; + bool _isMultiVol; + bool _needDeflate; - CObjectVector<CByteBuffer> _tables; UInt64 _cacheCluster; + unsigned _cacheExtent; CByteBuffer _cache; CByteBuffer _cacheCompressed; - + + unsigned _clusterBitsMax; UInt64 _phySize; - UInt32 _zeroSector; - bool _needDeflate; - bool _isArc; - bool _unsupported; - // bool _headerError; + CObjectVector<CExtent> _extents; CBufInStream *_bufInStreamSpec; CMyComPtr<ISequentialInStream> _bufInStream; @@ -222,24 +451,13 @@ class CHandler: public CHandlerImg CByteBuffer _descriptorBuf; CDescriptor _descriptor; - CHeader h; - - - HRESULT Seek(UInt64 offset) - { - _posInArc = offset; - return Stream->Seek(offset, STREAM_SEEK_SET, NULL); - } - - HRESULT InitAndSeek() + void InitAndSeekMain() { _virtPos = 0; - return Seek(0); } - HRESULT ReadForHeader(IInStream *stream, UInt64 sector, void *data, size_t numSectors); virtual HRESULT Open2(IInStream *stream, IArchiveOpenCallback *openCallback); - + virtual void CloseAtError(); public: INTERFACE_IInArchive_Img(;) @@ -261,19 +479,130 @@ STDMETHODIMP CHandler::Read(void *data, UInt32 size, UInt32 *processedSize) if (size == 0) return S_OK; } - + + unsigned extentIndex; + { + unsigned left = 0, right = _extents.Size(); + for (;;) + { + unsigned mid = (left + right) / 2; + if (mid == left) + break; + if (_virtPos < _extents[mid].StartOffset) + right = mid; + else + left = mid; + } + extentIndex = left; + } + + CExtent &extent = _extents[extentIndex]; + + { + const UInt64 vir = _virtPos - extent.StartOffset; + if (vir >= extent.NumBytes) + { + return E_FAIL; + /* + if (vir > extent.NumBytes) + _stream_dataError = true; + memset(data, 0, size); + _virtPos += size; + if (processedSize) + *processedSize = size; + return S_OK; + */ + } + + { + const UInt64 rem = extent.NumBytes - vir; + if (size > rem) + size = (UInt32)rem; + } + + if (vir >= extent.VirtSize) + { + // if vmdk's VirtSize is smaller than VirtSize from main multi-volume descriptor + _stream_dataError = true; + return S_FALSE; + /* + memset(data, 0, size); + _virtPos += size; + if (processedSize) + *processedSize = size; + return S_OK; + */ + } + + { + const UInt64 rem = extent.VirtSize - vir; + if (size > rem) + size = (UInt32)rem; + } + + if (extent.IsZero || !extent.IsOK || !extent.Stream || extent.Unsupported) + { + if (extent.Unsupported) + { + _stream_unsupportedMethod = true; + return S_FALSE; + } + if (!extent.IsOK || !extent.Stream) + { + _stream_unavailData = true; + return S_FALSE; + } + memset(data, 0, size); + _virtPos += size; + if (processedSize) + *processedSize = size; + return S_OK; + } + + if (extent.IsFlat) + { + UInt64 offset = extent.FlatOffset + vir; + if (offset != extent.PosInArc) + { + RINOK(extent.Seek(offset)); + } + UInt32 size2 = 0; + HRESULT res = extent.Stream->Read(data, size, &size2); + if (res == S_OK && size2 == 0) + { + _stream_unavailData = true; + /* + memset(data, 0, size); + _virtPos += size; + if (processedSize) + *processedSize = size; + return S_OK; + */ + } + // _stream_PackSize += size2; + extent.PosInArc += size2; + _virtPos += size2; + if (processedSize) + *processedSize = size2; + return res; + } + } + + for (;;) { - const UInt64 cluster = _virtPos >> _clusterBits; - const size_t clusterSize = (size_t)1 << _clusterBits; - const size_t lowBits = (size_t)_virtPos & (clusterSize - 1); + const UInt64 vir = _virtPos - extent.StartOffset; + const unsigned clusterBits = extent.ClusterBits; + const UInt64 cluster = vir >> clusterBits; + const size_t clusterSize = (size_t)1 << clusterBits; + const size_t lowBits = (size_t)vir & (clusterSize - 1); { size_t rem = clusterSize - lowBits; if (size > rem) size = (UInt32)rem; } - if (cluster == _cacheCluster) + if (extentIndex == _cacheExtent && cluster == _cacheCluster) { memcpy(data, _cache + lowBits, size); _virtPos += size; @@ -284,9 +613,9 @@ STDMETHODIMP CHandler::Read(void *data, UInt32 size, UInt32 *processedSize) const UInt64 high = cluster >> k_NumMidBits; - if (high < _tables.Size()) + if (high < extent.Tables.Size()) { - const CByteBuffer &table = _tables[(unsigned)high]; + const CByteBuffer &table = extent.Tables[(unsigned)high]; if (table.Size() != 0) { @@ -294,28 +623,27 @@ STDMETHODIMP CHandler::Read(void *data, UInt32 size, UInt32 *processedSize) const Byte *p = (const Byte *)table + (midBits << 2); const UInt32 v = Get32(p); - if (v != 0 && v != _zeroSector) + if (v != 0 && v != extent.ZeroSector) { UInt64 offset = (UInt64)v << 9; - if (_needDeflate) + if (extent.NeedDeflate) { - if (offset != _posInArc) + if (offset != extent.PosInArc) { - // printf("\n%12x %12x\n", (unsigned)offset, (unsigned)(offset - _posInArc)); - RINOK(Seek(offset)); + // printf("\n%12x %12x\n", (unsigned)offset, (unsigned)(offset - extent.PosInArc)); + RINOK(extent.Seek(offset)); } const size_t kStartSize = 1 << 9; { size_t curSize = kStartSize; - HRESULT res = ReadStream(Stream, _cacheCompressed, &curSize); - _posInArc += curSize; - RINOK(res); + RINOK(extent.Read(_cacheCompressed, &curSize)); + // _stream_PackSize += curSize; if (curSize != kStartSize) return S_FALSE; } - if (Get64(_cacheCompressed) != (cluster << (_clusterBits - 9))) + if (Get64(_cacheCompressed) != (cluster << (clusterBits - 9))) return S_FALSE; UInt32 dataSize = Get32(_cacheCompressed + 8); @@ -331,9 +659,8 @@ STDMETHODIMP CHandler::Read(void *data, UInt32 size, UInt32 *processedSize) return S_FALSE; size_t curSize = dataSize2 - kStartSize; const size_t curSize2 = curSize; - HRESULT res = ReadStream(Stream, _cacheCompressed + kStartSize, &curSize); - _posInArc += curSize; - RINOK(res); + RINOK(extent.Read(_cacheCompressed + kStartSize, &curSize)); + // _stream_PackSize += curSize; if (curSize != curSize2) return S_FALSE; } @@ -341,6 +668,8 @@ STDMETHODIMP CHandler::Read(void *data, UInt32 size, UInt32 *processedSize) _bufInStreamSpec->Init(_cacheCompressed + 12, dataSize); _cacheCluster = (UInt64)(Int64)-1; + _cacheExtent = (unsigned)(int)-1; + if (_cache.Size() < clusterSize) return E_FAIL; _bufOutStreamSpec->Init(_cache, clusterSize); @@ -349,16 +678,26 @@ STDMETHODIMP CHandler::Read(void *data, UInt32 size, UInt32 *processedSize) 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 (_bufOutStreamSpec->GetPos() != clusterSize) + { + _stream_dataError = true; + memset(_cache + _bufOutStreamSpec->GetPos(), 0, clusterSize - _bufOutStreamSpec->GetPos()); + } + */ - if (res == S_OK) if (_bufOutStreamSpec->GetPos() != clusterSize || _zlibDecoderSpec->GetInputProcessedSize() != dataSize) - res = S_FALSE; + { + _stream_dataError = true; + if (res == S_OK) + res = S_FALSE; + } RINOK(res); + _cacheCluster = cluster; + _cacheExtent = extentIndex; continue; /* @@ -371,16 +710,29 @@ STDMETHODIMP CHandler::Read(void *data, UInt32 size, UInt32 *processedSize) } { offset += lowBits; - if (offset != _posInArc) + if (offset != extent.PosInArc) { - // printf("\n%12x %12x\n", (unsigned)offset, (unsigned)(offset - _posInArc)); - RINOK(Seek(offset)); + // printf("\n%12x %12x\n", (unsigned)offset, (unsigned)(offset - extent.PosInArc)); + RINOK(extent.Seek(offset)); } - HRESULT res = Stream->Read(data, size, &size); - _posInArc += size; - _virtPos += size; + UInt32 size2 = 0; + HRESULT res = extent.Stream->Read(data, size, &size2); + if (res == S_OK && size2 == 0) + { + _stream_unavailData = true; + /* + memset(data, 0, size); + _virtPos += size; + if (processedSize) + *processedSize = size; + return S_OK; + */ + } + extent.PosInArc += size2; + // _stream_PackSize += size2; + _virtPos += size2; if (processedSize) - *processedSize = size; + *processedSize = size2; return res; } } @@ -404,9 +756,10 @@ static const Byte kProps[] = static const Byte kArcProps[] = { + kpidNumVolumes, + kpidMethod, kpidClusterSize, kpidHeadersSize, - kpidMethod, kpidId, kpidName, kpidComment @@ -421,38 +774,72 @@ STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) COM_TRY_BEGIN NCOM::CPropVariant prop; + const CExtent *e = NULL; + const CDescriptor *desc = NULL; + + if (_isMultiVol) + desc = &_descriptor; + else if (_extents.Size() == 1) + { + e = &_extents[0]; + desc = &e->Descriptor; + } + 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 kpidClusterSize: prop = (UInt32)((UInt32)1 << _clusterBitsMax); break; + case kpidHeadersSize: if (e) prop = (e->h.overHead << 9); break; case kpidMethod: { AString s; - if (!_descriptor.createType.IsEmpty()) - s = _descriptor.createType; - - if (h.algo != 0) + if (desc && !desc->createType.IsEmpty()) + s = desc->createType; + + bool zlib = false; + bool marker = false; + int algo = -1; + + FOR_VECTOR (i, _extents) { - s.Add_Space_if_NotEmpty(); - if (h.algo == 1) - s += "zlib"; - else + const CExtent &extent = _extents[i]; + if (!extent.IsOK || !extent.IsVmdk()) + continue; + + const CHeader &h = extent.h; + + if (h.algo != 0) { - char temp[16]; - ConvertUInt32ToString(h.algo, temp); - s += temp; + if (h.algo == 1) + zlib = true; + else if (algo != (int)h.algo) + { + s.Add_Space_if_NotEmpty(); + char temp[16]; + ConvertUInt32ToString(h.algo, temp); + s += temp; + algo = h.algo; + } } + + if (h.Is_Marker()) + marker = true; } - - if (h.Is_Marker()) + + if (zlib) + { + s.Add_Space_if_NotEmpty(); + s += "zlib"; + } + + if (marker) { s.Add_Space_if_NotEmpty(); s += "Marker"; } - + if (!s.IsEmpty()) prop = s; break; @@ -460,10 +847,10 @@ STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) case kpidComment: { - if (_descriptorBuf.Size() != 0) + if (e && e->DescriptorBuf.Size() != 0) { AString s; - s.SetFrom_CalcLen((const char *)(const Byte *)_descriptorBuf, (unsigned)_descriptorBuf.Size()); + s.SetFrom_CalcLen((const char *)(const Byte *)e->DescriptorBuf, (unsigned)e->DescriptorBuf.Size()); if (!s.IsEmpty() && s.Len() <= (1 << 16)) prop = s; } @@ -471,46 +858,38 @@ STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value) } case kpidId: - if (!_descriptor.CID.IsEmpty()) + if (desc && !desc->CID.IsEmpty()) { - prop = _descriptor.CID; + prop = desc->CID; break; } case kpidName: { - if (_descriptor.Extents.Size() == 1) + if (!_isMultiVol && desc && desc->Extents.Size() == 1) { - const AString &s = _descriptor.Extents[0]; - if (!s.IsEmpty()) + const CExtentInfo &ei = desc->Extents[0]; + if (!ei.FileName.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; - } - } - } + UString u; + CDescriptor::GetUnicodeName(ei.FileName, u); + if (!u.IsEmpty()) + prop = u; } } break; } - + + case kpidNumVolumes: if (_isMultiVol) prop = (UInt32)_extents.Size(); 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 (_unsupportedSome) v |= kpv_ErrorFlags_UnsupportedMethod; + if (_headerError) v |= kpv_ErrorFlags_HeadersError; + if (_missingVol) v |= kpv_ErrorFlags_UnexpectedEnd; if (v != 0) prop = v; break; @@ -533,9 +912,22 @@ STDMETHODIMP CHandler::GetProperty(UInt32 /* index */, PROPID propID, PROPVARIAN case kpidSize: prop = _size; break; case kpidPackSize: { - UInt64 ov = (h.overHead << 9); - if (_phySize >= ov) - prop = _phySize - ov; + UInt64 packSize = 0; + FOR_VECTOR (i, _extents) + { + const CExtent &e = _extents[i]; + if (!e.IsOK) + continue; + if (e.IsVmdk() && !_isMultiVol) + { + UInt64 ov = (e.h.overHead << 9); + if (e.PhySize >= ov) + packSize += e.PhySize - ov; + } + else + packSize += e.PhySize; + } + prop = packSize; break; } case kpidExtension: prop = (_imgExt ? _imgExt : "img"); break; @@ -556,19 +948,26 @@ static int inline GetLog(UInt64 num) } -HRESULT CHandler::ReadForHeader(IInStream *stream, UInt64 sector, void *data, size_t numSectors) +HRESULT CExtent::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; + if (PhySize < end) + PhySize = end; return S_OK; } +void CHandler::CloseAtError() +{ + _extents.Clear(); + CHandlerImg::CloseAtError(); +} + + HRESULT CHandler::Open2(IInStream *stream, IArchiveOpenCallback *openCallback) { const unsigned kSectoreSize = 512; @@ -578,43 +977,242 @@ HRESULT CHandler::Open2(IInStream *stream, IArchiveOpenCallback *openCallback) if (headerSize < sizeof(k_Signature)) return S_FALSE; + + CMyComPtr<IArchiveOpenVolumeCallback> volumeCallback; + 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) + const size_t k_SigDesc_Size = strlen(kSignature_Descriptor); + if (headerSize < k_SigDesc_Size) + return S_FALSE; + if (memcmp(buf, kSignature_Descriptor, k_SigDesc_Size) != 0) + return S_FALSE; + + UInt64 endPos; + RINOK(stream->Seek(0, STREAM_SEEK_END, &endPos)); + if (endPos > (1 << 20)) + return S_FALSE; + const size_t numBytes = (size_t)endPos; + _descriptorBuf.Alloc(numBytes); + RINOK(stream->Seek(0, STREAM_SEEK_SET, NULL)); + RINOK(ReadStream_FALSE(stream, _descriptorBuf, numBytes)); + + if (!_descriptor.Parse(_descriptorBuf, _descriptorBuf.Size())) + return S_FALSE; + _isMultiVol = true; + _isArc = true; + _phySize = numBytes; + if (_descriptor.IsThere_Parent()) + _unsupported = true; + + if (openCallback) + { + openCallback->QueryInterface(IID_IArchiveOpenVolumeCallback, (void **)&volumeCallback); + } + if (!volumeCallback) + { + _unsupported = true; + return E_NOTIMPL; + } + + /* + UInt64 totalVirtSize = 0; + FOR_VECTOR (i, _descriptor.Extents) + { + const CExtentInfo &ei = _descriptor.Extents[i]; + if (ei.NumSectors >= ((UInt64)1 << (63 - 9))) + return S_FALSE; + totalVirtSize += ei.NumSectors; + if (totalVirtSize >= ((UInt64)1 << (63 - 9))) + return S_FALSE; + } + totalVirtSize <<= 9; + */ + + if (_descriptor.Extents.Size() > 1) + { + const UInt64 numFiles = _descriptor.Extents.Size(); + RINOK(openCallback->SetTotal(&numFiles, NULL)); + } + } + + UInt64 complexity = 0; + + for (;;) + { + CExtent *e = NULL; + CMyComPtr<IInStream> nextStream; + + if (_isMultiVol) + { + const unsigned extentIndex = _extents.Size(); + if (extentIndex >= _descriptor.Extents.Size()) + break; + const CExtentInfo &ei = _descriptor.Extents[extentIndex]; + e = &_extents.AddNew(); + e->StartOffset = 0; + if (ei.NumSectors >= ((UInt64)1 << (62 - 9)) || + ei.StartSector >= ((UInt64)1 << (62 - 9))) + return S_FALSE; + e->NumBytes = ei.NumSectors << 9; + e->IsZero = ei.IsType_ZERO(); + if (extentIndex != 0) + e->StartOffset = _extents[extentIndex - 1].GetEndOffset(); + if (e->GetEndOffset() < e->StartOffset) + return S_FALSE; + + e->VirtSize = e->NumBytes; + if (e->IsZero) { - _unsupported = true; - _isArc = true; - // return E_NOTIMPL; + e->IsOK = true; + continue; } - return S_FALSE; + + e->IsFlat = ei.IsType_Flat(); + e->FlatOffset = ei.StartSector << 9; + + UString u; + CDescriptor::GetUnicodeName(ei.FileName, u); + if (u.IsEmpty()) + { + _missingVol = true; + continue; + } + + HRESULT result = volumeCallback->GetStream(u, &nextStream); + if (result == S_FALSE) + { + _missingVol = true; + continue; + } + if (result != S_OK) + return result; + if (!nextStream) + { + _missingVol = true; + continue; + } + + if (e->IsFlat) + { + e->IsOK = true; + e->Stream = nextStream; + e->PhySize = e->NumBytes; + continue; + } + + stream = nextStream; + + headerSize = kSectoreSize; + RINOK(ReadStream(stream, buf, &headerSize)); + + if (headerSize != kSectoreSize) + continue; + if (memcmp(buf, k_Signature, sizeof(k_Signature)) != 0) + continue; + } + else + { + if (headerSize != kSectoreSize) + return S_FALSE; + e = &_extents.AddNew(); + e->StartOffset = 0; + } + + HRESULT res = S_FALSE; + if (e->h.Parse(buf)) + res = e->Open3(stream, openCallback, _isMultiVol ? _descriptor.Extents.Size() : 1, _extents.Size() - 1, complexity); + + if (!_isMultiVol) + { + _isArc = e->IsArc; + _phySize = e->PhySize; + _unsupported = e->Unsupported; + } + + if (e->Unsupported) + _unsupportedSome = true; + if (e->HeadersError) + _headerError = true; + + if (res != S_OK) + { + if (res != S_FALSE) + return res; + if (!_isMultiVol) + return res; + continue; + } + + e->Stream = stream; + e->IsOK = true; + + if (!_isMultiVol) + { + e->NumBytes = e->VirtSize; + break; + } + + if (e->NumBytes != e->VirtSize) + _headerError = true; } - if (headerSize != kSectoreSize) - return S_FALSE; + if (!_extents.IsEmpty()) + _size = _extents.Back().GetEndOffset(); - // CHeader h; + _needDeflate = false; + _clusterBitsMax = 0; + + unsigned numOKs = 0; + unsigned numUnsupported = 0; - if (!h.Parse(buf)) - return S_FALSE; + FOR_VECTOR (i, _extents) + { + const CExtent &e = _extents[i]; + if (e.Unsupported) + numUnsupported++; + if (!e.IsOK) + continue; + numOKs++; + if (e.IsVmdk()) + { + if (e.NeedDeflate) + _needDeflate = true; + if (_clusterBitsMax < e.ClusterBits) + _clusterBitsMax = e.ClusterBits; + } + } + + if (numUnsupported != 0 && numUnsupported == _extents.Size()) + _unsupported = true; + + return S_OK; +} + +HRESULT CExtent::Open3(IInStream *stream, IArchiveOpenCallback *openCallback, + unsigned numVols, unsigned volIndex, UInt64 &complexity) +{ if (h.descriptorSize != 0) { - if (h.descriptorOffset < 1) - return S_FALSE; - if (h.descriptorSize > (1 << 20)) + if (h.descriptorOffset == 0 || + h.descriptorSize > (1 << 10)) 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) + DescriptorBuf.Alloc((size_t)h.descriptorSize << 9); + 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; } + + DescriptorOK = Descriptor.Parse(DescriptorBuf, DescriptorBuf.Size()); + if (!DescriptorOK) + HeadersError = true; + if (Descriptor.IsThere_Parent()) + Unsupported = true; } if (h.gdOffset == (UInt64)(Int64)-1) @@ -647,7 +1245,7 @@ HRESULT CHandler::Open2(IInStream *stream, IArchiveOpenCallback *openCallback) m.Parse(buf2 + 512 * 2); if (m.NumSectors != 0 || m.SpecSize != 0 || m.Type != k_Marker_END_OF_STREAM) return S_FALSE; - _phySize = endPos; + PhySize = endPos; } int grainSize_Log = GetLog(h.grainSize); @@ -658,27 +1256,27 @@ HRESULT CHandler::Open2(IInStream *stream, IArchiveOpenCallback *openCallback) if (h.overHead >= ((UInt64)1 << (63 - 9))) return S_FALSE; - _isArc = true; - _clusterBits = (9 + grainSize_Log); - _size = h.capacity << 9; - _needDeflate = (h.algo >= 1); + IsArc = true; + ClusterBits = (9 + grainSize_Log); + VirtSize = h.capacity << 9; + NeedDeflate = (h.algo >= 1); if (h.Is_Compressed() ? (h.algo > 1 || !h.Is_Marker()) : (h.algo != 0)) { - _unsupported = true; - _phySize = 0; + Unsupported = true; + PhySize = 0; return S_FALSE; } { UInt64 overHeadBytes = h.overHead << 9; - if (_phySize < overHeadBytes) - _phySize = overHeadBytes; + if (PhySize < overHeadBytes) + PhySize = overHeadBytes; } - _zeroSector = 0; + ZeroSector = 0; if (h.Is_ZeroGrain()) - _zeroSector = 1; + ZeroSector = 1; const UInt64 numSectorsPerGde = (UInt64)1 << (grainSize_Log + k_NumMidBits); const UInt64 numGdeEntries = (h.capacity + numSectorsPerGde - 1) >> (grainSize_Log + k_NumMidBits); @@ -713,12 +1311,22 @@ HRESULT CHandler::Open2(IInStream *stream, IArchiveOpenCallback *openCallback) RINOK(ReadForHeader(stream, h.gdOffset, table, numSectors)); } - size_t clusterSize = (size_t)1 << _clusterBits; + const size_t clusterSize = (size_t)1 << ClusterBits; + + const UInt64 complexityStart = complexity; if (openCallback) { - UInt64 totalBytes = (UInt64)numGdeEntries << (k_NumMidBits + 2); - RINOK(openCallback->SetTotal(NULL, &totalBytes)); + complexity += (UInt64)numGdeEntries << (k_NumMidBits + 2); + { + const UInt64 numVols2 = numVols; + RINOK(openCallback->SetTotal((numVols == 1) ? NULL : &numVols2, &complexity)); + } + if (numVols != 1) + { + const UInt64 volIndex2 = volIndex; + RINOK(openCallback->SetCompleted(numVols == 1 ? NULL : &volIndex2, &complexityStart)); + } } UInt64 lastSector = 0; @@ -728,14 +1336,14 @@ HRESULT CHandler::Open2(IInStream *stream, IArchiveOpenCallback *openCallback) 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) + CByteBuffer &buf = Tables.AddNew(); + if (v == 0 || v == ZeroSector) continue; - - if (openCallback && ((i - numProcessed_Prev) & 0xFFF) == 0) + if (openCallback && (i - numProcessed_Prev) >= 1024) { - UInt64 numBytes = (UInt64)i << (k_NumMidBits + 2); - RINOK(openCallback->SetCompleted(NULL, &numBytes)); + const UInt64 comp = complexityStart + ((UInt64)i << (k_NumMidBits + 2)); + const UInt64 volIndex2 = volIndex; + RINOK(openCallback->SetCompleted(numVols == 1 ? NULL : &volIndex2, &comp)); numProcessed_Prev = i; } @@ -764,25 +1372,24 @@ HRESULT CHandler::Open2(IInStream *stream, IArchiveOpenCallback *openCallback) for (size_t k = 0; k < k_NumMidItems; k++) { UInt32 v = Get32((const Byte *)buf + (size_t)k * 4); - if (v == 0 || v == _zeroSector) + if (v == 0 || v == ZeroSector) continue; if (v < h.overHead) return S_FALSE; if (lastSector < v) { lastSector = v; - if (_needDeflate) + if (NeedDeflate) lastVirtCluster = ((UInt64)i << k_NumMidBits) + k; } } } - - if (!_needDeflate) + if (!NeedDeflate) { UInt64 end = ((UInt64)lastSector << 9) + clusterSize; - if (_phySize < end) - _phySize = end; + if (PhySize < end) + PhySize = end; } else if (lastSector != 0) { @@ -790,27 +1397,18 @@ HRESULT CHandler::Open2(IInStream *stream, IArchiveOpenCallback *openCallback) if (ReadForHeader(stream, lastSector, buf, 1) == S_OK) { UInt64 lba = Get64(buf); - if (lba == (lastVirtCluster << (_clusterBits - 9))) + 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 (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; } @@ -819,21 +1417,26 @@ STDMETHODIMP CHandler::Close() { _phySize = 0; _size = 0; + _cacheCluster = (UInt64)(Int64)-1; - _zeroSector = 0; - _clusterBits = 0; + _cacheExtent = (unsigned)(int)-1; + + _clusterBitsMax = 0; - _needDeflate = false; _isArc = false; _unsupported = false; - // _headerError = false; + _unsupportedSome = false; + _headerError = false; + _missingVol = false; + _isMultiVol = false; + _needDeflate = false; - _tables.Clear(); _descriptorBuf.Free(); _descriptor.Clear(); _imgExt = NULL; - Stream.Release(); + Stream.Release(); // Stream vriable is unused + _extents.Clear(); return S_OK; } @@ -846,6 +1449,8 @@ STDMETHODIMP CHandler::GetStream(UInt32 /* index */, ISequentialInStream **strea if (_unsupported) return S_FALSE; + ClearStreamVars(); + // _stream_UsePackSize = true; if (_needDeflate) { @@ -867,13 +1472,18 @@ STDMETHODIMP CHandler::GetStream(UInt32 /* index */, ISequentialInStream **strea _zlibDecoder = _zlibDecoderSpec; } - size_t clusterSize = (size_t)1 << _clusterBits; + const size_t clusterSize = (size_t)1 << _clusterBitsMax; _cache.AllocAtLeast(clusterSize); _cacheCompressed.AllocAtLeast(clusterSize * 2); } + FOR_VECTOR (i, _extents) + { + RINOK(_extents[i].InitAndSeek()); + } + CMyComPtr<ISequentialInStream> streamTemp = this; - RINOK(InitAndSeek()); + InitAndSeekMain(); *stream = streamTemp.Detach(); return S_OK; COM_TRY_END |