// 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/TimeUtils.h" #include "../Common/RegisterArc.h" #include "../Common/StreamUtils.h" #include "HandlerCont.h" // #define _SHOW_RPM_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 }; 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 { unsigned char Major; unsigned char 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); } }; class CHandler: public CHandlerCont { 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 CLead _lead; #ifdef _SHOW_RPM_METADATA AString _metadata; #endif void SetTime(NCOM::CPropVariant &prop) const { if (_time_Defined && _buildTime != 0) { FILETIME ft; if (NTime::UnixTime64ToFileTime(_buildTime, ft)) prop = ft; } } 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 { pos = _headersSize; size = _size; return NExtract::NOperationResult::kOK; } public: INTERFACE_IInArchive_Cont(;) }; static const Byte kArcProps[] = { kpidHeadersSize, kpidCpu, kpidHostOS, kpidCTime #ifdef _SHOW_RPM_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) { char temp[16]; const char *p; if (_lead.Cpu < ARRAY_SIZE(k_CPUs)) p = k_CPUs[_lead.Cpu]; else { ConvertUInt32ToString(_lead.Cpu, temp); p = temp; } s += p; } } } AString CHandler::GetBaseName() const { AString s; if (!_name.IsEmpty()) { s = _name; if (!_version.IsEmpty()) { s += '-'; s += _version; } if (!_release.IsEmpty()) { s += '-'; s += _release; } } else s.SetFrom_CalcLen(_lead.Name, kNameSize); s += '.'; 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 += '.'; const char *s; if (!_compressor.IsEmpty()) { s = _compressor; if (_compressor == "bzip2") s = "bz2"; else if (_compressor == "gzip") s = "gz"; } else { const Byte *p = _payloadSig; if (p[0] == 0x1F && p[1] == 0x8B) 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 s = "lzma"; } res += s; } STDMETHODIMP 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 { char temp[16]; const char *p; if (_lead.Os < ARRAY_SIZE(k_OS)) p = k_OS[_lead.Os]; else { ConvertUInt32ToString(_lead.Os, temp); p = temp; } prop = p; } break; } #ifdef _SHOW_RPM_METADATA case kpidComment: SetStringProp(_metadata, prop); break; #endif case kpidName: { AString s = GetBaseName(); s += ".rpm"; SetStringProp(s, prop); break; } } prop.Detach(value); return S_OK; COM_TRY_END } STDMETHODIMP CHandler::GetProperty(UInt32 /* index */, PROPID propID, PROPVARIANT *value) { NWindows::NCOM::CPropVariant prop; switch (propID) { case kpidSize: case kpidPackSize: prop = _size; break; case kpidMTime: case kpidCTime: SetTime(prop); break; case kpidPath: { AString s = GetBaseName(); s += '.'; AddSubFileExtension(s); SetStringProp(s, prop); break; } /* case kpidExtension: { prop = GetSubFileExtension(); break; } */ } prop.Detach(value); return S_OK; } #ifdef _SHOW_RPM_METADATA static inline char GetHex(unsigned value) { return (char)((value < 10) ? ('0' + value) : ('A' + (value - 10))); } #endif 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 _SHOW_RPM_METADATA { char temp[16]; ConvertUInt32ToString(entry.Tag, temp); _metadata += temp; _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 _SHOW_RPM_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 _SHOW_RPM_METADATA for (UInt32 t = 0; t < entry.Count; t++) { if (t != 0) _metadata.Add_Space(); char temp[16]; ConvertUInt32ToString(Get32(p + t * 4), temp); _metadata += temp; } #endif } #ifdef _SHOW_RPM_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 += '\n'; 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(); char temp[16]; ConvertUInt32ToString(Get16(p + t * 2), temp); _metadata += temp; } } 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 += GetHex((b >> 4) & 0xF); _metadata += GetHex(b & 0xF); } } else { // p = p; } _metadata += '\n'; #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); } STDMETHODIMP 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->Seek(0, STREAM_SEEK_END, &endPos)); _size = endPos - _headersSize; } _stream = inStream; return S_OK; } COM_TRY_END } STDMETHODIMP 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 _SHOW_RPM_METADATA _metadata.Empty(); #endif _stream.Release(); return S_OK; } STDMETHODIMP CHandler::GetNumberOfItems(UInt32 *numItems) { *numItems = 1; return S_OK; } static const Byte k_Signature[] = { 0xED, 0xAB, 0xEE, 0xDB}; REGISTER_ARC_I( "Rpm", "rpm", 0, 0xEB, k_Signature, 0, 0, NULL) }}