《文件系统系列 1》:FAT32文件系统详解

一、预备知识

1.1 物理地址和逻辑地址

1.1.1 物理地址

物理地址: 实际的内存硬件中的地址,是处理器发送到内存总线上的地址信号,用来访问物理内存单元。物理地址可以访问整个硬盘的所有空间

1.1.2 逻辑地址

逻辑地址: 是由程序生成的地址,也称为虚拟地址或用户地址。在用户态下,程序访问的所有内存地址都是逻辑地址。由于硬盘中需要存储一部分的硬盘的信息(如下面的MBR等信息以及硬盘主控中的设计机制),所有,用户实际上能够使用的硬盘空间并不是硬盘的全部容量。例如,一个标称容量为512GB的硬盘,在Windows资源管理器中显示的可用空间通常只有480GB到500GB之间。

1.2 大端存储和小端存储

大端存储(Big Endian)小端存储(Little Endian) 是两种不同的多字节数据存储方式,它们决定了如何在计算机内存中存储和读取多字节数据,如整数或浮点数。
在这里插入图片描述

1.2.1 大端存储

  在大端存储方式中,数据的高字节存放在内存的低地址,而低字节存放在内存的高地址。这种方式可以类比为“人类的阅读方式”(从左到右),即从高位到低位的顺序。
示例:
假设有一个 32 位的整数 0x12345678,它在内存中的存储顺序为:

内存地址: 0x00 0x01 0x02 0x03 
存储数据: 0x12 0x34 0x56 0x78

1.2.2 小端存储

  在小端存储方式中,数据的 低字节 存放在内存的 低地址,而 高字节 存放在内存的 高地址,即(从右到左)。这种方式更符合计算机处理器内部的运作逻辑。
示例:
同样的 32 位整数 0x12345678,在小端存储方式下,它在内存中的存储顺序为:

内存地址: 0x00 0x01 0x02 0x03 
存储数据: 0x78 0x56 0x34 0x12

1.3 Unicode编码

Unicode 是一种字符编码标准,用于为世界上几乎所有的文本字符提供唯一的编号。它的目标是解决不同语言、平台和系统之间字符编码不兼容的问题,从而实现全球通用的文本处理。Unicode 三种编码格式分别是UTF-8, UTF-16, UTF-32,下面对三种编码格式进行详细的介绍。

1.3.1 UTF-8 编码格式

  • 存储大小:每个字符使用 1 到 4 个字节。
  • 特点:UTF-8 兼容 ASCII 编码,它对英语字符使用 1 个字节,对其他字符使用 2 至 4 个字节。因此,它在西方国家和网络传输中广泛应用,因为它对 ASCII 字符的处理高效。

示例
 字符 A 在 UTF-8 中编码为 1 字节 0x41,而中文字符 编码为 3 字节 0xE4B8AD

1.3.2 UTF-16 编码格式

  • 存储大小:每个字符使用 2 个或 4 个字节。
  • 特点:UTF-16 对常用字符(如大多数欧洲语言的字符和汉字)使用 2 个字节,对较少使用的字符使用 4 个字节。它在 Windows 系统和 Java 中较为流行。

示例
 字符 A 在 UTF-16 中编码为 0x0041,而中文字符 在 UTF-16 中编码为 0x4E2D

1.3.3 UTF-32 编码格式

  • 存储大小:每个字符固定使用 4 个字节。
  • 特点:由于每个字符固定占用 4 个字节,UTF-32 提供了简单的字符存取方式,但由于空间浪费较大,它在实际应用中较少使用。

示例
 字符 A 在 UTF-32 中编码为 0x00000041,而中文字符 编码为 0x00004E2D

1.3.4 Unicode 字符范围

 Unicode 使用 码位(Code Points)来表示每个字符,码位范围从 U+0000U+10FFFF,可以表示超过一百万个字符。目前已分配的字符涵盖了以下主要字符集:

  • U+0000 - U+007F:ASCII 字符集(英语字母、数字、符号)。
  • U+0080 - U+FFFF:包含西欧、中欧、希腊语、汉字等常用字符。
  • U+10000 - U+10FFFF:扩展字符,包括历史语言、表情符号、数学符号等。

1.4 软件

  为了方便后续我们进行可视化,需要事先下载两个软件分别是 WinHexBus Hound

二、FAT32

2.1 定义

