[操作系统][简单文件系统实现]
简单文件系统的实现
要求
-
内存中开辟一块
虚拟磁盘空间
作为文件存储分区,在其上实现一个简单的基于多级目录的单用户单任务系统
中的文件系统。 -
在退出该文件系统的使用时,虚拟文件系统以一个文件的方式保存到磁盘中,以便下次可以把它恢复到内存的虚拟存储空间
实际实现
-
以上两点均实现
-
能处理绝对路径和相对路径的命令
- 例如 :
cd /home/zy/Desktop/
这样的绝对路径cd ../hah/1/2
这样的相对路径
mkdir
,rmdir
,cd
,creat
,rm
均支持open_path
是open
的升级版,也是支持上述函数实现的主要函数。
- 例如 :
-
包装
open
,read
,close
实现了一个cat
直接打印文件内容。- 检查文件是否打开,如果打开了直接进行下一步,没有就打开
read
出所有内容- 如果之前不是打开的那么就关闭文件。
截图
-
建立目录树,
/home
是用户的根目录/home
下有/zy
/zy
下有/Documents
,/Desktop
,/Viedeos
,Music
等 -
在
/home/zy/Documents
目录下建立一个文件Hello.txt
- 并输入内容
Hello World!Fisrt
,能正确显示长度和文件内容。 - 测试了
creat
,open
,close
,read
,write
等基本用法
- 并输入内容
-
利用
creat /home/zy/hellozy.txt
在/home/zy
下建立了一个hellozy.txt
- 测试了
creat
在路径下的用法
- 测试了
-
测试了
mkdir
,cd
,rmdir
在路径下也能正常工作。其余几个类似的同理,OVER
可以改进的地方
- 有一些BUG还待处理
- 完善异常处理机制
- 完善文件信息,包括创建时间,修改时间。
- 尝试实现多任务的文件系统。
以下代码并非最终版本,之后还略有修改,详细代码存放在github
OS.h
几个常量定义
#include <cstdio>
#include <memory.h>
#include <string>
#include <iostream>
#include <malloc.h>
#include <time.h>
using namespace std;
/*常量定义*/
#define Path "/home" //根目录
#define BLOCKSIZE 1024 //磁盘块大小
#define BLOCKCOUNT 1000 //盘块大小
#define MAXOPENFILE 10 //能打开最多的文件数
#define DISKSIZE (BLOCKSIZE*BLOCKCOUNT)//磁盘大小
#define END -1
const int FCBCOUNT = BLOCKSIZE/sizeof(FCB);//一个块的最多FCB数量
DISK,DirFile,USEROPEN,FCB定义
DISK定义
- 总共1000个磁盘块
FAT1
:4个FAT2
:4个根目录
1个其余数据
991个
代码:
/*------------------磁盘------------------------*/
struct DISK
{
int FAT1[BLOCKCOUNT];//磁盘块0-3代表FAT
int FAT2[BLOCKCOUNT];//磁盘块4-7代表FAT2
DirFile RootDir; //根目录 磁盘块8
char Data[BLOCKCOUNT-9][BLOCKSIZE];//目录和其他文件 磁盘块9~1000
};
DirFile 定义
代码:
/*-----------------目录文件---------------------*/
struct DirFile{
FCB fcb[FCBCOUNT]; //文件控制块
void init(int father,int self)
{
//给根目录创建.. 和 . 序号0放".", 序号1放".."
memset(fcb,0,sizeof(fcb));
fcb[1].free=fcb[0].free=1;
fcb[1].attribute=fcb[0].attribute=1;
fcb[1].first=father;
fcb[0].first=self;
memcpy(fcb[0].filename,".",sizeof("."));
memcpy(fcb[1].filename,"..",sizeof(".."));
}
};
FCB
struct FCB
{
char filename[12]; //文件名
char attribute;//0表示目录,1表示数据文件
int time;//创建时间
int data;//创建日期
int first;//起始盘号
int length;//长度
char free;//表示目录项是否为空
};
USEROPEN
struct USEROPEN
{
FCB fcb;
char dir[80];//相应打开文件所在的目录名
int count;//读写指针在文件的位置
char fcbstate;//是否修改了文件的FCB内容,修改了置为1,否则置为0
char topenfile;//表示该用户表项是否被占用,1就是被占用,0就是没有被占用
char fatherfilename[12];//上一层目录的名字
int pos;
};
main.cpp
解析
- 全局变量的声明
- 简单的处理命令行的读入。
ls
函数
代码
#include "OS.h"
using namespace std;
/*-------------函数声明------------------------*/
void help();
int cd(char *dirname);
int startsys();
int format();
int mkdir(char *dirname);
int rmdir(char *dirname);
int close(int fd);
int open(char *filename);
int creat(char *filename);
int rm(char *filename);
int filewrite(int fd);
int dowrite(int fd,char *text,int len, char wstyle);
int fileread(int fd,int len);
int doread(int fd,int len,char *text);
void exitsys();
/*--------------全局变量-------------------------*/
char* myvhard;//虚拟磁盘起始地址
string currentdir="/home";//当前目录
string cmd; //读取指令
USEROPEN openfilelist[MAXOPENFILE];//文件打开表
USEROPEN *ptrcuridr;//当前目录在文件打开表的位置
DISK* disk;//将内容结构化
char command[50];//文件名标示符
/*--------------------- 显示目录函数 ---------------*/
void ls() {
int BlockDirNum = (ptrcuridr->fcb).first;
DirFile *dir = (DirFile *) disk->Data[BlockDirNum - 8];
for (int i = 0; i < FCBCOUNT; i++) {
if (dir->fcb[i].free == 1) {
if (dir->fcb[i].attribute == 0)
printf("%10s---length:%5d----File\n",dir->fcb[i].filename,dir->fcb[i].length);
else
printf("%10s---length:%5d----Directory\n",dir->fcb[i].filename,dir->fcb[i].length);
}
}
}
int main() {
printf("Welcome the OS FileSystem\n");
printf("input 'help' get more information\n\n\n");
// freopen("E:\\OSFileSystem\\a.in","r",stdin);
startsys(); //Init the System
int len;
while(1)
{
cout<<currentdir+">";
cin>>cmd;
if(cmd=="help"){ //帮助
help();
}
else if(cmd=="mkdir"){
cin>>command;
mkdir(command);
}
else if(cmd=="cd"){
cin>>command;
cd(command);
}
else if(cmd=="exit") {
break;
}
else if(cmd=="rmdir"){
cin>>command;
rmdir(command);
}
else if(cmd=="ls"){
ls();
}
else if(cmd=="open"){
cin>>command;
open(command);
}
else if(cmd=="close"){
cin>>command;
close(atoi(command));
}
else if(cmd=="creat"){
cin>>command;
creat(command);
}
else if(cmd=="rm"){
cin>>command;
rm(command);
}
//
else if(cmd=="write"){
cin>>command;
filewrite(atoi(command));
}
else if(cmd=="read") {
cin >> command >> len;
fileread(atoi(command),len);
}
else if(cmd=="exitsys"){
exitsys();
}else {
printf("The cmd is not exits\n");
}
}
}
startsys.cpp
分析
int format()
- 分配磁盘空间
- 初始化根目录
- 加入
.
和..
两个子目录
- 加入
int startsys()
- 申请磁盘空间
- 载入之前的磁盘,如果没有就申请。
- 把根目录加载进文件打开表。
代码
#include "OS.h"
/*--------------全局变量-------------------------*/
extern char* myvhard;//虚拟磁盘起始地址
extern string currentdir;//当前目录
extern string cmd; //读取指令
extern USEROPEN openfilelist[MAXOPENFILE];//文件打开表
extern USEROPEN *ptrcuridr;//当前目录在文件打开表的位置
extern DISK* disk;//将内容结构化
extern char command[50];//文件名标示符
/*--------------------------------磁盘格式化函数-------------------*/
int format() {
memset(myvhard,0,DISKSIZE);
//创建根目录,在磁盘块8
//前九个被FAT1+FAT2+root占用
for(int i=0;i<9;i++){
disk->FAT1[i]=disk->FAT2[i]=-2;//-2代表被占用
}
DirFile *dir=(DirFile *)disk->Data[8-8];//注意Data和FAT的区别
//初始化根目录.
dir->init(8,8);
return 1;
}
/*--------------------------------进入文件系统函数--------------------*/
int startsys() {
myvhard=(char *)malloc(DISKSIZE); //申请1024*1000磁盘空间
disk=(DISK *)myvhard;
FILE *fp=fopen("myfsys","r");
if(fp!=NULL)
{
printf("|-------------------------------------------|\n");
printf("|-----------myfsys is loading---------------|\n");
printf("|-------------------------------------------|\n\n");
fread(myvhard,sizeof(char),DISKSIZE,fp);
fclose(fp);
}
else{
printf("|-------------------------------------------|\n");
printf("|-----------myfsys is not exit--------------|\n");
printf("|--File system is being created now --------|\n");
printf("|-------------------------------------------|\n\n");
format();
}
//初始化用户打开表
memset(openfilelist,0,sizeof(openfilelist));
//将根目录打开,首先修改fcb里的内容
openfilelist->fcb.first=8;
//文件打开表项的内容
openfilelist[0].topenfile=1;
strcpy(openfilelist->dir,"");
strcpy(openfilelist->fcb.filename,"home");
strcpy(openfilelist->fatherfilename,"");
ptrcuridr=&openfilelist[0];
openfilelist[0].pos=0;
//当前目录设置为根目录
currentdir=Path;
return 1;
}
OPEN.CPP
分析
-
处理好
.
和..
两个子目录 -
维护好
openfilelist
里的每个值
代码
#include "OS.h"
/*--------------全局变量-------------------------*/
extern char* myvhard;//虚拟磁盘起始地址
extern string currentdir;//当前目录
extern string cmd; //读取指令
extern USEROPEN openfilelist[MAXOPENFILE];//文件打开表
extern USEROPEN *ptrcuridr;//当前目录在文件打开表的位置
extern DISK* disk;//将内容结构化
extern char command[50];//文件名标示符
/*-----------------------------打开文件函数--------------------*/
int open(char *filename){
//检查要被打开文件是否存在
int BlockDirNum = (ptrcuridr->fcb).first;
DirFile *dir = (DirFile *) disk->Data[BlockDirNum - 8];
int Fileaddr = -1;
for (int i = 0; i < FCBCOUNT; i++) {
if (dir->fcb[i].free == 1 && strcmp(dir->fcb[i].filename, filename) == 0)//表项被使用,且是目录,且文件名相等
{
Fileaddr = i;//文件存在
break;
}
}
//文件不存在 输出-1
if(Fileaddr==-1){
printf("file does not exist\n");
return -1;
}
//检查打开文件表是否还有空表项,没有报错,有则记录
int OpenFileaddr=-1;
for(int i=0;i<MAXOPENFILE;i++) {
if (openfilelist[i].topenfile == 0) {
OpenFileaddr=i;
}
}
//没有空表了
if(OpenFileaddr==-1) {
printf("File open table is full \n");
return -1;
}
//如果又要打开一个根目录,那么直接返回0
if(dir->fcb[Fileaddr].first==8)
{
OpenFileaddr=0;
if(ptrcuridr->fcb.first==8)
return 0;
}
//为该文件填写文件打开表项
//检查是否已经打开
//需要一个temp来表示实际的dir值
char temp[300];
if(strcmp(filename,"..")==0)
{
strcpy(temp,ptrcuridr->dir);
int len1=strlen(ptrcuridr->dir);
int len2=strlen(ptrcuridr->fatherfilename);
temp[len1-len2-1]=0;
}
else
{
char buffer[80];
memset(buffer,0,sizeof(buffer));
strcat(strcat(strcat(buffer,ptrcuridr->dir),"/"),ptrcuridr->fcb.filename);
strcpy(temp,buffer);
}
for(int i=1;i<MAXOPENFILE;i++) {
//"."一定是被打开了
if ((openfilelist[i].topenfile == 1 && strcmp(openfilelist[i].fcb.filename, filename) == 0 &&
strcmp(openfilelist[i].dir,temp) ==0 )||(strcmp(filename,".")==0))
{
printf(" The file has been opened !\n");
return -1;//无效返回-1
}
}
if(strcmp(filename,"..")==0)
{
openfilelist[OpenFileaddr].fcb=dir->fcb[Fileaddr];
//名字是错的,会是"..";正确的名字在工作块的父亲名字
strcpy(openfilelist[OpenFileaddr].fcb.filename,ptrcuridr->fatherfilename);
//曾经的路径减去名字减去'/' 就是新的
strcpy(openfilelist[OpenFileaddr].dir,ptrcuridr->dir);
int len1=strlen(openfilelist[OpenFileaddr].dir);
int len2=strlen(ptrcuridr->fatherfilename);
openfilelist[OpenFileaddr].dir[len1-len2-1]=0;
//找新的fathername,通过分析dir来得到
char test[20];
strcpy(test,openfilelist[OpenFileaddr].dir);
char *q;
int len=strlen(test);
for(int i=0;i<len;i++)
{
if(test[i]=='/'&&i!=len-1)
q=test+i+1;
}
strcpy(openfilelist[OpenFileaddr].fatherfilename,q);
}
else
{
openfilelist[OpenFileaddr].fcb=dir->fcb[Fileaddr];
char buffer[80];
memset(buffer,0,sizeof(buffer));
strcat(strcat(strcat(buffer,ptrcuridr->dir),"/"),ptrcuridr->fcb.filename);
strcpy(openfilelist[OpenFileaddr].dir,buffer);
strcpy(openfilelist[OpenFileaddr].fatherfilename,ptrcuridr->fcb.filename);
}
openfilelist[OpenFileaddr].pos=OpenFileaddr;
openfilelist[OpenFileaddr].count=0;
openfilelist[OpenFileaddr].fcbstate=0;
openfilelist[OpenFileaddr].topenfile=1;
//返回文件描述符fd,此时的fd跟下标相同,一般不同.
if(openfilelist[OpenFileaddr].fcb.attribute==0)//如果是文件,输出文件打开符号
printf("File Open Success,The fd is %d\n",OpenFileaddr);
return OpenFileaddr;
}
OPEN_PATH.CPP
分析
-
让系统函数都能处理绝对路径和相对路径,而不仅仅是当前目录下的文件了。
cd
,mkdir
,creaet
,rm
,rmdir
-
使用的方式是拆分路径的元素,然后分析元素
不断的调用
open
和close
来实现。
代码
#include "OS.h"
int open(char *filename);
int close(int fd);
/*--------------全局变量-------------------------*/
extern char* myvhard;//虚拟磁盘起始地址
extern string currentdir;//当前目录
extern string cmd; //读取指令
extern USEROPEN openfilelist[MAXOPENFILE];//文件打开表
extern USEROPEN *ptrcuridr;//当前目录在文件打开表的位置
extern DISK* disk;//将内容结构化
extern char command[50];//文件名标示符
/*----------------------更改当前目录函数---------------------
读入一条文件路径,返回一个打开的fd
支持绝对路径和相对路径.
输出,如果路径正确,返回文件打开后的fd.
------------------------------------------------------*/
int open_path(char *dirname) {
//如果dirname是绝对路径
int fd=0, ok = 1, fdtemp = -1;//ok代表是否找到dirname所指的文件,fdtemp存临时的,用来关闭
USEROPEN *temp = ptrcuridr;//暂时保管一下ptrcuridr原始值,可能要回溯
if (dirname[0] == '/') {
ptrcuridr = openfilelist;//我们的openfilelist[0]永远是根目录
//使工作目录暂时指向根目录
char *p = strtok(dirname + 1, "/");//用“/”分割 dirname[1]开始的字符串
if (p != NULL) p = strtok(NULL, "/");//跳过/home
while (p) {
fd = open(p);
if (fd == -1) {
ok = 0;
if (fdtemp != -1) //离开前记得关文件
close(fdtemp);//把上个打开的文件关掉
break;
}
ptrcuridr = openfilelist + fd;
if (fdtemp != -1)
close(fdtemp);//把上个打开的文件关掉
fdtemp = fd;
p = strtok(NULL, "/");
}
}
//如果是相对路径
else {
int len=strlen(dirname);
dirname[len]='/';
dirname[len+1]=0;
char *p = strtok(dirname, "/");//用“/”分割 dirname[1]开始的字符串
while (p) {
fd = open(p);
if (fd == -1) {
ok = 0;
if (fdtemp != -1) //离开前记得关文件
close(fdtemp);//把上个打开的文件关掉
break;
}
ptrcuridr = openfilelist + fd;
if (fdtemp != -1)
close(fdtemp);//把上个打开的文件关掉
fdtemp = fd;
p = strtok(NULL, "/");
}
}
ptrcuridr = temp;
//输出数据
if (ok == 1)
return fd;
else
return -1;
}
close.cpp
分析
- 记得把文件打开表的东西保存
代码
#include "OS.h"
int open_path(char* dirname);
/*--------------全局变量-------------------------*/
extern char* myvhard;//虚拟磁盘起始地址
extern string currentdir;//当前目录
extern string cmd; //读取指令
extern USEROPEN openfilelist[MAXOPENFILE];//文件打开表
extern USEROPEN *ptrcuridr;//当前目录在文件打开表的位置
extern DISK* disk;//将内容结构化
extern char command[16];//文件名标示符
/*---------------关闭文件函数-----------------*/
int close(int fd){
//检查fd的合法性
if(fd>=MAXOPENFILE||fd<=0){
printf(" Is not a legitimate fd \n");
return -1;
}else if(openfilelist[fd].topenfile==0){
printf("filewrite ERROR:The File Don't Open\n");
return -1;
}
//检查用户打开文件表表项的`fcbstate`字段,如果是1,则需要将该文件的FCB的内容保存的虚拟磁盘上该文件的目录项
//方法是:打开该文件的父目录文件,已覆盖写方式调用do_wirte()将欲关闭的FCB写入父目录文件的相应盘块.
if(openfilelist[fd].fcbstate==1){
char buffer[30]="..";
int fatherfd=open_path(buffer);
if(fatherfd!=-1) {
//找到相应的盘号
int pan1 = openfilelist[fatherfd].fcb.first;
int area1 = -1;
//盘号上相应的位置
DirFile *dirson = (DirFile *) (disk->Data[pan1 - 8]);
for (int i = 0; i < FCBCOUNT; i++) {
if (dirson->fcb[i].free == 1 &&
strcmp(dirson->fcb[i].filename, openfilelist[fd].fcb.filename) == 0) {
//找到了该文件,覆盖fcb
dirson->fcb[i] = openfilelist[fd].fcb;
break;
}
}
//关文件
if(fatherfd!=0)
close(fatherfd);
}
}
//回收该文件占据的用户打开表表项(clear),topenfile字段置0
memset(openfilelist+fd,0,sizeof(openfilelist[0]));
openfilelist[fd].topenfile=0;
//返回
return 0;
}
mkdir.cpp
分析
- 利用
open_path
来返打开一个父级目录,并返回fd
- 将当前工作目录
ptrcuridr
的原始值保存下来,然后将其赋值给那个父级目录。 - 最后还原
ptrcuridr
代码
#include "OS.h"
int close(int fd);
int open_path(char *dirname);
int FileSubstr(char *str);
/*--------------全局变量-------------------------*/
extern char* myvhard;//虚拟磁盘起始地址
extern string currentdir;//当前目录
extern string cmd; //读取指令
extern USEROPEN openfilelist[MAXOPENFILE];//文件打开表
extern USEROPEN *ptrcuridr;//当前目录在文件打开表的位置
extern DISK* disk;//将内容结构化
extern char command[50];//文件名标示符
int mkdir(char *dirname)
{
char newdir[20];
char dirpath[60];
USEROPEN *tempp = ptrcuridr;//暂时保管一下ptrcuridr原始值,可能要回溯
int k=FileSubstr(dirname),fd=-1;
if(k!=-1) {
dirname[k] = 0;
memset(newdir, 0, sizeof(newdir));
memset(dirpath, 0, sizeof(dirpath));
strcpy(newdir, dirname + k + 1);
strcpy(dirpath, dirname);
fd = open_path(dirpath);
if(fd!=-1) {
ptrcuridr = openfilelist + fd;
dirname = newdir;
}
else {
printf("error\n");
return -1;
}
}
//-------------以下为一天前的代码-----------------------//
//读取当前目录的地址
int BlockDirNum=(ptrcuridr->fcb).first;
DirFile *dir=(DirFile *)disk->Data[BlockDirNum-8];
//遍历文件目录,检查是否有文件名相同的文件或目录,并找一个没有被使用的目录空闲表项
int temp=-1, DirFreeItems =-1;
for(int i=0;i<FCBCOUNT;i++) {
if (dir->fcb[i].free == 1 && strcmp(dir->fcb[i].filename, dirname) == 0)//表项被使用,且是目录,且文件名相等
{
temp = i;//重名的表项
break;
}
else if (dir->fcb[i].free == 0) {
DirFreeItems = i;//目录空闲表项
}
}
//如果文件名已存在,报错并退出
if(temp!=-1)
{
printf("mkdir: cannot create directory '%s': Directory exists\n",dirname);
if(fd!=-1)
close(fd);
ptrcuridr=tempp;
return 0;
}
//如果没有空闲位置,报错退出
if(DirFreeItems==-1)
{
printf("mkdir: cannot create directory '%s': Directory is full \n",dirname);
if(fd!=-1)
close(fd);
ptrcuridr=tempp;
return 0;
}
//检查FAT中是否有空闲的盘块
int FATFreeItems=-1;
for(int i = 0;i < BLOCKCOUNT;i++)
{
if(disk->FAT1[i] == 0) {//没被使用的块标记为0
FATFreeItems=i;//找到了一个空闲块
break;
}
}
//如果FAT没有空闲块,报错退出
if(FATFreeItems==-1)
{
printf("mkdir: cannot create directory '%s': Disk is full \n",dirname);
if(fd!=-1)
close(fd);
ptrcuridr=tempp;
return 0;
}
/*----------------开始新建目录-------------------*/
//修改长度,fcbstate置为1
ptrcuridr->fcbstate=1;
ptrcuridr->fcb.length++;//不包括.和..的
//分配FAT的空闲块,2表示被目录使用
disk->FAT1[FATFreeItems]=disk->FAT2[FATFreeItems]=2;
//将改块分配到 当前目录的空闲项目下
strcpy(dir->fcb[DirFreeItems].filename,dirname);
dir->fcb[DirFreeItems].free=1;//被使用
dir->fcb[DirFreeItems].attribute=1;//是目录
dir->fcb[DirFreeItems].first=FATFreeItems;//分配FAT空闲块
dir->fcb[DirFreeItems].length=0;
dir->fcb[DirFreeItems].data=0;//时间之后弄
dir->fcb[DirFreeItems].time=0;//时间之后弄
//进入下一次目录,初始化新获得的块(重置给予"."和"..")
dir=(DirFile*)(disk->Data[FATFreeItems-8]);
dir->init(BlockDirNum,FATFreeItems);
/*----------------恢复现场------------------------*/
if(fd!=-1)
close(fd);
ptrcuridr=tempp;
return 1;
}
rmdir.cpp
分析
- 跟
mkdir
类似
代码
#include "OS.h"
int close(int fd);
int open_path(char *dirname);
int FileSubstr(char *str);
/*--------------全局变量-------------------------*/
extern char* myvhard;//虚拟磁盘起始地址
extern string currentdir;//当前目录
extern string cmd; //读取指令
extern USEROPEN openfilelist[MAXOPENFILE];//文件打开表
extern USEROPEN *ptrcuridr;//当前目录在文件打开表的位置
extern DISK* disk;//将内容结构化
extern char command[50];//文件名标示符
/*-------------------------------删除子目录函数---------------------*/
int rmdir(char *dirname) {
char newdir[20];
char dirpath[60];
USEROPEN *tempp = ptrcuridr;//暂时保管一下ptrcuridr原始值,可能要回溯
int k=FileSubstr(dirname),fd=-1;
if(k!=-1) {
dirname[k] = 0;
memset(newdir, 0, sizeof(newdir));
memset(dirpath, 0, sizeof(dirpath));
strcpy(newdir, dirname + k + 1);
strcpy(dirpath, dirname);
fd = open_path(dirpath);
if(fd!=-1) {
ptrcuridr = openfilelist + fd;
dirname = newdir;
}
else {
printf("error\n");
return -1;
}
}
/*---------使用open_path更新----------------------*/
//检查文件是否存在
int BlockDirNum = (ptrcuridr->fcb).first;
DirFile *dir = (DirFile *) disk->Data[BlockDirNum - 8];
int temp = -1;
for (int i = 0; i < FCBCOUNT; i++) {
if (dir->fcb[i].free == 1 && dir->fcb[i].attribute == 1 &&
strcmp(dir->fcb[i].filename, dirname) == 0)//表项被使用,且是目录,且文件名相等
{
temp = i;//文件存在
break;
}
}
//要删除的目录不存在
if(temp == -1) {
printf("rmdir: failed to remove '%s': No such file or directory\n",dirname);
if(fd!=-1)
close(fd);
ptrcuridr=tempp;
return 0;
}
//判断子目录是否为空,不包括0和1,也可以用length,懒得用
DirFile *dirson=(DirFile*)(disk->Data[dir->fcb[temp].first-8]);
int flag=-1;
for(int i=2; i < FCBCOUNT; i++ ) {
if (dir->fcb[i].free == 1){
flag=1;
break;
}
}
//要删除的目录不为空
if(flag==-1)
{
printf("rmdir: failed to remove '%s': Directory not empty\n",dirname);
if(fd!=-1)
close(fd);
ptrcuridr=tempp;
return 0;
}
//检查目录是否已打开,打开就关闭
//回收该目录文件所占据的磁盘块,修改FAT
disk->FAT1[dir->fcb[temp].first]=disk->FAT1[dir->fcb[temp].first]=0;
//当前目录文件中清空该目录文件的目录项,memset清理干净
memset(&(dir->fcb[temp]),0,sizeof(dir->fcb[temp]));
//修改长度,表项的fcbstate置为1
ptrcuridr->fcbstate=1;
ptrcuridr->fcb.length--;//不包括.和..的
/*-----------------恢复现场-------------*/
if(fd!=-1)
close(fd);
ptrcuridr=tempp;
return 1;
}
cd.cpp
分析
- 善于利用
open_path
十分简单的实现一个跳转到任意路径的函数
代码
#include "OS.h"
int open(char *filename);
int close(int fd);
int open_path(char *dirname);
/*--------------全局变量-------------------------*/
extern char* myvhard;//虚拟磁盘起始地址
extern string currentdir;//当前目录
extern string cmd; //读取指令
extern USEROPEN openfilelist[MAXOPENFILE];//文件打开表
extern USEROPEN *ptrcuridr;//当前目录在文件打开表的位置
extern DISK* disk;//将内容结构化
extern char command[50];//文件名标示符
/*----------------------更改当前目录函数---------------------*/
int cd(char *dirname) {
USEROPEN *temp = ptrcuridr;//暂时保管一下ptrcuridr原始值,可能要回溯
int fd=open_path(dirname);
if (fd != -1) {
//关掉旧的描述符
int old=temp->pos;
//获取旧的描述符
if (old != 0) //根目录描述符不关
close(old);
//工作目录指向它
ptrcuridr = openfilelist + fd;
//当前目录赋值
currentdir= ptrcuridr->dir ;
currentdir+= '/';
currentdir+= ptrcuridr->fcb.filename;
return 0;
}
else {
ptrcuridr = temp;
printf("No such file or directory\n");
return 0;
}
}
文件操作
creat.cpp
分析
- 有了
open_path
so easy
代码
#include "OS.h"
int close(int fd);
int open_path(char *dirname);
int FileSubstr(char *str);
/*--------------全局变量-------------------------*/
extern char* myvhard;//虚拟磁盘起始地址
extern string currentdir;//当前目录
extern string cmd; //读取指令
extern USEROPEN openfilelist[MAXOPENFILE];//文件打开表
extern USEROPEN *ptrcuridr;//当前目录在文件打开表的位置
extern DISK* disk;//将内容结构化
extern char command[50];//文件名标示符
int creat(char *filename){
//为新文件分配一个空闲打开文件表项,如果没有空闲表项就返回 -1
//检查打开文件表是否还有空表项,没有报错,有则记录
int OpenFileaddr=-1;
for(int i=0;i<MAXOPENFILE;i++) {
if (openfilelist[i].topenfile == 0) {
OpenFileaddr=i;
}
}
//没有空表了
if(OpenFileaddr==-1) {
printf("File open table is full \n");
return -1;
}
/*-----------打开路径所指的父目录---------*/
char newdir[20];
char dirpath[60];
USEROPEN *tempp = ptrcuridr;//暂时保管一下ptrcuridr原始值,可能要回溯
int k=FileSubstr(filename),fd=-1;
if(k!=-1) {
filename[k] = 0;
memset(newdir, 0, sizeof(newdir));
memset(dirpath, 0, sizeof(dirpath));
strcpy(newdir, filename + k + 1);
strcpy(dirpath, filename);
fd = open_path(dirpath);
if(fd!=-1) {
ptrcuridr = openfilelist + fd;
filename = newdir;
}
else {
printf("error\n");
return -1;
}
}
//检查重名
int BlockDirNum = (ptrcuridr->fcb).first;
DirFile *dir = (DirFile *) disk->Data[BlockDirNum - 8];
int temp = -1,DirFreeItems =-1;
for (int i = 0; i < FCBCOUNT; i++) {
if (dir->fcb[i].free == 1 &&
strcmp(dir->fcb[i].filename, filename) == 0)//表项被使用,且是目录,且文件名相等
{
temp = i;//有重名,可能文件,可能文件夹
break;
}
else if (dir->fcb[i].free == 0) {
DirFreeItems = i;//目录空闲表项
}
}
//如果文件名已存在,报错并退出
if(temp!=-1)
{
if(fd!=-1)
close(fd);
ptrcuridr=tempp;
printf("creat: cannot create file '%s': File exists\n",filename);
return 0;
}
//如果没有空闲位置,报错退出
if(DirFreeItems==-1)
{
printf("creat: cannot create file '%s': Directory is full \n",filename);
if(fd!=-1)
close(fd);
ptrcuridr=tempp;
return 0;
}
//检查FAT中是否有空闲的盘块
int FATFreeItems=-1;
for(int i = 0;i < BLOCKCOUNT;i++)
{
if(disk->FAT1[i] == 0) {//没被使用的块标记为0
FATFreeItems=i;//找到了一个空闲块
break;
}
}
//如果FAT没有空闲块,报错退出
if(FATFreeItems==-1)
{
printf("mkdir: cannot create directory '%s': Disk is full \n",filename);
if(fd!=-1)
close(fd);
ptrcuridr=tempp;
return 0;
}
/*----------------开始新建文件-------------------*/
//修改长度,fcbstate置为1
ptrcuridr->fcbstate=1;
ptrcuridr->fcb.length++;//不包括.和..的
//准备好新文件的FCB的内容,文件属性为数据文件,长度0.
//分配FAT的空闲块,-3表示被文件使用,-1表示文件结尾
disk->FAT1[FATFreeItems]=disk->FAT2[FATFreeItems]=-1;
//将改块分配到 当前目录的空闲项目下
strcpy(dir->fcb[DirFreeItems].filename,filename);
dir->fcb[DirFreeItems].free=1;//被使用
dir->fcb[DirFreeItems].attribute=0;//是文件
dir->fcb[DirFreeItems].first=FATFreeItems;//分配FAT空闲块
dir->fcb[DirFreeItems].length=0;
dir->fcb[DirFreeItems].data=0;//时间之后弄
dir->fcb[DirFreeItems].time=0;//时间之后弄
//文件新建完毕
//填写文件打开表项
openfilelist[OpenFileaddr].fcb=dir->fcb[DirFreeItems];
char buffer[80];
memset(buffer,0,sizeof(buffer));
strcat(strcat(strcat(buffer,ptrcuridr->dir),"/"),ptrcuridr->fcb.filename);
strcpy(openfilelist[OpenFileaddr].dir,buffer);
strcpy(openfilelist[OpenFileaddr].fatherfilename,ptrcuridr->fcb.filename);
openfilelist[OpenFileaddr].pos=OpenFileaddr;
openfilelist[OpenFileaddr].count=0;
openfilelist[OpenFileaddr].fcbstate=0;
openfilelist[OpenFileaddr].topenfile=1;
//关闭文件走人
if(fd!=-1)
close(fd);
ptrcuridr=tempp;
//返回文件描述符fd,此时的fd跟下标相同,一般不同.
printf("File Creat Success,The fd is %d\n",OpenFileaddr);
return OpenFileaddr;
}
rm.cpp
定义
- 记得遍历链表释放文件的空间
- 链表的结尾是
-1
- 链表的结尾是
代码
#include "OS.h"
int close(int fd);
int open_path(char *dirname);
int FileSubstr(char *str);
/*--------------全局变量-------------------------*/
extern char* myvhard;//虚拟磁盘起始地址
extern string currentdir;//当前目录
extern string cmd; //读取指令
extern USEROPEN openfilelist[MAXOPENFILE];//文件打开表
extern USEROPEN *ptrcuridr;//当前目录在文件打开表的位置
extern DISK* disk;//将内容结构化
extern char command[50];//文件名标示符
/*--------------------------删除文件函数---------------------*/
int rm(char *filename){
/*--------------------------打开上级目录---------------------*/
char newdir[20];
char dirpath[60];
USEROPEN *tempp = ptrcuridr;//暂时保管一下ptrcuridr原始值,可能要回溯
int k=FileSubstr(filename),fd=-1;
if(k!=-1) {
filename[k] = 0;
memset(newdir, 0, sizeof(newdir));
memset(dirpath, 0, sizeof(dirpath));
strcpy(newdir, filename + k + 1);
strcpy(dirpath, filename);
fd = open_path(dirpath);
if(fd!=-1) {
ptrcuridr = openfilelist + fd;
filename = newdir;
}
else {
printf("error\n");
return -1;
}
}
//调用read,判断目录下文件是否存在
int BlockDirNum = (ptrcuridr->fcb).first;
DirFile *dir = (DirFile *) disk->Data[BlockDirNum - 8];
int temp = -1;
for (int i = 0; i < FCBCOUNT; i++) {
if (dir->fcb[i].free == 1 && dir->fcb[i].attribute == 0 &&
strcmp(dir->fcb[i].filename, filename) == 0)//表项被使用,且是文件,且文件名相等
{
temp = i;//文件存在
break;
}
}
//要删除的目录不存在
if(temp == -1) {
printf("rm: failed to remove '%s': No such file or directory\n",filename);
if(fd!=-1)
close(fd);
ptrcuridr=tempp;
return 0;
}
//检查该文件是否打开,若打开则关闭
char buffer[80];
memset(buffer,0,sizeof(buffer));
strcat(strcat(strcat(buffer,ptrcuridr->dir),"/"),ptrcuridr->fcb.filename);
for(int i=1;i<MAXOPENFILE;i++) {
//"."一定是被打开了
if ((openfilelist[i].topenfile == 1 && strcmp(openfilelist[i].fcb.filename, filename) == 0 &&
strcmp(openfilelist[i].dir,buffer) ==0 ))
{
printf(" The file been opened,Now Close it !\n");
close(i);
break;
}
}
//回收磁盘,一个链表
int TEMP=0;
for(int p=dir->fcb[temp].first;p!=-1;p=TEMP)
{
TEMP=disk->FAT1[p];
disk->FAT1[p]=disk->FAT2[p]=0;
}
//清空该目录项,free字段为0,
memset(&(dir->fcb[temp]),0,sizeof(dir->fcb[temp]));
//修改长度,表项的fcbstate置为1
ptrcuridr->fcbstate=1;
ptrcuridr->fcb.length--;//不包括.和..的
/*-----------------恢复现场-------------*/
if(fd!=-1)
close(fd);
ptrcuridr=tempp;
return 1;
}
dowrite.cpp
分析
- 每次循处理一块磁盘
- 文件指针转换为逻辑块块号
blockno
和 块内偏移blockoff;
代码
#include "OS.h"
int open_path(char* dirname);
/*--------------全局变量-------------------------*/
extern char* myvhard;//虚拟磁盘起始地址
extern string currentdir;//当前目录
extern string cmd; //读取指令
extern USEROPEN openfilelist[MAXOPENFILE];//文件打开表
extern USEROPEN *ptrcuridr;//当前目录在文件打开表的位置
extern DISK* disk;//将内容结构化
extern char command[16];//文件名标示符
/*---------------实际写文件函数----------------*/
int dowrite(int fd,char *text,int len, char wstyle) {
//申请1024字节的缓冲区buf
char *buf = (char *) malloc(1024);
if (buf == NULL) {
printf("MALLOC ERROR b\n");
return -1;
}
int tmplen = 0;
int textpos = 0;
int textlen = strlen(text);
//将文件指针转换为逻辑块块号blockno 和 块内偏移blockoff;
/*--------------------分配-----------------*/
while(tmplen<len) {
int blockno = (openfilelist[fd].count) / 1024;
int blockoff = (openfilelist[fd].count) % 1024;
//寻找磁盘块blockno
int currentblock = 0;
int cnt = 0;
for (int p = openfilelist[fd].fcb.first; p != -1; p = disk->FAT1[p]) {
cnt++;
currentblock = p;
if (cnt == blockno + 1)
break;
}
int pre = currentblock;
if (cnt != blockno + 1)//如果找不到这样的一块,那么还需要给它分配blockno+1-cnt块
{
//从currentblock开始分配
for (int i = 1; i <= blockno + 1 - cnt; i++) {
//检查FAT中是否有空闲的盘块
int FATFreeItems = -1;
for (int i = 0; i < BLOCKCOUNT; i++) {
if (disk->FAT1[i] == 0) {//没被使用的块标记为0
FATFreeItems = i;//找到了一个空闲块
break;
}
}
//如果FAT没有空闲块,报错退出
if (FATFreeItems == -1) {
printf("FAT IS FULL\n");
return -1;
}
disk->FAT1[pre] = FATFreeItems;
pre = FATFreeItems;
}
}
//如果是覆盖写,或者块内偏移off不等于0,则将blkno的虚拟磁盘块全部写入buff中,否则memset
if (wstyle == 2 || blockoff != 0) {
memcpy(buf, disk->Data[currentblock - 8], 1024);
}
//将text中的内容暂存到缓冲区buff的第off字节开始的位置,直到缓冲区满
for (int i = blockoff; i < 1024 && textpos < textlen && tmplen<len; i++) {
buf[i] = text[textpos];
textpos++;
tmplen++; //读入长度
}
memcpy(disk->Data[currentblock - 8], buf, 1024);
openfilelist[fd].count += tmplen;
}
free(buf);
return tmplen;
}
filewrite.cpp
分析
- 实际的写函数
代码
#include "OS.h"
int open_path(char* dirname);
int dowrite(int fd,char *text,int len, char wstyle);
/*--------------全局变量-------------------------*/
extern char* myvhard;//虚拟磁盘起始地址
extern string currentdir;//当前目录
extern string cmd; //读取指令
extern USEROPEN openfilelist[MAXOPENFILE];//文件打开表
extern USEROPEN *ptrcuridr;//当前目录在文件打开表的位置
extern DISK* disk;//将内容结构化
extern char command[16];//文件名标示符
/*---------------写文件函数----------------*/
int filewrite(int fd) {
int way = 0;
//检查fd的有效性
if (fd >= MAXOPENFILE || fd <= 0) {
printf("filewirt ERROR:Is not a legitimate fd \n");
return -1;
}else if(openfilelist[fd].topenfile==0){
printf("filewrite ERROR:该文件没有被打开\n");
return -1;
}
while (1) {
//提示等待用户输入写方式
printf(" ------Please enter the way to write---------\n ");
//1 : 截断写 2: 覆盖写 3: 追加写
printf(" ------1:TRUNC 2:OVER 3:APPEND---------\n ");
scanf("%d", &way);
if (1 <= way && way <= 3) break;
else printf("Input Error,Please Try Again\n");
}
// 如果是截断写,释放文件除第一块外的磁盘空间内容
//内存用户打开表中文件长度为0,,读写指针置为0
if (way == 1) {
//释放文件除第一块外的磁盘空间内容
int TEMP = 0;
int ok = 1;
for (int p = openfilelist[fd].fcb.first; p != -1; p = TEMP) {
TEMP = disk->FAT1[p];
if (ok != 1) {
disk->FAT1[p] = disk->FAT2[p] = 0;
}
else {
ok = 0;
}
}
//长度置为0
openfilelist[fd].fcb.length = 0;
//读写指针置为0
openfilelist[fd].count = 0;
}
//如果是追加写,修改文件的当前读写指针到文件的末尾
else if (way == 3) {
openfilelist[fd].count = openfilelist[fd].fcb.length;
}
//提示用户,输入内容通过CTRL+Z结束,用户可分多次输入写入内容,每次用回车结束
printf(" Input CTRL+D end the input\n ");
int temp=0;
char buffer[3000];
while(gets(buffer)!=0){
int len=strlen(buffer);
buffer[len]='\n';
buffer[len+1]='\0';
int ret=dowrite(fd,buffer,strlen(buffer),way);
if(ret==-1) {
return -1;
}
else temp+=ret;
}
//如果当前读写指针位置大于长度,则更新长度,并置fcbstate置1
if(openfilelist[fd].count>openfilelist[fd].fcb.length) {
openfilelist[fd].fcb.length = openfilelist[fd].count;
openfilelist[fd].fcbstate=1;
}
//返回实际写入的字节
return temp;
}
doread.cpp
分析
- 每次读一片磁盘
代码
#include "OS.h"
int open_path(char* dirname);
int dowrite(int fd,char *text,int len, char wstyle);
/*--------------全局变量-------------------------*/
extern char* myvhard;//虚拟磁盘起始地址
extern string currentdir;//当前目录
extern string cmd; //读取指令
extern USEROPEN openfilelist[MAXOPENFILE];//文件打开表
extern USEROPEN *ptrcuridr;//当前目录在文件打开表的位置
extern DISK* disk;//将内容结构化
extern char command[16];//文件名标示符
/*------------------实际读文件函数--------------------------*/
//text的指向那个读出数据的用户地址
int doread(int fd,int len,char *text){
//申请1024字节的缓冲区buf
char *buf = (char *) malloc(1024);
if (buf == NULL) {
printf("MALLOC ERROR b\n");
return -1;
}
int tmplen = 0;
int textpos = 0;
//将最终指针转换为逻辑块块号blockno 和 块内偏移blockoff;
while(tmplen<len) {
int blockno = (openfilelist[fd].count) / 1024;
int blockoff = (openfilelist[fd].count) % 1024;
//寻找磁盘块blockno
int currentblock = 0;
int cnt = 0;
for (int p = openfilelist[fd].fcb.first; p != -1; p = disk->FAT1[p]) {
cnt++;
currentblock = p;
if (cnt == blockno + 1)
break;
}
memcpy(buf, disk->Data[currentblock - 8], 1024);
//
for (int i = blockoff; i < 1024 && tmplen<len && openfilelist[i].count<openfilelist[i].fcb.length; i++) {
text[textpos] = buf[i];
textpos++;
tmplen++; //读入长度
openfilelist[fd].count;
}
memcpy(disk->Data[currentblock - 8], buf, 1024);
}
free(buf);
return tmplen;
}
fileread.cpp
分析
- 实际的读函数
代码
#include "OS.h"
int open_path(char* dirname);
int dowrite(int fd,char *text,int len, char wstyle);
int doread(int fd,int len,char *text);
/*--------------全局变量-------------------------*/
extern char* myvhard;//虚拟磁盘起始地址
extern string currentdir;//当前目录
extern string cmd; //读取指令
extern USEROPEN openfilelist[MAXOPENFILE];//文件打开表
extern USEROPEN *ptrcuridr;//当前目录在文件打开表的位置
extern DISK* disk;//将内容结构化
extern char command[16];//文件名标示符
/*---------------读文件函数----------------*/
const int MAXSIZE=1024*50;
int fileread(int fd,int len){
char text[MAXSIZE];
memset(text,0,sizeof(text));
//检查fd的有效性
if (fd >= MAXOPENFILE || fd <= 0) {
printf("filewirt:Is not a legitimate fd \n");
return -1;
}else if(openfilelist[fd].topenfile==0){
printf("filewrite ERROR:The File Don't Open\n");
return -1;
}
//调用do_read()读取指定文件的len字节内容到text[]中.
int rt=doread(fd,len,text);
//如果do_read()返回值为负,则显示出错信息,否则将text[]中的内容显示到屏幕上;
if(rt==-1){
printf("READ FAIL");
return -1;
}else{
//输出text的内容
for(int i=0;i<len;i++){
printf("%c",text[i]);
}
printf("\n");
}
}
exitsys.cpp
分析
- 保存并退出
- 关闭所有打开的文件
代码
#include "OS.h"
int open_path(char* dirname);
int close(int fd);
/*--------------全局变量-------------------------*/
extern char* myvhard;//虚拟磁盘起始地址
extern string currentdir;//当前目录
extern string cmd; //读取指令
extern USEROPEN openfilelist[MAXOPENFILE];//文件打开表
extern USEROPEN *ptrcuridr;//当前目录在文件打开表的位置
extern DISK* disk;//将内容结构化
extern char command[16];//文件名标示符
/*--------------退出文件系统函数------------------*/
void exitsys(){
FILE * fd=fopen("myfsys","w");
//关闭所有打开的文件
for(int i=0;i<MAXOPENFILE;i++){
if(openfilelist[i].topenfile==1)
close(i);
}
fwrite(myvhard,sizeof(char),DISKSIZE,fd);
fclose(fd);
free(myvhard);
exit(0);
}
几个无关紧要的函数
#include "stdio.h"
void help()
{
printf("\n");
printf("-----------------------help------------\n");
printf("format :-------Format The Disk.\n");
printf("exit :-------Exit OS File System AND **NOT SAVE**\n");
printf("exitsys :-------Exit OS File System AND SAVE")
printf("cd dirname :-------Change Directory\n");
printf("mkdir dirname :-------Make Directory.\n");
printf("rmdir dirname :-------Delete Directory.\n");
printf("ls dirname :-------List Directory .\n");
printf("creat filename:-------Creat File\n");
printf("write fd :-------Wirte File\n");
printf("read fd :-------Read File\n");
printf("rm filename:-------Remove File\n");
printf("open filename:-------Open File\n");
printf("close fd :-------Close File\n");
printf("open_path\n");
printf("--------------------------------------\n\n");
}
和自己实现的分割字符串
//
// 切割字符串,例如/A/B/C/D 切割成 /A/B/C 和 D
//
#include "OS.h"
/*--------------全局变量-------------------------*/
extern char* myvhard;//虚拟磁盘起始地址
extern string currentdir;//当前目录
extern string cmd; //读取指令
extern USEROPEN openfilelist[MAXOPENFILE];//文件打开表
extern USEROPEN *ptrcuridr;//当前目录在文件打开表的位置
extern DISK* disk;//将内容结构化
extern char command[50];//文件名标示符
/*-----------------------------------------------*/
int FileSubstr(char *str){
int len=strlen(str);
int cnt=0,flag=0;
for(int i=1;i<len-1;i++)
{
if(str[i]=='/')
{
cnt++;
flag=i;
}
}
if(cnt==0) return -1;
else return flag;
}