【FatFs】FAT32文件系统协议原理讲解(以U盘为例)

 1、前言

        本篇文章主要是介绍FAT格式分区数据在硬盘中是如何存储的,FAT分区格式是Microsoft最早支持的分区格式,依据FAT 表中每个簇链的所占位数(有关概念,后面会讲到)分为FAT12FAT16FAT32 三种格式"变种",但其基本存储方式是相似的。我们在嵌入式系统中使用的最多是FAT32,所以后面主要介绍的是FAT32。

2、FAT文件系统结构

        FAT文件系统结构图如下所示:

2.1 主引导扇区(MBR)

        主引导扇区:硬盘的第一个扇区,由主引导记录(MBR)+ 主分区表(DPT)+ 引导扇区标记(Boot Record ID / Signature)组成,该区域是完成系统BIOS向操作系统交接的重要入口。该扇区在进行硬盘分区时产生。

        主引导扇区组成

        其中的MBR具体内容我们暂时不需要关心,重点要关心硬盘分区表DPT的数据,即上图中红色划线部分(重点关注蓝色和绿色方框数据),其各个字节表示的作用如下:

        对于常见硬盘来说,我们主要关注相对扇区数和总扇区数的值,结合原始数据和分区表含义可以得到重点数据:

1、相对扇区数:保留区1 N扇区 = 0x800 = 2048扇区,也可以得到DBR的地址 = 2048*512 = 0x10 0000 Bytes。

 2、总扇区数:对于只有一个分区的U盘来说,该数据表示分区卷的总容量(不包含相对扇区数)

2.2 操作系统引导扇区(DBR)

        操作系统引导扇区DBR,也称为引导扇区。包含BIOS参数块,该参数块存储有关卷布局和文件系统结构的信息,以及加载windows的引导代码(暂时不需要关心引导代码)。

        下面的示例演示了FAT32卷上引导扇区的十六进制数据:

下面两个表说明了FAT32卷的BPB和拓展BPB的布局,示例值对应FAT32卷上引导扇区的数据。

FAT32分区的BPB字段
字节位移

字段长度

(字节)

对应取值名称和定义
0x0B20x0200扇区字节数(Bytes Per Sector) 硬件扇区的大小。
本字段合法的十进制值有 512、1024、2048 和 4096。对大多数磁盘来说,本字段的值为
512
0x0D10x10每簇扇区数(Sectors Per Cluster),一簇中的扇区数。
一个卷默认的簇大小取决于该卷的大小。本字段的合法十进制值有 1、2、4、8、16、32、64 和 128。
0x0E20x0C80

保留扇区数(Reserved Sector) 第一个 FAT 开始之前的扇区数,包括引导扇区。

(相当于保留区2)

0x1010x02FAT 数(Number of FAT) 该分区上 FAT 表的个数。本字段的值一般为 2
0x1120x0000根目录项数(Root Entries) 只有 FAT12/FAT16 使用此字段。对 FAT32 分区而言,本字段必须设置为 0
0x1320x0000小扇区数(Small Sector) (只有 FAT12/FAT16 使用此字段)对 FAT32 分区而言,本字段必须设置为 0
0x1510xF8媒体描述符( Media Descriptor) 提供有关媒体被使用的信息。值 0xF8 表示硬盘,0xF0 表示高密度的 3.5寸软盘。
0x1620x0000每 FAT 扇区数(Sectors Per FAT) 只被 FAT12/FAT16 所使用,对 FAT32 分区而言,本字段必须设置为 0
0x1820x003F每道扇区数(Sectors Per Track) 包含使用 INT 13h 的磁盘的“每道扇区数”几何结构值。
该分区被多个磁头的柱面分成了多个磁道
0x1A20x00FF磁头数(Number of Head) 本字段包含使用 INT 13h 的磁盘的“磁头数”几何结构值。
0x1C40x00000800

隐藏扇区数(Hidden Sector) 该分区上引导扇区之前的扇区数。在引导序列计算到根目录的数据区的绝对位移的
过程中使用了该值。本字段一般只对那些在中断 13h 上可见的媒体有意义。在没有分区的媒体上它必须总是为 0

(相当于保留区1)

