RPM-Red hat Package Manager文件格式解析(翻译)

这是在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格式的归档文件。


转载于:https://my.oschina.net/michaelyuanyuan/blog/134176

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值