RPM package file: Ruby parsing library

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:

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.

Application

RPM Package Manager

File extension

["rpm", "srpm", "src.rpm", "drpm"]

KS implementation details

License: CC0-1.0
Minimal Kaitai Struct required: 0.11

References

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.

Usage

Runtime 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

Code

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.

Ruby source code to parse RPM package file

rpm.rb

# 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