FATFS文件系统详解

2023-10-30

FATFS文件系统详解

1. 简介

在早期计算机刚发展的时候,那时候硬盘大小、flash设备容量都比较小,随着技术的不断迭代更新,硬盘容量越来越大。在早期,面对小容量的硬盘/flash,往往采用对应地址存放对应数据的方案,由于数据量不大,操作起来尚还可以。但是发展到今天,随着硬盘/flash容量不断增大,存储的数据也越来越多,早期单一的对应地址存放对应数据的方案已经无法满足我们的需求,操作硬盘/flash会变得异常的困难复杂。

因此针对上述问题,一群大佬们便开始设计文件系统这样一个东西,用来管理硬盘/flash上的数据信息,像我们电脑上打开文件夹,访问里面的文件,这其实就是基于文件系统访问电脑硬盘上数据的一个操作。

发展至今,文件系统已有众多版本,本文主要分享 关于FAT文件系统的详细设计, FAT文件系统适用于嵌入式设备,如SD卡、SD nand、spi nor flash等众多存储设备,同时基于此文件系统的文件亦能被电脑正常读取。

2. 基础概念

在研究文件系统之前,我们需要首先弄清楚关于内存这块的几个基本概念:

  1. 区分 扇区、块、簇 的概念
  • 扇区(sector):flash可操作的最小单元,通常指我们擦除的最小单元大小,以sd nand举例,通常最小为512Byte

  • 块(block) 以及 簇(cluster):其实这是两个相同的概念,只是由于历史原因,在不同系统上的不同称呼,在windows中称簇,而在linux中称块。一个簇/块由多个扇区组成,由于一个扇区的空间较小,因此文件系统通过会将多个扇区组合在一起形成一个簇,并以簇为单位进行读写操作! 一个簇通常可以由 2、4、8、… 、2的n次方个扇区组成。

  1. FAT文件系统总共由FAT12、FAT16以及FAT32三个版本,这是由于随着存储技术不断发展,FAT文件系统迭代导致,数字越大,版本越新,新版本对老版本完全兼容!

3. FAT文件系统组成介绍

Fat文件系统官方文档:http://elm-chan.org/docs/fat_e.html

FAT文件系统在flash上的布局如下图所示,总共由四个区域组成:

  • 保留区
  • FAT区
  • 根目录区 (FAT32类型不包含此区域)
  • 数据区
    在这里插入图片描述
    在这里插入图片描述

接下来,我们对一张格式化为FAT格式的SD卡进行分析,理解FAT文件系统的实现细节:

4. FAT文件系统分析

4.1 采用FAT格式格式化SD nand/sd卡

  1. 使用win10格式化一张118.5M的SD nand / sd卡,我这里用的是手上的一颗 创世CS 家的sd nand加一块转接板,和SD卡完全没有区别,且SD nand在稳定性上比SD卡具有优势。
    在这里插入图片描述
    在这里插入图片描述

此处由于SD nand(SD卡)大小原因,默认采用FAT16进行了格式化!因此在下文中我们先以FAT16进行分析,之后再重新格式化为FAT32进行分析,就很容易懂了!

4.2 引导扇区分析

  1. 使用 winhex 工具打开对应磁盘,注意需使用管理员权限运行
    在这里插入图片描述
  2. 打开后我们可以以二进制的格式查看SD卡上所有数据,首先看到第一个扇区,也就是对应的引导扇区 boot sector,注意引导扇区位于保留区!
    在这里插入图片描述
  3. 接下来我们根据官方文档 对引导扇区进行分析

注意,FAT文件系统数据均采用小端格式!

a) 首先是FAT12/16/32公共部分,(偏移值 0 - 35):

  • EB 3C 90:BS_JmpBoot,跳转指令
  • 4D 53 44 4F 53 35 2E 30:BS_OEMName,MSDOS 5.0,一个名字,指示创建此卷的操作系统,无其他作用
  • 00 02:BPB_BytsPerSec,扇区大小 512 字节
  • 04BPB_SecPerClus,每次操作的最小扇区数,簇 Cluster,4 (与格式化时选择的大小匹配 2048 = 512 * 4)
  • 06 00BPB_RsvdSecCnt,保留区的扇区数,6 (通过此可计算,FAT区起始地址为 6 * 512 = 0xC00)
  • 02BPB_NumFATs,FATs的个数,2(一般此值为2,多一个用来做冗余备份,解决系统异常导致第一个损坏时,增大恢复的可能性,表示FAT区有两个FATs备份)
  • 00 02:BPB_RootEntCnt,512,在FAT12/16系统中,此字段表示根目录中32字节目录条目数量,设置此值时需注意对齐,为了最大的兼容性,FAT16系统上此值应设置为512,FAT32系统上此值应设置为0
  • 00 00BPB_TotSec16,16位大小区域描述FAT卷扇区总数,0。当FAT12/16系统扇区数 ≥0x10000(65536)时,此字段应设置为0,真实值存放在 BPB_TotSec32 字段;对于FAT32系统,此值必须为0。(此处由于我们的总扇区数=118.510241024/512 = 242688 > 65536,所以此字段为0)
  • F8:BPB_Media 媒体类型
  • ED 00BPB_FATSz16,237,一个FAT占用的扇区数,此字段仅在FAT12/16系统使用;FAT32系统,此字段必须为0,使用BPB_FATSz32字段替代。FAT区总大小等于 BPB_FATSz?? * BPB_NumFATs 扇区(2372512=242688=0x3B400,由此可推算根目录区起始地址:0x3B400+0xC00=0x3C000)。
  • 3F 00:BPB_SecPerTrk,每个磁道的扇区数,此字段仅与具有几何形状且仅用于 IBM PC 的磁盘 BIOS 的介质相关,不用管。
  • FF 00:BPB_NumHeads,头数量,此字段仅与具有几何形状且仅用于 IBM PC 的磁盘 BIOS 的介质相关,不用管。
  • 00 00 00 00BPB_HiddSec,0,FAT 卷之前的隐藏物理扇区数(当磁盘被分区之后,当前分区并不一定是从扇区头开始的)
  • 00 B4 03 00BPB_TotSec32,242688,32位大小区域描述FAT卷扇区总数(整个卷空间大小)。 FAT12/16系统,扇区总数小于0x10000时,此字段必须为0,真实值存放在BPB_FATSz16;FAT32系统,此字段一直有效。(118.5M = 512 * 242688)

