FAT文件系统介绍以及FatFs的移植
FAT文件系统介绍以及FatFs的移植
文章目录
- FAT文件系统介绍以及FatFs的移植
- 1、FAT文件系统介绍
- 1.1 MBR分区
- 1.1.1 MBR分区结构
- 1.1.2 MBR的DPT分区表解析
- 1.2 GPT分区
- 1.2.1 GPT分区结构
- 1.2.2 GPT各分区结构介绍
- 1.3 FAT文件系统分析
- 1.3.1 FAT简介
- 1.3.2 DBR解析
- 1.3.3 FSINFO扇区
- 1.3.4 FAT表
- 1.3.5 FAT表分析说明
- 1.3.6 FAT表示例
- 1.3.7 数据保存区
- 1.3.8 目录项
- 2、FatFs移植
- 2.1 FatFs简介
- 2.2 FatFs 模块层次结构
- 2.3 下载地址
- 2.4 FatFs文件系统文件介绍
- 3、结合FatFs源码理解FAT文件系统
- 3.1 FatFs的mount过程分析
- 3.2 FatFs的read过程
- 参考资料
1、FAT文件系统介绍
1.1 MBR分区
1.1.1 MBR分区结构
MBR位于磁盘的0号扇区,也就是第一个扇区。MBR由以下四个部分组成:
引导代码:引导代码占MBR分区的前446字节,负责整个系统启动。如果引导代码被破坏,系统将无法启动。
Windows磁盘签名:占引导代码后面的4字节,是Windows初始化磁盘写入的磁盘标签,如果此标签被破坏,则系统会提示“初始化磁盘”。
DPT分区表:占Windows磁盘标签后面的64个字节,是整个硬盘的分区表。
MBR结束标志:占MBR扇区最后2个字节,一直为“55 AA”。
下图是一个MBR扇区示例:
1.1.2 MBR的DPT分区表解析
磁盘在使用前都要进行分区,也就是将硬盘划分为一个个逻辑的区域。每一个分区都有一个确定的起始结束位置。MBR的DPT分区表位于引导代码结束以后的64个字节。其中每16个字节为一个分区表项,也就是在MBR扇区中只能记录4个分区信息。每一个分区表项的具体解析如下表:
存贮字节位 | 内容及含义 |
---|---|
第1字节 | 引导标志。若值为80H表示活动分区,若值为00H表示非活动分区。 |
第2、3、4字节 | 本分区的起始磁头号、扇区号、柱面号。其中: 磁头号——第2字节; 扇区号——第3字节的低6位; 柱面号——为第3字节高2位+第4字节8位。 |
第5字节 | 分区类型符。 00H——表示该分区未用(即没有指定); 06H——FAT16基本分区; 0BH——FAT32基本分区; 05H——扩展分区; 07H——NTFS分区; 0FH——(LBA模式)扩展分区(83H为Linux分区等)。 |
第6、7、8字节 | 本分区的结束磁头号、扇区号、柱面号。其中: 磁头号——第6字节; 扇区号——第7字节的低6位; 柱面号——第7字节的高2位+第8字节。 |
第9、10、11、12字节 | 逻辑起始扇区号 ,本分区之前已用了的扇区数。 |
第13、14、15、16字节 | 本分区的总扇区数。 |
1.2 GPT分区
1.2.1 GPT分区结构
GPT磁盘分区结构解决了MBR只能分4个主分区的的缺点,理论上说,GPT磁盘分区结构对分区的数量是没有限制的。但某些操作系统可能会对此有限制。GPT磁盘分区结构由6部分组成,如下图:
1.2.2 GPT各分区结构介绍
保护MBR:保护MBR位于磁盘的0号扇区。只包含一个类型值为0xEE的分区项(第450个字节等于0xee)它的作用是阻止不能识别GPT分区的磁盘工具试图对其进行格式化等操作,所以该扇区被称为“保护MBR”。
EFI部分:
EFI信息区(GPT头):起始于磁盘的LBA1,通常也只占用这个单一扇区。其作用是定义分区表的位置和大小。GPT头还包含头和分区表的校验和,这样就可以及时发现错误。下表是GPT头的具体解析:
相对字节偏移量 (十六进制) | 字节数 | 说明[整数皆以little endian方式表示] |
---|---|---|
00~07 | 8 | GPT头签名“45 46 49 20 50 41 52 54”(ASCII码为“EFI PART”) |
08~0B | 4 | 版本号,目前是1.0版,其值是“00 00 01 00” |
0C~0F | 4 | GPT头的大小(字节数),通常为“5C 00 00 00”(0x5C),也就是92字节。 |
10~13 | 4 | GPT头CRC校验和(计算时把这个字段本身看做零值) |
14~17 | 4 | 保留,必须为“00 00 00 00” |
18~1F | 8 | EFI信息区(GPT头)的起始扇区号,通常为“01 00 00 00 00 00 00 00”,也就是LBA1。 |
20~27 | 8 | EFI信息区(GPT头)备份位置的扇区号,也就是EFI区域结束扇区号。通常是整个磁盘最末一个扇区。 |
28~2F | 8 | GPT分区区域的起始扇区号,通常为“22 00 00 00 00 00 00 00”(0x22),也即是LBA34。 |
30~37 | 8 | GPT分区区域的结束扇区号,通常是倒数第34扇区。 |
38~47 | 16 | 磁盘GUID(全球唯一标识符,与UUID是同义词) |
48~4F | 8 | 分区表起始扇区号,通常为“02 00 00 00 00 00 00 00”(0x02),也就是LBA2。 |
50~53 | 4 | 分区表总项数,通常限定为“80 00 00 00”(0x80),也就是128个。 |
54~57 | 4 | 每个分区表项占用字节数,通常限定为“80 00 00 00”(0x80),也就是128字节。 |
58~5B | 4 | 分区表CRC校验和 |
5C~* | * | 保留,通常是全零填充 |
分区表:分区表区域包含分区表项。这个区域由GPT头定义,一般占用磁盘LBA2~LBA33扇区。分区表中的每个分区项由起始地址、结束地址、类型值、名字、属性标志、GUID值组成。分区表建立后,128位的GUID对系统来说是唯一的。分区表项的解析如下:
相对字节偏移量 (十六进制) | 字节数 | 说明[整数皆以little endian方式表示] |
---|---|---|
00~0F | 16 | 用GUID表示的分区类型 |
10~1F | 16 | 用GUID表示的分区唯一标示符 |
20~27 | 8 | 该分区的起始扇区,用LBA值表示。 |
28~2F | 8 | 该分区的结束扇区(包含),用LBA值表示,通常是奇数。 |
30~37 | 8 | 该分区的属性标志 |
38~7F | 72 | UTF-16LE编码的人类可读的分区名称,最大32个字符。 |
分区区域:
GPT分区区域就是用户使用的分区,也是用户进行数据存储的区域。分区区域的起始地址和结束地址由GPT头定义。
GPT头备份:
GPT头有一个备份,放在GPT磁盘的最后一个扇区,但这个GPT头备份并非完全GPT头备份,某些参数有些不一样。复制的时候根据实际情况更改一下即可。
分区表备份:
分区区域结束后就是分区表备份,其地址在GPT头备份扇区中有描述。分区表备份是对分区表32个扇区的完整备份。如果分区表被破坏,系统会自动读取分区表备份,也能够保证正常识别分区。
1.3 FAT文件系统分析
1.3.1 FAT简介
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扇区,DBR中记录着文件系统的起始位置、大小、FAT表个数及大小等相关信息。**在FAT文件系统中,同时使用“扇区地址”和“簇地址”两种地址管理方式。这是因为只有存储用户数据的数据区使用簇进行管理(FAT12和FAT16的根目录除外),所有簇都位于数据区。其他文件系统管理数据区域是不以簇进行管理的,这部分区域使用扇区地址进行管理。文件系统的起始扇区为0号扇区(逻辑0扇区)。
一个使用了FAT32文件系统的SD card的整体布局如下:
1.3.2 DBR解析
如下图,对读写FAT文件系统来说常用的就图中划线部分,48个字节,具体定义如下:
- 0x00~0x02:3字节,跳转指令。
- 0x03~0x0A:8字节,文件系统标志和版本号,这里为MSDOS5.0。
- 0x0B~0x0C:2字节,每扇区字节数,0x0200=512
- 0x0D~0x0D:1字节,每簇扇区数,0x08。
- 0x0E~0x0F:2字节,保留扇区数,0x0C22=3106
- 0x10~0x10:1字节,FAT表个数,0x02。
- 0x11~0x12:2字节,FAT32必须等于0,FAT12/FAT16为根目录中目录的个数;
- 0x13~0x14:2字节,FAT32必须等于0,FAT12/FAT16为扇区总数。
- 0x15~0x15:1字节,哪种存储介质,0xF8标准值,可移动存储介质。
- 0x16~0x17:2字节,FAT32必须为0,FAT12/FAT16为一个FAT 表所占的扇区数。
- 0x18~0x19:2字节,每磁道扇区数,只对于“特殊形状”(由磁头和柱面分割为若干磁道)的存储介质有效,0x003F=63。
- 0x1A~0x1B:2字节,磁头数,只对特殊的介质才有效,0x00FF=255。
- 0x1C~0x1F:4字节,EBR分区之前所隐藏的扇区数,与MBR中地址0x1C6开始的4个字节数值相等。
- 0x20~0x23:4字节,文件系统总扇区数,0x00E83800=15218688
- 0x24~0x27:4字节,每个FAT表占用扇区数,0x000039EF=14831
- 0x28~0x29:2字节,标记,此域FAT32 特有。
- 0x2A~0x2B:2字节,FAT32版本号0.0,FAT32特有。
- 0x2C~0x2F:4字节,根目录所在第一个簇的簇号,0x02。(虽然在FAT32文件系统下,根目录可以存放在数据区的任何位置,但是通常情况下还是起始于2号簇)
- 0x30~0x31:2字节,FSINFO(文件系统信息扇区)扇区号0x01,该扇区为操作系统提供关于空簇总数及下一可用簇的信息。
- 0x32~0x33:2字节,备份引导扇区的位置。备份引导扇区总是位于文件系统的6号扇区。
- 0x34~0x3F:12字节,用于以后FAT 扩展使用。
- 0x40~0x40:1字节,与FAT12/16 的定义相同,只不过两者位于启动扇区不同的位置而已。
- 0x41~0x41:1字节,与FAT12/16 的定义相同,只不过两者位于启动扇区不同的位置而已 。
- 0x42~0x42:1字节,扩展引导标志,0x29。与FAT12/16 的定义相同,只不过两者位于启动扇区不同的位置而已
- 0x43~0x46:4字节,卷序列号。通常为一个随机值。
- 0x47~0x51:11字节,卷标(ASCII码),如果建立文件系统的时候指定了卷标,会保存在此。
- 0x52~0x59:8字节,文件系统格式的ASCII码,FAT32。
- 0x5A~0x1FD:共420字节,引导代码。
- 0x1FE~0x1FF:签名标志“55 AA”。
FAT文件系统将引导代码与文件形同数据结构融合在FAT32文件系统引导扇区的512字节中,90509字节为引导代码,而FAT12/16则是62509字节为引导代码。同时,FAT32还可以利用引导扇区后的扇区空间存放附加的引导代码。一个FAT卷即使不是可引导文件系统,也会存在引导代码。
1.3.3 FSINFO扇区
FAT32在保留区中增加了一个FSINFO扇区,用以记录文件系统中空闲簇的数量以及下一可用簇的簇号等信息,以供操作系统作为参考。FSINFO信息扇区一般位于文件系统的1号扇区,结构非常简单。
- 0x200~0x203:4个字节,扩展引导标志“0x52526141”。
- 0x204~0x3E3:480个字节,未使用,全部置0。
- 0x3E4~0x3E7:4个字节,FSINFO签名“0x72724161”。
- 0x3E8~0x3EB:4个字节,文件系统的空簇数。
- 0x3EC~0x3EF:4个字节,下一可用簇号(0x00000002)。
- 0x3F0~0x3FD:14个字节,未使用。
- 0x3FE~0x3FF:2个字节,“55 AA”标志。
温馨提示:通常情况下,文件系统的2号扇区结尾也会被设置“55 AA”标志。6号扇区也会有一个引导扇区的备份,相应的7号扇区应该是一个备份FSINFO信息扇区,8号扇区可以看做是2号扇区的备份。
1.3.4 FAT表
紧跟在保留分区后面的是FAT区,其由两个完全相同的FAT(File Allocation Table, 文件分配表)表单组成,FAT文件系统的名字也是因此而来。FAT 表是一组与数据簇号对应的列表。FAT2紧跟在FAT1之后,它的位置可以通过FAT1的位置加上FAT表的扇区数计算出来。
- 文件系统分配磁盘空间按簇来分配。因此,文件占有磁盘空间时,基本单位不是字节而是簇,即使某个文件只有一个字节,操作系统也会给它分配一个最小单元:即一个簇。对于大文件,需要分配多个簇。同一个文件的数据并不一定完整地存放在磁盘中一个连续地区域内,而往往会分若干段,像链子一样存放。这种存储方式称为文件的链式存储。为了实现文件的链式存储,文件系统必须准确地记录哪些簇已经被文件占用,还必须为每个已经占用的簇指明存储后继的下一个簇的簇号,对于文件的最后一簇,则要指明本簇无后继簇。这些都是由FAT表来保存的,FAT 表对应表项中记录着它所代表的簇的有关信息:诸如是空,是不是坏簇,是否是已经是某个文件的尾簇等。
- 对于文件系统来说,FAT表有两个重要作用:描述簇的分配状态以及标明文件或目录的下一簇的簇号。
- 通常情况下,一个FAT文件系统会有两个FAT表,但有时也允许只有一个FAT表,FAT表的具体个数记录在引导扇区的偏移0x10字节处。
- 由于FAT区紧跟在文件系统保留区后,所以FAT1在文件系统中的位置可以通过引导记录中偏移0x0E~0x0F字节处的“保留扇区数”得到,即M值。
1.3.5 FAT表分析说明
- FAT32中每个簇的簇地址是有32bit(4个字节),FAT表中的所有字节位置以4字节为单位进行划分,并对所有划分后的位置由0进行地址编号。0号地址与1号地址被系统保留并存储特殊标志内容。从2号地址开始,每个地址对应于数据区的簇号,FAT表中的地址编号与数据区中的簇号相同。我们称FAT表中的这些地址为FAT表项,FAT表项中记录的值称为FAT表项值。
- 当文件系统被创建,也就是进行格式化操作时,分配给FAT区域的空间将会被清空,在FAT1与FAT2的0号表项与1号表项写入特定值。由于创建文件系统的同时也会创建根目录,也就是为根目录分配了一个簇空间,通常为2号簇,与之对应的2号FAT表项记录为2号簇,被写入一个结束标记。
- 由于簇号起始于2号,所以FAT表项的0号表项与1号表项不与任何簇对应。FAT32的0号表项值总是“F8FFFF0F”。
- 1号表项可能被用于记录脏标志,以说明文件系统没有被正常卸载或者磁盘表面存在错误。不过这个值并不重要。正常情况下1号表项值为“FFFFFFFF”或“FFFFFF0F”。
- 如果某个簇未被分配使用,它对应的FAT表项值0;
- 当某个簇已被分配使用,则它对应的FAT表项内的表项值也就是该文件的下一个存储位置的簇号。如果该文件结束于该簇,则在它的FAT表项中记录的是一个文件结束标记,对于FAT32而言,代表文件结束的FAT表项值为0x0FFFFFFF。
- 如果某个簇存在坏扇区,则整个簇会用0xFFFFFF7标记为坏簇,这个坏簇标记就记录在它所对应的FAT表项中。
- 在文件系统中新建文件时,如果新建的文件只占用一个簇,为其分配的簇对应的FAT表项将会写入结束标记。如果新建的文件不只占用一个簇,则在其所占用的每个簇对应的FAT表项中写入为其分配的下一簇的簇号,在最后一个簇对应的FAT表象中写入结束标记。
- 新建目录时,只为其分配一个簇的空间,对应的FAT表项中写入结束标记。当目录增大超出一个簇的大小时,将会在空闲空间中继续为其分配一个簇,并在FAT表中为其建立FAT表链以描述它所占用的簇情况。
1.3.6 FAT表示例
- 绿色划线:0号表项,0x0FFFFFF8,FAT表起始固定标识
- 红色划线,1号表项,0x0FFFFFFF,不使用,默认值
- 蓝色划线,2号表项,0x0FFFFFFF,标识文件结束,表项对应2号簇,根目录所在簇
- 如何找到FAT表所在扇区:
DBR的偏移0x0E-0x0F(0x0C22=3106)是保留区大小,保留区之后即为FAT1起始扇区,上图中偏移0x184400转换为扇区0x184400/512=3106,扇区从0计数,所以3106扇区即是FAT1所在扇区 - 计算根目录起始扇区:
N=保留区大小+2xFAT表大小=0x0C22+2x0x000039EF=32768 - 将SD卡格式化,新建TEST.txt文件,大小为8.5KB,FAT表结构如下:
- 红色划线:2号表项,对应2号簇,为根目录
- 绿色划线:3号表项,对应3号簇,表项值为0x04,Test.txt的下一个簇为4号簇
- 蓝色划线:4号表项,对应5号簇,表项值为0x05,Test.txt的下一个簇为5号簇
- 黄色划线:5号表项,对应5号簇,表项值为0x0FFFFFFF,文件结束
1.3.7 数据保存区
- 数据区是真正用于存放用户数据的区域。数据区紧跟在FAT2之后,被划分成一个个的簇。所有的簇从2开始进行编号,也就是说,2号簇的起始位置就是数据区的起始位置。
- 虽然原则上FAT32允许根目录位于数据区的任何位置,但通常情况下它都位于数据区起始扇区。在FAT文件系统中,先要寻找数据区的第一簇(即2号簇)的位置,它不是位于文件系统开始处,而是位于数据区。从前面的学习知道,在数据区前面是保留区域和FAT区域,在前面还有MBR区域,这些区域都不使用FAT表进行管理。因此,数据区以前的区域只能使用扇区地址,而无法使用簇地址。
- 数据区起始扇区号即是根目录扇区号,上面已计算得出32768。
1.3.8 目录项
目录所在的扇区,都是以32 Bytes划分为一个单位,每个单位称为一个目录项,即每个目录项的长度都是32 Bytes 。根目录由若干个目录项组成,一个目录项占用32个字节,可以是长文件名目录项、文件目录项、“.”目录项和“…”目录项等
此处只是简单的以上文中创建的TEST.txt为例说明短文件目录项的结构。
0x00-0x07:文件名,不足8个字节0x20补全(短文件名8.3命名规则)
0x08-0x0A:扩展名
0x0B:文件属性,0x20表示归档
0x0D:创建时间的10毫秒位
0x0E-0x0F:文件创建时间
0x10-0x11:文件创建日期
0x12-0x13:文件最后访问日期
0x14-0x15:文件起始簇号的高16位 0x0000
0x16-0x17:文件最近修改时间
0x18-0x19:文件最近修改日期
0x1A-0x1B:文件起始簇号的地16位 0x0003
0x1C-0x1F:文件的长度,0x2206=8710bytes=8.5K
2、FatFs移植
2.1 FatFs简介
FatFs是用于小型嵌入式系统的通用FAT / exFAT文件系统模块。FatFs模块是按照ANSI C(C89)编写的,并且与磁盘I / O层完全分开。因此,它独立于平台。它可以并入资源有限的小型微控制器中,例如8051,PIC,AVR,ARM,Z80,RX等。此处还提供了适用于小型微控制器的Petit FatFs模块。
特点:
DOS / Windows兼容的FAT / exFAT文件系统。
平台无关。易于移植。
程序代码和工作区的占用空间非常小。
支持以下各种配置选项:
ANSI / OEM或Unicode中的长文件名。
exFAT文件系统,64位LBA和GPT可存储大量数据。
RTOS的线程安全。
多个卷(物理驱动器和分区)。
可变扇区大小。
多个代码页,包括DBCS。
只读,可选API,I / O缓冲区等…
2.2 FatFs 模块层次结构
底层接口
包括存储媒介读/写接口(disk I/O)和供给文件创建修改时间的实时时钟,需要我们根据平台和存储介质编写移植代码。
中间层FATFS模块
实现了FAT 文件读/写协议。FATFS模块提供的是ff.c和ff.h。除非有必要,使用者一般不用修改,使用时将头文件直接包含进去即可。
顶层应用层
使用者无需理会FATFS的内部结构和复杂的FAT 协议,只需要调用FATFS模块提供给用户的一系列应用接口函数,如f_open,f_read,f_write 和f_close等,就可以像在PC 上读/写文件那样简单。
2.3 下载地址
http://elm-chan.org/fsw/ff/00index_e.html
此地址不仅仅包含资料包下载,还包括文件系统一些知 识,包括函数说明,函数调用实例等。
2.4 FatFs文件系统文件介绍
文件名 | 功能 | 说明 |
---|---|---|
ffconf.h | FatFs模块的配置文件 | 需要用户根据需求来配置 |
ff.h | FatFs和应用程序模块的通用包含文件 | 不需要修改 |
ff.c | FatFs模块源码 | 不需要修改 |
diskio.h | FatFs和disk I / O模块的公共包含文件 | 不需要修改 |
diskio.c | FatFs 和disk I / O 模块接口层文件 | 与平台相关的代码,需要用户根据存储介质来编写函数。 |
ffunicode.c | 可选的Unicode 相关的转换函数 | |
ffsystem.c | 可选的与操作系统对接的各接口相关实现示例 |
移植FatFs的过程中,用户需要根据自己的实际需求来对配置文件ffconf.h进行配置,然后就是需要实现diskio.c中的底层I/O驱动接口。
3、结合FatFs源码理解FAT文件系统
3.1 FatFs的mount过程分析
–mount_volume
--stat = disk_initialize(fs->pdrv);//初始化I/O设备
--fmt = find_volume(fs, LD2PT(vol));//寻找Fat表
static UINT find_volume ( /* Returns BS status found in the hosting drive */FATFS* fs, /* Filesystem object */UINT part /* Partition to fined = 0:auto, 1..:forced */
)
{UINT fmt, i;DWORD mbr_pt[4];fmt = check_fs(fs, 0); /* Load sector 0 and check if it is an FAT VBR as SFD format *///在这个函数中会load 0号扇区的数据,如果检查0号扇区符合DBR格式,那么返回。如果0号扇区的最后两个字节不是0xaa55,那么返回错误。if (fmt != 2 && (fmt >= 3 || part == 0)) return fmt; /* Returns if it is an FAT VBR as auto scan, not a BS or disk error *//* Sector 0 is not an FAT VBR or forced partition number wants a partition */#if FF_LBA64//在这里会进行gpt分区解析if (fs->win[MBR_Table + PTE_System] == 0xEE) { /* GPT protective MBR? *///判断0号扇区是保护MBR才继续往下解析,第450字节为0xee DWORD n_ent, v_ent, ofs;QWORD pt_lba;if (move_window(fs, 1) != FR_OK) return 4; /* Load GPT header sector (next to MBR) *///load 1号扇区的数据,也就是load gpt头if (!test_gpt_header(fs->win)) {//校验gpt头 NOTICE("BL1: find_volume, test_gpt_header failed.\n");return 3; /* Check if GPT header is valid */}//下面是gpt头的解析n_ent = ld_dword(fs->win + GPTH_PtNum); /* Number of entries *///读取0x50-0x53得到分区表总项数pt_lba = ld_qword(fs->win + GPTH_PtOfs); /* Table location *///读取0x48-0x4F得到分区表起始扇区号for (v_ent = i = 0; i < n_ent; i++) { /* Find FAT partition */if (move_window(fs, pt_lba + i * SZ_GPTE / SS(fs)) != FR_OK) return 4; /* PT sector *///前面已经得到分区表的起始扇区,在这里load分区表ofs = i * SZ_GPTE % SS(fs); /* Offset in the sector *///if (!memcmp(fs->win + ofs + GPTE_PtGuid, GUID_MS_Basic, 16)) { /* MS basic data partition? */v_ent++;fmt = check_fs(fs, ld_qword(fs->win + ofs + GPTE_FstLba)); /* Load VBR and check status *///每个分区表项的GPTE_FstLba字节(32)指定了该分区的起始扇区号,在这里会load该分区的起始扇区,检查是否符合DBR格式if (part == 0 && fmt <= 1) return fmt; /* Auto search (valid FAT volume found first) */if (part != 0 && v_ent == part) return fmt; /* Forced partition order (regardless of it is valid or not) *///}}return 3; /* Not found */}
#endif//下面部分的代码是MBR分区的解析if (FF_MULTI_PARTITION && part > 4) return 3; /* MBR has 4 partitions max */ //MBR最多支持4个主分区表项for (i = 0; i < 4; i++) { /* Load partition offset in the MBR */mbr_pt[i] = ld_dword(fs->win + MBR_Table + i * SZ_PTE + PTE_StLba);//找到各个分区的起始扇区}i = part ? part - 1 : 0; /* Table index to find first */do { /* Find an FAT volume */fmt = mbr_pt[i] ? check_fs(fs, mbr_pt[i]) : 3; /* Check if the partition is FAT */ //检查是否存在FAT分区} while (part == 0 && fmt >= 2 && ++i < 4);return fmt;
}
find_volume函数,基本遵循如下流程:
(1)load 0号扇区,如果0号扇区符合DBR格式,那么返回,如果0号扇区的最后两个字节不是0xaa55,那么返回错误
(2)如果0号扇区不符合DBR格式,继续判读第450个字节是不是0xee,如果是,那么按照gpt的格式继续进行解析,检查是否存在FAT分区
(3)如果也不是gpt格式,那么按照mbr的格式解析,检查能找到FAT分区
find_volume找到fat分区的起始扇区(DBR)以后,那么继续进行DBR扇区的解析,如下:
if (ld_word(fs->win + BPB_BytsPerSec) != SS(fs)) return FR_NO_FILESYSTEM; /* (BPB_BytsPerSec must be equal to the physical sector size) */ //读取DBR的0x0B~0x0C得到每个扇区的字节数,这里必须和物理设备保持一致fasize = ld_word(fs->win + BPB_FATSz16); /* Number of sectors per FAT */ if (fasize == 0) fasize = ld_dword(fs->win + BPB_FATSz32);//得到FAT表所占的扇区数fs->fsize = fasize;fs->n_fats = fs->win[BPB_NumFATs]; /* Number of FATs */ //得到FAT表的个数if (fs->n_fats != 1 && fs->n_fats != 2) return FR_NO_FILESYSTEM; /* (Must be 1 or 2) */fasize *= fs->n_fats; /* Number of sectors for FAT area */fs->csize = fs->win[BPB_SecPerClus]; /* Cluster size */if (fs->csize == 0 || (fs->csize & (fs->csize - 1))) return FR_NO_FILESYSTEM; /* (Must be power of 2) */fs->n_rootdir = ld_word(fs->win + BPB_RootEntCnt); /* Number of root directory entries */if (fs->n_rootdir % (SS(fs) / SZDIRE)) return FR_NO_FILESYSTEM; /* (Must be sector aligned) */tsect = ld_word(fs->win + BPB_TotSec16); /* Number of sectors on the volume */if (tsect == 0) tsect = ld_dword(fs->win + BPB_TotSec32);nrsv = ld_word(fs->win + BPB_RsvdSecCnt); /* Number of reserved sectors */if (nrsv == 0) return FR_NO_FILESYSTEM; /* (Must not be 0) *//* Determine the FAT sub type */sysect = nrsv + fasize + fs->n_rootdir / (SS(fs) / SZDIRE); /* RSV + FAT + DIR */if (tsect < sysect) return FR_NO_FILESYSTEM; /* (Invalid volume size) */nclst = (tsect - sysect) / fs->csize; /* Number of clusters */if (nclst == 0) return FR_NO_FILESYSTEM; /* (Invalid volume size) */fmt = 0;if (nclst <= MAX_FAT32) fmt = FS_FAT32;if (nclst <= MAX_FAT16) fmt = FS_FAT16;if (nclst <= MAX_FAT12) fmt = FS_FAT12;if (fmt == 0) return FR_NO_FILESYSTEM;/* Boundaries and Limits */fs->n_fatent = nclst + 2; /* Number of FAT entries */fs->volbase = bsect; /* Volume start sector */fs->fatbase = bsect + nrsv; /* FAT start sector */fs->database = bsect + sysect; /* Data start sector */
可以看到FatFs的mount过程基本就是MBR、GPT和DBR的解析过程。
3.2 FatFs的read过程
在这里没有详细介绍read的详细流程,结合前面的fat文件系统介绍,大概过程如下在根目录扇区中先找到对应的目录项,在目录项中可以得到文件的起始簇号以及文件的大小,根据文件的起始簇号,就能找到保存了该文件的所有扇区地址。
参考资料
1、GUID(GPT)分区表详解
https://blog.csdn.net/u011164819/article/details/50720692
2、详解MBR分区结构以及GPT分区结构
https://blog.51cto.com/dengqi/1348951
3、FAT32文件系统快速入门
https://blog.csdn.net/u010650845/article/details/60881687
4、图解fat32文件系统设计
https://blog.csdn.net/qq_35579835/article/details/106754991?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-2.no_search_link&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-2.no_search_link
5、FATFS文件系统
https://www.pianshen.com/article/15371184643/
FAT文件系统介绍以及FatFs的移植相关推荐
- elm FatFs文件系统移植总结
1 前言 本文将根据我的一些理解,针对elm FatFs文件系统做一个初步总结. 2 elm FatFs文件系统介绍 顾名思义FatFs文件系统就是针对FAT文件系统来的,主要是应用于MCU中,STM ...
- fatfs 文件属性_FatFs文件系统介绍
实验要求 在SD驱动移植实验的基础上,加上FatFs文件系统,实现SD卡中文件的读写及其它操作 实验目的 了解FatFs文件系统的原理 掌握FatFs文件系统的移植方法 实现SD卡中文件的读写及其它操 ...
- STM32+雷龙SD NAND(贴片SD卡)完成FATFS文件系统移植与测试
一.前言 在STM32项目开发中,经常会用到存储芯片存储数据. 比如:关机时保存机器运行过程中的状态数据,上电再从存储芯片里读取数据恢复:在存储芯片里也会存放很多资源文件.比如,开机音乐,界面上的菜单 ...
- 2021-08-11 TM32F103 Buffer FatFs 文件系统移植
FatFs 本文展示了STM32 FatFs文件系统移植 内容涉及 : FatFs 文件系统移植 SPI函数移植过程 SPI字节数据模拟输出独写 缓存读写 USART串口的识别 IO口输入输出 按键的 ...
- 文件系统FATFS的移植教程
最新FatFs:http://elm-chan.org/fsw/ff/00index_e.html 一.FATFS文件系统简介 FATFS是面向小型嵌入式系统的一种通用的FAT文件系统.它完全是由C语 ...
- FatFs文件系统移植过程及中度分析
FatFs 是一个通用的文件系统(FAT/exFAT)模块,用于在小型嵌入式系统中实现FAT文件系统. FatFs 组件的编写遵循ANSI C(C89),完全分离于磁盘 I/O 层,因此不依赖于硬件平 ...
- 【STM32H7】第2章 ThreadX FileX文件系统介绍
论坛原始地址(持续更新):http://www.armbbs.cn/forum.php?mod=viewthread&tid=100749 第2章 ThreadX FileX文件系统介绍 ...
- 【STM32F407】第2章 ThreadX FileX文件系统介绍
论坛原始地址(持续更新):http://www.armbbs.cn/forum.php?mod=viewthread&tid=100749 第2章 ThreadX FileX文件系统介绍 ...
- STM32F103读取SD卡的数据(fat文件模式)
实验目的 掌握SD卡协议原理,用STM32F103完成对SD卡的数据读取(fat文件模式). 实验原理 SD卡寄存器 SD卡总共有8个寄存器,用于设定或表示SD卡信息. 这些寄存器只能通过对应的命令访 ...
- STM32F103完成对SD卡的数据读取(fat文件模式)
目录 一.关于SD卡 1.简介 2.SD卡的寄存器 3.SD卡读取与写入(SPI模式) 二.实验操作 1.仪器 2.代码 3.连线 4.烧录程序 5.串口调试助手初始化并写入文件 6.sd卡里hell ...
最新文章
- subst将文件夹目录虚拟成虚拟磁盘
- Flink状态后端配置(设置State Backend)
- OGG学习笔记04-OGG复制部署快速参考
- 编程软件python怎样开始学-Python 3.7从零开始学
- hausdorff距离
- char* 大小_SQL Server中char, nchar, varchar和nvarchar数据类型有何区别
- day39-Spring 05-Spring的AOP:不带有切点的切面
- 【Flink】Flink 反压机制 导致checkpoint 失败
- python安装离线包window_python 离线安装unrar库
- 在Python中查找字符串长度
- 智·御未来 亚信安全巡展·2017即将起航
- python图片二值化提高识别率
- 采用C#泛型实现状态(State)模式
- window10 下面固定本地 ip
- 2021年北京市促进服务外包发展专项资金申报时间及材料,补贴500万
- 循序渐进ActiveMQ(6)----使用zookeeper实现activemq的主从环境搭建
- 投诉百度快照对排名的影响
- 如何使用计算机上合并计算方法,excel如何使用合并计算
- Utgard连接OPC Server常见故障码及解决方案
- 数据库实验三 嵌套查询和视图操作