转载自:我的个人博客
此文件系统是基于FUSE架构的EXT2文件系统,可在linux上运行。此文件系统用于为操作系统提供文件在特定存储设备上的组织、访问支持,可管理调度设备的存储空间,并实现了文件从标识到实际地址的映射,文件的控制存取。
文件系统的总体功能为:挂载(mount)、卸载(unmount)、创建文件/文件夹(touch/mkdir)、查看文件夹下的文件(ls)。
整体布局
系统主要分为五个部分:
- 超级块:包含系统整体信息。
- 索引节点位图:记录所有索引节点的使用情况,1bit对应一个索引节点。
- 数据块位图:记录所有数据块的使用情况,1bit对应一个数据块。
- 索引节点:记录文件的原数据,和一个文件对应。
- 数据块:记录文件的内容。分配时不一定连续分配。
数据/存储结构说明
超级块的内存结构:
struct newfs_super {
int driver_fd; // 磁盘对应的文件描述符
int max_ino; // 所能容纳的最大文件数量
int sz_io; // 与磁盘数据交换的块大小
int sz_block; // 文件系统的块大小
int sz_disk; // 磁盘的容量大小
uint8_t *map_inode; // inode位图
uint8_t *map_data; // data位图
int map_inode_blks; // inode 位图占用的块数
int map_inode_offset; // inode 位图在磁盘上的偏移
int map_data_blks; // data 位图占用的块数
int map_data_offset; // data 位图在磁盘上的偏移
struct newfs_dentry *root_dentry; // 根目录dentry
int inode_offset; // inode块在磁盘上的偏移
int data_offset; // data块在磁盘上的偏移
int sz_usage; // 已用空间
boolean is_mounted;
};
索引项inode的内存结构:
struct newfs_inode {
int ino; // 在inode位图中的下标
int size; // 文件已占用空间
int dir_cnt; // 目录项数量
struct newfs_dentry *dentry; // 指向该inode的dentry
struct newfs_dentry *dentrys; // 所有目录项
int block_pointer[6]; // 数据块指针
uint8_t* data[6]; // 对应数据块中存储的内容
};
目录项dentry的内存结构:
struct newfs_dentry {
char fname[MAX_NAME_LEN];
uint32_t ino; // 在inode位图中的下标
struct newfs_inode* inode;
struct newfs_dentry* parent;
struct newfs_dentry* brother;
FILE_TYPE ftype; // 指向的 ino 文件类型
int valid; // 该目录项是否有效
};
超级块的磁盘结构:
struct newfs_super_d
{
uint32_t magic_num; // 验证磁盘是否已经被挂载的幻数
int max_ino; // 最多支持的文件数
int map_inode_blks; // inode 位图占用的块数
int map_inode_offset; // inode 位图在磁盘上的偏移
int map_data_blks; // data 位图占用的块数
int map_data_offset; // data 位图在磁盘上的偏移
int inode_offset; // inode块在磁盘上的偏移
int data_offset; // data块在磁盘上的偏移
int sz_usage; // 已用空间
};
索引项inode的磁盘结构:
struct newfs_inode_d
{
uint32_t ino; // 在 inode 位图中的下标
int size; // 文件已占用空间
FILE_TYPE ftype; // 文件类型(目录类型、普通文件类型)
int dir_cnt; // 如果是目录类型文件,下面有几个目录项
int block_pointer[6]; // 数据块指针(可固定分配)
};
目录项dentry的磁盘结构:
struct newfs_dentry_d
{
char fname[SFS_MAX_FILE_NAME]; // 文件名
FILE_TYPE ftype; // 文件格式
int ino; // 在inode位图中的下标
};
功能点说明
mount
- 将文件系统的全局参数newfs_option作为参数,调用newfs_utils.c中的fs_mount函数,实现文件系统的挂载,其具体执行流程如下。
- fs_mount函数首先调用ddriver_open,打开磁盘文件,并将文件描述符存储到文件系统的超级块中。
- 从磁盘中读取磁盘容量,io块大小,数据块大小,并将其存储到超级块中。
- 从磁盘的超级块中读取文件系统的超级块信息到超级块的磁盘表示结构中。
- 调用new_dentry函数,创建根节点的目录项。
- 若磁盘的超级块中的幻数不正确,即磁盘从未被挂载,则根据之前读取到的块大小信息,计算超级块/索引节点位图/数据块位图/索引块/数据块占用的磁盘块数,在磁盘中的位置偏移。将计算的结果存储到超级块的磁盘存储结构/内存存储结构中。
- 从磁盘中读取索引节点位图、数据块位图。
- 调用fs_alloc_inode函数,为根节点的目录项创建相应的索引节点,并使根节点的目录项指向词索引节点。并将超级块中的根目录指针指向此目录项。
- 调用fs_dump_map打印此时的索引节点位图和数据块位图分布。
unmount
- 调用newfs_utils.c中的fs_unmount函数,实现文件系统的卸载,其具体执行流程如下。
- 首先判断超级块中的挂载标识符,若文件系统未被挂载,则直接错误返回。
- 调用fs_sync_inode函数,从根节点向下刷写节点,将文件和文件夹中的信息写回到磁盘。
- 将超级块内存表示中的信息复制到超级块的磁盘表示中,并将超级块的磁盘表示中存储的信息写回磁盘。
- 将索引节点位图、数据块位图写回到磁盘中。
- 释放内存中索引节点位图、数据块位图占用的内存空间。
- 关闭磁盘的文件描述符。
touch
- 调用newfs_getattr函数,获取当前目录的属性。
- 调用newfs_getattr函数,获取待创建文件的属性(判断文件是否已经存在)。
- 若待创建的文件已存在,则跳过创建文件的步骤,等待下一条命令。
- 若待创建的文件不存在,则调用newfs_mknod创建文件,newfs_mknod函数执行流程如下。
- 调用fs_lookup,解析文件路径,并得到待创建文件的上级目录的目录项。
- 如果fs_lookup找到了要创建的文件,即要创建的文件已存在,则错误返回。
- 调用fs_get_fname函数,解析文件的文件名(不含上级目录)。
- 创建待创建文件的目录项,并将其父目录项指针指向上级目录
- 调用fs_alloc_inode函数,为此文件分类索引节点,并使目录项指向索引节点。
- 调用fs_alloc_dentry函数,为上级目录创建索引节点,将其存储到内存中。
- 调用newfs_getattr函数,获取创建的文件的属性。若属性获取成功,则意味着文件创建成功。
mkdir
- 调用newfs_getattr函数,获取当前目录的属性。
- 调用newfs_getattr函数,获取待创建目录的属性(判断目录是否已经存在)。
- 若待创建的目录已存在,则提示错误信息:目录已存在。
- 若目录不存在,则调用newfs_mkdir创建目录。newfs_mkdir函数执行流程如下。
- 调用fs_lookup函数,解析文件路径,并得到上级目录的目录项。
- 如果fs_lookup函数找到了要创建的文件夹,即要创建的文件夹已存在,则错误返回。
- 如果上级目录的文件属性为文件,则无法再创建下级目录,错误返回。
- 调用fs_get_fname函数,解析目录的名字。
- 调用new_dentry函数,创建目录的目录项,并将其父目录项指针指向上级目录的目录项
- 调用fs_alloc_inode函数,创建此目录的索引节点,并将目录的目录项指向索引节点。
- 调用fs_alloc_dentry函数,创建上级目录项对应的索引节点
- 调用newfs_getattr函数,获取创建的目录的属性。若属性获取成功,则意味着目录创建成功。
ls
- 调用newfs_getattr函数,获取ls命令指定的路径(目录/文件)的属性。
- 若指定的路径为文件,则打印文件名。
- 若指定的路径为目录,则执行以下步骤。
- 依次调用newfs_readdir函数,读取指定目录中的内容,其中参数offset(指示第i个目录项)依次递增,直到所有目录项读取完毕。newfs_readdir的执行流程如步骤6-8所述。
- 调用newfs_getattr函数,获取上一步获取的目录项对应的属性值。。
- 调用fs_lookup,找到传入路径的目录项。
- 如果目录项存在,则找到目录项指向的索引节点,并调用fs_get_dentry函数获取目录中的第offset个目录项。
- 若目录项存在,则调用filler函数,把目录项中的内容填充到buf中。
使用指南
mount:
功能:挂载newfs文件系统
外部指令:
fusermount [options] mountpoint
参数说明:
- -h 显示帮助文档
- -v 显示版本号
- -o 挂载的选项 opt [,opt…]
- -u 卸载
- -q 不显示部分信息
- -z 强制卸载,尽管资源仍然处于被使用状态
umount:
功能:卸载newfs文件系统
使用语法:
fusermount -u mountpoint
参考mount中说明的fusermount的相关信息
mkdir:
功能:新建目录
使用语法:
mkdir [-p] dirName
参数说明:
- -p 确保目录名称存在,不存在的就建一个。
实例
在工作目录下,建立一个名为 dir0的子目录 :
mkdir dir0
在工作目录下的 dir0 目录中,建立一个名为 test 的子目录。若 dir0 目录原本不存在,则建立一个。(注:本例若不加 -p 参数,且原本 dir0 目录不存在,则产生错误。)
mkdir -p dir0/test
touch:
功能:修改文件或者目录的时间属性,包括存取时间和更改时间。若文件不存在,系统会建立一个新的文件。
使用语法:
touch [-acfm][-d<日期时间>][-r<参考文件或目录>] [-t<日期时间>][--help][--version][文件或目录…]
参数说明:
- -a 改变档案的读取时间记录。
- -m 改变档案的修改时间记录。
- -c 假如目的档案不存在,不会建立新的档案。与 --no-create 的效果一样。
- -f 不使用,是为了与其他 unix 系统的相容性而保留。
- -r 使用参考档的时间记录,与 --file 的效果一样。
- -d 设定时间与日期,可以使用各种不同的格式。
- -t 设定档案的时间记录,格式与 date 指令相同。
实例
使用指令"touch"修改文件"testfile"的时间属性为当前系统时间,输入如下命令:
$ touch testfile #修改文件的时间属性
首先,使用ls命令查看testfile文件的属性,如下所示:
$ ls -l testfile #查看文件的时间属性
#原来文件的修改时间为16:09
-rw-r--r-- 1 hdd hdd 55 2011-08-22 16:09 testfile
执行指令"touch"修改文件属性以后,并再次查看该文件的时间属性,如下所示:
$ touch testfile #修改文件时间属性为当前系统时间
$ ls -l testfile #查看文件的时间属性
#修改后文件的时间属性为当前系统时间
-rw-r--r-- 1 hdd hdd 55 2011-08-22 19:53 testfile
使用指令"touch"时,如果指定的文件不存在,则将创建一个新的空白文件。例如,在当前目录下,使用该指令创建一个空白文件"file",输入如下命令:
$ touch file #创建一个名为“file”的新的空白文件
ls:
功能:显示指定工作目录下之内容(列出目前工作目录所含之文件及子目录)。
使用语法:
ls [-alrtAFR] [name...]
参数 :
- -a 显示所有文件及目录 (. 开头的隐藏文件也会列出)
- -l 除文件名称外,亦将文件型态、权限、拥有者、文件大小等资讯详细列出
- -r 将文件以相反次序显示(原定依英文字母次序)
- -t 将文件依建立时间之先后次序列出
- -A 同 -a ,但不列出 “.” (目前目录) 及 “…” (父目录)
- -F 在列出的文件名称后加一符号;例如可执行档则加 “*”, 目录则加 “/”
- -R 若目录下有文件,则以下之文件亦皆依序列出
实例
列出当前目录(.\test\mnt)下的所有目录:
$ ls .
dir0 dir1 file0
列出目前工作目录下所有名称是 s 开头的文件,越新的排越后面 :
ls -ltr s*
将 /mnt 目录以下所有目录及文件详细资料列出 :
ls -lR /mnt
列出目前工作目录下所有文件及目录;目录于名称后加 “/”, 可执行档于名称后加 “*” :
ls -AF