FAT12文件系统镜像查看工具windows下的实现(ls、cat命令)

FAT12文件系统镜像查看工具windows下的实现(ls、cat命令)

简介

FAT12的这次project最终是要实现在linux上用nasm和C++联合编译。
本文代码是属于中间产品,是一开始在windows上编写时候的产品,所以某些地方会有一些bug,且有些混乱,除了这篇博客,我会再发一篇博客,将成品发出来。
本文代码实现了FAT12的读写,ls命令,ls -l path 命令和cat命令,可以实现读取超过512字节的文件。

构思简介

首先通过FAT12的特性进行其中内容的读取,依次读取boot->根目录->数据区的数据。通过读取FAT表,完成超过512字节文件的读取。在输入命令之前,完成所有的读取,并将其用链表Node装起来,接着不同的命令通过root根节点访问,完成相应的打印。
具体的可以看代码注释。

附加链接

FA12文件系统相关链接1:
https://blog.csdn.net/judyge/article/details/52373751
FA12文件系统相关链接2:
https://blog.csdn.net/qq_39654127/article/details/88429461#main-toc
借鉴代码,在原代码基础上进行改进,增加了新功能,改掉了FAT值的bug:
https://blog.csdn.net/yxc135/article/details/8769086
FAT12文件系统镜像查看工具linux下的实现(nasm、g++联合编译)
https://blog.csdn.net/richardzzzZ/article/details/103058903

最终实现截图

这是最后在linux上实现的截图,所以额外包括了颜色和ls+path的命令
在这里插入图片描述在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include<iostream>
#include<string>
#include<sstream>
#include<vector>


using namespace std;


typedef unsigned char u8;	//1字节
typedef unsigned short u16;	//2字节
typedef unsigned int u32;	//4字节


int  BytsPerSec;	//每扇区字节数
int  SecPerClus;	//每簇扇区数
int  RsvdSecCnt;	//Boot记录占用的扇区数
int  NumFATs;	//FAT表个数
int  RootEntCnt;	//根目录最大文件数
int  FATSz;	//FAT扇区数


#pragma pack (1) /*指定按1字节对齐*/

//偏移11个字节
struct BPB {
	u16  BPB_BytsPerSec;	//每扇区字节数
	u8   BPB_SecPerClus;	//每簇扇区数
	u16  BPB_RsvdSecCnt;	//Boot记录占用的扇区数
	u8   BPB_NumFATs;	//FAT表个数
	u16  BPB_RootEntCnt;	//根目录最大文件数
	u16  BPB_TotSec16;
	u8   BPB_Media;
	u16  BPB_FATSz16;	//FAT扇区数
	u16  BPB_SecPerTrk;
	u16  BPB_NumHeads;
	u32  BPB_HiddSec;
	u32  BPB_TotSec32;	//如果BPB_FATSz16为0,该值为FAT扇区数
};
//BPB至此结束,长度25字节

//根目录条目
struct RootEntry {
	char DIR_Name[11];
	u8   DIR_Attr;		//文件属性
	char reserved[10];
	u16  DIR_WrtTime;
	u16  DIR_WrtDate;
	u16  DIR_FstClus;	//开始簇号
	u32  DIR_FileSize;
};
//根目录条目结束,32字节

#pragma pack () /*取消指定对齐,恢复缺省对齐*/

extern"C" {

}


class Node {//链表的node类
public:
	string name;		//名字
	vector<Node *> next;	//下一级目录的Node数组
	string path;			//记录path,便于打印操作
	u32 FileSize;			//文件大小
	bool isfile = false;		//是文件还是目录
	bool isval = true;			//用于标记是否是.和..
	int dir_count = 0;			//记录下一级有多少目录
	int file_count = 0;			//记录下一级有多少文件
	char *content = new char[10000]{ NULL };		//存放文件内容
};



