您现在的位置是:首页 >其他 >读取FAT12文件系统的BPB信息网站首页其他

读取FAT12文件系统的BPB信息

一勺秋水 2023-07-01 12:00:05
简介读取FAT12文件系统的BPB信息

写操作系统的lab时,需要读取fat12文件系统中BPB的信息,主要思路就是创个BPB的类,成员变量就是BytesPerSec, SecPerClus这些,然后通过fseekfread来读取,从而实现成员变量的赋值。

开始的代码大致如下:

// 这是份错误代码
unsigned short BytesPerSec;
unsigned char SecPerClus;
unsigned short ResvdSecCnt;
unsigned char NumFATs;
unsigned short RootEntCnt;
unsigned short TotSec16;
unsigned char Media;
unsigned short FATSz16;
unsigned short SecPerTrk;
unsigned short NumHeads;
unsigned int HiddSec;
unsigned int TotSec32;

unsigned int BytesPerClus;
unsigned int fatBase;
unsigned int rootBase;
unsigned int fileBase;

// 使struct的成员变量按1字节对齐
// 默认的是4字节
// #pragma pack(1)

class BPB {
    unsigned short BPB_BytesPerSec;
    unsigned char BPB_SecPerClus;
    unsigned short BPB_ResvdSecCnt;
    unsigned char BPB_NumFATs;
    unsigned short BPB_RootEntCnt;
    unsigned short BPB_TotSec16;
    unsigned char BPB_Media;
    unsigned short BPB_FATSz16;
    unsigned short BPB_SecPerTrk;
    unsigned short BPB_NumHeads;
    unsigned int BPB_HiddSec;
    unsigned int BPB_TotSec32;

public:
    BPB() = default;
    void init(FILE *img) {
        fseek(img, 11, SEEK_SET);
        // 使用this指针正好给类中的成员赋值
        fread(this, 1, 25, img);
        BytesPerSec = this->BPB_BytesPerSec;
        SecPerClus = this->BPB_SecPerClus;
        ResvdSecCnt = this->BPB_ResvdSecCnt;
        NumFATs = this->BPB_NumFATs;
        RootEntCnt = this->BPB_RootEntCnt;
        TotSec16 = this->BPB_TotSec16;
        Media = this->BPB_Media;
        FATSz16 = this->BPB_FATSz16;
        SecPerTrk = this->BPB_SecPerTrk;
        NumHeads = this->BPB_NumHeads;
        HiddSec = this->BPB_HiddSec;
        TotSec32 = BPB_TotSec16 == 0 ? BPB_TotSec32 : 0;    // if TotSec16!=0, TotSec32就是0,而不是TotSec32

        BytesPerClus = BytesPerSec * SecPerClus;
        fatBase = ResvdSecCnt * BytesPerSec;
        rootBase = fatBase + NumFATs * FATSz16 * BytesPerSec;
        fileBase = rootBase + (RootEntCnt * 32 + BytesPerSec - 1) / BytesPerSec * BytesPerSec;
    }
};

#pragma pack(1)

然而事情并不简单,首先是结构体的内存对齐方式,对于char、short,成员默认的对齐方式是4个字节,从而提高效率(C++中结构体和类除了默认访问权限不一样,其他基本一样)

class Line {
	char s;
	int a;
}

// sizeof(Line) = 8
// 因为char占用了4个字节

#pragma pack(1) 可以让数据以连续形式存储,从而使得fread将读到的字节对应放入成员变量中。

fread的精妙之处在于使用了this,直接将读取到的值赋给成员变量,从而省略了手动赋值的麻烦

unsigned char

因为SecPerClus只需要1字节,所以我们想当然地使用了unsigned char,毕竟char也只是1字节嘛。但是有个十分重要的问题,那就是我们使用unsigned char的目的是为了存取数值,而cout << char会显示ASCII码中对应的字符

因为我们定义的全局变量SecPerClus Media类型为unsigned char,如果直接输出SecPerClus的值就是空的,因为ASCII码为1时对应的是开始符SOH(Start of Heading)。

解决方法就是 定义的全局变量类型全改为int,一劳永逸

当初想着验证下读取的对不对,就写了个helper函数输出所有信息,谁知道会遇到这种问题,还是经验不够哇

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。