FAT32(File Allocation Table 32)是一种广泛应用于存储设备(如硬盘、U 盘和存储卡)的文件系统。作为 FAT 文件系统的升级版本,FAT32 支持的最大文件大小为 4GB,单个分区的最大容量为 2TB。由于 FAT32 文件系统具有良好的兼容性,能够适用于多种操作系统(如 Windows、Linux 和 macOS),因此它在可移除存储设备中非常常见。FAT32 的特点如下:

  • 文件存储组织:将文件分割成簇(Cluster),通过 FAT 表记录每个文件所在的簇及其链接,确保文件数据能被完整地存取。
  • 簇的动态管理:通过 FAT 表跟踪每个簇的使用情况,支持动态地分配和回收磁盘空间。

2.2 FAT32 结构

 FAT32 文件系统的结构如下图所示:主要由 MBR、DBR(Boot Sector)、FAT 表(FAT1 和 FAT2)、Data 区域和 Reserved(保留区域) 组成。下面对各个部分进行详细介绍。

FAT32结构图

2.2.2 MBR

MBR(Master Boot Record),即主引导记录,位于硬盘的 第一个扇区,大小为512Byte。MBR 的字节及其含义如下表所示。其中最值得注意的是 《Partition 1 ~ 4》 :记录了每个分区(Partition)的信息(重要信息)。
在这里插入图片描述

Partition 1 ~ 4 :记录了每个分区的起始位置、大小、分区类型等关键数据,帮助操作系统识别和访问不同的分区。其中值得注意的是Relative Sector:代表的是该分区的DBR的物理地址。
在这里插入图片描述

2.2.3 DBR

DBR(DOS Boot Record),即分区引导记录 (也是2.2中FAT文件系统结构图中的BootSector),大小为512Byte,位于分区(Partition)的第一个扇区。DBR 包含文件系统的关键信息,DBR 的字节及其含义如下表所示。其中值得注意的是BPB+eBPB:包含了当前分区的相关信息。
在这里插入图片描述

2.2.4 BPB

BPB(BIOS Parameter Block),即 BIOS 参数块,它是 DBR 中的一部分,包含关于 FAT32 文件系统的详细参数。BPB的字节及其含义如下表所示。
在这里插入图片描述

2.2.5 eBPB

eBPB(Extend BIOS Parameter Block) 是BPB的扩展,包含更多与文件系统相关的元数据(紧跟着BPB)。eBBP的字节及其含义如下表所示。
在这里插入图片描述

2.2.6 FSInfo Sector

FSInfo Sector 是用于存储卷的关键信息,以提高文件系统操作的效率。(紧跟着DBR后的一个扇区), 大小为512Byte。其中值得注意的是当前分区的空闲簇数和当前分区下一个空闲的簇号(后续对硬盘进行删除和添加操作时需要更改)。
在这里插入图片描述

2.2.7 FAT表

FAT 表(File Allocation Table) 是 FAT32 文件系统的核心,用于记录每个簇(Cluster)的使用情况。每个文件的存储都是基于簇的,而 FAT 表记录了文件在各个簇中的位置。其中0号簇和1号簇为系统所使用,禁止修改。

  • 作用:FAT 表通过链接文件的各个簇,实现文件存储和寻址。
  • 簇链:每个文件占用的簇在 FAT 表中形成一条链,表中的每一项对应一个簇的使用情况。即每一项中如果不为0xFF FF FF 0F则代表该文件的下一个簇号,如果为0xFF FF FF 0F则代表当前簇是文件的最后一个簇,如果为00 00 00 00则代表未分配的簇 。
    在这里插入图片描述

2.2.8 Data 区域

Data 区域 是 FAT32 文件系统中实际存储文件和目录内容的地方。Data 区域紧跟在 FAT 表之后,每个文件或目录的内容都存储在此区域中的簇中。

  • 特点:文件系统会根据 FAT 表中的记录,找到文件在 Data 区域的具体存储位置。
  • 管理方式:通过 FAT 表链接不同的簇来存储大型文件,当一个文件跨越多个簇时,FAT 表会记录每个簇的位置,确保文件可以被正确读取。

在FAT32中,文件的相关信息(短文件名或者长文件名)存放在其父目录,其结构如2.2.8.1和2.2.8.2所示