void fillBPB(FILE * fat12, struct BPB* bpb_ptr);	//载入BPB
void ReadFiles(FILE * fat12, struct RootEntry* rootEntry_ptr,Node *father);	//打印文件名,这个函数在打印目录时会调用下面的printChildren
void readChildren(FILE * fat12,  int startClus, Node *father);	//打印目录及目录下子文件名
int  getFATValue(FILE * fat12, int num);	//读取FAT表,获取下一个簇号
void printLS(Node *root);      //打印ls命令
void creatNode(Node *p,Node *father);			//创建.和..node,但是并没有实际意义,没有实现cd命令
void printLS_L(Node *root);					//打印 ls -l命令
void printLS_LWithPath(Node *root, string path, int & exist);			//打印ls -l 地址  命令
void getContent(FILE * fat12, int startClus,Node* son);				//初始化链表时用来获取文件中的信息
void printCat(Node *root, string path, int & exist);				//打印cat命令



int main() {
	FILE* fat12;
	fat12 = fopen("a.img", "rb");	//打开FAT12的映像文件

	struct BPB bpb;
	struct BPB* bpb_ptr = &bpb;

	//创建根节点
	Node *root = new Node();
	root->name = "";
	root->path = "/";

	//载入BPB
	fillBPB(fat12, bpb_ptr);

	//初始化各个全局变量
	BytsPerSec = bpb_ptr->BPB_BytsPerSec;
	SecPerClus = bpb_ptr->BPB_SecPerClus;
	RsvdSecCnt = bpb_ptr->BPB_RsvdSecCnt;
	NumFATs = bpb_ptr->BPB_NumFATs;
	RootEntCnt = bpb_ptr->BPB_RootEntCnt;
	if (bpb_ptr->BPB_FATSz16 != 0) {
		FATSz = bpb_ptr->BPB_FATSz16;
	}
	else {
		FATSz = bpb_ptr->BPB_TotSec32;
	}

	struct RootEntry rootEntry;
	struct RootEntry* rootEntry_ptr = &rootEntry;

	ReadFiles(fat12, rootEntry_ptr, root);//*****构建文件链表

	while (true) {   //解析输入的命令
		cout << "> ";
		string input;
		getline(cin, input);
		if (input.compare("exit")==0) {
			//退出
			cout << "Bye!" << endl;
			fclose(fat12);
			system("Pause");
			return 0;
		}
		else if(input.length()<2){
			//命令长度小于2,无效命令
			cout << "error: command not found!"<<endl;
		}
		else if (input.substr(0,2).compare("ls")==0) {
			//ls命令
			if (input.length() == 2) {
				// 命令"ls"
				printLS(root);
			}
			else {
				//命令"ls /any -l - -llllll"
				bool error = false;
				bool hasL = false;
				bool pathExist = false;
				char path[100] = { NULL };
				bool isL = false;
				for (int i = 2; i < input.length(); i++) {
					if (input[i] != ' '&&input[i] != '-') {
						//记录path
						if (pathExist) {
							//第二次出现路径
							cout << "error: The parameter of ls is wrong!" << endl;
							error = true;
							break;
						}
						else {
							//第一次出现
							pathExist = true;
							char *p = path;
							while (i < input.length() && input[i] != ' ') {
								*p = input[i];
								i++;
								p++;
							}
						}
					}
					else if (input[i] == '-') {
						//出现“-”
						if (i == input.length() - 1||input[i + 1] != 'l') {
							// - -asd -wqeq
							error = true;
							cout << "error: The parameter of ls is not found!" << endl;
							break;
						}
						else {
							i++;
							while (i < input.length() && input[i] != ' ') {
								if (input[i] != 'l') {
									error = true;
									cout << "error: The parameter of ls is not found!"<<endl;
									break;
								}
								i++;
							}
							hasL = true;
						}
					}
				}
				if (hasL == false && pathExist == true && error == false) {
					//有路径没有L
					error = true;
					cout << "error: The parameter of ls is not found!" << endl;
					continue;
				}
				if (error) {
					//出现错误继续读取命令
					continue;
				}
				//正常运行
				string path_s = path;
				if (hasL && !pathExist) {
					//ls -l   ls -lllllll
					printLS_L(root);
				}
				else if (hasL&&pathExist) {
					if (path_s[0] != '/') {
						path_s = "/" + path_s;
					}
					if (path_s[path_s.length() - 1] != '/') {
						path_s += '/';
					}
					int exist = 0;//0不存在 ,2路径错误,1成功
					printLS_LWithPath(root, path_s,exist);
					if (exist==0) {
						cout << "error: no such dir!"<< endl;
						continue;
					}
					else if (exist == 2) {
						cout << "error:Not a dir!" << endl;
						continue;
					}
				}

			}
		}
		else if (input.length() <= 3) {
			//命令长度小于3,无效命令
			cout << "error: command not found!" << endl;
		}
		else if (input.substr(0, 3).compare("cat") == 0) {
			//cat命令
			char path[100] = { NULL };
			bool pathExist = false;
			bool error = false;
			for (int i = 3; i < input.length(); i++) {
				if (input[i] != ' ') {
					//记录path
					if (pathExist) {
						//第二次出现路径
						cout << "error: The parameter of ls is wrong!" << endl;
						error = true;
						break;
					}
					else {
						//第一次出现
						pathExist = true;
						char *p = path;
						while (i < input.length() && input[i] != ' ') {
							*p = input[i];
							i++;
							p++;
						}
					}
				}
			}
			if (!pathExist || error) {
				cout << "error: The parameter of ls is wrong!" << endl;
				continue;
			}
			int exist =0;
			string path_s = path;
			if (path_s[0] != '/') {
				path_s = "/" + path_s;
			}
			if (path_s[path_s.length() - 1] != '/') {
				path_s += '/';
			}
			printCat(root, path_s, exist);   //执行cat
			if (exist == 0) {
				cout << "error: no such file!" << endl;
				continue;
			}
			else if (exist == 2) {
				cout << "error:file can not open!" << endl;
				continue;
			}
		}
		else {
			cout << "error: command not found!" << endl;
		}

	}




}