b) 接下来是FAT12/16特有字段(偏移值36)

  • 80:BS_DrvNum,IBM PC 的磁盘 BIOS 使用的驱动器号,00h代表软盘,80h代表固定磁盘
  • 00:BS_Reserved,保留字段,0
  • 29:BS_BootSig,扩展引导签名,表示以下存在三个字段
  • 83 3E 07 E4:BS_VolID,与 BS_VolLab 一起构成卷序列号,一般在格式化的时候结合时间生成
  • 4E 4F 20 4E 41 4D 45 20 20 20 20:(解析为:"NO NAME “),BS_VolLab,11byte卷标,当卷标不存在时,此值应设置为"NO NAME”
  • 46 41 54 31 36 20 20 20:(解析为:"FAT16 "),BS_FilSysType文件系统类型,支持字段有:"FAT12 ", "FAT16 " or "FAT ",注意很多人认为是通过此字段区分FAT12/16/32系统类型,实际是错误的,文件系统类型实际上是根据磁盘大小确定的,官方文档 “Determination of FAT sub-type” 章节或本博文后文有描述,不过为了最大的兼容性考虑,此字段应设置为对应文件系统的名字。
  • 33 C9 ~ CB D8:BS_BootCode,引导启动程序,与平台有关,不使用时填充为0
  • 55 AA:BS_BootSign,0xAA55,引导签名,指示这是一个有效的引导扇区
    当扇区大小大于512字节时,剩余的字段应全部使用0x0填充。

c) 如果是FAT32,则采用FAT32特有字段解析(偏移值和FAT12/16特有字段一致为36)

虽然此处我们的是FAT16格式,不过此处也将FAT的字段进行描述,方便理解。

  • BPB_FATSz32:一个FAT占用的扇区数,此字段仅在FAT32系统有效。FAT区总大小等于 BPB_FATSz?? * BPB_NumFATs 扇区。
  • BPB_ExtFlags:扩展标识字段,bit7=0,表示所有FAT都是镜像的和活跃的;bit7=1,表示只有bit3-0表示的FAT是有效的。
  • BPB_FSVer:FAT32版本,高字节是主版本号,低字节是次版本号。
  • BPB_RootClus:根目录的第一个簇号,此值通常为2,因为前两个簇一般用于保留。
  • BPB_FSInfo:FSInfo结构扇区与FAT32卷顶部的偏移扇区值。此值通常为1,因为其通常位于引导扇区旁边。
  • BPB_BkBootSec:备份引导扇区与FAT32卷顶部的偏移扇区值。此值通常为6,考虑最大的兼容性,此值不建议为其他值。
  • BPB_Reserved:保留
  • BS_DrvNum:含义与FAT12/16字段一样
  • BS_Reserved:含义与FAT12/16字段一样
  • BS_BootSig:含义与FAT12/16字段一样
  • BS_VolID:含义与FAT12/16字段一样
  • BS_VolLab:含义与FAT12/16字段一样
  • BS_FilSysType:始终为"FAT32 ",对FAT类型的确定没有任何影响。
  • BS_BootCode32:引导启动程序,与平台有关,不使用时填充为0
  • BS_BootSign:0xAA55,引导签名,指示这是一个有效的引导扇区
    当扇区大小大于512字节时,剩余的字段应全部使用0x0填充。

以上就是引导扇区内容的详细分析了,通过引导扇区的内容,我们即可知道FAT文件系统依赖的硬件存储空间大小、簇大小、扇区大小以及以及FAT系统版本等重要信息。

同时通过引导扇区的内容,我们便可计算出对应的FAT的四个区域的大小及起始偏移位置等重要信息,接下来计算FAT四个分区的起始位置及大小。

4.3 分区偏移及大小计算

FAT卷总共分为以下四个区域:

  • 保留区
    • 第一个扇区为引导扇区,存放BPB(BIOS Parameter Block)数据,存放的是FAT卷的配置参数。
    • 上述参数中以 BPB_ 命名的字段都是 BPB 的一部分,而以 BS_ 标题命名的字段都不是 BPB 的一部分,而只是引导扇区的一部分
  • FAT区(分区表装载区)
  • 根目录区
  • 数据区

各分区偏移地址及大小如下:

分区名 起始扇区 起始地址 分区大小 备注
保留区 0x00 0x00 扇区数: BPB_RsvdSecCnt = 6
字节数: BPB_BytsPerSecBPB_RsvdSecCnt = 6 ✖ 512 = 3072 = 0xC00
FAT区 FatStartSector = BPB_RsvdSecCnt = 6 0xC00 扇区数:FatSectors = BPB_FATSz??BPB_NumFATs = 237 ✖ 2 = 474
字节数:474 ✖ 512 = 242688 = 0x3B400
根目录区 RootDirStartSector = FatStartSector + FatSectors = 6 + 474 = 480 0x3B400 + 0xC00 = 0x3C000 扇区数:RootDirSectors = (32 ✖ BPB_RootEntCnt+ BPB_BytsPerSec - 1) / BPB_BytsPerSec = (32 * 512 + 512 - 1) / 512 = 32
字节数:32 ✖ BPB_RootEntCnt = 32 ✖ 512 = 16384 = 0x4000
根目录区每个目录项占32Byte
数据区 DataStartSector = RootDirStartSector + RootDirSectors = 480 + 32 = 512 0x3C000 + 0x4000 = 0x40000 扇区数:DataSectors = BPB_TotSec - DataStartSector = 242688 - 500 = 242176

