1 // XarHandler.cpp 2 3 #include "StdAfx.h" 4 5 #include "../../../C/Sha256.h" 6 #include "../../../C/Sha512.h" 7 #include "../../../C/CpuArch.h" 8 9 #include "../../Common/ComTry.h" 10 #include "../../Common/MyLinux.h" 11 #include "../../Common/MyXml.h" 12 #include "../../Common/StringToInt.h" 13 #include "../../Common/UTFConvert.h" 14 15 #include "../../Windows/PropVariant.h" 16 #include "../../Windows/TimeUtils.h" 17 18 #include "../Common/LimitedStreams.h" 19 #include "../Common/ProgressUtils.h" 20 #include "../Common/RegisterArc.h" 21 #include "../Common/StreamObjects.h" 22 #include "../Common/StreamUtils.h" 23 24 #include "../Compress/BZip2Decoder.h" 25 #include "../Compress/CopyCoder.h" 26 #include "../Compress/ZlibDecoder.h" 27 28 #include "Common/OutStreamWithSha1.h" 29 30 using namespace NWindows; 31 32 #define XAR_SHOW_RAW 33 34 #define Get16(p) GetBe16(p) 35 #define Get32(p) GetBe32(p) 36 #define Get64(p) GetBe64(p) 37 38 namespace NArchive { 39 namespace NXar { 40 41 Z7_CLASS_IMP_NOQIB_1( 42 CInStreamWithSha256 43 , ISequentialInStream 44 ) 45 bool _sha512Mode; 46 CMyComPtr<ISequentialInStream> _stream; 47 CAlignedBuffer1 _sha256; 48 CAlignedBuffer1 _sha512; 49 UInt64 _size; 50 Sha256()51 CSha256 *Sha256() { return (CSha256 *)(void *)(Byte *)_sha256; } Sha512()52 CSha512 *Sha512() { return (CSha512 *)(void *)(Byte *)_sha512; } 53 public: 54 CInStreamWithSha256(): 55 _sha256(sizeof(CSha256)), 56 _sha512(sizeof(CSha512)) 57 {} 58 void SetStream(ISequentialInStream *stream) { _stream = stream; } 59 void Init(bool sha512Mode) 60 { 61 _sha512Mode = sha512Mode; 62 _size = 0; 63 if (sha512Mode) 64 Sha512_Init(Sha512(), SHA512_DIGEST_SIZE); 65 else 66 Sha256_Init(Sha256()); 67 } 68 void ReleaseStream() { _stream.Release(); } 69 UInt64 GetSize() const { return _size; } 70 void Final256(Byte *digest) { Sha256_Final(Sha256(), digest); } 71 void Final512(Byte *digest) { Sha512_Final(Sha512(), digest, SHA512_DIGEST_SIZE); } 72 }; 73 74 Z7_COM7F_IMF(CInStreamWithSha256::Read(void *data, UInt32 size, UInt32 *processedSize)) 75 { 76 UInt32 realProcessedSize; 77 const HRESULT result = _stream->Read(data, size, &realProcessedSize); 78 _size += realProcessedSize; 79 if (_sha512Mode) 80 Sha512_Update(Sha512(), (const Byte *)data, realProcessedSize); 81 else 82 Sha256_Update(Sha256(), (const Byte *)data, realProcessedSize); 83 if (processedSize) 84 *processedSize = realProcessedSize; 85 return result; 86 } 87 88 89 Z7_CLASS_IMP_NOQIB_1( 90 COutStreamWithSha256 91 , ISequentialOutStream 92 ) 93 bool _sha512Mode; 94 CMyComPtr<ISequentialOutStream> _stream; 95 CAlignedBuffer1 _sha256; 96 CAlignedBuffer1 _sha512; 97 UInt64 _size; 98 99 CSha256 *Sha256() { return (CSha256 *)(void *)(Byte *)_sha256; } 100 CSha512 *Sha512() { return (CSha512 *)(void *)(Byte *)_sha512; } 101 public: 102 COutStreamWithSha256(): 103 _sha256(sizeof(CSha256)), 104 _sha512(sizeof(CSha512)) 105 {} 106 void SetStream(ISequentialOutStream *stream) { _stream = stream; } 107 void ReleaseStream() { _stream.Release(); } 108 void Init(bool sha512Mode) 109 { 110 _sha512Mode = sha512Mode; 111 _size = 0; 112 if (sha512Mode) 113 Sha512_Init(Sha512(), SHA512_DIGEST_SIZE); 114 else 115 Sha256_Init(Sha256()); 116 } 117 UInt64 GetSize() const { return _size; } 118 void Final256(Byte *digest) { Sha256_Final(Sha256(), digest); } 119 void Final512(Byte *digest) { Sha512_Final(Sha512(), digest, SHA512_DIGEST_SIZE); } 120 }; 121 122 Z7_COM7F_IMF(COutStreamWithSha256::Write(const void *data, UInt32 size, UInt32 *processedSize)) 123 { 124 HRESULT result = S_OK; 125 if (_stream) 126 result = _stream->Write(data, size, &size); 127 // if (_calculate) 128 if (_sha512Mode) 129 Sha512_Update(Sha512(), (const Byte *)data, size); 130 else 131 Sha256_Update(Sha256(), (const Byte *)data, size); 132 _size += size; 133 if (processedSize) 134 *processedSize = size; 135 return result; 136 } 137 138 // we limit supported xml sizes: 139 // ((size_t)1 << (sizeof(size_t) / 2 + 28)) - (1u << 14); 140 static const size_t kXmlSizeMax = ((size_t)1 << 30) - (1u << 14); 141 static const size_t kXmlPackSizeMax = kXmlSizeMax; 142 143 #define XAR_CKSUM_NONE 0 144 #define XAR_CKSUM_SHA1 1 145 #define XAR_CKSUM_MD5 2 146 #define XAR_CKSUM_SHA256 3 147 #define XAR_CKSUM_SHA512 4 148 // #define XAR_CKSUM_OTHER 3 149 // fork version of xar can use (3) as special case, 150 // where name of hash is stored as string at the end of header 151 // we do not support such hash still. 152 153 static const char * const k_ChecksumNames[] = 154 { 155 "NONE" 156 , "SHA1" 157 , "MD5" 158 , "SHA256" 159 , "SHA512" 160 }; 161 162 static unsigned GetHashSize(int algo) 163 { 164 if (algo <= XAR_CKSUM_NONE || algo > XAR_CKSUM_SHA512) 165 return 0; 166 if (algo == XAR_CKSUM_SHA1) 167 return SHA1_DIGEST_SIZE; 168 return (16u >> XAR_CKSUM_MD5) << algo; 169 } 170 171 #define METHOD_NAME_ZLIB "zlib" 172 173 static int Find_ChecksumId_for_Name(const AString &style) 174 { 175 for (unsigned i = 0; i < Z7_ARRAY_SIZE(k_ChecksumNames); i++) 176 { 177 // old xars used upper case in "style" 178 // new xars use lower case in "style" 179 if (style.IsEqualTo_Ascii_NoCase(k_ChecksumNames[i])) 180 return (int)i; 181 } 182 return -1; 183 } 184 185 186 struct CCheckSum 187 { 188 int AlgoNumber; 189 bool Error; 190 CByteBuffer Data; 191 AString Style; 192 193 CCheckSum(): AlgoNumber(-1), Error(false) {} 194 void AddNameToString(AString &s) const; 195 }; 196 197 void CCheckSum::AddNameToString(AString &s) const 198 { 199 if (Style.IsEmpty()) 200 s.Add_OptSpaced("NO-CHECKSUM"); 201 else 202 { 203 s.Add_OptSpaced(Style); 204 if (Error) 205 s += "-ERROR"; 206 } 207 } 208 209 210 struct CFile 211 { 212 bool IsDir; 213 bool Is_SymLink; 214 bool HasData; 215 bool Mode_Defined; 216 bool INode_Defined; 217 bool UserId_Defined; 218 bool GroupId_Defined; 219 // bool Device_Defined; 220 bool Id_Defined; 221 222 int Parent; 223 UInt32 Mode; 224 225 UInt64 Size; 226 UInt64 PackSize; 227 UInt64 Offset; 228 UInt64 MTime; 229 UInt64 CTime; 230 UInt64 ATime; 231 UInt64 INode; 232 UInt64 UserId; 233 UInt64 GroupId; 234 // UInt64 Device; 235 236 AString Name; 237 AString Method; 238 AString User; 239 AString Group; 240 // AString Id; 241 AString Type; 242 AString Link; 243 // AString LinkType; 244 // AString LinkFrom; 245 246 UInt64 Id; 247 CCheckSum extracted_checksum; 248 CCheckSum archived_checksum; 249 250 CFile(int parent): 251 IsDir(false), 252 Is_SymLink(false), 253 HasData(false), 254 Mode_Defined(false), 255 INode_Defined(false), 256 UserId_Defined(false), 257 GroupId_Defined(false), 258 // Device_Defined(false), 259 Id_Defined(false), 260 Parent(parent), 261 Mode(0), 262 Size(0), PackSize(0), Offset(0), 263 MTime(0), CTime(0), ATime(0), 264 INode(0) 265 {} 266 267 bool IsCopyMethod() const 268 { 269 return Method.IsEmpty() || Method == "octet-stream"; 270 } 271 272 void UpdateTotalPackSize(UInt64 &totalSize) const 273 { 274 const UInt64 t = Offset + PackSize; 275 if (t >= Offset) 276 if (totalSize < t) 277 totalSize = t; 278 } 279 }; 280 281 282 Z7_CLASS_IMP_CHandler_IInArchive_2( 283 IArchiveGetRawProps, 284 IInArchiveGetStream 285 ) 286 bool _is_pkg; 287 bool _toc_CrcError; 288 CObjectVector<CFile> _files; 289 CMyComPtr<IInStream> _inStream; 290 UInt64 _dataStartPos; 291 UInt64 _phySize; 292 CAlignedBuffer _xmlBuf; 293 size_t _xmlLen; 294 // UInt64 CreationTime; 295 AString CreationTime_String; 296 UInt32 _checkSumAlgo; 297 Int32 _mainSubfile; 298 299 HRESULT Open2(IInStream *stream); 300 }; 301 302 303 static const Byte kArcProps[] = 304 { 305 kpidSubType, 306 // kpidHeadersSize, 307 kpidMethod, 308 kpidCTime 309 }; 310 311 // #define kpidLinkType 250 312 // #define kpidLinkFrom 251 313 314 static const Byte kProps[] = 315 { 316 kpidPath, 317 kpidSize, 318 kpidPackSize, 319 kpidMTime, 320 kpidCTime, 321 kpidATime, 322 kpidPosixAttrib, 323 kpidType, 324 kpidUser, 325 kpidGroup, 326 kpidUserId, 327 kpidGroupId, 328 kpidINode, 329 // kpidDeviceMajor, 330 // kpidDeviceMinor, 331 kpidSymLink, 332 // kpidLinkType, 333 // kpidLinkFrom, 334 kpidMethod, 335 kpidId, 336 kpidOffset 337 }; 338 339 IMP_IInArchive_Props 340 IMP_IInArchive_ArcProps 341 342 static bool ParseUInt64(const CXmlItem &item, const char *name, UInt64 &res) 343 { 344 const AString s (item.GetSubStringForTag(name)); 345 if (s.IsEmpty()) 346 return false; 347 const char *end; 348 res = ConvertStringToUInt64(s, &end); 349 return *end == 0; 350 } 351 352 353 #define PARSE_NUM(_num_, _dest_) \ 354 { const char *end; _dest_ = ConvertStringToUInt32(p, &end); \ 355 if ((unsigned)(end - p) != _num_) return 0; \ 356 p += _num_ + 1; } 357 358 static UInt64 ParseTime(const CXmlItem &item, const char *name /* , bool z_isRequired */ ) 359 { 360 const AString s (item.GetSubStringForTag(name)); 361 if (s.Len() < 20 /* (z_isRequired ? 20u : 19u) */) 362 return 0; 363 const char *p = s; 364 if (p[ 4] != '-' || 365 p[ 7] != '-' || 366 p[10] != 'T' || 367 p[13] != ':' || 368 p[16] != ':') 369 return 0; 370 // if (z_isRequired) 371 if (p[19] != 'Z') 372 return 0; 373 UInt32 year, month, day, hour, min, sec; 374 PARSE_NUM(4, year) 375 PARSE_NUM(2, month) 376 PARSE_NUM(2, day) 377 PARSE_NUM(2, hour) 378 PARSE_NUM(2, min) 379 PARSE_NUM(2, sec) 380 UInt64 numSecs; 381 if (!NTime::GetSecondsSince1601(year, month, day, hour, min, sec, numSecs)) 382 return 0; 383 return numSecs * 10000000; 384 } 385 386 387 static void ParseChecksum(const CXmlItem &item, const char *name, CCheckSum &checksum) 388 { 389 const CXmlItem *checkItem = item.FindSubTag_GetPtr(name); 390 if (!checkItem) 391 return; // false; 392 checksum.Style = checkItem->GetPropVal("style"); 393 const AString s (checkItem->GetSubString()); 394 if ((s.Len() & 1) == 0 && s.Len() <= (2u << 7)) // 1024-bit max 395 { 396 const size_t size = s.Len() / 2; 397 CByteBuffer temp(size); 398 if ((size_t)(ParseHexString(s, temp) - temp) == size) 399 { 400 checksum.Data = temp; 401 const int index = Find_ChecksumId_for_Name(checksum.Style); 402 if (index >= 0 && checksum.Data.Size() == GetHashSize(index)) 403 { 404 checksum.AlgoNumber = index; 405 return; 406 } 407 } 408 } 409 checksum.Error = true; 410 } 411 412 413 static bool AddItem(const CXmlItem &item, CObjectVector<CFile> &files, int parent, int level) 414 { 415 if (!item.IsTag) 416 return true; 417 if (level >= 1024) 418 return false; 419 if (item.Name == "file") 420 { 421 CFile file(parent); 422 parent = (int)files.Size(); 423 { 424 const AString id = item.GetPropVal("id"); 425 const char *end; 426 file.Id = ConvertStringToUInt64(id, &end); 427 if (*end == 0) 428 file.Id_Defined = true; 429 } 430 file.Name = item.GetSubStringForTag("name"); 431 z7_xml_DecodeString(file.Name); 432 { 433 const CXmlItem *typeItem = item.FindSubTag_GetPtr("type"); 434 if (typeItem) 435 { 436 file.Type = typeItem->GetSubString(); 437 // file.LinkFrom = typeItem->GetPropVal("link"); 438 if (file.Type == "directory") 439 file.IsDir = true; 440 else 441 { 442 // file.IsDir = false; 443 /* 444 else if (file.Type == "file") 445 {} 446 else if (file.Type == "hardlink") 447 {} 448 else 449 */ 450 if (file.Type == "symlink") 451 file.Is_SymLink = true; 452 // file.IsDir = false; 453 } 454 } 455 } 456 { 457 const CXmlItem *linkItem = item.FindSubTag_GetPtr("link"); 458 if (linkItem) 459 { 460 // file.LinkType = linkItem->GetPropVal("type"); 461 file.Link = linkItem->GetSubString(); 462 z7_xml_DecodeString(file.Link); 463 } 464 } 465 466 const CXmlItem *dataItem = item.FindSubTag_GetPtr("data"); 467 if (dataItem && !file.IsDir) 468 { 469 file.HasData = true; 470 if (!ParseUInt64(*dataItem, "size", file.Size)) 471 return false; 472 if (!ParseUInt64(*dataItem, "length", file.PackSize)) 473 return false; 474 if (!ParseUInt64(*dataItem, "offset", file.Offset)) 475 return false; 476 ParseChecksum(*dataItem, "extracted-checksum", file.extracted_checksum); 477 ParseChecksum(*dataItem, "archived-checksum", file.archived_checksum); 478 const CXmlItem *encodingItem = dataItem->FindSubTag_GetPtr("encoding"); 479 if (encodingItem) 480 { 481 AString s (encodingItem->GetPropVal("style")); 482 if (!s.IsEmpty()) 483 { 484 const AString appl ("application/"); 485 if (s.IsPrefixedBy(appl)) 486 { 487 s.DeleteFrontal(appl.Len()); 488 const AString xx ("x-"); 489 if (s.IsPrefixedBy(xx)) 490 { 491 s.DeleteFrontal(xx.Len()); 492 if (s == "gzip") 493 s = METHOD_NAME_ZLIB; 494 } 495 } 496 file.Method = s; 497 } 498 } 499 } 500 501 file.INode_Defined = ParseUInt64(item, "inode", file.INode); 502 file.UserId_Defined = ParseUInt64(item, "uid", file.UserId); 503 file.GroupId_Defined = ParseUInt64(item, "gid", file.GroupId); 504 // file.Device_Defined = ParseUInt64(item, "deviceno", file.Device); 505 file.MTime = ParseTime(item, "mtime"); // z_IsRequied = true 506 file.CTime = ParseTime(item, "ctime"); 507 file.ATime = ParseTime(item, "atime"); 508 { 509 const AString s (item.GetSubStringForTag("mode")); 510 if (s[0] == '0') 511 { 512 const char *end; 513 file.Mode = ConvertOctStringToUInt32(s, &end); 514 file.Mode_Defined = (*end == 0); 515 } 516 } 517 file.User = item.GetSubStringForTag("user"); 518 file.Group = item.GetSubStringForTag("group"); 519 520 files.Add(file); 521 } 522 523 FOR_VECTOR (i, item.SubItems) 524 if (!AddItem(item.SubItems[i], files, parent, level + 1)) 525 return false; 526 return true; 527 } 528 529 530 531 struct CInStreamWithHash 532 { 533 CMyComPtr2_Create<ISequentialInStream, CInStreamWithSha1> inStreamSha1; 534 CMyComPtr2_Create<ISequentialInStream, CInStreamWithSha256> inStreamSha256; 535 CMyComPtr2_Create<ISequentialInStream, CLimitedSequentialInStream> inStreamLim; 536 537 void SetStreamAndInit(ISequentialInStream *stream, int algo); 538 bool CheckHash(int algo, const Byte *digest_from_arc) const; 539 }; 540 541 542 void CInStreamWithHash::SetStreamAndInit(ISequentialInStream *stream, int algo) 543 { 544 if (algo == XAR_CKSUM_SHA1) 545 { 546 inStreamSha1->SetStream(stream); 547 inStreamSha1->Init(); 548 stream = inStreamSha1; 549 } 550 else if (algo == XAR_CKSUM_SHA256 551 || algo == XAR_CKSUM_SHA512) 552 { 553 inStreamSha256->SetStream(stream); 554 inStreamSha256->Init(algo == XAR_CKSUM_SHA512); 555 stream = inStreamSha256; 556 } 557 inStreamLim->SetStream(stream); 558 } 559 560 bool CInStreamWithHash::CheckHash(int algo, const Byte *digest_from_arc) const 561 { 562 if (algo == XAR_CKSUM_SHA1) 563 { 564 Byte digest[SHA1_DIGEST_SIZE]; 565 inStreamSha1->Final(digest); 566 if (memcmp(digest, digest_from_arc, sizeof(digest)) != 0) 567 return false; 568 } 569 else if (algo == XAR_CKSUM_SHA256) 570 { 571 Byte digest[SHA256_DIGEST_SIZE]; 572 inStreamSha256->Final256(digest); 573 if (memcmp(digest, digest_from_arc, sizeof(digest)) != 0) 574 return false; 575 } 576 else if (algo == XAR_CKSUM_SHA512) 577 { 578 Byte digest[SHA512_DIGEST_SIZE]; 579 inStreamSha256->Final512(digest); 580 if (memcmp(digest, digest_from_arc, sizeof(digest)) != 0) 581 return false; 582 } 583 return true; 584 } 585 586 587 HRESULT CHandler::Open2(IInStream *stream) 588 { 589 const unsigned kHeaderSize = 28; 590 UInt32 buf32[kHeaderSize / sizeof(UInt32)]; 591 RINOK(ReadStream_FALSE(stream, buf32, kHeaderSize)) 592 const unsigned headerSize = Get16((const Byte *)(const void *)buf32 + 4); 593 // xar library now writes 1 to version field. 594 // some old xars could have version == 0 ? 595 // specification allows (headerSize != 28), 596 // but we don't expect big value in (headerSize). 597 // so we restrict (headerSize) with 64 bytes to reduce false open. 598 const unsigned kHeaderSize_MAX = 64; 599 if (Get32(buf32) != 0x78617221 // signature: "xar!" 600 || headerSize < kHeaderSize 601 || headerSize > kHeaderSize_MAX 602 || Get16((const Byte *)(const void *)buf32 + 6) > 1 // version 603 ) 604 return S_FALSE; 605 _checkSumAlgo = Get32(buf32 + 6); 606 const UInt64 packSize = Get64(buf32 + 2); 607 const UInt64 unpackSize = Get64(buf32 + 4); 608 if (packSize >= kXmlPackSizeMax || 609 unpackSize >= kXmlSizeMax) 610 return S_FALSE; 611 /* some xar archives can have padding bytes at offset 28, 612 or checksum algorithm name at offset 28 (in xar fork, if cksum_alg==3) 613 But we didn't see such xar archives. 614 */ 615 if (headerSize != kHeaderSize) 616 { 617 RINOK(InStream_SeekSet(stream, headerSize)) 618 } 619 _dataStartPos = headerSize + packSize; 620 _phySize = _dataStartPos; 621 622 _xmlBuf.Alloc((size_t)unpackSize + 1); 623 if (!_xmlBuf.IsAllocated()) 624 return E_OUTOFMEMORY; 625 _xmlLen = (size_t)unpackSize; 626 627 CInStreamWithHash hashStream; 628 { 629 CMyComPtr2_Create<ICompressCoder, NCompress::NZlib::CDecoder> zlibCoder; 630 hashStream.SetStreamAndInit(stream, (int)(unsigned)_checkSumAlgo); 631 hashStream.inStreamLim->Init(packSize); 632 CMyComPtr2_Create<ISequentialOutStream, CBufPtrSeqOutStream> outStreamLim; 633 outStreamLim->Init(_xmlBuf, (size_t)unpackSize); 634 RINOK(zlibCoder.Interface()->Code(hashStream.inStreamLim, outStreamLim, NULL, &unpackSize, NULL)) 635 if (outStreamLim->GetPos() != (size_t)unpackSize) 636 return S_FALSE; 637 } 638 _xmlBuf[(size_t)unpackSize] = 0; 639 if (strlen((const char *)(const Byte *)_xmlBuf) != (size_t)unpackSize) 640 return S_FALSE; 641 CXml xml; 642 if (!xml.Parse((const char *)(const Byte *)_xmlBuf)) 643 return S_FALSE; 644 645 if (!xml.Root.IsTagged("xar") || xml.Root.SubItems.Size() != 1) 646 return S_FALSE; 647 const CXmlItem &toc = xml.Root.SubItems[0]; 648 if (!toc.IsTagged("toc")) 649 return S_FALSE; 650 651 // CreationTime = ParseTime(toc, "creation-time", false); // z_IsRequied 652 CreationTime_String = toc.GetSubStringForTag("creation-time"); 653 { 654 // we suppose that offset of checksum is always 0; 655 // but [TOC].xml contains exact offset value in <checksum> block. 656 const UInt64 offset = 0; 657 const unsigned hashSize = GetHashSize((int)(unsigned)_checkSumAlgo); 658 if (hashSize) 659 { 660 /* 661 const CXmlItem *csItem = toc.FindSubTag_GetPtr("checksum"); 662 if (csItem) 663 { 664 const int checkSumAlgo2 = Find_ChecksumId_for_Name(csItem->GetPropVal("style")); 665 UInt64 csSize, csOffset; 666 if (ParseUInt64(*csItem, "size", csSize) && 667 ParseUInt64(*csItem, "offset", csOffset) && 668 csSize == hashSize && 669 (unsigned)checkSumAlgo2 == _checkSumAlgo) 670 offset = csOffset; 671 } 672 */ 673 CByteBuffer digest_from_arc(hashSize); 674 RINOK(InStream_SeekSet(stream, _dataStartPos + offset)) 675 RINOK(ReadStream_FALSE(stream, digest_from_arc, hashSize)) 676 if (!hashStream.CheckHash((int)(unsigned)_checkSumAlgo, digest_from_arc)) 677 _toc_CrcError = true; 678 } 679 } 680 681 if (!AddItem(toc, _files, 682 -1, // parent 683 0)) // level 684 return S_FALSE; 685 686 UInt64 totalPackSize = 0; 687 unsigned numMainFiles = 0; 688 689 FOR_VECTOR (i, _files) 690 { 691 const CFile &file = _files[i]; 692 file.UpdateTotalPackSize(totalPackSize); 693 if (file.Parent == -1) 694 { 695 if (file.Name == "Payload" || file.Name == "Content") 696 { 697 _mainSubfile = (Int32)(int)i; 698 numMainFiles++; 699 } 700 else if (file.Name == "PackageInfo") 701 _is_pkg = true; 702 } 703 } 704 705 if (numMainFiles > 1) 706 _mainSubfile = -1; 707 708 const UInt64 k_PhySizeLim = (UInt64)1 << 62; 709 _phySize = (totalPackSize > k_PhySizeLim - _dataStartPos) ? 710 k_PhySizeLim : 711 _dataStartPos + totalPackSize; 712 713 return S_OK; 714 } 715 716 Z7_COM7F_IMF(CHandler::Open(IInStream *stream, 717 const UInt64 * /* maxCheckStartPosition */, 718 IArchiveOpenCallback * /* openArchiveCallback */)) 719 { 720 COM_TRY_BEGIN 721 { 722 Close(); 723 RINOK(Open2(stream)) 724 _inStream = stream; 725 } 726 return S_OK; 727 COM_TRY_END 728 } 729 730 Z7_COM7F_IMF(CHandler::Close()) 731 { 732 _phySize = 0; 733 _dataStartPos = 0; 734 _inStream.Release(); 735 _files.Clear(); 736 _xmlLen = 0; 737 _xmlBuf.Free(); 738 _mainSubfile = -1; 739 _is_pkg = false; 740 _toc_CrcError = false; 741 _checkSumAlgo = 0; 742 // CreationTime = 0; 743 CreationTime_String.Empty(); 744 return S_OK; 745 } 746 747 Z7_COM7F_IMF(CHandler::GetNumberOfItems(UInt32 *numItems)) 748 { 749 *numItems = _files.Size() 750 #ifdef XAR_SHOW_RAW 751 + 1 752 #endif 753 ; 754 return S_OK; 755 } 756 757 static void TimeToProp(UInt64 t, NCOM::CPropVariant &prop) 758 { 759 if (t != 0) 760 { 761 FILETIME ft; 762 ft.dwLowDateTime = (UInt32)(t); 763 ft.dwHighDateTime = (UInt32)(t >> 32); 764 prop = ft; 765 } 766 } 767 768 static void Utf8StringToProp(const AString &s, NCOM::CPropVariant &prop) 769 { 770 if (!s.IsEmpty()) 771 { 772 UString us; 773 ConvertUTF8ToUnicode(s, us); 774 prop = us; 775 } 776 } 777 778 Z7_COM7F_IMF(CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)) 779 { 780 COM_TRY_BEGIN 781 NCOM::CPropVariant prop; 782 switch (propID) 783 { 784 // case kpidHeadersSize: prop = _dataStartPos; break; 785 case kpidPhySize: prop = _phySize; break; 786 case kpidMainSubfile: if (_mainSubfile >= 0) prop = (UInt32)_mainSubfile; break; 787 case kpidSubType: if (_is_pkg) prop = "pkg"; break; 788 case kpidExtension: prop = _is_pkg ? "pkg" : "xar"; break; 789 case kpidCTime: 790 { 791 // it's local time. We can transfer it to UTC time, if we use FILETIME. 792 // TimeToProp(CreationTime, prop); break; 793 if (!CreationTime_String.IsEmpty()) 794 prop = CreationTime_String; 795 break; 796 } 797 case kpidMethod: 798 { 799 AString s; 800 if (_checkSumAlgo < Z7_ARRAY_SIZE(k_ChecksumNames)) 801 s = k_ChecksumNames[_checkSumAlgo]; 802 else 803 { 804 s += "Checksum"; 805 s.Add_UInt32(_checkSumAlgo); 806 } 807 prop = s; 808 break; 809 } 810 case kpidWarningFlags: 811 { 812 UInt32 v = 0; 813 if (_toc_CrcError) v |= kpv_ErrorFlags_CrcError; 814 prop = v; 815 break; 816 } 817 case kpidINode: prop = true; break; 818 case kpidIsTree: prop = true; break; 819 } 820 prop.Detach(value); 821 return S_OK; 822 COM_TRY_END 823 } 824 825 826 /* 827 inline UInt32 MY_dev_major(UInt64 dev) 828 { 829 return ((UInt32)(dev >> 8) & (UInt32)0xfff) | ((UInt32)(dev >> 32) & ~(UInt32)0xfff); 830 } 831 inline UInt32 MY_dev_minor(UInt64 dev) 832 { 833 return ((UInt32)(dev) & 0xff) | ((UInt32)(dev >> 12) & ~(UInt32)0xff); 834 } 835 */ 836 837 Z7_COM7F_IMF(CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)) 838 { 839 COM_TRY_BEGIN 840 NCOM::CPropVariant prop; 841 842 #ifdef XAR_SHOW_RAW 843 if (index >= _files.Size()) 844 { 845 switch (propID) 846 { 847 case kpidName: 848 case kpidPath: 849 prop = "[TOC].xml"; break; 850 case kpidSize: 851 case kpidPackSize: prop = (UInt64)_xmlLen; break; 852 } 853 } 854 else 855 #endif 856 { 857 const CFile &item = _files[index]; 858 switch (propID) 859 { 860 case kpidPath: 861 { 862 AString path; 863 unsigned cur = index; 864 for (;;) 865 { 866 const CFile &item2 = _files[cur]; 867 if (!path.IsEmpty()) 868 path.InsertAtFront(CHAR_PATH_SEPARATOR); 869 // #define XAR_EMPTY_NAME_REPLACEMENT "[]" 870 if (item2.Name.IsEmpty()) 871 { 872 AString s('['); 873 s.Add_UInt32(cur); 874 s.Add_Char(']'); 875 path.Insert(0, s); 876 } 877 else 878 path.Insert(0, item2.Name); 879 if (item2.Parent < 0) 880 break; 881 cur = (unsigned)item2.Parent; 882 } 883 Utf8StringToProp(path, prop); 884 break; 885 } 886 887 case kpidName: 888 { 889 if (item.Name.IsEmpty()) 890 { 891 AString s('['); 892 s.Add_UInt32(index); 893 s.Add_Char(']'); 894 prop = s; 895 } 896 else 897 Utf8StringToProp(item.Name, prop); 898 break; 899 } 900 901 case kpidIsDir: prop = item.IsDir; break; 902 903 case kpidSize: if (item.HasData && !item.IsDir) prop = item.Size; break; 904 case kpidPackSize: if (item.HasData && !item.IsDir) prop = item.PackSize; break; 905 906 case kpidMethod: 907 { 908 if (item.HasData) 909 { 910 AString s = item.Method; 911 item.extracted_checksum.AddNameToString(s); 912 item.archived_checksum.AddNameToString(s); 913 Utf8StringToProp(s, prop); 914 } 915 break; 916 } 917 918 case kpidMTime: TimeToProp(item.MTime, prop); break; 919 case kpidCTime: TimeToProp(item.CTime, prop); break; 920 case kpidATime: TimeToProp(item.ATime, prop); break; 921 922 case kpidPosixAttrib: 923 if (item.Mode_Defined) 924 { 925 UInt32 mode = item.Mode; 926 if ((mode & MY_LIN_S_IFMT) == 0) 927 mode |= ( 928 item.Is_SymLink ? MY_LIN_S_IFLNK : 929 item.IsDir ? MY_LIN_S_IFDIR : 930 MY_LIN_S_IFREG); 931 prop = mode; 932 } 933 break; 934 935 case kpidType: Utf8StringToProp(item.Type, prop); break; 936 case kpidUser: Utf8StringToProp(item.User, prop); break; 937 case kpidGroup: Utf8StringToProp(item.Group, prop); break; 938 case kpidSymLink: if (item.Is_SymLink) Utf8StringToProp(item.Link, prop); break; 939 940 case kpidUserId: if (item.UserId_Defined) prop = item.UserId; break; 941 case kpidGroupId: if (item.GroupId_Defined) prop = item.GroupId; break; 942 case kpidINode: if (item.INode_Defined) prop = item.INode; break; 943 case kpidId: if (item.Id_Defined) prop = item.Id; break; 944 // Utf8StringToProp(item.Id, prop); 945 /* 946 case kpidDeviceMajor: if (item.Device_Defined) prop = (UInt32)MY_dev_major(item.Device); break; 947 case kpidDeviceMinor: if (item.Device_Defined) prop = (UInt32)MY_dev_minor(item.Device); break; 948 case kpidLinkType: 949 if (!item.LinkType.IsEmpty()) 950 Utf8StringToProp(item.LinkType, prop); 951 break; 952 case kpidLinkFrom: 953 if (!item.LinkFrom.IsEmpty()) 954 Utf8StringToProp(item.LinkFrom, prop); 955 break; 956 */ 957 case kpidOffset: 958 if (item.HasData) 959 prop = _dataStartPos + item.Offset; 960 break; 961 } 962 } 963 prop.Detach(value); 964 return S_OK; 965 COM_TRY_END 966 } 967 968 969 // for debug: 970 // #define Z7_XAR_SHOW_CHECKSUM_PACK 971 972 #ifdef Z7_XAR_SHOW_CHECKSUM_PACK 973 enum 974 { 975 kpidChecksumPack = kpidUserDefined 976 }; 977 #endif 978 979 static const Byte kRawProps[] = 980 { 981 kpidChecksum 982 #ifdef Z7_XAR_SHOW_CHECKSUM_PACK 983 , kpidCRC // instead of kpidUserDefined / kpidCRC 984 #endif 985 }; 986 987 Z7_COM7F_IMF(CHandler::GetNumRawProps(UInt32 *numProps)) 988 { 989 *numProps = Z7_ARRAY_SIZE(kRawProps); 990 return S_OK; 991 } 992 993 Z7_COM7F_IMF(CHandler::GetRawPropInfo(UInt32 index, BSTR *name, PROPID *propID)) 994 { 995 *propID = kRawProps[index]; 996 *name = NULL; 997 998 #ifdef Z7_XAR_SHOW_CHECKSUM_PACK 999 if (index != 0) 1000 { 1001 *propID = kpidChecksumPack; 1002 *name = NWindows::NCOM::AllocBstrFromAscii("archived-checksum"); 1003 } 1004 #endif 1005 return S_OK; 1006 } 1007 1008 Z7_COM7F_IMF(CHandler::GetParent(UInt32 index, UInt32 *parent, UInt32 *parentType)) 1009 { 1010 *parentType = NParentType::kDir; 1011 *parent = (UInt32)(Int32)-1; 1012 #ifdef XAR_SHOW_RAW 1013 if (index >= _files.Size()) 1014 return S_OK; 1015 #endif 1016 { 1017 const CFile &item = _files[index]; 1018 *parent = (UInt32)(Int32)item.Parent; 1019 } 1020 return S_OK; 1021 } 1022 1023 1024 Z7_COM7F_IMF(CHandler::GetRawProp(UInt32 index, PROPID propID, const void **data, UInt32 *dataSize, UInt32 *propType)) 1025 { 1026 *data = NULL; 1027 *dataSize = 0; 1028 *propType = 0; 1029 1030 // COM_TRY_BEGIN 1031 NCOM::CPropVariant prop; 1032 1033 if (propID == kpidChecksum) 1034 { 1035 #ifdef XAR_SHOW_RAW 1036 if (index >= _files.Size()) 1037 { 1038 // case kpidPath: prop = "[TOC].xml"; break; 1039 } 1040 else 1041 #endif 1042 { 1043 const CFile &item = _files[index]; 1044 const size_t size = item.extracted_checksum.Data.Size(); 1045 if (size != 0) 1046 { 1047 *dataSize = (UInt32)size; 1048 *propType = NPropDataType::kRaw; 1049 *data = item.extracted_checksum.Data; 1050 } 1051 } 1052 } 1053 1054 #ifdef Z7_XAR_SHOW_CHECKSUM_PACK 1055 if (propID == kpidChecksumPack) 1056 { 1057 #ifdef XAR_SHOW_RAW 1058 if (index >= _files.Size()) 1059 { 1060 // we can show digest check sum here 1061 } 1062 else 1063 #endif 1064 { 1065 const CFile &item = _files[index]; 1066 const size_t size = (UInt32)item.archived_checksum.Data.Size(); 1067 if (size != 0) 1068 { 1069 *dataSize = (UInt32)size; 1070 *propType = NPropDataType::kRaw; 1071 *data = item.archived_checksum.Data; 1072 } 1073 } 1074 } 1075 #endif 1076 return S_OK; 1077 } 1078 1079 1080 1081 1082 Z7_COM7F_IMF(CHandler::Extract(const UInt32 *indices, UInt32 numItems, 1083 Int32 testMode, IArchiveExtractCallback *extractCallback)) 1084 { 1085 COM_TRY_BEGIN 1086 const bool allFilesMode = (numItems == (UInt32)(Int32)-1); 1087 if (allFilesMode) 1088 numItems = _files.Size() 1089 #ifdef XAR_SHOW_RAW 1090 + 1 1091 #endif 1092 ; 1093 if (numItems == 0) 1094 return S_OK; 1095 UInt64 totalSize = 0; 1096 UInt32 i; 1097 for (i = 0; i < numItems; i++) 1098 { 1099 const UInt32 index = allFilesMode ? i : indices[i]; 1100 #ifdef XAR_SHOW_RAW 1101 if (index >= _files.Size()) 1102 totalSize += _xmlLen; 1103 else 1104 #endif 1105 totalSize += _files[index].Size; 1106 } 1107 RINOK(extractCallback->SetTotal(totalSize)) 1108 1109 CMyComPtr2_Create<ICompressProgressInfo, CLocalProgress> lps; 1110 lps->Init(extractCallback, false); 1111 CInStreamWithHash inHashStream; 1112 CMyComPtr2_Create<ISequentialOutStream, COutStreamWithSha1> outStreamSha1; 1113 CMyComPtr2_Create<ISequentialOutStream, COutStreamWithSha256> outStreamSha256; 1114 CMyComPtr2_Create<ISequentialOutStream, CLimitedSequentialOutStream> outStreamLim; 1115 CMyComPtr2_Create<ICompressCoder, NCompress::CCopyCoder> copyCoder; 1116 CMyComPtr2_Create<ICompressCoder, NCompress::NZlib::CDecoder> zlibCoder; 1117 CMyComPtr2_Create<ICompressCoder, NCompress::NBZip2::CDecoder> bzip2Coder; 1118 bzip2Coder->FinishMode = true; 1119 1120 UInt64 cur_PackSize, cur_UnpSize; 1121 1122 for (i = 0;; i++, 1123 lps->InSize += cur_PackSize, 1124 lps->OutSize += cur_UnpSize) 1125 { 1126 cur_PackSize = 0; 1127 cur_UnpSize = 0; 1128 RINOK(lps->SetCur()) 1129 if (i >= numItems) 1130 break; 1131 1132 CMyComPtr<ISequentialOutStream> realOutStream; 1133 const Int32 askMode = testMode ? 1134 NExtract::NAskMode::kTest : 1135 NExtract::NAskMode::kExtract; 1136 const UInt32 index = allFilesMode ? i : indices[i]; 1137 RINOK(extractCallback->GetStream(index, &realOutStream, askMode)) 1138 1139 if (index < _files.Size()) 1140 { 1141 const CFile &item = _files[index]; 1142 if (item.IsDir) 1143 { 1144 RINOK(extractCallback->PrepareOperation(askMode)) 1145 realOutStream.Release(); 1146 RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK)) 1147 continue; 1148 } 1149 } 1150 1151 if (!testMode && !realOutStream) 1152 continue; 1153 RINOK(extractCallback->PrepareOperation(askMode)) 1154 1155 Int32 opRes = NExtract::NOperationResult::kOK; 1156 1157 #ifdef XAR_SHOW_RAW 1158 if (index >= _files.Size()) 1159 { 1160 cur_PackSize = cur_UnpSize = _xmlLen; 1161 if (realOutStream) 1162 RINOK(WriteStream(realOutStream, _xmlBuf, _xmlLen)) 1163 realOutStream.Release(); 1164 } 1165 else 1166 #endif 1167 { 1168 const CFile &item = _files[index]; 1169 if (!item.HasData) 1170 realOutStream.Release(); 1171 else 1172 { 1173 cur_PackSize = item.PackSize; 1174 cur_UnpSize = item.Size; 1175 1176 RINOK(InStream_SeekSet(_inStream, _dataStartPos + item.Offset)) 1177 1178 inHashStream.SetStreamAndInit(_inStream, item.archived_checksum.AlgoNumber); 1179 inHashStream.inStreamLim->Init(item.PackSize); 1180 1181 const int checksum_method = item.extracted_checksum.AlgoNumber; 1182 if (checksum_method == XAR_CKSUM_SHA1) 1183 { 1184 outStreamLim->SetStream(outStreamSha1); 1185 outStreamSha1->SetStream(realOutStream); 1186 outStreamSha1->Init(); 1187 } 1188 else if (checksum_method == XAR_CKSUM_SHA256 1189 || checksum_method == XAR_CKSUM_SHA512) 1190 { 1191 outStreamLim->SetStream(outStreamSha256); 1192 outStreamSha256->SetStream(realOutStream); 1193 outStreamSha256->Init(checksum_method == XAR_CKSUM_SHA512); 1194 } 1195 else 1196 outStreamLim->SetStream(realOutStream); 1197 1198 realOutStream.Release(); 1199 1200 // outStreamSha1->Init(item.Sha1IsDefined); 1201 1202 outStreamLim->Init(item.Size); 1203 HRESULT res = S_OK; 1204 1205 ICompressCoder *coder = NULL; 1206 if (item.IsCopyMethod()) 1207 { 1208 if (item.PackSize == item.Size) 1209 coder = copyCoder; 1210 else 1211 opRes = NExtract::NOperationResult::kUnsupportedMethod; 1212 } 1213 else if (item.Method == METHOD_NAME_ZLIB) 1214 coder = zlibCoder; 1215 else if (item.Method == "bzip2") 1216 coder = bzip2Coder; 1217 else 1218 opRes = NExtract::NOperationResult::kUnsupportedMethod; 1219 1220 if (coder) 1221 res = coder->Code(inHashStream.inStreamLim, outStreamLim, NULL, &item.Size, lps); 1222 1223 if (res != S_OK) 1224 { 1225 if (!outStreamLim->IsFinishedOK()) 1226 opRes = NExtract::NOperationResult::kDataError; 1227 else if (res != S_FALSE) 1228 return res; 1229 if (opRes == NExtract::NOperationResult::kOK) 1230 opRes = NExtract::NOperationResult::kDataError; 1231 } 1232 1233 if (opRes == NExtract::NOperationResult::kOK) 1234 { 1235 if (outStreamLim->IsFinishedOK()) 1236 { 1237 if (checksum_method == XAR_CKSUM_SHA1) 1238 { 1239 Byte digest[SHA1_DIGEST_SIZE]; 1240 outStreamSha1->Final(digest); 1241 if (memcmp(digest, item.extracted_checksum.Data, SHA1_DIGEST_SIZE) != 0) 1242 opRes = NExtract::NOperationResult::kCRCError; 1243 } 1244 else if (checksum_method == XAR_CKSUM_SHA256) 1245 { 1246 Byte digest[SHA256_DIGEST_SIZE]; 1247 outStreamSha256->Final256(digest); 1248 if (memcmp(digest, item.extracted_checksum.Data, sizeof(digest)) != 0) 1249 opRes = NExtract::NOperationResult::kCRCError; 1250 } 1251 else if (checksum_method == XAR_CKSUM_SHA512) 1252 { 1253 Byte digest[SHA512_DIGEST_SIZE]; 1254 outStreamSha256->Final512(digest); 1255 if (memcmp(digest, item.extracted_checksum.Data, sizeof(digest)) != 0) 1256 opRes = NExtract::NOperationResult::kCRCError; 1257 } 1258 if (opRes == NExtract::NOperationResult::kOK) 1259 if (!inHashStream.CheckHash( 1260 item.archived_checksum.AlgoNumber, 1261 item.archived_checksum.Data)) 1262 opRes = NExtract::NOperationResult::kCRCError; 1263 } 1264 else 1265 opRes = NExtract::NOperationResult::kDataError; 1266 } 1267 if (checksum_method == XAR_CKSUM_SHA1) 1268 outStreamSha1->ReleaseStream(); 1269 else if (checksum_method == XAR_CKSUM_SHA256) 1270 outStreamSha256->ReleaseStream(); 1271 } 1272 outStreamLim->ReleaseStream(); 1273 } 1274 RINOK(extractCallback->SetOperationResult(opRes)) 1275 } 1276 return S_OK; 1277 COM_TRY_END 1278 } 1279 1280 Z7_COM7F_IMF(CHandler::GetStream(UInt32 index, ISequentialInStream **stream)) 1281 { 1282 *stream = NULL; 1283 COM_TRY_BEGIN 1284 #ifdef XAR_SHOW_RAW 1285 if (index >= _files.Size()) 1286 { 1287 Create_BufInStream_WithNewBuffer(_xmlBuf, _xmlLen, stream); 1288 return S_OK; 1289 } 1290 else 1291 #endif 1292 { 1293 const CFile &item = _files[index]; 1294 if (item.HasData && item.IsCopyMethod() && item.PackSize == item.Size) 1295 return CreateLimitedInStream(_inStream, _dataStartPos + item.Offset, item.Size, stream); 1296 } 1297 return S_FALSE; 1298 COM_TRY_END 1299 } 1300 1301 // 0x1c == 28 is expected header size value for most archives. 1302 // but we want to support another (rare case) headers sizes. 1303 // so we must reduce signature to 4 or 5 bytes. 1304 static const Byte k_Signature[] = 1305 // { 'x', 'a', 'r', '!', 0, 0x1C, 0 }; 1306 { 'x', 'a', 'r', '!', 0 }; 1307 1308 REGISTER_ARC_I( 1309 "Xar", "xar pkg xip", NULL, 0xE1, 1310 k_Signature, 1311 0, 1312 0, 1313 NULL) 1314 1315 }} 1316