在STM32上实现NTFS之3:DPT与MBR的实现

2023-11-02 07:40
文章标签 实现 stm32 ntfs mbr dpt

本文主要是介绍在STM32上实现NTFS之3:DPT与MBR的实现,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

上一节讲到了NTFS的大致结构,这一节就其引导区中的扇区0,即引导扇区做说明。

图1为WinHEX截取的一个“标准的”NTFS的引导扇区数据。

 

 

图1 SD卡的物理0扇区

可以看到,柱面磁头扇区编号0,0,1,那么这是整个磁盘的0号扇区了。现在给出MBR的数据结构如下表(表格翻译整理自MSDN):

字节偏移量

数据长度(字节)

范例数值

数据项说明

0x00

3

4E 54 46 53 20 20 20 20

跳转代码(0xEB5290)

0x03

8

 

OEM号,这里是“NTFS”

0x0B

2

00 02

每扇区字节数。

0x0D

1

08

每簇扇区数

0x0E

2

00 00

保留扇区数

0x10

3

00 00 00

未使用

0x13

2

00 00

未使用

0x15

1

F8

介质描述符

0x16

2

00 00

未使用

0x18

2

3F 00

每磁道扇区数

0x1A

2

FF 00

每柱面磁头数

0x1C

4

3F 00 00 00

隐含扇区数

0x20

4

00 00 00 00

未使用

0x24

4

80 00 80 00

未使用,一般总是0x80008000

0x28

8

1C 91 11 01 00 00 00 00

总扇区数

0x30

8

00 00 04 00 00 00 00 00

MFT起始簇号

0x38

8

11 19 11 00 00 00 00 00

MFT备份起始簇号

0x40

1

F6

每个MFT项大小

0x41

3

00 00 00

未使用

0x44

1

01

每个索引所占的簇数

0x45

3

00 00 00

未使用

0x48

8

3A B2 7B 82 CD 7B 82 14

卷序列号

0x50

4

00 00 00 00

校验和

0x54

426

 

启动代码

0x01FE

2

55 AA

结束标记,0x55AA

在这里我们要先普及一个概念:簇。簇是由若干个扇区构成,构成数量可以是1,2,4,8,……个扇区。一个文件系统在操作文件时,往往不以扇区而是以为单位,而是以簇为单位,原因很简单:执行效率。在很多时候一个文件是大于一个扇区所能容纳的字节数,此时如果继续采用扇区作为基本单位,整个磁盘的碎片化会很严重,并且也不利于文件的读取,所以,由用户可选择的一个单位“簇”诞生了,我们可以在格式化界面看到他,在Windows界面格式化里是“分配单元大小”。这个数值往往决定了分区的小文件传输速度和硬盘使用率。

簇的大小选择很重要,很大的簇有利于一定体积的文件直接由1个簇存储,但过大的簇会导致空间利用效率低。例如10K的文件,如果用8个扇区为1簇,则需要使用3个簇(此处忽略一个文件系统对文件附加的属性,仅作为例子),第3个簇的后4个扇区将被浪费掉,而如果采用8KB的簇,那么会有12个扇区被浪费,如果采用2KB的簇,则不会出现浪费情况。但相对应的寻找簇的次数为3,2,5次,在某些时候,可能有上万个KB级的文件等待传输,2KB的簇显然不符合我们的传输速度考虑,但8KB的将有更大的可能浪费更多扇区,这样考虑,用户需要为每个扇区合理的选择簇大小。NTFS给出的默认簇大小是8个扇区,见上表,也就是4KB。

由此我们可以做出一个结构体如下:

 1 typedef struct
 2 {
 3     uint8_t  JmpCode[3];//跳转指令
 4     uint8_t  OEMName[8];//OEM名
 5     uint16_t BytesPerSector;//每扇区字节数
 6     uint8_t  SectorsPerCluster;//每簇扇区数
 7     uint8_t  ReservedSector[2];//保留扇区数
 8     uint8_t  Unuse1[5];//未使用
 9     uint8_t  MediaDescriptor;//介质描述符
10     uint8_t  Unuse2[2];//未使用
11     uint16_t SectorsPerTrack;//每磁道扇区数
12     uint16_t MegnaticHeadPerCylinder;//每柱面磁头数
13     uint32_t HiddenSectors;//隐含扇区数
14     uint8_t  Unuse3[4];//未使用
15     uint8_t  Unuse4[4];//未使用,此处总是0x80008000
16     uint64_t TotalSectors;//文件系统扇区总数
17     uint64_t MFTStartCluster;//MFT起始簇号
18     uint64_t MFTBackupStartCluster;//MFT备份起始簇号
19     uint8_t SizePerMFT;//每MFT大小
20     uint8_t  Unuse5[3];//未使用
21     uint8_t ClusterPerIndex;//每个索引的大小簇数
22     uint8_t  Unuse6[3];//未使用
23     uint64_t SerialNumber;//序列号
24     uint32_t CheckSum;//校验和
25 uint8_t BootCode[426];//启动代码
26 uint8_t EndSign[2];//结束标记
27 }MBR_Byte;

 

