在STM32上实现NTFS之5:GPT分区表的C语言实现(2)GPT实现以及统一方式读取磁盘分区...

  上一节实现了主GPT头的信息提取,这一节继续提取整个的GPT数据,并且将GPT分区表和MBR分区表两种格式融合成一个模块,使主调函数(也可以说是使用者)不需要关心磁盘的分区表类型:它太底层了,确实不需要过多的关心。

  继续看上一节的图1,这里就不贴图了,LBA1的主GPT头给出了分区信息的总数,还有每一个分区信息所占用的字节数,分区信息的结构如表1:

表1 分区信息结构(GPT Entry)

字节偏移量

数据长度(字节)

范例数值

数据项说明

0x00

16

28 73 2A C1 1F F8 D2 11 BA 4B 00 A0 C9 3E C9 3B

用GUID表示的分区类型

0x10

16

82 63 7A C8 13 0E F8 46 95 29 E6 31 E9 16 B5 42

用GUID表示的分区唯一标示符

0x20

8

00 08 00 00 00 00 00 00

该分区的起始扇区,用LBA值表示

0x28

8

FF 27 E6 31 E9 16 B5 42

该分区的结束扇区(包含),用LBA值表示,通常是奇数

0x30

8

00 00 00 00 00 00 00 80

该分区的属性标志

0x38

72

 

UTF-16LE编码的人类可读的分区名称,最大32个字符

可以看到,这个结构非常之简洁,只标注了起始扇区,结束扇区,分区类型,GUID,属性,分区名,而不关心磁头、磁道等等信息。

需要特殊说明的是,整个分区信息的结构,也就是从LBA2到LBA34(MS总是给划分128个分区信息),是完全连续的,不依靠任何扇区等信息去定位,也就是说,第n个分区信息的结构起始地址,仅仅根据LBA2的起始地址+分区字节数*n来确定,最常见的是微软定义的128个分区信息,512字节扇区,128字节的分区信息字节数,所以最常见的是每一个扇区4个分区信息,密集的排列32个扇区。然而正确的计数方法是每128个字节一个分区信息,密集排列128个。当扇区不是512字节时(注意这里不要跟簇混淆,目前我还没见过不是512字节扇区的存储器),每个扇区存储的分区信息有可能不是4个,下面的程序规避了这个问题,采用了一个新的磁盘读写函数——ReadDiskData而不是ReadSectorData——来防止读分区信息出现错误。

 1 /*******************************************************************************
 2 
 3 函 数 名:GetVolumeNumberOfGPT
 4 
 5 函数功能:获取一个GPT格式的磁盘中有效分卷数
 6 
 7 输入参数:
 8 
 9           hDisk:磁盘句柄
10 
11           DPT:  磁盘DPT
12 
13 返回参数:int型,返回DPT中包含的有效分区数量(包含未格式化的分卷),如果该DPT不是GPT
14 
15         分区形式,将会返回-1
16 
17 *******************************************************************************/
18 
19 int GetVolumeNumberOfGPT(HANDLE hDisk, DPT_Info* DPT)
20 
21 {
22 
23     if (DPTDetermination(DPT) == DPT_MBR)
24 
25         return -1;
26 
27     GPTEntry_Byte GPTEbuffer;
28 
29     GPTEntry_Info GPTEinfo;
30 
31     PGPTH_Info PGPTH;
32 
33     GetPGPTH(hDisk, &PGPTH);
34 
35     int ValidPartitions = 0;
36 
37     for (int i = 0; i < PGPTH.PartitionTables; i++)
38 
39     {
40 
41         ReadDiskData(
42 
43             hDisk, //读取磁盘句柄
44 
45             SECTOR_SIZE * PGPTH.PartitionStart + i * PGPTH.BytesPerPartitionTable, //计算读取的GPTE位置
46 
47             (uint8_t*)(void*)&GPTEbuffer, //缓冲区地址
48 
49             sizeof(GPTEntry_Byte)); //字节数
50 
51         GetGPTEInfo(&GPTEbuffer, &GPTEinfo);
52 
53         if (GUIDcmp(&(GPTEinfo.TypeGUID), (GUID_Info*)&GUID_ptUnuse))
54 
55             break;
56 
57         else
58 
59             ValidPartitions++;
60 
61     }
62 
63     return ValidPartitions;
64 
65 }

分区类型是有规定的,一般有表2的几种GUID;属性标志也是有规定的,见表3。

 

表2:分区类型的16Byte GUID

数值

类型说明

00000000-0000-0000-0000-000000000000

未使用

024DEE41-33E7-11D3-9D69-0008C781F39F

MBR分区表

C12A7328-F81F-11D2-BA4B-00A0C93EC93B

EFI系统分区[EFI System partition (ESP)],必须是VFAT格式

BC13C2FF-59E6-4262-A352-B275FD6F7172

扩展boot分区,必须是VFAT格式

21686148-6449-6E6F-744E-656564454649

BIOS引导分区,其对应的ASCII字符串是"Hah!IdontNeedEFI"。

D3BFE2DE-3DAF-11DF-BA40-E3A556D89593

Intel Fast Flash (iFFS) partition (for Intel Rapid Start technology)

E3C9E316-0B5C-4DB8-817D-F92DF00215AE

微软保留分区

EBD0A0A2-B9E5-4433-87C0-68B6B72699C7

基本数据分区

DE94BBA4-06D1-4D40-A16A-BFD50179D6AC

Windows恢复环境

0FC63DAF-8483-4772-8E79-3D69D8477DE4

