操作系统实践-预备知识-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参数(磁头/柱面/扇区)
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,按照比特流排序为 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  |................|

你可能感兴趣的:(操作系统实践)