要注意的是NTFS中使用的是小端模式,小端模式就是每个数据的第0个字节代表低8位,第1字节代表次低8位……依此类推,有关小端模式和大端模式,自行百度就可以,这里将不进行讲解。

所以我们当按字节读取第0扇区到MBR的时候,需要按照NTFS所运行的CPU平台,把MBR_Byte结构转化为有意义的正确的数据,我们再建立一个MBR_Info结构,里面的是正确的数据:

 1 typedef struct
 2 
 3 {
 4 
 5     uint8_t  OEMName[8];//OEM名
 6 
 7     uint16_t BytesPerSector;//每扇区字节数
 8 
 9     uint8_t  SectorsPerCluster;//每簇扇区数
10 
11     uint16_t ReservedSector;//保留扇区数
12 
13     uint8_t  MediaDescriptor;//介质描述符
14 
15     uint16_t SectorsPerTrack;//每磁道扇区数
16 
17     uint16_t MegnaticHeadPerCylinder;//每柱面磁头数
18 
19     uint32_t HiddenSectors;//隐含扇区数
20 
21     uint64_t TotalSectors;//文件系统扇区总数
22 
23     uint64_t MFTStartCluster;//MFT起始簇号
24 
25     uint64_t MFTBackupStartCluster;//MFT备份起始簇号
26 
27     uint8_t SizePerMFT;//每MFT大小
28 
29     uint8_t ClusterPerIndex;//每个索引的大小簇数
30 
31     uint64_t SerialNumber;//序列号
32 
33     uint32_t CheckSum;//校验和
34 
35 }MBR_Info;

 

有关小端转化的问题,一会再说,先来专注于本节的所有结构描述。

我们针对一个“标准的NTFS分区”的分析就是这样,从图1也非常容易看到正确数据。那么MBR的位置处在哪?根据MSDN的描述,处于引导扇区的0号扇区,但这个0号扇区并不代表整个磁盘的0号扇区,我们姑且将整个磁盘的扇区数命名为“物理扇区”和“绝对扇区”,将每个NTFS分区的从引导扇区起的扇区编号称之为“逻辑扇区”和“相对扇区”。这样概念就明确了,如果NTFS的分区信息无误,那么每个物理扇区至多会对应一个逻辑扇区号,反过来一个逻辑扇区一定会对应一个物理扇区,当然这是NTFS分区信息无误的情况。我们可以使用DiskGenius来构建一个物理0扇区与逻辑0扇区不同的,也即NTFS分区不是从物理0扇区开始的磁盘,操作过程见图2。

 

 

图2 新的分区选项

 

图3 新的分区结构

现在我们得到了一个并不十分标准的NTFS卷,结构如图3,可以看到前面剁了一小块没被使用的灰色区域。现在再打开WinHEX看一下物理0扇区,如图4。

 

图4 “不标准”的0扇区

和图1中出现了明显的差别,MBR的信息消失了,再次定位于物理32768扇区我们又会发现扇区数据和图1相似。那么此时,物理0扇区所描述的,就不是MBR,而是DPT(Disk Partition Table,硬盘分区表)了,DPT的结构如下:

字节偏移量

数据长度(字节)

数据项说明

0x00

1

是否为活动分区,是则为80H,否则为00H

0x01

1

该分区起始磁头号

0x02

1

该分区起始扇区号(低6位)和起始柱面号(高2位)

0x03

1

该分区起始柱面号的低8位 

0x04

1

系统标志

0x05

1

该分区结束磁头号

0x06

1

该分区结束扇区号(低6位)和结束柱面号(高2位)

0x07

1

该分区结束柱面号的低8位 

0x08

4

相对扇区号

0x12

4

分区所用扇区数

每个分区都有一个DPT项,放在整个磁盘的物理0扇区,为了不使物理扇区与逻辑扇区重合时MBR与DPT互相覆盖,MBR出让了一块区域,从第446字节起的64个字节,作为4个分区的DPT项,并规定,当MBR在物理0扇区时,这个MBR包含有其他所有分区的DPT(也就是64个字节全是有效数据),否则只包含自身所在扇区的DPT。