0x2040x01CE7800总扇区数(Large Sector) 本字段表示FAT32分区(不包含保留区1)中总的扇区数
0x2440x000039C0每 FAT 扇区数(Sectors Per FAT) (只被 FAT32 使用)该分区每个 FAT 所占的扇区数。计算机利用这个数和 FAT
数以及保留扇区数(本表中所描述的)来决定根目录从哪里开始。该计算机还可以从目录中的项数决定该分区的
用户数据区从哪里开始
0x2820x0000

扩展标志(Extended Flag) (只被 FAT32 使用)该两个字节结构中各位的值为:
位 0-3:活动 FAT 数(从 0 开始计数,而不是 1).只有在不使用镜像时才有效
位 4-6:保留

位 7:0值意味着在运行时FAT被映射到所有的FAT,1值表示只有一个 FAT是活动的
位 8-15:保留

0x2A20x0000文件系统版本(File ystem Version) 只供 FAT32 使用,高字节是主要的修订号,而低字节是次要的修订号。
本字段支持将来对该 FAT32 媒体类型进行扩展。
0x2C40x00000002根目录簇号(Root Cluster Number) (只供 FAT32 使用) 根目录第一簇在FAT表的簇号。
本字段的值一般为 2,但不总是如此
0x3020x0001

 文件系统信息扇区号(File System Information Sector Number) (只供 FAT32 使用)FAT32 分区的保留区中的文件系统信息(File System Information, FSINFO)结构的扇区号。其值一般为 1。在备份引导扇区(Backup
Boot Sector)中保留了该 FSINFO 结构的一个副本,但是这个副本不保持更新

0x3220x0006备份引导扇区(Backup Boot Sector) 非“0”表示存储引导扇区副本的扇区号。该字段的值通常为6。
0x341212 个字节均为 0x00保留(Reserved) (只供 FAT32 使用)供以后扩充使用的保留空间。本字段的值总为 0
FAT32分区的拓展BPB字段
字节位移

字段长度

(字节)

对应取值名称和定义
0x4010x80物理驱动器号( Physical Drive Number) 与 BIOS 物理驱动器号有关。软盘驱动器被标识为 0x00,物理硬盘被标识为 0x80,而与物理磁盘驱动器无关。一般地,在发出一个 INT13hBIOS 调用之前设置该值,具体指定所访问的设备。只有当该设备是一个引导设备时,这个值才有意义
0x4110x00保留(Reserved) FAT32 分区总是将本字段的值设置为 0
0x4210x29扩展引导标签(Extended Boot Signature) 本字段必须要有能被 Windows 2000 所识别的值 0x28 或 0x29
0x4340xB2DC3889分区序号(Volume Serial Number) 在格式化磁盘时所产生的一个随机序号,它有助于区分磁盘
0x4711"NO NAME    "(ASCII编码)卷标(Volume Label) 本字段只能使用一次,它被用来保存卷标号。现在,卷标被作为一个特殊文件保存在根目录中
0x528"FAT32   "(ASCII编码)系统 ID(System ID) FAT32文件系统中一般取为"FAT32"

BPB对我们比较重要的数据已经用蓝色标记出来了,可以了解到:

  • 扇区字节数和每簇扇区数

        硬件扇区的大小为512Bytes,每个簇占用扇区数为16个扇区。

  • 总扇区数

        该数据只表示分区卷所占用的扇区数,即 保留区2 + 文件分区表 + 数据区,不包含保留区1,所以要计算U盘总容量需要加上隐藏扇区(即保留区1)的大小,即:

      总容量=总扇区数+隐藏扇区:( 0x01CE7800+0x800 ) * 512 = 15,518,924,800 Bytes。

  • FAT开始地址

       我们可以通过保留扇区数+ 隐藏扇区找到FAT表的起始位置:(0xC80+0x800 ) * 512 = 0x290000 Bytes。

  • 根目录开始地址

        根目录开始地址 = (隐藏扇区 + 保留扇区 +  每FAT扇区数 * FAT数 )* 扇区字节数

                                  =  ( 0x800 + 0xC80 + 0x39C0 * 2)* 512 Bytes = 0x1100000 Bytes

2.3 FSINFO信息扇区

        FAT32在保留区2中中增加了一个FSINFO信息扇区,用以记录文件系统中空闲簇的数量以及下一个可用簇的簇号等信息,以供操作系统作为参考。FSINFO信息扇区一般位于文件系统的1号扇区。(此处是逻辑上,而非物理上的1号扇区!!),