2.2.8.1 短文件名格式

  当文件的名称长度不超过8Byte时会采用短文件名的格式来进行存储,每个短文件名的编码为32Byte。 其结构如下表所示。
在这里插入图片描述

2.2.8.2 长文件名格式

  当文件的名称长度超过8Byte时会采用长文件名的格式来进行存储。需要注意的是长文件名一定会有其短文件名。
在这里插入图片描述

2.2.8.3 日期和时间转换

  文件名中的日期与时间在转换为日常所见时需要通过以下规则来进行转换。
在这里插入图片描述

三、功能和代码实现

  在掌握了FAT32的基本结构后,我们将通过一个示例进行验证,并使用代码实现一些基本功能,如添加、删除、复制和恢复等操作。要实现这些操作,首先需要获取硬盘的相关信息(包括各部分的编码)。其中,获取目录结构和FAT表是实现这些基本功能的关键步骤。
在这里插入图片描述

3.1 实现的功能

3.1.1 解析目录结构

  为了获取目录结构和FAT表,我们需要按照FAT32的结构来进行解析。解析目录结构的大致流程如下

  1. 解析MDR: 通过读取MDR我们可以得到每个分区的开始位置(即DBR的开始位置)。
  2. 解析DBR: 通过读取DBR中的BPB获取当前分区的基本信息,包括,扇区大小和数量等相关信息(具体请看2.2.4)。
  3. 解析FAT表: 通过读取DBR中的BPB获取到DBR(Boot Sector)+Reserved的大小后,通过将基地址(DBR的开始位置)和Offset(DBR(Boot Sector)+Reserved区域大小)相加就可以得到FAT表的开始位置。
  4. 解析Data区域: 通过读取DBR中的BPB获取到DBR(Boot Sector)+Reserved的大小和FAT表的大小后,通过将基地址和地址Offset(DBR(Boot Sector)的大小+Reserved区域大小+2个FAT表的大小)相加就可以得到Data区域的开始位置。

3.1.1.1 解析MBR

  MBR 位于硬盘的第一个扇区(即扇区 0),可以使用 WinHex 查看。按照以下步骤,能够得到③,显示出 MBR 的编码。根据 2.2.3 中 MBR 的结构,可以得知当前分区的起始位置(即 Boot Sector/DBR 的位置)0x00 01 B6 80(小端存储)。由此,我们可以读出 DBR 的编码。注意,在Winhex打开的是《物理驱动器》。此外,使用代码读写时也要使用物理地址进行操作。
在这里插入图片描述

3.1.1.2 解析DBR

  在得到每个分区表的位置后,就可以计算出DBR(BootSector)的开始位置。这里使用上面的例子来进行演示。

DBR(Boot Sector)的开始位置: 0x00 01 B6 80 

  由于实际存储全是以Sector(扇区,512Bytes)为单位的地址,但WinHex使用的是以Byte为单位的地址。所以,在使用WinHex查看某个位置上的数据时,需要转为以Byte为单位的地址。

DBR(BootSector)的开始位置(字节为单位):0x00 01 B6 80 << 9 = 0x03 6D 00 00

  在WinHex点击《偏移地址》进行地址偏移,输入上述算出的地址0x03 6D 00 00即可转到相应的DBR区域(如下图所示)。
在这里插入图片描述
  在得到DBR的信息后,接下里就可以从中提取出BPB的信息,BPB中包含了许多重要的信息。接下里我们一一详解其中重要的信息。由2.2.4可知,以下信息均采用小端存储的方式

 1. Bytes per sector(每扇区的字节数): 0x20 00 = 512
 2. Sectors per cluster(每个簇的扇区数): 0x40 = 64
 3. Reserved sectors(保留扇区数, 即Boot Sector + Reserve的大小): 0x00 22 = 18 sector
 4. Number of FATs(FAT表个数): 0x00 02 = 2
 5. Sectors per FAT(每个FAT占用的扇区数): 0x00 00 75 1B = 29979
 6. Root dir 1st cluster(根目录的起始簇号): 0x00 00 00 02 = 2

在这里插入图片描述

3.1.1.3 解析FAT表

  由3.1.1.2得到DBR(Boot Sector) + Reserve的大小后,就可以使用分区开始位置加上DBR(Boot Sector) + Reserve的大小,从而得知FAT的开始位置。

