├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── README.md ├── SystemIilustration.svg └── src ├── disk_manager.rs ├── disk_manager └── disk.rs └── main.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | file-sys.vd 3 | /.vscode 4 | /.VSCodeCounter 5 | Cargo.lock 6 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "ansi_rgb" 7 | version = "0.2.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "a730095eb14ee842a0f1e68504b85c8d4a19b1ef2ac2a9b4debf0ed982f9b08a" 10 | dependencies = [ 11 | "rgb", 12 | ] 13 | 14 | [[package]] 15 | name = "bincode" 16 | version = "1.3.3" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" 19 | dependencies = [ 20 | "serde", 21 | ] 22 | 23 | [[package]] 24 | name = "bytemuck" 25 | version = "1.7.0" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "9966d2ab714d0f785dbac0a0396251a35280aeb42413281617d0209ab4898435" 28 | 29 | [[package]] 30 | name = "cfg-if" 31 | version = "1.0.0" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 34 | 35 | [[package]] 36 | name = "file-system" 37 | version = "0.1.0" 38 | dependencies = [ 39 | "ansi_rgb", 40 | "bincode", 41 | "rand", 42 | "serde", 43 | ] 44 | 45 | [[package]] 46 | name = "getrandom" 47 | version = "0.2.3" 48 | source = "registry+https://github.com/rust-lang/crates.io-index" 49 | checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" 50 | dependencies = [ 51 | "cfg-if", 52 | "libc", 53 | "wasi", 54 | ] 55 | 56 | [[package]] 57 | name = "libc" 58 | version = "0.2.98" 59 | source = "registry+https://github.com/rust-lang/crates.io-index" 60 | checksum = "320cfe77175da3a483efed4bc0adc1968ca050b098ce4f2f1c13a56626128790" 61 | 62 | [[package]] 63 | name = "ppv-lite86" 64 | version = "0.2.10" 65 | source = "registry+https://github.com/rust-lang/crates.io-index" 66 | checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" 67 | 68 | [[package]] 69 | name = "proc-macro2" 70 | version = "1.0.27" 71 | source = "registry+https://github.com/rust-lang/crates.io-index" 72 | checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038" 73 | dependencies = [ 74 | "unicode-xid", 75 | ] 76 | 77 | [[package]] 78 | name = "quote" 79 | version = "1.0.9" 80 | source = "registry+https://github.com/rust-lang/crates.io-index" 81 | checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" 82 | dependencies = [ 83 | "proc-macro2", 84 | ] 85 | 86 | [[package]] 87 | name = "rand" 88 | version = "0.8.4" 89 | source = "registry+https://github.com/rust-lang/crates.io-index" 90 | checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" 91 | dependencies = [ 92 | "libc", 93 | "rand_chacha", 94 | "rand_core", 95 | "rand_hc", 96 | ] 97 | 98 | [[package]] 99 | name = "rand_chacha" 100 | version = "0.3.1" 101 | source = "registry+https://github.com/rust-lang/crates.io-index" 102 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 103 | dependencies = [ 104 | "ppv-lite86", 105 | "rand_core", 106 | ] 107 | 108 | [[package]] 109 | name = "rand_core" 110 | version = "0.6.3" 111 | source = "registry+https://github.com/rust-lang/crates.io-index" 112 | checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" 113 | dependencies = [ 114 | "getrandom", 115 | ] 116 | 117 | [[package]] 118 | name = "rand_hc" 119 | version = "0.3.1" 120 | source = "registry+https://github.com/rust-lang/crates.io-index" 121 | checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" 122 | dependencies = [ 123 | "rand_core", 124 | ] 125 | 126 | [[package]] 127 | name = "rgb" 128 | version = "0.8.27" 129 | source = "registry+https://github.com/rust-lang/crates.io-index" 130 | checksum = "8fddb3b23626145d1776addfc307e1a1851f60ef6ca64f376bcb889697144cf0" 131 | dependencies = [ 132 | "bytemuck", 133 | ] 134 | 135 | [[package]] 136 | name = "serde" 137 | version = "1.0.126" 138 | source = "registry+https://github.com/rust-lang/crates.io-index" 139 | checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03" 140 | dependencies = [ 141 | "serde_derive", 142 | ] 143 | 144 | [[package]] 145 | name = "serde_derive" 146 | version = "1.0.126" 147 | source = "registry+https://github.com/rust-lang/crates.io-index" 148 | checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43" 149 | dependencies = [ 150 | "proc-macro2", 151 | "quote", 152 | "syn", 153 | ] 154 | 155 | [[package]] 156 | name = "syn" 157 | version = "1.0.73" 158 | source = "registry+https://github.com/rust-lang/crates.io-index" 159 | checksum = "f71489ff30030d2ae598524f61326b902466f72a0fb1a8564c001cc63425bcc7" 160 | dependencies = [ 161 | "proc-macro2", 162 | "quote", 163 | "unicode-xid", 164 | ] 165 | 166 | [[package]] 167 | name = "unicode-xid" 168 | version = "0.2.2" 169 | source = "registry+https://github.com/rust-lang/crates.io-index" 170 | checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" 171 | 172 | [[package]] 173 | name = "wasi" 174 | version = "0.10.2+wasi-snapshot-preview1" 175 | source = "registry+https://github.com/rust-lang/crates.io-index" 176 | checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" 177 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "file-system" 3 | version = "0.1.0" 4 | edition = "2018" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | # The core APIs, including the Serialize and Deserialize traits. Always 10 | # required when using Serde. The "derive" feature is only required when 11 | # using #[derive(Serialize, Deserialize)] to make Serde work with structs 12 | # and enums defined in your crate. 13 | serde = { version = "1.0", features = ["derive"] } 14 | bincode = "1.3.3" 15 | ansi_rgb = "0.2.0" 16 | rand = "0.8.4" 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 自己用Rust实现的简单文件系统,单用户多级目录,用FAT表进行实现。 2 | 3 | 以下是实验报告书。 4 | 5 | 6 | # 设计目的 7 | 8 | 通过设计一个小型文件系统,进一步掌握文件管理的方法和技术,在实践中去认识文件系统的实现原理,加深对文件系统存储、数据的安全性和一致性理解,使学生初步具有研究、设计、编制和调试操作系统模块的能力。 9 | 10 | # 设计内容 11 | 12 | 实现一个模拟文件系统,可为该文件系统设计相应的数据结构来管理目录、磁盘空闲空间、已分配空间等。提供文件的创建、删除、移位、改名等功能。提供良好的界面,可以显示磁盘文件系统的状态和空间的使用情况。提供虚拟磁盘转储功能,可将信息存入磁盘,还可从磁盘读入内存。 13 | 14 | # 设计准备 15 | 16 | ## 原理和概念 17 | 18 | 文件系统,指OS中管理文件的软件。它负责管理文件的存储、检索、更新,提供安全可靠的共享和保护手段,并为用户提供一整套方便有效的文件使用和操作方法。它在OS接口中占比例最大,是I/O系统的上层软件。 19 | 20 | 要实现一个简单的文件系统,首先就了解操作系统中文件系统所需要使用到相关知识。需要了解的概念:文件目录/目录文件、文件控制块(FCB)/目录项、磁盘簇、文件分配表(FAT)。 21 | 22 | 在此,选取单用户多级目录进行实现,并视所有文件为流式文件,建立类似于FAT32文件系统中的文件分配表(FAT)进行 文件连接-物理簇 的索引。 23 | 24 | ## 技术准备 25 | 26 | 本次操作系统课程设计使用Rust程序设计语言进行实现。为此,需要学习Rust语言的相关语法和编程知识,如所有权、借用、生命周期等概念。 27 | 28 | 实现环境:Windows 10 x64 21H1系统。程序运行时会将整个虚拟磁盘的内容全部读取到内存中进行修改,在内存中建立一个字节数组模拟硬盘的状态,仅当程序的开始和结束时加载和保存虚拟磁盘的状态,再将其写入到真实的外存中。因此,需要一个拥有足够内存的计算机,使其能够完全加载预设大小(1MB)的虚拟磁盘。但程序设计过程中没有使用到任何有关于操作系统的特性,因此在其他支持Rust语言的编译和运行的操作系统上理应也能正常运行。 29 | 30 | # 设计过程 31 | 32 | ## 基本数据结构的构建 33 | 34 | 首先,需要建立数据模型,才能对磁盘和磁盘中的对象进行具体的描述和操作。宏观上的逻辑结构说明可以参照第五章内容。 35 | 36 | ### 目录数据结构 37 | 38 | 从最根本的FCB结构开始构建。为了简单,仅仅在FCB中记录了很少的信息:文件名、文件类型(文件或目录)、FCB指向的起始簇号、文件长度。 39 | ```rust 40 | enum FileType { 41 | File, 42 | Directory, 43 | } 44 | 45 | struct Fcb { 46 | name: String, // 文件名 47 | file_type: FileType, // 文件类型 48 | first_cluster: usize, // 起始块号 49 | length: usize, // 文件大小 50 | } 51 | ``` 52 | 53 | 有了目录项FCB,可以开始构建目录文件了,所有的FCB条目都在目录文件中进行储存。目录结构更加简洁,只记录了文件名和一个动态的FCB项列表,可以进行对于FCB项的增加和删除。 54 | 55 | ```rust 56 | struct Directory { 57 | name: String, 58 | files: Vec, 59 | } 60 | ``` 61 | 62 | 新建目录时,目录文件的文件列表会被自动加入两个特殊文件:".." 和 "." ,分别是该目录父目录的FCB和该目录自己的FCB。根目录的父目录指向自己。这样不但方便定位,而且目录文件自己就包含了自身和父级的FCB信息,对如同修改目录文件自身信息的操作是至关重要的。 63 | 64 | ### FAT表数据结构 65 | 66 | FAT表即文件分配表(File Allocation Table),其中每一个表项都对应了相应簇的使用情况。一个簇可能有四种情况:未被使用、指向下一个簇、坏簇和文件结束。就是这种自索引的表结构,让一个文件能够从最开始的簇号通过FAT表一直查找到文件的末尾,而不必在硬盘上物理的相邻,极大的提高了空间利用的效率。 67 | 68 | ```rust 69 | enum FatItem { 70 | NotUsed, // 未使用的簇 71 | ClusterNo(usize), // 指向下一个的簇号 72 | BadCluster, // 坏簇 73 | EoF, // 文件结束 74 | } 75 | ``` 76 | 77 | 这是一个通过Rust语言中的枚举结构表达的FAT表的表项。 78 | 79 | ### 虚拟硬盘数据结构 80 | 81 | 现在来创建一个虚拟磁盘的数据结构。FAT表在磁盘内已经预先分配了固定大小的空间,因此在软件实现上可以从储存数据的区域中独立出来,假设它不占用簇空间,仅仅在保存的时候才连带着整个磁盘一起写入虚拟磁盘文件。 82 | 83 | ```rust 84 | struct Disk { 85 | fat: Vec, 86 | data: Vec, 87 | } 88 | 89 | impl Disk { 90 | fn new() -> Disk { 91 | Disk { 92 | // 创建FAT文件分配表 93 | fat: vec![FatItem::NotUsed; BLOCK_COUNT], 94 | 95 | // 数据区,初始值为0,块大小为1024. 96 | // 每一个块都有一个对应的FAT项 97 | // 所以真实的数据区域需要在总数中减去FAT项的数据大小保持硬盘大小 98 | data: vec![ 99 | 0u8; 100 | (BLOCK_COUNT - size_of::() * BLOCK_COUNT / BLOCK_SIZE - 1) 101 | * BLOCK_SIZE 102 | ], 103 | } 104 | } 105 | } 106 | ``` 107 | 108 | Disk数据结构存在一个FAT项列表(`Vec`)和字节列表(`Vec`)。在新建该数据结构的实例时,首先初始化FAT表的每一项都是未使用状态,然后在数据区内减去FAT表所占用的磁盘空间。 109 | 110 | ### 单例模式 111 | 112 | 使用单例模拟单目录,并在里面实现各种文件系统操作所需要的各种功能函数。里面简单的包含了Disk虚拟硬盘对象和一个当前目录的对象,用于保存当前用户的操作状态。 113 | 114 | ```rust 115 | struct DiskManager { 116 | disk: Disk, 117 | cur_dir: Directory, 118 | } 119 | ``` 120 | 121 | ## 磁盘簇操作 122 | 123 | 对上层建筑——文件和目录文件等对象的操作,最终还是要归化到最基础的簇操作上。本程序中使用簇号乘簇大小作为字节列表的索引来模拟簇。因为代码长到不适合打印,所以这里只介绍原理。 124 | 125 | ### 簇状态 126 | 127 | 簇是硬盘读写的基本单位。簇状态由FAT表进行标志,和簇的实际状态无关。例如,若FAT表中标识一个簇是未使用状态,那么不论这个簇中是否有实际数据,都被文件系统认为是空簇。这是该文件系统逻辑的基础。 128 | 129 | ### 写簇 130 | 131 | 要将一段完整的数据写入到硬盘簇中,首先需要处理FAT表。首先计算根据数据大小计算需要占用的簇数,然后在FAT表中查找到足够的未使用的簇。将簇标记改为到下一个簇的索引,最后一簇组改写为簇结束标记函数,这样一个文件的空间就在FAT表中被分配出来了。 132 | 133 | 下一个操作是把数据真正的写入到磁盘中。把一个整段的字节序列data分成以常量BLOCK_SIZE(值是1024,代表簇大小:一个簇所能储存的字节数)为大小的数据段,按照簇指针形成的链的顺序对每个簇写入数据,直到最后一个簇。若数据大小并非簇大小的整倍数,将在文件最后加上一个自定义的EoF标志(在程序中为常量EOF_BYTE,值是255),然后用0填充到簇大小的整数倍。 134 | 135 | ### 读簇 136 | 137 | 从FCB块中的首簇号开始读取,一直到簇的结束标记。截断最后一个簇中EOF标记和其以后的数据,否则会读入多余的数据。将每个簇的数据相加,就能得到一个完整的文件数据。 138 | 139 | ## 文件操作 140 | 141 | ### 文件 142 | 143 | 创建文件,如同3.2.2中所说的那样,先给文件的数据分配簇,然后写入磁盘。不过之后,还要创建一个新的包含指向该文件的首簇号数据的FCB,并把这个FCB添加到当前的目录文件的FCB列表中。 144 | 145 | 删除文件,只要找到这个文件所占用的所有簇的簇号,然后在FAT表中标记该簇为空,之后在目录中删除这个文件的FCB条目即可。没有特殊需求的话,不必真正的清空簇数据,否则会极大的降低性能。 146 | 147 | 重命名文件,改变那个文件的FCB中的文件名即可。 148 | 149 | ### 目录 150 | 151 | 目录本质上也是文件,区别只有它保存的是FCB的集合。一个目录文件也需要被一个FCB指向才能被成功的被检测到。对于目录的操作都可以归化到上面对文件的操作上,只不过操作的不是文件的FCB了而已。 152 | 153 | 值得注意的是,当目录中有文件时,直接删除目录文件会让目录中文件所占用的簇永远无法被文件系统回收,导致严重的"外存泄露",所以不能直接删除非空的目录文件。 154 | 155 | 156 | # 结果和分析 157 | ```Powershell 158 | [INFO] Do you want to try to load file-sys.vd? [Y/N] n 159 | [INFO] Will not load vd file from disk. 160 | 161 | [INFO] Creating new disk... 162 | 163 | ================================================== 164 | IvanD's Basic File System 165 | ================================================== 166 | 167 | Help: 168 | cd : Change current dir. 169 | mkdir : Create a new dir. 170 | ls : List all files and dir in current dir. 171 | cat : Show the file content. 172 | rm : Delete a file on disk. 173 | diskinfo : Show some info about disk. 174 | save : Save this virtual disk to file 'file-sys.vd'. 175 | exit : Exit the system. 176 | 177 | Testing: 178 | test create: Create a random file to test. 179 | 180 | System Inner Function: 181 | fn create_file_with_data(&mut self, name: &str, data: &[u8]) 182 | fn rename_file(&mut self, old: &str, new: &str) 183 | fn delete_file_by_name(&mut self, name: &str) 184 | fn read_file_by_name(&self, name: &str) -> Vec 185 | 186 | > ls 187 | 188 | Directroy 'root' Files: 189 | .. Directory Length: 0 190 | . Directory Length: 0 191 | 192 | > 193 | ``` 194 | 各组件经测试,在不输入错误的情况下运行良好,功能正常。 195 | 196 | # 系统结构 197 | 198 | 这是一个单用户多级目录文件系统,以下将用一个简单的例子说明这几个之前提到的对象的关系。 199 | 200 | ![SystemIllustration](https://github.com/ZHider/BasicFileSystem/blob/master/SystemIilustration.svg) 201 | 202 | 如图,说明了一个目录下有一个文件的目录在根目录下的情况。FAT表和硬盘簇号一一对应。根目录在第0簇,他的前两个FCB(应指向父级和自身的默认目录项)都指向第0簇,而后读出了他自己的数据——根目录文件。根目录下有一个指向另一个目录的FCB块,指向第1簇,读出第一簇数据后能够得到第二个目录对象。这个目录的第一个FCB指向第0簇,能够得到根目录对象,第二个FCB指向第2簇,能够得到这个目录对象本身。第三个FCB指向第3簇,通过FAT表跳转,得到了一个指向File的完整的以第3、5、6、7簇的数据的集合为内容的文件。 203 | 204 | 值得一提的是,"文件"其实只是一个逻辑上的实体,真正在储存器中存在的数据结构其实是FCB(提供文件名等文件信息和首簇)、FAT项(提供簇链)和硬盘簇(提供真实数据)。 205 | 206 | 207 | # 使用说明 208 | 209 | 本程序使用命令行交互界面。目前可以输入以下命令: 210 | 211 | - `cd `: 更改当前目录。 212 | - `mkdir `: 新建目录。 213 | - `ls` : 列出当前目录下所有文件和目录。 214 | - `cat `: 显示文件内容。 215 | - `rm `: 删除文件。 216 | - `diskinfo` : 显示磁盘统计信息。 217 | - `save` : 保存当前虚拟磁盘到文件 'file-sys.vd'。 218 | - `exit` : 退出系统。 219 | -------------------------------------------------------------------------------- /SystemIilustration.svg: -------------------------------------------------------------------------------- 1 | FileFCB 0FCB 0FCB 1DirectoryFile Allocation TableFCB 0FCB 1FCB 2DirectoryDisk ClusterDisk ClusterDisk ClusterDisk ClusterDisk ClusterDisk ClusterDisk ClusterDisk ClusterDisk ClusterDisk ClusterDisk ClusterDisk ClusterFATI EoFFATI EoFFATI 3FATI 5FAT ItemFATI 6FATI 7FATI EoFFAT ItemFAT ItemFAT ItemFAT Item -------------------------------------------------------------------------------- /src/disk_manager.rs: -------------------------------------------------------------------------------- 1 | pub mod disk; 2 | use disk::{Disk, FatItem, BLOCK_COUNT, BLOCK_SIZE}; 3 | 4 | use ansi_rgb::Foreground; 5 | use core::panic; 6 | use serde::{Deserialize, Serialize}; 7 | use std::str; 8 | use std::{fmt, string::String, usize, vec::Vec}; 9 | 10 | pub fn pinfo() { 11 | print!("{}", "[INFO]\t".fg(ansi_rgb::cyan_blue())); 12 | } 13 | pub fn pdebug() { 14 | print!("{}", "[DEBUG]\t".fg(ansi_rgb::magenta())); 15 | } 16 | 17 | #[derive(Serialize, Deserialize)] 18 | pub struct DiskManager { 19 | pub disk: Disk, 20 | pub cur_dir: Directory, 21 | } 22 | impl DiskManager { 23 | /// 初始化新磁盘,返回DiskManager对象。若输入None,则自动创建默认配置。 24 | pub fn new(root_dir: Option) -> DiskManager { 25 | pinfo(); 26 | println!("Creating new disk..."); 27 | // 生成虚拟磁盘 28 | let mut disk = Disk::new(); 29 | { 30 | // 放置第一个根目录 31 | let dir_data = bincode::serialize(&root_dir).unwrap(); 32 | disk.insert_data_by_offset(dir_data.as_slice(), 0); 33 | } 34 | disk.fat[0] = FatItem::EoF; 35 | 36 | DiskManager { 37 | disk, 38 | cur_dir: match root_dir { 39 | // 默认根目录配置 40 | None => Directory { 41 | name: String::from("root"), 42 | files: vec![ 43 | Fcb { 44 | name: String::from(".."), 45 | file_type: FileType::Directory, 46 | first_cluster: 0, 47 | length: 0, 48 | }, 49 | Fcb { 50 | name: String::from("."), 51 | file_type: FileType::Directory, 52 | first_cluster: 0, 53 | length: 0, 54 | }, 55 | ], 56 | }, 57 | Some(dir) => dir, 58 | }, 59 | } 60 | } 61 | 62 | /// 返回一个状态是NotUsed的簇块号 63 | pub fn find_next_empty_fat(&self) -> Option { 64 | let mut res = None; 65 | for i in 0..(self.disk.fat.len() - 1) { 66 | if let FatItem::NotUsed = self.disk.fat[i] { 67 | res = Some(i); 68 | break; 69 | } 70 | } 71 | 72 | res 73 | } 74 | 75 | /// 输入需要分配的簇数量,在FAT表上标记为已用(分配新空间),返回被分配的簇号数组。 76 | pub fn allocate_free_space_on_fat( 77 | &mut self, 78 | clusters_needed: usize, 79 | ) -> Result, &'static str> { 80 | pinfo(); 81 | println!("Allocating new space..."); 82 | 83 | let mut clusters: Vec = Vec::with_capacity(clusters_needed); 84 | for i in 0..clusters_needed { 85 | // 找到新未用的簇 86 | clusters.push(match self.find_next_empty_fat() { 87 | Some(cluster) => cluster, 88 | _ => return Err("[ERROR]\tCannot find a NotUsed FatItem!"), 89 | }); 90 | // this_cluster:每次循环进行操作的cluster 91 | let this_cluster = clusters[i]; 92 | 93 | // 对磁盘写入数据 94 | pdebug(); 95 | println!("Found new empty cluster: {}", this_cluster); 96 | if i != 0 { 97 | // 中间的和最后一次的写入 98 | // 将上一块改写成指向当前块的FatItem 99 | self.disk.fat[clusters[i - 1]] = FatItem::ClusterNo(this_cluster); 100 | } 101 | // 默认当前块是最后的 102 | self.disk.fat[this_cluster] = FatItem::EoF; 103 | } 104 | 105 | Ok(clusters) 106 | } 107 | 108 | /// 查找以`first_cluster`为开头的在FAT中所关联的所有文件块。 109 | /// 110 | /// # 错误 111 | /// 112 | /// 当检测到簇指向一个未使用的簇的时候,返回那个被指向的未使用的簇的索引。 113 | 114 | fn get_file_clusters(&self, first_cluster: usize) -> Result, String> { 115 | pinfo(); 116 | println!("Searching file clusters..."); 117 | let mut clusters: Vec = Vec::new(); 118 | let mut this_cluster = first_cluster; 119 | 120 | // 第一个簇 121 | clusters.push(first_cluster); 122 | 123 | // 然后循环读出之后所有簇 124 | loop { 125 | match self.disk.fat[this_cluster] { 126 | FatItem::ClusterNo(cluster) => { 127 | pdebug(); 128 | println!("Found next cluster: {}.", cluster); 129 | clusters.push(cluster); 130 | this_cluster = cluster; 131 | } 132 | FatItem::EoF => { 133 | pdebug(); 134 | println!("Found EoF cluster: {}.", this_cluster); 135 | break Ok(clusters); 136 | } 137 | FatItem::BadCluster => { 138 | // 跳过坏簇 139 | this_cluster += 1; 140 | continue; 141 | } 142 | _ => { 143 | break Err(format!( 144 | "[ERROR]\tBad cluster detected at {}!", 145 | this_cluster 146 | )) 147 | } 148 | } 149 | } 150 | } 151 | 152 | /// 删除已经被分配的簇(置空),返回已经被删除的簇号数组。 153 | fn delete_space_on_fat(&mut self, first_cluster: usize) -> Result, String> { 154 | pinfo(); 155 | println!("Deleting Fat space..."); 156 | let clusters_result = self.get_file_clusters(first_cluster); 157 | let clusters = clusters_result.clone().unwrap(); 158 | for cluster in clusters { 159 | self.disk.fat[cluster] = FatItem::NotUsed; 160 | } 161 | 162 | clusters_result 163 | } 164 | 165 | /// 重新分配已经被分配的簇,按需要的簇数量分配,原簇将被置空。 166 | fn reallocate_free_space_on_fat( 167 | &mut self, 168 | first_cluster: usize, 169 | clusters_needed: usize, 170 | ) -> Vec { 171 | pinfo(); 172 | println!("Realocating Fat space..."); 173 | // 删除原先的簇 174 | self.delete_space_on_fat(first_cluster).unwrap(); 175 | // 分配新的簇 - 多线程下第一簇可能不同,多线程不安全 176 | self.allocate_free_space_on_fat(clusters_needed).unwrap() 177 | } 178 | 179 | /// 计算写入文件需要的簇数量——针对EoF 180 | /// 返回(`bool`: 是否需要插入EoF,`usize`: 需要的总簇数) 181 | fn calc_clusters_needed_with_eof(length: usize) -> (bool, usize) { 182 | // 判断需要写入的总簇数 183 | let mut clusters_needed: f32 = length as f32 / BLOCK_COUNT as f32; 184 | // 判断cluster是否是整数。如果是,就不写入结束标志。 185 | let insert_eof = if (clusters_needed - clusters_needed as usize as f32) < 0.0000000001 { 186 | false 187 | } else { 188 | clusters_needed = clusters_needed.ceil(); 189 | true 190 | }; 191 | let clusters_needed: usize = clusters_needed as usize; 192 | 193 | (insert_eof, clusters_needed) 194 | } 195 | 196 | /// 提供想要写入的数据,返回数据的开始簇块号,可在FAT中查找 197 | pub fn write_data_to_disk(&mut self, data: &[u8]) -> usize { 198 | pinfo(); 199 | println!("Writing data to disk..."); 200 | 201 | let (insert_eof, clusters_needed) = DiskManager::calc_clusters_needed_with_eof(data.len()); 202 | 203 | let clusters = self.allocate_free_space_on_fat(clusters_needed).unwrap(); 204 | 205 | self.disk 206 | .write_data_by_clusters_with_eof(data, clusters.as_slice(), insert_eof); 207 | 208 | pdebug(); 209 | println!("Writing finished. Returned clusters: {:?}", clusters); 210 | 211 | clusters[0] 212 | } 213 | 214 | /// 提供目录名,在当前目录中新建目录,同时写入磁盘。 215 | pub fn new_directory_to_disk(&mut self, name: &str) -> Result<(), &'static str> { 216 | // 新文件夹写入磁盘块 217 | pinfo(); 218 | println!("Creating dir: {}.", name); 219 | pdebug(); 220 | println!("Trying to write to disk..."); 221 | 222 | if let Some(_fcb) = self.cur_dir.get_fcb_by_name(name) { 223 | return Err("[ERROR]\tThere's already a directory with a same name!"); 224 | } 225 | 226 | let mut new_directory = Directory::new(name); 227 | // 加入“..” 228 | new_directory.files.push(Fcb { 229 | name: String::from(".."), 230 | file_type: FileType::Directory, 231 | first_cluster: self.cur_dir.files[1].first_cluster, 232 | length: 0, 233 | }); 234 | // 加入“.” 235 | // TODO: 多线程不安全 236 | new_directory.files.push(Fcb { 237 | name: String::from("."), 238 | file_type: FileType::Directory, 239 | first_cluster: self.find_next_empty_fat().unwrap(), 240 | length: 0, 241 | }); 242 | 243 | let bin_dir = bincode::serialize(&new_directory).unwrap(); 244 | 245 | pdebug(); 246 | println!("Dir bytes: {:?}", bin_dir); 247 | let first_block = self.write_data_to_disk(&bin_dir); 248 | 249 | pdebug(); 250 | println!("Trying to add dir to current dir..."); 251 | // 在文件夹中添加新文件夹 252 | self.cur_dir.files.push(Fcb { 253 | name: String::from(name), 254 | file_type: FileType::Directory, 255 | first_cluster: first_block, 256 | length: 0, 257 | }); 258 | pdebug(); 259 | println!("Created dir {}.", name); 260 | 261 | Ok(()) 262 | } 263 | 264 | /// 提供簇号,读出所有数据。 265 | fn get_data_by_first_cluster(&self, first_cluster: usize) -> Vec { 266 | pdebug(); 267 | println!("Getting data from disk by clusters..."); 268 | 269 | let clusters = self.get_file_clusters(first_cluster).unwrap(); 270 | let data = self 271 | .disk 272 | .read_data_by_clusters_without_eof(clusters.as_slice()); 273 | 274 | pdebug(); 275 | println!("Data read: {:?}", &data); 276 | 277 | data 278 | } 279 | 280 | /// 通过FCB块找到目录项 281 | fn get_directory_by_fcb(&self, dir_fcb: &Fcb) -> Directory { 282 | pinfo(); 283 | println!("Getting dir by FCB...\n\tFCB: {:?}", dir_fcb); 284 | match dir_fcb.file_type { 285 | FileType::Directory => { 286 | let data_dir = self.get_data_by_first_cluster(dir_fcb.first_cluster); 287 | pdebug(); 288 | println!("Trying to deserialize data read from disk..."); 289 | let dir: Directory = bincode::deserialize(data_dir.as_slice()).unwrap(); 290 | pdebug(); 291 | println!("Getting dir finished."); 292 | dir 293 | } 294 | _ => panic!("[ERROR]\tGet Directory recieved a non-Directory FCB!"), 295 | } 296 | } 297 | 298 | /// 通过FCB块找到文件 299 | fn get_file_by_fcb(&self, fcb: &Fcb) -> Vec { 300 | pinfo(); 301 | println!("Getting file data by FCB...\n\tFCB: {:?}", fcb); 302 | match fcb.file_type { 303 | FileType::File => self.get_data_by_first_cluster(fcb.first_cluster), 304 | _ => panic!("[ERROR]\tGet File recieved a non-File FCB!"), 305 | } 306 | } 307 | 308 | /// 通过FCB块删除文件 309 | fn delete_file_by_fcb(&mut self, fcb: &Fcb) -> Result<(), String> { 310 | self.delete_file_by_fcb_with_index( 311 | fcb, 312 | Some(self.cur_dir.get_index_by_name(fcb.name.as_str()).unwrap()), 313 | ) 314 | } 315 | 316 | /// 通过FCB块删除文件,参数中含有FCB块在dir中的序号。 317 | fn delete_file_by_fcb_with_index( 318 | &mut self, 319 | fcb: &Fcb, 320 | index: Option, 321 | ) -> Result<(), String> { 322 | if let FileType::Directory = fcb.file_type { 323 | let dir = self.get_directory_by_fcb(fcb); 324 | if dir.files.len() > 2 { 325 | return Err(String::from("[ERROR]\tThe Directory is not empty!")); 326 | } 327 | } 328 | pdebug(); 329 | println!( 330 | "Trying to set all NotUsed clutster of file '{}' on FAT...", 331 | fcb.name 332 | ); 333 | // 直接返回删除文件的结果 334 | if let Err(err) = self.delete_space_on_fat(fcb.first_cluster) { 335 | return Err(err); 336 | } 337 | // 若给定index非None,则删除目录下的FCB条目 338 | if let Some(i) = index { 339 | self.cur_dir.files.remove(i); 340 | } 341 | 342 | Ok(()) 343 | } 344 | 345 | /// 在当前文件夹创建新文件并写入 346 | pub fn create_file_with_data(&mut self, name: &str, data: &[u8]) { 347 | pinfo(); 348 | println!("Creating new file in current dir..."); 349 | // 写入数据 350 | let first_cluster = self.write_data_to_disk(data); 351 | // 创建新FCB并插入当前目录中 352 | let fcb = Fcb { 353 | name: String::from(name), 354 | file_type: FileType::File, 355 | first_cluster, 356 | length: data.len(), 357 | }; 358 | self.cur_dir.files.push(fcb); 359 | } 360 | 361 | /// 通过文件名读取文件 362 | pub fn read_file_by_name(&self, name: &str) -> Vec { 363 | let (_index, fcb) = self.cur_dir.get_fcb_by_name(name).unwrap(); 364 | self.get_file_by_fcb(fcb) 365 | } 366 | 367 | /// 通过文件名删除文件 368 | pub fn delete_file_by_name(&mut self, name: &str) -> Result<(), String> { 369 | let index = self.cur_dir.get_index_by_name(name).unwrap(); 370 | // 从dir中先删除fcb,如果删除失败再还回来 371 | pdebug(); 372 | println!("Trying to delete file in dir file list..."); 373 | let fcb = self.cur_dir.files.remove(index); 374 | let res = self.delete_file_by_fcb_with_index(&fcb, None); 375 | 376 | if res.is_err() { 377 | self.cur_dir.files.push(fcb); 378 | } 379 | 380 | res 381 | } 382 | 383 | /// 通过文件夹名设置当前文件夹 384 | pub fn set_current_directory(&mut self, name: &str) { 385 | // 保存当前文件夹 386 | let dir_cloned = self.cur_dir.clone(); 387 | self.save_directory_to_disk(&dir_cloned); 388 | // 通过名字获取下一个文件夹 389 | let (_index, dir_fcb) = self.cur_dir.get_fcb_by_name(name).unwrap(); 390 | 391 | let dir = self.get_directory_by_fcb(dir_fcb); 392 | self.cur_dir = dir; 393 | } 394 | 395 | /// 保存文件夹到磁盘,返回第一个簇号——更改被保存,原目录文件将在磁盘上被覆盖 396 | pub fn save_directory_to_disk(&mut self, dir: &Directory) -> usize { 397 | pdebug(); 398 | println!("Trying to saving dir..."); 399 | let data = bincode::serialize(dir).unwrap(); 400 | let (insert_eof, clusters_needed) = DiskManager::calc_clusters_needed_with_eof(data.len()); 401 | let reallocated_clusters = 402 | self.reallocate_free_space_on_fat(self.cur_dir.files[1].first_cluster, clusters_needed); 403 | self.disk.write_data_by_clusters_with_eof( 404 | data.as_slice(), 405 | reallocated_clusters.as_slice(), 406 | insert_eof, 407 | ); 408 | 409 | reallocated_clusters[0] 410 | } 411 | 412 | /// 文件改名,没啥好说的。 413 | pub fn rename_file_by_name(&mut self, old: &str, new: &str) { 414 | let (index, fcb) = self.cur_dir.get_fcb_by_name(old).unwrap(); 415 | let new_fcb = Fcb { 416 | name: String::from(new), 417 | ..fcb.to_owned() 418 | }; 419 | self.cur_dir.files[index] = new_fcb; 420 | } 421 | 422 | /// 获取部分磁盘信息 423 | /// 返回 磁盘总大小/Byte,已分配簇数量、未分配簇的数量 424 | pub fn get_disk_info(&self) -> (usize, usize, usize) { 425 | let disk_size = BLOCK_SIZE * BLOCK_COUNT; 426 | let mut num_used = 0usize; 427 | let mut num_not_used = 0usize; 428 | 429 | for fat_item in &self.disk.fat { 430 | match fat_item { 431 | FatItem::ClusterNo(_no) => num_used += 1, 432 | FatItem::EoF => num_used += 1, 433 | FatItem::NotUsed => num_not_used += 1, 434 | _ => (), 435 | } 436 | } 437 | 438 | (disk_size, num_used, num_not_used) 439 | } 440 | 441 | /// FCB的移动 442 | pub fn move_fcb_between_dirs_by_name(&mut self, name: &str, des_dir: &mut Directory) { 443 | let fcb = self 444 | .cur_dir 445 | .files 446 | .remove(self.cur_dir.get_index_by_name(name).unwrap()); 447 | des_dir.files.push(fcb); 448 | } 449 | } 450 | 451 | #[derive(Serialize, Deserialize, Debug, Clone)] 452 | pub enum FileType { 453 | File, 454 | Directory, 455 | } 456 | impl fmt::Display for FileType { 457 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 458 | match self { 459 | FileType::Directory => write!(f, "Directory"), 460 | FileType::File => write!(f, "File"), 461 | } 462 | } 463 | } 464 | 465 | #[derive(Serialize, Deserialize, Debug, Clone)] 466 | pub struct Fcb { 467 | name: String, // 文件名 468 | file_type: FileType, // 文件类型 469 | first_cluster: usize, // 起始块号 470 | length: usize, // 文件大小 471 | } 472 | 473 | #[derive(Serialize, Deserialize, Debug, Clone)] 474 | pub struct Directory { 475 | name: String, 476 | files: Vec, 477 | } 478 | impl Directory { 479 | fn new(name: &str) -> Directory { 480 | Directory { 481 | name: String::from(name), 482 | files: Vec::with_capacity(2), 483 | } 484 | } 485 | 486 | /// 通过文件名获取文件在files中的索引和文件FCB 487 | fn get_fcb_by_name(&self, name: &str) -> Option<(usize, &Fcb)> { 488 | let mut res = None; 489 | for i in 0..self.files.len() { 490 | if self.files[i].name.as_str() == name { 491 | res = Some((i, &self.files[i])); 492 | break; 493 | } 494 | } 495 | 496 | res 497 | } 498 | 499 | /// 通过文件名获取文件在files中的索引和文件FCB 500 | fn get_index_by_name(&self, name: &str) -> Option { 501 | let mut res = None; 502 | for i in 0..self.files.len() { 503 | if self.files[i].name.as_str() == name { 504 | res = Some(i); 505 | break; 506 | } 507 | } 508 | 509 | res 510 | } 511 | } 512 | impl fmt::Display for Directory { 513 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 514 | // 仅将 self 的第一个元素写入到给定的输出流 `f`。返回 `fmt:Result`,此 515 | // 结果表明操作成功或失败。注意 `write!` 的用法和 `println!` 很相似。 516 | writeln!(f, "Directroy '{}' Files:", self.name)?; 517 | for file in &self.files { 518 | writeln!( 519 | f, 520 | "{}\t\t{}\t\tLength: {}", 521 | file.name, file.file_type, file.length 522 | )?; 523 | } 524 | 525 | fmt::Result::Ok(()) 526 | } 527 | } 528 | -------------------------------------------------------------------------------- /src/disk_manager/disk.rs: -------------------------------------------------------------------------------- 1 | use std::mem::size_of; 2 | 3 | use serde::{Deserialize, Serialize}; 4 | 5 | /// 簇大小:1KiB 6 | pub const BLOCK_SIZE: usize = 1024; 7 | /// 簇数量 8 | pub const BLOCK_COUNT: usize = 1000; 9 | /// 定义从后向前扫描时的EoF 10 | pub const EOF_BYTE: u8 = 255; 11 | 12 | #[derive(Serialize, Deserialize)] 13 | pub struct Disk { 14 | pub fat: Vec, 15 | data: Vec, 16 | } 17 | impl Disk { 18 | pub fn new() -> Disk { 19 | Disk { 20 | // 创建FAT文件分配表 21 | fat: vec![FatItem::NotUsed; BLOCK_COUNT], 22 | // 数据区,初始值为0,块大小为1024. 23 | // 每一个块都有一个对应的FAT项,所以真实的数据区域需要在总数中减去FAT项的数据大小 24 | data: vec![ 25 | 0u8; 26 | (BLOCK_COUNT - size_of::() * BLOCK_COUNT / BLOCK_SIZE - 1) 27 | * BLOCK_SIZE 28 | ], 29 | } 30 | } 31 | 32 | /// 向disk的data中插入数据。插入的数据将覆写相应位置的数据。 33 | pub fn insert_data_by_offset(&mut self, data: &[u8], offset: usize) { 34 | self.data 35 | .splice(offset..(offset + data.len()), data.iter().cloned()); 36 | } 37 | /// 向disk中的data插入数据。插入数据将覆写相应的位置。 38 | pub fn insert_data_by_cluster(&mut self, data: &[u8], cluster: usize) { 39 | self.insert_data_by_offset(data, cluster * BLOCK_SIZE); 40 | } 41 | 42 | /// 向disk中的data插入数据。插入数据将覆写相应的位置。 43 | pub fn write_data_by_clusters_with_eof( 44 | &mut self, 45 | data: &[u8], 46 | clusters: &[usize], 47 | insert_eof: bool, 48 | ) { 49 | for i in 0..clusters.len() { 50 | if i < clusters.len() - 1 { 51 | // 正常分BLOCK_SIZE写入簇 52 | self.insert_data_by_cluster( 53 | &data[i * BLOCK_SIZE..(i + 1) * BLOCK_SIZE], 54 | clusters[i], 55 | ); 56 | } else { 57 | // 开始写入最后一个块 58 | let mut buffer: Vec = Vec::with_capacity(BLOCK_SIZE); 59 | buffer.extend((&data[i * BLOCK_SIZE..data.len()]).iter()); 60 | if insert_eof { 61 | // 插入EoF 62 | buffer.push(EOF_BYTE); 63 | } 64 | if buffer.len() < BLOCK_SIZE { 65 | // 若未到 BLOCK_SIZE 则用0填充 66 | let mut zero = vec![0u8; BLOCK_SIZE - buffer.len()]; 67 | buffer.append(&mut zero); 68 | } 69 | self.insert_data_by_cluster(buffer.as_slice(), clusters[i]) 70 | } 71 | } 72 | } 73 | 74 | /// 从disk中读取数据。 75 | pub fn read_data_by_cluster(&self, cluster: usize) -> Vec { 76 | (&self.data[cluster * BLOCK_SIZE..(cluster + 1) * BLOCK_SIZE]).to_vec() 77 | } 78 | 79 | /// 工具给出的簇号,读出所有数据,并且检测EoF。 80 | pub fn read_data_by_clusters_without_eof(&self, clusters: &[usize]) -> Vec { 81 | let mut data: Vec = Vec::with_capacity(clusters.len() * BLOCK_SIZE); 82 | 83 | // 循环读出所有数据 84 | for cluster in clusters { 85 | let mut buffer = self.read_data_by_cluster(*cluster); 86 | data.append(&mut buffer); 87 | } 88 | // 从后向前查找,从EoF开始截断。若未找到EoF则直接返回。 89 | for i in 1..BLOCK_SIZE { 90 | let index = data.len() - i; 91 | if data[index] == EOF_BYTE { 92 | // 不加不减,刚好将EoF截断在外 93 | data.truncate(index); 94 | break; 95 | } 96 | } 97 | 98 | data 99 | } 100 | } 101 | 102 | #[derive(Serialize, Deserialize, Debug, Clone)] 103 | pub enum FatItem { 104 | NotUsed, // 未使用的簇 105 | ClusterNo(usize), // 指向下一个的簇号 106 | BadCluster, // 坏簇 107 | EoF, // 文件结束 108 | } 109 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | mod disk_manager; 4 | use std::fs; 5 | use std::io::{stdin, stdout, Write}; 6 | use std::str; 7 | use std::time::SystemTime; 8 | 9 | use disk_manager::disk::*; 10 | use disk_manager::*; 11 | 12 | // 多线程不安全:程序逻辑-下一个空块必定是要分配的簇,但实际上会在写入之前预先检查下一个空簇,然后在写入时再次检查下一个空簇。单线程下两个结果必定相同,多线程下不一定。 13 | // TODO:预先分配首簇,然后根据首簇写数据。 14 | 15 | fn main() { 16 | // 是否从磁盘中读取vd文件初始化 17 | let mut virtual_disk = ui_load_dm_loop(SAVE_FILE_NAME); 18 | ui_loop(&mut virtual_disk); 19 | } 20 | 21 | /// 默认保存的文件名 22 | const SAVE_FILE_NAME: &str = "./file-sys.vd"; 23 | /// 系统UI默认提示 24 | const UI_HELP: &str = "\ 25 | \n==================================================\ 26 | \n IvanD's Basic File System\ 27 | \n==================================================\ 28 | \nHelp:\ 29 | \n\tcd : Change current dir.\ 30 | \n\tmkdir : Create a new dir.\ 31 | \n\tls : List all files and dir in current dir.\ 32 | \n\tcat : Show the file content.\ 33 | \n\trm : Delete a file on disk.\ 34 | \n\tdiskinfo : Show some info about disk.\ 35 | \n\tsave : Save this virtual disk to file 'file-sys.vd'\ 36 | \n\texit : Exit the system. 37 | \n\ 38 | \nTesting:\ 39 | \n\ttest create: Create a random file to test.\ 40 | \n\ 41 | \nSystem Inner Function:\ 42 | \n\tfn create_file_with_data(&mut self, name: &str, data: &[u8])\ 43 | \n\tfn rename_file(&mut self, old: &str, new: &str)\ 44 | \n\tfn delete_file_by_name(&mut self, name: &str)\ 45 | \n\tfn read_file_by_name(&self, name: &str) -> Vec\ 46 | \n"; // UI主菜单 47 | 48 | /// 使用交互式让用户选择是否从硬盘中加载DiskManager进行使用 49 | fn ui_load_dm_loop(filename: &str) -> DiskManager { 50 | let mut buf_str = String::new(); 51 | loop { 52 | pinfo(); 53 | print!("Do you want to try to load file-sys.vd? [Y/N] "); 54 | stdout().flush().unwrap(); 55 | stdin().read_line(&mut buf_str).unwrap(); 56 | let first_char = buf_str.as_str().trim().chars().next().unwrap(); 57 | 58 | match first_char { 59 | 'N' | 'n' => { 60 | pinfo(); 61 | println!("Will not load vd file from disk.\n"); 62 | 63 | break DiskManager::new(None); 64 | } 65 | 'Y' | 'y' => { 66 | pinfo(); 67 | println!("Trying to load vd file from disk...\n"); 68 | let data = fs::read(filename).unwrap(); 69 | 70 | break bincode::deserialize(data.as_slice()).unwrap(); 71 | } 72 | _ => { 73 | println!("\nIncorrect input."); 74 | continue; 75 | } 76 | }; 77 | } 78 | } 79 | 80 | /// 一个简单的交互式界面。 81 | fn ui_loop(virtual_disk: &mut DiskManager) { 82 | // 交互界面 83 | println!("{}", UI_HELP); 84 | 85 | let mut buf_str = String::new(); 86 | 87 | loop { 88 | // 清空buffer 89 | buf_str.clear(); 90 | print!("> "); 91 | stdout().flush().unwrap(); 92 | stdin().read_line(&mut buf_str).unwrap(); 93 | // 去除首尾空格 94 | let command_line = String::from(buf_str.trim()); 95 | 96 | // 分支-test 97 | if let Some(cl) = command_line.strip_prefix("test ") { 98 | // 分支-create 99 | if let Some(cl) = cl.strip_prefix("create") { 100 | let data = format!("File has been created at {:?} .", SystemTime::now()); 101 | let cl_trim = cl.trim(); 102 | let name = if cl_trim.is_empty() { 103 | // 没有输入名字 104 | format!("test-{}", (rand::random::() * 100_f32) as usize) 105 | } else { 106 | // 输入了名字 107 | cl_trim.to_string() 108 | }; 109 | virtual_disk.create_file_with_data(name.as_str(), data.as_bytes()); 110 | } 111 | } else if command_line.starts_with("help") { 112 | // 显示菜单 113 | println!("{}", UI_HELP); 114 | } else if command_line.starts_with("exit") { 115 | // 跳出循环,结束程序 116 | pinfo(); 117 | println!("Exiting system...\n"); 118 | break; 119 | } else if command_line.starts_with("save") { 120 | // 保存系统 121 | pinfo(); 122 | println!("Saving..."); 123 | let data = bincode::serialize(&virtual_disk).unwrap(); 124 | fs::write(SAVE_FILE_NAME, data.as_slice()).unwrap(); 125 | pinfo(); 126 | println!("The virtual disk system has been saved.\n"); 127 | } else if command_line.starts_with("ls") { 128 | // 列出目录文件 129 | println!("{}", virtual_disk.cur_dir); 130 | } else if let Some(name) = command_line.strip_prefix("cd ") { 131 | // 切换到当前目录的某个文件夹 132 | pinfo(); 133 | println!("Set Location to: {} ...", name); 134 | virtual_disk.set_current_directory(name); 135 | } else if let Some(command_line) = command_line.strip_prefix("cat ") { 136 | // 显示文件内容 137 | let name = command_line.trim(); 138 | let data = virtual_disk.read_file_by_name(name); 139 | println!("{}", str::from_utf8(data.as_slice()).unwrap()); 140 | } else if let Some(command_line) = command_line.strip_prefix("mkdir ") { 141 | // 创建新文件夹 142 | let name = command_line.trim(); 143 | virtual_disk.new_directory_to_disk(name).unwrap(); 144 | } else if command_line.starts_with("diskinfo") { 145 | // 返回磁盘信息 146 | let (disk_size, num_used, num_not_used) = virtual_disk.get_disk_info(); 147 | println!( 148 | "Disk sized {} Bytes, {} Bytes used, {} Bytes available.", 149 | disk_size, 150 | num_used * BLOCK_SIZE, 151 | num_not_used * BLOCK_SIZE 152 | ); 153 | } else if let Some(command_line) = command_line.strip_prefix("rm ") { 154 | let name = command_line.trim(); 155 | virtual_disk 156 | .delete_file_by_name(name) 157 | .expect("[ERROR]\tDELETE FILE FAILED!"); 158 | } else { 159 | println!("Unknown Command."); 160 | } 161 | } 162 | } 163 | --------------------------------------------------------------------------------