操作系统实验——简易FAT16文件系统的实现

前言

暑假啦!呼,所有的补课终于也都结束了,虽然绩点还是一如既往的拉跨,但是很庆幸自己还是熬过来了,唯一有点小遗憾的是信安大赛没能进入到决赛,有点小可惜吧但也在意料之中,虽然尽力了但一个月准备的作品哪能有别人准备了那么久的作品成熟呢。好了回到正题,这是一个非常非常简陋的文件系统,也是在考试月最后的一个大实验,所以也是紧赶慢赶。接下来介绍一下代码思路,最后也会给出完整代码。

实验要求

实现一个简单的类 FAT-16 文件系统,具有以下功能(不要求全
部完成):mkdir,ls,delete,open,read,write,close 系统是基于内存的:
建立一个数组:
#define DISK_MAXLEN 2560
char Disk[DISK_MAXLEN];
把这个数组当成硬盘,实现文件系统。假设只有一个进程使用。

FAT16基础知识

磁盘组成部分

MBR(master boot record)位于硬盘的第一个扇区,bios 在执行自
己固有的程序以后就会进入到 mbr 中的第一条指令,MBR 分为两部
分:引导代码和 DPT(硬盘分区表)。在 DPT 共 64 个字节中,以 16
个字节为分区表项单位描述一个分区的属性。第一个分区表项描述一
个分区的属性,一般为基本分区。第二个分区表项描述除基本分区外
的其余空间,一般而言,是扩展分区。总的来说 MBR 的功能是描述
分区属性的,本次实验之划分为一个分区,所以这部分不做考虑。
其余还有保留扇区,一般为 62 个。第一个分区,一般为活动分区。
如果磁盘分了多个分区,则有第二个分区 DPT 和保留扇区等。

分区原理

1)DBR 区(DOS BOOT RECORD)即操作系统引导记录区
通常占用 512 字节,由跳转指令、厂商标志、操作系统版本号、
BPB(BIOS Parameter Block)、扩展 BPB、os 引导程序、结束标志几部
分组成。下如下图所示,前三个字节为跳转指令,之后 8 个字节为厂
商标志和操作系统版本号,之后被选中的 53 个字节为 BPB,之后的
代码为扩展 BPB,引导程序代码,结束标志(55AA)。
其中 BPB 部分存放了许多重要信息,本次实验中的 DBR 区也简化
成了只有 BPB 的部分信息,通常存放扇区字节数、一个字节为每个
簇的扇区数、保留扇区数、FAT 数、根目录项数、每个 FAT 表的扇区
数、每道扇区数、磁头数、隐藏扇区数等
2)FAT
为了解决链表的不足,在链表的基础上取出每个磁盘块的指针字,
把它们放在内存的一个表中,相应的表格称为文件分配表。每个目录
项通常存放的是磁盘块的信息,如空闲、下一个和最后。
在这里插入图片描述

3)目录
目录区中的记录项以32字节为单位,这32个字节的内容如下表所示,
根据文件的首簇号去 FAT 表中寻找文件的下一个部分,以此类推,以
链表的形式将文件不连续存储。
在这里插入图片描述

思路

根据实验要求虚拟磁盘空间为一个 char 数组 Disk,最大值为2560。考虑磁盘空间和文件数,选取了 32B 为一个簇,则 fat 表项最多有 80 个(其实达不到,因为有一部分要分给 DBR 等)。定义结构体 fcb,主要去存储当前文件在磁盘块中开始存储的盘块号。定义结构体目录项,去存储文件名、类型、fat 开始的索引号、fcb开始的索引号、目录项列表开始的索引号,这体现了本次实验最重要的思想就是通过文件名找到目录项,再通过目录项找到 fat,fcb 和目录项列表所在的位置。定义结构体目录项列表,去存储目录个数,以及维护一个目录结构体数组。定义全局变量根目录、当前目录和绝对路径。

创建文件。本实验思想是将文件和目录均当成文件,所以在创建中只使用一个函数,除了类型名不同以外,目录还需要维护一个目录项列表。创建过程中需要添加目录项、创建 fcb、创建 fat 等。

删除文件。删除文件前需要先判断是否存在这一个文件,然后删除目录、释放 fat 即可、清空磁盘数据。

展示目录下的文件。因为每个目录都维护了一个目录项列表,所以可以直接遍历目录项列表数组找到每一个文件,输出文件名。进入下一级目录。遍历所在的目录下的文件找到对应的下一级目录,修改当前目录。

读文件。得到用户输入的文件名和读取数据的长度,找到对应的fcb 索引,通过 fcb 找到开始的磁盘块输出即可。

写文件。与读文件操作类似,但需要根据读入的数据修改 fat 和更新 filesize。有些模拟的文件系统选择一开始就分配好固定的簇,但是我选择了一种动态分配的策略。

