1 // RpmHandler.cpp 2 3 #include "StdAfx.h" 4 5 #include "../../../C/CpuArch.h" 6 7 #include "../../Common/MyBuffer.h" 8 #include "../../Common/ComTry.h" 9 #include "../../Common/IntToString.h" 10 #include "../../Common/StringConvert.h" 11 #include "../../Common/UTFConvert.h" 12 13 #include "../../Windows/PropVariant.h" 14 #include "../../Windows/PropVariantUtils.h" 15 #include "../../Windows/TimeUtils.h" 16 17 #include "../Common/RegisterArc.h" 18 #include "../Common/StreamUtils.h" 19 20 #include "HandlerCont.h" 21 22 // #define Z7_RPM_SHOW_METADATA 23 24 using namespace NWindows; 25 26 #define Get16(p) GetBe16(p) 27 #define Get32(p) GetBe32(p) 28 29 namespace NArchive { 30 namespace NRpm { 31 32 static const unsigned kNameSize = 66; 33 static const unsigned kLeadSize = kNameSize + 30; 34 static const unsigned k_HeaderSig_Size = 16; 35 static const unsigned k_Entry_Size = 16; 36 37 #define RPMSIG_NONE 0 // Old signature 38 #define RPMSIG_PGP262_1024 1 // Old signature 39 #define RPMSIG_HEADERSIG 5 // New signature 40 41 enum 42 { 43 kRpmType_Bin = 0, 44 kRpmType_Src = 1 45 }; 46 47 // There are two sets of TAGs: signature tags and header tags 48 49 // ----- Signature TAGs ----- 50 51 #define RPMSIGTAG_SIZE 1000 // Header + Payload size (32bit) 52 53 // ----- Header TAGs ----- 54 55 #define RPMTAG_NAME 1000 56 #define RPMTAG_VERSION 1001 57 #define RPMTAG_RELEASE 1002 58 #define RPMTAG_BUILDTIME 1006 59 #define RPMTAG_OS 1021 // string (old version used int?) 60 #define RPMTAG_ARCH 1022 // string (old version used int?) 61 #define RPMTAG_PAYLOADFORMAT 1124 62 #define RPMTAG_PAYLOADCOMPRESSOR 1125 63 // #define RPMTAG_PAYLOADFLAGS 1126 64 65 enum 66 { 67 k_EntryType_NULL, 68 k_EntryType_CHAR, 69 k_EntryType_INT8, 70 k_EntryType_INT16, 71 k_EntryType_INT32, 72 k_EntryType_INT64, 73 k_EntryType_STRING, 74 k_EntryType_BIN, 75 k_EntryType_STRING_ARRAY, 76 k_EntryType_I18NSTRING 77 }; 78 79 static const char * const k_CPUs[] = 80 { 81 "noarch" 82 , "i386" 83 , "alpha" 84 , "sparc" 85 , "mips" 86 , "ppc" 87 , "m68k" 88 , "sgi" 89 , "rs6000" 90 , "ia64" 91 , "sparc64" // 10 ??? 92 , "mipsel" 93 , "arm" 94 , "m68kmint" 95 , "s390" 96 , "s390x" 97 , "ppc64" 98 , "sh" 99 , "xtensa" 100 , "aarch64" // 19 101 , "mipsr6" // 20 102 , "mips64r6" // 21 103 , "riscv64" // 22 104 , "loongarch64" // 23 105 // , "24" 106 // , "25" 107 // , "loongarch64" // 26 : why 23 and 26 for loongarch64? 108 // 255 for some non specified arch 109 }; 110 111 static const char * const k_OS[] = 112 { 113 "0" 114 , "Linux" 115 , "Irix" 116 , "solaris" 117 , "SunOS" 118 , "AmigaOS" // AIX 119 , "HP-UX" 120 , "osf" 121 , "FreeBSD" 122 , "SCO_SV" 123 , "Irix64" 124 , "NextStep" 125 , "bsdi" 126 , "machten" 127 , "cygwin32-NT" 128 , "cygwin32-95" 129 , "MP_RAS" 130 , "MiNT" 131 , "OS/390" 132 , "VM/ESA" 133 , "Linux/390" // "Linux/ESA" 134 , "Darwin" // "MacOSX" 21 135 }; 136 137 struct CLead 138 { 139 Byte Major; 140 // Byte Minor; 141 UInt16 Type; 142 UInt16 Cpu; 143 UInt16 Os; 144 UInt16 SignatureType; 145 char Name[kNameSize]; 146 // char Reserved[16]; 147 ParseNArchive::NRpm::CLead148 void Parse(const Byte *p) 149 { 150 Major = p[4]; 151 // Minor = p[5]; 152 Type = Get16(p + 6); 153 Cpu= Get16(p + 8); 154 memcpy(Name, p + 10, kNameSize); 155 p += 10 + kNameSize; 156 Os = Get16(p); 157 SignatureType = Get16(p + 2); 158 } 159 IsSupportedNArchive::NRpm::CLead160 bool IsSupported() const { return Major >= 3 && Type <= 1; } 161 }; 162 163 struct CEntry 164 { 165 UInt32 Tag; 166 UInt32 Type; 167 UInt32 Offset; 168 UInt32 Count; 169 ParseNArchive::NRpm::CEntry170 void Parse(const Byte *p) 171 { 172 Tag = Get32(p + 0); 173 Type = Get32(p + 4); 174 Offset = Get32(p + 8); 175 Count = Get32(p + 12); 176 } 177 }; 178 179 180 #ifdef Z7_RPM_SHOW_METADATA 181 struct CMetaFile 182 { 183 UInt32 Tag; 184 UInt32 Offset; 185 UInt32 Size; 186 }; 187 #endif 188 189 Z7_class_CHandler_final: public CHandlerCont 190 { 191 Z7_IFACE_COM7_IMP(IInArchive_Cont) 192 193 UInt64 _headersSize; // is equal to start offset of payload data 194 UInt64 _payloadSize; 195 UInt64 _size; 196 // _size = _payloadSize, if (_payloadSize_Defined) 197 // _size = (fileSize - _headersSize), if (!_payloadSize_Defined) 198 UInt64 _phySize; // _headersSize + _payloadSize, if (_phySize_Defined) 199 UInt32 _headerPlusPayload_Size; 200 UInt32 _buildTime; 201 202 bool _payloadSize_Defined; 203 bool _phySize_Defined; 204 bool _headerPlusPayload_Size_Defined; 205 bool _time_Defined; 206 207 Byte _payloadSig[6]; // start data of payload 208 209 AString _name; // p7zip 210 AString _version; // 9.20.1 211 AString _release; // 8.1.1 212 AString _arch; // x86_64 213 AString _os; // linux 214 215 AString _format; // cpio 216 AString _compressor; // xz, gzip, bzip2, lzma, zstd 217 218 CLead _lead; 219 220 #ifdef Z7_RPM_SHOW_METADATA 221 AString _metadata; 222 CRecordVector<CMetaFile> _metaFiles; 223 #endif 224 225 void SetTime(NCOM::CPropVariant &prop) const 226 { 227 if (_time_Defined && _buildTime != 0) 228 PropVariant_SetFrom_UnixTime(prop, _buildTime); 229 } 230 231 void SetStringProp(const AString &s, NCOM::CPropVariant &prop) const 232 { 233 UString us; 234 if (!ConvertUTF8ToUnicode(s, us)) 235 us = GetUnicodeString(s); 236 if (!us.IsEmpty()) 237 prop = us; 238 } 239 240 void AddCPU(AString &s) const; 241 AString GetBaseName() const; 242 void AddSubFileExtension(AString &res) const; 243 244 HRESULT ReadHeader(ISequentialInStream *stream, bool isMainHeader); 245 HRESULT Open2(ISequentialInStream *stream); 246 247 virtual int GetItem_ExtractInfo(UInt32 /* index */, UInt64 &pos, UInt64 &size) const Z7_override 248 { 249 pos = _headersSize; 250 size = _size; 251 return NExtract::NOperationResult::kOK; 252 } 253 }; 254 255 static const Byte kArcProps[] = 256 { 257 kpidHeadersSize, 258 kpidCpu, 259 kpidHostOS, 260 kpidCTime 261 #ifdef Z7_RPM_SHOW_METADATA 262 , kpidComment 263 #endif 264 }; 265 266 static const Byte kProps[] = 267 { 268 kpidPath, 269 kpidSize, 270 kpidCTime 271 }; 272 273 IMP_IInArchive_Props 274 IMP_IInArchive_ArcProps 275 276 void CHandler::AddCPU(AString &s) const 277 { 278 if (!_arch.IsEmpty()) 279 s += _arch; 280 else 281 { 282 if (_lead.Type == kRpmType_Bin) 283 { 284 if (_lead.Cpu < Z7_ARRAY_SIZE(k_CPUs)) 285 s += k_CPUs[_lead.Cpu]; 286 else 287 s.Add_UInt32(_lead.Cpu); 288 } 289 } 290 } 291 292 AString CHandler::GetBaseName() const 293 { 294 AString s; 295 if (!_name.IsEmpty()) 296 { 297 s = _name; 298 if (!_version.IsEmpty()) 299 { 300 s.Add_Minus(); 301 s += _version; 302 } 303 if (!_release.IsEmpty()) 304 { 305 s.Add_Minus(); 306 s += _release; 307 } 308 } 309 else 310 s.SetFrom_CalcLen(_lead.Name, kNameSize); 311 312 s.Add_Dot(); 313 if (_lead.Type == kRpmType_Src) 314 s += "src"; 315 else 316 AddCPU(s); 317 return s; 318 } 319 320 void CHandler::AddSubFileExtension(AString &res) const 321 { 322 if (!_format.IsEmpty()) 323 res += _format; 324 else 325 res += "cpio"; 326 res.Add_Dot(); 327 328 const char *s; 329 330 if (!_compressor.IsEmpty()) 331 { 332 s = _compressor; 333 if (_compressor == "bzip2") 334 s = "bz2"; 335 else if (_compressor == "gzip") 336 s = "gz"; 337 else if (_compressor == "zstd") 338 s = "zst"; 339 } 340 else 341 { 342 const Byte *p = _payloadSig; 343 if (p[0] == 0x1F && p[1] == 0x8B && p[2] == 8) 344 s = "gz"; 345 else if (p[0] == 0xFD && p[1] == '7' && p[2] == 'z' && p[3] == 'X' && p[4] == 'Z' && p[5] == 0) 346 s = "xz"; 347 else if (p[0] == 'B' && p[1] == 'Z' && p[2] == 'h' && p[3] >= '1' && p[3] <= '9') 348 s = "bz2"; 349 else if (p[0] == 0x28 && p[1] == 0xb5 && p[2] == 0x2f && p[3] == 0xfd) 350 s = "zst"; 351 else 352 s = "lzma"; 353 } 354 355 res += s; 356 } 357 358 Z7_COM7F_IMF(CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)) 359 { 360 COM_TRY_BEGIN 361 NCOM::CPropVariant prop; 362 switch (propID) 363 { 364 case kpidMainSubfile: prop = (UInt32)0; break; 365 366 case kpidHeadersSize: prop = _headersSize; break; 367 case kpidPhySize: if (_phySize_Defined) prop = _phySize; break; 368 369 case kpidMTime: 370 case kpidCTime: 371 SetTime(prop); 372 break; 373 374 case kpidCpu: 375 { 376 AString s; 377 AddCPU(s); 378 /* 379 if (_lead.Type == kRpmType_Src) 380 s = "src"; 381 */ 382 SetStringProp(s, prop); 383 break; 384 } 385 386 case kpidHostOS: 387 { 388 if (!_os.IsEmpty()) 389 SetStringProp(_os, prop); 390 else 391 { 392 TYPE_TO_PROP(k_OS, _lead.Os, prop); 393 } 394 break; 395 } 396 397 #ifdef Z7_RPM_SHOW_METADATA 398 // case kpidComment: SetStringProp(_metadata, prop); break; 399 #endif 400 401 case kpidName: 402 { 403 SetStringProp(GetBaseName() + ".rpm", prop); 404 break; 405 } 406 } 407 prop.Detach(value); 408 return S_OK; 409 COM_TRY_END 410 } 411 412 413 Z7_COM7F_IMF(CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)) 414 { 415 NWindows::NCOM::CPropVariant prop; 416 if (index == 0) 417 switch (propID) 418 { 419 case kpidSize: 420 case kpidPackSize: 421 prop = _size; 422 break; 423 424 case kpidMTime: 425 case kpidCTime: 426 SetTime(prop); 427 break; 428 429 case kpidPath: 430 { 431 AString s (GetBaseName()); 432 s.Add_Dot(); 433 AddSubFileExtension(s); 434 SetStringProp(s, prop); 435 break; 436 } 437 438 /* 439 case kpidExtension: 440 { 441 prop = GetSubFileExtension(); 442 break; 443 } 444 */ 445 } 446 #ifdef Z7_RPM_SHOW_METADATA 447 else 448 { 449 index--; 450 if (index > _metaFiles.Size()) 451 return E_INVALIDARG; 452 const CMetaFile &meta = _metaFiles[index]; 453 switch (propID) 454 { 455 case kpidSize: 456 case kpidPackSize: 457 prop = meta.Size; 458 break; 459 460 case kpidMTime: 461 case kpidCTime: 462 SetTime(prop); 463 break; 464 465 case kpidPath: 466 { 467 AString s ("[META]"); 468 s.Add_PathSepar(); 469 s.Add_UInt32(meta.Tag); 470 prop = s; 471 break; 472 } 473 } 474 } 475 #endif 476 477 prop.Detach(value); 478 return S_OK; 479 } 480 481 482 HRESULT CHandler::ReadHeader(ISequentialInStream *stream, bool isMainHeader) 483 { 484 UInt32 numEntries; 485 UInt32 dataLen; 486 { 487 char buf[k_HeaderSig_Size]; 488 RINOK(ReadStream_FALSE(stream, buf, k_HeaderSig_Size)) 489 if (Get32(buf) != 0x8EADE801) // buf[3] = 0x01 - is version 490 return S_FALSE; 491 // reserved = Get32(buf + 4); 492 numEntries = Get32(buf + 8); 493 dataLen = Get32(buf + 12); 494 if (numEntries >= 1 << 24) 495 return S_FALSE; 496 } 497 size_t indexSize = (size_t)numEntries * k_Entry_Size; 498 size_t headerSize = indexSize + dataLen; 499 if (headerSize < dataLen) 500 return S_FALSE; 501 CByteBuffer buffer(headerSize); 502 RINOK(ReadStream_FALSE(stream, buffer, headerSize)) 503 504 for (UInt32 i = 0; i < numEntries; i++) 505 { 506 CEntry entry; 507 508 entry.Parse(buffer + (size_t)i * k_Entry_Size); 509 if (entry.Offset > dataLen) 510 return S_FALSE; 511 512 const Byte *p = buffer + indexSize + entry.Offset; 513 size_t rem = dataLen - entry.Offset; 514 515 if (!isMainHeader) 516 { 517 if (entry.Tag == RPMSIGTAG_SIZE && 518 entry.Type == k_EntryType_INT32) 519 { 520 if (rem < 4 || entry.Count != 1) 521 return S_FALSE; 522 _headerPlusPayload_Size = Get32(p); 523 _headerPlusPayload_Size_Defined = true; 524 } 525 } 526 else 527 { 528 #ifdef Z7_RPM_SHOW_METADATA 529 { 530 _metadata.Add_UInt32(entry.Tag); 531 _metadata += ": "; 532 } 533 #endif 534 535 if (entry.Type == k_EntryType_STRING) 536 { 537 if (entry.Count != 1) 538 return S_FALSE; 539 size_t j; 540 for (j = 0; j < rem && p[j] != 0; j++); 541 if (j == rem) 542 return S_FALSE; 543 AString s((const char *)p); 544 switch (entry.Tag) 545 { 546 case RPMTAG_NAME: _name = s; break; 547 case RPMTAG_VERSION: _version = s; break; 548 case RPMTAG_RELEASE: _release = s; break; 549 case RPMTAG_ARCH: _arch = s; break; 550 case RPMTAG_OS: _os = s; break; 551 case RPMTAG_PAYLOADFORMAT: _format = s; break; 552 case RPMTAG_PAYLOADCOMPRESSOR: _compressor = s; break; 553 } 554 555 #ifdef Z7_RPM_SHOW_METADATA 556 _metadata += s; 557 #endif 558 } 559 else if (entry.Type == k_EntryType_INT32) 560 { 561 if (rem / 4 < entry.Count) 562 return S_FALSE; 563 if (entry.Tag == RPMTAG_BUILDTIME) 564 { 565 if (entry.Count != 1) 566 return S_FALSE; 567 _buildTime = Get32(p); 568 _time_Defined = true; 569 } 570 571 #ifdef Z7_RPM_SHOW_METADATA 572 for (UInt32 t = 0; t < entry.Count; t++) 573 { 574 if (t != 0) 575 _metadata.Add_Space(); 576 _metadata.Add_UInt32(Get32(p + t * 4)); 577 } 578 #endif 579 } 580 581 #ifdef Z7_RPM_SHOW_METADATA 582 583 else if ( 584 entry.Type == k_EntryType_STRING_ARRAY || 585 entry.Type == k_EntryType_I18NSTRING) 586 { 587 const Byte *p2 = p; 588 size_t rem2 = rem; 589 for (UInt32 t = 0; t < entry.Count; t++) 590 { 591 if (rem2 == 0) 592 return S_FALSE; 593 if (t != 0) 594 _metadata.Add_LF(); 595 size_t j; 596 for (j = 0; j < rem2 && p2[j] != 0; j++); 597 if (j == rem2) 598 return S_FALSE; 599 _metadata += (const char *)p2; 600 j++; 601 p2 += j; 602 rem2 -= j; 603 } 604 } 605 else if (entry.Type == k_EntryType_INT16) 606 { 607 if (rem / 2 < entry.Count) 608 return S_FALSE; 609 for (UInt32 t = 0; t < entry.Count; t++) 610 { 611 if (t != 0) 612 _metadata.Add_Space(); 613 _metadata.Add_UInt32(Get16(p + t * 2)); 614 } 615 } 616 else if (entry.Type == k_EntryType_BIN) 617 { 618 if (rem < entry.Count) 619 return S_FALSE; 620 for (UInt32 t = 0; t < entry.Count; t++) 621 { 622 const unsigned b = p[t]; 623 _metadata += GET_HEX_CHAR_UPPER(b >> 4); 624 _metadata += GET_HEX_CHAR_UPPER(b & 0xF); 625 } 626 } 627 else 628 { 629 // p = p; 630 } 631 632 _metadata.Add_LF(); 633 #endif 634 } 635 636 #ifdef Z7_RPM_SHOW_METADATA 637 CMetaFile meta; 638 meta.Offset = entry.Offset; 639 meta.Tag = entry.Tag; 640 meta.Size = entry.Count; 641 _metaFiles.Add(meta); 642 #endif 643 } 644 645 headerSize += k_HeaderSig_Size; 646 _headersSize += headerSize; 647 if (isMainHeader && _headerPlusPayload_Size_Defined) 648 { 649 if (_headerPlusPayload_Size < headerSize) 650 return S_FALSE; 651 _payloadSize = _headerPlusPayload_Size - headerSize; 652 _size = _payloadSize; 653 _phySize = _headersSize + _payloadSize; 654 _payloadSize_Defined = true; 655 _phySize_Defined = true; 656 } 657 return S_OK; 658 } 659 660 HRESULT CHandler::Open2(ISequentialInStream *stream) 661 { 662 { 663 Byte buf[kLeadSize]; 664 RINOK(ReadStream_FALSE(stream, buf, kLeadSize)) 665 if (Get32(buf) != 0xEDABEEDB) 666 return S_FALSE; 667 _lead.Parse(buf); 668 if (!_lead.IsSupported()) 669 return S_FALSE; 670 } 671 672 _headersSize = kLeadSize; 673 674 if (_lead.SignatureType == RPMSIG_NONE) 675 { 676 677 } 678 else if (_lead.SignatureType == RPMSIG_PGP262_1024) 679 { 680 Byte temp[256]; 681 RINOK(ReadStream_FALSE(stream, temp, sizeof(temp))) 682 } 683 else if (_lead.SignatureType == RPMSIG_HEADERSIG) 684 { 685 RINOK(ReadHeader(stream, false)) 686 unsigned pos = (unsigned)_headersSize & 7; 687 if (pos != 0) 688 { 689 Byte temp[8]; 690 unsigned num = 8 - pos; 691 RINOK(ReadStream_FALSE(stream, temp, num)) 692 _headersSize += num; 693 } 694 } 695 else 696 return S_FALSE; 697 698 return ReadHeader(stream, true); 699 } 700 701 702 Z7_COM7F_IMF(CHandler::Open(IInStream *inStream, const UInt64 *, IArchiveOpenCallback *)) 703 { 704 COM_TRY_BEGIN 705 { 706 Close(); 707 RINOK(Open2(inStream)) 708 709 // start of payload is allowed to be unaligned 710 RINOK(ReadStream_FALSE(inStream, _payloadSig, sizeof(_payloadSig))) 711 712 if (!_payloadSize_Defined) 713 { 714 UInt64 endPos; 715 RINOK(InStream_GetSize_SeekToEnd(inStream, endPos)) 716 _size = endPos - _headersSize; 717 } 718 _stream = inStream; 719 return S_OK; 720 } 721 COM_TRY_END 722 } 723 724 Z7_COM7F_IMF(CHandler::Close()) 725 { 726 _headersSize = 0; 727 _payloadSize = 0; 728 _size = 0; 729 _phySize = 0; 730 _headerPlusPayload_Size = 0; 731 732 _payloadSize_Defined = false; 733 _phySize_Defined = false; 734 _headerPlusPayload_Size_Defined = false; 735 _time_Defined = false; 736 737 _name.Empty(); 738 _version.Empty(); 739 _release.Empty(); 740 _arch.Empty(); 741 _os.Empty(); 742 743 _format.Empty(); 744 _compressor.Empty(); 745 746 #ifdef Z7_RPM_SHOW_METADATA 747 _metadata.Empty(); 748 _metaFiles.Size(); 749 #endif 750 751 _stream.Release(); 752 return S_OK; 753 } 754 755 Z7_COM7F_IMF(CHandler::GetNumberOfItems(UInt32 *numItems)) 756 { 757 *numItems = 1 758 #ifdef Z7_RPM_SHOW_METADATA 759 + _metaFiles.Size() 760 #endif 761 ; 762 763 return S_OK; 764 } 765 766 static const Byte k_Signature[] = { 0xED, 0xAB, 0xEE, 0xDB}; 767 768 REGISTER_ARC_I( 769 "Rpm", "rpm", NULL, 0xEB, 770 k_Signature, 771 0, 772 0, 773 NULL) 774 775 }} 776