FAT(File Allocation Table,文件分配表)文件系统是windows操作系统所使用的一种文件系统,它的发展过程经历了FAT12、FAT16、FAT32三个阶段。
FAT文件系统用“簇”作为数据单元。一个“簇”由一组连续的扇区组成,簇所含的扇区数必须是2的整数次幂。簇的最大值为64个扇区,即32KB。所有簇从2开始进行编号,每个簇都有一个自己的地址编号。用户文件和目录都存储在簇中。
FAT文件系统的数据结构中有两个重要的结构:文件分配表和目录项:
&文件和文件夹内容储存在簇中,如果一个文件或文件夹需要多余一个簇的空间,则用FAT表来描述如何找到另外的簇。FAT结构用于指出文件的下一个簇,同时也说明了簇的分配状态。FAT12、FAT16、FAT32这三种文件系统之间的主要区别在与FAT项的大小不同。
&FAT文件系统的每一个文件和文件夹都被分配到一个目录项,目录项中记录着文件名、大小、文件内容起始地址以及其他一些元数据。
在FAT文件系统中,文件系统的数据记录在“引导扇区中(DBR)”中。引导扇区位于整个文件系统的0号扇区,是文件系统隐藏区域(也称为保留区)的一部分,我们称其为DBR(DOS Boot Recorder——DOS引导记录)扇区,DBR中记录着文件系统的起始位置、大小、FAT表个数及大小等相关信息。
在FAT文件系统中,同时使用“扇区地址”和“簇地址”两种地址管理方式。这是因为只有存储用户数据的数据区使用簇进行管理(FAT12和FAT16的根目录除外),所有簇都位于数据区。其他文件系统管理数据区域是不以簇进行管理的,这部分区域使用扇区地址进行管理。文件系统的起始扇区为0号扇区。
如图所示:
说明:
【1】 保留区含有一个重要的数据结构——系统引导扇区(DBR)。FAT12、FAT16的保留区通常只有一个扇区,而FAT32的保留扇区要多一些,除0号扇区外,还有其他一些扇区,其中包括了DBR的备份扇区。
【2】 FAT区由来年各个大小相等的FAT表组成——FAT1、FAT2,FAT2紧跟在FAT1之后。
【3】 FAT12、FAT16的根目录虽然也属于数据区,但是他们并不由簇进行管理。也就是说FAT12、FAT16的根目录是没有簇号的,他们的2号簇从根目录之后开始。而FAT32的根目录通常位于2号簇。
FAT32文件系统的开始部分有一个由若干个扇区组成的保留区,保留区的刀削会记录在DBR扇区中,比较常见的为32、34或38个扇区。
引导扇区是FAT32文件系统的第一个扇区,也称为DBR扇区。它包含这样一些文件系统的基本信息:
【1】 每扇区字节数
【2】 每簇扇区数
【3】 保留扇区数
【4】 FAT表个数
【5】 文件系统大小(扇区数)
【6】 每个FAT表大小(扇区数)
【7】 根目录起始簇号
【8】 其他一些附加信息
(DBR扇区中记录文件系统参数的部分也称为BPB(BIOS Parameter Block))
说明: &引导扇区中的某些参数是至关重要的:【1】、【2】、【3】、【4】、【5】、【6】、【7】等。
我们可以通过每个FAT表的大小扇区数乘以FAT表的个数得到FAT区域的大小;通过保留扇区数和FAT区域的大小就可以得知数据区的起始位置,也就得到了文件系统第一簇的位置。由根目录的簇号和第一簇的位置就可以得到根目录的位 置。
FAT文件系统将引导代码与文件形同数据结构融合在一起,而不像Unix文件系统那样各自存在,引导扇区的前三个字节为一个由机器代码构成的跳转指令,以使CPU越过跟在后面的配置数据跳转到配置数据后面的引导代码处。
FAT32文件系统引导扇区的512字节中,90~509字节为引导代码,而FAT12/16则是62~509字节为引导代码。同时,FAT32还可以利用引导扇区后的山区空间存放附加的引导代码。
一个FAT卷即使不是可引导文件文件系统,也会存在引导代码。
FAT32在保留区中增加了一个FSINFO扇区,用以记录文件系统中空闲簇的数量以及下一可用簇的簇号等信息,以供操作系统作为参考。
1. FSINFO信息扇区结构
大多数的FSINFO信息扇区一般位于文件系统的1号扇区,结构非常简单。
字节偏移(十六进制) 字节数 含义
00~03 4 扩展引导标志“52526141”
04~1E3 480 未使用
1E4~1E7 4 FSINFO签名“72724161”
1E8~1EB 4 空闲簇数
1EC~1EF 4 下一可用簇号
1F0~1FD 14 未使用
1EE~1EF 2 “55 AA”标志
位于保留区后的是FAT区,有两个完全相同的FAT(File Allocation Table, 文件分配表)表组成,FAT文件系统的名字也是因此而来。
重要说明:
1. 对于文件系统来说,FAT表有两个重要作用:描述簇的分配状态以及标明文件或目录的下一簇的簇号。
2. 通常情况下,一个FAT把文件系统会有两个FAT表,但有时也允许只有一个FAT表,FAT表的具体个数记录在引导扇区的偏移0x10字节处。
3. 由于FAT区紧跟在文件系统保留区后,所以FAT1在文件系统中的位置可以通过引导记录中偏移0x0E~0x0F字节处的“保留扇区数”得到。
4. FAT2紧跟在FAT1之后,它的位置可以通过FAT1的位置加上FAT表的大小扇区数计算出来。
FAT表由一系列大小相等的FAT表项组成,总的说来FAT表有如下特性:
1 FAT32中每个簇的簇地址,是有32bit(4个字节)记录在FAT表中。FAT表中的所有字节位置以4字节为单位进行划分,并对所有划分后的位置由0进行地址编号。0号地址与1号地址被系统保留并存储特殊标志内容。从2号地址开始,每个地址对应于数据区的簇号,FAT表中的地址编号与数据区中的簇号相同。我们称FAT表中的这些地址为FAT表项,FAT表项中记录的值称为FAT表项值。
2. 当文件系统被创建,也就是进行格式化操作时,分配给FAT区域的空间将会被清空,在FAT1与FAT2的0号表项与1号表项写入特定值。由于创建文件系统的同时也会创建根目录,也就是为根目录分配了一个簇空间,通常为2号簇,所以2号簇所对应的2号FAT表项也会被写入一个结束标记。如下图所示:
3. 如果某个簇未被分配使用,它所对应的FAT表项内的FAT表项值即用0进行填充,表示该FAT表项所对应的簇未被分配。
4. 当某个簇已被分配使用时,则它对应的FAT表项内的FAT表项值也就是该文件的下一个存储位置的簇号。如果该文件结束于该簇,则在它的FAT表项中记录的是一个文件结束标记,对于FAT32而言,代表文件结束的FAT表项值为0x0FFFFFFF。
5. 如果某个簇存在坏扇区,则整个簇会用FAT表项值0xFFFFFF7标记为坏簇,不再使用,这个坏簇标记就记录在它所对应的FAT表项中。
6. 由于簇号起始于2号,所以FAT表项的0号表项与1号表项不与任何簇对应。FAT32的0号表项值总是“F8FFFF0F”。如上图所示。
7. 1号表项可能被用于记录脏标志,以说明文件系统没有被正常卸载或者磁盘表面存在错误。不过这个值并不重要。正常情况下1号表项的值为“FFFFFFFF”或“FFFFFF0F”。
8. 在文件系统中新建文件时,如果新建的文件只占用一个簇,为其分配的簇对应的FAT表项将会写入结束标记。如果新建的文件不只占用一个簇,则在其所占用的每个簇对应的FAT表项中写入为其分配的下一簇的簇号,在最后一个簇对应的FAT表象中写入结束标记。
9. 新建目录时,只为其分配一个簇的空间,对应的FAT表项中写入结束标记。当目录增大超出一个簇的大小时,将会在空闲空间中继续为其分配一个簇,并在FAT表中为其建立FAT表链以描述它所占用的簇情况。
10. 对文件或目录进行操作时,他们所对应的FAT表项将会被清空,设置为0以表示其所对应的簇处于未分配状态。
一个文件的起始簇号记录在它的目录项中,该文件的其他簇则用一个簇链结构记录在FAT表中。如果要寻找一个文件的下一簇,只需要查看该文件的目录项中描述的起始簇号所对应的FAT表项,如果该文件只有一个簇,则此处的值为一个结束标记;如果该文件不止一个簇,则此处的值是它的下一个簇的簇号。
数据区时真正用于存放用户数据的区域。数据区紧跟在FAT2之后,被划分成一个个的簇。所有的簇从2开始进行编号。也就是说,2号簇的起始位置就是数据区的起始位置。
虽然原则上FAT32允许根目录位于数据去的任何位置,但通常情况下它都位于2号簇。
1. 定位根目录
在FAT文件系统中,寻找第一簇(即2号簇)的位置也就是寻找数据区的开始位置,这并不是一件容易的事,因为它不是位于文件系统开始处,而是位于数据区。在数据区前面是保留区域和FAT区域,这两个区域都不使用FAT表进行管理。因此,数据区以前的区域只能使用扇区地址(逻辑卷地址),而无法使用簇地址。
要想定位一个FAT32文件系统的数据起始处,可以通过引导扇区的相关参数计算出来。
1.从引导扇区的偏移0x0E~0x0F字节处得到保留扇区。
2.从偏移0x10字节处得到FAT表的个数。
3.从偏移0x24~0x27字节处得到每个FAT表的大小扇区数。
4.利用如下公式计算:
保留扇区数 + 每个FAT表大小扇区数 × FAT表个数 = 数据区起始扇区号
要想计算其他已知簇号的扇区号,还要由引导扇区的偏移0x0D字节处查找到每个簇大小扇区数,并使用如下公式计算:
某簇起始扇区号 = 保留扇区数 + 每个FAT表大小扇区数 × FAT表个数 + (该簇簇号 - 2) × 每簇扇区数
2. 根目录分析
根目录在文件系统建立时即已被创建,其目的就是存储目录(也称文件夹)或文件的目录项。每个目录项的大小为32个字节。
文件系统刚被创建时,还没有存储任何数据时,根目录下没有任何内容,文件系统只是为根目录分配了一个簇的空间(通常为2号簇),将结束标记写入该簇对应的FAT表项,表示该簇已经被分配使用。这时候,为根目录分配的空间没有任何内容。但如果在创建文件系统的时候是定了卷标,则会在根目录下为其建立一个卷标目录项,该目录项占用根目录中的第一个目录项位置。
在FAT32文件系统中,除根目录在创建文件系统时即被建立并分配空间外,其他所有的子目录都是在使用过程中根据需要建立的。新建一个子目录时,在其父目录中为其建立目录项,在空闲空间中为其分配一个簇并对该簇进行清零操作,同时将这个簇号记录在它的目录项中。如果在根目录下创建一个子目录,我们就称这个子目录为根目录的子目录,称根目录为这个子目录的父目录。
创建子目录时,在为其父目录分配的簇中建立目录项,目录项中描述了这个目录的起始簇号。在为子目录建立目录项的同时,也在为子目录分配的簇中,使用前两个目录项描述它与父目录的关系。
子目录实在存储过程中根据需要由用户建立的,随着子目录建立的同时,会在为其分配的簇空间开始处建立两个目录项来描述子目录本身和其父目录的信息,以使字符目录间建立联系。
在FAT32文件系统中,根据结构不同可以讲目录项大致分为四种:卷标目录项、“.”目录项和“..”目录项、短文件名目录项、长文件名目录项。短文件名目录项是最重要的数据结构,其中存放着有关子目录或文件的短文件名、属性、起始簇号、时间值以及内容大小等信息。在FAT32文件系统中,将子目录看作是一种特殊的文件。
1. 短文件名目录项
短文件名目录项是FAT文件系统中非常重要的一种数据结构。之所以称其为短文件名目录项,是因为它所记录的文件名延续了DOS时代的8.3格式,即8个字符的名字加上3个字符的扩展名:
n 如果文件名不足8个字符,用0x20进行填充。
n 超过8个字符时则会被截短,因为短文件名目录项中没有足够的空间记录超出的部分。截短的方法是取文件名的前6个字符加上“~1”(如果有同名文件,则会依次递增该数值),然后加上其扩展名。
n 如果是子目录,则将扩展名部分用“0x20”进行填充。
为了解决长文件名的问题,FAT文件系统又增加了一种“长文件名”目录项结构。其从windows95开始,不管文件名的长度是否超过8个字符,都会同时为其创建短文件名目录项和长文件名目录项,因为短文件名不区分大小写,而长文件名则是区分大小写的。
2. 端文件名目录项的特性
每个文件或子目录都分配有一个大小为32字节的目录项,用以描述文件或目录的属性。
n 所有的目录项并不是具有相同的地址,要找到一个目录项的位置只能用分配给文件或子目录的全名进行是搜索。
n 目录项结构中有一个属性区域,每个文件可以设置7中属性。
n 每个文件或目录还有四个非关键性属性:
l 只读属性
l 隐藏属性
l 系统属性
l 存档属性
n 每个目录项包括三个时间值,即建立时间、最后访问时间、最后修改时间:
l 建立时间,精确到十分之一
l 最后访问时间,精确到日
l 最后修改时间,精确到2秒
n 一个目录项是否被分配使用它的第一个字节来描述。对于已经分配使用的目录项,它的第一个字节是文件名的第一个字符,而文件或目录被删除后,它所对应的目录项的第一个字节将被置为0xE5,这就是为什么有的FAT数据恢复工具需要用户自己输入文件名的第一个字符的原因。
3.短文件名目录项的数据结构
每个短文件名目录项占32个字节,数据结构如下图所示:
具体解释如下:
【1】0x00~0x00:1个字节,如果该目录项正在使用中,则为文件名或子目录名的第一个字符。
n 0x00:说明该目录项未被分配使用。
n 0xE5:说明该目录项曾经被使用过,但是现在已被删除。目前处于未分配状态
【2】0x01~0x0A:10个字节,文件名的第2至第11个ASCII码,除扩展名外,如果文件的名字不足8个字符则用0x20进行填充。
【3】0x0B~0x0B:1个字节,所描述文件的属性
n 0x01-只读
n 0x02-隐藏
n 0x04-系统文件
n 0x08-卷标
n 0x0F-为此值时表示该目录项为长文件名目录项
n 0x10-目录
n 0x20-存档
【4】0x0C~0x0C:1字节,保留
【5】0x0D~0x0D:1个字节,文件穿件的时间,精确到创建时刻的十分之一秒
【6】0x0E~0x0F:2个字节,文件创建的时间——时分秒
两个字节的16bit被划分为3个部分:
n 0~4bit为秒,以2秒为单位,有效值为0~29,可以表示的时刻为0~58
n 5~10bit为分,有效值为0~59
n 11~15bit为时,有效值为0~23
我们来总结一下在FAT文件系统下建立和删除一个文件的步骤,来说明FAT文件系统的工作过程。
假设现在有一个子目录,它的名字是“smart monkey”,我们要在其下建立一个文件“yatou.txt”。使用的文件系统为FAT32,簇大小为4096字节,我们要建立的文件大小为5000个字节。
步骤1:读取位于卷0号扇区的引导扇区,根据引导扇区中的信息定位FAT表、数据区和根目录的位置。
步骤2:遵照“smart monkey”的位置。查看根目录下的每个目录项,寻找名字为“smart monkey”且具有目录属性的目录项。找到后,查看它的起始簇号为3。
步骤3:读取smart monkey的起始簇(3号簇)的内容,查找每个目录项,直到找到一个为分配的目录项。
步骤4:找到可用项后写入文件名“yatou.txt”,并将文件大小和当前时间写入相应的位置。
步骤5:为文件内容分配簇空间。转到FAT表,寻找空闲的位置。发现4号FAT表项未使用,这就说明4号簇是空闲的。将4号簇分配给文件,并在4号簇的FAT表项内写入结束标记。
步骤6:将簇号4写入文件目录项的起始簇号区域。将文件的钱4096字节写入到4号簇中,还剩下904字节,所以还需要再为其分配一个簇。
步骤7:在FAT表中继续寻找为分配簇,找到5号簇为空闲未使用(因其FAT表项为0)。
步骤8:将文件第一簇(即4号簇)的FAT表项值改写为5,将文件的最后904字节写入5号簇。
步骤9:在5号簇的FAT表项内写入结束标记。
现在我们将前面例子中建立的“smart monkey\yatou.txt”文件删除。
步骤1:从卷0号扇区读取引导扇区,根据引导扇区中的信息定位FAT表、数据区和根目录的位置。
步骤2:在根目录下寻找名字为“smart monkey”且具有目录属性的目录项。
步骤3:由“smart monkey”的目录项中获取它的起始簇号为3,到3号簇查看“smart monkey”的内容,从中找到文件“yatou.txt”的目录项,提取出它的起始簇号,为4号簇。
步骤4:到FAT表中找到该文件的簇链,确定他飞存储位置为4号簇和5号簇。
步骤5:将4号簇和5号簇的FAT项设置为0.