Linux 文件系统学习之 EROFS 源码阅读笔记

本文是作者通过阅读Linux EROFS源码进行的学习笔记,详细介绍了如何使用erofs-utils进行环境配置、工具编译及压缩,以及在VSCode中进行调试。文章深入探讨了文件系统的格式化流程,包括main函数、压缩初始化、文件系统内容组织形式、块写入管理和目录文件、普通文件的构建。作者通过跟踪执行流程,解析了文件系统构建过程中的关键步骤,如z_erofs_compress_init、erofs_mkfs_build_tree_from_path等,以及文件数据的压缩和写入机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

学 C 语言已有两个多月,还没尝试过调试一个完整的项目。故借 erofs-utils 项目实战一下,记录一些调试笔记,并对 erofs 文件系统根据源码进行更近一步的梳理

erofs-utils 使用

笔者使用的主机环境为 Ubuntu 18.04,可正常运行

环境配置

可以先查阅 linux 官方文档 https://www.kernel.org/doc/html/latest/filesystems/erofs.html

其中提供了 erofs-utils 的地址

git://git.kernel.org/pub/scm/linux/kernel/git/xiang/erofs-utils.git

clone 到本地后,根据 README 可以进行使用。由于 erofs 默认支持 lz4 压缩算法,因此需要安装相应的库,不然 .configure 时会关闭 lz4

sudo apt-get install liblz4-dev

工具编译

	$ ./autogen.sh
	$ ./configure
	$ make

进行压缩

先生成一个 img 为 erofs_disk,并创建测试所用待压缩路径 /home/srcd

# dd if=/dev/zero of=/home/erofs_disk bs=512 count=23000

# mkdir /home/srcd
# cp README /home/srcd
# cp COPYING /home/srcd
# cp ChangeLog /home/srcd
# cp Makefile /home/srcd

使用 lz4 进行压缩

# ./mkfs/mkfs.erofs -zlz4 /home/erofs_disk /home/srcd/

将 img 挂载到某个路径下进行查看

mount -t erofs /home/erofs_disk /mnt/scratch -oloop

vscode 配置

我们需要对 vscode 进行配置,使其能够对 mkfs.erofs 进行调试

安装 C/C++ 插件(这部分不赘述)

在 {workspace} 中创建 .vscode 目录,并在其下创建 launch.json。笔者没有编写 task.json。而是每次手动进行 make

{
   
    "version": "0.2.0",
    "configurations": [
 
        {
   
            "name": "(gdb) Launch",// 配置名称,将会在启动配置的下拉菜单中显示
            "type": "cppdbg",// 配置类型,这里只能为cppdbg
            "request": "launch",// 请求配置类型,可以为launch(启动)或attach(附加)
            "program": "${workspaceRoot}/linux_learn/new_erofs-utils/erofs-utils/mkfs/mkfs.erofs",// 将要进行调试的程序的路径
            "stopAtEntry": true, // 设为true时程序将暂停在程序入口处,我一般设置为true
            "cwd": "${workspaceRoot}",// 调试程序时的工作目录
            "environment": [],// (环境变量?)
            "externalConsole": false,// 调试时是否显示控制台窗口,vscode自带控制台
            "MIMode": "gdb",// 指定连接的调试器,可以为gdb或lldb。
            //"preLaunchTask": "shell" // 调试会话开始前执行的任务,一般为编译程序。
            //与tasks.json的taskName相对应,可根据需求选择是否使用,本文不需要。
            "args": ["-zlz4","/home/erofs_disk","/home/srcd/"]
        }
    ]
}

至此,便能够开始调试了,运行-》启动调试。vscode 便会进入 main 函数,如下所示

在这里插入图片描述

跟踪执行流程

上一篇文章只是根据源码对 erofs 格式化流程进行了大致梳理,在此,笔者通过跟踪的方式,进一步细化对 erofs 的学习,整个过程以下述命令为实际例子。

# ./mkfs/mkfs.erofs -zlz4 /home/erofs_disk /home/srcd/

main

