有必要说明一下,以下对FAT16系统的介绍,很多都是参考文献。由于FAT16系统一般在U盘、MMC卡、SD卡以及一些小型存储设备上使用比较多,以后把这些小型存储设备统称为存储卡,这里仅局限于对存储卡的介绍,但很有针对性,而且比较全面。
参考来源:http://www.sjhf.net/pdf/fat.pdf
1.1 存储卡的结构
FAT16文件系统把存储卡的划分为几个区,包括DBR扇区、保留扇区、FAT表1、FAT表2、根目录和数据区,具体结构如图1-1所示,其实保留扇区包含MBR扇区,这里把DBR标志出来是为了强调它的重要性。
DBR扇区 |
保留扇区 |
FAT表1 |
FAT表2 |
根目录 |
数据区 |
图1-1 存储卡的结构图
1.2 DBR扇区:
DBR区(DOS BOOT RECORD)即操作系统引导记录区的意思,也称为引导扇区或启动扇区。它记录了存储卡的所有重要信息,一共512个字节,其结构如图1-2所示:
跳转指令JUMP |
OEM厂商标志 |
BPB和扩展BPB |
引导代码 |
结束标志0xAA55 |
图1-2 DBR结构
为了更好的理解DBR扇区,下面我们可以用WinHex打开一个存储卡,看一下DBR的结构,如图1-3所示。
在图1-3的最下面,可以明显的看到有“55 AA”的字样,这就是启动扇区的结束标志,它为16进制,大端格式,即把高字节放在低地址,高字节放在搞地址,读出来就是0xAA55。以下涉及的数据都是这样存储的。
根据图1-2和图1-3,表1-1给出了引导扇区各个字段的定义和说明。
表1-1 一个FAT16分区上的引导扇区段 | ||
字节位移 | 字段长度(字节) | 字段名称 |
0x00 | 3 | 跳转指令(Jump Instruction) |
0x03 | 8 | OEM ID |
0x0B | 25 | BPB |
0x24 | 26 | 扩展BPB |
0x3E | 448 | 引导程序代码(Bootstrap Code) |
0x01FE | 4 | 扇区结束标识符(0xAA55) |
2.1.2.1 跳转指令JUMP
图1-3所列出的偏移0x00-0x02的跳转指令“EB 3C 90“,清楚地指明了引导代码的偏移位置。“jump 3CH“加上跳转指令所需的位移量2,即开始于0x3E。此段指令在不同的操作系统上和不同的引导方式上,其内容也是不同的。
2.1.2.2 OEM厂商标志
跳转指令之后是8 字节长的OEM ID,它是一个字符串,OEM ID标识了格式化该分区的操作系统的名称和版本号。为了保留与MS-DOS的兼容性,通常Windows格式化该盘是在FAT16磁盘上的该字段中记录了“MSDOS5.0”,如图1-3所示。
2.1.2.3 BPB和扩展BPB
BPB(BIOS Parameter Block)参数块记录着本分区的起始扇区、结束扇区、文件存储格式、硬盘介质描述符、根目录大小、FAT个数、分配单元(也称之为簇)的大小等重要参数。如表1-2和表1-3所示(来自参考资料【2】)
表1-2 FAT16分区的BPB字段 | |||
字节位移 | 字段长度(字节) | 例值 | 名称、定义和描述 |
0x0B | 2 | 0x0200 | 扇区字节数(Bytes Per Sector) 硬件扇区的大小。本字段合法的十进制值有512、1024、2048和4096。对大多数磁盘来说,本字段的值为512 |
0x0D | 1 | 0x10 | 每簇扇区数(Sectors Per Cluster) 一个簇中的扇区数。由于FAT16文件系统只能跟踪有限个簇(最多为65536个)。因此,通过增加每簇的扇区数可以支持最大分区数。分区的缺省的簇的大小取决于该分区的大小。本字段合法的十进制值有 1、2、4、8、16、32、64和128。导致簇大于32KB(每扇区字节数*每簇扇区数)的值会引起磁盘错误和软件错误 |
0x0e | 2 | 0x0006 | 保留扇区数(Reserved Sector) 第一个FAT开始之前的扇区数,包括引导扇区。 |
0x10 | 1 | 0x02 | FAT数(Number of FAT)该分区上FAT的副本数。本字段的值一般为2 |
0x11 | 2 | 0x0200 | 根目录项数(Root Entries) 能够保存在该分区的根目录文件夹中的32个字节长的文件和文件夹名称项的总数。在一个典型的硬盘上,本字段的值为512。其中一个项常常被用作卷标号(Volume Label),长名称的文件和文件夹每个文件使用多个项。文件和文件夹项的最大数一般为511,但是如果使用的长文件名,往往都达不到这个数。 |
0x13 | 2 | 0x0000 | 小扇区数(Small Sector) 该分区上的扇区数,表示为16位(<65536)。对大于65536个扇区的分区来说,本字段的值为0,而使用大扇区数来取代它。 |
0x15 | 1 | 0xF8 | 媒体描述符( Media Descriptor)提供有关媒体被使用的信息。值0xF8表示硬盘,0xF0表示高密度的3.5寸软盘。媒体描述符要用于MS-DOS FAT16磁盘,在Windows 2000中未被使用 |
0x16 | 2 | 0x00F5 | 每FAT扇区数(Sectors Per FAT) 该分区上每个FAT所占用的扇区数。计算机利用这个数和FAT数以及隐藏扇区数来决定根目录在哪里开始。计算机还可以根据根目录中的项数(512)决定该分区的用户数据区从哪里开始 |
0x18 | 2 | 0x003F | 每道扇区数(Sectors Per Trark) |
0x1A | 2 | 0x00FF | 磁头数(Number of head) |
0x1C | 4 | 0x000000400 | 隐藏扇区数(Hidden Sector) 该分区上引导扇区之前的扇区数。在引导序列计算到根目录和数据区的绝对位移的过程中使用了该值 |
0x20 | 4 | 0x000F4C00 | 大扇区数(Large Sector) 如果小扇区数字段的值为0,本字段就包含该FAT16分区中的总扇区数。如果小扇区数字段的值不为0,那么本字段的值为0 |
表1-3 FAT16分区的扩展BPB字段 | |||
字节位移 | 字段长度(字节) | 图8对应取值 | 名称、定义和描述 |
0x24 | 1 | 0x80 | 物理驱动器号( Physical Drive Number) 与BIOS物理驱动器号有关。软盘驱动器被标识为0x00,物理硬盘被标识为0x80,而与物理磁盘驱动器无关。一般地,在发出一个INT13h BIOS调用之前设置该值,具体指定所访问的设备。只有当该设备是一个引导设备时,这个值才有意义 |
0x25 | 1 | 0x01 | 保留(Reserved) FAT16分区一般将本字段的值设置为1 |
0x26 | 1 | 0x29 | 扩展引导标签(Extended Boot Signature) 本字段必须要有能被Windows 2000所识别的值0x28或0x29 |
0x27 | 2 | 0xABA13358 | 卷序号(Volume Serial Number) 在格式化磁盘时所产生的一个随机序号,它有助于区分磁盘 |
0x2B | 11 | "NO NAME" | 卷标(Volume Label) 本字段只能使用一次,它被用来保存卷标号。现在,卷标被作为一个特殊文件保存在根目录中 |
0x36 | 8 | "FAT16" | 文件系统类型(File System Type) 根据该磁盘格式,该字段的值可以为FAT、FAT12或FAT16 |
2.1.2.4 引导代码和结束标志
在图1-3中,偏移0x3E开始的数据为操作系统引导代码。这是由偏移0x00开始的跳转指令所指向的,此段指令在不同的操作系统上和不同的引导方式上,其内容也是不同的,这里就不对引导代码进行探讨了,因为它涉及的很多东西,且本设计并没有使用到它。
扇区的最后两个字节存储值为0xAA55的DBR有效标志,对于其他的取值,系统将不会执行DBR相关指令。
2.1.2 关于保留扇区
操作系统之所以设置保留扇区,是为了对DBR作备份或留待以后升级时用。当DBR扇区被破坏导致分区无法访问时。可以用保留扇区的备份替换第0扇区来找回数据。在图1-3中,DBR偏移量0x0E位置的2个字节是0x0006,指明了保留的扇区数为6,即第一个FAT开始之前的扇区数,它包括引导扇区。
2.1.3 FAT表和数据的存储原则
FAT(File Allocation Table 即文件分配表)是Microsoft在FAT文件系统中用于磁盘数据(文件)索引和定位引进的一种链式结构。它有两个,为的是备份。假如把磁盘比作一本书,FAT表可以认为是相当于书中的目录,而文件就是各个章节的内容,但FAT表的表示方法与目录有些不同。
在FAT文件系统中,文件的存储依照FAT表制定的簇链式数据结构来进行。同时,FAT 文件系统将组织数据时使用的目录也抽象为文件,以简化对数据的管理。
图1-5所示给出了某个存储卡上FAT表的数据。
图1-5 FAT表
FAT 文件系统之所以有12,16,32 不同的版本之分,其根本在于FAT 表用来记录任意一簇链接的二进制位数。以FAT16为例,每一簇在FAT表中占据2个字节(二进制16位)。所以,FAT16最大可以表示的簇号为0xFFFF,以32K为簇的大小的话,FAT16可以管理的最大磁盘空间为:32KB×65535=2048MB,这就是为什么FAT16不支持超过2GB分区的原因。
FAT表实际上是一个数据表,以2个字节为单位,我们暂将这个单位称为FAT记录项,从图1-5中,可以知道,前两个记录项(即前四个字节)是0x7FFFFFF8,它是FAT表的标志,通常情况,第1、2个记录项用作介质描述。从第三个记录项开始记录除根目录外的其他文件及文件夹的簇链情况。根据簇的表现情况FAT用相应的取值来描述,见表1-4。
表1-4 FAT16 记录项的取值 | |
FAT16 记录项的取值 | 对应簇的表现情况 |
0x0000 | 未使用的簇 |
0x0002~0xFFEF | 已分配的簇 |
0xFFF0~0xFFF6 | 系统保留 |
0xFFF8~0xFFFF | 文件结束 |
FAT表记录项存放的数据是它下一个FAT表项的位置,我们来分析一下图1-5的FAT表,相对偏移量0x4~0x5的数据是0xFFFF,表示存储在这一簇上的文件(或目录)只占用1个簇的空间。偏移量0x6~0x7的数据是0x0004,指向了第4簇,也就是说这个文件还没有结束,它还有数据存放在第4簇,我们找到第4簇,也就是偏移量0x08~0x09的地方,数据是0x0005,这个文件的数据也没有结束,它指向了第5簇,我们这样一直找下去,直到找到偏移量为0x14~0x15的地方,数据位0xFFFF,表明这个文件到此就结束了,可以算的这个文件大小占8个簇。
2.1.4 根目录
FAT文件系统的目录结构其实是一颗有向的从根到叶的树,这里提到的有向是指对于FAT 分区内的任一文件(包括文件夹),均需从根目录寻址来找到。可以这样认为:目录存储结构的入口就是根目录。
FAT 文件系统根据根目录来寻址其他文件(包括文件夹),故而根目录的位置必须在磁盘存取数据之前得以确定。FAT文件系统就是根据分区的相关DBR参数与DBR中存放的已经计算好的FAT表(2 份)的大小来确定的。格式化以后,根目录的大小和位置其实都已经确定下来了——位置紧随FAT2之后。
FAT文件系统的一个重要思想是把目录(文件夹)当作一个特殊的文件来处理,在FAT16中,虽然根目录地位并不等同于普通的文件或者说是目录,但其组织形式和普通的目录(文件夹)并没有不同。FAT 分区中所有的文件夹(目录)文件,实际上可以看作是一个存放其他文件(文件夹)入口参数的数据表。其具体存储原理是:不管目录文件所占空间为多少簇,一簇为多少字节。系统都会以32个字节为单位进行目录文件所占簇的分配。这32个字节以确定的偏移来定义本目录下的一个文件(或文件夹)的属性,实际上是一个简单的二维表。这32个字节的各字节偏移定义如表1-5所示。
表1-5 目录项字段定义 | |||
偏移地址 | 字节数 | 说明 | |
0x00~0x07 | 8 | 文件名 | |
0x08~0x0A | 3 | 扩展名 | |
0x0B | 1 | 属 性 | 00000000(读写) 00000001(只读) 00000010(隐藏) 00000100(系统) 00001000(卷标) 00010000(目录) 00100000(归档) |
0x0C~0x15 | 10 | 保留 | |
0x16~0x17 | 2 | 文件最近修改时间 | |
0x18~0x19 | 2 | 文件最近修改日期 | |
0x1A~0x1B | 2 | 文件的首簇号 | |
0x1C~0x1F | 4 | 文件长度大小 |
以下对表1-5中的一些取值进行说明:
1、对于短文件名,系统将文件名分成两部分进行存储,即主文件名+扩展名。0x0~0x7 字节记录文件的主文件名,0x8~0xA记录文件的扩展名,取文件名中的ASCII码值。不记录主文件名与扩展名之间的"." 主文件名不足8 个字符以空白符(20H)填充,扩展名不足3 个字符同样以空白符(20H)填充。0x0 偏移处的取值若为00H,表明目录项为空;若为E5H,表明目录项曾被使用,但对应的文件或文件夹已被删除。(这也是误删除后恢复的理论依据)。文件名中的第一个字符若为“.”或“..”表示这个簇记录的是一个子目录的目录项。“.”代表当前目录;“..”代表上级目录(在dos中,使用命令“cd ..“就是返回上一级目录)。
2、偏移量0x16~0x17中的时间=小时*512+分钟*32+秒/2。偏移量0x18~0x19中的日期=(年份-1980)*512+月份*32+日。得出的结果换算成16 进制填入即可。
3、偏移量0x1A~0x1B存放文件或目录的表示文件的首簇号,系统根据掌握的首簇号在FAT表中找到入口,然后再跟踪簇链直至簇尾,同时用0x1C~0x1F处字节判定有效性。就可以完全无误的读取文件。
说明:由于篇幅过长,这里就不对长文件名和子目录的存储方式进行说明了。FAT16虚拟文件系统我已实现(已上传),创建的虚拟硬盘有启动扇区,保留扇区,fat表和更目录,可用WinHex工具(已上传)查看里面的内容,和一个真实硬盘没有区别,我还设计了数据恢复的功能。