Bootstrap

操作系统实践-预备知识-FAT12文件系统

参考资料: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参数(磁头/柱面/扇区)
01(512B)引导程序0/0/1
19(4608B)FAT文件分配表10/0/2 ~0/0/10
109(4608B)FAT文件分配表2,冗余数据0/0/11 ~1/0/1
1914(9728B)目录文件项长度不固定,在第一个扇区中指定
33end文件数据区
  • 引导扇区:0
  • FAT1表:1-9扇区
  • FAT2表:10-18扇区,FAT1 的冗余结构
  • 根目录区:长度非固定,由BPB信息中的目录条目数 BPB_RootEntCnt 决定,每个目录条目占用32字节(支持长文件名时至少占 64 字节)
  • 数据区:长度非固定

引导扇区

软盘的第1个扇区就是引导扇区,保存文件系统元数据(类型、逻辑扇区总数、每簇包含的扇区数、磁道和扇区的关系等)。这 512B 按照顺序划分为三部分:

  • BPB 数据结构:大小为62字节,保存文件系统元数据
  • 其他448字节:可用来存放引导代码、数据和其他填充字符
  • 启动扇区标志:末尾2字节,0xAA55

启动扇区的 512 字节分布如下:

名称开始字节长度内容参考值
BS_jmpBOOT03跳转指令jmp _bootsect
nop(短跳转2字节,加1字节nop)
BS_OEMName38OEM 字符串,必须为8个字符,不足以空格填空“MSWIN4.1”
BPB_BytesPerSec112每扇区字节数0x200
BPB_SecPerClus131每簇扇区数0x1
BPB_ResvdSecCnt142保留扇区数(用于Boot)0x1
BPB_NumFATs161共有多少FAT表0x2
BPB_RootEntCnt172根目录的最大条目数0xE0
BPB_TotSec16192逻辑扇区总数0xB40
BPB_Media211介质描述符0xF0
BPB_FATSz16222每个FAT表所占扇区数0x9
BPB_SecPerTrk242每磁道扇区数0x12
BPB_NumHeads262磁头数(面数)0x2
BPB_HiddSec284隐藏扇区数0
BPB_TotSec32324如果BPB_TotSec16=0,则在这里给出扇区数0
BS_DrvNum361INT 13H的驱动器号,从0开始0
BS_Reserved1371保留,未使用0
BS_BootSig381扩展引导标记,固定0x29
BS_VolID394卷序列号0
BS_VolLab4311卷标,必须是11个字符,不足以空格填充“hello world”
BS_FileSysType548文件系统类型,必须是8个字符,不足填充空格"FAT12 "
引导代码及其他内容62448引导代码及数据,,不足填充空格。由0字节处跳转过来
结束标志0xAA555102第510字节为0x55,第511字节为0xAA0xAA55

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
08Filename (but see notes below about the first byte in this field)
83Extension
111Attributes (see details below)
122Reserved
142Creation Time
162Creation Date
182Last Access Date
202Ignore in FAT12
222Last Write Time
242Last Write Date
262First Logical Cluster
284File Size (in bytes)

文件目录项占用14个扇区,每个项占用32个字节,最大支持的根目录个数为:14扇区 * 512字节 / 32字节 = 224个。

根目录区的开始扇区号是19,它是由若干个目录条目(Directory Entry)组成,条目最多有BPB_RootEntCnt个,由于根目录区的大小是依赖于BPB_RootEntCnt的,所以长度不固定。

部分目录条目内容如下:

名称偏移长度描述
DIR_Name00xB文件名8字节,扩展名3字节
DIR_Attr0xB1文件属性
DIR_WrtTime0x162最后修改时间
DIR_WrtDate0x182最后修改日期
DIR_FstClus0x1A2此条目对应的开始簇号
DIR_FileSize0x1C4文件大小

支持长文件名的 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_jmpBOOT03
BS_OEMName38“mkfs.fat”
BPB_BytesPerSec1120x0200
BPB_SecPerClus1310x01
BPB_ResvdSecCnt142保留扇区数(用于Boot) 0x0001
BPB_NumFATs161共有多少FAT表 0x02
BPB_RootEntCnt172根目录的最大文件数 0x00E0
BPB_TotSec16192逻辑扇区总数 0x0B40
BPB_Media211介质描述符 0xF0
BPB_FATSz16222每个FAT表所占扇区数 0x0009
BPB_SecPerTrk242每磁道扇区数 0x0012
BPB_NumHeads262磁头数(面数) 0x0002
BPB_HiddSec284隐藏扇区数 0x00000000
BPB_TotSec32324如果BPB_TotSec16=0,则在这里给出扇区数 0x00000000
BS_DrvNum361INT 13H的驱动器号,从0开始 0x00
BS_Reserved1371保留,未使用 0x00
BS_BootSig381扩展引导标记,固定 0x29
BS_VolID394卷序列号 0x5a114718
BS_VolLab4311卷标,必须是11个字符,不足以空格填充 "NO NAME "
BS_FileSysType548文件系统类型,必须是8个字符,不足填充空格 "FAT12 "
引导代码及其他内容62448引导代码及数据,,不足填充空格。由0字节处跳转过来
结束标志0xAA5551020xaa55

挂载并写入文件

命令

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,按照比特流排序为 000fff 两个条目。所以第 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  |................|
;