1 // VhdHandler.cpp
2
3 #include "StdAfx.h"
4
5 #include "../../../C/CpuArch.h"
6
7 #include "../../Common/ComTry.h"
8 #include "../../Common/IntToString.h"
9
10 #include "../../Windows/PropVariant.h"
11
12 #include "../Common/LimitedStreams.h"
13 #include "../Common/RegisterArc.h"
14 #include "../Common/StreamUtils.h"
15
16 #include "HandlerCont.h"
17
18 #define Get16(p) GetBe16(p)
19 #define Get32(p) GetBe32(p)
20 #define Get64(p) GetBe64(p)
21
22 #define G32(_offs_, dest) dest = Get32(p + (_offs_))
23 #define G64(_offs_, dest) dest = Get64(p + (_offs_))
24
25 using namespace NWindows;
26
27 namespace NArchive {
28 namespace NVhd {
29
30 static const unsigned kSignatureSize = 10;
31 static const Byte kSignature[kSignatureSize] =
32 { 'c', 'o', 'n', 'e', 'c', 't', 'i', 'x', 0, 0 };
33
34 static const UInt32 kUnusedBlock = 0xFFFFFFFF;
35
36 static const UInt32 kDiskType_Fixed = 2;
37 static const UInt32 kDiskType_Dynamic = 3;
38 static const UInt32 kDiskType_Diff = 4;
39
40 static const char * const kDiskTypes[] =
41 {
42 "0"
43 , "1"
44 , "Fixed"
45 , "Dynamic"
46 , "Differencing"
47 };
48
49 struct CFooter
50 {
51 // UInt32 Features;
52 // UInt32 FormatVersion;
53 UInt64 DataOffset;
54 UInt32 CTime;
55 UInt32 CreatorApp;
56 UInt32 CreatorVersion;
57 UInt32 CreatorHostOS;
58 // UInt64 OriginalSize;
59 UInt64 CurrentSize;
60 UInt32 DiskGeometry;
61 UInt32 Type;
62 Byte Id[16];
63 Byte SavedState;
64
IsFixedNArchive::NVhd::CFooter65 bool IsFixed() const { return Type == kDiskType_Fixed; }
ThereIsDynamicNArchive::NVhd::CFooter66 bool ThereIsDynamic() const { return Type == kDiskType_Dynamic || Type == kDiskType_Diff; }
67 // bool IsSupported() const { return Type == kDiskType_Fixed || Type == kDiskType_Dynamic || Type == kDiskType_Diff; }
NumCylsNArchive::NVhd::CFooter68 UInt32 NumCyls() const { return DiskGeometry >> 16; }
NumHeadsNArchive::NVhd::CFooter69 UInt32 NumHeads() const { return (DiskGeometry >> 8) & 0xFF; }
NumSectorsPerTrackNArchive::NVhd::CFooter70 UInt32 NumSectorsPerTrack() const { return DiskGeometry & 0xFF; }
71 void AddTypeString(AString &s) const;
72 bool Parse(const Byte *p);
73 };
74
AddTypeString(AString & s) const75 void CFooter::AddTypeString(AString &s) const
76 {
77 if (Type < Z7_ARRAY_SIZE(kDiskTypes))
78 s += kDiskTypes[Type];
79 else
80 s.Add_UInt32(Type);
81 }
82
CheckBlock(const Byte * p,unsigned size,unsigned checkSumOffset,unsigned zeroOffset)83 static bool CheckBlock(const Byte *p, unsigned size, unsigned checkSumOffset, unsigned zeroOffset)
84 {
85 UInt32 sum = 0;
86 unsigned i;
87 for (i = 0; i < checkSumOffset; i++)
88 sum += p[i];
89 for (i = checkSumOffset + 4; i < size; i++)
90 sum += p[i];
91 if (~sum != Get32(p + checkSumOffset))
92 return false;
93 for (i = zeroOffset; i < size; i++)
94 if (p[i] != 0)
95 return false;
96 return true;
97 }
98
99 static const unsigned kSectorSize_Log = 9;
100 static const unsigned kSectorSize = 1 << kSectorSize_Log;
101 static const unsigned kHeaderSize = 512;
102
Parse(const Byte * p)103 bool CFooter::Parse(const Byte *p)
104 {
105 if (memcmp(p, kSignature, kSignatureSize) != 0)
106 return false;
107 // G32(0x08, Features);
108 // G32(0x0C, FormatVersion);
109 G64(0x10, DataOffset);
110 G32(0x18, CTime);
111 G32(0x1C, CreatorApp);
112 G32(0x20, CreatorVersion);
113 G32(0x24, CreatorHostOS);
114 // G64(0x28, OriginalSize);
115 G64(0x30, CurrentSize);
116 G32(0x38, DiskGeometry);
117 G32(0x3C, Type);
118 if (Type < kDiskType_Fixed ||
119 Type > kDiskType_Diff)
120 return false;
121 memcpy(Id, p + 0x44, 16);
122 SavedState = p[0x54];
123 // if (DataOffset > ((UInt64)1 << 62)) return false;
124 // if (CurrentSize > ((UInt64)1 << 62)) return false;
125 return CheckBlock(p, kHeaderSize, 0x40, 0x55);
126 }
127
128 struct CParentLocatorEntry
129 {
130 UInt32 Code;
131 UInt32 DataSpace;
132 UInt32 DataLen;
133 UInt64 DataOffset;
134
ParseNArchive::NVhd::CParentLocatorEntry135 bool Parse(const Byte *p)
136 {
137 G32(0x00, Code);
138 G32(0x04, DataSpace);
139 G32(0x08, DataLen);
140 G64(0x10, DataOffset);
141 return Get32(p + 0x0C) == 0; // Reserved
142 }
143 };
144
145 struct CDynHeader
146 {
147 // UInt64 DataOffset;
148 UInt64 TableOffset;
149 // UInt32 HeaderVersion;
150 UInt32 NumBlocks;
151 unsigned BlockSizeLog;
152 UInt32 ParentTime;
153 Byte ParentId[16];
154 bool RelativeNameWasUsed;
155 UString ParentName;
156 UString RelativeParentNameFromLocator;
157 CParentLocatorEntry ParentLocators[8];
158
159 bool Parse(const Byte *p);
NumBitMapSectorsNArchive::NVhd::CDynHeader160 UInt32 NumBitMapSectors() const
161 {
162 UInt32 numSectorsInBlock = (1 << (BlockSizeLog - kSectorSize_Log));
163 return (numSectorsInBlock + kSectorSize * 8 - 1) / (kSectorSize * 8);
164 }
ClearNArchive::NVhd::CDynHeader165 void Clear()
166 {
167 RelativeNameWasUsed = false;
168 ParentName.Empty();
169 RelativeParentNameFromLocator.Empty();
170 }
171 };
172
Parse(const Byte * p)173 bool CDynHeader::Parse(const Byte *p)
174 {
175 if (memcmp(p, "cxsparse", 8) != 0)
176 return false;
177 // G64(0x08, DataOffset);
178 G64(0x10, TableOffset);
179 // G32(0x18, HeaderVersion);
180 G32(0x1C, NumBlocks);
181 {
182 UInt32 blockSize = Get32(p + 0x20);
183 unsigned i;
184 for (i = kSectorSize_Log;; i++)
185 {
186 if (i > 31)
187 return false;
188 if (((UInt32)1 << i) == blockSize)
189 break;
190 }
191 BlockSizeLog = i;
192 }
193 G32(0x38, ParentTime);
194 if (Get32(p + 0x3C) != 0) // reserved
195 return false;
196 memcpy(ParentId, p + 0x28, 16);
197 {
198 const unsigned kNameLen = 256;
199 wchar_t *s = ParentName.GetBuf(kNameLen);
200 unsigned i;
201 for (i = 0; i < kNameLen; i++)
202 {
203 wchar_t c = Get16(p + 0x40 + i * 2);
204 if (c == 0)
205 break;
206 s[i] = c;
207 }
208 s[i] = 0;
209 ParentName.ReleaseBuf_SetLen(i);
210 }
211 for (unsigned i = 0; i < 8; i++)
212 if (!ParentLocators[i].Parse(p + 0x240 + i * 24))
213 return false;
214 return CheckBlock(p, 1024, 0x24, 0x240 + 8 * 24);
215 }
216
217 Z7_class_CHandler_final: public CHandlerImg
218 {
219 UInt64 _posInArcLimit;
220 UInt64 _startOffset;
221 UInt64 _phySize;
222
223 CFooter Footer;
224 CDynHeader Dyn;
225 CRecordVector<UInt32> Bat;
226 CByteBuffer BitMap;
227 UInt32 BitMapTag;
228 UInt32 NumUsedBlocks;
229 CMyComPtr<IInStream> ParentStream;
230 CHandler *Parent;
231 UInt64 NumLevels;
232 UString _errorMessage;
233 // bool _unexpectedEnd;
234
235 void AddErrorMessage(const char *message, const wchar_t *name = NULL)
236 {
237 if (!_errorMessage.IsEmpty())
238 _errorMessage.Add_LF();
239 _errorMessage += message;
240 if (name)
241 _errorMessage += name;
242 }
243
244 void UpdatePhySize(UInt64 value)
245 {
246 if (_phySize < value)
247 _phySize = value;
248 }
249
250 HRESULT Seek2(UInt64 offset);
251 HRESULT InitAndSeek();
252 HRESULT ReadPhy(UInt64 offset, void *data, UInt32 size);
253
254 bool NeedParent() const { return Footer.Type == kDiskType_Diff; }
255 UInt64 GetPackSize() const
256 { return Footer.ThereIsDynamic() ? ((UInt64)NumUsedBlocks << Dyn.BlockSizeLog) : Footer.CurrentSize; }
257
258 UString GetParentSequence() const
259 {
260 const CHandler *p = this;
261 UString res;
262 while (p && p->NeedParent())
263 {
264 if (!res.IsEmpty())
265 res += " -> ";
266 UString mainName;
267 UString anotherName;
268 if (Dyn.RelativeNameWasUsed)
269 {
270 mainName = p->Dyn.RelativeParentNameFromLocator;
271 anotherName = p->Dyn.ParentName;
272 }
273 else
274 {
275 mainName = p->Dyn.ParentName;
276 anotherName = p->Dyn.RelativeParentNameFromLocator;
277 }
278 res += mainName;
279 if (mainName != anotherName && !anotherName.IsEmpty())
280 {
281 res.Add_Space();
282 res.Add_Char('(');
283 res += anotherName;
284 res.Add_Char(')');
285 }
286 p = p->Parent;
287 }
288 return res;
289 }
290
291 bool AreParentsOK() const
292 {
293 const CHandler *p = this;
294 while (p->NeedParent())
295 {
296 p = p->Parent;
297 if (!p)
298 return false;
299 }
300 return true;
301 }
302
303 HRESULT Open3();
304 HRESULT Open2(IInStream *stream, CHandler *child, IArchiveOpenCallback *openArchiveCallback, unsigned level);
305 HRESULT Open2(IInStream *stream, IArchiveOpenCallback *openArchiveCallback) Z7_override
306 {
307 return Open2(stream, NULL, openArchiveCallback, 0);
308 }
309 void CloseAtError() Z7_override;
310
311 public:
312 Z7_IFACE_COM7_IMP(IInArchive_Img)
313
314 Z7_IFACE_COM7_IMP(IInArchiveGetStream)
315 Z7_IFACE_COM7_IMP(ISequentialInStream)
316 };
317
318 HRESULT CHandler::Seek2(UInt64 offset) { return InStream_SeekSet(Stream, _startOffset + offset); }
319
320 HRESULT CHandler::InitAndSeek()
321 {
322 if (ParentStream)
323 {
324 RINOK(Parent->InitAndSeek())
325 }
326 _virtPos = _posInArc = 0;
327 BitMapTag = kUnusedBlock;
328 BitMap.Alloc(Dyn.NumBitMapSectors() << kSectorSize_Log);
329 return Seek2(0);
330 }
331
332 HRESULT CHandler::ReadPhy(UInt64 offset, void *data, UInt32 size)
333 {
334 if (offset + size > _posInArcLimit)
335 return S_FALSE;
336 if (offset != _posInArc)
337 {
338 _posInArc = offset;
339 RINOK(Seek2(offset))
340 }
341 HRESULT res = ReadStream_FALSE(Stream, data, size);
342 if (res == S_OK)
343 _posInArc += size;
344 else
345 Reset_PosInArc();
346 return res;
347 }
348
349 HRESULT CHandler::Open3()
350 {
351 // Fixed archive uses only footer
352
353 UInt64 startPos;
354 RINOK(InStream_GetPos(Stream, startPos))
355 _startOffset = startPos;
356 Byte header[kHeaderSize];
357 RINOK(ReadStream_FALSE(Stream, header, kHeaderSize))
358 bool headerIsOK = Footer.Parse(header);
359 _size = Footer.CurrentSize;
360
361 if (headerIsOK && !Footer.ThereIsDynamic())
362 {
363 // fixed archive
364 if (startPos < Footer.CurrentSize)
365 return S_FALSE;
366 _posInArcLimit = Footer.CurrentSize;
367 _phySize = Footer.CurrentSize + kHeaderSize;
368 _startOffset = startPos - Footer.CurrentSize;
369 _posInArc = _phySize;
370 return S_OK;
371 }
372
373 UInt64 fileSize;
374 RINOK(InStream_GetSize_SeekToEnd(Stream, fileSize))
375 if (fileSize < kHeaderSize)
376 return S_FALSE;
377
378 const UInt32 kDynSize = 1024;
379 Byte buf[kDynSize];
380
381 RINOK(InStream_SeekSet(Stream, fileSize - kHeaderSize))
382 RINOK(ReadStream_FALSE(Stream, buf, kHeaderSize))
383
384 if (!headerIsOK)
385 {
386 if (!Footer.Parse(buf))
387 return S_FALSE;
388 _size = Footer.CurrentSize;
389 if (Footer.ThereIsDynamic())
390 return S_FALSE; // we can't open Dynamic Archive backward.
391 // fixed archive
392 _posInArcLimit = Footer.CurrentSize;
393 _phySize = Footer.CurrentSize + kHeaderSize;
394 _startOffset = fileSize - kHeaderSize - Footer.CurrentSize;
395 _posInArc = _phySize;
396 return S_OK;
397 }
398
399 _phySize = kHeaderSize;
400 _posInArc = fileSize - startPos;
401 _posInArcLimit = _posInArc - kHeaderSize;
402
403 bool headerAndFooterAreEqual = false;
404 if (memcmp(header, buf, kHeaderSize) == 0)
405 {
406 headerAndFooterAreEqual = true;
407 _phySize = fileSize - _startOffset;
408 }
409
410 RINOK(ReadPhy(Footer.DataOffset, buf, kDynSize))
411 if (!Dyn.Parse(buf))
412 return S_FALSE;
413
414 UpdatePhySize(Footer.DataOffset + kDynSize);
415
416 for (int i = 0; i < 8; i++)
417 {
418 const CParentLocatorEntry &locator = Dyn.ParentLocators[i];
419 const UInt32 kNameBufSizeMax = 1024;
420 if (locator.DataLen < kNameBufSizeMax &&
421 locator.DataOffset < _posInArcLimit &&
422 locator.DataOffset + locator.DataLen <= _posInArcLimit)
423 {
424 if (locator.Code == 0x57327275 && (locator.DataLen & 1) == 0)
425 {
426 // "W2ru" locator
427 // Path is encoded as little-endian UTF-16
428 Byte nameBuf[kNameBufSizeMax];
429 UString tempString;
430 unsigned len = (locator.DataLen >> 1);
431 {
432 wchar_t *s = tempString.GetBuf(len);
433 RINOK(ReadPhy(locator.DataOffset, nameBuf, locator.DataLen))
434 unsigned j;
435 for (j = 0; j < len; j++)
436 {
437 wchar_t c = GetUi16(nameBuf + j * 2);
438 if (c == 0)
439 break;
440 s[j] = c;
441 }
442 s[j] = 0;
443 tempString.ReleaseBuf_SetLen(j);
444 }
445 if (tempString[0] == L'.' && tempString[1] == L'\\')
446 tempString.DeleteFrontal(2);
447 Dyn.RelativeParentNameFromLocator = tempString;
448 }
449 }
450 if (locator.DataLen != 0)
451 UpdatePhySize(locator.DataOffset + locator.DataLen);
452 }
453
454 if (Dyn.NumBlocks >= (UInt32)1 << 31)
455 return S_FALSE;
456 if (Footer.CurrentSize == 0)
457 {
458 if (Dyn.NumBlocks != 0)
459 return S_FALSE;
460 }
461 else if (((Footer.CurrentSize - 1) >> Dyn.BlockSizeLog) + 1 != Dyn.NumBlocks)
462 return S_FALSE;
463
464 Bat.ClearAndReserve(Dyn.NumBlocks);
465
466 UInt32 bitmapSize = Dyn.NumBitMapSectors() << kSectorSize_Log;
467
468 while ((UInt32)Bat.Size() < Dyn.NumBlocks)
469 {
470 RINOK(ReadPhy(Dyn.TableOffset + (UInt64)Bat.Size() * 4, buf, kSectorSize))
471 UpdatePhySize(Dyn.TableOffset + kSectorSize);
472 for (UInt32 j = 0; j < kSectorSize; j += 4)
473 {
474 UInt32 v = Get32(buf + j);
475 if (v != kUnusedBlock)
476 {
477 UInt32 blockSize = (UInt32)1 << Dyn.BlockSizeLog;
478 UpdatePhySize(((UInt64)v << kSectorSize_Log) + bitmapSize + blockSize);
479 NumUsedBlocks++;
480 }
481 Bat.AddInReserved(v);
482 if ((UInt32)Bat.Size() >= Dyn.NumBlocks)
483 break;
484 }
485 }
486
487 if (headerAndFooterAreEqual)
488 return S_OK;
489
490 if (_startOffset + _phySize + kHeaderSize > fileSize)
491 {
492 // _unexpectedEnd = true;
493 _posInArcLimit = _phySize;
494 _phySize += kHeaderSize;
495 return S_OK;
496 }
497
498 RINOK(ReadPhy(_phySize, buf, kHeaderSize))
499 if (memcmp(header, buf, kHeaderSize) == 0)
500 {
501 _posInArcLimit = _phySize;
502 _phySize += kHeaderSize;
503 return S_OK;
504 }
505
506 if (_phySize == 0x800)
507 {
508 /* WHY does empty archive contain additional empty sector?
509 We skip that sector and check footer again. */
510 unsigned i;
511 for (i = 0; i < kSectorSize && buf[i] == 0; i++);
512 if (i == kSectorSize)
513 {
514 RINOK(ReadPhy(_phySize + kSectorSize, buf, kHeaderSize))
515 if (memcmp(header, buf, kHeaderSize) == 0)
516 {
517 _phySize += kSectorSize;
518 _posInArcLimit = _phySize;
519 _phySize += kHeaderSize;
520 return S_OK;
521 }
522 }
523 }
524 _posInArcLimit = _phySize;
525 _phySize += kHeaderSize;
526 AddErrorMessage("Can't find footer");
527 return S_OK;
528 }
529
530 Z7_COM7F_IMF(CHandler::Read(void *data, UInt32 size, UInt32 *processedSize))
531 {
532 if (processedSize)
533 *processedSize = 0;
534 if (_virtPos >= Footer.CurrentSize)
535 return S_OK;
536 {
537 const UInt64 rem = Footer.CurrentSize - _virtPos;
538 if (size > rem)
539 size = (UInt32)rem;
540 }
541 if (size == 0)
542 return S_OK;
543
544 if (Footer.IsFixed())
545 {
546 if (_virtPos > _posInArcLimit)
547 return S_FALSE;
548 {
549 const UInt64 rem = _posInArcLimit - _virtPos;
550 if (size > rem)
551 size = (UInt32)rem;
552 }
553 HRESULT res = S_OK;
554 if (_virtPos != _posInArc)
555 {
556 _posInArc = _virtPos;
557 res = Seek2(_virtPos);
558 }
559 if (res == S_OK)
560 {
561 UInt32 processedSize2 = 0;
562 res = Stream->Read(data, size, &processedSize2);
563 if (processedSize)
564 *processedSize = processedSize2;
565 _posInArc += processedSize2;
566 }
567 if (res != S_OK)
568 Reset_PosInArc();
569 return res;
570 }
571
572 const UInt32 blockIndex = (UInt32)(_virtPos >> Dyn.BlockSizeLog);
573 if (blockIndex >= Bat.Size())
574 return E_FAIL; // it's some unexpected case
575 const UInt32 blockSectIndex = Bat[blockIndex];
576 const UInt32 blockSize = (UInt32)1 << Dyn.BlockSizeLog;
577 UInt32 offsetInBlock = (UInt32)_virtPos & (blockSize - 1);
578 size = MyMin(blockSize - offsetInBlock, size);
579
580 HRESULT res = S_OK;
581 if (blockSectIndex == kUnusedBlock)
582 {
583 if (ParentStream)
584 {
585 RINOK(InStream_SeekSet(ParentStream, _virtPos))
586 res = ParentStream->Read(data, size, &size);
587 }
588 else
589 memset(data, 0, size);
590 }
591 else
592 {
593 const UInt64 newPos = (UInt64)blockSectIndex << kSectorSize_Log;
594 if (BitMapTag != blockIndex)
595 {
596 RINOK(ReadPhy(newPos, BitMap, (UInt32)BitMap.Size()))
597 BitMapTag = blockIndex;
598 }
599 RINOK(ReadPhy(newPos + BitMap.Size() + offsetInBlock, data, size))
600 for (UInt32 cur = 0; cur < size;)
601 {
602 const UInt32 rem = MyMin(0x200 - (offsetInBlock & 0x1FF), size - cur);
603 const UInt32 bmi = offsetInBlock >> kSectorSize_Log;
604 if (((BitMap[bmi >> 3] >> (7 - (bmi & 7))) & 1) == 0)
605 {
606 if (ParentStream)
607 {
608 RINOK(InStream_SeekSet(ParentStream, _virtPos + cur))
609 RINOK(ReadStream_FALSE(ParentStream, (Byte *)data + cur, rem))
610 }
611 else
612 {
613 const Byte *p = (const Byte *)data + cur;
614 for (UInt32 i = 0; i < rem; i++)
615 if (p[i] != 0)
616 return S_FALSE;
617 }
618 }
619 offsetInBlock += rem;
620 cur += rem;
621 }
622 }
623 if (processedSize)
624 *processedSize = size;
625 _virtPos += size;
626 return res;
627 }
628
629
630 enum
631 {
632 kpidParent = kpidUserDefined,
633 kpidSavedState
634 };
635
636 static const CStatProp kArcProps[] =
637 {
638 { NULL, kpidOffset, VT_UI8},
639 { NULL, kpidCTime, VT_FILETIME},
640 { NULL, kpidClusterSize, VT_UI8},
641 { NULL, kpidMethod, VT_BSTR},
642 { NULL, kpidNumVolumes, VT_UI4},
643 { NULL, kpidTotalPhySize, VT_UI8},
644 { "Parent", kpidParent, VT_BSTR},
645 { NULL, kpidCreatorApp, VT_BSTR},
646 { NULL, kpidHostOS, VT_BSTR},
647 { "Saved State", kpidSavedState, VT_BOOL},
648 { NULL, kpidId, VT_BSTR}
649 };
650
651 static const Byte kProps[] =
652 {
653 kpidSize,
654 kpidPackSize,
655 kpidCTime
656
657 /*
658 { kpidNumCyls, VT_UI4},
659 { kpidNumHeads, VT_UI4},
660 { kpidSectorsPerTrack, VT_UI4}
661 */
662 };
663
664 IMP_IInArchive_Props
665 IMP_IInArchive_ArcProps_WITH_NAME
666
667 // VHD start time: 2000-01-01
668 static const UInt64 kVhdTimeStartValue = (UInt64)3600 * 24 * (399 * 365 + 24 * 4);
669
670 static void VhdTimeToFileTime(UInt32 vhdTime, NCOM::CPropVariant &prop)
671 {
672 FILETIME ft, utc;
673 UInt64 v = (kVhdTimeStartValue + vhdTime) * 10000000;
674 ft.dwLowDateTime = (DWORD)v;
675 ft.dwHighDateTime = (DWORD)(v >> 32);
676 // specification says that it's UTC time, but Virtual PC 6 writes local time. Why?
677 LocalFileTimeToFileTime(&ft, &utc);
678 prop = utc;
679 }
680
681 static void StringToAString(char *dest, UInt32 val)
682 {
683 for (int i = 24; i >= 0; i -= 8)
684 {
685 const Byte b = (Byte)((val >> i) & 0xFF);
686 if (b < 0x20 || b > 0x7F)
687 break;
688 *dest++ = (char)b;
689 }
690 *dest = 0;
691 }
692
693 Z7_COM7F_IMF(CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value))
694 {
695 COM_TRY_BEGIN
696 NCOM::CPropVariant prop;
697 switch (propID)
698 {
699 case kpidMainSubfile: prop = (UInt32)0; break;
700 case kpidCTime: VhdTimeToFileTime(Footer.CTime, prop); break;
701 case kpidClusterSize: if (Footer.ThereIsDynamic()) prop = (UInt32)1 << Dyn.BlockSizeLog; break;
702 case kpidShortComment:
703 case kpidMethod:
704 {
705 AString s;
706 Footer.AddTypeString(s);
707 if (NeedParent())
708 {
709 s += " -> ";
710 const CHandler *p = this;
711 while (p && p->NeedParent())
712 p = p->Parent;
713 if (!p)
714 s += '?';
715 else
716 p->Footer.AddTypeString(s);
717 }
718 prop = s;
719 break;
720 }
721 case kpidCreatorApp:
722 {
723 char s[16];
724 StringToAString(s, Footer.CreatorApp);
725 AString res (s);
726 res.Trim();
727 res.Add_Space();
728 res.Add_UInt32(Footer.CreatorVersion >> 16);
729 res.Add_Dot();
730 res.Add_UInt32(Footer.CreatorVersion & 0xFFFF);
731 prop = res;
732 break;
733 }
734 case kpidHostOS:
735 {
736 if (Footer.CreatorHostOS == 0x5769326B)
737 prop = "Windows";
738 else
739 {
740 char s[16];
741 StringToAString(s, Footer.CreatorHostOS);
742 prop = s;
743 }
744 break;
745 }
746 case kpidId:
747 {
748 char s[sizeof(Footer.Id) * 2 + 2];
749 ConvertDataToHex_Upper(s, Footer.Id, sizeof(Footer.Id));
750 prop = s;
751 break;
752 }
753 case kpidSavedState: prop = Footer.SavedState ? true : false; break;
754 case kpidParent: if (NeedParent()) prop = GetParentSequence(); break;
755 case kpidOffset: prop = _startOffset; break;
756 case kpidPhySize: prop = _phySize; break;
757 case kpidTotalPhySize:
758 {
759 const CHandler *p = this;
760 UInt64 sum = 0;
761 do
762 {
763 sum += p->_phySize;
764 p = p->Parent;
765 }
766 while (p);
767 prop = sum;
768 break;
769 }
770 case kpidNumVolumes: if (NumLevels != 1) prop = (UInt32)NumLevels; break;
771
772 /*
773 case kpidErrorFlags:
774 {
775 UInt32 flags = 0;
776 if (_unexpectedEnd)
777 flags |= kpv_ErrorFlags_UnexpectedEndOfArc;
778 if (flags != 0)
779 prop = flags;
780 break;
781 }
782 */
783 case kpidError: if (!_errorMessage.IsEmpty()) prop = _errorMessage; break;
784 }
785 prop.Detach(value);
786 return S_OK;
787 COM_TRY_END
788 }
789
790
791 HRESULT CHandler::Open2(IInStream *stream, CHandler *child, IArchiveOpenCallback *openArchiveCallback, unsigned level)
792 {
793 Close();
794 Stream = stream;
795 if (level > (1 << 12)) // Maybe we need to increase that limit
796 return S_FALSE;
797
798 RINOK(Open3())
799
800 NumLevels = 1;
801 if (child && memcmp(child->Dyn.ParentId, Footer.Id, 16) != 0)
802 return S_FALSE;
803 if (Footer.Type != kDiskType_Diff)
804 return S_OK;
805
806 bool useRelative;
807 UString name;
808
809 if (!Dyn.RelativeParentNameFromLocator.IsEmpty())
810 {
811 useRelative = true;
812 name = Dyn.RelativeParentNameFromLocator;
813 }
814 else
815 {
816 useRelative = false;
817 name = Dyn.ParentName;
818 }
819
820 Dyn.RelativeNameWasUsed = useRelative;
821
822 Z7_DECL_CMyComPtr_QI_FROM(
823 IArchiveOpenVolumeCallback,
824 openVolumeCallback, openArchiveCallback)
825
826 if (openVolumeCallback)
827 {
828 CMyComPtr<IInStream> nextStream;
829 HRESULT res = openVolumeCallback->GetStream(name, &nextStream);
830
831 if (res == S_FALSE)
832 {
833 if (useRelative && Dyn.ParentName != Dyn.RelativeParentNameFromLocator)
834 {
835 res = openVolumeCallback->GetStream(Dyn.ParentName, &nextStream);
836 if (res == S_OK)
837 Dyn.RelativeNameWasUsed = false;
838 }
839 }
840
841 if (res != S_OK && res != S_FALSE)
842 return res;
843
844 if (res == S_FALSE || !nextStream)
845 {
846 AddErrorMessage("Missing volume : ", name);
847 return S_OK;
848 }
849
850 Parent = new CHandler;
851 ParentStream = Parent;
852
853 res = Parent->Open2(nextStream, this, openArchiveCallback, level + 1);
854
855 if (res != S_OK)
856 {
857 Parent = NULL;
858 ParentStream.Release();
859 if (res == E_ABORT)
860 return res;
861 if (res != S_FALSE)
862 {
863 // we must show that error code
864 }
865 }
866 if (res == S_OK)
867 {
868 NumLevels = Parent->NumLevels + 1;
869 }
870 }
871 {
872 const CHandler *p = this;
873 while (p->NeedParent())
874 {
875 p = p->Parent;
876 if (!p)
877 {
878 AddErrorMessage("Can't open parent VHD file : ", Dyn.ParentName);
879 break;
880 }
881 }
882 }
883 return S_OK;
884 }
885
886
887 void CHandler::CloseAtError()
888 {
889 // CHandlerImg:
890 Stream.Release();
891 Clear_HandlerImg_Vars();
892
893 _phySize = 0;
894 NumLevels = 0;
895 Bat.Clear();
896 NumUsedBlocks = 0;
897 Parent = NULL;
898 ParentStream.Release();
899 Dyn.Clear();
900 _errorMessage.Empty();
901 // _unexpectedEnd = false;
902 }
903
904 Z7_COM7F_IMF(CHandler::Close())
905 {
906 CloseAtError();
907 return S_OK;
908 }
909
910 Z7_COM7F_IMF(CHandler::GetProperty(UInt32 /* index */, PROPID propID, PROPVARIANT *value))
911 {
912 COM_TRY_BEGIN
913 NCOM::CPropVariant prop;
914
915 switch (propID)
916 {
917 case kpidSize: prop = Footer.CurrentSize; break;
918 case kpidPackSize: prop = GetPackSize(); break;
919 case kpidCTime: VhdTimeToFileTime(Footer.CTime, prop); break;
920 case kpidExtension: prop = (_imgExt ? _imgExt : "img"); break;
921
922 /*
923 case kpidNumCyls: prop = Footer.NumCyls(); break;
924 case kpidNumHeads: prop = Footer.NumHeads(); break;
925 case kpidSectorsPerTrack: prop = Footer.NumSectorsPerTrack(); break;
926 */
927 }
928
929 prop.Detach(value);
930 return S_OK;
931 COM_TRY_END
932 }
933
934
935 Z7_COM7F_IMF(CHandler::GetStream(UInt32 /* index */, ISequentialInStream **stream))
936 {
937 COM_TRY_BEGIN
938 *stream = NULL;
939 if (Footer.IsFixed())
940 {
941 CMyComPtr2<ISequentialInStream, CLimitedInStream> streamSpec;
942 streamSpec.Create_if_Empty();
943 streamSpec->SetStream(Stream);
944 // fixme : check (startOffset = 0)
945 streamSpec->InitAndSeek(_startOffset, Footer.CurrentSize);
946 RINOK(streamSpec->SeekToStart())
947 *stream = streamSpec.Detach();
948 return S_OK;
949 }
950 if (!Footer.ThereIsDynamic() || !AreParentsOK())
951 return S_FALSE;
952 CMyComPtr<ISequentialInStream> streamTemp = this;
953 RINOK(InitAndSeek())
954 *stream = streamTemp.Detach();
955 return S_OK;
956 COM_TRY_END
957 }
958
959 REGISTER_ARC_I(
960 "VHD", "vhd", NULL, 0xDC,
961 kSignature,
962 0,
963 NArcInfoFlags::kUseGlobalOffset,
964 NULL)
965
966 }}
967