第5课 - 主引导程序的扩展(下)
1. 在 FAT12 根目录中查找目标文件
在前面课程的学习中,我们知道主引导程序有一个 512 字节的限制,如何突破这种限制呢?我们想到的办法是:再写一个程序(LOADER)放到存储介质中,在主引导程序中将该程序加载到内存中,并将控制权转交给该程序(jump)。
那如何找到存储介质中的程序(LOADER)呢?答案就是需要借助一个文件系统(FAT12),将该程序放到软盘中(软盘的文件系统格式是 FAT12),根据文件系统数据组织的方式便能方便的找到这个程序。那如何具体的实现呢,请看本节。
1.1 根目录区的大小和位置
其中:BPB_RootEntCnt 表示根目录区的目录项的个数(0xE0,224)
RootEntry 表示根目录区每个目录项的大小(32 Bytes)
BPB_BytsPerSec 表示每扇区的字节数(512 Bytes)
224 * 32 = 7168Bytes 7168 / 512 = 14扇区
1.2 FAT12 文件系统中的根目录区
根目录区由目录项构成,每一个目录项代表根目录中的一个文件索引。
文件名 8 字节,扩展名 3 字节,未占用的部分用 “空格” 代替,ascii = 0x20
在 FAT12 中,1簇 = 1扇区。
目录项中的关键成员:
-
- DIR_Name
文件名(用于判断是否为目标文件)
-
- DIR_FstClus
文件数据起始存储位置(用于确定读取位置)
-
- DIR_FileSize
文件大小(用于确定读取的字节数)
1.3 实验:读取 FAT12 文件系统的根目录信息
— 步骤:
-
- 创建 RootEntry 结构体类型(根据前面目录项的那个表格创建)
-
- 使用文件流顺序读取每个项的内容
- 解析并打印相关的信息
【实验1:读取根目录区的目录项】 (实验的环境为 Qt)
1 #include <QtCore/QCoreApplication> 2 #include <QFile> 3 #include <QDataStream> 4 #include <QDebug> 5 #include <QVector> 6 #include <QByteArray> 7 #include <stdio.h> 8 9 #pragma pack(push) 10 #pragma pack(1) 11 12 struct Fat12Header 13 { 14 char BS_OEMName[8]; 15 ushort BPB_BytsPerSec; 16 uchar BPB_SecPerClus; 17 ushort BPB_RsvdSecCnt; 18 uchar BPB_NumFATs; 19 ushort BPB_RootEntCnt; 20 ushort BPB_TotSec16; 21 uchar BPB_Media; 22 ushort BPB_FATSz16; 23 ushort BPB_SecPerTrk; 24 ushort BPB_NumHeads; 25 uint BPB_HiddSec; 26 uint BPB_TotSec32; 27 uchar BS_DrvNum; 28 uchar BS_Reserved1; 29 uchar BS_BootSig; 30 uint BS_VolID; 31 char BS_VolLab[11]; 32 char BS_FileSysType[8]; 33 }; 34 35 struct RootEntry 36 { 37 char DIR_Name[11]; 38 uchar DIR_Attr; 39 uchar reserve[10]; 40 ushort DIR_WrtTime; 41 ushort DIR_WrtDate; 42 ushort DIR_FstClus; 43 uint DIR_FileSize; 44 }; 45 46 #pragma pack(pop) 47 48 void PrintHeader(Fat12Header& rf, QString p) 49 { 50 QFile file(p); 51 52 if( file.open(QIODevice::ReadOnly) ) 53 { 54 QDataStream in(&file); 55 56 file.seek(3); 57 58 in.readRawData(reinterpret_cast<char*>(&rf), sizeof(rf)); 59 60 rf.BS_OEMName[7] = 0; 61 rf.BS_VolLab[10] = 0; 62 rf.BS_FileSysType[7] = 0; 63 64 qDebug() << "BS_OEMName: " << rf.BS_OEMName; 65 qDebug() << "BPB_BytsPerSec: " << hex << rf.BPB_BytsPerSec; 66 qDebug() << "BPB_SecPerClus: " << hex << rf.BPB_SecPerClus; 67 qDebug() << "BPB_RsvdSecCnt: " << hex << rf.BPB_RsvdSecCnt; 68 qDebug() << "BPB_NumFATs: " << hex << rf.BPB_NumFATs; 69 qDebug() << "BPB_RootEntCnt: " << hex << rf.BPB_RootEntCnt; 70 qDebug() << "BPB_TotSec16: " << hex << rf.BPB_TotSec16; 71 qDebug() << "BPB_Media: " << hex << rf.BPB_Media; 72 qDebug() << "BPB_FATSz16: " << hex << rf.BPB_FATSz16; 73 qDebug() << "BPB_SecPerTrk: " << hex << rf.BPB_SecPerTrk; 74 qDebug() << "BPB_NumHeads: " << hex << rf.BPB_NumHeads; 75 qDebug() << "BPB_HiddSec: " << hex << rf.BPB_HiddSec; 76 qDebug() << "BPB_TotSec32: " << hex << rf.BPB_TotSec32; 77 qDebug() << "BS_DrvNum: " << hex << rf.BS_DrvNum; 78 qDebug() << "BS_Reserved1: " << hex << rf.BS_Reserved1; 79 qDebug() << "BS_BootSig: " << hex << rf.BS_BootSig; 80 qDebug() << "BS_VolID: " << hex << rf.BS_VolID; 81 qDebug() << "BS_VolLab: " << rf.BS_VolLab; 82 qDebug() << "BS_FileSysType: " << rf.BS_FileSysType; 83 84 file.seek(510); 85 86 uchar b510 = 0; 87 uchar b511 = 0; 88 89 in.readRawData(reinterpret_cast<char*>(&b510), sizeof(b510)); 90 in.readRawData(reinterpret_cast<char*>(&b511), sizeof(b511)); 91 92 qDebug() << "Byte 510: " << hex << b510; 93 qDebug() << "Byte 511: " << hex << b511; 94 } 95 96 file.close(); 97 } 98 99 RootEntry FindRootEntry(Fat12Header& rf, QString p, int i) 100 { 101 RootEntry ret = {{0}}; 102 103 QFile file(p); 104 105 if( file.open(QIODevice::ReadOnly) && (0 <= i) && (i < rf.BPB_RootEntCnt) ) 106 { 107 QDataStream in(&file); 108 109 file.seek(19 * rf.BPB_BytsPerSec + i * sizeof(RootEntry)); 110 111 in.readRawData(reinterpret_cast<char*>(&ret), sizeof(ret)); 112 } 113 114 file.close(); 115 116 return ret; 117 } 118 119 void PrintRootEntry(Fat12Header& rf, QString p) 120 { 121 for(int i=0; i<rf.BPB_RootEntCnt; i++) 122 { 123 RootEntry re = FindRootEntry(rf, p, i); 124 125 if( re.DIR_Name[0] != '