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