文章目录

  • 前言
  • 关于GPT的那点事
    • 硬盘分区和文件系统的关系
    • GPT规范和MBR规范的历史[^1]
    • GPT规范
  • 开始干活
    • GPT类
      • 定义GPT分区相关管理结构
      • 创建一个GPT的管理类
        • GPT类的声明
        • 创建一个虚拟硬盘
        • 读取一个虚拟硬盘
        • 创建一个新的分区
    • FAT32文件系统
      • FAT32的管理结构
      • FAT32类的声明
      • Set_Parition函数
      • SetActiveParition函数
      • Write_File函数
      • Read_File和KeRead_File函数
      • Delete_File和KeDelete_File函数
      • CreateACatlog函数
      • Search_Catlog、Search_Catlog_Addr、Search_ACatlogFile搜索函数
      • 余下的函数
    • 测试
  • 源码附录
    • CRC32.h
    • CRC32.cpp
    • FAT32.h
    • FAT32.cpp
    • GPT.h
    • GPT.cpp
    • type.h

前言

这次的实验要求写一个文件管理系统,使用FAT格式存储文件,但是我想既让要写,那就写好一点吧,所以这次实验,我的最终目的是创建一个虚拟硬盘,并编写一个符合UEFI规范中的GPT分区规范的“驱动程序”来格式化这个硬盘,并可以将其中的分区写为FAT32格式。简单来说,这次准备写一个DiskGenius这个软件的超级缩略的命令行版本。

功能主要有两个:

  1. 新建一个GPT格式的虚拟硬盘;
  2. 在虚拟硬盘上划分FAT32分区;

在理论上,结合Windows 的扇区读写API是可以真正在物理磁盘上跑起来的,但是手头没有不用的硬盘,就先用虚拟硬盘玩一下吧。

关于GPT的那点事

首先需要明确的一点是硬盘分区是将逻辑扇区号组织成更有结构,和硬盘的物理结构、物理地址没有什么关系。GPT是一种分区方案,那么我们要首先了解一下什么是硬盘分区。

硬盘分区和文件系统的关系

我们可以把一个硬盘比作一座城市,硬盘中的每个逻辑扇区就是城市中的那一小栋栋建筑物。当硬盘刚刚出厂的时候,整块硬盘都是空的(每栋建筑物里都没有住人),这时市长决定让这个城市显得更加有序一点,于是他决定先将相邻的建筑物的集合划分成一个个小区,我们把这个过程叫做分区,在这个过程中,我们不得不划分一部分建筑物来储存这部分规划地图。现在整个城市显得比较规整了,并且某个小区出问题也不会影响其他小区,但是仍然有一个问题没有解决,那就是如何编制小区里的建筑,市长决定将权力下放给小区里的人自己解决,而小区里的人自己决定的管理方式就叫做文件系统,在这个过程中,同样需要一部分建筑物来存储规划地图。

当外来的人需要寻找某栋建筑物时,只需要说到:“我要找的建筑物在某个小区(分区),他在这个小区里面的某个地址。”当然,这个二维地址的格式是事先约定好的。

简单来说,硬盘可以划分为多个分区,每个分区的文件系统可以不同,在这个过程中需要占用一部分扇区来储存管理信息。

GPT规范和MBR规范的历史1

在最初的时候,做电脑的人聚在一起,商量了好大家一起按照一种格式来进行分区,只要按照这种规范进行分区的硬盘,就能被主板上的软件识别,这种分区就叫做MBR分区方案,而这种主板上的软件就叫做BIOS

后来,随着电脑硬件越来越牛逼,电脑行业的人发现,卧槽,之前的规范好难用腌,不行,俺要自己制定一个规范!于是大家就又聚集在一起,商量了一种新的分区方法,并重写了主板上的软件。这种新的分区方法就叫做GPT,新的主板软件就叫做UEFI

GPT规范

关于GPT规范,网络上的文章已经讲的很清楚了,这里我就不费时间叨叨了,传送门

开始干活

本来是打算写一个Interface用户交互类的,但是写完GPT类和FAT32类之后,发现实在是写不动了,所以后面的测试样例全部是编译的。

GPT类

定义GPT分区相关管理结构

我们一步一步的来,首先创建将规范里面的相关参数定义下来。需要注意的是在创建结构体时我们需要加上对应的编译选项以时结构体不进行字节对齐#pragma pack(1)。其中的GPT相关结构为传送门中的对应表格


constexpr int SectorSize = 512;//一个扇区大小,默认为512B
constexpr int GptLength = 128;//当前规范下的GPT长度
constexpr unsigned int GpLength = sizeof(GlobalPartiton);//分区表项长度
constexpr unsigned int GptHeaderLength = sizeof(GPT_Header);//分区表头长度//*******************************分区表中的类型GUID,属性,签名************************************************////引导分区
constexpr char SystemParitionSignature[] = "SystemParition";//分区签名
constexpr qword SystemAttribute = 0x8000000000000001;//分区表中的系统属性
constexpr char  SystemParitionTypeGuid[] = { 0x28, 0x73, 0x2A, 0xC1, 0x1F, 0xF8, 0xD2, 0x11, 0xBA, 0x4B, 0x00, 0xA0, 0xC9, 0x3E, 0xC9, 0x3B };//分区GUID
//引导分区//数据分区
constexpr char DataParitionSignature[] = "DataParition";
constexpr qword DataAttribute = 0;
constexpr char  DataParitionTypeGuid[] =  {0xA2, 0xA0, 0xD0, 0xEB, 0xE5, 0xB9, 0x33, 0x44, 0x87, 0xC0, 0x68, 0xB6, 0xB7, 0x26, 0x99, 0xC7 };
//*******************************分区表中的类型GUID,属性,签名************************************************////数据分区
//分区表中的类型GUID,属性,签名//*******************************MBR分区的相关结构************************************************//
typedef struct MBR_DP //MBR方案下的分区表项
{byte GuideFlag;byte HeadBegin;word sector_cylinder_begin;byte PartitionFlag;byte HeadEnd;word sector_cylinder_end;dword StartSector;dword PartitionSectorNum;
}MBR_DP;
struct MBR
{byte Reversed[446];//在GPT分区中MBR引导代码无所谓MBR_DP DPT[4];short signature;MBR();
};//*******************************MBR分区的相关结构************************************************////*******************************GPT分区的相关结构************************************************////GPT表头
struct GPT_Header
{char signautre[8];dword Version;dword GPT_HeaderLength;//分区表头长度,该字段用于扩展用,在目前的规范下值为0x5cdword CRC32_GPTHeader;dword Reversed;qword Main_GPT_Header_LAB;qword Recover_GPT_Header_LAB;qword DataLAB_Begin;qword DataLAB_End;byte  Disk_GUID[16];qword GPTLAB_Begin;dword GPT_Length;dword GP_Length;dword CRC32_GPT;byte  Reversed2[420];GPT_Header();
};
//GPT表头//GPT方案下的分区表项
struct GlobalPartiton
{byte PartitonType_GUID[16];byte Partition_GUID[16];qword BeginLBA;qword EndLBA;qword Attribute;byte  PartitionName[72];
};//GPT方案下的分区表项
//*******************************GPT分区的相关结构************************************************//

其中使用的相关数据类型为:

typedef unsigned int dword;
typedef unsigned long long qword;
typedef unsigned short word;
typedef unsigned char byte;

创建一个GPT的管理类

可以看到,在GPT类中有一个叫做SetParition的虚函数,该虚函数的作用是将分区格式化为不同的文件系统。

GPT类的声明

//*******************************GPT的返回值的含义************************************************//constexpr unsigned int NOVirtualDisk = 1;
constexpr unsigned int NoEnoughCapacity = 2;
constexpr unsigned int OK = 0;
constexpr unsigned int OpenFileFiled = 3;
//*******************************GPT的返回值的含义************************************************//class GPT {
protected:byte* Disk;FILE* VirtualDisk;GPT_Header              MainGptHeader;GlobalPartiton        MainGpt[128];GPT_Header             BackupGptHeader;GlobalPartiton          BackupGpt[128];dword UnusedSectorNum;dword   FirestUnusedSector;dword    RealGptLength;void CreateAParition(dword ParitionCapacity, int SystemParition = 0);int virtual SetParition(dword FirstLAB,dword EndLAB, std::string ParitionName);
public:static dword Random();int CreateAVirtualDisk(dword DiskCapacity, std::string DiskFilePath);int ReadVirtualDisk(std::string VirtualDiskPath);int CreateAParition_FAT32(std::string ParitionName,dword ParitionCapacity,int SystemParition=0);~GPT();
};

其中的参数含义为:

参数名 含义
Disk 用于存放虚拟硬盘部分内容的缓冲区
VirtualDisk 打开的虚拟硬盘的文件指针
MainGptHeader 主分期表头
MainGpt[128] 主分区表
BackupGptHeader 备份分期表头
BackupGpt[128] 备份分区表
UnusedSectorNum 尚未使用的扇区数目,用于在创建新扇区时进行判断空闲空间是否足够
FirestUnusedSector 第一个未被使用的扇区
RealGptLength 实际上的分区表数目

创建一个虚拟硬盘

int GPT::CreateAVirtualDisk(dword DiskCapacity,std::string DiskFilePath) {this->Disk = new byte[SectorSize*34];//Windows下的GPT最多能有128个分区,每个分区128bytes,一共32个扇区,再加上1个保留的MBR扇区,1个GPT头扇区,一共34个扇区memset(Disk, 0, SectorSize * 34);//写入一个MBR保留分区,0扇区MBR* GPT_MBR=new MBR;GPT_MBR->signature = 0xaa55;GPT_MBR->DPT[0].PartitionFlag = 0xee;memcpy(Disk, GPT_MBR,sizeof(*GPT_MBR));delete GPT_MBR;//写入一个MBR保留分区,0扇区//写入一个GPT头GPT_Header* gpt_header = new GPT_Header;gpt_header->Recover_GPT_Header_LAB = (DiskCapacity << 1) - 1;gpt_header->DataLAB_End = (DiskCapacity << 1) - (1 + 32) - 1;//一个GPT表头,一个分区表for (int i = 0; i < 4; i++){*(dword*)(gpt_header->Disk_GUID + i * 4) = Random();}gpt_header->CRC32_GPT = CRC32::crc32(Disk + sizeof(MBR) + GptHeaderLength, GpLength * 128);gpt_header->CRC32_GPTHeader = CRC32::crc32((const unsigned char *)gpt_header, GptHeaderLength-420);//只校验有用的92字节memcpy(Disk + sizeof(MBR), gpt_header, GptHeaderLength);//写入一个GPT头FILE* DiskFile;fopen_s(&DiskFile,DiskFilePath.c_str(), "wb");if (!DiskFile)return OpenFileFiled;fwrite(Disk, SectorSize, 34, DiskFile);char tmp = 0x00;//填入空闲扇区for (qword i = 0; i < ((DiskCapacity << 1) - 34 - 1) *SectorSize; i++){fwrite(&tmp, 1, 1, DiskFile);}//填入空闲扇区//填写备份分区表gpt_header->Main_GPT_Header_LAB = (DiskCapacity << 1) - 1;gpt_header->Recover_GPT_Header_LAB = 1;memset(&gpt_header->CRC32_GPTHeader, 0, sizeof(gpt_header->CRC32_GPTHeader));gpt_header->CRC32_GPTHeader = CRC32::crc32((const unsigned char*)gpt_header, GptHeaderLength - 420);//只校验有用的92字节fwrite(gpt_header, GptHeaderLength, 1, DiskFile);fclose(DiskFile);delete gpt_header;delete Disk;return OK;
}

这段代码内容看上去很多,但是实际上只是做了MBR主引导扇区的填充,GPT头填充,GPT表填充这几件事。需要注意的有以下几点:

  • 在计算分区表校验和时,无论实际上的使用了几个分区表项,但是校验时需要校验全部,也就是128*128个字节;
  • 计算分区表头校验和时,只校验目前标准下的92个字节,且计算顺序为:将将当前字段置零->填写其余分区表头字段->计算CRC32并填入当前字段
  • 两个分区表互为主备,也就是说分区表头的Main_GPT_Header_LAB的LAB需要指向自身,而Recover_GPT_Header_LAB指向另外一个分区表头;

以下是利用这个函数做出来的虚拟硬盘。

剩下了的读取虚拟硬盘和创建分区的代码重复度就比较高了,就不再解析了。

读取一个虚拟硬盘

int GPT::ReadVirtualDisk(std::string VirtualDiskPath) {fopen_s(&this->VirtualDisk, VirtualDiskPath.c_str(), "rb+");if (!this->VirtualDisk){return NOVirtualDisk;}//读取虚拟硬盘的基本信息fseek(this->VirtualDisk, sizeof(MBR), SEEK_SET);fread(&MainGptHeader, GptHeaderLength, 1, VirtualDisk);fread(MainGpt, GpLength, 128, VirtualDisk);UnusedSectorNum = MainGptHeader.DataLAB_End - MainGptHeader.DataLAB_Begin;int i;for (i = 0; i < 128 && MainGpt[i].PartitonType_GUID[0]; i++){UnusedSectorNum -= MainGpt[i].EndLBA - MainGpt[i].BeginLBA;}RealGptLength = i;if (!i)  FirestUnusedSector = MainGptHeader.DataLAB_Begin;  //当GPT表中无分区时,第一个未使用的扇区为GPT头中规定的第一个数据扇区else{FirestUnusedSector = MainGpt[i - 1].EndLBA + 1;}fseek(VirtualDisk, 0 - (GptHeaderLength + GpLength * 128), SEEK_END);fread(&BackupGpt, GpLength, 128, VirtualDisk);fread(&BackupGptHeader, GptHeaderLength, 1, VirtualDisk);//读取虚拟硬盘的基本信息
}

创建一个新的分区

