FAT12文件系统
基本结构
引导扇区BOOT
BS_jmpBOOT | 0 | 3 | 一个短跳转指令 | jmp short LABEL_STARTnop |
---|---|---|---|---|
BS_OEMName | 3 | 8 | 厂商名 | ‘ZGH’ |
BPB_BytesPerSec | 11 | 2 | 每扇区字节数(Bytes/Sector) | 0x200 |
BPB_SecPerClus | 13 | 1 | 每簇扇区数(Sector/Cluster) | 0x1 |
BPB_ResvdSecCnt | 14 | 2 | Boot记录占用多少扇区 | ox1 |
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 | 每磁道扇区数(Sector/track) | 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 |
BS_Reserved1 | 37 | 1 | 保留,未使用 | 0 |
BS_BootSig | 38 | 1 | 扩展引导标记(29h) | 0x29 |
BS_VolID | 39 | 4 | 卷序列号 | 0 |
BS_VolLab | 43 | 11 | 卷标 | ‘ZGH’ |
BS_FileSysType | 54 | 8 | 文件系统类型 | ‘FAT12’ |
引导代码及其他内容 | 62 | 448 | 引导代码及其他数据 | 引导代码(剩余空间用0填充) |
结束标志0xAA55 | 510 | 2 | 第510字节为0x55,第511字节为0xAA | 0xAA55 |
FAT-File Allocation Table
-
FAT1和FAT2互为备份。
-
每12位成为一个FAT项(FATEntry),代表一个簇。所以2个FAT项会占用3个字节
-
数据区起始于簇2,why?
-在1.44M软盘上,FAT前三个字节的值是固定的0xF0、0xFF、0xFF,用于表示这是一个应用在1.44M软盘上的FAT12文件系统。本来序号为0和1的FAT表项应该对应于簇0和簇1,但是由于这两个表项被设置成了固定值,簇0和簇1就没有存在的意义.
-
FAT项的值代表文件的下一个簇号
- 值为0xFF7,表示坏簇
- 值大于或等于0xFF8,表示当前簇是本文件的最后一个簇
根目录区
- 根目录区由目录项组成,一个目录项占32个字节。
g++与nasm混合编程
-
环境ubuntu64,安装g++,nasm
-
my_print.asm负责输出,1.cpp为主文件
-
1.cpp中首先声明函数
1
extern "C" void my_print(char *, int, int);
-
my_print.asm定义函数,注意用global my_print声明函数名。
-
命令
1
2
3$ nasm -f elf32 my_print.asm
$ g++ -o fat -m32 1.cpp my_print.o
$ ./fat
如何读取FAT12文件?
Steps
-
读取Boot,得到必要的参数:
- BPB_BytsPerSec 每扇区字节数
- BPB_SecPerClus 每簇扇区数
- BPB_RsvdSecCnt Boot记录占用的扇区数
- BPB_NumFATs FAT表个数
- BPB_RootEntCnt 根目录最大文件数
- BPB_TotSec16 FAT扇区数,如果该值为0,则BPB_FATSz32为FAT扇区数
- BPB_HiddSec
-
计算每簇的字节数,每个区域的初始位置的偏移量。
- 每簇字节数=每簇扇区数*每扇区字节数。
- fatBase=Boot记录占用的扇区数*每扇区字节数
- fileRootBase=(Boot记录占用的扇区数+FAT表个数* FAT扇区数)*每扇区字节数
- dataBase=(Boot记录占用的扇区数+FAT表个数* FAT扇区数+(根目录最大文件数* 32+每扇区字节数-1)/每扇区字节数)*每扇区字节数。
数据区的偏移量又乘又除是不是吃饱了撑的0.0?可不可以等于fileRootBase+根目录最大文件数*32?不阔以!
dataBase初始位置=FileRootBase最后一个簇的结束位置。如果根目录没有填满最后一个簇,数据区也是从下一个簇开始的,而不是默认于最后一个根目录的结束位置开始。
用 (根目录最大文件数 32+每扇区字节数-1)/每扇区字节数* 可以得到根目录区占用的真正扇区数。
-
广搜遍历Fat12文件系统
-
首先读取根目录区
-
判断文件属性
- 目录项的属性记录了该项是文件夹or文件
- 用DIR_Attr&0x10判断,结果为0是文件,否则为文件夹。
-
处理文件夹
-
得到下一层目录的数据区初始位置,首先需要FAT号。文件夹的目录项中DIR_FstClus记录了文件夹FAT号(num),其内容即为所需FAT号。
-
FAT项大小为12位。
-
从fatBase + num * 3 / 2读取2个字节(16位)。结合存储的小尾顺序和FAT项结构可以得到。num为偶去掉高4位,num为奇去掉低4位。
-
(num & 1) ? (bytes >> 4) : (bytes & ((1 << 12) - 1))为下一个FAT号
-
-
用FAT号计算在数据区中的位置。
- startByte = dataBase + (fat号- 2) ***** BytsPerClus;
-
-
处理普通文件
- 如果文件在数据区中需要多个簇,那么用上面的办法读取数据。
-
广搜遍历
- 分层遍历
- 每一层的文件夹放进队列。
- 队列为空则停止
-
用文件名查询特定文件
- 对于目录项中的文件名做处理,去除非法字符和空格。如果是普通文件,第一个空格为‘.’。
- 判断文件名是否相等,strcmp等等各种方法都可。
制作FAT镜像
-
创建新的软盘镜像
1
mkfs.fat -C a.img 1440
-
挂载点,可以创建一个新的目录
1
mkdir 文件名
-
将镜像挂载到挂载点
1
sudo mount a.img 文件名
-
挂载后,就可以通过操作./mount文件夹,来向a.img加入和查看文件。可使用系统自带的资源管理器类似 GUI工具,或者使用命令行操作。
我的呆码👉 click me
支持ls,cat 文件名,ls -l[lll] [文件名],ls [文件名] -l[lll] .
reference
https://blog.csdn.net/qq_39654127/article/details/88429461#main-toc