文件说明
image/
: fat12格式测试文件
DiskLib.[h|lib|dll]
: 动态链接库文件
fat12/
: FAT12动态链接库目录
fat12_test/
: 测试程序目录
fat12引导扇区字段说明
名称 | 偏移(BYTE) | 长度(BYTE) | 内容 | 软盘参考值 |
---|---|---|---|---|
BS_jmpBoot | 0 | 3 | - | jmp LABEL_START && nop |
BS_OEMName | 3 | 8 | 厂商名 | ‘Forrest Y’ |
BPB_BytsPerSec | 11 | 2 | 每扇区字节数 | 0x200(512字节) |
BPB_SecPerClus | 13 | 1 | 每簇扇区数 | 0x01 |
BPB_RsvdSecCnt | 14 | 2 | Boot记录占用多少 | 0x01 |
BPB_NumFATs | 16 | 1 | 共有多少FAT表 | 0x02 |
BPB_RootEntCnt | 17 | 2 | 根目录文件数最大值 | 0xE0(224) |
BPB_TotSec16 | 19 | 2 | 扇区总数 | 0xB40(2280) |
BPB_Media | 21 | 1 | 介质描述符 | 0xF0 |
BPB_FATSz16 | 22 | 2 | 每FAT扇区数 | 0x09 |
BPB_SecPerTrk | 24 | 2 | 每磁道扇区数 | 0x12 |
BPB_NumHeads | 26 | 2 | 磁头数 | 0x02 |
BPB_HiddSec | 28 | 4 | 隐藏扇区数 | 0 |
BPB_TotSec32 | 32 | 4 | 如果BPB_TotSec16是0,由这个值记录扇区数 | 0xB40(2280) |
BS_DrvNum | 36 | 1 | 中断13的驱动器号 | 0 |
BS_Reserved1 | 37 | 1 | 未使用 | 0 |
BS_BootSig | 38 | 1 | 扩展引导标记 | 0x29 |
BS_VolD | 39 | 4 | 卷序列号 | 0 |
BS_VolLab | 43 | 11 | 卷标 | ‘OrangeS0.02’ |
BS_FileSysType | 54 | 8 | 文件系统类型 | ‘FAT12’ |
引导代码 | 62 | 448 | 引导代码、数据及其他填充字符等 | |
结束标志 | 510 | 2 | 0xAA55 |
fat1
在偏移512字节处开始(即200h),每个fat项占用12bit,fat表开始3个字节不用于用户文件分配,也就是占用了0簇和1簇,所以用户的数据从簇2开始分配。
Fat表从头开始按3字节分成一组,一组中第2字节的低半字节作为最高半字节和一组中第1字节组成整数表示一个簇号,第2字节的高半字节作为最低半字节和第3字节组成整数表示一个簇号。
我们只需要知道一个文件的首簇号,根据该簇号内容指向的下一个簇号寻找,直到遇到簇号对应内容为0xfff则停止索引,说明已经到了文件末尾。
根目录表字段说明
名称 | 偏移 | 长度 | 描述 |
---|---|---|---|
DIR_Name | 0 | 0xB | 文件名8个字节,扩展名3个字节,文件名不够8字节用空格0x20填充 |
DIR_Attr | 0xB | 1 | 文件属性,卷标项是0x28,文件项是0x20,目录是0x10 |
保留 | 0xC | 10 | |
DIR_WrtTime | 0x16 | 2 | 最后修改时间 |
DIR_WrtDate | 0x18 | 2 | 最后修改日期 |
DIR_FstClus | 0x1A | 2 | 此条目对应开始的簇号 |
DIR_FileSize | 0x1C | 4 | 文件大小 |
如何寻找首簇号
根目录起始地址:1(boot区大小)+2(fat数)*9(fat的扇区数)*512(扇区大小)=0x2600h
从boot记录获取根区记录数,缺省224条。读出所有记录,每条记录32字节,其开始字段为文件名,比较文件名匹配则找到记录,从首簇字段(偏1ah处)获得首簇号。
如何定位多级目录
e.g. a:\tem\tem.txt
目录tem作为一个目录项存储在根目录区中。这样可找到tem目录,且目录项属性应该不是文件(0x20),而是其它。于是可以继续查找。
目录tem本身存储为一个文件,和根目录区一样格式,由32字节的目录项组成。这样tem下的不论是文件还是子目录均由一个目录项登记。而tem目录文件的大小可以随意(不同于根目录),因为文件存储空间可以用fat表的簇链来表示。这样找tem目录项后,从首簇字段获取到tem的目录文件(即类似根目录表),遍历其中的32字节目录项,查找是否存在某个文件或目录即可。 如此,即可形成多层次的目录嵌套。
先来看看fat12格式文件系统的格式:
数据区(长度非固定) |
---|
根目录区(长度非固定) |
FAT12(9扇区) |
FAT12(9扇区) |
引导扇区(1扇区) |
可以计算得出,根目录区的起始地址为(1+9+9)*512=0x2600h
,假定根目录区大小为0x1c00(软盘缺省值224*32Bytes),则数据区起始偏移为0x2600+0x1c00=0x4200
。
又例如:Mytxt.txt的簇号为5,减去开始两个号分配给系统对用户区而言,实际是第4个簇(簇号为3)。前面已知用户数据区从0x4200开始,那么,文件实际起始偏移是0x4200+3*512 = 0x4800
。
“.”和”..”
空目录下会有两个目录项,一个是“.”,即2E,一个是“..”,即2E2E,代表了当前目录和上级目录。“.”是当前目录的别名,首簇就应该指向自己,而“..”首簇就改指向上级目录文件的首簇。如果上级目录是根目录,根目录区并没有在用户数据区分配内容。如何填写其簇号呢?其实只要让系统明白这是根目录即可。既然用户簇从2开始(其实这正是从2开始的原因,将特殊簇号留给特殊条目),用0或1作为根目录代表都可以,从实际看,用0代表根目录。
总结文件查找算法
1、先查看根目录区,是否有匹配的目录,如果有,通过对应目录项的首簇段获取其目录文件的首簇号。
2、通过fat表获得该目录文件的全部内容,遍历该文件,一次偏移32字节继续查找目录项,匹配查询路径中对应的项。如果查到则类似1,2查找对应的目录文件及目录项,否则说明找不到,结束。
3、如果在倒数第一层目录文件中找到了被查文件的目录项,从中获取首簇号,即可通过fat表访问该文件整个相关簇。
删除一个文件
目录项第一个字节修改为E5代表删除。
回收已用的簇
在fat表中,从被删除文件的首簇开始,将每个fat项置为00,实际文件扇区内容不用修改,这应该就是所谓的标记删除了吧,所以我们可以找到e5打头的目录项尝试恢复文件嘿嘿~
如何创建一个文件
1、创建文件时,需要首先定位到文件所在的目录文件,然后查找目录项(以32字节为偏移递增),如果第一个字节为0或e5表示可用。
2、根据文件大小计算出需要的簇数目,然后从fat首部开始(第4个字节),查看值为0的项,如果是则可用。找到第一个为0的项,将其索引值(以0开始)写入步骤1找到的目录项的首簇段。接着搜寻为0的项,将其索引值写入前一步找到的项。这样形成一个链,如果是最后一簇,其对应的簇项的值为fff。
3、填写文件目录项的创建时间,属性,大小等字段。
调试
用好压2345打开文件系统,就可以直接看到里面的目录、文件,用于直观查看运行效果!但是细节的内容建议使用ultraEdit打开查看文件镜像二/十六进制码,确认格式是否正确。