FAT的开始位置(单位为Sector): 0x00 01 B6 80 + 0x00 22 = 0x00 01 B6 A2
FAT的开始位置(单位为Byte):  (0x00 01 B6 80 + 0x00 22) << 9 = 0x03 6D 44 00

  在WinHex点击《偏移地址》进行地址偏移,输入上述算出的地址0x03 6D 44 00即可转到相应的FAT区域(如下图所示),FAT表中每个簇号占4 Byte。
  在寻找文件的所有簇时,流程如下:

 1. 在文件的父目录中找到该文件的开始簇号,这里我们假设是5(关于怎么寻找文件的开始簇号会在3.1.1.4中会讲解到)
 2. 读取FAT表
 3. 找到第5个簇(簇号从0开始),即从5*4=20往后的4个Byte:0xFF FF FF 0F
 4. 如果对应的值为0xFF FF FF 0F, 则代表当前簇是该文件的最后一个簇,在我们举的例子中就表示为该文件只占了簇5.

在这里插入图片描述

3.1.1.4 解析根目录和目录结构

  由3.1.1.2得到 Root dir 1st cluster(根目录的起始簇号)后,就可以使用分区开始位置加上DBR(Boot Sector)+Reserve的大小和2个FAT表的大小,从而得知根目录的开始位置。

需要注意:根目录的起始簇号是2,但是Data区域的开始位置就是根目录的位置。也就是根据文件或者目录的簇号寻找其内容时,需要将簇号减去2,这是因为FAT表前两个簇是被系统占用了,但数据区域没有保存对应的数据,所以,所有的簇号都比实际存储的位置大了2个簇号。
根目录的开始位置(以Sector为单位)0x00 01 B6 80 + 0x00 22 + 0x75 1B + 0x75 1B = 0x00 02 A0 D8
根目录的开始位置(以Bytes为单位)(0x00 01 B6 80 + 0x00 22 + 0x75 1B + 0x75 1B) << 9 = 0x05 41 B0 00

  在WinHex点击《偏移地址》进行地址偏移,输入上述算出的地址0x05 41 B0 00即可转到相应的Data区域(如下图所示)。从图中,我们可以看出每个框是32Bytes,相同颜色的框属于同一个文件名。此外可以看出每种颜色的框数量不同,这是因为长文件名和短文件名的区别,长文件名由多个32Byte组成,而短文件名由1个Byte组成。 下面我们取图中的例子对文件名编码格式进行详细的介绍。
在这里插入图片描述
  这里我们使用图中的4和5进行例子的讲解。首先讲解一下短文件名(图中的4)
在这里插入图片描述

 1. 文件名称(8 Byte)			:20 20 45 54 45 4C 45 44 =delete”, 采用UTF-8进行编码,直接将其转为UTF-8的字符串即可。
 2. 文件扩展名(3 Byte)			:20 20 20 = “”,同样采用UTF-8进行编码,并使用大端存储,直接将其转为UTF-8的字符串即可。此外,需注意目录无文件扩展名,所以,这里是空格(20)。
 3. 文件属性(1 Byte)			:10 = 目录, 不同的编码代表文件或者目录的不同属性,具体参照2.2.8.14. Resrved区域(1 Byte)		:08
 5. 创建时间的10毫秒位(1 Byte)	:4A
 6. 文件创建时间(2 Byte)		:CD 9C, 参考2.2.8.3进行转换
 7. 文件创建日期(2 Byte)		:3D 59, 参考2.2.8.3进行转换
 8. 文件最后访问日期(2 Byte)	:3D 59, 参考2.2.8.3进行转换
 9. 文件起始簇号的高16位(2 Byte)	:00 00,与12拼接后转为文件的簇号
 10. 文件的最近修改时间(2 Byte)  	:46 78, 参考2.2.8.3进行转换
 11. 文件的最近修改日期(2 Byte)  	:1E 59, 参考2.2.8.3进行转换
 12. 文件起始簇号的低16位(2 Byte)	:D9 00,与9拼接后转为文件的簇号
 13. 文件大小(2 Byte)			  	:00 00 00 00 ,文件的大小

  接下来讲解一下长文件文件名(图中的5),需要注意的是,长文件名由多个32Byte组成,并且最后一个32Byte一定是它对应的短文件名,拼接长文件名时还需要从下往上的进行拼接,即从下面的倒数第二个32Byte开始-最上面的32Byte
