第5课

第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] != '' )
126         {
127             qDebug() << i << ":";
128             qDebug() << "DIR_Name: " << re.DIR_Name;  //这里打印字符串其实越界了,不过RootEntry的reserve会终止打印,reserve初始化为0
129             qDebug() << "DIR_Attr: " << re.DIR_Attr;
130             qDebug() << "DIR_WrtDate: " << re.DIR_WrtDate;
131             qDebug() << "DIR_WrtTime: " << re.DIR_WrtTime;
132             qDebug() << "DIR_FstClus: " << re.DIR_FstClus;
133             qDebug() << "DIR_FileSize: " << re.DIR_FileSize;
134         }
135     }
136 }
137 
138 
139 int main(int argc, char *argv[])
140 {
141     QCoreApplication a(argc, argv);
142     QString img = "F:\data.img";
143     Fat12Header f12;
144 
145     qDebug() << "Read Header:";
146 
147     PrintHeader(f12, img);
148 
149     qDebug() << endl;
150 
151     qDebug() << "Print Root Entry:";
152 
153     PrintRootEntry(f12, img);
154 
155     qDebug() << endl;
156 
157     return a.exec();
158 }
读取根目录区的目录项1

  

  打印结果中出现的 0 2 两项,是由于虚拟软盘 data.img 是在 FreeDos 中格式化,然后挂载到 linux 中写入文件导致的,是一些没有意义的垃圾数据

  改变打印格式,验证上面的说法:

  1 #include <QtCore/QCoreApplication>
  2 #include <QFile>
  3 #include <QDataStream>
  4 #include <QDebug>
  5 #include <QVector>
  6 #include <QByteArray>
  7 
  8 #pragma pack(push)
  9 #pragma pack(1)
 10 
 11 struct Fat12Header
 12 {
 13     char BS_OEMName[8];
 14     ushort BPB_BytsPerSec;
 15     uchar BPB_SecPerClus;
 16     ushort BPB_RsvdSecCnt;
 17     uchar BPB_NumFATs;
 18     ushort BPB_RootEntCnt;
 19     ushort BPB_TotSec16;
 20     uchar BPB_Media;
 21     ushort BPB_FATSz16;
 22     ushort BPB_SecPerTrk;
 23     ushort BPB_NumHeads;
 24     uint BPB_HiddSec;
 25     uint BPB_TotSec32;
 26     uchar BS_DrvNum;
 27     uchar BS_Reserved1;
 28     uchar BS_BootSig;
 29     uint BS_VolID;
 30     char BS_VolLab[11];
 31     char BS_FileSysType[8];
 32 };
 33 
 34 struct RootEntry
 35 {
 36     char DIR_Name[11];
 37     uchar DIR_Attr;
 38     uchar reserve[10];
 39     ushort DIR_WrtTime;
 40     ushort DIR_WrtDate;
 41     ushort DIR_FstClus;
 42     uint DIR_FileSize;
 43 };
 44 
 45 #pragma pack(pop)
 46 
 47 void PrintHeader(Fat12Header& rf, QString p)
 48 {
 49     QFile file(p);
 50 
 51     if( file.open(QIODevice::ReadOnly) )
 52     {
 53         QDataStream in(&file);
 54 
 55         file.seek(3);
 56 
 57         in.readRawData(reinterpret_cast<char*>(&rf), sizeof(rf));
 58 
 59         rf.BS_OEMName[7] = 0;
 60         rf.BS_VolLab[10] = 0;
 61         rf.BS_FileSysType[7] = 0;
 62 
 63         qDebug() << "BS_OEMName: " << rf.BS_OEMName;
 64         qDebug() << "BPB_BytsPerSec: " << hex << rf.BPB_BytsPerSec;
 65         qDebug() << "BPB_SecPerClus: " << hex << rf.BPB_SecPerClus;
 66         qDebug() << "BPB_RsvdSecCnt: " << hex << rf.BPB_RsvdSecCnt;
 67         qDebug() << "BPB_NumFATs: " << hex << rf.BPB_NumFATs;
 68         qDebug() << "BPB_RootEntCnt: " << hex << rf.BPB_RootEntCnt;
 69         qDebug() << "BPB_TotSec16: " << hex << rf.BPB_TotSec16;
 70         qDebug() << "BPB_Media: " << hex << rf.BPB_Media;
 71         qDebug() << "BPB_FATSz16: " << hex << rf.BPB_FATSz16;
 72         qDebug() << "BPB_SecPerTrk: " << hex << rf.BPB_SecPerTrk;
 73         qDebug() << "BPB_NumHeads: " << hex << rf.BPB_NumHeads;
 74         qDebug() << "BPB_HiddSec: " << hex << rf.BPB_HiddSec;
 75         qDebug() << "BPB_TotSec32: " << hex << rf.BPB_TotSec32;
 76         qDebug() << "BS_DrvNum: " << hex << rf.BS_DrvNum;
 77         qDebug() << "BS_Reserved1: " << hex << rf.BS_Reserved1;
 78         qDebug() << "BS_BootSig: " << hex << rf.BS_BootSig;
 79         qDebug() << "BS_VolID: " << hex << rf.BS_VolID;
 80         qDebug() << "BS_VolLab: " << rf.BS_VolLab;
 81         qDebug() << "BS_FileSysType: " << rf.BS_FileSysType;
 82 
 83         file.seek(510);
 84 
 85         uchar b510 = 0;
 86         uchar b511 = 0;
 87 
 88         in.readRawData(reinterpret_cast<char*>(&b510), sizeof(b510));
 89         in.readRawData(reinterpret_cast<char*>(&b511), sizeof(b511));
 90 
 91         qDebug() << "Byte 510: " << hex << b510;
 92         qDebug() << "Byte 511: " << hex << b511;
 93     }
 94 
 95     file.close();
 96 }
 97 
 98 RootEntry FindRootEntry(Fat12Header& rf, QString p, int i)
 99 {
100     RootEntry ret = {{0}};
101 
102     QFile file(p);
103 
104     if( file.open(QIODevice::ReadOnly) && (0 <= i) && (i < rf.BPB_RootEntCnt) )
105     {
106         QDataStream in(&file);
107 
108         file.seek(19 * rf.BPB_BytsPerSec + i * sizeof(RootEntry));
109 
110         in.readRawData(reinterpret_cast<char*>(&ret), sizeof(ret));
111     }
112 
113     file.close();
114 
115     return ret;
116 }
117 
118 void PrintRootEntry(Fat12Header& rf, QString p)
119 {
120     for(int i=0; i<rf.BPB_RootEntCnt; i++)
121     {
122         RootEntry re = FindRootEntry(rf, p, i);
123 
124         if( re.DIR_Name[0] != '' )
125         {
126             qDebug() << i << ":";
127             qDebug() << "DIR_Name: " << hex << re.DIR_Name;   //采用十六进制打印输出
128             qDebug() << "DIR_Attr: " << hex << re.DIR_Attr;
129             qDebug() << "DIR_WrtDate: " << hex << re.DIR_WrtDate;
130             qDebug() << "DIR_WrtTime: " << hex << re.DIR_WrtTime;
131             qDebug() << "DIR_FstClus: " << hex << re.DIR_FstClus;
132             qDebug() << "DIR_FileSize: " << hex << re.DIR_FileSize;
133         }
134     }
135 }
136 
137 int main(int argc, char *argv[])
138 {
139     QCoreApplication a(argc, argv);
140     QString img = "F:\data.img";
141     Fat12Header f12;
142 
143     qDebug() << "Read Header:";
144 
145     PrintHeader(f12, img);
146 
147     qDebug() << endl;
148 
149     qDebug() << "Print Root Entry:";
150 
151     PrintRootEntry(f12, img);
152 
153     qDebug() << endl;
154 
155     return a.exec();
156 }
读取根目录区的目录项2

  

 2.  介绍 FAT 表

