diff options
Diffstat (limited to 'CPP/7zip/Archive/VhdxHandler.cpp')
-rw-r--r-- | CPP/7zip/Archive/VhdxHandler.cpp | 2062 |
1 files changed, 2062 insertions, 0 deletions
diff --git a/CPP/7zip/Archive/VhdxHandler.cpp b/CPP/7zip/Archive/VhdxHandler.cpp new file mode 100644 index 0000000..e1e4692 --- /dev/null +++ b/CPP/7zip/Archive/VhdxHandler.cpp | |||
@@ -0,0 +1,2062 @@ | |||
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 | |||
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 | |||
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 | |||
64 | 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 | |||
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 | |||
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 | |||
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 | |||
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 | |||
116 | bool IsZero() const { return IsZeroArr(Data, 16); } | ||
117 | bool IsEqualTo(const Byte *a) const { return memcmp(Data, a, 16) == 0; } | ||
118 | bool IsEqualTo(const CGuid &g) const { return IsEqualTo(g.Data); } | ||
119 | void AddHexToString(UString &s) const; | ||
120 | |||
121 | void SetFrom(const Byte *p) { memcpy(Data, p, 16); } | ||
122 | |||
123 | 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 | |||
152 | 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 | |||
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 | |||
219 | UInt64 GetEndPos() const { return Offset + Len; } | ||
220 | bool Parse(const Byte *p); | ||
221 | }; | ||
222 | |||
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 | |||
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 | |||
327 | bool IsUser() const { return (Flags0 & 1) != 0; } | ||
328 | bool IsVirtualDisk() const { return (Flags0 & 2) != 0; } | ||
329 | bool IsRequired() const { return (Flags0 & 4) != 0; } | ||
330 | |||
331 | 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 | |||
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 | |||
389 | 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 | |||
400 | bool Is_LeaveBlockAllocated() const { return (Flags & 1) != 0; } | ||
401 | bool Is_HasParent() const { return (Flags & 2) != 0; } | ||
402 | |||
403 | 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 | |||
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 | |||
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 | |||
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 | |||
608 | void Clear() { Data.Free(); } | ||
609 | 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 | |||
665 | void UpdatePhySize(UInt64 value) | ||
666 | { | ||
667 | if (_phySize < value) | ||
668 | _phySize = value; | ||
669 | } | ||
670 | |||
671 | HRESULT Seek2(UInt64 offset); | ||
672 | HRESULT Read_FALSE(Byte *data, size_t size) | ||
673 | { | ||
674 | return ReadStream_FALSE(Stream, data, size); | ||
675 | } | ||
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 | |||
685 | 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 | |||
692 | 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 | |||
707 | UInt64 GetPackSize() const | ||
708 | { | ||
709 | return (UInt64)NumUsedBlocks << Meta.BlockSize_Log; | ||
710 | } | ||
711 | |||
712 | 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 | |||
726 | 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 | |||
755 | CHandler(): | ||
756 | _child(NULL), | ||
757 | _level(0), | ||
758 | _isCyclic(false), | ||
759 | _isCyclic_or_CyclicParent(false) | ||
760 | {} | ||
761 | }; | ||
762 | |||
763 | |||
764 | HRESULT CHandler::Seek2(UInt64 offset) | ||
765 | { | ||
766 | return Stream->Seek(offset, STREAM_SEEK_SET, NULL); | ||
767 | } | ||
768 | |||
769 | |||
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 | |||
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 | |||
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 | |||
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 | |||
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 | |||
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 | |||
1521 | void CHandler::AddErrorMessage(const char *message) | ||
1522 | { | ||
1523 | if (!_errorMessage.IsEmpty()) | ||
1524 | _errorMessage.Add_LF(); | ||
1525 | _errorMessage += message; | ||
1526 | } | ||
1527 | |||
1528 | void CHandler::AddErrorMessage(const char *message, const wchar_t *name) | ||
1529 | { | ||
1530 | AddErrorMessage(message); | ||
1531 | _errorMessage += name; | ||
1532 | } | ||
1533 | |||
1534 | |||
1535 | static void AddComment_Name(UString &s, const char *name) | ||
1536 | { | ||
1537 | s += name; | ||
1538 | s += ": "; | ||
1539 | } | ||
1540 | |||
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 | |||
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 | |||
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 | |||
1568 | 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 | |||
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 | |||
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 | |||
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 | |||
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 | |||
2016 | STDMETHODIMP CHandler::Close() | ||
2017 | { | ||
2018 | CloseAtError(); | ||
2019 | return S_OK; | ||
2020 | } | ||
2021 | |||
2022 | |||
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 | |||
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 | }} | ||