void fillBPB(FILE* fat12, struct BPB* bpb_ptr) {  //读取boot信息
	int check;

	//BPB从偏移11个字节处开始
	check = fseek(fat12, 11, SEEK_SET);
	if (check == -1)
		printf("fseek in fillBPB failed!");

	//BPB长度为25字节
	check = fread(bpb_ptr, 1, 25, fat12);
	if (check != 25)
		printf("fread in fillBPB failed!");
}



void ReadFiles(FILE * fat12, struct RootEntry* rootEntry_ptr , Node *father) {
	int base = (RsvdSecCnt + NumFATs * FATSz) * BytsPerSec;	//根目录首字节的偏移数
	int check;
	char realName[12];	//暂存文件名

	//依次处理根目录中的各个条目
	int i;
	for (i = 0; i < RootEntCnt; i++) {

		check = fseek(fat12, base, SEEK_SET);
		if (check == -1)
			printf("fseek in printFiles failed!");

		check = fread(rootEntry_ptr, 1, 32, fat12);
		if (check != 32)
			printf("fread in printFiles failed!");

		base += 32;

		if (rootEntry_ptr->DIR_Name[0] == '\0') continue;	//空条目不输出

		//过滤非目标文件
		int j;
		int boolean = 0;
		for (j = 0; j < 11; j++) {
			if (!(((rootEntry_ptr->DIR_Name[j] >= 48) && (rootEntry_ptr->DIR_Name[j] <= 57)) ||
				((rootEntry_ptr->DIR_Name[j] >= 65) && (rootEntry_ptr->DIR_Name[j] <= 90)) ||
				((rootEntry_ptr->DIR_Name[j] >= 97) && (rootEntry_ptr->DIR_Name[j] <= 122)) ||
				(rootEntry_ptr->DIR_Name[j] == ' '))) {
				boolean = 1;	//非英文及数字、空格
				break;
			}
		}
		if (boolean == 1) continue;	//非目标文件不输出

		int k;   //名字的处理
		if ((rootEntry_ptr->DIR_Attr & 0x10) == 0) {
			//文件
			int tempLong = -1;
			for (k = 0; k < 11; k++) {
				if (rootEntry_ptr->DIR_Name[k] != ' ') {
					tempLong++;
					realName[tempLong] = rootEntry_ptr->DIR_Name[k];
				}
				else {
					tempLong++;
					realName[tempLong] = '.';
					while (rootEntry_ptr->DIR_Name[k] == ' ') k++;
					k--;
				}
			}
			tempLong++;
			realName[tempLong] = '\0';	//到此为止,把文件名提取出来放到了realName里
			Node *son = new Node();   //新建该文件的节点
			father->next.push_back(son);  //存到father的next数组中
			son->name = realName;
			son->FileSize = rootEntry_ptr->DIR_FileSize;
			son->isfile = true;
			son->path =father->path+realName+ "/";
			father->file_count++;
			getContent(fat12, rootEntry_ptr->DIR_FstClus,son);//读取文件的内容
		}
		else {
			//目录
			int tempLong = -1;
			for (k = 0; k < 11; k++) {
				if (rootEntry_ptr->DIR_Name[k] != ' ') {
					tempLong++;
					realName[tempLong] = rootEntry_ptr->DIR_Name[k];
				}
				else {
					tempLong++;
					realName[tempLong] = '\0';
					break;
				}
			}	//到此为止,把目录名提取出来放到了realName
			Node *son = new Node();
			father->next.push_back(son);
			son->name = realName;
			son->path = father->path + realName + "/";
			father->dir_count++;
			creatNode(son, father);
			//输出目录及子文件
			readChildren(fat12, rootEntry_ptr->DIR_FstClus,son);  //读取目录的内容
		}
	}
}



