Imported Upstream version 9.20
[platform/upstream/7zip.git] / CPP / 7zip / Archive / VhdHandler.cpp
1 // VhdHandler.cpp\r
2 \r
3 #include "StdAfx.h"\r
4 \r
5 #include "../../../C/CpuArch.h"\r
6 \r
7 #include "Common/Buffer.h"\r
8 #include "Common/ComTry.h"\r
9 #include "Common/IntToString.h"\r
10 #include "Common/MyString.h"\r
11 \r
12 #include "Windows/PropVariant.h"\r
13 \r
14 #include "../Common/LimitedStreams.h"\r
15 #include "../Common/ProgressUtils.h"\r
16 #include "../Common/RegisterArc.h"\r
17 #include "../Common/StreamUtils.h"\r
18 \r
19 #include "../Compress/CopyCoder.h"\r
20 \r
21 #define Get16(p) GetBe16(p)\r
22 #define Get32(p) GetBe32(p)\r
23 #define Get64(p) GetBe64(p)\r
24 \r
25 #define G32(p, dest) dest = Get32(p);\r
26 #define G64(p, dest) dest = Get64(p);\r
27 \r
28 using namespace NWindows;\r
29 \r
30 namespace NArchive {\r
31 namespace NVhd {\r
32 \r
33 static const UInt32 kUnusedBlock = 0xFFFFFFFF;\r
34 \r
35 static const UInt32 kDiskType_Fixed = 2;\r
36 static const UInt32 kDiskType_Dynamic = 3;\r
37 static const UInt32 kDiskType_Diff = 4;\r
38 \r
39 static const char *kDiskTypes[] =\r
40 {\r
41   "0",\r
42   "1",\r
43   "Fixed",\r
44   "Dynamic",\r
45   "Differencing"\r
46 };\r
47 \r
48 struct CFooter\r
49 {\r
50   // UInt32 Features;\r
51   // UInt32 FormatVersion;\r
52   UInt64 DataOffset;\r
53   UInt32 CTime;\r
54   UInt32 CreatorApp;\r
55   UInt32 CreatorVersion;\r
56   UInt32 CreatorHostOS;\r
57   // UInt64 OriginalSize;\r
58   UInt64 CurrentSize;\r
59   UInt32 DiskGeometry;\r
60   UInt32 Type;\r
61   Byte Id[16];\r
62   Byte SavedState;\r
63 \r
64   bool IsFixed() const { return Type == kDiskType_Fixed; }\r
65   bool ThereIsDynamic() const { return Type == kDiskType_Dynamic || Type == kDiskType_Diff; }\r
66   // bool IsSupported() const { return Type == kDiskType_Fixed || Type == kDiskType_Dynamic || Type == kDiskType_Diff; }\r
67   UInt32 NumCyls() const { return DiskGeometry >> 16; }\r
68   UInt32 NumHeads() const { return (DiskGeometry >> 8) & 0xFF; }\r
69   UInt32 NumSectorsPerTrack() const { return DiskGeometry & 0xFF; }\r
70   AString GetTypeString() const;\r
71   bool Parse(const Byte *p);\r
72 };\r
73 \r
74 AString CFooter::GetTypeString() const\r
75 {\r
76   if (Type < sizeof(kDiskTypes) / sizeof(kDiskTypes[0]))\r
77     return kDiskTypes[Type];\r
78   char s[16];\r
79   ConvertUInt32ToString(Type, s);\r
80   return s;\r
81 }\r
82 \r
83 static bool CheckBlock(const Byte *p, unsigned size, unsigned checkSumOffset, unsigned zeroOffset)\r
84 {\r
85   UInt32 sum = 0;\r
86   unsigned i;\r
87   for (i = 0; i < checkSumOffset; i++)\r
88     sum += p[i];\r
89   for (i = checkSumOffset + 4; i < size; i++)\r
90     sum += p[i];\r
91   if (~sum != Get32(p + checkSumOffset))\r
92     return false;\r
93   for (i = zeroOffset; i < size; i++)\r
94     if (p[i] != 0)\r
95       return false;\r
96   return true;\r
97 }\r
98 \r
99 bool CFooter::Parse(const Byte *p)\r
100 {\r
101   if (memcmp(p, "conectix", 8) != 0)\r
102     return false;\r
103   // G32(p + 0x08, Features);\r
104   // G32(p + 0x0C, FormatVersion);\r
105   G64(p + 0x10, DataOffset);\r
106   G32(p + 0x18, CTime);\r
107   G32(p + 0x1C, CreatorApp);\r
108   G32(p + 0x20, CreatorVersion);\r
109   G32(p + 0x24, CreatorHostOS);\r
110   // G64(p + 0x28, OriginalSize);\r
111   G64(p + 0x30, CurrentSize);\r
112   G32(p + 0x38, DiskGeometry);\r
113   G32(p + 0x3C, Type);\r
114   memcpy(Id, p + 0x44, 16);\r
115   SavedState = p[0x54];\r
116   return CheckBlock(p, 512, 0x40, 0x55);\r
117 }\r
118 \r
119 /*\r
120 struct CParentLocatorEntry\r
121 {\r
122   UInt32 Code;\r
123   UInt32 DataSpace;\r
124   UInt32 DataLen;\r
125   UInt64 DataOffset;\r
126 \r
127   bool Parse(const Byte *p);\r
128 };\r
129 bool CParentLocatorEntry::Parse(const Byte *p)\r
130 {\r
131   G32(p + 0x00, Code);\r
132   G32(p + 0x04, DataSpace);\r
133   G32(p + 0x08, DataLen);\r
134   G32(p + 0x10, DataOffset);\r
135   return (Get32(p + 0x0C) == 0); // Resrved\r
136 }\r
137 */\r
138 \r
139 struct CDynHeader\r
140 {\r
141   // UInt64 DataOffset;\r
142   UInt64 TableOffset;\r
143   // UInt32 HeaderVersion;\r
144   UInt32 NumBlocks;\r
145   int BlockSizeLog;\r
146   UInt32 ParentTime;\r
147   Byte ParentId[16];\r
148   UString ParentName;\r
149   // CParentLocatorEntry ParentLocators[8];\r
150 \r
151   bool Parse(const Byte *p);\r
152   UInt32 NumBitMapSectors() const\r
153   {\r
154     UInt32 numSectorsInBlock = (1 << (BlockSizeLog - 9));\r
155     return (numSectorsInBlock + 512 * 8 - 1) / (512 * 8);\r
156   }\r
157 };\r
158 \r
159 static int GetLog(UInt32 num)\r
160 {\r
161   for (int i = 0; i < 31; i++)\r
162     if (((UInt32)1 << i) == num)\r
163       return i;\r
164   return -1;\r
165 }\r
166 \r
167 bool CDynHeader::Parse(const Byte *p)\r
168 {\r
169   if (memcmp(p, "cxsparse", 8) != 0)\r
170     return false;\r
171   // G64(p + 0x08, DataOffset);\r
172   G64(p + 0x10, TableOffset);\r
173   // G32(p + 0x18, HeaderVersion);\r
174   G32(p + 0x1C, NumBlocks);\r
175   BlockSizeLog = GetLog(Get32(p + 0x20));\r
176   if (BlockSizeLog < 9 || BlockSizeLog > 30)\r
177     return false;\r
178   G32(p + 0x38, ParentTime);\r
179   if (Get32(p + 0x3C) != 0) // reserved\r
180     return false;\r
181   memcpy(ParentId, p + 0x28, 16);\r
182   {\r
183     const int kNameLength = 256;\r
184     wchar_t *s = ParentName.GetBuffer(kNameLength);\r
185     for (unsigned i = 0; i < kNameLength; i++)\r
186       s[i] = Get16(p + 0x40 + i * 2);\r
187     s[kNameLength] = 0;\r
188     ParentName.ReleaseBuffer();\r
189   }\r
190   /*\r
191   for (int i = 0; i < 8; i++)\r
192     if (!ParentLocators[i].Parse(p + 0x240 + i * 24))\r
193       return false;\r
194   */\r
195   return CheckBlock(p, 1024, 0x24, 0x240 + 8 * 24);\r
196 }\r
197 \r
198 class CHandler:\r
199   public IInStream,\r
200   public IInArchive,\r
201   public IInArchiveGetStream,\r
202   public CMyUnknownImp\r
203 {\r
204   UInt64 _virtPos;\r
205   UInt64 _phyPos;\r
206   UInt64 _phyLimit;\r
207 \r
208   CFooter Footer;\r
209   CDynHeader Dyn;\r
210   CRecordVector<UInt32> Bat;\r
211   CByteBuffer BitMap;\r
212   UInt32 BitMapTag;\r
213   UInt32 NumUsedBlocks;\r
214   CMyComPtr<IInStream> Stream;\r
215   CMyComPtr<IInStream> ParentStream;\r
216   CHandler *Parent;\r
217 \r
218   HRESULT Seek(UInt64 offset);\r
219   HRESULT InitAndSeek();\r
220   HRESULT ReadPhy(UInt64 offset, void *data, UInt32 size);\r
221 \r
222   bool NeedParent() const { return Footer.Type == kDiskType_Diff; }\r
223   UInt64 GetPackSize() const\r
224     { return Footer.ThereIsDynamic() ? ((UInt64)NumUsedBlocks << Dyn.BlockSizeLog) : Footer.CurrentSize; }\r
225 \r
226   UString GetParentName() const\r
227   {\r
228     const CHandler *p = this;\r
229     UString res;\r
230     while (p && p->NeedParent())\r
231     {\r
232       if (!res.IsEmpty())\r
233         res += L" -> ";\r
234       res += p->Dyn.ParentName;\r
235       p = p->Parent;\r
236     }\r
237     return res;\r
238   }\r
239 \r
240   bool IsOK() const\r
241   {\r
242     const CHandler *p = this;\r
243     while (p->NeedParent())\r
244     {\r
245       p = p->Parent;\r
246       if (p == 0)\r
247         return false;\r
248     }\r
249     return true;\r
250   }\r
251 \r
252   HRESULT Open3();\r
253   HRESULT Open2(IInStream *stream, CHandler *child, IArchiveOpenCallback *openArchiveCallback, int level);\r
254 \r
255 public:\r
256   MY_UNKNOWN_IMP3(IInArchive, IInArchiveGetStream, IInStream)\r
257 \r
258   INTERFACE_IInArchive(;)\r
259   STDMETHOD(GetStream)(UInt32 index, ISequentialInStream **stream);\r
260   STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize);\r
261   STDMETHOD(Seek)(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition);\r
262 };\r
263 \r
264 HRESULT CHandler::Seek(UInt64 offset) { return Stream->Seek(offset, STREAM_SEEK_SET, NULL); }\r
265 \r
266 HRESULT CHandler::InitAndSeek()\r
267 {\r
268   if (ParentStream)\r
269   {\r
270     RINOK(Parent->InitAndSeek());\r
271   }\r
272   _virtPos = _phyPos = 0;\r
273   BitMapTag = kUnusedBlock;\r
274   BitMap.SetCapacity(Dyn.NumBitMapSectors() << 9);\r
275   return Seek(0);\r
276 }\r
277 \r
278 HRESULT CHandler::ReadPhy(UInt64 offset, void *data, UInt32 size)\r
279 {\r
280   if (offset + size > _phyLimit)\r
281     return S_FALSE;\r
282   if (offset != _phyPos)\r
283   {\r
284     _phyPos = offset;\r
285     RINOK(Seek(offset));\r
286   }\r
287   HRESULT res = ReadStream_FALSE(Stream, data, size);\r
288   _phyPos += size;\r
289   return res;\r
290 }\r
291 \r
292 HRESULT CHandler::Open3()\r
293 {\r
294   RINOK(Stream->Seek(0, STREAM_SEEK_END, &_phyPos));\r
295   if (_phyPos < 512)\r
296     return S_FALSE;\r
297   const UInt32 kDynSize = 1024;\r
298   Byte buf[kDynSize];\r
299 \r
300   _phyLimit = _phyPos;\r
301   RINOK(ReadPhy(_phyLimit - 512, buf, 512));\r
302   if (!Footer.Parse(buf))\r
303     return S_FALSE;\r
304   _phyLimit -= 512;\r
305 \r
306   if (!Footer.ThereIsDynamic())\r
307     return S_OK;\r
308 \r
309   RINOK(ReadPhy(0, buf + 512, 512));\r
310   if (memcmp(buf, buf + 512, 512) != 0)\r
311     return S_FALSE;\r
312 \r
313   RINOK(ReadPhy(Footer.DataOffset, buf, kDynSize));\r
314   if (!Dyn.Parse(buf))\r
315     return S_FALSE;\r
316   \r
317   if (Dyn.NumBlocks >= (UInt32)1 << 31)\r
318     return S_FALSE;\r
319   if (Footer.CurrentSize == 0)\r
320   {\r
321     if (Dyn.NumBlocks != 0)\r
322       return S_FALSE;\r
323   }\r
324   else if (((Footer.CurrentSize - 1) >> Dyn.BlockSizeLog) + 1 != Dyn.NumBlocks)\r
325     return S_FALSE;\r
326 \r
327   Bat.Reserve(Dyn.NumBlocks);\r
328   while ((UInt32)Bat.Size() < Dyn.NumBlocks)\r
329   {\r
330     RINOK(ReadPhy(Dyn.TableOffset + (UInt64)Bat.Size() * 4, buf, 512));\r
331     for (UInt32 j = 0; j < 512; j += 4)\r
332     {\r
333       UInt32 v = Get32(buf + j);\r
334       if (v != kUnusedBlock)\r
335         NumUsedBlocks++;\r
336       Bat.Add(v);\r
337       if ((UInt32)Bat.Size() >= Dyn.NumBlocks)\r
338         break;\r
339     }\r
340   }\r
341   return S_OK;\r
342 }\r
343 \r
344 STDMETHODIMP CHandler::Read(void *data, UInt32 size, UInt32 *processedSize)\r
345 {\r
346   if (processedSize != NULL)\r
347     *processedSize = 0;\r
348   if (_virtPos >= Footer.CurrentSize)\r
349     return (Footer.CurrentSize == _virtPos) ? S_OK: E_FAIL;\r
350   UInt64 rem = Footer.CurrentSize - _virtPos;\r
351   if (size > rem)\r
352     size = (UInt32)rem;\r
353   if (size == 0)\r
354     return S_OK;\r
355   UInt32 blockIndex = (UInt32)(_virtPos >> Dyn.BlockSizeLog);\r
356   UInt32 blockSectIndex = Bat[blockIndex];\r
357   UInt32 blockSize = (UInt32)1 << Dyn.BlockSizeLog;\r
358   UInt32 offsetInBlock = (UInt32)_virtPos & (blockSize - 1);\r
359   size = MyMin(blockSize - offsetInBlock, size);\r
360 \r
361   HRESULT res = S_OK;\r
362   if (blockSectIndex == kUnusedBlock)\r
363   {\r
364     if (ParentStream)\r
365     {\r
366       RINOK(ParentStream->Seek(_virtPos, STREAM_SEEK_SET, NULL));\r
367       res = ParentStream->Read(data, size, &size);\r
368     }\r
369     else\r
370       memset(data, 0, size);\r
371   }\r
372   else\r
373   {\r
374     UInt64 newPos = (UInt64)blockSectIndex << 9;\r
375     if (BitMapTag != blockIndex)\r
376     {\r
377       RINOK(ReadPhy(newPos, BitMap, (UInt32)BitMap.GetCapacity()));\r
378       BitMapTag = blockIndex;\r
379     }\r
380     RINOK(ReadPhy(newPos + BitMap.GetCapacity() + offsetInBlock, data, size));\r
381     for (UInt32 cur = 0; cur < size;)\r
382     {\r
383       UInt32 rem = MyMin(0x200 - (offsetInBlock & 0x1FF), size - cur);\r
384       UInt32 bmi = offsetInBlock >> 9;\r
385       if (((BitMap[bmi >> 3] >> (7 - (bmi & 7))) & 1) == 0)\r
386       {\r
387         if (ParentStream)\r
388         {\r
389           RINOK(ParentStream->Seek(_virtPos + cur, STREAM_SEEK_SET, NULL));\r
390           RINOK(ReadStream_FALSE(ParentStream, (Byte *)data + cur, rem));\r
391         }\r
392         else\r
393         {\r
394           const Byte *p = (const Byte *)data + cur;\r
395           for (UInt32 i = 0; i < rem; i++)\r
396             if (p[i] != 0)\r
397               return S_FALSE;\r
398         }\r
399       }\r
400       offsetInBlock += rem;\r
401       cur += rem;\r
402     }\r
403   }\r
404   if (processedSize != NULL)\r
405     *processedSize = size;\r
406   _virtPos += size;\r
407   return res;\r
408 }\r
409 \r
410 STDMETHODIMP CHandler::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition)\r
411 {\r
412   switch(seekOrigin)\r
413   {\r
414     case STREAM_SEEK_SET: _virtPos = offset; break;\r
415     case STREAM_SEEK_CUR: _virtPos += offset; break;\r
416     case STREAM_SEEK_END: _virtPos = Footer.CurrentSize + offset; break;\r
417     default: return STG_E_INVALIDFUNCTION;\r
418   }\r
419   if (newPosition)\r
420     *newPosition = _virtPos;\r
421   return S_OK;\r
422 }\r
423 \r
424 enum\r
425 {\r
426   kpidParent = kpidUserDefined,\r
427   kpidSavedState\r
428 };\r
429 \r
430 STATPROPSTG kArcProps[] =\r
431 {\r
432   { NULL, kpidSize, VT_UI8},\r
433   { NULL, kpidCTime, VT_FILETIME},\r
434   { NULL, kpidClusterSize, VT_UI8},\r
435   { NULL, kpidMethod, VT_BSTR},\r
436   { L"Parent", kpidParent, VT_BSTR},\r
437   { NULL, kpidCreatorApp, VT_BSTR},\r
438   { NULL, kpidHostOS, VT_BSTR},\r
439   { L"Saved State", kpidSavedState, VT_BOOL},\r
440   { NULL, kpidId, VT_BSTR}\r
441  };\r
442 \r
443 STATPROPSTG kProps[] =\r
444 {\r
445   { NULL, kpidSize, VT_UI8},\r
446   { NULL, kpidPackSize, VT_UI8},\r
447   { NULL, kpidCTime, VT_FILETIME}\r
448   \r
449   /*\r
450   { NULL, kpidNumCyls, VT_UI4},\r
451   { NULL, kpidNumHeads, VT_UI4},\r
452   { NULL, kpidSectorsPerTrack, VT_UI4}\r
453   */\r
454 };\r
455 \r
456 IMP_IInArchive_Props\r
457 IMP_IInArchive_ArcProps_WITH_NAME\r
458 \r
459 // VHD start time: 2000-01-01\r
460 static const UInt64 kVhdTimeStartValue = (UInt64)3600 * 24 * (399 * 365 + 24 * 4);\r
461 \r
462 static void VhdTimeToFileTime(UInt32 vhdTime, NCOM::CPropVariant &prop)\r
463 {\r
464   FILETIME ft, utc;\r
465   UInt64 v = (kVhdTimeStartValue + vhdTime) * 10000000;\r
466   ft.dwLowDateTime = (DWORD)v;\r
467   ft.dwHighDateTime = (DWORD)(v >> 32);\r
468   // specification says that it's UTC time, but Virtual PC 6 writes local time. Why?\r
469   LocalFileTimeToFileTime(&ft, &utc);\r
470   prop = utc;\r
471 }\r
472 \r
473 static void StringToAString(char *dest, UInt32 s)\r
474 {\r
475   for (int i = 24; i >= 0; i -= 8)\r
476   {\r
477     Byte b = (Byte)((s >> i) & 0xFF);\r
478     if (b < 0x20 || b > 0x7F)\r
479       break;\r
480     *dest++ = b;\r
481   }\r
482   *dest = 0;\r
483 }\r
484 \r
485 static void ConvertByteToHex(unsigned value, char *s)\r
486 {\r
487   for (int i = 0; i < 2; i++)\r
488   {\r
489     unsigned t = value & 0xF;\r
490     value >>= 4;\r
491     s[1 - i] = (char)((t < 10) ? ('0' + t) : ('A' + (t - 10)));\r
492   }\r
493 }\r
494 \r
495 STDMETHODIMP CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)\r
496 {\r
497   COM_TRY_BEGIN\r
498   NCOM::CPropVariant prop;\r
499   switch(propID)\r
500   {\r
501     case kpidMainSubfile: prop = (UInt32)0; break;\r
502     case kpidCTime: VhdTimeToFileTime(Footer.CTime, prop); break;\r
503     case kpidClusterSize: if (Footer.ThereIsDynamic()) prop = (UInt32)1 << Dyn.BlockSizeLog; break;\r
504     case kpidMethod:\r
505     {\r
506       AString s = Footer.GetTypeString();\r
507       if (NeedParent())\r
508       {\r
509         s += " -> ";\r
510         const CHandler *p = this;\r
511         while (p != 0 && p->NeedParent())\r
512           p = p->Parent;\r
513         if (p == 0)\r
514           s += '?';\r
515         else\r
516           s += p->Footer.GetTypeString();\r
517       }\r
518       prop = s;\r
519       break;\r
520     }\r
521     case kpidCreatorApp:\r
522     {\r
523       char s[16];\r
524       StringToAString(s, Footer.CreatorApp);\r
525       AString res = s;\r
526       res.Trim();\r
527       ConvertUInt32ToString(Footer.CreatorVersion >> 16, s);\r
528       res += ' ';\r
529       res += s;\r
530       res += '.';\r
531       ConvertUInt32ToString(Footer.CreatorVersion & 0xFFFF, s);\r
532       res += s;\r
533       prop = res;\r
534       break;\r
535     }\r
536     case kpidHostOS:\r
537     {\r
538       if (Footer.CreatorHostOS == 0x5769326b)\r
539         prop = "Windows";\r
540       else\r
541       {\r
542         char s[16];\r
543         StringToAString(s, Footer.CreatorHostOS);\r
544         prop = s;\r
545       }\r
546       break;\r
547     }\r
548     case kpidId:\r
549     {\r
550       char s[32 + 4];\r
551       for (int i = 0; i < 16; i++)\r
552         ConvertByteToHex(Footer.Id[i], s + i * 2);\r
553       s[32] = 0;\r
554       prop = s;\r
555       break;\r
556     }\r
557     case kpidSavedState: prop = Footer.SavedState ? true : false; break;\r
558     case kpidParent: if (NeedParent()) prop = GetParentName(); break;\r
559   }\r
560   prop.Detach(value);\r
561   return S_OK;\r
562   COM_TRY_END\r
563 }\r
564 \r
565 HRESULT CHandler::Open2(IInStream *stream, CHandler *child, IArchiveOpenCallback *openArchiveCallback, int level)\r
566 {\r
567   Close();\r
568   Stream = stream;\r
569   if (level > 32)\r
570     return S_FALSE;\r
571   RINOK(Open3());\r
572   if (child && memcmp(child->Dyn.ParentId, Footer.Id, 16) != 0)\r
573     return S_FALSE;\r
574   if (Footer.Type != kDiskType_Diff)\r
575     return S_OK;\r
576   CMyComPtr<IArchiveOpenVolumeCallback> openVolumeCallback;\r
577   if (openArchiveCallback->QueryInterface(IID_IArchiveOpenVolumeCallback, (void **)&openVolumeCallback) != S_OK)\r
578     return S_FALSE;\r
579   CMyComPtr<IInStream> nextStream;\r
580   HRESULT res = openVolumeCallback->GetStream(Dyn.ParentName, &nextStream);\r
581   if (res == S_FALSE)\r
582     return S_OK;\r
583   RINOK(res);\r
584 \r
585   Parent = new CHandler;\r
586   ParentStream = Parent;\r
587   return Parent->Open2(nextStream, this, openArchiveCallback, level + 1);\r
588 }\r
589 \r
590 STDMETHODIMP CHandler::Open(IInStream *stream,\r
591     const UInt64 * /* maxCheckStartPosition */,\r
592     IArchiveOpenCallback * openArchiveCallback)\r
593 {\r
594   COM_TRY_BEGIN\r
595   {\r
596     HRESULT res;\r
597     try\r
598     {\r
599       res = Open2(stream, NULL, openArchiveCallback, 0);\r
600       if (res == S_OK)\r
601         return S_OK;\r
602     }\r
603     catch(...)\r
604     {\r
605       Close();\r
606       throw;\r
607     }\r
608     Close();\r
609     return res;\r
610   }\r
611   COM_TRY_END\r
612 }\r
613 \r
614 STDMETHODIMP CHandler::Close()\r
615 {\r
616   Bat.Clear();\r
617   NumUsedBlocks = 0;\r
618   Parent = 0;\r
619   Stream.Release();\r
620   ParentStream.Release();\r
621   return S_OK;\r
622 }\r
623 \r
624 STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems)\r
625 {\r
626   *numItems = 1;\r
627   return S_OK;\r
628 }\r
629 \r
630 STDMETHODIMP CHandler::GetProperty(UInt32 /* index */, PROPID propID, PROPVARIANT *value)\r
631 {\r
632   COM_TRY_BEGIN\r
633   NWindows::NCOM::CPropVariant prop;\r
634 \r
635   switch(propID)\r
636   {\r
637     case kpidSize: prop = Footer.CurrentSize; break;\r
638     case kpidPackSize: prop = GetPackSize(); break;\r
639     case kpidCTime: VhdTimeToFileTime(Footer.CTime, prop); break;\r
640     /*\r
641     case kpidNumCyls: prop = Footer.NumCyls(); break;\r
642     case kpidNumHeads: prop = Footer.NumHeads(); break;\r
643     case kpidSectorsPerTrack: prop = Footer.NumSectorsPerTrack(); break;\r
644     */\r
645   }\r
646   prop.Detach(value);\r
647   return S_OK;\r
648   COM_TRY_END\r
649 }\r
650 \r
651 STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,\r
652     Int32 testMode, IArchiveExtractCallback *extractCallback)\r
653 {\r
654   COM_TRY_BEGIN\r
655   if (numItems == 0)\r
656     return S_OK;\r
657   if (numItems != (UInt32)-1 && (numItems != 1 || indices[0] != 0))\r
658     return E_INVALIDARG;\r
659 \r
660   RINOK(extractCallback->SetTotal(Footer.CurrentSize));\r
661   CMyComPtr<ISequentialOutStream> outStream;\r
662   Int32 askMode = testMode ?\r
663       NExtract::NAskMode::kTest :\r
664       NExtract::NAskMode::kExtract;\r
665   RINOK(extractCallback->GetStream(0, &outStream, askMode));\r
666   if (!testMode && !outStream)\r
667     return S_OK;\r
668   RINOK(extractCallback->PrepareOperation(askMode));\r
669 \r
670   NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder();\r
671   CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;\r
672 \r
673   CLocalProgress *lps = new CLocalProgress;\r
674   CMyComPtr<ICompressProgressInfo> progress = lps;\r
675   lps->Init(extractCallback, false);\r
676 \r
677   int res = NExtract::NOperationResult::kDataError;\r
678   CMyComPtr<ISequentialInStream> inStream;\r
679   HRESULT hres = GetStream(0, &inStream);\r
680   if (hres == S_FALSE)\r
681     res = NExtract::NOperationResult::kUnSupportedMethod;\r
682   else\r
683   {\r
684     RINOK(hres);\r
685     HRESULT hres = copyCoder->Code(inStream, outStream, NULL, NULL, progress);\r
686     if (hres == S_OK)\r
687     {\r
688       if (copyCoderSpec->TotalSize == Footer.CurrentSize)\r
689         res = NExtract::NOperationResult::kOK;\r
690     }\r
691     else\r
692     {\r
693       if (hres != S_FALSE)\r
694       {\r
695         RINOK(hres);\r
696       }\r
697     }\r
698   }\r
699   outStream.Release();\r
700   return extractCallback->SetOperationResult(res);\r
701   COM_TRY_END\r
702 }\r
703 \r
704 STDMETHODIMP CHandler::GetStream(UInt32 /* index */, ISequentialInStream **stream)\r
705 {\r
706   COM_TRY_BEGIN\r
707   *stream = 0;\r
708   if (Footer.IsFixed())\r
709   {\r
710     CLimitedInStream *streamSpec = new CLimitedInStream;\r
711     CMyComPtr<ISequentialInStream> streamTemp = streamSpec;\r
712     streamSpec->SetStream(Stream);\r
713     streamSpec->InitAndSeek(0, Footer.CurrentSize);\r
714     RINOK(streamSpec->SeekToStart());\r
715     *stream = streamTemp.Detach();\r
716     return S_OK;\r
717   }\r
718   if (!Footer.ThereIsDynamic() || !IsOK())\r
719     return S_FALSE;\r
720   CMyComPtr<ISequentialInStream> streamTemp = this;\r
721   RINOK(InitAndSeek());\r
722   *stream = streamTemp.Detach();\r
723   return S_OK;\r
724   COM_TRY_END\r
725 }\r
726 \r
727 static IInArchive *CreateArc() { return new CHandler; }\r
728 \r
729 static CArcInfo g_ArcInfo =\r
730   { L"VHD", L"vhd", L".mbr", 0xDC, { 'c', 'o', 'n', 'e', 'c', 't', 'i', 'x', 0, 0 }, 10, false, CreateArc, 0 };\r
731 \r
732 REGISTER_ARC(Vhd)\r
733 \r
734 }}\r