FAT 文件起源于 70 年代末 80 年代初,用于微软的 MS-DOS 操作系统。它开始被设计成一个简单的文件系统用于小于 500K 的软件盘。后来被功能被大大增强用于支持越来越大的媒质。现在的文件系统有 FAT12 , FAT16 和 FAT32 三种子类。


FAT12 是最早的一版,主要用于软盘,它对簇的编址采用 12bit 宽度的数,所以称为 FAT12 。 12bit 的地址可以寻址 4096 个簇,事实上在 FAT12 中只能寻址 4078 个簇(在 Linux 下可寻址 4084 个簇),有一些簇号是不能用的,在后面会给出具体的说明。磁盘的扇区是用 16bit 的数进行计算的,所以磁盘的容量就被局限在 32M 空间之内。


在 FAT16 中,采用了 16bit 宽的簇地址, 32bit 宽扇区地址。虽然 32bit 的扇区地址可以寻址 2^32*512 ,约 2 个 TB 的容量, 但于由规定每簇最大的容量不超过 1024*32 ,所以 FAT16 文件系统的容量也就限制到了 2^16*1024*32 ,大约 2.1GB 的空量,并且实际还达不到这个值。


FAT32 文件系统使用了 32bit 宽的簇地址,所以称为 FAT32 。但在微软件的文件系统中只使用了低 28 位,最大容量为 2^28*1024*32, 约 8.7TB 的空量。有的人认为 32bit 全用,最大容量为 2^32*1024*32 ,这种说法是不正确的。


虽然 FAT32 具有容纳近乎 8.7TB 的容量,但实际应用中通常不使用超过 32GB 的 FAT32 分区。 WIN2000 及之上的 OS 已经不直接支持对超过 32GB 的分区格式化成 FAT32 ,但 WIN98 依然可以格式化大到 127GB 的 FAT32 分区,但不推荐这样做。


下面是一个 FAT 分区的构成概况


引导扇区

其他保留扇区(可选)

FAT 表 1

FAT 表 2

根目录区



(仅 FAT12/16 )

数据区



(用于文件和目录)


需要说明的是:


1 .引导扇区和其他保留扇区一起称为保留扇区,而其他保留扇区是可选的,当没有时候,引导扇区后紧跟的就是 FAT 表 1


2 .根目录区是仅 FAT12/16 才有, FAT32 的目录项位于数据区。由于 FAT12/16 的根目录区是一个固定的区域,所以它的根目录的项数是有限制的,意即不能在根录建立超过这个定数的目录项数。


(一)引导扇区与 BPB


BPB ( BIOS Parametre Block )是 FAT 文件系统中第一个重要的数据结构,它位于该 FAT 分区的第一个扇区,同时也属于 FAT 文件系统基本区域的保留区,


 


        在下面的描述中。凡名称以 BPB_ 开头的都是 BPB 的一部分,凡名称与 BS_ 开头的项都是启动扇区的一部分,而不是属于 BPB 的内容,以下是启动扇区的结构


 

offset ( byte )

长度( byte )

描述

BS_jmpBoot

0x00

3

跳转指令,指向启动代码

BS_OEMName

0x03

8

建议值为“ MSWIN4.1”。有些厂商的FAT 驱动可能会检测此项,所以设为“ MSWIN4.1”可以尽量避免兼容性的问题

BPB_BytsPerSec

0x0b

2

每扇区的字节数,取值只能是以下几种: 512 , 1024 , 2048 或是 4096 。设为 512 会取得最好的兼容性,目前有很多 FAT 代码都是硬性规定每扇区的字节数为 512 ,而不是实际的检测此值。但微软的操作系统能够很好支持 1024 , 2048 或是 4096

BPB_SecPerClus

0x0d

1

每簇的扇区数,其值必须中 2 的整数次方(该整数必须 >=0 ),同时还要保证每簇的字节数不能超过 32K ,也就是 1024*32 字节

BPB_RsvdSecCnt

0x0e

2

保留扇区的数目,此域不能为 0 , FAT12/FAT16 必须为 1 , FAT32 的典型值取为 32 ,,微软的系统支持任何非 0 值

BPB_BumFATs

0x10

1

分区中 FAT 表的份数,,任何 FAT 格式都建议为 2

BPB_RootEntCnt

0x11

2