数据分区。Linux曾经使用和Windows基本数据分区相同的GUID。
这个新的GUID是由 GPT fdisk 和 GNU Parted 开发者根据Linux传统的"8300"分区代码发明的。

44479540-F297-41B2-9AF7-D131D5F0458A

x86根分区 (/) 这是systemd的发明,可用于无fstab时的自动挂载

4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709

x86-64根分区 (/) 这是systemd的发明,可用于无fstab时的自动挂载

69DAD710-2CE4-4E3C-B16C-21A1D49ABED3

ARM32根分区 (/) 这是systemd的发明,可用于无fstab时的自动挂载

B921B045-1DF0-41C3-AF44-4C6F280D3FAE

AArch64根分区 (/) 这是systemd的发明,可用于无fstab时的自动挂载

3B8F8425-20E0-4F3B-907F-1A25A76F98E8

服务器数据分区(/srv) 这是systemd的发明,可用于无fstab时的自动挂载

933AC7E1-2EB4-4F13-B844-0E14E2AEF915

HOME分区 (/home) 这是systemd的发明,可用于无fstab时的自动挂载

0657FD6D-A4AB-43C4-84E5-0933C84B4F4F

交换分区(swap) 不是systemd的发明,但同样可用于无fstab时的自动挂载

A19D880F-05FC-4D3B-A006-743F0F84911E

RAID分区

E6D6D379-F507-44C2-A23C-238F2A3DF928

逻辑卷管理器(LVM)分区

8DA63339-0007-60C0-C436-083AC8230908

保留

    上面的GUID数值比较有意思,根据winHex得到的扇区数据,它将16字节的GUID分成了5部分,在很多编译器中已经有GUID结构体的定义不过是4部分的,在这里无法使用,所以我们另外定义一个结构

 1 typedef struct
 2 
 3 {
 4 
 5     uint32_t Part1;                 //GUID第1部分
 6 
 7     uint16_t Part2;                 //GUID第2部分
 8 
 9     uint16_t Part3;                 //GUID第3部分
10 
11     uint16_t Part4;                 //GUID第4部分
12 
13     uint48_t Part5;                 //GUID第5部分
14 
15 }GUID_Info;

 

来表示GUID。而有意思的地方在于,这5部分并不是单纯的完全大端或者小端模式,它是混合着来的,前三个部分是小端,后两个部分是大端,我们在读取结构的时候一定要注意这点。

    表3:分区属性

数值

类型说明

0

系统分区

1

EFI隐藏分区(EFI不可见分区)

2

传统的BIOS的可引导分区标志

60

只读

62

隐藏

63

不自动挂载,也就是不自动分配盘符

    以上的属性列表并不完全,至少我的硬盘分区之后,类型就是0x80。因为只有一个字节有用的信息,一个枚举类型就足以解决问题。

    现在我们实际看一个拥有4个分区信息的LBA,如图1:

 

图1 LBA分区

    如图所示,红框框起来的部分就是一个分区信息结构,比较精髓的部分是粉色框里的72字节,它是以UTF16的小端模式编码的,因为涉及到了中文问题,UTF16LE解码比较复杂,在这里我们直接用wchar_t来获取这个结构:

 1 typedef struct
 2 
 3 {
 4 
 5     uint8_t TypeGUID[16];           //用GUID表示的分区类型
 6 
 7     uint8_t UniqueGUID[16];         //用GUID表示的分区唯一标示符
 8 
 9     uint8_t SectorStart[8];         //该分区的起始扇区,用LBA值表示
10 
11     uint8_t SectorEnd[8];           //该分区的结束扇区(包含),用LBA值表示,通常是奇数
12 
13     uint8_t PartitionAttrib[8];     //该分区的属性标志
14 
15     WCHAR   PartitionName[36];      //UTF-16LE编码的人类可读的分区名称,最大32个字符。
16 
17 }GPTEntry_Byte;

 

 

    现在GPT结构已经可以完整的读取,由于博文实在实验阶段性完成后做的,所以没有截图(=_=尴尬)。

接下来的步骤是整合GPT和MBR。由于DPT引导到GPT和MBR的部分可以直接整合,所以原DPT部分代码可以不动,但不同的是GPT是通过读取LBA直接得到每个分区的起始扇区、终止扇区、分区类型等等,而MBR方式是先通过DPT得到小于等于4个引导扇区的位置,然后对这几个引导扇区进行解析,每个引导扇区对应一个分区信息。

在使用其他的一些库的时候,笔者受到困扰的地方就是,API函数太多,有很多的库有不同的API功能是接近的,同时又有很多API名字相近但功能截然不同,所以为了避免这种情况,在这里尽量缩减API。根据这个思想,可以构建大致的流程如下:首先由用户提取分区总数,然后用户来进行malloc操作来根据分区数建立分区列表,然后根据分区列表可以找到每个分区的引导扇区位置。在本节中,所谓的“分区信息”是针对于分区表的分区信息,事实上根据前几节的介绍,分卷容量并不一定等于分区容量,所以目前的代码中都是以Partition代表分区,而以Volume代表分卷。

详细的代码结构这里不进行更多的截取,有关代码的调用方式在main.cpp中很容易的看到。在压缩包里有基于VS2015community环境的完整工程。

最后贴上一张完整的分区列表运行结果(磁盘3是我的移动硬盘,磁盘4是我的64GB SD卡)。

 

图2 代码运行结果

 工程下载地址:https://files.cnblogs.com/files/Coder-Ku/NTFS5.rar

转载于:https://www.cnblogs.com/Coder-Ku/p/7628078.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值