那么现在可以大致描述出从系统上电到进入NTFS分区这一段的顺序:

首先读取物理0扇区,得到4个分区表和每个分区的逻辑0扇区相对物理0扇区的偏移量(也就是该分区的起始磁头、柱面、扇区,可以直接算出来物理扇区);然后根据每个逻辑0扇区的偏移量去读取对应扇区,得到每个分区的MBR;如果有第四个分区,把第四个逻辑0扇区作为扩展分区表继续进行解析;分析每个MBR,得到当前分区的所有信息,以及最重要的MFT项的起始地址和MFT尺寸。下图5就是磁盘存储结构和读取的顺序。

 

 

图5 硬盘数据存储结构

DPT的数据结构实现如下:

typedef struct{uint8_t  ActivePartition;    //活动分区
uint8_t  StartInfo[3];       //本分区的起始磁头号、扇区号、柱面号
uint8_t  PartitionType;      //分区类型
uint8_t  EndInfo[3];         //本分区的结束磁头号、扇区号、柱面号
uint8_t  UsedSector[4];      //本分区前已使用的扇区数
uint8_t  TotalSector[4];     //本分区的总扇区数

}DPT_Byte;

 

同时添加一个有效的数据信息结构:

typedef struct{bool           ActivePartition;      //活动分区
uint8_t        StartMagneticHead;    //起始磁头号
uint8_t        StartSector;          //起始扇区号
uint16_t       StartCylinder;        //起始柱面号
Partition_Type PartitionType;        //分区类型
uint8_t        EndMagneticHead;      //结束磁头号
uint8_t        EndSector;            //结束扇区号
uint16_t       EndCylinder;          //结束柱面号
uint32_t       UsedSector;           //本分区前已使用的扇区数
uint32_t       TotalSector;          //本分区的总扇区数

}DPT_Info;

 

Partition_Type是一个枚举类型,它代表了DPT中对于分区的操作系统描述,在这里我们将其完全枚举出来:

typedef enum{fsptNullType                          = 0x00,fsptFAT32                             = 0x01,fsptXENIX__root                       = 0X02,fsptXENIX_usr                         = 0X03,fsptFAT16_32M                         = 0X04,fsptExtended                          = 0X05,fsptFAT16                             = 0X06,fsptHPFS_NTFS                         = 0X07,fsptAIX                               = 0X08,fsptAIX_bootable                      = 0X09,fsptOS_2_Boot_Manage                  = 0X0A,fsptWin95_FAT32                       = 0X0B,fsptWin95_Fat32                       = 0X0C,fsptWin95_FAT16                       = 0X0E,fsptWin95_Extended_8GB                = 0X0F,fsptOPUS                              = 0X10,fsptHidden_FAT12                      = 0X11,fsptCompaq_diagnost                   = 0X12,fsptHidden_FAT16                      = 0X16,fsptHidden_FAT16_32GB                 = 0X14,fsptHidden_HPFS_NTFS                  = 0X17,fsptAST_Windows_swap                  = 0X18,fsptHidden_FAT32                      = 0X1B,fsptHidden_FAT32_partition            = 0X1C,fsptHidden_LBA_VFAT_partition         = 0X1E,fsptNEC_DOS                           = 0X24,fsptPartition_Magic                   = 0X3C,fsptVenix_80286                       = 0X40,fsptPPC_PreP_Boot                     = 0X41,fsptSFS                               = 0X42,fsptQNX4_x                            = 0X4D,fsptQNX4_x_2nd_part                   = 0X4E,fsptQNX4_x_3rd_part                   = 0X4F,fsptOntrack_DM                        = 0X50,fsptOntrack_DM6_Aux                   = 0X51,fsptCP_M                              = 0X52,fsptOnTrack_DM6_AUX                   = 0X53,fsptOnTrack_DM6                       = 0X54,fsptEZ_Drive                          = 0X55,fsptGolden_Bow                        = 0X56,fsptPriam_Edisk                       = 0X5C,fsptSpeed_Stor                        = 0X61,fsptGNU_HURD_or_Sys                   = 0X63,fsptNovell_Netware                    = 0X64,fsptNovell_NetWare                    = 0X65,fsptDisk_Secure_Mult                  = 0X70,fsptPC_IX                             = 0X75,fsptOld_Minix                         = 0X80,fsptMinix_Old_Linux                   = 0X81,fsptLinux_swap                        = 0X82,fsptLinux                             = 0X83,fsptOS_2_hidden_C                     = 0X84,fsptLinux_extended                    = 0X85,fsptNTFS_volume_set                   = 0X86,fsptNTFS_Volume_Set                   = 0X87,fsptAmoeba                            = 0X93,fsptAmoeba_BBT                        = 0X94,fsptIBM_Thinkpad_hidden               = 0XA0,fsptBSD_386                           = 0XA5,fsptOpen_BSD                          = 0XA6,fsptNextSTEP                          = 0XA7,fsptBSDI_fs                           = 0XB7,fsptBSDI_swap                         = 0XB8,fsptSolaris_boot_partition            = 0XBE,fsptDRDOS_NovellDOS_secured_Partition = 0XC0,fsptDRDOS_sec                         = 0XC1,fsptDRDOS_Sec                         = 0XC4,fsptDRDOS_SEC                         = 0XC6,fsptSyrinx                            = 0XC7,fsptCP_M_CTOS                         = 0XDB,fsptDOS_access                        = 0XE1,fsptDOS_R_O                           = 0XE3,fsptSpeedStor                         = 0XE4,fsptBeOS_fs                           = 0XEB,fsptSpeedstor                         = 0XF1,fsptDOS3_3_secondary_partition        = 0XF2,fsptSpeed_stor                        = 0XF4,fsptLAN_step                          = 0XFE,fsptBBT                               = 0XFF}Partition_Type;

 

