主引导程序的扩展
主引导程序如何加载存储介质中的其他程序?
引入FAT12文件系统
实验:
创建虚拟软盘
将虚拟软盘插入到FreeDos里面,并且盘符是b盘
进入Freedos
对B盘进行格式化,B盘就是虚拟软驱 data.img就拥有了FAT12文件系统
将虚拟软盘挂载到Linux里面
卸载
此时查看虚拟软盘就能看到两个文件
查看一下
查看data.img FAT12文件系统等价于文件格式
-------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------
下一步:Boot 查找目标文件(Loader),并读取文件的内容
--实验:读取data.img中的文件系统信息
定义结构体
0x200对应的十进制是512
输出了16进制的55aa
主引导程序的代码量不能超过512字节
---------------------------------------------------------------------------------------------------------------------------------
一个扇区占512字节
实验-读取FAT12文件系统的根目录信息
//创建 RootEntry 结构体类型 struct RootEntry { char DIR_Name[11]; uchar DIR_Attr; uchar reserve[10]; ushort DIR_WrtTime; ushort DIR_WrtDate; ushort DIR_FstClus; uint DIR_FileSize; }
读取文件项
RootEntry FindRootEntry(Fat12Header& rf, QString p, int i) { RootEntry ret = {{0}}; QFile file(p); if( file.open(QIODevice::ReadOnly) && (0 <= i) && (i < rf.BPB_RootEntCnt) ) { QDataStream in(&file); file.seek(19 * rf.BPB_BytsPerSec + i * sizeof(RootEntry)); in.readRawData(reinterpret_cast<char*>(&ret), sizeof(ret)); } file.close(); return ret; }
打印根目录项
void PrintRootEntry(Fat12Header& rf, QString p) { for(int i=0; i<rf.BPB_RootEntCnt; i++) { RootEntry re = FindRootEntry(rf, p, i); if( re.DIR_Name[0] != '\0' )//检验读取得到的目录项是否合法 { qDebug() << i << ":"; qDebug() << "DIR_Name: " << hex << re.DIR_Name; qDebug() << "DIR_Attr: " << hex << re.DIR_Attr; qDebug() << "DIR_WrtDate: " << hex << re.DIR_WrtDate; qDebug() << "DIR_WrtTime: " << hex << re.DIR_WrtTime; qDebug() << "DIR_FstClus: " << hex << re.DIR_FstClus; qDebug() << "DIR_FileSize: " << hex << re.DIR_FileSize; } } }
2. 根据文件名查找根目录下对应的目录文件项
RootEntry FindRootEntry(Fat12Header& rf, QString p, QString fn) { RootEntry ret = {{0}}; for(int i=0; i<rf.BPB_RootEntCnt; i++) { RootEntry re = FindRootEntry(rf, p, i); if( re.DIR_Name[0] != '\0' ) { int d = fn.lastIndexOf("."); //做一个字符串分割的工作 QString name = QString(re.DIR_Name).trimmed(); //去掉前后的空格 if( d >= 0 ) { QString n = fn.mid(0, d); //开头 QString p = fn.mid(d + 1); //结尾 if( name.startsWith(n) && name.endsWith(p) ) { ret = re; break; } } else { if( fn == name ) { ret = re; break; } } } } return ret; }
--------------------------------——————————————————————————————————————————————————————————————————————————————————————————
有了文件数据的起始位置,有了数据的大小,读取数据就变得不困难
1簇=512字节(FAT12)
我们的文件往往都是大于512个字节,一根文件需要若干个簇才能存取所有内容
FAT表中的先后关系
- 以簇(扇区)为单位存储文件数据(这里一簇等于一扇区大小)
- 每个表项(vec[i])表示文件数据的实际位置(簇)
(1)DIR_FstClus表示文件第0簇(扇区)的位置
(2)vec[DIR_FstClus]表示文件第1簇(扇区)的位置
(3)vec[vec[DIR_FstClus]]表示文件第2簇(扇区)的位置..
借助于FAT表项
即使数据簇离散的存储在介质里,也不影响文件数据的连续性(由FAT表项来保证)
FAT12数据逻辑组织示意图
链表
实验:加载FAT12中的文件数据
不同于从0开始的,数据区的偏移地址是从2开始的
实验:读取指定文件内容
QVector<ushort> ReadFat(Fat12Header& rf, QString p) { QFile file(p); int size = rf.BPB_BytsPerSec * 9; //计算FAT表的大小 uchar* fat = new uchar[size]; //用于存储直接从文件系统里读出来的FAT表 QVector<ushort> ret(size * 2 / 3, 0xFFFF); if( file.open(QIODevice::ReadOnly) ) { QDataStream in(&file); file.seek(rf.BPB_BytsPerSec * 1); in.readRawData(reinterpret_cast<char*>(fat), size); for(int i=0, j=0; i<size; i+=3, j+=2) //三个字节表示两个FAT表项 { ret[j] = static_cast<ushort>((fat[i+1] & 0x0F) << 8) | fat[i]; ret[j+1] = static_cast<ushort>(fat[i+2] << 4) | ((fat[i+1] >> 4) & 0x0F); } } file.close(); delete[] fat; return ret; }
转自https://blog.csdn.net/qq_39654127/article/details/88429461?spm=1001.2014.3001.5501
QByteArray ReadFileContent(Fat12Header& rf, QString p, QString fn) { QByteArray ret; RootEntry re = FindRootEntry(rf, p, fn); if( re.DIR_Name[0] != '\0' ) { QVector<ushort> vec = ReadFat(rf, p); QFile file(p); if( file.open(QIODevice::ReadOnly) ) { char buf[512] = {0}; //一簇 QDataStream in(&file); int count = 0; ret.resize(re.DIR_FileSize);//设置返回值数组对象的大小-目标文件的大小 for(int i=0, j=re.DIR_FstClus; j<0xFF7; i+=512, j=vec[j]) //vec记录的是数据簇的地址 { file.seek(rf.BPB_BytsPerSec * (33 + j - 2)); in.readRawData(buf, sizeof(buf)); for(uint k=0; k<sizeof(buf); k++) { if( count < ret.size() ) //确保读到的是有效的文件 { ret[i+k] = buf[k]; count++; } } } } file.close(); } return ret; }
此时就读到了文件里的内容