此外,关于FAT区,通常存在一个以上的FAT,如此处所格式化的sd卡便存在两个FAT,对应的偏移地址和大小如下:

FAT 起始扇区 起始地址 分区大小 备注
FAT1 6 0xC00 扇区数:237
字节数:237 ✖ 512 = 121344
FAT2 6 + 237 = 243 0xC00 + 237 ✖ 512 = 0x1E600 扇区数:237
字节数:237 ✖ 512 = 121344

4.4 FAT子类型确认

关于FAT的类型是FAT12/16/32确认:FAT类型由数据区内簇的数量决定,除此之外无其他办法!

  • 当一个卷,簇的数量 ≤4085 时,为FAT12
  • 当一个卷,簇的数量 ≥4086 且 ≤65525 时,为FAT16
  • 当一个卷,簇的数量 ≥65526 时,为FAT32

簇的数量计算公式:CountofClusters = DataSectors / BPB_SecPerClus;
如我们这里:CountofClusters = 242176 / 4 = 60544,所以为 FAT16!

当簇的大小从 512 ~ 32768字节的各种条件下,不同类型FAT对应卷的大小范围如下:
在这里插入图片描述

4.4 访问FAT条目

FAT区由一条条FAT条目构成,关于 FAT[N] 对应的条目具体位置计算如下:

  • FAT16:在这里插入图片描述
  • FAT32:
    在这里插入图片描述

格外需要注意的是,不同格式,对应的FAT条目的长度和格式不一样:

此外对于FAT32格式,高4位是保留位,只有低28位有效!

具体如下图所示:

在这里插入图片描述

4.5 文件与簇之间的关系

那么文件和簇之间的相互关系又是怎样的呢?我们又是如何准确的找到存放在flash上的文件的呢?接下来让我们看下文件与簇之间的关系映射。

在FAT卷上文件通过目录管理,目录是一个32字节数组组成的目录条目结构,此目录结构包含:文件名、文件大小、时间戳以及文件所在的第一个簇号。

簇号为0和1的簇被保留,由参数BPB_RootClus可知,有效簇从第2号簇开始。FAT[2](2号簇)对应数据区的第一个簇

因此第N个簇的位置计算公式如下:
FirstSectorofCluster = DataStartSector + (N - 2) * BPB_SecPerClus

每个条目所在的位置,对应一个簇。当文件长度大于一个簇长度时,条目内的值为下一个条目的索引,直到文件所在的最后一个簇,由此构成簇链!文件所在的最有一个簇所对应的FAT条目内的值由一个特殊的值(EOC)组成,它永远不会匹配任何有效的簇号,如下:

  • FAT12: 0xFF8 - 0xFFF (typically 0xFFF)
  • FAT16: 0xFFF8 - 0xFFFF (typically 0xFFFF)
  • FAT32: 0x0FFFFFF8 - 0x0FFFFFFF (typically 0x0FFFFFFF)

存在一些特殊的值被用来做损坏簇的标记,如下:

  • FAT12: 0xFF7
  • FAT16:0xFFF7
  • FAT32:0xFFFFFFF7

不过此处需要注意,在FAT12/16系统上,上述特殊值绝不会和任何有效簇匹配,但是在FAT32上有可能,因此为了防止混淆,你在创建FAT32系统的时候应该避免这种情况发生!因此FAT32系统上簇的上限为268435445(大于256M个簇)

FAT条目初始化的时候,FAT[2] 及以后的数据应被初始化为0,指示未被使用处于空闲状态,如果值不为0,则意味着簇被损坏或被使用状态。在FAT12/16系统上,空闲簇的数量未被记录,而在FAT32系统上,FAT32支持FSInfo结构体,里面记录了空闲簇的数量。

关于FAT[0]和FAT[1]:

此两个保留的条目,没有与任何簇有联系;不过具有其他意义,如下:

  • FAT12: FAT[0] = 0xF??; FAT[1] = 0xFFF;
  • FAT16: FAT[0] = 0xFF??; FAT[1] = 0xFFFF;
  • FAT32: FAT[0] = 0xFFFFFF??; FAT[1] = 0xFFFFFFFF;

FAT[0]中的?? 与 BPB_Media 相同;

FAT[1] 记录了错误历史记录:卷脏标志(FAT16:bit15、FAT32:bit31),系统在启动的时候清除此位,正常关闭的时候恢复。
如果此位已经清除,表明上次未被正常关闭,可能存在逻辑卷错误;硬件错误标志(FAT16:bit14、FAT32:bit30)当出现无法恢复的读写错误时清除,表明需要进行全面检查。

关于FAT区域,有两个重点注意事项:

  1. 第一个是FAT的最后一个扇区可能没有被完全使用。在大多数情况下,FAT在扇区的中间结束。FAT驱动程序不应该对未使用的区域做出任何假设。在格式化卷时,应该用零填充它,并且在此之后不应更改它。
  2. 另一个是BPB_FATSz16/32可以指示比卷需要的值大的值。换句话说,未使用的扇区可以跟随每个FAT。这可能是数据区域对齐或其他原因导致的。同时,在格式化时这些扇区也会被用零填充。

下表展示了不同FAT类型中FAT值所对应的含义解释:

在这里插入图片描述

4.6 FSInfo扇区结构及备份引导扇区

此部分内容只在FAT32系统上存在,对于FAT12系统FAT区域大小最大6KB,对于FAT16系统FAT区域最大128KB,但是在FAT32系统上FAT区域通常上达数MB,这是因为FAT32系统支持FSInfo数据结构。