从主引导记录的结构可以知道,它仅仅包含一个64个字节的硬盘分区表。由于每个分区信息需要16个字节,所以对于采用MBR型分区结构的硬盘,最多只能识别4个主要分区(Primary partition)。所以对于一个采用此种分区结构的硬盘来说,想要得到4个以上的主要分区是不可能的。这里就需要引出扩展分区了。扩展分区也是主要分区的一种,但它与主分区的不同在于理论上可以划分为无数个逻辑分区。

扩展分区中逻辑驱动器的引导记录是链式的。每一个逻辑分区都有一个和MBR结构类似的扩展引导记录(EBR),其分区表的第一项指向该逻辑分区本身的引导扇区,第二项指向下一个逻辑驱动器的EBR,分区表第三、第四项没有用到。

完成了基本数据类型,我们就可以开始写代码了,MBR和DPT部分的代码读写实现很简单,注意读取到结构体时使用内存按1字节对齐的关键字,因为结构体有很多1,2,3字节的项,如果不按照字节对齐,会导致结构体字节数不对,从而读取时出现错位。在VC++6.0中,以#pragma pack(1)和pack()两个预编译指令作为字节对齐命令。

需要注意的是,在Windows中只能以Win32API操作磁盘,操作磁盘需要用到CreateFileA()、ReadFile()、WriteFile()这三个函数。

为了便于移植,将读写操作封装成为了单独的读写扇区的函数,要注意WriteFile()时的操作需要小心处理,因为是对磁盘直接进行数据写入,一单写入错误数据,就得重新分区了!在这里我我使用一块1TB的移动硬盘来做实验,如图6,我们可以看到使用的是磁盘4,即物理磁盘号4,这个数在后面的代码中会用到。

 

图6 移动硬盘在“计算机管理”中

图7为上述磁盘分区读取DPT、EBR和MBR主要信息的程序运行效果。

图7 本节程序运行效果

完整代码在最后给出,因为Windows 7中磁盘扇区的直接读写需要获取一定的权限,所以为了避免我们的程序变成“病毒”,本节和后面在PC部分的操作都将以读取为主,而NTFS的格式化、创建文件/目录、删除文件/目录,修改属性等等,都将在STM32平台进行。关于代码中逻辑结构的不合理和需要优化的地方,也会在以后进行优化。下一篇我们将进行MFT项的实现。

 

 1 //main.cpp
 2 #include <stdio.h>
 3 #include <windows.h>
 4 #include "ntfs_types.h"
 5 #include "ntfs_diskio.h"
 6 #include "ntfs_basic.h"
 7 
 8 DPT_Info DPT[10];
 9 MBR_Info MBR[10];
