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