void GPT::CreateAParition(dword ParitionCapacity, int SystemParition) {//书写新的分区表GlobalPartiton& NewGp = MainGpt[RealGptLength];NewGp.BeginLBA = FirestUnusedSector;NewGp.EndLBA = FirestUnusedSector + (ParitionCapacity << 1) - 1;for (int i = 0; i < 4; i++){*(dword*)(NewGp.Partition_GUID+i*4) = Random();}if (SystemParition) {NewGp.Attribute = SystemAttribute;memcmp(&NewGp.PartitionName, SystemParitionSignature, sizeof(SystemParitionSignature));memcpy(&NewGp.PartitonType_GUID, SystemParitionTypeGuid, 16);}else {NewGp.Attribute = DataAttribute;memcmp(NewGp.PartitionName, DataParitionSignature, sizeof(DataParitionSignature));memcpy(NewGp.PartitonType_GUID, DataParitionTypeGuid, 16);}memcpy(&BackupGpt[RealGptLength] , &NewGp, GpLength);//书写新的分区表//更新GPT头//主GPTMainGptHeader.CRC32_GPT = CRC32::crc32((byte*)MainGpt, GpLength * GpLength);memset(&MainGptHeader.CRC32_GPTHeader, 0, sizeof(MainGptHeader.CRC32_GPTHeader));MainGptHeader.CRC32_GPTHeader = CRC32::crc32((byte*)&MainGptHeader, 92);//主GPT//备GPTBackupGptHeader.CRC32_GPT = CRC32::crc32((byte*)BackupGpt, GpLength * GpLength);memset(&BackupGptHeader.CRC32_GPTHeader, 0, sizeof(BackupGptHeader.CRC32_GPTHeader));BackupGptHeader.CRC32_GPTHeader = CRC32::crc32((byte*)&BackupGptHeader, 92);//备GPT//更新GPT头//写入虚拟硬盘文件fseek(VirtualDisk, sizeof(MBR), SEEK_SET);fwrite(&MainGptHeader, GptHeaderLength, 1, VirtualDisk);fwrite(MainGpt, GpLength, GptLength, VirtualDisk);fseek(VirtualDisk, 0 - (GptHeaderLength + GpLength * 128), SEEK_END);fwrite(BackupGpt, GpLength, GptLength, VirtualDisk);fwrite(&BackupGptHeader, GptHeaderLength, 1, VirtualDisk);//写入虚拟硬盘文件
}

FAT32文件系统

这个文件系统断断续续写了三个多星期…在最初开始写这部分代码的时候没有预料到这块的复杂度会这么高,所以也没有用名字空间之类的,写到一半才发现这部分的代码耦合度太高了,但是已经写到一半了…也不太想重构,就只能硬着头皮写下去。结果写着写着心态就有点崩,最后代码一团乱糟糟的,可扩展性也不是太好,API的部分设计也比较烂,BUG也比较多,另外还有一点就是耦合度较高,导致了很大一部分的地址搜索、字符串的重复操作…希望大佬们轻喷
以上一堆巴拉巴拉的缺点导致调试的心态有点小崩- -也算一个小教训吧,下次还是不偷懒早点重构比较好。这次写文档也算回顾一下这个小项目的开发思路吧。

FAT32的管理结构

需要注意的是,本FAT32仅仅支持短目录项,长目录项由于时间和精力就不准备写了,接下来是管理的数据结构

struct FAT32_DBR
{byte JmpCode[3];byte OEM[8];word BytesPreSectors;byte SectorsPreCluster;word ReversedSector;//在FAT表之前的扇区数量,备DBR、备份FSINFO和主FSINFO、主DBR都在这部分区域中byte FAT_Num;dword Reversed1;byte MediumType;word Reversed2;//******硬盘物理参数******//word SectorsPreTrack;word HeadNum;//******硬盘物理参数******//dword BeginLAB;//本分区的开始扇区dword TotalSector;dword SectorPre_FAT;word ExtendedFlag;word Version;dword RootClusterNum;word FileSystemInformationSector;word Recovery_DBR_LAB;byte Reversed3[12];byte DriveNum;//物理驱动器号byte Reversed4;byte ExtendedBootSignature;dword ParitionSerialNum;byte ParitionName[11];byte System_ID[8];//本来用于标识FAT32分区的名称,现在已经被根目录中的卷标目录项所代替byte Reversed5[420];word EndSignautre;FAT32_DBR();
};
struct FSINFO
{dword Signature;byte  Reversed[480];dword Signature2;dword UnusedClusterNum;dword FirstUnusedCluster;byte  Reversed2[14];word  EndSignature = 0xaa55;FSINFO();
};
struct ShortCatlog
{byte FileName[8];byte EntendName[3];byte Attribute;byte Reversed;byte Tenmillisecond;//文件创建时的10毫秒位word FileCreateTime;word FileCreateDate;word FileLastVisitDate;word HighClusterNum;word LastModifyTime;word LastModifyDate;word LowClusterNum;dword FileLength;//字节为单位ShortCatlog();bool operator==(const ShortCatlog& a1) {if (this->Attribute != a1.Attribute)return FAT32_ERROR;for (int i = 0; i < sizeof(ShortCatlog); i++){if (*(byte*)&a1 + i != *(byte*)this + i)return 0;}return 1;}
};
struct ClusterAddress {dword Cluster;dword ByteOffset;
};typedef std::vector<std::string> Vector_Str;
typedef std::vector<ShortCatlog> Vector_Catlog;
typedef std::vector<byte> Vector_Byte;

FAT32类的声明

class FAT32 :public GPT {
protected:FAT32_DBR Main_FAT32_DBR;FAT32_DBR Backup_FAT32_DBR;FSINFO      Main_FSINFO;FSINFO      Backup_FSINFO;ShortCatlog RootCatlog;//该结构仅存在于内存中,不位于FAT32结构体中,仅做管理需要Vector_Str CurrentPath;dword* FAT1;dword* FAT2;int Search_Catlog_Addr(std::string FilePath, ClusterAddress& CatlogAddr);int Search_Catlog(std::string FilePath, ShortCatlog& Found_Catlog);int KeRead_File(ShortCatlog Catlog, Vector_Byte& Buffer);//通过目录项读一个文件dword Malloc_Cluster(dword LastCluster);int Search_ACatlogFile(ShortCatlog Catlog, std::string File, ClusterAddress& Result);int StrSplit(std::string String, char pattern, Vector_Str& StrVector);dword   Cluster_To_PhysicalSector(dword Cluster);int DiskIO(const ClusterAddress& Addr, void* Buffer, dword len, int type);int Modify_Catlog(std::string CatlogPath, ShortCatlog& NewCatlog);int Set_CatlogTime(ShortCatlog& Catlog,int tpye);int DeleteFiles_Catlog(ClusterAddress& CatlogAddr, ClusterAddress& FatherCatlogAddr);int Delete_File(ClusterAddress& CatlogAddr, ClusterAddress& FatherCatlogAddr);int FileAddr_To_LogicCluster(ShortCatlog& FileCatlog, dword ByteOffset,ClusterAddress& Result);int Preserve_ManageInfromation();int GetFatherPath(std::string& Path, std::string& FatherPath);int KeWrite_File(ClusterAddress& FatherCatlogAddr, Vector_Byte& File, char Type);int SynCatlog(ShortCatlog& FileCatlog);int RealtivePath_AbsolutePath(Vector_Str& CurrentPath, Vector_Str& AbsolutePath, std::string TransferPath);
public:int Delete_File(std::string FilePath);int virtual SetParition(dword FirstLAB, dword EndLAB, std::string ParitionName);int Write_File(std::string FilePath, Vector_Byte& File,char Type,byte FileType = FILE_RW);int Read_File(std::string FilePath, Vector_Byte& Buffer);int SetActiveParition(std::string ParitionName);int CreateACatlog(std::string Catlog);int EnterANewCatlog(std::string Path);~FAT32();
};

这里面需要注意数据成员RootCatlog,该成员并不位于FAT32分区中,而是一个初始化FAT32分区时一起初始化的目录项。这样做的原因是在FAT32中无论是目录文件还是普通文件,都有一个FCB(File Control Block)来描述,但是其中有一个特别的目录文件,也就是根目录文件是没有FCB块来控制的,为了简化代码逻辑,我设置了一个针对根目录文件的虚拟FCB块来进行控制。
可以看到,实现一个FAT32的软件还是很困难的,函数的作用及具体解释让我们慢慢道来。

Set_Parition函数

该函数的作用是将一个新设定好的分区格式化为FAT32文件系统,该函数由GPT类中的SetActiveParition调用。
需要注意的地方是在设置根目录文件时为了更加的符合规范,我们添加了一个点目录项。