对于 FAT12 和 FAT16 此域包含根目录中目录的个数(每项长度为 32bytes ),对于 FAT32 ,此项必须为 0 。对于 FAT12 和 FAT16 ,此数乘以 32 必为 BPB_BytesPerSec 的偶数倍,为了达到更好的兼容性, FAT12 和 FAT16 都应该取值为 512

BPB_ToSec16

0x13

2

早期版本中 16bit 的总扇区,这里总扇区数包括 FAT 卷上四个基本分区的全部扇区,此域可以为 0 ,若此域为 0 ,那么 BPB_ToSec32 必须为 0 ,对于 FAT32 ,此域必为 0 。对于 FAT12/FAT16 ,此域填写总扇区数,如果该值小于 0x10000 的话, BPB_ToSec32 必须为 0

BPB_Media

0x15

1

对于“固定”(不可移动)存储介质而言, 0xF8 是标准值,对于可移动存储介质,经常使用的数值是 0xF0 ,此域合法的取值可以取 0xF0,0xF8,0xF9,0xFA,0xFC,0xFD,0xFE,0xFF 。另外要提醒的是,无沦此域写入什么数值,同时也必须在 FAT[0] 的低字节写入相同的值,这是因为早期的 MSDOS 1.x 使用该字节来判定是何种存储介质

BPB_FATz16

0x16

2

FAT12/FAT16 一个 FAT 表所占的扇区数,对于 FAT32 来说此域必须为 0 ,在 BPB_FATZ32 中有指定 FAT 表的大小

BPB_SecPerTrk

0x18

2

每磁道的扇区数,用于 BIOS 中断 0x13 ,此域只对于有“特殊形状”(由磁头和柱面每分割为若干磁道)的存储介质有效,同时必须可以调用 BIOS 的 0x13 中断得到此数值

BPB_NumHeads

0x1A

2

磁头数,用于 BIOS 的 0x13 中断,类似于上面的 BPB_ SecPerTrk ,只对特殊的介质才有效,此域包含一个至少为 1 的数值,比如 1,4M 的软盘此域为 2

BPB_HidSec

0x1C

4

在此 FAT 分区之前所隐藏的扇区数,必须使得调用 BIOS 的 0x13 中断可以得到此数值,对于那些没有分区的存储介质,此域必须为 0 ,具体使用什么值由操作系统决定

BPB_ToSec32

0x20

4

该卷总扇区数( 32bit ),这里的扇区总数包括 FAT 卷四个个基本分的全部扇区,此域可以为 0 ,若此域为 0 , BPB_ToSec16 必须为非 0 ,对 FAT32 ,此域必须是非 0 。对于 FAT12/FAT16 如果总扇区数大于或等于 0x10000 的话,此域就是扇区总数,同时 BPB_ToSec16 的值为 0 。


FAT32 的 BPB 的内容和 FAT12/16 的内容在地址 36 以前是完全一样的,从偏移量 36 开始,他们的内容有所区别,具体的内容要看 FAT 类型为 FAT12/16 还是 FAT32 ,这点保证了在启动扇区中包含一个完整的 FAT12/16 或 FAT32 的 BPB 的内容,这么做是为了达到最好的兼容性,同时也为了保证所有的 FAT 文件系统驱动程序能正确的识别和驱动不同的 FAT 格式,并让他们良好地工作,因为他们包含了现有的全部内容


从 offset 36 开始 FAT12/FAT16 的内容开始区别于 FAT32 ,下面分两个表格列出,下表为 FAT12/FAT16 的内容


名称

offset ( byte )

长度( byte )

描述

BS_drvNum

0x24

1

用于 BIOS 中断 0x13 得到磁盘驱动器参数,( 0x00 为软盘, 0x80 为硬盘)。此域实际上由操作系统决定

BS_Reseved1

0x25

1

保留(供 NT 使用),格式化 FAT 卷时必须设为 0

BS_VolID

0x26

1

扩展引导标记( 0x29 )用于指明此后的 3 个域可用

BS_BootSig

0x27

4

卷标序列号,此域以 BS_VolLab 一起可以用来检测磁盘是否正确, FAT 文件系统可以用此判断连接的可移动磁盘是否正确,引域往往是由时间和日期组成的一个 32 位的值

BS_VolLab

0x2B

11

磁盘卷标,此域必须与根目录中 11 字节长的卷标一致。



FAT 文件系统必须保证在根目录的卷标文件列改或是创建的同时,此域的内容能得到时的更新,当 FAT 卷没有卷标时,此域的内容为“ NO NAME ”

BS_FilSysType

0x36

8

