【OS】05 – 主引导程序的扩展(下)
本文最后更新于 228 天前,其中的信息可能已经有所发展或是发生改变。
内容目录

点击查看:主引导程序的扩展(下) 知识详情

代码

#include <QtCore/QCoreApplication>
#include <QFile>
#include <QDataStream>
#include <QDebug>
#include <QVector>
#include <QByteArray>

#pragma pack(push)
#pragma pack(1)

struct Fat12Header
{
    char BS_OEMName[8];
    ushort BPB_BytsPerSec;
    uchar BPB_SecPerClus;
    ushort BPB_RsvdSecCnt;
    uchar BPB_NumFATs;
    ushort BPB_RootEntCnt;
    ushort BPB_TotSec16;
    uchar BPB_Media;
    ushort BPB_FATSz16;
    ushort BPB_SecPerTrk;
    ushort BPB_NumHeads;
    uint BPB_HiddSec;
    uint BPB_TotSec32;
    uchar BS_DrvNum;
    uchar BS_Reserved1;
    uchar BS_BootSig;
    uint BS_VolID;
    char BS_VolLab[11];
    char BS_FileSysType[8];
};

struct RootEntry
{
    char DIR_Name[11];
    uchar DIR_Attr;
    uchar reserve[10];
    ushort DIR_WrtTime;
    ushort DIR_WrtDate;
    ushort DIR_FstClus;
    uint DIR_FileSize;
};

#pragma pack(pop)

void PrintHeader(Fat12Header& rf, QString p)
{
    QFile file(p);

    if( file.open(QIODevice::ReadOnly) )
    {
        QDataStream in(&file);
        file.seek(3);
        in.readRawData(reinterpret_cast<char*>(&rf), sizeof(rf));

        rf.BS_OEMName[7] = 0;
        rf.BS_VolLab[10] = 0;
        rf.BS_FileSysType[7] = 0;

        qDebug() << "BS_OEMName: " << rf.BS_OEMName;
        qDebug() << "BPB_BytsPerSec: " << hex << rf.BPB_BytsPerSec;
        qDebug() << "BPB_SecPerClus: " << hex << rf.BPB_SecPerClus;
        qDebug() << "BPB_RsvdSecCnt: " << hex << rf.BPB_RsvdSecCnt;
        qDebug() << "BPB_NumFATs: " << hex << rf.BPB_NumFATs;
        qDebug() << "BPB_RootEntCnt: " << hex << rf.BPB_RootEntCnt;
        qDebug() << "BPB_TotSec16: " << hex << rf.BPB_TotSec16;
        qDebug() << "BPB_Media: " << hex << rf.BPB_Media;
        qDebug() << "BPB_FATSz16: " << hex << rf.BPB_FATSz16;
        qDebug() << "BPB_SecPerTrk: " << hex << rf.BPB_SecPerTrk;
        qDebug() << "BPB_NumHeads: " << hex << rf.BPB_NumHeads;
        qDebug() << "BPB_HiddSec: " << hex << rf.BPB_HiddSec;
        qDebug() << "BPB_TotSec32: " << hex << rf.BPB_TotSec32;
        qDebug() << "BS_DrvNum: " << hex << rf.BS_DrvNum;
        qDebug() << "BS_Reserved1: " << hex << rf.BS_Reserved1;
        qDebug() << "BS_BootSig: " << hex << rf.BS_BootSig;
        qDebug() << "BS_VolID: " << hex << rf.BS_VolID;
        qDebug() << "BS_VolLab: " << rf.BS_VolLab;
        qDebug() << "BS_FileSysType: " << rf.BS_FileSysType;

        file.seek(510);

        uchar b510 = 0;
        uchar b511 = 0;

        in.readRawData(reinterpret_cast<char*>(&b510), sizeof(b510));
        in.readRawData(reinterpret_cast<char*>(&b511), sizeof(b511));

        qDebug() << "Byte 510: " << hex << b510;
        qDebug() << "Byte 511: " << hex << b511;
    }

    file.close();
}

/**************************************** 查找根目录下所有/指定文件 *************************************************/
/*
函数功能:查找根目录中指定的文件项,并返回
*/
RootEntry FindRootEntry(Fat12Header& rf, QString p, int i)
{
    RootEntry ret = {{0}};

    QFile file(p);

    // 打开虚拟软盘并且i的范围要合法,rf.BPB_RootEntCnt表示根目录最多有多少个文件项
    if( file.open(QIODevice::ReadOnly) && (0 <= i) && (i < rf.BPB_RootEntCnt) )
    {
        QDataStream in(&file);

        // 定位读取的位置:根目录的起始地址在19扇区处+偏移
        file.seek(19 * rf.BPB_BytsPerSec + i * sizeof(RootEntry));

        in.readRawData(reinterpret_cast<char*>(&ret), sizeof(ret));
    }

    file.close();

    return ret;
}