int FAT32::SetParition(dword FirstLAB, dword EndLAB, std::string ParitionName) {if (!VirtualDisk)return NOVirtualDisk;if (ParitionName.length() >= 12)//短文件目录项最多支持12个字符return FAT32_NAME_TOOLONG;//********************设置DBR分区****************************//传入的参数是左右都是闭合区间,能用的扇区为[FirstLAB,EndLAB],为了方便//转化为左闭右开,即[FirestLAB,EndLAB)EndLAB++;if (EndLAB - FirstLAB < 0x20)//保留扇区为0x10,最少两个扇区的FAT表,还有至少一个根目录的簇(8个扇区)return NoEnoughCapacity;//文件系统能用的分区一定是8的整数倍EndLAB -= (EndLAB - FirstLAB) % 8;dword   TotalClusterNum;TotalClusterNum = (EndLAB - FirstLAB) / 8;Main_FAT32_DBR.BeginLAB = FirstLAB;Main_FAT32_DBR.TotalSector = EndLAB - FirstLAB;//每个扇区能放128个簇号,最少一个扇区Main_FAT32_DBR.SectorPre_FAT = (TotalClusterNum + 2) / (SectorSize / 4);if ((TotalClusterNum + 2) % 128) {Main_FAT32_DBR.SectorPre_FAT++;}Main_FAT32_DBR.FileSystemInformationSector = 1;//该值为起始分区的偏移量Main_FAT32_DBR.Recovery_DBR_LAB = 2;memcpy(&Backup_FSINFO, &Main_FSINFO, 512);Backup_FAT32_DBR.Recovery_DBR_LAB = 0;//********************设置DBR分区****************************//*********************************设置FAT表***************************************FAT1 = (dword*)new byte[Main_FAT32_DBR.SectorPre_FAT * SectorSize];FAT2 = (dword*)new byte[Main_FAT32_DBR.SectorPre_FAT * SectorSize];memset(FAT1, 0, SectorSize * Main_FAT32_DBR.SectorPre_FAT);FAT1[0] = 0x0ffffff8;FAT1[1] = 0xffffffff;FAT1[2] = END_CLUSTER_FLAG;//刚刚创建分区时只有一个卷名项memcpy(FAT2, FAT1, Main_FAT32_DBR.SectorPre_FAT * SectorSize);//*********************************设置FAT表***************************************//***************************设置FSINFO分区相关************************************Main_FSINFO.UnusedClusterNum = TotalClusterNum - 1;Main_FSINFO.FirstUnusedCluster = 3;memcpy(&Backup_FAT32_DBR, &Main_FAT32_DBR, 512);//***************************设置FSINFO分区相关************************************//*************************设置初始的目录项****************************************//卷标目录项ShortCatlog ParitionCatlog;memcpy(ParitionCatlog.FileName, ParitionName.c_str(), ParitionName.length());ParitionCatlog.Attribute = DISK_LABEL;//每个目录文件都有的'.'目录项ShortCatlog RootCatlogCatlog;RootCatlogCatlog.Attribute = CATLOG;RootCatlogCatlog.FileName[0] = '.';RootCatlogCatlog.HighClusterNum = Main_FAT32_DBR.RootClusterNum >> 16;RootCatlogCatlog.LowClusterNum = Main_FAT32_DBR.RootClusterNum;RootCatlogCatlog.FileLength = 2 * sizeof(ShortCatlog);//*************************设置初始的目录项****************************************//该根目录项只是管理使用,实际上并不存在于虚拟硬盘中RootCatlog.FileLength = sizeof(ShortCatlog);RootCatlog.LowClusterNum = Main_FAT32_DBR.RootClusterNum;//该根目录项只是管理使用,实际上并不存在于虚拟硬盘中//*********************************将新的东西写入**********************************fseek(VirtualDisk, SectorSize * FirstLAB, SEEK_SET);fwrite(&Main_FAT32_DBR, SectorSize, 1, VirtualDisk);fwrite(&Main_FSINFO, SectorSize, 1, VirtualDisk);fwrite(&Backup_FAT32_DBR, SectorSize, 1, VirtualDisk);fwrite(&Backup_FSINFO, SectorSize, 1, VirtualDisk);fseek(VirtualDisk, SectorSize * (FirstLAB + Main_FAT32_DBR.ReversedSector), SEEK_SET);fwrite(FAT1, 1, SectorSize * Main_FAT32_DBR.SectorPre_FAT, VirtualDisk);fwrite(FAT2, 1, SectorSize * Main_FAT32_DBR.SectorPre_FAT, VirtualDisk);fwrite(&ParitionCatlog, sizeof(ShortCatlog), 1, VirtualDisk);fwrite(&RootCatlogCatlog, sizeof(ShortCatlog), 1, VirtualDisk);//*********************************将新的东西写入**********************************}

SetActiveParition函数

需要注意的是,在FAT32文件中,根目录文件和普通目录文件有着几点不同,主要区别如下:

  1. 普通目录文件硬盘中具有FCB块,这也意味着可以通过FCB块中的文件长度结构来获取目录文件的长度;而根目录文件并没有一个FCB块来表明长度,所以FAT32管理软件在读根目录文件的时候,遇到第一个不合法的目录项就认为根目录文件结束。
  2. 普通目录文件总是约定有点目录项和点点目录项,但是根目录文件中并没有这两个目录项,至少在Windows的UEFI启动分区中是这样的。
    该函数用于选择硬盘中的不同分区,这次的项目是FAT32文件系统的管理项目,所以本函数仅仅支持FAT32文件系统。该函数的主要实现逻辑如下:
  3. 判断虚拟硬盘是否已经载入,如果没有载入的话,返回错误。
  4. 遍历GPT分区表,该GPT分区表由GPT类中的ReadVIrtualDisk函数进行初始化,找到对应名称的FAT32分区;
  5. 判断之前是否已经指定分区,通过调用Preserve_ManageInfromation将之前分区的管理结构写入硬盘中;
  6. 初始化RootCatlogFCB,该FCB是虚拟的,用于管理根目录;
  7. CurrentPath设置为:
int FAT32::SetActiveParition(std::string ParitionName) {FAT32_DBR Tmp;ShortCatlog PatitionCatlog;if (!VirtualDisk)return FAT32_ERROR;//***********************寻找指定分区************************************for (int i = 0; i < 128; i++){fseek(VirtualDisk, MainGpt[i].BeginLBA*SectorSize, SEEK_SET);fread(&Tmp, sizeof(FAT32_DBR), 1, VirtualDisk);if (!Tmp.BeginLAB)//分区表已经读完,但还没有找到对应的分区名字return FAT32_ERROR;if (strcmp((char*)&Tmp.System_ID, "FAT32"))continue;fseek(VirtualDisk, (Tmp.BeginLAB + Tmp.ReversedSector + Tmp.SectorPre_FAT * 2) * SectorSize, SEEK_SET);int CatlogPreClu = Tmp.SectorsPreCluster * SectorSize / sizeof(ShortCatlog);do{fread(&PatitionCatlog, sizeof(ShortCatlog), 1, VirtualDisk);if (!PatitionCatlog.Attribute)//根目录文件已经读完,但还没有找到卷目录项,默认卷标目录项在第一个簇中return FAT32_ERROR;} while (PatitionCatlog.Attribute != DISK_LABEL );if (!strcmp((char*)&PatitionCatlog.FileName, ParitionName.c_str()))break;}//***********************寻找指定分区************************************//***********************保存原来的扇区内容************************************if (Main_FAT32_DBR.BeginLAB) {Preserve_ManageInfromation();delete FAT1;}//***********************读取扇区内容****************************************************************************************fseek(VirtualDisk, Tmp.BeginLAB*SectorSize, SEEK_SET);//读DBRfread(&Main_FAT32_DBR, sizeof(FAT32_DBR), 1, VirtualDisk);fseek(VirtualDisk, (Main_FAT32_DBR.FileSystemInformationSector + Main_FAT32_DBR.BeginLAB) * SectorSize, SEEK_SET);//读FSINFOfread(&Main_FSINFO, sizeof(FSINFO), 1, VirtualDisk);fseek(VirtualDisk, (Main_FAT32_DBR.BeginLAB + Main_FAT32_DBR.ReversedSector) * SectorSize, SEEK_SET);//读FAT表FAT1 = (dword*)new byte[Main_FAT32_DBR.SectorPre_FAT * SectorSize];fread(FAT1, 1, Main_FAT32_DBR.SectorPre_FAT * SectorSize, VirtualDisk);//****************************读取根目录文件下的目录项个数来设置根目录项的文件长度************************************memset(&RootCatlog, 0, sizeof(ShortCatlog));RootCatlog.Attribute = CATLOG;RootCatlog.LowClusterNum = Main_FAT32_DBR.RootClusterNum;RootCatlog.HighClusterNum = Main_FAT32_DBR.RootClusterNum >> 16;int j = 0;dword NextClu = Main_FAT32_DBR.RootClusterNum;fseek(VirtualDisk, (Main_FAT32_DBR.BeginLAB + Main_FAT32_DBR.ReversedSector + Main_FAT32_DBR.SectorPre_FAT * 2) * SectorSize, SEEK_SET);int CatlogPreClu = Main_FAT32_DBR.SectorsPreCluster * SectorSize / sizeof(ShortCatlog);do{fread(&PatitionCatlog, sizeof(ShortCatlog), 1, VirtualDisk);//此时PatitionCatlog已经没有用了,用于做暂时变量RootCatlog.FileLength += sizeof(ShortCatlog);j++;if (j == CatlogPreClu - 1) {j = 0;fseek(VirtualDisk, this->Cluster_To_PhysicalSector(NextClu) * SectorSize, SEEK_SET);NextClu = FAT1[NextClu];if (NextClu == END_CLUSTER_FLAG)break;}} while (PatitionCatlog.FileName[0]);RootCatlog.FileLength -= sizeof(ShortCatlog);CurrentPath.push_back(":");//****************************读取根目录文件下的目录项个数来设置根目录项的文件长度************************************//***********************读取扇区内容****************************************************************************************}

Write_File函数

该函数的函数原型为:

int Write_File(std::string FilePath, Vector_Byte& File,char Type,byte FileType = FILE_RW);

其中参数Type的两个有效取值为'a'wFileType有两个有效取值,分别对应着目录文件和普通文件,需要注意的是FileType仅仅在Type的参数为w时才有效。
可能有人会注意到,这个函数和C语言标准库里面的fopen函数有些类似,但是这个函数相比较之下是残缺的函数,残缺的地方在于在这个FAT32类中文件指针总是默认指向文件尾部的,无法指向其他位置。这样的原因之一是完整的文件I/O应该是位于FAT32文件类之上的。
接下来是这个函数的源码

int FAT32::Write_File (std::string FilePath, Vector_Byte& File, char Type,byte FileType) {ShortCatlog FileCatlog;int flag = Search_Catlog(FilePath, FileCatlog);Vector_Str StrTmp;std::string PartentPath;Vector_Byte CatlogBuffer;ClusterAddress Tmp;if (Main_FSINFO.UnusedClusterNum * 8 * 512 < File.size())return FAT32_ERROR;if (Type == 'a') {//************************************判断文件是否存在***************************************//if (flag)return FAT32_ERROR;//************************************判断文件是否存在***************************************////************************************追加写内容***************************************//dword ByteOffset = FileCatlog.FileLength % (Main_FAT32_DBR.SectorsPreCluster * SectorSize);dword LastCluster = 0;for (LastCluster = (FileCatlog.HighClusterNum << 16) + FileCatlog.LowClusterNum; FAT1[LastCluster] != END_CLUSTER_FLAG; LastCluster = FAT1[LastCluster]);if (File.size() <= Main_FAT32_DBR.SectorsPreCluster * SectorSize - ByteOffset) {//判断当前簇是否能够放下新增的内容Tmp.Cluster = LastCluster;Tmp.ByteOffset = ByteOffset;DiskIO(Tmp, File.data(), File.size(), IO_OUT);}else{dword UnWriteFileLength = File.size();//先将第一个簇写满Tmp.Cluster = LastCluster;Tmp.ByteOffset = ByteOffset;DiskIO(Tmp, File.data(), File.size(), IO_OUT);UnWriteFileLength -= Main_FAT32_DBR.SectorsPreCluster * SectorSize - ByteOffset;//先将第一个簇写满while (UnWriteFileLength)//每次写一个簇{LastCluster = Malloc_Cluster(LastCluster);//将文件的一部分写入dword WriteLength_ThisLoop;if (UnWriteFileLength <= Main_FAT32_DBR.SectorsPreCluster * SectorSize) {WriteLength_ThisLoop = UnWriteFileLength;}else {WriteLength_ThisLoop = Main_FAT32_DBR.SectorsPreCluster * SectorSize;}Tmp.Cluster = LastCluster;Tmp.ByteOffset = 0;DiskIO(Tmp, File.data(), File.size(), IO_OUT);//将文件的一部分写入UnWriteFileLength -= WriteLength_ThisLoop;}}//************************************追加写内容***************************************////************************************修改文件FCB,FCB块***************************************//Set_CatlogTime(FileCatlog, MODIFY);FileCatlog.FileLength += File.size();Modify_Catlog(FilePath, FileCatlog);if (FileCatlog.Attribute == CATLOG)this->SynCatlog(FileCatlog);//************************************修改文件FCB,FCB块***************************************//return 0;}else if (Type == 'w') {if (!flag) {Delete_File(FilePath);}//************************************如果文件已存在,则首先删除***************************************//ShortCatlog Useless;if (!this->Search_Catlog(FilePath, Useless))return FAT32_ERROR;//************************************如果文件已存在,则首先删除***************************************////************************************计算需要几个簇***************************************//dword ClusterNum = File.size() / (Main_FAT32_DBR.SectorsPreCluster * SectorSize);if (File.size() % (Main_FAT32_DBR.SectorsPreCluster * SectorSize))ClusterNum++;//************************************计算需要几个簇***************************************////************************************新建一个新的目录项,分配文件空间并将这个目录项追加到父目录文件中***************************************////*******设置新的目录项属性*******//CatlogBuffer.resize(sizeof(ShortCatlog));ShortCatlog* NewShortCatlog = (ShortCatlog*)CatlogBuffer.data();NewShortCatlog->Attribute = FileType;StrSplit(FilePath, '/', StrTmp);memcpy(NewShortCatlog, StrTmp.back().c_str(), StrTmp.back().length());Set_CatlogTime(*NewShortCatlog, CREATE);//创建一个文件时创建时间、访问时间和修改时间相同Set_CatlogTime(*NewShortCatlog, VISIT);Set_CatlogTime(*NewShortCatlog, MODIFY);dword FirstCluster = Malloc_Cluster(-1);NewShortCatlog->HighClusterNum = FirstCluster >> 16;NewShortCatlog->LowClusterNum = FirstCluster;NewShortCatlog->FileLength = File.size();//*******设置新的目录项属性*******////*******在上一级目录文件中注册这个FCB*******//this->GetFatherPath(FilePath, PartentPath);Write_File(PartentPath, CatlogBuffer, 'a');//*******在上一级目录文件中注册这个FCB*******////*******分配剩下的文件空间*******//for (int i = 1; i < ClusterNum; i++){FirstCluster = Malloc_Cluster(FirstCluster);}//*******分配剩下的文件空间*******////************************************新建一个新的目录项并将这个目录项追加到父目录文件中***************************************////************************************向分配好的文件空间中写入文件内容***************************************//FirstCluster = (NewShortCatlog->HighClusterNum << 16) + NewShortCatlog->LowClusterNum;for (int i = 0; true; i++){ClusterAddress Tmp;Tmp.Cluster = FirstCluster;Tmp.ByteOffset = 0;if (FAT1[FirstCluster] != END_CLUSTER_FLAG) {DiskIO(Tmp, File.data() + i * Main_FAT32_DBR.SectorsPreCluster * SectorSize, Main_FAT32_DBR.SectorsPreCluster * SectorSize, IO_OUT);}else {DiskIO(Tmp, File.data() + i * Main_FAT32_DBR.SectorsPreCluster * SectorSize, NewShortCatlog->FileLength % (Main_FAT32_DBR.SectorsPreCluster * SectorSize), IO_OUT); break;}FirstCluster = FAT1[FirstCluster];}//************************************向分配好的文件空间中写入文件内容***************************************//return 0;}return FAT32_ERROR;
}

其中用到的函数作用如下:

函数 作用
Search_Catlog 通过文件路径来寻找对应的FCB块内容
Set_CatlogTime 设置FCB块的时间属性
Modify_Catlog 更换FCB块
SynCatlog 同步一个目录文件的FCB块和.FCB块
DiskIO 虚拟磁盘读写函数
StrSplit 字符串分离函数
GetFatherPath 在相对路径下获取父路径
Malloc_Cluster 请求分配一个簇

这个函数是整个FAT32类中最为复杂的一个函数,那么我们来看一下是怎么实现的。
在进入这个函数的时候我们首先要查看一下硬盘空间是否足够,接下来,这个函数分成了两大块,分别对应着追加写和覆盖写的两个实现代码。
那么我们首先看一下创建一个新的文件需要做什么工作,我们需要做如下工作:

  1. 查询这个文件是否已经存在,如果存在的话,需要删除这个文件;
  2. 计算需要这个新的文件需要几个簇;
  3. 新建一个新的目录项,并设置文件名、文件属性、其实簇号等属性,需要注意的是,在FCB中,我们需要第一个簇号这个属性,所以我们必须先分配一个簇;
  4. 向上级目录文件注册这个FCB(通过向上级目录文件追加写);
  5. 分配剩下的空间;
  6. 向分配好的文件空间中写入文件内容;

通过以上步骤,一个新的文件就创建好了,可以看到,在整个创建过程中,该函数递归调用了自身一次,目的是在上级文件目录中添加新的FCB。
接下来是实现追加写需要的工作:

  1. 判断该文件是否存在;
  2. 找到文件的最后一个簇;
  3. 判断新增加的内容是否跨簇(即最后一个簇是否能容纳下)
  4. 如果能容纳下,则跳过5
  5. 否则,先写一个簇,并维护一个UnWriteFileLength变量循环写内容,在这个过程中除最后一次写外,每次申请一个簇,并一次性写一个簇;
  6. 修改本文件的FCB块,如果本文件的属性是目录文件的话,还需要将目录文件中的.FCB块和FCB块进行同步;

另外需要指出一点时,在实际进行观察的过程中,似乎.FCB和..FCB的结构中只有起始簇号是正确的,而文件大小和其他属性则不一定正确,在这里进行修改仅仅为了符合规范。逻辑严谨的人可能还会注意到一点,这里的..目录项是不同步的,原因是在于同步.目录项仅需要进行一次同步操作,而如果同步..目录项的话,当前目录下有几个目录文件,就需要将这几个目录文件的..目录项进行修改。

Read_File和KeRead_File函数

该函数负责文件的读操作,实现代码如下:

int FAT32::Read_File(std::string FilePath, Vector_Byte& Buffer) {ShortCatlog FileCatlog;if (Search_Catlog(FilePath, FileCatlog))return FAT32_ERROR;KeRead_File(FileCatlog, Buffer);return 0;
}

可以看到,这部分仅仅做了文件名转换操作,得到了FileCatlog对象后就将工作交给了KeRead_File函数来处理,本来Write_File也应该是这样设计的,但是这部分的设计是在发现Write函数过于复杂之后才进行了分层设计…而此时Write函数已经完成了,所以也就不动他了吧- -
接下来是KeRead_File函数的实现代码。

int FAT32::KeRead_File(ShortCatlog Catlog, Vector_Byte& Buffer) {Buffer.resize(Catlog.FileLength);ClusterAddress tmp;tmp.Cluster = (Catlog.HighClusterNum << 16) + Catlog.LowClusterNum;tmp.ByteOffset = 0;dword i = 0;qword CluSize = Main_FAT32_DBR.SectorsPreCluster * (qword)SectorSize;for (i = 0; i < Catlog.FileLength / CluSize; i++){if (DiskIO(tmp, Buffer.data() + i * CluSize, CluSize, IO_IN))return FAT32_ERROR;tmp.Cluster = FAT1[tmp.Cluster];}if (DiskIO(tmp, Buffer.data() + i * CluSize, Catlog.FileLength % CluSize, IO_IN))return FAT32_ERROR;return 0;
}

这部分的代码也很简单,通过读FCB块来读文件就可以了。

Delete_File和KeDelete_File函数

函数的源码如下:

int FAT32::Delete_File(std::string FilePath) {ClusterAddress CatlogAddr, FatherCatlogAddr;std::string FatherPath;this->GetFatherPath(FilePath, FatherPath);if (Search_Catlog_Addr(FatherPath, FatherCatlogAddr))return FAT32_ERROR;if (Search_Catlog_Addr(FilePath, CatlogAddr))return FAT32_ERROR;if (KeDelete_File(CatlogAddr, FatherCatlogAddr))return FAT32_ERROR;return 0;
}

Read_File类似,做了简单的校验工作后就转交给了内层函数来进行处理。KeDelete_File的实现代码如下:

int FAT32::KeDelete_File(ClusterAddress& CatlogAddr, ClusterAddress& FatherCatlogAddr) {ShortCatlog Catlog;DiskIO(CatlogAddr, (byte*)&Catlog, sizeof(ShortCatlog), IO_IN);Vector_Byte CatlogFile;//******************如果是目录文件(对应文件夹)的话,递归删除文件夹下的所有文件********************//if (Catlog.Attribute == CATLOG) {KeRead_File(Catlog, CatlogFile);ShortCatlog* SubCatlog = NULL;for (int i = 0; i < Catlog.FileLength; i += sizeof(ShortCatlog)){SubCatlog = (ShortCatlog*)((char*)CatlogFile.data() + i);ClusterAddress SubCatlogAddr;FileAddr_To_LogicCluster(*SubCatlog, i, SubCatlogAddr);if (SubCatlog->Attribute == CATLOG) {KeDelete_File(SubCatlogAddr, CatlogAddr);}DeleteFile_Catlog(SubCatlogAddr, CatlogAddr);}}//******************如果是目录文件(对应文件夹)的话,递归删除文件夹下的所有文件********************//DeleteFile_Catlog(CatlogAddr, FatherCatlogAddr);return 0;
}

在这部分代码中,主要处理删除文件夹的递归问题,具体的删除操作交由DeleteFile_Catlog来做,实现代码如下:

int FAT32::DeleteFile_Catlog(ClusterAddress& CatlogAddr, ClusterAddress& FatherCatlogAddr) {std::vector<dword> FreeCluster;ShortCatlog Catlog;DiskIO(CatlogAddr, (byte*)&Catlog, sizeof(ShortCatlog), IO_IN);//********************************释放文件所占的空间************************************dword ClusterPtr = (Catlog.HighClusterNum << 16) + Catlog.LowClusterNum;while (ClusterPtr != END_CLUSTER_FLAG){dword NextClusterPtr = FAT1[ClusterPtr];FreeCluster.push_back(ClusterPtr);FAT1[ClusterPtr] = 0;ClusterPtr = NextClusterPtr;}std::sort(FreeCluster.begin(), FreeCluster.end());Main_FSINFO.UnusedClusterNum += FreeCluster.size();Main_FSINFO.FirstUnusedCluster = FreeCluster.back();//********************************释放文件所占的空间************************************//********************************删除文件目录项************************************ShortCatlog FatherCatlog;dword PointCatlogFileOffset;if (!FatherCatlogAddr.Cluster) {//规定Cluster==0时地址对应的为虚拟根目录文件的FCBFatherCatlog = RootCatlog;}else {DiskIO(FatherCatlogAddr, (byte*)&FatherCatlog, sizeof(ShortCatlog), IO_IN);}//****读取上层目录文件并查找需要删除的FCB位于文件内的偏移*********//Vector_Byte FatherCatlogFile;KeRead_File(FatherCatlog, FatherCatlogFile);ShortCatlog* CatlogPtr;dword i = 0;for (dword loop = 0; loop < FatherCatlogFile.size(); loop += sizeof(ShortCatlog)){CatlogPtr = (ShortCatlog*)(FatherCatlogFile.data() + loop);if (*CatlogPtr == Catlog) {i = loop;}if (CatlogPtr->FileName[0] == '.' && CatlogPtr->FileName[1] != '.')PointCatlogFileOffset = loop;}//****读取上层目录文件并查找需要删除的FCB位于文件内的偏移*********////****将剩余的目录项前移以填补空白********//dword SheftNum = (FatherCatlog.FileLength - i - sizeof(ShortCatlog)) / sizeof(ShortCatlog);ClusterAddress PreAddr = CatlogAddr, Addr;Addr = PreAddr;ShortCatlog Tmp;for (i = 0; i < SheftNum; i++){Addr.ByteOffset += sizeof(ShortCatlog);if (Addr.ByteOffset >= Main_FAT32_DBR.SectorsPreCluster * SectorSize) {Addr.ByteOffset -= Main_FAT32_DBR.SectorsPreCluster * SectorSize;Addr.Cluster = FAT1[Addr.Cluster];}DiskIO(Addr, &Tmp, sizeof(ShortCatlog), IO_IN);DiskIO(PreAddr, &Tmp, sizeof(ShortCatlog), IO_OUT);PreAddr = Addr;}memset(&Tmp, 0, sizeof(ShortCatlog));DiskIO(Addr, &Tmp, sizeof(ShortCatlog), IO_OUT);//****将剩余的目录项前移以填补空白********////****将父目录文件的FCB进行修改********//FatherCatlog.FileLength -= sizeof(ShortCatlog);this->Set_CatlogTime(FatherCatlog, MODIFY);DiskIO(FatherCatlogAddr, &FatherCatlog, sizeof(ShortCatlog), IO_OUT);ClusterAddress PointCatlogAddr;this->FileAddr_To_LogicCluster(FatherCatlog, PointCatlogFileOffset, PointCatlogAddr);FatherCatlog.FileName[0] = '.';DiskIO(PointCatlogAddr, &FatherCatlog, sizeof(ShortCatlog), IO_OUT);//****将父目录文件的FCB进行修改********////********************************删除文件目录项************************************return 0;
}

这部分的代码看起来很多,但是实际上只做了三件事:

  1. 释放文件所占有的空间;
  2. 修改需要删除的FCB所占的目录文件(即父目录文件);
  3. 修改父目录文件的FCB;

第一步需要更新FAT表和FSINFO扇区,具体的思路是逐个遍历文件的簇指针,每遍历一个时,将当前簇的偏移置入向量数据结构中,并将其FAT表中对应的指针项置零,当遍历完成之后,筛选出最小的那个簇号,并更新FSINFO扇区。
其第2步占了绝大部分代码,其原因是在于这部分操作并没有屏蔽文件在硬盘中是离散的这个细节,大致的实现修改的思路是将整个父目录文件读出,并计算需要移动的目录项个数,并逐个左移,在左移的过程中要注意地址的离散性。

CreateACatlog函数

该函数用于创建一个新的文件夹(即一个目录文件),其大致的实现思路为:

  1. 判断是否已经存在该目录项,如果存在则不进行创建;
  2. 通过调用GetFatherPathSearch_Catlog来获取父目录文件的FCB;
  3. 通过修改父目录文件的FCB名称来生成点点目录项;
  4. 调用Write_Filew方式来创建一个目录文件,注意此时的目录文件中不存在点目录项;
  5. 通过Search_Catlog来获取刚刚生成的目录文件的FCB,并生成点目录项;
  6. 调用Write_Filea方式来向刚刚生成的目录文件中追加一个点目录项;

之所以要两次写目录文件的原因是在于在创建这个目录文件之前,是不存在这个正在创建文件的目录项的,我们只能当创建完成之后,向这个目录文件追加写一个点目录项。
对应的实现代码如下:

int FAT32::CreateACatlog(std::string CatlogPath) {ShortCatlog CatlogFileCatlog;if (!this->Search_Catlog(CatlogPath, CatlogFileCatlog))return FAT32_ERROR;std::string FatherPath;ShortCatlog FatherCatlog;Vector_Str Tmp;StrSplit(CatlogPath, '/', Tmp);this->GetFatherPath(CatlogPath, FatherPath);this->Search_Catlog(FatherPath, FatherCatlog);if (Tmp.back().length() >= 11)return FAT32_NAME_TOOLONG;memset(&FatherCatlog, 0, 11);FatherCatlog.FileName[0] = '.';FatherCatlog.FileName[1] = '.';Vector_Byte TmpByte;TmpByte.resize(sizeof(ShortCatlog));memcpy(TmpByte.data(), &FatherCatlog, sizeof(ShortCatlog));this->Write_File(CatlogPath, TmpByte, 'w', CATLOG);this->Search_Catlog(CatlogPath, CatlogFileCatlog);memset(&CatlogFileCatlog, 0, 11);CatlogFileCatlog.FileName[0] = '.';memcpy(TmpByte.data(), &CatlogFileCatlog, sizeof(ShortCatlog));this->Write_File(CatlogPath, TmpByte, 'a');return 0;
}

Search_Catlog、Search_Catlog_Addr、Search_ACatlogFile搜索函数

这三个函数算是整个系统的最为重要的三个函数,主要作用是路径对应的目录项。

这三个函数的声明为:

 int Search_ACatlogFile(ShortCatlog Catlog, std::string File, ClusterAddress& Result);int Search_Catlog_Addr(std::string FilePath, ClusterAddress& CatlogAddr);int Search_Catlog(std::string FilePath, ShortCatlog& Found_Catlog);

这三个函数的名称相似,但是作用不同,每个函数对应实现功能为:

函数名 实现功能
Search_Catlog 通过给出的文件路径来获取对应的目录项
Search_Catlog_Addr 通过给出的文件路径来获取对应的目录地址(可以理解为指针)
Search_ACatlogFile 具体搜索一个目录文件下的目录项

可以看到,Search_CatlogSearch_Catlog_Addr这两个函数的实现功能类似,但是主要的区别在于Search_Catlog得到的目录项和硬盘中的目录项是分离的,类似于如下的C代码:

int a=1,b=a;
b=2;//a的值不会改变

而如果想修改硬盘中的目录项,则需要通过Search_Catlog_Addr来获取目录项的地址(指针),并通过DiskIO来设置硬盘中的目录项,类似于如下C代码:

int a=1
int *b;
b=&a;
*b=2;

那么我们来看一看这三个函数的实现逻辑是怎么样的。首先是Search_Catlog

int FAT32::Search_Catlog(std::string FilePath, ShortCatlog& Found_Catlog) {ClusterAddress CatlogAddr;if (Search_Catlog_Addr(FilePath, CatlogAddr))return FAT32_ERROR;if (!CatlogAddr.Cluster)//簇号为0时表示该地址是虚拟根目录FCB块Found_Catlog = RootCatlog;elseDiskIO(CatlogAddr, (byte*)&Found_Catlog, sizeof(ShortCatlog), IO_IN);return 0;
}

可以看到,该函数通过调用Search_Catlog_Addr实现了FCB块的获取,需要注意的是,实现代码中有一段逻辑来判断获取的地址中的簇地址是否为0,这样设计的原因之前也提过,在根目录FCB在FAT32中是不存在的(Windows 的FAT32驱动并未在根目录文件中设置点目录项),无法直接通过DiskIO函数从硬盘中读取。
Search_Catlog_Addr的实现逻辑如下

int FAT32::Search_Catlog_Addr(std::string FilePath, ClusterAddress& CatlogAddr) {ShortCatlog Found_Catlog;Vector_Str AbsolutePath;Vector_Str tmp;int ReadIndex = 0;if(FilePath[0]!=':'){//约定路径名第一个为:表示绝对路径this->RealtivePath_AbsolutePath(this->CurrentPath, AbsolutePath, FilePath);}else {StrSplit(FilePath, '/', AbsolutePath);}//*****如果要搜索根目录FCB的话,返回阅读的0簇地址*****//if (AbsolutePath.size() == 1) {CatlogAddr.Cluster = 0;return 0;}//*****如果要搜索根目录FCB的话,返回阅读的0簇地址*****//if (Search_ACatlogFile(RootCatlog, AbsolutePath[1], CatlogAddr))return FAT32_ERROR;DiskIO(CatlogAddr, &Found_Catlog, sizeof(ShortCatlog), IO_IN);for (int i = 2; i < AbsolutePath.size(); i++){if (this->Search_ACatlogFile(Found_Catlog, AbsolutePath[1], CatlogAddr))return FAT32_ERROR;DiskIO(CatlogAddr, &Found_Catlog, sizeof(ShortCatlog), IO_IN);}return 0;
}

可以看到,该实现代码做了如下几件事:

  1. 首先判断了输入的路径是否是绝对路径,如果不是绝对路径的话通过RealtivePath_AbsolutePath将相对路径转化为绝对路径。
  2. 判断要搜索的是否是根目录文件的FCB
  3. 如果不是根目录文件FCB的话,初始化搜索过程,即搜索根目录文件;
  4. 迭代搜索绝对路径下的下一个路径名,如果搜索途中有一个路径名找不到的话,就搜索失败;

在上述函数中调用了Search_ACatlogFile函数,该函数的实现逻辑比较简单:

  1. 通过KeRead_File读取了目录文件
  2. 逐项搜索目录项
  3. 搜索到后调用FileAddr_To_LogicCluster函数将文件逻辑地址转化为簇地址

在这个函数中搜索目录项时仅仅判断了目录项的名字是否相同,没有判断目录项的属性,这可能会导致潜在的BUG。

余下的函数

剩下的函数实现过程较为简单,这里就不再进行赘述了,仅仅列出他们的功能,实现部分请参照源代码

函数 实现功能
FileAddr_To_LogicCluster 该函数用于将文件的逻辑地址转化为本分区的簇地址
Set_CatlogTime 这个函数用于将目录项的对应时间属性设置为当前时间
StrSplit 本函数实现了一个简单的划分字符串的算法
Modify_Catlog 修改文件目录的时间属性,如最后访问时间
RealtivePath_AbsolutePath 通过CurrentPath向量来实现相对路径到绝对路径的转化
EnterANewCatlog 和’Cd’类似,实质上是修改CurrentPath
GetFatherPath 获取父路径
Preserve_ManageInfromation 将类中的管理结构写入硬盘中
Malloc_Cluster 申请一个新的簇
SynCatlog 同步一个目录文件中的点目录项
Cluster_To_PhysicalSector 将本分区的地址映射为硬盘的物理地址

测试

对应的测试代码为:

int main() {FAT32 test;test.CreateAVirtualDisk(1024,"C:\\Users\\ASUS\\Desktop\\test2.img");test.ReadVirtualDisk("C:\\Users\\ASUS\\Desktop\\test2.img");test.CreateAParition_FAT32("tset",400);test.SetActiveParition("tset");test.CreateACatlog(":/abcd");test.EnterANewCatlog("./abcd");test.CreateACatlog("./abcw");Vector_Byte tmp;for (int i = 0; i < 4; i++){tmp.push_back('b');}test.Write_File("./abce.txt", tmp, 'w');test.Write_File("./123.txt");return 0;
}

创建好的虚拟硬盘如下所示:

源码附录

CRC32.h

#pragma once
class CRC32 {
private:static const unsigned int crc32tab[256];
public:static unsigned int crc32(const unsigned char* buf, unsigned int size);
};

CRC32.cpp

#include"CRC32.h"
unsigned const int CRC32::crc32tab[256] = {0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL,0x076dc419L, 0x706af48fL, 0xe963a535L, 0x9e6495a3L,0x0edb8832L, 0x79dcb8a4L, 0xe0d5e91eL, 0x97d2d988L,0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, 0x90bf1d91L,0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L,0x136c9856L, 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL,0x14015c4fL, 0x63066cd9L, 0xfa0f3d63L, 0x8d080df5L,0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, 0xa2677172L,0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L,0x32d86ce3L, 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L,0x26d930acL, 0x51de003aL, 0xc8d75180L, 0xbfd06116L,0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, 0xb8bda50fL,0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL,0x76dc4190L, 0x01db7106L, 0x98d220bcL, 0xefd5102aL,0x71b18589L, 0x06b6b51fL, 0x9fbfe4a5L, 0xe8b8d433L,0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, 0xe10e9818L,0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL,0x6c0695edL, 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L,0x65b0d9c6L, 0x12b7e950L, 0x8bbeb8eaL, 0xfcb9887cL,0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, 0xfbd44c65L,0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL,0x4369e96aL, 0x346ed9fcL, 0xad678846L, 0xda60b8d0L,0x44042d73L, 0x33031de5L, 0xaa0a4c5fL, 0xdd0d7cc9L,0x5005713cL, 0x270241aaL, 0xbe0b1010L, 0xc90c2086L,0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L,0x59b33d17L, 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL,0xedb88320L, 0x9abfb3b6L, 0x03b6e20cL, 0x74b1d29aL,0xead54739L, 0x9dd277afL, 0x04db2615L, 0x73dc1683L,0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L,0xf00f9344L, 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL,0xf762575dL, 0x806567cbL, 0x196c3671L, 0x6e6b06e7L,0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, 0x67dd4accL,0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L,0xd1bb67f1L, 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL,0xd80d2bdaL, 0xaf0a1b4cL, 0x36034af6L, 0x41047a60L,0xdf60efc3L, 0xa867df55L, 0x316e8eefL, 0x4669be79L,0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL,0xc5ba3bbeL, 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L,0xc2d7ffa7L, 0xb5d0cf31L, 0x2cd99e8bL, 0x5bdeae1dL,0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, 0x026d930aL,0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L,0x92d28e9bL, 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L,0x86d3d2d4L, 0xf1d4e242L, 0x68ddb3f8L, 0x1fda836eL,0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, 0x18b74777L,0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L,0xa00ae278L, 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L,0xa7672661L, 0xd06016f7L, 0x4969474dL, 0x3e6e77dbL,0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, 0x37d83bf0L,0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L,0xbad03605L, 0xcdd70693L, 0x54de5729L, 0x23d967bfL,0xb3667a2eL, 0xc4614ab8L, 0x5d681b02L, 0x2a6f2b94L,0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, 0x2d02ef8dL
};unsigned int CRC32::crc32(const unsigned char* buf, unsigned int size)
{unsigned int i, crc;crc = 0xFFFFFFFF;for (i = 0; i < size; i++)crc = crc32tab[(crc ^ buf[i]) & 0xff] ^ (crc >> 8);return crc ^ 0xFFFFFFFF;
}

FAT32.h

#pragma once
#include"GPT.h"
#include<vector>
#pragma pack(1)//********************************************函数的返回状态******************************************//
#define FAT32_NAME_TOOLONG 4
#define NO_SUCH_PATH 5
#define FAT32_ERROR -1
//********************************************函数的返回状态******************************************////********************************************目录项的属性******************************************//
#define DISK_LABEL 0b00001000
#define FILE_RW 0b00000000 //文件可读写
#define CATLOG 0b00010000 //目录文件
//********************************************目录项的属性******************************************////********************************************FAT表中最后一个链接的标志******************************************//
#define END_CLUSTER_FLAG 0x0fffffff
//********************************************FAT表中最后一个链接的标志******************************************////********************************************用于DiskIO函数中,用于指示数据的流动方向******************************************//
#define IO_IN 0
#define IO_OUT 1
//********************************************用于DiskIO函数中,用于指示数据的流动方向******************************************////********************************************用于Set_CatlogTime函数中,用于指示修改目录项的何种时间属性******************************************//
#define CREATE 1
#define MODIFY 2
#define VISIT  3
//********************************************用于Set_CatlogTime函数中,用于指示修改目录项的何种时间属性******************************************//struct FAT32_DBR
{byte JmpCode[3];byte OEM[8];word BytesPreSectors;byte SectorsPreCluster;word ReversedSector;//在FAT表之前的扇区数量,备DBR、备份FSINFO和主FSINFO、主DBR都在这部分区域中byte FAT_Num;dword Reversed1;byte MediumType;word Reversed2;//******硬盘物理参数******//word SectorsPreTrack;word HeadNum;//******硬盘物理参数******//dword BeginLAB;//本分区的开始扇区dword TotalSector;dword SectorPre_FAT;word ExtendedFlag;word Version;dword RootClusterNum;word FileSystemInformationSector;word Recovery_DBR_LAB;byte Reversed3[12];byte DriveNum;//物理驱动器号byte Reversed4;byte ExtendedBootSignature;dword ParitionSerialNum;byte ParitionName[11];byte System_ID[8];//本来用于标识FAT32分区的名称,现在已经被根目录中的卷标目录项所代替byte Reversed5[420];word EndSignautre;FAT32_DBR();
};
struct FSINFO
{dword Signature;byte  Reversed[480];dword Signature2;dword UnusedClusterNum;dword FirstUnusedCluster;byte  Reversed2[14];word  EndSignature = 0xaa55;FSINFO();
};
struct ShortCatlog
{byte FileName[8];byte EntendName[3];byte Attribute;byte Reversed;byte Tenmillisecond;//文件创建时的10毫秒位word FileCreateTime;word FileCreateDate;word FileLastVisitDate;word HighClusterNum;word LastModifyTime;word LastModifyDate;word LowClusterNum;dword FileLength;//字节为单位ShortCatlog();bool operator==(const ShortCatlog& a1) {if (this->Attribute != a1.Attribute)return FAT32_ERROR;for (int i = 0; i < sizeof(ShortCatlog); i++){if (*(byte*)&a1 + i != *(byte*)this + i)return 0;}return 1;}
};
struct ClusterAddress {dword Cluster;dword ByteOffset;
};typedef std::vector<std::string> Vector_Str;
typedef std::vector<ShortCatlog> Vector_Catlog;
typedef std::vector<byte> Vector_Byte;class FAT32 :public GPT {
protected:FAT32_DBR Main_FAT32_DBR;FAT32_DBR Backup_FAT32_DBR;FSINFO      Main_FSINFO;FSINFO      Backup_FSINFO;ShortCatlog RootCatlog;//该结构仅存在于内存中,不位于FAT32结构体中,仅做管理需要Vector_Str CurrentPath;dword* FAT1;dword* FAT2;int Search_ACatlogFile(ShortCatlog Catlog, std::string File, ClusterAddress& Result);int Search_Catlog_Addr(std::string FilePath, ClusterAddress& CatlogAddr);int Search_Catlog(std::string FilePath, ShortCatlog& Found_Catlog);int KeRead_File(ShortCatlog Catlog, Vector_Byte& Buffer);//通过目录项读一个文件dword Malloc_Cluster(dword LastCluster);int StrSplit(std::string String, char pattern, Vector_Str& StrVector);dword   Cluster_To_PhysicalSector(dword Cluster);int DiskIO(const ClusterAddress& Addr, void* Buffer, dword len, int type);int Modify_Catlog(std::string CatlogPath, ShortCatlog& NewCatlog);int Set_CatlogTime(ShortCatlog& Catlog,int tpye);int DeleteFile_Catlog(ClusterAddress& CatlogAddr, ClusterAddress& FatherCatlogAddr);int KeDelete_File(ClusterAddress& CatlogAddr, ClusterAddress& FatherCatlogAddr);int FileAddr_To_LogicCluster(ShortCatlog& FileCatlog, dword ByteOffset,ClusterAddress& Result);int Preserve_ManageInfromation();int GetFatherPath(std::string& Path, std::string& FatherPath);int KeWrite_File(ClusterAddress& FatherCatlogAddr, Vector_Byte& File, char Type);int SynCatlog(ShortCatlog& FileCatlog);int RealtivePath_AbsolutePath(Vector_Str& CurrentPath, Vector_Str& AbsolutePath, std::string TransferPath);
public:int Delete_File(std::string FilePath);int virtual SetParition(dword FirstLAB, dword EndLAB, std::string ParitionName);int Write_File(std::string FilePath, Vector_Byte& File,char Type,byte FileType = FILE_RW);int Read_File(std::string FilePath, Vector_Byte& Buffer);int SetActiveParition(std::string ParitionName);int CreateACatlog(std::string Catlog);int EnterANewCatlog(std::string Path);~FAT32();
};
#pragma pack()

FAT32.cpp

#include"FAT32.h"
#include<string.h>
#include<Windows.h>
#include <algorithm>FAT32_DBR::FAT32_DBR() {memset(this, 0, sizeof(FAT32_DBR));memcpy(OEM, "MSDOS5.0", 8);BytesPreSectors = 512;SectorsPreCluster = 8;ReversedSector = 0x28;//该保留扇区最少为24个,MAGIC NUMBERFAT_Num = 2;MediumType = 0xF8;SectorsPreTrack = 0x10;//假设每个磁道63个扇区HeadNum = 8;//假设10个磁头ExtendedFlag = 0x0;Version = 0;RootClusterNum = 2;DriveNum = 0x80;ExtendedBootSignature = 0x29;memcpy(ParitionName, "NO NAME", 8);memcpy(System_ID, "FAT32", 6);ParitionSerialNum = GPT::Random();EndSignautre = 0xaa55;
}
FSINFO::FSINFO() {char* tmp = (char*)"RRaA";memcpy(&Signature, tmp, 4);tmp = (char*)"rrAa";memcpy(&Signature2, tmp, 4);}
ShortCatlog::ShortCatlog() {memset(this, 0, sizeof(ShortCatlog));memset(this, 0x0, 11);
}
//该FAT32文件系统采取8扇区对齐
dword   FAT32::Cluster_To_PhysicalSector(dword Cluster) {return (Cluster - 2) * Main_FAT32_DBR.SectorsPreCluster + Main_FAT32_DBR.ReversedSector + Main_FAT32_DBR.BeginLAB + 2 * Main_FAT32_DBR.SectorPre_FAT;//转换公式为:分区起始扇区+分区保留扇区+2*每FAT扇区数+(簇数-2)*每簇扇区数
}
int FAT32::SynCatlog(ShortCatlog& FileCatlog) {Vector_Byte File;this->KeRead_File(FileCatlog, File);ShortCatlog* CatlogPtr = NULL;dword i;for (i = 0; i < File.size(); i += sizeof(ShortCatlog)){CatlogPtr = (ShortCatlog*)(File.data() + i);if (CatlogPtr->Attribute == CATLOG && CatlogPtr->FileName[0] == '.' && CatlogPtr->FileName[1] == '\0')break;}ClusterAddress Tmp;FileAddr_To_LogicCluster(FileCatlog, i, Tmp);ShortCatlog NewPointCatlog = FileCatlog;memset(&NewPointCatlog, 0, 11);NewPointCatlog.FileName[0] = '.';DiskIO(Tmp, &NewPointCatlog, sizeof(ShortCatlog), IO_OUT);return 0;
}dword FAT32::Malloc_Cluster(dword LastCluster) {//参数为文件的最后一个簇号//为文件新分配一个簇if (Main_FSINFO.UnusedClusterNum <= 0)return FAT32_ERROR;//*************判断最后一个簇是否存在*************//if (LastCluster != -1) {FAT1[LastCluster] = Main_FSINFO.FirstUnusedCluster;FAT1[FAT1[LastCluster]] = END_CLUSTER_FLAG;}//*************判断最后一个簇是否存在*************//else {//表示这是文件的第一个簇FAT1[Main_FSINFO.FirstUnusedCluster] = END_CLUSTER_FLAG;}LastCluster = Main_FSINFO.FirstUnusedCluster;//为文件新分配一个簇//修改FSINFO信息Main_FSINFO.UnusedClusterNum--;dword i;for (i = Main_FSINFO.FirstUnusedCluster; i < Main_FAT32_DBR.TotalSector && FAT1[i]; i++);Main_FSINFO.FirstUnusedCluster = i;//修改FSINFO信息return LastCluster;
}
int FAT32::DiskIO(const ClusterAddress& Addr, void* Buffer, dword len, int type) {if (Addr.Cluster > Main_FAT32_DBR.TotalSector/Main_FAT32_DBR.SectorsPreCluster)return FAT32_ERROR;if (Addr.Cluster == 0) {if(type==IO_IN)memcpy(Buffer, &RootCatlog, len);elsememcpy(&RootCatlog,Buffer , len);return 0;}fseek(VirtualDisk, Cluster_To_PhysicalSector(Addr.Cluster)*SectorSize + Addr.ByteOffset, SEEK_SET);switch (type){case IO_IN:fread(Buffer, 1, len, VirtualDisk);break;case IO_OUT:fwrite(Buffer, 1, len, VirtualDisk);break;default:return FAT32_ERROR;}return 0;
}
int FAT32::Preserve_ManageInfromation() {fseek(VirtualDisk, Main_FAT32_DBR.BeginLAB * SectorSize, SEEK_SET);fwrite(&Main_FAT32_DBR, sizeof(Main_FAT32_DBR), 1, VirtualDisk);fseek(VirtualDisk, (Main_FAT32_DBR.Recovery_DBR_LAB + Main_FAT32_DBR.BeginLAB) * SectorSize, SEEK_SET);fwrite(&Backup_FAT32_DBR, sizeof(Main_FAT32_DBR), 1, VirtualDisk);//DBR管理区//FAT1、FAT2fseek(VirtualDisk, (Main_FAT32_DBR.BeginLAB + Main_FAT32_DBR.ReversedSector) * SectorSize, SEEK_SET);long test = ftell(VirtualDisk);fwrite(FAT1, 1, Main_FAT32_DBR.SectorPre_FAT * SectorSize, VirtualDisk);fwrite(FAT1, 1, Main_FAT32_DBR.SectorPre_FAT * SectorSize, VirtualDisk);//FAT1、FAT2FSINFOfseek(VirtualDisk, (Main_FAT32_DBR.BeginLAB + Main_FAT32_DBR.FileSystemInformationSector) * SectorSize, SEEK_SET);fwrite(&Main_FSINFO, sizeof(Main_FSINFO), 1, VirtualDisk);fseek(VirtualDisk, (Main_FAT32_DBR.FileSystemInformationSector + Main_FAT32_DBR.Recovery_DBR_LAB + Main_FAT32_DBR.BeginLAB) * SectorSize, SEEK_SET);fwrite(&Main_FSINFO, sizeof(Main_FSINFO), 1, VirtualDisk);return 0;
}int FAT32::GetFatherPath(std::string& Path, std::string& FatherPath) {Vector_Str Tmp;StrSplit(Path, '/', Tmp);for (int i = 0; i < Tmp.size() - 1; i++){FatherPath += Tmp[i] + '/';}FatherPath = FatherPath.substr(0, FatherPath.size() - 1);return 0;
}
int FAT32::EnterANewCatlog(std::string Path) {Vector_Str Tmp;ShortCatlog UseLess;if (this->Search_Catlog(Path, UseLess))return -1;this->RealtivePath_AbsolutePath(this->CurrentPath, this->CurrentPath, Path);return 0;
}FAT32::~FAT32() {Preserve_ManageInfromation();//FSINFOdelete FAT1;delete FAT2;
}
int FAT32::SetParition(dword FirstLAB, dword EndLAB, std::string ParitionName) {if (!VirtualDisk)return NOVirtualDisk;if (ParitionName.length() >= 12)//短文件目录项最多支持12个字符return FAT32_NAME_TOOLONG;//********************设置DBR分区****************************//传入的参数是左右都是闭合区间,能用的扇区为[FirstLAB,EndLAB],为了方便//转化为左闭右开,即[FirestLAB,EndLAB)EndLAB++;if (EndLAB - FirstLAB < 0x20)//保留扇区为0x10,最少两个扇区的FAT表,还有至少一个根目录的簇(8个扇区)return NoEnoughCapacity;//文件系统能用的分区一定是8的整数倍EndLAB -= (EndLAB - FirstLAB) % 8;dword  TotalClusterNum;TotalClusterNum = (EndLAB - FirstLAB) / 8;Main_FAT32_DBR.BeginLAB = FirstLAB;Main_FAT32_DBR.TotalSector = EndLAB - FirstLAB;//每个扇区能放128个簇号,最少一个扇区Main_FAT32_DBR.SectorPre_FAT = (TotalClusterNum + 2) / (SectorSize / 4);if ((TotalClusterNum + 2) % 128) {Main_FAT32_DBR.SectorPre_FAT++;}Main_FAT32_DBR.FileSystemInformationSector = 1;//该值为起始分区的偏移量Main_FAT32_DBR.Recovery_DBR_LAB = 2;memcpy(&Backup_FSINFO, &Main_FSINFO, 512);Backup_FAT32_DBR.Recovery_DBR_LAB = 0;//********************设置DBR分区****************************//*********************************设置FAT表***************************************FAT1 = (dword*)new byte[Main_FAT32_DBR.SectorPre_FAT * SectorSize];FAT2 = (dword*)new byte[Main_FAT32_DBR.SectorPre_FAT * SectorSize];memset(FAT1, 0, SectorSize * Main_FAT32_DBR.SectorPre_FAT);FAT1[0] = 0x0ffffff8;FAT1[1] = 0xffffffff;FAT1[2] = END_CLUSTER_FLAG;//刚刚创建分区时只有一个卷名项memcpy(FAT2, FAT1, Main_FAT32_DBR.SectorPre_FAT * SectorSize);//*********************************设置FAT表***************************************//***************************设置FSINFO分区相关************************************Main_FSINFO.UnusedClusterNum = TotalClusterNum - 1;Main_FSINFO.FirstUnusedCluster = 3;memcpy(&Backup_FAT32_DBR, &Main_FAT32_DBR, 512);//***************************设置FSINFO分区相关************************************//*************************设置初始的目录项****************************************//卷标目录项ShortCatlog ParitionCatlog;memcpy(ParitionCatlog.FileName, ParitionName.c_str(), ParitionName.length());ParitionCatlog.Attribute = DISK_LABEL;//每个目录文件都有的'.'目录项ShortCatlog RootCatlogCatlog;RootCatlogCatlog.Attribute = CATLOG;RootCatlogCatlog.FileName[0] = '.';RootCatlogCatlog.HighClusterNum = Main_FAT32_DBR.RootClusterNum >> 16;RootCatlogCatlog.LowClusterNum = Main_FAT32_DBR.RootClusterNum;RootCatlogCatlog.FileLength = 2 * sizeof(ShortCatlog);//*************************设置初始的目录项****************************************//该根目录项只是管理使用,实际上并不存在于虚拟硬盘中RootCatlog.FileLength = sizeof(ShortCatlog);RootCatlog.LowClusterNum = Main_FAT32_DBR.RootClusterNum;//该根目录项只是管理使用,实际上并不存在于虚拟硬盘中//*********************************将新的东西写入**********************************fseek(VirtualDisk, SectorSize * FirstLAB, SEEK_SET);fwrite(&Main_FAT32_DBR, SectorSize, 1, VirtualDisk);fwrite(&Main_FSINFO, SectorSize, 1, VirtualDisk);fwrite(&Backup_FAT32_DBR, SectorSize, 1, VirtualDisk);fwrite(&Backup_FSINFO, SectorSize, 1, VirtualDisk);fseek(VirtualDisk, SectorSize * (FirstLAB + Main_FAT32_DBR.ReversedSector), SEEK_SET);fwrite(FAT1, 1, SectorSize * Main_FAT32_DBR.SectorPre_FAT, VirtualDisk);fwrite(FAT2, 1, SectorSize * Main_FAT32_DBR.SectorPre_FAT, VirtualDisk);fwrite(&ParitionCatlog, sizeof(ShortCatlog), 1, VirtualDisk);fwrite(&RootCatlogCatlog, sizeof(ShortCatlog), 1, VirtualDisk);//*********************************将新的东西写入**********************************}
int FAT32::Modify_Catlog(std::string CatlogPath, ShortCatlog& NewCatlog) {ClusterAddress ClusterAddress;if (CatlogPath.size() == 1 && CatlogPath[0] == ':') {//当需要修改的目录为根目录时,只需要修改内存中的根目录memcpy(&RootCatlog, &NewCatlog, sizeof(ShortCatlog));return 0;}if (Search_Catlog_Addr(CatlogPath, ClusterAddress))return FAT32_ERROR;DiskIO(ClusterAddress, (byte*)&NewCatlog, sizeof(ShortCatlog), IO_OUT);return 0;
}
int FAT32::SetActiveParition(std::string ParitionName) {FAT32_DBR Tmp;ShortCatlog PatitionCatlog;if (!VirtualDisk)return FAT32_ERROR;//***********************寻找指定分区************************************for (int i = 0; i < 128; i++){fseek(VirtualDisk, MainGpt[i].BeginLBA * SectorSize, SEEK_SET);fread(&Tmp, sizeof(FAT32_DBR), 1, VirtualDisk);if (!Tmp.BeginLAB)//分区表已经读完,但还没有找到对应的分区名字return FAT32_ERROR;if (strcmp((char*)&Tmp.System_ID, "FAT32"))continue;fseek(VirtualDisk, (Tmp.BeginLAB + Tmp.ReversedSector + Tmp.SectorPre_FAT * 2) * SectorSize, SEEK_SET);int CatlogPreClu = Tmp.SectorsPreCluster * SectorSize / sizeof(ShortCatlog);do{fread(&PatitionCatlog, sizeof(ShortCatlog), 1, VirtualDisk);if (!PatitionCatlog.Attribute)//根目录文件已经读完,但还没有找到卷目录项,默认卷标目录项在第一个簇中return FAT32_ERROR;} while (PatitionCatlog.Attribute != DISK_LABEL);if (!strcmp((char*)&PatitionCatlog.FileName, ParitionName.c_str()))break;}//***********************寻找指定分区************************************//***********************保存原来的扇区内容************************************if (Main_FAT32_DBR.BeginLAB) {Preserve_ManageInfromation();delete FAT1;}//***********************读取扇区内容****************************************************************************************fseek(VirtualDisk, Tmp.BeginLAB * SectorSize, SEEK_SET);//读DBRfread(&Main_FAT32_DBR, sizeof(FAT32_DBR), 1, VirtualDisk);fseek(VirtualDisk, (Main_FAT32_DBR.FileSystemInformationSector + Main_FAT32_DBR.BeginLAB) * SectorSize, SEEK_SET);//读FSINFOfread(&Main_FSINFO, sizeof(FSINFO), 1, VirtualDisk);fseek(VirtualDisk, (Main_FAT32_DBR.BeginLAB + Main_FAT32_DBR.ReversedSector) * SectorSize, SEEK_SET);//读FAT表FAT1 = (dword*)new byte[Main_FAT32_DBR.SectorPre_FAT * SectorSize];fread(FAT1, 1, Main_FAT32_DBR.SectorPre_FAT * SectorSize, VirtualDisk);//****************************读取根目录文件下的目录项个数来设置根目录项的文件长度************************************memset(&RootCatlog, 0, sizeof(ShortCatlog));RootCatlog.Attribute = CATLOG;RootCatlog.LowClusterNum = Main_FAT32_DBR.RootClusterNum;RootCatlog.HighClusterNum = Main_FAT32_DBR.RootClusterNum >> 16;int j = 0;dword NextClu = Main_FAT32_DBR.RootClusterNum;fseek(VirtualDisk, (Main_FAT32_DBR.BeginLAB + Main_FAT32_DBR.ReversedSector + Main_FAT32_DBR.SectorPre_FAT * 2) * SectorSize, SEEK_SET);int CatlogPreClu = Main_FAT32_DBR.SectorsPreCluster * SectorSize / sizeof(ShortCatlog);do{fread(&PatitionCatlog, sizeof(ShortCatlog), 1, VirtualDisk);//此时PatitionCatlog已经没有用了,用于做暂时变量RootCatlog.FileLength += sizeof(ShortCatlog);j++;if (j == CatlogPreClu - 1) {j = 0;fseek(VirtualDisk, this->Cluster_To_PhysicalSector(NextClu) * SectorSize, SEEK_SET);NextClu = FAT1[NextClu];if (NextClu == END_CLUSTER_FLAG)break;}} while (PatitionCatlog.FileName[0]);RootCatlog.FileLength -= sizeof(ShortCatlog);CurrentPath.push_back(":");//****************************读取根目录文件下的目录项个数来设置根目录项的文件长度************************************//***********************读取扇区内容****************************************************************************************}
int FAT32::Read_File(std::string FilePath, Vector_Byte& Buffer) {ShortCatlog FileCatlog;if (Search_Catlog(FilePath, FileCatlog))return FAT32_ERROR;KeRead_File(FileCatlog, Buffer);return 0;
}
int FAT32::Write_File(std::string FilePath, Vector_Byte& File, char Type, byte FileType) {ShortCatlog FileCatlog;int flag = Search_Catlog(FilePath, FileCatlog);Vector_Str StrTmp;std::string PartentPath;Vector_Byte CatlogBuffer;ClusterAddress Tmp;if (Main_FSINFO.UnusedClusterNum * 8 * 512 < File.size())return FAT32_ERROR;if (Type == 'a') {//************************************判断文件是否存在***************************************//if (flag)return FAT32_ERROR;//************************************判断文件是否存在***************************************////************************************追加写内容***************************************//dword ByteOffset = FileCatlog.FileLength % (Main_FAT32_DBR.SectorsPreCluster * SectorSize);dword LastCluster = 0;for (LastCluster = (FileCatlog.HighClusterNum << 16) + FileCatlog.LowClusterNum; FAT1[LastCluster] != END_CLUSTER_FLAG; LastCluster = FAT1[LastCluster]);if (File.size() <= Main_FAT32_DBR.SectorsPreCluster * SectorSize - ByteOffset) {//判断当前簇是否能够放下新增的内容Tmp.Cluster = LastCluster;Tmp.ByteOffset = ByteOffset;DiskIO(Tmp, File.data(), File.size(), IO_OUT);}else{dword UnWriteFileLength = File.size();//先将第一个簇写满Tmp.Cluster = LastCluster;Tmp.ByteOffset = ByteOffset;DiskIO(Tmp, File.data(), File.size(), IO_OUT);UnWriteFileLength -= Main_FAT32_DBR.SectorsPreCluster * SectorSize - ByteOffset;//先将第一个簇写满while (UnWriteFileLength)//每次写一个簇{LastCluster = Malloc_Cluster(LastCluster);//将文件的一部分写入dword WriteLength_ThisLoop;if (UnWriteFileLength <= Main_FAT32_DBR.SectorsPreCluster * SectorSize) {WriteLength_ThisLoop = UnWriteFileLength;}else {WriteLength_ThisLoop = Main_FAT32_DBR.SectorsPreCluster * SectorSize;}Tmp.Cluster = LastCluster;Tmp.ByteOffset = 0;DiskIO(Tmp, File.data(), File.size(), IO_OUT);//将文件的一部分写入UnWriteFileLength -= WriteLength_ThisLoop;}}//************************************追加写内容***************************************////************************************修改文件FCB,FCB块***************************************//Set_CatlogTime(FileCatlog, MODIFY);FileCatlog.FileLength += File.size();Modify_Catlog(FilePath, FileCatlog);if (FileCatlog.Attribute == CATLOG)this->SynCatlog(FileCatlog);//************************************修改文件FCB,FCB块***************************************//return 0;}else if (Type == 'w') {if (!flag) {Delete_File(FilePath);}//************************************如果文件已存在,则首先删除***************************************//ShortCatlog Useless;if (!this->Search_Catlog(FilePath, Useless))return FAT32_ERROR;//************************************如果文件已存在,则首先删除***************************************////************************************计算需要几个簇***************************************//dword ClusterNum = File.size() / (Main_FAT32_DBR.SectorsPreCluster * SectorSize);if (File.size() % (Main_FAT32_DBR.SectorsPreCluster * SectorSize))ClusterNum++;//************************************计算需要几个簇***************************************////************************************新建一个新的目录项,分配文件空间并将这个目录项追加到父目录文件中***************************************////*******设置新的目录项属性*******//CatlogBuffer.resize(sizeof(ShortCatlog));ShortCatlog* NewShortCatlog = (ShortCatlog*)CatlogBuffer.data();NewShortCatlog->Attribute = FileType;StrSplit(FilePath, '/', StrTmp);memcpy(NewShortCatlog, StrTmp.back().c_str(), StrTmp.back().length());Set_CatlogTime(*NewShortCatlog, CREATE);//创建一个文件时创建时间、访问时间和修改时间相同Set_CatlogTime(*NewShortCatlog, VISIT);Set_CatlogTime(*NewShortCatlog, MODIFY);dword FirstCluster = Malloc_Cluster(-1);NewShortCatlog->HighClusterNum = FirstCluster >> 16;NewShortCatlog->LowClusterNum = FirstCluster;NewShortCatlog->FileLength = File.size();//*******设置新的目录项属性*******////*******在上一级目录文件中注册这个FCB*******//this->GetFatherPath(FilePath, PartentPath);Write_File(PartentPath, CatlogBuffer, 'a');//*******在上一级目录文件中注册这个FCB*******////*******分配剩下的文件空间*******//for (int i = 1; i < ClusterNum; i++){FirstCluster = Malloc_Cluster(FirstCluster);}//*******分配剩下的文件空间*******////************************************新建一个新的目录项并将这个目录项追加到父目录文件中***************************************////************************************向分配好的文件空间中写入文件内容***************************************//FirstCluster = (NewShortCatlog->HighClusterNum << 16) + NewShortCatlog->LowClusterNum;for (int i = 0; true; i++){ClusterAddress Tmp;Tmp.Cluster = FirstCluster;Tmp.ByteOffset = 0;if (FAT1[FirstCluster] != END_CLUSTER_FLAG) {DiskIO(Tmp, File.data() + i * Main_FAT32_DBR.SectorsPreCluster * SectorSize, Main_FAT32_DBR.SectorsPreCluster * SectorSize, IO_OUT);}else {DiskIO(Tmp, File.data() + i * Main_FAT32_DBR.SectorsPreCluster * SectorSize, NewShortCatlog->FileLength % (Main_FAT32_DBR.SectorsPreCluster * SectorSize), IO_OUT);break;}FirstCluster = FAT1[FirstCluster];}//************************************向分配好的文件空间中写入文件内容***************************************//return 0;}return FAT32_ERROR;
}
int FAT32::KeRead_File(ShortCatlog Catlog, Vector_Byte& Buffer) {Buffer.resize(Catlog.FileLength);ClusterAddress tmp;tmp.Cluster = (Catlog.HighClusterNum << 16) + Catlog.LowClusterNum;tmp.ByteOffset = 0;dword i = 0;qword CluSize = Main_FAT32_DBR.SectorsPreCluster * (qword)SectorSize;for (i = 0; i < Catlog.FileLength / CluSize; i++){if (DiskIO(tmp, Buffer.data() + i * CluSize, CluSize, IO_IN))return FAT32_ERROR;tmp.Cluster = FAT1[tmp.Cluster];}if (DiskIO(tmp, Buffer.data() + i * CluSize, Catlog.FileLength % CluSize, IO_IN))return FAT32_ERROR;return 0;
}
int FAT32::Delete_File(std::string FilePath) {ClusterAddress CatlogAddr, FatherCatlogAddr;std::string FatherPath;this->GetFatherPath(FilePath, FatherPath);if (Search_Catlog_Addr(FatherPath, FatherCatlogAddr))return FAT32_ERROR;if (Search_Catlog_Addr(FilePath, CatlogAddr))return FAT32_ERROR;if (KeDelete_File(CatlogAddr, FatherCatlogAddr))return FAT32_ERROR;return 0;
}
int FAT32::KeDelete_File(ClusterAddress& CatlogAddr, ClusterAddress& FatherCatlogAddr) {ShortCatlog Catlog;DiskIO(CatlogAddr, (byte*)&Catlog, sizeof(ShortCatlog), IO_IN);Vector_Byte CatlogFile;//******************如果是目录文件(对应文件夹)的话,递归删除文件夹下的所有文件********************//if (Catlog.Attribute == CATLOG) {KeRead_File(Catlog, CatlogFile);ShortCatlog* SubCatlog = NULL;for (int i = 0; i < Catlog.FileLength; i += sizeof(ShortCatlog)){SubCatlog = (ShortCatlog*)((char*)CatlogFile.data() + i);ClusterAddress SubCatlogAddr;FileAddr_To_LogicCluster(*SubCatlog, i, SubCatlogAddr);if (SubCatlog->Attribute == CATLOG) {KeDelete_File(SubCatlogAddr, CatlogAddr);}DeleteFile_Catlog(SubCatlogAddr, CatlogAddr);}}//******************如果是目录文件(对应文件夹)的话,递归删除文件夹下的所有文件********************//DeleteFile_Catlog(CatlogAddr, FatherCatlogAddr);return 0;
}
int FAT32::DeleteFile_Catlog(ClusterAddress& CatlogAddr, ClusterAddress& FatherCatlogAddr) {std::vector<dword> FreeCluster;ShortCatlog Catlog;DiskIO(CatlogAddr, (byte*)&Catlog, sizeof(ShortCatlog), IO_IN);//********************************释放文件所占的空间************************************dword ClusterPtr = (Catlog.HighClusterNum << 16) + Catlog.LowClusterNum;while (ClusterPtr != END_CLUSTER_FLAG){dword NextClusterPtr = FAT1[ClusterPtr];FreeCluster.push_back(ClusterPtr);FAT1[ClusterPtr] = 0;ClusterPtr = NextClusterPtr;}std::sort(FreeCluster.begin(), FreeCluster.end());Main_FSINFO.UnusedClusterNum += FreeCluster.size();Main_FSINFO.FirstUnusedCluster = FreeCluster.back();//********************************释放文件所占的空间************************************//********************************删除文件目录项************************************ShortCatlog FatherCatlog;dword PointCatlogFileOffset;if (!FatherCatlogAddr.Cluster) {//规定Cluster==0时地址对应的为虚拟根目录文件的FCBFatherCatlog = RootCatlog;}else {DiskIO(FatherCatlogAddr, (byte*)&FatherCatlog, sizeof(ShortCatlog), IO_IN);}//****读取上层目录文件并查找需要删除的FCB位于文件内的偏移*********//Vector_Byte FatherCatlogFile;KeRead_File(FatherCatlog, FatherCatlogFile);ShortCatlog* CatlogPtr;dword i = 0;for (dword loop = 0; loop < FatherCatlogFile.size(); loop += sizeof(ShortCatlog)){CatlogPtr = (ShortCatlog*)(FatherCatlogFile.data() + loop);if (*CatlogPtr == Catlog) {i = loop;}if (CatlogPtr->FileName[0] == '.' && CatlogPtr->FileName[1] != '.')PointCatlogFileOffset = loop;}//****读取上层目录文件并查找需要删除的FCB位于文件内的偏移*********////****将剩余的目录项前移以填补空白********//dword SheftNum = (FatherCatlog.FileLength - i - sizeof(ShortCatlog)) / sizeof(ShortCatlog);ClusterAddress PreAddr = CatlogAddr, Addr;Addr = PreAddr;ShortCatlog Tmp;for (i = 0; i < SheftNum; i++){Addr.ByteOffset += sizeof(ShortCatlog);if (Addr.ByteOffset >= Main_FAT32_DBR.SectorsPreCluster * SectorSize) {Addr.ByteOffset -= Main_FAT32_DBR.SectorsPreCluster * SectorSize;Addr.Cluster = FAT1[Addr.Cluster];}DiskIO(Addr, &Tmp, sizeof(ShortCatlog), IO_IN);DiskIO(PreAddr, &Tmp, sizeof(ShortCatlog), IO_OUT);PreAddr = Addr;}memset(&Tmp, 0, sizeof(ShortCatlog));DiskIO(Addr, &Tmp, sizeof(ShortCatlog), IO_OUT);//****将剩余的目录项前移以填补空白********////****将父目录文件的FCB进行修改********//FatherCatlog.FileLength -= sizeof(ShortCatlog);this->Set_CatlogTime(FatherCatlog, MODIFY);DiskIO(FatherCatlogAddr, &FatherCatlog, sizeof(ShortCatlog), IO_OUT);ClusterAddress PointCatlogAddr;this->FileAddr_To_LogicCluster(FatherCatlog, PointCatlogFileOffset, PointCatlogAddr);FatherCatlog.FileName[0] = '.';DiskIO(PointCatlogAddr, &FatherCatlog, sizeof(ShortCatlog), IO_OUT);//****将父目录文件的FCB进行修改********////********************************删除文件目录项************************************return 0;
}
int FAT32::FileAddr_To_LogicCluster(ShortCatlog& FileCatlog, dword ByteOffset, ClusterAddress& Result) {dword BaseCluster = (FileCatlog.HighClusterNum << 16) + FileCatlog.LowClusterNum;Result.Cluster = BaseCluster + (ByteOffset / (Main_FAT32_DBR.SectorPre_FAT * SectorSize));Result.ByteOffset = ByteOffset % (Main_FAT32_DBR.SectorPre_FAT * SectorSize);return 0;
}
int FAT32::Set_CatlogTime(ShortCatlog& Catlog, int type) {SYSTEMTIME CurrentTime;GetLocalTime(&CurrentTime);dword DateTmp, TimeTmp;DateTmp = (CurrentTime.wYear & 0b1111111 - 1980) << 9;DateTmp ^= (CurrentTime.wMonth & 0b1111) << 5;DateTmp ^= (CurrentTime.wDay & 0b11111);TimeTmp = (CurrentTime.wHour & 0b11111) << 11;TimeTmp ^= CurrentTime.wMinute & 0b111111 << 5;TimeTmp ^= (CurrentTime.wSecond / 2) & 0b11111;switch (type){case CREATE:Catlog.FileCreateDate = DateTmp;Catlog.FileCreateTime = TimeTmp;Catlog.Tenmillisecond = CurrentTime.wMilliseconds & 0b11111111;break;case MODIFY:Catlog.LastModifyDate = DateTmp;Catlog.LastModifyTime = TimeTmp;break;case VISIT:Catlog.FileLastVisitDate = DateTmp;break;default:break;}return 0;
}
int FAT32::StrSplit(std::string String, char pattern, Vector_Str& StrVector) {int start = 0, end = 0;for (int i = 0; i < String.length(); i++){if (String[i] == pattern) {end = i;if (start != end)StrVector.push_back(String.substr(start, end - start));start = end + 1;}}StrVector.push_back(String.substr(start, String.length()));//最后一个字符串return 0;
}
int FAT32::Search_Catlog(std::string FilePath, ShortCatlog& Found_Catlog) {ClusterAddress CatlogAddr;if (Search_Catlog_Addr(FilePath, CatlogAddr))return FAT32_ERROR;if (!CatlogAddr.Cluster)Found_Catlog = RootCatlog;elseDiskIO(CatlogAddr, (byte*)&Found_Catlog, sizeof(ShortCatlog), IO_IN);return 0;
}
int FAT32::Search_ACatlogFile(ShortCatlog Catlog, std::string File, ClusterAddress& Result) {Vector_Byte Buffer;ShortCatlog* CatlogPtr;Result.Cluster = -1;KeRead_File(Catlog, Buffer);for (int i = 0; i < Catlog.FileLength; i += sizeof(ShortCatlog)){CatlogPtr = (ShortCatlog*)((byte*)Buffer.data() + i);std::string tmp((char*)CatlogPtr);//仅仅判断了目录项的名字,无法区分目录项的属性if (File == tmp) {Result.Cluster = (Catlog.HighClusterNum << 16) + Catlog.LowClusterNum;Result.ByteOffset = i;return 0;}}return FAT32_ERROR;
}
int FAT32::Search_Catlog_Addr(std::string FilePath, ClusterAddress& CatlogAddr) {ShortCatlog Found_Catlog;Vector_Str AbsolutePath;Vector_Str tmp;int ReadIndex = 0;if (FilePath[0] != ':') {//约定路径名第一个为:表示绝对路径this->RealtivePath_AbsolutePath(this->CurrentPath, AbsolutePath, FilePath);}else {StrSplit(FilePath, '/', AbsolutePath);}//*****如果要搜索根目录FCB的话,返回阅读的0簇地址*****//if (AbsolutePath.size() == 1) {CatlogAddr.Cluster = 0;return 0;}//*****如果要搜索根目录FCB的话,返回阅读的0簇地址*****//if (Search_ACatlogFile(RootCatlog, AbsolutePath[1], CatlogAddr))return FAT32_ERROR;DiskIO(CatlogAddr, &Found_Catlog, sizeof(ShortCatlog), IO_IN);for (int i = 2; i < AbsolutePath.size(); i++){if (this->Search_ACatlogFile(Found_Catlog, AbsolutePath[1], CatlogAddr))return FAT32_ERROR;DiskIO(CatlogAddr, &Found_Catlog, sizeof(ShortCatlog), IO_IN);}return 0;
}
int FAT32::CreateACatlog(std::string CatlogPath) {ShortCatlog CatlogFileCatlog;if (!this->Search_Catlog(CatlogPath, CatlogFileCatlog))return FAT32_ERROR;std::string FatherPath;ShortCatlog FatherCatlog;Vector_Str Tmp;StrSplit(CatlogPath, '/', Tmp);this->GetFatherPath(CatlogPath, FatherPath);this->Search_Catlog(FatherPath, FatherCatlog);if (Tmp.back().length() >= 11)return FAT32_NAME_TOOLONG;memset(&FatherCatlog, 0, 11);FatherCatlog.FileName[0] = '.';FatherCatlog.FileName[1] = '.';Vector_Byte TmpByte;TmpByte.resize(sizeof(ShortCatlog));memcpy(TmpByte.data(), &FatherCatlog, sizeof(ShortCatlog));this->Write_File(CatlogPath, TmpByte, 'w', CATLOG);this->Search_Catlog(CatlogPath, CatlogFileCatlog);memset(&CatlogFileCatlog, 0, 11);CatlogFileCatlog.FileName[0] = '.';memcpy(TmpByte.data(), &CatlogFileCatlog, sizeof(ShortCatlog));this->Write_File(CatlogPath, TmpByte, 'a');return 0;
}
int FAT32::RealtivePath_AbsolutePath(Vector_Str& CurrentPath, Vector_Str& AbsolutePath, std::string TransferPath) {Vector_Str tmp;StrSplit(TransferPath, '/', tmp);AbsolutePath = CurrentPath;for (int i = 0; i < tmp.size(); i++){if (tmp[i] == ".")continue;else if (tmp[i] == "..") {if (AbsolutePath.size() == 1)//此时已经到达根目录continue;elseAbsolutePath.pop_back();}else {AbsolutePath.push_back(tmp[i]);}}return 0;
}

GPT.h

#include<string>
#include"type.h"
#include"CRC32.h"
#include<map>
#pragma once#pragma pack(1)//*******************************分区表中的类型GUID,属性,签名************************************************////引导分区
constexpr char SystemParitionSignature[] = "SystemParition";//分区签名
constexpr qword SystemAttribute = 0x8000000000000001;//分区表中的系统属性
constexpr char  SystemParitionTypeGuid[] = { 0x28, 0x73, 0x2A, 0xC1, 0x1F, 0xF8, 0xD2, 0x11, 0xBA, 0x4B, 0x00, 0xA0, 0xC9, 0x3E, 0xC9, 0x3B };//分区GUID
//引导分区//数据分区
constexpr char DataParitionSignature[] = "DataParition";
constexpr qword DataAttribute = 0;
constexpr char  DataParitionTypeGuid[] =  {0xA2, 0xA0, 0xD0, 0xEB, 0xE5, 0xB9, 0x33, 0x44, 0x87, 0xC0, 0x68, 0xB6, 0xB7, 0x26, 0x99, 0xC7 };
//*******************************分区表中的类型GUID,属性,签名************************************************////数据分区
//分区表中的类型GUID,属性,签名//*******************************MBR分区的相关结构************************************************//
typedef struct MBR_DP //MBR方案下的分区表项
{byte GuideFlag;byte HeadBegin;word sector_cylinder_begin;byte PartitionFlag;byte HeadEnd;word sector_cylinder_end;dword StartSector;dword PartitionSectorNum;
}MBR_DP;
struct MBR
{byte Reversed[446];//在GPT分区中MBR引导代码无所谓MBR_DP DPT[4];short signature;MBR();
};//*******************************MBR分区的相关结构************************************************////*******************************GPT分区的相关结构************************************************////GPT表头
struct GPT_Header
{char signautre[8];dword Version;dword GPT_HeaderLength;//分区表头长度,该字段用于扩展用,在目前的规范下值为0x5cdword CRC32_GPTHeader;dword Reversed;qword Main_GPT_Header_LAB;qword Recover_GPT_Header_LAB;qword DataLAB_Begin;qword DataLAB_End;byte  Disk_GUID[16];qword GPTLAB_Begin;dword GPT_Length;dword GP_Length;dword CRC32_GPT;byte  Reversed2[420];GPT_Header();
};
//GPT表头//GPT方案下的分区表项
struct GlobalPartiton
{byte PartitonType_GUID[16];byte Partition_GUID[16];qword BeginLBA;qword EndLBA;qword Attribute;byte  PartitionName[72];
};constexpr int SectorSize = 512;//一个扇区大小,默认为512B
constexpr int GptLength = 128;//当前规范下的GPT长度
constexpr unsigned int GpLength = sizeof(GlobalPartiton);//分区表项长度
constexpr unsigned int GptHeaderLength = sizeof(GPT_Header);//分区表头长度
//*******************************GPT分区的相关结构************************************************////*******************************GPT的返回值的含义************************************************//constexpr unsigned int NOVirtualDisk = 1;
constexpr unsigned int NoEnoughCapacity = 2;
constexpr unsigned int OK = 0;
constexpr unsigned int OpenFileFiled = 3;
//*******************************GPT的返回值的含义************************************************//class GPT {
protected:byte* Disk;FILE* VirtualDisk;GPT_Header              MainGptHeader;GlobalPartiton        MainGpt[128];GPT_Header             BackupGptHeader;GlobalPartiton          BackupGpt[128];dword UnusedSectorNum;dword   FirestUnusedSector;dword    RealGptLength;void CreateAParition(dword ParitionCapacity, int SystemParition = 0);int virtual SetParition(dword FirstLAB,dword EndLAB, std::string ParitionName);
public:static dword Random();int CreateAVirtualDisk(dword DiskCapacity, std::string DiskFilePath);int ReadVirtualDisk(std::string VirtualDiskPath);int CreateAParition_FAT32(std::string ParitionName,dword ParitionCapacity,int SystemParition=0);~GPT();
};
#pragma pack()

GPT.cpp

#include"GPT.h"
#include<iostream>
#include<stdio.h>
#pragma warning(disable:4996)
//创建一个新的虚拟硬盘时,写入一个MBR分区和GPT头;
MBR::MBR() {memset(this, 0, sizeof(MBR));DPT[0].PartitionFlag = 0xee;signature = 0xaa55;
}
dword GPT::Random() {__asm {rdrand eax;}
}
GPT_Header::GPT_Header() {memset(this, 0, GptHeaderLength);char tmp[9] = "EFI PART";memcpy(signautre, tmp, 8);Version = 0x01;GPT_HeaderLength = 0x5c;Main_GPT_Header_LAB = 1;//GPT表头为1DataLAB_Begin = 34;GPTLAB_Begin = 2;GPT_Length = 128;GP_Length = 128;
}
int GPT::CreateAVirtualDisk(dword DiskCapacity,std::string DiskFilePath) {this->Disk = new byte[SectorSize*34];//Windows下的GPT最多能有128个分区,每个分区128bytes,一共32个扇区,再加上1个保留的MBR扇区,1个GPT头扇区,一共34个扇区memset(Disk, 0, SectorSize * 34);//写入一个MBR保留分区,0扇区MBR* GPT_MBR=new MBR;GPT_MBR->signature = 0xaa55;GPT_MBR->DPT[0].PartitionFlag = 0xee;memcpy(Disk, GPT_MBR,sizeof(*GPT_MBR));delete GPT_MBR;//写入一个MBR保留分区,0扇区//写入一个GPT头GPT_Header* gpt_header = new GPT_Header;gpt_header->Recover_GPT_Header_LAB = (DiskCapacity << 1) - 1;gpt_header->DataLAB_End = (DiskCapacity << 1) - (1 + 32) - 1;//一个GPT表头,一个分区表for (int i = 0; i < 4; i++){*(dword*)(gpt_header->Disk_GUID + i * 4) = Random();}gpt_header->CRC32_GPT = CRC32::crc32(Disk + sizeof(MBR) + GptHeaderLength, GpLength * 128);gpt_header->CRC32_GPTHeader = CRC32::crc32((const unsigned char *)gpt_header, GptHeaderLength-420);//只校验有用的92字节memcpy(Disk + sizeof(MBR), gpt_header, GptHeaderLength);//写入一个GPT头FILE* DiskFile;fopen_s(&DiskFile,DiskFilePath.c_str(), "wb");if (!DiskFile)return OpenFileFiled;fwrite(Disk, SectorSize, 34, DiskFile);char tmp = 0x00;//填入空闲扇区for (qword i = 0; i < ((DiskCapacity << 1) - 34 - 1) *SectorSize; i++){fwrite(&tmp, 1, 1, DiskFile);}//填入空闲扇区//填写备份分区表gpt_header->Main_GPT_Header_LAB = (DiskCapacity << 1) - 1;gpt_header->Recover_GPT_Header_LAB = 1;memset(&gpt_header->CRC32_GPTHeader, 0, sizeof(gpt_header->CRC32_GPTHeader));gpt_header->CRC32_GPTHeader = CRC32::crc32((const unsigned char*)gpt_header, GptHeaderLength - 420);//只校验有用的92字节fwrite(gpt_header, GptHeaderLength, 1, DiskFile);fclose(DiskFile);delete gpt_header;delete Disk;return OK;
}
int GPT::ReadVirtualDisk(std::string VirtualDiskPath) {fopen_s(&this->VirtualDisk, VirtualDiskPath.c_str(), "rb+");if (!this->VirtualDisk){return NOVirtualDisk;}//读取虚拟硬盘的基本信息fseek(this->VirtualDisk, sizeof(MBR), SEEK_SET);fread(&MainGptHeader, GptHeaderLength, 1, VirtualDisk);fread(MainGpt, GpLength, 128, VirtualDisk);UnusedSectorNum = MainGptHeader.DataLAB_End - MainGptHeader.DataLAB_Begin;int i;for (i = 0; i < 128 && MainGpt[i].PartitonType_GUID[0]; i++){UnusedSectorNum -= MainGpt[i].EndLBA - MainGpt[i].BeginLBA;}RealGptLength = i;if (!i) FirestUnusedSector = MainGptHeader.DataLAB_Begin;  //当GPT表中无分区时,第一个未使用的扇区为GPT头中规定的第一个数据扇区else{FirestUnusedSector = MainGpt[i - 1].EndLBA + 1;}fseek(VirtualDisk, 0 - (GptHeaderLength + GpLength * 128), SEEK_END);fread(&BackupGpt, GpLength, 128, VirtualDisk);fread(&BackupGptHeader, GptHeaderLength, 1, VirtualDisk);//读取虚拟硬盘的基本信息
}
void GPT::CreateAParition(dword ParitionCapacity, int SystemParition) {//书写新的分区表GlobalPartiton& NewGp = MainGpt[RealGptLength];NewGp.BeginLBA = FirestUnusedSector;NewGp.EndLBA = FirestUnusedSector + (ParitionCapacity << 1) - 1;for (int i = 0; i < 4; i++){*(dword*)(NewGp.Partition_GUID+i*4) = Random();}if (SystemParition) {NewGp.Attribute = SystemAttribute;memcmp(&NewGp.PartitionName, SystemParitionSignature, sizeof(SystemParitionSignature));memcpy(&NewGp.PartitonType_GUID, SystemParitionTypeGuid, 16);}else {NewGp.Attribute = DataAttribute;memcmp(NewGp.PartitionName, DataParitionSignature, sizeof(DataParitionSignature));memcpy(NewGp.PartitonType_GUID, DataParitionTypeGuid, 16);}memcpy(&BackupGpt[RealGptLength] , &NewGp, GpLength);//书写新的分区表//更新GPT头//主GPTMainGptHeader.CRC32_GPT = CRC32::crc32((byte*)MainGpt, GpLength * GpLength);memset(&MainGptHeader.CRC32_GPTHeader, 0, sizeof(MainGptHeader.CRC32_GPTHeader));MainGptHeader.CRC32_GPTHeader = CRC32::crc32((byte*)&MainGptHeader, 92);//主GPT//备GPTBackupGptHeader.CRC32_GPT = CRC32::crc32((byte*)BackupGpt, GpLength * GpLength);memset(&BackupGptHeader.CRC32_GPTHeader, 0, sizeof(BackupGptHeader.CRC32_GPTHeader));BackupGptHeader.CRC32_GPTHeader = CRC32::crc32((byte*)&BackupGptHeader, 92);//备GPT//更新GPT头//写入虚拟硬盘文件fseek(VirtualDisk, sizeof(MBR), SEEK_SET);fwrite(&MainGptHeader, GptHeaderLength, 1, VirtualDisk);fwrite(MainGpt, GpLength, GptLength, VirtualDisk);fseek(VirtualDisk, 0 - (GptHeaderLength + GpLength * 128), SEEK_END);fwrite(BackupGpt, GpLength, GptLength, VirtualDisk);fwrite(&BackupGptHeader, GptHeaderLength, 1, VirtualDisk);//写入虚拟硬盘文件this->RealGptLength++;this->UnusedSectorNum -= ParitionCapacity << 1;this->FirestUnusedSector = MainGpt[RealGptLength - 1].EndLBA + 1;
}
int GPT::SetParition(dword FirstLAB, dword EndLAB, std::string ParitionName) {return 0;
}
int GPT::CreateAParition_FAT32(std::string ParitionName,dword ParitionCapacity, int SystemParition) {if (!VirtualDisk)return NOVirtualDisk;if (ParitionCapacity << 1 > UnusedSectorNum)return NoEnoughCapacity;CreateAParition(ParitionCapacity, SystemParition);return SetParition(MainGpt[RealGptLength-1].BeginLBA, MainGpt[RealGptLength - 1].EndLBA,ParitionName);
}
GPT::~GPT() {if (VirtualDisk)fclose(VirtualDisk);
}

type.h

#pragma once
typedef unsigned int dword;
typedef unsigned long long qword;
typedef unsigned short word;
typedef unsigned char byte;

  1. https://zhuanlan.zhihu.com/p/25281151,老狼,UEFI背后的历史 ↩︎

一个简单的硬盘管理器的实现暨南京邮电大学操作系统——实验四:简单文件系统模拟实验相关推荐

  1. Mac硬盘管理器:Paragon Hard Disk Manager for Mac

    Paragon Hard Disk Manager破解版是区分私人用户的理想的系统和数据管理方案,拥有可靠地备份和灵活的恢复功能,优化工具,完美分区所需要的一切.需要的朋友赶紧下载吧. paragon ...

  2. Unity实现简单技能冷却管理器

    前言 这段时间做技能系统,需要实现技能冷却.如果每个技能都写一段计时器代码的话重复性太高而且还不方便管理,于是根据使用到的功能写了个简单的冷却管理器统一处理. 实现 代码很简单,理解思路即可. usi ...

  3. 又一个短小精悍的软件包管理器-pnpm

    一个上班摸鱼的契机,看到了pnpm这个新名词,带着我的一丝好奇和一丝疑问,就想着了解一下. 什么是pnpm? pnpm是我们正常印象中的软件包管理器,类似的我们肯定用过淘宝的cnpm,无非是如何又全又 ...

  4. Multipass - 一个轻量虚拟机管理器

    Multipass 是一个轻量虚拟机管理器,是由 Ubuntu 运营公司 Canonical 所推出的开源项目.运行环境支持 Linux.Windows.macOS.在不同的操作系统上,使用的是不同的 ...

  5. MT管理器修改、添加、删除system分区里面的文件提示Read-only file system

    MT管理器修改.添加.删除system分区里面的文件提示Read-only file system错误 root完手机以后打开MT想修改.写入.删除system分区里面的文件,提示没有写权限,为只读文 ...

  6. android 剪贴板管理器,Clipper一个强大的剪贴板管理器为Android | MOS86

    最重要的Android手机具有非常好的复制/粘贴功能.您只需点击并按下单词或输入字段,将出现复制或粘贴选项.如果您需要的不只是基本的复制和粘贴功能,例如访问剪贴板历史记录?您将需要一个第三方应用程序来 ...

  7. 【Week9 作业】A - 咕咕东的目录管理器、B - 东东学打牌、C - 签到题,独立思考哈

    A - 咕咕东的目录管理器 题意: 咕咕东的雪梨电脑的操作系统在上个月受到宇宙射线的影响,时不时发生故障,他受不了了,想要写一个高效易用零bug的操作系统 -- 这工程量太大了,所以他定了一个小目标, ...

  8. VIM妙用及linux使用技巧(包括vim插件管理器、指令与键盘映射、打开多个文件、奇偶删除行、vim替换等)

    前言 vim 是个非常高效.非常好用的工具,很多人一旦开始使用 Vim 之后就再也无法自拔.然而, Vim 仍然有其自身缺陷,对于普通用户来说,很难在入门的时候就体会到Vim的所谓高效性,同时,为了展 ...

  9. 南京邮电大学操作系统实验四:简单文件系统模拟实验

    实验目的和要求 理解操作系统的文件系统组成以及基本原理,利用这些知识在内存中模拟一个FAT格式的文件系统,完成文件的创建和索引功能,实现以下命令接口: (1)新建文件,格式:mkfile filena ...

最新文章

  1. hibernate增删改查的标准范例
  2. 【LDA学习系列】Latent Dirichlet Allocation主题模型理解
  3. 堆积柱形图显示总数_送你一份堆积柱形图小点心,请收下~
  4. Cocoa Autolayout:内容拥抱与内容压缩阻力优先
  5. 阶段1 语言基础+高级_1-3-Java语言高级_05-异常与多线程_第1节 异常_13_自定义异常类...
  6. Practise 5.2测试与封装(黑白盒
  7. 2020 年TI 杯大学生电子设计竞赛-无人机
  8. cppcheck的安装与使用
  9. 请结合实例说明欧洲中世纪哥特式建筑的风格特点
  10. 报警c语言程序,PIC单片机警报声C程序
  11. 九宫怎么排列和使用_奇门遁甲九宫数字 九宫数字的排列和算法
  12. 萨尔大学计算机受限,【德国留学】留学受限专业是怎么回事?如何被受限专业录取?...
  13. 基于贝叶斯分类器的手写字判别
  14. 网络收音机!免费的成本和不间断的音乐
  15. 关于DDD领域驱动设计的理论知识收集汇总
  16. Ubuntu下利用Opencv进行点阵汉字的字模读取与显示
  17. 推荐系统衡量指标总结
  18. AutoCAD常见问题及解决
  19. 网络舆情分析软件工具汇总及功能作用详解
  20. 巨盾网游安全盾 2013

热门文章

  1. 杂谈(4)---比风水厉害100倍的惊人定律
  2. IDEA、AndroidStudio写代码辅助插件
  3. C# Microsoft.Office.Interop.Excel分组汇总
  4. iphone与iTunes同步应用程序时,保持iphone上的图标顺序
  5. 10.20美盘黄金独家指导解套,美原油白银td行情价格趋势分析
  6. Linux合入patch命令,Linux patch命令
  7. 擦地机器人排行榜_智能扫地机器人排行榜前十名
  8. linux c 多显卡编程,Linux 编程之GPU计算(转)
  9. maya删除锁定节点,清理无效节点,无法保存ma问题
  10. java静态代码块在什么时候会执行