FAT(File Allocation Table),最早在DOS v1.0 中被引入,是一种极简的文件系统,占用空间,是目前最常见的文件系统之一。
FATX系列文件系统的不同版本都是针对不同的文件存储介质(storage media)规模设计的。
FAT12
FAT 12 是专为软盘设计的,最大可管理 16 兆字节的大小,因为它使用 12 位来寻址磁盘组。
FAT16
FAT 16 是为早期硬盘设计的,可处理的最大簇大小为 64K * 簇大小。硬盘越大,簇的大小就越大,这会导致磁盘上出现大量的 “闲置空间”(slack space)。
FAT32
FAT 32 由 Windows95-B 和 Windows98 引入。FAT32 解决了 FAT 的一些问题。不再有最大 64K 的簇!虽然 FAT32 每个 FAT 条目使用 32 位,但实际上只有最下面的 28 位用于寻址磁盘上的磁簇(最上面的 4 位为保留位)。由于每个 FAT 条目有 28 位,文件系统在一个分区中最多可寻址约 2.7 亿个群集。这使得超大硬盘仍能保持相当小的簇大小,从而减少文件之间的空闲空间。
ExFAT
ExFAT 是用于 SDXC 卡的文件系统,由微软创建。它是 FAT32,每个 FAT 条目实际上有 32 位,能指示文件在磁盘上是完全连续的(允许你跳过读取 FAT),还有一些更高级的功能和完全重新设计的文件输入系统。
VFAT
VFAT 是 FAT 文件系统的扩展,可以使用长文件名(最多 255 个字符)。它由 Windows 95 首次引入,使用了一种 “笨办法”,即用 "卷标 "属性标记长文件名,然后将文件名以 11 个字节的块存储在顺序目录条目中。
FAT 文件系统将存储介质视为扁平的磁簇阵列。如果物理介质不是以扇区的平面列表来处理数据(如老式硬盘和软盘),那么在将簇号发送到磁盘之前就需要进行转换。
存储介质分为三个基本区域:
如果物理介质不是以扇区的平面列表来处理数据
这段话的意思是,如果物理介质(例如老式硬盘和软盘)不是将数据作为扇区的线性列表来寻址的,那么在将簇号发送到磁盘之前,需要对簇号进行转换。
在现代硬盘中,数据通常被组织成线性的扇区列表,每个扇区都有一个唯一的扇区号。但是,在一些老式的硬盘和软盘中,数据可能被组织成不同的方式,例如使用簇(cluster)的概念来组织数据。簇是一组相邻的扇区,通常是2个或4个扇区的大小。在这种情况下,每个簇都有一个唯一的簇号,而不是每个扇区都有一个唯一的扇区号。这种组织方式可以提高磁盘的存储效率,但也需要进行簇号到扇区号的转换,以便正确地访问磁盘上的数据。
因此,如果文件系统使用簇号来寻址数据,而不是扇区号,那么在将簇号发送到磁盘之前,需要将簇号转换为对应的扇区号。这个过程称为簇号到扇区号的转换。这个转换过程通常由文件系统驱动程序来完成,以确保文件系统能够正确地访问磁盘上的数据。
“The boot record”(引导记录)是指存储在磁盘的第一个扇区的特殊区域。它也被称为引导扇区(boot sector)或主引导记录(master boot record,MBR)。
存储在磁盘的第一个扇区的特殊区域
指的是启动记录占一个扇区,始终位于 "分区 "的逻辑零号扇区。如果介质没有分区,那么这就是介质的起始位置。这是计算机加载时最容易找到的分区扇区。
引导记录是一个重要的数据结构,它包含了启动计算机所需的关键信息。当计算机启动时,BIOS(基本输入/输出系统)会读取磁盘的引导记录,并执行其中的引导代码。这段引导代码负责加载操作系统的核心部分,从而完成系统的启动过程。
引导记录通常包含以下内容:
引导代码:这是一段特定的机器代码,用于初始化计算机硬件、加载操作系统的核心部分,并将控制权转交给操作系统。
分区表:引导记录还包含一个分区表,用于描述磁盘上的分区布局。分区表记录了每个分区的起始位置、大小和文件系统类型等信息。
引导标志:引导记录中还包含一个引导标志,用于指示该扇区是否为引导扇区。
文件分配表 (FAT) 是存储在存储介质上的一个表,用于显示磁盘上所有数据集群的状态和位置。它可以被视为磁盘的 “目录”。簇可能可供使用,可能被操作系统保留,可能因磁盘上的坏扇区而不可用,也可能被文件使用。文件的簇不一定在磁盘上紧挨在一起。事实上,它们很可能分散在磁盘的各个角落。FAT 允许操作系统跟踪文件中的簇 “链”。
FAT 32 使用 28 位来寻址磁盘上的群集。最高 4 位为保留位。exFAT 使用完整的 32 位来编码扇区号。
unsigned char FAT_table[sector_size];
unsigned int fat_offset = active_cluster * 4;
unsigned int fat_sector = first_fat_sector + (fat_offset / sector_size);
unsigned int ent_offset = fat_offset % sector_size;
//at this point you need to read from sector "fat_sector" on the disk into "FAT_table".
//remember to ignore the high 4 bits.
unsigned int table_value = *(unsigned int*)&FAT_table[ent_offset];
if (fat32) table_value &= 0x0FFFFFFF;
//the variable "table_value" now has the information you need about the next cluster in the chain
如果 "table_value "大于或等于 (>=) 0x0FFFFFF8(或 exFAT 的 0xFFFFFFF8),则链中不再有任何簇。这意味着整个文件已被读取。如果 "table_value "等于(==)0x0FFFFFF7(或 exFAT 为 0xFFFFFFF7),则该簇被标记为 "坏 "簇。坏 "簇容易出错,应避免使用。如果 "table_value "不属于上述情况,那么它就是文件中下一个簇的簇号。
索引 0 和 1 下的条目被保留。保留第 0 个条目是因为索引 0 被用作其他条目的值,表示给定的簇是空闲的。第 0 个条目必须保留 BPB_Media 字段低 8 位的值,其余位必须置 0。例如,如果 BPB_Media 为 0xF8,则第 0 个条目应为 0xFFFFFFF8。第一个条目是为将来保留的,其值必须为 0xFFFFFFFF。
引导扇区始终位于逻辑扇区编号 0 处。你可以将引导扇区读入一个数组并访问其中的成员,也可以将其读入一个结构并通过结构访问。无论哪种方式,引导扇区中的值都需要随时可用,这样才能在 FAT 文件系统中做很多事情。
typedef struct fat_extBS_32
{
//extended fat32 stuff
unsigned int table_size_32;
unsigned short extended_flags;
unsigned short fat_version;
unsigned int root_cluster;
unsigned short fat_info;
unsigned short backup_BS_sector;
unsigned char reserved_0[12];
unsigned char drive_number;
unsigned char reserved_1;
unsigned char boot_signature;
unsigned int volume_id;
unsigned char volume_label[11];
unsigned char fat_type_label[8];
}__attribute__((packed)) fat_extBS_32_t;
typedef struct fat_extBS_16
{
//extended fat12 and fat16 stuff
unsigned char bios_drive_num;
unsigned char reserved1;
unsigned char boot_signature;
unsigned int volume_id;
unsigned char volume_label[11];
unsigned char fat_type_label[8];
}__attribute__((packed)) fat_extBS_16_t;
typedef struct fat_BS
{
unsigned char bootjmp[3];
unsigned char oem_name[8];
unsigned short bytes_per_sector;
unsigned char sectors_per_cluster;
unsigned short reserved_sector_count;
unsigned char table_count;
unsigned short root_entry_count;
unsigned short total_sectors_16;
unsigned char media_type;
unsigned short table_size_16;
unsigned short sectors_per_track;
unsigned short head_side_count;
unsigned int hidden_sector_count;
unsigned int total_sectors_32;
//this will be cast to it's specific type once the driver actually knows what type of FAT this is.
unsigned char extended_section[54];
}__attribute__((packed)) fat_BS_t;