以下的几种之一:“ FAT12 ”,“ FAT16 ”,“ FAT32 ”



不少人错误的认为 FAT 文件系统的类型由此域来确认,他细点你就能发现此域并不是 BPB 的一部分,只是一个字符串而已,微软的操作系统并不使用此此域来确定 FAT 文件的类型,;因为它常常被写错或是根本就不存在。


 


下表为 FAT32 的内容


名称

offset ( byte )

长度( byte )

描述

BPB_FATSz32

0x24

4

一个 FAT 表所占的扇区数,此域为 FAT32 特有,同时 BPB_FATSz16 必须为 0

BPB_Flags

0x28

2

此域 FAT32 特有。



Bits0-3: 不小于 0 的 FAT ( active FAT )数目,只有在镜像( mirrorig )禁止时才有效。



Bits 4-6:  保留



Bits 7 :  0 表示 FAT 实时镜像到所有的 FAT 表中



        1  表示只有一个活动的 FAT 表。这个表就是 Bits0-3 所指定的那个



Bits8-15 : 保留

BPB_FSVer

0x2A

2

此域为 FAT32 特有,



高位为 FAT32 的主版本号,低位为次版本号,这个版本号是为了以后更高级的 FAT 版本考虑,假设当前的操作系统只能支持的 FAT32 版本号为 0.0 。那么该操作系统检测到此域不为 0 时,它便会忽略 FAT 卷,因为它的版本号比系统能支持的版式本要高

BPB_RootClus

0x2C

4

根目录所在第一个簇的簇号,通常该数值为 2 ,但不是必须为 2



磁盘工具在改变根目录位置时,必须想办法让磁盘上第一个非坏簇作为根目录的第一个簇(比如第 2 簇,除非它已经被标记为坏簇),这样的话,如果此域正好为 0 的话磁盘检测工具也能轻松的找到根目录所在簇的位置

BPB_FSIfo

0x30

2

保留区中 FAT32 卷 FSINFO 结构所占的扇区数,通常为 1



在 Backup Boot  中会有一个 FSINFO 的备份,但该备份只是更新其中的指针,也就是说无论是主引导记录还是备份引导记录都是指向同一个 FSINFO 结构

BPB__BkBootSec

0x32

2

如果不为 0 ,表示在保留区中引导记录的备数据所占的扇区数,通常为 6 。同时不建议使用 6 以外的其他数值

BPB_Reserved

0x34

12

用于以后 FAT 扩展使用,对 FAT32 。此域用 0 填充

BS_DrvNum

0x40

1

与 FAT12/16 的定义相同,只不过两者位于启动扇区不同的位置而已

BS_Reserved1

0x41

1

与 FAT12/16 的定义相同,只不过两者位于启动扇区不同的位置而已

BS_BootSig

0x42

1

与 FAT12/16 的定义相同,只不过两者位于启动扇区不同的位置而已

BS_VolID

0x43

4

与 FAT12/16 的定义相同,只不过两者位于启动扇区不同的位置而已

BS_FilSysType

0x47

11

与 FAT12/16 的定义相同,只不过两者位于启动扇区不同的位置而已

BS_FilSysType

0x52

8

通常设置为“ FAT32 ”,请参照 FAT12/16 此部分的陈述。


关于 FAT 启动扇区还有一点重要的说明,我们假设里面的内容是按字节排序的,那么扇区 [510] 的内容一定 0x55 ,扇区 [511] 的内容一定是 0xAA


很多 FAT 资数文档会把 0xAA55 说成是“启动扇区最后两字节的内容”,这样的陈述是正确的 — 仅仅是如果 —BPB_BytsPerSec 的值为 512 的话。若 BPB_BytsSec 的值大于 512 ,该标记的位置并没有改变(虽然在启动扇区的最后两个字节写 0xAA55 并没有问题)


关于 BPB_ToSec16/32 这里再作一点补充:假设一现在我们有一块磁盘或一个分区,它的扇区数为 DskSz ,如果 BPB_aToSec(BPB_ToSec16 或是 BPB_ToSec32 基中不为 0 的那个 ) 的值小于或等于 DskSz 并不会使该 FAT 卷在使用中出现什么错误,实际上 BPB_ToSec16/32 的值不要比 DskSz 小得离谱就不会有什么错误


这样做将造成磁盘空间的浪费,程序本身并不会认为该 FAT 卷存在什么错误,但是,如果 BPB_ToSec16/32 的值比 DskSz 大将会使 FAT 卷遭到严重的损坏,因为它超出了存储介质或是磁盘分区的边界。当 BPB_ToSec16/32 的值比 DskSz 大时,一些数据将不幸地被丢失


 


