本文详细分析了exfat文件系统结构,并通过实际数据对exfat文件系统各个字段中的定义进行解释。
目录
2 -1Master Boot Record(MBR)-MBR分区
2-2 GUID Partition Table(GPT)分区
3.1.2 扩展引导扇区(Extended Boot Sector)
3.1.4 引导校验和(ExFAT Boot Checksum)
1文件系统整体布局
硬盘有两种分区格式:MBR(Master Boot Record)分区和GPT(GUID Partition Table)分区。MBR磁盘分区支持最大卷为2 TB(Terabytes)并且每个磁盘最多有4个主分区(或3个主分区,1个扩展分区和无限制的逻辑驱动器);GPT磁盘分区样式支持最大卷为18 EB(Exabytes)并且每磁盘的分区数没有上限,只受到操作系统限制,Windows最大仅支持128个GPT分区。硬盘是哪种分区可以根据主引导记录(Master Boot Record)中分区类型字段判断,分区类型为“EE”,代表GPT分区。
2 -1Master Boot Record(MBR)-MBR分区
2.1 结构
参考:https://en.wikipedia.org/wiki/Master_boot_record#PTE
传统通用的Master Boot Record(MBR)结构
地址 | 描述 | 大小/字节 | ||
十六进制 | 十进制 | |||
+000hex | +0 | 引导代码区 | 446 | |
+1BEhex | +446 | 第1分区 | 分区表 | 16 |
+1CEhex | +462 | 第2分区 | 16 | |
+1DEhex | +478 | 第3分区 | 16 | |
+1EEhex | +494 | 第4分区 | 16 | |
+1FEhex | +510 | 55hex | 引导结束标志 | 2 |
+1FFhex | +511 | AAhex | ||
Total size: 446 + 4×16 + 2 | 512 |
每个分区格式
偏移量/字节 | 长度/字节 | 描述 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
0x00 | 1 | 物理驱动器状态,旧的MBR只接受80H,00H表示不活动的,01H~7FH表示无效 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
0x01 | 3 | 在分区的第一个绝对扇区CHS地址,格式如下 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
0x04 | 1 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
0x05 | 3 | 在分区的最后绝对扇区CHS地址,格式同第一个绝对扇区CHS地址 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
0x08 | 4 | 分区中第一个LBA(逻辑块寻址)绝对扇区 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
0x0C | 4 | 分区扇区数 |
分区格式有MBR分区和GPT分区,以上分区格式属于MBR。
2.2 数据分析
480G与943G SATA盘 MBR分区数据对比
480G | 943G |
33 00 8E D0 BC 00 7C 8E C0 8E D8 BE 00 7C BF 00 06 B9 00 02 FC F3 A4 50 68 1C 06 CB FB B9 04 00 BD BE 07 80 7E 00 00 7C 0B 0F 85 0E 01 83 C5 10 E2 F1 CD 18 88 56 00 55 C6 46 11 05 C6 46 10 00 B4 41 BB AA 55 CD 13 5D 72 0F 81 FB 55 AA 75 09 F7 C1 01 00 74 03 FE 46 10 66 60 80 7E 10 00 74 26 66 68 00 00 00 00 66 FF 76 08 68 00 00 68 00 7C 68 01 00 68 10 00 B4 42 8A 56 00 8B F4 CD 13 9F 83 C4 10 9E EB 14 B8 01 02 BB 00 7C 8A 56 00 8A 76 01 8A 4E 02 8A 6E 03 CD 13 66 61 73 1C FE 4E 11 75 0C 80 7E 00 80 0F 84 8A 00 B2 80 EB 84 55 32 E4 8A 56 00 CD 13 5D EB 9E 81 3E FE 7D 55 AA 75 6E FF 76 00 E8 8D 00 75 17 FA B0 D1 E6 64 E8 83 00 B0 DF E6 60 E8 7C 00 B0 FF E6 64 E8 75 00 FB B8 00 BB CD 1A 66 23 C0 75 3B 66 81 FB 54 43 50 41 75 32 81 F9 02 01 72 2C 66 68 07 BB 00 00 66 68 00 02 00 00 66 68 08 00 00 00 66 53 66 53 66 55 66 68 00 00 00 00 66 68 00 7C 00 00 66 61 68 00 00 07 CD 1A 5A 32 F6 EA 00 7C 00 00 CD 18 A0 B7 07 EB 08 A0 B6 07 EB 03 A0 B5 07 32 E4 05 00 07 8B F0 AC 3C 00 74 09 BB 07 00 B4 0E CD 10 EB F2 F4 EB FD 2B C9 E4 64 EB 00 24 02 E0 F8 24 02 C3 49 6E 76 61 6C 69 64 20 70 61 72 74 69 74 69 6F 6E 20 74 61 62 6C 65 00 45 72 72 6F 72 20 6C 6F 61 64 69 6E 67 20 6F 70 65 72 61 74 69 6E 67 20 73 79 73 74 65 6D 00 4D 69 73 73 69 6E 67 20 6F 70 65 72 61 74 69 6E 67 20 73 79 73 74 65 6D 00 00 00 63 7B 9A 54 AC 76 97 00 00 00 20 21 00 07 FE FF FF 00 08 00 00 00 20 E4 37 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 AA | 33 00 8E D0 BC 00 7C 8E C0 8E D8 BE 00 7C BF 00 06 B9 00 02 FC F3 A4 50 68 1C 06 CB FB B9 04 00 BD BE 07 80 7E 00 00 7C 0B 0F 85 0E 01 83 C5 10 E2 F1 CD 18 88 56 00 55 C6 46 11 05 C6 46 10 00 B4 41 BB AA 55 CD 13 5D 72 0F 81 FB 55 AA 75 09 F7 C1 01 00 74 03 FE 46 10 66 60 80 7E 10 00 74 26 66 68 00 00 00 00 66 FF 76 08 68 00 00 68 00 7C 68 01 00 68 10 00 B4 42 8A 56 00 8B F4 CD 13 9F 83 C4 10 9E EB 14 B8 01 02 BB 00 7C 8A 56 00 8A 76 01 8A 4E 02 8A 6E 03 CD 13 66 61 73 1C FE 4E 11 75 0C 80 7E 00 80 0F 84 8A 00 B2 80 EB 84 55 32 E4 8A 56 00 CD 13 5D EB 9E 81 3E FE 7D 55 AA 75 6E FF 76 00 E8 8D 00 75 17 FA B0 D1 E6 64 E8 83 00 B0 DF E6 60 E8 7C 00 B0 FF E6 64 E8 75 00 FB B8 00 BB CD 1A 66 23 C0 75 3B 66 81 FB 54 43 50 41 75 32 81 F9 02 01 72 2C 66 68 07 BB 00 00 66 68 00 02 00 00 66 68 08 00 00 00 66 53 66 53 66 55 66 68 00 00 00 00 66 68 00 7C 00 00 66 61 68 00 00 07 CD 1A 5A 32 F6 EA 00 7C 00 00 CD 18 A0 B7 07 EB 08 A0 B6 07 EB 03 A0 B5 07 32 E4 05 00 07 8B F0 AC 3C 00 74 09 BB 07 00 B4 0E CD 10 EB F2 F4 EB FD 2B C9 E4 64 EB 00 24 02 E0 F8 24 02 C3 49 6E 76 61 6C 69 64 20 70 61 72 74 69 74 69 6F 6E 20 74 61 62 6C 65 00 45 72 72 6F 72 20 6C 6F 61 64 69 6E 67 20 6F 70 65 72 61 74 69 6E 67 20 73 79 73 74 65 6D 00 4D 69 73 73 69 6E 67 20 6F 70 65 72 61 74 69 6E 67 20 73 79 73 74 65 6D 00 00 00 63 7B 9A 15 F2 CA 2C 00 00 00 20 21 00 07 FE FF FF 00 08 00 00 00 E8 FF 75 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 AA |
_应该是个随机数,以上两个磁盘的隐藏扇区数均为0x0800,总扇区数分别为0x37E42000、0x75FFE800。
943G 分成多个分区(MBR)的MBR数据
2个 | 4个 |
33 00 8E D0 BC 00 7C 8E C0 8E D8 BE 00 7C BF 00 06 B9 00 02 FC F3 A4 50 68 1C 06 CB FB B9 04 00 BD BE 07 80 7E 00 00 7C 0B 0F 85 0E 01 83 C5 10 E2 F1 CD 18 88 56 00 55 C6 46 11 05 C6 46 10 00 B4 41 BB AA 55 CD 13 5D 72 0F 81 FB 55 AA 75 09 F7 C1 01 00 74 03 FE 46 10 66 60 80 7E 10 00 74 26 66 68 00 00 00 00 66 FF 76 08 68 00 00 68 00 7C 68 01 00 68 10 00 B4 42 8A 56 00 8B F4 CD 13 9F 83 C4 10 9E EB 14 B8 01 02 BB 00 7C 8A 56 00 8A 76 01 8A 4E 02 8A 6E 03 CD 13 66 61 73 1C FE 4E 11 75 0C 80 7E 00 80 0F 84 8A 00 B2 80 EB 84 55 32 E4 8A 56 00 CD 13 5D EB 9E 81 3E FE 7D 55 AA 75 6E FF 76 00 E8 8D 00 75 17 FA B0 D1 E6 64 E8 83 00 B0 DF E6 60 E8 7C 00 B0 FF E6 64 E8 75 00 FB B8 00 BB CD 1A 66 23 C0 75 3B 66 81 FB 54 43 50 41 75 32 81 F9 02 01 72 2C 66 68 07 BB 00 00 66 68 00 02 00 00 66 68 08 00 00 00 66 53 66 53 66 55 66 68 00 00 00 00 66 68 00 7C 00 00 66 61 68 00 00 07 CD 1A 5A 32 F6 EA 00 7C 00 00 CD 18 A0 B7 07 EB 08 A0 B6 07 EB 03 A0 B5 07 32 E4 05 00 07 8B F0 AC 3C 00 74 09 BB 07 00 B4 0E CD 10 EB F2 F4 EB FD 2B C9 E4 64 EB 00 24 02 E0 F8 24 02 C3 49 6E 76 61 6C 69 64 20 70 61 72 74 69 74 69 6F 6E 20 74 61 62 6C 65 00 45 72 72 6F 72 20 6C 6F 61 64 69 6E 67 20 6F 70 65 72 61 74 69 6E 67 20 73 79 73 74 65 6D 00 4D 69 73 73 69 6E 67 20 6F 70 65 72 61 74 69 6E 67 20 73 79 73 74 65 6D 00 00 00 63 7B 9A 15 F2 CA 2C 00 00 00 20 21 00 07 FE FF FF 00 08 00 00 00 00 80 0C 00 FE FF FF 07 FE FF FF 00 08 80 0C 00 E8 7F 69 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 AA | 33 00 8E D0 BC 00 7C 8E C0 8E D8 BE 00 7C BF 00 06 B9 00 02 FC F3 A4 50 68 1C 06 CB FB B9 04 00 BD BE 07 80 7E 00 00 7C 0B 0F 85 0E 01 83 C5 10 E2 F1 CD 18 88 56 00 55 C6 46 11 05 C6 46 10 00 B4 41 BB AA 55 CD 13 5D 72 0F 81 FB 55 AA 75 09 F7 C1 01 00 74 03 FE 46 10 66 60 80 7E 10 00 74 26 66 68 00 00 00 00 66 FF 76 08 68 00 00 68 00 7C 68 01 00 68 10 00 B4 42 8A 56 00 8B F4 CD 13 9F 83 C4 10 9E EB 14 B8 01 02 BB 00 7C 8A 56 00 8A 76 01 8A 4E 02 8A 6E 03 CD 13 66 61 73 1C FE 4E 11 75 0C 80 7E 00 80 0F 84 8A 00 B2 80 EB 84 55 32 E4 8A 56 00 CD 13 5D EB 9E 81 3E FE 7D 55 AA 75 6E FF 76 00 E8 8D 00 75 17 FA B0 D1 E6 64 E8 83 00 B0 DF E6 60 E8 7C 00 B0 FF E6 64 E8 75 00 FB B8 00 BB CD 1A 66 23 C0 75 3B 66 81 FB 54 43 50 41 75 32 81 F9 02 01 72 2C 66 68 07 BB 00 00 66 68 00 02 00 00 66 68 08 00 00 00 66 53 66 53 66 55 66 68 00 00 00 00 66 68 00 7C 00 00 66 61 68 00 00 07 CD 1A 5A 32 F6 EA 00 7C 00 00 CD 18 A0 B7 07 EB 08 A0 B6 07 EB 03 A0 B5 07 32 E4 05 00 07 8B F0 AC 3C 00 74 09 BB 07 00 B4 0E CD 10 EB F2 F4 EB FD 2B C9 E4 64 EB 00 24 02 E0 F8 24 02 C3 49 6E 76 61 6C 69 64 20 70 61 72 74 69 74 69 6F 6E 20 74 61 62 6C 65 00 45 72 72 6F 72 20 6C 6F 61 64 69 6E 67 20 6F 70 65 72 61 74 69 6E 67 20 73 79 73 74 65 6D 00 4D 69 73 73 69 6E 67 20 6F 70 65 72 61 74 69 6E 67 20 73 79 73 74 65 6D 00 00 00 63 7B 9A 15 F2 CA 2C 00 00 00 20 21 00 07 FE FF FF 00 08 00 00 00 00 80 0C 00 FE FF FF 07 FE FF FF 00 08 80 0C 00 00 00 19 00 FE FF FF 07 FE FF FF 00 08 80 25 00 00 80 25 00 FE FF FF 0F FE FF FF 00 08 00 4B 00 F0 FF 2A 55 AA |
2.3 读取硬盘MBR数据C++语言代码
#include "windows.h"
#include "stdio.h"
const UINT uSectorSize = 512;
const UINT uBegSector = 0;
const UINT uSectorNum = 1;
const UINT uReadSize = 512*1;
BYTE bBuffer[512] = { 0 };
const char pDiskPath[] = "\\\\.\\PHYSICALDRIVE2";
void ShowByteInform(PBYTE pBuf, UINT uSize)
{
for (UINT i = 1; i <= uSize; i++)
{
printf("%02X ", pBuf[i - 1]);
if (i % 16 == 0)
{
printf("\n");
}
else if (i % 8 == 0)
{
printf(" ");
}
}
}
int main(int argc, char* argv[])
{
HANDLE hDisk;
HANDLE hFile;
hDisk = CreateFile(pDiskPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0);
if (hDisk == INVALID_HANDLE_VALUE)
{
MessageBox(0, "can't open the disk!", 0, 0);
return 0;
}
SetFilePointer(hDisk, uBegSector*uSectorSize, 0, FILE_BEGIN);
DWORD dwReadByte;
ReadFile(hDisk, (LPVOID)bBuffer, uReadSize, &dwReadByte, NULL);
if (dwReadByte == 0)
{
MessageBox(0, "read disk error!", 0, 0);
return 0;
}
ShowByteInform(bBuffer, uReadSize);
hFile =CreateFile("\\\\.\\D:\\MBR_BackUp.txt", GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); //将数据保存到D盘MBR_BackUp.txt
if (hFile == INVALID_HANDLE_VALUE)
{
MessageBox(0, "can't open the file!", 0, 0);
return 0;
}
WriteFile(hFile, (LPVOID)bBuffer, uReadSize, &dwReadByte, NULL);
if (dwReadByte == 0)
{
MessageBox(0, "write file error!", 0, 0);
return 0;
}
CloseHandle(hFile);
CloseHandle(hDisk);
while (1);
return 0;
}
2-2 GUID Partition Table(GPT)分区
GPT头格式
偏移量 | 大小/字节 | 说明 |
0 (0x00) | 8 | 标识"EFI PART" ,45h 46h 49h 20h 50h 41h 52h 54h |
8 (0x08) | 4 | 保留(00h 00h 01h 00h) |
12 (0x0C) | 4 | 头大小/字节(5Ch 00h 00h 00h ) |
16 (0x10) | 4 | GPT 头的CRC32校验 |
20 (0x14) | 4 | 保留,必须为0 |
24 (0x18) | 8 | 当前逻辑扇区号 |
32 (0x20) | 8 | 备份逻辑扇区号 |
40 (0x28) | 8 | First usable LBA for partitions (primary partition table last LBA + 1) |
48 (0x30) | 8 | Last usable LBA (secondary partition table first LBA - 1) |
56 (0x38) | 16 | Disk GUID (also referred as UUID on UNIXes) |
72 (0x48) | 8 | 分区表起始扇区号 |
80 (0x50) | 4 | Number of partition entries in array 队列中的分区数(最多128个分区) |
84 (0x54) | 4 | 单个分区表的大小(80H) |
88 (0x58) | 4 | 分区阵列的CRC32 |
92 (0x5C) | * | 保留,420字节(512字节/每扇区) |
3号扇区第2个分区表的起始扇区“0x040800”与用Winhex读取到的BPB参数中的隐藏扇区数一致,总扇区数=(最后扇区-起始扇区)+1。
Microsoft reserved patition: 对于小于 16 GB 的磁盘,MSR 分区为 32 MB。对于大于 16 GB 的磁盘,MSR 分区为 128 MB。
3 ExFAT文件系统卷分布
偏移量/扇区 | 大小/扇区 | 块 | 说明 | |||||
Main Boot Region 主引导区 | ||||||||
0 | 1 | Boot Sector | 引导扇区 | |||||
1 | 8 | Extended Boot Sectors | 扩展引导扇区 | |||||
9 | 1 | OEM Parameters | OEM参数 | |||||
10 | 1 | Reserved | 保留区,数据全为0 | |||||
11 | 1 | Boot Checksum | 引导校验和 | |||||
Backup Boot Region备份引导区 | ||||||||
12 | 1 | Boot Sector | 引导扇区 | |||||
13 | 8 | Extended Boot Sectors | 扩展引导扇区 | |||||
21 | 1 | OEM Parameters | OEM参数 | |||||
22 | 1 | Reserved | 保留区 | |||||
23 | 1 | Boot Checksum | 引导校验和 | |||||
FAT Region FAT表区 | ||||||||
FAT表起始扇区号 | 占1个簇的空间 | FAT 表项 | FAT表起始扇区号及FAT表扇区数见BPB参数 | |||||
Data Region数据区 | ||||||||
首簇起始扇区号(2号簇) | 占1个簇的空间 | 簇位图文件 | 见BPB参数 | |||||
3号簇:(3-2)*每簇扇区数+首簇起始扇区号 | 占1个簇的空间,数据占5836字节,剩余为保留区 | 大写字符文件 |
| |||||
4号簇:(4-2)*每簇扇区数+首簇起始扇区号 | 占1个簇的空间
| 根目录文件项 |
| |||||
3.1 Main Boot Region 主引导区
3.1.1 引导扇区(Boot Sector)
偏移量 | 大小 | 描述 | 内容 | ||||||||||||||||||
0x00 | 3 | 跳转指令 | EB 76 90 | ||||||||||||||||||
0x03 | 8 | 文件系统名称 | "EXFAT " | ||||||||||||||||||
0x0B | 53 | 保留区 | 全为0 | ||||||||||||||||||
0x40 | 8 | 隐藏扇区数 |
| ||||||||||||||||||
0x48 | 8 | 分区总扇区数 |
| ||||||||||||||||||
0x50 | 4 | FAT表起始扇区号 |
| ||||||||||||||||||
0x54 | 4 | FAT表扇区数 |
| ||||||||||||||||||
0x58 | 4 | 首簇起始扇区号 |
| ||||||||||||||||||
0x5C | 4 | 分区内的总簇数 | 最大值为2^32-11 | ||||||||||||||||||
0x60 | 4 | 根目录首簇号 | 0x04 只占1个簇 | ||||||||||||||||||
0x64 | 4 | 卷序列号 | 4个字节的随机数 | ||||||||||||||||||
0x68 | 2 | 卷版本号 | 固定为“00 01” | ||||||||||||||||||
0x6A | 2 | 卷状态 | 固定为“00 00” | ||||||||||||||||||
| |||||||||||||||||||||
0x6C | 1 | 每扇区字节数 | 2^N(最小值为9à 2^9=512字节/扇区 最大值为12à 2^12=4096字节/扇区) | ||||||||||||||||||
0x6D | 1 | 每簇扇区数 | 2^N(最小值为0à 2^0=1扇区/簇 最大值为25à 2^16=65536扇区/簇 = 32 MB) | ||||||||||||||||||
0x6E | 1 | FAT表个数 | 该值为1,只有对TexFAT才为2 | ||||||||||||||||||
0x6F | 1 | 驱动标记 | 这个是提供给INT13中断使用的,通常为80H | ||||||||||||||||||
0x70 | 1 | 分区使用百分比 | 0~100。百分之多少的簇已被分配。该值可不准确 | ||||||||||||||||||
0x71 | 7 | 保留 | 00 | ||||||||||||||||||
0x78 | 390 | 引导程序 | 固定值 | ||||||||||||||||||
0x1FE | 2 | 引导结束标志 | 0x55AA |
以480G的SATA盘为例,前面MBR数据中,第一个分区数据为:00 20 21 00 07 FE FF FF 00 08 00 00 00 20 E4 37。其隐藏扇区数均为0x0800,总扇区数为0x37E42000。用Winhex打开该磁盘可看到
隐藏扇区数与总扇区数以MBR中的一致。Winhex不能读取到MBR数据和隐藏扇区,而在FPGA中可以读取到,所以这里的起始地址0x00000000对应FPGA中的起始地址为0x0800(扇区)*512(字节/扇区)=0x100000。
参数名称 | 参数值 | 参数名称 | 参数值 |
隐藏扇区数 | 0x0800 | 分区内的总簇数 | Ox37E2 |
分区总扇区数 | 0x37E42000 | 根目录首簇号 | 0x04 |
FAT表起始扇区号 | 0x010000 | 每扇区字节数 | 2^9=512 |
FAT表扇区数 | 0x010000 | 每簇扇区数 | 2^16=65536 |
首簇起始扇区号 | 0x020000 | FAT表个数 | 1 |
3.1.2 扩展引导扇区(Extended Boot Sector)
偏移量 | 大小 | 描述 | 说明 |
0x00 | 512-4=508 | 扩展引导码 | 如果不用则全为00 |
0x1FC | 4 | 扩展引导结束标志 | 0x000055AA |
在1-8号扇区
3.1.3 OEM参数(OEM Parameters)
偏移量 | 大小 | 描述 | 说明 | ||||||||||||||||
0x00 | 48 | 参数[0] |
| ||||||||||||||||
... | ... | ... |
| ||||||||||||||||
0x1B0 | 48 | 参数[9] |
| ||||||||||||||||
| |||||||||||||||||||
0x01E0 | 32 | 保留 |
|
OEM参数在Windows上被忽略了,但是可以在OEM上执行。OEMs可以通过GUID定义自己的参数。在Windows上,该扇区数据全为0.
3.1.4 引导校验和(ExFAT Boot Checksum)
该扇区重复了32bit的对前11个扇区(0~10号扇区)数据的校验和,校验和的计算不包括引导扇区中的卷标志和使用百分比(第106、107、112字节)。校验和一直重复到该扇区结束。
校验和计算代码
UNIT32 BootChecksum(const unsigned chardata[], int bytes)
{
UINT32 checksum = 0;
for (inti = 0; i < bytes; i++)
{
if (i == 106 || i == 107 || i == 112)
continue;
checksum = (checksum<<31) | (checksum>> 1) + data[i];
}
returnchecksum;
}
Backup Boot Region备份引导区(12~23号扇区)与Main Boot Region 主引导区(0~11号扇区)数据完成一样。
4 FAT Region FAT表区
参数名称 | 参数值 | 参数名称 | 参数值 |
隐藏扇区数 | 0x0800 | 分区内的总簇数 | Ox37E2 |
分区总扇区数 | 0x37E42000 | 根目录首簇号 | 0x04 |
FAT表起始扇区号 | 0x010000 | 每扇区字节数 | 2^9=512 |
FAT表扇区数 | 0x010000 | 每簇扇区数 | 2^16=65536 |
首簇起始扇区号 | 0x020000 | FAT表个数 | 1 |
从BPB参数中可以得到FAT表的起始扇区号为0x010000 =65536。在Winhex跳转到65536号扇区可看到FAT表数据。
- ExFAT的每个FAT项由4字节构成,也就是32位的表项。
- 每个FAT项都有一个固定的编号,这个编号从0开始,也就是说,第一个FAT项是0号FAT项,第二个FAT项是1号FAT项,以此类推。
- 每个FAT项占用4字节:其中0号FAT项描述介质类型,其首字节为“F8”,表示介质类型为硬盘;1号FAT项写入4个“FF”;从2号FAT项开始对应2号簇,3号FAT项开始对应3号簇,一直到最后一个簇。目前2、3、4三个FAT项中都是结束标志,说明簇位图文件、大写字符文件、根目录各占一个簇。
- 分区的数据区中的每一个簇都会映射到FAT表中的唯一一个FAT项。因为0号FAT项和1号FAT项有特殊的用途,无法与数据区中的簇形成映射,所以数据区中的第一个簇也就编号为2号簇,这也是没有0号簇和1号簇的原因,然后3号簇与3号FAT项映射,4号簇与4号FAT项映射,以此类推。
- 分区格式化后,分区的两个元文件(簇位图文件和大写字符文件)及用户文件都以簇为单位存放在数据区中,一个文件至少占用一个簇。当一个文件占用多个簇时,这些簇的簇号可能是连续的,也可能是不连续的。如果文件存放的簇不连续,这些簇的簇号就以簇链的形式登记在FAT表中;而如果文件存放在连续的簇中,FAT表则不登记这些连续的簇链。
- ⑧综合上面的说明可以看出,ExFAT文件系统FAT表的功能主要是登记不连续存储的文件的簇链,所以在FAT中可以看到数值为0的FAT项,并不能说明该FAT项对应的簇是可用簇。
占用不连续簇的文件,fat表记录
(1)1.txt文件占5、7号簇,2.txt占6号簇,fat表如下:
(2)1.txt文件占5、7号簇,2.txt占6号簇,3.txt文件占8、0x0A号簇,4.txt占9号簇,fat表如下:
(3)1.txt文件占5、7号簇,2.txt占6、0x0B、0x0C号簇,3.txt文件占8、0x0A号簇,4.txt占9号簇,fat表如下:
(4)1.txt文件占5、7号簇,2.txt占6、0x0B、0x0C、0x0D号簇,3.txt文件占8、0x0A号簇,4.txt占9号簇,fat表如下:
仅占一个簇,或占连续簇的文件,对应fat项均为00
(5)1.txt文件占5、7号簇,2.txt占6、0x0B、0x0C、0x0D号簇,3.txt文件占8、0x0A号簇,4.txt占9号簇,5.txt占0x0E、0x0F、0x10、0x11 ,6.txt占0x12、0x14, 7.txt占0x13号簇fat表如下:
5 簇位图文件
数据区中的第一个簇就是2号簇,2号簇一般都分配给簇位图文件使用。
参数名称 | 参数值 | 参数名称 | 参数值 |
隐藏扇区数 | 0x0800 | 分区内的总簇数 | Ox37E2 |
分区总扇区数 | 0x37E42000 | 根目录首簇号 | 0x04 |
FAT表起始扇区号 | 0x010000 | 每扇区字节数 | 2^9=512 |
FAT表扇区数 | 0x010000 | 每簇扇区数 | 2^16=65536 |
首簇起始扇区号 | 0x020000 | FAT表个数 | 1 |
从BPB参数可以得到首簇起始扇区号0x020000 =131072,Winhex跳转到131072号扇区可看到簇位图。
该扇区只有一个字节“07H”,这就是簇位图文件的内容。
簇位图文件是ExFAT文件系统中的一个元文件,类似于NTFS文件系统中的元文件$BitMap,它的作用是用来管理分区中簇的使用情况。簇位图文件中的每一个位,映射到数据区中的每一个簇。如果某个簇分配给了文件,该簇在簇位图文件中对应的位就会被填入“1”,表示该簇已经占用;如果没有使用的空簇,它们在簇位图文件中对应的位就是“0”。
簇位图文件的内容为“07H”,换算成二进制等于“0000 0111”,这8位就对应数据区的8个簇,也就是从2号簇到9号簇这8个簇。从“0000 0111”这个数值中能够很明确的看出2、3、4这三个簇是被使用的,其它5个簇未被使用。而2、3、4这三个簇正是被簇位图文件、大写字符文件、根目录所占用的。
在簇位图文件的目录项中可以看到簇位图文件的起始簇号及文件的大小。
6 大写字符文件
大写字符文件是ExFAT文件系统中的第二个元文件,类似于NTFS文件系统中的元文件$UpCase。Unicode字母表中的每一个字符在这个文件中都有对应的条目,用于比较、排序、计算Hash值方面。
簇位图文件结束后的下一个簇一般分配给大写字符文件使用,也就是3号簇开始,扇区号:(3-2)*每簇扇区号+首簇起始扇区号 =65536+131072=196608号扇区。
参数名称 | 参数值 | 参数名称 | 参数值 |
隐藏扇区数 | 0x0800 | 分区内的总簇数 | Ox37E2 |
分区总扇区数 | 0x37E42000 | 根目录首簇号 | 0x04 |
FAT表起始扇区号 | 0x010000 | 每扇区字节数 | 2^9=512 |
FAT表扇区数 | 0x010000 | 每簇扇区数 | 2^16=65536 |
首簇起始扇区号 | 0x020000 | FAT表个数 | 1 |
大写字符文件第一个扇区的一部分,从图中可以看到其内容都是Unicode字母表中的字符,每一个字符占用两个字节。大写字符文件的大小固定为5836字节。
7 根目录文件项
目录项对于ExFAT文件系统来讲是非常重要的组成部分,其主要作用及结构特点如下:
①分区中的每个文件及文件夹(也称为目录)都被分配多个大小为32字节的目录项,用以描述文件及文件夹的属性、大小、起始簇号和时间、日期等信息,当然还会把文件名或目录名也记录在目录项中。
②在ExFAT文件系统中,目录也被视为特殊类型的文件,所以每个目录也与文件一样有目录项。
③在ExFAT文件系统下,分区根目录下的文件及文件夹的目录项存放在根目录区(4号簇)中,分区子目录下的文件及文件夹的目录项存放在数据区(从5号簇开始及后面的簇)相应的簇中。
④ExFAT文件系统目录项的第一个字节用来描述目录项的类型,剩下的31个字节用来记录文件的相关信息。
⑤根据目录项的作用和结构特点,可以把目录项分为四种类型:
◆卷标目录项;
◆簇位图文件的目录项;
◆大写字符文件的目录项;
◆用户文件的目录项。
根目录文件在第4号簇,起始扇区位置:(4-2)*每簇扇区号+首簇起始扇区号=2*65536+131072=262144,Winhex跳转到262144号扇区可以看到根目录文件。
7.1 卷标目录项
卷标就是一个分区的名称,可以在格式化分区时创建,也可以随时修改。ExFAT文件系统把卷标当做文件,用文件目录项进行管理。系统为卷标建一个目录项,放在根目录区中。
卷标的目录项占用32字节,其中第一个字节时特征值,用来描述类型。卷标目录项的特征值为“83H”,如果没有卷标或者将卷标删除,该特征值为“03H”。
卷标的长度理论上为11个字符。
卷标为“ExFAT sys”
卷标目录项中各字节的含义见表7-1。
(表7-1 ExFAT卷标目录项的含义)
字节偏移 | 字段长度(字节) | 内容及含义 |
0x00 | 1 | 目录项的类型(特征值为“83H”) |
0x01 | 1 | 卷标字符数 |
0x02 | 22 | 卷标 |
0x18 | 8 | 保留 |
卷标目录项有如下特点:
①对于ExFAT格式的分区,卷标的字符要求在11个之内。卷标使用unicode码字符。
②卷标的目录项中不记录起始簇号和大小。
③卷标的目录项中不记录时间戳。
7.2 簇位图文件的目录项
ExFAT文件系统格式化时会创建一个簇位图文件,并为其建一个目录项,放在根目录区中。
簇位图文件的目录项占用32个字节,其中第一个字节是特征值,用来描述类型。簇位图文件目录项的特征值为“81H”。
簇位图文件目录项中各字节的含义见表7--2。
(表7--2 ExFAT簇位图文件目录项的含义)
字节偏移 | 字段长度(字节) | 内容及含义 |
0x00 | 1 | 目录项的类型(特征值为“81H”) |
0x01 | 1 | 保留 |
0x02 | 18 | 保留 |
0x14 | 4 | 起始簇号 |
0x18 | 8 | 文件大小 |
簇位图文件的目录项有如下特点:
①对于ExFAT格式的分区,簇位图文件起始簇号一般为2。
②簇位图文件的目录项中不记录时间戳。
7.3 大写字符文件的目录项
ExFAT文件系统格式化时会创建一个大写字符文件,并为其建一个目录项,放在根目录区中。
大写字符文件的目录项占用32个字节,其中第一个字节是特征值,用来描述类型。大写字符文件目录项的特征值为“82H”。
大写字符文件目录项中各字节的含义见表7--3。
(表7--3 ExFAT大写字符文件目录项的含义)
字节偏移 | 字段长度(字节) | 内容及含义 |
0x00 | 1 | 目录项的类型(特征值为“82H”) |
0x01 | 3 | 保留 |
0x04 | 4 | 校验和 |
0x08 | 12 | 保留 |
0x14 | 4 | 起始簇号 |
0x18 | 8 | 文件大小 |
大写字符文件的目录项有如下特点:
①对于ExFAT格式的分区,大写字符文件的目录项一般都跟在簇位图文件的目录项之后。
②大写字符文件的目录项中不记录时间戳。
7.4 用户文件的目录项
ExFAT文件系统中每个用户文件至少有三个目录项,这三个目录项被称为三个属性:第一个目录项称为“属性1”,目录项首字节的特征值为“85H”;第二个目录项称为“属性2”,目录项首字节的特征值为“C0H”;第三个目录项称为“属性3”,目录项首字节的特征值为“C1H”。
7.4.1 属性1目录项
“属性1”目录项用来记录该目录项的附属目录项数、校验和、文件属性、时间戳等信息。用户文件的“属性1”目录项如图7--8所示。
用户文件的“属性1”目录项中各字节的含义见表7--4。
(表7--4 用户文件的“属性1”目录项的含义)
字节偏移 | 字段长度(字节) | 内容及含义 | ||
0x00 | 1 | 目录项的类型(特征值为“85H”) | ||
0x01 | 1 | 附属目录项数 | ||
0x02 | 2 | 校验和 | ||
0x04 | 2 | 文件属性 | 只读/0 | 0x01 |
隐藏/1 | 0x02 | |||
系统/2 | 0x04 | |||
保留1/3 |
| |||
子目录/4 | 0x10 | |||
存档/5 | 0x20 | |||
保留2/6~15 |
| |||
0x06 | 2 | 保留 | ||
0x08 | 4 | 文件创建时间 | ||
0x0C | 4 | 文件最后修改时间 | ||
0x10 | 4 | 文件最后访问时间 | ||
0x14 | 1 | 文件创建时间精确至10ms | ||
0x15 | 1 | 文件最后修改时间精确至10ms | ||
0x16 | 1 | 创建时间时区差,间隔15分钟 | ||
0x17 | 1 | 最后修改时间时区差,间隔15分钟 | ||
0x18 | 1 | 最后访问时间时区差,间隔15分钟 | ||
0x19 | 7 | 保留 |
时间格式:
比特 | 大小 | 描述 | 说明 |
0-4 | 5 | 秒。间隔为2秒 | 0~29。29表示58秒 |
5-10 | 6 | 分 | 0..59 |
11-15 | 5 | 时 | 0..23 |
16-20 | 5 | 日 | 1..31 |
21-24 | 4 | 月 | 1..12 |
25-31 | 7 | 年(偏移量从1980开始) | 0 代表 1980 |
十六进制时间:3E 77 72 38
格式 | 年 | 月 | 日 | 时 | 分 | 秒 |
二进制 | 0011111 | 0011 | 10111 | 01110 | 010001 | 11000 |
| 31+1980 | 3 | 23 | 14 | 17 | 24*2 |
时间 | 2011 | 3 | 23 | 14 | 17 | 48 |
目录项的校验和计算
* @brief 计算当前用户文件目录项(96字节)的Checksum值用于判断文件是否可用
* @param [in] octets,目录项缓冲
* @param [in] NumberOfBytes,所有字节数(包括其中的Checksum位置)
* @return 计算结果
*/
unsigned short EntrySetChecksum(unsigned char octets[], long NumberOfBytes)
{
unsigned short checksum = 0;
long index = 0;
for (index = 0; index < NumberOfBytes; index++)
{
if (index == 2 || index == 3)
{
continue;
}
checksum = ((checksum <<15) | (checksum>> 1)) + (unsigned short) octets[index];
}
return checksum;
}
7.4.2 属性2目录项
“属性2”目录项用来记录文件是否有碎片、文件名的字符数、文件名的Hash值、文件的起始簇号及大小等信息。
用户文件的“属性2”目录项中各字节的含义见表7--6。
(表7--6 用户文件的“属性2”目录项的含义)
字节偏移 | 长度/字节 | 内容及含义 |
0x00 | 1 | 目录项的类型(特征值为“C0H”) |
0x01 | 1 | 文件碎片标志。如果是连续存放没有碎片,该标志为“03H”;如果是不连续存放,文件有碎片,该标志就为“01H” |
0x02 | 1 | 保留 |
0x03 | 1 | 文件名字符数N。用unicode码表示,每个字符占用2个字节 |
0x04 | 2 | 文件名Hash值。当文件名发生改变时,Hash值也会发生改变。 |
0x06 | 2 | 保留 |
0x08 | 8 | 文件大小1。文件的总字节数 |
0x10 | 4 | 保留 |
0x14 | 4 | 起始簇号 |
0x18 | 8 | 文件大小2。一般情况下与“文件大小1”的数值保持一致 |
Hash值的计算
u16 NameHash(u8 *fileName, u8 nameLength)
{
u16 hash = 0,cc;
int i;
for (i = 0; i <nameLength; i++)
{
cc = char_upcase(*fileName++);
hash = (u16)(((hash << 15) | (hash >> 1)) + (cc & 0x00ff));
hash = (u16)(((hash << 15) | (hash >> 1)) + ((cc & 0xff00) >> 8));
}
return hash;
}
7.4.3 属性3目录项
“属性3”目录项用来具体记录文件的名称。如果文件名很长,“属性3”可以包含多个目录项,每个目录项称为一个片段,从上至下依次记录文件名的每一个字符。
文件名:“helloExfat.txt”
文件名:“0123456789abcdefghijklmnopqrstuvwxyz.txt”
用户文件的“属性3”目录项中各字节的含义见表7--7。
(表7--7 用户文件的“属性3”目录项的含义)
字节偏移 | 字段长度(字节) | 内容及含义 |
0x00 | 1 | 目录项的类型(特征值为“C1H”) |
0x01 | 1 | 保留 |
0x02 | 2N | 文件名 |
例如在根目录项创建一个名为文件“helloExfat.txt”的文件,文件中的内容为:“0123456789abcdefghijklmnopqrstuvwxyz”,根目录文件项的数据如图所示。
文件创建时间:0x4AEA7042
最后修改时间和最后访问时间:0x4AEA709D
文件碎片标志:0x03,没有碎片
文件名长度:0x0E ,14字节
文件大小:0x24 ,36字节
起始簇号:05 ,跳转到5号簇可看到文件中的内容。扇区号:(5-2)*65536+131072=327680
长文件名,C1续
8 ExFAT文件系统的根目录及子目录的管理
在根目录下创建一个文件夹“001”和一个文件“root01.txt”如下图所示
在“001”文件夹下再创建一个“002”文件夹和一个“00101.txt”文件
进入5号簇查看文件夹“001”中的内容
进入8号簇查看“001/00101”文件夹的内容
进入9号簇,查看“001/00101”文件夹下“0010101.txt”文件内容:
从以上可以看出文件夹的文件大小均为“0x02 00 00 00” = 32M ,为一个簇的大小。
此时的簇位图信息:
5~9号簇均被占用了。
文件创建总结:
- 创建文件目录项
- 向对应的簇号中写入shuj
- 将簇位图中相应的簇号置“1”
9 删除文件
根目录下的文件“root01.txt”,文件中内容为“this file is in root folder”,删除前文件目录项如下图所示。
将“root01.txt”文件删除后,文件目录项为:
进入到6号簇,查看:
发现数据并没有被清空。
10 Exfat文件系统元文件大小分配问题
簇位图大小=(卷大小-首簇扇区号)/每簇扇区数
Fat表大小=卷大小/每簇大小*4
FAT表初始化
在Windows下目录项的插入规则:
- 在目录项的最后一个簇中,以32字节为单位,查找首字节为“00”的位置添加目录项信息;
- 如果目录项文件的最后一个簇末尾剩余空间不足以存放一个新的完整的目录项信息,则该簇里面查找是否有删除文件“05H 40H 41H”,如果有,则覆盖;
- 如果以上两步都没有找到合适的位置,则找到下一个可用簇。如果上一个簇中有部分空余的空间,但不够,则将目录项信息填满空余空间后,将剩余的信息存到下一个簇中,前后两个簇的首尾连续起来。