xref: /aosp_15_r20/external/lzma/CPP/7zip/Archive/QcowHandler.cpp (revision f6dc9357d832569d4d1f5d24eacdb3935a1ae8e6)
1 // QcowHandler.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/IntToString.h"
11 #include "../../Common/MyBuffer2.h"
12 
13 #include "../../Windows/PropVariant.h"
14 #include "../../Windows/PropVariantUtils.h"
15 
16 #include "../Common/RegisterArc.h"
17 #include "../Common/StreamObjects.h"
18 #include "../Common/StreamUtils.h"
19 
20 #include "../Compress/DeflateDecoder.h"
21 
22 #include "HandlerCont.h"
23 
24 #define Get32(p) GetBe32a(p)
25 #define Get64(p) GetBe64a(p)
26 
27 using namespace NWindows;
28 
29 namespace NArchive {
30 namespace NQcow {
31 
32 static const Byte k_Signature[] =  { 'Q', 'F', 'I', 0xFB, 0, 0, 0 };
33 
34 /*
35 VA to PA maps:
36   high bits (L1) :              : index in L1 (_dir) : _dir[high_index] points to Table.
37   mid bits  (L2) : _numMidBits  : index in Table, Table[index] points to cluster start offset in arc file.
38   low bits       : _clusterBits : offset inside cluster.
39 */
40 
41 Z7_class_CHandler_final: public CHandlerImg
42 {
43   Z7_IFACE_COM7_IMP(IInArchive_Img)
44   Z7_IFACE_COM7_IMP(IInArchiveGetStream)
45   Z7_IFACE_COM7_IMP(ISequentialInStream)
46 
47   unsigned _clusterBits;
48   unsigned _numMidBits;
49   UInt64 _compressedFlag;
50 
51   CObjArray2<UInt32> _dir;
52   CAlignedBuffer _table;
53   CByteBuffer _cache;
54   CByteBuffer _cacheCompressed;
55   UInt64 _cacheCluster;
56 
57   UInt64 _comprPos;
58   size_t _comprSize;
59 
60   bool _needCompression;
61   bool _isArc;
62   bool _unsupported;
63   Byte _compressionType;
64 
65   UInt64 _phySize;
66 
67   CMyComPtr2<ISequentialInStream, CBufInStream> _bufInStream;
68   CMyComPtr2<ISequentialOutStream, CBufPtrSeqOutStream> _bufOutStream;
69   CMyComPtr2<ICompressCoder, NCompress::NDeflate::NDecoder::CCOMCoder> _deflateDecoder;
70 
71   UInt32 _version;
72   UInt32 _cryptMethod;
73   UInt64 _incompatFlags;
74 
75   HRESULT Seek2(UInt64 offset)
76   {
77     _posInArc = offset;
78     return InStream_SeekSet(Stream, offset);
79   }
80 
81   HRESULT InitAndSeek()
82   {
83     _virtPos = 0;
84     return Seek2(0);
85   }
86 
87   HRESULT Open2(IInStream *stream, IArchiveOpenCallback *openCallback) Z7_override;
88 };
89 
90 
91 static const UInt32 kEmptyDirItem = (UInt32)0 - 1;
92 
93 Z7_COM7F_IMF(CHandler::Read(void *data, UInt32 size, UInt32 *processedSize))
94 {
95   if (processedSize)
96     *processedSize = 0;
97   // printf("\nRead _virtPos = %6d  size = %6d\n", (UInt32)_virtPos, size);
98   if (_virtPos >= _size)
99     return S_OK;
100   {
101     const UInt64 rem = _size - _virtPos;
102     if (size > rem)
103       size = (UInt32)rem;
104     if (size == 0)
105       return S_OK;
106   }
107 
108   for (;;)
109   {
110     const UInt64 cluster = _virtPos >> _clusterBits;
111     const size_t clusterSize = (size_t)1 << _clusterBits;
112     const size_t lowBits = (size_t)_virtPos & (clusterSize - 1);
113     {
114       const size_t rem = clusterSize - lowBits;
115       if (size > rem)
116         size = (UInt32)rem;
117     }
118     if (cluster == _cacheCluster)
119     {
120       memcpy(data, _cache + lowBits, size);
121       break;
122     }
123 
124     const UInt64 high = cluster >> _numMidBits;
125 
126     if (high < _dir.Size())
127     {
128       const UInt32 tabl = _dir[(size_t)high];
129       if (tabl != kEmptyDirItem)
130       {
131         const size_t midBits = (size_t)cluster & (((size_t)1 << _numMidBits) - 1);
132         const Byte *p = _table + ((((size_t)tabl << _numMidBits) + midBits) << 3);
133         UInt64 v = Get64(p);
134 
135         if (v)
136         {
137           if (v & _compressedFlag)
138           {
139             if (_version <= 1)
140               return E_FAIL;
141             /*
142             the example of table record for 12-bit clusters (4KB uncompressed):
143               2 bits : isCompressed status
144               (4 == _clusterBits - 8) bits : (num_sectors - 1)
145                   packSize = num_sectors * 512;
146                   it uses one additional bit over unpacked cluster_bits.
147               (49 == 61 - _clusterBits) bits : offset of 512-byte sector
148               9 bits : offset in 512-byte sector
149             */
150             const unsigned numOffsetBits = 62 - (_clusterBits - 8);
151             const UInt64 offset = v & (((UInt64)1 << 62) - 1);
152             const size_t dataSize = ((size_t)(offset >> numOffsetBits) + 1) << 9;
153             UInt64 sectorOffset = offset & (((UInt64)1 << numOffsetBits) - (1 << 9));
154             const UInt64 offset2inCache = sectorOffset - _comprPos;
155 
156             // _comprPos is aligned for 512-bytes
157             // we try to use previous _cacheCompressed that contains compressed data
158             // that was read for previous unpacking
159 
160             if (sectorOffset >= _comprPos && offset2inCache < _comprSize)
161             {
162               if (offset2inCache)
163               {
164                 _comprSize -= (size_t)offset2inCache;
165                 memmove(_cacheCompressed, _cacheCompressed + (size_t)offset2inCache, _comprSize);
166                 _comprPos = sectorOffset;
167               }
168               sectorOffset += _comprSize;
169             }
170             else
171             {
172               _comprPos = sectorOffset;
173               _comprSize = 0;
174             }
175 
176             if (dataSize > _comprSize)
177             {
178               if (sectorOffset != _posInArc)
179               {
180                 // printf("\nDeflate-Seek %12I64x %12I64x\n", sectorOffset, sectorOffset - _posInArc);
181                 RINOK(Seek2(sectorOffset))
182               }
183               if (_cacheCompressed.Size() < dataSize)
184                 return E_FAIL;
185               const size_t dataSize3 = dataSize - _comprSize;
186               size_t dataSize2 = dataSize3;
187               // printf("\n\n=======\nReadStream = %6d _comprPos = %6d \n", (UInt32)dataSize2, (UInt32)_comprPos);
188               const HRESULT hres = ReadStream(Stream, _cacheCompressed + _comprSize, &dataSize2);
189               _posInArc += dataSize2;
190               RINOK(hres)
191               if (dataSize2 != dataSize3)
192                 return E_FAIL;
193               _comprSize += dataSize2;
194             }
195 
196             const size_t kSectorMask = (1 << 9) - 1;
197             const size_t offsetInSector = (size_t)offset & kSectorMask;
198             _bufInStream->Init(_cacheCompressed + offsetInSector, dataSize - offsetInSector);
199             _cacheCluster = (UInt64)(Int64)-1;
200             if (_cache.Size() < clusterSize)
201               return E_FAIL;
202             _bufOutStream->Init(_cache, clusterSize);
203             // Do we need to use smaller block than clusterSize for last cluster?
204             const UInt64 blockSize64 = clusterSize;
205             HRESULT res = _deflateDecoder.Interface()->Code(_bufInStream, _bufOutStream, NULL, &blockSize64, NULL);
206             /*
207             if (_bufOutStreamSpec->GetPos() != clusterSize)
208               memset(_cache + _bufOutStreamSpec->GetPos(), 0, clusterSize - _bufOutStreamSpec->GetPos());
209             */
210             if (res == S_OK)
211               if (!_deflateDecoder->IsFinished()
212                   || _bufOutStream->GetPos() != clusterSize)
213                 res = S_FALSE;
214             RINOK(res)
215             _cacheCluster = cluster;
216             continue;
217             /*
218             memcpy(data, _cache + lowBits, size);
219             break;
220             */
221           }
222 
223           // version_3 supports zero clusters
224           if (((UInt32)v & 511) != 1)
225           {
226             v &= _compressedFlag - 1;
227             v += lowBits;
228             if (v != _posInArc)
229             {
230               // printf("\n%12I64x\n", v - _posInArc);
231               RINOK(Seek2(v))
232             }
233             const HRESULT res = Stream->Read(data, size, &size);
234             _posInArc += size;
235             _virtPos += size;
236             if (processedSize)
237               *processedSize = size;
238             return res;
239           }
240         }
241       }
242     }
243 
244     memset(data, 0, size);
245     break;
246   }
247 
248   _virtPos += size;
249   if (processedSize)
250     *processedSize = size;
251   return S_OK;
252 }
253 
254 
255 static const Byte kProps[] =
256 {
257   kpidSize,
258   kpidPackSize
259 };
260 
261 static const Byte kArcProps[] =
262 {
263   kpidClusterSize,
264   kpidSectorSize, // actually we need variable to show table size
265   kpidHeadersSize,
266   kpidUnpackVer,
267   kpidMethod,
268   kpidCharacts
269 };
270 
271 IMP_IInArchive_Props
272 IMP_IInArchive_ArcProps
273 
274 static const CUInt32PCharPair g_IncompatFlags_Characts[] =
275 {
276   {  0, "Dirty" },
277   {  1, "Corrupt" },
278   {  2, "External_Data_File" },
279   {  3, "Compression" },
280   {  4, "Extended_L2" }
281 };
282 
283 Z7_COM7F_IMF(CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value))
284 {
285   COM_TRY_BEGIN
286   NCOM::CPropVariant prop;
287 
288   switch (propID)
289   {
290     case kpidMainSubfile: prop = (UInt32)0; break;
291     case kpidClusterSize: prop = (UInt32)1 << _clusterBits; break;
292     case kpidSectorSize: prop = (UInt32)1 << (_numMidBits + 3); break;
293     case kpidHeadersSize: prop = _table.Size() + (UInt64)_dir.Size() * 8; break;
294     case kpidPhySize: if (_phySize) prop = _phySize; break;
295     case kpidUnpackVer: prop = _version; break;
296     case kpidCharacts:
297     {
298       if (_incompatFlags)
299       {
300         AString s ("incompatible: ");
301         // we need to show also high 32-bits.
302         s += FlagsToString(g_IncompatFlags_Characts,
303             Z7_ARRAY_SIZE(g_IncompatFlags_Characts), (UInt32)_incompatFlags);
304         prop = s;
305       }
306       break;
307     }
308     case kpidMethod:
309     {
310       AString s;
311 
312       if (_compressionType)
313       {
314         if (_compressionType == 1)
315           s += "ZSTD";
316         else
317         {
318           s += "Compression:";
319           s.Add_UInt32(_compressionType);
320         }
321       }
322       else if (_needCompression)
323         s.Add_OptSpaced("Deflate");
324 
325       if (_cryptMethod)
326       {
327         s.Add_Space_if_NotEmpty();
328         if (_cryptMethod == 1)
329           s += "AES";
330         if (_cryptMethod == 2)
331           s += "LUKS";
332         else
333         {
334           s += "Encryption:";
335           s.Add_UInt32(_cryptMethod);
336         }
337       }
338       if (!s.IsEmpty())
339         prop = s;
340       break;
341     }
342 
343     case kpidErrorFlags:
344     {
345       UInt32 v = 0;
346       if (!_isArc) v |= kpv_ErrorFlags_IsNotArc;
347       if (_unsupported) v |= kpv_ErrorFlags_UnsupportedMethod;
348       // if (_headerError) v |= kpv_ErrorFlags_HeadersError;
349       if (!Stream && v == 0)
350         v = kpv_ErrorFlags_HeadersError;
351       if (v)
352         prop = v;
353       break;
354     }
355   }
356 
357   prop.Detach(value);
358   return S_OK;
359   COM_TRY_END
360 }
361 
362 
363 Z7_COM7F_IMF(CHandler::GetProperty(UInt32 /* index */, PROPID propID, PROPVARIANT *value))
364 {
365   COM_TRY_BEGIN
366   NCOM::CPropVariant prop;
367 
368   switch (propID)
369   {
370     case kpidSize: prop = _size; break;
371     case kpidPackSize: prop = _phySize; break;
372     case kpidExtension: prop = (_imgExt ? _imgExt : "img"); break;
373   }
374 
375   prop.Detach(value);
376   return S_OK;
377   COM_TRY_END
378 }
379 
380 
381 HRESULT CHandler::Open2(IInStream *stream, IArchiveOpenCallback *openCallback)
382 {
383   UInt64 buf64[0x70 / 8];
384   RINOK(ReadStream_FALSE(stream, buf64, sizeof(buf64)))
385   const void *buf = (const void *)buf64;
386   // signature: { 'Q', 'F', 'I', 0xFB }
387   if (*(const UInt32 *)buf != Z7_CONV_BE_TO_NATIVE_CONST32(0x514649fb))
388     return S_FALSE;
389   _version = Get32((const Byte *)(const void *)buf64 + 4);
390   if (_version < 1 || _version > 3)
391     return S_FALSE;
392 
393   const UInt64 k_UncompressedSize_MAX = (UInt64)1 << 60;
394   const UInt64 k_CompressedSize_MAX   = (UInt64)1 << 60;
395 
396   _size = Get64((const Byte *)(const void *)buf64 + 0x18);
397   if (_size > k_UncompressedSize_MAX)
398     return S_FALSE;
399   size_t l1Size;
400   UInt32 headerSize;
401 
402   if (_version == 1)
403   {
404     // _mTime = Get32((const Byte *)(const void *)buf64 + 0x14); // is unused in most images
405     _clusterBits = ((const Byte *)(const void *)buf64)[0x20];
406     _numMidBits  = ((const Byte *)(const void *)buf64)[0x21];
407     if (_clusterBits < 9 || _clusterBits > 30)
408       return S_FALSE;
409     if (_numMidBits < 1 || _numMidBits > 28)
410       return S_FALSE;
411     _cryptMethod = Get32((const Byte *)(const void *)buf64 + 0x24);
412     const unsigned numBits2 = _clusterBits + _numMidBits;
413     const UInt64 l1Size64 = (_size + (((UInt64)1 << numBits2) - 1)) >> numBits2;
414     if (l1Size64 > ((UInt32)1 << 31))
415       return S_FALSE;
416     l1Size = (size_t)l1Size64;
417     headerSize = 0x30;
418   }
419   else
420   {
421     _clusterBits = Get32((const Byte *)(const void *)buf64 + 0x14);
422     if (_clusterBits < 9 || _clusterBits > 30)
423       return S_FALSE;
424     _numMidBits = _clusterBits - 3;
425     _cryptMethod = Get32((const Byte *)(const void *)buf64 + 0x20);
426     l1Size = Get32((const Byte *)(const void *)buf64 + 0x24);
427     headerSize = 0x48;
428     if (_version >= 3)
429     {
430       _incompatFlags = Get64((const Byte *)(const void *)buf64 + 0x48);
431       // const UInt64 CompatFlags    = Get64((const Byte *)(const void *)buf64 + 0x50);
432       // const UInt64 AutoClearFlags = Get64((const Byte *)(const void *)buf64 + 0x58);
433       // const UInt32 RefCountOrder = Get32((const Byte *)(const void *)buf64 + 0x60);
434       headerSize = 0x68;
435       const UInt32 headerSize2  = Get32((const Byte *)(const void *)buf64 + 0x64);
436       if (headerSize2 > (1u << 30))
437         return S_FALSE;
438       if (headerSize < headerSize2)
439           headerSize = headerSize2;
440       if (headerSize2 >= 0x68 + 1)
441         _compressionType = ((const Byte *)(const void *)buf64)[0x68];
442     }
443 
444     const UInt64 refOffset = Get64((const Byte *)(const void *)buf64 + 0x30); // must be aligned for cluster
445     const UInt32 refClusters = Get32((const Byte *)(const void *)buf64 + 0x38);
446     // UInt32 numSnapshots = Get32((const Byte *)(const void *)buf64 + 0x3C);
447     // UInt64 snapshotsOffset = Get64((const Byte *)(const void *)buf64 + 0x40); // must be aligned for cluster
448     /*
449     if (numSnapshots)
450       return S_FALSE;
451     */
452     if (refClusters)
453     {
454       if (refOffset > k_CompressedSize_MAX)
455         return S_FALSE;
456       const UInt64 numBytes = (UInt64)refClusters << _clusterBits;
457       const UInt64 end = refOffset + numBytes;
458       if (end > k_CompressedSize_MAX)
459         return S_FALSE;
460       /*
461       CByteBuffer refs;
462       refs.Alloc(numBytes);
463       RINOK(InStream_SeekSet(stream, refOffset))
464       RINOK(ReadStream_FALSE(stream, refs, numBytes));
465       */
466       if (_phySize < end)
467           _phySize = end;
468       /*
469       for (size_t i = 0; i < numBytes; i += 2)
470       {
471         UInt32 v = GetBe16((const Byte *)refs + (size_t)i);
472         if (v == 0)
473           continue;
474       }
475       */
476     }
477   }
478 
479   const UInt64 l1Offset = Get64((const Byte *)(const void *)buf64 + 0x28); // must be aligned for cluster ?
480   if (l1Offset < headerSize || l1Offset > k_CompressedSize_MAX)
481     return S_FALSE;
482   if (_phySize < headerSize)
483       _phySize = headerSize;
484 
485   _isArc = true;
486   {
487     const UInt64 backOffset = Get64((const Byte *)(const void *)buf64 + 8);
488     // UInt32 backSize = Get32((const Byte *)(const void *)buf64 + 0x10);
489     if (backOffset)
490     {
491       _unsupported = true;
492       return S_FALSE;
493     }
494   }
495 
496   UInt64 fileSize = 0;
497   RINOK(InStream_GetSize_SeekToBegin(stream, fileSize))
498 
499   const size_t clusterSize = (size_t)1 << _clusterBits;
500   const size_t t1SizeBytes = (size_t)l1Size << 3;
501   {
502     const UInt64 end = l1Offset + t1SizeBytes;
503     if (end > k_CompressedSize_MAX)
504       return S_FALSE;
505     // we need to use align end for empty qcow files
506     // some files has no cluster alignment padding at the end
507     // but has sector alignment
508     // end = (end + clusterSize - 1) >> _clusterBits << _clusterBits;
509     if (_phySize < end)
510         _phySize = end;
511     if (end > fileSize)
512       return S_FALSE;
513     if (_phySize < fileSize)
514     {
515       const UInt64 end2 = (end + 511) & ~(UInt64)511;
516       if (end2 == fileSize)
517         _phySize = end2;
518     }
519   }
520   CObjArray<UInt64> table64(l1Size);
521   {
522     // if ((t1SizeBytes >> 3) != l1Size) return S_FALSE;
523     RINOK(InStream_SeekSet(stream, l1Offset))
524     RINOK(ReadStream_FALSE(stream, table64, t1SizeBytes))
525   }
526 
527   _compressedFlag = (_version <= 1) ? ((UInt64)1 << 63) : ((UInt64)1 << 62);
528   const UInt64 offsetMask = _compressedFlag - 1;
529   const size_t midSize = (size_t)1 << (_numMidBits + 3);
530   size_t numTables = 0;
531   size_t i;
532 
533   for (i = 0; i < l1Size; i++)
534   {
535     const UInt64 v = Get64(table64 + (size_t)i) & offsetMask;
536     if (!v)
537       continue;
538     numTables++;
539     const UInt64 end = v + midSize;
540     if (end > k_CompressedSize_MAX)
541       return S_FALSE;
542     if (_phySize < end)
543         _phySize = end;
544     if (end > fileSize)
545       return S_FALSE;
546   }
547 
548   if (numTables)
549   {
550     const size_t size = (size_t)numTables << (_numMidBits + 3);
551     if (size >> (_numMidBits + 3) != numTables)
552       return E_OUTOFMEMORY;
553     _table.Alloc(size);
554     if (!_table.IsAllocated())
555       return E_OUTOFMEMORY;
556     if (openCallback)
557     {
558       const UInt64 totalBytes = size;
559       RINOK(openCallback->SetTotal(NULL, &totalBytes))
560     }
561   }
562 
563   _dir.SetSize((unsigned)l1Size);
564 
565   UInt32 curTable = 0;
566 
567   for (i = 0; i < l1Size; i++)
568   {
569     Byte *buf2;
570     {
571       const UInt64 v = Get64(table64 + (size_t)i) & offsetMask;
572       if (v == 0)
573       {
574         _dir[i] = kEmptyDirItem;
575         continue;
576       }
577       _dir[i] = curTable;
578       const size_t tableOffset = (size_t)curTable << (_numMidBits + 3);
579       buf2 = (Byte *)_table + tableOffset;
580       curTable++;
581       if (openCallback && (tableOffset & 0xFFFFF) == 0)
582       {
583         const UInt64 numBytes = tableOffset;
584         RINOK(openCallback->SetCompleted(NULL, &numBytes))
585       }
586       RINOK(InStream_SeekSet(stream, v))
587       RINOK(ReadStream_FALSE(stream, buf2, midSize))
588     }
589 
590     for (size_t k = 0; k < midSize; k += 8)
591     {
592       const UInt64 v = Get64((const Byte *)buf2 + (size_t)k);
593       if (v == 0)
594         continue;
595       UInt64 offset = v & offsetMask;
596       size_t dataSize = clusterSize;
597 
598       if (v & _compressedFlag)
599       {
600         if (_version <= 1)
601         {
602           const unsigned numOffsetBits = 63 - _clusterBits;
603           dataSize = ((size_t)(offset >> numOffsetBits) + 1) << 9;
604           offset &= ((UInt64)1 << numOffsetBits) - 1;
605           dataSize = 0; // why ?
606           // offset &= ~(((UInt64)1 << 9) - 1);
607         }
608         else
609         {
610           const unsigned numOffsetBits = 62 - (_clusterBits - 8);
611           dataSize = ((size_t)(offset >> numOffsetBits) + 1) << 9;
612           offset &= ((UInt64)1 << numOffsetBits) - (1 << 9);
613         }
614         _needCompression = true;
615       }
616       else
617       {
618         const UInt32 low = (UInt32)v & 511;
619         if (low)
620         {
621           // version_3 supports zero clusters
622           if (_version < 3 || low != 1)
623           {
624             _unsupported = true;
625             return S_FALSE;
626           }
627         }
628       }
629 
630       const UInt64 end = offset + dataSize;
631       if (_phySize < end)
632           _phySize = end;
633     }
634   }
635 
636   if (curTable != numTables)
637     return E_FAIL;
638 
639   if (_cryptMethod)
640     _unsupported = true;
641   if (_needCompression && _version <= 1) // that case was not implemented
642     _unsupported = true;
643   if (_compressionType)
644     _unsupported = true;
645 
646   Stream = stream;
647   return S_OK;
648 }
649 
650 
651 Z7_COM7F_IMF(CHandler::Close())
652 {
653   _table.Free();
654   _dir.Free();
655   // _cache.Free();
656   // _cacheCompressed.Free();
657   _phySize = 0;
658 
659   _cacheCluster = (UInt64)(Int64)-1;
660   _comprPos = 0;
661   _comprSize = 0;
662 
663   _needCompression = false;
664   _isArc = false;
665   _unsupported = false;
666 
667   _compressionType = 0;
668   _incompatFlags = 0;
669 
670   // CHandlerImg:
671   Clear_HandlerImg_Vars();
672   Stream.Release();
673   return S_OK;
674 }
675 
676 
677 Z7_COM7F_IMF(CHandler::GetStream(UInt32 /* index */, ISequentialInStream **stream))
678 {
679   COM_TRY_BEGIN
680   *stream = NULL;
681   if (_unsupported || !Stream)
682     return S_FALSE;
683   if (_needCompression)
684   {
685     if (_version <= 1 || _compressionType)
686       return S_FALSE;
687     _bufInStream.Create_if_Empty();
688     _bufOutStream.Create_if_Empty();
689     _deflateDecoder.Create_if_Empty();
690     _deflateDecoder->Set_NeedFinishInput(true);
691     const size_t clusterSize = (size_t)1 << _clusterBits;
692     _cache.AllocAtLeast(clusterSize);
693     _cacheCompressed.AllocAtLeast(clusterSize * 2);
694   }
695   CMyComPtr<ISequentialInStream> streamTemp = this;
696   RINOK(InitAndSeek())
697   *stream = streamTemp.Detach();
698   return S_OK;
699   COM_TRY_END
700 }
701 
702 
703 REGISTER_ARC_I(
704   "QCOW", "qcow qcow2 qcow2c", NULL, 0xCA,
705   k_Signature,
706   0,
707   0,
708   NULL)
709 
710 }}
711