FAT文件系统数据结构C语言表示


FAT文件系统相关数据结构

struct fat_boot_sector { 
  
    uint8_t    ignored[3];    /* 0x00 Boot strap short or near jump */ 
  
    int8_t    system_id[8];    /* 0x03 Name - can be used to special case 
  
                   partition manager volumes */ 
  
    uint8_t    sector_size[2];    /* 0x0B bytes per logical sector */ 
  
    uint8_t    sectors_per_cluster;    /* 0x0D sectors/cluster */ 
  
    uint16_t    reserved;    /* 0x0E reserved sectors */ 
  
    uint8_t    fats;        /* 0x10 number of FATs */ 
  
    uint8_t    dir_entries[2];    /* 0x11 root directory entries */ 
  
    uint8_t    sectors[2];    /* 0x13 number of sectors */ 
  
    uint8_t    media;        /* 0x15 media code (unused) */ 
  
    uint16_t    fat_length;    /* 0x16 sectors/FAT */ 
  
    uint16_t    secs_track;    /* 0x18 sectors per track */ 
  
    uint16_t    heads;        /* 0x1A number of heads */ 
  
    uint32_t    hidden;        /* 0x1C hidden sectors (unused) */ 
  
    uint32_t    total_sect;    /* 0x20 number of sectors (if sectors == 0) */ 
  

    /* The following fields are only used by FAT32 */ 
  
    uint32_t    fat32_length;    /* 0x24=36 sectors/FAT */ 
  
    uint16_t    flags;        /* 0x28 bit 8: fat mirroring, low 4: active fat */ 
  
    uint8_t    version[2];    /* 0x2A major, minor filesystem version */ 
  
    uint32_t    root_cluster;    /* 0x2C first cluster in root directory */ 
  
    uint16_t    info_sector;    /* 0x30 filesystem info sector */ 
  
    uint16_t    backup_boot;    /* 0x32 backup boot sector */ 
  
    uint8_t    BPB_Reserved[12];    /* 0x34 Unused */ 
  
    uint8_t    BS_DrvNum;        /* 0x40 */ 
  
    uint8_t    BS_Reserved1;        /* 0x41 */ 
  
    uint8_t    BS_BootSig;        /* 0x42 */ 
  
    uint8_t    BS_VolID[4];        /* 0x43 */ 
  
    uint8_t    BS_VolLab[11];        /* 0x47 */ 
  
    uint8_t    BS_FilSysType[8];    /* 0x52=82*/ 
  

    /* */ 
  
    uint8_t    nothing[420];    /* 0x5A */ 
  
    uint16_t    marker; 
  
} __attribute__ ((__packed__)); 
  

struct msdos_dir_entry { 
  
    int8_t    name[8],ext[3];        /* 00 name and extension */ 
  
    uint8_t    attr;            /* 0B attribute bits */ 
  
    uint8_t    lcase;        /* 0C Case for base and extension */ 
  
    uint8_t            ctime_ms;    /* 0D Creation time, milliseconds */ 
  
    uint16_t    ctime;        /* 0E Creation time */ 
  
    uint16_t    cdate;        /* 10 Creation date */ 
  
    uint16_t    adate;        /* 12 Last access date */ 
  
    uint16_t        starthi;    /* 14 High 16 bits of cluster in FAT32 */ 
  
    uint16_t    time;           /* 16 time, date and first cluster */ 
  
        uint16_t        date;        /* 18 */ 
  
        uint16_t        start;        /* 1A */ 
  
    uint32_t    size;        /* 1C file size (in bytes) */ 
  
}; 
  

/* Up to 13 characters of the name */ 
  
struct msdos_dir_slot { 
  
    uint8_t    id;            /* 00 sequence number for slot */ 
  
    uint8_t    name0_4[10];        /* 01 first 5 characters in name */ 
  
    uint8_t    attr;        /* 0B attribute byte */ 
  
    uint8_t    reserved;        /* 0C always 0 */ 
  
    uint8_t    alias_checksum;    /* 0D checksum for 8.3 alias */ 
  
    uint8_t    name5_10[12];    /* 0E 6 more characters in name */ 
  
    uint16_t   start;        /* 1A starting cluster number, 0 in long slots */ 
  
    uint8_t    name11_12[4];    /* 1C last 2 characters in name */ 
  
};