FAT文件系统原理详细介绍

第一部分 FAT文件系统详细介绍

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 分区的构成概况

引导扇区
其他保留扇区(可选) FAT1 FAT2
根目录区
(仅 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 每扇区的字节数,取值只能是以下几种:51210242048或是4096。设为512会取得最好的兼容性,目前有很多FAT代码都是硬性规定每扇区的字节数为512,而不是实际的检测此值。但微软的操作系统能够很好支持10242048或是4096
BPB_SecPerClus 0x0d 1 每簇的扇区数,其值必须中2的整数次方(该整数必须>=0),同时还要保证每簇的字节数不能超过32K,也就是1024*32字节
BPB_RsvdSecCnt 0x0e 2 保留扇区的数目,此域不能为0FAT12/FAT16必须为1FAT32的典型值取为32,,微软的系统支持任何非0
BPB_BumFATs 0x10 1 分区中FAT表的份数,,任何FAT格式都建议为2
BPB_RootEntCnt 0x11 2 对于FAT12FAT16此域包含根目录中目录的个数(每项长度为32bytes),对于FAT32,此项必须为0。对于FAT12FAT16,此数乘以32必为BPB_BytesPerSec的偶数倍,为了达到更好的兼容性,FAT12FAT16都应该取值为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,此域只对于有“特殊形状”(由磁头和柱面每分割为若干磁道)的存储介质有效,同时必须可以调用BIOS0x13中断得到此数值
BPB_NumHeads 0x1A 2 磁头数,用于BIOS0x13中断,类似于上面的BPB_ SecPerTrk,只对特殊的介质才有效,此域包含一个至少为1的数值,比如1,4M的软盘此域为2
BPB_HidSec 0x1C 4 在此FAT分区之前所隐藏的扇区数,必须使得调用BIOS0x13中断可以得到此数值,对于那些没有分区的存储介质,此域必须为0,具体使用什么值由操作系统决定
BPB_ToSec32 0x20 4 该卷总扇区数(32bit),这里的扇区总数包括FAT卷四个个基本分的全部扇区,此域可以为0,若此域为0BPB_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 */
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值