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