Argh-P-M! – Dissecting the RPM file format

As the first actual content on my new blog, let me tell you the story of how I went absolutely crazy.

On my private systems, I ship configuration as system packages. Every distribution has their own tooling and process for building these packages, but I eventually grew tired of the ceremony involved in it, and wrote my own system package compiler. Since I’m using Arch Linux everywhere, the first version generated only Pacman packages, but I was determined to make it truly cross-distribution. The first step was support for Debian packages, which I implemented in a mere two evenings (one for understanding the format, one for writing the generator).

Next to dpkg, the other widely deployed package format is RPM, so I set out to add support for RPM as well. If I could write the Debian generator in two days, then surely RPM support wouldn’t take that long, either. Little did I know that I was embarking on a multi-month endeavor (including multiple week-long breaks to restore my sanity). To add insult to injury, I stubbornly refused to add dependencies and use existing tooling (i.e., the rpm-build(1) command). I wanted to serialize the format directly from my own code, like I did for Pacman and Debian packages.

How to get a package format right

Before we get to RPM, let’s have a brief look at how a sane package format looks like. I always use the package for tree(1) as a sample because it’s a small package with just one executable and maybe a manpage that’s available on every distribution.

$ file -kr tree_1.7.0-3_amd64.deb
tree_1.7.0-3_amd64.deb: Debian binary package (format 2.0)
- current ar archive
- archive file
- data

file tells us that a Debian package is actually an ar archive. Let’s unpack it.

$ ar x tree_1.7.0-3_amd64.deb
$ ls
control.tar.gz  data.tar.xz  debian-binary  tree_1.7.0-3_amd64.deb

$ cat debian-binary
2.0

$ tar tf control.tar.gz
./
./control
./md5sums

$ tar tf data.tar.xz | grep -v '/$'
./usr/bin/tree
./usr/share/doc/tree/copyright
./usr/share/doc/tree/TODO
./usr/share/doc/tree/changelog.Debian.gz
./usr/share/doc/tree/changelog.gz
./usr/share/doc/tree/README.gz
./usr/share/man/man1/tree.1.gz

The ar archive contains three files:

The separation into two different subarchives is really clever: The actual application data can be compressed very tightly with xz, to minimize disk space and network usage on the package mirror. The metadata is compressed with gz only, so it can be accessed quickly when a repository index is being created. The files in control.tar.gz are simple plain text files, so you can process them comfortably even from shell scripts:

$ tar Oxf control.tar.gz ./control | head
Package: tree
Version: 1.7.0-3
Architecture: amd64
Maintainer: Florian Ernst <florian@debian.org>
Installed-Size: 100
Depends: libc6 (>= 2.14)
Section: utils
Priority: optional
Homepage: http://mama.indstate.edu/users/ice/tree/
Description: displays an indented directory tree, in color

So much for dpkg. Pacman packages (for Arch Linux) don’t look too different, but they only have a single archive for everything, as opposed to the two tarballs inside a Debian package. Metadata is placed in the package as files with special names in the root directory:

$ tar tf tree-1.7.0-1-x86_64.pkg.tar.xz
.PKGINFO
.MTREE
usr/
usr/bin/
usr/share/
usr/share/man/
usr/share/man/man1/
usr/share/man/man1/tree.1.gz
usr/bin/tree

$ tar Oxf tree-1.7.0-1-x86_64.pkg.tar.xz .PKGINFO | head
# Generated by makepkg 4.1.2
# using fakeroot version 1.20
# Thu Apr 24 18:18:51 UTC 2014
pkgname = tree
pkgver = 1.7.0-1
pkgdesc = A directory listing program displaying a depth indented list of files
url = http://mama.indstate.edu/users/ice/tree/
builddate = 1398363531
packager = Giovanni Scafora <giovanni@archlinux.org>
size = 78848

The Debian approach with multiple archives is preferable, though, because you can decompress the metadata quicker. With the single archive approach of Pacman, it might be that the metadata is put at the end of the tarball. You would then have to decompress the whole archive (possibly over a gigabyte) to get at a few kilobytes of metadata.