内容划分如下:

 2.4 FAT32根目录(Root Folder)

        为了更好的理解FAT表,我们需要先讲一下根目录。

        根文件夹描述根分区中的文件和文件夹。目录所在的扇区,都是以32Byte为一个单位划分的,每个单位称为一个目录项。根目录由若干个目录项组成。目录项有两种格式:短文件格式长文件格式

2.4.1 短文件格式

        1、对于短文件名,系统将文件名分成两部分进行存储,即主文件名+扩展名。0x0~0x7字节记录文件的主文件名,0x8~0xA记录文件的扩展名,取文件名中的ASCII码值。不记录主文件名与扩展名之间的"."主文件名不足 8 个字符以空白符(20H)填充,扩展名不足 3 个字符同样以空白符(20H)填充。0x00 偏移处的取值若为 00H,表明目录项为空;若为E5H,表明目录项曾被使用,但对应的文件或文件夹已被删除。(这也是误删除后恢复的理论依据)。文件名中的第一个字符若为“.”“..”表示这个簇记录的是一个子目录的目录项。“.”代表当前目录“..”代表上级目录

        2、0xB的属性字段:可以看作系统将 0xB的一个字节分成 8 位,用其中的一位代表某种属性的有或无。如 00000101 就表示这是个文件,属性是只读、系统。

        3、0x0E~0x0F文件创建时间字段

        Bit 0~4:为文件创建时间字段秒/2的值;
        Bit 5~10:为文件创建时间字段分钟的值;
        Bit 11~15:为文件创建时间字段小时的值;

        4、0x10~0x11文件创建日期字段

        Bit 0~4:为文件创建日期字段日期数的值;
        Bit 5~8:为文件创建日期字段月份的值;
        Bit 9~15:为文件创建时间字段年号-1980的值

        5、普通子目录的寻址过程也是通过其父目录中的目录项来指定的,与数据文件(指非目录文件)不同的是目录项偏移 0xB的第 4 位置 1,而数据文件0

        具体FAT32短文件格式表格如下:

2.4.2 长文件格式

        FAT32 的一个重要的特点是完全支持长文件名。长文件名依然是记录在目录项中的。为了低版本的 OS 或程序能正确读取长文件名文件,系统自动为所有长文件名文件创建了一个对应的短文件名,使 对应数据既可以用长文件名寻址,也可以用短文件名寻址。不支持长文件名的 OS 或程序会忽略它认为不合法的长文件名字段,而支持长文件名的 OS 或程序则会以长文件名为显式项来记录和编辑,并隐藏起短文件名。
        当创建一个长文件名文件时,系统会自动加上对应的短文件名,其一般有的原则:

  1. 取长文件名的前 6 个字符加上"~1"形成短文件名,扩展名不变。
  2. 如果已存在这个文件名,则符号"~"后的数字递增,直到 5
  3. 如果文件名中~后面的数字达到 5,则短文件名只使用长文件名的前两个字母。通过数学操纵长文件名的剩余字母生成短文件名的后四个字母,然后加后缀~1直到最后。
  4. 如果存在老 OS 或程序无法读取的字符,换以"_"。

        长文件名的实现有赖于目录项偏移为 0xB 的属性字节,当此字节的属性为:只读、隐藏、系统、卷标,即其值为 0FH 时,DOS 和 WIN32 会认为其不合法而忽略其存在。系统将长文件名以 13 个字符为单位进行切割,每一组占据一个目录项。所以可能一个文件需要多个目录项,这时长文件名的 各个目录项按倒序排列在目录表中,以防与其他文件名混淆。长文件名中的字符采用 unicode 形式编码,每个字符占据 2 字节的空间。

 注意:系统在存储长文件名时,总是先按倒序填充长文件名目录项,然后紧跟其对应的短文件名。从以上的表格可以看出,长文件名中并不存储对应文件的文件开始簇、文件大小、各种时间和日期属性。文件的这些属性还是存放在短文件名目录项中,一个长文件名总是和其相应的短文件名一 一对应,短文件名没有了长文件名还可以读,但长文件名如果没有对应的短文件名,不管什么系统都将忽略其存在。所以短文件名是至关重要的。

 2.4.3 实例解析

        由于根目录和子目录在目录项上有一点区别,所以在此处对其分开讲解。

