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