An RPM package consists of the lead, the signature (contains digests and signatures), the header (contains the package metadata) and the payload (a compressed archive of the package files).
This structure is shared by all package format versions supported by this Kaitai Struct implementation:
%_rpmformat macro is set
to 4 - see
https://github.com/rpm-software-management/rpm/blob/ec9ea8c43808c346da4b6cb454cdc58aef8e506a/docs/man/rpmbuild-config.5.scd?plain=1#L189-L192.
For example, Fedora 43 and 44 patch RPM 6.0 to keep producing v4 packages by
default - see
https://src.fedoraproject.org/rpms/rpm/blob/7099d81c3b5ecf1777a43095be429cb198bcc566/f/rpm-6.0-rpmformat.patch.The versions differ mainly in the tags they use: v6 packages store all sizes
as 64-bit integers, carry only cryptographic data in the signature and always
use the stripped-down cpio archive format (see the payload instance).
The formats before v3, as well as the abandoned rpm5 fork, are not covered by this implementation.
This page hosts a formal specification of RPM package file using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing library.
-- This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
--
-- This file is compatible with Lua 5.3
local class = require("class")
require("kaitaistruct")
local enum = require("enum")
local utils = require("utils")
local stringstream = require("string_stream")
local str_decode = require("string_decode")
--
-- An RPM package consists of the lead, the signature (contains digests and
-- signatures), the header (contains the package metadata) and the payload (a
-- compressed archive of the package files).
--
-- This structure is shared by all package format versions supported by this
-- Kaitai Struct implementation:
--
-- * v3, written by RPM 2.1 to 3.x.
-- * v4, written by RPM 4.x, and by RPM 6.x when the `%_rpmformat` macro is set
-- to 4 - see
-- <https://github.com/rpm-software-management/rpm/blob/ec9ea8c43808c346da4b6cb454cdc58aef8e506a/docs/man/rpmbuild-config.5.scd?plain=1#L189-L192>.
-- For example, Fedora 43 and 44 patch RPM 6.0 to keep producing v4 packages by
-- default - see
-- <https://src.fedoraproject.org/rpms/rpm/blob/7099d81c3b5ecf1777a43095be429cb198bcc566/f/rpm-6.0-rpmformat.patch>.
-- * v6, written by upstream RPM 6.0 by default - see
-- <https://github.com/rpm-software-management/rpm/commit/99d80a22d3d299bdc4418f7e61cd491731626d37>.
--
-- The versions differ mainly in the tags they use: v6 packages store all sizes
-- as 64-bit integers, carry only cryptographic data in the signature and always
-- use the stripped-down cpio archive format (see the `payload` instance).
--
-- The formats before v3, as well as the abandoned rpm5 fork, are not covered by
-- this implementation.
-- See also: Source (https://github.com/rpm-software-management/rpm/blob/ec9ea8c43808c346da4b6cb454cdc58aef8e506a/docs/manual/format_v6.md)
-- See also: Source (https://github.com/rpm-software-management/rpm/blob/ec9ea8c43808c346da4b6cb454cdc58aef8e506a/docs/manual/format_v4.md)
-- See also: Source (https://github.com/rpm-software-management/rpm/blob/ec9ea8c43808c346da4b6cb454cdc58aef8e506a/docs/manual/format_v3.md)
-- See also: Source (https://github.com/rpm-software-management/rpm/blob/ec9ea8c43808c346da4b6cb454cdc58aef8e506a/docs/manual/signatures_digests.md)
-- See also: Source (https://github.com/rpm-software-management/rpm/blob/ec9ea8c43808c346da4b6cb454cdc58aef8e506a/docs/manual/large_files.md)
-- See also: Source (https://github.com/rpm-software-management/rpm/blob/ec9ea8c43808c346da4b6cb454cdc58aef8e506a/docs/manual/tags.md)
-- See also: Source (https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/pkgformat.html)
-- See also: Source (https://ftp.osuosl.org/pub/rpm/max-rpm/)
Rpm = class.class(KaitaiStruct)
Rpm.Architectures = enum.Enum {
not_set = 0,
x86 = 1,
alpha = 2,
sparc = 3,
mips = 4,
ppc = 5,
m68k = 6,
sgi = 7,
rs6000 = 8,
ia64 = 9,
sparc64 = 10,
mips64 = 11,
arm = 12,
m68k_mint = 13,
s390 = 14,
s390x = 15,
ppc64 = 16,
sh = 17,
xtensa = 18,
aarch64 = 19,
mips_r6 = 20,
mips64_r6 = 21,
riscv = 22,
loongarch64 = 23,
e2k = 24,
no_arch = 255,
}
Rpm.HeaderTags = enum.Enum {
signatures = 62,
header_immutable = 63,
i18n_table = 100,
name = 1000,
version = 1001,
release = 1002,
epoch = 1003,
summary = 1004,
description = 1005,
build_time = 1006,
build_host = 1007,
install_time = 1008,
size = 1009,
distribution = 1010,
vendor = 1011,
gif_obsolete = 1012,
xpm_obsolete = 1013,
license = 1014,
packager = 1015,
group = 1016,
changelog_internal = 1017,
source = 1018,
patch = 1019,
url = 1020,
os = 1021,
arch = 1022,
pre_install_scriptlet = 1023,
post_install_scriptlet = 1024,
pre_uninstall_scriptlet = 1025,
post_uninstall_scriptlet = 1026,
old_file_names_obsolete = 1027,
file_sizes = 1028,
file_states = 1029,
file_modes = 1030,
file_uids_internal = 1031,
file_gids_internal = 1032,
device_number = 1033,
mtimes = 1034,
file_digests = 1035,
link_tos = 1036,
file_flags = 1037,
root_internal = 1038,
file_owner = 1039,
file_group = 1040,
exclude_internal = 1041,
exclusive_internal = 1042,
icon_obsolete = 1043,
source_rpm = 1044,
file_verify_flags = 1045,
archive_size = 1046,
provide_name = 1047,
require_flags = 1048,
require_name = 1049,
require_version = 1050,
no_source = 1051,
no_patch = 1052,
conflict_flags = 1053,
conflict_name = 1054,
conflict_version = 1055,
default_prefix_internal = 1056,
build_root_internal = 1057,
install_prefix_internal = 1058,
exclude_arch = 1059,
exclude_os = 1060,
exclusive_arch = 1061,
exclusive_os = 1062,
autoreqprov_internal = 1063,
rpm_version = 1064,
trigger_scripts = 1065,
trigger_name = 1066,
trigger_version = 1067,
trigger_flags = 1068,
trigger_index = 1069,
verify_script = 1079,
changelog_time = 1080,
changelog_name = 1081,
changelog_text = 1082,
broken_md5_internal = 1083,
prereq_internal = 1084,
pre_install_interpreter = 1085,
post_install_interpreter = 1086,
pre_uninstall_interpreter = 1087,
post_uninstall_interpreter = 1088,
build_archs = 1089,
obsolete_name = 1090,
verify_script_prog = 1091,
trigger_script_prog = 1092,
doc_dir_internal = 1093,
cookie = 1094,
file_devices = 1095,
file_inodes = 1096,
file_langs = 1097,
prefixes = 1098,
install_prefixes = 1099,
trigger_install_internal = 1100,
trigger_uninstall_internal = 1101,
trigger_post_uninstall_internal = 1102,
autoreq_internal = 1103,
autoprov_internal = 1104,
capability_internal = 1105,
source_package = 1106,
old_orig_filenames_internal = 1107,
build_prereq_internal = 1108,
build_requires_internal = 1109,
build_conflicts_internal = 1110,
build_macros_internal = 1111,
provide_flags = 1112,
provide_version = 1113,
obsolete_flags = 1114,
obsolete_version = 1115,
dir_indexes = 1116,
base_names = 1117,
dir_names = 1118,
orig_dir_indexes = 1119,
orig_base_names = 1120,
orig_dir_names = 1121,
opt_flags = 1122,
dist_url = 1123,
payload_format = 1124,
payload_compressor = 1125,
payload_flags = 1126,
install_color = 1127,
install_tid = 1128,
remove_tid_obsolete = 1129,
sha1_rhn_internal = 1130,
rhn_platform_internal = 1131,
platform = 1132,
patches_name_obsolete = 1133,
patches_flags_obsolete = 1134,
patches_version_obsolete = 1135,
cache_ctime_internal = 1136,
cache_pkg_path_internal = 1137,
cache_pkg_size_internal = 1138,
cache_pkg_mtime_internal = 1139,
file_colors = 1140,
file_class = 1141,
class_dict = 1142,
file_depends_idx = 1143,
file_depends_num = 1144,
depends_dict = 1145,
source_sig_md5 = 1146,
file_contexts_obsolete = 1147,
fs_contexts_obsolete = 1148,
re_contexts_obsolete = 1149,
policies = 1150,
pre_trans = 1151,
post_trans = 1152,
pre_trans_prog = 1153,
post_trans_prog = 1154,
dist_tag = 1155,
old_suggests_name_obsolete = 1156,
old_suggests_version_obsolete = 1157,
old_suggests_flags_obsolete = 1158,
old_enhances_name_obsolete = 1159,
old_enhances_version_obsolete = 1160,
old_enhances_flags_obsolete = 1161,
priority_unimplemented = 1162,
cvsid_unimplemented = 1163,
blink_pkgid_unimplemented = 1164,
blink_hdrid_unimplemented = 1165,
blink_nevra_unimplemented = 1166,
flink_pkgid_unimplemented = 1167,
flink_hdrid_unimplemented = 1168,
flink_nevra_unimplemented = 1169,
package_origin_unimplemented = 1170,
trigger_pre_install_internal = 1171,
build_suggests_unimplemented = 1172,
build_enhances_unimplemented = 1173,
script_states_unimplemented = 1174,
script_metrics_unimplemented = 1175,
build_cpu_clock_unimplemented = 1176,
file_digest_algos_unimplemented = 1177,
variants_unimplemented = 1178,
xmajor_unimplemented = 1179,
xminor_unimplemented = 1180,
repo_tag_unimplemented = 1181,
keywords_unimplemented = 1182,
build_platforms_unimplemented = 1183,
package_color_unimplemented = 1184,
package_pref_color_unimplemented = 1185,
xattrs_dict_unimplemented = 1186,
filex_attrsx_unimplemented = 1187,
dep_attrs_dict_unimplemented = 1188,
conflict_attrsx_unimplemented = 1189,
obsolete_attrsx_unimplemented = 1190,
provide_attrsx_unimplemented = 1191,
require_attrsx_unimplemented = 1192,
build_provides_unimplemented = 1193,
build_obsoletes_unimplemented = 1194,
db_instance = 1195,
nvra = 1196,
file_names = 5000,
file_provide = 5001,
file_require = 5002,
fs_names_unimplemented = 5003,
fs_sizes_unimplemented = 5004,
trigger_conds = 5005,
trigger_type = 5006,
orig_file_names = 5007,
long_file_sizes = 5008,
long_size = 5009,
file_caps = 5010,
file_digest_algo = 5011,
bug_url = 5012,
evr = 5013,
nvr = 5014,
nevr = 5015,
nevra = 5016,
header_color = 5017,
verbose = 5018,
epoch_num = 5019,
pre_install_flags = 5020,
post_install_flags = 5021,
pre_uninstall_flags = 5022,
post_uninstall_flags = 5023,
pre_trans_flags = 5024,
post_trans_flags = 5025,
verify_script_flags = 5026,
trigger_script_flags = 5027,
collections_unimplemented = 5029,
policy_names = 5030,
policy_types = 5031,
policy_types_indexes = 5032,
policy_flags = 5033,
vcs = 5034,
order_name = 5035,
order_version = 5036,
order_flags = 5037,
mssf_manifest_unimplemented = 5038,
mssf_domain_unimplemented = 5039,
inst_file_names = 5040,
require_nevrs = 5041,
provide_nevrs = 5042,
obsolete_nevrs = 5043,
conflict_nevrs = 5044,
file_nlinks = 5045,
recommend_name = 5046,
recommend_version = 5047,
recommend_flags = 5048,
suggest_name = 5049,
suggest_version = 5050,
suggest_flags = 5051,
supplement_name = 5052,
supplement_version = 5053,
supplement_flags = 5054,
enhance_name = 5055,
enhance_version = 5056,
enhance_flags = 5057,
recommend_nevrs = 5058,
suggest_nevrs = 5059,
supplement_nevrs = 5060,
enhance_nevrs = 5061,
encoding = 5062,
file_trigger_install_internal = 5063,
file_trigger_uninstall_internal = 5064,
file_trigger_post_uninstall_internal = 5065,
file_trigger_scripts = 5066,
file_trigger_script_prog = 5067,
file_trigger_script_flags = 5068,
file_trigger_name = 5069,
file_trigger_index = 5070,
file_trigger_version = 5071,
file_trigger_flags = 5072,
trans_file_trigger_install_internal = 5073,
trans_file_trigger_uninstall_internal = 5074,
trans_file_trigger_post_uninstall_internal = 5075,
trans_file_trigger_scripts = 5076,
trans_file_trigger_script_prog = 5077,
trans_file_trigger_script_flags = 5078,
trans_file_trigger_name = 5079,
trans_file_trigger_index = 5080,
trans_file_trigger_version = 5081,
trans_file_trigger_flags = 5082,
remove_path_postfixes_internal = 5083,
file_trigger_priorities = 5084,
trans_file_trigger_priorities = 5085,
file_trigger_conds = 5086,
file_trigger_type = 5087,
trans_file_trigger_conds = 5088,
trans_file_trigger_type = 5089,
file_signatures = 5090,
file_signature_length = 5091,
payload_sha256 = 5092,
payload_sha256_algo_obsolete = 5093,
auto_installed_unimplemented = 5094,
identity_unimplemented = 5095,
modularity_label = 5096,
payload_sha256_alt = 5097,
arch_suffix = 5098,
spec = 5099,
translation_url = 5100,
upstream_releases = 5101,
source_license_internal = 5102,
pre_untrans = 5103,
post_untrans = 5104,
pre_untrans_prog = 5105,
post_untrans_prog = 5106,
pre_untrans_flags = 5107,
post_untrans_flags = 5108,
sys_users = 5109,
build_system_internal = 5110,
build_option_internal = 5111,
payload_size = 5112,
payload_size_alt = 5113,
rpm_format = 5114,
file_mime_index = 5115,
mime_dict = 5116,
file_mimes = 5117,
package_digests = 5118,
package_digest_algos = 5119,
source_nevr = 5120,
payload_sha512 = 5121,
payload_sha512_alt = 5122,
payload_sha3_256 = 5123,
payload_sha3_256_alt = 5124,
}
Rpm.OperatingSystems = enum.Enum {
not_set = 0,
linux = 1,
irix = 2,
no_os = 255,
}
Rpm.RecordTypes = enum.Enum {
char = 1,
uint8 = 2,
uint16 = 3,
uint32 = 4,
uint64 = 5,
string = 6,
bin = 7,
string_array = 8,
i18n_string = 9,
}
Rpm.RpmTypes = enum.Enum {
binary = 0,
source = 1,
}
Rpm.SignatureTags = enum.Enum {
signatures = 62,
header_immutable = 63,
i18n_table = 100,
bad_sha1_1_obsolete = 264,
bad_sha1_2_obsolete = 265,
dsa = 267,
rsa = 268,
sha1 = 269,
long_size = 270,
long_archive_size = 271,
sha256 = 273,
file_signatures = 274,
file_signature_length = 275,
verity_signatures = 276,
verity_signature_algo = 277,
openpgp = 278,
sha3_256 = 279,
reserved = 999,
size = 1000,
le_md5_1_obsolete = 1001,
pgp = 1002,
le_md5_2_obsolete = 1003,
md5 = 1004,
gpg = 1005,
pgp5_obsolete = 1006,
payload_size = 1007,
reserved_space = 1008,
}
function Rpm:_init(io, parent, root)
KaitaiStruct._init(self, io)
self._parent = parent
self._root = root or self
self:_read()
end
function Rpm:_read()
self.lead = Rpm.Lead(self._io, self, self._root)
self.signature = Rpm.Header(true, self._io, self, self._root)
self.signature_padding = self._io:read_bytes(-(self._io:pos()) % 8)
if self.ofs_header < 0 then
self._unnamed3 = self._io:read_bytes(0)
end
self.header = Rpm.Header(false, self._io, self, self._root)
if self.ofs_payload < 0 then
self._unnamed5 = self._io:read_bytes(0)
end
self.signature_tags_steps = {}
for i = 0, self.signature.header_record.num_index_records - 1 do
self.signature_tags_steps[i + 1] = Rpm.SignatureTagsStep(i, utils.box_unwrap((i ~= 0) and utils.box_wrap(self.signature_tags_steps[(i - 1) + 1].size_tag_idx) or (-1)), utils.box_unwrap((i ~= 0) and utils.box_wrap(self.signature_tags_steps[(i - 1) + 1].long_size_tag_idx) or (-1)), self._io, self, self._root)
end
self.header_tags_steps = {}
for i = 0, self.header.header_record.num_index_records - 1 do
self.header_tags_steps[i + 1] = Rpm.HeaderTagsStep(i, utils.box_unwrap((i ~= 0) and utils.box_wrap(self.header_tags_steps[(i - 1) + 1].payload_size_tag_idx) or (-1)), self._io, self, self._root)
end
end
Rpm.property.has_header_payload_size_tag = {}
function Rpm.property.has_header_payload_size_tag:get()
if self._m_has_header_payload_size_tag ~= nil then
return self._m_has_header_payload_size_tag
end
self._m_has_header_payload_size_tag = self.header_tags_steps[#self.header_tags_steps].payload_size_tag_idx ~= -1
return self._m_has_header_payload_size_tag
end
Rpm.property.has_payload = {}
function Rpm.property.has_payload:get()
if self._m_has_payload ~= nil then
return self._m_has_payload
end
self._m_has_payload = ((self.has_header_payload_size_tag) or (self.has_signature_long_size_tag) or (self.has_signature_size_tag))
return self._m_has_payload
end
Rpm.property.has_signature_long_size_tag = {}
function Rpm.property.has_signature_long_size_tag:get()
if self._m_has_signature_long_size_tag ~= nil then
return self._m_has_signature_long_size_tag
end
self._m_has_signature_long_size_tag = self.signature_tags_steps[#self.signature_tags_steps].long_size_tag_idx ~= -1
return self._m_has_signature_long_size_tag
end
Rpm.property.has_signature_size_tag = {}
function Rpm.property.has_signature_size_tag:get()
if self._m_has_signature_size_tag ~= nil then
return self._m_has_signature_size_tag
end
self._m_has_signature_size_tag = self.signature_tags_steps[#self.signature_tags_steps].size_tag_idx ~= -1
return self._m_has_signature_size_tag
end
Rpm.property.header_payload_size_tag = {}
function Rpm.property.header_payload_size_tag:get()
if self._m_header_payload_size_tag ~= nil then
return self._m_header_payload_size_tag
end
if self.has_header_payload_size_tag then
self._m_header_payload_size_tag = self.header.index_records[self.header_tags_steps[#self.header_tags_steps].payload_size_tag_idx + 1]
end
return self._m_header_payload_size_tag
end
Rpm.property.len_header = {}
function Rpm.property.len_header:get()
if self._m_len_header ~= nil then
return self._m_len_header
end
self._m_len_header = self.ofs_payload - self.ofs_header
return self._m_len_header
end
--
-- Size of the (compressed) payload in bytes. v6 packages store it in
-- `header_tags::payload_size`, v4/v3 packages in `signature_tags::size`
-- (which also includes the size of the header).
--
-- If the header and payload together or the uncompressed payload reach
-- 4 GiB, v4 packages use `signature_tags::long_size` instead - see
-- <https://github.com/rpm-software-management/rpm/blob/ec9ea8c43808c346da4b6cb454cdc58aef8e506a/lib/signature.cc#L182-L212>.
--
-- RPM never writes both (so this is just a hypothetical scenario), but if
-- both are present, `signature_tags::long_size` takes precedence over
-- `signature_tags::size`, just like in RPM's `printSize()` function:
-- <https://github.com/rpm-software-management/rpm/blob/ec9ea8c43808c346da4b6cb454cdc58aef8e506a/lib/signature.cc#L36-L43>
Rpm.property.len_payload = {}
function Rpm.property.len_payload:get()
if self._m_len_payload ~= nil then
return self._m_len_payload
end
if self.has_payload then
self._m_len_payload = utils.box_unwrap((self.has_header_payload_size_tag) and utils.box_wrap(self.header_payload_size_tag.body.values[0 + 1]) or (utils.box_unwrap((self.has_signature_long_size_tag) and utils.box_wrap(self.signature_long_size_tag.body.values[0 + 1] - self.len_header) or (self.signature_size_tag.body.values[0 + 1] - self.len_header))))
end
return self._m_len_payload
end
Rpm.property.ofs_header = {}
function Rpm.property.ofs_header:get()
if self._m_ofs_header ~= nil then
return self._m_ofs_header
end
self._m_ofs_header = self._io:pos()
return self._m_ofs_header
end
Rpm.property.ofs_payload = {}
function Rpm.property.ofs_payload:get()
if self._m_ofs_payload ~= nil then
return self._m_ofs_payload
end
self._m_ofs_payload = self._io:pos()
return self._m_ofs_payload
end
--
-- Archive of the package files, compressed using the method specified by
-- `header_tags::payload_compressor`. If this tag is missing, it's almost
-- certainly uncompressed (except for some very old v3 packages built by RPM
-- 3.0.3 or earlier, which didn't use the tag because the payload was always
-- gzipped; RPM 3.0.5 added support for bzip2 payloads and started writing
-- the tag). However, RPM reads the payload as gzip by default - see
-- <https://github.com/rpm-software-management/rpm/blob/ec9ea8c43808c346da4b6cb454cdc58aef8e506a/lib/rpmte.cc#L643-L645>.
-- Since zlib's gzip reader passes data that is not in gzip format through
-- unchanged (see
-- <https://github.com/madler/zlib/blob/da607da739fa6047df13e66a2af6b8bec7c2a498/zlib.h#L1386-L1389>),
-- this also works for uncompressed payloads.
--
-- The archive format is given by `header_tags::payload_format`, which is
-- `"cpio"` for regular packages. In v4/v3 packages, it's a SVR4 cpio archive
-- without a checksum (the `070701` variant) - the [v4 format
-- documentation](https://github.com/rpm-software-management/rpm/blob/ec9ea8c43808c346da4b6cb454cdc58aef8e506a/docs/manual/format_v4.md?plain=1#L106-L107)
-- claims "with a CRC checksum", but that's not true since RPM 2.4.4
-- (released in 1997).
--
-- v6 packages and v4 packages with a file over 4 GiB use a stripped-down
-- variant of cpio with the magic `07070X`. Its file headers only hold the
-- index of the file in the file lists of the RPM header, which is the only
-- place where the file names, sizes and other metadata are stored.
-- See also: Source (https://github.com/rpm-software-management/rpm/blob/ec9ea8c43808c346da4b6cb454cdc58aef8e506a/docs/manual/format_v6.md#payload)
-- See also: Source (https://github.com/rpm-software-management/rpm/blob/ec9ea8c43808c346da4b6cb454cdc58aef8e506a/docs/manual/format_v4.md#payload)
Rpm.property.payload = {}
function Rpm.property.payload:get()
if self._m_payload ~= nil then
return self._m_payload
end
if self.has_payload then
local _pos = self._io:pos()
self._io:seek(self.ofs_payload)
self._m_payload = self._io:read_bytes(self.len_payload)
self._io:seek(_pos)
end
return self._m_payload
end
Rpm.property.signature_long_size_tag = {}
function Rpm.property.signature_long_size_tag:get()
if self._m_signature_long_size_tag ~= nil then
return self._m_signature_long_size_tag
end
if self.has_signature_long_size_tag then
self._m_signature_long_size_tag = self.signature.index_records[self.signature_tags_steps[#self.signature_tags_steps].long_size_tag_idx + 1]
end
return self._m_signature_long_size_tag
end
Rpm.property.signature_size_tag = {}
function Rpm.property.signature_size_tag:get()
if self._m_signature_size_tag ~= nil then
return self._m_signature_size_tag
end
if self.has_signature_size_tag then
self._m_signature_size_tag = self.signature.index_records[self.signature_tags_steps[#self.signature_tags_steps].size_tag_idx + 1]
end
return self._m_signature_size_tag
end
Rpm.Dummy = class.class(KaitaiStruct)
function Rpm.Dummy:_init(io, parent, root)
KaitaiStruct._init(self, io)
self._parent = parent
self._root = root
self:_read()
end
function Rpm.Dummy:_read()
end
--
-- header structure used for both the "header" and "signature", but some tag
-- values have different meanings in signature and header (hence they use
-- different enums)
Rpm.Header = class.class(KaitaiStruct)
function Rpm.Header:_init(is_signature, io, parent, root)
KaitaiStruct._init(self, io)
self._parent = parent
self._root = root
self.is_signature = is_signature
self:_read()
end
function Rpm.Header:_read()
self.header_record = Rpm.HeaderRecord(self._io, self, self._root)
self.index_records = {}
for i = 0, self.header_record.num_index_records - 1 do
self.index_records[i + 1] = Rpm.HeaderIndexRecord(self._io, self, self._root)
end
self._raw_storage_section = self._io:read_bytes(self.header_record.len_storage_section)
local _io = KaitaiStream(stringstream(self._raw_storage_section))
self.storage_section = Rpm.Dummy(_io, self, self._root)
end
Rpm.Header.property.is_header = {}
function Rpm.Header.property.is_header:get()
if self._m_is_header ~= nil then
return self._m_is_header
end
self._m_is_header = not(self.is_signature)
return self._m_is_header
end
Rpm.HeaderIndexRecord = class.class(KaitaiStruct)
function Rpm.HeaderIndexRecord:_init(io, parent, root)
KaitaiStruct._init(self, io)
self._parent = parent
self._root = root
self:_read()
end
function Rpm.HeaderIndexRecord:_read()
self.tag_raw = self._io:read_u4be()
self.record_type = Rpm.RecordTypes(self._io:read_u4be())
if self.record_type == nil then
error("ValidationNotInEnumError")
end
self.ofs_body = self._io:read_u4be()
self.count = self._io:read_u4be()
end
Rpm.HeaderIndexRecord.property.body = {}
function Rpm.HeaderIndexRecord.property.body:get()
if self._m_body ~= nil then
return self._m_body
end
local _io = self._parent.storage_section._io
local _pos = _io:pos()
_io:seek(self.ofs_body)
local _on = self.record_type
if _on == Rpm.RecordTypes.bin then
self._m_body = Rpm.RecordTypeBin(self.len_value, _io, self, self._root)
elseif _on == Rpm.RecordTypes.char then
self._m_body = Rpm.RecordTypeUint8(self.num_values, _io, self, self._root)
elseif _on == Rpm.RecordTypes.i18n_string then
self._m_body = Rpm.RecordTypeStringArray(self.num_values, _io, self, self._root)
elseif _on == Rpm.RecordTypes.string then
self._m_body = Rpm.RecordTypeString(_io, self, self._root)
elseif _on == Rpm.RecordTypes.string_array then
self._m_body = Rpm.RecordTypeStringArray(self.num_values, _io, self, self._root)
elseif _on == Rpm.RecordTypes.uint16 then
self._m_body = Rpm.RecordTypeUint16(self.num_values, _io, self, self._root)
elseif _on == Rpm.RecordTypes.uint32 then
self._m_body = Rpm.RecordTypeUint32(self.num_values, _io, self, self._root)
elseif _on == Rpm.RecordTypes.uint64 then
self._m_body = Rpm.RecordTypeUint64(self.num_values, _io, self, self._root)
elseif _on == Rpm.RecordTypes.uint8 then
self._m_body = Rpm.RecordTypeUint8(self.num_values, _io, self, self._root)
end
_io:seek(_pos)
return self._m_body
end
Rpm.HeaderIndexRecord.property.header_tag = {}
function Rpm.HeaderIndexRecord.property.header_tag:get()
if self._m_header_tag ~= nil then
return self._m_header_tag
end
if self._parent.is_header then
self._m_header_tag = Rpm.HeaderTags(self.tag_raw)
end
return self._m_header_tag
end
Rpm.HeaderIndexRecord.property.len_value = {}
function Rpm.HeaderIndexRecord.property.len_value:get()
if self._m_len_value ~= nil then
return self._m_len_value
end
if self.record_type == Rpm.RecordTypes.bin then
self._m_len_value = self.count
end
return self._m_len_value
end
Rpm.HeaderIndexRecord.property.num_values = {}
function Rpm.HeaderIndexRecord.property.num_values:get()
if self._m_num_values ~= nil then
return self._m_num_values
end
if self.record_type ~= Rpm.RecordTypes.bin then
self._m_num_values = self.count
end
return self._m_num_values
end
Rpm.HeaderIndexRecord.property.signature_tag = {}
function Rpm.HeaderIndexRecord.property.signature_tag:get()
if self._m_signature_tag ~= nil then
return self._m_signature_tag
end
if self._parent.is_signature then
self._m_signature_tag = Rpm.SignatureTags(self.tag_raw)
end
return self._m_signature_tag
end
--
-- prefer to access `signature_tag` and `header_tag` instead.
--
-- internal; access `num_values` and `len_value` instead.
Rpm.HeaderRecord = class.class(KaitaiStruct)
function Rpm.HeaderRecord:_init(io, parent, root)
KaitaiStruct._init(self, io)
self._parent = parent
self._root = root
self:_read()
end
function Rpm.HeaderRecord:_read()
self.magic = self._io:read_bytes(4)
if not(self.magic == "\142\173\232\001") then
error("not equal, expected " .. "\142\173\232\001" .. ", but got " .. self.magic)
end
self.reserved = self._io:read_bytes(4)
if not(self.reserved == "\000\000\000\000") then
error("not equal, expected " .. "\000\000\000\000" .. ", but got " .. self.reserved)
end
self.num_index_records = self._io:read_u4be()
if not(self.num_index_records >= 1) then
error("ValidationLessThanError")
end
if not(self.num_index_records <= utils.box_unwrap((self._parent.is_signature) and utils.box_wrap(32) or (65535))) then
error("ValidationGreaterThanError")
end
self.len_storage_section = self._io:read_u4be()
if not(self.len_storage_section <= utils.box_unwrap((self._parent.is_signature) and utils.box_wrap((64 * 1024) * 1024) or (268435455))) then
error("ValidationGreaterThanError")
end
end
--
-- Size of the storage area for the data
-- pointed to by the Index Records.
--
-- Like `signature_tags_step`, but looks for `header_tags::payload_size`,
-- which is where v6 packages store the payload size.
Rpm.HeaderTagsStep = class.class(KaitaiStruct)
function Rpm.HeaderTagsStep:_init(idx, prev_payload_size_tag_idx, io, parent, root)
KaitaiStruct._init(self, io)
self._parent = parent
self._root = root
self.idx = idx
self.prev_payload_size_tag_idx = prev_payload_size_tag_idx
self:_read()
end
function Rpm.HeaderTagsStep:_read()
end
Rpm.HeaderTagsStep.property.payload_size_tag_idx = {}
function Rpm.HeaderTagsStep.property.payload_size_tag_idx:get()
if self._m_payload_size_tag_idx ~= nil then
return self._m_payload_size_tag_idx
end
self._m_payload_size_tag_idx = utils.box_unwrap((self.prev_payload_size_tag_idx ~= -1) and utils.box_wrap(self.prev_payload_size_tag_idx) or (utils.box_unwrap(( ((self._parent.header.index_records[self.idx + 1].header_tag == Rpm.HeaderTags.payload_size) and (self._parent.header.index_records[self.idx + 1].record_type == Rpm.RecordTypes.uint64) and (self._parent.header.index_records[self.idx + 1].num_values >= 1)) ) and utils.box_wrap(self.idx) or (-1))))
return self._m_payload_size_tag_idx
end
--
-- In 2021, Panu Matilainen (an RPM developer) [described this
-- structure](https://github.com/kaitai-io/kaitai_struct_formats/pull/469#discussion_r718288192)
-- as follows:
--
-- > The lead as a structure is 25 years obsolete, the data there is
-- > meaningless. Seriously. Except to check for the magic to detect that
-- > it's an rpm file in the first place, just ignore everything in it.
-- > Literally everything.
--
-- RPM 4.19 and older rejected packages that didn't meet the `valid`
-- constraints specified here, while RPM 4.20 and later only check the
-- `magic` - see
-- <https://github.com/rpm-software-management/rpm/commit/b3449a0774487a091bbe59e821b4004b06d4fa66>.
-- Nevertheless, RPM still writes values that pass these checks for backwards
-- compatibility, so any `.rpm` file should pass.
-- See also: Source (https://github.com/rpm-software-management/rpm/blob/ec9ea8c43808c346da4b6cb454cdc58aef8e506a/docs/manual/format_lead.md)
Rpm.Lead = class.class(KaitaiStruct)
function Rpm.Lead:_init(io, parent, root)
KaitaiStruct._init(self, io)
self._parent = parent
self._root = root
self:_read()
end
function Rpm.Lead:_read()
self.magic = self._io:read_bytes(4)
if not(self.magic == "\237\171\238\219") then
error("not equal, expected " .. "\237\171\238\219" .. ", but got " .. self.magic)
end
self.version = Rpm.RpmVersion(self._io, self, self._root)
self.type = Rpm.RpmTypes(self._io:read_u2be())
self.architecture = Rpm.Architectures(self._io:read_u2be())
self.package_name = str_decode.decode(KaitaiStream.bytes_terminate(self._io:read_bytes(66), 0, false), "UTF-8")
self.os = Rpm.OperatingSystems(self._io:read_u2be())
self.signature_type = self._io:read_u2be()
if not(self.signature_type == 5) then
error("not equal, expected " .. 5 .. ", but got " .. self.signature_type)
end
self.reserved = self._io:read_bytes(16)
end
--
-- See also: Source (https://github.com/rpm-software-management/rpm/blob/ec9ea8c43808c346da4b6cb454cdc58aef8e506a/lib/rpmlead.cc#L20-L21)
Rpm.RecordTypeBin = class.class(KaitaiStruct)
function Rpm.RecordTypeBin:_init(len_value, io, parent, root)
KaitaiStruct._init(self, io)
self._parent = parent
self._root = root
self.len_value = len_value
self:_read()
end
function Rpm.RecordTypeBin:_read()
self.values = {}
for i = 0, 1 - 1 do
self.values[i + 1] = self._io:read_bytes(self.len_value)
end
end
Rpm.RecordTypeString = class.class(KaitaiStruct)
function Rpm.RecordTypeString:_init(io, parent, root)
KaitaiStruct._init(self, io)
self._parent = parent
self._root = root
self:_read()
end
function Rpm.RecordTypeString:_read()
self.values = {}
for i = 0, 1 - 1 do
self.values[i + 1] = str_decode.decode(self._io:read_bytes_term(0, false, true, true), "UTF-8")
end
end
Rpm.RecordTypeStringArray = class.class(KaitaiStruct)
function Rpm.RecordTypeStringArray:_init(num_values, io, parent, root)
KaitaiStruct._init(self, io)
self._parent = parent
self._root = root
self.num_values = num_values
self:_read()
end
function Rpm.RecordTypeStringArray:_read()
self.values = {}
for i = 0, self.num_values - 1 do
self.values[i + 1] = str_decode.decode(self._io:read_bytes_term(0, false, true, true), "UTF-8")
end
end
Rpm.RecordTypeUint16 = class.class(KaitaiStruct)
function Rpm.RecordTypeUint16:_init(num_values, io, parent, root)
KaitaiStruct._init(self, io)
self._parent = parent
self._root = root
self.num_values = num_values
self:_read()
end
function Rpm.RecordTypeUint16:_read()
self.values = {}
for i = 0, self.num_values - 1 do
self.values[i + 1] = self._io:read_u2be()
end
end
Rpm.RecordTypeUint32 = class.class(KaitaiStruct)
function Rpm.RecordTypeUint32:_init(num_values, io, parent, root)
KaitaiStruct._init(self, io)
self._parent = parent
self._root = root
self.num_values = num_values
self:_read()
end
function Rpm.RecordTypeUint32:_read()
self.values = {}
for i = 0, self.num_values - 1 do
self.values[i + 1] = self._io:read_u4be()
end
end
Rpm.RecordTypeUint64 = class.class(KaitaiStruct)
function Rpm.RecordTypeUint64:_init(num_values, io, parent, root)
KaitaiStruct._init(self, io)
self._parent = parent
self._root = root
self.num_values = num_values
self:_read()
end
function Rpm.RecordTypeUint64:_read()
self.values = {}
for i = 0, self.num_values - 1 do
self.values[i + 1] = self._io:read_u8be()
end
end
Rpm.RecordTypeUint8 = class.class(KaitaiStruct)
function Rpm.RecordTypeUint8:_init(num_values, io, parent, root)
KaitaiStruct._init(self, io)
self._parent = parent
self._root = root
self.num_values = num_values
self:_read()
end
function Rpm.RecordTypeUint8:_read()
self.values = {}
for i = 0, self.num_values - 1 do
self.values[i + 1] = self._io:read_u1()
end
end
Rpm.RpmVersion = class.class(KaitaiStruct)
function Rpm.RpmVersion:_init(io, parent, root)
KaitaiStruct._init(self, io)
self._parent = parent
self._root = root
self:_read()
end
function Rpm.RpmVersion:_read()
self.major = self._io:read_u1()
if not(self.major >= 3) then
error("ValidationLessThanError")
end
if not(self.major <= 4) then
error("ValidationGreaterThanError")
end
self.minor = self._io:read_u1()
end
--
-- 3 in v3 and v4 packages, 4 in v6 packages.
-- See also: Source (https://github.com/rpm-software-management/rpm/blob/ec9ea8c43808c346da4b6cb454cdc58aef8e506a/lib/rpmlead.cc#L51-L52)
--
-- Finds the first `signature_tags::size` and `signature_tags::long_size`
-- index record. Since Kaitai Struct doesn't have a built-in way to search an
-- array directly, each step receives the indexes found so far via
-- parameters.
Rpm.SignatureTagsStep = class.class(KaitaiStruct)
function Rpm.SignatureTagsStep:_init(idx, prev_size_tag_idx, prev_long_size_tag_idx, io, parent, root)
KaitaiStruct._init(self, io)
self._parent = parent
self._root = root
self.idx = idx
self.prev_size_tag_idx = prev_size_tag_idx
self.prev_long_size_tag_idx = prev_long_size_tag_idx
self:_read()
end
function Rpm.SignatureTagsStep:_read()
end
Rpm.SignatureTagsStep.property.long_size_tag_idx = {}
function Rpm.SignatureTagsStep.property.long_size_tag_idx:get()
if self._m_long_size_tag_idx ~= nil then
return self._m_long_size_tag_idx
end
self._m_long_size_tag_idx = utils.box_unwrap((self.prev_long_size_tag_idx ~= -1) and utils.box_wrap(self.prev_long_size_tag_idx) or (utils.box_unwrap(( ((self._parent.signature.index_records[self.idx + 1].signature_tag == Rpm.SignatureTags.long_size) and (self._parent.signature.index_records[self.idx + 1].record_type == Rpm.RecordTypes.uint64) and (self._parent.signature.index_records[self.idx + 1].num_values >= 1)) ) and utils.box_wrap(self.idx) or (-1))))
return self._m_long_size_tag_idx
end
Rpm.SignatureTagsStep.property.size_tag_idx = {}
function Rpm.SignatureTagsStep.property.size_tag_idx:get()
if self._m_size_tag_idx ~= nil then
return self._m_size_tag_idx
end
self._m_size_tag_idx = utils.box_unwrap((self.prev_size_tag_idx ~= -1) and utils.box_wrap(self.prev_size_tag_idx) or (utils.box_unwrap(( ((self._parent.signature.index_records[self.idx + 1].signature_tag == Rpm.SignatureTags.size) and (self._parent.signature.index_records[self.idx + 1].record_type == Rpm.RecordTypes.uint32) and (self._parent.signature.index_records[self.idx + 1].num_values >= 1)) ) and utils.box_wrap(self.idx) or (-1))))
return self._m_size_tag_idx
end