[操作系统][简单文件系统实现]

简单文件系统的实现

要求

  • 内存中开辟一块虚拟磁盘空间作为文件存储分区,在其上实现一个简单的基于多级目录单用户单任务系统中的文件系统。

  • 在退出该文件系统的使用时,虚拟文件系统以一个文件的方式保存到磁盘中,以便下次可以把它恢复到内存的虚拟存储空间

实际实现

  • 以上两点均实现

  • 能处理绝对路径和相对路径的命令

    • 例如 :
      • cd /home/zy/Desktop/ 这样的绝对路径
      • cd ../hah/1/2 这样的相对路径
    • mkdir,rmdir,cd,creat,rm均支持
    • open_pathopen的升级版,也是支持上述函数实现的主要函数。
  • 包装open,read,close实现了一个cat直接打印文件内容。

    • 检查文件是否打开,如果打开了直接进行下一步,没有就打开
    • read出所有内容
    • 如果之前不是打开的那么就关闭文件。

截图

  • 建立目录树,/home是用户的根目录

    /home下有/zy

    /zy下有 /Documents,/Desktop,/Viedeos,Music

  • /home/zy/Documents目录下建立一个文件Hello.txt

    • 并输入内容Hello World!Fisrt,能正确显示长度和文件内容。
    • 测试了creat,open,closeread,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
",dir->fcb[i].filename,dir->fcb[i].length);
            else
                printf("%10s---length:%5d----Directory
",dir->fcb[i].filename,dir->fcb[i].length);
        }
    }
}



int main() {
    printf("Welcome the OS FileSystem
");
    printf("input 'help' get more information


");
   // 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
");
        }
    }
}

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("|-------------------------------------------|
");
        printf("|-----------myfsys is loading---------------|
");
        printf("|-------------------------------------------|

");
        fread(myvhard,sizeof(char),DISKSIZE,fp);
        fclose(fp);
    }
    else{
        printf("|-------------------------------------------|
");
        printf("|-----------myfsys is not exit--------------|
");
        printf("|--File system is being created now --------|
");
        printf("|-------------------------------------------|

");
        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
");
        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 
");
        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 !
");
            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
",OpenFileaddr);
    return OpenFileaddr;
}

OPEN_PATH.CPP

分析

  • 让系统函数都能处理绝对路径相对路径,而不仅仅是当前目录下的文件了。

    • cd,mkdir,creaet,rm,rmdir
  • 使用的方式是拆分路径的元素,然后分析元素

    不断的调用openclose来实现。

代码

#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 
");
        return -1;
    }else if(openfilelist[fd].topenfile==0){
        printf("filewrite ERROR:The File Don't Open
");
        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
");
            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
",dirname);
        if(fd!=-1)
            close(fd);
        ptrcuridr=tempp;
        return 0;
    }
    //如果没有空闲位置,报错退出
    if(DirFreeItems==-1)
    {
        printf("mkdir: cannot create directory '%s': Directory is full 
",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 
",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
");
            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
",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
",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
");
            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 
");
        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
");
            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
",filename);
        return 0;
    }
    //如果没有空闲位置,报错退出
    if(DirFreeItems==-1)
    {
        printf("creat: cannot create file '%s': Directory is full 
",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 
",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
",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
");
            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
",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 !
");
            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
");
        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
");
                    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 
");
        return -1;
    }else if(openfilelist[fd].topenfile==0){
        printf("filewrite ERROR:该文件没有被打开
");
        return -1;
    }
    while (1) {
        //提示等待用户输入写方式
        printf(" ------Please enter the way to write---------
 ");
        //1 : 截断写 2: 覆盖写 3: 追加写
        printf(" ------1:TRUNC 2:OVER 3:APPEND---------
 ");
        scanf("%d", &way);
        if (1 <= way && way <= 3) break;
        else printf("Input Error,Please Try Again
");
    }
    // 如果是截断写,释放文件除第一块外的磁盘空间内容
    //内存用户打开表中文件长度为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
 ");
    int temp=0;
    char buffer[3000];
    while(gets(buffer)!=0){
        int len=strlen(buffer);
        buffer[len]='
';
        buffer[len+1]='';
        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
");
        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 
");
        return -1;
    }else if(openfilelist[fd].topenfile==0){
        printf("filewrite ERROR:The File Don't Open
");
        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("
");
    }
}

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("
");
    printf("-----------------------help------------
");
    printf("format         :-------Format The Disk.
");
    printf("exit           :-------Exit OS File System AND **NOT SAVE**
");
    printf("exitsys        :-------Exit OS File System AND SAVE")
    printf("cd     dirname :-------Change Directory
");
    printf("mkdir  dirname :-------Make Directory.
");
    printf("rmdir  dirname :-------Delete Directory.
");
    printf("ls     dirname :-------List Directory .
");
    printf("creat  filename:-------Creat File
");
    printf("write  fd      :-------Wirte File
");
    printf("read   fd      :-------Read File
");
    printf("rm     filename:-------Remove File
");
    printf("open   filename:-------Open File
");
    printf("close  fd      :-------Close File
");
    printf("open_path
");
    printf("--------------------------------------

");
}

和自己实现的分割字符串

//
// 切割字符串,例如/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;
}
原文地址:https://www.cnblogs.com/zy691357966/p/5565699.html