/*
函数功能:调用FindRootEntry函数,打印虚拟软盘中所有根目录项
*/
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;
        }
    }
}

/*
函数功能:调用FindRootEntry函数,根据文件名查找对应的目录文件项
*/
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);

        // 首先:在fat12格式中,文件名称后缀之前是不带"."的名称,如:TEST TXT
        if( re.DIR_Name[0] != '\0' )
        {
            int d = fn.lastIndexOf(".");   //用户输入的文件名 TEST.TXT
            QString name = QString(re.DIR_Name).trimmed();

            if( d >= 0 )  // 情况1:TEST.TXT, 如果用户输入的是带"."的名称,处理过程
            {
                QString n = fn.mid(0, d);  // TEST
                QString p = fn.mid(d + 1); // TXT

                if( name.startsWith(n) && name.endsWith(p) )
                {
                    ret = re;
                    break;
                }
            }
            else  // 情况2:如果用户输入的是不带"."的名称,处理过程
            {
                if( fn == name )
                {
                    ret = re;
                    break;
                }
            }
        }
    }

    return ret;
}
/********************************************************************************************************/

**************************************** 读取目标文件内容 *************************************************/
/*
函数功能:读取fat12中fat表。返回值是一个数组,数组的每一项(short)代表一个fat表项(1.5bit)
*/
QVector<ushort> ReadFat(Fat12Header& rf, QString p)
{
    QFile file(p);
    int size = rf.BPB_BytsPerSec * 9;
    uchar* fat = new uchar[size];
    QVector<ushort> ret(size * 2 / 3, 0xFFFF); //参数2表示每个参数的默认值

    if( file.open(QIODevice::ReadOnly) )
    {
        QDataStream in(&file);

        file.seek(rf.BPB_BytsPerSec * 1);  //定位到fat表的起始位置,第一扇区处

        in.readRawData(reinterpret_cast<char*>(fat), size);

        for(int i=0, j=0; i<size; i+=3, j+=2) // fat表中3个字节表示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;
}

/*
函数功能:读取指定文件内容
参数3:指定的文件名
*/
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);//返回值对象的大小,也就是目标文件的大小

            // j表示数据簇的编号,i每次读取一个数据簇的大小512字节
            // j=vec[j] j的这种改变方式是因为fat表象中记录的是数据簇的地址,
            for(int i=0, j=re.DIR_FstClus; j<0xFF7; i+=512, j=vec[j])
            {
                file.seek(rf.BPB_BytsPerSec * (33 + j - 2)); // 根据偏移地址来算的绝对地址,定位到第"0"簇的位置。

                in.readRawData(buf, sizeof(buf)); //每次读取一簇的数据

                for(uint k=0; k<sizeof(buf); k++)
                {
                    if( count < ret.size() ) // 确保读取到的是有效的字节,每次拷贝1个字节,count++
                    {
                        ret[i+k] = buf[k];//把读到的数据拷贝到返回值数组对象当中
                        count++;
                    }
                }
            }
        }

        file.close();
    }

    return ret;
}
/********************************************************************************************************/

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    QString img = "/home/delphi/OS/data.img";
    Fat12Header f12;

#if 0
    qDebug() << "Read Header:";
    PrintHeader(f12, img);
    qDebug() << endl;
#endif
#if 0
    qDebug() << "Print Root Entry:";
    PrintRootEntry(f12, img);
    qDebug() << endl;
#endif
#if 0
    RootEntry re = FindRootEntry(f12, img, "LOADER.BIN");
    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;
    qDebug() << endl;
#endif
#if 0 
    qDebug() << "Print File Content:";
    QString content = QString(ReadFileContent(f12, img, "LOADER.BIN"));
    qDebug() << content;
#endif
    return a.exec();
}

结果验证分析

实验1

读取fat12中根目录区里的目录文件项

验证结果: 通过名字来查找文件;

里面原本写入了2个文件,为什么打印出来4个?

用16进制输出看一下,发现有两个起始从第0簇开始的,而且是全f,是不合法的目录文件;

file

实验2

根据名称查找是否有具体的某个项 FindRootEntry

file

实验3

代码解释:

把fat表的值放到返回值数组里

画出三个字节 i、i+1、 i+2, 那么这三个字节对应两个表象j、j+1

file

// 组成一个完整的值:取得i+1的低4位,作为j的高4位
ret[j] = static_cast<ushort>((fat[i+1] & 0x0F) << 8) | fat[i];     

// (fat[i+2] << 4) 空出低4位置
// ((fat[i+1] >> 4) & 0x0F)组成一个完整的值:取得i+1的高4位,作为j+1的低4位
ret[j+1] = static_cast<ushort>(fat[i+2] << 4) | ((fat[i+1] >> 4) & 0x0F);  

验证结果

file

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