在FAT32系统上新增FSInfo数据结构的原因是:在FAT12/16系统上,想要知道flash上剩余的簇数需要扫描整个FAT区才能计算出来,但随着flash容量的不断扩大,扫描花费的时长越来越长,为了避免扫描浪费过多的时间,因此在FAT32系统上增加了FSInfo结构,用于记录flash上剩余的簇数。

FSInfo数据结构如下:

字段名 偏移 大小 描述
FSI_LeadSig 0 4 固定值为0x41615252,头部签名
FSI_Reserved1 4 480 保留区域,采用0x00覆盖
FSI_StrucSig 484 4 固定值为0x61417272,也是一个签名
FSI_Free_Count 488 4 记录了空闲的簇数,如果这个值为0xFFFF FFFF,则表示不知道具体的空闲簇数
FSI_Nxt_Free 492 4 提示驱动程序应该从此参数提示的簇开始寻找空闲的簇,通过此参数便可以不用从FAT区头开始寻找下一个空闲簇了,节省了大量时间;如果此参数为0xFFFF FFFF,则驱动程序应该从头部(2号簇)开始寻找空闲簇
FSI_Reserved2 496 12 保留区域,采用0x00覆盖
FSI_TrailSig 508 4 固定值0xAA550000,尾部签名

注意:当扇区大小大于512字节时, 剩余空间采用0x00填充

4.7 FAT目录

FAT目录分为长文件名目录(LFN)以及短文件名目录(SFN),长文件目录是在短文件名目录上的一个扩展,具体采用长文件名还是短文件名由读取FAT文件系统的操作系统决定,如windows;设置长文件名时短文件名也被设置,具有兼容性。

此外,有一个很重要的概念:在FAT文件系统上目录也是一个文件,只是此文件的属性不一样而已

在所有目录中,有一个比较特殊的是根目录,且根目录作为顶层目录必须存在。

  • 在FAT12/16系统中,根目录不是一个文件,且放在根目录区,根目录的最大条目数由 BPB_RootEntCnt 参数指示;

  • 在FAT32系统中,根目录与子目录没有什么区别,且根目录的起始簇由 BPB_RootClus 参数指示。

  • 根目录与子目录的另外一个区别是,根目录不包含 . .. 此两个点目录,且它可以包含卷标(具有ATTR_VOLUME_ID属性的条目)

4.7.1 SFN 短文件名目录

目录条目结构如下:

字段名 偏移 大小 说明
DIR_Name 0 11 短文件名
DIR_Attr 11 1 文件属性标志,由以下标志组合构成(高2位保留且为0)
0x01: ATTR_READ_ONLY (Read-only)
0x02: ATTR_HIDDEN (Hidden)
0x04: ATTR_SYSTEM (System)
0x08: ATTR_VOLUME_ID (Volume label)   根目录项中有且仅有一个目录具有此属性,且具有此属性的目录 DIR_FstClusHI, DIR_FstClusLO, DIR_FileSize 字段需要为0
0x10: ATTR_DIRECTORY (Directory)
0x20: ATTR_ARCHIVE (Archive)  备份使用
0x0F: ATTR_LONG_FILE_NAME (LFN entry)
DIR_NTRes 12 1 指示 SFN 大小写信息的可选标志
0x08: 文件名内的每个字符都是小写
0x10: 扩展名内每个字符都是小写
DIR_CrtTimeTenth 13 1 可选的次秒级信息对应于 DIR_CrtTimeDIR_CrtTime 的时间分辨率为2秒,因此该字段给出的计数为亚秒,其有效值范围为0到199,单位为10毫秒。如果不受支持,则设置为零,以后不要更改。
DIR_CrtTime 14 2 可选的创建文件的时间,如果不支持,应设置为0且之后不能被改变
DIR_CrtDate 16 2 可选的创建文件的日期,如果不支持,应设置为0且之后不能被改变
DIR_LstAccDate 18 2 可选的最后访问日期,如果不支持,应设置为0且之后不能被改变
DIR_FstClusHI 20 2 指示文件所在簇号的上半部分,在FAT12/16系统上始终为0
DIR_WrtTime 22 2 文件被修改的最后时间
DIR_WrtDate 24 2 文件被修改的最后日期
DIR_FstClusLO 26 2 指示文件所在簇号的下半部分,注意当文件大小为0时,不会有簇分配,此时此值为0;当此文件为一个目录时,此值始终为有效值
DIR_FileSize 28 4 文件大小,单位:字节;当此文件为目录时,此参数未被使用,且应该设为0

关于目录结构的第一个字段 DIR_Name 的第一个元素 DIR_Name[0] 在目录表中有着特殊作用,如下:

  • 当此值为 0xE5 时,代表此目录条目未被使用(或已废弃)
  • 当此值为 0x00 时,也代表此目录条目未被使用;此外还提示后续目录条目也未被使用,因为后续的目录条目 DIR_Name[0] 都会是 0x00
  • 如果文件名的第一个字符为 0xE5 这个特殊值,则使用 0x05 替代。

这么设计的意义是什么呢?将 DIR_Name[0] 用作特殊字符,其目录在于方便文件删除!当我们删除一个文件的时候,文件系统并不会将此文件所对应的数据全部删除,因为那样太费时间了,也没有必要,而是直接将对应文件的目录项中的 DIR_Name[0] 修改为 0xE5 即可!


关于文件名字段 DIR_Name,在FAT文件系统中还有如下规定:

  • DIR_Name 字段的11字节的文件名分为两个部分:8 字节的主文件名 + 3字节的扩展名
  • 文件名中主文件名与扩展名中间的 . 被省略,不在此记录
  • 如果主文件名长度不够,小于8字节,则使用 0x20 空格填充
  • 用于设置文件名的字符也有限制,支持的字符有 0~9 A~Z ! # $ % & ’ ( ) - @ ^ _ ` { } ~
  • 主文件名和扩展名中的(a~z)ASCII字符都会被转化成大写字符保存

以下为文件名存储示例:
在这里插入图片描述

4.7.2 LFN长文件名

