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