mkfs/main.c ---> main
    --- erofs_init_configure
    --- erofs_mkfs_default_options
    --- mkfs_parse_options_cfg
    --- dev_open
    --- erofs_set_fs_root
    --- erofs_buffer_init
    --- erofs_load_compress_hints
    --- z_erofs_compress_init ()
    	--- erofs_compressor_init
    		--- compressors[i]->init(c)
    --- erofs_generate_devtable
    --- erofs_inode_manager_init
    --- erofs_build_shared_xattrs_from_path
    --- erofs_mkfs_build_tree_from_path ()
    	--- erofs_iget_from_path : 初始化文件系统结构
    	--- erofs_mkfs_build_tree : 压缩文件
    --- erofs_lookupnid()
    --- erofs_mkfs_update_super_block()

由于初始化部分比较简单,笔者主要跟踪后几个方法的执行流程

z_erofs_compress_init

该方法主要调用 erofs_compressor_init 用于初始化 compressors。后续压缩过程需要调用 compressors 的 compress 方法 。 由于命令行传入了 lz4 参数,因此对应 compressors 中只有一个元素,即 erofs_compressor_lz4

static struct erofs_compressor *compressors[] = {
   
#if LZ4_ENABLED
#if LZ4HC_ENABLED
		&erofs_compressor_lz4hc,
#endif
		&erofs_compressor_lz4,
#endif
#if HAVE_LIBLZMA
		&erofs_compressor_lzma,
#endif
};

erofs_mkfs_build_tree_from_path

该函数是最核心的函数,我们一步一步跟踪,对于部分参数,笔者直接替换为真实值

  1. 通过 erofs_iget_from_path 为 “/home/srcd” 创建目录文件 inode。该目录文件对应的是 erofs 文件系统的根目录 / ,其对应了源文件系统的 “/home/srcd” 目录。
  2. 将该 inode 的 parent 指向自己,说明自己是根目录
  3. 调用 erofs_mkfs_build_tree 递归地为根目录创建子目录及文件,并一一对应 “/home/srcd” 下的子目录和文件
struct erofs_inode *erofs_mkfs_build_tree_from_path(struct erofs_inode *parent,
						    const char *path)
{
   
    // *parent = NULL,path = "/home/srcd"
	struct erofs_inode *const inode = erofs_iget_from_path(path, true);

	if (IS_ERR(inode))
		return inode;

	/* a hardlink to the existed inode */
	if (inode->i_parent) {
   
		++inode->i_nlink;
		return inode;
	}

	/* a completely new inode is found */
	if (parent)
		inode->i_parent = parent;
	else
		inode->i_parent = inode;	/* rootdir mark */

	return erofs_mkfs_build_tree(inode);
}

具体地,在执行 erofs_iget_from_path 的过程中,有如下流程

  1. 通过 lstat64 解析 path,可以快速获知当前 path 是目录还是文件
  2. “/home/srcd” 是目录,因此不会执行 erofs_iget 而直接调用 erofs_new_inode 创建一个新的 inode
  3. 通过 erofs_fill_inode 对新 inode 进行初始化
static struct erofs_inode *erofs_iget_from_path(const char *path, bool is_src)
{
   
	struct stat64 st;
	struct erofs_inode *inode;
	int ret;

	/* currently, only source path is supported */
	if (!is_src)
		return ERR_PTR(-EINVAL);

	ret = lstat64(path, &st);
	if (ret)
		return ERR_PTR(-errno);

	/*
	 * lookup in hash table first, if it already exists we have a
	 * hard-link, just return it. Also don't lookup for directories
	 * since hard-link directory isn't allowed.
	 */
	if (!S_ISDIR(st.st_mode)) {
   
		inode = erofs_iget(st.st_dev, st.st_ino);
		if (inode)
			return inode;
	}

	/* cannot find in the inode cache */
	inode = erofs_new_inode();
	if (IS_ERR(inode))
		return inode;

	ret = erofs_fill_inode(inode, &st, path);
	if (ret) {
   
		free(inode);
		return ERR_PTR(ret);
	}

	return inode;
}

在 erofs_fill_inode 中,主要就是装填 inode 的属性。此时,也将 path 设入 inode 的 srcpath 中,建立了源文件系统与目标文件系统的映射关系。