长文件名是文件名的另外一种存储方式,由于SFN短文件名具有长度、字符等限制,在一些场景下不能很好的满足需求,因此就需要使用到长文件名,关于长文件名的具体内容如下:

长文件名是一个具有特殊属性的目录条目。长文件名目录属性 DIR_Attr 字段的值 ATTR_LONG_NAME = (ATTR_READ_ONLY | ATTR_HIDDEN | ATTR_SYSTEM | ATTR_VOLUME_ID) = 0x0F
在这里插入图片描述
关于长文件名的目录属性如下:

字段名 偏移 大小 描述
LDIR_Ord 0 1 序号(1-20),用来表示此条目属于长文件名序列条目中的第几条。且长文件名序列首条条目的值应& 0x40以进行标识!
LDIR_Name1 1 10 长文件名 第1 ~ 第5 字符(注意此处一个字符占两个字节)
LDIR_Attr 11 1 长文件名属性,此值永远为 ATTR_LONG_NAME 0x0F
LDIR_Type 12 1 类型,必须为0
LDIR_Chksum 13 1 和校验
LDIR_Name2 14 12 长文件名 第6 ~ 第11 字符(注意此处一个字符占两个字节)
LDIR_FstClusLO 26 2 必须为0
LDIR_Name3 28 4 长文件名 第12 ~ 第13 字符(注意此处一个字符占两个字节)

关于长文件名,有以下几点重要概念:

  • 一个文件一定有短文名SFN,但不一定有长文件名LFN
  • 长文件名LFN字段中仅包含文件名信息,不包含其他内容,其他内容需要通过短文件名SFN查看
  • 如果一个文件既有长文件名也有短文件名,则长文件名是其主要名字,而短文件名则为附加名字
  • 长文件名LFN条目在对应的短文件名SFN条目前面
  • 一个文件的长文件名最长255字符,对应最多20个长文件名LFN条目
  • 长文件名简单理解起始就是存储一个字符串,因此没有类似SFN的限制,允许有空格、支持大小写、允许多个.符号等
  • LFN条目文件名长度不够,仍然采用0x20填充

下图是官方关于一个名为 “MultiMediaCard System Summary.pdf” 的长文件名在flash上的长文件名条目,如下所示,一眼没看明白也没关系,后文有实例说明,对长文件名有概念了就行!
在这里插入图片描述
关于长文件名的checksum字段和计算,算法如下:

uint8_t create_sum (const DIR* entry)
{
    int i;
    uint8_t sum;

    for (i = sum = 0; i < 11; i++) { /* Calculate sum of DIR_Name[] field */
        sum = (sum >> 1) + (sum << 7) + entry->DIR_Name[i];
    }
    return sum;
}

4.7.3 LFN系统对于SFN的兼容

在使用LFN长文件名的系统中,会自动生成SFN短文件名已确保此文件在短文件名的文件系统中可使用。同时为了防止生成的短文件名冲突,SFN的生成采用 名称+数字后缀+扩展 的格式,同时采用以下规则生成SFN:

  1. 小写自动转大写
  2. 如果存在空格,则删去空格,设置有损转换标识
  3. .开头的文件删除头部的.,并设置有损转换标识
  4. 存在多个.的文件名,仅保留最后一个作为文件名与扩展的分隔,并设置有损转换标识
  5. 其他不支持的字符,采用_代替,并设置有损转换标识
  6. 文件名如果是Unicode编码,则转化为ANSI/OEM编码;不能转换的字符采用_代替,并设置有损转换标识
  7. 长度超过8字节的部分,截断,并设置有损转换标识
  8. 扩展名字段超过3字节的,截断,并设置有损转换标识

有损转转换标识为:~,ASCII值为0x7E,十进制126

示例如下:
在这里插入图片描述


至此,FAT文件系统的理论部分已经描述完了,接下来我们继续使用winhex对数据进行分析。

5. 分区分析

继续回顾我们一开始的这张布局图
在这里插入图片描述

5.1 保留分区分析

保留分区为第一个分区,其中引导扇区位于保留分区的第一个扇区。

根据 4.3 章节计算结果可知,保留分区起始地址为 0x00大小 0xC00

保留分区数据如下,保留分区内最重要的内容即为引导扇区,除引导扇区外,其他剩余空间全部保留,采用0x00覆盖。关于引导扇区已在 4.2 章节详细分析,此处不再做介绍。

在这里插入图片描述

5.2 FAT区分析

根据 4.3 章节描述,FAT区的起始地址为 0xC00,大小为 0x3B400,此外存在两个FAT区,FAT1和FAT2,起始地址分别为:0xC000x1E600,对应地址数据如下:

FAT1 数据:
在这里插入图片描述
FAT2 数据如下:
在这里插入图片描述

由于此处采用FAT16格式,所以每个FAT条目占据两个字节!

根据上述数据进行分析:

  1. 确认 FAT2 为 FAT1 的备份;
  2. 存在5个FAT条目其中 FAT[0] 和 FAT[1] 为保留条目,FAT[0] 的内容与 BPB_Media 媒体类型字段一致,FAT[1] 用来记录错误历史记录 (详见 4.5 章节描述)
  3. 根据4.5章节描述,FAT[2](2号簇)对应数据区的第一个簇,又FAT[2]、FAT[3]、FAT[4] 数据均为 0xFF,表明存在三个文件,且每个文件的大小小于等于一个簇的空间;且分别存放在数据区第1到第3个簇上!

此处可能大家会由疑问,刚刚格式化的sd卡为什么会存在文件内,其实这个是系统文件,格式化后自带的,默认是隐藏的,只有使用winhex才能看到,也就是对应的System Volume Information文件夹。

5.3 根目录区分析

注意,根目录区只有 FAT12 / FAT16 系统上存在,在FAT32系统上不存在此区域。