10 int main(void)
11 {
12     GetPartitioninformation(DPT);
13     DisplayPartitionInformation(DPT);
14     for(int i = 0; i < 7; i++)
15     {
16         GetMBR(&MBR[i], &DPT[i]);
17     }
18     printf("\n");
19     DisplayMBR(MBR);
20 
21     return 0;
22 }
main.cpp
 1 //ntfs_diskio.cpp
 2 #include <stdio.h>
 3 #include <windows.h>
 4 #include "ntfs_diskio.h"
 5 #include "ntfs_basic.h"
 6 
 7 BOOL ReadSectorData( HANDLE& hDevice,UINT64 redpos, char * lpOutBuffer512 )
 8 {
 9     memset(lpOutBuffer512,0,512);
10     LARGE_INTEGER li;
11     li.QuadPart = redpos*0x200;//0x200 = 512,求出扇区的 字节地址,通过设置读取的地址和长度进行read
12     SetFilePointer(hDevice,li.LowPart,&li.HighPart,FILE_BEGIN);
13     DWORD DCount=0; //计数
14     BOOL bResult=ReadFile(hDevice, lpOutBuffer512, 512, &DCount, NULL);
15     return bResult;
16 }
17 
18 //通过给定磁盘的编号,获取到磁盘的句柄
19 HANDLE GetDiskHandle(int iDiskNo)
20 {
21     char szDriverBuffer[128];
22     memset(szDriverBuffer,0,128);
23     //格式化设备文件名称
24     sprintf(szDriverBuffer,"\\\\.\\PhysicalDrive%d",iDiskNo);
25     HANDLE m_hDevice = NULL;
26     //CreateFile获取到设备句柄
27     m_hDevice = CreateFileA(
28         szDriverBuffer,// 设备名称,这里指第一块硬盘,多个硬盘的自己修改就好了
29         GENERIC_READ, // 指定读访问方式
30         FILE_SHARE_READ | FILE_SHARE_WRITE, // 共享模式为读|写,0表示不能共享
31         NULL, // NULL表示该句柄不能被子程序继承
32         OPEN_EXISTING, // 打开已经存在的文件,文件不存在则函数调用失败
33         NULL, // 指定文件属性
34         NULL);
35     if (m_hDevice==INVALID_HANDLE_VALUE)
36     {
37         m_hDevice = NULL;
38         //无效
39         return INVALID_HANDLE_VALUE;
40     }
41     //设备句柄
42     return m_hDevice;
43 }
ntfs_diskio.cpp
 1 //ntfs_diskio.h
 2 #ifndef __NTFS_DISKIO_H
 3 #define __NTFS_DISKIO_H
 4 
 5 #include "ntfs_types.h"
 6 
 7 BOOL ReadSectorData( HANDLE& hDevice,UINT64 redpos, char * lpOutBuffer512 );//读取扇区函数,移植到MCU改变函数实现
 8 HANDLE GetDiskHandle(int iDiskNo);
 9 
10 #endif
ntfs_diskio.h
 1 //ntfs_basic.h
 2 #ifndef __NTFS_BASIC_H
 3 #define __NTFS_BASIC_H
 4 
 5 #include "ntfs_types.h"
 6 
 7 uint16_t ArrayToU16LittleEnd(uint8_t *str);
 8 uint32_t ArrayToU32LittleEnd(uint8_t *str);
 9 uint64_t ArrayToU64LittleEnd(uint8_t *str);