最后,由于是新的 inode 。需要将其插入 inode_hashtable 中。inode_hashtable 可以理解为 inode 的缓存,便于加速 inode 的分配与释放。

static int erofs_fill_inode(struct erofs_inode *inode,
			    struct stat64 *st,
			    const char *path)
{
   
	// 省略部分代码
	inode->i_mode = st->st_mode;
	inode->i_uid = cfg.c_uid == -1 ? st->st_uid : cfg.c_uid;
	inode->i_gid = cfg.c_gid == -1 ? st->st_gid : cfg.c_gid;
	inode->i_ctime = st->st_ctime;
	inode->i_ctime_nsec = ST_CTIM_NSEC(st);
	// 省略部分代码
    inode->i_ctime = sbi.build_time;
    inode->i_ctime_nsec = sbi.build_time_nsec;
	
#include <stdio.h> #include <memory.h> #include <string> #include <iostream> using namespace std; //1代表普通文件2代表目录文件0表示空文件 #define GENERAL 1 #define DIRECTORY 2 #define HXSH 0 struct FCB { char fname[16]; //文件名 char type; int size; //文件大小 int fatherBlockNum; //当前的父目录盘块号 int currentBlockNum; //当前的盘块 void initialize() { strcpy(fname,"\0"); type = HXSH; size =0; fatherBlockNum = currentBlockNum = 0; } }; /*常量设置*/ const char* FilePath = "C:\\myfiles"; const int BlockSize = 512; //盘块大小 const int OPEN_MAX = 5; //能打开最多的文件数 const int BlockCount = 128; //盘块数 const int DiskSize = BlockSize*BlockCount; //磁盘大小 const int BlockFcbCount = BlockSize/sizeof(FCB);//目录文件的最多FCB数 int OpenFileCount = 0; struct OPENLIST //用户文件打开表 { int files; //当前打开文件数 FCB f[OPEN_MAX]; //FCB拷贝 OPENLIST() { files=0; for(int i=0;i<OPEN_MAX;i++){ f[i].fatherBlockNum=-1;//为分配打开 f[i].type=GENERAL; } } }; /*-------------目录文件结构---------------*/ struct dirFile { struct FCB fcb[BlockFcbCount]; void init(int _FatherBlockNum,int _CurrentBlockNum,char *name)//父块号,当前块号,目录名 { strcpy(fcb[0].fname,name); //本身的FCB fcb[0].fatherBlockNum=_FatherBlockNum; fcb[0].currentBlockNum=_CurrentBlockNum; fcb[0].type=DIRECTORY; //标记目录文件 for(int i=1;i<BlockFcbCount;i++){ fcb[i].fatherBlockNum=_CurrentBlockNum; //标记为子项 fcb[i].type=HXSH; // 标记为空白项 } } }; /**********************************************************************/ struct DISK { int FAT1[BlockCount]; //FAT1 int FAT2[BlockCount]; //FAT2 struct dirFile root; //根目录 char data[BlockCount-3][BlockSize]; void format() { memset(FAT1,0,BlockCount); //FAT1 memset(FAT2,0,BlockCount); //FAT2 FAT1[0]=FAT1[1]=FAT1[2]=-2; //0,1,2盘块号依次代表FAT1,FAT2,根目录区 FAT2[0]=FAT2[1]=FAT2[2]=-2; //FAT作备份 root.init(2,2,"C:\\");//根目录区 memset(data,0,sizeof(data));//数据区 } }; /*-----------------全局变量--------------------------*/ FILE *fp; //磁盘文件地址 char * BaseAddr; //虚拟磁盘空间基地址 string currentPath="C:\\\\"; //当前路径 int current=2; //当前目录的盘块号 string cmd; //输入指令 struct DISK *osPoint; //磁盘操作系统指针 char command[16]; //文件名标识 struct OPENLIST* openlist; //用户文件列表指针 /*-----------函数事先申明--------------------*/ int format(); int mkdir(char *sonfname); int rmdir(char *sonfname); int create(char *name); int listshow(); int delfile(char *name); int changePath(char *sonfname); int write(char *name); int exit(); int open(char *file); int close(char *file); int read(char *file); /*------------初始化-----------------------*/ int format() { current = 2; currentPath="C:\\\\"; //当前路径 osPoint->format();//打开文件列表初始化 delete openlist; openlist=new OPENLIST; /*-------保存到磁盘上myfiles--------*/ fp = fopen(FilePath,"w+"); fwrite(BaseAddr,sizeof(char),DiskSize,fp); fclose(fp); printf("----------------------------------------------------------\n\n"); return 1; } /*-----------------------创建子目录-------------------*/ int mkdir(char *sonfname) { //判断是否有重名 //寻找空白子目录项 //寻找空白盘块号 //当前目录下增加该子目录项 //分配子目录盘块,并且初始化 //修改fat表 int i,temp,iFAT; struct dirFile *dir; //当前目录的指针 if(current==2) dir=&(osPoint->root); else dir=(struct dirFile *)(osPoint->data [current-3]); /*--------为了避免该目录下同名文件夹--------*/ for(i = 1;i<BlockFcbCount;i++) { if(dir->fcb[i].type==DIRECTORY && strcmp(dir->fcb[i].fname,sonfname)==0 ) { printf("该文件夹下已经有同名的文件夹存在了!\n"); return 0; } } //查找空白fcb序号 for(i=1;i<BlockFcbCount;i++) { if(dir->fcb[i].type==HXSH) break; } if(i==BlockFcbCount) { printf("该目录已满!请选择新的目录下创建!\n"); return 0; } temp=i; for(i = 3;i < BlockCount;i++) { if(osPoint->FAT1[i] == 0) break; } if(i == BlockCount) { printf("磁盘已满!\n"); return 0; } iFAT=i; /*-------------接下来进行分配----------*/ osPoint->FAT1[iFAT]=osPoint->FAT2[iFAT] = 2; //2表示分配给下级目录文件 //填写该分派新的盘块的参数 strcpy(dir->fcb[temp].fname,sonfname); dir->fcb[temp].type=DIRECTORY; dir->fcb[temp].fatherBlockNum=current; dir->fcb[temp].currentBlockNum=iFAT; //初始化子目录文件盘块 dir=(struct dirFile*)(osPoint->data [iFAT-3]); //定位到子目录盘块号 dir->init (current,iFAT,sonfname);//iFAT是要分配的块号,这里的current用来指要分配的块的父块号 printf("---------------------------------------------------------------\n\n"); return 1; } /*-------删除当前目录下的文件夹--------*/ int rmdir(char *sonfname) { int i,temp,j;//确保当前目录下有该文件,并记录下该FCB下标 struct dirFile *dir; //当前目录的指针 if(current==2) dir=&(osPoint->root); else dir=(struct dirFile *)(osPoint->data [current-3]); for(i=1;i<BlockFcbCount;i++) { //查找该目录文件 if(dir->fcb[i].type==DIRECTORY && strcmp(dir->fcb[i].fname,sonfname)==0) { break; } } temp=i; if(i==BlockFcbCount) { printf("当前目录下不存在该子目录!\n"); return 0; } j = dir->fcb[temp].currentBlockNum; struct dirFile *sonDir; //当前子目录的指针 sonDir=(struct dirFile *)(osPoint->data [ j - 3]); for(i=1;i<BlockFcbCount;i++) //查找子目录是否为空目录 { if(sonDir->fcb[i].type!=HXSH) { printf("该文件夹为非空文件夹,为确保安全,请清空后再删除!\n"); return 0; } } /*开始删除子目录操作*/ osPoint->FAT1[j] = osPoint->FAT2[j]=0; //fat清空 char *p=osPoint->data[j-3]; //格式化子目录 memset(p,0,BlockSize); dir->fcb[temp].initialize(); //回收子目录占据目录项 printf("---------------------------------------------------------------\n\n"); return 1; } /*-----------在当前目录下创建文本文件---------------*/ int create(char *name) { int i,iFAT;//temp, int emptyNum = 0,isFound = 0; //空闲目录项个数 struct dirFile *dir; //当前目录的指针 if(current==2) dir=&(osPoint->root); else dir=(struct dirFile *)(osPoint->data [current-3]); //查看目录是否已满 //为了避免同名的文本文件 for(i=1;i<BlockFcbCount;i++) { if(dir->fcb[i].type == HXSH && isFound == 0) { emptyNum = i; isFound = 1; } else if(dir->fcb[i].type==GENERAL && strcmp(dir->fcb[i].fname,name)==0 ) { printf("无法在同一目录下创建同名文件!\n"); return 0; } } if(emptyNum == 0) { printf("已经达到目录项容纳上限,无法创建新目录!\n"); return 0; } //查找FAT表寻找空白区,用来分配磁盘块号j for(i = 3;i<BlockCount;i++) { if(osPoint->FAT1[i]==0) break; } if(i==BlockCount) { printf("磁盘已满!\n"); return 0; } iFAT=i; /*------进入分配阶段---------*/ //分配磁盘块 osPoint->FAT1[iFAT] = osPoint->FAT2[iFAT] = 1; /*-----------接下来进行分配----------*/ //填写该分派新的盘块的参数 strcpy(dir->fcb[emptyNum].fname,name); dir->fcb[emptyNum].type=GENERAL; dir->fcb[emptyNum].fatherBlockNum=current; dir->fcb[emptyNum].currentBlockNum=iFAT; dir->fcb[emptyNum].size =0; char* p = osPoint->data[iFAT -3]; memset(p,4,BlockSize); printf("----------------------------------------------------------------\n\n"); return 1; } /*-------查询子目录------------*/ int listshow() { int i,DirCount=0,FileCount=0; //搜索当前目录 struct dirFile *dir; //当前目录的指针 if(current==2) dir=&(osPoint->root); else dir=(struct dirFile *)(osPoint->data [current-3]); for(i=1;i<BlockFcbCount;i++) { if(dir->fcb[i].type==GENERAL) { //查找普通文件 FileCount++; printf("%s 文本文件.\n",dir->fcb[i].fname); } if(dir->fcb[i].type==DIRECTORY) { //查找目录文件 DirCount++; printf("%s 文件夹.\n",dir->fcb[i].fname); } } printf("\n该目录下共有 %d 个文本文件, %d 个文件夹\n\n",FileCount,DirCount); printf("--------------------------------------------------------\n\n"); return 1; } /*---------在当前目录下删除文件-----------*/ int delfile(char *name) { int i,temp,j; //确保当前目录下有该文件,并且记录下它的FCB下标 struct dirFile *dir; //当前目录的指针 if(current==2) dir=&(osPoint->root); else dir=(struct dirFile *)(osPoint->data [current-3]); for(i=1;i<BlockFcbCount;i++) //查找该文件 { if(dir->fcb[i].type==GENERAL && strcmp(dir->fcb[i].fname,name)==0) { break; } } if(i==BlockFcbCount) { printf("当前目录下不存在该文件!\n"); return 0; } int k; for(k=0;k<OPEN_MAX;k++) { if((openlist->f [k].type = GENERAL)&& (strcmp(openlist->f [k].fname,name)==0)) { if(openlist->f[k].fatherBlockNum == current) { break; } else { printf("该文件未在当前目录下!\n"); return 0; } } } if(k!=OPEN_MAX) { close(name); } //从打开列表中删除 temp=i; /*开始删除文件操作*/ j = dir->fcb [temp].currentBlockNum ; //查找盘块号j osPoint->FAT1[j]=osPoint->FAT2[j]=0; //fat1,fat2表标记为空白 char *p=osPoint->data[j - 3]; memset(p,0,BlockSize); //清除原文本文件的内容 dir->fcb[temp].initialize(); //type=0; //标记该目录项为空文件 printf("------------------------------------------------------------\n\n"); return 1; } /*--------------进入当前目录下的子目录--------------*/ int changePath(char *sonfname) { struct dirFile *dir; //当前目录的指针 if(current==2) dir=&(osPoint->root); else dir=(struct dirFile *)(osPoint->data [current-3]); /*回到父目录*/ if(strcmp(sonfname,"..")==0) { if(current==2) { printf("你现已经在根目录下!\n"); return 0; } current = dir->fcb[0].fatherBlockNum ; currentPath = currentPath.substr(0,currentPath.size() - strlen(dir->fcb[0].fname )-1); return 1; } /*进入子目录*/ int i,temp; //确保当前目录下有该目录,并且记录下它的FCB下标 for(i = 1; i < BlockFcbCount; i++) { //查找该文件 if(dir->fcb[i].type==DIRECTORY && strcmp(dir->fcb[i].fname,sonfname)==0 ) { temp=i; break; } } if(i==BlockFcbCount) { printf("不存在该目录!\n"); return 0; } //修改当前文件信息 current=dir->fcb [temp].currentBlockNum ; currentPath = currentPath+dir->fcb [temp].fname +"\\"; printf("-------------------------------------------------------------\n\n"); return 1; } /*--------System exit---------------------*/ int exit() { //将所有文件都关闭 //保存到磁盘上C:\myfiles fp=fopen(FilePath,"w+"); fwrite(BaseAddr,sizeof(char),DiskSize,fp); fclose(fp); //释放内存上的虚拟磁盘 free(osPoint); //释放用户打开文件表 delete openlist; printf("---------------------------------------------------------\n\n"); return 1; } /*-------------在指定的文件里记录信息---------------*/ int write(char *name) { int i; char *startPoint,*endPoint; //在打开文件列表中查找 file(还需要考虑同名不同目录文件的情况!!!) for(i=0;i<OPEN_MAX;i++) { if(strcmp(openlist->f [i].fname,name)==0 ) { if(openlist->f[i].fatherBlockNum ==current) { break; } else { printf("该文件处于打开列表中,本系统只能改写当前目录下文件!\n"); return 0; } } } if(i==OPEN_MAX) { printf("该文件尚未打开,请先打开后写入信息!!\n"); return 0; } int active=i; int fileStartNum = openlist->f[active].currentBlockNum - 3 ; startPoint = osPoint->data[fileStartNum]; endPoint = osPoint->data[fileStartNum + 1]; printf("请输入文本以Ctrl D号结束:\t"); char input; while(((input=getchar())!=4)) { if(startPoint < endPoint-1) { *startPoint++ = input; } else { printf("达到单体文件最大容量!"); *startPoint++ = 4; break; } } return 1; } /*---------选择一个打开的文件读取信息----------*/ int read(char *file) { int i,fileStartNum; char *startPoint,*endPoint; //struct dirFile *dir; //在打开文件列表中查找 file(还需要考虑同名不同目录文件的情况!!!) for(i=0;i<OPEN_MAX;i++) { if(strcmp(openlist->f [i].fname,file)==0 ) { if(openlist->f[i].fatherBlockNum ==current) { break; } else { printf("该文件处于打开列表中,本系统只能阅读当前目录下文件!\n"); return 0; } } } if(i==OPEN_MAX) { printf("该文件尚未打开,请先打开后读取信息!\n"); return 0; } int active=i; //计算文件物理地址 fileStartNum = openlist->f[active].currentBlockNum - 3 ; startPoint = osPoint->data[fileStartNum]; endPoint = osPoint->data[fileStartNum + 1]; //end_dir=(struct dirFile *)[BlockSize-1]; //q=(char *)end_dir; printf("该文件的内容为: "); while((*startPoint)!=4&& (startPoint < endPoint)) { putchar(*startPoint++); } printf("\n"); return 1; } /*当前目录下添加一个打开文件*/ int open(char *file)//打开文件 { int i,FcbIndex; //确保没有打开过该文件 = 相同名字 + 相同目录 for(i=0;i<OPEN_MAX;i++) { if(openlist->f[i].type ==GENERAL && strcmp(openlist->f [i].fname,file)==0 &&openlist;->f[i].fatherBlockNum == current) { printf("该文件已经被打开!\n"); return 0; } } //确保有空的打开文件项 if(openlist->files == OPEN_MAX) { printf("打开文件数目达到上限!无法再打开新文件.\n"); return 0; } //确保当前目录下有该文件,并且记录下它的FCB下标 struct dirFile *dir; //当前目录的指针 if(current==2) dir=&(osPoint->root); else dir=(struct dirFile *)(osPoint->data [current-3]); for(i = 1;i< BlockFcbCount;i++) { //查找该文件 if(dir->fcb[i].type==GENERAL && strcmp(dir->fcb[i].fname,file)==0 ) { FcbIndex=i; break; } } if(i==BlockFcbCount) { printf("当前目录下不存在该文件!\n"); return 0; } //装载新文件进入打开文件列表,(FCB信息,文件数++) ??难道名字过不来? openlist->f[OpenFileCount] = dir->fcb[FcbIndex]; //FCB拷贝 openlist->files ++; printf("文件打开成功!\n"); OpenFileCount++; return 1; } int close(char *file) { //释放该文件所占内存 //释放用户打开文件列表表项 int i; //在打开文件列表中查找 file(还需要考虑同名不同目录文件的情况!!!) for(i=0;i<OPEN_MAX;i++) { if((openlist->f [i].type = GENERAL)&& (strcmp(openlist->f [i].fname,file)==0)) { if(openlist->f[i].fatherBlockNum == current) { break; } else { printf("该文件已打开,但未在当前目录下,无法关闭!\n"); return 0; } } } if(i==OPEN_MAX) { printf("该文件未在打开列表中!\n"); return 0; } int active=i; openlist->files --; openlist->f[active].initialize(); OpenFileCount--; printf("该文件已关闭!\n"); return 1; } int main() { /*********************************************************************/ printf("-----Welcome To My Operate System Of File(FAT)-----\n"); //使用说明书 printf("\n 以下是使用说明书:\n"); printf("--------------------------------------------------------------\n"); printf("|| format :对磁盘格式化. || \n"); printf("|| exit :安全退出该文件系统,保存信息. || \n"); printf("|| mkdir dirname :创建子目录. || \n"); printf("|| rmdir dirname :删除子目录. || \n"); printf("|| ls dirname :显示当前目录下信息. || \n"); printf("|| cd dirname :更改当前目录. || \n"); printf("|| create filename :创建一个新文件,并且打开. || \n"); printf("|| write filename :选择一个打开的文件写入信息 || \n"); printf("|| read filename :选择一个打开的文件读取信息. || \n"); printf("|| rm filename :删除文件. || \n"); printf("|| open filename :打开文件. || \n"); printf("|| close filename :关闭文件. || \n"); printf("-------------------------------------------------------------\n\n"); //创建用户文件打开表 openlist=new OPENLIST; //申请虚拟空间并且初始化 BaseAddr=(char *)malloc(DiskSize); //虚拟磁盘初始化 osPoint=(struct DISK *)(BaseAddr); //加载磁盘文件 if((fp=fopen(FilePath,"r"))!=NULL){ fread(BaseAddr,sizeof(char),DiskSize,fp); printf("加载磁盘文件( %s )成功,现在可以进行操作了!\n\n",FilePath); } else{ printf("这是你第一次使用该文件管理系统!\t正在初始化...\n"); format(); printf("初始化已经完成,现在可以进行操作了!\n\n"); } printf("--------------------------------------------------------------\n\n"); while(1){ cout<<currentPath; cin>>cmd; if(cmd=="format"){ format(); } else if(cmd=="mkdir"){ cin>>command; mkdir(command); } else if(cmd=="rmdir"){ cin>>command; rmdir(command); } else if(cmd=="ls"){ listshow(); } else if(cmd=="cd"){ cin>>command; changePath(command); } else if(cmd=="create"){ cin>>command; create(command); } else if(cmd=="write"){ cin>>command; write(command); } else if(cmd=="read"){ cin>>command; read(command); } else if(cmd=="rm"){ cin>>command; delfile(command); } else if(cmd=="open"){ cin>>command; open(command); } else if(cmd=="close"){ cin>>command; close(command); } else if(cmd=="exit"){ exit(); break; } else cout<<"无效指令,请重新输入:"<<endl; } printf("Thank you for using my file system!\n"); return 0; }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值