void readChildren(FILE * fat12,  int startClus,  Node *father) {
	//数据区的第一个簇(即2号簇)的偏移字节
	int dataBase = BytsPerSec * (RsvdSecCnt + FATSz * NumFATs + (RootEntCnt * 32 + BytsPerSec - 1) / BytsPerSec);

	int currentClus = startClus;
	int value = 0;//value用来查看是否存在多个簇(查FAT表)
	while (value < 0xFF8) {
		value = getFATValue(fat12, currentClus);//查FAT表获取下一个簇号
		if (value == 0xFF7) {
			printf("坏簇,读取失败!\n");
			break;
		}


		int startByte = dataBase + (currentClus - 2)*SecPerClus*BytsPerSec;
		int check;

		int count = SecPerClus * BytsPerSec;	//每簇的字节数
		int loop = 0;
		while (loop < count) {
			int i;

			RootEntry sonEntry;//读取目录项
			RootEntry *sonEntryP = &sonEntry;
			check = fseek(fat12, startByte + loop, SEEK_SET);
			if (check == -1)
				printf("fseek in printFiles failed!");

			check = fread(sonEntryP, 1, 32, fat12);
			if (check != 32)
				printf("fread in printFiles failed!");//读取完毕
			loop += 32;
			if (sonEntryP->DIR_Name[0] == '\0') {
				continue;
			}	//空条目不输出
			//过滤非目标文件
			int j;
			int boolean = 0;
			for (j = 0; j <  11; j++) {
				if (!(((sonEntryP->DIR_Name[j] >= 48) && (sonEntryP->DIR_Name[j] <= 57)) ||
					((sonEntryP->DIR_Name[j] >= 65) && (sonEntryP->DIR_Name[j] <= 90)) ||
					((sonEntryP->DIR_Name[j] >= 97) && (sonEntryP->DIR_Name[j] <= 122)) ||
					(sonEntryP->DIR_Name[j] == ' '))) {
					boolean = 1;	//非英文及数字、空格
					break;
				}
			}
			if (boolean == 1) {
				continue;
			}	

			
			if ((sonEntryP->DIR_Attr & 0x10) == 0) {
				//文件处理
				char tempName[12];	//暂存替换空格为点后的文件名
				int k;
				int tempLong = -1;
				for (k = 0; k < 11; k++) {
					if (sonEntryP->DIR_Name[k] != ' ') {
						tempLong++;
						tempName[tempLong] = sonEntryP->DIR_Name[k];
					}
					else {
						tempLong++;
						tempName[tempLong] = '.';
						while (sonEntryP->DIR_Name[k] == ' ') k++;
						k--;
					}
				}
				tempLong++;
				tempName[tempLong] = '\0';	//到此为止,把文件名提取出来放到tempName里
				Node *son = new Node();
				father->next.push_back(son);
				son->name = tempName;
				son->FileSize = sonEntryP->DIR_FileSize;
				son->isfile = true;
				son->path = father->path + tempName + "/";
				father->file_count++;
				getContent(fat12, sonEntryP->DIR_FstClus, son);
				
			}
			else {
				char tempName[12];
				int count = -1;
				for (int k = 0; k < 11; k++) {
					if (sonEntryP->DIR_Name[k] != ' ') {
						count++;
						tempName[count] = sonEntryP->DIR_Name[k];
					}
					else {
						count++;
						tempName[count] = '\0';
					}
				}

				Node *son = new Node();
				father->next.push_back(son);
				son->name = tempName;
				son->path = father->path + tempName + "/";
				father->dir_count++;
				creatNode(son, father);
				readChildren(fat12, sonEntryP->DIR_FstClus,son);
			}
			

		}


		currentClus = value;//下一个簇
	};
}


