参考资料:http://www.disc.ua.es/~gil/FAT12Description.pdf
软盘
软盘的文件组织格式一般为 FAT12,组织单位由大到小分为:
- 分区:例如 C 盘、D 盘
- 簇:文件以簇为单位分配存储空间,一个簇包含一个或多个扇区
- 扇区:磁盘上的最小数据单元
每个软盘有 2个面 * 80个柱面 * 18个扇区(物理扇区编号从1开始) * 512字节 = 1.44MB。
磁盘的逻辑扇区从0开始编号,每次读完一个柱面的两面。例如,0~17 对应0面0柱面,18~35 对应1面0柱面。也有说每个扇区奇偶异面的,待定。
FAT12 组织格式
逻辑扇区 | 占用扇区 | 内容 | 磁盘CHS参数(磁头/柱面/扇区) |
---|---|---|---|
0 | 1(512B) | 引导程序 | 0/0/1 |
1 | 9(4608B) | FAT文件分配表1 | 0/0/2 ~0/0/10 |
10 | 9(4608B) | FAT文件分配表2,冗余数据 | 0/0/11 ~1/0/1 |
19 | 14(9728B) | 目录文件项 | 长度不固定,在第一个扇区中指定 |
33 | end | 文件数据区 |
- 引导扇区:0
- FAT1表:1-9扇区
- FAT2表:10-18扇区,FAT1 的冗余结构
- 根目录区:长度非固定,由BPB信息中的目录条目数 BPB_RootEntCnt 决定,每个目录条目占用32字节(支持长文件名时至少占 64 字节)
- 数据区:长度非固定
引导扇区
软盘的第1个扇区就是引导扇区,保存文件系统元数据(类型、逻辑扇区总数、每簇包含的扇区数、磁道和扇区的关系等)。这 512B 按照顺序划分为三部分:
- BPB 数据结构:大小为62字节,保存文件系统元数据
- 其他448字节:可用来存放引导代码、数据和其他填充字符
- 启动扇区标志:末尾2字节,0xAA55
启动扇区的 512 字节分布如下:
名称 | 开始字节 | 长度 | 内容 | 参考值 |
---|---|---|---|---|
BS_jmpBOOT | 0 | 3 | 跳转指令 | jmp _bootsect nop(短跳转2字节,加1字节nop) |
BS_OEMName | 3 | 8 | OEM 字符串,必须为8个字符,不足以空格填空 | “MSWIN4.1” |
BPB_BytesPerSec | 11 | 2 | 每扇区字节数 | 0x200 |
BPB_SecPerClus | 13 | 1 | 每簇扇区数 | 0x1 |
BPB_ResvdSecCnt | 14 | 2 | 保留扇区数(用于Boot) | 0x1 |
BPB_NumFATs | 16 | 1 | 共有多少FAT表 | 0x2 |
BPB_RootEntCnt | 17 | 2 | 根目录的最大条目数 | 0xE0 |
BPB_TotSec16 | 19 | 2 | 逻辑扇区总数 | 0xB40 |
BPB_Media | 21 | 1 | 介质描述符 | 0xF0 |
BPB_FATSz16 | 22 | 2 | 每个FAT表所占扇区数 | 0x9 |
BPB_SecPerTrk | 24 | 2 | 每磁道扇区数 | 0x12 |
BPB_NumHeads | 26 | 2 | 磁头数(面数) | 0x2 |
BPB_HiddSec | 28 | 4 | 隐藏扇区数 | 0 |
BPB_TotSec32 | 32 | 4 | 如果BPB_TotSec16=0,则在这里给出扇区数 | 0 |
BS_DrvNum | 36 | 1 | INT 13H的驱动器号,从0开始 | 0 |
BS_Reserved1 | 37 | 1 | 保留,未使用 | 0 |
BS_BootSig | 38 | 1 | 扩展引导标记,固定 | 0x29 |
BS_VolID | 39 | 4 | 卷序列号 | 0 |
BS_VolLab | 43 | 11 | 卷标,必须是11个字符,不足以空格填充 | “hello world” |
BS_FileSysType | 54 | 8 | 文件系统类型,必须是8个字符,不足填充空格 | "FAT12 " |
引导代码及其他内容 | 62 | 448 | 引导代码及数据,,不足填充空格。由0字节处跳转过来 | |
结束标志0xAA55 | 510 | 2 | 第510字节为0x55,第511字节为0xAA | 0xAA55 |
FAT 表
FAT1 和 FAT2 表完全一样,各9个扇区。FAT 表是个位图,每 12 位组成一个 FAT 项(FAT Entry)表示一个簇。
物理扇区号 = 33 + FAT扇区号 - 2
FAT 项编号从 0 开始,且 0 和 1 号保留不用,2 号 FAT 项开始表示数据区的每一个簇,指向物理扇区的 33 号(0~32 号不放数据),但 2 号通常放分区后的卷标,一般从 3 号开始放数据,指向物理扇区的 34 号(地址 0x4400)。最多支持4096个簇(9扇区 * 512字节 * 8bit / 12bit)。
FAT 项非0时,表示对应扇区已被文件使用,且其值代表的是文件的下一个簇号(逻辑扇区号,0~2880),FAT 项的值的含义:
- 0x0ff0 ~ 0x0ff6:保留
- 0x0ff7:坏簇,磁道或柱面损坏不可使用,在格式式磁盘时由系统自动填充
- 0x0ff8 ~ 0x0fff代表文件内容结束,到此簇为止
- 其它值:当前文件的下一个簇号
根目录区
普通条目
文件名最大8个字符,后缀最大3个字符,仅支持 ASCII。
Offset (in bytes) | Length (in bytes) | Description |
---|---|---|
0 | 8 | Filename (but see notes below about the first byte in this field) |
8 | 3 | Extension |
11 | 1 | Attributes (see details below) |
12 | 2 | Reserved |
14 | 2 | Creation Time |
16 | 2 | Creation Date |
18 | 2 | Last Access Date |
20 | 2 | Ignore in FAT12 |
22 | 2 | Last Write Time |
24 | 2 | Last Write Date |
26 | 2 | First Logical Cluster |
28 | 4 | File Size (in bytes) |
文件目录项占用14个扇区,每个项占用32个字节,最大支持的根目录个数为:14扇区 * 512字节 / 32字节 = 224个。
根目录区的开始扇区号是19,它是由若干个目录条目(Directory Entry)组成,条目最多有BPB_RootEntCnt个,由于根目录区的大小是依赖于BPB_RootEntCnt的,所以长度不固定。
部分目录条目内容如下:
名称 | 偏移 | 长度 | 描述 |
---|---|---|---|
DIR_Name | 0 | 0xB | 文件名8字节,扩展名3字节 |
DIR_Attr | 0xB | 1 | 文件属性 |
DIR_WrtTime | 0x16 | 2 | 最后修改时间 |
DIR_WrtDate | 0x18 | 2 | 最后修改日期 |
DIR_FstClus | 0x1A | 2 | 此条目对应的开始簇号 |
DIR_FileSize | 0x1C | 4 | 文件大小 |
支持长文件名的 LFN 条目
长文件名(Long File Name)
FAT12早期文件名最大8个字符,后缀最大3个。此时每个目录项是 32 字节。为了支持长文件名,增加了 32 字节为单位的 LFN 条目。LFN 条目在普通条目之前,个数不限。
LFN 参考这里。
LFN 条目结构
字节范围 | 描述 |
---|---|
0 | 序号和分配状态 |
1 - 10 | 文件名字符(Unicode) |
11 | 文件属性 |
12 | 保留 |
13 | 校验和 |
14 - 25 | 文件名字符(Unicode) |
26 - 27 | 保留 |
28 - 31 | 文件名字符(Unicode) |
创建目录跟创建文件的差异
- 根目录下,不管创建文件还是目录,都会在 FAT 表中分配至少一个条目,并且在根目录区中分配一个条目。
- 子目录下创建时,会在 FAT 表中分配至少一个条目,并且在当前子目录区中分配一个条目。
数据区
每个文件至少占 1 个扇区,通过 FAT 表形成簇链。
Linux 下创建并操作 FAT12
创建 FAT12 文件系统
创建空文件
dd if=/dev/zero of=floppy.img bs=512 count=2880
格式化位 FAT12
mkfs.vfat -F 12 floppy.img
查看内容
通过 hexdump -C floppy.img | less
命令查看软盘的二进制内容:
00000000 eb 3c 90 6d 6b 66 73 2e 66 61 74 00 02 01 01 00 |.<.mkfs.fat.....|
00000010 02 e0 00 40 0b f0 09 00 12 00 02 00 00 00 00 00 |...@............|
00000020 00 00 00 00 00 00 29 18 47 11 5a 4e 4f 20 4e 41 |......).G.ZNO NA|
00000030 4d 45 20 20 20 20 46 41 54 31 32 20 20 20 0e 1f |ME FAT12 ..|
00000040 be 5b 7c ac 22 c0 74 0b 56 b4 0e bb 07 00 cd 10 |.[|.".t.V.......|
00000050 5e eb f0 32 e4 cd 16 cd 19 eb fe 54 68 69 73 20 |^..2.......This |
00000060 69 73 20 6e 6f 74 20 61 20 62 6f 6f 74 61 62 6c |is not a bootabl|
00000070 65 20 64 69 73 6b 2e 20 20 50 6c 65 61 73 65 20 |e disk. Please |
00000080 69 6e 73 65 72 74 20 61 20 62 6f 6f 74 61 62 6c |insert a bootabl|
00000090 65 20 66 6c 6f 70 70 79 20 61 6e 64 0d 0a 70 72 |e floppy and..pr|
000000a0 65 73 73 20 61 6e 79 20 6b 65 79 20 74 6f 20 74 |ess any key to t|
000000b0 72 79 20 61 67 61 69 6e 20 2e 2e 2e 20 0d 0a 00 |ry again ... ...|
000000c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
000001f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 aa |..............U.|
00000200 f0 ff ff 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000210 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00001400 f0 ff ff 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00001410 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00168000
名称 | 开始字节 | 长度 | 内容 |
---|---|---|---|
BS_jmpBOOT | 0 | 3 | |
BS_OEMName | 3 | 8 | “mkfs.fat” |
BPB_BytesPerSec | 11 | 2 | 0x0200 |
BPB_SecPerClus | 13 | 1 | 0x01 |
BPB_ResvdSecCnt | 14 | 2 | 保留扇区数(用于Boot) 0x0001 |
BPB_NumFATs | 16 | 1 | 共有多少FAT表 0x02 |
BPB_RootEntCnt | 17 | 2 | 根目录的最大文件数 0x00E0 |
BPB_TotSec16 | 19 | 2 | 逻辑扇区总数 0x0B40 |
BPB_Media | 21 | 1 | 介质描述符 0xF0 |
BPB_FATSz16 | 22 | 2 | 每个FAT表所占扇区数 0x0009 |
BPB_SecPerTrk | 24 | 2 | 每磁道扇区数 0x0012 |
BPB_NumHeads | 26 | 2 | 磁头数(面数) 0x0002 |
BPB_HiddSec | 28 | 4 | 隐藏扇区数 0x00000000 |
BPB_TotSec32 | 32 | 4 | 如果BPB_TotSec16=0,则在这里给出扇区数 0x00000000 |
BS_DrvNum | 36 | 1 | INT 13H的驱动器号,从0开始 0x00 |
BS_Reserved1 | 37 | 1 | 保留,未使用 0x00 |
BS_BootSig | 38 | 1 | 扩展引导标记,固定 0x29 |
BS_VolID | 39 | 4 | 卷序列号 0x5a114718 |
BS_VolLab | 43 | 11 | 卷标,必须是11个字符,不足以空格填充 "NO NAME " |
BS_FileSysType | 54 | 8 | 文件系统类型,必须是8个字符,不足填充空格 "FAT12 " |
引导代码及其他内容 | 62 | 448 | 引导代码及数据,,不足填充空格。由0字节处跳转过来 |
结束标志0xAA55 | 510 | 2 | 0xaa55 |
挂载并写入文件
命令
mount floppy.img /mnt
cp a.txt /mnt
umount /mnt
hexdump -C floppy.img | less
查看文件系统
根目录区
根目录区从第 19 个扇区开始(19 * 512Byte,即 0x2600),共 224 个条目,占 14 个扇区。
可以看到第一个文件 a.txt 对应目录区的 64 个字节,说明支持 LFN 长文件名(下面有示例),根目录条目前有 LFN 条目。刚才创建的文件的属性就是:
- 文件名+后缀名:41 61 00 2e 00 74 00 78 00 74 00
- 属性:0f
- 第一个逻辑簇序号:基本目录项的倒数第5、6字节,即 0x0003,说明文件的第一个扇区在第 3 个逻辑扇区,对应物理扇区的 33+3-2 = 34。去 FAT 表找第 3 个,也就是 fff,说明文件只有一个扇区
- 文件长度:基本目录项的倒数 4 个字节。d3,211 字节
- 最后一次写的日期:基本目录项的倒数第7、8字节,即 0x502c,20524
- 最后一次写的时间:基本目录项的倒数第9、10字节,即 0x1ef9,7929
FAT 表
FAT1 表从第1个扇区开始(512Byte,即 0x200),去掉前两个条目(共24bit),得到 00 f0 ff
,按照比特流排序为 000
和 fff
两个条目。所以第 3 号条目就是 fff,表示这个文件只有一个扇区。
数据区
1个启动扇区+18个FAT扇区+14个根目录扇区,数据区之前有33个扇区。数据区起始地址 = 34 * 512B = 0x4400。
FAT12 文件的二进制
000000c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
000001f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 aa |..............U.|
00000200 f0 ff ff 00 f0 ff 00 00 00 00 00 00 00 00 00 00 |................|
00000210 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00001400 f0 ff ff 00 f0 ff 00 00 00 00 00 00 00 00 00 00 |................|
00001410 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00002600 41 61 00 2e 00 74 00 78 00 74 00 0f 00 5d 00 00 |Aa...t.x.t...]..|
00002610 ff ff ff ff ff ff ff ff ff ff 00 00 ff ff ff ff |................|
00002620 41 20 20 20 20 20 20 20 54 58 54 20 00 1e f9 1e |A TXT ....|
00002630 2c 50 2c 50 00 00 f9 1e 2c 50 03 00 d3 00 00 00 |,P,P....,P......|
00002640 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00004400 74 6f 74 61 6c 20 31 34 34 38 0a 64 72 77 78 72 |total 1448.drwxr|
00004410 2d 78 72 2d 78 20 32 20 72 6f 6f 74 20 72 6f 6f |-xr-x 2 root roo|
00004420 74 20 20 20 20 34 30 39 36 20 4a 61 6e 20 31 31 |t 4096 Jan 11|
00004430 20 32 32 3a 35 35 20 2e 2f 0a 64 72 77 78 72 2d | 22:55 ./.drwxr-|
00004440 78 72 2d 78 20 35 20 72 6f 6f 74 20 72 6f 6f 74 |xr-x 5 root root|
00004450 20 20 20 20 34 30 39 36 20 4a 61 6e 20 31 31 20 | 4096 Jan 11 |
00004460 32 32 3a 34 31 20 2e 2e 2f 0a 2d 72 77 2d 72 2d |22:41 ../.-rw-r-|
00004470 2d 72 2d 2d 20 31 20 72 6f 6f 74 20 72 6f 6f 74 |-r-- 1 root root|
00004480 20 20 20 20 20 20 20 30 20 4a 61 6e 20 31 31 20 | 0 Jan 11 |
00004490 32 32 3a 35 35 20 61 2e 74 78 74 0a 2d 72 77 2d |22:55 a.txt.-rw-|
000044a0 72 2d 2d 72 2d 2d 20 31 20 72 6f 6f 74 20 72 6f |r--r-- 1 root ro|
000044b0 6f 74 20 31 34 37 34 35 36 30 20 4a 61 6e 20 31 |ot 1474560 Jan 1|
000044c0 31 20 32 32 3a 35 35 20 66 6c 6f 70 70 79 2e 69 |1 22:55 floppy.i|
000044d0 6d 67 0a 00 00 00 00 00 00 00 00 00 00 00 00 00 |mg..............|
000044e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00168000
创建一个大于 512B 的文件和目录
大于一个扇区的文件
- 文件大小:0x0000020a,即 522B
- 第一个扇区对应 FAT 逻辑序号:0004,对应的数据是
05 f0 ff
,即 005,说明这个文件不止一个扇区且下一个扇区的逻辑编号是 005,刚好里面放的是 fff,因此这个文件占两个扇区
目录
- 第一个扇区对应 FAT 逻辑序号:0006,对应的数据是
fff
,因此这个目录占一个扇区,这个扇区用于放子目录条目(类似根目录条目) - 去物理扇区 0x4a00(33+6-2 = 37),可以看到创建了两个目录:
.
和..
00000200 f0 ff ff 00 f0 ff 05 f0 ff ff 0f 00 00 00 00 00 |................|
00000210 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00001400 f0 ff ff 00 f0 ff 05 f0 ff ff 0f 00 00 00 00 00 |................|
00001410 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00002600 41 61 00 2e 00 74 00 78 00 74 00 0f 00 5d 00 00 |Aa...t.x.t...]..|
00002610 ff ff ff ff ff ff ff ff ff ff 00 00 ff ff ff ff |................|
00002620 41 20 20 20 20 20 20 20 54 58 54 20 00 1e f9 1e |A TXT ....|
00002630 2c 50 2c 50 00 00 f9 1e 2c 50 03 00 d3 00 00 00 |,P,P....,P......|
00002640 41 62 00 2e 00 74 00 78 00 74 00 0f 00 1d 00 00 |Ab...t.x.t......|
00002650 ff ff ff ff ff ff ff ff ff ff 00 00 ff ff ff ff |................|
00002660 42 20 20 20 20 20 20 20 54 58 54 20 00 2d f1 22 |B TXT .-."|
00002670 2c 50 2c 50 00 00 f1 22 2c 50 04 00 0a 02 00 00 |,P,P...",P......|
00002680 41 64 00 69 00 72 00 00 00 ff ff 0f 00 fe ff ff |Ad.i.r..........|
00002690 ff ff ff ff ff ff ff ff ff ff 00 00 ff ff ff ff |................|
000026a0 44 49 52 20 20 20 20 20 20 20 20 10 00 a6 8f 2a |DIR ....*|
000026b0 2c 50 2c 50 00 00 8f 2a 2c 50 06 00 00 00 00 00 |,P,P...*,P......|
000026c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
...
00004800 6c 6f 70 70 79 2e 69 6d 67 0a 00 00 00 00 00 00 |loppy.img.......|
00004810 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00004a00 2e 20 20 20 20 20 20 20 20 20 20 10 00 a6 8f 2a |. ....*|
00004a10 2c 50 2c 50 00 00 8f 2a 2c 50 06 00 00 00 00 00 |,P,P...*,P......|
00004a20 2e 2e 20 20 20 20 20 20 20 20 20 10 00 a6 8f 2a |.. ....*|
00004a30 2c 50 2c 50 00 00 8f 2a 2c 50 00 00 00 00 00 00 |,P,P...*,P......|
00004a40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00168000
再创建 abcdefghijklmn.txt 和 /dir/c.txt
长文件名
可以看到长文件名的文件,总共占了 3 个条目(两个 LFN,一个基本条目):
目录区:
00002600 41 61 00 2e 00 74 00 78 00 74 00 0f 00 5d 00 00 |Aa...t.x.t...]..|
00002610 ff ff ff ff ff ff ff ff ff ff 00 00 ff ff ff ff |................|
00002620 41 20 20 20 20 20 20 20 54 58 54 20 00 1e f9 1e |A TXT ....|
00002630 2c 50 2c 50 00 00 f9 1e 2c 50 03 00 d3 00 00 00 |,P,P....,P......|
00002640 41 62 00 2e 00 74 00 78 00 74 00 0f 00 1d 00 00 |Ab...t.x.t......|
00002650 ff ff ff ff ff ff ff ff ff ff 00 00 ff ff ff ff |................|
00002660 42 20 20 20 20 20 20 20 54 58 54 20 00 2d f1 22 |B TXT .-."|
00002670 2c 50 2c 50 00 00 f1 22 2c 50 04 00 0a 02 00 00 |,P,P...",P......|
00002680 41 64 00 69 00 72 00 00 00 ff ff 0f 00 fe ff ff |Ad.i.r..........|
00002690 ff ff ff ff ff ff ff ff ff ff 00 00 ff ff ff ff |................|
000026a0 44 49 52 20 20 20 20 20 20 20 20 10 00 01 a8 2c |DIR ....,|
000026b0 2c 50 2c 50 00 00 a8 2c 2c 50 06 00 00 00 00 00 |,P,P...,,P......|
000026c0 42 6e 00 6f 00 70 00 71 00 2e 00 0f 00 27 74 00 |Bn.o.p.q.....'t.|
000026d0 78 00 74 00 00 00 ff ff ff ff 00 00 ff ff ff ff |x.t.............|
000026e0 01 61 00 62 00 63 00 64 00 65 00 0f 00 27 66 00 |.a.b.c.d.e...'f.|
000026f0 67 00 68 00 69 00 6a 00 6b 00 00 00 6c 00 6d 00 |g.h.i.j.k...l.m.|
00002700 41 42 43 44 45 46 7e 31 54 58 54 20 00 8a 94 2c |ABCDEF~1TXT ...,|
00002710 2c 50 2c 50 00 00 94 2c 2c 50 07 00 0a 02 00 00 |,P,P...,,P......|
00002720 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
子目录下创建文件
这时会把文件属性写在子目录的数据区,而不是根目录区。
dir 目录对应的数据区:
00004a00 2e 20 20 20 20 20 20 20 20 20 20 10 00 a6 8f 2a |. ....*|
00004a10 2c 50 2c 50 00 00 8f 2a 2c 50 06 00 00 00 00 00 |,P,P...*,P......|
00004a20 2e 2e 20 20 20 20 20 20 20 20 20 10 00 a6 8f 2a |.. ....*|
00004a30 2c 50 2c 50 00 00 8f 2a 2c 50 00 00 00 00 00 00 |,P,P...*,P......|
00004a40 41 63 00 2e 00 74 00 78 00 74 00 0f 00 dc 00 00 |Ac...t.x.t......|
00004a50 ff ff ff ff ff ff ff ff ff ff 00 00 ff ff ff ff |................|
00004a60 43 20 20 20 20 20 20 20 54 58 54 20 00 01 a8 2c |C TXT ...,|
00004a70 2c 50 2c 50 00 00 a8 2c 2c 50 09 00 09 00 00 00 |,P,P...,,P......|
00004a80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
可以看到 c.txt 指向 FAT表 9 号,其值是 fff,所以文件只占一个扇区:
00000200 f0 ff ff 00 f0 ff 05 f0 ff ff 8f 00 ff ff ff 00 |................|
00000210 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
c.txt 的数据区:
00005000 61 61 61 61 61 61 61 61 0a 00 00 00 00 00 00 00 |aaaaaaaa........|
00005010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|