By the way: I want to take a moment to acknowledge the outstanding Debian Policy Manual, which specifies every last bit of the dpkg format in a relevant and concise manner. Kudos to everyone who contributed to it.

Cracking open an RPM file

Now let’s grab an RPM from rpmfind.net and see what’s in it.

$ file -kr tree-1.7.0-7.fc26.x86_64.rpm
tree-1.7.0-7.fc26.x86_64.rpm: RPM v3.0 bin i386/x86_64
- data

file cannot tell us anything except that the RPM file actually is an RPM file. It appears that RPM is really an original file format and not built on top of an existing format such as ar (for dpkg) or tar (for Pacman). However, there is a formal specification of the RPM format, as part of the Linux Standard Base. Let’s see what we got there.

An RPM format file consists of 4 sections, the Lead, Signature, Header, and the Payload. All values are stored in network byte order.

Two sentences in, and I already don’t like where this is going. Let’s start at the top. The lead section is defined by its C declaration:

struct rpmlead {
    unsigned char magic[4];
    unsigned char major, minor;
    short type;
    short archnum;
    char name[66];
    short osnum;
    short signature_type;
    char reserved[16];
};

Here’s how it looks in my sample. I’m highlighting the fields in alternating colors for your viewing pleasure:

$ xxd tree-1.7.0-7.fc26.x86_64.rpm | head -n6
00000000: edab eedb 0300 0000 0001 7472 6565 2d31  ..........tree-1
00000010: 2e37 2e30 2d37 2e66 6332 3600 0000 0000  .7.0-7.fc26.....
00000020: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000030: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000040: 0000 0000 0000 0000 0000 0000 0001 0005  ................
00000050: 0000 0000 0000 0000 0000 0000 0000 0000  ................

The spec tells us what everything means:

If you happen to have RPM installed, you can check out /usr/lib/rpm/rpmrc for a list of acceptable values for archnum and osnum. Look for lines starting with arch_canon and os_canon. This file comes in really handy if you ever need a list of every single architecture and Unix-like OS that has ever existed, anywhere.

Headers inside headers

According to a document on the rpm-org website, the lead section used to be the only place in an RPM file for metadata. But when you take a second look, you will notice that a lot of very important metadata is missing in it. There is no way to specify dependencies between packages, a feature so fundamental to system package managers that I wonder how RPM ever got by without it.

The RPM developers decided to ditch the lead section, leaving it in the file format only for the magic number and for backwards compatibility. What happened next is an effect so common in software development that it has its own name: the second-system effect or second-system syndrome. From Wikipedia:

The second-system effect proposes that, when an architect designs a second system, it is the most dangerous system they will ever design, because they will tend to incorporate all of the additions they originally did not add to the first system due to inherent time constraints. Thus, when embarking on a second system, an engineer should be mindful that they are susceptible to over-engineering it.

And that’s exactly what happened. While Debian and Pacman packages use simple text files with key: value lines for their metadata, the RPM spec includes a custom binary format for a key-value database. Both the signature section and header section follow this format. My favorite part of this is that because of this, an RPM file actually has five headers:

The key-value database’s header looks like this (comments mine):

struct rpmheader {
    unsigned char magic[4];    // must be "\216\255\350\001"
    unsigned char reserved[4]; // must be "\0\0\0\0"
    int nindex;                // number of index records
    int hsize;                 // size of storage area for data
};

After that comes one index record for each key-value pair, followed by the storage area. The index records look like this (comments mine):

struct rpmhdrindex {
    int tag;    // the key
    int type;   // data type
    int offset; // where to find the data in the storage area
    int count;  // how many data items are stored in this key
};

There are a few things to notice here:

The header section

With the storage format out of the way, let’s have a look at what data is actually stored in there. I’ll use the output of dump-package, a little inspection tool that I wrote during this endeavor. It dumps the binary data structures into a nice and readable format. Unfortunately, the data in the structures is not nearly as readable.

tag 1000 (NAME): length 1
    string: tree
tag 1001 (VERSION): length 1
    string: 1.7.0
tag 1002 (RELEASE): length 1
    string: 7.fc26