根目录区用来记录根目录下的文件内容,根据 4.3 章节计算可知,根目录区起始地址为:0x3C000,大小为0x4000,数据内容如下:

在这里插入图片描述
以下是对数据字段进行分析后的内容,如下图所示:
在这里插入图片描述

格式化之后,默认会生成一个System Volume Infomation的系统文件夹,同时此文件夹是根目录下唯一的一个文件,因此在根目录的数据如上图所示。

  1. 此文件夹为目录属性,是隐藏的系统目录
  2. 长文件名为System Volume Information,短文件名为SYSTEM~1
  3. 此目录指向存放的数据在2号簇(对应数据区第一个簇),文件大小字段,由于此文件为目录属性,此字段无意义,因此强制为0

至此,根目录区分析完了,同时根目录区的 System Volume Information文件指向数据区第一个簇(2号簇),接下来我们便进入数据区进行分析。

5.4 数据区分析

根据 4.3 章节计算可知,数据区起始地址为:0x40000,大小为242176 * 512 = 0x764 0000,数据内容如下:
在这里插入图片描述
对应数据字段分析如下:
在这里插入图片描述

System Volume Information 目录下存在两个文件,分别是IndexerVolumeGuidWPSettings.dat。根据上述分析可知:

  • IndexerVolumeGuid文件的数据存放在 FAT[3],3号簇上,即数据区的第3个簇(数据区的第1个簇为2号簇);
  • WPSettings.dat 文件的数据存放在 FAT[4],4号簇上,即数据区的第2个簇(数据区的第1个簇为2号簇);

首先,我们跳转到4号簇上查看IndexerVolumeGuid的数据,对应地址计算方式为:
FirstSectorofCluster = DataStartSector + (N - 2) * BPB_SecPerClus = 512 + (4 - 2) * 4
= 520;

对应地址为: FirstSectorofCluster * BPB_BytsPerSec = 520 * 512 = 0x0004 1000

在这里插入图片描述


接着跳转到3号簇上查看WPSettings.dat的数据,对应地址计算方式为:
FirstSectorofCluster = DataStartSector + (N - 2) * BPB_SecPerClus = 512 + (3 - 2) * 4
= 516;

对应地址为: FirstSectorofCluster * BPB_BytsPerSec = 520 * 512 = 0x0004 0800

在这里插入图片描述

5.5 新增文件测试

  1. 在根目录下新增 test 目录,使用winhex更新磁盘数据,观察各数据区变化
  • 保留区无变化

  • FAT区变化如下:
    在这里插入图片描述
    在这里插入图片描述

  • 根目录区变化如下:
    在这里插入图片描述

  • 数据区变化:
    在这里插入图片描述

  1. 新增long file test文件夹,里面存入一个 长度为 2050 Byte(占据两个簇的空间)test.txt文件,使用winhex重新打开磁盘进行分析。
    在这里插入图片描述
  • 保留区无变化
  • FAT区变化如下:
    在这里插入图片描述
    在这里插入图片描述
  • 根目录区变化如下:
    在这里插入图片描述
  • 数据区变化如下:
    long file test 目录数据指向6号簇,跳转至6号簇,地址 DataStartSector + (N - 2) * BPB_SecPerClus = 0x40000 + (6-2) * 4 * 512 = 0x420000
    在这里插入图片描述
    test.txt 文件指向 7号簇,跳转至7号簇,地址 DataStartSector + (N - 2) * BPB_SecPerClus = 0x40000 + (7-2) * 4 * 512 = 0x428000,均为test.txt的实际有效数据,如下:
    在这里插入图片描述
    在这里插入图片描述

6. 总结

以上便是关于FAT文件系统的全部分析了,通过上述分析,外加新增文件辅助理解,对于文件在FAT文件系统下如何管理、存储,相信已经有了非常深入的了解。

FAT文件系统分为四个区:

  1. 保留区最重要的是里面包含引导扇区,引导扇区内存放着BIOS参数信息,通过此参数可以知道FAT文件系统的flash布局,以及flash大小,fat块大小、簇大小等关键信息;
  2. FAT区,记录了文件所占用簇的情况,以及对于文件大小大于一个簇的文件,在FAT区内形成簇链,记录文件由哪几个簇组成
  3. 根目录区,只有FAT12/16系统所有,记录了根目录下的文件/目录条目信息
  4. 数据区,记录数据分为两个部分,第一部分为目录信息,除根目录外,每个文件夹需要占据一个及以上的簇描述对应目录下的文件情况;第二部分为具体文件数据。两部分数据通过短文件名SFN字段进行关联!

以上就是FAT文件系统的简单概括,由于本文使用的是FAT16文件系统作为实例分析,关于FAT32文件系统,在下一篇博文中进行补充,链接如下:FAT32文件系统详解(点击跳转!!!)

关于FAT文件系统或者本文有关问题,欢迎大家在评论区讨论!


创作不易,转载请注明出处!

关注、点赞+收藏,可快速查收博主有关分享!


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

