xref: /aosp_15_r20/external/lzma/CPP/7zip/Archive/VhdxHandler.cpp (revision f6dc9357d832569d4d1f5d24eacdb3935a1ae8e6)
1 // VhdxHandler.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/StringToInt.h"
12 #include "../../Common/MyBuffer.h"
13 
14 #include "../../Windows/PropVariant.h"
15 
16 #include "../Common/RegisterArc.h"
17 #include "../Common/StreamUtils.h"
18 
19 #include "HandlerCont.h"
20 
21 #define Get16(p) GetUi16(p)
22 #define Get32(p) GetUi32(p)
23 #define Get64(p) GetUi64(p)
24 
25 #define G32(_offs_, dest) dest = Get32(p + (_offs_))
26 #define G64(_offs_, dest) dest = Get64(p + (_offs_))
27 
28 using namespace NWindows;
29 
30 
31 EXTERN_C_BEGIN
32 
33 // CRC-32C (Castagnoli) : reversed for poly 0x1EDC6F41
34 #define k_Crc32c_Poly 0x82f63b78
35 
36 MY_ALIGN(64)
37 static UInt32 g_Crc32c_Table[256];
38 
Crc32c_GenerateTable()39 static void Z7_FASTCALL Crc32c_GenerateTable()
40 {
41   UInt32 i;
42   for (i = 0; i < 256; i++)
43   {
44     UInt32 r = i;
45     unsigned j;
46     for (j = 0; j < 8; j++)
47       r = (r >> 1) ^ (k_Crc32c_Poly & ((UInt32)0 - (r & 1)));
48     g_Crc32c_Table[i] = r;
49   }
50 }
51 
52 
53 #define CRC32C_INIT_VAL 0xFFFFFFFF
54 
55 #define CRC_UPDATE_BYTE_2(crc, b) (table[((crc) ^ (b)) & 0xFF] ^ ((crc) >> 8))
56 
57 // UInt32 Z7_FASTCALL CrcUpdateT1(UInt32 v, const void *data, size_t size, const UInt32 *table);
CrcUpdateT1_vhdx(UInt32 v,const void * data,size_t size,const UInt32 * table)58 static UInt32 Z7_FASTCALL CrcUpdateT1_vhdx(UInt32 v, const void *data, size_t size, const UInt32 *table)
59 {
60   const Byte *p = (const Byte *)data;
61   const Byte *pEnd = p + size;
62   for (; p != pEnd; p++)
63     v = CRC_UPDATE_BYTE_2(v, *p);
64   return v;
65 }
66 
Crc32c_Calc(const void * data,size_t size)67 static UInt32 Z7_FASTCALL Crc32c_Calc(const void *data, size_t size)
68 {
69   return CrcUpdateT1_vhdx(CRC32C_INIT_VAL, data, size, g_Crc32c_Table) ^ CRC32C_INIT_VAL;
70 }
71 
72 EXTERN_C_END
73 
74 
75 namespace NArchive {
76 namespace NVhdx {
77 
C_CRC32c_TableInitNArchive::NVhdx::C_CRC32c_TableInit78 static struct C_CRC32c_TableInit { C_CRC32c_TableInit() { Crc32c_GenerateTable(); } } g_CRC32c_TableInit;
79 
80 static const unsigned kSignatureSize = 8;
81 static const Byte kSignature[kSignatureSize] =
82   { 'v', 'h', 'd', 'x', 'f', 'i', 'l', 'e' };
83 
84 static const unsigned kBitmapSize_Log = 20;
85 static const size_t kBitmapSize = (size_t)1 << kBitmapSize_Log;
86 
87 
IsZeroArr(const Byte * p,size_t size)88 static bool IsZeroArr(const Byte *p, size_t size)
89 {
90   for (size_t i = 0; i < size; i++)
91     if (p[i] != 0)
92       return false;
93   return true;
94 }
95 
96 
97 
98 Z7_FORCE_INLINE
DecodeFrom2HexChars(const wchar_t * s)99 static int DecodeFrom2HexChars(const wchar_t *s)
100 {
101   unsigned v0 = (unsigned)s[0];  Z7_PARSE_HEX_DIGIT(v0, return -1;)
102   unsigned v1 = (unsigned)s[1];  Z7_PARSE_HEX_DIGIT(v1, return -1;)
103   return (int)((v0 << 4) | v1);
104 }
105 
106 
107 struct CGuid
108 {
109   Byte Data[16];
110 
IsZeroNArchive::NVhdx::CGuid111   bool IsZero() const { return IsZeroArr(Data, 16); }
IsEqualToNArchive::NVhdx::CGuid112   bool IsEqualTo(const Byte *a) const { return memcmp(Data, a, 16) == 0; }
IsEqualToNArchive::NVhdx::CGuid113   bool IsEqualTo(const CGuid &g) const { return IsEqualTo(g.Data); }
114   void AddHexToString(UString &s) const;
115 
SetFromNArchive::NVhdx::CGuid116   void SetFrom(const Byte *p) { memcpy(Data, p, 16); }
117 
ParseFromFormatedHexStringNArchive::NVhdx::CGuid118   bool ParseFromFormatedHexString(const UString &s)
119   {
120     const unsigned kLen = 16 * 2 + 4 + 2;
121     if (s.Len() != kLen || s[0] != '{' || s[kLen - 1] != '}')
122       return false;
123     unsigned pos = 0;
124     for (unsigned i = 1; i < kLen - 1;)
125     {
126       if (i == 9 || i == 14 || i == 19 || i == 24)
127       {
128         if (s[i] != '-')
129           return false;
130         i++;
131         continue;
132       }
133       const int v = DecodeFrom2HexChars(s.Ptr(i));
134       if (v < 0)
135         return false;
136       unsigned pos2 = pos;
137       if (pos < 8)
138         pos2 ^= (pos < 4 ? 3 : 1);
139       Data[pos2] = (Byte)v;
140       pos++;
141       i += 2;
142     }
143     return true; // pos == 16;
144   }
145 };
146 
AddHexToString(UString & s) const147 void CGuid::AddHexToString(UString &s) const
148 {
149   char temp[sizeof(Data) * 2 + 2];
150   ConvertDataToHex_Lower(temp, Data, sizeof(Data));
151   s += temp;
152 }
153 
154 
155 #define IS_NON_ALIGNED(v) (((v) & 0xFFFFF) != 0)
156 
157 static const unsigned kHeader_GUID_Index_FileWriteGuid = 0;
158 static const unsigned kHeader_GUID_Index_DataWriteGuid = 1;
159 static const unsigned kHeader_GUID_Index_LogGuid = 2;
160 
161 struct CHeader
162 {
163   UInt64 SequenceNumber;
164   // UInt16 LogVersion;
165   // UInt16 Version;
166   UInt32 LogLength;
167   UInt64 LogOffset;
168   CGuid Guids[3];
169 
IsEqualToNArchive::NVhdx::CHeader170   bool IsEqualTo(const CHeader &h) const
171   {
172     if (SequenceNumber != h.SequenceNumber)
173       return false;
174     if (LogLength != h.LogLength)
175       return false;
176     if (LogOffset != h.LogOffset)
177       return false;
178     for (unsigned i = 0; i < 3; i++)
179       if (!Guids[i].IsEqualTo(h.Guids[i]))
180         return false;
181     return true;
182   }
183 
184   bool Parse(Byte *p);
185 };
186 
187 static const unsigned kHeader2Size = 1 << 12;
188 
Parse(Byte * p)189 bool CHeader::Parse(Byte *p)
190 {
191   if (Get32(p) != 0x64616568) // "head"
192     return false;
193   const UInt32 crc = Get32(p + 4);
194   SetUi32(p + 4, 0)
195   if (Crc32c_Calc(p, kHeader2Size) != crc)
196     return false;
197   G64(8, SequenceNumber);
198   for (unsigned i = 0; i < 3; i++)
199     Guids[i].SetFrom(p + 0x10 + 0x10 * i);
200   // LogVersion = Get16(p + 0x40);
201   /* LogVersion MUST be set to zero, for known log format
202      but we don't parse log so we ignore it */
203   G32(0x44, LogLength);
204   G64(0x48, LogOffset);
205   if (Get16(p + 0x42) != 1) // Header format Version
206     return false;
207   if (IS_NON_ALIGNED(LogLength))
208     return false;
209   if (IS_NON_ALIGNED(LogOffset))
210     return false;
211   return true;
212   // return IsZeroArr(p + 0x50, kHeader2Size - 0x50);
213 }
214 
215 
216 
217 static const Byte kBat[16] =
218   { 0x66,0x77,0xC2,0x2D,0x23,0xF6,0x00,0x42,0x9D,0x64,0x11,0x5E,0x9B,0xFD,0x4A,0x08 };
219 static const Byte kMetadataRegion[16] =
220   { 0x06,0xA2,0x7C,0x8B,0x90,0x47,0x9A,0x4B,0xB8,0xFE,0x57,0x5F,0x05,0x0F,0x88,0x6E };
221 
222 struct CRegionEntry
223 {
224   // CGuid Guid;
225   UInt64 Offset;
226   UInt32 Len;
227   UInt32 Required;
228 
GetEndPosNArchive::NVhdx::CRegionEntry229   UInt64 GetEndPos() const { return Offset + Len; }
230   bool Parse(const Byte *p);
231 };
232 
Parse(const Byte * p)233 bool CRegionEntry::Parse(const Byte *p)
234 {
235   // Guid.SetFrom(p);
236   G64(0x10, Offset);
237   G32(0x18, Len);
238   G32(0x1c, Required);
239   if (IS_NON_ALIGNED(Offset))
240     return false;
241   if (IS_NON_ALIGNED(Len))
242     return false;
243   if (Offset + Len < Offset)
244     return false;
245   return true;
246 }
247 
248 
249 struct CRegion
250 {
251   bool Bat_Defined;
252   bool Meta_Defined;
253   UInt64 EndPos;
254   UInt64 DataSize;
255 
256   CRegionEntry BatEntry;
257   CRegionEntry MetaEntry;
258 
259   bool Parse(Byte *p);
260 };
261 
262 
263 static const size_t kRegionSize = 1 << 16;
264 static const unsigned kNumRegionEntriesMax = (1 << 11) - 1;
265 
Parse(Byte * p)266 bool CRegion::Parse(Byte *p)
267 {
268   Bat_Defined = false;
269   Meta_Defined = false;
270   EndPos = 0;
271   DataSize = 0;
272 
273   if (Get32(p) != 0x69676572) // "regi"
274     return false;
275   const UInt32 crc = Get32(p + 4);
276   SetUi32(p + 4, 0)
277   const UInt32 crc_calced = Crc32c_Calc(p, kRegionSize);
278   if (crc_calced != crc)
279     return false;
280 
281   const UInt32 EntryCount = Get32(p + 8);
282   if (Get32(p + 12) != 0) // reserved field must be set to 0.
283     return false;
284   if (EntryCount > kNumRegionEntriesMax)
285     return false;
286   for (UInt32 i = 0; i < EntryCount; i++)
287   {
288     CRegionEntry e;
289     const Byte *p2 = p + 0x10 + 0x20 * (size_t)i;
290     if (!e.Parse(p2))
291       return false;
292     DataSize += e.Len;
293     const UInt64 endPos = e.GetEndPos();
294     if (EndPos < endPos)
295       EndPos = endPos;
296     CGuid Guid;
297     Guid.SetFrom(p2);
298     if (Guid.IsEqualTo(kBat))
299     {
300       if (Bat_Defined)
301         return false;
302       BatEntry = e;
303       Bat_Defined = true;
304     }
305     else if (Guid.IsEqualTo(kMetadataRegion))
306     {
307       if (Meta_Defined)
308         return false;
309       MetaEntry = e;
310       Meta_Defined = true;
311     }
312     else
313     {
314       if (e.Required != 0)
315         return false;
316       // it's allowed to ignore unknown non-required region entries
317     }
318   }
319   /*
320   const size_t k = 0x10 + 0x20 * EntryCount;
321   return IsZeroArr(p + k, kRegionSize - k);
322   */
323   return true;
324 }
325 
326 
327 
328 
329 struct CMetaEntry
330 {
331   CGuid Guid;
332   UInt32 Offset;
333   UInt32 Len;
334   UInt32 Flags0;
335   // UInt32 Flags1;
336 
IsUserNArchive::NVhdx::CMetaEntry337   bool IsUser()        const { return (Flags0 & 1) != 0; }
IsVirtualDiskNArchive::NVhdx::CMetaEntry338   bool IsVirtualDisk() const { return (Flags0 & 2) != 0; }
IsRequiredNArchive::NVhdx::CMetaEntry339   bool IsRequired()    const { return (Flags0 & 4) != 0; }
340 
CheckLimitNArchive::NVhdx::CMetaEntry341   bool CheckLimit(size_t regionSize) const
342   {
343     return Offset <= regionSize && Len <= regionSize - Offset;
344   }
345 
346   bool Parse(const Byte *p);
347 };
348 
349 
Parse(const Byte * p)350 bool CMetaEntry::Parse(const Byte *p)
351 {
352   Guid.SetFrom(p);
353 
354   G32(0x10, Offset);
355   G32(0x14, Len);
356   G32(0x18, Flags0);
357   UInt32 Flags1;
358   G32(0x1C, Flags1);
359 
360   if (Offset != 0 && Offset < (1 << 16))
361     return false;
362   if (Len > (1 << 20))
363     return false;
364   if (Len == 0 && Offset != 0)
365     return false;
366   if ((Flags0 >> 3) != 0) // Reserved
367     return false;
368   if ((Flags1 & 3) != 0) // Reserved2
369     return false;
370   return true;
371 }
372 
373 
374 struct CParentPair
375 {
376   UString Key;
377   UString Value;
378 };
379 
380 
381 struct CMetaHeader
382 {
383   // UInt16 EntryCount;
384   bool Guid_Defined;
385   bool VirtualDiskSize_Defined;
386   bool Locator_Defined;
387 
388   unsigned BlockSize_Log;
389   unsigned LogicalSectorSize_Log;
390   unsigned PhysicalSectorSize_Log;
391 
392   UInt32 Flags;
393   UInt64 VirtualDiskSize;
394   CGuid Guid;
395   // CGuid LocatorType;
396 
397   CObjectVector<CParentPair> ParentPairs;
398 
FindParentKeyNArchive::NVhdx::CMetaHeader399   int FindParentKey(const char *name) const
400   {
401     FOR_VECTOR (i, ParentPairs)
402     {
403       const CParentPair &pair = ParentPairs[i];
404       if (pair.Key.IsEqualTo(name))
405         return (int)i;
406     }
407     return -1;
408   }
409 
Is_LeaveBlockAllocatedNArchive::NVhdx::CMetaHeader410   bool Is_LeaveBlockAllocated() const { return (Flags & 1) != 0; }
Is_HasParentNArchive::NVhdx::CMetaHeader411   bool Is_HasParent() const { return (Flags & 2) != 0; }
412 
ClearNArchive::NVhdx::CMetaHeader413   void Clear()
414   {
415     Guid_Defined = false;
416     VirtualDiskSize_Defined = false;
417     Locator_Defined = false;
418     BlockSize_Log = 0;
419     LogicalSectorSize_Log = 0;
420     PhysicalSectorSize_Log = 0;
421     Flags = 0;
422     VirtualDiskSize = 0;
423     ParentPairs.Clear();
424   }
425 
426   bool Parse(const Byte *p, size_t size);
427 };
428 
429 
GetLogSize(UInt32 size)430 static unsigned GetLogSize(UInt32 size)
431 {
432   unsigned k;
433   for (k = 0; k < 32; k++)
434     if (((UInt32)1 << k) == size)
435       return k;
436   return k;
437 }
438 
439 
440 static const unsigned kMetadataSize = 8;
441 static const Byte kMetadata[kMetadataSize] =
442   { 'm','e','t','a','d','a','t','a' };
443 
444 static const unsigned k_Num_MetaEntries_Max = (1 << 11) - 1;
445 
446 static const Byte kFileParameters[16] =
447   { 0x37,0x67,0xa1,0xca,0x36,0xfa,0x43,0x4d,0xb3,0xb6,0x33,0xf0,0xaa,0x44,0xe7,0x6b };
448 static const Byte kVirtualDiskSize[16] =
449   { 0x24,0x42,0xa5,0x2f,0x1b,0xcd,0x76,0x48,0xb2,0x11,0x5d,0xbe,0xd8,0x3b,0xf4,0xb8 };
450 static const Byte kVirtualDiskID[16] =
451   { 0xab,0x12,0xca,0xbe,0xe6,0xb2,0x23,0x45,0x93,0xef,0xc3,0x09,0xe0,0x00,0xc7,0x46 };
452 static const Byte kLogicalSectorSize[16] =
453   { 0x1d,0xbf,0x41,0x81,0x6f,0xa9,0x09,0x47,0xba,0x47,0xf2,0x33,0xa8,0xfa,0xab,0x5f };
454 static const Byte kPhysicalSectorSize[16] =
455   { 0xc7,0x48,0xa3,0xcd,0x5d,0x44,0x71,0x44,0x9c,0xc9,0xe9,0x88,0x52,0x51,0xc5,0x56 };
456 static const Byte kParentLocator[16] =
457   { 0x2d,0x5f,0xd3,0xa8,0x0b,0xb3,0x4d,0x45,0xab,0xf7,0xd3,0xd8,0x48,0x34,0xab,0x0c };
458 
GetString16(UString & s,const Byte * p,size_t size)459 static bool GetString16(UString &s, const Byte *p, size_t size)
460 {
461   s.Empty();
462   if (size & 1)
463     return false;
464   for (size_t i = 0; i < size; i += 2)
465   {
466     const wchar_t c = Get16(p + i);
467     if (c == 0)
468       return false;
469     s += c;
470   }
471   return true;
472 }
473 
474 
Parse(const Byte * p,size_t size)475 bool CMetaHeader::Parse(const Byte *p, size_t size)
476 {
477   if (memcmp(p, kMetadata, kMetadataSize) != 0)
478     return false;
479   if (Get16(p + 8) != 0) // Reserved
480     return false;
481   const UInt32 EntryCount = Get16(p + 10);
482   if (EntryCount > k_Num_MetaEntries_Max)
483     return false;
484   if (!IsZeroArr(p + 12, 20)) // Reserved
485     return false;
486 
487   for (unsigned i = 0; i < EntryCount; i++)
488   {
489     CMetaEntry e;
490     if (!e.Parse(p + 32 + 32 * (size_t)i))
491       return false;
492     if (!e.CheckLimit(size))
493       return false;
494     const Byte *p2 = p + e.Offset;
495 
496     if (e.Guid.IsEqualTo(kFileParameters))
497     {
498       if (BlockSize_Log != 0)
499         return false;
500       if (e.Len != 8)
501         return false;
502       const UInt32 v = Get32(p2);
503       Flags = Get32(p2 + 4);
504       BlockSize_Log = GetLogSize(v);
505       if (BlockSize_Log < 20 || BlockSize_Log > 28) // specification from 1 MB to 256 MB
506         return false;
507       if ((Flags >> 2) != 0) // reserved
508         return false;
509     }
510     else if (e.Guid.IsEqualTo(kVirtualDiskSize))
511     {
512       if (VirtualDiskSize_Defined)
513         return false;
514       if (e.Len != 8)
515         return false;
516       VirtualDiskSize = Get64(p2);
517       VirtualDiskSize_Defined = true;
518     }
519     else if (e.Guid.IsEqualTo(kVirtualDiskID))
520     {
521       if (e.Len != 16)
522         return false;
523       Guid.SetFrom(p2);
524       Guid_Defined = true;
525     }
526     else if (e.Guid.IsEqualTo(kLogicalSectorSize))
527     {
528       if (LogicalSectorSize_Log != 0)
529         return false;
530       if (e.Len != 4)
531         return false;
532       const UInt32 v = Get32(p2);
533       LogicalSectorSize_Log = GetLogSize(v);
534       if (LogicalSectorSize_Log != 9 && LogicalSectorSize_Log != 12)
535         return false;
536     }
537     else if (e.Guid.IsEqualTo(kPhysicalSectorSize))
538     {
539       if (PhysicalSectorSize_Log != 0)
540         return false;
541       if (e.Len != 4)
542         return false;
543       const UInt32 v = Get32(p2);
544       PhysicalSectorSize_Log = GetLogSize(v);
545       if (PhysicalSectorSize_Log != 9 && PhysicalSectorSize_Log != 12)
546         return false;
547     }
548     else if (e.Guid.IsEqualTo(kParentLocator))
549     {
550       if (Locator_Defined)
551         return false;
552       if (e.Len < 20)
553         return false;
554       // LocatorType.SetFrom(p2);
555       /* Specifies the type of the parent virtual disk.
556          is different for each type: VHDX, VHD or iSCSI.
557          only "B04AEFB7-D19E-4A81-B789-25B8E9445913" (for VHDX) is supported now
558       */
559       Locator_Defined = true;
560       if (Get16(p2 + 16) != 0) // reserved
561         return false;
562       const UInt32 KeyValueCount = Get16(p2 + 18);
563       if (20 + (UInt32)KeyValueCount * 12 > e.Len)
564         return false;
565       for (unsigned k = 0; k < KeyValueCount; k++)
566       {
567         const Byte *p3 = p2 + 20 + (size_t)k * 12;
568         const UInt32 KeyOffset   = Get32(p3);
569         const UInt32 ValueOffset = Get32(p3 + 4);
570         const UInt32 KeyLength   = Get16(p3 + 8);
571         const UInt32 ValueLength = Get16(p3 + 10);
572         if (KeyOffset > e.Len || KeyLength > e.Len - KeyOffset)
573           return false;
574         if (ValueOffset > e.Len || ValueLength > e.Len - ValueOffset)
575           return false;
576         CParentPair pair;
577         if (!GetString16(pair.Key, p2 + KeyOffset, KeyLength))
578           return false;
579         if (!GetString16(pair.Value, p2 + ValueOffset, ValueLength))
580           return false;
581         ParentPairs.Add(pair);
582       }
583     }
584     else
585     {
586       if (e.IsRequired())
587         return false;
588       // return false; // unknown metadata;
589     }
590   }
591 
592   // some properties are required for correct processing
593 
594   if (BlockSize_Log == 0)
595     return false;
596   if (LogicalSectorSize_Log == 0)
597     return false;
598   if (!VirtualDiskSize_Defined)
599     return false;
600   if (((UInt32)VirtualDiskSize & ((UInt32)1 << LogicalSectorSize_Log)) != 0)
601     return false;
602 
603   // vhdx specification sets limit for 64 TB.
604   // do we need to check over same limit ?
605   const UInt64 kVirtualDiskSize_Max = (UInt64)1 << 46;
606   if (VirtualDiskSize > kVirtualDiskSize_Max)
607     return false;
608 
609   return true;
610 }
611 
612 
613 
614 struct CBat
615 {
616   CByteBuffer Data;
617 
ClearNArchive::NVhdx::CBat618   void Clear() { Data.Free(); }
GetItemNArchive::NVhdx::CBat619   UInt64 GetItem(size_t n) const
620   {
621     return Get64(Data + n * 8);
622   }
623 };
624 
625 
626 
627 Z7_class_CHandler_final: public CHandlerImg
628 {
629   UInt64 _phySize;
630 
631   CBat Bat;
632   CObjectVector<CByteBuffer> BitMaps;
633 
634   unsigned ChunkRatio_Log;
635   size_t ChunkRatio;
636   size_t TotalBatEntries;
637 
638   CMetaHeader Meta;
639   CHeader Header;
640 
641   UInt32 NumUsedBlocks;
642   UInt32 NumUsedBitMaps;
643   UInt64 HeadersSize;
644 
645   UInt32 NumLevels;
646   UInt64 PackSize_Total;
647 
648   /*
649   UInt64 NumUsed_1MB_Blocks; // data and bitmaps
650   bool NumUsed_1MB_Blocks_Defined;
651   */
652 
653   CMyComPtr<IInStream> ParentStream;
654   CHandler *Parent;
655   UString _errorMessage;
656   UString _creator;
657 
658   bool _nonEmptyLog;
659   bool _isDataContiguous;
660   // bool _batOverlap;
661 
662   CGuid _parentGuid;
663   bool _parentGuid_IsDefined;
664   UStringVector ParentNames;
665   UString ParentName_Used;
666 
667   const CHandler *_child;
668   unsigned _level;
669   bool _isCyclic;
670   bool _isCyclic_or_CyclicParent;
671 
672   void AddErrorMessage(const char *message);
673   void AddErrorMessage(const char *message, const wchar_t *name);
674 
675   void UpdatePhySize(UInt64 value)
676   {
677     if (_phySize < value)
678       _phySize = value;
679   }
680 
681   HRESULT Seek2(UInt64 offset);
682   HRESULT Read_FALSE(Byte *data, size_t size)
683   {
684     return ReadStream_FALSE(Stream, data, size);
685   }
686   HRESULT ReadToBuf_FALSE(CByteBuffer &buf, size_t size)
687   {
688     buf.Alloc(size);
689     return ReadStream_FALSE(Stream, buf, size);
690   }
691 
692   void InitSeekPositions();
693   HRESULT ReadPhy(UInt64 offset, void *data, UInt32 size, UInt32 &processed);
694 
695   bool IsDiff() const
696   {
697     // here we suppose that only HasParent() flag is mandatory for Diff archive type
698     return Meta.Is_HasParent();
699     // return _parentGuid_IsDefined;
700   }
701 
702   void AddTypeString(AString &s) const
703   {
704     if (IsDiff())
705       s += "Differencing";
706     else
707     {
708       if (Meta.Is_LeaveBlockAllocated())
709         s +=  _isDataContiguous ? "fixed" : "fixed-non-cont";
710       else
711         s += "dynamic";
712     }
713   }
714 
715   void AddComment(UString &s) const;
716 
717   UInt64 GetPackSize() const
718   {
719     return (UInt64)NumUsedBlocks << Meta.BlockSize_Log;
720   }
721 
722   UString GetParentSequence() const
723   {
724     const CHandler *p = this;
725     UString res;
726     while (p && p->IsDiff())
727     {
728       if (!res.IsEmpty())
729         res += " -> ";
730       res += ParentName_Used;
731       p = p->Parent;
732     }
733     return res;
734   }
735 
736   bool AreParentsOK() const
737   {
738     if (_isCyclic_or_CyclicParent)
739       return false;
740     const CHandler *p = this;
741     while (p->IsDiff())
742     {
743       p = p->Parent;
744       if (!p)
745         return false;
746     }
747     return true;
748   }
749 
750   // bool ParseLog(CByteBuffer &log);
751   bool ParseBat();
752   bool CheckBat();
753 
754   HRESULT Open3();
755   HRESULT Open2(IInStream *stream, IArchiveOpenCallback *openArchiveCallback) Z7_override;
756   HRESULT OpenParent(IArchiveOpenCallback *openArchiveCallback, bool &_parentFileWasOpen);
757   virtual void CloseAtError() Z7_override;
758 
759 public:
760   Z7_IFACE_COM7_IMP(IInArchive_Img)
761 
762   Z7_IFACE_COM7_IMP(IInArchiveGetStream)
763   Z7_IFACE_COM7_IMP(ISequentialInStream)
764 
765   CHandler():
766     _child(NULL),
767     _level(0),
768     _isCyclic(false),
769     _isCyclic_or_CyclicParent(false)
770     {}
771 };
772 
773 
774 HRESULT CHandler::Seek2(UInt64 offset)
775 {
776   return InStream_SeekSet(Stream, offset);
777 }
778 
779 
780 void CHandler::InitSeekPositions()
781 {
782   /* (_virtPos) and (_posInArc) is used only in Read() (that calls ReadPhy()).
783      So we must reset these variables before first call of Read() */
784   Reset_VirtPos();
785   Reset_PosInArc();
786   if (ParentStream)
787     Parent->InitSeekPositions();
788 }
789 
790 
791 HRESULT CHandler::ReadPhy(UInt64 offset, void *data, UInt32 size, UInt32 &processed)
792 {
793   processed = 0;
794   if (offset > _phySize
795       || offset + size > _phySize)
796   {
797     // we don't expect these cases, if (_phySize) was set correctly.
798     return S_FALSE;
799   }
800   if (offset != _posInArc)
801   {
802     const HRESULT res = Seek2(offset);
803     if (res != S_OK)
804     {
805       Reset_PosInArc(); // we don't trust seek_pos in case of error
806       return res;
807     }
808     _posInArc = offset;
809   }
810   {
811     size_t size2 = size;
812     const HRESULT res = ReadStream(Stream, data, &size2);
813     processed = (UInt32)size2;
814     _posInArc += size2;
815     if (res != S_OK)
816       Reset_PosInArc(); // we don't trust seek_pos in case of reading error
817     return res;
818   }
819 }
820 
821 
822 #define PAYLOAD_BLOCK_NOT_PRESENT   0
823 #define PAYLOAD_BLOCK_UNDEFINED     1
824 #define PAYLOAD_BLOCK_ZERO          2
825 #define PAYLOAD_BLOCK_UNMAPPED      3
826 #define PAYLOAD_BLOCK_FULLY_PRESENT 6
827 #define PAYLOAD_BLOCK_PARTIALLY_PRESENT 7
828 
829 #define SB_BLOCK_NOT_PRESENT 0
830 #define SB_BLOCK_PRESENT     6
831 
832 #define BAT_GET_OFFSET(v) ((v) & ~(UInt64)0xFFFFF)
833 #define BAT_GET_STATE(v)  ((UInt32)(v) & 7)
834 
835 /* The log contains only   updates to metadata, bat and region tables
836    The log doesn't contain updates to start header, and 2 headers (first 192 KB of file).
837    The log is array of 4 KB blocks and each block has 4-byte signature.
838    So it's possible to scan whole log to find the latest entry sequence (and header for replay).
839 */
840 
841 /*
842 struct CLogEntry
843 {
844   UInt32 EntryLength;
845   UInt32 Tail;
846   UInt64 SequenceNumber;
847   CGuid LogGuid;
848   UInt32 DescriptorCount;
849   UInt64 FlushedFileOffset;
850   UInt64 LastFileOffset;
851 
852   bool Parse(const Byte *p);
853 };
854 
855 bool CLogEntry::Parse(const Byte *p)
856 {
857   G32 (8, EntryLength);
858   G32 (12,Tail);
859   G64 (16, SequenceNumber);
860   G32 (24, DescriptorCount); // it's 32-bit, but specification says 64-bit
861   if (Get32(p + 28) != 0) // reserved
862     return false;
863   LogGuid.SetFrom(p + 32);
864   G64 (48, FlushedFileOffset);
865   G64 (56, LastFileOffset);
866 
867   if (SequenceNumber == 0)
868     return false;
869   if ((Tail & 0xfff) != 0)
870     return false;
871   if (IS_NON_ALIGNED(FlushedFileOffset))
872     return false;
873   if (IS_NON_ALIGNED(LastFileOffset))
874     return false;
875   return true;
876 }
877 
878 
879 bool CHandler::ParseLog(CByteBuffer &log)
880 {
881   CLogEntry lastEntry;
882   lastEntry.SequenceNumber = 0;
883   bool lastEntry_found = false;
884   size_t lastEntry_Offset = 0;
885   for (size_t i = 0; i < log.Size(); i += 1 << 12)
886   {
887     Byte *p = (Byte *)(log + i);
888 
889     if (Get32(p) != 0x65676F6C) // "loge"
890       continue;
891     const UInt32 crc = Get32(p + 4);
892 
893     CLogEntry e;
894     if (!e.Parse(p))
895     {
896       return false;
897       continue;
898     }
899     const UInt32 entryLength = Get32(p + 8);
900     if (e.EntryLength > log.Size() || (e.EntryLength & 0xFFF) != 0 || e.EntryLength == 0)
901     {
902       return false;
903       continue;
904     }
905     SetUi32(p + 4, 0);
906     const UInt32 crc_calced = Crc32c_Calc(p, entryLength);
907     SetUi32(p + 4, crc); // we must restore crc if we want same data in log
908     if (crc_calced != crc)
909       continue;
910     if (!lastEntry_found || lastEntry.SequenceNumber < e.SequenceNumber)
911     {
912       lastEntry = e;
913       lastEntry_found = true;
914       lastEntry_Offset = i;
915     }
916   }
917 
918   return true;
919 }
920 */
921 
922 
923 bool CHandler::ParseBat()
924 {
925   ChunkRatio_Log = kBitmapSize_Log + 3 + Meta.LogicalSectorSize_Log - Meta.BlockSize_Log;
926   ChunkRatio = (size_t)1 << (ChunkRatio_Log);
927 
928   UInt64 totalBatEntries64;
929   const bool isDiff = IsDiff();
930   const UInt32 blockSize = (UInt32)1 << Meta.BlockSize_Log;
931   {
932     const UInt64 up = Meta.VirtualDiskSize + blockSize - 1;
933     if (up < Meta.VirtualDiskSize)
934       return false;
935     const UInt64 numDataBlocks = up >> Meta.BlockSize_Log;
936 
937     if (isDiff)
938     {
939       // differencing table must be finished with bitmap entry
940       const UInt64 numBitmaps = (numDataBlocks + ChunkRatio - 1) >> ChunkRatio_Log;
941       totalBatEntries64 = numBitmaps * (ChunkRatio + 1);
942     }
943     else
944     {
945       // we don't need last Bitmap entry
946       totalBatEntries64 = numDataBlocks + ((numDataBlocks - 1) >> ChunkRatio_Log);
947     }
948   }
949 
950   if (totalBatEntries64 > Bat.Data.Size() / 8)
951     return false;
952 
953   const size_t totalBatEntries = (size_t)totalBatEntries64;
954   TotalBatEntries = totalBatEntries;
955 
956   bool isCont = (!isDiff && Meta.Is_LeaveBlockAllocated());
957   UInt64 prevBlockOffset = 0;
958   UInt64 maxBlockOffset = 0;
959 
960   size_t remEntries = ChunkRatio + 1;
961 
962   size_t i;
963   for (i = 0; i < totalBatEntries; i++)
964   {
965     const UInt64 v = Bat.GetItem(i);
966     if ((v & 0xFFFF8) != 0)
967       return false;
968     const UInt64 offset = BAT_GET_OFFSET(v);
969     const unsigned state = BAT_GET_STATE(v);
970 
971     /*
972     UInt64 index64 = v >> 20;
973     printf("\n%7d", i);
974     printf("%10d, ", (unsigned)index64);
975     printf("%4x, ", (unsigned)state);
976     */
977 
978     remEntries--;
979     if (remEntries == 0)
980     {
981       // printf(" ========");
982       // printf("\n");
983       remEntries = ChunkRatio + 1;
984       if (state == SB_BLOCK_PRESENT)
985       {
986         isCont = false;
987         if (!isDiff)
988           return false;
989         if (offset == 0)
990           return false;
991         const UInt64 lim = offset + kBitmapSize;
992         if (lim < offset)
993           return false;
994         if (_phySize < lim)
995           _phySize = lim;
996         NumUsedBitMaps++;
997       }
998       else if (state != SB_BLOCK_NOT_PRESENT)
999         return false;
1000     }
1001     else
1002     {
1003       if (state == PAYLOAD_BLOCK_FULLY_PRESENT
1004           || state == PAYLOAD_BLOCK_PARTIALLY_PRESENT)
1005       {
1006         if (offset == 0)
1007           return false;
1008         if (maxBlockOffset < offset)
1009           maxBlockOffset = offset;
1010 
1011         if (state == PAYLOAD_BLOCK_PARTIALLY_PRESENT)
1012         {
1013           isCont = false;
1014           if (!isDiff)
1015             return false;
1016         }
1017         else if (isCont)
1018         {
1019           if (prevBlockOffset != 0 && prevBlockOffset + blockSize != offset)
1020             isCont = false;
1021           else
1022             prevBlockOffset = offset;
1023         }
1024 
1025         NumUsedBlocks++;
1026       }
1027       else if (state == PAYLOAD_BLOCK_UNMAPPED)
1028       {
1029         isCont = false;
1030         // non-empty (offset) is allowed
1031       }
1032       else if (state == PAYLOAD_BLOCK_NOT_PRESENT
1033           || state == PAYLOAD_BLOCK_UNDEFINED
1034           || state == PAYLOAD_BLOCK_ZERO)
1035       {
1036         isCont = false;
1037         /* (offset) is reserved and (offset == 0) is expected here,
1038            but we ignore (offset) here */
1039         // if (offset != 0) return false;
1040       }
1041       else
1042         return false;
1043     }
1044   }
1045 
1046   _isDataContiguous = isCont;
1047 
1048   if (maxBlockOffset != 0)
1049   {
1050     const UInt64 lim = maxBlockOffset + blockSize;
1051     if (lim < maxBlockOffset)
1052       return false;
1053     if (_phySize < lim)
1054       _phySize = lim;
1055     const UInt64 kPhyLimit = (UInt64)1 << 62;
1056     if (maxBlockOffset >= kPhyLimit)
1057       return false;
1058   }
1059   return true;
1060 }
1061 
1062 
1063 bool CHandler::CheckBat()
1064 {
1065   const UInt64 upSize = _phySize + kBitmapSize * 8 - 1;
1066   if (upSize < _phySize)
1067     return false;
1068   const UInt64 useMapSize64 = upSize >> (kBitmapSize_Log + 3);
1069   const size_t useMapSize = (size_t)useMapSize64;
1070 
1071   const UInt32 blockSizeMB = (UInt32)1 << (Meta.BlockSize_Log - kBitmapSize_Log);
1072 
1073   // we don't check useMap, if it's too big.
1074   if (useMapSize != useMapSize64)
1075     return true;
1076   if (useMapSize == 0 || useMapSize > ((size_t)1 << 28))
1077     return true;
1078 
1079   CByteArr useMap;
1080   useMap.Alloc(useMapSize);
1081   memset(useMap, 0, useMapSize);
1082   // useMap[0] = (Byte)(1 << 0); // first 1 MB is used by headers
1083   // we can also update useMap for log, and region data.
1084 
1085   const size_t totalBatEntries = TotalBatEntries;
1086   size_t remEntries = ChunkRatio + 1;
1087 
1088   size_t i;
1089   for (i = 0; i < totalBatEntries; i++)
1090   {
1091     const UInt64 v = Bat.GetItem(i);
1092     const UInt64 offset = BAT_GET_OFFSET(v);
1093     const unsigned state = BAT_GET_STATE(v);
1094     const UInt64 index = offset >> kBitmapSize_Log;
1095     UInt32 numBlocks = 1;
1096     remEntries--;
1097     if (remEntries == 0)
1098     {
1099       remEntries = ChunkRatio + 1;
1100       if (state != SB_BLOCK_PRESENT)
1101         continue;
1102     }
1103     else
1104     {
1105       if (state != PAYLOAD_BLOCK_FULLY_PRESENT &&
1106           state != PAYLOAD_BLOCK_PARTIALLY_PRESENT)
1107         continue;
1108       numBlocks = blockSizeMB;
1109     }
1110 
1111     for (unsigned k = 0; k < numBlocks; k++)
1112     {
1113       const UInt64 index2 = index + k;
1114       const unsigned flag = (unsigned)1 << ((unsigned)index2 & 7);
1115       const size_t byteIndex = (size_t)(index2 >> 3);
1116       if (byteIndex >= useMapSize)
1117         return false;
1118       const unsigned m = useMap[byteIndex];
1119       if (m & flag)
1120         return false;
1121       useMap[byteIndex] = (Byte)(m | flag);
1122     }
1123   }
1124 
1125   /*
1126   UInt64 num = 0;
1127   for (i = 0; i < useMapSize; i++)
1128   {
1129     Byte b = useMap[i];
1130     unsigned t = 0;
1131     t += (b & 1);  b >>= 1;
1132     t += (b & 1);  b >>= 1;
1133     t += (b & 1);  b >>= 1;
1134     t += (b & 1);  b >>= 1;
1135     t += (b & 1);  b >>= 1;
1136     t += (b & 1);  b >>= 1;
1137     t += (b & 1);  b >>= 1;
1138     t += (b & 1);
1139     num += t;
1140   }
1141   NumUsed_1MB_Blocks = num;
1142   NumUsed_1MB_Blocks_Defined = true;
1143   */
1144 
1145   return true;
1146 }
1147 
1148 
1149 
1150 HRESULT CHandler::Open3()
1151 {
1152   {
1153     const unsigned kHeaderSize = 512; // + 8
1154     Byte header[kHeaderSize];
1155 
1156     RINOK(Read_FALSE(header, kHeaderSize))
1157 
1158     if (memcmp(header, kSignature, kSignatureSize) != 0)
1159       return S_FALSE;
1160 
1161     const Byte *p = &header[0];
1162     for (unsigned i = kSignatureSize; i < kHeaderSize; i += 2)
1163     {
1164       const wchar_t c = Get16(p + i);
1165       if (c < 0x20 || c > 0x7F)
1166         break;
1167       _creator += c;
1168     }
1169   }
1170 
1171   HeadersSize = (UInt32)1 << 20;
1172   CHeader headers[2];
1173   {
1174     Byte header[kHeader2Size];
1175     for (unsigned i = 0; i < 2; i++)
1176     {
1177       RINOK(Seek2((1 << 16) * (1 + i)))
1178       RINOK(Read_FALSE(header, kHeader2Size))
1179       bool headerIsOK = headers[i].Parse(header);
1180       if (!headerIsOK)
1181         return S_FALSE;
1182     }
1183   }
1184   unsigned mainIndex;
1185        if (headers[0].SequenceNumber > headers[1].SequenceNumber) mainIndex = 0;
1186   else if (headers[0].SequenceNumber < headers[1].SequenceNumber) mainIndex = 1;
1187   else
1188   {
1189     /* Disk2vhd v2.02 can create image with 2 full copies of headers.
1190        It's violation of VHDX specification:
1191           "A header is current if it is the only valid header
1192            or if it is valid and its SequenceNumber field is
1193            greater than the other header's SequenceNumber".
1194        but we support such Disk2vhd archives. */
1195     if (!headers[0].IsEqualTo(headers[1]))
1196       return S_FALSE;
1197     mainIndex = 0;
1198   }
1199 
1200   const CHeader &h = headers[mainIndex];
1201   Header = h;
1202   if (h.LogLength != 0)
1203   {
1204     HeadersSize += h.LogLength;
1205     UpdatePhySize(h.LogOffset + h.LogLength);
1206     if (!h.Guids[kHeader_GUID_Index_LogGuid].IsZero())
1207     {
1208       _nonEmptyLog = true;
1209       AddErrorMessage("non-empty LOG was not replayed");
1210       /*
1211       if (h.LogVersion != 0)
1212         AddErrorMessage("unknown LogVresion");
1213       else
1214       {
1215         CByteBuffer log;
1216         RINOK(Seek2(h.LogOffset));
1217         RINOK(ReadToBuf_FALSE(log, h.LogLength));
1218         if (!ParseLog(log))
1219         {
1220           return S_FALSE;
1221         }
1222       }
1223       */
1224     }
1225   }
1226   CRegion regions[2];
1227   int correctRegionIndex = -1;
1228 
1229   {
1230     CByteBuffer temp;
1231     temp.Alloc(kRegionSize * 2);
1232     RINOK(Seek2((1 << 16) * 3))
1233     RINOK(Read_FALSE(temp, kRegionSize * 2))
1234     unsigned numTables = 1;
1235     if (memcmp(temp, temp + kRegionSize, kRegionSize) != 0)
1236     {
1237       AddErrorMessage("Region tables mismatch");
1238       numTables = 2;
1239     }
1240 
1241     for (unsigned i = 0; i < numTables; i++)
1242     {
1243       // RINOK(Seek2((1 << 16) * (3 + i)));
1244       // RINOK(Read_FALSE(temp, kRegionSize));
1245       if (regions[i].Parse(temp))
1246       {
1247         if (correctRegionIndex < 0)
1248           correctRegionIndex = (int)i;
1249       }
1250       else
1251       {
1252         AddErrorMessage("Incorrect region table");
1253       }
1254     }
1255     if (correctRegionIndex < 0)
1256       return S_FALSE;
1257     /*
1258     if (!regions[0].IsEqualTo(regions[1]))
1259       return S_FALSE;
1260     */
1261   }
1262 
1263   // UpdatePhySize((1 << 16) * 5);
1264   UpdatePhySize(1 << 20);
1265 
1266   {
1267     const CRegion &region = regions[correctRegionIndex];
1268     HeadersSize += region.DataSize;
1269     UpdatePhySize(region.EndPos);
1270     {
1271       if (!region.Meta_Defined)
1272         return S_FALSE;
1273       const CRegionEntry &e = region.MetaEntry;
1274       if (e.Len == 0)
1275         return S_FALSE;
1276       {
1277         // static const kMetaTableSize = 1 << 16;
1278         CByteBuffer temp;
1279         {
1280           RINOK(Seek2(e.Offset))
1281           RINOK(ReadToBuf_FALSE(temp, e.Len))
1282         }
1283         if (!Meta.Parse(temp, temp.Size()))
1284           return S_FALSE;
1285       }
1286       // UpdatePhySize(e.GetEndPos());
1287     }
1288     {
1289       if (!region.Bat_Defined)
1290         return S_FALSE;
1291       const CRegionEntry &e = region.BatEntry;
1292       if (e.Len == 0)
1293         return S_FALSE;
1294       // UpdatePhySize(e.GetEndPos());
1295       {
1296         RINOK(Seek2(e.Offset))
1297         RINOK(ReadToBuf_FALSE(Bat.Data, e.Len))
1298       }
1299       if (!ParseBat())
1300         return S_FALSE;
1301       if (!CheckBat())
1302       {
1303         AddErrorMessage("BAT overlap");
1304         // _batOverlap = true;
1305         // return S_FALSE;
1306       }
1307     }
1308   }
1309 
1310   {
1311     // do we need to check "parent_linkage2" also?
1312     FOR_VECTOR (i, Meta.ParentPairs)
1313     {
1314       const CParentPair &pair = Meta.ParentPairs[i];
1315       if (pair.Key.IsEqualTo("parent_linkage"))
1316       {
1317         _parentGuid_IsDefined = _parentGuid.ParseFromFormatedHexString(pair.Value);
1318         break;
1319       }
1320     }
1321   }
1322 
1323   {
1324     // absolute paths for parent stream can be rejected later in client callback
1325     // the order of check by specification:
1326     const char * const g_ParentKeys[] =
1327     {
1328         "relative_path"       // "..\..\path2\sub3\parent.vhdx"
1329       , "volume_path"         // "\\?\Volume{26A21BDA-A627-11D7-9931-806E6F6E6963}\path2\sub3\parent.vhdx")
1330       , "absolute_win32_path" // "d:\path2\sub3\parent.vhdx"
1331     };
1332     for (unsigned i = 0; i < Z7_ARRAY_SIZE(g_ParentKeys); i++)
1333     {
1334       const int index = Meta.FindParentKey(g_ParentKeys[i]);
1335       if (index < 0)
1336         continue;
1337       ParentNames.Add(Meta.ParentPairs[index].Value);
1338     }
1339   }
1340 
1341   if (Meta.Is_HasParent())
1342   {
1343     if (!Meta.Locator_Defined)
1344       AddErrorMessage("Parent locator is not defined");
1345     else
1346     {
1347       if (!_parentGuid_IsDefined)
1348         AddErrorMessage("Parent GUID is not defined");
1349       if (ParentNames.IsEmpty())
1350         AddErrorMessage("Parent VHDX file name is not defined");
1351     }
1352   }
1353   else
1354   {
1355     if (Meta.Locator_Defined)
1356       AddErrorMessage("Unexpected parent locator");
1357   }
1358 
1359   // here we suppose that and locator can be used only with HasParent flag
1360 
1361   // return S_FALSE;
1362 
1363   _size = Meta.VirtualDiskSize; // CHandlerImg
1364 
1365   // _posInArc = 0;
1366   // Reset_PosInArc();
1367   // RINOK(InStream_SeekToBegin(Stream))
1368 
1369   return S_OK;
1370 }
1371 
1372 
1373 /*
1374 static UInt32 g_NumCalls = 0;
1375 static UInt32 g_NumCalls2 = 0;
1376 static struct CCounter { ~CCounter()
1377 {
1378   printf("\nNumCalls = %10u\n", g_NumCalls);
1379   printf("NumCalls2 = %10u\n", g_NumCalls2);
1380 } } g_Counter;
1381 */
1382 
1383 Z7_COM7F_IMF(CHandler::Read(void *data, UInt32 size, UInt32 *processedSize))
1384 {
1385   // g_NumCalls++;
1386   if (processedSize)
1387     *processedSize = 0;
1388   if (_virtPos >= Meta.VirtualDiskSize)
1389     return S_OK;
1390   {
1391     const UInt64 rem = Meta.VirtualDiskSize - _virtPos;
1392     if (size > rem)
1393       size = (UInt32)rem;
1394   }
1395   if (size == 0)
1396     return S_OK;
1397   const size_t blockIndex = (size_t)(_virtPos >> Meta.BlockSize_Log);
1398   const size_t chunkIndex = blockIndex >> ChunkRatio_Log;
1399   const size_t chunkRatio = (size_t)1 << ChunkRatio_Log;
1400   const size_t blockIndex2 = chunkIndex * (chunkRatio + 1) + (blockIndex & (chunkRatio - 1));
1401   const UInt64 blockSectVal = Bat.GetItem(blockIndex2);
1402   const UInt64 blockOffset = BAT_GET_OFFSET(blockSectVal);
1403   const UInt32 blockState = BAT_GET_STATE(blockSectVal);
1404 
1405   const UInt32 blockSize = (UInt32)1 << Meta.BlockSize_Log;
1406   const UInt32 offsetInBlock = (UInt32)_virtPos & (blockSize - 1);
1407   size = MyMin(blockSize - offsetInBlock, size);
1408 
1409   bool needParent = false;
1410   bool needRead = false;
1411 
1412   if (blockState == PAYLOAD_BLOCK_FULLY_PRESENT)
1413     needRead = true;
1414   else if (blockState == PAYLOAD_BLOCK_NOT_PRESENT)
1415   {
1416     /* for a differencing VHDX: parent virtual disk SHOULD be
1417          inspected to determine the associated contents (SPECIFICATION).
1418          we suppose that we should not check BitMap.
1419        for fixed or dynamic VHDX files: the block contents are undefined and
1420          can contain arbitrary data (SPECIFICATION). NTFS::pagefile.sys can use such state. */
1421     if (IsDiff())
1422       needParent = true;
1423   }
1424   else if (blockState == PAYLOAD_BLOCK_PARTIALLY_PRESENT)
1425   {
1426     // only allowed for differencing VHDX files.
1427     // associated sector bitmap block MUST be valid
1428     if (chunkIndex >= BitMaps.Size())
1429       return S_FALSE;
1430     // else
1431     {
1432       const CByteBuffer &bitmap = BitMaps[(unsigned)chunkIndex];
1433       const Byte *p = (const Byte *)bitmap;
1434       if (!p)
1435         return S_FALSE;
1436       // else
1437       {
1438         // g_NumCalls2++;
1439         const UInt64 sectorIndex = _virtPos >> Meta.LogicalSectorSize_Log;
1440 
1441         #define BIT_MAP_UNIT_LOG  3 // it's for small block (4 KB)
1442         // #define BIT_MAP_UNIT_LOG  5 // speed optimization for large blocks (16 KB)
1443 
1444         const size_t offs = (size_t)(sectorIndex >> 3) &
1445             (
1446               (kBitmapSize - 1)
1447               & ~(((UInt32)1 << (BIT_MAP_UNIT_LOG - 3)) - 1)
1448             );
1449 
1450         unsigned sector2 = (unsigned)sectorIndex & ((1 << BIT_MAP_UNIT_LOG) - 1);
1451       #if BIT_MAP_UNIT_LOG == 5
1452         UInt32 v = GetUi32(p + offs) >> sector2;
1453       #else
1454         unsigned v = (unsigned)p[offs] >> sector2;
1455       #endif
1456         // UInt32 v = GetUi32(p + offs) >> sector2;
1457         const UInt32 sectorSize = (UInt32)1 << Meta.LogicalSectorSize_Log;
1458         const UInt32 offsetInSector = (UInt32)_virtPos & (sectorSize - 1);
1459         const unsigned bit = (unsigned)(v & 1);
1460         if (bit)
1461           needRead = true;
1462         else
1463           needParent = true; // zero - from the parent VHDX file
1464         UInt32 rem = sectorSize - offsetInSector;
1465         for (sector2++; sector2 < (1 << BIT_MAP_UNIT_LOG); sector2++)
1466         {
1467           v >>= 1;
1468           if (bit != (v & 1))
1469             break;
1470           rem += sectorSize;
1471         }
1472         if (size > rem)
1473           size = rem;
1474       }
1475     }
1476   }
1477 
1478   bool needZero = true;
1479 
1480   HRESULT res = S_OK;
1481 
1482   if (needParent)
1483   {
1484     if (!ParentStream)
1485       return S_FALSE;
1486     // if (ParentStream)
1487     {
1488       RINOK(InStream_SeekSet(ParentStream, _virtPos))
1489       size_t processed = size;
1490       res = ReadStream(ParentStream, (Byte *)data, &processed);
1491       size = (UInt32)processed;
1492       needZero = false;
1493     }
1494   }
1495   else if (needRead)
1496   {
1497     UInt32 processed = 0;
1498     res = ReadPhy(blockOffset + offsetInBlock, data, size, processed);
1499     size = processed;
1500     needZero = false;
1501   }
1502 
1503   if (needZero)
1504     memset(data, 0, size);
1505 
1506   if (processedSize)
1507     *processedSize = size;
1508 
1509   _virtPos += size;
1510   return res;
1511 }
1512 
1513 
1514 enum
1515 {
1516   kpidParent = kpidUserDefined
1517 };
1518 
1519 static const CStatProp kArcProps[] =
1520 {
1521   { NULL, kpidClusterSize, VT_UI4},
1522   { NULL, kpidSectorSize, VT_UI4},
1523   { NULL, kpidMethod, VT_BSTR},
1524   { NULL, kpidNumVolumes, VT_UI4},
1525   { NULL, kpidTotalPhySize, VT_UI8},
1526   { "Parent", kpidParent, VT_BSTR},
1527   { NULL, kpidCreatorApp, VT_BSTR},
1528   { NULL, kpidComment, VT_BSTR},
1529   { NULL, kpidId, VT_BSTR}
1530  };
1531 
1532 static const Byte kProps[] =
1533 {
1534   kpidSize,
1535   kpidPackSize
1536 };
1537 
1538 IMP_IInArchive_Props
1539 IMP_IInArchive_ArcProps_WITH_NAME
1540 
1541 
1542 void CHandler::AddErrorMessage(const char *message)
1543 {
1544   if (!_errorMessage.IsEmpty())
1545     _errorMessage.Add_LF();
1546   _errorMessage += message;
1547 }
1548 
1549 void CHandler::AddErrorMessage(const char *message, const wchar_t *name)
1550 {
1551   AddErrorMessage(message);
1552   _errorMessage += name;
1553 }
1554 
1555 
1556 static void AddComment_Name(UString &s, const char *name)
1557 {
1558   s += name;
1559   s += ": ";
1560 }
1561 
1562 static void AddComment_Bool(UString &s, const char *name, bool val)
1563 {
1564   AddComment_Name(s, name);
1565   s.Add_Char(val ? '+' : '-');
1566   s.Add_LF();
1567 }
1568 
1569 static void AddComment_UInt64(UString &s, const char *name, UInt64 v, bool showMB = false)
1570 {
1571   AddComment_Name(s, name);
1572   s.Add_UInt64(v);
1573   if (showMB)
1574   {
1575     s += " (";
1576     s.Add_UInt64(v >> 20);
1577     s += " MiB)";
1578   }
1579   s.Add_LF();
1580 }
1581 
1582 static void AddComment_BlockSize(UString &s, const char *name, unsigned logSize)
1583 {
1584   if (logSize != 0)
1585     AddComment_UInt64(s, name, ((UInt64)1 << logSize));
1586 }
1587 
1588 
1589 void CHandler::AddComment(UString &s) const
1590 {
1591   AddComment_UInt64(s, "VirtualDiskSize", Meta.VirtualDiskSize);
1592   AddComment_UInt64(s, "PhysicalSize", _phySize);
1593 
1594   if (!_errorMessage.IsEmpty())
1595   {
1596     AddComment_Name(s, "Error");
1597     s += _errorMessage;
1598     s.Add_LF();
1599   }
1600 
1601   if (Meta.Guid_Defined)
1602   {
1603     AddComment_Name(s, "Id");
1604     Meta.Guid.AddHexToString(s);
1605     s.Add_LF();
1606   }
1607 
1608   AddComment_UInt64(s, "SequenceNumber", Header.SequenceNumber);
1609   AddComment_UInt64(s, "LogLength", Header.LogLength, true);
1610 
1611   for (unsigned i = 0; i < 3; i++)
1612   {
1613     const CGuid &g = Header.Guids[i];
1614     if (g.IsZero())
1615       continue;
1616     if (i == 0)
1617       s += "FileWrite";
1618     else if (i == 1)
1619       s += "DataWrite";
1620     else
1621       s += "Log";
1622     AddComment_Name(s, "Guid");
1623     g.AddHexToString(s);
1624     s.Add_LF();
1625   }
1626 
1627   AddComment_Bool(s, "HasParent", Meta.Is_HasParent());
1628   AddComment_Bool(s, "Fixed", Meta.Is_LeaveBlockAllocated());
1629   if (Meta.Is_LeaveBlockAllocated())
1630     AddComment_Bool(s, "DataContiguous", _isDataContiguous);
1631 
1632   AddComment_BlockSize(s, "BlockSize", Meta.BlockSize_Log);
1633   AddComment_BlockSize(s, "LogicalSectorSize", Meta.LogicalSectorSize_Log);
1634   AddComment_BlockSize(s, "PhysicalSectorSize", Meta.PhysicalSectorSize_Log);
1635 
1636   {
1637     const UInt64 packSize = GetPackSize();
1638     AddComment_UInt64(s, "PackSize", packSize, true);
1639     const UInt64 headersSize = HeadersSize + ((UInt64)NumUsedBitMaps << kBitmapSize_Log);
1640     AddComment_UInt64(s, "HeadersSize", headersSize, true);
1641     AddComment_UInt64(s, "FreeSpace", _phySize - packSize - headersSize, true);
1642     /*
1643     if (NumUsed_1MB_Blocks_Defined)
1644       AddComment_UInt64(s, "used2", (NumUsed_1MB_Blocks << 20));
1645     */
1646   }
1647 
1648   if (Meta.ParentPairs.Size() != 0)
1649   {
1650     s += "Parent:";
1651     s.Add_LF();
1652     FOR_VECTOR(i, Meta.ParentPairs)
1653     {
1654       const CParentPair &pair = Meta.ParentPairs[i];
1655       s += "  ";
1656       s += pair.Key;
1657       s += ": ";
1658       s += pair.Value;
1659       s.Add_LF();
1660     }
1661     s.Add_LF();
1662   }
1663 }
1664 
1665 
1666 
1667 Z7_COM7F_IMF(CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value))
1668 {
1669   COM_TRY_BEGIN
1670   NCOM::CPropVariant prop;
1671   switch (propID)
1672   {
1673     case kpidMainSubfile: prop = (UInt32)0; break;
1674     case kpidClusterSize: prop = (UInt32)1 << Meta.BlockSize_Log; break;
1675     case kpidSectorSize: prop = (UInt32)1 << Meta.LogicalSectorSize_Log; break;
1676     case kpidShortComment:
1677     case kpidMethod:
1678     {
1679       AString s;
1680       AddTypeString(s);
1681       if (IsDiff())
1682       {
1683         s += " -> ";
1684         const CHandler *p = this;
1685         while (p && p->IsDiff())
1686           p = p->Parent;
1687         if (!p)
1688           s += '?';
1689         else
1690           p->AddTypeString(s);
1691       }
1692       prop = s;
1693       break;
1694     }
1695     case kpidComment:
1696     {
1697       UString s;
1698       {
1699         if (NumLevels > 1)
1700         {
1701           AddComment_UInt64(s, "NumVolumeLevels", NumLevels);
1702           AddComment_UInt64(s, "PackSizeTotal", PackSize_Total, true);
1703           s += "----";
1704           s.Add_LF();
1705         }
1706 
1707         const CHandler *p = this;
1708         for (;;)
1709         {
1710           if (p->_level != 0 || p->Parent)
1711             AddComment_UInt64(s, "VolumeLevel", p->_level + 1);
1712           p->AddComment(s);
1713           if (!p->Parent)
1714             break;
1715           s += "----";
1716           s.Add_LF();
1717           {
1718             s.Add_LF();
1719             if (!p->ParentName_Used.IsEmpty())
1720             {
1721               AddComment_Name(s, "Name");
1722               s += p->ParentName_Used;
1723               s.Add_LF();
1724             }
1725           }
1726           p = p->Parent;
1727         }
1728       }
1729       prop = s;
1730       break;
1731     }
1732     case kpidCreatorApp:
1733     {
1734       if (!_creator.IsEmpty())
1735         prop = _creator;
1736       break;
1737     }
1738     case kpidId:
1739     {
1740       if (Meta.Guid_Defined)
1741       {
1742         UString s;
1743         Meta.Guid.AddHexToString(s);
1744         prop = s;
1745       }
1746       break;
1747     }
1748     case kpidName:
1749     {
1750       if (Meta.Guid_Defined)
1751       {
1752         UString s;
1753         Meta.Guid.AddHexToString(s);
1754         s += ".vhdx";
1755         prop = s;
1756       }
1757       break;
1758     }
1759     case kpidParent: if (IsDiff()) prop = GetParentSequence(); break;
1760     case kpidPhySize: prop = _phySize; break;
1761     case kpidTotalPhySize:
1762     {
1763       const CHandler *p = this;
1764       UInt64 sum = 0;
1765       do
1766       {
1767         sum += p->_phySize;
1768         p = p->Parent;
1769       }
1770       while (p);
1771       prop = sum;
1772       break;
1773     }
1774     case kpidNumVolumes: if (NumLevels != 1) prop = (UInt32)NumLevels; break;
1775     case kpidError:
1776     {
1777       UString s;
1778       const CHandler *p = this;
1779       do
1780       {
1781         if (!p->_errorMessage.IsEmpty())
1782         {
1783           if (!s.IsEmpty())
1784             s.Add_LF();
1785           s += p->_errorMessage;
1786         }
1787         p = p->Parent;
1788       }
1789       while (p);
1790       if (!s.IsEmpty())
1791         prop = s;
1792       break;
1793     }
1794   }
1795   prop.Detach(value);
1796   return S_OK;
1797   COM_TRY_END
1798 }
1799 
1800 
1801 HRESULT CHandler::Open2(IInStream *stream, IArchiveOpenCallback *openArchiveCallback)
1802 {
1803   Stream = stream;
1804   if (_level >= (1 << 20))
1805     return S_FALSE;
1806 
1807   RINOK(Open3())
1808 
1809   NumLevels = 1;
1810   PackSize_Total = GetPackSize();
1811 
1812   if (_child)
1813   {
1814     if (!_child->_parentGuid.IsEqualTo(Header.Guids[kHeader_GUID_Index_DataWriteGuid]))
1815       return S_FALSE;
1816     const CHandler *child = _child;
1817     do
1818     {
1819       /* We suppose that only FileWriteGuid is unique.
1820          Another IDs must be identical in in difference and parent archives. */
1821       if (Header.Guids[kHeader_GUID_Index_FileWriteGuid].IsEqualTo(
1822               child->Header.Guids[kHeader_GUID_Index_FileWriteGuid])
1823           && _phySize == child->_phySize)
1824       {
1825         _isCyclic = true;
1826         _isCyclic_or_CyclicParent = true;
1827         AddErrorMessage("Cyclic parent archive was blocked");
1828         return S_OK;
1829       }
1830       child = child->_child;
1831     }
1832     while (child);
1833   }
1834 
1835   if (!Meta.Is_HasParent())
1836     return S_OK;
1837 
1838   if (!Meta.Locator_Defined
1839       || !_parentGuid_IsDefined
1840       || ParentNames.IsEmpty())
1841   {
1842     return S_OK;
1843   }
1844 
1845   ParentName_Used = ParentNames.Front();
1846 
1847   HRESULT res;
1848   const unsigned kNumLevelsMax = (1 << 8);  // Maybe we need to increase that limit
1849   if (_level >= kNumLevelsMax - 1)
1850   {
1851     AddErrorMessage("Too many parent levels");
1852     return S_OK;
1853   }
1854 
1855   bool _parentFileWasOpen = false;
1856 
1857   if (!openArchiveCallback)
1858     res = S_FALSE;
1859   else
1860     res = OpenParent(openArchiveCallback, _parentFileWasOpen);
1861 
1862   if (res != S_OK)
1863   {
1864     if (res != S_FALSE)
1865       return res;
1866 
1867     if (_parentFileWasOpen)
1868       AddErrorMessage("Can't parse parent VHDX file : ", ParentName_Used);
1869     else
1870       AddErrorMessage("Missing parent VHDX file : ", ParentName_Used);
1871   }
1872 
1873 
1874   return S_OK;
1875 }
1876 
1877 
1878 HRESULT CHandler::OpenParent(IArchiveOpenCallback *openArchiveCallback, bool &_parentFileWasOpen)
1879 {
1880   _parentFileWasOpen = false;
1881   Z7_DECL_CMyComPtr_QI_FROM(
1882       IArchiveOpenVolumeCallback,
1883       openVolumeCallback, openArchiveCallback)
1884   if (!openVolumeCallback)
1885     return S_FALSE;
1886   {
1887     CMyComPtr<IInStream> nextStream;
1888     HRESULT res = S_FALSE;
1889     UString name;
1890 
1891     FOR_VECTOR (i, ParentNames)
1892     {
1893       name = ParentNames[i];
1894 
1895       // we remove prefix ".\\', but client already can support any variant
1896       if (name[0] == L'.' && name[1] == L'\\')
1897         name.DeleteFrontal(2);
1898 
1899       res = openVolumeCallback->GetStream(name, &nextStream);
1900 
1901       if (res == S_OK && nextStream)
1902         break;
1903 
1904       if (res != S_OK && res != S_FALSE)
1905         return res;
1906     }
1907 
1908     if (res == S_FALSE || !nextStream)
1909       return S_FALSE;
1910 
1911     ParentName_Used = name;
1912     _parentFileWasOpen = true;
1913 
1914     Parent = new CHandler;
1915     ParentStream = Parent;
1916 
1917     try
1918     {
1919       Parent->_level = _level + 1;
1920       Parent->_child = this;
1921       /* we could call CHandlerImg::Open() here.
1922          but we don't need (_imgExt) in (Parent). So we call Open2() here */
1923       Parent->Close();
1924       res = Parent->Open2(nextStream, openArchiveCallback);
1925     }
1926     catch(...)
1927     {
1928       Parent = NULL;
1929       ParentStream.Release();
1930       res = S_FALSE;
1931       throw;
1932     }
1933 
1934     if (res != S_OK)
1935     {
1936       Parent = NULL;
1937       ParentStream.Release();
1938       if (res == E_ABORT)
1939         return res;
1940       if (res != S_FALSE)
1941       {
1942         // we must show that error code
1943       }
1944     }
1945 
1946     if (res == S_OK)
1947     {
1948       if (Parent->_isCyclic_or_CyclicParent)
1949         _isCyclic_or_CyclicParent = true;
1950 
1951       NumLevels = Parent->NumLevels + 1;
1952       PackSize_Total += Parent->GetPackSize();
1953 
1954       // we read BitMaps only if Parent was open
1955 
1956       UInt64 numBytes = (UInt64)NumUsedBitMaps << kBitmapSize_Log;
1957       if (openArchiveCallback && numBytes != 0)
1958       {
1959         RINOK(openArchiveCallback->SetTotal(NULL, &numBytes))
1960       }
1961       numBytes = 0;
1962       for (size_t i = ChunkRatio; i < TotalBatEntries; i += ChunkRatio + 1)
1963       {
1964         const UInt64 v = Bat.GetItem(i);
1965         const UInt64 offset = BAT_GET_OFFSET(v);
1966         const unsigned state = BAT_GET_STATE(v);
1967 
1968         CByteBuffer &buf = BitMaps.AddNew();
1969         if (state == SB_BLOCK_PRESENT)
1970         {
1971           if (openArchiveCallback)
1972           {
1973             RINOK(openArchiveCallback->SetCompleted(NULL, &numBytes))
1974           }
1975           numBytes += kBitmapSize;
1976           buf.Alloc(kBitmapSize);
1977           RINOK(Seek2(offset))
1978           RINOK(Read_FALSE(buf, kBitmapSize))
1979           /*
1980           for (unsigned i = 0; i < (1 << 20); i+=4)
1981           {
1982             UInt32 v = GetUi32(buf + i);
1983             if (v != 0 && v != (UInt32)(Int32)-1)
1984               printf("\n%7d %8x", i, v);
1985           }
1986           */
1987         }
1988       }
1989     }
1990   }
1991 
1992   return S_OK;
1993 }
1994 
1995 
1996 void CHandler::CloseAtError()
1997 {
1998   // CHandlerImg
1999   Clear_HandlerImg_Vars();
2000   Stream.Release();
2001 
2002   _phySize = 0;
2003   Bat.Clear();
2004   BitMaps.Clear();
2005   NumUsedBlocks = 0;
2006   NumUsedBitMaps = 0;
2007   HeadersSize = 0;
2008   /*
2009   NumUsed_1MB_Blocks = 0;
2010   NumUsed_1MB_Blocks_Defined = false;
2011   */
2012 
2013   Parent = NULL;
2014   ParentStream.Release();
2015   _errorMessage.Empty();
2016   _creator.Empty();
2017   _nonEmptyLog = false;
2018   _parentGuid_IsDefined = false;
2019   _isDataContiguous = false;
2020   // _batOverlap = false;
2021 
2022   ParentNames.Clear();
2023   ParentName_Used.Empty();
2024 
2025   Meta.Clear();
2026 
2027   ChunkRatio_Log = 0;
2028   ChunkRatio = 0;
2029   TotalBatEntries = 0;
2030   NumLevels = 0;
2031   PackSize_Total = 0;
2032 
2033   _isCyclic = false;
2034   _isCyclic_or_CyclicParent = false;
2035 }
2036 
2037 Z7_COM7F_IMF(CHandler::Close())
2038 {
2039   CloseAtError();
2040   return S_OK;
2041 }
2042 
2043 
2044 Z7_COM7F_IMF(CHandler::GetProperty(UInt32 /* index */, PROPID propID, PROPVARIANT *value))
2045 {
2046   COM_TRY_BEGIN
2047   NCOM::CPropVariant prop;
2048 
2049   switch (propID)
2050   {
2051     case kpidSize: prop = Meta.VirtualDiskSize; break;
2052     case kpidPackSize: prop = PackSize_Total; break;
2053     case kpidExtension: prop = (_imgExt ? _imgExt : "img"); break;
2054   }
2055 
2056   prop.Detach(value);
2057   return S_OK;
2058   COM_TRY_END
2059 }
2060 
2061 
2062 Z7_COM7F_IMF(CHandler::GetStream(UInt32 /* index */, ISequentialInStream **stream))
2063 {
2064   COM_TRY_BEGIN
2065   *stream = NULL;
2066   // if some prarent is not OK, we don't create stream
2067   if (!AreParentsOK())
2068     return S_FALSE;
2069   InitSeekPositions();
2070   CMyComPtr<ISequentialInStream> streamTemp = this;
2071   *stream = streamTemp.Detach();
2072   return S_OK;
2073   COM_TRY_END
2074 }
2075 
2076 REGISTER_ARC_I(
2077   "VHDX", "vhdx avhdx", NULL, 0xc4,
2078   kSignature,
2079   0,
2080   0,
2081   NULL)
2082 
2083 }}
2084