在这里插入图片描述

 1. 文件名的第1~5个Unicode码字符(10 Byte):6C 00 65 00 00 00 FF, 采用UTF-16进行编码并使用大端存储,与24拼接起来转为UTF-16的字符串。
 2. 长文件名标志(1 Byte):0F, 长文件名标志位,0F表示为为长文件名。
 3. 长文件名的第6~11个Unicode码字符(12 Byte):FF FF FF FF FF FF FF FF FF FF FF FF,同样采用UTF-16进行编码并使用大端存储。
 4. 文长文件名的第12~13个Unicode码字符(6 Byte)00 00 FF FF FF FF, 同样采用UTF-16进行编码并使用大端存储。
 5. 长文件名标志(1 Byte):0F, 长文件名标志位,0F表示为为长文件名。

最后,在来看一下其它需要特别注意的

 1. 每个32Byte的第一个字节如果是E5,则代表该文件已经被删除了。如果是2E则代表是当前目录的上一级或是当前目录,解析目录结构时直接忽略即可。
 2. 根目录的起始簇号是2,但是Data区域的开始位置就是根目录的位置。也就是根据文件或者目录的簇号寻找其内容时,需要将簇号减去2,这是因为FAT表前两个簇是被系统占用了,但数据区域没有保存对应的数据,所以,所有的簇号都比实际存储的位置大了2个簇号。

3.1.2查找文件

 在解析得到目录结构后,我们就可以进行其他的操作,如查找操作:即查找包含某个字符串的文件或者目录。
因为我们已经得到了目录结构所以这一步就比较简答,直接遍历即可。
 需要注意的是:

在搜索的时候为了大小写不敏感,可以将搜索的字符串和所有的文件名改为大写或者小写进行匹配。

3.1.3 删除文件或文件夹

  删除文件或文件夹流程如下:

 1. 删除文件
 	1. 找到文件的父目录以及文件对应的文件名,将其32Byte的标志位改为E5。
 	2. 在FAT表中将文件所占的簇全部置为0
 
 2. 删除目录
  	1. 找到目录的父目录以及目录对应的文件名,将其32Byte的标志位改为E5。
 	2. 如果目录时空目录,那么做完第一步就算完成了删除操作,这是因为目录不会被分配簇号。但是如果目录不为空,则需要递归删除文件和其子目录。

3.1.4 创建文件或文件夹

  创建文件或文件夹流程如下:

 1. 创建文件(空文件)
 	1. 找到文件的父目录,在父目录下找到一个空32Byte。
 	2. 在FAT表中找到未使用的簇分配给该文件
 	3. 根据短文件名和长文件名的格式生成文件对应的文件名,并放到父目录的空位置下。
 2. 创建目录
  	1. 找到目录的父目录,在父目录下找到一个空32Byte。
 	2. 根据短文件名和长文件名的格式生成文件对应的目录名,并放到父目录的空位置下。

3.1.5 复制文件或文件夹

  复制文件或文件夹流程如下:

 1. 复制文件
 	1. 找到源文件的父目录以及文件对应的文件名。
 	2. 在FAT表找到源文件所有的簇,并给目标文件分配相同数量的簇。
 	3. 找到目标文件的父目录,在父目录下找到一个空32Byte,将源文件的文件名编码直接复制到当前位置即可。
 	4. 根据分配的簇号,将源文件的数据复制到目标文件的簇下。
 
 2. 复制目录
 	1. 找到源目录的父目录以及源目录对应的文件名。
 	2. 找到目标目录的父目录,在父目录下找到一个空32Byte,将源目录的文件名编码直接复制到当前位置即可。
 	3. 递归复制其子目录和文件。

3.1.6 恢复删除的图片文档

  恢复删除的图片文档:

 1. 找到文件的父目录以及文件对应的文件名,直接将其32Byte的标志位改为空格(20)或者其它字符即可。

3.2 代码实现

  代码已上传至Github:FAT32.

四、参考文献

  [1] 物理地址和逻辑地址的定于与区别
  [2] WinHex下载、安装和使用教程
  [3] Bus Hound下载、安装和使用教程
  [4] FAT32详解
  [5] 经典FAT文件系统格式详解
  [6] FAT32文件系统结构详解

五、意见与反馈

  各位读者,如果您有任何问题和建议,欢迎在评论区留言,或者发送邮件至2466764951@qq.com,我会及时回复您。感谢您的支持!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值