// RpmHandler.cpp #include "StdAfx.h" #include "../../../C/CpuArch.h" #include "../../Common/MyBuffer.h" #include "../../Common/ComTry.h" #include "../../Common/IntToString.h" #include "../../Common/StringConvert.h" #include "../../Common/UTFConvert.h" #include "../../Windows/PropVariant.h" #include "../../Windows/PropVariantUtils.h" #include "../../Windows/TimeUtils.h" #include "../Common/RegisterArc.h" #include "../Common/StreamUtils.h" #include "HandlerCont.h" // #define Z7_RPM_SHOW_METADATA using namespace NWindows; #define Get16(p) GetBe16(p) #define Get32(p) GetBe32(p) namespace NArchive { namespace NRpm { static const unsigned kNameSize = 66; static const unsigned kLeadSize = kNameSize + 30; static const unsigned k_HeaderSig_Size = 16; static const unsigned k_Entry_Size = 16; #define RPMSIG_NONE 0 // Old signature #define RPMSIG_PGP262_1024 1 // Old signature #define RPMSIG_HEADERSIG 5 // New signature enum { kRpmType_Bin = 0, kRpmType_Src = 1 }; // There are two sets of TAGs: signature tags and header tags // ----- Signature TAGs ----- #define RPMSIGTAG_SIZE 1000 // Header + Payload size (32bit) // ----- Header TAGs ----- #define RPMTAG_NAME 1000 #define RPMTAG_VERSION 1001 #define RPMTAG_RELEASE 1002 #define RPMTAG_BUILDTIME 1006 #define RPMTAG_OS 1021 // string (old version used int?) #define RPMTAG_ARCH 1022 // string (old version used int?) #define RPMTAG_PAYLOADFORMAT 1124 #define RPMTAG_PAYLOADCOMPRESSOR 1125 // #define RPMTAG_PAYLOADFLAGS 1126 enum { k_EntryType_NULL, k_EntryType_CHAR, k_EntryType_INT8, k_EntryType_INT16, k_EntryType_INT32, k_EntryType_INT64, k_EntryType_STRING, k_EntryType_BIN, k_EntryType_STRING_ARRAY, k_EntryType_I18NSTRING }; static const char * const k_CPUs[] = { "noarch" , "i386" , "alpha" , "sparc" , "mips" , "ppc" , "m68k" , "sgi" , "rs6000" , "ia64" , "sparc64" // 10 ??? , "mipsel" , "arm" , "m68kmint" , "s390" , "s390x" , "ppc64" , "sh" , "xtensa" , "aarch64" // 19 , "mipsr6" // 20 , "mips64r6" // 21 , "riscv64" // 22 , "loongarch64" // 23 // , "24" // , "25" // , "loongarch64" // 26 : why 23 and 26 for loongarch64? // 255 for some non specified arch }; static const char * const k_OS[] = { "0" , "Linux" , "Irix" , "solaris" , "SunOS" , "AmigaOS" // AIX , "HP-UX" , "osf" , "FreeBSD" , "SCO_SV" , "Irix64" , "NextStep" , "bsdi" , "machten" , "cygwin32-NT" , "cygwin32-95" , "MP_RAS" , "MiNT" , "OS/390" , "VM/ESA" , "Linux/390" // "Linux/ESA" , "Darwin" // "MacOSX" 21 }; struct CLead { Byte Major; // Byte Minor; UInt16 Type; UInt16 Cpu; UInt16 Os; UInt16 SignatureType; char Name[kNameSize]; // char Reserved[16]; void Parse(const Byte *p) { Major = p[4]; // Minor = p[5]; Type = Get16(p + 6); Cpu= Get16(p + 8); memcpy(Name, p + 10, kNameSize); p += 10 + kNameSize; Os = Get16(p); SignatureType = Get16(p + 2); } bool IsSupported() const { return Major >= 3 && Type <= 1; } }; struct CEntry { UInt32 Tag; UInt32 Type; UInt32 Offset; UInt32 Count; void Parse(const Byte *p) { Tag = Get32(p + 0); Type = Get32(p + 4); Offset = Get32(p + 8); Count = Get32(p + 12); } }; #ifdef Z7_RPM_SHOW_METADATA struct CMetaFile { UInt32 Tag; UInt32 Offset; UInt32 Size; }; #endif Z7_class_CHandler_final: public CHandlerCont { Z7_IFACE_COM7_IMP(IInArchive_Cont) UInt64 _headersSize; // is equal to start offset of payload data UInt64 _payloadSize; UInt64 _size; // _size = _payloadSize, if (_payloadSize_Defined) // _size = (fileSize - _headersSize), if (!_payloadSize_Defined) UInt64 _phySize; // _headersSize + _payloadSize, if (_phySize_Defined) UInt32 _headerPlusPayload_Size; UInt32 _buildTime; bool _payloadSize_Defined; bool _phySize_Defined; bool _headerPlusPayload_Size_Defined; bool _time_Defined; Byte _payloadSig[6]; // start data of payload AString _name; // p7zip AString _version; // 9.20.1 AString _release; // 8.1.1 AString _arch; // x86_64 AString _os; // linux AString _format; // cpio AString _compressor; // xz, gzip, bzip2, lzma, zstd CLead _lead; #ifdef Z7_RPM_SHOW_METADATA AString _metadata; CRecordVector _metaFiles; #endif void SetTime(NCOM::CPropVariant &prop) const { if (_time_Defined && _buildTime != 0) PropVariant_SetFrom_UnixTime(prop, _buildTime); } void SetStringProp(const AString &s, NCOM::CPropVariant &prop) const { UString us; if (!ConvertUTF8ToUnicode(s, us)) us = GetUnicodeString(s); if (!us.IsEmpty()) prop = us; } void AddCPU(AString &s) const; AString GetBaseName() const; void AddSubFileExtension(AString &res) const; HRESULT ReadHeader(ISequentialInStream *stream, bool isMainHeader); HRESULT Open2(ISequentialInStream *stream); virtual int GetItem_ExtractInfo(UInt32 /* index */, UInt64 &pos, UInt64 &size) const Z7_override { pos = _headersSize; size = _size; return NExtract::NOperationResult::kOK; } }; static const Byte kArcProps[] = { kpidHeadersSize, kpidCpu, kpidHostOS, kpidCTime #ifdef Z7_RPM_SHOW_METADATA , kpidComment #endif }; static const Byte kProps[] = { kpidPath, kpidSize, kpidCTime }; IMP_IInArchive_Props IMP_IInArchive_ArcProps void CHandler::AddCPU(AString &s) const { if (!_arch.IsEmpty()) s += _arch; else { if (_lead.Type == kRpmType_Bin) { if (_lead.Cpu < Z7_ARRAY_SIZE(k_CPUs)) s += k_CPUs[_lead.Cpu]; else s.Add_UInt32(_lead.Cpu); } } } AString CHandler::GetBaseName() const { AString s; if (!_name.IsEmpty()) { s = _name; if (!_version.IsEmpty()) { s.Add_Minus(); s += _version; } if (!_release.IsEmpty()) { s.Add_Minus(); s += _release; } } else s.SetFrom_CalcLen(_lead.Name, kNameSize); s.Add_Dot(); if (_lead.Type == kRpmType_Src) s += "src"; else AddCPU(s); return s; } void CHandler::AddSubFileExtension(AString &res) const { if (!_format.IsEmpty()) res += _format; else res += "cpio"; res.Add_Dot(); const char *s; if (!_compressor.IsEmpty()) { s = _compressor; if (_compressor == "bzip2") s = "bz2"; else if (_compressor == "gzip") s = "gz"; else if (_compressor == "zstd") s = "zst"; } else { const Byte *p = _payloadSig; if (p[0] == 0x1F && p[1] == 0x8B && p[2] == 8) s = "gz"; else if (p[0] == 0xFD && p[1] == '7' && p[2] == 'z' && p[3] == 'X' && p[4] == 'Z' && p[5] == 0) s = "xz"; else if (p[0] == 'B' && p[1] == 'Z' && p[2] == 'h' && p[3] >= '1' && p[3] <= '9') s = "bz2"; else if (p[0] == 0x28 && p[1] == 0xb5 && p[2] == 0x2f && p[3] == 0xfd) s = "zst"; else s = "lzma"; } res += s; } Z7_COM7F_IMF(CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value)) { COM_TRY_BEGIN NCOM::CPropVariant prop; switch (propID) { case kpidMainSubfile: prop = (UInt32)0; break; case kpidHeadersSize: prop = _headersSize; break; case kpidPhySize: if (_phySize_Defined) prop = _phySize; break; case kpidMTime: case kpidCTime: SetTime(prop); break; case kpidCpu: { AString s; AddCPU(s); /* if (_lead.Type == kRpmType_Src) s = "src"; */ SetStringProp(s, prop); break; } case kpidHostOS: { if (!_os.IsEmpty()) SetStringProp(_os, prop); else { TYPE_TO_PROP(k_OS, _lead.Os, prop); } break; } #ifdef Z7_RPM_SHOW_METADATA // case kpidComment: SetStringProp(_metadata, prop); break; #endif case kpidName: { SetStringProp(GetBaseName() + ".rpm", prop); break; } } prop.Detach(value); return S_OK; COM_TRY_END } Z7_COM7F_IMF(CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)) { NWindows::NCOM::CPropVariant prop; if (index == 0) switch (propID) { case kpidSize: case kpidPackSize: prop = _size; break; case kpidMTime: case kpidCTime: SetTime(prop); break; case kpidPath: { AString s (GetBaseName()); s.Add_Dot(); AddSubFileExtension(s); SetStringProp(s, prop); break; } /* case kpidExtension: { prop = GetSubFileExtension(); break; } */ } #ifdef Z7_RPM_SHOW_METADATA else { index--; if (index > _metaFiles.Size()) return E_INVALIDARG; const CMetaFile &meta = _metaFiles[index]; switch (propID) { case kpidSize: case kpidPackSize: prop = meta.Size; break; case kpidMTime: case kpidCTime: SetTime(prop); break; case kpidPath: { AString s ("[META]"); s.Add_PathSepar(); s.Add_UInt32(meta.Tag); prop = s; break; } } } #endif prop.Detach(value); return S_OK; } HRESULT CHandler::ReadHeader(ISequentialInStream *stream, bool isMainHeader) { UInt32 numEntries; UInt32 dataLen; { char buf[k_HeaderSig_Size]; RINOK(ReadStream_FALSE(stream, buf, k_HeaderSig_Size)) if (Get32(buf) != 0x8EADE801) // buf[3] = 0x01 - is version return S_FALSE; // reserved = Get32(buf + 4); numEntries = Get32(buf + 8); dataLen = Get32(buf + 12); if (numEntries >= 1 << 24) return S_FALSE; } size_t indexSize = (size_t)numEntries * k_Entry_Size; size_t headerSize = indexSize + dataLen; if (headerSize < dataLen) return S_FALSE; CByteBuffer buffer(headerSize); RINOK(ReadStream_FALSE(stream, buffer, headerSize)) for (UInt32 i = 0; i < numEntries; i++) { CEntry entry; entry.Parse(buffer + (size_t)i * k_Entry_Size); if (entry.Offset > dataLen) return S_FALSE; const Byte *p = buffer + indexSize + entry.Offset; size_t rem = dataLen - entry.Offset; if (!isMainHeader) { if (entry.Tag == RPMSIGTAG_SIZE && entry.Type == k_EntryType_INT32) { if (rem < 4 || entry.Count != 1) return S_FALSE; _headerPlusPayload_Size = Get32(p); _headerPlusPayload_Size_Defined = true; } } else { #ifdef Z7_RPM_SHOW_METADATA { _metadata.Add_UInt32(entry.Tag); _metadata += ": "; } #endif if (entry.Type == k_EntryType_STRING) { if (entry.Count != 1) return S_FALSE; size_t j; for (j = 0; j < rem && p[j] != 0; j++); if (j == rem) return S_FALSE; AString s((const char *)p); switch (entry.Tag) { case RPMTAG_NAME: _name = s; break; case RPMTAG_VERSION: _version = s; break; case RPMTAG_RELEASE: _release = s; break; case RPMTAG_ARCH: _arch = s; break; case RPMTAG_OS: _os = s; break; case RPMTAG_PAYLOADFORMAT: _format = s; break; case RPMTAG_PAYLOADCOMPRESSOR: _compressor = s; break; } #ifdef Z7_RPM_SHOW_METADATA _metadata += s; #endif } else if (entry.Type == k_EntryType_INT32) { if (rem / 4 < entry.Count) return S_FALSE; if (entry.Tag == RPMTAG_BUILDTIME) { if (entry.Count != 1) return S_FALSE; _buildTime = Get32(p); _time_Defined = true; } #ifdef Z7_RPM_SHOW_METADATA for (UInt32 t = 0; t < entry.Count; t++) { if (t != 0) _metadata.Add_Space(); _metadata.Add_UInt32(Get32(p + t * 4)); } #endif } #ifdef Z7_RPM_SHOW_METADATA else if ( entry.Type == k_EntryType_STRING_ARRAY || entry.Type == k_EntryType_I18NSTRING) { const Byte *p2 = p; size_t rem2 = rem; for (UInt32 t = 0; t < entry.Count; t++) { if (rem2 == 0) return S_FALSE; if (t != 0) _metadata.Add_LF(); size_t j; for (j = 0; j < rem2 && p2[j] != 0; j++); if (j == rem2) return S_FALSE; _metadata += (const char *)p2; j++; p2 += j; rem2 -= j; } } else if (entry.Type == k_EntryType_INT16) { if (rem / 2 < entry.Count) return S_FALSE; for (UInt32 t = 0; t < entry.Count; t++) { if (t != 0) _metadata.Add_Space(); _metadata.Add_UInt32(Get16(p + t * 2)); } } else if (entry.Type == k_EntryType_BIN) { if (rem < entry.Count) return S_FALSE; for (UInt32 t = 0; t < entry.Count; t++) { const unsigned b = p[t]; _metadata += GET_HEX_CHAR_UPPER(b >> 4); _metadata += GET_HEX_CHAR_UPPER(b & 0xF); } } else { // p = p; } _metadata.Add_LF(); #endif } #ifdef Z7_RPM_SHOW_METADATA CMetaFile meta; meta.Offset = entry.Offset; meta.Tag = entry.Tag; meta.Size = entry.Count; _metaFiles.Add(meta); #endif } headerSize += k_HeaderSig_Size; _headersSize += headerSize; if (isMainHeader && _headerPlusPayload_Size_Defined) { if (_headerPlusPayload_Size < headerSize) return S_FALSE; _payloadSize = _headerPlusPayload_Size - headerSize; _size = _payloadSize; _phySize = _headersSize + _payloadSize; _payloadSize_Defined = true; _phySize_Defined = true; } return S_OK; } HRESULT CHandler::Open2(ISequentialInStream *stream) { { Byte buf[kLeadSize]; RINOK(ReadStream_FALSE(stream, buf, kLeadSize)) if (Get32(buf) != 0xEDABEEDB) return S_FALSE; _lead.Parse(buf); if (!_lead.IsSupported()) return S_FALSE; } _headersSize = kLeadSize; if (_lead.SignatureType == RPMSIG_NONE) { } else if (_lead.SignatureType == RPMSIG_PGP262_1024) { Byte temp[256]; RINOK(ReadStream_FALSE(stream, temp, sizeof(temp))) } else if (_lead.SignatureType == RPMSIG_HEADERSIG) { RINOK(ReadHeader(stream, false)) unsigned pos = (unsigned)_headersSize & 7; if (pos != 0) { Byte temp[8]; unsigned num = 8 - pos; RINOK(ReadStream_FALSE(stream, temp, num)) _headersSize += num; } } else return S_FALSE; return ReadHeader(stream, true); } Z7_COM7F_IMF(CHandler::Open(IInStream *inStream, const UInt64 *, IArchiveOpenCallback *)) { COM_TRY_BEGIN { Close(); RINOK(Open2(inStream)) // start of payload is allowed to be unaligned RINOK(ReadStream_FALSE(inStream, _payloadSig, sizeof(_payloadSig))) if (!_payloadSize_Defined) { UInt64 endPos; RINOK(InStream_GetSize_SeekToEnd(inStream, endPos)) _size = endPos - _headersSize; } _stream = inStream; return S_OK; } COM_TRY_END } Z7_COM7F_IMF(CHandler::Close()) { _headersSize = 0; _payloadSize = 0; _size = 0; _phySize = 0; _headerPlusPayload_Size = 0; _payloadSize_Defined = false; _phySize_Defined = false; _headerPlusPayload_Size_Defined = false; _time_Defined = false; _name.Empty(); _version.Empty(); _release.Empty(); _arch.Empty(); _os.Empty(); _format.Empty(); _compressor.Empty(); #ifdef Z7_RPM_SHOW_METADATA _metadata.Empty(); _metaFiles.Size(); #endif _stream.Release(); return S_OK; } Z7_COM7F_IMF(CHandler::GetNumberOfItems(UInt32 *numItems)) { *numItems = 1 #ifdef Z7_RPM_SHOW_METADATA + _metaFiles.Size() #endif ; return S_OK; } static const Byte k_Signature[] = { 0xED, 0xAB, 0xEE, 0xDB}; REGISTER_ARC_I( "Rpm", "rpm", NULL, 0xEB, k_Signature, 0, 0, NULL) }}