Linux GPT分区表16进制实例分析
-----------------------------------------------------------------------------
-----------------------------------------------------------------------------
00000200
0-8字节:GPT Signature,它是一串ASCII码字符串“EFI PART”,把这8个16进制数45 46 49 20 50 41 52 54分别转换成10进制,就是69 70 73 32 80 65 82 84,去查一下ASCII码表,它就是字符串“EFI PART”,注意两个单词中间还有个空格。
9-11字节:GPT修订版本,表示是1.0版,即00 00 01 00。
12-15字节:GPT分区表头大小,一般是92字节,也就是16进制数5c,即5c 00 00 00。
00000210
16-19字节:GPT头部共92字节数据的CRC32校验值,计算该CRC32值时把这4个字节先置为0再计算,计算完成之后,再将计算出来的值填入这4个字节。我们来计算一下。先把hexdump输出结果的92个字节摘出来,再把16-19字节全填成0,就是下面这样:
45 46 49 20 50 41 52 54 00 00 01 00 5c 00 00 00
00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00
ff ff 9f 00 00 00 00 00 22 00 00 00 00 00 00 00
de ff 9f 00 00 00 00 00 dd 25 f8 96 c6 47 56 4f
aa ed c2 48 bd ee 50 7d 02 00 00 00 00 00 00 00
80 00 00 00 80 00 00 00 86 d2 54 ab
然后把这一串16进制数全选复制填入CRC32在线计算网站:https://www.lammertbies.nl/comm/info/crc-calculation.html 注意要在Input type中选Hex,这个网站的输入框它会自动忽略空格和换行符,点击Calculate所得计算结果如下:
可以看到,这个结果跟hexdump显示的16-19字节数据一致,即47 f1 8d 83。
20-23字节:保留位,必须为0。
24-31字节:当前GPT表头所在的LBA,上面说到主GPT表一般是LBA1,所以结果是01,即01 00 00 00 00 00 00 00。
00000220
32-39字节:Alternate GPT表头所在的LBA,上面说到这个一般是最后一个LBA,也就是硬盘的最后一个512字节,这个值是怎么算出来的呢?/dev/sdb容量大小是140000000(Hex) = 5368709120(Dec),除于512字节,5368709120/512 = 10485760(Dec),就是这个硬盘的总LBA数,因为是最后一个LBA所以再减1,即10485760-1 = 10485759(Dec) = 9FFFFF(Hex),
ff ff 9f 00 00 00 00 00
40-47字节:第一个可用的LBA,根据UEFI官方文档规定,LBA大小为512字节时,第一个可用的LBA必须要大于或等于34,即34(Dec) = 22(Hex),即22 00 00 00 00 00 00 00。
00000230
00000240
48-55字节:最后一个可用的LBA,跟上面的第一个可用LBA同样的道理,就是最后一个LBA-34,/dev/sdb容量大小是140000000(Hex) = 5368709120(Dec),5368709120/512 = 10485760(Dec),10485760 - 34 = 10485726(Dec) = 9FFFDE,即de ff 9f 00 00 00 00 00。
56-71字节:硬盘的GUID,这个值是唯一的,不可重复。
72-79字节:定义GPT分区入口的LBA,就是LBA1+1=LBA2,即02 00 00 00 00 00 00 00。
00000250
80-83字节:定义的分区入口的数量,80(Hex) = 128(Dec),128个分区。即80 00 00 00。
84-87字节:每个分区入口的大小,一般是80(Hex) = 128(Dec),即80 00 00 00。
88-92字节:分区入口列表的CRC32校验值。
-----------------------------------------------------------------------------
上面就是主GPT分区表头的16进制逐行分析,下面就是备份分区表头部分了,根据上面的分区,备份的分区表头位置是在硬盘的最后一个LBA,即140000000(Hex) = 5368709120(Dec),5368709120/512 - 1 = 10485759(Dec),10485759 * 512 = 5368708608(Dec) = 13FFFFE00(Hex),即截图中最后一部分的备份GPT分区表头的起码位置。
通过比较主表头和备份表头,我们发现有如下几个地方不一样:
1、16-19字节的GPT头部共92字节数据的CRC32校验值,这个CRC32校验值不一样是由于24-31字节(当前GPT表头所在的LBA)和32-39字节(备份GPT表头所在的LBA)的位置正好是跟主表头位置相反引起的。
2、24-31字节(当前GPT表头所在的LBA)和32-39字节(备份GPT表头所在的LBA),这两个值刚好跟主GPT表头的值是相反的,即互为主备。
3、72-79字节GPT分区入口的LBA,备份表头肯定指向的备份的分区入口列表的LBA。
除了上述3个地方不一样之外,其它地方跟主GPT表头完全一致。
-----------------------------------------------------------------------------
然后我们在/dev/sdb上创建两个分区,再看分区表会有什么样的变化。
[root@RHEL68 ~]# parted /dev/sdb mkpart sdb1 1 100M
[root@RHEL68 ~]# parted /dev/sdb mkpart sdb2 100M 300M
跟未分区时候相比,多了两个分区入口,这些分区入口在官方文档中被称为GPT Partition Entry Array。分区表头部分和未分区相比,有两个地方发生了变化,一个是16-19字节的GPT头部共92字节数据的CRC32校验值,这个值发生了变化是因为88-92字节分区入口列表的CRC32校验这个发生了变化,因为多了两个分区,所以分区入口列表的CRC32校验值肯定会发生变化。
第一个分区的入口起始位置是0x400,第二个分区入口起始位置是0x480,所以每个分区入口描述长度是80(Hex) = 128(Dec) Byte。这个128byte其实已经在表头里面有定义过的,上面有说过,就是表头的84-87字节。
每个分区的入口描述根据官方文档是这样定义的,见下图。
00000400
0-15字节:分区类型GUID。根据上面提到的维基百科Wikipedia的英文版的GUID分区表的页面:https://en.wikipedia.org/wiki/GUID_Partition_Table ,可以查到这个GUID EBD0A0A2-B9E5-4433-87C0-68B6B72699C7 是指基本数据分区。需要说明的是,各种类型的分区的GUID都是已经有明确定义的,可以参考维基百科页面,里面列了很多常见类型的分区GUID。
00000410
16-31字节:分区唯一标识符,跟分区表头定义的硬盘GUID一个道理,也必须是唯一值。
00000420
32-39字节:分区起始LBA,也就是800(Hex) = 2048(Dec),分区真正开始使用是2048*512 = 1048576(Dec) = 100000(Hex)。
40-47字节:分区结束LBA,也就是2F7FF(Hex) = 194559(Dec),分区的最后一个LBA的起始字节数是 194559*512 = 99614208(Dec) = 5EFFE00(Hex),还要加上一个LBA即512字节,即5F00000(Hex),也就是下一个分区的起始字节数。
00000430
00000440
48-55字节:属性标识符,保留的。
56-127字节: 分区名称,共72字节。本例中,为0073(Hex),0064(Hex),0062(Hex),0031(Hex),转换到10进制,就是115(Dec), 100(Dec), 98(Dec), 49(Dec),查询一下ASCII码,即sdb1。