FATFS文件系统详解 的相关文章

  • Linux文件系统(一)文件系统基本概念

    文件系统基本概念 1 文件系统概述2 文件系统的类型 xff08 1 xff09 ext系列 xff08 2 xff09 Reiserfs 3 Ubuntu文件系统的结构 xff08 1 xff09 概述 xff08 2 xff09 路径
  • Fatfs文件系统向文件写内容出现f_write返回值为1的问题

    f write返回值为1 xff0c 则就是FR DISK ERR 1 A hard error occurred in the low level disk I O layer 低级磁盘I O层中发生硬错误 问题解决方式 xff1a 1
  • 认识romfs文件系统

    1 1 什么是romfs romfs 是一个只读文件系统 xff0c 主要用在 mainly for initial RAM disks of installation disks 使用romfs 文件系统可以构造出一个最小的内核 xff0
  • FATFS_API接口说明表

    FatFs API 应用程序接口 根据 FatFS 的教程手册来看 xff0c 将程序接口分为了四个部分 文件访问 目录访问 文件和目录管理 卷管理和系统配置 文件访问 名称简介f open打开 创建文件f close关闭已打开的文件f r
  • 芯片的SD/MMC控制器以及SD卡介绍

    1 MMC SD卡 eMMC介绍 1 1 三者关联 1 最早出现的是MMC卡 卡片式结构 按照MMC协议设计 相较于NandFlash芯片来说 MMC卡有2个优势 第一是卡片化 便于拆装 第二是统一了协议接口 兼容性好 2 后来出现SD卡
  • 【嵌入式】基于FATFS/Littlefs文件系统的日志框架实现

    嵌入式 基于FATFS Littlefs文件系统的日志框架实现 文章目录 嵌入式 基于FATFS Littlefs文件系统的日志框架实现 1 概述 2 设计概要 3 设计实现 3 1 初始化 init 3 2 日志写入 write 3 3
  • ARM单片机FATFS文件系统的移植

    ARM单片机FATFS文件系统的移植 测试效果 前提条件 下载所需源码 FATFS 文件系统 SFUD万能驱动 加入工程 接口驱动 测试代码 FreeRTOS10 0 1 FATFS FF14A SFUD V1 1 0 STM32F103Z
  • 六、操作系统之文件管理

    六 文件管理 文件系统的概念 文件系统时OS与用户关系最紧密的一部分 对用户来说 它是OS中最直观的部分 能否方便使用OS 以及OS的可信赖程度往往取决于文件系统的功能和性能 1 文件和文件系统 2 文件系统的功能 3
  • S3C2440读写sd卡的一些总结

    整理硬盘的时候发现这个文档 以前写2440操作sd卡程序的时候总结的 1 我的2440 sdi对sd卡发送ACMD41时总是反馈crc fail 但是可以得到正确的response sd卡可以正常使用 2 sd卡可以没有mbr 在物理的 0
  • 【linux kernel】记一次linux内核裁剪总结

    记一次linux内核裁剪总结 一 背景 在linux内核构建过程中 根据实际应用场景 小生构建了一个大约5 7M大小的linux内核zImage镜像 发现其稍微有点大了 想着将其裁剪和缩小 于是乎对linux内核进行裁剪 本篇文章主要记录一
  • FAT16 FAT32 文件系统

    FAT 英文为File Allocation Table 文档分配表 先要记住几个概念 扇区 一般扇区为512个字节 簇 由若干个扇区组成 而FAT文件系统 其他文件系统应该相似 就是专门管理这些簇的 一个文件可能占据一个或者多个簇 按正确
  • 嵌入式设备文件系统构建——增加用户登录功能

    1 修改inittab文件 first run the system script file sysinit etc init d rcS 进入命令行 askfirst bin sh 添加执行登录验证 sysinit bin login c
  • Linux read的核心函数generic_file_buffered_read

    内核 5 9 0 流程图 generic file buffered read一种调用路径 cat某个文件触发 0 ondemand readahead mapping 0xffff888005c61340 ra 0xffff8880059
  • 深入理解overlayfs(一):初识

    版权声明 本文为CSDN博主 luckyapple1028 的原创文章 遵循CC 4 0 BY SA版权协议 转载请附上原文出处链接及本声明 原文链接 https blog csdn net luckyapple1028 article d
  • buffer_head数据结构

    内核版本 5 9 0 数据结构 Historically a buffer head was used to map a single block within a page and of course as the unit of I O
  • GPT概述

    全局唯一标识分区表 GUID Partition Table 缩写 GPT 是一个实体硬盘的分区结构 它是可扩展固件接口标准的一部分 用来替代BIOS中的主引导记录分区表 传统的主启动记录 MBR 磁盘分区支持最大卷为 2 2 TB ter
  • 关于如何修复烧写镜像文件失败的SD卡

    前言 使用某些软件 比如 win32 Disk Imager 向SD卡烧写镜像文件时 很有可能出现烧写失败的情况 通常如果烧写失败 系统会弹出请求格式化SD卡的提示框 此时不要点格式化 点了可能会造成不可挽救的结果 也可能不会 而是进行以下
  • STM3利用FATFS向SD卡文件追加数据的三种方法

    1 f sync SDFile 该方法简单粗暴 适合一直连续向sd卡中写入数据 while之前就f open 循坏内重复 向缓存填充数据 gt 写入数据 gt 刷新写入 整个过程并没有f colse retSD f open SDFile
  • [架构之路-185]-《软考-系统分析师》-3-操作系统基本原理 - 文件索引表

    目录 一 文件的索引块 二 索引分配表 三 索引表的链接方案 四 多层索引 五 混合索引分配 一 文件的索引块 存放在目录中的文件 并非是文件的真实内容 目录中记录了文件的索引块是几号磁盘块 文件对应的索引表是存放在指定的磁盘块中的 二 索
  • 是否有 FAT FS 驱动程序希望引导扇区的字节 508 和 509 为零?

    在实施的同时我自己的引导扇区加载程序从 2012 年开始 https hg ulukai org ecm ldosboot rev 17884e6352e6 l1 255我确保将偏移量 508 和 509 处的字节清零 这些是标准 512