10 //获取扇区MBR信息
11 void GetMBR(MBR_Info *MBR, DPT_Info *DPT);
12 //获取磁盘分区信息,不区分扩展分区和主分区,均以“分区”表示,DPT_Info中保存物理起始扇区
13 void GetPartitioninformation(DPT_Info* DPT);
14 //扇区信息转成可以识别的Info结构
15 void DptTransferLittleEnd(DPT_Byte* src, DPT_Info *dest);
16 void EbrTransferLittleEnd(DPT_Byte* src, DPT_Info *dest);
17 void MbrTransferLittleEnd(MBR_Byte* src, MBR_Info *dest);
18 
19 //以下为PC机调试显示用,移植到MCU需要移除
20 void DisplayPartitionInformation(DPT_Info* DPT);
21 void DisplayMBR(MBR_Info *MBR);
22 
23 #endif
ntfs_basic.h
  1 //ntfs_basic.cpp
  2 #include <stdio.h>
  3 #include <windows.h>
  4 #include "ntfs_basic.h"
  5 #include "ntfs_diskio.h"
  6 
  7 #define PHYDRIVE 4
  8 
  9 uint16_t ArrayToU16LittleEnd(uint8_t *str)
 10 {
 11     return (str[0] + (str[1] << 8));
 12 }
 13 
 14 uint32_t ArrayToU32LittleEnd(uint8_t *str)
 15 {
 16     return (str[0] + (str[1] << 8) + (str[2] << 16) + (str[3] << 24));
 17 }
 18 
 19 uint64_t ArrayToU64LittleEnd(uint8_t *str)
 20 {
 21     return (str[0] + (str[1] << 8) + (str[2] << 16) + (str[3] << 24) + (str[4] << 32) + (str[5] << 40) + (str[6] << 48) + (str[7] << 56));
 22 }
 23 
 24 void GetMBR(MBR_Info *MBR, DPT_Info *DPT)
 25 {
 26     MBR_Byte MBRsector;
 27     HANDLE hdl = GetDiskHandle(PHYDRIVE);//获取一个指定物理驱动器句柄
 28     ReadSectorData(hdl, DPT->RealOffset, (char*)(void*)&MBRsector);
 29     MbrTransferLittleEnd(&MBRsector, MBR);
 30 }
 31 void GetPartitioninformation(DPT_Info* DPT)
 32 {
 33     DPT_Byte DPTsector;//定义DPT扇区作为buffer,注意MCU的栈尺寸要大于512字节
 34     unsigned long sectorsoffset = 0;
 35     unsigned long extendsoffset = 0;
 36     HANDLE hdl = GetDiskHandle(PHYDRIVE);//获取一个指定物理驱动器句柄
 37     ReadSectorData(hdl, 0, (char*)(void*)&DPTsector);//载入DPT
 38     DptTransferLittleEnd(&DPTsector, DPT);//小端模式转换扇区数据为分区信息
 39     int k;
 40     for(k = 0; k < 4; k++)
 41     {
 42         if(DPT[k].PartitionType == fsptWin95_Extended_8GB || DPT[k].PartitionType == fsptExtended)//两种扩展分区
 43             break;
 44         DPT[k].RealOffset = DPT[k].UsedSector;
 45     }
 46     extendsoffset = DPT[k].UsedSector;
 47     sectorsoffset = extendsoffset;
 48     int i = k;
 49     unsigned char Partitions = 255;//sizeof(DPT) / sizeof(DPT_Info);
 50     while(i < Partitions)//最大遍历分区数量Partitions,防止由于分区过多导致下标越界
 51     {
 52         if(DPT[i].PartitionType == fsptWin95_Extended_8GB || DPT[i].PartitionType == fsptExtended)//两种扩展分区
 53         {
 54             if(i == k)
 55                 DPT[i].RealOffset = extendsoffset;
 56             else
 57                 DPT[i].RealOffset = extendsoffset + DPT[i].UsedSector + DPT[i+1].UsedSector;
 58             ReadSectorData(hdl, sectorsoffset, (char*)(void*)&DPTsector);//载入EBR
 59             DptTransferLittleEnd(&DPTsector, &DPT[i]);//小端模式转换扇区数据为分区信息
 60             sectorsoffset = extendsoffset + DPT[i+1].UsedSector;
 61             DPT[i].RealOffset+=DPT[i].UsedSector;//对扩展扇区MBR地址做补偿
 62             i++;
 63         }
 64         else if(DPT[i].TotalSector == NULL)
 65         {
 66             break;
 67         }
 68     }
 69     
 70 }
 71 
 72 void DisplayPartitionInformation(DPT_Info* DPT)
 73 {
 74     printf("磁盘%d:\n", PHYDRIVE);
 75     printf("               活动分区     分区类型     起始扇区     总扇区数      容量\n");
 76     printf("------------------------------------------------------------------------\n");
 77     int i = 0;
 78     unsigned char Partitions = 255;//sizeof(DPT) / sizeof(DPT_Info);
 79     while(DPT[i].TotalSector != NULL && i < Partitions)
 80     {
 81         printf("磁盘4分区%d:  ", i);
 82         if(DPT[i].ActivePartition)
 83             printf("");
 84         else
 85             printf("");
 86         printf("%13d",    DPT[i].PartitionType);
 87         printf("%13d",    DPT[i].RealOffset);
 88         printf("%13d",    DPT[i].TotalSector);
 89         printf("%8.2fGB", DPT[i].TotalSector/2097152.0);
 90         printf("\n");
 91         i++;
 92     }
 93 }
 94 
 95 void DisplayMBR(MBR_Info *MBR)
 96 {
 97     printf("磁盘%d:\n", PHYDRIVE);
 98     printf("OEM名        扇区总数    MFT起始簇号    MFT备份簇号    MFT大小    索引大小\n");
 99     printf("--------------------------------------------------------------------------\n");
100     int i = 0;
101     unsigned char Partitions = 255;//sizeof(MBR) / sizeof(MBR_Info);
102     while(MBR[i].TotalSectors != NULL && i < Partitions)
103     {
104         printf("%s",     MBR[i].OEMName);
105         printf("%13lld", MBR[i].TotalSectors);
106         printf("%15lld", MBR[i].MFTStartCluster);
107         printf("%15lld", MBR[i].MFTBackupStartCluster);
108         printf("%11d",   MBR[i].SizePerMFT);
109         printf("%12lld", MBR[i].ClusterPerIndex);
110         printf("\n");
111         i++;
112     }
113 }
114 void DptTransferLittleEnd(DPT_Byte* src, DPT_Info *dest)
115 {
116     for(int i = 0; i < 4; i++)
117     {
118         dest[i].ActivePartition   = src->DPT_Table[i].ActivePartition == 0x80 ? true : false;//0x80是活动扇区,0x00是非活动扇区
119         dest[i].StartMagneticHead = src->DPT_Table[i].StartInfo[0];
120         dest[i].StartSector       = src->DPT_Table[i].StartInfo[1] & 0x3F;
121         dest[i].StartCylinder     = src->DPT_Table[i].StartInfo[2] + ((src->DPT_Table[i].StartInfo[1] & 0xC0) << 2);
122         dest[i].PartitionType     = (Partition_Type)src->DPT_Table[i].PartitionType;
123         dest[i].EndMagneticHead   = src->DPT_Table[i].EndInfo[0];
124         dest[i].EndSector         = src->DPT_Table[i].EndInfo[1] & 0x3F;
125         dest[i].EndCylinder       = src->DPT_Table[i].EndInfo[2] + ((src->DPT_Table[i].EndInfo[1] & 0xC0) << 2);
126         dest[i].UsedSector        = ArrayToU32LittleEnd(src->DPT_Table[i].UsedSector);
127         dest[i].TotalSector       = ArrayToU32LittleEnd(src->DPT_Table[i].TotalSector);
128     }
129 }
130 
131 void EbrTransferLittleEnd(DPT_Byte* src, DPT_Info *dest)
132 {
133     for(int i = 0; i < 2; i++)
134     {
135         dest[i].ActivePartition   = src->DPT_Table[i].ActivePartition == 0x80 ? true : false;//0x80是活动扇区,0x00是非活动扇区
136         dest[i].StartMagneticHead = src->DPT_Table[i].StartInfo[0];
137         dest[i].StartSector       = src->DPT_Table[i].StartInfo[1] & 0x3F;
138         dest[i].StartCylinder     = src->DPT_Table[i].StartInfo[2] + ((src->DPT_Table[i].StartInfo[1] & 0xC0) << 2);
139         dest[i].PartitionType     = (Partition_Type)src->DPT_Table[i].PartitionType;
140         dest[i].EndMagneticHead   = src->DPT_Table[i].EndInfo[0];
141         dest[i].EndSector         = src->DPT_Table[i].EndInfo[1] & 0x3F;
142         dest[i].EndCylinder       = src->DPT_Table[i].EndInfo[2] + ((src->DPT_Table[i].EndInfo[1] & 0xC0) << 2);
143         dest[i].UsedSector        = ArrayToU32LittleEnd(src->DPT_Table[i].UsedSector);
144         dest[i].TotalSector       = ArrayToU32LittleEnd(src->DPT_Table[i].TotalSector);
145     }
146 }
147 
148 void MbrTransferLittleEnd(MBR_Byte* src, MBR_Info *dest)
149 {
150     dest->OEMName[0]              = src->OEMName[0];
151     dest->OEMName[1]              = src->OEMName[1];
152     dest->OEMName[2]              = src->OEMName[2];
153     dest->OEMName[3]              = src->OEMName[3];
154     dest->OEMName[4]              = src->OEMName[4];
155     dest->OEMName[5]              = src->OEMName[5];
156     dest->OEMName[6]              = src->OEMName[6];
157     dest->OEMName[7]              = src->OEMName[7];
158     dest->OEMName[8]              = 0;
159     dest->BytesPerSector          = ArrayToU16LittleEnd(src->BytesPerSector);
160     dest->SectorsPerCluster       = src->SectorsPerCluster;
161     dest->ReservedSector          = ArrayToU16LittleEnd(src->ReservedSector);
162     dest->MediaDescriptor         = src->MediaDescriptor;
163     dest->SectorsPerTrack         = ArrayToU16LittleEnd(src->SectorsPerTrack);
164     dest->MegnaticHeadPerCylinder = ArrayToU16LittleEnd(src->MegnaticHeadPerCylinder);
165     dest->HiddenSectors           = ArrayToU32LittleEnd(src->HiddenSectors);
166     dest->TotalSectors            = ArrayToU64LittleEnd(src->TotalSectors);
167     dest->MFTStartCluster         = ArrayToU64LittleEnd(src->MFTStartCluster);
168     dest->MFTBackupStartCluster   = ArrayToU64LittleEnd(src->MFTBackupStartCluster);
169     dest->SizePerMFT              = src->SizePerMFT;
170     dest->ClusterPerIndex         = src->ClusterPerIndex;
171     dest->SerialNumber            = ArrayToU64LittleEnd(src->SerialNumber);
172     dest->CheckSum                = ArrayToU32LittleEnd(src->CheckSum);
173 }
ntfs_basic.cpp

 

 