tag 1004 (SUMMARY): length 1
    translatable string: File system tree viewer
tag 1005 (DESCRIPTION): length 1
    translatable string: The tree utility recursively displays the contents of directories in a
    tree-like format.  Tree is basically a UNIX port of the DOS tree
    utility.
tag 1009 (SIZE): length 1
    int32: 99355
tag 1014 (LICENSE): length 1
    string: GPLv2+
tag 1021 (OS): length 1
    string: linux
tag 1022 (ARCH): length 1
    string: x86_64

So far, so unsurprising. Some stuff is duplicated with the lead section (which is okay since it is considered deprecated). Name, version and release are cleanly separated.

tag 1010 (DISTRIBUTION): length 1
    string: Fedora Project
tag 1011 (VENDOR): length 1
    string: Fedora Project
tag 1015 (PACKAGER): length 1
    string: Fedora Project

From the department of redundancy department.

tag 1016 (GROUP): length 1
    translatable string: Applications/File

The RPMTAG_GROUP is listed in the LSB spec as “required”, but it doesn’t say anything about acceptable values. The documentation for openSUSE has such a list, but the Fedora Project Wiki says that “the Group: tag is unnecessary”.

tag 1020 (URL): length 1
    string: http://mama.indstate.edu/users/ice/tree/

The spec lists two tags with URLs, with incredibly helpful descriptions:

RPMTAG_URL – Generic package information URL
RPMTAG_DISTURL – URL for package

Luckily, they’re both optional, so I don’t need to care.

tag 1006 (BUILDTIME): length 1
    int32: 1472740798
tag 1007 (BUILDHOST): length 1
    string: buildvm-23.phx2.fedoraproject.org
tag 1044 (SOURCERPM): length 1
    string: tree-1.7.0-7.fc26.src.rpm
tag 1064 (RPMVERSION): length 1
    string: 4.13.0-rc1
tag 1122 (OPTFLAGS): length 1
    string: -O2 -g -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -m64 -mtune=generic
tag 1132 (PLATFORM): length 1
    string: x86_64-redhat-linux-gnu

With over 300 tags, it’s no surprise that the usefulness of many of these tags is debatable, especially considering the recent push towards reproducible packages.

The usefulness curve quickly goes downhill:

tag 1124 (PAYLOADFORMAT): length 1
    string: cpio
tag 1125 (PAYLOADCOMPRESSOR): length 1
    string: xz
tag 1126 (PAYLOADFLAGS): length 1
    string: 2

This tells us that the payload is a CPIO archive, compressed with XZ. You know, because there is no way at all to infer this from the payload itself. Fun fact: This part of the dump shows that this Fedora RPM is actually not LSB-compliant. The LSB spec states clearly that the payload compressor must be gzip, and that RPMTAG_PAYLOADFLAGS must be 9. If you’re wondering what payload flags are:

This tag indicates the compression level used for the Payload.

We’ve reached negative usefulness values.

By now, you might be wondering: RPM files may have sections, with two sections having their own headers that define a key-value database, with several keys of dubious utility. But do you have anything in store for a true connaisseur of byzantine complexity? Well, say no more!

tag 1080 (CHANGELOGTIME): length 70
    int32: 1472731200
    int32: 1454673600
    int32: 1434715200
    [...]
tag 1081 (CHANGELOGNAME): length 70
    string: Kamil Dudka <kdudka@redhat.com> - 1.7.0-7
    string: Fedora Release Engineering <releng@fedoraproject.org> - 1.7.0-6
    string: Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.7.0-5
    [...]
