FAT32 文件系统

FAT32 文件系统


需求:在一个不带文件系统的操作系统上完成对SD card的访问和读写操作,参考标准FAT32格式做一个驱动。
辅助工具:WinHex

按照自己的学习习惯,第一部分先摘取关键有效信息做笔记,接着再转为有效的合适的数据结构,最后画流程图。

1 FAT32 信息摘要

学习过程中来自此处博客文章 [http://blog.csdn.net/mjx91282041/article/details/8904705]的信息量巨大,实在可做为较全的开发教程,摘录供再次参考。
另外再需要一份正规点的开发白皮书“Microsoft Extensible Firmware Initiative FAT32 File System Specification”作细节校正。

1.1 FSInfo

名称 Offset(byte) 大小(Byte) 描述
FSI_LeadSig 0 4 值为0x41615252,这个标记用来表示该扇区为FSInfo扇区。
FSI_Reserved1 4 480 保留为以后扩展使用,FAT32格式化程序应该把此域全部设置为0,当前版本的FAT程序不可以访问该域。
FSI_StrucSig 484 4 值为0x61417272,更具体地表明该扇区已经被使用。
FSI_Free_Count 488 4 保存最新的剩余簇的数量,如果为0xFFFFFFFF表示剩余簇未知,需要重新计算,除此之外的其他值都可以用,而且不要求十分精确,但必须保证其值≤磁盘所有的簇数。
FSI_Nxt_Free 492 4 该域为FAT驱动程序提供一条有利的线索,它告诉驱动程序应该从哪里开始寻找剩余簇,因为FAT32的FAT表可能非常庞大,如果已经分配的簇很多的话要从头开始查找剩余簇,这将耗费大量时间。通常这个值被设定为驱动程序最后分配出去的簇号,如果值为0xFFFFFFFF,那么驱动程序必须从簇2开始查找,除此之外其他的值都可以使用,当然,前提是这个值必须是合法的。
FSI_Reserved2 496 12 保留为以后扩展使用,FAT32格式化程序应该把此域全部设置为0,当前版本的FAT程序不可以访问该域。
FSI_TrailSig 508 4 值为0xAA550000,此结束标记用来表示这是一个FSInfo扇区,注意此域的高两位偏移量为510和511,这和启动扇区在相同偏移处的标记是一样的。


1.2 FAT表

//...
// 待补充
//...

1.3 目录项

 FAT32的根目录由簇链组成,其扇区数是不确定的,这点和普通文件相同,根目录的第一个扇区号存储在BPB_RootClus中。

根目录不同于其他的目录,没有日期和时间戳,也没有目录名(“/”并不是其目录名),同时根目录里没有“.”和“..”这两个目录项,根目录另一个特殊的地方在于,根目录中有一个设置了ATTR_VOLUME_ID位(见下表)的文件,这个文件在整个FAT卷中是唯一的。

文件系统刚被创建(可通过格式化你的设备实现),还没有任何存储数据时,文件系统只为根目录分配了一个簇的空间(通常为2号簇),并将结束标记0x0FFFFFFF,表示该簇已经被使用,但根目录下没有任何内容,目录项全部为00

此时通过新建文件,或者更改卷标,刚才全部为空的根目录就会如以上根目录项的格式填充内容。
DIR_Name域实际由两部分组成:8个字符的主文件名和3个字符的扩展名。两部分如果字符数不够的话用空格(0x20)填充(Trailing Space Padded)。

[注]:根目录项中只有定义了卷标,才会出现 DIR_Attr =ATTR_VOLUME_ID 的内容。否则没有属性为卷标的目录项。另外卷标所在的目录项不一定会分配到根目录到第一个目录项空间,根目录的空间分配按照创建时间从0号目录项依次分配,所以在当检测到DIR_Name[0]为0x00时,可以认为此后的目录项都没有内容。

    目录所在的扇区,都是以32 Bytes划分为一个单位,每个单位称为一个目录项(Directory Entry ),即每个目录项的长度都是32 Bytes 根目录由若干个目录项组成,一个目录项占用32个字节,可以是长文件名目录项、文件目录项、子目录项等。

名称 Offset (Byte) 大小(Byte) 描述
DIR_Name 0 11 短文件名
DIR_Attr 11 1 文件属性:
ATTR_READ_ONLY       0x01
ATTR_HIDDEN            0x02
ATTR_SYSTEM            0x04
ATTR_VOLUME_ID       0x08
ATTR_DIRECTORY       0x10
ATTR_ARCHIVE           0x20
ATTR_LONG_NAME     ATTR_READ_ONLY |
                                  ATTR_HIDDEN |
                                  ATTR_SYSTEM |
                                  ATTR_VOLUME_ID
前两个属性位为保留位,在文件创建时应该把这两位设为0,在以后的使用中不能读写和更改。
DIR_NTRes 12 1 保留给Windows NT使用,在文件创建时设置该位为0,在以后的使用中不能读写和更改。
DIR_CrtTimeTeenth 13 1 文件创建时间的毫秒级时间戳,由于DIR_CrtTime的精度为2秒,所以此域的有效值在0-199之间。
DIR_CrtTime 14 2 文件创建时间。
DIR_CrtData 16 2 文件创建日期。
DIR_LastAccDate 18 2 最后访问日期,请注意并没有最后访问时间域,而只有日期,这日期是指文件被读写的日期,如果是写,该日期还应该被写到DIR_WrDate中。
DIR_FstClusHI 20 2 该目录项起始簇号的高位字(FAT12/16此位为0)
DIR_WrtTime 22 2 最后写的时间,文件创建被认作写
DIR_WrtDate 24 2 最后写的日期,文件创建被认作写
DIR_FstClusL0 26 2 该目录项起始簇号的低位字
DIR_FileSize 28 4 文件大小,由32-bit双字组成
1.3.1 DIR_Name[0]

此处特别注释目录项的第一个字节 DIR_Name[0]:

  1. 如果DIR_Name[0] == 0xE5,则此目录为空(目录项不包含文件和目录),或表示该项已被删除
  2. 如果DIR_Name[0] == 0x00,则此目录为空(同0xE5),并且此后的不再分配有目录项(此后所有的DIR_Name[0]均为0)。不同于0xE5,如果DIR_Name[0]的值为0,那么FAT程序将不会再去检测其后续的磁盘空间,因为这些空间都是空闲的。
  3. 如果DIR_Name[0] == 0x05,则文件名在该位的实际值为0xE5,但0xE5是日文中合法的字符,当需要用0xE5来作为DIR_Name[0]时使用0x05来代替,避免程序误认为该目录项为空。
  4. DIR_Name[0]不允许为0x20,主文件名和扩展文件名的间隔“.”并不真实存在于DIR_Name中,小写字母不允许出现在DIR_Name中(这些字符因为不同的国家和地区而异)。

[注]:以下字符不允许出现在DIR_Name中的任何位置:0x22, 0x2A, 0x2B, 0x2C, 0x2E, 0x2F, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x5B, 0x5C, 0x5D还有0x7C。

1.4 数据区举例

//...
// 待补充
//...

1.5 文件创建

1.5.1 文件分配规则

首先,根据创建文件的不同,FAT32的结构分配也不同,需要知道的是:

1. 是不是所有类型和大小的文件都需要被分配FAT表从而占用至少一个簇都空间?
  1. 所有文件大小大于0的文本文件
  2. 所有文件夹,不论是否为空
  3. 所有目录文件
2. 是不是所有类型和大小的文件都需要创建目录文件?
  1. 首先系统将2号簇默认为根目录文件
  2. 任何类型的文件都有一个目录项用于存储其文件名,文件大小,起始簇地址以及其他属性
  3. 目录项存储于目录文件中,一个目录文件中可存放多个目录项(目录项大小32Byte)
  4. 目录文件根据当前文件的数量动态创建,当前目录文件没有足够空间存储一个新建文件(不论大小)的目录项时,在FAT表中新建一个簇作为目录文件
  5. 同一个目录文件中的所有文件同属于该目录文件的路径下
1.5.2 创建一个文件和一个目录
文件创建中,目录创建和文档创建在属性上存在不同:
  • 文档在被新建的时候,已经明确其所在的路径,即已经有目录被创建,不存在为每个新建文档分配目录文件的问题,当文档被新建,其起始簇的0地址偏移处即存储文档内容;
  • 文件夹作为目录的创建方式,必须分配新簇空间。需要存储该目录下所有的文件信息,所以起始簇的0地址偏移处存储的是当前目录下的所有文件信息,即使是空目录也会存放子目录和父目录信息,用来表示该目录所处的路径。
  • 创建文档和目录都需要将文件属性信息存储在目录项中,另外,创建非空文档时需要被分配有效的可用簇存储文档内容;创建目录需要在目录项中明确父目录子目录关系,但不需要再分配空间。
逻辑上应该是以下步骤:
  1. 获取文件(文档或目录)大小
  2. 遍历FAT表,按照合适的文件分配策略在FAT表中填写簇的分配信息
  3. 定位当前所在目录,在其所在目录文件中增加目录项
  4. 根据FAT表分配在目录项中写入所在簇起始地址以及其他已知信息
  5. 将文件内容写入给文档分配的起始簇
实际上,在WinHex中发现当创建一个空的txt文档时,即只有文件名,则FAT表不一定会分配新簇空间,而是只在其所在父目录处新建目录项,当此目录文件(目录文件也是一个标准的簇)被占用满,FAT表中才会再分配下一个可用空间给新建的空文档。所以以上补充:
  • 2‘. 当文件大小为0时,查看当前目录大小是否够用(只需要一个目录项的大小),不够用则新增FAT表,否则跳过步骤2到步骤3
  • 5'. 新建文件夹或空文档跳过步骤5

以上新建内容是目前所理解做的整理,实际开发之后补充验证结果。

1.5.3 文件创建举例
1.5.3.1 txt文件创建

1.5.3.2 文件夹 目录创建




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值