完整代码

#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
using namespace std;

const int DISK_MAXLEN = 2560;
const int dirtable_max_size = 80;
const int fatnum = 80;
char Disk[DISK_MAXLEN];
int dbr[2];
int startindex;

void init();
void createfile(char filename[], int filesize, int type);
void adddirunit(char fileName[], int type);
int findunit(char filename[]);
void ls();
void deletefile(char filename[]);
void deleteunit(int unitindex);
short findfreefat();
void freefat(char filename[]);
void changedir(char dirname[]);
void read(char filename[], int length);
void write(char filename[], char content[]);
void adjustfat(short num, int unitindex);

struct fcb {
	short blockindex;//which is block
	short filesize;
	short datasize;
};

struct dirunit {
	char filename[80];
	char type;
	short startfat;
	short startfcb;
	short startdir;
};

struct dirtable {
	int dirunitnum;
	dirunit dirs[dirtable_max_size];
};

short fat[fatnum];
//free:0 last:-1 next:id
dirtable* table[10];
fcb* FCB[fatnum];

dirtable rootdirtable;
dirtable* currentdirtable;
char path[100] = "C:\\root\\";

void init() {
	rootdirtable.dirunitnum = 0;
	currentdirtable = &rootdirtable;
	//8B has been allocated
	dbr[0] = fatnum;
	dbr[1] = dirtable_max_size;
	//B_num that allocated to fat table
	int fat_B = fatnum * sizeof(short);
	//calculate startindex of data
	startindex = 8 + fatnum;
}

/*
struct fcb{
	int blockindex;
	int filesize;
	int datasize;
};*/

int FCBrecord = 0; //record current index of  fcb when creating
int TABrecord = 0; //record current index of  dir table when creating
void createfile(char filename[], int filesize, int type) {
	if (strlen(filename) > 80) {
		cout << "file name is too long" << endl;
		return;
	}
	//add dir
	adddirunit(filename, type);
	int index = findunit(filename);
	//create fcb
	fcb* curfcb = (fcb*)new fcb();
	curfcb->blockindex = startindex++;
	curfcb->filesize = filesize;
	curfcb->datasize = 0;//nothing is writed
	FCB[FCBrecord] = curfcb;
	currentdirtable->dirs[index].startfcb = FCBrecord;
	//create fat
	int i = findfreefat();
	currentdirtable->dirs[index].startfat = i;
	fat[i] = -1;
	//create dir
	if (type == 0) {
		dirtable* curdirtable = (dirtable*)new dirtable();
		table[TABrecord] = curdirtable;
		curdirtable->dirunitnum = 0;
		currentdirtable->dirs[index].startdir = TABrecord;
	}
}

/*
struct dirunit{
	char filename[80];
	char type;
	int startfat;
};

struct dirtable{
	int dirunitnum;
	dirunit dirs[dirtable_max_size];
};*/

void adddirunit(char filename[], int type)
{
	int dirunitnum = currentdirtable->dirunitnum;
	//whether is full
	if (dirunitnum == dirtable_max_size)
	{
		cout << "dirTables is full, try to delete some file\n";
		return;
	}

	//whether is existed
	if (findunit(filename) != -1)
	{
		printf("file already exist\n");
		return;
	}
	//creater new dirunit
	dirunit* newdirunit = &currentdirtable->dirs[dirunitnum];
	currentdirtable->dirunitnum++;
	int i = strlen(filename);
	while (i--)
		newdirunit->filename[i] = filename[i];
	newdirunit->type = type;

	return;
}

int findunit(char filename[])
{
	//look up in order
	int dirunitnum = currentdirtable->dirunitnum;
	int unitIndex = -1;
	for (int i = 0; i < dirunitnum; i++)
		if (strcmp(filename, currentdirtable->dirs[i].filename) == 0)
			unitIndex = i;
	return unitIndex;
}

void ls() {
	int uninum = currentdirtable->dirunitnum;
	for (int i = 0; i < uninum; i++) {
		dirunit curunit = currentdirtable->dirs[i];
		cout << curunit.filename << " ";
	}
	cout << endl;
}

void deletefile(char filename[]) {
	int unitindex = findunit(filename);
	if (unitindex == -1) {
		cout << "sorry,not found" << endl;
		return;
	}
	//delete unit in the table
	deleteunit(unitindex);
	freefat(filename);
}

void deleteunit(int unitindex) {
	//let the next item covers 
	int dirunitnum = currentdirtable->dirunitnum;
	for (int i = unitindex; i < dirunitnum - 1; i++) {
		currentdirtable->dirs[i] = currentdirtable->dirs[i + 1];
	}
	currentdirtable->dirunitnum--;
}

