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.
All Ruby code generated by Kaitai Struct depends on the Kaitai Struct runtime library for Ruby. You must add this dependency to your project before you can parse or serialize any data.
The Ruby runtime library can be installed from RubyGems:
gem install kaitai-struct
Parse a local file and get structure in memory:
data = Rpm.from_file("path/to/local/file.rpm")
Or parse structure from a string of bytes:
bytes = "\x00\x01\x02..."
data = Rpm.new(Kaitai::Struct::Stream.new(bytes))
After that, one can get various attributes from the structure by invoking getter methods like:
data.payload # => 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.
# This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
require 'kaitai/struct/struct'
unless Gem::Version.new(Kaitai::Struct::VERSION) >= Gem::Version.new('0.11')
raise "Incompatible Kaitai Struct Ruby API: 0.11 or later is required, but you have #{Kaitai::Struct::VERSION}"
end
##
# 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 https://github.com/rpm-software-management/rpm/blob/ec9ea8c43808c346da4b6cb454cdc58aef8e506a/docs/manual/format_v6.md Source
# @see https://github.com/rpm-software-management/rpm/blob/ec9ea8c43808c346da4b6cb454cdc58aef8e506a/docs/manual/format_v4.md Source
# @see https://github.com/rpm-software-management/rpm/blob/ec9ea8c43808c346da4b6cb454cdc58aef8e506a/docs/manual/format_v3.md Source
# @see https://github.com/rpm-software-management/rpm/blob/ec9ea8c43808c346da4b6cb454cdc58aef8e506a/docs/manual/signatures_digests.md Source
# @see https://github.com/rpm-software-management/rpm/blob/ec9ea8c43808c346da4b6cb454cdc58aef8e506a/docs/manual/large_files.md Source
# @see https://github.com/rpm-software-management/rpm/blob/ec9ea8c43808c346da4b6cb454cdc58aef8e506a/docs/manual/tags.md Source
# @see https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/pkgformat.html Source
# @see https://ftp.osuosl.org/pub/rpm/max-rpm/ Source
class Rpm < Kaitai::Struct::Struct
ARCHITECTURES = {
0 => :architectures_not_set,
1 => :architectures_x86,
2 => :architectures_alpha,
3 => :architectures_sparc,
4 => :architectures_mips,
5 => :architectures_ppc,
6 => :architectures_m68k,
7 => :architectures_sgi,
8 => :architectures_rs6000,
9 => :architectures_ia64,
10 => :architectures_sparc64,
11 => :architectures_mips64,
12 => :architectures_arm,
13 => :architectures_m68k_mint,
14 => :architectures_s390,
15 => :architectures_s390x,
16 => :architectures_ppc64,
17 => :architectures_sh,
18 => :architectures_xtensa,
19 => :architectures_aarch64,
20 => :architectures_mips_r6,
21 => :architectures_mips64_r6,
22 => :architectures_riscv,
23 => :architectures_loongarch64,
24 => :architectures_e2k,
255 => :architectures_no_arch,
}
I__ARCHITECTURES = ARCHITECTURES.invert
HEADER_TAGS = {
62 => :header_tags_signatures,
63 => :header_tags_header_immutable,
100 => :header_tags_i18n_table,
1000 => :header_tags_name,
1001 => :header_tags_version,
1002 => :header_tags_release,
1003 => :header_tags_epoch,
1004 => :header_tags_summary,
1005 => :header_tags_description,
1006 => :header_tags_build_time,
1007 => :header_tags_build_host,
1008 => :header_tags_install_time,
1009 => :header_tags_size,
1010 => :header_tags_distribution,
1011 => :header_tags_vendor,
1012 => :header_tags_gif_obsolete,
1013 => :header_tags_xpm_obsolete,
1014 => :header_tags_license,
1015 => :header_tags_packager,
1016 => :header_tags_group,
1017 => :header_tags_changelog_internal,
1018 => :header_tags_source,
1019 => :header_tags_patch,
1020 => :header_tags_url,
1021 => :header_tags_os,
1022 => :header_tags_arch,
1023 => :header_tags_pre_install_scriptlet,
1024 => :header_tags_post_install_scriptlet,
1025 => :header_tags_pre_uninstall_scriptlet,
1026 => :header_tags_post_uninstall_scriptlet,
1027 => :header_tags_old_file_names_obsolete,
1028 => :header_tags_file_sizes,
1029 => :header_tags_file_states,
1030 => :header_tags_file_modes,
1031 => :header_tags_file_uids_internal,
1032 => :header_tags_file_gids_internal,
1033 => :header_tags_device_number,
1034 => :header_tags_mtimes,
1035 => :header_tags_file_digests,
1036 => :header_tags_link_tos,
1037 => :header_tags_file_flags,
1038 => :header_tags_root_internal,
1039 => :header_tags_file_owner,
1040 => :header_tags_file_group,
1041 => :header_tags_exclude_internal,
1042 => :header_tags_exclusive_internal,
1043 => :header_tags_icon_obsolete,
1044 => :header_tags_source_rpm,
1045 => :header_tags_file_verify_flags,
1046 => :header_tags_archive_size,
1047 => :header_tags_provide_name,
1048 => :header_tags_require_flags,
1049 => :header_tags_require_name,
1050 => :header_tags_require_version,
1051 => :header_tags_no_source,
1052 => :header_tags_no_patch,
1053 => :header_tags_conflict_flags,
1054 => :header_tags_conflict_name,
1055 => :header_tags_conflict_version,
1056 => :header_tags_default_prefix_internal,
1057 => :header_tags_build_root_internal,
1058 => :header_tags_install_prefix_internal,
1059 => :header_tags_exclude_arch,
1060 => :header_tags_exclude_os,
1061 => :header_tags_exclusive_arch,
1062 => :header_tags_exclusive_os,
1063 => :header_tags_autoreqprov_internal,
1064 => :header_tags_rpm_version,
1065 => :header_tags_trigger_scripts,
1066 => :header_tags_trigger_name,
1067 => :header_tags_trigger_version,
1068 => :header_tags_trigger_flags,
1069 => :header_tags_trigger_index,
1079 => :header_tags_verify_script,
1080 => :header_tags_changelog_time,
1081 => :header_tags_changelog_name,
1082 => :header_tags_changelog_text,
1083 => :header_tags_broken_md5_internal,
1084 => :header_tags_prereq_internal,
1085 => :header_tags_pre_install_interpreter,
1086 => :header_tags_post_install_interpreter,
1087 => :header_tags_pre_uninstall_interpreter,
1088 => :header_tags_post_uninstall_interpreter,
1089 => :header_tags_build_archs,
1090 => :header_tags_obsolete_name,
1091 => :header_tags_verify_script_prog,
1092 => :header_tags_trigger_script_prog,
1093 => :header_tags_doc_dir_internal,
1094 => :header_tags_cookie,
1095 => :header_tags_file_devices,
1096 => :header_tags_file_inodes,
1097 => :header_tags_file_langs,
1098 => :header_tags_prefixes,
1099 => :header_tags_install_prefixes,
1100 => :header_tags_trigger_install_internal,
1101 => :header_tags_trigger_uninstall_internal,
1102 => :header_tags_trigger_post_uninstall_internal,
1103 => :header_tags_autoreq_internal,
1104 => :header_tags_autoprov_internal,
1105 => :header_tags_capability_internal,
1106 => :header_tags_source_package,
1107 => :header_tags_old_orig_filenames_internal,
1108 => :header_tags_build_prereq_internal,
1109 => :header_tags_build_requires_internal,
1110 => :header_tags_build_conflicts_internal,
1111 => :header_tags_build_macros_internal,
1112 => :header_tags_provide_flags,
1113 => :header_tags_provide_version,
1114 => :header_tags_obsolete_flags,
1115 => :header_tags_obsolete_version,
1116 => :header_tags_dir_indexes,
1117 => :header_tags_base_names,
1118 => :header_tags_dir_names,
1119 => :header_tags_orig_dir_indexes,
1120 => :header_tags_orig_base_names,
1121 => :header_tags_orig_dir_names,
1122 => :header_tags_opt_flags,
1123 => :header_tags_dist_url,
1124 => :header_tags_payload_format,
1125 => :header_tags_payload_compressor,
1126 => :header_tags_payload_flags,
1127 => :header_tags_install_color,
1128 => :header_tags_install_tid,
1129 => :header_tags_remove_tid_obsolete,
1130 => :header_tags_sha1_rhn_internal,
1131 => :header_tags_rhn_platform_internal,
1132 => :header_tags_platform,
1133 => :header_tags_patches_name_obsolete,
1134 => :header_tags_patches_flags_obsolete,
1135 => :header_tags_patches_version_obsolete,
1136 => :header_tags_cache_ctime_internal,
1137 => :header_tags_cache_pkg_path_internal,
1138 => :header_tags_cache_pkg_size_internal,
1139 => :header_tags_cache_pkg_mtime_internal,
1140 => :header_tags_file_colors,
1141 => :header_tags_file_class,
1142 => :header_tags_class_dict,
1143 => :header_tags_file_depends_idx,
1144 => :header_tags_file_depends_num,
1145 => :header_tags_depends_dict,
1146 => :header_tags_source_sig_md5,
1147 => :header_tags_file_contexts_obsolete,
1148 => :header_tags_fs_contexts_obsolete,
1149 => :header_tags_re_contexts_obsolete,
1150 => :header_tags_policies,
1151 => :header_tags_pre_trans,
1152 => :header_tags_post_trans,
1153 => :header_tags_pre_trans_prog,
1154 => :header_tags_post_trans_prog,
1155 => :header_tags_dist_tag,
1156 => :header_tags_old_suggests_name_obsolete,
1157 => :header_tags_old_suggests_version_obsolete,
1158 => :header_tags_old_suggests_flags_obsolete,
1159 => :header_tags_old_enhances_name_obsolete,
1160 => :header_tags_old_enhances_version_obsolete,
1161 => :header_tags_old_enhances_flags_obsolete,
1162 => :header_tags_priority_unimplemented,
1163 => :header_tags_cvsid_unimplemented,
1164 => :header_tags_blink_pkgid_unimplemented,
1165 => :header_tags_blink_hdrid_unimplemented,
1166 => :header_tags_blink_nevra_unimplemented,
1167 => :header_tags_flink_pkgid_unimplemented,
1168 => :header_tags_flink_hdrid_unimplemented,
1169 => :header_tags_flink_nevra_unimplemented,
1170 => :header_tags_package_origin_unimplemented,
1171 => :header_tags_trigger_pre_install_internal,
1172 => :header_tags_build_suggests_unimplemented,
1173 => :header_tags_build_enhances_unimplemented,
1174 => :header_tags_script_states_unimplemented,
1175 => :header_tags_script_metrics_unimplemented,
1176 => :header_tags_build_cpu_clock_unimplemented,
1177 => :header_tags_file_digest_algos_unimplemented,
1178 => :header_tags_variants_unimplemented,
1179 => :header_tags_xmajor_unimplemented,
1180 => :header_tags_xminor_unimplemented,
1181 => :header_tags_repo_tag_unimplemented,
1182 => :header_tags_keywords_unimplemented,
1183 => :header_tags_build_platforms_unimplemented,
1184 => :header_tags_package_color_unimplemented,
1185 => :header_tags_package_pref_color_unimplemented,
1186 => :header_tags_xattrs_dict_unimplemented,
1187 => :header_tags_filex_attrsx_unimplemented,
1188 => :header_tags_dep_attrs_dict_unimplemented,
1189 => :header_tags_conflict_attrsx_unimplemented,
1190 => :header_tags_obsolete_attrsx_unimplemented,
1191 => :header_tags_provide_attrsx_unimplemented,
1192 => :header_tags_require_attrsx_unimplemented,
1193 => :header_tags_build_provides_unimplemented,
1194 => :header_tags_build_obsoletes_unimplemented,
1195 => :header_tags_db_instance,
1196 => :header_tags_nvra,
5000 => :header_tags_file_names,
5001 => :header_tags_file_provide,
5002 => :header_tags_file_require,
5003 => :header_tags_fs_names_unimplemented,
5004 => :header_tags_fs_sizes_unimplemented,
5005 => :header_tags_trigger_conds,
5006 => :header_tags_trigger_type,
5007 => :header_tags_orig_file_names,
5008 => :header_tags_long_file_sizes,
5009 => :header_tags_long_size,
5010 => :header_tags_file_caps,
5011 => :header_tags_file_digest_algo,
5012 => :header_tags_bug_url,
5013 => :header_tags_evr,
5014 => :header_tags_nvr,
5015 => :header_tags_nevr,
5016 => :header_tags_nevra,
5017 => :header_tags_header_color,
5018 => :header_tags_verbose,
5019 => :header_tags_epoch_num,
5020 => :header_tags_pre_install_flags,
5021 => :header_tags_post_install_flags,
5022 => :header_tags_pre_uninstall_flags,
5023 => :header_tags_post_uninstall_flags,
5024 => :header_tags_pre_trans_flags,
5025 => :header_tags_post_trans_flags,
5026 => :header_tags_verify_script_flags,
5027 => :header_tags_trigger_script_flags,
5029 => :header_tags_collections_unimplemented,
5030 => :header_tags_policy_names,
5031 => :header_tags_policy_types,
5032 => :header_tags_policy_types_indexes,
5033 => :header_tags_policy_flags,
5034 => :header_tags_vcs,
5035 => :header_tags_order_name,
5036 => :header_tags_order_version,
5037 => :header_tags_order_flags,
5038 => :header_tags_mssf_manifest_unimplemented,
5039 => :header_tags_mssf_domain_unimplemented,
5040 => :header_tags_inst_file_names,
5041 => :header_tags_require_nevrs,
5042 => :header_tags_provide_nevrs,
5043 => :header_tags_obsolete_nevrs,
5044 => :header_tags_conflict_nevrs,
5045 => :header_tags_file_nlinks,
5046 => :header_tags_recommend_name,
5047 => :header_tags_recommend_version,
5048 => :header_tags_recommend_flags,
5049 => :header_tags_suggest_name,
5050 => :header_tags_suggest_version,
5051 => :header_tags_suggest_flags,
5052 => :header_tags_supplement_name,
5053 => :header_tags_supplement_version,
5054 => :header_tags_supplement_flags,
5055 => :header_tags_enhance_name,
5056 => :header_tags_enhance_version,
5057 => :header_tags_enhance_flags,
5058 => :header_tags_recommend_nevrs,
5059 => :header_tags_suggest_nevrs,
5060 => :header_tags_supplement_nevrs,
5061 => :header_tags_enhance_nevrs,
5062 => :header_tags_encoding,
5063 => :header_tags_file_trigger_install_internal,
5064 => :header_tags_file_trigger_uninstall_internal,
5065 => :header_tags_file_trigger_post_uninstall_internal,
5066 => :header_tags_file_trigger_scripts,
5067 => :header_tags_file_trigger_script_prog,
5068 => :header_tags_file_trigger_script_flags,
5069 => :header_tags_file_trigger_name,
5070 => :header_tags_file_trigger_index,
5071 => :header_tags_file_trigger_version,
5072 => :header_tags_file_trigger_flags,
5073 => :header_tags_trans_file_trigger_install_internal,
5074 => :header_tags_trans_file_trigger_uninstall_internal,
5075 => :header_tags_trans_file_trigger_post_uninstall_internal,
5076 => :header_tags_trans_file_trigger_scripts,
5077 => :header_tags_trans_file_trigger_script_prog,
5078 => :header_tags_trans_file_trigger_script_flags,
5079 => :header_tags_trans_file_trigger_name,
5080 => :header_tags_trans_file_trigger_index,
5081 => :header_tags_trans_file_trigger_version,
5082 => :header_tags_trans_file_trigger_flags,
5083 => :header_tags_remove_path_postfixes_internal,
5084 => :header_tags_file_trigger_priorities,
5085 => :header_tags_trans_file_trigger_priorities,
5086 => :header_tags_file_trigger_conds,
5087 => :header_tags_file_trigger_type,
5088 => :header_tags_trans_file_trigger_conds,
5089 => :header_tags_trans_file_trigger_type,
5090 => :header_tags_file_signatures,
5091 => :header_tags_file_signature_length,
5092 => :header_tags_payload_sha256,
5093 => :header_tags_payload_sha256_algo_obsolete,
5094 => :header_tags_auto_installed_unimplemented,
5095 => :header_tags_identity_unimplemented,
5096 => :header_tags_modularity_label,
5097 => :header_tags_payload_sha256_alt,
5098 => :header_tags_arch_suffix,
5099 => :header_tags_spec,
5100 => :header_tags_translation_url,
5101 => :header_tags_upstream_releases,
5102 => :header_tags_source_license_internal,
5103 => :header_tags_pre_untrans,
5104 => :header_tags_post_untrans,
5105 => :header_tags_pre_untrans_prog,
5106 => :header_tags_post_untrans_prog,
5107 => :header_tags_pre_untrans_flags,
5108 => :header_tags_post_untrans_flags,
5109 => :header_tags_sys_users,
5110 => :header_tags_build_system_internal,
5111 => :header_tags_build_option_internal,
5112 => :header_tags_payload_size,
5113 => :header_tags_payload_size_alt,
5114 => :header_tags_rpm_format,
5115 => :header_tags_file_mime_index,
5116 => :header_tags_mime_dict,
5117 => :header_tags_file_mimes,
5118 => :header_tags_package_digests,
5119 => :header_tags_package_digest_algos,
5120 => :header_tags_source_nevr,
5121 => :header_tags_payload_sha512,
5122 => :header_tags_payload_sha512_alt,
5123 => :header_tags_payload_sha3_256,
5124 => :header_tags_payload_sha3_256_alt,
}
I__HEADER_TAGS = HEADER_TAGS.invert
OPERATING_SYSTEMS = {
0 => :operating_systems_not_set,
1 => :operating_systems_linux,
2 => :operating_systems_irix,
255 => :operating_systems_no_os,
}
I__OPERATING_SYSTEMS = OPERATING_SYSTEMS.invert
RECORD_TYPES = {
1 => :record_types_char,
2 => :record_types_uint8,
3 => :record_types_uint16,
4 => :record_types_uint32,
5 => :record_types_uint64,
6 => :record_types_string,
7 => :record_types_bin,
8 => :record_types_string_array,
9 => :record_types_i18n_string,
}
I__RECORD_TYPES = RECORD_TYPES.invert
RPM_TYPES = {
0 => :rpm_types_binary,
1 => :rpm_types_source,
}
I__RPM_TYPES = RPM_TYPES.invert
SIGNATURE_TAGS = {
62 => :signature_tags_signatures,
63 => :signature_tags_header_immutable,
100 => :signature_tags_i18n_table,
264 => :signature_tags_bad_sha1_1_obsolete,
265 => :signature_tags_bad_sha1_2_obsolete,
267 => :signature_tags_dsa,
268 => :signature_tags_rsa,
269 => :signature_tags_sha1,
270 => :signature_tags_long_size,
271 => :signature_tags_long_archive_size,
273 => :signature_tags_sha256,
274 => :signature_tags_file_signatures,
275 => :signature_tags_file_signature_length,
276 => :signature_tags_verity_signatures,
277 => :signature_tags_verity_signature_algo,
278 => :signature_tags_openpgp,
279 => :signature_tags_sha3_256,
999 => :signature_tags_reserved,
1000 => :signature_tags_size,
1001 => :signature_tags_le_md5_1_obsolete,
1002 => :signature_tags_pgp,
1003 => :signature_tags_le_md5_2_obsolete,
1004 => :signature_tags_md5,
1005 => :signature_tags_gpg,
1006 => :signature_tags_pgp5_obsolete,
1007 => :signature_tags_payload_size,
1008 => :signature_tags_reserved_space,
}
I__SIGNATURE_TAGS = SIGNATURE_TAGS.invert
def initialize(_io, _parent = nil, _root = nil)
super(_io, _parent, _root || self)
_read
end
def _read
@lead = Lead.new(@_io, self, @_root)
@signature = Header.new(@_io, self, @_root, true)
@signature_padding = @_io.read_bytes(-(_io.pos) % 8)
if ofs_header < 0
@_unnamed3 = @_io.read_bytes(0)
end
@header = Header.new(@_io, self, @_root, false)
if ofs_payload < 0
@_unnamed5 = @_io.read_bytes(0)
end
@signature_tags_steps = []
(signature.header_record.num_index_records).times { |i|
@signature_tags_steps << SignatureTagsStep.new(@_io, self, @_root, i, (i != 0 ? signature_tags_steps[i - 1].size_tag_idx : -1), (i != 0 ? signature_tags_steps[i - 1].long_size_tag_idx : -1))
}
@header_tags_steps = []
(header.header_record.num_index_records).times { |i|
@header_tags_steps << HeaderTagsStep.new(@_io, self, @_root, i, (i != 0 ? header_tags_steps[i - 1].payload_size_tag_idx : -1))
}
self
end
class Dummy < Kaitai::Struct::Struct
def initialize(_io, _parent = nil, _root = nil)
super(_io, _parent, _root)
_read
end
def _read
self
end
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)
class Header < Kaitai::Struct::Struct
def initialize(_io, _parent = nil, _root = nil, is_signature)
super(_io, _parent, _root)
@is_signature = is_signature
_read
end
def _read
@header_record = HeaderRecord.new(@_io, self, @_root)
@index_records = []
(header_record.num_index_records).times { |i|
@index_records << HeaderIndexRecord.new(@_io, self, @_root)
}
_io_storage_section = @_io.substream(header_record.len_storage_section)
@storage_section = Dummy.new(_io_storage_section, self, @_root)
self
end
def is_header
return @is_header unless @is_header.nil?
@is_header = !(is_signature)
@is_header
end
attr_reader :header_record
attr_reader :index_records
attr_reader :storage_section
attr_reader :is_signature
attr_reader :_raw_storage_section
end
class HeaderIndexRecord < Kaitai::Struct::Struct
def initialize(_io, _parent = nil, _root = nil)
super(_io, _parent, _root)
_read
end
def _read
@tag_raw = @_io.read_u4be
@record_type = Kaitai::Struct::Stream::resolve_enum(Rpm::RECORD_TYPES, @_io.read_u4be)
raise Kaitai::Struct::ValidationNotInEnumError.new(@record_type, @_io, "/types/header_index_record/seq/1") if not Rpm::I__RECORD_TYPES.key?(@record_type)
@ofs_body = @_io.read_u4be
@count = @_io.read_u4be
self
end
def body
return @body unless @body.nil?
io = _parent.storage_section._io
_pos = io.pos
io.seek(ofs_body)
case record_type
when :record_types_bin
@body = RecordTypeBin.new(io, self, @_root, len_value)
when :record_types_char
@body = RecordTypeUint8.new(io, self, @_root, num_values)
when :record_types_i18n_string
@body = RecordTypeStringArray.new(io, self, @_root, num_values)
when :record_types_string
@body = RecordTypeString.new(io, self, @_root)
when :record_types_string_array
@body = RecordTypeStringArray.new(io, self, @_root, num_values)
when :record_types_uint16
@body = RecordTypeUint16.new(io, self, @_root, num_values)
when :record_types_uint32
@body = RecordTypeUint32.new(io, self, @_root, num_values)
when :record_types_uint64
@body = RecordTypeUint64.new(io, self, @_root, num_values)
when :record_types_uint8
@body = RecordTypeUint8.new(io, self, @_root, num_values)
end
io.seek(_pos)
@body
end
def header_tag
return @header_tag unless @header_tag.nil?
if _parent.is_header
@header_tag = Kaitai::Struct::Stream::resolve_enum(Rpm::HEADER_TAGS, tag_raw)
end
@header_tag
end
def len_value
return @len_value unless @len_value.nil?
if record_type == :record_types_bin
@len_value = count
end
@len_value
end
def num_values
return @num_values unless @num_values.nil?
if record_type != :record_types_bin
@num_values = count
end
@num_values
end
def signature_tag
return @signature_tag unless @signature_tag.nil?
if _parent.is_signature
@signature_tag = Kaitai::Struct::Stream::resolve_enum(Rpm::SIGNATURE_TAGS, tag_raw)
end
@signature_tag
end
##
# prefer to access `signature_tag` and `header_tag` instead
attr_reader :tag_raw
attr_reader :record_type
attr_reader :ofs_body
##
# internal; access `num_values` and `len_value` instead
attr_reader :count
end
class HeaderRecord < Kaitai::Struct::Struct
def initialize(_io, _parent = nil, _root = nil)
super(_io, _parent, _root)
_read
end
def _read
@magic = @_io.read_bytes(4)
raise Kaitai::Struct::ValidationNotEqualError.new([142, 173, 232, 1].pack('C*'), @magic, @_io, "/types/header_record/seq/0") if not @magic == [142, 173, 232, 1].pack('C*')
@reserved = @_io.read_bytes(4)
raise Kaitai::Struct::ValidationNotEqualError.new([0, 0, 0, 0].pack('C*'), @reserved, @_io, "/types/header_record/seq/1") if not @reserved == [0, 0, 0, 0].pack('C*')
@num_index_records = @_io.read_u4be
raise Kaitai::Struct::ValidationLessThanError.new(1, @num_index_records, @_io, "/types/header_record/seq/2") if not @num_index_records >= 1
raise Kaitai::Struct::ValidationGreaterThanError.new((_parent.is_signature ? 32 : 65535), @num_index_records, @_io, "/types/header_record/seq/2") if not @num_index_records <= (_parent.is_signature ? 32 : 65535)
@len_storage_section = @_io.read_u4be
raise Kaitai::Struct::ValidationGreaterThanError.new((_parent.is_signature ? (64 * 1024) * 1024 : 268435455), @len_storage_section, @_io, "/types/header_record/seq/3") if not @len_storage_section <= (_parent.is_signature ? (64 * 1024) * 1024 : 268435455)
self
end
attr_reader :magic
attr_reader :reserved
attr_reader :num_index_records
##
# Size of the storage area for the data
# pointed to by the Index Records.
attr_reader :len_storage_section
end
##
# Like `signature_tags_step`, but looks for `header_tags::payload_size`,
# which is where v6 packages store the payload size.
class HeaderTagsStep < Kaitai::Struct::Struct
def initialize(_io, _parent = nil, _root = nil, idx, prev_payload_size_tag_idx)
super(_io, _parent, _root)
@idx = idx
@prev_payload_size_tag_idx = prev_payload_size_tag_idx
_read
end
def _read
self
end
def payload_size_tag_idx
return @payload_size_tag_idx unless @payload_size_tag_idx.nil?
@payload_size_tag_idx = (prev_payload_size_tag_idx != -1 ? prev_payload_size_tag_idx : ( ((_parent.header.index_records[idx].header_tag == :header_tags_payload_size) && (_parent.header.index_records[idx].record_type == :record_types_uint64) && (_parent.header.index_records[idx].num_values >= 1)) ? idx : -1))
@payload_size_tag_idx
end
attr_reader :idx
attr_reader :prev_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 https://github.com/rpm-software-management/rpm/blob/ec9ea8c43808c346da4b6cb454cdc58aef8e506a/docs/manual/format_lead.md Source
class Lead < Kaitai::Struct::Struct
def initialize(_io, _parent = nil, _root = nil)
super(_io, _parent, _root)
_read
end
def _read
@magic = @_io.read_bytes(4)
raise Kaitai::Struct::ValidationNotEqualError.new([237, 171, 238, 219].pack('C*'), @magic, @_io, "/types/lead/seq/0") if not @magic == [237, 171, 238, 219].pack('C*')
@version = RpmVersion.new(@_io, self, @_root)
@type = Kaitai::Struct::Stream::resolve_enum(Rpm::RPM_TYPES, @_io.read_u2be)
@architecture = Kaitai::Struct::Stream::resolve_enum(Rpm::ARCHITECTURES, @_io.read_u2be)
@package_name = (Kaitai::Struct::Stream::bytes_terminate(@_io.read_bytes(66), 0, false)).force_encoding("UTF-8")
@os = Kaitai::Struct::Stream::resolve_enum(Rpm::OPERATING_SYSTEMS, @_io.read_u2be)
@signature_type = @_io.read_u2be
raise Kaitai::Struct::ValidationNotEqualError.new(5, @signature_type, @_io, "/types/lead/seq/6") if not @signature_type == 5
@reserved = @_io.read_bytes(16)
self
end
attr_reader :magic
attr_reader :version
attr_reader :type
attr_reader :architecture
attr_reader :package_name
attr_reader :os
##
# @see https://github.com/rpm-software-management/rpm/blob/ec9ea8c43808c346da4b6cb454cdc58aef8e506a/lib/rpmlead.cc#L20-L21 Source
attr_reader :signature_type
attr_reader :reserved
end
class RecordTypeBin < Kaitai::Struct::Struct
def initialize(_io, _parent = nil, _root = nil, len_value)
super(_io, _parent, _root)
@len_value = len_value
_read
end
def _read
@values = []
(1).times { |i|
@values << @_io.read_bytes(len_value)
}
self
end
attr_reader :values
attr_reader :len_value
end
class RecordTypeString < Kaitai::Struct::Struct
def initialize(_io, _parent = nil, _root = nil)
super(_io, _parent, _root)
_read
end
def _read
@values = []
(1).times { |i|
@values << (@_io.read_bytes_term(0, false, true, true)).force_encoding("UTF-8")
}
self
end
attr_reader :values
end
class RecordTypeStringArray < Kaitai::Struct::Struct
def initialize(_io, _parent = nil, _root = nil, num_values)
super(_io, _parent, _root)
@num_values = num_values
_read
end
def _read
@values = []
(num_values).times { |i|
@values << (@_io.read_bytes_term(0, false, true, true)).force_encoding("UTF-8")
}
self
end
attr_reader :values
attr_reader :num_values
end
class RecordTypeUint16 < Kaitai::Struct::Struct
def initialize(_io, _parent = nil, _root = nil, num_values)
super(_io, _parent, _root)
@num_values = num_values
_read
end
def _read
@values = []
(num_values).times { |i|
@values << @_io.read_u2be
}
self
end
attr_reader :values
attr_reader :num_values
end
class RecordTypeUint32 < Kaitai::Struct::Struct
def initialize(_io, _parent = nil, _root = nil, num_values)
super(_io, _parent, _root)
@num_values = num_values
_read
end
def _read
@values = []
(num_values).times { |i|
@values << @_io.read_u4be
}
self
end
attr_reader :values
attr_reader :num_values
end
class RecordTypeUint64 < Kaitai::Struct::Struct
def initialize(_io, _parent = nil, _root = nil, num_values)
super(_io, _parent, _root)
@num_values = num_values
_read
end
def _read
@values = []
(num_values).times { |i|
@values << @_io.read_u8be
}
self
end
attr_reader :values
attr_reader :num_values
end
class RecordTypeUint8 < Kaitai::Struct::Struct
def initialize(_io, _parent = nil, _root = nil, num_values)
super(_io, _parent, _root)
@num_values = num_values
_read
end
def _read
@values = []
(num_values).times { |i|
@values << @_io.read_u1
}
self
end
attr_reader :values
attr_reader :num_values
end
class RpmVersion < Kaitai::Struct::Struct
def initialize(_io, _parent = nil, _root = nil)
super(_io, _parent, _root)
_read
end
def _read
@major = @_io.read_u1
raise Kaitai::Struct::ValidationLessThanError.new(3, @major, @_io, "/types/rpm_version/seq/0") if not @major >= 3
raise Kaitai::Struct::ValidationGreaterThanError.new(4, @major, @_io, "/types/rpm_version/seq/0") if not @major <= 4
@minor = @_io.read_u1
self
end
##
# 3 in v3 and v4 packages, 4 in v6 packages.
# @see https://github.com/rpm-software-management/rpm/blob/ec9ea8c43808c346da4b6cb454cdc58aef8e506a/lib/rpmlead.cc#L51-L52 Source
attr_reader :major
attr_reader :minor
end
##
# 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.
class SignatureTagsStep < Kaitai::Struct::Struct
def initialize(_io, _parent = nil, _root = nil, idx, prev_size_tag_idx, prev_long_size_tag_idx)
super(_io, _parent, _root)
@idx = idx
@prev_size_tag_idx = prev_size_tag_idx
@prev_long_size_tag_idx = prev_long_size_tag_idx
_read
end
def _read
self
end
def long_size_tag_idx
return @long_size_tag_idx unless @long_size_tag_idx.nil?
@long_size_tag_idx = (prev_long_size_tag_idx != -1 ? prev_long_size_tag_idx : ( ((_parent.signature.index_records[idx].signature_tag == :signature_tags_long_size) && (_parent.signature.index_records[idx].record_type == :record_types_uint64) && (_parent.signature.index_records[idx].num_values >= 1)) ? idx : -1))
@long_size_tag_idx
end
def size_tag_idx
return @size_tag_idx unless @size_tag_idx.nil?
@size_tag_idx = (prev_size_tag_idx != -1 ? prev_size_tag_idx : ( ((_parent.signature.index_records[idx].signature_tag == :signature_tags_size) && (_parent.signature.index_records[idx].record_type == :record_types_uint32) && (_parent.signature.index_records[idx].num_values >= 1)) ? idx : -1))
@size_tag_idx
end
attr_reader :idx
attr_reader :prev_size_tag_idx
attr_reader :prev_long_size_tag_idx
end
def has_header_payload_size_tag
return @has_header_payload_size_tag unless @has_header_payload_size_tag.nil?
@has_header_payload_size_tag = header_tags_steps.last.payload_size_tag_idx != -1
@has_header_payload_size_tag
end
def has_payload
return @has_payload unless @has_payload.nil?
@has_payload = ((has_header_payload_size_tag) || (has_signature_long_size_tag) || (has_signature_size_tag))
@has_payload
end
def has_signature_long_size_tag
return @has_signature_long_size_tag unless @has_signature_long_size_tag.nil?
@has_signature_long_size_tag = signature_tags_steps.last.long_size_tag_idx != -1
@has_signature_long_size_tag
end
def has_signature_size_tag
return @has_signature_size_tag unless @has_signature_size_tag.nil?
@has_signature_size_tag = signature_tags_steps.last.size_tag_idx != -1
@has_signature_size_tag
end
def header_payload_size_tag
return @header_payload_size_tag unless @header_payload_size_tag.nil?
if has_header_payload_size_tag
@header_payload_size_tag = header.index_records[header_tags_steps.last.payload_size_tag_idx]
end
@header_payload_size_tag
end
def len_header
return @len_header unless @len_header.nil?
@len_header = ofs_payload - ofs_header
@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>
def len_payload
return @len_payload unless @len_payload.nil?
if has_payload
@len_payload = (has_header_payload_size_tag ? header_payload_size_tag.body.values[0] : (has_signature_long_size_tag ? signature_long_size_tag.body.values[0] - len_header : signature_size_tag.body.values[0] - len_header))
end
@len_payload
end
def ofs_header
return @ofs_header unless @ofs_header.nil?
@ofs_header = _io.pos
@ofs_header
end
def ofs_payload
return @ofs_payload unless @ofs_payload.nil?
@ofs_payload = _io.pos
@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 https://github.com/rpm-software-management/rpm/blob/ec9ea8c43808c346da4b6cb454cdc58aef8e506a/docs/manual/format_v6.md#payload Source
# @see https://github.com/rpm-software-management/rpm/blob/ec9ea8c43808c346da4b6cb454cdc58aef8e506a/docs/manual/format_v4.md#payload Source
def payload
return @payload unless @payload.nil?
if has_payload
_pos = @_io.pos
@_io.seek(ofs_payload)
@payload = @_io.read_bytes(len_payload)
@_io.seek(_pos)
end
@payload
end
def signature_long_size_tag
return @signature_long_size_tag unless @signature_long_size_tag.nil?
if has_signature_long_size_tag
@signature_long_size_tag = signature.index_records[signature_tags_steps.last.long_size_tag_idx]
end
@signature_long_size_tag
end
def signature_size_tag
return @signature_size_tag unless @signature_size_tag.nil?
if has_signature_size_tag
@signature_size_tag = signature.index_records[signature_tags_steps.last.size_tag_idx]
end
@signature_size_tag
end
attr_reader :lead
attr_reader :signature
attr_reader :signature_padding
attr_reader :_unnamed3
attr_reader :header
attr_reader :_unnamed5
attr_reader :signature_tags_steps
attr_reader :header_tags_steps
end