簡單文件系統的實現
要求
-
內存中開辟一塊
虛擬磁盤空間作為文件存儲分區,在其上實現一個簡單的基於多級目錄的單用戶單任務系統中的文件系統。 -
在退出該文件系統的使用時,虛擬文件系統以一個文件的方式保存到磁盤中,以便下次可以把它恢復到內存的虛擬存儲空間
實際實現
-
以上兩點均實現
-
能處理絕對路徑和相對路徑的命令
- 例如 :
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_pathso 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;
}
