这是在www.rpm.org上看到的一篇文章。
原文参见:http://www.rpm.org/max-rpm/s1-rpm-file-format-rpm-file-format.html
RPM文件格式
以下描述的关于RPM文件的格式细节只在当时写此文的时候是准确的,以下三点请一定要注意:
1.RPM文件格式随着时间的推移会逐步改进的。
2.如果想要对一个RPM文件进行操作,强烈建议你使用rpmlib提供的库函数来操作,为什么?看第一条!
3.本附录描述的是最近的RPM文件格式的细节,当时叫做version 3。你可以使用shell命令:file,来查看一个RPM文件的文件格式的版本。
一个RPM文件的组成
RPM :: [lead 96|signature xx|header xx|archive xx]
每一个RPM文件包在逻辑上可以被分成以下独立的四个分区。他们分别是:
1.lead
2.signature
3.header
4.archive
RPM文件是使用网络字节序来存储在磁盘上的。如果需要,RPM会自动把它们转成主机字节序,当读取RPM文件包的时候。让我们来看一看每一个分区的组成,先从lead分区开始。
Lead分区
RPM包文件的第一分区就是lead,所谓的第一,可以认为是文件的最靠前的一分区文件内容。这分区以前是被用来存储一些信息,这分区信息被RPM内部使用。但是到了现在,lead分区的唯一目的是使得RPM文件更容易被外界识别。举例来说的话,file(1)命令就是读取了lead分区,从而识别一个文件是不是rpm文件。Lead分区所包含的所有信息,全部被header分区重复或者取代了。
RPM定义了一个C结构体来描述lead分区:
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];
};
让我们来看看一个真是的RPM包文件,检查lead分区的所有数据的组成。在接下来的展示中,最左边的列表示从文件开头的字节偏移数,十六进制表示的。八个由四个字符组成的组代表十六进制的值——每个组分别代表两个字节。最右边的列表示这些值对应的ASCII字符。但是当一个值对应的ASCII字符是不可打印的,我们用“.”来代替。下面就是rpm-2.2.1-1.i386.rpm这个文件的前32个字节的详细内容:
00000000: edab eedb 0300 0000 0001 7270 6d2d 322e ..........rpm-2.
00000010: 322e 312d 3100 0000 0000 0000 0000 0000 2.1-1...........
前4个字节(edab eedb)就是magic值(插一句,magic值一般翻译成魔数,是一个固定的数值,用来标识一种特定的东西)。在RPM中,这个魔数就是用来鉴别一个文件是不是RPM文件。file(1)命令和RPM都是用这魔数来判断一个RPM文件是否合法有效。(也就是说,看一个RPM文件是否有效,就读取前四个字节,看看是不是edab eedb。推而广之,那么其他类型的文件格式,有可能就用另外的一个魔数来标识自己,例如FLV文件,前3个字节就是FLV的ASCII表里面对应的数值)
接下来的2个字节(0300)代表RPM文件格式的版本。在本例中,文件的主版本号是3,次版本号是0,所以这个RPM文件的版本号就是3.0。
再往下2个字节决定了RPM文件的哪种类型的。目前为止,有两种类型:
1.二进制文件(type = 0000)
2.源代码文件(type = 0001)
二进制文件就是说,RPM是由已经编译好的二进制文件打包而来的。跟目标OS的架构有关系,后面2个字节会指示这个目标OS的架构究竟是什么。
源文件就是说,RPM是由未编译的源代码文件打包而来的,这样就跟OS的架构无关了,在任意架构的OS上都可以用,一般来说。
在本例中,这个文件是二进制打包的RPM文件。
然后继续往下2个字节(0001),用来存储这个RPM包对应的目标架构的代号。在本例中是1,代表i386架构。当然,如果RPM是由源代码文件打包而来的,那么这2个字节就可以忽略了,你也知道,源代码对于平台是无关紧要的,一般在任意平台都可以进行编译,然后生成可执行的二进制文件或者库。
接下来的66个字节(本例中从7270 6d2d开始)包含的是这个包的名字(不是文件名,切记)。名字必须以'\0'来结尾,意味着真正的名字长度其实是65字节。名字的模版如下:name-version-release-sytle。在本例中,我们可以从右边那几列中读取到这个包的名字:rpm-2.2.1-1。
由于此包的名字rpm-2.2.1-1的长度比65字节小,那接下里就填充'\0'就可以,满足66个字节长。
让我们跳过这些填充的'\0'字节,直接看到偏移00000040这一行,会看到2个字节(0001):
00000040: 0000 0000 0000 0000 0000 0000 0001 0005 ................
00000050: 0400 0000 24e1 ffbf 6bb3 0008 00e6 ffbf ....$...k.......
这2个字节代表构建该RPM包的操作系统的代号。在本例中是1,代表Linux。正如前面说的“架构-代号”的转换,这里的“操作系统-代号”的对应关系可以在/usr/lib/rpmrc中找到。
接下来的2个字节(0005)指代这个RPM文件中signature分区的类型。在本例中,类型值是5,这也是RPM version 3中新加入的一种类型。好了,0005之后的内容就是属于signature分区的了,所以lead一共是96个字节长。但是在讲解signature分区之前,我们需要先讨论几个附加的细节。的了,所以lead分区一共是96个字节长。但是在讲解signature分区之前,我们需要先讨论几个附加的细节。
渴求:一种新的RPM数据结构
通过回顾之前上面提到的那个lead分区定义的C语言结构体,然后再对照着真实RPM文件的每个字节来看,想要理解出lead分区存储的数据显得太繁琐了。但是从编程的角度来看,这又显得更容易来操作lead分区中的数据——只需要简单的使用该结构体中的成员即可。但是终究这是一个困恼的问题,所以因为这个原因,lead分区现在已经不被RPM内部所使用了。定义的C语言结构体,然后再对照着真实RPM文件的每个字节来看,想要理解出lead分区存储的数据显得太繁琐了。但是从编程的角度来看,这又显得更容易来操作lead分区中的数据——只需要简单的使用该结构体中的成员即可。但是终究这是一个困恼的问题,所以因为这个原因,lead分区现在已经不被RPM内部所使用了。
Lead分区,一个被抛弃的数据结构
究竟问题在哪,为何lead分区不再被RPM文件内部所使用呢?答案很简单:可扩展性差。在文件中定义一个C语言结构体这种技术不是很具有扩展性,让我们举个例子说明一下!
请翻到之前介绍lead那个章节,回顾一下那个C语言的结构体定义。打个比方说,有个名字很长很长的软件诞生了,名字超长,至少超过66个字节了。意味着结构体里面的66个字节的成员name已经不足以hold住这个软件的名字了。
这时我们该怎么办?我们当然可以修改之前定义的结构体定义,然后把name成员的长度加到100字节。感觉好像你解决了这个问题,但与此同时你也创造了一个新版本的RPM文件,此时我们又面临两个新的问题:
1.任何新版本RPM程序创建的RPM包文件不能被老版本的RPM程序来解析安装了;
2.任何老版本的RPM程序不能解析安装新版本RPM程序创建的RPM包文件;
情况不妙啊!!理论上来说,无论如何,我们不应该来使用这种“写死”的方式来设计文件格式。好的文件格式应该在不损失兼容性的前提下,还允许我们完成以下三件事:
1.在文件格式中新增额外的数据分区;
2.改变已存在的数据分区的大小;
3.给数据分区重新排序;
听上去感觉很难的样子,但是已经有一种解决方案了。
真的有解决方案?
这个方案就是把从文件中检索信息的方式给标准化、规范化。通过创造设计一种优秀的文件数据结构,包含易于检索的有关信息,并且在物理上在文件数据结构中隔离那些信息。
当想要查找某个数据分区时,通过那些易于检索的信息(这些信息有点儿类似于一本书的目录),从而得知数据分区的位置。那么这样做带来的好处就是每个数据分区可以随意的放置于RPM文件中的任意位置,并且某个数据分区自己的格式是可以改变的。
解决方案:头结构(Header Structure)
头结构就是RPM程序中关于之前那个标准化、规范化从文件中简便检索信息的解决方案。头结构的唯一目标就是包含零或者数据。一个RPM文件可以包含不止一个这种结构。事实上,现在的RPM文件中包含两个这种结构,signature分区和header分区。(请不要混淆头结构是头分区,可以认为头结构是类,而签名分区和头部分区是这个类对应的两个实例对象)
每一个头结构由三个section组成。第一个section叫做头结构的头部(header structure header,不得不说,这样起名字真心让人晕),头部是用来确定这个头结构的起始位置、大小、包含的数据项的数量。
第二个叫做index(索引部分)。索引部分包含一个或者多个入口(entry),每一个索引部分的入口包含了相关的信息,一个具体的数据项,以及一个指向数据项的指针。
最后一个叫做store(存储部分)。真正的数据项就是存储在存储部分的。存储部分的数据被尽可能紧凑的打包在一起。数据存储的顺序是无关紧要的——这种格式和lead分区中用到的C语言结构体大相径庭吧!
深入了解头结构
Header Structure :: [header 16|index 16|store N] = 32 + N Bytes
让我们深入的来看一下头结构的真实格式吧,从头结构的头部分开始:
1.头结构的头部,header
header :: [8e ad e8|version 1|reserved 4|number 4|size 4] = 16 Bytes
头结构的头部总是以一个3字节长的魔数开始的——8e ad e8。紧接着是一个1字节的版本号。再接着4个字节是保留字节,预留给未来扩展使用的。在保留字节之后有一个4字节的数字来指示这个头结构中一共有多少个索引入口存在,然后又有一个4字节的数字来指示这个头结构一共有多大。
2.索引入口,index entry
index :: [tag 4|type 4|offset 4|count 4] = 16 Bytes
头结构的索引部分是由0个或者多个索引入口组成,每一个入口都是16字节长。入口的前4个字节包含一个tag(标签)——一个数值,用来明确该入口指向的数据的类型是哪种。标签的值依赖于头结构在RPM包文件中的位置。文章的后面会有一张表,说明了标签的每个取值代表什么意思。
标签后面跟着一个4字节的type(类型),也是一个数值,用来描述指向的数据的格式。type不同于tag,type的值在不同的头结构中都是一样的意义,下面是一张表,描述了type的值和对应的数据的格式:
NULL = 0
CHAR = 1
INT8 = 2
INT16 = 3
INT32 = 4
INT64 = 5
STRING = 6
BIN = 7
STRING_ARRAY = 8
上表中有些类型需要解释说明一下。STRING类型就是一种普通的以null结尾的字符串,而STRING_ARRAY是一组字符串。最后一点,BIN类型的数据是一些二进制数据。BIN通常用来识别比INT长,但又是不可打印的数据的。
然后接下来又是一个4字节的offset(偏移),它存储了数据相对于store部分的开始的位置的偏移。我们稍后会讨论store部分的。
最后还有一个4字节的count(计数),它存储了这个入口所指向的数据项的个数,对于不同的类型的数据项,count的意义会有一些不同。STRING类型,count总是1;对于STRING_ARRAY来说,count的值等于其中包含的字符串的个数。
3.存储部分,store
store部分就是头结构中存储数据的地方。根据index.type中的值的不同,下面有三点细节我们应该记住:
1.STRING类型的数据,每个字符串是以null结尾。
2.INT类型的数据,每个INT*的数据都是按照它的类型的自然边界存储的。例如,一个INT64是存储在一个8字节的范围之内,INT16存储在2个字节的范围之内。
3.所有的数据都是用网络字节序存储的。(切记)
Signature分区
RPM :: [lead 96|
signature xx|header xx|archive xx]
Header Structure :: [
header 16|index 16|store N] = 32 + N Bytes
header :: [8e ad e8|version 1|reserved 4|number 4|size 4] = 16 Bytes
Signature分d区在RPM文件中紧接着lead分区。它包含了一些信息,这些信息可以被用来验证该文件的完整性,另外(选择性的啦)还可以用来验证绝大对数RPM包文件的真实性、可靠性。Signature分区是用头结构来实现的。
你可能注意到了我上面用了“绝大多数”这个词。其实signature分区的内容完全就是依赖于header分区和archive分区的。当创建签名相关的信息时并不包括lead分区和signature分区,并且后续也不会基于签名信息对这两个分区进行任何检查。
似乎看起来这是一个RPM程序的疏忽?不!对于lead分区来说,由于这个分区是用来给别人方便的识别RPM文件的,所以任何对此分区的改变,如果说最坏的情况,会使得这个文件成为不被RPM程序识别的文件(假设你把lead分区的前4个魔数字节改掉,可想而知)。同样的,对于signature分区来说,任何对其的改变将会使得RPM程序认为此文件的不完整、不可靠,因为签名信息被改了,与其原来的值不同了。
分析signature分区
用我们刚学到的关于头结构的知识,我们深入的看一下rpm-2.2.1-1.i386.rpm这个文件:
00000060: 8ead e801 0000 0000 0000 0003 0000 00ac ................
我们切换到了00000060偏移位置,因为lead分区是96字节(定长),那么00000060就是第97字节了。
前3个字节一看便知,头结构的魔数(8e ad e8),味着头结构从这开始。然后接下来的1个字节是头结构的版本号(01)。正如我之前讨论过的,接下来的4个是保留字节,那么(0000 0000)也没什么可解释的了。接下来的4个字节是一个数值(网络字节序),(0000 0003)就表示接下来的index这个section中包含有3个index entry入口。再下来的4个字节就告诉我们这个头结构(在这里是signature头结构)一共有(0000 00ac)个字节,也就是十六进制的ac,就是十进制的172字节。
接下来的16个字节就是signature头结构中的index section了。
你还记得每个entry是16字节吧?分别由:
index entry :: [tag 4|type 4|offset 4|count 4] = 16 Bytes
如上所述,index entry section中有3个entry,我们一个一个来看,先看第一个:
00000070: 0000 03e8 0000 0004 0000 0000 0000 0001 ................
从上面可以看出,tag的只是十六进制的(0000 03e8),也就是十进制的1000,不妨看一下rpm的源文件中的“lib/signature.h”(我在rpm-4.11的源代码中找到下列信息是在“lib/rpmtag.h”中),我们会发现类似于下面这些东西:
#define SIGTAG_SIZE 1000
#define SIGTAG_MD5 1001
#define SIGTAG_PGP 1002
所以从这个映射就可以看出,我们正在研究的index entry指向的数据是SIZE类型的。(SIZE具体是什么意思,我也不清楚,接着往下看)
接下来的4个字节就是type,(0000 0004),意味着这个index entry指向的store区域存储的是INT32类型的数据。先暂时跳过接下来的4个字节offset(之后有详细说明的),直接看到最后4个字节(0000 0001),就是说这个index entry对应的store存储了“1”个type类型(INT32)的数据。
现在让我们回到之前跳过的那4个字节,offset(0000 0000)。偏移是0,这个好理解,但是从哪数起呢?答案就是从本头结构的store的开始数起!本头结构,那就是signature分区啦,很简单。那本头结构的store section在哪开始呢?想一下,在header section中提到,本头结构一共有3个index entry,然后每一个index entry的长度是固定的16字节,那header section也是16字节固定的。那好,从本头结构的开始,也就是那个魔数“8e ad e8”的偏移(00000060)开始,跳跃(16 + 16 * 3)个字节,那么我们就到了000000a0这个位置了:
000000a0: 0004 4c4f b025 b097 1597 0132 df35 d169 ..LO.%.....2.5.i
如果我们没算错,从000000a0起就是store section了,那么前4个字节(0004 4c4f)就是第一个index entry所指指向的一个INT32的数据了。回顾一下,第一个index entry的tag值告诉我们,它所指向的数值的意义是SIGTAG_SIZE,从字面上看起来是大小的意思,那究竟指的是哪个大小呢?是指的该RPM文件的大小吗?先转换成十进制,得到281,679。然后我们看看该RPM文件的大小:
# ls -al rpm-2.2.1-1.i386.rpm
-rw-rw-r-- 1 ed ed 282015 Jul 21 16:05 rpm-2.2.1-1.i386.rpm
#
呃,RPM文件大小是282,015,不对哦。再看看,他们两个数值之间差多少,282015 - 281679 = 336 Bytes,也就是十六进制的150Bytes,差了0x150个字节。那到底这差的0x150字节去哪了?先别急,让我们继续分析剩下的两个index entry(signature的header说了一共有3个index entry)。
下面展示第二个index entry。tag值是十进制的1001,也就是表示指向的数据是一个MD5摘要字符串;type的值是7,也就是BIN类型的;count是0x10,也就是说BIN类型的字符串是16个字节长。
00000080: 0000 03e9 0000 0007 0000 0004 0000 0010 ................
下面给出第二个index entry对应的store section中的数据内容,从(b025)开始,为啥不是从0004开始呢?再看一眼上面的(0000 0004),它是offset,意思就是第二个entry的数据是从store开始,再数4个字节,然后才是对应的数据。这个MD5串到(5357)结束,一共16个字节长,它是使用MD5算法,对于header分区和archive分区产生的验证摘要信息。
000000a0: 0004 4c4f b025 b097 1597 0132 df35 d169 ..LO.%.....2.5.i
000000b0: 329c 5375 8900 9503 0500 31ed 6390 a520 2.Su......1.c..
再看最后一个index entry。
00000090: 0000 03ea 0000 0007 0000 0014 0000 0098 ................
tag值是(03ea),也就是十进制1002,意味着指向的数据是一个PGP签名块(PGP signature block)。type同样是BIN,offset是(0014),count居然是(0098),十进制是152个字节!下面展示一下数据吧,从8900开始:
000000b0: 329c 5375 8900 9503 0500 31ed 6390 a520 2.Su......1.c..
000000c0: e8f1 cba2 9bf9 0101 437b 0400 9c8e 0ad4 ........C{......
000000d0: 3790 364e dfb0 9a8a 22b5 b0b3 dc30 4c6f 7.6N...."....0Lo
000000e0: 91b8 c150 704e 2c64 d88a 8fca 18ab 5b6f ...PpN,d......[o
000000f0: f041 ebc8 d18a 01c9 3601 66f0 9ddd e956 .A......6.f....V
00000100: 3142 61b3 b1da 8494 6bef 9c19 4574 c49f 1Ba.....k...Et..
00000110: ee17 35e1 d105 fb68 0ce6 715a 60f1 c660 ..5....h..qZ`..`
00000120: 279f 0306 28ed 0ba0 0855 9e82 2b1c 2ede '...(....U..+...
00000130: e8e3 5090 6260 0b3c ba04 69a9 2573 1bbb ..P.b`.<..i.%s..
00000140: 5b65 4de1 b1d2 c07f 8afa 4a9b 0000 0000 [eM.......J.....
到(4a9b)截止。这是一个1,216bit长的PGP签名块。同样,(4a9b)也是signature分区的结束位置。你可以看到在(4a9b)的后面跟着4个null byte,有了它们就填充了signature分区,是的signature分区的数据8字节对其。所以下一分区(header分区)就是从00000150开始了。这个150和之前说的150是一个意思嘛?对,就是这样!所以signature分区中的SIGTAG_SIZE的tag就是指header分区加上archive分区的大小了。
Header分区
RPM :: [lead 96|signature xx|header xx|archive xx]
Header Structure :: [header 16|index 16*M|store N] = 32 + N + 16*M Bytes
header :: [8e ad e8|version 1|reserved 4|number 4|size 4] = 16 Bytes
Header分区包含了所有RPM包文件可用的信息。它里面的entry将会包括包文件的名字,版本,文件列表。就想signature分区一样,header分区也是基于头结构实现的。但是又和signature分区有点儿不同,signature分区一般只包含3个意义的tag,例如大小、MD5签名串、PGP签名串,而header分区可能包含60个以上的不意义tag,tag值对应的所有编号的意义将会在Header tag listing里面列出来(下面)。有一点要注意,这个表里面的定义可能会很频繁的变动。
分析header分区
最简单的方式来找寻header分区的开始位置就是检索第二个头结构的魔数(8e ad e8)了。
00000150: 8ead e801 0000 0000 0000 0021 0000 09d3 ...........!....
简单明了,有0x21(33个)个index entry,store长度是09d3,也就是2515字节。
接下来看第一个index entry:
00000160: 0000 03e8 0000 0006 0000 0000 0000 0001 ................
前4个字节是(0000 03e8),这种tag表示package name,也就是文件名。然后type是6,就是STRING。第一个index entry的offset肯定是0啦,不用怀疑。count是1,一个字符长?NO,还记得吧,STRING的count值总是1,表示一个字符串。
下面看看第一个tag指向的STRING数据到底长什么样,我们应该跳到(0x160 + 21行),那就是0x370的偏移位置,请看:
00000370: 7270 6d00 322e 322e 3100 3100 5265 6420 rpm.2.2.1.1.Red
由于我们之前说过STRING类型的字符串是以null结尾的,所以我们应该继续往后看,直到看见00为止。很好,00在本行,(6d00)中的(00)就是null,那么文件名就是“rpm”。
下面我们再看一个稍微复杂点的index entry:
00000250: 0000 0403 0000 0008 0000 0199 0000 0018 ................
tag值0x403表示这个entry是一个文件列表(一些文件的名字)。你可以先猜猜,type肯定是8,也就是STRING_ARRAY,不然怎么去表示啊,对吧!偏移从0x199开始。count值是0x18,意思就是STRING_ARRAY中含有0x18个STRING,每个STRING是以null结束的。不妨验证一样,让我们跳到(0x370 + 0x199的位置,0x509):
00000500: 696e 6974 6462 0a0a 002f 6269 6e2f 7270 initdb.../bin/rp
00000510: 6d00 2f65 7463 2f72 706d 7263 002f 7573 m./etc/rpmrc./us
0x509是从(002f)中的2f开始的,也就是“/”。然后一直读,读到第一个null,就是(6d00)中的00,结果翻译成ASCII码就是“/bin/rpm”,这是第一个文件,接下来还有0x18 - 0x1个。
实际上还有很多tag等着我们来解析,但是就不一一举例了,因为方法很相似。
Header Tag Listing
这就是上面提到过的,tag的值和意义的映射表,在lib/rpmlib.h中(新版本可能变化了)。
#define RPMTAG_NAME 1000
#define RPMTAG_VERSION 1001
#define RPMTAG_RELEASE 1002
#define RPMTAG_SERIAL 1003
#define RPMTAG_SUMMARY 1004
#define RPMTAG_DESCRIPTION 1005
#define RPMTAG_BUILDTIME 1006
#define RPMTAG_BUILDHOST 1007
#define RPMTAG_INSTALLTIME 1008
#define RPMTAG_SIZE 1009
#define RPMTAG_DISTRIBUTION 1010
#define RPMTAG_VENDOR 1011
#define RPMTAG_GIF 1012
#define RPMTAG_XPM 1013
#define RPMTAG_COPYRIGHT 1014
#define RPMTAG_PACKAGER 1015
#define RPMTAG_GROUP 1016
#define RPMTAG_CHANGELOG 1017
#define RPMTAG_SOURCE 1018
#define RPMTAG_PATCH 1019
#define RPMTAG_URL 1020
#define RPMTAG_OS 1021
#define RPMTAG_ARCH 1022
#define RPMTAG_PREIN 1023
#define RPMTAG_POSTIN 1024
#define RPMTAG_PREUN 1025
#define RPMTAG_POSTUN 1026
#define RPMTAG_FILENAMES 1027
#define RPMTAG_FILESIZES 1028
#define RPMTAG_FILESTATES 1029
#define RPMTAG_FILEMODES 1030
#define RPMTAG_FILEUIDS 1031
#define RPMTAG_FILEGIDS 1032
#define RPMTAG_FILERDEVS 1033
#define RPMTAG_FILEMTIMES 1034
#define RPMTAG_FILEMD5S 1035
#define RPMTAG_FILELINKTOS 1036
#define RPMTAG_FILEFLAGS 1037
#define RPMTAG_ROOT 1038
#define RPMTAG_FILEUSERNAME 1039
#define RPMTAG_FILEGROUPNAME 1040
#define RPMTAG_EXCLUDE 1041 /* not used */
#define RPMTAG_EXCLUSIVE 1042 /* not used */
#define RPMTAG_ICON 1043
#define RPMTAG_SOURCERPM 1044
#define RPMTAG_FILEVERIFYFLAGS 1045
#define RPMTAG_ARCHIVESIZE 1046
#define RPMTAG_PROVIDES 1047
#define RPMTAG_REQUIREFLAGS 1048
#define RPMTAG_REQUIRENAME 1049
#define RPMTAG_REQUIREVERSION 1050
#define RPMTAG_NOSOURCE 1051
#define RPMTAG_NOPATCH 1052
#define RPMTAG_CONFLICTFLAGS 1053
#define RPMTAG_CONFLICTNAME 1054
#define RPMTAG_CONFLICTVERSION 1055
#define RPMTAG_DEFAULTPREFIX 1056
#define RPMTAG_BUILDROOT 1057
#define RPMTAG_INSTALLPREFIX 1058
#define RPMTAG_EXCLUDEARCH 1059
#define RPMTAG_EXCLUDEOS 1060
#define RPMTAG_EXCLUSIVEARCH 1061
#define RPMTAG_EXCLUSIVEOS 1062
#define RPMTAG_AUTOREQPROV 1063 /* used internally by build */
#define RPMTAG_RPMVERSION 1064
#define RPMTAG_TRIGGERSCRIPTS 1065
#define RPMTAG_TRIGGERNAME 1066
#define RPMTAG_TRIGGERVERSION 1067
#define RPMTAG_TRIGGERFLAGS 1068
#define RPMTAG_TRIGGERINDEX 1069
#define RPMTAG_VERIFYSCRIPT 1079
Archive分区
Header分区的后面就是archive分区,该分区存储的真正的那个打包文件。Archive分区是用GNU的zip软件压缩而成的,不妨来看一眼,archive分区的真正样子:
00000d40: 0000 00
1f 8b08 0000 0000 0002 03ec fd7b ...............{
00000d50: 7c13 d516 388e 4e92 691b 4a20 010a 1428 |...8.N.i.J ...(
在这个例子中,archive从偏移0xd43开始。不妨对照/usr/lib/magic来看看,gzip生成的压缩文件的头部魔数是(1f8b),和偏移0xd43恰好吻合。接下来的1个字节(08)这个标志位代表gzip压缩的时候使用的“deflation”设定。然后继续往后看,直到archive分区的第8个字节(02),这个表示gz压缩的时候使用了最大压缩比的设定。接下来的1个字节表示使用gzip的操作系统是哪种,(03)表示Unix-like的操作系统。
剩下的RPM文件内容就是被压缩的归档文件了。当archive分区被解压之后,就成了一个常见的cpio格式的归档文件。