根目录

        我们对U盘进行格式化后,在根目录下新建文件结构。     

        在winhex查看:

        可以看到,两者有一点区别,在winhex多了一部分文件夹和文件:新建文件夹和新建文本文档。不必担心,这是由于电脑在创建如”001.txt“文件时,会先创建”新建文件文本文档.txt“,然后如果在你修改文件名时,操作系统会把原来的文件删除,然后才创建新的文件

        下面通过实例:文件夹:System Volume Information来解析里面的数据,如下:

        通过上面实例表格可了解到重要信息总结如下:

        文件名称和属性

                文件名System Volume Information

                属性目录、系统、隐藏

        文件创建时间

                文件创建时间为0x73B8,按照前面介绍的公式解析时间为14:29:48,解析过程如下:

        文件创建日期

                文件创建日期为0x5908,按照前面介绍的公式解析时间为2024/08/08,解析过程如下:

        文件起始簇号

                文件起始簇号的值为0x00000003,说明该文件的起始地址为:

                文件地址 =  根目录地址 + (文件起始簇号 -根目录簇号)* 每簇扇区数 * 扇区字节数

                                = 0x1100000 + (3 - 2)*16 * 512 = 0x 1102000 Byte

子目录

        根目录文件夹和子目录文件夹在目录项组成上有一点区别,所以需要单独拿出来讲一讲,我们继续以前面讲的System Volume Information为例,我们可以看到该文件夹中目录项组成如下:

         可以明显的看到,相比于根目录,子目录中多了当前目录和上一级目录的目录项,其他组成基本一致。我们可以通过各目录项的起始簇号跳转到对应文件位置。

        注意:如果上一级目录是根目录时,其文件起始簇有点特殊,值为0x00000000。我们以test_dir_111111111文件夹里面新建文件夹hello文件夹和hello.txt为例:

        我们可以明显看到,该示例中文件夹簇号链:根目录(0x00000000)-》test_dir_111111111(0x00000006)-》hello文件夹(0x0000001C)

文件

        最后我们来讲一讲文件目录项,以根目录下的001.txt为例:

        可以看到文件的起始簇号为0x00000008,按照之前的计算公式可以得到文件地址为0x110C000,跳转到该地址可以看到文件内容:

 

 2.5 FAT表

        FAT表记录了磁盘数据文件的存储链表,对于数据的读取而言是极其重要的,以至于Microsoft为其开发的FAT文件系统中的FAT表创建了一份备份,就是我们看到的FAT2。FAT2 与FAT1 的内容通常是即时同步的,也就是说如果通过正常的系统读写对FAT1 做了更改,那么FAT2 也同样被更新。

        FAT表工作的原理:当存储一个大的文件时,会以为单位拆分成多个簇存放在磁盘,它们的存放的簇号并不一定是连续的,所以这里用到了FAT表,将该文件所用到的簇号串联起来。

        例如截图如下,绿色的部分为存储文件的数据,FAT表查询顺序如下:

        1、从根目录(Root folder)找到该文件的起始簇号 12,并且读取簇号 12地址的文件数据。

        2、从FAT表中,找到簇号12存放的下一个簇号为13,并且读取簇号 13地址的文件数据。以此类推到簇号65。

        3、从FAT表中,找到簇号65存放的下一个簇号为87,并且读取簇号 87地址的文件数据。下一次FAT表查询直接从簇号65跳转到簇号 87

        4、经过上面的连续查询FAT,直到读取FF标记,代表文件读取完成。

        我们通过之前获取到的地址0x290000查看FAT表数据,如下:

        

        内容解析:

        接下来我们以 根目录文件:stm32l4xx_hal_spi.c为例来介绍FAT表的使用:

        文件起始簇号:

                文件起始簇号为0x0000000C,说明该文件的起始地址为:

                文件地址 =  根目录地址 + (文件起始簇号 -根目录簇号)* 每簇扇区数 * 扇区字节数

                                = 0x1100000 + (12 - 2)*16 * 512 = 0x 1114000 Byte

                可以看到在该位置处果然找到了文件内容。

       文件大小:

                文件大小为0x24D95(150,933),在winhex中可以查看到文件大小确实是

 注意:系统读取磁盘的单位以簇为单位,存放数据不满一簇时也将占用这整个簇(其他文件也无法使用该簇多余的空间),如下:

        文件内容为12字节,但是不满1簇,系统读取时还是读一簇的数据(16*512 = 8192 Byte)。

         读取完文件起始簇号存放的文件数据后,就查询下一个簇号存放数据。 FAT表以簇为单位,标识分区中空间的使用情况(每个标识占4字节)。FAT前2簇为保留簇(簇0和簇1),不分配给文件使用,其内容含义如下所示:

  • FAT16:F8 FF FF FF
  • FAT32:F8 FF FF 0F FF FF FF FF

        一个FAT表项值表明了文件占用的一个簇号并指明下一簇号的位置,说明读取完簇号0x0C(12)后读下一个簇号0x0D(13),结合FAT表数据可以知道,文件的簇号链接为:

