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 ®ion = 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