部分内容参考自:

http://www.blogfshare.com/mbr-dpt-ebr.html

http://blog.csdn.net/jha334201553/article/details/9088921

http://bbs.csdn.net/topics/390374342

https://support.microsoft.com/zh-cn/kb/942448

https://technet.microsoft.com/en-us/library/cc781134(v=ws.10).aspx

http://www.youranshare.com/push/code/win-c-cpp/278.html

 

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

这篇关于在STM32上实现NTFS之3:DPT与MBR的实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/329117

相关文章

C++使用栈实现括号匹配的代码详解

《C++使用栈实现括号匹配的代码详解》在编程中,括号匹配是一个常见问题,尤其是在处理数学表达式、编译器解析等任务时,栈是一种非常适合处理此类问题的数据结构,能够精确地管理括号的匹配问题,本文将通过C+... 目录引言问题描述代码讲解代码解析栈的状态表示测试总结引言在编程中,括号匹配是一个常见问题,尤其是在

Java实现检查多个时间段是否有重合

《Java实现检查多个时间段是否有重合》这篇文章主要为大家详细介绍了如何使用Java实现检查多个时间段是否有重合,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录流程概述步骤详解China编程步骤1:定义时间段类步骤2:添加时间段步骤3:检查时间段是否有重合步骤4:输出结果示例代码结语作

