FAT32 文件系统
在FAT16中,采用了16bit宽的簇地址, 32bit宽扇区地址, 但由于规定每簇最大的容量不超过1024 * 32. 所以FAT16文件系统的容量也就限制到了2^16 * 1024 * 32,大约2.1GB的空量,并且实际还达不到这个值。
FAT32文件系统使用了32bit宽的簇地址,所以称为FAT32。但在微软件的文件系统中只使用了低28位,最大容量为2^28 * 1024 * 32,约8.7TB的空量.
最大单文件大小: 4 GB (Fat16分区是2 GB )
最大文件数量: 268,435,437
一个FAT文件系统包括四个不同的部分。
图片1
图片2
SD卡为例:
图片3
MBR:Master boot record
主引导记录,占446字节, 为计算机启动后从可启动介质上首先装入内存并且执行的代码。
在FAT文件系统中,同时使用“扇区地址”和“簇地址”两种地址管理方式。这是因为只有存储用户数据的数据区使用簇进行管理(FAT12和FAT16的根目录除外),所有簇都位于数据区。其他文件系统管理数据区域是不以簇进行管理的,这部分区域使用扇区地址进行管理。文件系统的起始扇区为0号扇区。
FAT32文件系统的DBR有5部分组成,分别为跳转指令,OEM代号,BPB,引导程序和结束标志。如下图是一个完整的FAT32文件系统的DBR。
图片4
MBR:
偏移(字节) 长度(字节) 说明
0x00 3 跳转指令(跳过开头一段区域)
0x03 8 OEM名称常见值是MSDOS5.0.
0x0b 2 每个扇区的字节数。
取值只能是以下几种:512,1024,2048或是4096。设为512会取得最好的兼容性
0x0d 1 每簇扇区数。 其值必须中2的整数次方,同时还要保证每簇的字节数不能超过32K
0x0e 2 保留扇区数(包括启动扇区)此域不能为0,FAT12/FAT16必须为1,FAT32的典型值取为32
0x10 1 文件分配表数目。 NumFATS,任何FAT格式都建议为2
0x11 2 最大根目录条目个数, 0 for fat32, 512 for fat16
0x13 2 总扇区数(如果是0,就使用偏移0x20处的4字节值)0 for fat32
0x15 1 介质描述 0xF8 单面、每面80磁道、每磁道9扇区
0xF9 双面、每面80磁道、每磁道9扇区
0xFA 单面、每面80磁道、每磁道8扇区
0xFB 双面、每面80磁道、每磁道8扇区
0xFC 单面、每面40磁道、每磁道9扇区
0xFD 双面、每面40磁道、每磁道9扇区
0xFE 单面、每面40磁道、每磁道8扇区
0xFF 双面、每面40磁道、每磁道8扇区
无沦此域写入什么数值,同时也必须在FAT[0]的低字节写入相同的值,这是因为早期的MSDOS 1.x使 用该字节来判定是何种存储介质
0x16 2 每个文件分配表的扇区(FAT16),0 for fat32
0x18 2 每磁道的扇区, 0x003f
0x1a 2 磁头数,0xff
0x1c 4 隐藏扇区, 与MBR中地址0x1C6开始的4个字节数值相等
0x20 4 总扇区数(如果超过65535使用此地址,小于65536参见偏移0x13,对FAT32,此域必须是非0)
0x24 4每个FAT表占用扇区数。(FAT32特有)
0x24 1 物理驱动器个数(FAT16),由操作系统决定
0x25 1 当前磁头(FAT16),格式化FAT卷时必须设为0
0x26 1 签名(FAT16),扩展引导标记(0x29)用于指明此后的3个域可用
0x27 4 ID (FAT16)
0x28 2 Flags (FAT32特有)
Bits0-3:不小于0的FAT(active FAT)数目,只有在镜像(mirrorig)禁止时才有效。
Bits 4-6: 保留
Bits 7: 0,FAT实时镜像到所有的FAT表中,1 ,只有一个活动的FAT表。这个表就是Bits0-3所指定的
Bits8-15:保留
0x2a 2 版本号 (FAT32特有)
0x2c 4 根目录起始簇 (FAT32),一般为2
0x2b 11 卷标(非FAT32)
0x30 2 FSInfo 扇区 (FAT32) bootstrap
0x32 2 启动扇区备份 (FAT32)
如果不为0,表示在保留区中引导记录的备数据所占的扇区数,通常为6同时不建议使用6以外的其他数值
0x34 2 保留未使用 (FAT32) 此域用0填充
0x36 8 FAT文件系统类型(如FAT、FAT12、FAT16)含"FAT"就是PBR,否则就是MBR
0x3e 2 操作系统自引导代码
0x40 1 BIOS设备代号 (FAT32)
0x41 1 未使用 (FAT32)
0x42 1 标记 (FAT32)
0x43 4 卷序号 (FAT32)
0x47 11 卷标(FAT32)
0x52 8 FAT文件系统类型(FAT32)
0x1be 64 partitions table, DOS_PART_TBL_OFFSET
0X1BE ~0X1CD 16 talbe entry for Partition 1
0X1CE ~0X1DD 16 talbe entry for Partition 2
0X1DE ~0X1ED 16 talbe entry for Partition 3
0X1EE ~0X1FD 16 talbe entry for Partition 4
talbe entry for Partition:
Offse length content
0 1 boot indicator(80h=active)
1 3 start chs , (Cylinder, Head, Sector )
4 1 type descriptor(0x0b is 32bit fat,0x83 is ext2/3/4 )
5 3 end chs
8 4 start sector
12 4 partition size(unit is sectors)
0x1FE 2 扇区结束符(0x55 0xAA) 结束标志:MBR的结束标志与DBR,EBR的结束标志相同。
BPB(BIOS Parameter Block)表,描述逻辑盘结构组成,
包含隐藏扇区数目、FAT扇区数、FAT拷贝数、硬盘磁头总数、根目录表项最大值等。
FAT32文件系统的扩展BPB区位于DBR内,从地址0x00B~0x052都是BPB的范围。
0X5A~0X1FD字节为引导代码. 对于没有安装操作系统的分区来说这段程序是没有用处的。
Decoding CHS Values:
图片5
ref: https://thestarman.pcministry.com/asm/mbr/PartTables.htm
example:
图片6
则LBA = 877 * 254 * 63 -1; LBA是从0开始的。
引导扇区DBR DOS boot record
DOS引导记录,为操作系统进入文件系统以后可以访问的第一个扇区, 512字节,通常只用前48个字节,与MBR格式相同。MBR中0x1c6~0x1c9所指的内容就是DBR的起始地址.
FAT32文件系统在DBR的保留扇区中安排了一个文件系统信息扇区,用以记录数据区中空闲簇的数量及下一个空闲簇的簇号,该扇区一般在分区的1号扇区,也就是紧跟着DBR后的一个扇区
图片7
1,DBR与BPB 实例如下表:
图片8
【1】0x00~0x02:3字节,“EB5890”,跳转指令。
“EB 58”,就是代表汇编语言中的“JMP 58”。 一条空的指令NOP(90H)
【2】0x03~0x0A:8字节,文件系统标志和版本号,这里为MSDOS5.0。
【3】0x0B~0x0C:2字节,每扇区字节数,512(0X02 00)。
【4】0x0D~0x0D:1字节,每簇扇区数,8(0x08),这个值不能为0,而且必须是2的整数次方,比如1、2、4、8、16、32、64、128。规定每簇最大的容量不超过1024* 32,1024* 32/512=64
【5】0x0E~0x0F:2字节,保留扇区数,38(0x00 26),那么就知道FAT1起始位置在38扇区。
【6】0x10~0x10:1字节,FAT表个数为2,另外一个是备份的。
【7】0x11~0x12:2字节,FAT32必须等于0,FAT12/FAT16为根目录中目录的个数;
【8】0x13~0x14:2字节,FAT32必须等于0,FAT12/FAT16为扇区总数。
【9】0x15~0x15:1字节,哪种存储介质,0xF8标准值,可移动存储介质。
【10】0x16~0x17:2字节,FAT32必须为0,FAT12/FAT16为一个FAT表所占的扇区数。
【11】0x18~0x19:2字节,每磁道扇区数,只对于有“特殊形状”(由磁头和柱面每 分割为若干磁道)的存储介质有效,63(0x003F)。
【12】0x1A~0x1B:2字节,磁头数,只对特殊的介质才有效,255(0x00 FF)。
【13】0x1C~0x1F:4字节,EBR分区之前所隐藏的扇区数,0(0x00 00 00 00)。
【14】0x20~0x23:4字节,此文件系统分区的总扇区数,3887104(0x 00 3B 50 00),3887104 * 512 = 1990197248 ≈ 1.9GB。
【15】0x24~0x27:4字节,每个FAT表占用扇区数,3789(0x 00 00 0E CD)。
【16】0x28~0x29:2字节,标记,此域FAT32 特有。
【17】0x2A~0x2B:2字节,FAT32版本号0.0,FAT32特有。
【18】0x2C~0x2F:4字节,根目录所在第一个簇的簇号,2。(虽然在FAT32文件系统 下,根目录可以存放在数据区的任何位置,但是通常情况下还是起始于2号簇)。
【19】0x30~0x31:2字节,FSINFO(文件系统信息扇区)扇区号是1,该扇区为操作系统提供关于空簇总数及下一可用簇的信息。
【20】0x32~0x33:2字节,备份引导扇区的位置。
【21】0x34~0x3F:12字节,用于以后FAT 扩展使用。
【22】0x40~0x40:1字节,与FAT12/16 的定义相同,只不过两者位于启动扇区不同的位置而已。
【23】0x41~0x41:1字节,与FAT12/16 的定义相同,只不过两者位于启动扇区不同的位置而已 。
【24】0x42~0x42:1字节,扩展引导标志,0x29。与FAT12/16 的定义相同,只不过两者位于启动扇区不同的位置而已
【25】0x43~0x46:4字节,卷序列号。通常为一个随机值。
【26】0x47~0x51:11字节,卷标(ASCII码),如果建立文件系统的时候指定了卷标,会保存在此。
【27】0x52~0x59:8字节,文件系统格式的ASCII码,FAT32。
2, FSINFO分区
FSINFO信息扇区一般位于文件系统的1号扇区,结构非常简单
【1】0x200~0x203: 4个字节,扩展引导标志“0x52526141”。
【2】0x204~0x3E3:480个字节,未使用,全部置0。
【3】0x3E4~0x3E7: 4个字节,FSINFO签名“0x72724161”。
【4】0x3E8~0x3EB: 4个字节,文件系统的空簇数,484934(0x00 07 66 46)。
【5】0x3EC~0x3EF: 4个字节,下一可用簇号(0x 00 00 00 04)。
【6】0x3F0~0x3FD: 14个字节,未使用。
【7】0x3FE~0x3FF: 2个字节,“55 AA”标志。
3, 文件分区FAT表
文件系统分配磁盘空间按簇来分配。因此,文件占有磁盘空间时,基本单位不是字节而是簇,即使某个文件只有一个字节,操作系统也会给它分配一个最小单元:即一个簇。对于大文件,需要分配多个簇。同一个文件的数据并不一定完整地存放在磁盘中一个连续地区域内,而往往会分若干段,像链子一样存放。这种存储方式称为文件的链式存储。为了实现文件的链式存储,文件系统必须准确地记录哪些簇已经被文件占用,还必须为每个已经占用的簇指明存储后继的下一个簇的簇号,对于文件的最后一簇,则要指明本簇无后继簇。这些都是由FAT表来保存的,FAT 表对应表项中记录着它所代表的簇的有关信息:诸如是空,是不是坏簇,是否是已经是某个文件的尾簇等。
FAT表中的表项也是以4字节为单位进行划分,对FAT表由0进行逻辑编号。0号地址与1号地址被系统保留并存储特殊标志内容。从2号地址开始,每个地址对应于数据区的簇号,FAT表中的地址编号与数据区中的簇号一一对应。我们称FAT表中的这些单元为FAT表项,FAT表项中记录的值称为FAT表项值。
FAT32中把簇是以32bit(4个字节)进行编码,而这种编码是逻辑编码,与物理地址也有一定的对应关系。FAT表紧接着的是有效数据区,从此区开始从2进行簇标号。从2开始,以4KB (8*512)为跨度进行簇编号。
当文件系统被创建,也就是进行格式化操作时,分配给FAT区域的空间将会被清空,在FAT1与FAT2的0号表项与1号表项写入特定值。由于创建文件系统的同时也会创建根目录,也就是为根目录分配了一个簇空间,通常为2号簇,与之对应的2号FAT表项被写入一个结束标记。
说明:
① 由于簇号起始于2号,所以FAT表项的0号表项与1号表项不与任何簇对应。
FAT32的0号表项值总是“F8FFFF0F”。 0号FAT为介质类型,1号FAT为文件系统错误标志
② 1号表项可能被用于记录脏标志,以说明文件系统没有被正常卸载或者磁盘表面存在错误。不过这个值并不重要。正常情况下1号表项的值为“FFFFFFFF”或“FFFFFF0F”。
② 如果某个簇未被分配使用,它对应的FAT表项内容为0;
③ 当某个簇已被分配使用,则它对应的FAT表项内的FAT表项值也就是该文件的下一个存储位置的簇号。如果该文件结束于该簇,则在它的FAT表项中记录的是一个文件结束标记,
对于FAT32而言,代表文件结束的FAT表项值为0x0FFFFFFF。
③ 如果某个簇存在坏扇区,则整个簇会用0xFFFFFF7标记为坏簇,这个坏簇标记就记录在它所对应的FAT表项中。
④ 在文件系统中新建文件时,如果新建的文件只占用一个簇,为其分配的簇对应的FAT表项将会写入结束标记。如果新建的文件不只占用一个簇,则在其所占用的每个簇对应的FAT表项中写入为其分配的下一簇的簇号,在最后一个簇对应的FAT表象中写入结束标记。
⑤ 新建目录时,只为其分配一个簇的空间,对应的FAT表项中写入结束标记。当目录增大超出一个簇的大小时,将会在空闲空间中继续为其分配一个簇,并在FAT表中为其建立FAT表链以描述它所占用的簇情况。
定位FAT绝对位置的方法如下:
1、首先从MBR的分区表中得知分区的起始扇区[0x1c6],偏移到此扇区。
2、从DBR的BPB中得知DBR的保留扇区数,FAT表的个数,FAT表的大小。
3、因此FAT1=分区起始扇区+DBR保留扇区,FAT2=分区起始扇区+DBR保留扇区+FAT1。
假设MBR[0x16~0x19]=0x00000010; 则DBR的起始地址为0x10 * 512=0x2000;
假设DBR[0x0e]=0x1234,即[0x200e]=0x1234;则FAT1的起始扇区=0X10+0X1234
图片10
FAT表占据扇区数
= 簇总数 * 4 Bytes/ 扇区占据字节数
= (数据区扇区数/每簇占据扇区数)* 4 / 512
= ( 3879488 / 8 ) * 4 / 512
= 3788.5625
FAT表占据扇区数取整数为3789 Sector 即 0x24单元的0x0ecd。
数据区扇区数:(不含FAT)
总扇区数-保留扇区数-FAT表所占扇区数 参考图片8
3879488 =3887104(0x 00 3B 50 00)-3789 * 2-38(0x00 26)
下面介绍如何为cluster_size选择一个合适的值,及FAT表占据扇区数的确定方法:
有效扇区数(lFatDataSectors) =总扇区数(lTotalSectors_-保留扇区数(reserveSectors)
#define SECTOR_SIZE 512
#define FAT_NUM 2
#define MAX_CLUST_32 ((1 << 28) - 16)
unsigned long lFAT_sectors; //fat32表占的扇区数
mbs.cluster_size = 8;//2^n,的一个随机值,但要小于128
do{
unsigned long lCluster32= (lFatDataSectors * SECTOR_SIZE- FAT_NUM*8)/(mbr.cluster_size* SECTOR_SIZE+ FAT_NUM*4);
unsigned long lFAT_bytes= (lCluster32+2)*4; //保留2个簇,每个簇在FAT中占有4个字节
lFAT_sectors = (lFAT_bytes+ SECTOR_SIZE-1)/ SECTOR_SIZE;
//重新验证
lCluster32= (lFatDataSectors - FAT_NUM * lFAT_sectors)/mbs.cluster_size;
unsigned long lMaxClust32 = (lFAT_sectors * SECTOR_SIZE) / 4;
if (lMaxClust32 > MAX_CLUST_32)
lMaxClust32 = MAX_CLUST_32;
if (lCluster32> lMaxClust32)
lCluster32= 0;
if (lCluster32)
break;
mbr.cluster_size <<=1;
} while(mbs.cluster_size <= 128)
4,数据区
数据区时真正用于存放用户数据的区域。数据区紧跟在FAT2之后,被划分成一个个的簇。所有的簇从2开始进行编号,也就是说,2号簇的起始位置就是数据区的起始位置。
数据区起始扇区号 =分区的起始扇区+ 保留扇区数 + 每个FAT表大小扇区数(offset:0x24~0x27) × FAT表个数
=38+3789 * 2
根目录起始扇区=数据区起始扇区号+(根目录起始簇-2)x每簇的扇区数
=数据区的起始扇区+(簇大小 * 2)
数据区的位置在FAT2的后面,具体定位方式如下;
1、通过MBR中的分区表信息得知分区的起始位置。
2、通过分区中DBR得知DBR的保留扇区数以及FAT表的大小,FAT表的个数。
3、通过上面的信息就可以找到数据区的起始位置,根目录=数据区的起始扇区+(簇大小 * 2)。
1、目录项定义
目录所在的扇区,都是以32 Bytes划分为一个单位,每个单位称为一个目录项(Directory Entry),即每个目录项的长度都是32 Bytes 。根目录由若干个目录项组成,一个目录项占用32个字节,可以是长文件名目录项、文件目录项、子目录项等。
<1> 短文件目录项的具体定义
图片11
☆ 目录项的第一个字节为“0xE5”,表示该项已被删除。
☆ 目录项的第一个字节为0x2E(“.”),表示当前目录的信息(目录的数据空间也会保存当前目录的目录项信息)
☆ 目录项的前两个字节为“0x2E 0x2E(“. .”),表示上一级目录。
☆ 目录项的第一个字节为"0x00",代表从此位置开始以后的数据空间都没有使用。
☆ 0x1c-0x1f 文件的长度以字节为单位
图片12
图片13
注意:
对于目录对应的短目录项,没有文件长度参数,也就是说0x1C-0x1F等于0。那么对于目录,又如何知道它占用空间的大小呢,又如何遍历目录呢?
目录中的目录项是按照从前到后紧密排放,即使文件或者目录删除,也不会删除它们对应的目录项(删除目录项意味着目录项清零),只是将目录项标记为删除(0xE5)。这样目录的数据空间直到遇到第一个空目录项之前,所有的目录项都是非空的。我们可以根据目录项是否非空判断是否到达了目录的目录项尾部。
至于目录的大小,其实已经没有意义了。我们获取目录的大小就是想判断目录的结束位置,而现在第一个空目录项就是目录的结束位置,所以已经没有必要知道目录的大小。当然, 通过遍历目录可以确定目录占用空间的大小,不过没有意义。
<2> 长文件目录项的具体定义
长文件名其实就是将文件名进行分段,分成若干个短文件名进行存储
图片14
再详细的列出上表中地址0x0处各位段的含义
图片15
☆ 长文件名文件目录项的unicode编码:无论是英文字符,还是中文字符都占用2个字节。
☆ 目录项的第一个字节为“0xE5”,表示该项已被删除。
☆ 一个长文件它在目录中的记录可能有几个目录项组成,包含若干个长目录项和一个短目录项
☆ 0xB位段的取值如果是0FH代表是长目录项,反之就是短目录项。
☆ 1个长文件目录项:能记录13个字符,对应26个字节
☆ 顺序是从1开始编号直到13,倘若到了结尾的地方,除了要按照规则的编号还需要将0x0地址的第6位置1。
长文件名在进行分段存储的时候,遵循一下三个处理原则:
1、系统取长文件名的前6个字符加上“~1”形成短文件名,其扩展名不变;0
2、如果已存在这个名字的文件,则符号“~”后的数字自动增加;
3、如果文件名内存在windows非法的字符,则以下划线“_”代替;
4、长文件名目录项采用倒叙的方式进行记录,如下图:
图片16
目录项举例
<1> 短目录项举例
格式化SD卡,然后在其中创建一个文件readme.txt和Test子目录,WinHex打开根目录内容如下:
图片17
首先看子目录Test的对应目录项:
【1】0x3B8020 - 0x3B8027 目录项名为Test
【2】0x3B802B 10H(0001 0000B),表明为子目录
【3】[0x3B8034 - 0x3B8035,0x3B803A - 0x3B803B] 00 03H,表明Test子目录位于3号簇的位置,在FAT表中为3号表项
【4】0x3B803C - 0x3B803F 0000H,大小为0
readme.txt的对应目录项:
【1】0x3B8040 - 0x3B8047 目录项名为readme
【2】0x3B8048 - 0x3B804A 目录项扩展名为TXT
【3】0x3B804B 20H(0010 0000B),表明为归档文件
【3】[0x3B8054 - 0x3B8055,0x3B805A - 0x3B805B] 00 04H,表明readme.txt位于4号簇的位置,在FAT表中为4号表项
【4】0x3B805C - 0x3B805F 23A7H,大小为9127 Bytes
<2> 长目录项举例
格式化SD卡,然后在其中创建一个文件abcdefghigklmnopqrstuvwxyz.txt,WinHex打开根目录内容如下:
图片18
先看看卷标
【1】0x3B8000 - 0x3B8007 目录项名为Huang
【2】0x3B800B 08H(0000 1000B),表明为"卷标"
再看看长文件目录项
【1】003B8020 0x43 表明此长文件包含了3个长目录项,并且当前目录项是它的最后一个长目录项
【2】003B8040 0x02 表明这是长文件的第2个目录项
【3】003B8041 - 003B804A 长文件目录项的unicode的第一部分, 006EH 006FH 0070H 0071H 0072H 代表了nopqr五个字符
【4】003B804B 0x0F 表明是一个长目录项
【5】003B804D 0x27 校验和
【6】003B804E -003B8059 长文件目录项的unicode的第二部分, 0073H 0074H 0075H 0076H 0077H 0078H 代表了stuvwx六个字符
【7】003B805A - 003B805B 起始簇号,目前常置0
【8】003B805C - 003B805F 长文件目录项的unicode的第三部分, 0079H 007AH 代表了yz两个字符
为了记录abcdefghigklmnopqrstuvwxyz.txt,使用了3个长目录项和1个短目录项。至于位于003B8080处的短目录项,就不在说明了。
FAT32文件系统总结
上述的内容已经简单的介绍了FAT32文件系统,下面根据定位某个文件来详细的了解FAT32文件系统是如何存储数据的。
1、根据磁盘0号扇区MBR的分区表得知分区的起始位置,既DBR;
2、根据DBR中BPB记录的信息,得知DBR保留扇区数,FAT的大小,FAT的个数;
3、根据上述信息可以算出数据的起始位置,数据区=分区起始扇区+DBR保留扇区+(FAT表 * 2);
4、计算根目录所在的绝对位置,根目录=数据区的起始扇区+(簇大小 * 2);
5、根据根目录中的目录项信息得知,根目录下的文件以及子目录等所对应的簇;
6、根据文件的簇号就可以找到文件内容的绝对扇区;
7、如果一个文件占用多个簇,则需要根据FAT表项得知下一个数据簇的簇号。
7、如果根目录下的目录项是子目录的话,则根据子目录中的文件目录项得知文件内容的簇号;
8、如果子目录中还有子目录的话,则根据这种方法一直找下去即可。