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