FAT12镜像查看
前言:为了搞定OS的Lab2实验——实现一个FAT12镜像查看工具,还是耗费了较多的精力的,以此文记录下收获
准备工作
工具:为了方便较为直观呈现效果,采用vscode + Hex Editor插件
镜像文件:a.img 1.44MB
镜像结构:
FAT12文件系统
FAT
FAT(File Allocation Table)文件配置表。用于记录文件所在位置的表格。
在DOS v1.0时代就已引入,是最基本的文件系统之一。
FAT家族包括:FAT12、FAT16、FAT32、ExFAT、VFAT
FAT12
12位地址,故最大容量为16MB,软盘文件系统使用。
标准以FAT12组织的软盘包括以下设定:
- 两磁头
- 每磁头80个磁道
- 每磁道18扇区
- 每扇区512字节
故FAT12的标准软盘大小为2 * 80 * 18 * 512 = 1.44MB,故一个标准1.44MB的FAT12软盘共有2 * 80 * 18 = 2880扇区
文件系统为了方便组织与管理,会将磁盘分为若干层次:
- 扇区:磁盘上最小数据单元
- 簇:一个或多个扇区,是数据存储的基本单位
- 分区:不同功能区
上述2880扇区被分为如下5个不同的分区
由于根目录分区的大小不确定,数据区大小也不能够加以确定。
MBR区
引导扇区,占用一个扇区大小空间。
先前的学习中,我们了解到上电后Bios自检,会检测第0磁头、第0磁道、第1扇区是否以0x55和0xaa作结,以此判断该扇区是否为引导扇区。
从第11个字节开始的25个字节构成一个较为特殊的结构BPB(Bios Parameter Block)
用一个结构体描述BPB的结构就可以表示为:
typedef struct BPB { |
可以看出BPB对于接下来许多操作都具有决定性作用,比如:
BPB_BytsPerSec * BPB_SecPerClus,这就是一个簇的字节大小
BPB_RsvdSecCnt + BPB_FATSz16 * BPB_NumFATs,这就是MBR区以及FAT表区所占扇区数,所对应扇区也是根目录区开始扇区号
查看以下1.44MB的a.img的第一个扇区
- 扇区以
0x550xAA结尾 - 扇区第
11到35字节为00 02 01 01 00 02 E0 00 40 0B F0 09 00 12 00 02 00 00 00 00 00 00 00 00 00
对应于BPB即有
BPB { |
符合先前
标准以FAT12组织的软盘包括以下设定: |
同时扇区划分,扇区总数,每个簇所对应扇区数,FAT表个数以及每个FAT表所占扇区数等数据均吻合。
FAT表
FAT1与FAT2互为备份,所以理论上两张表是一样的。
FAT表被划分为紧密排列的若干表项,每个表项与数据区中一个簇对应,表项序号同时与簇号一一对应。
FAT12每个表项由12位组成,故先前说其是12位地址,最大容量为16MB。
需要注意的是,由于1.44MB的软盘上FAT12前三个字节为固定的0xF0、0xFF、0xFF,用于表示这是一个FAT12文件系统,所以3字节,24位,2表项被占据,对应的簇0 簇1就没有存在意义,故数据区起始不是簇0,而是簇2。
FAT表项的值的含义:
- 通常情况下代表文件下一簇号
- 值
>= 0xFF8,该簇已经是文件最后一个簇 - 值
= 0xFF7,表示一个坏簇
根目录区
根目录区由根目录下的目录项/文件构成,一个目录项占据32字节
类似的,每个目录项都有固定的结构,可用如下结构体进行模拟:
typedef struct RootEntry { |
通过查询根目录区即可得到文件/目录开始簇号,也就可以去相应簇号读取。那么引入FAT表进行定位意义何在?大文件!
上述方式只适用于文件大小小于一个簇的,鉴于文件的零散分布,就需要FAT表指示文件的下一部分所在簇号,方便加以定位,将散乱的文件整合起来。
由此,归结起来,FAT12访问文件的基本操作为:
- 首先通过根目录文件查找文件名,确定是哪一个条目,接着在条目中访问DIR_FstClus对应的开始簇号
- 当一个簇号访问完后,通过FAT表项查询下一簇号,决定是结束还是继续访问下一簇号,重复第二条
由上面区域划分可知,根目录区始于19扇区,即19 * 512 = 2600h
先前又可知根目录区最大文件数为224,故根目录区所占扇区大小为(32 * BPB_RootEntCnt + BPB_BytsPerSec - 1) / BPB_BytsPerSec = (32 * 224 + 512 - 1) / 512 = 14
加上BPB_BytsPerSec-1保证此公式在根目录区无法填满整个扇区时仍然能够成立
数据区
数据存放区域,包括文件以及根目录下目录对应的文件信息,该信息同根目录区,使用Entry对目录加以存储。
由于根目录区占据14扇区,加上先前MBR以及FAT占据的19扇区,计算得出数据区起始于33扇区,即33 * 512 = 4200h
实例
下面就实际的通过FAT12对文件进行查找
回顾步骤:
- 首先通过根目录文件查找文件名,确定是哪一个条目,接着在条目中访问DIR_FstClus对应的开始簇号
- 当一个簇号访问完后,通过FAT表项查询下一簇号,决定是结束还是继续访问下一簇号,重复第二条
大致流程也就如上图所示
再看一下目录结构
分别以一个目录和文件举例
查看HOUSE目录
step1:查看根目录目录项
定位到2600h
HOUSE目录具有以下结构
RootEntry HOUSE { |
判断是文件还是目录,FAT12下文件属性常见如下:
- 0x01:只读文件
- 0x02:隐藏文件
- 0x04:系统文件
- 0x08:卷标
- 0x10:子目录
- 0x20:存档文件
根据DIR_Attr = 0x10也能判断出HOUSE为子目录类型
HOUSE子目录下还有ROOM子目录,去数据区HOUSE开始簇号4查看
簇号4应该是定位到(4 - 2) * 512 + 4200h = 0400h + 4200h = 4600h
忽略.和..子目录,ROOM子目录具有以下结构:
RootEntry ROOM { |
去对应的簇号6(4A00h)处
查看到ROOM目录下的信息
step2:查询FAT表
HOUSE起始簇内容全部访问完了,紧接着我们就要去访问与簇号对应的FAT表了,FAT表对应表项为簇号4
FAT1表起始于1扇区,即200h
前三个字节为固定的0xF0、0xFF、0xFF,符合FAT12
如何读取12位FAT表项取值?
可不是照着顺序F0F FFF这样,这就涉及到存储的方式了。以下是几张较为直观的示意图:
以我的看法,由于是按字节(8位)进行存取的,12位的尴尬取值,就不得不由一个完整的字节与另一字节的一半拼接起来。
以FAT(0) 和 FAT(1)为例,依据低地址在大端,高地址在小端的顺序,构成的三个字节应该为0xFF-FF-F0,读取12字节划分应该为0xFFF-FF0,FAT(0)的高位F为byte2的低位,FAT(1)的低位F为byte2的高位F,真是奇怪的读取方式。
用16位存储关联两字节后获取这12位的FAT表项也就得分奇偶进行区分了。粗略有以下写法
if(奇数表项号){ |
所以簇号4对应FAT表项值为0xFFF,>0xFF8,表明已是文件的最后一个簇(HOUSE作为目录,对应的首簇能放512 / 32 = 16条entry)
查看/NJU/SOFTWARE/SE1.TXT
之所以选择该文件一是目录结构有点复杂,而是文件大小超过512字节,也就是1簇,便于理解FAT表
step1:查看根目录目录项
大致与上文一样,定位NJU目录开始簇号为0x0005 = 5,定位至4800h
定位SOFTWARE目录开始簇号为0x0008 = 8,定位至4E00h
定位SE1.TXT开始簇号为0x000B = 11,定位至5400h
step2:查询FAT表
查询表项FAT(11),为0x00C = 12,接着继续去簇12读取接下来内容,查询表项FAT(12),为0x00D = 13,接着去簇13读取下一段内容,查询表项FAT(13) = 0x00E = 14,去簇14读取剩余内容,查询表项FAT(14) = 0xFFF,读取完毕。
所以簇14为最后一个簇,定位到5A00h,确实如此。
至此已完成了FAT12文件系统的了解,镜像查看工具的实现依据上面所说文件读取过程进行模拟即可。
参考文章:





















