本文已参与「新人创作礼」活动,一起开启掘金创作之路。
一、前言
硬盘是用来存储数据的,为了使用和管理方便,这些数据以文件的形式存储在硬盘上。任何操作系统都有自己的文件管理系统,不同的文件系统又有各自不同的逻辑组织方式。例如:常见的文件系统有FAT,NTFS,EXT,UFS,HFS+等等。本博客是基于linux的FAT32文件系统讲解,应用于嵌入式产品磁盘存储的预分配。
二、FAT32文件系统结构
FAT32文件系统由DBR及其保留扇区,FAT1,FAT2和DATA四个部分组成,其机构如下图:
DBR 及保留区 | FAT1 | FAT2 | 数据区 |
这些结构是在分区被格式化时创建出来的,含义解释如下:
DBR及其保留扇区:DBR的含义是DOS引导记录,也称为操作系统引导记录,在DBR之后往往会有一些保留扇区。
FAT1:FAT的含义是文件分配表,FAT32一般有两份FAT,FAT1是第一份,也是主FAT。
FAT2:FAT2是FAT32的第二份文件分配表,也是FAT1的备份。
DATA:DATA也就是数据区,是FAT32文件系统的主要区域,其中包含目录区域。
保留区从第一扇区开始,保存了该逻辑盘每扇区字节数,每簇对应的扇区数等等重要参数和引导记录,之后还留有若干保留扇区。
文件分配表区共保存了两个相同的文件分配表,因为文件所占用的存储空间(簇链)及空闲空间的管理都是通过 FAT 实现的,FAT如此重要,保存两个以便第一个损坏时,还有第二个可用。文件系统对数据区的存储空间是按簇进行划分和管理的,簇是空间分配和回收的基本单位,即,一个文件总是占用若干个整簇,文件所使用的最后一簇剩余的空间就不再使用,而是浪费掉了。
根目录区(ROOT 区)是不固定区域、不固定大小的,可看作是数据区的一部分。一般情况下从第二簇开始使用,大小视需要增加。
2.1、WinHex工具磁盘分析Fat32文件系统
2.1.1选择打开磁盘
2.1.2选择磁盘
WinHex工具分析出Fat32文件系统中的各个分区信息。
三、分析FAT32文件系统的DBR
FAT32文件系统的DBR有5部分组成,分别为跳转指令,OEM代号,BPB,引导程序和结束标志。如下图是一个完整的FAT32文件系统的DBR。
跳转指令:本身占2字节它将程序执行流程跳转到引导程序处。
OEM代号:这部分占8字节,其内容由创建该文件系统的OEM厂商具体安排。
BPB:FAT32的BPB从DBR的第12个字节开始,占用79字节,记录了有关该文件系统的重要信息,各参数解释如下表:
起始字节 | 定义 | 字节数 | 备注 |
0x0 | 跳转指令 | 3 | |
0x3 | 文件系统标志和版本 | 8 | 有各种默认值:MSDOC5.0 |
0x0B | 每个扇区字节数 | 2 | 有的可能是1024、2048、4096 |
0x0D | 每簇扇区数 | 1 | 这个值不能为0,而且必须是2的整数次方,比如1、2、4、8、16、32、64、128.但是这个值不能使每个簇超过32KB字节 |
0x0E | 保留扇区数 | 2 | |
0x10 | Fat表分数 | 2 | |
0x11 | Fat16中根目录的文件总数 | 2 | |
0x13 | FAT16扇区总数 | 2 | |
0x15 | 哪种储存介质 | 1 | |
0x16 | 1个FAT16 表所占的扇区数 | 2 | |
0x18 | 每磁道扇区数 | 2 | 仅仅对于有“特殊形状”(由磁头和柱面每 切割为若干磁道)的存储介质有效,63(0x00 3F) |
0x1A | 磁头数 | 2 | 仅仅对特殊的介质才有效,255(0x00 FF) |
0x1C | EBR分区之前所隐藏的扇区数 | 4 | |
0x20 | 文件系统总扇区数 | 4 | |
0x24 | 每一个FAT32表占用扇区数 | 4 | |
0x28 | 标记 | 2 | 此域FAT32 特有 |
0x2A | 根目录起始簇号 | 4 | 默认为2 |
030 | FSINFO(文件系统信息扇区)扇区号 | 2 | 文件系统信息扇区默认为1,该扇区为操作 系统提供关于空簇总数及下一可用簇的信息,参考表:2.1.2 |
0x32 | 备份引导扇区的位置 | 2 | 备份引导扇区总是位于文件系统 的6号扇区 |
0x34 | FAT 系统待扩展使用 | 12 | |
0x40 | 1 | ||
0x41 | 1 | ||
0x42 | 1 | ||
0x43 | 卷序列号 | 4 | 通常为一个随机值 |
0x47 | 卷标 | 11 | 建立文件系统的时候指定了卷 标,会保存在此 |
0x52 | 文件系统格式的ASCII码 | 8 | Fat32 |
0x5A | 未使用 | 410 | |
0x1FE | 签名标记 | 2 | 默认0x55 0xAA |
3.1、引导扇区数据解析
有用的内容用彩色线标志下
⑴.灰色线内容:EB 59 90 跳转指令
⑵.灰色点线内容:4D 53 44 4F 53 35 2E 30 为厂商标志和os 版本号,这里是MSDOS5.0
⑶.红色部分:00 20 (偏移地址0BH,长度2)注意这里数据的布局,高地址放高字节,低地址放低字节(数据为小端格式组织),所以数据应该是0200,就是512。表示的意思是,该磁盘每个扇区有512个字节。有的可能是1024、2048、4096.
⑷.黄色部分:08 (偏移地址0DH,长度1)表示的意思是每个簇有8个扇区。这个值不能为0,而且必须是2的整数次方,比如1、2、4、8、16、32、64、128.但是这个值不能使每个簇超过32KB字节。
⑸.蓝色部分:24 00 (偏移地址0EH,长度2),转换一下,就是00 24,意思是保留区域中的保留扇区数为36个。那么就可以知道下面的FAT1区的开始的地址就是:0x24*0x200(每个扇区的字节数)=0x4800,
⑹.粉色部分:02 (偏移地址10H,长度1),此卷中的FAT结构的份数为2,另外一个是备份的。
⑺.黑色部分:C6 03 (偏移地址24H,长度2)转换一下,03C6,每个FAT占用的扇区数。那么每个扇区占用的字节数就是0x03c6*200=78C00。根据启动区、FAT1、FAT2、根目录、数据区的次序,可以依次计算出它们的地址了。
启动区:理所当然是0x00;
FAT1:0x4800;
FAT2:0x4800 + 0x78C00 = 7D400;
根目录区:7D400 + 78C00 = F6000;
数据区的地址,等等再计算。这个只是计算,可以看看是不是和实际的一致。
Fat1起始地址:
Fat2起始地址:
根目录起始地址:
为什么要计算SD数据的读取要给出地址,而且每次读取都是一个整扇区,512个字节。找出这些地址后,可以很方便的找到数据。
3.2、文件系统信息扇区数据解析
FAT32文件系统在DBR的保留扇区中安排了一个文件系统信息扇区,用以记录数据区中空闲簇的数量及下一个空闲簇的簇号,该扇区一般在分区的1号扇区,也就是紧跟着DBR后的一个扇区,其内容如下:
起始字节 | 定义 | 字节数 | 备注 |
0x00 | 扩展引导标志 | 4 | 0x52526141 |
0x04 | 未使用 | 480 | 全部置0 |
0x1E4 | FSINFO签名: | 4 | 0x72724161 |
0x1E8 | 文件系统的空簇数 | 4 | |
0x1EC | 下一可用簇号 | 4 | |
0x1F0 | 未使用 | 14 | |
0x1FE | 签名标记 | 2 | 默认0x55 0xAA |
示例数据如下:
52 52 61 41:拓展引导标签
接下来480字节未使用,如果安装了操作系统在这个分区上的话应该是有用的。
72 72 41 61:文件系统信息签名
00 14 56 7F:空闲簇数,约为20.3Gb
00 00 00 03:下一个空闲簇号
接下来的14个字节:未用
55 AA:结束标志
引导程序代码:FAT32的DBR引导程序占用420字节,对于没有安装操作系统的分区来说这段程序是没有用处的。
结束标志:DBR的结束标志与MBR,EBR的结束标志相同,为“55 AA”。
四、分析FAT32文件系统的FAT表
FAT32 系统簇号用32 位二进制数表示,大致从 00000002H 到 FFFFFEFFH 个可用簇号。FAT表按顺序依次记录了该盘各簇的使用情况,是一种位示图法。每簇的使用情况用 32 位二进制填写,未被分配的簇相应位置写零;坏簇相应位置填入特定值;已分配的簇相应位置填入非零值,具体为:如果该簇是文件的最后一簇,填入的值为 FFFFFF0FH,如果该簇不是文件的最后一簇,填入的值为该文件占用的下一个簇的簇号,这样,正好将文件占用的各簇构成一个簇链,保存在 FAT 表中。0000000H、00000001H两簇号不使用, 其对应的两个 DWORD 位置(FAT 表开头的 8 个字节)用来存放该盘介质类型编号。FAT 表的大小就由该逻辑盘数据区共有多少簇所决定,取整数个扇区。
Fat表编号从0开始计数,每4字节表示数据区每个簇的使用情况,编号为0和1的簇用来表示存放该盘介质类型编号,所以簇号一般从2开始计数,且簇号2一般默认用来存储根目录簇的使用情况。
4.1 FAT表结构及作用
(1)FAT32文件一般有两份FAT,他们由格式化程序在对分区进行格式化时创建,FAT1是主,FAT2是备份。
(2)FAT1跟在DBR之后,其具体地址由DBR的BPB参数中指定,FAT2跟在FAT1的后面。
(3)FAT表由FAT表项构成,我们把FAT表项简称FAT项,每个FAT项占用4字节。
(4)每个FAT项都有一个固定的编号,这个编号从0开始。
(5)FAT表项的前两个FAT项为文件系统保留使用,0号FAT为介质类型,1号FAT为文件系统错误标志。
(6)分区的数据区中每个簇都会映射到FAT表中的唯一一个FAT项,因为0号FAT和1号FAT被系统占用,用户的数据从2号FAT开始记录。
(7)如果某个文件占用很多个簇,则第一个FAT项记录下一个FAT项的编号(既簇号),如果这个文件结束了,则用“0F FF FF FF”表示。
(8)分区格式化后,用户文件以簇为单位存放在数据区中,一个文件至少占用一个簇。
(9)FAT的主要作用是标明分区存储的介质以及簇的使用情况。
4.2 定位FAT绝对位置的方法如下:
(1)首先从MBR的分区表中得知分区的起始扇区,偏移到此扇区。
(2)从DBR的BPB中得知DBR的保留扇区数,FAT表的个数,FAT表的大小。
(3)因此FAT1=分区起始扇区+DBR保留扇区,FAT2=分区起始扇区+DBR保留扇区+FAT1。
五、分析FAT32文件系统的数据区
数据区的位置在FAT2的后面,具体定 位方式如下;
1、通过MBR中的分区表信息得知分区的起始位置。
2、通过分区中DBR得知DBR的保留扇区数以及FAT表的大小,FAT表的个数。
3、通过上面的信息就可以找到数据区的起始位置,根目录=数据区的起始扇区+(簇大小*2)。
数据区的内容主要由三部分组成:根目录,子目录和文件内容。在数据区中是以“簇”为单位进行存储的,2号簇被分配给根目录使用。
5.1根目录区详解
根目录的定位方式为:根目录=分区起始扇区+DBR保留扇区+(FAT表2)+(簇大小2)
FAT32文件系统中,分区根目录下的文件和目录都放在根目录区中,子目录中的文件和目录都放在子目录区中,并且没每32个字节为一个目录项,每个目录项纪录着一个目录或文件(也可能是多个目录项记录一个文件或目录),如上图所示就是一个目录项。
在FAT32文件系统中,目录项可以分为四类:卷标目录项、“.”和“..”目录项、短文件名目录项、长文件名目录项。
卷标目录项:卷标就是分区的名字,可以在格式化分区时创建,也可以随意修改,长度为11字节。
“.”和“..”目录项:“.”表示当前目录,“..”表示上一层目录。这两个目录项多存在子目录中。
短文件名目录项:所谓短文件名既文件名的“8.3”格式,此格式支持主文件名不能超过8字节,扩展名不能超过3字节。短文件名目录始终存放在一个目录项中。
5.2、目录项32字节解析
根目录区中的目录项变化较多,一个目录项仍占 32 字节,可以是文件目录项、子目录项、卷标项(仅根目录有)、已删除目录项、长文件名目录项等。全部 32 字节的定义如下:
起始字节 | 定义 | 字节数 | 备注 |
0 | 文件正名 | 8 | |
8 | 文件扩展名 | 3 | |
11 | 文件属性 | 1 | 按二进制位定义,最高两位保留未用,0至5位分别是只读位、隐藏位、系统位、卷标位、子目录位、归档位。 |
12 | 长文件名扩展 | 2 | 用来存储其对应的短文件名目录项的文件名字节校验和等 |
14 | 文件创建时间 | 2 | 16 位二进制的文件建立时间,其中的高5位为小时,次6位为分钟,最低5位为秒[0-32],一个度量单位为2秒 |
16 | 文件创建日期 | 2 | 16 位二进制的文件建立日期,其中的高7位为相对于1980年的年份值,次4位为月份,后5位为月内日期。 |
18 | 文件最新访问日期 | 2 | 参考文件创建日期 |
20 | 文件起始簇高16位 | 2 | |
22 | 文件最新修改时间 | 2 | 参考文件创建时间 |
24 | 文件最新修改日期 | 2 | 参考文件创建日期 |
26 | 文件起始簇低16位 | 2 | |
28 | 文件长度 | 4 |
5.3、文件数据存放
从文件的大小可以计算出,需要占用多少个簇。根据前面的数据,每个簇放8个扇区,每个扇区512个字节,那么一个簇的空间就是4096字节了,4KB。那么11639字节需要3个簇,这三个簇的开始的地址就可以计算出来了。
Fat表1如下:
上面已经知道开始簇开始的地址了:03,Fat表1中编号为3的簇中数据数据为:04 00 00 00 ,表示文件占用下一个簇为第4簇,同理编号为4簇告诉我们,占用编码为5的簇,编号为5的簇保存着:FF FF FF 0F告诉我们这是一个结束簇,刚好文件占用了三个簇,簇编号为3、4、5。到这里有个很重要的概念出来了,预分配文件怎么形成?1、在根目录区建立一个有效的32字节目录项;2、根据目录项中的起始簇号和文件大小,更新两份Fat表中的相关簇记录。即可完成文件的预分配工作,但文件中的数据失效数据。
第三簇起始地址:F6000(根目录区地址)+ (03-2)*08(多少扇区每簇)*200(多少字节每扇区)=F7000
第四簇起始地址:F6000+(04-2)08200 = F8000
第五簇起始地址:F6000+(05-2)08200 = F9000
簇3起始数据:
簇4起始数据:
簇5起始数据:
六、FAT32文件系统总结
上述的内容已经简单的介绍了FAT32文件系统,下面根据定位某个文件来详细的了解FAT32文件系统是如何存储数据的。
1、根据磁盘0号扇区MBR的分区表得知分区的起始位置,既DBR;
2、根据DBR中BPB记录的信息,得知DBR保留扇区数,FAT的大小,FAT的个数;
3、根据上述信息可以算出数据的起始位置,数据区=分区起始扇区+DBR保留扇区+(FAT表*2);
4、计算根目录所在的绝对位置,根目录=数据区的起始扇区+(簇大小*2);
5、根据根目录中的目录项信息得知,根目录下的文件以及子目录等所对应的簇;
6、根据文件的簇号就可以找到文件内容的绝对扇区;
7、如果一个文件占用多个簇,则需要根据FAT表项得知下一个数据簇的簇号。
7、如果根目录下的目录项是子目录的话,则根据子目录中的文件目录项得知文件内容的簇号;
8、如果子目录中还有子目录的话,则根据这种方法一直找下去即可。
七、预分配文件生产原理
一个新的磁盘在分区和格式化为Fat32文件系统之后,读Fat32文件系统的启动扇区信息,获取Fat表的起始地址(一般2个fat表),数据区的起始地址。数据区起始地址之后的第一个簇是根目录区,根目录区中每32个字节记录一个短目录项,所以当我们规定的文件名符合短目录项命名规则(文件正名小于等于8字节,文件扩展名为3字节)时,可以通过直接偏移到根目录区,将每一生成好的文件信息按32字节目录项规则填充,同时对系统Fat表中簇的使用记录进行同步更新,理论上就完成了文件系统的预分配工作。
7.1、检测系统磁盘个数
根据/proc/partition系统文件信息,确定设备中存在几个磁盘文件,并初始化磁盘的基本信息,挂在磁盘到指定目录。
7.2、预分配文件系统生成
1、读取磁盘启动区的基本信息,解析磁盘扇区大小、簇大小、Fat1起始地址和大小、Fat2起始地址和大小、数据区起始地址。数据区起始地址即根目录起始地址。
2、在根目录区新建/video目录(写入32字节目录项信息),并指定其数据(该目录下文件或目录的“目录项”)起始簇为03号簇,同时更新Fat表1,2的03簇信息(00簇与01簇用来存放该盘介质类型编号,02簇用来存放根目录,所以从03簇开始)。
3、在03簇的起始地址新建 . (当前目录)和 ..(上一级目录)目录项(写入32字节目录项信息),当前目录项数据起始簇号为03,上一级目录的数据起始簇号为00,更新Fat表1、2。当前目录和上一级目录占用03簇的前64字节。
4、从03簇的第三个目录项开始写入Video0的录像信息等文件,同时根据Video0文件大小,计算出视频文件使用多少簇。根据簇的使用情况更新Fat表1、2,如果该簇是文件的最后一簇,填入的值为 FFFFFF0FH,否则填入的值为该文件占用的下一个簇的簇号。假设Video0专用了6个连续的簇(6-11),则在Fat表06-11簇中依次写入:
0x00000007 0x00000008 0x00000009 0x0000000A 0x0000000B 0xFFFFFF0F信息,则完成Video0视频文件的预分配。
7.3、实例解析
下面使用一个64GB的SD卡做实例讲解。
1、使用WinHex.exe工具打开SD卡所在分区,这里是G盘。
在启动扇区中取出扇区大小、每簇扇区数等重要信息:
偏移地址 | 数据项 | 大小(字节) |
0x0B | 扇区大小 | 0200 |
0x0D | 每簇扇区数 | 20 |
0x0E | 保留扇区数 | 0006 |
0x24 | 一个FAT32表所占扇区数 | 73481. |
2.、计算出簇大小、FAT32表大小、FAT表地址、根目录地址。
簇大小 = 每簇扇区数 × 扇区大小 = 4000
FAT32表大小 = 扇区大小 × FAT32表扇区数 = E69000
FAT1表地址 = 扇区大小 × 保留扇区数 = C00
FAT2地址 = FAT1位置 + FAT32大小 = E69C00
根目录地址 = FAT2位置 + FAT32大小 = 1CD2C00
第3簇地址 = 根目录地址 + (3-2)× 簇大小 = 1CD6C00
第4簇地址 = 根目录地址 + (4-2)× 簇大小 = 1CDAC00
3、在根目录区新建/video目录及相关预分配文件。
(1) 在根目录区写入短目录结构的/video目录项,数据区对应为03簇。
(2) 更新两张 FAT表相应簇的使用情况,这里由于/video目录下文件数较多,使用了两个簇,即03簇与04簇。所以在FAT表中03簇对应地址填写入下一簇的地址(04 00 00 00),04簇是最后一簇所以写入FF FF FF 0F。
(3) 在03簇(图-6.3.4.3)与04簇(图-6.3.4.4)数据区依次写入当前目录(./)、上级目录(../)、预分配文件(bsjVideo0)目录项。
\