大家都知道磁盘有多个盘片重叠在一起组成,那么在不同的盘片的盘面上怎么访问数据呢?肯定一个磁头是不够的,所以每个盘片的盘面需要一个对应的磁 头。在每个盘面上又根据不同的半径分为不同的同心圆磁圈,这些在不同盘面上的半径相等的磁圈组成一个柱面,为什么需要这么分呢?其实也很好理解,因为磁盘 总共就一个马达所以就只好同手同脚的运动了。而这些柱面又由更小的单元组成,这些最小的单元被称为扇区。(之前博文提到系统启动可以跳过文件系统来访问就 是因为由这些硬编码的磁头柱面和扇区的存在)。接下来是两个容易混淆的概念:分区和卷。分区主要用于管理一组连续的扇区,而卷则是我们所常见的盘符,是分 区的一种逻辑表现。不过卷对磁盘的管理不是以扇区为单位,而是以簇为单位,一簇由2的N次方个扇区组成。这也就是为什么我们查看文件属性的时候,就算文件 再小也是4Kb,因为是以一簇为单位进行管理的。MBR(master boot record)也就是之前所提到的主引导记录。整个主引导记录的格式如 表格所示。
偏移地址范围 |
字段字节长度 |
字段名 |
0000 – 01BD |
446 |
MBR启动程序 |
01BE – 01FD |
64 |
4个磁盘分区表 |
01FE – 01FF |
2 |
分区效结束标志(55AA) |
由于这里包含四个磁盘分区表,所以系统才只能支持四个主分区,当然windows对此进行扩展使得理论上可以无限支持分区。下面进入NTFS的分析:
NTFS的数据大体上包含四个部分:
(1) Partition boot sector(引导扇区,又称BPB),此部分为所有磁盘格式都共有,占用一个扇区。
(2) Master File Table(主文件列表,MFT),它是对卷上所有文件的记录,每一个文件对应一个记录项,理论上占用该卷12%的空间。
(3) System files(系统文件),NTFS系统一共有16个系统文件,和8个保留文件。
(4) File area(数据区),留给用户的空间。
下面开始一步步的分析NTFS的内部实现。(源代码参照reactOS的文件系统ntfs部分)
typedef struct _BOOT_SECTOR
{
UCHAR Jump[3]; // 0x00
UCHAR OEMID[8]; // 0x03
BIOS_PARAMETERS_BLOCK BPB;
EXTENDED_BIOS_PARAMETERS_BLOCK EBPB;
UCHAR BootStrap[426]; // 0x54
USHORT EndSector; // 0x1FE
} BOOT_SECTOR, *PBOOT_SECTOR;
这里就是整个NTFS文件 系统的第一个扇区的样子,第一个是跳转指令。不要忘了当MBR向分区传递控制权的时候需要一条跳转指令将后面的不可执行字节都跳过去。第二个表明磁盘的标 识。下面两个是连续的结构体,BootStrap部分是第一个成员跳转的后续指令;最后是结尾部分用于标识结束。由于这里只适用于找到真正的NTFS分区 所在,所以不能看到上面所提及的四个部分。
typedef struct _BIOS_PARAMETERS_BLOCK
{
USHORT BytesPerSector; // 0x0B
UCHAR SectorsPerCluster; // 0x0D
UCHAR Unused0[7]; // 0x0E, checked when volume is mounted
UCHAR MediaId; // 0x15
UCHAR Unused1[2]; // 0x16
USHORT SectorsPerTrack; // 0x18
USHORT Heads; // 0x1A
UCHAR Unused2[4]; // 0x1C
UCHAR Unused3[4]; // 0x20, checked when volume is mounted
} BIOS_PARAMETERS_BLOCK, *PBIOS_PARAMETERS_BLOCK;
上面是关于BIOS参数块的保存,毕竟对磁盘的存取还是需要最底层的一些信息,成员变量的含义基本可以通过翻译得到。
typedef struct _EXTENDED_BIOS_PARAMETERS_BLOCK
{
USHORT Unknown[2]; // 0x24, always 80 00 80 00
ULONGLONG SectorCount; // 0x28
ULONGLONG MftLocation; // 0x30
ULONGLONG MftMirrLocation; // 0x38
CHAR ClustersPerMftRecord; // 0x40
UCHAR Unused4[3]; // 0x41
CHAR ClustersPerIndexRecord; // 0x44
UCHAR Unused5[3]; // 0x45
ULONGLONG SerialNumber; // 0x48
UCHAR Checksum[4]; // 0x50
} EXTENDED_BIOS_PARAMETERS_BLOCK, *PEXTENDED_BIOS_PARAMETERS_BLOCK;
上 面这个慢慢的包含NTFS特定的分区信息,MftLocation指向MFT文件在NTFS当中的位置,而MftMirrLocation则指向相应的 MFT镜像文件,ClustersPerMftRecord则表明每个MFT记录的大小,而ClustersPerIndexRecord则表明每个索引 记录所占的大小。
typedef struct
{
ULONG Magic; //文件记录标志,总为FILE
USHORT USAOffset; // 从此结构的起始点到更新序列的偏移
USHORT USACount; //整个更新序列的大小,和上面的部分结合用于写回文件
ULONGLONG LogSequenceNumber; //日志文件的序列号
USHORT SequenceNumber; //本文件所在的序列号
USHORT LinkCount; //符号链接的数目
USHORT AttributesOffset; //本文件所在的属性的偏移
USHORT Flags; //表明文件的类型(目录亦或是文件)以及使用还是未用
ULONG BytesInUse; // 文件的实际长度,包含文件头,结尾标识符以及属性等等
ULONG BytesAllocated; //给文件分配的长度
ULONGLONG BaseMFTRecord; //如果属性文件太长以至于不能被保存,则利用这一个作为索引找到下一个属性文件,否则为0
USHORT NextAttributeInstance; //下一个自由ID所在的位置
} NTFS_MFT_RECORD, *PNTFS_MFT_RECORD;