2.1 FAT 表 - FAT12 的数据组织核心

  (1)FAT1 和 FAT2 是相互备份的关系,数据内容完全一致

  (2)FAT 表是一个关系图,记录了文件数据的先后关系

  (3)每一个 FAT 表项占用 12 比特

  (4)FAT 表的前 2 个表项规定不使用

2.2 FAT 表中的先后关系

  (1)以簇(扇区)为单位存储文件数据

  (2)每个表项( vec[i] )表示文件数据的实际位置(簇)

      • DIR_FstClus 表示文件第 0 簇(扇区)的位置
      • vec[DIR_FstClus] 表示文件第 1 簇(扇区)的位置
      • vec[vec[DIR_FstClus]] 表示文件第 2 簇(扇区)的位置
      • ......

2.3 FAT12 数据物理组织示意

  

2.4 FAT12 数据逻辑组织示意

  

2.5 实验:加载 FAT12 中的文件数据

  — 步骤:

    • 在根目录区查找目标文件对应的项
    • 获取目标文件的起始簇号和文件大小
    • 根据 FAT 表中记录的逻辑先后关系读取数据

3. 小贴士

3.1 小贴士一

  (1)FAT 表中的每个表项只占用 12 比特(1.5字节)

  (2)FAT 表一共记录了 BPB_BytsPerSec * 9 * 2 / 3 个表项

  (3)可以使用一个 short 表示一个表项的值

  (4)如果表象值大于等于 0xFF8 ,则说明已经到达最后一个簇

  (5)如果表项值等于 0xFF7 ,则说明当前簇已经损坏

3.2 小贴士二

  (1)数据区起始簇()号为33,地址为 0x4200

  (2)数据区起始地址所对应的编号为 2(不为 0)

  (3)因此,DIR_FstClus 对应的地址为:

    • 0x4200 + ( DIR_FstClus - 2 ) * 512

编程实验:读取指定文件内容】

4. 小结

  (1)FAT12 根目录区记录了文件的起始簇号长度

  (2)通过查找根目录区能够确定是否存在目标文件

  (3)FAT12 文件数据的组织使用了单链表的思想

      文件数据离散的分布于存储介质中

      文件数据通过 FAT 项进行关联 

注:本文整理于《狄泰12月提升计划》课程内容

狄泰QQ群:199546072

本人QQ号:502218614

原文地址:https://www.cnblogs.com/shiwenjie/p/9021779.html