int  getFATValue(FILE * fat12, int num) {
	//FAT1的偏移字节
	int fatBase = RsvdSecCnt * BytsPerSec;
	//FAT项的偏移字节
	int fatPos = fatBase + num * 3 / 2;
	//奇偶FAT项处理方式不同,分类进行处理,从0号FAT项开始
	int type = 0;
	if (num % 2 == 0) {
		type = 0;
	}
	else {
		type = 1;
	}

	//先读出FAT项所在的两个字节
	u16 bytes;
	u16* bytes_ptr = &bytes;
	int check;
	check = fseek(fat12, fatPos, SEEK_SET);
	if (check == -1)
		printf("fseek in getFATValue failed!");

	check = fread(bytes_ptr, 1, 2, fat12);
	if (check != 2)
		printf("fread in getFATValue failed!");

	//u16为short,结合存储的小尾顺序和FAT项结构可以得到
	//type为0的话,取byte2的低4位和byte1构成的值,type为1的话,取byte2和byte1的高4位构成的值
	if (type == 0) {
		bytes=bytes << 4;   //这里原文错误,原理建议看网上关于FAT表的文章
		return bytes >> 4;
	}
	else {
		return bytes >> 4;
	}
}

void printLS(Node *r) {
	Node *p = r;//通过root节点,遍历所有的节点
	if (p->isfile == true) {
		return;
	}
	else {
		cout << p->path << ":" << endl;
		//打印每个next
		Node *q;
		int leng = p->next.size();
		for (int i = 0; i <leng; i++) {
			q = p->next[i];
			if (q->isfile == false) {
				//文件夹
	//			cout << "\033[31m"<<q->name << "\033[0m"<<"  ";
				cout << q->name << "  ";
			}
			else {
				//文件
				cout << q->name << "  ";
			}
		}
		cout << endl;
		//进行递归
		for (int i = 0; i < leng; i++) {
			if(p->next[i]->isval==true) printLS(p->next[i]);
		}
		

	}
	

}
void creatNode(Node *p, Node *father) {
	Node *q = new Node();
	q->name = ".";
	q->isval = false;
	p->next.push_back(q);
	q = new Node();
	q->name = "..";
	q->isval = false;
	p->next.push_back(q);
}