short findfreefat() {
	for (short i = 0; i < fatnum; i++) {
		if (fat[i] == 0)return i;
	}
}

void freefat(char filename[]) {
	//find the link in fat and free each of it
	int i = currentdirtable->dirs[findunit(filename)].startfat;
	if (i == -1) {
		fat[i] = 0;
		return;
	}
	while (i == -1) {
		int temp = i;
		i = fat[i];
		fat[temp] = 0;
	}
	if (i == -1) {
		fat[i] = 0;
		return;
	}
}

void changedir(char dirname[]) {
	//see whether the name is valid
	int unitindex = findunit(dirname);
	if (unitindex == -1) {
		cout << "sorry,not found" << endl;
		return;
	}
	if (currentdirtable->dirs[unitindex].type == 1) {
		cout << "not a dir" << endl;
		return;
	}
	//change currentdir
	int i = currentdirtable->dirs[unitindex].startdir;
	currentdirtable = table[i];
	//change path
	if (strcmp(dirname, "..") == 0) {
		//reback
		int length = strlen(path);
		for (int i = length - 2; i >= 0; i--) {
			if (path[i] == '\\') {
				path[i + 1] = '\0';
				break;
			}
		}
	}
	else {
		strcat_s(path, dirname);
		strcat_s(path, "\\");
	}
}

void read(char filename[], int length) {
	int unitindex = findunit(filename);
	if (unitindex == -1) {
		cout << "sorry,not found" << endl;
		return;
	}

	//read the data of given length 
	int index = currentdirtable->dirs[unitindex].startfcb;
	fcb* myfcb = FCB[index];
	for (int i = 0; i < length; i++) {
		cout << Disk[i + myfcb->blockindex];
	}
	cout << endl;
}

void write(char filename[], char content[]) {
	int unitindex = findunit(filename);
	if (unitindex == -1) {
		cout << "sorry,not found" << endl;
		return;
	}
	int length = sizeof(content);
	//how many clusters need
	int num = (length % 32 == 0) ? length / 32 : length / 32 + 1;
	adjustfat(num, unitindex);

	//renew the filesize
	FCB[currentdirtable->dirs[unitindex].startfcb]->filesize = num;

	//get the start index and write the content in order
	int index = currentdirtable->dirs[unitindex].startfcb;
	fcb* myfcb = FCB[index];
	for (int i = 0; i < length; i++) {
		Disk[i + myfcb->blockindex] = content[i];
	}
	cout << endl;

}

void adjustfat(short num, int unitindex) {
	int index = currentdirtable->dirs[unitindex].startfat;
	for (int i = 0; i < num - 1; i++) {
		short j = findfreefat();
		fat[index] = j;
		index = j;
	}
	fat[index] = -1;
}


int main() {
	init();
	string s;
	char name[80] = { 0 };
	char content[100] = { 0 };
	int length = 0;
	for (int i = 0; i < 35; i++)
		cout << '*';
	cout << endl;
	cout << "you can do the operation as follows" << endl;
	cout << "1.mkdir+dirname\n2.vi+filename\n3.ls\n4.cd+dirname\n5.read+filename+length\n6.write+filename+data\n7.delete+filename\n8.quit" << endl;
	for (int i = 0; i < 35; i++)
		cout << '*';
	cout << endl;
	while (1) {
		int i = strlen(path);
		int j = 0;
		while (j < i) {
			cout << path[j];
			j++;
		}
		cout << '>';
		char operation[5];
		char op = getchar();
		if (op == '\n')continue;
		cin >> s;
		memcpy(operation, s.c_str(), s.length());
		switch (op) {
		case 'q':return 0;
		case 'm':
			cin >> s;
			memcpy(name, s.c_str(), s.length());
			createfile(name, 1, 0);
			getchar();
			break;
		case 'v':
			cin >> s;
			memcpy(name, s.c_str(), s.length());
			createfile(name, 1, 1);
			getchar();
			break;
		case 'l':
			ls(); getchar(); break;
		case 'c':
			cin >> s;
			memcpy(name, s.c_str(), s.length());
			changedir(name);
			getchar();
			break;
		case 'r':
			cin >> s;
			memcpy(name, s.c_str(), s.length());
			scanf_s("%d", &length);
			read(name, length);
			getchar();
			break;
		case 'w':
			cin >> s;
			memcpy(name, s.c_str(), s.length());
			cin >> s;
			memcpy(content, s.c_str(), s.length());
			write(name, content);
			getchar();
			break;
		case 'd':
			cin >> s;
			memcpy(name, s.c_str(), s.length());
			deletefile(name);
			getchar();
			break;
		}
	}
}



Logo

鸿蒙生态一站式服务平台。

更多推荐