使用C++实现链表元素的反转

《使用C++实现链表元素的反转》反转链表是链表操作中一个经典的问题,也是面试中常见的考题,本文将从思路到实现一步步地讲解如何实现链表的反转,帮助初学者理解这一操作,我们将使用C++代码演示具体实现,同... 目录问题定义思路分析代码实现带头节点的链表代码讲解其他实现方式时间和空间复杂度分析总结问题定义给定

Java覆盖第三方jar包中的某一个类的实现方法

《Java覆盖第三方jar包中的某一个类的实现方法》在我们日常的开发中,经常需要使用第三方的jar包,有时候我们会发现第三方的jar包中的某一个类有问题,或者我们需要定制化修改其中的逻辑,那么应该如何... 目录一、需求描述二、示例描述三、操作步骤四、验证结果五、实现原理一、需求描述需求描述如下:需要在

如何使用Java实现请求deepseek

《如何使用Java实现请求deepseek》这篇文章主要为大家详细介绍了如何使用Java实现请求deepseek功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1.deepseek的api创建2.Java实现请求deepseek2.1 pom文件2.2 json转化文件2.2

python使用fastapi实现多语言国际化的操作指南

《python使用fastapi实现多语言国际化的操作指南》本文介绍了使用Python和FastAPI实现多语言国际化的操作指南,包括多语言架构技术栈、翻译管理、前端本地化、语言切换机制以及常见陷阱和... 目录多语言国际化实现指南项目多语言架构技术栈目录结构翻译工作流1. 翻译数据存储2. 翻译生成脚本

如何通过Python实现一个消息队列

《如何通过Python实现一个消息队列》这篇文章主要为大家详细介绍了如何通过Python实现一个简单的消息队列,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录如何通过 python 实现消息队列如何把 http 请求放在队列中执行1. 使用 queue.Queue 和 reque

Python如何实现PDF隐私信息检测

《Python如何实现PDF隐私信息检测》随着越来越多的个人信息以电子形式存储和传输,确保这些信息的安全至关重要,本文将介绍如何使用Python检测PDF文件中的隐私信息,需要的可以参考下... 目录项目背景技术栈代码解析功能说明运行结php果在当今,数据隐私保护变得尤为重要。随着越来越多的个人信息以电子形

使用 sql-research-assistant进行 SQL 数据库研究的实战指南(代码实现演示)

《使用sql-research-assistant进行SQL数据库研究的实战指南(代码实现演示)》本文介绍了sql-research-assistant工具,该工具基于LangChain框架,集... 目录技术背景介绍核心原理解析代码实现演示安装和配置项目集成LangSmith 配置(可选)启动服务应用场景

使用Python快速实现链接转word文档

《使用Python快速实现链接转word文档》这篇文章主要为大家详细介绍了如何使用Python快速实现链接转word文档功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 演示代码展示from newspaper import Articlefrom docx import