void printLS_L(Node *root) {
	Node *p = root;
	if (p->isfile) {
		//如果该Node是文件,不处理
		return;
	}
	else {
		cout << p->path <<" "<<p->dir_count<<" "<<p->file_count<< ":" << endl;
		//打印每个next
		Node *q;
		int leng = p->next.size();
		for (int i = 0; i < leng; i++) {
			q = p->next[i];
			if (q->isfile == false) {
				//文件夹
	//			cout << "\033[31m"<<q->name << "\033[0m"<<"  ";
				if (q->isval) {
					cout << q->name << "  " << q->dir_count << " " << q->file_count << endl;
				}
				else {
					//处理. ..
					cout << q->name << "  "<<endl;
				}
			}
			else {
				//文件
				cout << q->name << "  "<<q->FileSize<<endl;
			}
		}
		cout << endl;
		//进行递归
		for (int i = 0; i < leng; i++) {
			if (p->next[i]->isval == true) printLS_L(p->next[i]);
		}


	}
}

void printLS_LWithPath(Node *root, string p, int & exist) {
	if (p.compare(root->path) == 0) {  //路径完全相同,说明就是该节点
		//查找到
		if (root->isfile) {  //如果是文件,无法打开
			exist = 2;
			return;
		}
		else {
			exist = 1;
			printLS_L(root);
		}
		return;
	}
	if (p.length() <= root->path.length()) {  //出现该情况直接出去
		return;
	}
	string temp = p.substr(0, root->path.length());   //截取输入的path的部分字符串,和当前节点的path比较,如果相同,说明目标在该节点下级
	if (temp.compare(root->path) == 0) {
		//路径部分匹配
		for (Node *q : root->next) {
			printLS_LWithPath(q, p, exist);
		}
	}
}

void getContent(FILE * fat12, int startClus,Node *son) {//获取文件内容
	int dataBase = BytsPerSec * (RsvdSecCnt + FATSz * NumFATs + (RootEntCnt * 32 + BytsPerSec - 1) / BytsPerSec);
	int currentClus = startClus;
	int value = 0;		//这里用value来进行不同簇的读取(超过512字节)
	char *p = son->content;
	if (startClus == 0) {
		return;
	}
	while (value < 0xFF8) {
		value = getFATValue(fat12, currentClus);//获取下一个簇
		if (value == 0xFF7
			) {
			printf("坏簇,读取失败!\n");
			break;
		}
		char* str = (char*)malloc(SecPerClus*BytsPerSec);	//暂存从簇中读出的数据
		char *content = str;
		int startByte = dataBase + (currentClus - 2)*SecPerClus*BytsPerSec;
		int check;
		check = fseek(fat12, startByte, SEEK_SET);
		if (check == -1)
			printf("fseek in printChildren failed!");

		check = fread(content, 1, SecPerClus*BytsPerSec, fat12);//提取数据
		if (check != SecPerClus * BytsPerSec)
			printf("fread in printChildren failed!");

		int count = SecPerClus * BytsPerSec;
		int loop = 0;
		for (int i = 0; i < count; i++) {//读取赋值
			*p = content[i];
			p++;
		}
		free(str);
		currentClus = value;
	}
}

void printCat(Node *root, string p, int & exist) {
	if (p.compare(root->path) == 0) {
		//查找到
		if (root->isfile) {
			exist = 1;
			if (root->content[0] != 0) {
				cout << root->content << endl;
			}
			return;
		}
		else {
			exist = 2;
			return;
		}
	}
	if (p.length() <= root->path.length()) {
		return;
	}
	string temp = p.substr(0, root->path.length());
	if (temp.compare(root->path) == 0) {
		//路径部分匹配
		for (Node *q : root->next) {
			printCat(q, p, exist);
		}
	}
}
  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值