tag 1082 (CHANGELOGTEXT): length 70
    string: - drop a non-upstream patch that disabled color output by default (#1284657)
    string: - Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild
    string: - Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild
    [...]

A changelog.txt is for filthy peasants. Real men store their changelog in a structured key-value database. I don’t understand why they didn’t split RPMTAG_CHANGELOGNAME into author name, author e-mail and package version, though.

But wait, there’s more!

Every package format I looked at knows four types of relations to other packages:

Here’s how RPM does package relations:

tag 1047 (PROVIDENAME): length 2
    string: tree
    string: tree(x86-64)
tag 1112 (PROVIDEFLAGS): length 2
    int32: 8 = 0x8 = 0o10
    int32: 8 = 0x8 = 0o10
tag 1113 (PROVIDEVERSION): length 2
    string: 1.7.0-7.fc26
    string: 1.7.0-7.fc26

Each relation is split into three fields: the other package’s name and version, and some flags. Now if this were a Debian package, this part would look like



because it’s extremely obvious that tree and tree(x86-64) are provided by the x86_64 package that’s literally called tree and nobody would ever want to write that down. If you wonder what the RPMTAG_PROVIDEFLAGS are:

/** \ingroup rpmds
 * Dependency Attributes.
 */
enum rpmsenseFlags_e {
    RPMSENSE_ANY	= 0,
    RPMSENSE_LESS	= (1 << 1),
    RPMSENSE_GREATER	= (1 << 2),
    RPMSENSE_EQUAL	= (1 << 3),
    /* bit 4 unused */
    RPMSENSE_POSTTRANS	= (1 << 5),	/*!< %posttrans dependency */
    RPMSENSE_PREREQ	= (1 << 6), 	/* legacy prereq dependency */
    RPMSENSE_PRETRANS	= (1 << 7),	/*!< Pre-transaction dependency. */
    RPMSENSE_INTERP	= (1 << 8),	/*!< Interpreter used by scriptlet. */
    RPMSENSE_SCRIPT_PRE	= (1 << 9),	/*!< %pre dependency. */
    RPMSENSE_SCRIPT_POST = (1 << 10),	/*!< %post dependency. */
    RPMSENSE_SCRIPT_PREUN = (1 << 11),	/*!< %preun dependency. */
    RPMSENSE_SCRIPT_POSTUN = (1 << 12), /*!< %postun dependency. */
    RPMSENSE_SCRIPT_VERIFY = (1 << 13),	/*!< %verify dependency. */
    RPMSENSE_FIND_REQUIRES = (1 << 14), /*!< find-requires generated dependency. */
    RPMSENSE_FIND_PROVIDES = (1 << 15), /*!< find-provides generated dependency. */

    RPMSENSE_TRIGGERIN	= (1 << 16),	/*!< %triggerin dependency. */
    RPMSENSE_TRIGGERUN	= (1 << 17),	/*!< %triggerun dependency. */
    RPMSENSE_TRIGGERPOSTUN = (1 << 18),	/*!< %triggerpostun dependency. */
    RPMSENSE_MISSINGOK	= (1 << 19),	/*!< suggests/enhances hint. */
    /* bits 20-23 unused */
    RPMSENSE_RPMLIB = (1 << 24),	/*!< rpmlib(feature) dependency. */
    RPMSENSE_TRIGGERPREIN = (1 << 25),	/*!< %triggerprein dependency. */
    RPMSENSE_KEYRING	= (1 << 26),
    /* bit 27 unused */
    RPMSENSE_CONFIG	= (1 << 28)
};

The name of the enum is probably a typing error because there is no sense anywhere to be found in RPM. But let’s look at the values… Zero is any, that makes sense. Then we have less, greater, equal… so far so good. Bit 4 is unused, well some legacy is to be expected. Then we have… What? Posttrans dependencies? Interpreter used by what? rpmlib feature what what? And what the fuck is a keyring dependency?!?

As you can see, I can’t explain every last bit of this, and documentation ranges from sparse to non-existent. But I can enlighten you on a particular bit in this field, RPMSENSE_RPMLIB. Let’s look at the dependencies this package requires:

tag 1048 (REQUIREFLAGS): length 11
    int32: 16384 = 0x4000 = 0o40000
    int32: 16384 = 0x4000 = 0o40000
    int32: 16384 = 0x4000 = 0o40000
    int32: 16384 = 0x4000 = 0o40000
    int32: 16384 = 0x4000 = 0o40000
    int32: 16384 = 0x4000 = 0o40000
    int32: 16777226 = 0x100000A = 0o100000012
    int32: 16777226 = 0x100000A = 0o100000012
    int32: 16777226 = 0x100000A = 0o100000012
    int32: 16777226 = 0x100000A = 0o100000012
    int32: 16384 = 0x4000 = 0o40000
tag 1049 (REQUIRENAME): length 11
    string: libc.so.6()(64bit)
    string: libc.so.6(GLIBC_2.14)(64bit)
    string: libc.so.6(GLIBC_2.2.5)(64bit)
    string: libc.so.6(GLIBC_2.3)(64bit)
    string: libc.so.6(GLIBC_2.3.4)(64bit)
    string: libc.so.6(GLIBC_2.4)(64bit)
    string: rpmlib(CompressedFileNames)
    string: rpmlib(FileDigests)
    string: rpmlib(PayloadFilesHavePrefix)
    string: rpmlib(PayloadIsXz)
    string: rtld(GNU_HASH)
tag 1050 (REQUIREVERSION): length 11
    string: 
    string: 
    string: 
    string: 
    string: 
    string: 
    string: 3.0.4-1
    string: 4.6.0-1
    string: 4.0-1
    string: 5.2-1
    string: 

I have no idea why there are six dependencies to different versions of libc.so.6, and why they encoded the version in the name field rather than in the version field.

Update: I’ve been told that these strange libc dependencies refer to different ABIs supported by the same libc binary. That’s somehow even worse than what I was imagining.

But the thing that’s even more brilliant is the four dependencies below that. The flags include the RPMSENSE_RPMLIB bit from before, and the names are all like rpmlib(SomeThing). Let’s write them down more nicely:

rpmlib(CompressedFileNames) = 3.0.4-1
rpmlib(FileDigests) = 4.6.0-1
rpmlib(PayloadFilesHavePrefix) = 4.0-1
rpmlib(PayloadIsXz) = 5.2-1

These dependencies are not actually dependencies. They are parsed by RPM (hence the flag name “rpmlib”) and trigger certain behavior.

Let me quickly recap how we got here: We are inside the third section of a file format, in a key-value database describing the package contents, in a very specific and completely unrelated part of the database, and people suddenly start embedding structure information about the package itself in it.

I literally can’t even.

(The only way I can explain this is that the dependency field is something that’s readily accessible to packagers and flexible enough to store build options without breaking older RPM implementations that try to read the spec.)

What are these version strings about, you say? Nothing. Just arbitrary, hard-coded values that must be replicated bit by bit if you want to generate a valid RPM. Please take a moment to let this sink in: The version field is filled with arbitrary magic numbers where it is irrelevant, but empty for the glibc dependencies where it would actually make sense.

And of course, the PayloadIsXz dependency is a duplicate of RPMTAG_PAYLOADCOMPRESSOR that we saw earlier. The word you’re looking for right now is “fractal clusterfuck”.

But wait, there’s more!

We haven’t covered the largest group of tags in this header section. I’ll copy-paste it in its entirety for you to appreciate in its grace and beauty.

tag 1028 (FILESIZES): length 5
    int32: 71296
    int32: 4096
    int32: 18009
    int32: 5620
    int32: 4430
tag 1030 (FILEMODES): length 5
    int16: 0o100755
    int16: 0o40755
    int16: 0o100644
    int16: 0o100644
    int16: 0o100644
tag 1033 (FILERDEVS): length 5
    int16: 0
    int16: 0
    int16: 0
    int16: 0
    int16: 0
tag 1034 (FILEMTIMES): length 5
    int32: 1472740798
    int32: 1472740798
    int32: 1092343095
    int32: 1398112287
    int32: 1398281952
tag 1035 (FILEMD5S): length 5
    string: 14c9b283109f49724b561a422ef6db0e7f6092572789b594c4c758b4af1cb19a
    string: 
    string: 204d8eff92f95aac4df6c8122bc1505f468f3a901e5a4cc08940e0ede1938994
    string: 5e703bb68f8a0bc6eb22433a825e36609562668f311605c46af31a25f7a95c08
    string: bbcd538f8da16fb0a6847a71b06272f155dd5becff44bc2cf8eaf72b3c3a76c4
tag 1036 (FILELINKTOS): length 5
    string: 
    string: 
    string: 
    string: 
    string: 
tag 1037 (FILEFLAGS): length 5
    int32: 0
    int32: 0
    int32: 2
    int32: 2
    int32: 2
tag 1039 (FILEUSERNAME): length 5
    string: root
    string: root
    string: root
    string: root
    string: root
tag 1040 (FILEGROUPNAME): length 5
    string: root
    string: root
    string: root
    string: root
    string: root
tag 1045 (FILEVERIFYFLAGS): length 5
    int32: -1
    int32: -1
    int32: -1
    int32: -1
    int32: -1
tag 1095 (FILEDEVICES): length 5
    int32: 1
    int32: 1
    int32: 1
    int32: 1
    int32: 1
tag 1096 (FILEINODES): length 5
    int32: 1
    int32: 2
    int32: 3
    int32: 4
    int32: 5
tag 1097 (FILELANGS): length 5
    string: 
    string: 
    string: 
    string: 
    string: 
tag 1140 (FILECOLORS): length 5
    int32: 2
    int32: 0
    int32: 0
    int32: 0
    int32: 0
tag 1141 (FILECLASS): length 5
    int32: 0
    int32: 1
    int32: 2
    int32: 2
    int32: 3
tag 1143 (FILEDEPENDSX): length 5
    int32: 0
    int32: 0
    int32: 0
    int32: 0
    int32: 0
tag 1144 (FILEDEPENDSN): length 5
    int32: 7
    int32: 0
    int32: 0
    int32: 0
    int32: 0
tag 5011 (FILEDIGESTALGO): length 1
    int32: 80

This would be even longer if I hadn’t chosen a small package with only four files and one directory in it. The RPM header section includes the entire filesystem metadata of every single file or directory in its payload, down to device numbers for device files. You know, because there is no way at all to store this information in a tarball…

Granted, though, there are some useful things in here. For example, RPMTAG_FILEMD5S corresponds to the md5sums file in a Debian package’s control.tar.gz.

You might be wondering why this list up there contains all conceivable sorts of filesystem metadata, but not the file names themselves. That’s because I saved the best for last. Remember the rpmlib(CompressedFileNames) dependency from earlier? Here’s what it means:

tag 1117 (BASENAMES): length 5
    string: tree
    string: tree
    string: LICENSE
    string: README
    string: tree.1.gz
tag 1116 (DIRINDEXES): length 5
    int32: 0 = 0x0 = 0o0
    int32: 1 = 0x1 = 0o1
    int32: 2 = 0x2 = 0o2
    int32: 2 = 0x2 = 0o2
    int32: 3 = 0x3 = 0o3
tag 1118 (DIRNAMES): length 4
    string: /usr/bin/
    string: /usr/share/doc/
    string: /usr/share/doc/tree/
    string: /usr/share/man/man1/

Someone must have thought really hard about how to shave some bytes off your regular RPM header, and after failing to come up with one of the few dozen obvious options, decided to deduplicate the directory names in the file name list. Here’s how to read it: Every file (or directory) has its basename in the first list, and a directory index in the second list. The directory index goes into the third list and yields the dirname for the basename. Without “compressed file names”, we would instead see:

tag 1027 (OLDFILENAMES): length 5
    string: /usr/bin/tree
    string: /usr/share/doc/tree
    string: /usr/share/doc/tree/LICENSE
    string: /usr/share/doc/tree/README
    string: /usr/share/man/man1/tree.1.gz

I don’t say that this compressed encoding will not save some bytes for large packages that have a lot of files in a few directories, but it’s a weird place to start optimizing when there’s so much low-hanging fruit. For one, everything that we saw so far is stored in the RPM file entirely uncompressed. Only the payload (i.e. the actual files and directories) has xz or LZMA compression.

I already said that I saved the best for last before I showed you the compressed file names, but it’s just so hard to choose the best part of this mess. So here’s my other candidate:

tag 63 (HEADERIMMUTABLE): length 16
    00000000  00 00 00 3f 00 00 00 07  ff ff fc 60 00 00 00 10  |...?.......`....|

Until now, all the fields were pretty obvious, and their meaning could be inferred more or less directly from my samples (except for the dependency flags, but the relevant ones are actually defined in the spec). But the RPMTAG_HEADERIMMUTABLE field had me stymied for quite some time. The spec has this to say about it:

This tag contains an index record which specifies the portion of the Header Record which was used for the calculation of a signature. This data shall be preserved or any header-only signature will be invalidated.

Because I could not establish a relationship between these words up there and the 16 bytes of data in the tag, I looked through the source code of rpm-org (thus marking my final descent into madness), and found this concept under the name “region”. I’ll just copy-paste a comment from my implementation because it captures my original bewilderment perfectly:

I don’t fully grasp the meaning, but it appears that a region tag marks a set of header tags and data that are to be considered immutable, i.e. they may be used for validation purposes, such as calculating hash digests and signatures. I hope that I’m wrong, because this would imply that RPM has a concept of “metadata that’s okay to manipulate even if the package is GPG-signed”, which is insane even for RPM’s standards. However, from what I’ve seen in the implementation, their regions always seem to span the whole header structure, therefore marking everything as immutable.

We do the same thing. The index record for the region tag is at the start of the index record array, and its data is located at the end of the data area. The data is another index record that (using a negative offset into the data area) points back at the original index record. (I’m tempted to use the word “insane”, but it feels like I use that word so often when talking about RPM that it has lost all meaning.)

The signature section

Another thing that sets RPM apart from other package formats is that it includes signatures right in the file, rather than e.g. a detached GPG signature in a separate file.

Some of the signatures go over the header section, so they cannot go into there. So there is a signature section before the header section. And again, it’s a key-value database. Let’s see what’s in it.

tag 62 (HEADERSIGNATURES): length 16
    00000000  00 00 00 3e 00 00 00 07  ff ff ff 80 00 00 00 10  |...>............|

Another region tag that works the same way as above.

tag 269 (SHA1): length 1
    string: 268371792c4ffd4e716111a182176c6922ae55ce
tag 1004 (MD5): length 16
    00000000  27 54 c3 9e 94 8e 68 d5  1e aa a9 82 8f 5c df 4c  |'T....h......\.L|

The SHA-1 checksum goes over the header section. The MD5 checksum goes over the header section plus the payload. Also, the first one is a hex-encoded string, the second one is binary data.

tag 1000 (SIZE): length 1
    int32: 52270
tag 1007 (PAYLOADSIZE): length 1
    int32: 100168

These two are even weirder: RPMSIGTAG_SIZE is the byte count for the header section plus the compressed payload. RPMSIGTAG_PAYLOADSIZE is the byte count for the uncompressed payload.

tag 268 (RSA): length 536
    00000000  89 02 15 03 05 00 57 da  83 60 81 2a 6b 4b 64 da  |......W..`.*kKd.|
    00000010  b8 5d 01 08 dc 84 0f ff  47 5b c6 39 93 c2 9d 75  |.]......G[.9...u|
    00000020  90 cf b5 66 8e df fe 7c  62 37 a0 7f 3c 5d f2 80  |...f...|b7..<]..|
    [...]
