xref: /aosp_15_r20/external/lzma/CPP/7zip/Archive/VmdkHandler.cpp (revision f6dc9357d832569d4d1f5d24eacdb3935a1ae8e6)
1 // VmdkHandler.cpp
2 
3 #include "StdAfx.h"
4 
5 // #include <stdio.h>
6 
7 #include "../../../C/CpuArch.h"
8 
9 #include "../../Common/ComTry.h"
10 #include "../../Common/IntToString.h"
11 #include "../../Common/StringConvert.h"
12 #include "../../Common/StringToInt.h"
13 #include "../../Common/UTFConvert.h"
14 
15 #include "../../Windows/PropVariant.h"
16 
17 #include "../Common/RegisterArc.h"
18 #include "../Common/StreamObjects.h"
19 #include "../Common/StreamUtils.h"
20 
21 #include "../Compress/ZlibDecoder.h"
22 
23 #include "HandlerCont.h"
24 
25 using namespace NWindows;
26 
27 namespace NArchive {
28 namespace NVmdk {
29 
30 #define Get16(p) GetUi16(p)
31 #define Get32(p) GetUi32(p)
32 #define Get64(p) GetUi64(p)
33 
34 #define LE_16(offs, dest) dest = Get16(p + (offs))
35 #define LE_32(offs, dest) dest = Get32(p + (offs))
36 #define LE_64(offs, dest) dest = Get64(p + (offs))
37 
38 
39 static const Byte k_Signature[] = { 'K', 'D', 'M', 'V' };
40 
41 static const UInt32 k_Flags_NL         = (UInt32)1 << 0;
42 // static const UInt32 k_Flags_RGD        = (UInt32)1 << 1;
43 static const UInt32 k_Flags_ZeroGrain  = (UInt32)1 << 2;
44 static const UInt32 k_Flags_Compressed = (UInt32)1 << 16;
45 static const UInt32 k_Flags_Marker     = (UInt32)1 << 17;
46 
47 static const unsigned k_NumMidBits = 9; // num bits for index in Grain Table
48 
49 struct CHeader
50 {
51   UInt32 flags;
52   UInt32 version;
53 
54   UInt64 capacity;
55   UInt64 grainSize;
56   UInt64 descriptorOffset;
57   UInt64 descriptorSize;
58 
59   UInt32 numGTEsPerGT;
60   UInt16 algo;
61   // Byte uncleanShutdown;
62   // UInt64 rgdOffset;
63   UInt64 gdOffset;
64   UInt64 overHead;
65 
Is_NLNArchive::NVmdk::CHeader66   bool Is_NL()         const { return (flags & k_Flags_NL) != 0; }
Is_ZeroGrainNArchive::NVmdk::CHeader67   bool Is_ZeroGrain()  const { return (flags & k_Flags_ZeroGrain) != 0; }
Is_CompressedNArchive::NVmdk::CHeader68   bool Is_Compressed() const { return (flags & k_Flags_Compressed) != 0; }
Is_MarkerNArchive::NVmdk::CHeader69   bool Is_Marker()     const { return (flags & k_Flags_Marker) != 0; }
70 
71   bool Parse(const Byte *p);
72 
IsSameImageForNArchive::NVmdk::CHeader73   bool IsSameImageFor(const CHeader &h) const
74   {
75     return flags == h.flags
76         && version == h.version
77         && capacity == h.capacity
78         && grainSize == h.grainSize
79         && algo == h.algo;
80   }
81 };
82 
Parse(const Byte * p)83 bool CHeader::Parse(const Byte *p)
84 {
85   if (memcmp(p, k_Signature, sizeof(k_Signature)) != 0)
86     return false;
87 
88   LE_32 (0x04, version);
89   LE_32 (0x08, flags);
90   LE_64 (0x0C, capacity);
91   LE_64 (0x14, grainSize);
92   LE_64 (0x1C, descriptorOffset);
93   LE_64 (0x24, descriptorSize);
94   LE_32 (0x2C, numGTEsPerGT);
95   // LE_64 (0x30, rgdOffset);
96   LE_64 (0x38, gdOffset);
97   LE_64 (0x40, overHead);
98   // uncleanShutdown = buf[0x48];
99   LE_16(0x4D, algo);
100 
101   if (Is_NL() && Get32(p + 0x49) != 0x0A0D200A) // do we need Is_NL() check here?
102     return false;
103 
104   return (numGTEsPerGT == (1 << k_NumMidBits)) && (version <= 3);
105 }
106 
107 
108 enum
109 {
110   k_Marker_END_OF_STREAM = 0,
111   k_Marker_GRAIN_TABLE   = 1,
112   k_Marker_GRAIN_DIR     = 2,
113   k_Marker_FOOTER        = 3
114 };
115 
116 struct CMarker
117 {
118   UInt64 NumSectors;
119   UInt32 SpecSize; // = 0 for metadata sectors
120   UInt32 Type;
121 
ParseNArchive::NVmdk::CMarker122   void Parse(const Byte *p)
123   {
124     LE_64 (0, NumSectors);
125     LE_32 (8, SpecSize);
126     LE_32 (12, Type);
127   }
128 };
129 
130 
Str_to_ValName(const AString & s,AString & name,AString & val)131 static bool Str_to_ValName(const AString &s, AString &name, AString &val)
132 {
133   name.Empty();
134   val.Empty();
135   int qu = s.Find('"');
136   int eq = s.Find('=');
137   if (eq < 0 || (qu >= 0 && eq > qu))
138     return false;
139   name.SetFrom(s.Ptr(), eq);
140   name.Trim();
141   val = s.Ptr(eq + 1);
142   val.Trim();
143   return true;
144 }
145 
IsSpaceChar(char c)146 static inline bool IsSpaceChar(char c)
147 {
148   return (c == ' ' || c == '\t');
149 }
150 
SkipSpaces(const char * s)151 static const char *SkipSpaces(const char *s)
152 {
153   for (;; s++)
154   {
155     char c = *s;
156     if (c == 0 || !IsSpaceChar(c))
157       return s;
158   }
159 }
160 
161 #define SKIP_SPACES(s) s = SkipSpaces(s);
162 
GetNextWord(const char * s,AString & dest)163 static const char *GetNextWord(const char *s, AString &dest)
164 {
165   dest.Empty();
166   SKIP_SPACES(s)
167   const char *start = s;
168   for (;; s++)
169   {
170     char c = *s;
171     if (c == 0 || IsSpaceChar(c))
172     {
173       dest.SetFrom(start, (unsigned)(s - start));
174       return s;
175     }
176   }
177 }
178 
GetNextNumber(const char * s,UInt64 & val)179 static const char *GetNextNumber(const char *s, UInt64 &val)
180 {
181   SKIP_SPACES(s)
182   if (*s == 0)
183     return s;
184   const char *end;
185   val = ConvertStringToUInt64(s, &end);
186   char c = *end;
187   if (c != 0 && !IsSpaceChar(c))
188     return NULL;
189   return end;
190 }
191 
192 
193 struct CExtentInfo
194 {
195   AString Access;    // RW, RDONLY, or NOACCESS
196   UInt64 NumSectors; // 512 bytes sectors
197   AString Type;      // FLAT, SPARSE, ZERO, VMFS, VMFSSPARSE, VMFSRDM, VMFSRAW
198   AString FileName;
199   UInt64 StartSector; // used for FLAT
200 
201   // for VMWare Player 9:
202   // PartitionUUID
203   // DeviceIdentifier
204 
IsType_ZERONArchive::NVmdk::CExtentInfo205   bool IsType_ZERO() const { return Type == "ZERO"; }
206   // bool IsType_FLAT() const { return Type == "FLAT"; }
IsType_FlatNArchive::NVmdk::CExtentInfo207   bool IsType_Flat() const { return Type == "FLAT" || Type == "VMFS" || Type == "VMFSRAW"; }
208 
209   bool Parse(const char *s);
210 };
211 
Parse(const char * s)212 bool CExtentInfo::Parse(const char *s)
213 {
214   NumSectors = 0;
215   StartSector = 0;
216   Access.Empty();
217   Type.Empty();
218   FileName.Empty();
219 
220   s = GetNextWord(s, Access);
221   s = GetNextNumber(s, NumSectors);
222   if (!s)
223     return false;
224   s = GetNextWord(s, Type);
225 
226   if (Type.IsEmpty())
227     return false;
228 
229   SKIP_SPACES(s)
230 
231   if (IsType_ZERO())
232     return (*s == 0);
233 
234   if (*s != '\"')
235     return false;
236   s++;
237   {
238     const char *s2 = strchr(s, '\"');
239     if (!s2)
240       return false;
241     FileName.SetFrom(s, (unsigned)(s2 - s));
242     s = s2 + 1;
243   }
244   SKIP_SPACES(s)
245   if (*s == 0)
246     return true;
247 
248   s = GetNextNumber(s, StartSector);
249   if (!s)
250     return false;
251   return true;
252   // SKIP_SPACES(s);
253   // return (*s == 0);
254 }
255 
256 
257 struct CDescriptor
258 {
259   AString CID;
260   AString parentCID;
261   AString createType;
262   // AString encoding; // UTF-8, windows-1252 - default is UTF-8
263 
264   CObjectVector<CExtentInfo> Extents;
265 
GetUnicodeNameNArchive::NVmdk::CDescriptor266   static void GetUnicodeName(const AString &s, UString &res)
267   {
268     if (!ConvertUTF8ToUnicode(s, res))
269       MultiByteToUnicodeString2(res, s);
270   }
271 
ClearNArchive::NVmdk::CDescriptor272   void Clear()
273   {
274     CID.Empty();
275     parentCID.Empty();
276     createType.Empty();
277     Extents.Clear();
278   }
279 
IsThere_ParentNArchive::NVmdk::CDescriptor280   bool IsThere_Parent() const
281   {
282     return !parentCID.IsEmpty() && !parentCID.IsEqualTo_Ascii_NoCase("ffffffff");
283   }
284 
285   bool Parse(const Byte *p, size_t size);
286 };
287 
288 
Parse(const Byte * p,size_t size)289 bool CDescriptor::Parse(const Byte *p, size_t size)
290 {
291   Clear();
292 
293   AString s;
294   AString name;
295   AString val;
296 
297   for (;;)
298   {
299     Byte c = 0;
300     if (size != 0)
301     {
302       size--;
303       c = *p++;
304     }
305     if (c == 0 || c == 0xA || c == 0xD)
306     {
307       if (!s.IsEmpty() && s[0] != '#')
308       {
309         if (Str_to_ValName(s, name, val))
310         {
311           if (name.IsEqualTo_Ascii_NoCase("CID"))
312             CID = val;
313           else if (name.IsEqualTo_Ascii_NoCase("parentCID"))
314             parentCID = val;
315           else if (name.IsEqualTo_Ascii_NoCase("createType"))
316             createType = val;
317         }
318         else
319         {
320           CExtentInfo ei;
321           if (!ei.Parse(s))
322             return false;
323           Extents.Add(ei);
324         }
325       }
326 
327       s.Empty();
328       if (c == 0)
329         return true;
330     }
331     else
332       s += (char)c;
333   }
334 }
335 
336 
337 struct CExtent
338 {
339   bool IsOK;
340   bool IsArc;
341   bool NeedDeflate;
342   bool Unsupported;
343   bool IsZero;
344   bool IsFlat;
345   bool DescriptorOK;
346   bool HeadersError;
347 
348   unsigned ClusterBits;
349   UInt32 ZeroSector;
350 
351   CObjectVector<CByteBuffer> Tables;
352 
353   CMyComPtr<IInStream> Stream;
354   UInt64 PosInArc;
355 
356   UInt64 PhySize;
357   UInt64 VirtSize;     // from vmdk header of volume
358 
359   UInt64 StartOffset;  // virtual offset of this extent
360   UInt64 NumBytes;     // from main descriptor, if multi-vol
361   UInt64 FlatOffset;   // in Stream
362 
363   CByteBuffer DescriptorBuf;
364   CDescriptor Descriptor;
365 
366   CHeader h;
367 
GetEndOffsetNArchive::NVmdk::CExtent368   UInt64 GetEndOffset() const { return StartOffset + NumBytes; }
369 
IsVmdkNArchive::NVmdk::CExtent370   bool IsVmdk() const { return !IsZero && !IsFlat; }
371   // if (IsOK && IsVmdk()), then VMDK header of this extent was read
372 
CExtentNArchive::NVmdk::CExtent373   CExtent():
374       IsOK(false),
375       IsArc(false),
376       NeedDeflate(false),
377       Unsupported(false),
378       IsZero(false),
379       IsFlat(false),
380       DescriptorOK(false),
381       HeadersError(false),
382 
383       ClusterBits(0),
384       ZeroSector(0),
385 
386       PosInArc(0),
387 
388       PhySize(0),
389       VirtSize(0),
390 
391       StartOffset(0),
392       NumBytes(0),
393       FlatOffset(0)
394         {}
395 
396 
397   HRESULT ReadForHeader(IInStream *stream, UInt64 sector, void *data, size_t numSectors);
398   HRESULT Open3(IInStream *stream, IArchiveOpenCallback *openCallback,
399         unsigned numVols, unsigned volIndex, UInt64 &complexity);
400 
SeekNArchive::NVmdk::CExtent401   HRESULT Seek(UInt64 offset)
402   {
403     PosInArc = offset;
404     return InStream_SeekSet(Stream, offset);
405   }
406 
InitAndSeekNArchive::NVmdk::CExtent407   HRESULT InitAndSeek()
408   {
409     if (Stream)
410       return Seek(0);
411     return S_OK;
412   }
413 
ReadNArchive::NVmdk::CExtent414   HRESULT Read(void *data, size_t *size)
415   {
416     HRESULT res = ReadStream(Stream, data, size);
417     PosInArc += *size;
418     return res;
419   }
420 };
421 
422 
423 Z7_class_CHandler_final: public CHandlerImg
424 {
425   bool _isArc;
426   bool _unsupported;
427   bool _unsupportedSome;
428   bool _headerError;
429   bool _missingVol;
430   bool _isMultiVol;
431   bool _needDeflate;
432 
433   UInt64 _cacheCluster;
434   unsigned _cacheExtent;
435   CByteBuffer _cache;
436   CByteBuffer _cacheCompressed;
437 
438   unsigned _clusterBitsMax;
439   UInt64 _phySize;
440 
441   CObjectVector<CExtent> _extents;
442 
443   CBufInStream *_bufInStreamSpec;
444   CMyComPtr<ISequentialInStream> _bufInStream;
445 
446   CBufPtrSeqOutStream *_bufOutStreamSpec;
447   CMyComPtr<ISequentialOutStream> _bufOutStream;
448 
449   NCompress::NZlib::CDecoder *_zlibDecoderSpec;
450   CMyComPtr<ICompressCoder> _zlibDecoder;
451 
452   CByteBuffer _descriptorBuf;
453   CDescriptor _descriptor;
454 
455   UString _missingVolName;
456 
457   void InitAndSeekMain()
458   {
459     _virtPos = 0;
460   }
461 
462   virtual HRESULT Open2(IInStream *stream, IArchiveOpenCallback *openCallback) Z7_override;
463   virtual void CloseAtError() Z7_override;
464 public:
465   Z7_IFACE_COM7_IMP(IInArchive_Img)
466 
467   Z7_IFACE_COM7_IMP(IInArchiveGetStream)
468   Z7_IFACE_COM7_IMP(ISequentialInStream)
469 };
470 
471 
472 Z7_COM7F_IMF(CHandler::Read(void *data, UInt32 size, UInt32 *processedSize))
473 {
474   if (processedSize)
475     *processedSize = 0;
476   if (_virtPos >= _size)
477     return S_OK;
478   {
479     UInt64 rem = _size - _virtPos;
480     if (size > rem)
481       size = (UInt32)rem;
482     if (size == 0)
483       return S_OK;
484   }
485 
486   unsigned extentIndex;
487   {
488     unsigned left = 0, right = _extents.Size();
489     for (;;)
490     {
491       unsigned mid = (left + right) / 2;
492       if (mid == left)
493         break;
494       if (_virtPos < _extents[mid].StartOffset)
495         right = mid;
496       else
497         left = mid;
498     }
499     extentIndex = left;
500   }
501 
502   CExtent &extent = _extents[extentIndex];
503 
504   {
505     const UInt64 vir = _virtPos - extent.StartOffset;
506     if (vir >= extent.NumBytes)
507     {
508       return E_FAIL;
509       /*
510       if (vir > extent.NumBytes)
511         _stream_dataError = true;
512       memset(data, 0, size);
513       _virtPos += size;
514       if (processedSize)
515         *processedSize = size;
516       return S_OK;
517       */
518     }
519 
520     {
521       const UInt64 rem = extent.NumBytes - vir;
522       if (size > rem)
523         size = (UInt32)rem;
524     }
525 
526     if (vir >= extent.VirtSize)
527     {
528       // if vmdk's VirtSize is smaller than VirtSize from main multi-volume descriptor
529       _stream_dataError = true;
530       return S_FALSE;
531       /*
532       memset(data, 0, size);
533       _virtPos += size;
534       if (processedSize)
535         *processedSize = size;
536       return S_OK;
537       */
538     }
539 
540     {
541       const UInt64 rem = extent.VirtSize - vir;
542       if (size > rem)
543         size = (UInt32)rem;
544     }
545 
546     if (extent.IsZero || !extent.IsOK || !extent.Stream || extent.Unsupported)
547     {
548       if (extent.Unsupported)
549       {
550         _stream_unsupportedMethod = true;
551         return S_FALSE;
552       }
553       if (!extent.IsOK || !extent.Stream)
554       {
555         _stream_unavailData = true;
556         return S_FALSE;
557       }
558       memset(data, 0, size);
559       _virtPos += size;
560       if (processedSize)
561         *processedSize = size;
562       return S_OK;
563     }
564 
565     if (extent.IsFlat)
566     {
567       UInt64 offset = extent.FlatOffset + vir;
568       if (offset != extent.PosInArc)
569       {
570         RINOK(extent.Seek(offset))
571       }
572       UInt32 size2 = 0;
573       HRESULT res = extent.Stream->Read(data, size, &size2);
574       if (res == S_OK && size2 == 0)
575       {
576         _stream_unavailData = true;
577         /*
578         memset(data, 0, size);
579         _virtPos += size;
580         if (processedSize)
581           *processedSize = size;
582         return S_OK;
583         */
584       }
585       // _stream_PackSize += size2;
586       extent.PosInArc += size2;
587       _virtPos += size2;
588       if (processedSize)
589         *processedSize = size2;
590       return res;
591     }
592   }
593 
594 
595   for (;;)
596   {
597     const UInt64 vir = _virtPos - extent.StartOffset;
598     const unsigned clusterBits = extent.ClusterBits;
599     const UInt64 cluster = vir >> clusterBits;
600     const size_t clusterSize = (size_t)1 << clusterBits;
601     const size_t lowBits = (size_t)vir & (clusterSize - 1);
602     {
603       size_t rem = clusterSize - lowBits;
604       if (size > rem)
605         size = (UInt32)rem;
606     }
607 
608     if (extentIndex == _cacheExtent && cluster == _cacheCluster)
609     {
610       memcpy(data, _cache + lowBits, size);
611       _virtPos += size;
612       if (processedSize)
613         *processedSize = size;
614       return S_OK;
615     }
616 
617     const UInt64 high = cluster >> k_NumMidBits;
618 
619     if (high < extent.Tables.Size())
620     {
621       const CByteBuffer &table = extent.Tables[(unsigned)high];
622 
623       if (table.Size() != 0)
624       {
625         const size_t midBits = (size_t)cluster & ((1 << k_NumMidBits) - 1);
626         const Byte *p = (const Byte *)table + (midBits << 2);
627         const UInt32 v = Get32(p);
628 
629         if (v != 0 && v != extent.ZeroSector)
630         {
631           UInt64 offset = (UInt64)v << 9;
632           if (extent.NeedDeflate)
633           {
634             if (offset != extent.PosInArc)
635             {
636               // printf("\n%12x %12x\n", (unsigned)offset, (unsigned)(offset - extent.PosInArc));
637               RINOK(extent.Seek(offset))
638             }
639 
640             const size_t kStartSize = 1 << 9;
641             {
642               size_t curSize = kStartSize;
643               RINOK(extent.Read(_cacheCompressed, &curSize))
644               // _stream_PackSize += curSize;
645               if (curSize != kStartSize)
646                 return S_FALSE;
647             }
648 
649             if (Get64(_cacheCompressed) != (cluster << (clusterBits - 9)))
650               return S_FALSE;
651 
652             UInt32 dataSize = Get32(_cacheCompressed + 8);
653             if (dataSize > ((UInt32)1 << 31))
654               return S_FALSE;
655 
656             size_t dataSize2 = (size_t)dataSize + 12;
657 
658             if (dataSize2 > kStartSize)
659             {
660               dataSize2 = (dataSize2 + 511) & ~(size_t)511;
661               if (dataSize2 > _cacheCompressed.Size())
662                 return S_FALSE;
663               size_t curSize = dataSize2 - kStartSize;
664               const size_t curSize2 = curSize;
665               RINOK(extent.Read(_cacheCompressed + kStartSize, &curSize))
666               // _stream_PackSize += curSize;
667               if (curSize != curSize2)
668                 return S_FALSE;
669             }
670 
671             _bufInStreamSpec->Init(_cacheCompressed + 12, dataSize);
672 
673             _cacheCluster = (UInt64)(Int64)-1;
674             _cacheExtent = (unsigned)(int)-1;
675 
676             if (_cache.Size() < clusterSize)
677               return E_FAIL;
678             _bufOutStreamSpec->Init(_cache, clusterSize);
679 
680             // Do we need to use smaller block than clusterSize for last cluster?
681             const UInt64 blockSize64 = clusterSize;
682             HRESULT res = _zlibDecoder->Code(_bufInStream, _bufOutStream, NULL, &blockSize64, NULL);
683 
684             /*
685             if (_bufOutStreamSpec->GetPos() != clusterSize)
686             {
687               _stream_dataError = true;
688               memset(_cache + _bufOutStreamSpec->GetPos(), 0, clusterSize - _bufOutStreamSpec->GetPos());
689             }
690             */
691 
692               if (_bufOutStreamSpec->GetPos() != clusterSize
693                   || _zlibDecoderSpec->GetInputProcessedSize() != dataSize)
694               {
695                 _stream_dataError = true;
696                 if (res == S_OK)
697                   res = S_FALSE;
698               }
699 
700             RINOK(res)
701 
702             _cacheCluster = cluster;
703             _cacheExtent = extentIndex;
704 
705             continue;
706             /*
707             memcpy(data, _cache + lowBits, size);
708             _virtPos += size;
709             if (processedSize)
710               *processedSize = size;
711             return S_OK;
712             */
713           }
714           {
715             offset += lowBits;
716             if (offset != extent.PosInArc)
717             {
718               // printf("\n%12x %12x\n", (unsigned)offset, (unsigned)(offset - extent.PosInArc));
719               RINOK(extent.Seek(offset))
720             }
721             UInt32 size2 = 0;
722             HRESULT res = extent.Stream->Read(data, size, &size2);
723             if (res == S_OK && size2 == 0)
724             {
725               _stream_unavailData = true;
726               /*
727               memset(data, 0, size);
728               _virtPos += size;
729               if (processedSize)
730                 *processedSize = size;
731               return S_OK;
732               */
733             }
734             extent.PosInArc += size2;
735             // _stream_PackSize += size2;
736             _virtPos += size2;
737             if (processedSize)
738               *processedSize = size2;
739             return res;
740           }
741         }
742       }
743     }
744 
745     memset(data, 0, size);
746     _virtPos += size;
747     if (processedSize)
748       *processedSize = size;
749     return S_OK;
750   }
751 }
752 
753 
754 static const Byte kProps[] =
755 {
756   kpidSize,
757   kpidPackSize
758 };
759 
760 static const Byte kArcProps[] =
761 {
762   kpidNumVolumes,
763   kpidTotalPhySize,
764   kpidMethod,
765   kpidClusterSize,
766   kpidHeadersSize,
767   kpidId,
768   kpidName,
769   kpidComment
770 };
771 
772 IMP_IInArchive_Props
773 IMP_IInArchive_ArcProps
774 
775 
776 Z7_COM7F_IMF(CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value))
777 {
778   COM_TRY_BEGIN
779   NCOM::CPropVariant prop;
780 
781   const CExtent *e = NULL;
782   const CDescriptor *desc = NULL;
783 
784   if (_isMultiVol)
785     desc = &_descriptor;
786   else if (_extents.Size() == 1)
787   {
788     e = &_extents[0];
789     desc = &e->Descriptor;
790   }
791 
792   switch (propID)
793   {
794     case kpidMainSubfile: prop = (UInt32)0; break;
795     case kpidPhySize: if (_phySize != 0) prop = _phySize; break;
796     case kpidTotalPhySize:
797     {
798       UInt64 sum = _phySize;
799       if (_isMultiVol)
800       {
801         FOR_VECTOR (i, _extents)
802           sum += _extents[i].PhySize;
803       }
804       prop = sum;
805       break;
806     }
807     case kpidClusterSize: prop = (UInt32)((UInt32)1 << _clusterBitsMax); break;
808     case kpidHeadersSize: if (e) prop = (e->h.overHead << 9); break;
809     case kpidMethod:
810     {
811       AString s;
812 
813       if (desc && !desc->createType.IsEmpty())
814         s = desc->createType;
815 
816       bool zlib = false;
817       bool marker = false;
818       Int32 algo = -1;
819 
820       FOR_VECTOR (i, _extents)
821       {
822         const CExtent &extent = _extents[i];
823         if (!extent.IsOK || !extent.IsVmdk())
824           continue;
825 
826         const CHeader &h = extent.h;
827 
828         if (h.algo != 0)
829         {
830           if (h.algo == 1)
831             zlib = true;
832           else if (algo != h.algo)
833           {
834             s.Add_Space_if_NotEmpty();
835             s.Add_UInt32(h.algo);
836             algo = h.algo;
837           }
838         }
839 
840         if (h.Is_Marker())
841           marker = true;
842       }
843 
844       if (zlib)
845         s.Add_OptSpaced("zlib");
846 
847       if (marker)
848         s.Add_OptSpaced("Marker");
849 
850       if (!s.IsEmpty())
851         prop = s;
852       break;
853     }
854 
855     case kpidComment:
856     {
857       if (e && e->DescriptorBuf.Size() != 0)
858       {
859         AString s;
860         s.SetFrom_CalcLen((const char *)(const Byte *)e->DescriptorBuf, (unsigned)e->DescriptorBuf.Size());
861         if (!s.IsEmpty() && s.Len() <= (1 << 16))
862           prop = s;
863       }
864       break;
865     }
866 
867     case kpidId:
868     {
869       if (desc && !desc->CID.IsEmpty())
870       {
871         prop = desc->CID;
872       }
873       break;
874     }
875 
876     case kpidName:
877     {
878       if (!_isMultiVol && desc && desc->Extents.Size() == 1)
879       {
880         const CExtentInfo &ei = desc->Extents[0];
881         if (!ei.FileName.IsEmpty())
882         {
883           UString u;
884           CDescriptor::GetUnicodeName(ei.FileName, u);
885           if (!u.IsEmpty())
886             prop = u;
887         }
888       }
889       break;
890     }
891 
892     case kpidNumVolumes: if (_isMultiVol) prop = (UInt32)_extents.Size(); break;
893 
894     case kpidError:
895     {
896       if (_missingVol || !_missingVolName.IsEmpty())
897       {
898         UString s ("Missing volume : ");
899         if (!_missingVolName.IsEmpty())
900           s += _missingVolName;
901         prop = s;
902       }
903       break;
904     }
905 
906     case kpidErrorFlags:
907     {
908       UInt32 v = 0;
909       if (!_isArc) v |= kpv_ErrorFlags_IsNotArc;
910       if (_unsupported) v |= kpv_ErrorFlags_UnsupportedMethod;
911       if (_unsupportedSome) v |= kpv_ErrorFlags_UnsupportedMethod;
912       if (_headerError) v |= kpv_ErrorFlags_HeadersError;
913       // if (_missingVol)  v |= kpv_ErrorFlags_UnexpectedEnd;
914       if (v != 0)
915         prop = v;
916       break;
917     }
918   }
919 
920   prop.Detach(value);
921   return S_OK;
922   COM_TRY_END
923 }
924 
925 
926 Z7_COM7F_IMF(CHandler::GetProperty(UInt32 /* index */, PROPID propID, PROPVARIANT *value))
927 {
928   COM_TRY_BEGIN
929   NCOM::CPropVariant prop;
930 
931   switch (propID)
932   {
933     case kpidSize: prop = _size; break;
934     case kpidPackSize:
935     {
936       UInt64 packSize = 0;
937       FOR_VECTOR (i, _extents)
938       {
939         const CExtent &e = _extents[i];
940         if (!e.IsOK)
941           continue;
942         if (e.IsVmdk() && !_isMultiVol)
943         {
944           UInt64 ov = (e.h.overHead << 9);
945           if (e.PhySize >= ov)
946             packSize += e.PhySize - ov;
947         }
948         else
949           packSize += e.PhySize;
950       }
951       prop = packSize;
952       break;
953     }
954     case kpidExtension: prop = (_imgExt ? _imgExt : "img"); break;
955   }
956 
957   prop.Detach(value);
958   return S_OK;
959   COM_TRY_END
960 }
961 
962 
963 static int inline GetLog(UInt64 num)
964 {
965   for (int i = 0; i < 64; i++)
966     if (((UInt64)1 << i) == num)
967       return i;
968   return -1;
969 }
970 
971 
972 HRESULT CExtent::ReadForHeader(IInStream *stream, UInt64 sector, void *data, size_t numSectors)
973 {
974   sector <<= 9;
975   RINOK(InStream_SeekSet(stream, sector))
976   size_t size = numSectors << 9;
977   RINOK(ReadStream_FALSE(stream, data, size))
978   UInt64 end = sector + size;
979   if (PhySize < end)
980     PhySize = end;
981   return S_OK;
982 }
983 
984 
985 void CHandler::CloseAtError()
986 {
987   _extents.Clear();
988   CHandlerImg::CloseAtError();
989 }
990 
991 
992 static const char * const kSignature_Descriptor = "# Disk DescriptorFile";
993 
994 
995 HRESULT CHandler::Open2(IInStream *stream, IArchiveOpenCallback *openCallback)
996 {
997   const unsigned kSectoreSize = 512;
998   Byte buf[kSectoreSize];
999   size_t headerSize = kSectoreSize;
1000   RINOK(ReadStream(stream, buf, &headerSize))
1001 
1002   if (headerSize < sizeof(k_Signature))
1003     return S_FALSE;
1004 
1005   CMyComPtr<IArchiveOpenVolumeCallback> volumeCallback;
1006 
1007   if (memcmp(buf, k_Signature, sizeof(k_Signature)) != 0)
1008   {
1009     const size_t k_SigDesc_Size = strlen(kSignature_Descriptor);
1010     if (headerSize < k_SigDesc_Size)
1011       return S_FALSE;
1012     if (memcmp(buf, kSignature_Descriptor, k_SigDesc_Size) != 0)
1013       return S_FALSE;
1014 
1015     UInt64 endPos;
1016     RINOK(InStream_GetSize_SeekToEnd(stream, endPos))
1017     if (endPos > (1 << 20))
1018       return S_FALSE;
1019     const size_t numBytes = (size_t)endPos;
1020     _descriptorBuf.Alloc(numBytes);
1021     RINOK(InStream_SeekToBegin(stream))
1022     RINOK(ReadStream_FALSE(stream, _descriptorBuf, numBytes))
1023 
1024     if (!_descriptor.Parse(_descriptorBuf, _descriptorBuf.Size()))
1025       return S_FALSE;
1026     _isMultiVol = true;
1027     _isArc = true;
1028     _phySize = numBytes;
1029     if (_descriptor.IsThere_Parent())
1030       _unsupported = true;
1031 
1032     if (openCallback)
1033     {
1034       openCallback->QueryInterface(IID_IArchiveOpenVolumeCallback, (void **)&volumeCallback);
1035     }
1036     if (!volumeCallback)
1037     {
1038       _unsupported = true;
1039       return E_NOTIMPL;
1040     }
1041 
1042     /*
1043     UInt64 totalVirtSize = 0;
1044     FOR_VECTOR (i, _descriptor.Extents)
1045     {
1046       const CExtentInfo &ei = _descriptor.Extents[i];
1047       if (ei.NumSectors >= ((UInt64)1 << (63 - 9)))
1048         return S_FALSE;
1049       totalVirtSize += ei.NumSectors;
1050       if (totalVirtSize >= ((UInt64)1 << (63 - 9)))
1051         return S_FALSE;
1052     }
1053     totalVirtSize <<= 9;
1054     */
1055 
1056     if (_descriptor.Extents.Size() > 1)
1057     {
1058       const UInt64 numFiles = _descriptor.Extents.Size();
1059       RINOK(openCallback->SetTotal(&numFiles, NULL))
1060     }
1061   }
1062 
1063   UInt64 complexity = 0;
1064 
1065   for (;;)
1066   {
1067     CExtent *e = NULL;
1068     CMyComPtr<IInStream> nextStream;
1069 
1070     if (_isMultiVol)
1071     {
1072       const unsigned extentIndex = _extents.Size();
1073       if (extentIndex >= _descriptor.Extents.Size())
1074         break;
1075       const CExtentInfo &ei = _descriptor.Extents[extentIndex];
1076       e = &_extents.AddNew();
1077       e->StartOffset = 0;
1078       if (ei.NumSectors >= ((UInt64)1 << (62 - 9)) ||
1079           ei.StartSector >= ((UInt64)1 << (62 - 9)))
1080         return S_FALSE;
1081       e->NumBytes = ei.NumSectors << 9;
1082       e->IsZero = ei.IsType_ZERO();
1083       if (extentIndex != 0)
1084         e->StartOffset = _extents[extentIndex - 1].GetEndOffset();
1085       if (e->GetEndOffset() < e->StartOffset)
1086         return S_FALSE;
1087 
1088       e->VirtSize = e->NumBytes;
1089       if (e->IsZero)
1090       {
1091         e->IsOK = true;
1092         continue;
1093       }
1094 
1095       e->IsFlat = ei.IsType_Flat();
1096       e->FlatOffset = ei.StartSector << 9;
1097 
1098       UString u;
1099       CDescriptor::GetUnicodeName(ei.FileName, u);
1100       if (u.IsEmpty())
1101       {
1102         _missingVol = true;
1103         continue;
1104       }
1105 
1106       HRESULT result = volumeCallback->GetStream(u, &nextStream);
1107 
1108       if (result != S_OK && result != S_FALSE)
1109         return result;
1110 
1111       if (!nextStream || result != S_OK)
1112       {
1113         if (_missingVolName.IsEmpty())
1114           _missingVolName = u;
1115         _missingVol = true;
1116         continue;
1117       }
1118 
1119       if (e->IsFlat)
1120       {
1121         e->IsOK = true;
1122         e->Stream = nextStream;
1123         e->PhySize = e->NumBytes;
1124         continue;
1125       }
1126 
1127       stream = nextStream;
1128 
1129       headerSize = kSectoreSize;
1130       RINOK(ReadStream(stream, buf, &headerSize))
1131 
1132       if (headerSize != kSectoreSize)
1133         continue;
1134       if (memcmp(buf, k_Signature, sizeof(k_Signature)) != 0)
1135         continue;
1136     }
1137     else
1138     {
1139       if (headerSize != kSectoreSize)
1140         return S_FALSE;
1141       e = &_extents.AddNew();
1142       e->StartOffset = 0;
1143     }
1144 
1145     HRESULT res = S_FALSE;
1146     if (e->h.Parse(buf))
1147       res = e->Open3(stream, openCallback, _isMultiVol ? _descriptor.Extents.Size() : 1, _extents.Size() - 1, complexity);
1148 
1149     if (!_isMultiVol)
1150     {
1151       _isArc = e->IsArc;
1152       _phySize = e->PhySize;
1153       _unsupported = e->Unsupported;
1154     }
1155 
1156     if (e->Unsupported)
1157       _unsupportedSome = true;
1158     if (e->HeadersError)
1159       _headerError = true;
1160 
1161     if (res != S_OK)
1162     {
1163       if (res != S_FALSE)
1164         return res;
1165       if (!_isMultiVol)
1166         return res;
1167       continue;
1168     }
1169 
1170     e->Stream = stream;
1171     e->IsOK = true;
1172 
1173     if (!_isMultiVol)
1174     {
1175       e->NumBytes = e->VirtSize;
1176       break;
1177     }
1178 
1179     if (e->NumBytes != e->VirtSize)
1180       _headerError = true;
1181   }
1182 
1183   if (!_extents.IsEmpty())
1184     _size = _extents.Back().GetEndOffset();
1185 
1186   _needDeflate = false;
1187   _clusterBitsMax = 0;
1188 
1189   // unsigned numOKs = 0;
1190   unsigned numUnsupported = 0;
1191 
1192   FOR_VECTOR (i, _extents)
1193   {
1194     const CExtent &e = _extents[i];
1195     if (e.Unsupported)
1196       numUnsupported++;
1197     if (!e.IsOK)
1198       continue;
1199     // numOKs++;
1200     if (e.IsVmdk())
1201     {
1202       if (e.NeedDeflate)
1203         _needDeflate = true;
1204       if (_clusterBitsMax < e.ClusterBits)
1205         _clusterBitsMax = e.ClusterBits;
1206     }
1207   }
1208 
1209   if (numUnsupported != 0 && numUnsupported == _extents.Size())
1210     _unsupported = true;
1211 
1212   return S_OK;
1213 }
1214 
1215 
1216 HRESULT CExtent::Open3(IInStream *stream, IArchiveOpenCallback *openCallback,
1217     unsigned numVols, unsigned volIndex, UInt64 &complexity)
1218 {
1219   if (h.descriptorSize != 0)
1220   {
1221     if (h.descriptorOffset == 0 ||
1222         h.descriptorSize > (1 << 10))
1223       return S_FALSE;
1224     DescriptorBuf.Alloc((size_t)h.descriptorSize << 9);
1225     RINOK(ReadForHeader(stream, h.descriptorOffset, DescriptorBuf, (size_t)h.descriptorSize))
1226     if (h.descriptorOffset == 1 && h.Is_Marker() && Get64(DescriptorBuf) == 0)
1227     {
1228       // We check data as end marker.
1229       // and if probably it's footer's copy of header, we don't want to open it.
1230       return S_FALSE;
1231     }
1232 
1233     DescriptorOK = Descriptor.Parse(DescriptorBuf, DescriptorBuf.Size());
1234     if (!DescriptorOK)
1235       HeadersError = true;
1236     if (Descriptor.IsThere_Parent())
1237       Unsupported = true;
1238   }
1239 
1240   if (h.gdOffset == (UInt64)(Int64)-1)
1241   {
1242     // Grain Dir is at end of file
1243     UInt64 endPos;
1244     RINOK(InStream_GetSize_SeekToEnd(stream, endPos))
1245     if ((endPos & 511) != 0)
1246       return S_FALSE;
1247 
1248     const size_t kEndSize = 512 * 3;
1249     Byte buf2[kEndSize];
1250     if (endPos < kEndSize)
1251       return S_FALSE;
1252     RINOK(InStream_SeekSet(stream, endPos - kEndSize))
1253     RINOK(ReadStream_FALSE(stream, buf2, kEndSize))
1254 
1255     CHeader h2;
1256     if (!h2.Parse(buf2 + 512))
1257       return S_FALSE;
1258     if (!h.IsSameImageFor(h2))
1259       return S_FALSE;
1260 
1261     h = h2;
1262 
1263     CMarker m;
1264     m.Parse(buf2);
1265     if (m.NumSectors != 1 || m.SpecSize != 0 || m.Type != k_Marker_FOOTER)
1266       return S_FALSE;
1267     m.Parse(buf2 + 512 * 2);
1268     if (m.NumSectors != 0 || m.SpecSize != 0 || m.Type != k_Marker_END_OF_STREAM)
1269       return S_FALSE;
1270     PhySize = endPos;
1271   }
1272 
1273   const int grainSize_Log = GetLog(h.grainSize);
1274   if (grainSize_Log < 3 || grainSize_Log > 30 - 9) // grain size must be >= 4 KB
1275     return S_FALSE;
1276   if (h.capacity >= ((UInt64)1 << (63 - 9)))
1277     return S_FALSE;
1278   if (h.overHead >= ((UInt64)1 << (63 - 9)))
1279     return S_FALSE;
1280 
1281   IsArc = true;
1282   ClusterBits = (9 + (unsigned)grainSize_Log);
1283   VirtSize = h.capacity << 9;
1284   NeedDeflate = (h.algo >= 1);
1285 
1286   if (h.Is_Compressed() ? (h.algo > 1 || !h.Is_Marker()) : (h.algo != 0))
1287   {
1288     Unsupported = true;
1289     PhySize = 0;
1290     return S_FALSE;
1291   }
1292 
1293   {
1294     const UInt64 overHeadBytes = h.overHead << 9;
1295     if (PhySize < overHeadBytes)
1296       PhySize = overHeadBytes;
1297   }
1298 
1299   ZeroSector = 0;
1300   if (h.Is_ZeroGrain())
1301     ZeroSector = 1;
1302 
1303   const UInt64 numSectorsPerGde = (UInt64)1 << ((unsigned)grainSize_Log + k_NumMidBits);
1304   const UInt64 numGdeEntries = (h.capacity + numSectorsPerGde - 1) >> ((unsigned)grainSize_Log + k_NumMidBits);
1305   CByteBuffer table;
1306 
1307   if (numGdeEntries != 0)
1308   {
1309     if (h.gdOffset == 0)
1310       return S_FALSE;
1311 
1312     size_t numSectors = (size_t)((numGdeEntries + ((1 << (9 - 2)) - 1)) >> (9 - 2));
1313     size_t t1SizeBytes = numSectors << 9;
1314     if ((t1SizeBytes >> 2) < numGdeEntries)
1315       return S_FALSE;
1316     table.Alloc(t1SizeBytes);
1317 
1318     if (h.Is_Marker())
1319     {
1320       Byte buf2[1 << 9];
1321       if (ReadForHeader(stream, h.gdOffset - 1, buf2, 1) != S_OK)
1322         return S_FALSE;
1323       {
1324         CMarker m;
1325         m.Parse(buf2);
1326         if (m.Type != k_Marker_GRAIN_DIR
1327             || m.NumSectors != numSectors
1328             || m.SpecSize != 0)
1329           return S_FALSE;
1330       }
1331     }
1332 
1333     RINOK(ReadForHeader(stream, h.gdOffset, table, numSectors))
1334   }
1335 
1336   const size_t clusterSize = (size_t)1 << ClusterBits;
1337 
1338   const UInt64 complexityStart = complexity;
1339 
1340   if (openCallback)
1341   {
1342     complexity += (UInt64)numGdeEntries << (k_NumMidBits + 2);
1343     {
1344       const UInt64 numVols2 = numVols;
1345       RINOK(openCallback->SetTotal((numVols == 1) ? NULL : &numVols2, &complexity))
1346     }
1347     if (numVols != 1)
1348     {
1349       const UInt64 volIndex2 = volIndex;
1350       RINOK(openCallback->SetCompleted(numVols == 1 ? NULL : &volIndex2, &complexityStart))
1351     }
1352   }
1353 
1354   UInt64 lastSector = 0;
1355   UInt64 lastVirtCluster = 0;
1356   size_t numProcessed_Prev = 0;
1357 
1358   for (size_t i = 0; i < numGdeEntries; i++)
1359   {
1360     const size_t k_NumSectors = (size_t)1 << (k_NumMidBits - 9 + 2);
1361     const size_t k_NumMidItems = (size_t)1 << k_NumMidBits;
1362 
1363     CByteBuffer &buf = Tables.AddNew();
1364 
1365     {
1366       const UInt32 v = Get32((const Byte *)table + (size_t)i * 4);
1367       if (v == 0 || v == ZeroSector)
1368         continue;
1369       if (openCallback && (i - numProcessed_Prev) >= 1024)
1370       {
1371         const UInt64 comp = complexityStart + ((UInt64)i << (k_NumMidBits + 2));
1372         const UInt64 volIndex2 = volIndex;
1373         RINOK(openCallback->SetCompleted(numVols == 1 ? NULL : &volIndex2, &comp))
1374         numProcessed_Prev = i;
1375       }
1376 
1377       if (h.Is_Marker())
1378       {
1379         Byte buf2[1 << 9];
1380         if (ReadForHeader(stream, v - 1, buf2, 1) != S_OK)
1381           return S_FALSE;
1382         {
1383           CMarker m;
1384           m.Parse(buf2);
1385           if (m.Type != k_Marker_GRAIN_TABLE
1386             || m.NumSectors != k_NumSectors
1387             || m.SpecSize != 0)
1388             return S_FALSE;
1389         }
1390       }
1391 
1392       buf.Alloc(k_NumMidItems * 4);
1393       RINOK(ReadForHeader(stream, v, buf, k_NumSectors))
1394     }
1395 
1396     for (size_t k = 0; k < k_NumMidItems; k++)
1397     {
1398       const UInt32 v = Get32((const Byte *)buf + (size_t)k * 4);
1399       if (v == 0 || v == ZeroSector)
1400         continue;
1401       if (v < h.overHead)
1402         return S_FALSE;
1403       if (lastSector < v)
1404       {
1405         lastSector = v;
1406         if (NeedDeflate)
1407           lastVirtCluster = ((UInt64)i << k_NumMidBits) + k;
1408       }
1409     }
1410   }
1411 
1412   if (!NeedDeflate)
1413   {
1414     UInt64 end = ((UInt64)lastSector << 9) + clusterSize;
1415     if (PhySize < end)
1416       PhySize = end;
1417   }
1418   else if (lastSector != 0)
1419   {
1420     Byte buf[1 << 9];
1421     if (ReadForHeader(stream, lastSector, buf, 1) == S_OK)
1422     {
1423       UInt64 lba = Get64(buf);
1424       if (lba == (lastVirtCluster << (ClusterBits - 9)))
1425       {
1426         UInt32 dataSize = Get32(buf + 8);
1427         size_t dataSize2 = (size_t)dataSize + 12;
1428         dataSize2 = (dataSize2 + 511) & ~(size_t)511;
1429         UInt64 end = ((UInt64)lastSector << 9) + dataSize2;
1430         if (PhySize < end)
1431           PhySize = end;
1432       }
1433     }
1434   }
1435 
1436   return S_OK;
1437 }
1438 
1439 
1440 Z7_COM7F_IMF(CHandler::Close())
1441 {
1442   _phySize = 0;
1443 
1444   _cacheCluster = (UInt64)(Int64)-1;
1445   _cacheExtent = (unsigned)(int)-1;
1446 
1447   _clusterBitsMax = 0;
1448 
1449   _isArc = false;
1450   _unsupported = false;
1451   _unsupportedSome = false;
1452   _headerError = false;
1453   _missingVol = false;
1454   _isMultiVol = false;
1455   _needDeflate = false;
1456 
1457   _missingVolName.Empty();
1458 
1459   _descriptorBuf.Free();
1460   _descriptor.Clear();
1461 
1462   // CHandlerImg:
1463   Clear_HandlerImg_Vars();
1464   Stream.Release();
1465 
1466   _extents.Clear();
1467   return S_OK;
1468 }
1469 
1470 
1471 Z7_COM7F_IMF(CHandler::GetStream(UInt32 /* index */, ISequentialInStream **stream))
1472 {
1473   COM_TRY_BEGIN
1474   *stream = NULL;
1475 
1476   if (_unsupported)
1477     return S_FALSE;
1478 
1479   ClearStreamVars();
1480   // _stream_UsePackSize = true;
1481 
1482   if (_needDeflate)
1483   {
1484     if (!_bufInStream)
1485     {
1486       _bufInStreamSpec = new CBufInStream;
1487       _bufInStream = _bufInStreamSpec;
1488     }
1489 
1490     if (!_bufOutStream)
1491     {
1492       _bufOutStreamSpec = new CBufPtrSeqOutStream();
1493       _bufOutStream = _bufOutStreamSpec;
1494     }
1495 
1496     if (!_zlibDecoder)
1497     {
1498       _zlibDecoderSpec = new NCompress::NZlib::CDecoder;
1499       _zlibDecoder = _zlibDecoderSpec;
1500     }
1501 
1502     const size_t clusterSize = (size_t)1 << _clusterBitsMax;
1503     _cache.AllocAtLeast(clusterSize);
1504     _cacheCompressed.AllocAtLeast(clusterSize * 2);
1505   }
1506 
1507   FOR_VECTOR (i, _extents)
1508   {
1509     RINOK(_extents[i].InitAndSeek())
1510   }
1511 
1512   CMyComPtr<ISequentialInStream> streamTemp = this;
1513   InitAndSeekMain();
1514   *stream = streamTemp.Detach();
1515   return S_OK;
1516   COM_TRY_END
1517 }
1518 
1519 
1520 REGISTER_ARC_I(
1521   "VMDK", "vmdk", NULL, 0xC8,
1522   k_Signature,
1523   0,
1524   0,
1525   NULL)
1526 
1527 }}
1528