├── .gitignore ├── CMakeLists.txt ├── README.org ├── TODOs.org ├── TODOs ├── design.png └── file2.png ├── datastruct ├── string.c └── string.h ├── device_io.c ├── device_io.h ├── documents.md ├── fs.c ├── fs.h ├── fs_def.h ├── fulfs ├── base_block_file.c ├── base_block_file.h ├── base_file.c ├── base_file.h ├── block.h ├── data_block.c ├── data_block.h ├── def.h ├── file_dir.c ├── file_dir.h ├── filesystem.c ├── filesystem.h ├── fulfs.h ├── inode.c ├── inode.h ├── mem_inode.c ├── mem_inode.h ├── superblock.c └── superblock.h ├── main.c ├── memory ├── alloc.c └── alloc.h ├── shell.c ├── shell.h ├── shell_command.c ├── shell_command.h ├── test.c └── utils ├── log.c ├── log.h ├── math.h ├── path.c ├── path.h ├── sys.c ├── sys.h ├── testtools.c └── testtools.h /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | 10 | # Linker output 11 | *.ilk 12 | *.map 13 | *.exp 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Libraries 20 | *.lib 21 | *.a 22 | *.la 23 | *.lo 24 | 25 | # Shared objects (inc. Windows DLLs) 26 | *.dll 27 | *.so 28 | *.so.* 29 | *.dylib 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | *.i*86 36 | *.x86_64 37 | *.hex 38 | 39 | # Debug files 40 | *.dSYM/ 41 | *.su 42 | *.idb 43 | *.pdb 44 | 45 | # Kernel Module Compile Results 46 | *.mod* 47 | *.cmd 48 | modules.order 49 | Module.symvers 50 | Mkfile.old 51 | dkms.conf 52 | 53 | # build目录 54 | build/ 55 | 56 | # tag文件 57 | GPATH 58 | GTAGS 59 | GRTAGS 60 | GSYMS 61 | 62 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | PROJECT (UNIXLIKEFS) 2 | 3 | set(CMAKE_BUILD_TYPE "Debug") 4 | set(CMAKE_C_FLAGS_DEBUG "$ENV{CFLAGS} -O0 -Wall -g -ggdb -std=c99") 5 | set(CMAKE_C_FLAGS_RELEASE "$ENV{CFLAGS} -O3 -Wall -std=c99") 6 | 7 | 8 | 9 | SET(LIB_LIST memory/alloc.c utils/sys.c utils/testtools.c utils/log.c utils/path.c datastruct/string.c 10 | shell.c shell_command.c 11 | fs.c 12 | device_io.c) 13 | 14 | aux_source_directory("fulfs/" FULFS_SRCS) 15 | 16 | 17 | 18 | MESSAGE(STATUS "This is BINARY dir" ${PROJECT_BINARY_DIR}) 19 | MESSAGE(STATUS "This is SOURCE dir" ${PROJECT_SOURCE_DIR}) 20 | 21 | 22 | # 相关的源文件是SRC_LIST中定义的源文件列表 23 | ADD_EXECUTABLE(main main.c ${LIB_LIST} ${FULFS_SRCS}) 24 | 25 | 26 | # 测试 27 | ENABLE_TESTING() 28 | ADD_EXECUTABLE(my_test test.c ${LIB_LIST} ${FULFS_SRCS}) 29 | ADD_TEST(my_test my_test) 30 | -------------------------------------------------------------------------------- /README.org: -------------------------------------------------------------------------------- 1 | [[https://github.com/frapples/fulfs-filesystem/blob/master/documents.md][课程设计报告与程序设计文档]] 2 | 3 | * 简单介绍 4 | 这是我的操作系统课程设计。题目是要求实现类 unix 文件系统,并且: 5 | - 采用索引节点储存文件系统。根据课本的解释,索引节点储存另外文件控制块 FCB 中除文件名之外的内容。 6 | - 采用混合分配方式组织文件数据。 7 | - 采用成组链接法管理空闲的数据块。 8 | 9 | 10 | * 程序功能设计 11 | 在开始设计程序时,就考虑到的特性: 12 | ** 抽象磁盘 13 | 程序能够在多种抽象磁盘上建立文件系统。其中,抽象磁盘可以是: 14 | - 真正的磁盘 15 | - 主操作系统中的一个大文件 16 | 但是实际上,任何能够看成一系列扇区组合的并且支持对某个扇区进行读写操作的对象,都能视为抽象磁盘,从而基于此建构整个文件系统。 17 | 18 | 19 | ** 多文件系统挂载支持 20 | 程序能够将不同的磁盘挂载到不同盘符(类似 Windows 系统的盘符),在通过路径操作文件时,程序能自动根据盘符识别具体的文件系统类型,从而执行对应的操作。 21 | 一个磁盘可以格式化为一个文件系统,一个磁盘可以挂载到一个盘符。程序支持多种文件系统,如: 22 | - FAT32 23 | - ext3 24 | - ext4 25 | - fulfs 26 | 由于此课程设计的目标是实现类 unix 文件系统(在这次课程设计里将其命名为 fulfs),因此实际上只实现了 fulfs。 27 | 但是理论上能够进行方便的扩展支持其它的各种文件系统。 28 | 29 | ** 通用的 API 30 | 由于第 2 点的设计需要,程序必须提供一组抽象的接口用于根据文件路径来操作文件系统而不用关心文件系统的具体类型。 31 | 因此,程序封装了一组接口,并且为了对使用者友好,其接口形式尽量模仿了 linux 系统调用。 32 | 33 | ** 易用的 shell 34 | 4. 提供了一个 shell 用于操作挂载的文件系统。shell 的命令都是仿照 linux 的,像 ls, mv, cd 等。 35 | 36 | ** 对跨平台的良好支持 37 | 5. 整个程序的实现用到且只用到了 ANSI C 和其标准库,方便源代码级的跨平台。 38 | 39 | ** 现代化的文件系统 40 | fulfs 文件系统功能上包括: 41 | - 对最基本的文件储存的支持。 42 | - 对文件,即能够将文件当成流来读写,也能够对文件进行随机读写。 43 | - 对路径和目录的完整支持。 44 | - 对硬链接的完整支持。 45 | - 对符号链接的支持。 46 | 47 | * 代码设计 48 | #+BEGIN_SRC 49 | . 50 | ├── CMakeLists.txt cmake 文件 51 | ├── main.c 程序的主入口 52 | ├── test.c 这里是一组测试,在开发时用来测试代码的正确性 53 | ├── shell.h 程序提供的模拟 shell 入口。 54 | ├── shell.c 55 | ├── shell_command.c 56 | ├── shell_command.h 程序提供的模拟 shell 提供了一组模仿 linux 命令的命令,在这个模块里实现 57 | ├── fs.c 58 | ├── fs.h 这里对应设计图的模拟系统调用层。通过这个模块,即可无视文件系统类型操作文件 59 | ├── fs_def.h 这里定义了各种具体文件系统的实现和 fs.h 模块的约定接口 60 | ├── device_io.c 61 | ├── device_io.h 抽象磁盘。这里把一个大文件虚拟成一个磁盘,赋予一个磁盘号,并以扇区为单位进行读写 62 | ├── fulfs 63 | │   ├── fulfs.h 64 | │   ├── filesystem.c 65 | │   ├── filesystem.h 对 fulfs 文件系统的操作,如格式化,查看总空闲和剩余空间等。 66 | │   ├── file_dir.c 67 | │   ├── file_dir.h 这里实现了 fulfs 文件系统的普通文件 I/O,目录操作,硬链接以及符号链接 68 | │   ├── base_file.c 在 fulfs 底层,一切都视为以 inode 编号为标志的底层文件 69 | │   ├── base_file.h 70 | │   ├── base_block_file.c 把底层文件成是一个个的盘块组成,提供定位盘块,增加盘块,删除盘块等核心算法。 71 | │   ├── base_block_file.h 72 | │   ├── superblock.c 73 | │   ├── superblock.h superblock 数据结构的操作和封装。 74 | │   ├── inode.c 75 | │   ├── inode.h inode 节点数据结构的操作 76 | │   ├── data_block.c 77 | │   ├── data_block.h 储存数据盘块区的操作,使用成组链接法的核心算法在此 78 | │   ├── block.h 将抽象磁盘看成一个个 block 组成的块,以 block 为单位进行读写 79 | │   └── def.h 80 | ├── memory 81 | │   ├── alloc.c 82 | │   └── alloc.h 对动态内存分配的包装 83 | ├── datastruct 84 | │   ├── string.c 85 | │   └── string.h 字符串操作的辅助函数 86 | └── utils 87 | ├── log.c 88 | ├── log.h 日志功能,方便调试 89 | ├── math.h 计算相关的小函数 90 | ├── path.c 91 | ├── path.h 处理路径的辅助函数 92 | ├── sys.c 有几个小函数,通过 ANSI C 标准库给原系统的文件加几个操作 93 | ├── sys.h 94 | ├── testtools.c 95 | └── testtools.h 测试工具 96 | #+END_SRC 97 | 98 | 99 | 100 | * 编译以及使用 101 | ** 编译 102 | #+BEGIN_SRC shell 103 | mkdir build 104 | cd build 105 | cmake .. 106 | make 107 | #+END_SRC 108 | 之后,目录中生成 main 文件,为可执行文件。 109 | 110 | ** 创建及挂载 111 | 使用示例: 112 | ./main create test.fs 32000000 113 | 创建一个大小为 32000000 字节的 test.fs 的文件,之后会将此大文件模拟成磁盘供程序使用。 114 | 115 | ./main format test.fs fulfs 2048 116 | 对 test.fs 这个大文件虚拟成的磁盘进行格式化,格式化为 fulfs 文件系统,文件系统盘块大小为 2048. 117 | 118 | 其中,磁盘的挂载需要写入配置文件 config.txt,如: 119 | #+BEGIN_SRC 120 | A test.fs 121 | B test2.fs 122 | #+END_SRC 123 | 这表示,将 test.fs 这个磁盘挂载到盘符 A,将 test2.fs 这个磁盘挂载到盘符 B. 124 | 等进入 shell 后,可以使用前面介绍的命令操作文件系统,这些命令都是仿照 linux 命令格式设计的。 125 | 126 | ./main help 127 | 查看使用帮助。 128 | 129 | ** 使用 shell 130 | ./main enter 131 | 进入程序的 shell 以操作挂载的磁盘的文件系统。 132 | 133 | 这个 shell 模仿 linux 的命令实现了部分命令,包括: 134 | - cd 切换当前目录。 135 | - pwd 输出当前目录。 136 | - ls 列出目录中的文件。 137 | - rmdir 删除空目录。 138 | - mkdir 创建目录。 139 | - ln 创建链接,支持硬链接和符号链接。 140 | - rm 删除文件。 141 | - cp 复制文件。 142 | - mv 移动文件。 143 | - stat 输出文件的信息。 144 | - df 输出挂载的文件系统的信息。 145 | 146 | 147 | * 最后 148 | 以前总觉得像文件系统实现是遥不可及的事情, 149 | 但是直到自己动手实现一个 demo 才发现原来也没想象中的那么难:joy: 150 | 151 | 这个课程设计断断续续的花了我一个月的时间, 152 | 作为我第一个上 5000 行的 C 语言程序,写完之后很累但是也很开心。:smile: 153 | 154 | -------------------------------------------------------------------------------- /TODOs.org: -------------------------------------------------------------------------------- 1 | * 设计 2 | 3 | shell 层 4 | -------------------- 5 | 系统调用 6 | 文件操作 | 文件系统操作 | 目录操作 | 挂载文件系统(指定盘符和文件系统信息) 7 | -------------------- 8 | 具体的某种文件系统的操作 9 | 文件操作 | 文件系统操作 | 目录操作 10 | -------------------- 11 | 底层文件操作(该文件系统底层一切皆文件) 12 | ------------------------------ 13 | 底层块文件操作(以块为单位的文件) 14 | ------------------------------ 15 | 文件系统实现-和数据有关的数据结构 16 | 文件系统元信息 | inode 管理 | data-block 管理 17 | -------------------- 18 | -------------------- 19 | 模拟的 底层 IO 操作 20 | io_read() 从模拟磁盘的扇区中读取内容 21 | io_write() 向模拟磁盘的扇区中写入内容 22 | -------------------- 23 | 模拟的 底层 IO 操作实际实现 24 | 标准库的文件操作函数 25 | 26 | 图形表示: 27 | 28 | #+BEGIN_SRC dot :file TODOs/design.png :cmdline -Tpng :exports none :results silent 29 | digraph structs { 30 | node [shape=record]; 31 | //node[fontsize=10]; 32 | //edge[fontsize=9]; 33 | 34 | struct0 [shape=record, label="shell 层"]; 35 | struct1 [shape=record, label="系统调用层:| 磁盘挂载 |文件系统操作 | 文件操作 | 目录操作| 软链接操作"]; 36 | struct2 [shape=record, label="具体文件系统层(可多个):|文件系统操作| 文件操作 | 目录操作| 软链接操作"]; 37 | struct3 [shape=record, label="底层文件层:| 底层文件读写"]; 38 | struct8 [shape=record, label="块文件层:| 底层块文件读写"]; 39 | 40 | struct4 [shape=record, label="底层数据结构: | superblock | inode | data block"]; 41 | 42 | struct5 [shape=record, label="block I/O 操作"]; 43 | 44 | struct6 [shape=record, label="抽象磁盘:| 磁盘挂载 | 扇区 I/O 操作"]; 45 | 46 | struct7 [shape=record, label="具体磁盘(可多个) | 将原系统的大文件虚拟成磁盘"]; 47 | 48 | struct0->struct1; 49 | struct1 -> struct2; 50 | 51 | struct2:f1 -> struct3; 52 | struct2:f2 -> struct3; 53 | struct2:f3 -> struct3; 54 | 55 | struct2:f4 -> struct4:f1; 56 | 57 | struct3 -> struct8; 58 | struct8 -> struct4:f1; 59 | struct8 -> struct4:f2; 60 | struct8 -> struct4:f3; 61 | 62 | struct4:f1 -> struct5; 63 | struct4:f2 -> struct5; 64 | struct4:f3 -> struct5; 65 | 66 | struct5 -> struct6:f2; 67 | 68 | struct6 -> struct7; 69 | } 70 | #+END_SRC 71 | [[file:TODOs/design.png]] 72 | 73 | * 类 unix 文件系统的设计 74 | ** 磁盘布局 75 | Superblock | Inode table | Data blocks 76 | 77 | superblock:超级块存放文件系统的全局控制信息。 78 | Inode table: 一个大的 Inode 数组。每个块中存放一定的 Inode。 79 | Data blocks: 存放文件内容的空间。 80 | 81 | 磁盘是以块 block 为单位管理的,一个 block 常用 1K,4K,64K。大小应该是扇区大小的整数倍。 82 | block 从 0 开始编号,最大到 2^32 - 1,(uint32 的最大值) 83 | 84 | 假设 block 为 1K,可管理的最大磁盘容量达到 4T。 85 | 86 | 第一扇区存放 Spuerblock 的起始部分。包括最重要的信息:block 大小。也包括完整的 Spuerblock 所占用的 block 大小。 87 | 之后的几个扇区可能存放剩余的 Spuerblock 内容。 88 | 也许 superblock 的完整内容用一个扇区就能放入。但是即使这样,superblock 也会占用整数倍的 block 块。 89 | 90 | ** Superblock 91 | 起始部分的 Superblock 包括: 92 | #+BEGIN_SRC c 93 | struct superblock_t { 94 | uint16_t sectors_per_block; /* block 大小 */ 95 | uint16_t size_per_sector; /* 扇区大小 */ 96 | uint16_t superblock_used_block_count; /* spuerblock 总共占用的 block 块个数 */ 97 | } 98 | #+END_SRC 99 | 剩余部分的 Superblock 包括: 100 | #+BEGIN_SRC c 101 | struct superblock_t { 102 | uint64_t total_size; /* 文件系统总大小 */ 103 | uint64_t used_size; /* 被使用的大小 */ 104 | uint16_t root_dir; /* 指向根目录的 inode */ 105 | uint32_t inode_table_block; /* inode table 起始的 block 号 */ 106 | uint32_t data_block_free_stack; /* data block 的空闲管理相关 */ 107 | } 108 | 109 | 目前的设计: 110 | 搞简单点,spuerblock 的东西不多,假设它小于一个扇区的大小吧。 111 | 即使这样,方便起见,superblock 仍然占有第 0 个 block。 112 | 113 | #+END_SRC 114 | 关于 inode_table 的大小,data blocks 的起始 blocks 号能够根据已经有的信息计算出来。 115 | 116 | ** inode 117 | *** inode 储存的信息 118 | Inode 的 mode 为 0 代表此 Inode 为空闲的。 119 | 此外,mode 可标示文件的类型:普通文件,目录,符号链接。 120 | mode 为 0 表示此 inode 为空闲的。 121 | 122 | 编号为 0 的 inode 节点永远给根目录使用。 123 | #+BEGIN_SRC c 124 | struct inode_t 125 | { 126 | uint16_t mode; 127 | uint16_t link_count; 128 | uint64_t size; 129 | uint32_t blocks[10]; 130 | uint32_t single_indirect_block; 131 | uint32_t double_indirect_block; 132 | uint32_t triple_indirect_block; 133 | time_t accessed_time; 134 | time_t modified_time; 135 | time_t created_time; 136 | }; 137 | #+END_SRC 138 | 139 | *** inode 数组 140 | Inode table 区用连续的一定的 block 储存 inode 数组。 141 | inode 不跨 block 储存,也就是一个 block 最多可储存整数个 inode。具体数量是:BLOCK_SIZE / sizeof(inode) 142 | 整个 Inode table 里面按顺序储存了一定的 inode。从 0 开始将其编号,到 2^16 -1(uint16 的最大值) 143 | 共有 2^16 个 inode。 144 | 因此用于储存 Inode table 的连续 block 块的个数为:2^16 / (BLOCK_SIZE / sizeof(inode)) (能整除加 0,不能整除加 1) 145 | 146 | 当 mode 为 0 时表示 inode 为空闲的。利用此来进行空闲 inode 的管理。 147 | 148 | ** Data blocks 149 | 这里的 blocks 用于给文件的具体数据分配储存空间。 150 | 151 | 使用成组链接方式将空闲的 blocks 组成一个大栈套小栈的数据结构。。。 152 | 153 | ** 文件 154 | 这个文件概念和平常的文件概念不一样。 155 | 156 | 文件的 inode 中记录的文件的属性信息。 157 | 当文件大小不超过 10 * BLOCK_SIZE,使用 inode 节点的 blocks 数组直接指向储存数据的 data block。 158 | 放不下时,启用 single_indirect_block 的一级索引。 159 | 还放不下时,启用二级索引,之后三级索引。。。 160 | 161 | *** 目录 162 | 实际上,目录也是文件,只是在文件内容中放了目录表项。 163 | 目录表项占 16 字节,其中 14 字节为文件名,2 字节为其 inode 节点号。 164 | 165 | *** 普通文件 166 | 这个就是一般概念的文件。 167 | 168 | *** 符号链接 169 | 这个文件链接到另外一个文件上。被链接的文件路径放在文件内容中。 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | * 代码规范方面 178 | ** 所有命名一律使用下划线。 179 | ** 类型名称后一律加_t。 180 | ** 真正的全局变量(非设计意义上局限于某几个模块使用的),前加 g_。 181 | ** 看上一条。如果发现需要定义真正的全局变量,是否可以把它转换成函数调用的形式? 182 | ** 函数参数的顺序:IN 参数,EDIT 参数,OUT 参数。(附属参数例外) 183 | 184 | ** 工具之类的模块函数前加命名空间 ft_ 185 | ** 系统调用层的模块函数前加命名空间 fufs_ 186 | ** 其它的模块没有要求 187 | 188 | ** 字节敏感的地方, *不允许* 使用 int 等类型,可以使用 stdint.h 中的类型。 189 | 190 | 191 | * 工具 192 | cmake http://hahack.com/codes/cmake/ 193 | -------------------------------------------------------------------------------- /TODOs/design.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frapples/fulfs-filesystem/be859edf88938b671a181883c66243f5512b3e74/TODOs/design.png -------------------------------------------------------------------------------- /TODOs/file2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frapples/fulfs-filesystem/be859edf88938b671a181883c66243f5512b3e74/TODOs/file2.png -------------------------------------------------------------------------------- /datastruct/string.c: -------------------------------------------------------------------------------- 1 | #include "string.h" 2 | 3 | #include 4 | 5 | const char* ft_string_split_next(const char* str, const char* split, size_t* p_size) 6 | { 7 | 8 | const char* start; 9 | 10 | 11 | size_t split_size = strlen(split); 12 | const char *pstr = str; 13 | const char* p = strstr(str, split); 14 | while (p == pstr) { 15 | pstr += split_size; 16 | p = strstr(pstr, split); 17 | } 18 | if (str[0] == '\0') { 19 | return NULL; 20 | } 21 | 22 | start = pstr; 23 | if (p == NULL) { 24 | *p_size = strlen(pstr); 25 | } else { 26 | *p_size = p - start; 27 | } 28 | return start; 29 | } 30 | 31 | void ft_str_strip(char* str) 32 | { 33 | long start = 0; 34 | while (strchr(" \n\t", str[start]) != NULL) { 35 | start++; 36 | } 37 | 38 | 39 | size_t size = strlen(str); 40 | if (size == 0) { 41 | return; 42 | } 43 | 44 | if (size == 1 && strchr(" \n\t", str[0])) { 45 | str[0] = '\0'; 46 | return; 47 | } 48 | 49 | long end = size - 1; 50 | while (strchr(" \n\t", str[end]) != NULL) { 51 | end--; 52 | } 53 | 54 | if (start <= end) { 55 | for (long i = 0; i <= end - start; i++) { 56 | str[i] = str[i + start]; 57 | } 58 | str[end + 1] = '\0'; 59 | } 60 | } 61 | 62 | void ft_str_reverse(char* str) 63 | { 64 | size_t size = strlen(str); 65 | for (size_t i = 0; i < size / 2; i++) { 66 | char tmp = str[i]; 67 | str[i] = str[size - 1 - i]; 68 | str[size - 1 - i] = tmp; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /datastruct/string.h: -------------------------------------------------------------------------------- 1 | #ifndef __DATASRUCT_STRING__H__ 2 | #define __DATASRUCT_STRING__H__ 3 | 4 | #include 5 | #include 6 | 7 | 8 | const char* ft_string_split_next(const char* str, const char* split, size_t* p_size); 9 | 10 | void ft_str_strip(char* str); 11 | 12 | 13 | void ft_str_reverse(char* str); 14 | 15 | #endif /* __DATASRUCT_STRING__H__ */ 16 | -------------------------------------------------------------------------------- /device_io.c: -------------------------------------------------------------------------------- 1 | #include "device_io.h" 2 | #include 3 | #include 4 | 5 | #include"memory/alloc.h" 6 | #include"utils/sys.h" 7 | #include"utils/log.h" 8 | 9 | struct _device_s{ 10 | char* path; 11 | FILE* fp; 12 | sector_no_t section_count; 13 | }; 14 | 15 | #define MAX_DEVICE_COUNT 1024 16 | struct _device_s* device_handle[MAX_DEVICE_COUNT] = {NULL}; 17 | 18 | int device_add(const char* path) 19 | { 20 | /* 已经挂载的文件就不允许别人挂载了 */ 21 | for (int i = 0; i < MAX_DEVICE_COUNT; i++) { 22 | if (device_handle[i] != NULL && strcmp(device_handle[i]->path, path) == 0) { 23 | log_info("%s文件已经被当成设备挂载了,请先卸载", path); 24 | return DEVICE_IO_ERROR; 25 | } 26 | } 27 | 28 | for (int i = 0; i < MAX_DEVICE_COUNT; i++) { 29 | if (device_handle[i] == NULL) { 30 | device_handle[i] = FT_NEW(struct _device_s, 1); 31 | 32 | device_handle[i]->path = FT_NEW(char, strlen(path) + 1); 33 | strcpy(device_handle[i]->path, path); 34 | 35 | device_handle[i]->fp = fopen(path, "r+b"); 36 | device_handle[i]->section_count = ft_filesize_from_fp(device_handle[i]->fp) / BYTES_PER_SECTOR; 37 | 38 | return i; 39 | } 40 | } 41 | 42 | return DEVICE_IO_ERROR; 43 | } 44 | 45 | 46 | void device_del(device_handle_t handle) 47 | { 48 | if (handle < MAX_DEVICE_COUNT && handle >= 0 && device_handle[handle] != NULL) { 49 | fclose(device_handle[handle]->fp); 50 | ft_free(device_handle[handle]->path); 51 | ft_free(device_handle[handle]); 52 | device_handle[handle] = NULL; 53 | } 54 | } 55 | 56 | static struct _device_s* handle_to_struct(device_handle_t handle) 57 | { 58 | if (!(handle < MAX_DEVICE_COUNT && handle >= 0 && device_handle[handle] != NULL)) { 59 | return NULL; 60 | } else { 61 | return device_handle[handle]; 62 | } 63 | } 64 | 65 | int device_read(device_handle_t handle, sector_no_t sector_no, int count, char* buf) 66 | { 67 | struct _device_s* device = handle_to_struct(handle); 68 | if (device == NULL) { 69 | return DEVICE_IO_ERROR; 70 | } 71 | 72 | size_t offset = sector_no * BYTES_PER_SECTOR; 73 | 74 | if (!(sector_no + count <= device->section_count)) { 75 | count = device->section_count - sector_no; 76 | } 77 | 78 | if(fseek(device->fp, offset, SEEK_SET) != 0) { 79 | return DEVICE_IO_ERROR; 80 | } 81 | 82 | fread(buf, BYTES_PER_SECTOR, count, device->fp); 83 | 84 | if (ferror(device->fp)) { 85 | return DEVICE_IO_ERROR; 86 | } else { 87 | return count; 88 | } 89 | } 90 | 91 | 92 | int device_write(device_handle_t handle, sector_no_t sector_no, int count, const char* buf) 93 | { 94 | struct _device_s* device = handle_to_struct(handle); 95 | if (device == NULL) { 96 | return DEVICE_IO_ERROR; 97 | } 98 | 99 | 100 | size_t offset = sector_no * BYTES_PER_SECTOR; 101 | 102 | if (!(sector_no + count <= device->section_count)) { 103 | count = device->section_count - sector_no; 104 | } 105 | 106 | if(fseek(device->fp, offset, SEEK_SET) != 0) { 107 | return DEVICE_IO_ERROR; 108 | } 109 | 110 | fwrite(buf, BYTES_PER_SECTOR, count, device->fp); 111 | 112 | 113 | if (ferror(device->fp)) { 114 | return DEVICE_IO_ERROR; 115 | } else { 116 | return count; 117 | } 118 | } 119 | 120 | int device_section_count(device_handle_t handle) 121 | { 122 | struct _device_s* device = handle_to_struct(handle); 123 | if (device == NULL) { 124 | return 0; 125 | } else { 126 | return device->section_count; 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /device_io.h: -------------------------------------------------------------------------------- 1 | #ifndef __DEVICE_IO__ 2 | #define __DEVICE_IO__ 3 | 4 | #include 5 | #include 6 | 7 | /* 8 | 此模块是模拟对磁盘进行以扇区为单位的读写。 9 | 10 | 对磁盘上的所有扇区,会对其从0编号。每个扇区占512字节。 11 | */ 12 | 13 | /* 扇区大小 */ 14 | #define BYTES_PER_SECTOR 512 15 | 16 | #define DEVICE_IO_ERROR -1 17 | 18 | #define DEVICE_IO_SUCCESS(handle) (handle >= 0) 19 | 20 | typedef int device_handle_t; 21 | typedef uint64_t sector_no_t; 22 | 23 | /* 将外部系统的path文件模拟成系统的一个磁盘,返回其句柄,错误则返回-1 */ 24 | device_handle_t device_add(const char* path); 25 | 26 | /* 把设备卸载 */ 27 | void device_del(device_handle_t handle); 28 | 29 | 30 | /* 从section_no扇区开始,读取count个扇区的内容到buf中。 31 | 返回读取的扇区数。错误返回-1 32 | */ 33 | int device_read(device_handle_t handle, sector_no_t sector_no, int count, char* buf); 34 | 35 | /* 将buf中内容写入到从section_no扇区开始,count个扇区中 36 | 返回写入的扇区数。错误返回-1 37 | */ 38 | int device_write(device_handle_t handle, sector_no_t sector_no, int count, const char* buf); 39 | 40 | int device_section_count(device_handle_t handle); 41 | 42 | #endif /* __DEVICE_IO__ */ 43 | -------------------------------------------------------------------------------- /documents.md: -------------------------------------------------------------------------------- 1 | > 本文档于课程设计时(2016年12月)撰写,于2020年3月在电脑上发现整理,不仅怀念起当时的时光。 2 | 3 | # 设计任务,要求 4 | 5 | # 程序设计目标 6 | 7 | ## 程序全局设计目标 8 | 9 | 经过小组讨论,实际的程序的功能设计除课程设计的要求之外,还额外的增加了一些必要且实用功能和设计。 10 | 一来使得整个程序的的设计结构更加模块化,更富有弹性和扩展性,还能提供友好的操作方式; 11 | 二来通过这些额外设计增进小组对操作系统和编程的理解和能力。 12 | 13 | 总的来说,程序在设计之初,规划中的功能有: 14 | 15 | 一、程序能够在多种抽象磁盘上建立文件系统。其中,抽象磁盘可以是: 16 | 17 | - 真正的磁盘 18 | - 主操作系统中的一个大文件 19 | 但是实际上,任何能够看成一系列扇区组合的并且支持对某个扇区进行读写操作的对象,都能视为抽象磁盘,从而基于此建构整个文件系统。 20 | 21 | 二、程序能够将不同的磁盘挂载到不同盘符(类似 Windows 系统的盘符),在通过路径操作文件时,程序能自动根据盘符识别具体的文件系统类型,从而执行对应的操作。 22 | 一个磁盘可以格式化为一个文件系统,一个磁盘可以挂载到一个盘符。理论上,程序支持多种文件系统,如: 23 | 24 | - FAT32 25 | - ext3 26 | - ext4 27 | - fulfs 28 | 29 | 由于此课程设计的目标是实现类 unix 文件系统(在这次课程设计里将其命名为 fulfs),因此实际上只实现了 fulfs。 30 | 但是理论上能够进行方便的扩展支持其它的各种文件系统。 31 | 32 | 三、由于第 2 点的设计需要,程序必须提供一组抽象的接口用于根据文件路径来操作文件系统而不用关心文件系统的具体类型。 33 | 因此,程序封装了一组接口,并且为了对使用者友好,其接口形式尽量模仿了 linux 系统调用。 34 | 35 | 四、程序提供了一个 shell 用于操作挂载的文件系统。并且为了提供良好的用户体验,shell 的绝大数命令都是仿照 linux 的形式。包括以下命令: 36 | 37 | - cd 切换当前目录。 38 | - pwd 输出当前目录。 39 | - ls 列出目录中的文件。 40 | - rmdir 删除空目录。 41 | - mkdir 创建目录。 42 | - ln 创建链接,支持硬链接和符号链接。 43 | - rm 删除文件。 44 | - cp 复制文件。 45 | - mv 移动文件。 46 | - stat 输出文件的信息。 47 | - df 输出挂载的文件系统的信息。 48 | 49 | 五、目前整个程序的实现用到且只用到了 ANSI C 和其标准库: 50 | 51 | - 不涉及任何 windows 或 linux 系统调用或其它依赖于具体操作系统的库 52 | - 不涉及任何第三方库 53 | - 涉及到的算法和数据结构全部由小组编码完成 54 | - 理论上,能够跨平台执行,包括 Windows 和 linux. 55 | 56 | ## fulfs 文件系统设计目标 57 | 58 | fulfs 是小组独立实现的并且符合题目要求的文件系统,为了方便代码编写,将其命名为 fulfs。fulfs 的总体设计目标如下: 59 | 60 | - 底层上: 61 | 62 | - 采用索引节点储存文件系统。根据课本的解释,索引节点储存另外文件控制块 FCB 中除文件名之外的内容。 63 | - 采用混合分配方式组织文件数据。 64 | - 采用成组链接法管理空闲的数据块。 65 | 66 | - 功能上: 67 | 68 | - 对最基本的文件储存的支持。 69 | - 对文件,即能够将文件当成流来读写,也能够对文件进行随机读写。 70 | - 对路径和目录的完整支持。 71 | - 对硬链接的完整支持。 72 | - 对符号链接的支持。 73 | 74 | # 原理以及算法描述 75 | 76 | ## 程序整体设计图 77 | 78 | ![design](./TODOs/design.png) 79 | 80 | ## fulfs 文件系统整体格式 81 | 82 | 首先,整个磁盘是以 盘块(block) 为单位进行划分的。将整个磁盘每 sectors_per_block 个扇区分为一组,称为盘块(block)。 83 | 整个文件系统的数据结构和算法都是以盘块为单位进行操作,读写的。 84 | 85 | block 的大小是扇区大小的整数倍。一个 block 常用 1K,2K,4K,8K。 86 | block 从 0 开始编号,最大到 2^32 - 1,为 uint32_t 的最大值。 87 | 88 | 假设 block 为 1K,理论上,可管理的最大磁盘容量达到 4T。 89 | 90 | ```dot 91 | digraph structs { 92 | node [shape=record]; 93 | 94 | struct1 [shape=record, label="Superblock| inode table | Data blocks"]; 95 | 96 | } 97 | ``` 98 | 99 | ![design](./TODOs/file2.png) 100 | 101 | 在磁盘的布局上,如图所示,整个磁盘被划分成了三个部分。分别是: 102 | `superblock`:超级块存放文件系统的全局控制信息。如文件系统的 block 大小相关信息,文件系统总扇区数,根目录 inode 号等一些文件系统全局的信息。 103 | 考虑到在最开始是不知道 block 的大小的,因此为了实现方便,superblock 始终占用第 0 号 block,但是其真正储存数据只限于第 0 号扇区。 104 | 105 | `Inode table`: 这个区域里每个 block 存放可最多存放的整数个 inode 节点。 106 | 由于 inode 节点编号是两位无符号整型数,因此理论上 inode 编号最大到 2^16 - 1,是 uint16_t 的最大值。 107 | 由此可知,fulfs 文件系统能够储存的目录和文件的总和是有限的。 108 | 109 | `Data blocks`: 存放文件内容的空间。当文件需要储存具体的数据时,会从这里划分出一块空闲盘块,并且自己组织管理。 110 | 111 | ## 文件和目录管理 112 | 113 | 普通文件和目录甚至符号链接在此文件系统底层都统一视为文件。 114 | inode 节点中储存一个字段 mode 来表示此文件在上层表现为普通文件,目录,还是符号链接。 115 | 除此之外,为了实现方便,inode 空闲管理采用比较低效的方式。 116 | mode 字段还被用来标记是否是空闲块,申请空闲块时需要遍历所有的 inode 节点以寻找空闲块。 117 | 118 | 除此之外,inode 节点还记录了文件的创建时间,最后访问时间,最后修改时间等信息,保存了丰富的文件信息。 119 | 120 | 由于此文件系统使用混合分配方式组织文件数据,因此 inode 节点中,提供 10 个直接索引,1 个一级索引,1 个二级索引,1 个三级索引。 121 | 当低级的索引满后,启用高一级的索引。 122 | 123 | 对于数据盘块(data block)的空闲管理,此文件系统使用成组链接法管理数据盘块分配和回收。具体的数据结构实现在后面的重要算法中叙述。 124 | 125 | ## 多磁盘挂载 126 | 127 | 程序采用类似 Windows 盘符的设计,支持将一个磁盘上的文件系统挂载到某个盘符上。 128 | 之后,盘符路径即为此文件系统的根目录。 129 | 程序内部会记录盘符对应的文件系统信息,比如磁盘号,文件系统类型等。 130 | 131 | 如设计图所示,在模拟系统调用那一层,操作函数所接收到的路径是带有盘符信息的。 132 | 首先这层的操作函数抽取出路径的盘符部分,找到对应的磁盘号和文件系统类型。 133 | 之后,抽取出路径中不带盘符的部分,根据文件系统类型找到该文件系统具体的处理函数,调用之以执行对应操作。 134 | 135 | # 开发环境 136 | 137 | - 操作系统环境:Debian GNU/Linux 8.6 (jessie),armv7l,2G RAM 138 | - 编译器环境:gcc version 4.9.2 (Debian 4.9.2-10) 139 | - 调试器:GNU gdb (Debian 7.7.1+dfsg-5) 7.7.1 140 | - make 工具:cmake version 3.0.2,GNU Make 4.0 141 | - 版本控制:git version 2.1.4 142 | 143 | 注:由于整个程序用且仅用了 ANSI C 和其标准库,因此虽然是用 linux 开发环境,但是也能无缝在 Windows 下编译执行。 144 | 145 | # 重要算法和设计思路描述 146 | 147 | 在整个程序的实现中,涉及的几个关键而且重要的算法分别为混合分配方式中第 n 个相对盘块的定位与添加盘块以及删除盘块, 148 | 成组链接法中数据结构的实现和对应的盘块分配/回收算法, 149 | 对多个文件名引用的正确处理(此文件系统支持硬链接),以及根据文件的绝对路径定位文件的 inode 编号。 150 | 151 | 下面逐个阐述这些关键算法和其设计思路。 152 | 153 | ### 1. 文件中第 n 个相对盘块的定位 154 | 155 | 文件中第 n 个相对盘块是指将文件储存数据的盘块从 0 开始编号,编号为 n 的盘块。 156 | 假设盘块编号是 64 位的无符号整数,位数记为 `sizeof(block_no_t)`, 每盘块大小记为 `block_size`,且是 2 的整数次方。 157 | 那么,在一个间接盘块中可记录的盘块号数为:`block_size / sizeof(block_no_t)` 158 | 159 | 这样,可知: 160 | 直接索引中能储存的数据大小为:`count_0 = block_size * 10` 161 | 一级索引中能储存的数据大小为:`count_1 = (block_size / sizeof(block_no_t)) * block_size` 162 | 二级索引中能储存的数据大小为:`count_2 = (block_size / sizeof(block_no_t)) * (block_size / sizeof(block_no_t)) * block_size` 163 | 三级索引中能储存的数据大小为:`count_3 = (block_size / sizeof(block_no_t)) * (block_size / sizeof(block_no_t)) * (block_size / sizeof(block_no_t)) * block_size` 164 | 165 | 由于 inode 中记录了文件的数据大小,因此通过比较,就能知道第 n 个盘块位于第几级索引上。 166 | 167 | 以三级索引为例,三级索引的具体结构很像一颗树。 168 | 三级索引中的第一层块里记录了一组第二层块盘块号,每个第二层块里记录了一组第三层块盘块号,每个第三层块里记录了一组数据块盘块号(称为第四层)。 169 | 将第一层块从 0 开始编号称为第一层相对块号,将第二层块从 0 开始编号称为第二层相对块号,以此类推。。。 170 | 171 | 通过简单的计算可知,第 n 个相对盘块对应储存数据的三级索引第四层块的第四层相对块号为 `m = n - count_0 - count_1 - count2`。 172 | 173 | 之后,可知,第 m 个相对盘块在上层的第 `m / ((block_size / sizeof(block_no_t))` 个第三层盘块里, 174 | 并且偏移量为 `m % ((block_size / sizeof(block_no_t))` 175 | 176 | 现在,只需要定位出第三层的相应块,就能读取出第四层的对应的第四层相对块号的盘块号。 177 | 按照类似的计算方法,定位出第三层的相应块需要定位出第二层的某一块。。。。 178 | 因此,整个算法是,是个递归算法。 179 | 180 | ## 2. 文件中储存数据的盘块的增加与删除 181 | 182 | 这个算法是与上一个算法有很多类似的地方,相同的地方不再叙述。 183 | 总体上需要注意到地方是: 184 | 对增加盘块来说,判断增加盘块是否需要启用新等级的索引,两种情况需要分别处理。 185 | 对删除盘块来说,判断删除盘块后是否会降低最大的索引等级,两种情况也需要分开处理。 186 | 187 | 算法首先定位到最后一个盘块。 188 | 对增加盘块来说,判断是否需要添加新的上层盘块,若是,则递归添加上层盘块。之后分配新的盘块。 189 | 对删除盘块来说,判断删除后是否上层盘块为空,若是,回收旧盘块,之后递归删除上层盘块。 190 | 191 | ## 3. 成组链接法的盘块分配和回收 192 | 193 | 最初在磁盘上建立文件系统时(格式化),需要将划分为数据盘块的区域组织成成组链接法中的数据结构。 194 | 195 | 这种数据结构的整体结构是将,空闲块分成若干组(比如每组 100 个),之间用类似链表的数据结构串起来。 196 | 在每组内部,是最大大小为 100 的栈。 197 | 总体上,整个混合的数据结构表现为一个抽象的大栈,分配空闲块即弹栈,释放空闲块即压栈。 198 | 199 | 首先,这种数据结构拥有类似链表一样的一个首个盘块指针指向某个盘块。 200 | 这个盘块里,储存了第一组空闲的盘块号和本组的栈顶指针。 201 | 在栈底,最后一个空闲盘块号指向了的空闲盘块中,储存了下一组的空闲盘块号。 202 | 以此类推,最后一组盘块中,栈底的盘块指向 0,表示整个数据结构到此终止。 203 | 204 | 分配盘块时,首先根据首个盘块指针找到第一组空闲盘块,从其中弹出一个空闲盘块。 205 | 假如弹出是最后一个盘块号,则将最后一个盘块号指向的盘块的内容复制到首个盘块指针指向的盘块中。 206 | 207 | 回收盘块中,首先根据首个盘块指针找到第一组空闲盘块, 208 | 假如第一组空闲盘块已经满了,就把这些空闲盘也即首个盘块指针指向的盘块内容复制到回收盘块中, 209 | 然后将首先盘块指针指向的盘块内容清空,并向其中压入回收盘块的盘块号。 210 | 211 | ## 4. 对硬链接的正确处理 212 | 213 | 由于此文件系统是使用 inode 节点,文件名和 inode 是分开储存的,因此可以将多个文件名对应到同一个 inode 上,也即同一个文件上。 214 | 所以此文件系统能很方便的实现硬链接。 215 | 216 | 首先 inode 中要记录一个字段,类似引用计数,表示此文件有几份文件名引用。 217 | 当创建文件时,引用计数默认为 1. 218 | 当创建硬链接时,对应文件的引用计数加 1. 219 | 当文件名被删除时引用计数减一。当引用计数减小到 0 时,表示没有文件名指向此文件,文件会被真正的删除。 220 | 221 | ## 5. 根据文件的绝对路径定位文件的 inode 222 | 223 | 在底层,目录也被视为一个文件。 224 | 其中储存的数据是一组目录项,每条目录索引储存 14 字节的文件名和 2 字节的文件 inode 编号。 225 | 226 | 首先,定位出根目录,从路径中抽取出一级子目录。从根目录中找到一级子目录,定位出其 inode 编号。 227 | 之后,打开一级子目录,从路径中抽取出二级子目录,从一级子目录中查找出二级子目录的 inode 编号。 228 | 以此类推,最终定位到最终的文件 inode 编号。 229 | 230 | # 程序实现 --- 数据结构 231 | 232 | fulfs 文件系统设计到的数据结构有:suprblock,磁盘 inode,内存 inode,目录项,成组链接法中的某一组。 233 | 234 | superblcok 的数据结构,位于 fulfs/suprblock.h: 235 | 236 | ```c 237 | typedef struct { 238 | uint16_t sectors_per_block; /* 每个 block 占的扇区数 */ 239 | sector_no_t sectors; /* 总的扇区数 */ 240 | uint64_t total_size; /* 文件系统总大小 */ 241 | // uint64_t used_size; /* 被使用的大小 */ 242 | inode_no_t root_dir; /* 指向根目录的 inode */ 243 | block_no_t inode_table_block; /* inode table 起始的 block 号 */ 244 | inode_no_t inode_total_count; /* inode table 区总 inode 个数 */ 245 | block_no_t data_block; /* data 区的起始 block 号 */ 246 | block_no_t data_block_free_stack; /* data block 的空闲管理相关 */ 247 | 248 | inode_no_t used_inode_count; /* 已经使用的 inode 节点个数 注:在 base_file 层,删除文件减 1,创建文件加 1 */ 249 | block_no_t used_data_block_count; /* 已经使用的 data block 块个数 注:在 base_block_file 层负责维护此变量 */ 250 | 251 | }superblock_t; 252 | ``` 253 | 254 | 磁盘 inode 的数据结构,位于 fulfs/inode.h: 255 | 256 | ```c 257 | #define LEVEL_0_INDIRECT_COUNT 10 258 | typedef struct 259 | { 260 | uint16_t mode; 261 | uint16_t link_count; 262 | uint64_t size; 263 | block_no_t blocks[LEVEL_0_INDIRECT_COUNT]; 264 | block_no_t single_indirect_block; 265 | block_no_t double_indirect_block; 266 | block_no_t triple_indirect_block; 267 | timestamp_t accessed_time; 268 | timestamp_t modified_time; 269 | timestamp_t created_time; 270 | }inode_t; 271 | 272 | enum { 273 | MODE_EMPTY, 274 | MODE_FILE, 275 | MODE_DIR, 276 | MODE_SYMBOL_LINK 277 | }; 278 | ``` 279 | 280 | 这个数据结构包含了内存 inode 节点,位于 fulfs/base_file.h: 281 | 282 | ```c 283 | typedef struct { 284 | device_handle_t device; 285 | inode_no_t inode_no; 286 | inode_t inode; 287 | superblock_t* sb; 288 | 289 | /* 储存内部指针的底层信息 */ 290 | struct { 291 | block_no_t current_block_relative; 292 | int current_offset; 293 | }current; 294 | 295 | }base_file_t; 296 | ``` 297 | 298 | 目录项的数据结构,位于 fulfs/file_dir.c: 299 | 300 | ```c 301 | #define DIR_ITEM_NAME_SIZE 14 302 | #define DIR_ITEM_SIZE (DIR_ITEM_NAME_SIZE + sizeof(inode_no_t)) 303 | 304 | struct dir_item_s { 305 | char name[DIR_ITEM_NAME_SIZE + 1]; 306 | block_no_t inode_no; 307 | }; 308 | ``` 309 | 310 | (注:此数据结构是储存在内存中的,实际磁盘中的目录项占 16 位。) 311 | 312 | 成组链接法中表示某一组的数据结构,位于 fulfs/data_block.h: 313 | 314 | ```c 315 | #define MAX_GROUP_COUNT 100 316 | struct group_s{ 317 | uint8_t top; 318 | block_no_t free_block[MAX_GROUP_COUNT]; 319 | }; 320 | ``` 321 | 322 | # 程序实现 --- 程序清单 323 | 324 | ## 代码结构 325 | 326 | 以下是代码结构其文件介绍: 327 | 328 | ``` 329 | . 330 | ├── CMakeLists.txt cmake 文件 331 | ├── main.c 程序的主入口 332 | ├── test.c 这里是一组测试,在开发时用来测试代码的正确性 333 | ├── shell.h 程序提供的模拟 shell 入口。 334 | ├── shell.c 335 | ├── shell_command.c 336 | ├── shell_command.h 程序提供的模拟 shell 提供了一组模仿 linux 命令的命令,在这个模块里实现 337 | ├── fs.c 338 | ├── fs.h 这里对应设计图的模拟系统调用层。通过这个模块,即可无视文件系统类型操作文件 339 | ├── fs_def.h 这里定义了各种具体文件系统的实现和 fs.h 模块的约定接口 340 | ├── device_io.c 341 | ├── device_io.h 抽象磁盘。这里把一个大文件虚拟成一个磁盘,赋予一个磁盘号,并以扇区为单位进行读写 342 | ├── fulfs 343 | │   ├── fulfs.h 344 | │   ├── filesystem.c 345 | │   ├── filesystem.h 对 fulfs 文件系统的操作,如格式化,查看总空闲和剩余空间等。 346 | │   ├── file_dir.c 347 | │   ├── file_dir.h 这里实现了 fulfs 文件系统的普通文件 I/O,目录操作,硬链接以及符号链接 348 | │   ├── base_file.c 在 fulfs 底层,一切都视为以 inode 编号为标志的底层文件 349 | │   ├── base_file.h 350 | │   ├── base_block_file.c 把底层文件成是一个个的盘块组成,提供定位盘块,增加盘块,删除盘块等核心算法。 351 | │   ├── base_block_file.h 352 | │   ├── superblock.c 353 | │   ├── superblock.h superblock 数据结构的操作和封装。 354 | │   ├── inode.c 355 | │   ├── inode.h inode 节点数据结构的操作 356 | │   ├── data_block.c 357 | │   ├── data_block.h 储存数据盘块区的操作,使用成组链接法的核心算法在此 358 | │   ├── block.h 将抽象磁盘看成一个个 block 组成的块,以 block 为单位进行读写 359 | │   └── def.h 360 | ├── memory 361 | │   ├── alloc.c 362 | │   └── alloc.h 对动态内存分配的包装 363 | ├── datastruct 364 | │   ├── string.c 365 | │   └── string.h 字符串操作的辅助函数 366 | └── utils 367 | ├── log.c 368 | ├── log.h 日志功能,方便调试 369 | ├── math.h 计算相关的小函数 370 | ├── path.c 371 | ├── path.h 处理路径的辅助函数 372 | ├── sys.c 有几个小函数,通过 ANSI C 标准库给原系统的文件加几个操作 373 | ├── sys.h 374 | ├── testtools.c 375 | └── testtools.h 测试工具 376 | ``` 377 | 378 | 具体的代码参考相关附件。 379 | 380 | ## 代码编译以及执行方式 381 | 382 | 编译: 383 | 384 | ```shell 385 | mkdir build 386 | cd build 387 | cmake .. 388 | make 389 | ``` 390 | 391 | 之后,目录中生成 main 文件,为可执行文件。 392 | 393 | 使用示例: 394 | `./main create test.fs 32000000` 395 | 创建一个大小为 32000000 字节的 test.fs 的文件,之后会将此大文件模拟成磁盘供程序使用。 396 | 397 | `./main format test.fs fulfs 2048` 398 | 对 test.fs 这个大文件虚拟成的磁盘进行格式化,格式化为 fulfs 文件系统,文件系统盘块大小为 2048. 399 | 400 | `./main enter ` 401 | 进入程序的 shell 以操作挂载的磁盘的文件系统。 402 | 其中,磁盘的挂载需要写入配置文件 config.txt,如: 403 | 404 | ``` 405 | A test.fs 406 | B test2.fs 407 | ``` 408 | 409 | 这表示,将 test.fs 这个磁盘挂载到盘符 A,将 test2.fs 这个磁盘挂载到盘符 B. 410 | 等进入 shell 后,可以使用前面介绍的命令操作文件系统,这些命令都是仿照 linux 命令格式设计的。 411 | 412 | `./main help` 413 | 查看使用帮助。 414 | 415 | # 总结 416 | 417 | 通过此时操作系统课程设计,将本学期所学习的文件系统理论知识用于编码实践, 418 | 通过动手从头开始设计,编码,测试,直到最终运行,对文件系统知识的体会更为深刻。 419 | 这次课程设计小组的收获主要有: 420 | 421 | 1. 整个文件系统的整体知识掌握的更加牢固。 422 | 2. 通过亲自设计代码,思考并明白了为什么文件系统需要此设计而非彼设计的深层原因。 423 | 3. 通过回顾整体设计和代码设计,小组讨论分析各种实现方案的优劣,对文件系统相关模块的各种方式的适用特点的理解更加深入。 424 | 4. 通过亲自动手编程一个具有实用性的项目,编程技能和对问题的理解能力大大提高。 425 | 5. 对 C 语言的掌握和理解更加牢固,也理解了一些 C 语言的惯用法和技巧的深层原因。 426 | 6. 通过控制一个超过 1000 行的项目,对软件工程的一些如高内聚,低耦合等原则有了深入的体会。 427 | 7. 深刻体会到正确并熟练的掌握各种辅助工具如 cmake, git 能大大提高效率,事倍功半。 428 | 429 | # 参考文献 430 | 431 | 1. 《操作系统》课本 432 | 2. 《现代操作系统 第三版》 433 | 434 | 3. 其它网络资料 435 | -------------------------------------------------------------------------------- /fs.c: -------------------------------------------------------------------------------- 1 | #include "fs.h" 2 | 3 | #include "fs_def.h" 4 | #include "fulfs/fulfs.h" 5 | #include "memory/alloc.h" 6 | #include "utils/path.h" 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | 13 | struct fs_operate_functions_s g_operate_functions[FS_TYPE_TOTAL]; 14 | 15 | 16 | struct dev_fsctrl_s g_device_filesystem['z' - 'a' + 1]; 17 | 18 | 19 | void fs_init(void) 20 | { 21 | FS_OPERATE_FUNCTIONS_SET(g_operate_functions[FS_TYPE_FULFS], fulfs); 22 | 23 | for (int i = 0; i <= 'z' - 'a'; i++) { 24 | g_device_filesystem[i].fs_type = FS_TYPE_NULL; 25 | } 26 | } 27 | 28 | 29 | /************************/ 30 | 31 | 32 | bool fs_mount(device_handle_t device, char drive_letter, int fs_type) 33 | { 34 | drive_letter = tolower(drive_letter); 35 | if (!('a' <= drive_letter && drive_letter <= 'z')) { 36 | return false; 37 | } 38 | 39 | if (!(0 <=fs_type && fs_type < FS_TYPE_TOTAL)) { 40 | return false; 41 | } 42 | 43 | 44 | for (int i = 0; i <= 'z' - 'a'; i++) { 45 | if (g_device_filesystem[i].fs_type != FS_TYPE_NULL && 46 | g_device_filesystem[i].device == device) { 47 | return false; 48 | } 49 | } 50 | 51 | if (g_device_filesystem[drive_letter - 'a'].fs_type == FS_TYPE_NULL) { 52 | g_device_filesystem[drive_letter - 'a'].fs_type = fs_type; 53 | g_device_filesystem[drive_letter - 'a'].device = device; 54 | g_device_filesystem[drive_letter - 'a'].fs_ctrl = g_operate_functions[fs_type].filesystem_new(device); 55 | g_device_filesystem[drive_letter - 'a'].opfuncs = &g_operate_functions[fs_type]; 56 | return true; 57 | } else { 58 | return false; 59 | } 60 | } 61 | 62 | bool fs_dev_fs_ctrl(char drive_letter, struct dev_fsctrl_s* ctrl) 63 | { 64 | drive_letter = tolower(drive_letter); 65 | if (!('a' <= drive_letter && drive_letter <= 'z')) { 66 | return false; 67 | } 68 | 69 | if (g_device_filesystem[drive_letter- 'a'].fs_type != FS_TYPE_NULL) { 70 | *ctrl = g_device_filesystem[drive_letter- 'a']; 71 | return true; 72 | } else { 73 | return false; 74 | } 75 | } 76 | 77 | static inline struct dev_fsctrl_s* drive_letter_to_ctrl(char letter) 78 | { 79 | return &g_device_filesystem[tolower(letter) - 'a']; 80 | } 81 | 82 | 83 | /************************/ 84 | 85 | static inline bool path_check(const char* path) 86 | { 87 | return isalpha(path[0]) && path[1] == ':'; 88 | } 89 | 90 | static inline char path_drive_letter(const char* path) 91 | { 92 | return path[0]; 93 | } 94 | 95 | /* 去除盘符以外的部分 */ 96 | static inline const char* path_remain(const char* path) 97 | { 98 | return path + 2; 99 | } 100 | 101 | static inline struct dev_fsctrl_s* path_to_ctrl(const char* path) { 102 | return drive_letter_to_ctrl(path_drive_letter(path)); 103 | } 104 | 105 | 106 | #define FS_MAX_FILE_FD UINT16_MAX 107 | 108 | /* FIXME:似乎C语言全局数组默认就是NULL? 记不清楚了 */ 109 | struct { 110 | fs_file_t* file; 111 | char drive_letter; 112 | } g_fs_files[FS_MAX_FILE_FD]; 113 | 114 | int fs_open(const char* path) 115 | { 116 | char abspath[FS_MAX_FILE_PATH]; 117 | fs_abs_path(path, abspath, FS_MAX_FILE_PATH); 118 | 119 | if (!path_check(abspath)) { 120 | return FS_ERROR; 121 | } 122 | 123 | for (int fd = 0; fd < FS_MAX_FILE_FD; fd++) { 124 | if (g_fs_files[fd].file == NULL) { 125 | struct dev_fsctrl_s* ctrl = path_to_ctrl(abspath); 126 | 127 | fs_file_t* file = ctrl->opfuncs->open(ctrl->device, ctrl->fs_ctrl, path_remain(abspath)); 128 | if (file == NULL) { 129 | return FS_ERROR; 130 | } 131 | 132 | g_fs_files[fd].file = file; 133 | g_fs_files[fd].drive_letter = path_drive_letter(abspath); 134 | return fd; 135 | } 136 | } 137 | return FS_ERROR; 138 | } 139 | 140 | void fs_close(int fd) 141 | { 142 | if (fd < FS_MAX_FILE_FD && g_fs_files[fd].file != NULL) { 143 | struct dev_fsctrl_s* ctrl = drive_letter_to_ctrl(g_fs_files[fd].drive_letter); 144 | ctrl->opfuncs->close(g_fs_files[fd].file); 145 | } 146 | 147 | } 148 | 149 | int fs_read(int fd, char* buf, int count) 150 | { 151 | if (fd < FS_MAX_FILE_FD && g_fs_files[fd].file != NULL) { 152 | struct dev_fsctrl_s* ctrl = drive_letter_to_ctrl(g_fs_files[fd].drive_letter); 153 | return ctrl->opfuncs->read(g_fs_files[fd].file, buf, count); 154 | } else { 155 | return FS_ERROR; 156 | } 157 | } 158 | int fs_write(int fd, const char* buf, int count) 159 | { 160 | if (fd < FS_MAX_FILE_FD && g_fs_files[fd].file != NULL) { 161 | struct dev_fsctrl_s* ctrl = drive_letter_to_ctrl(g_fs_files[fd].drive_letter); 162 | return ctrl->opfuncs->write(g_fs_files[fd].file, buf, count); 163 | } else { 164 | return FS_ERROR; 165 | } 166 | } 167 | 168 | bool fs_ftruncate(int fd, fs_off_t size) 169 | { 170 | if (fd < FS_MAX_FILE_FD && g_fs_files[fd].file != NULL) { 171 | struct dev_fsctrl_s* ctrl = drive_letter_to_ctrl(g_fs_files[fd].drive_letter); 172 | return ctrl->opfuncs->ftruncate(g_fs_files[fd].file, size); 173 | } else { 174 | return FS_ERROR; 175 | } 176 | } 177 | 178 | fs_off_t fs_lseek(int fd, fs_off_t off, int where) 179 | { 180 | if (fd < FS_MAX_FILE_FD && g_fs_files[fd].file != NULL) { 181 | struct dev_fsctrl_s* ctrl = drive_letter_to_ctrl(g_fs_files[fd].drive_letter); 182 | return ctrl->opfuncs->lseek(g_fs_files[fd].file, off, where); 183 | } else { 184 | return FS_ERROR; 185 | } 186 | } 187 | 188 | 189 | int fs_mkdir(const char* path) 190 | { 191 | char abspath[FS_MAX_FILE_PATH]; 192 | fs_abs_path(path, abspath, FS_MAX_FILE_PATH); 193 | 194 | if (!path_check(abspath)) { 195 | return FS_ERROR; 196 | } 197 | 198 | struct dev_fsctrl_s* ctrl = path_to_ctrl(abspath); 199 | if (ctrl->opfuncs->mkdir(ctrl->device, ctrl->fs_ctrl, path_remain(abspath))) { 200 | return FS_SUCCESS; 201 | } else { 202 | return FS_ERROR; 203 | } 204 | } 205 | 206 | int fs_rmdir(const char* path) 207 | { 208 | char abspath[FS_MAX_FILE_PATH]; 209 | fs_abs_path(path, abspath, FS_MAX_FILE_PATH); 210 | 211 | if (!path_check(abspath)) { 212 | return FS_ERROR; 213 | } 214 | 215 | struct dev_fsctrl_s* ctrl = path_to_ctrl(abspath); 216 | if (ctrl->opfuncs->rmdir(ctrl->device, ctrl->fs_ctrl, path_remain(abspath))) { 217 | return FS_SUCCESS; 218 | } else { 219 | return FS_ERROR; 220 | } 221 | } 222 | 223 | int fs_link(const char* src_path, const char* new_path) 224 | { 225 | char src_abspath[FS_MAX_FILE_PATH]; 226 | fs_abs_path(src_path, src_abspath, FS_MAX_FILE_PATH); 227 | 228 | char new_abspath[FS_MAX_FILE_PATH]; 229 | fs_abs_path(new_path, new_abspath, FS_MAX_FILE_PATH); 230 | 231 | if (!path_check(src_abspath) || !path_check(new_abspath)) { 232 | return FS_ERROR; 233 | } 234 | 235 | if ((path_drive_letter(src_abspath)) != path_drive_letter(new_abspath)) { 236 | return FS_ERROR; 237 | } 238 | 239 | struct dev_fsctrl_s* ctrl = path_to_ctrl(src_abspath); 240 | if (ctrl->opfuncs->link(ctrl->device, ctrl->fs_ctrl, path_remain(src_abspath), path_remain(new_abspath))) { 241 | return FS_SUCCESS; 242 | } else { 243 | return FS_ERROR; 244 | } 245 | } 246 | int fs_unlink(const char* path) 247 | { 248 | char abspath[FS_MAX_FILE_PATH]; 249 | fs_abs_path(path, abspath, FS_MAX_FILE_PATH); 250 | 251 | if (!path_check(abspath)) { 252 | return FS_ERROR; 253 | } 254 | struct dev_fsctrl_s* ctrl = path_to_ctrl(abspath); 255 | if (ctrl->opfuncs->unlink(ctrl->device, ctrl->fs_ctrl, path_remain(abspath))) { 256 | return FS_SUCCESS; 257 | } else { 258 | return FS_ERROR; 259 | } 260 | } 261 | 262 | int fs_symlink(const char* src_path, const char* new_path) 263 | { 264 | char src_abspath[FS_MAX_FILE_PATH]; 265 | fs_abs_path(src_path, src_abspath, FS_MAX_FILE_PATH); 266 | 267 | char new_abspath[FS_MAX_FILE_PATH]; 268 | fs_abs_path(new_path, new_abspath, FS_MAX_FILE_PATH); 269 | 270 | if (!path_check(src_abspath) || !path_check(new_abspath)) { 271 | return FS_ERROR; 272 | } 273 | 274 | if ((path_drive_letter(src_abspath)) != path_drive_letter(new_abspath)) { 275 | return FS_ERROR; 276 | } 277 | 278 | struct dev_fsctrl_s* ctrl = path_to_ctrl(src_abspath); 279 | if (ctrl->opfuncs->symlink(ctrl->device, ctrl->fs_ctrl, path_remain(src_abspath), path_remain(new_abspath))) { 280 | return FS_SUCCESS; 281 | } else { 282 | return FS_ERROR; 283 | } 284 | } 285 | int fs_readlink(const char *path, char *buf, size_t size) 286 | { 287 | char abspath[FS_MAX_FILE_PATH]; 288 | fs_abs_path(path, abspath, FS_MAX_FILE_PATH); 289 | 290 | if (!path_check(abspath)) { 291 | return FS_ERROR; 292 | } 293 | struct dev_fsctrl_s* ctrl = path_to_ctrl(abspath); 294 | if (ctrl->opfuncs->readlink(ctrl->device, ctrl->fs_ctrl, path_remain(abspath), buf, size)) { 295 | return FS_SUCCESS; 296 | } else { 297 | return FS_ERROR; 298 | } 299 | return FS_ERROR; 300 | } 301 | 302 | 303 | int fs_stat(const char *path, struct fs_stat *buf) 304 | { 305 | char abspath[FS_MAX_FILE_PATH]; 306 | fs_abs_path(path, abspath, FS_MAX_FILE_PATH); 307 | 308 | if (!path_check(abspath)) { 309 | return FS_ERROR; 310 | } 311 | struct dev_fsctrl_s* ctrl = path_to_ctrl(abspath); 312 | if (ctrl->opfuncs->stat(ctrl->device, ctrl->fs_ctrl, path_remain(abspath), buf)) { 313 | return FS_SUCCESS; 314 | } else { 315 | return FS_ERROR; 316 | } 317 | } 318 | 319 | 320 | struct _fs_dir_wrapper_s 321 | { 322 | fs_dir_t* dir; 323 | char drive_letter; 324 | }; 325 | 326 | FS_DIR* fs_opendir(const char *path) 327 | { 328 | char abspath[FS_MAX_FILE_PATH]; 329 | fs_abs_path(path, abspath, FS_MAX_FILE_PATH); 330 | 331 | if (!path_check(abspath)) { 332 | return NULL; 333 | } 334 | 335 | 336 | struct dev_fsctrl_s* ctrl = path_to_ctrl(abspath); 337 | fs_dir_t* dir = ctrl->opfuncs->opendir(ctrl->device, ctrl->fs_ctrl, path_remain(abspath)); 338 | if (dir == NULL) { 339 | return NULL; 340 | } 341 | 342 | FS_DIR* dir_wrapper = FT_NEW(FS_DIR, 1); 343 | dir_wrapper->dir = dir; 344 | dir_wrapper->drive_letter = path_drive_letter(abspath); 345 | return dir_wrapper; 346 | } 347 | 348 | int fs_readdir(FS_DIR* dir, char* name) 349 | { 350 | if (dir != NULL) { 351 | struct dev_fsctrl_s* ctrl = drive_letter_to_ctrl(dir->drive_letter); 352 | if (ctrl->opfuncs->readdir(dir->dir, name)) { 353 | return FS_SUCCESS; 354 | } else { 355 | return FS_ERROR; 356 | } 357 | } else { 358 | return FS_ERROR; 359 | } 360 | } 361 | 362 | void fs_closedir(FS_DIR* dir) 363 | { 364 | if (dir != NULL) { 365 | struct dev_fsctrl_s* ctrl = drive_letter_to_ctrl(dir->drive_letter); 366 | ctrl->opfuncs->closedir(dir->dir); 367 | ft_free(dir); 368 | } 369 | } 370 | 371 | int fs_format(device_handle_t device, int sectors_per_block, int fs_type) 372 | { 373 | if (!(0 <= fs_type && fs_type < FS_TYPE_TOTAL)) { 374 | return FS_ERROR; 375 | } 376 | 377 | if (g_operate_functions[fs_type].format(device, sectors_per_block)) { 378 | return FS_SUCCESS; 379 | } else { 380 | return FS_ERROR; 381 | } 382 | } 383 | 384 | 385 | fs_size_t fs_filesystem_used_size(char drive_letter) 386 | { 387 | struct dev_fsctrl_s ctrl; 388 | if (fs_dev_fs_ctrl(drive_letter, &ctrl)) { 389 | return ctrl.opfuncs->filesystem_used_size(ctrl.fs_ctrl); 390 | } else { 391 | return FS_ERROR; 392 | } 393 | } 394 | fs_size_t fs_filesystem_total_size(char drive_letter) 395 | { 396 | struct dev_fsctrl_s ctrl; 397 | if (fs_dev_fs_ctrl(drive_letter, &ctrl)) { 398 | return ctrl.opfuncs->filesystem_total_size(ctrl.fs_ctrl); 399 | } else { 400 | return FS_ERROR; 401 | } 402 | } 403 | 404 | char g_current_dir[FS_MAX_FILE_PATH] = ""; 405 | 406 | char* fs_getcwd(char *buffer,size_t size) 407 | { 408 | if (strlen(g_current_dir) <= 0 || size < 1) { 409 | return NULL; 410 | } else { 411 | strncpy(buffer, g_current_dir, size - 1); 412 | buffer[size - 1] = '\0'; 413 | } 414 | return buffer; 415 | } 416 | 417 | int fs_chdir(const char* path) 418 | { 419 | struct fs_stat st; 420 | if (fs_stat(path, &st) != FS_SUCCESS) { 421 | return FS_ERROR; 422 | } 423 | 424 | if (st.st_mode != FS_S_IFDIR) { 425 | return FS_ERROR; 426 | } 427 | 428 | char abspath[FS_MAX_FILE_PATH]; 429 | if (fs_abs_path(path, abspath, FS_MAX_FILE_PATH) == NULL) { 430 | return FS_ERROR; 431 | } 432 | 433 | strncpy(g_current_dir, abspath, FS_MAX_FILE_PATH - 1); 434 | g_current_dir[FS_MAX_FILE_PATH - 1] = '\0'; 435 | return FS_SUCCESS; 436 | } 437 | 438 | /* FIXME: 目前对于带盘符的路径的一些约定有些混乱,先暂时这样吧 */ 439 | char* fs_abs_path(const char* path, char* abs_path, size_t size) 440 | { 441 | if (path[1] != ':') { 442 | if (fs_getcwd(abs_path, size) == NULL) { 443 | return NULL; 444 | } 445 | 446 | path_join(abs_path, size, path); 447 | 448 | } else { 449 | strncpy(abs_path, path, size - 1); 450 | abs_path[size - 1] = '\0'; 451 | } 452 | 453 | path_simplify(abs_path); 454 | 455 | assert(abs_path[1] == ':'); 456 | 457 | if (abs_path[2] == '\0' ) { 458 | abs_path[2] = '/'; 459 | abs_path[3] = '\0'; 460 | } 461 | return abs_path; 462 | } 463 | -------------------------------------------------------------------------------- /fs.h: -------------------------------------------------------------------------------- 1 | #ifndef __FS__H__ 2 | #define __FS__H__ 3 | 4 | #include "fs_def.h" 5 | #include "device_io.h" 6 | 7 | /* 在启动整个文件系统大模块前,必须调用此函数 */ 8 | void fs_init(void); 9 | 10 | /* 将文件系统挂载到某盘符上 */ 11 | enum { 12 | FS_TYPE_FULFS, 13 | FS_TYPE_TOTAL, 14 | FS_TYPE_NULL 15 | }; 16 | 17 | /* 这个表记录了盘符到具体操作系统的记录 */ 18 | struct dev_fsctrl_s{ 19 | int fs_type; 20 | struct fs_operate_functions_s* opfuncs; 21 | device_handle_t device; 22 | fs_filesystem_t* fs_ctrl; 23 | }; 24 | 25 | bool fs_mount(device_handle_t device, char drive_letter, int fs_type); 26 | bool fs_dev_fs_ctrl(char drive_letter, struct dev_fsctrl_s* ctrl); 27 | 28 | 29 | /* 模拟文件IO的系统调用 */ 30 | 31 | #define FS_ERROR -1 32 | #define FS_SUCCESS 0 33 | 34 | int fs_open(const char* path); 35 | void fs_close(int fd); 36 | 37 | int fs_read(int fd, char* buf, int count); 38 | int fs_write(int fd, const char* buf, int count); 39 | bool fs_ftruncate(int fd, fs_off_t size); 40 | fs_off_t fs_lseek(int fd, fs_off_t off, int where); 41 | 42 | int fs_mkdir(const char* path); 43 | int fs_rmdir(const char* path); 44 | 45 | int fs_link(const char* src_path, const char* new_path); 46 | int fs_unlink(const char* path); 47 | 48 | int fs_symlink(const char* src_path, const char* new_path); 49 | int fs_readlink(const char *path, char *buf, size_t size); 50 | 51 | int fs_stat(const char *path, struct fs_stat *buf); 52 | 53 | struct _fs_dir_wrapper_s; 54 | typedef struct _fs_dir_wrapper_s FS_DIR; 55 | 56 | FS_DIR* fs_opendir(const char *path); 57 | int fs_readdir(FS_DIR* dir, char* name); 58 | void fs_closedir(FS_DIR* dir); 59 | 60 | /* 文件系统操作 */ 61 | int fs_format(device_handle_t device, int sectors_per_block, int fs_type); 62 | fs_size_t fs_filesystem_used_size(char drive_letter); 63 | fs_size_t fs_filesystem_total_size(char drive_letter); 64 | 65 | 66 | /* 当前目录 */ 67 | char* fs_getcwd(char* buffer, size_t size); 68 | int fs_chdir(const char* path); 69 | 70 | char* fs_abs_path(const char* path, char* abs_path, size_t size); 71 | #endif /* __FS__H__ */ 72 | -------------------------------------------------------------------------------- /fs_def.h: -------------------------------------------------------------------------------- 1 | #ifndef __FS__DEF__H__ 2 | #define __FS__DEF__H__ 3 | 4 | #include "device_io.h" 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | /* 这里面定义的函数接口,所有的文件系统应该要遵循此接口 */ 11 | 12 | typedef int64_t fs_off_t; 13 | typedef int64_t fs_size_t; 14 | #define FS_MAX_FILE_PATH 2048 15 | 16 | #define FS_ERROR -1 17 | #define FS_MAX_PATH 18 | 19 | enum { 20 | FS_SEEK_SET, 21 | FS_SEEK_CUR, 22 | FS_SEEK_END 23 | }; 24 | 25 | #define FS_S_IFREG (2 >> 0) 26 | #define FS_S_IFDIR (2 >> 1) 27 | #define FS_S_IFLNK (2 >> 2) 28 | 29 | struct fs_stat { 30 | long st_ino; 31 | int st_nlink; //连到该文件的硬连接数目,刚建立的文件值为1 32 | int st_mode; 33 | fs_off_t st_size; //文件字节数(文件大小) 34 | unsigned long st_blksize; //块大小(文件系统的I/O 缓冲区大小) 35 | unsigned long st_blocks; //块数 36 | time_t st_atime; //最后一次访问时间 37 | time_t st_mtime; //最后一次修改时间 38 | time_t st_ctime; //最后一次改变时间(指属性) 39 | }; 40 | 41 | 42 | 43 | /** 实现的某种特定的文件系统必须要先实现以下的接口 */ 44 | typedef void fs_filesystem_t; 45 | 46 | typedef fs_filesystem_t* (fs_filesystem_new_f)(device_handle_t device); 47 | 48 | typedef void fs_file_t; 49 | typedef void fs_dir_t; 50 | 51 | typedef fs_file_t* (fs_open_f)(device_handle_t device, fs_filesystem_t* fs, const char* path); 52 | typedef void (fs_close_f)(fs_file_t* file); 53 | 54 | typedef int (fs_read_f)(fs_file_t* file, char* buf, int count); 55 | 56 | typedef int (fs_write_f)(fs_file_t* file, const char* buf, int count); 57 | typedef bool (fs_ftruncate_f)(fs_file_t* file, fs_off_t size); 58 | typedef fs_off_t (fs_lseek_f)(fs_file_t* file, fs_off_t off, int where); 59 | 60 | typedef bool (fs_mkdir_f)(device_handle_t device, fs_filesystem_t* fs, const char* path); 61 | typedef bool (fs_rmdir_f)(device_handle_t device, fs_filesystem_t* fs, const char* path); 62 | 63 | typedef bool (fs_link_f)(device_handle_t device, fs_filesystem_t* fs, const char* src_path, const char* new_path); 64 | typedef bool (fs_unlink_f)(device_handle_t device, fs_filesystem_t* fs, const char* path); 65 | 66 | typedef bool (fs_symlink_f)(device_handle_t device, fs_filesystem_t* fs, const char* src_path, const char* new_path); 67 | typedef bool (fs_readlink_f)(device_handle_t device, fs_filesystem_t* fs, const char *path, char *buf, size_t size); 68 | 69 | typedef bool (fs_stat_f)(device_handle_t device, fs_filesystem_t* fs, const char *path, struct fs_stat *buf); 70 | 71 | typedef fs_dir_t* (fs_opendir_f)(device_handle_t device, fs_filesystem_t* fs, const char *path); 72 | typedef bool (fs_readdir_f)(fs_dir_t* dir, char* name); 73 | typedef bool (fs_closedir_f)(fs_dir_t* dir); 74 | 75 | /* 文件系统本身操作 */ 76 | typedef bool (fs_format_f)(device_handle_t device, int sectors_per_block); 77 | 78 | typedef fs_size_t (fs_filesystem_used_size_f)(fs_filesystem_t* fs); 79 | typedef fs_size_t (fs_filesystem_total_size_f)(fs_filesystem_t* fs); 80 | 81 | /* 类似虚表的一个东西 */ 82 | 83 | struct fs_operate_functions_s{ 84 | fs_filesystem_new_f* filesystem_new; 85 | 86 | fs_open_f* open; 87 | fs_close_f* close; 88 | fs_read_f* read; 89 | fs_write_f* write; 90 | fs_ftruncate_f* ftruncate; 91 | fs_lseek_f* lseek; 92 | fs_mkdir_f* mkdir; 93 | fs_rmdir_f* rmdir; 94 | 95 | fs_link_f* link; 96 | fs_unlink_f* unlink; 97 | 98 | fs_symlink_f* symlink; 99 | fs_readlink_f* readlink; 100 | fs_stat_f* stat; 101 | fs_opendir_f* opendir; 102 | fs_readdir_f* readdir; 103 | fs_closedir_f* closedir; 104 | 105 | fs_format_f* format; 106 | fs_filesystem_total_size_f* filesystem_total_size; 107 | fs_filesystem_used_size_f* filesystem_used_size; 108 | }; 109 | 110 | #define FS_OPERATE_FUNCTIONS_SET(var, type_name) \ 111 | do { \ 112 | (var).open = (fs_open_f*)type_name##_open; \ 113 | (var).close = (fs_close_f*)type_name##_close; \ 114 | (var).read = (fs_read_f*)type_name##_read; \ 115 | (var).write = (fs_write_f*)type_name##_write; \ 116 | (var).ftruncate = (fs_ftruncate_f*)type_name##_ftruncate; \ 117 | (var).lseek = (fs_lseek_f*)type_name##_lseek; \ 118 | (var).mkdir = (fs_mkdir_f*)type_name##_mkdir; \ 119 | (var).rmdir = (fs_rmdir_f*)type_name##_rmdir; \ 120 | (var).link = (fs_link_f*)type_name##_link; \ 121 | (var).unlink = (fs_unlink_f*)type_name##_unlink; \ 122 | (var).symlink = (fs_symlink_f*)type_name##_symlink; \ 123 | (var).readlink = (fs_readlink_f*)type_name##_readlink; \ 124 | (var).stat = (fs_stat_f*)type_name##_stat; \ 125 | (var).opendir = (fs_opendir_f*)type_name##_opendir; \ 126 | (var).readdir = (fs_readdir_f*)type_name##_readdir; \ 127 | (var).closedir = (fs_closedir_f*)type_name##_closedir; \ 128 | (var).filesystem_new = (fs_filesystem_new_f*)type_name##_filesystem_new; \ 129 | (var).format = (fs_format_f*)type_name##_format; \ 130 | (var).filesystem_total_size = (fs_filesystem_total_size_f*)type_name##_filesystem_total_size; \ 131 | (var).filesystem_used_size = (fs_filesystem_used_size_f*)type_name##_filesystem_used_size; \ 132 | \ 133 | } while(0); \ 134 | 135 | #endif /* __FS__DEF__H__ */ 136 | -------------------------------------------------------------------------------- /fulfs/base_block_file.c: -------------------------------------------------------------------------------- 1 | #include "base_block_file.h" 2 | 3 | #include "../utils/math.h" 4 | 5 | #include 6 | #include 7 | 8 | 9 | /* 把间接索引方式看成一颗树,将第level层进行编号得no,得到该树中该节点对应的block */ 10 | static bool locate(device_handle_t device, int sectors_per_block, block_no_t first, int level, block_no_t no, block_no_t* p_block); 11 | /* 给这颗树加叶子 12 | 其中, device, sector_per_block不解释,data_blocks_stack参数是分配新块需要 13 | p_first是树的根节点 14 | level 是索引等级 15 | size 是当前的树有多少叶子 16 | */ 17 | static bool add(device_handle_t device, int sectors_per_block, block_no_t data_blocks_stack, 18 | block_no_t* p_first, int level, block_no_t size, block_no_t* p_block, block_no_t* p_used_block_count); 19 | /* 给这颗树剪叶子 */ 20 | static bool pop(device_handle_t device, int sectors_per_block, block_no_t data_blocks_stack, 21 | block_no_t* p_first, int level, block_no_t size, block_no_t* p_used_block_count); 22 | 23 | 24 | #define MAX_NUM_PER_INDIRECT (MAX_BYTES_PER_BLOCK / sizeof(block_no_t)) 25 | static bool indirect_load(device_handle_t device, int sectors_per_block, block_no_t block, block_no_t blocks[]); 26 | static bool indirect_dump(device_handle_t device, int sectors_per_block, block_no_t block, block_no_t blocks[]); 27 | static int num_per_indirect(int block_size); 28 | 29 | 30 | bool base_block_file_locate(device_handle_t device, superblock_t* sb, inode_t* inode, block_no_t block_relative, block_no_t *p_block) 31 | { 32 | assert(inode->size > 0); 33 | 34 | int blocknos_per_block = num_per_indirect(superblock_block_size(sb)); 35 | block_no_t level_0_max_block_count = (fsize_t)(LEVEL_0_INDIRECT_COUNT); 36 | block_no_t level_1_max_block_count = level_0_max_block_count + blocknos_per_block; 37 | block_no_t level_2_max_block_count = level_1_max_block_count + (blocknos_per_block * blocknos_per_block); 38 | block_no_t level_3_max_block_count = level_2_max_block_count + (blocknos_per_block * blocknos_per_block * blocknos_per_block); 39 | 40 | if (block_relative < level_0_max_block_count) { 41 | *p_block = inode->blocks[block_relative]; 42 | return true; 43 | } else if (block_relative < level_1_max_block_count) { 44 | return locate(device, superblock_sectors_per_block(sb), inode->single_indirect_block, 1, block_relative - level_0_max_block_count, p_block); 45 | } else if (block_relative < level_2_max_block_count) { 46 | return locate(device, superblock_sectors_per_block(sb), inode->double_indirect_block, 2, block_relative - level_1_max_block_count, p_block); 47 | } else if (block_relative < level_3_max_block_count) { 48 | return locate(device, superblock_sectors_per_block(sb), inode->triple_indirect_block, 3, block_relative - level_2_max_block_count, p_block); 49 | } else { 50 | assert(false); 51 | } 52 | 53 | return false; 54 | } 55 | 56 | bool base_block_file_push_block(device_handle_t device, superblock_t* sb, inode_t* inode, block_no_t* p_block) 57 | { 58 | /* 目前的block个数 */ 59 | block_no_t block_count = count_groups(inode->size, superblock_block_size(sb)); 60 | 61 | int blocknos_per_block = num_per_indirect(superblock_block_size(sb)); 62 | block_no_t level_0_max_block_count = (fsize_t)(LEVEL_0_INDIRECT_COUNT); 63 | block_no_t level_1_max_block_count = level_0_max_block_count + blocknos_per_block; 64 | block_no_t level_2_max_block_count = level_1_max_block_count + (blocknos_per_block * blocknos_per_block); 65 | block_no_t level_3_max_block_count = level_2_max_block_count + (blocknos_per_block * blocknos_per_block * blocknos_per_block); 66 | 67 | int sectors_per_block = superblock_sectors_per_block(sb); 68 | block_no_t data_block_stack = superblock_data_block_free_stack(sb); 69 | if (block_count < level_0_max_block_count) { 70 | bool success = data_block_alloc(device, sectors_per_block, data_block_stack, p_block, &sb->used_data_block_count); 71 | inode->blocks[block_count] = *p_block; 72 | return success; 73 | } else if (block_count < level_1_max_block_count) { 74 | return add(device, sectors_per_block, data_block_stack, 75 | &inode->single_indirect_block, 1, block_count - level_0_max_block_count, p_block, &sb->used_data_block_count); 76 | } else if (block_count < level_2_max_block_count) { 77 | return add(device, sectors_per_block, data_block_stack, 78 | &inode->double_indirect_block, 2, block_count - level_1_max_block_count, p_block, &sb->used_data_block_count); 79 | } else if (block_count < level_3_max_block_count) { 80 | return add(device, sectors_per_block, data_block_stack, 81 | &inode->triple_indirect_block, 3, block_count - level_2_max_block_count, p_block, &sb->used_data_block_count); 82 | } else { 83 | assert(false); 84 | } 85 | 86 | return false; 87 | } 88 | 89 | bool base_block_file_pop_block(device_handle_t device, superblock_t* sb, inode_t* inode) 90 | { 91 | assert(inode->size > 0); 92 | 93 | block_no_t block_count = (inode->size - 1) / superblock_block_size(sb) + 1; 94 | 95 | int blocknos_per_block = num_per_indirect(superblock_block_size(sb)); 96 | block_no_t level_0_max_block_count = (fsize_t)(LEVEL_0_INDIRECT_COUNT); 97 | block_no_t level_1_max_block_count = level_0_max_block_count + blocknos_per_block; 98 | block_no_t level_2_max_block_count = level_1_max_block_count + (blocknos_per_block * blocknos_per_block); 99 | block_no_t level_3_max_block_count = level_2_max_block_count + (blocknos_per_block * blocknos_per_block * blocknos_per_block); 100 | 101 | int sectors_per_block = superblock_sectors_per_block(sb); 102 | block_no_t data_block_stack = superblock_data_block_free_stack(sb); 103 | if (block_count <= level_0_max_block_count) { 104 | block_no_t new_block; 105 | data_block_free(device, sectors_per_block, data_block_stack, inode->blocks[block_count - 1], &sb->used_data_block_count); 106 | return data_block_alloc(device, sectors_per_block, data_block_stack, &new_block, &sb->used_data_block_count); 107 | } else if (block_count <= level_1_max_block_count) { 108 | return pop(device, sectors_per_block, data_block_stack, 109 | &inode->single_indirect_block, 1, block_count - level_0_max_block_count, &sb->used_data_block_count); 110 | } else if (block_count <= level_2_max_block_count) { 111 | return pop(device, sectors_per_block, data_block_stack, 112 | &inode->double_indirect_block, 2, block_count - level_1_max_block_count, &sb->used_data_block_count); 113 | } else if (block_count <= level_3_max_block_count) { 114 | return pop(device, sectors_per_block, data_block_stack, 115 | &inode->triple_indirect_block, 3, block_count - level_2_max_block_count, &sb->used_data_block_count); 116 | } else { 117 | assert(false); 118 | } 119 | return false; 120 | } 121 | 122 | 123 | static bool locate(device_handle_t device, int sectors_per_block, block_no_t first, int level, block_no_t no, block_no_t* p_block) 124 | { 125 | if (level == 0) { 126 | 127 | *p_block = first; 128 | return true; 129 | 130 | } else { 131 | 132 | block_no_t block; 133 | bool success = locate(device, sectors_per_block, 134 | first, level - 1, no / num_per_indirect(sectors_per_block * BYTES_PER_SECTOR), &block); 135 | if (!success) { 136 | return false; 137 | } 138 | 139 | block_no_t blocks[MAX_NUM_PER_INDIRECT]; 140 | success = indirect_load(device, sectors_per_block, block, blocks); 141 | if (!success) { 142 | return false; 143 | } 144 | 145 | *p_block = blocks[no % num_per_indirect(sectors_per_block * BYTES_PER_SECTOR)]; 146 | return true; 147 | 148 | } 149 | } 150 | 151 | 152 | static bool add(device_handle_t device, int sectors_per_block, block_no_t data_blocks_stack, block_no_t* p_first, int level, block_no_t size, block_no_t* p_block, block_no_t* p_used_block_count) 153 | { 154 | if (level == 0) { 155 | bool success = data_block_alloc(device, sectors_per_block, data_blocks_stack, p_block, p_used_block_count); 156 | if (!success) { 157 | return false; 158 | } 159 | *p_first = *p_block; 160 | return true; 161 | 162 | } else { 163 | 164 | int blocknos_per_block = num_per_indirect(sectors_per_block * BYTES_PER_SECTOR); 165 | /* 新加块在上级的offset */ 166 | int offset = size % blocknos_per_block; 167 | block_no_t block; 168 | if (offset == 0) { 169 | /* 上一级没有空余了 */ 170 | bool success = add(device, sectors_per_block, data_blocks_stack, 171 | p_first, level - 1, count_groups(size, blocknos_per_block), &block, p_used_block_count); 172 | if (!success) { 173 | return false; 174 | } 175 | } else { 176 | 177 | assert(size > 0); 178 | 179 | bool success = locate(device, sectors_per_block, *p_first, level - 1, (size - 1) / blocknos_per_block, &block); 180 | if (!success) { 181 | return false; 182 | } 183 | } 184 | 185 | block_no_t blocks[MAX_NUM_PER_INDIRECT]; 186 | bool success = indirect_load(device, sectors_per_block, block, blocks); 187 | if (!success) { 188 | return false; 189 | } 190 | 191 | success = data_block_alloc(device, sectors_per_block, data_blocks_stack, &(blocks[offset]), p_used_block_count); 192 | if (!success) { 193 | return false; 194 | } 195 | 196 | success = indirect_dump(device, sectors_per_block, block, blocks); 197 | if (!success) { 198 | return false; 199 | } 200 | *p_block = blocks[offset]; 201 | return true; 202 | } 203 | } 204 | 205 | 206 | static bool pop(device_handle_t device, int sectors_per_block, block_no_t data_blocks_stack, block_no_t* p_first, int level, block_no_t size, block_no_t* p_used_block_count) 207 | { 208 | assert(size > 0); 209 | 210 | if (level == 0) { 211 | return data_block_free(device, sectors_per_block, data_blocks_stack, *p_first, p_used_block_count); 212 | } else { 213 | 214 | int blocknos_per_block = num_per_indirect(sectors_per_block * BYTES_PER_SECTOR); 215 | int offset = (size - 1) / blocknos_per_block; 216 | 217 | 218 | block_no_t block; 219 | bool success = locate(device, sectors_per_block, *p_first, level - 1, offset, &block); 220 | if (!success) { 221 | return false; 222 | } 223 | 224 | if (offset == 0) { 225 | success = pop(device, sectors_per_block, data_blocks_stack, p_first, level - 1, (size - 1) / blocknos_per_block + 1, p_used_block_count); 226 | if (!success) { 227 | return false; 228 | } 229 | } 230 | 231 | /* FIXME:这里要是释放失败了,其实是很尴尬的事情,破坏了完整性 */ 232 | success = data_block_free(device, sectors_per_block, data_blocks_stack, *p_first, p_used_block_count); 233 | if (!success) { 234 | return false; 235 | } 236 | return true; 237 | } 238 | } 239 | 240 | 241 | bool base_block_file_block_count(device_handle_t device, superblock_t* sb, inode_t* inode, long* p_count) 242 | { 243 | /* 目前的block个数 */ 244 | block_no_t block_count = count_groups(inode->size, superblock_block_size(sb)); 245 | 246 | int blocknos_per_block = num_per_indirect(superblock_block_size(sb)); 247 | block_no_t level_0_max_block_count = (fsize_t)(LEVEL_0_INDIRECT_COUNT); 248 | block_no_t level_1_max_block_count = level_0_max_block_count + blocknos_per_block; 249 | block_no_t level_2_max_block_count = level_1_max_block_count + (blocknos_per_block * blocknos_per_block); 250 | block_no_t level_3_max_block_count = level_2_max_block_count + (blocknos_per_block * blocknos_per_block * blocknos_per_block); 251 | 252 | *p_count = block_count; 253 | if (block_count < level_0_max_block_count) { 254 | *p_count += 0; 255 | } else if (block_count < level_1_max_block_count) { 256 | *p_count += 1; 257 | } else if (block_count < level_2_max_block_count) { 258 | *p_count += 1 + count_groups(block_count - level_1_max_block_count, blocknos_per_block); 259 | } else if (block_count < level_3_max_block_count) { 260 | int indirect_3_count = count_groups(block_count - level_2_max_block_count, blocknos_per_block); 261 | *p_count += 1 + count_groups(indirect_3_count, blocknos_per_block) + indirect_3_count; 262 | } else { 263 | assert(false); 264 | } 265 | return true; 266 | } 267 | /************************************************/ 268 | /* NOTE: 简单实现,未考虑字节序 */ 269 | static bool indirect_load(device_handle_t device, int sectors_per_block, block_no_t block, block_no_t blocks[]) 270 | { 271 | char buf[MAX_BYTES_PER_BLOCK]; 272 | bool success = block_read(device, sectors_per_block, block, buf); 273 | if (!success) { 274 | return false; 275 | } 276 | 277 | memcpy(blocks, buf, sectors_per_block * BYTES_PER_SECTOR); 278 | 279 | return true; 280 | } 281 | 282 | static bool indirect_dump(device_handle_t device, int sectors_per_block, block_no_t block, block_no_t blocks[]) 283 | { 284 | char buf[MAX_BYTES_PER_BLOCK]; 285 | 286 | memcpy(buf, blocks, sectors_per_block * BYTES_PER_SECTOR); 287 | 288 | return block_write(device, sectors_per_block, block, buf); 289 | } 290 | 291 | 292 | static int num_per_indirect(int block_size) 293 | { 294 | return block_size / sizeof(block_no_t); 295 | } 296 | -------------------------------------------------------------------------------- /fulfs/base_block_file.h: -------------------------------------------------------------------------------- 1 | #ifndef __FULFS__BASE_BLOCK_FILE__H__ 2 | #define __FULFS__BASE_BLOCK_FILE__H__ 3 | 4 | #include "superblock.h" 5 | #include "inode.h" 6 | #include "data_block.h" 7 | 8 | bool base_block_file_locate(device_handle_t device, superblock_t* sb, inode_t* inode, block_no_t block_relative, block_no_t* p_block); 9 | bool base_block_file_push_block(device_handle_t device, superblock_t* sb, inode_t* inode, block_no_t* p_block); 10 | bool base_block_file_pop_block(device_handle_t device, superblock_t* sb, inode_t* inode); 11 | 12 | bool base_block_file_block_count(device_handle_t device, superblock_t* sb, inode_t* inode, long* p_count); 13 | 14 | #endif /* __FULFS__BASE_BLOCK_FILE__H__ */ 15 | -------------------------------------------------------------------------------- /fulfs/base_file.c: -------------------------------------------------------------------------------- 1 | #include "base_file.h" 2 | 3 | #include "mem_inode.h" 4 | #include "block.h" 5 | #include "base_block_file.h" 6 | #include "../utils/math.h" 7 | #include "../utils/log.h" 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | 14 | /* TODO 关于时间的部分没管它 */ 15 | 16 | static bool base_file_del(device_handle_t device, superblock_t* sb, inode_no_t inode_no); 17 | 18 | 19 | bool base_file_open(base_file_t* base_file, device_handle_t device, superblock_t* sb, inode_no_t inode_no) 20 | { 21 | base_file->device = device; 22 | base_file->inode_no = inode_no; 23 | dev_inode_ctrl_t dev_inode_ctrl; 24 | dev_inode_ctrl_init_from_superblock(&dev_inode_ctrl, device, sb); 25 | base_file->sb = sb; 26 | bool success = mem_inode_get(&dev_inode_ctrl, inode_no, &(base_file->mem_inode)); 27 | if (!success) { 28 | return false; 29 | } 30 | 31 | base_file->current.current_block_relative = 0; 32 | base_file->current.current_offset = 0; 33 | 34 | base_file->mem_inode->inode.accessed_time = time(NULL); 35 | return true; 36 | } 37 | 38 | int base_file_mode(const base_file_t* base_file) 39 | { 40 | return base_file->mem_inode->inode.mode; 41 | } 42 | 43 | fsize_t base_file_size(const base_file_t* base_file) 44 | { 45 | return base_file->mem_inode->inode.size; 46 | } 47 | 48 | timestamp_t base_file_accessed_time(const base_file_t* base_file) 49 | { 50 | return base_file->mem_inode->inode.accessed_time; 51 | } 52 | 53 | timestamp_t base_file_modified_time(const base_file_t* base_file) 54 | { 55 | return base_file->mem_inode->inode.modified_time; 56 | } 57 | 58 | timestamp_t base_file_created_time(const base_file_t* base_file) 59 | { 60 | return base_file->mem_inode->inode.created_time; 61 | } 62 | 63 | bool base_file_seek(base_file_t* base_file, fsize_t offset) 64 | { 65 | assert(offset <= base_file->mem_inode->inode.size); 66 | 67 | block_no_t block_relative = offset / superblock_block_size(base_file->sb); 68 | base_file->current.current_block_relative = block_relative; 69 | base_file->current.current_offset = offset % superblock_block_size(base_file->sb); 70 | return true; 71 | } 72 | 73 | fsize_t base_file_tell(const base_file_t* base_file) 74 | { 75 | return base_file->current.current_block_relative * superblock_block_size(base_file->sb) 76 | + base_file->current.current_offset; 77 | } 78 | 79 | int base_file_read(base_file_t* base_file, char* buf, int count) 80 | { 81 | int sectors_per_block = superblock_sectors_per_block(base_file->sb); 82 | 83 | /* 保证接下来的count没有超过文件大小 */ 84 | if (base_file_size(base_file) - base_file_tell(base_file) < (fsize_t)count) { 85 | count = base_file_size(base_file) - base_file_tell(base_file); 86 | } 87 | 88 | char block_buf[MAX_BYTES_PER_BLOCK]; 89 | int readed_count = 0; 90 | while (readed_count < count) { 91 | block_no_t current_block; 92 | bool success = base_block_file_locate(base_file->device, base_file->sb, &(base_file->mem_inode->inode), 93 | base_file->current.current_block_relative, ¤t_block); 94 | if (!success) { 95 | log_debug("定位文件block失败: %d号设备, 文件inode号%d, 相对块号%d\n", base_file->device, 96 | base_file->inode_no, base_file->current.current_block_relative); 97 | return readed_count; 98 | } 99 | 100 | success = block_read(base_file->device, sectors_per_block, current_block, block_buf); 101 | if (!success) { 102 | log_debug("读取块失败: %d号设备, 文件inode号%d, 块号%d\n", base_file->device, base_file->inode_no, current_block); 103 | return readed_count; 104 | } 105 | 106 | int should_read_size = min_int(sectors_per_block * BYTES_PER_SECTOR - base_file->current.current_offset, count - readed_count); 107 | 108 | memcpy(buf + readed_count, block_buf + base_file->current.current_offset, should_read_size); 109 | readed_count += should_read_size; 110 | 111 | base_file_seek(base_file, base_file_tell(base_file) + should_read_size); 112 | } 113 | 114 | return readed_count; 115 | } 116 | 117 | int base_file_write(base_file_t* base_file, const char* buf, int count) 118 | { 119 | int sectors_per_block = superblock_sectors_per_block(base_file->sb); 120 | 121 | char block_buf[MAX_BYTES_PER_BLOCK]; 122 | int writed_count = 0; 123 | while (writed_count < count) { 124 | 125 | block_no_t current_block; 126 | if (base_file->current.current_block_relative >= count_groups(base_file_size(base_file), sectors_per_block * BYTES_PER_SECTOR)) { 127 | bool success = base_block_file_push_block(base_file->device, base_file->sb, &base_file->mem_inode->inode, ¤t_block); 128 | if (!success) { 129 | log_debug("分配新block失败: %d号设备, 文件inode号%d, 相对块号%d\n", base_file->device, 130 | base_file->inode_no, base_file->current.current_block_relative); 131 | return writed_count; 132 | } 133 | } else { 134 | bool success = base_block_file_locate(base_file->device, base_file->sb, &base_file->mem_inode->inode, 135 | base_file->current.current_block_relative, ¤t_block); 136 | if (!success) { 137 | log_debug("定位文件block失败: %d号设备, 文件inode号%d, 相对块号%d\n", base_file->device, 138 | base_file->inode_no, base_file->current.current_block_relative); 139 | return writed_count; 140 | } 141 | } 142 | 143 | bool success = block_read(base_file->device, sectors_per_block, current_block, block_buf); 144 | if (!success) { 145 | return writed_count; 146 | } 147 | 148 | int should_write_size = min_int(sectors_per_block * BYTES_PER_SECTOR - base_file->current.current_offset, 149 | count - writed_count); 150 | 151 | memcpy(block_buf + base_file->current.current_offset, buf + writed_count, should_write_size); 152 | 153 | success = block_write(base_file->device, sectors_per_block, current_block, block_buf); 154 | if (!success) { 155 | return writed_count; 156 | } 157 | 158 | writed_count += should_write_size; 159 | 160 | fsize_t will_pos = base_file_tell(base_file) + should_write_size; 161 | if (will_pos > base_file_size(base_file)) { 162 | base_file->mem_inode->inode.size = will_pos; 163 | } 164 | base_file_seek(base_file, will_pos); 165 | } 166 | 167 | base_file->mem_inode->inode.modified_time = time(NULL); 168 | return writed_count; 169 | } 170 | 171 | bool base_file_close(base_file_t* base_file) 172 | { 173 | dev_inode_ctrl_t dev_inode_ctrl; 174 | dev_inode_ctrl_init_from_superblock(&dev_inode_ctrl, base_file->device, base_file->sb); 175 | 176 | if (!superblock_dump(base_file->device, base_file->sb)) { 177 | return false; 178 | } 179 | 180 | return mem_inode_put(&dev_inode_ctrl, base_file->mem_inode); 181 | } 182 | 183 | bool base_file_create(device_handle_t device, superblock_t* sb, int mode, inode_no_t* p_inode_no) 184 | { 185 | dev_inode_ctrl_t dev_inode_ctrl; 186 | dev_inode_ctrl_init_from_superblock(&dev_inode_ctrl, device, sb); 187 | 188 | bool success = inode_alloc(&dev_inode_ctrl, p_inode_no); 189 | if (!success) { 190 | return false; 191 | } 192 | 193 | inode_t inode; 194 | success = inode_load(&dev_inode_ctrl, *p_inode_no, &inode); 195 | if (!success) { 196 | return false; 197 | } 198 | 199 | inode.mode = mode; 200 | inode.size = 0; 201 | inode.link_count = 1; 202 | 203 | inode.accessed_time = time(NULL); 204 | inode.modified_time = time(NULL); 205 | inode.created_time = time(NULL); 206 | 207 | sb->used_inode_count++; 208 | 209 | if (!superblock_dump(device, sb)) { 210 | return false; 211 | } 212 | success = inode_dump(&dev_inode_ctrl, *p_inode_no, &inode); 213 | if (!success) { 214 | return false; 215 | } 216 | 217 | return true; 218 | } 219 | 220 | bool base_file_ref(device_handle_t device, superblock_t* sb, inode_no_t inode_no) 221 | { 222 | dev_inode_ctrl_t dev_inode_ctrl; 223 | dev_inode_ctrl_init_from_superblock(&dev_inode_ctrl, device, sb); 224 | 225 | mem_inode_t* mem_inode; 226 | bool success = mem_inode_get(&dev_inode_ctrl, inode_no, &mem_inode); 227 | if (!success) { 228 | return false; 229 | } 230 | 231 | mem_inode->inode.link_count++; 232 | 233 | return mem_inode_put(&dev_inode_ctrl, mem_inode); 234 | } 235 | 236 | bool base_file_unref(device_handle_t device, superblock_t* sb, inode_no_t inode_no) 237 | { 238 | dev_inode_ctrl_t dev_inode_ctrl; 239 | dev_inode_ctrl_init_from_superblock(&dev_inode_ctrl, device, sb); 240 | 241 | mem_inode_t* mem_inode; 242 | bool success = mem_inode_get(&dev_inode_ctrl, inode_no, &mem_inode); 243 | if (!success) { 244 | return false; 245 | } 246 | 247 | mem_inode->inode.link_count--; 248 | if (mem_inode->inode.link_count > 0) { 249 | return mem_inode_put(&dev_inode_ctrl, mem_inode); 250 | } else { 251 | return base_file_del(device, sb, inode_no); 252 | } 253 | } 254 | 255 | bool base_file_truncate(base_file_t* base_file, fsize_t size) 256 | { 257 | /* 这个函数要保证出错时不破坏完整性 */ 258 | if (base_file->mem_inode->inode.size > size) { 259 | 260 | int block_size = superblock_block_size(base_file->sb); 261 | 262 | block_no_t block_num = count_groups(base_file->mem_inode->inode.size, block_size); 263 | block_no_t should_block_num = count_groups(size, block_size); 264 | 265 | for (block_no_t i = 0; i < block_num - should_block_num; i++) { 266 | 267 | /* 那么,完整性的责任转由这个函数保证 */ 268 | bool success = base_block_file_pop_block(base_file->device, base_file->sb, &(base_file->mem_inode->inode)); 269 | if (!success) { 270 | return false; 271 | } 272 | 273 | base_file->mem_inode->inode.size -= block_size; 274 | } 275 | 276 | base_file->mem_inode->inode.size = size; 277 | 278 | base_file->mem_inode->inode.modified_time = time(NULL); 279 | return true; 280 | 281 | } else { 282 | return true; 283 | } 284 | } 285 | 286 | 287 | int base_file_ref_count(base_file_t* base_file) 288 | { 289 | return base_file->mem_inode->inode.link_count; 290 | } 291 | 292 | bool base_file_block_count(base_file_t* base_file, long* p_count) 293 | { 294 | return base_block_file_block_count(base_file->device, base_file->sb, &base_file->mem_inode->inode, p_count); 295 | } 296 | 297 | /*********************************/ 298 | static bool base_file_del(device_handle_t device, superblock_t* sb, inode_no_t inode_no) 299 | { 300 | base_file_t base_file; 301 | bool success = base_file_open(&base_file, device, sb, inode_no); 302 | if (!success) { 303 | return false; 304 | } 305 | 306 | bool has_fp = base_file.mem_inode->ref_count > 0; 307 | 308 | /* 释放block */ 309 | success = base_file_truncate(&base_file, 0); 310 | if (!success) { 311 | return false; 312 | } 313 | base_file_close(&base_file); 314 | 315 | if (!has_fp) { 316 | return false; 317 | } 318 | 319 | 320 | /* 释放inode */ 321 | dev_inode_ctrl_t dev_inode_ctrl; 322 | dev_inode_ctrl_init_from_superblock(&dev_inode_ctrl, device, sb); 323 | inode_free(&dev_inode_ctrl, inode_no); 324 | 325 | sb->used_inode_count--; 326 | return true; 327 | } 328 | 329 | -------------------------------------------------------------------------------- /fulfs/base_file.h: -------------------------------------------------------------------------------- 1 | #ifndef __FULFS__BASE__FILE__H__ 2 | #define __FULFS__BASE__FILE__H__ 3 | 4 | /* 本文件系统底层中一切皆文件,这里是底层的文件 */ 5 | 6 | #include "mem_inode.h" 7 | #include "superblock.h" 8 | 9 | typedef struct { 10 | device_handle_t device; 11 | inode_no_t inode_no; 12 | mem_inode_t* mem_inode; 13 | superblock_t* sb; 14 | 15 | /* 储存内部指针的底层信息 */ 16 | struct { 17 | block_no_t current_block_relative; 18 | int current_offset; 19 | }current; 20 | }base_file_t; 21 | 22 | /* 打开底层文件, 注意:spuerblock的内存使用由用户决定 */ 23 | bool base_file_open(base_file_t* base_file, device_handle_t device, superblock_t* sb, inode_no_t inode_no); 24 | 25 | int base_file_mode(const base_file_t* base_file); /* 文件类型 */ 26 | fsize_t base_file_size(const base_file_t* base_file); /* 文件大小 */ 27 | 28 | /* 时间属性 */ 29 | timestamp_t base_file_accessed_time(const base_file_t* base_file); 30 | timestamp_t base_file_modified_time(const base_file_t* base_file); 31 | timestamp_t base_file_created_time(const base_file_t* base_file); 32 | 33 | /* IO */ 34 | bool base_file_seek(base_file_t* base_file, fsize_t offset); 35 | fsize_t base_file_tell(const base_file_t* base_file); 36 | 37 | #define BASE_FILE_IO_ERROR -1 38 | int base_file_read(base_file_t* base_file, char* buf, int count); 39 | int base_file_write(base_file_t* base_file, const char* buf, int count); 40 | 41 | bool base_file_truncate(base_file_t* base_file, fsize_t size); /* 把文件内容截断 */ 42 | 43 | bool base_file_close(base_file_t* base_file); 44 | 45 | int base_file_ref_count(base_file_t* base_file); 46 | bool base_file_block_count(base_file_t* base_file, long* count); 47 | 48 | bool base_file_create(device_handle_t device, superblock_t* sb, int mode, inode_no_t* p_inode_no); /* 创建底层文件 */ 49 | 50 | bool base_file_ref(device_handle_t device, superblock_t* sb, inode_no_t inode_no); /* 文件的引用加1 */ 51 | bool base_file_unref(device_handle_t device, superblock_t* sb, inode_no_t inode_no); /* 文件引用减1,到0就删掉 */ 52 | 53 | 54 | 55 | 56 | #endif /* __FULFS__BASE__FILE__H__ */ 57 | -------------------------------------------------------------------------------- /fulfs/block.h: -------------------------------------------------------------------------------- 1 | #ifndef __FULFS_BLOCK__ 2 | #define __FULFS_BLOCK__ 3 | 4 | #include 5 | #include 6 | #include "../device_io.h" 7 | #include "../utils/log.h" 8 | 9 | 10 | #define MAX_BYTES_PER_BLOCK (8 * 2 * BYTES_PER_SECTOR) 11 | 12 | typedef uint32_t block_no_t; 13 | 14 | static inline bool block_read(device_handle_t device, int sectors_per_block, block_no_t no, char* buf) 15 | { 16 | 17 | bool success = DEVICE_IO_SUCCESS(device_read(device, no * sectors_per_block, sectors_per_block, buf)); 18 | 19 | if (!success) { 20 | log_debug("读取block失败: %d号设备, 块号%ld\n", device, no); 21 | } 22 | return success; 23 | } 24 | 25 | static inline bool block_write(device_handle_t device, int sectors_per_block, block_no_t no, const char* buf) 26 | { 27 | if (no == 0) { 28 | log_warning("写入%d号设备的0号块(可能是superblock所在块)\n", device); 29 | } 30 | 31 | bool success = DEVICE_IO_SUCCESS(device_write(device, no * sectors_per_block, sectors_per_block, buf)); 32 | 33 | if (!success) { 34 | log_debug("写入block失败: %d号设备, 块号%ld\n", device, no); 35 | } 36 | return success; 37 | } 38 | 39 | static inline bool block_copy(device_handle_t device, int sectors_per_block, block_no_t from, block_no_t to) 40 | { 41 | char buf[MAX_BYTES_PER_BLOCK]; 42 | bool success = block_read(device, sectors_per_block, from, buf); 43 | if (!success) { 44 | return false; 45 | } 46 | 47 | return block_write(device, sectors_per_block, to, buf); 48 | } 49 | 50 | 51 | 52 | #endif /* __FULFS_BLOCK__ */ 53 | -------------------------------------------------------------------------------- /fulfs/data_block.c: -------------------------------------------------------------------------------- 1 | 2 | #include "data_block.h" 3 | 4 | #include 5 | #include 6 | #include "block.h" 7 | 8 | #include 9 | 10 | /* 使用成组链接法管理空闲的block */ 11 | 12 | #define MAX_GROUP_COUNT 100 13 | struct group_s{ 14 | uint8_t top; 15 | block_no_t free_block[MAX_GROUP_COUNT]; 16 | }; 17 | 18 | #define GROUP_NEXT_END ((block_no_t)0) 19 | 20 | #define GROUP_NEXT(group) (group.free_block[0]) 21 | 22 | /* 同理,一个简单的实现,没有考虑字节序和内存布局 */ 23 | static void group_load(const char* buf, struct group_s* group) { 24 | *group = *((struct group_s*)buf); 25 | } 26 | static void group_dump(const struct group_s* group, char* buf) { 27 | *((struct group_s*)buf) = *group; 28 | } 29 | 30 | 31 | bool data_blocks_init(device_handle_t device, int sectors_per_block, block_no_t data_block_start, block_no_t data_block_count, block_no_t* p_start) 32 | { 33 | block_no_t next = data_block_start; 34 | for (block_no_t block = data_block_start + 1; block < data_block_start + data_block_count; block += MAX_GROUP_COUNT) { 35 | bool is_end = (data_block_start + data_block_count - block) <= MAX_GROUP_COUNT; 36 | 37 | struct group_s group; 38 | group.top = is_end ? (data_block_start + data_block_count - block) : MAX_GROUP_COUNT; 39 | 40 | assert(group.top <= MAX_GROUP_COUNT); 41 | 42 | for (int i = 0; i < group.top; i++) { 43 | group.free_block[i] = block + i; 44 | } 45 | 46 | if (is_end) { 47 | GROUP_NEXT(group) = GROUP_NEXT_END; 48 | } 49 | 50 | char buf[MAX_BYTES_PER_BLOCK]; 51 | group_dump(&group, buf); 52 | 53 | assert(next >= data_block_start); 54 | 55 | bool success = block_write(device, sectors_per_block, next, buf); 56 | if (!success) { 57 | return false; 58 | } 59 | next = GROUP_NEXT(group); 60 | } 61 | *p_start = data_block_start; 62 | return true; 63 | } 64 | 65 | bool data_block_alloc(device_handle_t device, int sectors_per_block, block_no_t data_blocks_stack, block_no_t* p_block, block_no_t* p_used_block_count) 66 | { 67 | char buf[MAX_BYTES_PER_BLOCK]; 68 | block_read(device, sectors_per_block, data_blocks_stack, buf); 69 | 70 | struct group_s group; 71 | group_load(buf, &group); 72 | 73 | assert(group.top > 0); 74 | assert(group.top <= MAX_GROUP_COUNT); 75 | 76 | 77 | if (group.top == 1) { 78 | block_no_t next = GROUP_NEXT(group); 79 | if (next == GROUP_NEXT_END) { 80 | return false; 81 | } 82 | 83 | *p_block = group.free_block[--group.top]; 84 | 85 | bool success = block_copy(device, sectors_per_block, next, data_blocks_stack); 86 | if (!success) { 87 | return false; 88 | } 89 | } else { 90 | *p_block = group.free_block[--group.top]; 91 | 92 | group_dump(&group, buf); 93 | bool success = block_write(device, sectors_per_block, data_blocks_stack, buf); 94 | if (!success) { 95 | return false; 96 | } 97 | } 98 | *p_used_block_count += 1; 99 | return true; 100 | } 101 | 102 | bool data_block_free(device_handle_t device, int sectors_per_block, block_no_t data_blocks_stack, block_no_t block, block_no_t* p_used_block_count) 103 | { 104 | char buf[MAX_BYTES_PER_BLOCK]; 105 | block_read(device, sectors_per_block, data_blocks_stack, buf); 106 | 107 | struct group_s group; 108 | group_load(buf, &group); 109 | 110 | assert(group.top <= MAX_GROUP_COUNT); 111 | 112 | if (group.top == MAX_GROUP_COUNT) { 113 | bool success = block_copy(device, sectors_per_block, data_blocks_stack, block); 114 | if (!success) { 115 | return false; 116 | } 117 | 118 | group.top = 1; 119 | GROUP_NEXT(group) = block; 120 | } else { 121 | group.free_block[group.top++] = block; 122 | } 123 | 124 | group_dump(&group, buf); 125 | bool success = block_write(device, sectors_per_block, data_blocks_stack, buf); 126 | if (!success) { 127 | return false; 128 | } 129 | 130 | assert(p_used_block_count > 0); 131 | 132 | *p_used_block_count -= 1; 133 | return true; 134 | } 135 | -------------------------------------------------------------------------------- /fulfs/data_block.h: -------------------------------------------------------------------------------- 1 | #ifndef __FULFS_DATA_BLOCK__H__ 2 | #define __FULFS_DATA_BLOCK__H__ 3 | 4 | #include "block.h" 5 | #include "../device_io.h" 6 | 7 | bool data_blocks_init(device_handle_t device, int sectors_per_block, block_no_t data_block_start, block_no_t data_block_count, block_no_t* p_start); 8 | 9 | /* 分配block */ 10 | bool data_block_alloc(device_handle_t device, int sectors_per_block, block_no_t data_blocks_stack, block_no_t* p_block, block_no_t *p_used_block_count); 11 | /* 释放block */ 12 | bool data_block_free(device_handle_t device, int sectors_per_block, block_no_t data_blocks_stack, block_no_t block, block_no_t *p_used_block_count); 13 | 14 | 15 | #endif /* __FULFS_DATA_BLOCK__H__ */ 16 | -------------------------------------------------------------------------------- /fulfs/def.h: -------------------------------------------------------------------------------- 1 | #ifndef __FULFS__DEF__H__ 2 | #define __FULFS__DEF__H__ 3 | 4 | #include 5 | 6 | typedef uint16_t inode_no_t; 7 | 8 | typedef uint64_t timestamp_t; 9 | 10 | typedef uint64_t fsize_t; 11 | 12 | 13 | #endif /* __FULFS__DEF__H__ */ 14 | -------------------------------------------------------------------------------- /fulfs/file_dir.c: -------------------------------------------------------------------------------- 1 | #include "file_dir.h" 2 | 3 | #include "base_file.h" 4 | #include "../utils/math.h" 5 | #include "../utils/log.h" 6 | #include "../memory/alloc.h" 7 | #include "../utils/path.h" 8 | #include 9 | #include 10 | 11 | #define FILE_MAX_NAME 14 12 | 13 | static bool dir_locate(device_handle_t device, fulfs_filesystem_t* fs, inode_no_t dir, const char* name, bool* p_exist, inode_no_t* p_no); 14 | static bool dir_add(device_handle_t device, fulfs_filesystem_t* fs, inode_no_t dir, const char* name, inode_no_t no); 15 | static bool dir_del(device_handle_t device, fulfs_filesystem_t* fs, inode_no_t dir, const char* name); 16 | static bool dir_tree_locate(device_handle_t device, fulfs_filesystem_t* fs, inode_no_t start, const char* relative_path, bool* p_exist, inode_no_t* p_no); 17 | static bool dir_roottree_locate(device_handle_t device, fulfs_filesystem_t* fs, const char* path, bool* p_exist, inode_no_t* p_no); 18 | 19 | /* -------------------- */ 20 | #define DIR_ITEM_NAME_SIZE 14 21 | #define DIR_ITEM_SIZE (DIR_ITEM_NAME_SIZE + sizeof(inode_no_t)) 22 | 23 | struct dir_item_s { 24 | char name[DIR_ITEM_NAME_SIZE + 1]; 25 | block_no_t inode_no; 26 | }; 27 | 28 | static void dir_item_load_from_bin(struct dir_item_s* item, const char* bin); 29 | static void dir_item_dump_to_bin(const struct dir_item_s* item, char* bin); 30 | 31 | /* -------------------- */ 32 | 33 | 34 | static bool fulfs_file_init(fulfs_file_t* file, device_handle_t device, fulfs_filesystem_t* fs, const char* path); 35 | 36 | fulfs_file_t* fulfs_open(device_handle_t device, fulfs_filesystem_t* fs, const char* path) 37 | { 38 | fulfs_file_t* file = FT_NEW(fulfs_file_t, 1); 39 | if (!fulfs_file_init(file, device, fs, path)) { 40 | return NULL; 41 | } 42 | return file; 43 | } 44 | 45 | static bool fulfs_file_init(fulfs_file_t* file, device_handle_t device, fulfs_filesystem_t* fs, const char* path) 46 | { 47 | char dir_path[FS_MAX_FILE_PATH]; 48 | char name[FILE_MAX_NAME]; 49 | path_dirname(path, dir_path); 50 | path_basename(path, name, FILE_MAX_NAME); 51 | 52 | /* 定位出目录所在的inode */ 53 | bool exist; 54 | inode_no_t dir_no; 55 | bool success = dir_roottree_locate(device, fs, dir_path, &exist, &dir_no); 56 | if (!success || !exist) { 57 | return false; 58 | } 59 | 60 | inode_no_t file_no; 61 | success = dir_locate(device, fs, dir_no, name, &exist, &file_no); 62 | if (!success) { 63 | return false; 64 | } 65 | 66 | /* 不存在就建立新文件 */ 67 | if (!exist) { 68 | bool success = base_file_create(device, &fs->sb, MODE_FILE, &file_no); 69 | if (!success) { 70 | return false; 71 | } 72 | 73 | success = dir_add(device, fs, dir_no, name, file_no); 74 | /* FIXME: 这里失败了应该要把新建立的文件删除 */ 75 | if (!success) { 76 | return false; 77 | } 78 | } 79 | 80 | success = base_file_open(&file->base_file, device, &fs->sb, file_no); 81 | if (!success) { 82 | return false; 83 | } 84 | 85 | return true; 86 | } 87 | 88 | void fulfs_close(fulfs_file_t* file) 89 | { 90 | base_file_close(&file->base_file); 91 | ft_free(file); 92 | } 93 | 94 | int fulfs_read(fulfs_file_t* file, char* buf, int count) 95 | { 96 | return base_file_read(&file->base_file, buf, count); 97 | } 98 | 99 | int fulfs_write(fulfs_file_t* file, const char* buf, int count) 100 | { 101 | return base_file_write(&file->base_file, buf, count); 102 | } 103 | 104 | 105 | bool fulfs_ftruncate(fulfs_file_t* file, fs_off_t size) 106 | { 107 | return base_file_truncate(&file->base_file, size); 108 | } 109 | 110 | fs_off_t fulfs_lseek(fulfs_file_t* file, fs_off_t off, int where) 111 | { 112 | fsize_t new_off; 113 | if (where == FS_SEEK_SET) { 114 | new_off = off; 115 | } else if (where == FS_SEEK_CUR) { 116 | new_off = base_file_tell(&file->base_file) + off; 117 | } else if (where == FS_SEEK_END) { 118 | new_off = base_file_size(&file->base_file) + off; 119 | } else { 120 | return FS_ERROR; 121 | } 122 | 123 | if (new_off > base_file_size(&file->base_file)) { 124 | return FS_ERROR; 125 | } 126 | 127 | base_file_seek(&file->base_file, off); 128 | 129 | return new_off; 130 | } 131 | 132 | bool fulfs_mkdir(device_handle_t device, fulfs_filesystem_t* fs, const char* path) 133 | { 134 | char dir_path[FS_MAX_FILE_PATH]; 135 | char name[FILE_MAX_NAME]; 136 | path_dirname(path, dir_path); 137 | path_basename(path, name, FILE_MAX_NAME); 138 | 139 | /* 定位出目录所在的inode */ 140 | bool exist; 141 | inode_no_t dir_no; 142 | bool success = dir_roottree_locate(device, fs, dir_path, &exist, &dir_no); 143 | if (!success) { 144 | return false; 145 | } 146 | 147 | 148 | /* 是否存在同名文件 */ 149 | inode_no_t file_no; 150 | dir_locate(device, fs, dir_no, name, &exist, &file_no); 151 | if (exist) { 152 | return false; 153 | } 154 | 155 | success = base_file_create(device, &fs->sb, MODE_DIR, &file_no); 156 | if (!success) { 157 | return false; 158 | } 159 | 160 | success = dir_add(device, fs, dir_no, name, file_no); 161 | /* FIXME: 这里失败了应该要把新建立的文件删除 */ 162 | if (!success) { 163 | return false; 164 | } 165 | 166 | return true; 167 | } 168 | 169 | bool fulfs_rmdir(device_handle_t device, fulfs_filesystem_t* fs, const char* path) 170 | { 171 | char dir_path[FS_MAX_FILE_PATH]; 172 | char name[FILE_MAX_NAME]; 173 | path_dirname(path, dir_path); 174 | path_basename(path, name, FILE_MAX_NAME); 175 | 176 | /* 定位出目录所在的inode */ 177 | bool exist; 178 | inode_no_t parent_dir_no; 179 | bool success = dir_roottree_locate(device, fs, dir_path, &exist, &parent_dir_no); 180 | if (!success) { 181 | return false; 182 | } 183 | 184 | 185 | /* 是否存在同名文件 */ 186 | inode_no_t dir_no; 187 | success = dir_locate(device, fs, parent_dir_no, name, &exist, &dir_no); 188 | if (!success && !exist) { 189 | return false; 190 | } 191 | 192 | base_file_t base_file; 193 | success = base_file_open(&base_file, device, &fs->sb, dir_no); 194 | if (!success) { 195 | return false; 196 | } 197 | 198 | bool is_dir = (base_file_mode(&base_file) == MODE_DIR); 199 | bool is_empty = (base_file_size(&base_file) == 0); 200 | base_file_close(&base_file); 201 | 202 | if (is_dir && is_empty) { 203 | success = dir_del(device, fs, parent_dir_no, name); 204 | if (!success) { 205 | return false; 206 | } 207 | base_file_unref(device, &fs->sb, dir_no); 208 | return true; 209 | } else { 210 | return false; 211 | } 212 | } 213 | 214 | bool fulfs_link(device_handle_t device, fulfs_filesystem_t* fs, const char* src_path, const char* new_path) 215 | { 216 | /* src文件所在的inode */ 217 | bool exist; 218 | inode_no_t no; 219 | bool success = dir_roottree_locate(device, fs, src_path, &exist, &no); 220 | if (!success || !exist) { 221 | return false; 222 | } 223 | 224 | 225 | /* 检查是满足硬链接要求 */ 226 | base_file_t base_file; 227 | success = base_file_open(&base_file, device, &fs->sb, no); 228 | if (!success) { 229 | return false; 230 | } 231 | if (base_file_mode(&base_file) != MODE_FILE) { 232 | log_warning("不能给除真正文件之外的东西做硬链接: %s, %s\n", src_path, new_path); 233 | return false; 234 | } 235 | base_file_close(&base_file); 236 | 237 | 238 | /* 引用计数加1 */ 239 | success = base_file_ref(device, &fs->sb, no); 240 | if (!success) { 241 | return false; 242 | } 243 | 244 | 245 | /* 创建硬链接 */ 246 | char dir_path[FS_MAX_FILE_PATH]; 247 | char name[FILE_MAX_NAME]; 248 | path_dirname(new_path, dir_path); 249 | path_basename(new_path, name, FILE_MAX_NAME); 250 | 251 | inode_no_t dir_no; 252 | success = dir_roottree_locate(device, fs, dir_path, &exist, &dir_no); 253 | if (!success || !exist) { 254 | /* 这个要是失败了,很尴尬的事情 */ 255 | base_file_unref(device, &fs->sb, no); 256 | return false; 257 | } 258 | 259 | success = dir_add(device, fs, dir_no, name, no); 260 | if (!success) { 261 | /* 这个要是失败了,很尴尬的事情 */ 262 | base_file_unref(device, &fs->sb, no); 263 | return false; 264 | } 265 | 266 | return true; 267 | } 268 | 269 | bool fulfs_unlink(device_handle_t device, fulfs_filesystem_t* fs, const char* path) 270 | { 271 | char dir_path[FS_MAX_FILE_PATH]; 272 | char name[FILE_MAX_NAME]; 273 | path_dirname(path, dir_path); 274 | path_basename(path, name, FILE_MAX_NAME); 275 | 276 | bool exist; 277 | inode_no_t dir_no; 278 | bool success = dir_roottree_locate(device, fs, dir_path, &exist, &dir_no); 279 | if (!success || !exist) { 280 | return false; 281 | } 282 | 283 | inode_no_t file_no; 284 | success = dir_locate(device, fs, dir_no, name, &exist, &file_no); 285 | if (!success || !exist) { 286 | return false; 287 | } 288 | 289 | /* 检查是满足要求 */ 290 | base_file_t base_file; 291 | success = base_file_open(&base_file, device, &fs->sb, file_no); 292 | if (!success) { 293 | return false; 294 | } 295 | if (base_file_mode(&base_file) == MODE_DIR) { 296 | return false; 297 | } 298 | base_file_close(&base_file); 299 | 300 | 301 | success = dir_del(device, fs, dir_no, name); 302 | if (!success) { 303 | return false; 304 | } 305 | 306 | success = base_file_unref(device, &fs->sb, file_no); 307 | if (!success) { 308 | return false; 309 | } 310 | 311 | return true; 312 | } 313 | 314 | bool fulfs_symlink(device_handle_t device, fulfs_filesystem_t* fs, const char* src_path, const char* new_path) 315 | { 316 | /* FIXME: 暂时先这样判断文件是否存在 */ 317 | if (!fulfs_stat(device, fs, src_path, NULL) || !fulfs_stat(device, fs, src_path, NULL)) { 318 | return false; 319 | } 320 | 321 | fulfs_file_t* file = fulfs_open(device, fs, new_path); 322 | if (file == NULL) { 323 | return false; 324 | } 325 | 326 | 327 | int write_size = strlen(new_path) + 1; 328 | int count = fulfs_write(file, new_path, write_size); 329 | if (count != write_size) { 330 | return false; 331 | } 332 | 333 | fulfs_close(file); 334 | 335 | return true; 336 | } 337 | 338 | bool fulfs_readlink(device_handle_t device, fulfs_filesystem_t* fs, const char *path, char *buf, size_t size) 339 | { 340 | struct fs_stat st; 341 | if (!fulfs_stat(device, fs, path, &st)) { 342 | return false; 343 | } 344 | 345 | if (st.st_mode != FS_S_IFLNK) { 346 | return false; 347 | } 348 | 349 | fulfs_file_t* file = fulfs_open(device, fs, path); 350 | if (file == NULL) { 351 | return false; 352 | } 353 | 354 | 355 | int read_size = min_int(size - 1, st.st_size); 356 | int count = fulfs_read(file, buf, read_size); 357 | if (count != read_size) { 358 | return false; 359 | } 360 | buf[size] = '\0'; 361 | 362 | fulfs_close(file); 363 | 364 | return true; 365 | } 366 | 367 | 368 | bool fulfs_stat(device_handle_t device, fulfs_filesystem_t* fs, const char *path, struct fs_stat *buf) 369 | { 370 | bool exist; 371 | inode_no_t no; 372 | bool success = dir_roottree_locate(device, fs, path, &exist, &no); 373 | if (!success || !exist) { 374 | return false; 375 | } 376 | 377 | base_file_t base_file; 378 | success = base_file_open(&base_file, device, &fs->sb, no); 379 | if (!success) { 380 | return false; 381 | } 382 | 383 | if (buf == NULL) { 384 | return true; 385 | } 386 | 387 | buf->st_ino = no; 388 | buf->st_nlink = base_file_ref_count(&base_file); 389 | buf->st_size = base_file_size(&base_file); 390 | long blocks; 391 | success = base_file_block_count(&base_file, &blocks); 392 | if (!success) { 393 | return false; 394 | } 395 | buf->st_blocks = blocks; 396 | buf->st_blksize = superblock_block_size(&fs->sb); 397 | buf->st_atime = base_file_accessed_time(&base_file); 398 | buf->st_mtime = base_file_modified_time(&base_file); 399 | buf->st_ctime = base_file_created_time(&base_file); 400 | 401 | int mode = base_file_mode(&base_file); 402 | if (mode == MODE_FILE) { 403 | buf->st_mode = FS_S_IFREG; 404 | } else if (mode == MODE_DIR) { 405 | buf->st_mode = FS_S_IFDIR; 406 | } else if (mode == MODE_SYMBOL_LINK) { 407 | buf->st_mode = FS_S_IFLNK; 408 | } else { 409 | assert(false); 410 | } 411 | 412 | return true; 413 | } 414 | 415 | 416 | fulfs_dir_t* fulfs_opendir(device_handle_t device, fulfs_filesystem_t* fs, const char *path) 417 | { 418 | fulfs_dir_t* dir = FT_NEW(fulfs_dir_t, 1); 419 | 420 | bool exist; 421 | inode_no_t no; 422 | bool success = dir_roottree_locate(device, fs, path, &exist, &no); 423 | if (!success || !exist) { 424 | return NULL; 425 | } 426 | 427 | success = base_file_open(&dir->base_file, device, &fs->sb, no); 428 | if (!success) { 429 | return NULL; 430 | } 431 | 432 | return dir; 433 | } 434 | 435 | bool fulfs_readdir(fulfs_dir_t* dir, char* name) 436 | { 437 | assert(base_file_size(&dir->base_file) % DIR_ITEM_SIZE == 0); 438 | 439 | if (base_file_tell(&dir->base_file) >= base_file_size(&dir->base_file)) { 440 | name[0] = '\0'; 441 | return true; 442 | } 443 | 444 | char buf[DIR_ITEM_SIZE]; 445 | int count = base_file_read(&dir->base_file, buf, DIR_ITEM_SIZE); 446 | if (count != DIR_ITEM_SIZE) { 447 | return false; 448 | } 449 | 450 | struct dir_item_s dir_item; 451 | dir_item_load_from_bin(&dir_item, buf); 452 | 453 | strcpy(name, dir_item.name); 454 | 455 | return true; 456 | } 457 | 458 | bool fulfs_closedir(fulfs_dir_t* dir) 459 | { 460 | bool success = base_file_close(&dir->base_file); 461 | ft_free(dir); 462 | return success; 463 | } 464 | 465 | /*************************************/ 466 | 467 | static bool dir_locate(device_handle_t device, fulfs_filesystem_t* fs, inode_no_t dir, const char* name, bool* p_exist, inode_no_t* p_no) 468 | { 469 | base_file_t base_file; 470 | bool success = base_file_open(&base_file, device, &fs->sb, dir); 471 | if (!success) { 472 | return false; 473 | } 474 | 475 | assert(base_file_mode(&base_file) == MODE_DIR); 476 | assert(base_file_size(&base_file) % DIR_ITEM_SIZE == 0); 477 | 478 | char buf[DIR_ITEM_SIZE]; 479 | while (base_file_tell(&base_file) < base_file_size(&base_file)) { 480 | int count = base_file_read(&base_file, buf, DIR_ITEM_SIZE); 481 | if (count != DIR_ITEM_SIZE) { 482 | base_file_close(&base_file); 483 | return false; 484 | } 485 | 486 | struct dir_item_s dir_item; 487 | dir_item_load_from_bin(&dir_item, buf); 488 | if (strcmp(dir_item.name, name) == 0) { 489 | *p_exist = true; 490 | *p_no = dir_item.inode_no; 491 | base_file_close(&base_file); 492 | return true; 493 | } 494 | } 495 | 496 | base_file_close(&base_file); 497 | *p_exist = false; 498 | return true; 499 | } 500 | 501 | static bool dir_add(device_handle_t device, fulfs_filesystem_t* fs, inode_no_t dir, const char* name, inode_no_t no) 502 | { 503 | assert(strlen(name) > 0); 504 | 505 | base_file_t base_file; 506 | bool success = base_file_open(&base_file, device, &fs->sb, dir); 507 | if (!success) { 508 | return false; 509 | } 510 | 511 | assert(base_file_mode(&base_file) == MODE_DIR); 512 | 513 | assert(base_file_size(&base_file) % DIR_ITEM_SIZE == 0); 514 | 515 | char buf[DIR_ITEM_SIZE]; 516 | struct dir_item_s dir_item; 517 | strncpy(dir_item.name, name, DIR_ITEM_NAME_SIZE); 518 | dir_item.name[DIR_ITEM_NAME_SIZE] = '\0'; 519 | dir_item.inode_no = no; 520 | dir_item_dump_to_bin(&dir_item, buf); 521 | 522 | base_file_seek(&base_file, base_file_size(&base_file)); 523 | 524 | /* FIXME: 这里写入不完整的话,应该把文件截断到之前的状态 */ 525 | int count = base_file_write(&base_file, buf, DIR_ITEM_SIZE); 526 | if (count != DIR_ITEM_SIZE) { 527 | base_file_close(&base_file); 528 | return false; 529 | } 530 | 531 | base_file_close(&base_file); 532 | return true; 533 | } 534 | 535 | static bool dir_del(device_handle_t device, fulfs_filesystem_t* fs, inode_no_t dir, const char* name) 536 | { 537 | base_file_t base_file; 538 | bool success = base_file_open(&base_file, device, &fs->sb, dir); 539 | if (!success) { 540 | return false; 541 | } 542 | 543 | assert(base_file_mode(&base_file) == MODE_DIR); 544 | assert(base_file_size(&base_file) % DIR_ITEM_SIZE == 0); 545 | 546 | char buf[DIR_ITEM_SIZE]; 547 | while (base_file_tell(&base_file) < base_file_size(&base_file)) { 548 | int count = base_file_read(&base_file, buf, DIR_ITEM_SIZE); 549 | if (count != DIR_ITEM_SIZE) { 550 | base_file_close(&base_file); 551 | return false; 552 | } 553 | 554 | struct dir_item_s dir_item; 555 | dir_item_load_from_bin(&dir_item, buf); 556 | if (strcmp(dir_item.name, name) == 0) { 557 | /* 把最后一项读出来覆盖欲删除的项 */ 558 | fsize_t current = base_file_tell(&base_file) - DIR_ITEM_SIZE; 559 | 560 | base_file_seek(&base_file, base_file_size(&base_file) - DIR_ITEM_SIZE); 561 | int count = base_file_read(&base_file, buf, DIR_ITEM_SIZE); 562 | if (count != DIR_ITEM_SIZE) { 563 | base_file_close(&base_file); 564 | return false; 565 | } 566 | 567 | base_file_seek(&base_file, current); 568 | count = base_file_write(&base_file, buf, DIR_ITEM_SIZE); 569 | if (count != DIR_ITEM_SIZE) { 570 | base_file_close(&base_file); 571 | return false; 572 | } 573 | 574 | /* FIXME: 这里要是失败了,又是个很尴尬的问题,会导致这个目录里出现两个相同的项 */ 575 | bool success = base_file_truncate(&base_file, base_file_size(&base_file) - DIR_ITEM_SIZE); 576 | if (!success) { 577 | base_file_close(&base_file); 578 | return false; 579 | } 580 | 581 | base_file_close(&base_file); 582 | return true; 583 | } 584 | } 585 | 586 | base_file_close(&base_file); 587 | return true; 588 | } 589 | 590 | 591 | 592 | /*************************************/ 593 | 594 | /* 简单实现,考虑了内存布局但是未考虑字节序 */ 595 | static void dir_item_load_from_bin(struct dir_item_s* item, const char* bin) 596 | { 597 | /* 二进制的数据里面,文件名称在达到14个字节的情况下,允许不带\0 */ 598 | memcpy(&item->inode_no, bin, sizeof(inode_no_t)); 599 | memcpy(item->name, bin + sizeof(inode_no_t), DIR_ITEM_NAME_SIZE); 600 | item->name[DIR_ITEM_NAME_SIZE] = '\0'; 601 | } 602 | 603 | static void dir_item_dump_to_bin(const struct dir_item_s* item, char* bin) 604 | { 605 | memcpy(bin, &item->inode_no, sizeof(inode_no_t)); 606 | memcpy(bin + sizeof(inode_no_t), item->name, DIR_ITEM_NAME_SIZE); 607 | } 608 | 609 | static bool dir_tree_locate(device_handle_t device, fulfs_filesystem_t* fs, inode_no_t start, const char* relative_path, bool* p_exist, inode_no_t* p_no) 610 | { 611 | assert(relative_path[0] != '/'); 612 | 613 | char name[DIR_ITEM_NAME_SIZE + 1] = {'\0'}; 614 | 615 | int count = 0; 616 | for (const char* p = relative_path; *p != '\0'; p++) { 617 | count++; 618 | 619 | if (*(p + 1) == '/' || *(p + 1) == '\0') { 620 | 621 | bool exist; 622 | inode_no_t no; 623 | strncpy(name, (p + 1) - count, min_int(DIR_ITEM_NAME_SIZE, count)); 624 | name[min_int(DIR_ITEM_NAME_SIZE, count)] = '\0'; 625 | bool success = dir_locate(device, fs, start, name, &exist, &no); 626 | if (!success) { 627 | return false; 628 | } 629 | 630 | if (!exist) { 631 | *p_exist = false; 632 | return true; 633 | } else { 634 | start = no; 635 | } 636 | 637 | if (*(p + 1) == '/') { 638 | p++; 639 | } 640 | count = 0; 641 | } 642 | } 643 | 644 | *p_exist = true; 645 | *p_no = start; 646 | return true; 647 | } 648 | 649 | static bool dir_roottree_locate(device_handle_t device, fulfs_filesystem_t* fs, const char* path, bool* p_exist, inode_no_t* p_no) 650 | { 651 | assert(path[0] == '/' || path[0] == '\0'); 652 | const char* relative_path = path[0] == '/' ? path + 1 : path; 653 | 654 | return dir_tree_locate(device, fs, superblock_root_dir_inode(&fs->sb), relative_path, p_exist, p_no); 655 | } 656 | 657 | -------------------------------------------------------------------------------- /fulfs/file_dir.h: -------------------------------------------------------------------------------- 1 | #ifndef __FULFS__FILE_DIR__H__ 2 | #define __FULFS__FILE_DIR__H__ 3 | 4 | #include "base_file.h" 5 | #include "filesystem.h" 6 | 7 | #include "../fs_def.h" 8 | 9 | typedef struct { 10 | base_file_t base_file; 11 | }fulfs_file_t; 12 | 13 | 14 | fulfs_file_t* fulfs_open(device_handle_t device, fulfs_filesystem_t* fs, const char* path); 15 | void fulfs_close(fulfs_file_t* file); 16 | 17 | int fulfs_read(fulfs_file_t* file, char* buf, int count); 18 | int fulfs_write(fulfs_file_t* file, const char* buf, int count); 19 | bool fulfs_ftruncate(fulfs_file_t* file, fs_off_t size); 20 | fs_off_t fulfs_lseek(fulfs_file_t* file, fs_off_t off, int where); 21 | 22 | bool fulfs_mkdir(device_handle_t device, fulfs_filesystem_t* fs, const char* path); 23 | bool fulfs_rmdir(device_handle_t device, fulfs_filesystem_t* fs, const char* path); 24 | 25 | bool fulfs_link(device_handle_t device, fulfs_filesystem_t* fs, const char* src_path, const char* new_path); 26 | bool fulfs_unlink(device_handle_t device, fulfs_filesystem_t* fs, const char* path); 27 | 28 | bool fulfs_symlink(device_handle_t device, fulfs_filesystem_t* fs, const char* src_path, const char* new_path); 29 | bool fulfs_readlink(device_handle_t device, fulfs_filesystem_t* fs, const char *path, char *buf, size_t size); 30 | 31 | bool fulfs_stat(device_handle_t device, fulfs_filesystem_t* fs, const char *path, struct fs_stat *buf); 32 | 33 | 34 | typedef struct { 35 | base_file_t base_file; 36 | }fulfs_dir_t; 37 | 38 | fulfs_dir_t* fulfs_opendir(device_handle_t device, fulfs_filesystem_t* fs, const char *path); 39 | bool fulfs_readdir(fulfs_dir_t* dir, char* name); 40 | bool fulfs_closedir(fulfs_dir_t* dir); 41 | 42 | #endif /* __FULFS__FILE_DIR__H__ */ 43 | -------------------------------------------------------------------------------- /fulfs/filesystem.c: -------------------------------------------------------------------------------- 1 | #include "filesystem.h" 2 | #include "block.h" 3 | #include "inode.h" 4 | #include "superblock.h" 5 | #include "data_block.h" 6 | #include "base_file.h" 7 | 8 | #include "../utils/math.h" 9 | #include "../utils/log.h" 10 | #include "../memory/alloc.h" 11 | 12 | 13 | bool fulfs_format(device_handle_t device, int sectors_per_block) 14 | { 15 | block_no_t block_count = device_section_count(device) / sectors_per_block; 16 | block_no_t inode_table = 1; 17 | 18 | /* inode 所占的block数 */ 19 | int inode_blocksize = inode_block_count(sectors_per_block * BYTES_PER_SECTOR, INODE_MAX_COUNT); 20 | 21 | block_no_t data_block = inode_table + inode_blocksize; 22 | 23 | 24 | if (block_count <= 0 || 25 | data_block >= block_count) { 26 | log_debug("空间太小无法格式化"); 27 | return false; 28 | } 29 | 30 | 31 | /* 初始化磁盘的inode区 */ 32 | dev_inode_ctrl_t dev_inode_ctrl; 33 | dev_inode_ctrl_init(&dev_inode_ctrl, device, sectors_per_block * BYTES_PER_SECTOR, inode_table, inode_blocksize); 34 | 35 | inode_t inode; 36 | inode_init(&inode); 37 | 38 | for (inode_no_t i = 0; i < INODE_MAX_COUNT; i++) { 39 | if (!inode_dump(&dev_inode_ctrl, i, &inode)) { 40 | log_debug("inode写入失败: %d号设备, %d号inode", device, (int)i); 41 | return false; 42 | } 43 | } 44 | 45 | /* 初始化磁盘的data block区 */ 46 | block_no_t data_block_free_stack; 47 | bool success = data_blocks_init(device, sectors_per_block, data_block, block_count - data_block, &data_block_free_stack); 48 | if (!success) { 49 | log_debug("data_block区初始化失败: %d号设备", device); 50 | return false; 51 | } 52 | 53 | /* 建立根目录 */ 54 | superblock_t temp_sb; 55 | superblock_create(&temp_sb, device_section_count(device), sectors_per_block, 56 | inode_table, INODE_MAX_COUNT, data_block, data_block_free_stack, 0); 57 | inode_no_t root_dir; 58 | success = base_file_create(device, &temp_sb, MODE_DIR, &root_dir); 59 | if (!success) { 60 | return false; 61 | } 62 | 63 | 64 | 65 | /* 写入superblock */ 66 | superblock_t sb; 67 | superblock_create(&sb, device_section_count(device), sectors_per_block, 68 | inode_table, INODE_MAX_COUNT, data_block, data_block_free_stack, root_dir); 69 | 70 | if (!superblock_dump(device, &sb)) { 71 | log_debug("superblock写入失败: %d号设备", device); 72 | return false; 73 | } 74 | 75 | return true; 76 | } 77 | 78 | bool fulfs_filesystem_init(fulfs_filesystem_t* fs, device_handle_t device) 79 | { 80 | return superblock_load(device, &fs->sb); 81 | } 82 | 83 | fulfs_filesystem_t* fulfs_filesystem_new(device_handle_t device) 84 | { 85 | fulfs_filesystem_t* fs_ctrl = FT_NEW(fulfs_filesystem_t, 1); 86 | if (fs_ctrl == NULL) { 87 | return false; 88 | } 89 | 90 | if (!fulfs_filesystem_init(fs_ctrl, device)) { 91 | return false; 92 | } 93 | 94 | return fs_ctrl; 95 | } 96 | 97 | fs_size_t fulfs_filesystem_used_size(fulfs_filesystem_t* fs) 98 | { 99 | return superblock_used_size(&fs->sb); 100 | } 101 | 102 | fs_size_t fulfs_filesystem_total_size(fulfs_filesystem_t* fs) 103 | { 104 | return superblock_total_size(&fs->sb); 105 | } 106 | -------------------------------------------------------------------------------- /fulfs/filesystem.h: -------------------------------------------------------------------------------- 1 | #ifndef __FULFS__FILESYSETM__H__ 2 | #define __FULFS__FILESYSETM__H__ 3 | 4 | #include 5 | #include "superblock.h" 6 | #include "block.h" 7 | #include "../device_io.h" 8 | #include "../fs_def.h" 9 | /* 10 | 这里是关于unix like filesystem的高层操作。 11 | 现在我决定把这个文件系统命名为fulfs ^_^ 12 | */ 13 | 14 | 15 | 16 | typedef enum { 17 | FULFS_SUCCESS, 18 | FULFS_FAIL 19 | /* 未来可以添加更多错误信息 */ 20 | }fulfs_errcode_t; 21 | 22 | typedef struct { 23 | superblock_t sb; 24 | }fulfs_filesystem_t; 25 | 26 | 27 | fulfs_filesystem_t* fulfs_filesystem_new(device_handle_t device); 28 | bool fulfs_filesystem_init(fulfs_filesystem_t* fs, device_handle_t device); 29 | 30 | fs_size_t fulfs_filesystem_used_size(fulfs_filesystem_t* fs); 31 | fs_size_t fulfs_filesystem_total_size(fulfs_filesystem_t* fs); 32 | 33 | /* 将设备device格式化 */ 34 | bool fulfs_format(device_handle_t device, int sectors_per_block); 35 | 36 | 37 | 38 | 39 | 40 | #endif /* __FULFS__FILESYSETM__H__ */ 41 | -------------------------------------------------------------------------------- /fulfs/fulfs.h: -------------------------------------------------------------------------------- 1 | #ifndef __FULFS__FULFS__H 2 | #define __FULFS__FULFS__H 3 | 4 | /* 为了使用fulfs操作系统,包含这个文件就行 */ 5 | 6 | #include "filesystem.h" 7 | #include "file_dir.h" 8 | 9 | 10 | #endif /* __FULFS__FULFS__H */ 11 | -------------------------------------------------------------------------------- /fulfs/inode.c: -------------------------------------------------------------------------------- 1 | #include "inode.h" 2 | 3 | #include "../utils/math.h" 4 | #include "../utils/log.h" 5 | #include "block.h" 6 | #include 7 | #include 8 | 9 | /*NOTE: 同理,一个简单的实现,没考虑内存布局和字节序 */ 10 | size_t inode_bin_size(void) 11 | { 12 | return sizeof(inode_t); 13 | } 14 | 15 | void inode_init(inode_t* inode) 16 | { 17 | inode->mode = 0; 18 | } 19 | 20 | 21 | void dev_inode_ctrl_init(dev_inode_ctrl_t* dev_inode_ctrl, device_handle_t device, size_t block_size, block_no_t start, block_no_t size) 22 | { 23 | dev_inode_ctrl->device = device; 24 | dev_inode_ctrl->block_size = block_size; 25 | dev_inode_ctrl->start = start; 26 | dev_inode_ctrl->size = size; 27 | } 28 | 29 | void dev_inode_ctrl_init_from_superblock(dev_inode_ctrl_t* dev_inode_ctrl, device_handle_t device, const superblock_t* sb) 30 | { 31 | dev_inode_ctrl_init(dev_inode_ctrl, device, superblock_block_size(sb), superblock_inode_table_start(sb), superblock_inode_table_blocksize(sb)); 32 | } 33 | 34 | static void inode_no_to_block_no_and_offset(dev_inode_ctrl_t *dev_inode_ctrl, inode_no_t no, block_no_t* p_block_no, size_t* p_offset); 35 | bool inode_load(dev_inode_ctrl_t* dev_inode_ctrl, inode_no_t no, inode_t* inode) 36 | { 37 | block_no_t block_no; 38 | size_t offset; 39 | inode_no_to_block_no_and_offset(dev_inode_ctrl, no, &block_no, &offset); 40 | 41 | char buf[MAX_BYTES_PER_BLOCK]; 42 | bool sucess = block_read(dev_inode_ctrl->device, dev_inode_ctrl->block_size / BYTES_PER_SECTOR, block_no, buf); 43 | if (!sucess) { 44 | return false; 45 | } 46 | 47 | *inode = *((inode_t*)(buf + offset)); 48 | 49 | return true; 50 | } 51 | 52 | bool inode_dump(dev_inode_ctrl_t * dev_inode_ctrl, inode_no_t no, inode_t* inode) 53 | { 54 | block_no_t block_no; 55 | size_t offset; 56 | inode_no_to_block_no_and_offset(dev_inode_ctrl, no, &block_no, &offset); 57 | 58 | char buf[MAX_BYTES_PER_BLOCK] = {'\0'}; 59 | bool sucess = block_read(dev_inode_ctrl->device, dev_inode_ctrl->block_size / BYTES_PER_SECTOR, block_no, buf); 60 | if (!sucess) { 61 | return false; 62 | } 63 | 64 | *((inode_t*)(buf + offset)) = *inode; 65 | 66 | sucess = block_write(dev_inode_ctrl->device, dev_inode_ctrl->block_size / BYTES_PER_SECTOR, block_no, buf); 67 | if (!sucess) { 68 | log_debug("inode写入错误,block写入失败: %d", (int)block_no); 69 | return false; 70 | } 71 | 72 | return true; 73 | } 74 | 75 | /* 把inode号转为block号和block内偏移 */ 76 | static void inode_no_to_block_no_and_offset(dev_inode_ctrl_t *dev_inode_ctrl, inode_no_t no, block_no_t* p_block_no, size_t* p_offset) 77 | { 78 | assert(no < INODE_MAX_COUNT); 79 | 80 | int inode_num_per_block = dev_inode_ctrl->block_size / inode_bin_size(); 81 | int inode_blocksize = inode_block_count(dev_inode_ctrl->block_size, INODE_MAX_COUNT); 82 | int block_relative = no / inode_num_per_block; 83 | 84 | assert(block_relative < inode_blocksize); 85 | 86 | *p_block_no = dev_inode_ctrl->start + block_relative; 87 | *p_offset = (no - (inode_num_per_block * block_relative)) * inode_bin_size(); 88 | } 89 | 90 | int inode_block_count(size_t block_size, int inode_count) 91 | { 92 | return count_groups(inode_count, block_size / inode_bin_size()); 93 | } 94 | 95 | bool inode_alloc(dev_inode_ctrl_t* dev_inode_ctrl, inode_no_t* p_inode_no) 96 | { 97 | inode_t inode; 98 | for (inode_no_t i = 0; i < INODE_MAX_COUNT; i++) { 99 | bool success = inode_load(dev_inode_ctrl, i, &inode); 100 | if (!success) { 101 | return false; 102 | } 103 | if (inode.mode == 0) { 104 | *p_inode_no = i; 105 | return true; 106 | } 107 | } 108 | return false; 109 | } 110 | 111 | void inode_free(dev_inode_ctrl_t* dev_inode_ctrl, inode_no_t no) 112 | { 113 | inode_t inode; 114 | bool success = inode_load(dev_inode_ctrl, no, &inode); 115 | if (success) { 116 | inode.mode = 0; 117 | } 118 | inode_dump(dev_inode_ctrl, no, &inode); 119 | } 120 | -------------------------------------------------------------------------------- /fulfs/inode.h: -------------------------------------------------------------------------------- 1 | #ifndef __FULFS_INODE__H__ 2 | #define __FULFS_INODE__H__ 3 | 4 | #include 5 | #include 6 | 7 | #include "def.h" 8 | #include "block.h" 9 | #include "superblock.h" 10 | 11 | 12 | /* 如果INODE_MAX_COUNT是inode_no_t的最大值的话,uint16_t的 i < INODE_MAX_COUNT永真,所以为了方便减一 */ 13 | #define INODE_MAX_COUNT (UINT16_MAX - 1) 14 | 15 | typedef struct { 16 | device_handle_t device; 17 | size_t block_size; 18 | block_no_t start; 19 | block_no_t size; 20 | }dev_inode_ctrl_t; 21 | 22 | #define LEVEL_0_INDIRECT_COUNT 10 23 | typedef struct 24 | { 25 | uint16_t mode; 26 | uint16_t link_count; 27 | uint64_t size; 28 | block_no_t blocks[LEVEL_0_INDIRECT_COUNT]; 29 | block_no_t single_indirect_block; 30 | block_no_t double_indirect_block; 31 | block_no_t triple_indirect_block; 32 | timestamp_t accessed_time; 33 | timestamp_t modified_time; 34 | timestamp_t created_time; 35 | }inode_t; 36 | 37 | enum { 38 | MODE_EMPTY, 39 | MODE_FILE, 40 | MODE_DIR, 41 | MODE_SYMBOL_LINK 42 | }; 43 | 44 | size_t inode_bin_size(void); 45 | 46 | 47 | void inode_init(inode_t* inode); 48 | 49 | void dev_inode_ctrl_init(dev_inode_ctrl_t* dev_inode_ctrl, device_handle_t device, size_t block_size, block_no_t start, block_no_t size); 50 | void dev_inode_ctrl_init_from_superblock(dev_inode_ctrl_t* dev_inode_ctrl, device_handle_t device, const superblock_t* sb); 51 | 52 | 53 | /* 读取no号inode */ 54 | bool inode_load(dev_inode_ctrl_t* dev_inode_ctrl, inode_no_t no, inode_t* inode); 55 | /* 写入no号inode */ 56 | bool inode_dump(dev_inode_ctrl_t* dev_inode_ctrl, inode_no_t no, inode_t* inode); 57 | 58 | int inode_block_count(size_t block_size, int inode_count); 59 | 60 | 61 | bool inode_alloc(dev_inode_ctrl_t* dev_inode_ctrl, inode_no_t* p_inode_no); /* 分配一个空闲的inode */ 62 | void inode_free(dev_inode_ctrl_t* dev_inode_ctrl, inode_no_t no); /* 释放inode */ 63 | 64 | #endif /* __FULFS_INODE__H__ */ 65 | -------------------------------------------------------------------------------- /fulfs/mem_inode.c: -------------------------------------------------------------------------------- 1 | #include "mem_inode.h" 2 | 3 | #include 4 | #include "inode.h" 5 | 6 | #include 7 | 8 | /* 目前采用简单的数组保存和组织内存inode节点 9 | 这会造成一些性能问题,后期应该重构成hash或平衡树来提高性能 10 | */ 11 | 12 | static struct { 13 | mem_inode_t mem_inode; 14 | bool is_free; 15 | }mem_inodes[MAX_MEM_INODE_COUNT]; 16 | 17 | static bool mem_inodes_inited = false; 18 | 19 | 20 | #define READ_ERROR -1 21 | static int mem_inode_read(dev_inode_ctrl_t* dev_inode_ctrl, inode_no_t inode_no); 22 | static void mem_inodes_init(); 23 | 24 | bool mem_inode_get(dev_inode_ctrl_t* dev_inode_ctrl, inode_no_t inode_no, mem_inode_t** p_result) 25 | { 26 | if (!mem_inodes_inited) { 27 | mem_inodes_init(); 28 | mem_inodes_inited = true; 29 | } 30 | 31 | for (int i = 0; i < MAX_MEM_INODE_COUNT; i++) { 32 | mem_inode_t* mem_inode = &(mem_inodes[i].mem_inode); 33 | if (!mem_inodes[i].is_free && 34 | mem_inode->device == dev_inode_ctrl->device && mem_inode->inode_no == inode_no) { 35 | *p_result = mem_inode; 36 | } 37 | } 38 | 39 | int i = mem_inode_read(dev_inode_ctrl, inode_no); 40 | 41 | if (i != READ_ERROR) { 42 | *p_result = &(mem_inodes[i].mem_inode); 43 | } else { 44 | return false; 45 | } 46 | 47 | (*p_result)->ref_count++; 48 | return true; 49 | } 50 | 51 | bool mem_inode_put(dev_inode_ctrl_t* dev_inode_ctrl, mem_inode_t* mem_inode) 52 | { 53 | mem_inode->ref_count--; 54 | 55 | if (mem_inode->ref_count <= 0) { 56 | bool success = inode_dump(dev_inode_ctrl, mem_inode->inode_no, &(mem_inode->inode)); 57 | if (!success) { 58 | return false; 59 | } 60 | 61 | for (int i = 0; i < MAX_MEM_INODE_COUNT; i++) { 62 | if (&(mem_inodes[i].mem_inode) == mem_inode) { 63 | mem_inodes[i].is_free = true; 64 | return true; 65 | } 66 | } 67 | 68 | assert(false); 69 | 70 | } else { 71 | return true; 72 | } 73 | } 74 | 75 | static int mem_inode_read(dev_inode_ctrl_t* dev_inode_ctrl, inode_no_t inode_no) 76 | { 77 | for (int i = 0; i < MAX_MEM_INODE_COUNT; i++) { 78 | if (mem_inodes[i].is_free) { 79 | bool success = inode_load(dev_inode_ctrl, inode_no, &(mem_inodes[i].mem_inode.inode)); 80 | if (!success) { 81 | return READ_ERROR; 82 | } 83 | 84 | mem_inodes[i].is_free = false; 85 | mem_inodes[i].mem_inode.inode_no = inode_no; 86 | mem_inodes[i].mem_inode.ref_count = 0; 87 | mem_inodes[i].mem_inode.device = dev_inode_ctrl->device; 88 | return i; 89 | } 90 | } 91 | 92 | /* 超过了MAX_MEM_INODE_COUNT */ 93 | return READ_ERROR; 94 | } 95 | 96 | static void mem_inodes_init() 97 | { 98 | for (int i = 0; i < MAX_MEM_INODE_COUNT; i++) { 99 | mem_inodes[i].is_free = true; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /fulfs/mem_inode.h: -------------------------------------------------------------------------------- 1 | #ifndef __MEM_INODE_H__RAND6425650369782152371357051017830150564 2 | #define __MEM_INODE_H__RAND6425650369782152371357051017830150564 3 | 4 | #include "inode.h" 5 | 6 | #define MAX_MEM_INODE_COUNT 1024 7 | 8 | typedef struct { 9 | device_handle_t device; 10 | inode_no_t inode_no; 11 | inode_t inode; 12 | int ref_count; 13 | }mem_inode_t; 14 | 15 | bool mem_inode_get(dev_inode_ctrl_t* dev_inode_ctrl, inode_no_t inode_no, mem_inode_t** p_result); 16 | bool mem_inode_put(dev_inode_ctrl_t* dev_inode_ctrl, mem_inode_t* mem_inode); 17 | 18 | #endif /* __MEM_INODE_H__RAND6425650369782152371357051017830150564 */ 19 | -------------------------------------------------------------------------------- /fulfs/superblock.c: -------------------------------------------------------------------------------- 1 | #include "superblock.h" 2 | 3 | #include 4 | #include 5 | 6 | 7 | size_t superblock_bin_size(void) 8 | { 9 | assert(sizeof(superblock_t) <= 512); 10 | 11 | return sizeof(superblock_t); 12 | } 13 | 14 | /* NOTE:目前的实现并未考虑太多的通用性,包括内存布局和字节序 */ 15 | void superblock_dump_to_bin(superblock_t* sb, char* bin, size_t bin_size) 16 | { 17 | assert(bin_size >= superblock_bin_size()); 18 | 19 | *(superblock_t *)bin = *sb; 20 | memset(bin + superblock_bin_size(), 0, bin_size - superblock_bin_size()); 21 | } 22 | 23 | void superblock_load_from_bin(const char* bin, superblock_t* sb) 24 | { 25 | *sb = *(superblock_t *)bin; 26 | } 27 | 28 | void superblock_create(superblock_t* sb, sector_no_t sectors, int sectors_per_block, 29 | block_no_t inode_table, inode_no_t inode_count, block_no_t data_block, block_no_t data_block_free_stack, inode_no_t root_inode) 30 | { 31 | sb->sectors = sectors; 32 | sb->sectors_per_block = sectors_per_block; 33 | 34 | sb->total_size = ((sb->sectors / sb->sectors_per_block) - data_block) * (BYTES_PER_SECTOR * sb->sectors_per_block); 35 | /* sb->used_size = 0; */ 36 | 37 | sb->root_dir = root_inode; 38 | sb->inode_table_block = inode_table; 39 | sb->data_block = data_block; 40 | sb->data_block_free_stack = data_block_free_stack; 41 | 42 | 43 | sb->used_inode_count = 1; 44 | sb->used_data_block_count = 0; 45 | sb->inode_total_count = inode_count; 46 | } 47 | 48 | 49 | bool superblock_load(device_handle_t device, superblock_t* sb) 50 | { 51 | char buf[512]; 52 | bool success = DEVICE_IO_SUCCESS(device_read(device, 0, 1, buf)); 53 | if (!success) { 54 | return false; 55 | } 56 | superblock_load_from_bin(buf, sb); 57 | 58 | return true; 59 | } 60 | 61 | bool superblock_dump(device_handle_t device, superblock_t* sb) 62 | { 63 | char buf[512]; 64 | superblock_dump_to_bin(sb, buf, sizeof(buf)); 65 | return DEVICE_IO_SUCCESS(device_write(device, 0, 1, buf)); 66 | } 67 | 68 | 69 | block_no_t superblock_block_count(const superblock_t* sb) 70 | { 71 | return sb->sectors / sb->sectors_per_block; 72 | } 73 | 74 | uint64_t superblock_total_size(const superblock_t* sb) 75 | { 76 | return sb->total_size; 77 | } 78 | uint64_t superblock_used_size(const superblock_t* sb) 79 | { 80 | fsize_t inode_table_total_size = superblock_inode_table_blocksize(sb) * superblock_block_size(sb); 81 | 82 | /* 按比例计算 */ 83 | /* sb->used_inode_count / sb->inode_total_count * inode_table_total_size */ 84 | fsize_t inode_used_size = sb->used_inode_count * inode_table_total_size / sb->inode_total_count; 85 | fsize_t data_block_used_size = sb->used_data_block_count * superblock_block_size(sb); 86 | 87 | return inode_used_size + data_block_used_size; 88 | } 89 | 90 | uint64_t superblock_free_size(const superblock_t* sb) 91 | { 92 | uint64_t used_size = superblock_used_size(sb); 93 | uint64_t total_size = superblock_total_size(sb); 94 | 95 | assert(used_size <= total_size); 96 | 97 | return total_size - used_size; 98 | } 99 | 100 | void superblock_set_used_size(superblock_t* sb, uint64_t new_size) 101 | { 102 | assert(false); 103 | /* assert(sb->used_size + new_size <= sb->total_size); */ 104 | 105 | /* sb->used_size = new_size; */ 106 | 107 | } 108 | 109 | inode_no_t superblock_root_dir_inode(const superblock_t* sb) 110 | { 111 | return sb->root_dir; 112 | } 113 | 114 | 115 | block_no_t superblock_inode_table_start(const superblock_t* sb) 116 | { 117 | return sb->inode_table_block; 118 | } 119 | 120 | block_no_t superblock_inode_table_blocksize(const superblock_t* sb) 121 | { 122 | assert(sb->data_block > sb->inode_table_block); 123 | 124 | return sb->data_block - sb->inode_table_block; 125 | } 126 | 127 | block_no_t superblock_data_block_start(const superblock_t* sb) 128 | { 129 | return sb->data_block; 130 | } 131 | 132 | block_no_t superblock_data_block_size(const superblock_t* sb) 133 | { 134 | assert(superblock_block_count(sb) > sb->data_block); 135 | 136 | return superblock_block_count(sb) - sb->data_block; 137 | } 138 | 139 | block_no_t superblock_data_block_free_stack(const superblock_t* sb) 140 | { 141 | return sb->data_block_free_stack; 142 | } 143 | 144 | void superblock_data_block_free_stack_set(superblock_t* sb, block_no_t new_stack) 145 | { 146 | sb->data_block_free_stack = new_stack; 147 | } 148 | 149 | size_t superblock_block_size(const superblock_t* sb) 150 | { 151 | return sb->sectors_per_block * BYTES_PER_SECTOR; 152 | } 153 | 154 | int superblock_sectors_per_block(const superblock_t *sb) 155 | { 156 | return sb->sectors_per_block; 157 | } 158 | -------------------------------------------------------------------------------- /fulfs/superblock.h: -------------------------------------------------------------------------------- 1 | #ifndef __FULFS_SUPERBLOCK__H__ 2 | #define __FULFS_SUPERBLOCK__H__ 3 | 4 | #include 5 | #include 6 | 7 | #include "../device_io.h" 8 | #include "block.h" 9 | #include "def.h" 10 | 11 | 12 | 13 | /* 14 | superblock储存在磁盘的头部,记录了文件系统总体的信息。 15 | */ 16 | 17 | /* 这个类型能容纳下一切的和文件系统相关的大小数据 */ 18 | typedef uint64_t fssize_t; 19 | 20 | typedef struct { 21 | uint16_t sectors_per_block; /* 每个block占的扇区数 */ 22 | sector_no_t sectors; /* 总的扇区数 */ 23 | uint64_t total_size; /* 文件系统总大小 */ 24 | // uint64_t used_size; /* 被使用的大小 */ 25 | inode_no_t root_dir; /* 指向根目录的 inode */ 26 | block_no_t inode_table_block; /* inode table 起始的 block 号 */ 27 | inode_no_t inode_total_count; /* inode table区总inode个数 */ 28 | block_no_t data_block; /* data区的起始block号 */ 29 | block_no_t data_block_free_stack; /* data block 的空闲管理相关 */ 30 | 31 | inode_no_t used_inode_count; /* 已经使用的inode节点个数 注:在base_file层,删除文件减1,创建文件加1 */ 32 | block_no_t used_data_block_count; /* 已经使用的data block块个数 注:在base_block_file层负责维护此变量 */ 33 | }superblock_t; 34 | 35 | /* superblock的二进制表示的大小 */ 36 | size_t superblock_bin_size(void); 37 | 38 | /* 将superblock转成二进制字节。bin的剩余部分会被填0,返回bin */ 39 | void superblock_dump_to_bin(superblock_t* sb, char* bin, size_t bin_size); 40 | /* 将二进制字节转换成superblock。bin太小了概不负责。返回sb */ 41 | void superblock_load_from_bin(const char* bin, superblock_t* sb); 42 | 43 | /* 给一个新的文件系统初始化superblock */ 44 | void superblock_create(superblock_t* sb, sector_no_t sectors, int sectors_per_block, 45 | block_no_t inode_table, inode_no_t inode_count, block_no_t data_block, block_no_t data_block_free_stack, inode_no_t root_inode); 46 | 47 | 48 | /* 从磁盘加载superblock */ 49 | bool superblock_load(device_handle_t device, superblock_t* sb); 50 | /* 将superblock写入磁盘 */ 51 | bool superblock_dump(device_handle_t device, superblock_t* sb); 52 | 53 | /* 文件系统block总数 */ 54 | block_no_t superblock_block_count(const superblock_t* sb); 55 | size_t superblock_block_size(const superblock_t* sb); /* 每个block的大小 */ 56 | int superblock_sectors_per_block(const superblock_t *sb); 57 | 58 | uint64_t superblock_total_size(const superblock_t* sb); /* 文件系统总大小 */ 59 | uint64_t superblock_used_size(const superblock_t* sb); /* 文件系统已用大小 */ 60 | uint64_t superblock_free_size(const superblock_t* sb); /* 文件系统空闲大小 */ 61 | void superblock_set_used_size(superblock_t* sb, uint64_t new_size); /* 设置已使用的空间大小 */ 62 | inode_no_t superblock_root_dir_inode(const superblock_t* sb); /* 根目录inode */ 63 | 64 | 65 | block_no_t superblock_inode_table_start(const superblock_t* sb); /* inode起始block */ 66 | block_no_t superblock_inode_table_blocksize(const superblock_t* sb); /* inode 占用的block数 */ 67 | 68 | block_no_t superblock_data_block_start(const superblock_t* sb); /* data起始block */ 69 | block_no_t superblock_data_block_size(const superblock_t* sb); /* data占用的block数 */ 70 | 71 | block_no_t superblock_data_block_free_stack(const superblock_t* sb); /* free空闲栈管理相关 */ 72 | 73 | void superblock_data_block_free_stack_set(superblock_t* sb, block_no_t new_stack); 74 | 75 | 76 | #endif /* __FULFS_SUPERBLOCK__H__ */ 77 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "fs.h" 8 | 9 | #include "memory/alloc.h" 10 | #include "device_io.h" 11 | #include "utils/sys.h" 12 | #include "shell.h" 13 | 14 | #define ERROR -1 15 | 16 | int create(const char* path, size_t size); 17 | int format(const char* path, const char* type, int block_size); 18 | 19 | int enter(void); 20 | 21 | 22 | int main(int argc, char* argv[]) 23 | { 24 | fs_init(); 25 | 26 | int arg_count = argc - 1; 27 | char** args = argv + 1; 28 | 29 | if (arg_count == 0) { 30 | return enter(); 31 | } 32 | 33 | const char* sub_cmd = args[0]; 34 | if (strcmp(sub_cmd, "create") == 0) { 35 | 36 | if (arg_count < 3) { 37 | fprintf(stderr, "命令有误!\n"); 38 | return ERROR; 39 | } 40 | 41 | char* end; 42 | size_t size = strtoumax(args[2], &end, 10); 43 | return create(args[1], size); 44 | 45 | }else if (strcmp(sub_cmd, "format") == 0) { 46 | 47 | if (arg_count < 4) { 48 | fprintf(stderr, "命令有误!\n"); 49 | return ERROR; 50 | } 51 | 52 | char* end; 53 | int block_size = strtol(args[3], &end, 10); 54 | return format(args[1], args[2], block_size); 55 | 56 | } else if (strcmp(sub_cmd, "enter") == 0) { 57 | 58 | return enter(); 59 | 60 | } else if (strcmp(sub_cmd, "help") == 0) { 61 | 62 | printf("命令帮助:\n" 63 | "create <文件路径> <文件大小> 创建容器文件\n" 64 | "format <容器路径> <文件系统类型> <扇区大小> \n" 65 | "enter 进入文件系统shell以操作文件系统\n" 66 | "help 查看此帮助 \n" 67 | ); 68 | 69 | } else { 70 | fprintf(stderr, "命令有误!\n"); 71 | return ERROR; 72 | } 73 | } 74 | 75 | int create(const char* path, size_t size) 76 | { 77 | if (ft_create_bin_file(path, size)) { 78 | printf("创建容器文件%s成功,文件大小%d\n", path, (int)size); 79 | return ERROR; 80 | } else { 81 | printf("创建容器文件%s,大小%d失败。。。\n", path, (int)size); 82 | return 0; 83 | } 84 | } 85 | 86 | int format(const char* path, const char* type, int block_size) 87 | { 88 | int fs_type; 89 | if (strcmp(type, "fulfs") == 0) { 90 | fs_type = FS_TYPE_FULFS; 91 | } else { 92 | printf("未知的文件系统类型。。。\n"); 93 | return ERROR; 94 | } 95 | 96 | int device = device_add(path); 97 | if (!DEVICE_IO_SUCCESS(device)) { 98 | printf("设备挂载失败。。。\n"); 99 | return ERROR; 100 | } 101 | 102 | int sectors_per_block = block_size / BYTES_PER_SECTOR; 103 | if (! (0 < sectors_per_block && sectors_per_block <= 16)) { 104 | printf("目前只支持512的1到16倍的块大小"); 105 | device_del(device); 106 | return ERROR; 107 | } 108 | 109 | 110 | if (fs_format(device, sectors_per_block, fs_type) != FS_SUCCESS) { 111 | printf("格式化失败。。。\n"); 112 | device_del(device); 113 | return ERROR; 114 | } else { 115 | printf("格式化成功! 块大小: %d\n", sectors_per_block * BYTES_PER_SECTOR); 116 | device_del(device); 117 | return 0; 118 | } 119 | } 120 | 121 | int enter(void) 122 | { 123 | const char* config_file = "config.txt"; 124 | 125 | FILE* fp = fopen(config_file, "rb"); 126 | if (fp == NULL) { 127 | printf("打开配置文件失败!可能是没有配置文件?\n"); 128 | return ERROR; 129 | } 130 | 131 | /* 挂载设备 */ 132 | while (true) { 133 | char buf[32]; 134 | char path[FS_MAX_FILE_PATH]; 135 | int ret = fscanf(fp, "%s%s", buf, path); 136 | char drive_letter = buf[0]; 137 | if (ret == EOF || ret == 0) { 138 | break; 139 | } 140 | 141 | if (ret != 2) { 142 | printf("解析配置文件%s错误!\n", config_file); 143 | fclose(fp); 144 | return ERROR; 145 | } 146 | 147 | int device = device_add(path); 148 | if (!DEVICE_IO_SUCCESS(device)) { 149 | printf("挂载失败:盘号%c 容器文件%s\n", drive_letter, path); 150 | continue; 151 | } 152 | 153 | if (!fs_mount(device, drive_letter, FS_TYPE_FULFS)) { 154 | printf("挂载失败:盘号%c 容器文件%s\n", drive_letter, path); 155 | continue; 156 | } 157 | 158 | printf("挂载成功:盘号%c 容器文件%s\n", drive_letter, path); 159 | } 160 | fclose(fp); 161 | 162 | 163 | 164 | printf("******** 欢迎使用fulfs文件系统shell! ********\n"); 165 | return shell_main(); 166 | } 167 | -------------------------------------------------------------------------------- /memory/alloc.c: -------------------------------------------------------------------------------- 1 | #include "alloc.h" 2 | #include 3 | 4 | void* ft_malloc(size_t size) 5 | { 6 | return malloc(size); 7 | } 8 | 9 | void* ft_malloc0(size_t size) 10 | { 11 | return calloc((size_t)1, size); 12 | } 13 | 14 | void* ft_realloc(void* p, size_t size) 15 | { 16 | return realloc(p, size); 17 | } 18 | 19 | void ft_free(void * p) 20 | { 21 | free(p); 22 | } 23 | -------------------------------------------------------------------------------- /memory/alloc.h: -------------------------------------------------------------------------------- 1 | #ifndef __MEMORY_ALLOC__ 2 | #define __MEMORY_ALLOC__ 3 | 4 | #include 5 | 6 | /* 7 | 这个模块提供了动态内存管理的功能。 8 | 目前只是对标准库的包装而已。可是万一哪天我需要更好的内存分配器呢? 9 | */ 10 | 11 | #define FT_NEW(type_, size) ((type_ *)ft_malloc0(sizeof(type_) * size)) 12 | 13 | void* ft_malloc(size_t size); 14 | void* ft_malloc0(size_t size); 15 | void* ft_realloc(void* p, size_t size); 16 | void ft_free(void * p); 17 | 18 | 19 | #endif /* __MEMORY_ALLOC__ */ 20 | -------------------------------------------------------------------------------- /shell.c: -------------------------------------------------------------------------------- 1 | #include "shell.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "memory/alloc.h" 8 | #include "datastruct/string.h" 9 | #include "fs.h" 10 | 11 | #include "shell_command.h" 12 | 13 | #define MAX_LINE (1024 * 4) 14 | #define MAX_ARGC 1024 15 | 16 | static int cmd_dispath(const char* cmd, int argc, char* argv[]); 17 | 18 | int shell_main(void) 19 | { 20 | fs_chdir("A:/"); 21 | char line[MAX_LINE]; 22 | 23 | char* argv[MAX_ARGC]; 24 | while (true) { 25 | char cd[FS_MAX_FILE_PATH]; 26 | fs_getcwd(cd, FS_MAX_FILE_PATH); 27 | printf("fulfs@%s", cd); 28 | printf(" >>> "); 29 | if (fgets(line, sizeof(line) / sizeof(*line), stdin) == NULL) { 30 | return 0; 31 | } 32 | 33 | /* 解析参数 */ 34 | int argc = 0; 35 | const char* p; 36 | size_t size = 0; 37 | const char* next = line; 38 | while (((p = ft_string_split_next(next, " ", &size)) != NULL) && argc <= MAX_ARGC) { 39 | argv[argc] = (char* )p; 40 | ft_str_strip(argv[argc]); 41 | 42 | if (p[size] == '\0') { 43 | next = p + size; 44 | } else { 45 | next = p + size + 1; 46 | } 47 | 48 | ((char *)p)[size] = '\0'; 49 | 50 | if (argv[argc][0] != '\0') { 51 | argc++; 52 | } 53 | 54 | } 55 | 56 | if (argc > 0) { 57 | cmd_dispath(argv[0], argc - 1, argv + 1); 58 | } 59 | } 60 | 61 | return 0; 62 | } 63 | 64 | int cmd_dispath(const char* cmd, int argc, char* argv[]) 65 | { 66 | struct { 67 | const char* name; 68 | shell_cmd_f* func; 69 | }commands[] = { 70 | {"pwd", cmd_pwd}, 71 | {"cd", cmd_cd}, 72 | {"rmdir", cmd_rmdir}, 73 | {"mkdir", cmd_mkdir}, 74 | {"ls", cmd_ls}, 75 | {"cp", cmd_cp}, 76 | {"mv", cmd_mv}, 77 | {"rm", cmd_rm}, 78 | {"tree", cmd_tree}, 79 | {"ln", cmd_ln}, 80 | {"stat", cmd_stat}, 81 | {"cat", cmd_cat}, 82 | {"df", cmd_df}, 83 | {"createfile", cmd_createfile}, 84 | }; 85 | 86 | for (size_t i = 0; i < sizeof(commands) / sizeof(*commands); i++) { 87 | if (strcmp(cmd, commands[i].name) == 0) { 88 | return commands[i].func(argc, argv); 89 | } 90 | } 91 | 92 | printf("不存在命令%s!\n", cmd); 93 | return -1; 94 | } 95 | -------------------------------------------------------------------------------- /shell.h: -------------------------------------------------------------------------------- 1 | #ifndef __SHELL__H__ 2 | #define __SHELL__H__ 3 | 4 | 5 | int shell_main(void); 6 | 7 | #endif /* __SHELL__H__ */ 8 | -------------------------------------------------------------------------------- /shell_command.c: -------------------------------------------------------------------------------- 1 | #include "shell_command.h" 2 | 3 | #include 4 | #include "utils/path.h" 5 | #include "utils/sys.h" 6 | /* 7 | int cmd_pwd(int argc, char* argv[]) 8 | { 9 | char path[FS_MAX_FILE_PATH]; 10 | fs_getcwd(path, FS_MAX_FILE_PATH); 11 | printf("%s\n", path); 12 | return 0; 13 | } 14 | 15 | int cmd_cd(int argc, char* argv[]) 16 | { 17 | if (argc <= 0) { 18 | printf("命令有误\n"); 19 | return -1; 20 | } 21 | 22 | if (fs_chdir(argv[0]) == FS_SUCCESS) { 23 | return 0; 24 | } else { 25 | printf("改变失败,可能目录不存在\n"); 26 | return -1; 27 | } 28 | 29 | } 30 | 31 | int cmd_ls(int argc, char* argv[]) 32 | { 33 | const char* path; 34 | char wd[FS_MAX_FILE_PATH]; 35 | if (argc <= 0) { 36 | fs_getcwd(wd, FS_MAX_FILE_PATH); 37 | path = wd; 38 | } else { 39 | path = argv[0]; 40 | } 41 | 42 | FS_DIR* dir = fs_opendir(path); 43 | if (dir == NULL) { 44 | printf("列出目录信息失败\n"); 45 | } 46 | 47 | char name[32]; 48 | do { 49 | fs_readdir(dir, name); 50 | printf("%s ", name); 51 | } while(name[0] != '\0'); 52 | printf("\n"); 53 | 54 | fs_closedir(dir); 55 | 56 | return 0; 57 | } 58 | 59 | int cmd_mkdir(int argc, char* argv[]) 60 | { 61 | if (argc <= 0) { 62 | printf("命令有误\n"); 63 | return -1; 64 | } 65 | 66 | if (fs_mkdir(argv[0]) == FS_SUCCESS) { 67 | return 0; 68 | } else { 69 | printf("建立失败\n"); 70 | return -1; 71 | } 72 | } 73 | 74 | */ 75 | 76 | static bool tree(char* path, int* p_level) 77 | { 78 | for (int i = 0; i < *p_level; i++) { 79 | printf(" "); 80 | } 81 | printf("%s\n", path_p_basename(path)); 82 | 83 | struct fs_stat st; 84 | fs_stat(path, &st); 85 | if (st.st_mode != FS_S_IFDIR) { 86 | return true; 87 | } 88 | 89 | FS_DIR* dir = fs_opendir(path); 90 | char name[32]; 91 | 92 | while ((fs_readdir(dir, name), name[0] != '\0')) { 93 | size_t tail = strlen(path); 94 | path_join(path, FS_MAX_FILE_PATH, name); 95 | 96 | (*p_level)++; 97 | tree(path, p_level); 98 | (*p_level)--; 99 | path[tail] = '\0'; 100 | } 101 | 102 | fs_closedir(dir); 103 | return true; 104 | } 105 | 106 | int cmd_tree(int argc, char* argv[]) 107 | { 108 | char path[FS_MAX_FILE_PATH]; 109 | if (argc <= 0) { 110 | fs_getcwd(path, FS_MAX_FILE_PATH); 111 | } else { 112 | strcpy(path, argv[0]); 113 | } 114 | 115 | int level = 0; 116 | tree(path, &level); 117 | return 0; 118 | } 119 | 120 | 121 | int cmd_createfile(int argc, char* argv[]) 122 | { 123 | if (argc != 2) { 124 | printf("参数错误!\n"); 125 | return -1; 126 | } 127 | 128 | int ret = fs_stat(argv[0], NULL); 129 | if(ret == FS_SUCCESS) { 130 | printf("已存在同名文件!\n"); 131 | return -1; 132 | } 133 | 134 | int fd = fs_open(argv[0]); 135 | fs_write(fd, argv[1], strlen(argv[1])); 136 | fs_close(fd); 137 | 138 | return 0; 139 | } 140 | 141 | int cmd_cat(int argc, char* argv[]) 142 | { 143 | if (argc != 1) { 144 | printf("参数错误!\n"); 145 | return -1; 146 | } 147 | 148 | struct fs_stat st; 149 | int ret = fs_stat(argv[0], &st); 150 | if(ret != FS_SUCCESS) { 151 | printf("文件不存在!\n"); 152 | return -1; 153 | } 154 | 155 | if (st.st_mode != FS_S_IFREG) { 156 | printf("不是普通文件!\n"); 157 | return -1; 158 | } 159 | 160 | char buf[1024]; 161 | int size = 1024; 162 | 163 | 164 | int fd = fs_open(argv[0]); 165 | while (true) { 166 | int count = fs_read(fd, buf, size - 1); 167 | buf[count] = '\0'; 168 | printf("%s", buf); 169 | if (count != size - 1) { 170 | break; 171 | } 172 | } 173 | printf("\n"); 174 | fs_close(fd); 175 | 176 | return 0; 177 | } 178 | 179 | int cmd_df(int argc, char* argv[]) 180 | { 181 | printf("盘号\t设备号\t类型\t已用\t总共\n"); 182 | for (int i = 0; i <= 'z' - 'a'; i++) { 183 | struct dev_fsctrl_s ctrl; 184 | if (fs_dev_fs_ctrl(i + 'a', &ctrl)) { 185 | printf("%c\t", i + 'A'); 186 | printf("%d\t", ctrl.device); 187 | 188 | printf("%s\t", ctrl.fs_type == FS_TYPE_FULFS ? "fulfs" : "未知"); 189 | 190 | size_t size; 191 | char sym = ft_human_size((size_t)fs_filesystem_used_size(i + 'a'), &size); 192 | printf("%d%c\t", (int)size, sym); 193 | 194 | sym = ft_human_size((size_t)fs_filesystem_total_size(i + 'a'), &size); 195 | printf("%d%c\t", (int)size, sym); 196 | 197 | printf("\n"); 198 | } 199 | } 200 | return 0; 201 | } 202 | 203 | /* 由大印编写 */ 204 | int cmd_pwd(int argc, char* argv[]) 205 | { 206 | char path[FS_MAX_FILE_PATH]; 207 | fs_getcwd(path, FS_MAX_FILE_PATH); 208 | printf("%s\n", path); 209 | return 0; 210 | } 211 | 212 | int cmd_cd(int argc, char* argv[]) 213 | { 214 | if(argc !=1){ 215 | printf("输入格式错误"); 216 | return -1; 217 | } 218 | int a = fs_chdir(argv[0]); 219 | if(a == -1){ 220 | printf("改变目录失败\n"); 221 | return -1; 222 | } 223 | return 0; 224 | } 225 | 226 | int cmd_ls(int argc, char* argv[]) 227 | { 228 | if(argc == 0){ 229 | char path[FS_MAX_FILE_PATH]; 230 | fs_getcwd(path,FS_MAX_FILE_PATH); 231 | FS_DIR* dir = fs_opendir(path); 232 | if (dir == NULL){ 233 | printf("路径名无效\n"); 234 | return -1; 235 | } 236 | char name[100] = "no"; 237 | for(int i = 0;name[0] != '\0';i++){ 238 | fs_readdir(dir, name); 239 | printf("%s ",name); 240 | } 241 | printf("\n"); 242 | return -1; 243 | } 244 | if(argc > 1){ 245 | printf("输入格式错误\n"); 246 | return -1; 247 | } 248 | FS_DIR* dir = fs_opendir(argv[0]); 249 | if (dir == NULL){ 250 | printf("路径名无效\n"); 251 | return -1; 252 | } 253 | char name[100]="no"; 254 | for(int i = 0;name[0] != '\0';i++){ 255 | fs_readdir(dir, name); 256 | printf("%s ",name); 257 | } 258 | printf("\n"); 259 | return 0; 260 | } 261 | 262 | int cmd_mkdir(int argc, char* argv[]) 263 | { 264 | int a = fs_mkdir(argv[0]); 265 | if(a == -1){ 266 | printf("输入格式错误"); 267 | return -1; 268 | } 269 | return 0; 270 | } 271 | 272 | int cmd_rmdir(int argc, char* argv[]) 273 | { 274 | struct fs_stat st; 275 | int ret = fs_stat(argv[0], &st); 276 | if(ret == -1){ 277 | printf("目录不存在"); 278 | return -1; 279 | } 280 | 281 | else if (st.st_mode == FS_S_IFDIR) { 282 | fs_rmdir(argv[0]); 283 | } 284 | else printf("此文件不是目录不能执行删除目录操作"); 285 | return 0; 286 | } 287 | 288 | int cmd_stat(int argc, char* argv[]) 289 | { 290 | if (argc != 1) { 291 | printf("参数错误\n"); 292 | return -1; 293 | } 294 | 295 | struct fs_stat st; 296 | int ret = fs_stat(argv[0], &st); 297 | if (ret != FS_SUCCESS) { 298 | printf("此路径可能不存在\n"); 299 | return -1; 300 | } 301 | if (st.st_mode == FS_S_IFDIR){ 302 | printf("此文件是目录\n"); 303 | } 304 | else if (st.st_mode == FS_S_IFLNK){ 305 | printf("此文件为符号链接\n"); 306 | } 307 | else if (st.st_mode == FS_S_IFREG){ 308 | printf("此文件为普通文件\n"); 309 | } 310 | printf("inode 编号: %d\n", (int)st.st_ino); 311 | printf("硬连接数目: %d\n", (int)st.st_nlink); 312 | printf("文件大小: %d\n", (int)st.st_size); 313 | printf("块大小: %d\n", (int)st.st_blksize); 314 | printf("块数: %d\n", (int)st.st_blocks); 315 | 316 | char buf[32]; 317 | size_t size = 32; 318 | strftime(buf, size, "%Y-%m-%d %H:%M:%S", localtime(&st.st_ctime)); 319 | printf("创建时间: %s\n", buf); 320 | 321 | strftime(buf, size, "%Y-%m-%d %H:%M:%S", localtime(&st.st_atime)); 322 | printf("最后一次访问时间: %s\n", buf); 323 | 324 | strftime(buf, size, "%Y-%m-%d %H:%M:%S", localtime(&st.st_mtime)); 325 | printf("最后一次修改时间: %s\n", buf); 326 | return 0; 327 | } 328 | 329 | /* 由金举编写 */ 330 | int cmd_cp(int argc, char* argv[]) 331 | { 332 | int sourceFile; 333 | int targetFile; 334 | if(argc == 2) 335 | { 336 | sourceFile = fs_open(argv[0]); 337 | if (sourceFile == FS_ERROR) 338 | { 339 | /* code */ 340 | printf("操作失败!\n"); 341 | return 0; 342 | } 343 | targetFile = fs_open(argv[1]); 344 | if (targetFile == FS_ERROR) 345 | { 346 | /* code */ 347 | printf("操作失败!\n"); 348 | return 0; 349 | } 350 | char buf[1024]; 351 | int c = fs_read(sourceFile, buf, sizeof(buf) - 1); 352 | while(c > 0) 353 | { 354 | fs_write(targetFile, buf, c); 355 | c = fs_read(sourceFile, buf, sizeof(buf) - 1); 356 | } 357 | 358 | fs_close(sourceFile); 359 | fs_close(targetFile); 360 | } 361 | else 362 | { 363 | printf("指令格式不正确!\n"); 364 | } 365 | return 0; 366 | } 367 | 368 | int cmd_mv(int argc, char* argv[]) 369 | { 370 | if (argc == 2) 371 | { 372 | int isSuccessed; 373 | isSuccessed = fs_link(argv[0],argv[1]); 374 | if (isSuccessed == FS_ERROR) 375 | { 376 | printf("移动失败!\n"); 377 | return 0; 378 | } 379 | fs_unlink(argv[0]); 380 | } 381 | else 382 | { 383 | printf("指令格式不正确!\n"); 384 | } 385 | return 0; 386 | } 387 | 388 | int cmd_rm(int argc, char* argv[]) 389 | { 390 | int isSuccessed; 391 | for(int i =0;i 5 | #include 6 | 7 | #include "fs.h" 8 | 9 | typedef int (shell_cmd_f) (int argc, char* argv[]); 10 | 11 | int cmd_cd(int argc, char* argv[]); 12 | int cmd_pwd(int argc, char* argv[]); 13 | 14 | int cmd_ls(int argc, char* argv[]); 15 | 16 | int cmd_mkdir(int argc, char* argv[]); 17 | int cmd_rmdir(int argc, char* argv[]); 18 | 19 | int cmd_cp(int argc, char* argv[]); 20 | 21 | int cmd_mv(int argc, char* argv[]); 22 | 23 | int cmd_rm(int argc, char* argv[]); 24 | 25 | int cmd_tree(int argc, char* argv[]); 26 | 27 | int cmd_ln(int argc, char* argv[]); 28 | 29 | int cmd_stat(int argc, char* argv[]); 30 | 31 | int cmd_cat(int argc, char* argv[]); 32 | int cmd_createfile(int argc, char* argv[]); 33 | 34 | int cmd_df(int argc, char* argv[]); 35 | 36 | 37 | 38 | #endif /* __SHELL_COMMAND__H__ */ 39 | -------------------------------------------------------------------------------- /test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "device_io.h" 8 | #include "datastruct/string.h" 9 | #include "utils/sys.h" 10 | #include "utils/testtools.h" 11 | #include "utils/path.h" 12 | #include "fulfs/filesystem.h" 13 | #include "fulfs/superblock.h" 14 | #include "fulfs/data_block.h" 15 | #include "fulfs/inode.h" 16 | #include "fulfs/base_file.h" 17 | #include "fulfs/base_block_file.h" 18 | #include "fs.h" 19 | #include "utils/log.h" 20 | 21 | #include 22 | 23 | void bytearray_rand(char* arr, size_t size); 24 | bool bytearray_equal(const char* a1, const char* a2, size_t size); 25 | void bytearray_dump(const char* arr, size_t size); 26 | 27 | bool test_device_io(void) 28 | { 29 | const char* path = "device_io_test.bin"; 30 | size_t file_size = 512 * 1024; 31 | if (ft_filesize(path) != file_size) { 32 | bool success = ft_create_bin_file(path, file_size); 33 | assert(success); 34 | } 35 | 36 | char buf[512 * 32] = {0}; 37 | 38 | int handle = device_add(path); 39 | char rand_buf[512 * 32]; 40 | bytearray_rand(rand_buf, 512 * 32); 41 | 42 | device_write(handle, 1, 1, rand_buf); 43 | device_read(handle, 1, 1, buf); 44 | 45 | TEST_ASSERT(bytearray_equal(buf, rand_buf, 512)); 46 | 47 | 48 | device_write(handle, 120, 32, rand_buf); 49 | device_read(handle, 120, 32, buf); 50 | 51 | TEST_ASSERT(bytearray_equal(buf, rand_buf, 512)); 52 | 53 | device_del(handle); 54 | return true; 55 | } 56 | 57 | bool test_inode_io(void) 58 | { 59 | const char* path = "device_io_test.bin"; 60 | int device = device_add(path); 61 | 62 | dev_inode_ctrl_t dev_inode_ctrl; 63 | dev_inode_ctrl_init(&dev_inode_ctrl, device, 4 * 1024, 1, 2048); 64 | 65 | /* 顺着写 倒着读 */ 66 | inode_t inode[1000]; 67 | inode_t inode2[1000]; 68 | for (inode_no_t i = 0; i < 1000; i++) { 69 | bytearray_rand((char *)&inode[i], sizeof(inode_t)); 70 | TEST_ASSERT(inode_dump(&dev_inode_ctrl, i, &inode[i])); 71 | } 72 | 73 | for (int i = 1000 - 1; i >= 0; i--) { 74 | TEST_ASSERT(inode_load(&dev_inode_ctrl, i, &inode2[i])); 75 | } 76 | 77 | for (inode_no_t i = 0; i < 1000; i++) { 78 | TEST_ASSERT(bytearray_equal((char *)&inode[i], (char *)&inode2[i], sizeof(inode_t))); 79 | } 80 | 81 | device_del(device); 82 | return true; 83 | } 84 | 85 | bool test_path(void) 86 | { 87 | char path[FS_MAX_FILE_PATH]; 88 | 89 | strcpy(path, "C:/abc/.././dfj"); 90 | path_simplify(path); 91 | TEST_ASSERT(strcmp(path, "C:/dfj") == 0); 92 | 93 | strcpy(path, "C:/abc/def/../../dfj"); 94 | path_simplify(path); 95 | TEST_ASSERT(strcmp(path, "C:/dfj") == 0); 96 | 97 | return true; 98 | const char* p = "fdfd hgjf fdie fd"; 99 | size_t size = 0; 100 | while ((p = ft_string_split_next(p + size, " ", &size)) != NULL) { 101 | char buf[32]; 102 | /* NOTE: strncpy的大坑 =_=*/ 103 | strncpy(buf, p, size); 104 | buf[size] = '\0'; 105 | printf("%s %d\n", buf, (int)size); 106 | } 107 | return true; 108 | } 109 | 110 | bool test_format(void) 111 | { 112 | const char* path = "device_io_test.bin"; 113 | size_t file_size = 32 * 1024 * 1024; 114 | if (ft_filesize(path) != file_size) { 115 | bool success = ft_create_bin_file(path, file_size); 116 | assert(success); 117 | } 118 | 119 | int device = device_add(path); 120 | TEST_ASSERT(fulfs_format(device, 4 * 1024 / 512)); 121 | 122 | superblock_t sb; 123 | TEST_ASSERT(superblock_load(device, &sb)); 124 | TEST_ASSERT(superblock_block_size(&sb) == 4 * 1024); 125 | /* TEST_ASSERT(superblock_used_size(&sb) == 0); */ 126 | log_info("测试的文件系统data block范围:[%ld, %ld)\n", superblock_data_block_start(&sb), 127 | superblock_data_block_start(&sb) + superblock_data_block_size(&sb)); 128 | 129 | 130 | dev_inode_ctrl_t dev_inode_ctrl; 131 | dev_inode_ctrl_init_from_superblock(&dev_inode_ctrl, device, &sb); 132 | inode_t inode; 133 | 134 | TEST_ASSERT(inode_load(&dev_inode_ctrl, 0, &inode)); 135 | TEST_ASSERT(inode.mode == MODE_DIR); 136 | 137 | for (inode_no_t i = 1; i < INODE_MAX_COUNT; i++) { 138 | TEST_ASSERT(inode_load(&dev_inode_ctrl, i, &inode)); 139 | TEST_ASSERT(inode.mode == 0); 140 | } 141 | 142 | 143 | base_file_t base_file; 144 | TEST_ASSERT(base_file_open(&base_file, device, &sb, 0)); 145 | TEST_ASSERT(base_file_mode(&base_file) == MODE_DIR); 146 | 147 | device_del(device); 148 | return true; 149 | } 150 | 151 | bool test_base_block_file(void) 152 | { 153 | const char* path = "device_io_test.bin"; 154 | int device = device_add(path); 155 | 156 | superblock_t sb; 157 | TEST_ASSERT(superblock_load(device, &sb)); 158 | 159 | inode_no_t inode_no; 160 | TEST_ASSERT(base_file_create(device, &sb, MODE_FILE, &inode_no)); 161 | 162 | dev_inode_ctrl_t dev_inode_ctrl; 163 | dev_inode_ctrl_init_from_superblock(&dev_inode_ctrl, device, &sb); 164 | inode_t inode; 165 | TEST_ASSERT(inode_load(&dev_inode_ctrl, inode_no, &inode)); 166 | 167 | for (int i = 0; i < 32; i++) { 168 | block_no_t block_1, block_2; 169 | TEST_ASSERT(base_block_file_push_block(device, &sb, &inode, &block_1)); 170 | inode.size = (i + 1) * superblock_block_size(&sb); 171 | TEST_ASSERT(base_block_file_locate(device, &sb, &inode, i, &block_2)); 172 | TEST_INT_EQUAL(block_1, block_2); 173 | } 174 | 175 | /* =_= 还是有错误 测试是否0号块被修改了 */ 176 | TEST_ASSERT(superblock_load(device, &sb)); 177 | TEST_ASSERT(sb.data_block_free_stack != 0); 178 | device_del(device); 179 | return true; 180 | } 181 | 182 | bool test_base_file(void) 183 | { 184 | const char* path = "device_io_test.bin"; 185 | int device = device_add(path); 186 | 187 | superblock_t sb; 188 | TEST_ASSERT(superblock_load(device, &sb)); 189 | 190 | inode_no_t inode_no; 191 | TEST_ASSERT(base_file_create(device, &sb, MODE_FILE, &inode_no)); 192 | TEST_ASSERT(inode_no > 0); 193 | 194 | base_file_t base_file; 195 | TEST_ASSERT(base_file_open(&base_file, device, &sb, inode_no)); 196 | TEST_ASSERT(base_file_size(&base_file) == 0); 197 | TEST_ASSERT(base_file_tell(&base_file) == 0); 198 | 199 | int size = 1024 * 4 * 30; 200 | char buf[1024 * 4 * 30]; 201 | char rand_buf[1024 * 4 * 30]; 202 | 203 | TEST_ASSERT(base_file_read(&base_file, buf, size) == 0); 204 | 205 | bytearray_rand(rand_buf, size); 206 | int res = base_file_write(&base_file, rand_buf, size); 207 | TEST_ASSERT(res == size); 208 | TEST_ASSERT(base_file_size(&base_file) == (fsize_t)size); 209 | TEST_ASSERT(base_file_tell(&base_file) == (fsize_t)size); 210 | 211 | base_file_seek(&base_file, 0); 212 | res = base_file_read(&base_file, buf, size); 213 | TEST_ASSERT(res == size); 214 | TEST_ASSERT(bytearray_equal(buf, rand_buf, size)); 215 | 216 | 217 | TEST_ASSERT(base_file_open(&base_file, device, &sb, 0)); 218 | TEST_ASSERT(base_file_mode(&base_file) == MODE_DIR); 219 | 220 | device_del(device); 221 | return true; 222 | } 223 | 224 | 225 | bool test_fs(void) 226 | { 227 | fs_init(); 228 | 229 | const char* path = "device_io_test.bin"; 230 | int device = device_add(path); 231 | 232 | TEST_ASSERT(fs_mount(device, 'A', FS_TYPE_FULFS)); 233 | 234 | int fd = fs_open("A:/text.txt"); 235 | TEST_ASSERT(fd != FS_ERROR); 236 | fs_close(fd); 237 | 238 | 239 | fd = fs_open("A:/text2.txt"); 240 | TEST_ASSERT(fd != FS_ERROR); 241 | fs_close(fd); 242 | 243 | TEST_ASSERT(fs_mkdir("A:/testdir") == FS_SUCCESS); 244 | 245 | fd = fs_open("A:/text3.txt"); 246 | TEST_ASSERT(fd != FS_ERROR); 247 | fs_close(fd); 248 | 249 | 250 | FS_DIR* dir = fs_opendir("A:/"); 251 | char name[30]; 252 | do { 253 | fs_readdir(dir, name); 254 | printf("%s\n", name); 255 | }while (name[0] != '\0'); 256 | printf("\n"); 257 | 258 | 259 | TEST_ASSERT(fs_rmdir("A:/testdir") == FS_SUCCESS); 260 | dir = fs_opendir("A:/"); 261 | do { 262 | fs_readdir(dir, name); 263 | printf("%s\n", name); 264 | }while (name[0] != '\0'); 265 | printf("\n"); 266 | 267 | fs_closedir(dir); 268 | 269 | 270 | return true; 271 | } 272 | 273 | 274 | int main(int argc, char *argv[]) 275 | { 276 | srand(time(NULL)); 277 | 278 | TestFunc funcs[] = { 279 | /* test_device_io, */ 280 | /* test_inode_io, */ 281 | test_path, 282 | test_format, 283 | /* test_base_block_file, */ 284 | /* test_base_file, */ 285 | test_fs, 286 | }; 287 | return test_main(funcs, sizeof(funcs) / sizeof(*funcs)); 288 | } 289 | 290 | 291 | bool bytearray_equal(const char* a1, const char* a2, size_t size) 292 | { 293 | for (size_t i = 0; i < size; i++) { 294 | if (a1[i] != a2[i]) { 295 | return false; 296 | } 297 | } 298 | return true; 299 | } 300 | 301 | void bytearray_rand(char* arr, size_t size) 302 | { 303 | for (size_t i = 0; i < size; i++) { 304 | arr[i] = rand() % 64; 305 | } 306 | } 307 | 308 | 309 | void bytearray_dump(const char* arr, size_t size) 310 | { 311 | for (size_t i = 0; i < size; i++) { 312 | printf(" %3u ", (unsigned int)arr[i]); 313 | if ((i + 1) % 20 == 0) { 314 | printf("\n"); 315 | } 316 | } 317 | printf("\n"); 318 | } 319 | -------------------------------------------------------------------------------- /utils/log.c: -------------------------------------------------------------------------------- 1 | #include "log.h" 2 | 3 | #include 4 | #include 5 | 6 | static const char* level_to_str(int level) 7 | { 8 | static const char* strs[] = {"DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"}; 9 | return strs[level]; 10 | } 11 | 12 | static FILE* log_file_fp = NULL; 13 | 14 | void log_set_file(FILE* fp) 15 | { 16 | log_file_fp = fp; 17 | } 18 | 19 | FILE* log_get_file(void) 20 | { 21 | if (log_file_fp == NULL) { 22 | return stderr; 23 | } else { 24 | return log_file_fp; 25 | } 26 | } 27 | 28 | 29 | void log_log_valist(int level, const char* format, va_list ap) 30 | { 31 | FILE* fp = log_get_file(); 32 | fprintf(fp, "%s:", level_to_str(level)); 33 | vfprintf(fp, format, ap); 34 | fprintf(fp, "\n"); 35 | } 36 | 37 | -------------------------------------------------------------------------------- /utils/log.h: -------------------------------------------------------------------------------- 1 | #ifndef __LOG__H__ 2 | #define __LOG__H__ 3 | 4 | #include 5 | #include 6 | 7 | /* 为了在关闭日志时能达到0开销,所以没法设计成能使用多个日志实例的形式 */ 8 | 9 | enum { 10 | LOG_DEBUG, 11 | LOG_INFO, 12 | LOG_WARNING, 13 | LOG_ERROR, 14 | LOG_CRITICAL 15 | }; 16 | 17 | #define LOG_LEVEL LOG_DEBUG 18 | 19 | void log_set_file(FILE* fp); 20 | FILE* log_get_file(void); 21 | 22 | void log_log_valist(int level, const char* format, va_list ap); 23 | 24 | static inline void log_debug(const char *format, ...); 25 | static inline void log_info(const char *format, ...); 26 | static inline void log_warning(const char *format, ...); 27 | static inline void log_error(const char *format, ...); 28 | static inline void log_critical(const char *format, ...); 29 | 30 | static void log_debug(const char *format, ...) 31 | { 32 | #if LOG_LEVEL <= LOG_DEBUG 33 | va_list ap; 34 | va_start(ap, format); 35 | log_log_valist(LOG_DEBUG, format, ap); 36 | va_end(ap); 37 | #endif 38 | } 39 | 40 | static void log_info(const char *format, ...) 41 | { 42 | #if LOG_LEVEL <= LOG_INFO 43 | va_list ap; 44 | va_start(ap, format); 45 | log_log_valist(LOG_INFO, format, ap); 46 | va_end(ap); 47 | #endif 48 | } 49 | 50 | static void log_warning(const char *format, ...) 51 | { 52 | #if LOG_LEVEL <= LOG_WARNING 53 | va_list ap; 54 | va_start(ap, format); 55 | log_log_valist(LOG_WARNING, format, ap); 56 | va_end(ap); 57 | #endif 58 | } 59 | 60 | static void log_error(const char *format, ...) 61 | { 62 | #if LOG_LEVEL <= LOG_ERROR 63 | va_list ap; 64 | va_start(ap, format); 65 | log_log_valist(LOG_ERROR, format, ap); 66 | va_end(ap); 67 | #endif 68 | } 69 | 70 | static void log_critical(const char *format, ...) 71 | { 72 | #if LOG_LEVEL <= LOG_CRITICAL 73 | va_list ap; 74 | va_start(ap, format); 75 | log_log_valist(LOG_CRITICAL, format, ap); 76 | va_end(ap); 77 | #endif 78 | } 79 | 80 | #endif /* __LOG__H__ */ 81 | -------------------------------------------------------------------------------- /utils/math.h: -------------------------------------------------------------------------------- 1 | #ifndef __UTILS_MATH__H__ 2 | #define __UTILS_MATH__H__ 3 | 4 | 5 | 6 | /* 每组有per_group个,n个可以分成几组? */ 7 | static inline unsigned long count_groups(unsigned long n, unsigned long per_group) 8 | { 9 | return n / per_group + ((n % per_group == 0) ? 0 : 1); 10 | } 11 | 12 | static inline int min_int(int a, int b) 13 | { 14 | return a < b ? a : b; 15 | } 16 | 17 | 18 | #endif /* __UTILS_MATH__H__ */ 19 | -------------------------------------------------------------------------------- /utils/path.c: -------------------------------------------------------------------------------- 1 | #include "path.h" 2 | 3 | #include "../memory/alloc.h" 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | static char* str_next(char* s, char c); 10 | static bool str_equal(const char* start, size_t size, const char* s2); 11 | 12 | void path_simplify(char* path) 13 | { 14 | int size = strlen(path); 15 | 16 | char* buf = FT_NEW(char, size); 17 | char* top = buf; 18 | 19 | char* start = path; 20 | char* end = str_next(path, '/'); 21 | 22 | while (*start != '\0') { 23 | if (!str_equal(start, end - start, "/.")) { 24 | if (!str_equal(start, end - start, "/..")) { 25 | memcpy(top, start, end - start); 26 | top += end - start; 27 | *top = '\0'; 28 | } else { 29 | top = (char *)path_p_basename(buf) - 1; 30 | *top = '\0'; 31 | } 32 | } 33 | 34 | start = end; 35 | end = str_next(end, '/'); 36 | } 37 | 38 | strcpy(path, buf); 39 | ft_free(buf); 40 | } 41 | 42 | static char* str_next(char* s, char c) 43 | { 44 | if (*s == c) { 45 | s++; 46 | } 47 | 48 | while (*s != '\0') { 49 | if (*s == c) { 50 | return s; 51 | } 52 | s++; 53 | } 54 | return s; 55 | } 56 | 57 | static bool str_equal(const char* start, size_t size, const char* s2) 58 | { 59 | if (strlen(s2) != size) { 60 | return false; 61 | } 62 | 63 | for (size_t i = 0; i < size; i++) { 64 | if (start[i] != s2[i]) { 65 | return false; 66 | } 67 | } 68 | return true; 69 | } 70 | 71 | void path_dirname(const char* path, char* dir) 72 | { 73 | strcpy(dir, path); 74 | int src_size = strlen(path); 75 | 76 | bool is_abs = (dir[0] == '/'); 77 | 78 | for (int i = src_size - 1; i >= 0; i--) { 79 | if (dir[i] == '/') { 80 | 81 | /* 绝对路径的情况下,/的上级还是/,/xxx 的上级是/ */ 82 | if (is_abs && i == 0) { 83 | dir[i + 1] = '\0'; 84 | return; 85 | } else { 86 | dir[i] = '\0'; 87 | return; 88 | } 89 | } 90 | } 91 | 92 | /* 能走到这儿的肯定是相对路径 */ 93 | dir[0] = '\0'; 94 | } 95 | 96 | void path_basename(const char* path, char* name, size_t size) 97 | { 98 | const char* p_name = path_p_basename(path); 99 | strncpy(name, p_name, size); 100 | name[size] = '\0'; 101 | } 102 | 103 | const char* path_p_basename(const char* path) 104 | { 105 | int src_size = strlen(path); 106 | if (src_size == 0) { 107 | return path; 108 | } 109 | 110 | for (int i = src_size - 1; i >= 0; i--) { 111 | if (path[i] == '/') { 112 | return path + i + 1; 113 | } 114 | } 115 | return path; 116 | } 117 | 118 | char* path_join(char* path1, size_t size, const char* path) 119 | { 120 | size_t top = 0; 121 | while (path1[top] != '\0') { 122 | top++; 123 | } 124 | 125 | if (top > 0 && path1[top - 1] != '/') { 126 | if (top >= size) { 127 | return path1; 128 | } 129 | 130 | path1[top] = '/'; 131 | top++; 132 | path1[top + 1] = '\0'; 133 | } 134 | 135 | strncpy(path1 + top, path, size - top - 1); 136 | (path1 + top)[size - top - 1] = '\0'; 137 | return path1; 138 | } 139 | -------------------------------------------------------------------------------- /utils/path.h: -------------------------------------------------------------------------------- 1 | #ifndef __UTILS__PATH__H__ 2 | #define __UTILS__PATH__H__ 3 | 4 | #include 5 | 6 | void path_simplify(char* path); 7 | 8 | void path_dirname(const char* path, char* dir); 9 | void path_basename(const char* path, char* name, size_t size); 10 | 11 | const char* path_p_basename(const char* path); 12 | 13 | char* path_join(char* path1, size_t size, const char* path); 14 | 15 | #endif /* __UTILS__PATH__H__ */ 16 | -------------------------------------------------------------------------------- /utils/sys.c: -------------------------------------------------------------------------------- 1 | #include "sys.h" 2 | 3 | #include 4 | 5 | size_t ft_filesize_from_fp(FILE* fp) 6 | { 7 | size_t old = ftell(fp); 8 | fseek(fp, 0, SEEK_END); 9 | size_t size = ftell(fp); 10 | fseek(fp, old, SEEK_SET); 11 | return size; 12 | } 13 | 14 | size_t ft_filesize(const char* path) 15 | { 16 | FILE* fp = fopen(path, "rb"); 17 | if (fp == NULL) { 18 | return 0; 19 | } 20 | 21 | size_t size = ft_filesize_from_fp(fp); 22 | fclose(fp); 23 | return size; 24 | } 25 | 26 | bool ft_create_bin_file(const char* path, size_t file_size) 27 | { 28 | FILE* fp = fopen(path, "wb"); 29 | if (fp == NULL) { 30 | return false; 31 | } 32 | 33 | int buf_size = 512; 34 | char buf[512] = {0}; 35 | 36 | for (size_t i = 0; i < file_size; i += buf_size) { 37 | size_t writed_size; 38 | if (i + buf_size > file_size) { 39 | writed_size = file_size - i; 40 | } else { 41 | writed_size = buf_size; 42 | } 43 | size_t success = fwrite(buf, writed_size, 1, fp); 44 | if (success < 1) { 45 | fclose(fp); 46 | remove(path); 47 | return false; 48 | } 49 | } 50 | fclose(fp); 51 | return true; 52 | } 53 | 54 | char ft_human_size(size_t size, size_t* p_size) 55 | { 56 | if (size > 1024) { 57 | size /= 1024; 58 | } else { 59 | *p_size = size; 60 | return 'B'; 61 | } 62 | 63 | if (size > 1024) { 64 | size /= 1024; 65 | } else { 66 | *p_size = size; 67 | return 'K'; 68 | } 69 | 70 | if (size > 1024) { 71 | size /= 1024; 72 | } else { 73 | *p_size = size; 74 | return 'M'; 75 | } 76 | 77 | if (size > 1024) { 78 | size /= 1024; 79 | } else { 80 | *p_size = size; 81 | return 'G'; 82 | } 83 | 84 | *p_size = size; 85 | return 'T'; 86 | } 87 | -------------------------------------------------------------------------------- /utils/sys.h: -------------------------------------------------------------------------------- 1 | #ifndef __UTILS_SYS__ 2 | #define __UTILS_SYS__ 3 | 4 | #include 5 | #include 6 | size_t ft_filesize_from_fp(FILE* fp); 7 | size_t ft_filesize(const char* path); 8 | 9 | bool ft_create_bin_file(const char* path, size_t file_size); 10 | 11 | char ft_human_size(size_t size, size_t* p_size); 12 | 13 | 14 | #endif /* __UTILS_SYS__ */ 15 | -------------------------------------------------------------------------------- /utils/testtools.c: -------------------------------------------------------------------------------- 1 | #include "testtools.h" 2 | 3 | #include 4 | 5 | int test_main(TestFunc funcs[], size_t size) 6 | { 7 | for (size_t i = 0; i < size; i++) { 8 | bool result = (*funcs[i])(); 9 | if (!result) { 10 | return i + 1; 11 | } 12 | } 13 | return 0; 14 | } 15 | -------------------------------------------------------------------------------- /utils/testtools.h: -------------------------------------------------------------------------------- 1 | #ifndef __TESTTOOLS__H__ 2 | #define __TESTTOOLS__H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #define TEST_ASSERT(exp) \ 10 | do { \ 11 | if (!(exp)) { \ 12 | fprintf(stderr, "Test error in %s:%d: %s\n", __FILE__, __LINE__, #exp); \ 13 | return false; \ 14 | } \ 15 | } while(0) \ 16 | 17 | #define TEST_ASSERT_MSG(exp, msg_exp) \ 18 | do { \ 19 | if (!(exp)) { \ 20 | fprintf(stderr, "Test error in %s:%d: %s\n", __FILE__, __LINE__, #exp); \ 21 | msg_exp; \ 22 | return false; \ 23 | } \ 24 | } while(0) \ 25 | 26 | #define TEST_INT_EQUAL(a, b) \ 27 | do { \ 28 | int64_t __a = a; \ 29 | int64_t __b = b; \ 30 | \ 31 | if (!(__a == __b)) { \ 32 | fprintf(stderr, "Test error in %s:%d: %s == %s. In fact:%lld != %lld\n", __FILE__, __LINE__, #a, #b, __a, __b); \ 33 | return false; \ 34 | } \ 35 | \ 36 | }while(0) \ 37 | 38 | typedef bool (*TestFunc)(void); 39 | 40 | int test_main(TestFunc funcs[], size_t size); 41 | 42 | #endif /* __TESTTOOLS__H__ */ 43 | --------------------------------------------------------------------------------