1 FAT32文件系统介绍
参考文件
FAT32文件系统有几个基本的几个概念:
- MBR(Master Boot Record,主引导记录)
- DPT(Disk Partition Table,磁盘分区表)
- DBR(Dos boot record,Dos引导记录)
- FAT(File Allocation Table,文件分配表)
先拿一个真实的4G SD卡分析数据:
使用winhex物理方式查看,截一部分图:
1.1 主引导记录(MBR)
全称Master Boot Record,即主引导记录。
主引导记录分为两个部分,一个部分是前446字节,另外一个部分是后64字节。
1.1.1 前446字节
前446个字节,一般取值为0,不过有的时候也会有其他的设置值。
1.1.2 后64字节
这部分也叫DPT,全称Disk Partition Table,即磁盘分区表。
磁盘分区表有4个区,每个区有16字节,总体是64字节。因此,MBR字节数为512字节,刚好是一个扇区的数量。
DPT的4个部分,每一个部分的16字节含义如下表:
偏移 | 字节数 | 功能 |
---|---|---|
00H | 1字节 | 引导指示符,0x80表示可从此分区引导操作系统,通常取值为0。 |
01H | 1字节 | 开始磁头 |
02H | 2字节 | 前6位为开始扇区,后10位为开始柱面 |
04H | 1字节 | 系统id,用于定义分区类型 |
05H | 1字节 | 结束磁头 |
06H | 2字节 | 前6位为结束扇区,后10位为结束柱面 |
08H | 4字节 | 分区的开始扇区地址 |
12H | 4字节 | 分区的总扇区数 |
00 82 03 00 0B E6 DC CA 00 20 00 00 00 E0 ED 00(16进制)
这里,就贴出来上面截图的数据内容。(我们使用的是小字节序)
0x00 :
通常的取值。
0x82 :
开始磁头。
0x03 0x00 :
实际为0x0003,按照2进制编码为0000 0000 0000 0011。
那么开始扇区为:0。
开始柱面为:3。
0x0B :
分区类型,为Win95 FAT32类型。
0xE6 :
结束磁头为230。
0xDC 0xCA:
实际为0xCADC,按照2进制编码为1101 1100 1100 1010。
那么结束扇区为:55。
结束柱面为:220。
0x00 0x20 0x00 0x00 :
实际为:0x00002000,也就是8192,为分区的开始扇区地址。那么我们后面就可以从这里去找DBR。
0x00 0xE0 0xED 0x00 :
实际为:0x00EDE000,也就是15589376,为分区的总扇区数。
从这里其实可以算出SD卡的容量。
也就是:
分区大小:15589376x512 = 7981760512。这个值大约是7.612G。
从物理0地址到起始地址:8192*512 = 4194304
那么总容量为:7981760512+4194304 = 7985954816。
1.2 分区引导扇区(DBR)
该部分包括一个叫BPB的内容。
先截出DBR部分的实际SD卡上的内容:
在WinHex下使用逻辑磁盘打开FAT32文件系统的存储介质来看,偏移位置与表示的含义有如下表所示:
偏移 | 字节数 | 格式 |
---|---|---|
00H | 3字节 | EB 58 90 |
03H | 8字节 | 4D 53 44 4F 53 35 2E 30(MSDOS5.0) |
0BH | 25字节 | 如下表(BPB描述) |
25字节的分区块(BPB):
偏移 | 字节数 | 功能 | 举例 |
---|---|---|---|
0BH | 2字节 | 扇区字节数 | 0X0200,512字节 |
0DH | 1字节 | 每簇扇区数 | 0x08,即每簇包括8个扇区 |
0EH | 2字节 | 保留扇区数 | 0x0024,即保留36个扇区 |
10H | 1字节 | FAT表份数 | 0x02,即两个FAT表 |
11H | 2字节 | 保留 | 0x0000 |
13H | 2字节 | 保留 | 0x0000 |
15H | 1字节 | 介质类型 | 0xF8即本地硬盘 |
16H | 2字节 | 保留 | 0x0000 |
18H | 2字节 | 每磁道扇区数 | 0x003F,即每磁道63扇区 |
1AH | 2字节 | 磁头数 | 0x00FF,即255个磁头 |
1CH | 4字节 | 隐藏扇区数 | 0x1F80,即8064个隐藏扇区 |
20H | 4字节 | 磁盘总扇区数 | 0x0077F080,即总共7860352个扇区 |
24H | 52字节 | 扩展分区参数块(扩展BPB) | 细分如下表 |
扩展分区BPB:
偏移 | 字节数 | 功能 | 举例 |
---|---|---|---|
24H | 4字节 | FAT表占用扇区数 | 0x00001DEE,即FAT表占7662个扇区 |
28H | 4字节 | 未用 | 0x00000000 |
2CH | 4字节 | 根目录入口簇号 | 0x00000002,即根目录从02号簇开始 |
30H | 2字节 | 文件系统信息扇区号 | 0x0001,即扇区1 |
32H | 2字节 | 备份引导扇区的位置 | 0x0006,即6号扇区(第7个扇区) |
34H | 12字节 | 未用 | 00 00 00 00 00 00 00 00 00 00 00 00 |
40H | 1字节 | 物理磁盘号 | 00 |
41H | 1字节 | 未用 | 00 |
42H | 1字节 | 扩展引导标志 | 00 |
43H | 4字节 | 磁盘序列号 | 通常为一随机数 |
47H | 11字节 | 卷标ASCII | 4E 4F 20 4E 41 4D 45 20 20 20 20 即NO NAME |
52H | 8字节 | 文件系统格式ASCII | 46 41 54 33 32 20 20 20即FAT32 |
5AH | 420字节 | 分区引导代码 | 略 |
1FEH | 2字节 | 有效扇区结束标志 | 55 AA |
按照定义,将实际的图 MBR.png分析如下:
从偏移为0x0BH地址开始:
00 02 10 96 04 02 00 00 00 00 F8 00 00 3F 00 FF 00 00 20 00 00 00 E0 ED 00 B5 1D 00 00
00 02 :
即0x0200,表示扇区字节数,也就是512字节。
10:
即0x10,每簇扇区数为16。也就是16512=81024=8K。
96 04:
即0x0496,保留扇区数为1174。
02:
即0x02,FAT表份数为2。
00 00:
即0x0000,为保留。
00 00:
即0x0000,为保留。
F8:
即为本地硬盘。
00 00:
即0x0000,为保留。
3F 00:
即0x003F,即每磁道扇区数为63。
FF 00:
即0x00FF,即磁头数为255。
00 20 00 00:
即0x00002000,即隐藏扇区数为8192。
00 E0 ED 00:
即0x00EDE000,即磁盘总扇区数为15589376。
B5 1D 00 00 :
即0x001DB5,即FAT表占用扇区数为7605。
好了,根据这些内容,可以计算出两个参数:
FAT1的开始扇区 = 分区开始扇区+ 保留扇区=8192+1174 = 9366。
FAT2的开始扇区 = FAT1的开始扇区+FAT表占用的大小 = 9366 + 7605 = 16971。
首目录簇 = FAT1的开始扇区+FAT的表份数 x FAT表占用的大小=9366+2*7605 = 24576。
继续追踪,找到首目录簇中的已经新建的文件。
现在,先解析粘贴出来的每个部分的内容的含义。
具体内容下面的FAT中会说。
a.txt部分的解析:
41 20 20 20 20 20 20 20 54 58 54 20 18 8B 2A 90
96 4A A1 4A 00 00 0F 8C A1 4A 03 00 10 00 00 00
41 20 20 20 20 20 20 20:
表示文件名:“A TXT”
20 :
表示的是文件的属性。归档。
18:
为系统保留。
8B:
创建时间的10ms位。也就是1390ms。
2A 90:
也就是0x902A,换成2进制为1001 0000 0010 1010 。表示的是创建时间。
细分如下:
位数 | 含义 |
---|---|
5bit | 时 |
6bit | 分 |
5bit | 2秒 |
则时间值为:
时:0x12 = 18,
分:0x1 = 1,
秒:0xAx 2s + 1390ms= 10x 2s + (1390ms) = 21s,
所以创建时间为18:01:21。
96 4A:
也就是0x4A96,换成2进制为0100 1010 1001 0110,表示的是创建日期。
细分如下:
位数 | 含义 |
---|---|
7bit | 年 |
4bit | 月 |
5bit | 日 |
则日期值为:
年:1980+0x25 = 2017,
月:0x4 = 4,
日:0x16= 22,
所以,创建日期为2017年4月22日。
A1 4A:
也就是0x4AA1,换成2进制为0100 1010 1010 0001,表示的是最后访问日期。
则日期为:
年:1980+0x25 = 2017,
月:0x5 = 5,
日:0x1= 1,
所以,最后访问日期为2017年5月1日。
00 00:
起始簇高16位。
0F 8C:
也就是0x8C0F,换成2进制为1000 1100 0000 1111,表示的是最近修改时间。
时:0x11 = 17,
分:0x20 = 32,
秒:0xf x 2 = 30s,
所以最近修改时间为17:32:30。
A1 4A:
也就是0x4AA1,换成2进制为0100 1010 1010 0001,表示的是最近修改日期。
则日期为:
年:1980+0x25 = 2017,
月:0x5 = 5,
日:0x1= 1,
所以,最近修改日期为2017年5月1日。
03 00:
即0x0003,表示起始簇低16位,也就是说,起始簇高位与起始簇低位相或,得到0x00000003,起始簇为3。
10 00 00 00:
即0x00000010,表示文件数据长度,也就是16字节。
分析完a.txt,b.txt也一样分析即可。
从而可以得到的是,a.txt的内容位置在:(3-2)*16+24576 = 24592上。
后使用winhex进行验证,确认是正确的。
1.3 文件分配表(FAT)
文件分配表(FAT),是FAT文件系统中用于磁盘数据索引和定位而引进的一种链式结构。在FAT文件系统中,文件的存储依照FAT表制定的簇链式数据结构来进行。同时,FAT文件系统将组织数据时使用的目录也抽象为文件,以简化对数据的管理。
1.3.1 FAT1表位置的定位
如DBR所述,偏移0EH处存储了保留扇区的个数,而保留扇区数指的就是当前分区内DBR到FAT表之间的所有扇区的个数(包括DBR但不包括FAT表)。据此,可以定位FAT表所在的起始偏移位置了。
1.3.2 FAT2位置的定位
如DBR所述,偏移24H处存储了保留扇区的个数,知道FAT2是紧邻FAT1的。那么FAT2表的位置为:FAT1的起始偏移地址+FAT1的大小。
1.3.3 FAT表特性
FAT表由一系列大小相等的FAT表项组成,它有如下特性:
- FAT32中每个簇的簇地址,使用32bit(4个字节)记录在FAT表中。FAT表中的所有字节位置以4个字节为单位进行划分,并对所有划分后的位置由0进行地址编号。0号地址与1号地址被系统保留并存储特殊标志内容。从2号地址开始,每个地址对应于数据区的簇号,FAT表中的地址编号与数据区中的簇号相同。我们称FAT中的这些地址为FAT表项,FAT表项中记录的值称为FAT表项值。
当文件系统被创建,也就是进行格式化操作时,分配给FAT区域的空间将会被清空,在FAT1与FAT2的0号表项与1号表项写入特定值。由于创建文件系统的同时也会创建根目录,也就是为根目录分配了一个簇空间,通常为2号簇,所以2号簇所对应的2号FAT表项也会被写入一个结束标记。
如果某个簇未被分配使用,它所对应的FAT表项内的FAT表项值即用0进行填充,表示该FAT表项所对应的簇未分配使用。
当某个簇已被分配使用时,则它对应的FAT表项值也就是该文件的下一个存储位置的簇号。如果该文件结束于该簇,则在它的FAT表项中记录的是一个文件结束标记,对于FAT32而言,代表文件结束的FAT表项值为0x0FFFFFFF。
如果某个簇存在坏扇区,则整个簇会用FAT表项值0x0FFFFFF7标记为坏簇,不再使用,这个坏簇标记就记录在它所对应的FAT表项中。
由于簇号起始于2,所以FAT表的0号表项与1号表项不与任何簇对应。FAT32的0号表项值总是“F8FFFF0F”。1号表项可能被用于记录脏标志,以说明文件系统没有被正常卸载或者磁盘表面存在错误。不过此值似乎并不重要,因此我们只要了解就可以。正常情况下,1号表项值为“FFFFFFFF”或“FFFFFF0F"。
在文件系统中新建文件时,如果新建的文件只占用一个簇,为其分配的簇所对应的FAT表项将会被写入结束标记。如果新建的文件不只占用一个簇,则在其所占用的每个簇对应的FAT表项中写入为其分配的下一簇的簇号,在最后一个簇对应的FAT表项中写入结束标记。
新建目录时,只为其分配一个簇的空间,对应的FAT表项中写入结束标记。当目录增大超出一个簇的大小时,将会在空闲空间中继续为其分配一个簇,并在FAT表中为其建立FAT表链以描述它所占用的簇情况。
对文件或目录进行删除操作时,它们所对应的FAT表项将会被清空,设置为0以表示其所对应的簇处于未分配状态。
1.4 根目录区
在FAT32文件系统中,根目录的位置不再硬性地固定,可以存储在分区内可寻址的任意簇内,不过通常根目录是最早建立的(格式化就生成了)目录表。所以,我们看到的情况基本上都是根目录首簇紧邻FAT2,占簇区顺序上的第1个簇(即2号簇)。同时,FAT32文件系统将根目录当做普通的数据文件来看,所有没有了目录项数的限制,在需要的时候可以分配空簇,存储更多的目录项。
1.4.1 起始偏移地址定位
- 根目录起始扇区=保留扇区数+FAT×2+(起始簇-2)x每簇的扇区数
目录区的一个目录项占用32个字节,可以是长文件名目录项、文件目录项、子目录项等。
对于短文件名格式的目录项:
字节偏移(16进制) | 字节数 | 定义 |
---|---|---|
00H~07H | 8字节 | 文件名 |
08H~0AH | 3字节 | 扩展名 |
0BH* | 1字节 | 属性字节(如下表) |
0CH | 1字节 | 系统保留 |
0DH | 1字节 | 创建时间的10毫秒位 |
0EH~0FH | 2字节 | 文件创建时间 |
10H~11H | 2字节 | 文件创建日期 |
12H~13H | 2字节 | 文件最后访问日期 |
14H~15H | 2字节 | 文件起始簇号的高16位 |
16H~17H | 2字节 | 文件最近的修改时间 |
18H~19H | 2字节 | 文件最近的修改日期 |
1AH~1BH | 2字节 | 文件起始簇号的低16位 |
1CH~1FH | 4字节 | 文件的长度 |
注意:*表示此字段在段文件目录项中,不可取值0FH。若取值为0FH,目录段为长文件名目录段。
0BH字段取值如下:
字段 | 取值 | 定义 |
---|---|---|
0BH | 00000000 | 读写 |
0BH | 00000001 | 只读 |
0BH | 00000010 | 隐藏 |
0BH | 00000100 | 系统 |
0BH | 00001000 | 卷标 |
0BH | 00010000 | 子目录 |
0BH | 00100000 | 归档 |
FAT32的一个重要的特点是完全支持长文件名。长文件名依然是记录在目录项中的。为了低版本的OS或程序能正确读取长文件名文件,系统自动为所有长文件名文件创建了一个对应的短文件名,使对应数据既可以用长文件名寻址,也可以用短文件名寻址。不支持长文件名的OS或程序会忽略它认为不合法的长文件名字段,而支持长文件名的OS或程序则会以长文件名为显式项来记录和编辑,并隐藏起短文件名。
- 当创建一个长文件名文件时,系统会自动加上对应的短文件名,其原则如下:
(1)取长文件名的前6个字符加上"~1"形成短文件名,扩展名不变。
(2)如果已存在这个文件名,则符号"~"后的数字递增,直到5。
长文件名的实现有赖于目录项第12字节属性字节,当此字节的值为0FH时,支持长文件名的系统会将其当做长文件名的依据,而只支持短文件名的系统会认为是异常而忽略掉。系统将长文件名以13个字符为单位进行切割,每一组占据一个目录项。所以可能一个文件需要多个目录项,这时长文件名的各个目录项按倒序排列在目录表中,以防与其他文件名混淆。
长文件名中的字符采用unicode形式编码,每个字符占据2字节的空间。其目录项定义如:
字节偏移(16进制) | 字节数 | 定义 |
---|---|---|
00H | 1字节 | 属性字节位意义,参考下表 |
01H~0AH | 10字节 | 长文件名unicode码 |
0BH | 1字节 | 长文件名目录项标志,取值0FH |
0CH | 1字节 | 系统保留 |
0DH | 1字节 | 校验值(根据短文件名计算得出) |
0EH~19H | 12字节 | 长文件名unicode码 |
1AH~1BH | 2字节 | 文件起始簇号(目前常置0) |
1CH~1FH | 4字节 | 长文件名unicode码 |