12 -> 13 -> 14 ->15 -> 16 ->17 ->18 -> 19 ->20 ->21 ->22 ->23 ->24 ->25 ->26 -> 27 -> 28 -> 29 -> 30

         FAT表按顺序依次记录了该盘各簇的使用情况,是一种位示图法。每簇的使用情况用32位二进制填写,未被分配的簇相应位置写;坏簇相应位置填入特定值;已分配的簇相应位置填入非零值,具体为:如果该簇是文件的最后一簇,填入的值为FFFFFF0FH(即0x0FFFFFFF),如果该簇不是文件的最后一簇,填入的值为该文件占用的下一个簇的簇号,这样,正好将文件占用的各簇构成一个簇链,保存在FAT表中。0000000H、00000001H两簇号不使用,其对应的两个DWORD位置(FAT表开头的8个字节)用来存放该盘介质类型编号。

        FAT表的大小就由该逻辑盘数据区共有多少簇所决定,取整数个扇区。

        当文件系统被创建,也就是进行格式化操作时,分配给FAT区域的空间将会被清空,在FAT1与FAT20号表项1号表项写入特定值。由于创建文件系统的同时也会创建根目录,也就是为根目录分配了一个簇空间,通常为2号簇,所以2号簇所对应的2号FAT表项也会被写入一个结束标记,如上图所示。

        如果某个簇存在坏扇区,则整个簇会用FAT表项值0xFFFFFF7标记为坏簇,不再使用,这个坏簇标记就记录在它所对应的FAT表项中。

        在文件系统中新建文件时,如果新建的文件只占用一个簇,为其分配的簇对应的FAT表项将会写入结束标记。如果新建的文件不只占用一个簇,则在其所占用的每个簇对应的FAT表项中写入为其分配的下一簇的簇号,在最后一个簇对应的FAT表象中写入结束标记。

        新建目录时,只为其分配一个簇的空间,对应的FAT表项中写入结束标记。当目录增大超出一个簇的大小时,将会在空闲空间中继续为其分配一个簇,并在FAT表中为其建立FAT表链以描述它所占用的簇情况。

        对文件或目录进行操作时,他们所对应的FAT表项将会被清空,设置为0以表示其所对应的簇处于未分配状态。

3、winhex的使用手册

        我们在分析FAT32文件系统时,需要用到winhex工具查看磁盘上的原始数据,操作流程如下:

        3.1 WinHex工具下载安装

              下载链接:https://www.x-ways.net/winhex/index-m.html

               安装完成后的工具图标如下所示:

        3.2 打开WinHex工具

                该软件需要以”管理员身份运行“。

        3.3 打开磁盘

                选择Tools —> Open Disk打开磁盘

        3.4 选择需要打开的磁盘

        3.5 查看磁盘数据

              选择分区1,然后鼠标键右击选择模板,可以查看DBR数据

        3.6 查看FAT32 DBR数据

              FAT32 DBR数据详细分析可以查看前面的解析,也可以用winhex软件查看(点击模板后后会自动弹出)。

        3.7 查看FAT32分区数据

                鼠标双击分区1,进入FAT32根目录,然后点击根目录文件夹(注意此时的offset是相对偏移,不是绝对偏移)。

        3.8 绝对位置查看数据

                为了更好的学习FAT32,推荐使用绝对地址导航来查看数据。先退出分区界面,然后点击导航-》跳转到偏移量,然后设置如下:

 4、 参考资料

        本文全部内容都来自下面两篇优秀文章,结合了自我理解后对其进行了整合。

https://blog.csdn.net/ZHONGCAI0901/article/details/115196445

https://blog.csdn.net/s9wmu_f/article/details/83863019?ops_request_misc=&request_id=&biz_id=102&utm_term=fat32%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9Fj%E7%BB%93%E6%9E%84&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-5-83863019.142^v100^control&spm=1018.2226.3001.4187

        

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值