p7zip-rar/CPP/7zip/Archive/RpmHandler.cpp
2017-10-11 12:40:22 +02:00

740 lines
15 KiB
C++

// 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)
}}