tag 1002 (PGP): length 536
    00000000  89 02 15 03 05 00 57 da  83 60 81 2a 6b 4b 64 da  |......W..`.*kKd.|
    00000010  b8 5d 01 08 81 bb 10 00  80 f4 79 e8 cc 6a f9 3b  |.]........y..j.;|
    00000020  be 2c 52 cb a6 2b 0a 7d  43 9b 4c 6f 41 94 e6 24  |.,R..+.}C.LoA..$|
    [...]

Don’t let the names fool you. These are both RSA signatures, formatted as a Version 3 Signature Packet as specified in RFC 2440: OpenPGP Message Format. RPMSIGTAG_RSA goes over the header section, RPMSIGTAG_PGP goes over the header section plus payload.

And if doubly sure is not enough for you, you can be quadruply sure and add RPMSIGTAG_DSA and RPMSIGTAG_GPG, which are the same deal, but with DSA instead of RSA signatures. Nobody uses them anymore, though.

But as always, I’ve saved the best for last. There’s one last tag in this signature section that I want you to stare at and admire like a piece of art:

tag 1008 (RESERVEDSPACE): length 4096
    00000000  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000030  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000040  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000050  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000060  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000070  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000080  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000090  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    000000a0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    000000b0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    000000c0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    000000d0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    000000e0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    000000f0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000100  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000110  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000120  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000130  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000140  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000150  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000160  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000170  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000180  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000190  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    000001a0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    000001b0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    000001c0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    000001d0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    000001e0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    000001f0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000200  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000210  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000220  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000230  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000240  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000250  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000260  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000270  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000280  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000290  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    000002a0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    000002b0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    000002c0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    000002d0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    000002e0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    000002f0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000300  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000310  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000320  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000330  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000340  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000350  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000360  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000370  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000380  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000390  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    000003a0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    000003b0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    000003c0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    000003d0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    000003e0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    000003f0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000400  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000410  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000420  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000430  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000440  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000450  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000460  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000470  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000480  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000490  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    000004a0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    000004b0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    000004c0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    000004d0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    000004e0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    000004f0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000500  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000510  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000520  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000530  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000540  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000550  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000560  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000570  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000580  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000590  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    000005a0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    000005b0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    000005c0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    000005d0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    000005e0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    000005f0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000600  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000610  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000620  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000630  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000640  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000650  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000660  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000670  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000680  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000690  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    000006a0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    000006b0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    000006c0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    000006d0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    000006e0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    000006f0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000700  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000710  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000720  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000730  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000740  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000750  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000760  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000770  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000780  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000790  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    000007a0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    000007b0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    000007c0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    000007d0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    000007e0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    000007f0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000800  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000810  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000820  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000830  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000840  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000850  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000860  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000870  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000880  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000890  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    000008a0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    000008b0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    000008c0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    000008d0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    000008e0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    000008f0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000900  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000910  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000920  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000930  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000940  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000950  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000960  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000970  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000980  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000990  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    000009a0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    000009b0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    000009c0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    000009d0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    000009e0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    000009f0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000a00  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000a10  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000a20  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000a30  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000a40  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000a50  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000a60  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000a70  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000a80  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000a90  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000aa0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000ab0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000ac0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000ad0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000ae0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000af0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000b00  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000b10  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000b20  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000b30  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000b40  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000b50  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000b60  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000b70  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000b80  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000b90  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000ba0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000bb0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000bc0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000bd0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000be0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000bf0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000c00  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000c10  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000c20  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000c30  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000c40  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000c50  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000c60  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000c70  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000c80  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000c90  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000ca0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000cb0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000cc0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000cd0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000ce0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000cf0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000d00  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000d10  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000d20  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000d30  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000d40  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000d50  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000d60  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000d70  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000d80  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000d90  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000da0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000db0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000dc0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000dd0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000de0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000df0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000e00  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000e10  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000e20  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000e30  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000e40  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000e50  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000e60  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000e70  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000e80  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000e90  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000ea0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000eb0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000ec0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000ed0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000ee0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000ef0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000f00  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000f10  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000f20  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000f30  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000f40  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000f50  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000f60  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000f70  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000f80  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000f90  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000fa0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000fb0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000fc0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000fd0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000fe0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000ff0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|

While you stare and admire, recall that all of this is stored completely uncompressed.

Since I don’t know what to say anymore, I’ll leave you with this little gem that I found in the C headers of RPM. Because if you’re anything like me, you hate when your files have the wrong color.

/**
 * File States (when installed).
 */
typedef enum rpmfileState_e {
    RPMFILE_STATE_MISSING	= -1,	/* used for unavailable data */
    RPMFILE_STATE_NORMAL 	= 0,
    RPMFILE_STATE_REPLACED 	= 1,
    RPMFILE_STATE_NOTINSTALLED	= 2,
    RPMFILE_STATE_NETSHARED	= 3,
    RPMFILE_STATE_WRONGCOLOR	= 4
} rpmfileState;