随机推荐

  • C# 基础程序结构和入门实例(学习心得 2)

    超级小白友好 讲解C 基础 每集5分钟轻松学习 拒绝从入门到放弃 C Hello World 实例 C 最小的程序结构需要包含以下部分 命名空间声明 一个 class 一个 Class 方法 该 Class 方法的属性 一个 Main 方法
  • python中class的作用

    在 Python 中 class 是用来定义对象的类型的 通过定义类 可以创建该类型的多个实例 每个实例都有相同的属性和方法 类也可以继承其它类 并扩展或重写属性和方法
  • 人工智能:未来制胜之道

    在数据 算法 计算 场景驱动新一轮人工智能飞速发展 未来3 5年内人工智能将处于服务智能阶段 即技术边际突破但应用海量拓展 人工智能未来竞争格局将由生态构建者 技术算法驱动者 应用聚焦者 垂直行业先行者 基础设施提供者五类竞争定位模式主导
  • springgateway限流-令牌桶算法

    限流配置 参见 https blog csdn net forezp article details 85081162 https cloud spring io spring cloud gateway 2 2 x reference h
  • 学计算机的感想300字,大学生计算机实训心得体会3篇

    大学生计算机实训心得体会3篇 我们有一些启发后 可以通过写心得体会的方式将其记录下来 这样我们可以养成良好的总结方法 那么心得体会到底应该怎么写呢 下面是小编为大家整理的大学生计算机实训心得体会 欢迎大家分享 大学生计算机实训心得体会1 在
  • 在服务器上下载安装anaconda

    anaconda下载与安装 1 连接到服务器 进入服务器界面 同时连上网络 登录到Anaconda官网 如果你的服务器是Linux系统 选择这一款 2 打开服务器的终端 Open in Terminal 进入命令行输入 bash Anaco
  • DATAX_HOOK,怎么实现的

    DATAX HOOK 怎么实现的 JobContainer 类Datax的job执行类 JobContainer 类 An highlighted block JobContainer 类关于 finally if isDryRun thi
  • 【portainer.io】可视化界面学习

    portainer io 学习目标 学习内容 portaine背景 docker基于界面管理工具Portainer 安装portainer 启动portainer 使用portainer 学习总结 学习目标 学习内容 了解portainer
  • maven当中如何用SpringMVC和mybatis创建一个项目

    创建一个SpringMVC mybatis项目 马克 to win 马克 java社区 防盗版实名手机尾号 73203 下部我们做一个SpringMVC mybatis的例子 我们还是继续刚才项目的底子 参见我的mybatis那一章 这个例
  • 本地ecshop网站怎么上传到服务器,ECSHOP商品批量上传,ECSHOP商品数据包导入

    各位ECSHOP网店系统用户大家好 欢迎来到ECSHOP商品批量上传 ECSHOP商品数据导入设置 第一节 1 1 进入后台管理中心 商品管理 商品批量上传 进入商品批量上传页面 如图所示 1 2 您可以在上图中红色方块区域标注中下载批量上
  • linux protobuf静态库,Mac下交叉编译protobuffer版本库(C++版)

    一 前言 这几天和开发jni的同学对接SDK 其中包含了protobuffer和openssl库 这里主要说一下protobuffer编译mac环境静态库过程及遇到的问题 在此记录下来供后续参考 由于对linux及英文不是很熟 过程中也感谢
  • 如何合并多个工作表或多个工作簿?3种合并方法都在这

    分享三个方法 一分钟搞定 简单 快速 步骤少 总有一个适合你 话不多说 往下看 01 多个工作簿单张工作表的合并 如下图所示 我们有几个区域的销售数据分别放在不同的工作簿中 每个工作簿内只有一张工作表 每个工作簿的表格结构是一致的 现在我们
  • android 防止反编译的若干方法

    第一种方式 混淆策略 混淆策略是每个应用必须增加的一种防护策略 同时他不仅是为了防护 也是为了减小应用安装包的大小 所以他是每个应用发版之前必须要添加的一项功能 现在混淆策略一般有两种 对代码的混淆 我们在反编译apk之后 看到的代码类名
  • vue中父组件异步数据通过props方式传递给子组件,子组件接收不到的问题

    vue中父组件异步数据通过props方式传递给子组件 子组件接收不到的问题 问题描述 组件化开发中经常用到父子组件的通信 父传子子传父等数据的操作 如果父组件的数据是发请求从后端获取的异步数据 那么父组件将这个数据传递给子组件的时候 因为是
  • 超详细的 pytest 教程【入门篇】

    前言 关于自动化测试 这些年经历了太多的坑 有被动的坑 也有自己主动挖的坑 在这里做了一些总结 主要思考总结下这些年来自动化测试过程中的一些基本的东西 例如何时进行自动化 如何自动化 或是怎么自动化我们的测试工作 接下来我们先对pytest
  • idea全局搜索失效,Ctrl+shift+F快捷键不起作用

    方法1 是否与搜狗等输入法软件存在快捷键冲突 当然也可能是你新下载的什么软件导致的快捷键冲突导致IDEA全局搜索失效 比如下图 可以改掉输入法的快捷键或者直接关闭输入法的快捷键 这样idea的全局搜索功能就恢复了 方法2 终极办法 如果你一
  • Leetcode 268. 缺失数字(有效利用异或和)

    缺失数字 给定一个包含 0 1 2 n 中 n 个数的序列 找出 0 n 中没有出现在序列中的那个数 示例 1 输入 3 0 1 输出 2 示例 2 输入 9 6 4 2 3 5 7 0 1 输出 8 class Solution publ
  • js 事件发布订阅销毁

    在vue中 通过 on订阅事件 通过 emit触发事件以此可用来事件跨组件传值等功能 但是有个弊端就是通过这种方式订阅的事件可能会触发多次 特别是通过 on订阅的事件中如果有http请求 将会造成触发一次会发出很多同样的http请求 造成资
  • AppZygote是什么?

    众所周知 app进程 SystemServer进程都是由Zygote进程孵化的 最近梳理这块逻辑的时候 无意中发现了一个叫AppZygote和一个叫AppZygoteInit的java类 挺新鲜的 先看看源码开头对AppZygote的定义吧
  • FATFS文件系统详解

    FATFS文件系统详解 文章目录 FATFS文件系统详解 1 简介 2 基础概念 3 FAT文件系统组成介绍 4 FAT文件系统分析 4 1 采用FAT格式格式化SD nand sd卡 4 2 引导扇区分析 4 3 分区偏移及大小计算 4