1 #![allow(dead_code)] 2 3 pub mod core; 4 pub mod fcntl; 5 pub mod file; 6 pub mod io; 7 pub mod mount; 8 pub mod syscall; 9 mod utils; 10 11 use ::core::{any::Any, fmt::Debug}; 12 13 use alloc::{string::String, sync::Arc, vec::Vec}; 14 15 use crate::{libs::casting::DowncastArc, syscall::SystemError, time::TimeSpec}; 16 17 use self::{core::generate_inode_id, file::FileMode}; 18 pub use self::{core::ROOT_INODE, file::FilePrivateData, mount::MountFS}; 19 20 /// vfs容许的最大的路径名称长度 21 pub const MAX_PATHLEN: usize = 1024; 22 23 /// 定义inode号的类型为usize 24 pub type InodeId = usize; 25 26 /// 文件的类型 27 #[derive(Debug, Clone, Copy, PartialEq, Eq)] 28 pub enum FileType { 29 /// 文件 30 File, 31 /// 文件夹 32 Dir, 33 /// 块设备 34 BlockDevice, 35 /// 字符设备 36 CharDevice, 37 /// 管道文件 38 Pipe, 39 /// 符号链接 40 SymLink, 41 /// 套接字 42 Socket, 43 } 44 45 /* these are defined by POSIX and also present in glibc's dirent.h */ 46 /// 完整含义请见 http://www.gnu.org/software/libc/manual/html_node/Directory-Entries.html 47 pub const DT_UNKNOWN: u16 = 0; 48 /// 命名管道,或者FIFO 49 pub const DT_FIFO: u16 = 1; 50 // 字符设备 51 pub const DT_CHR: u16 = 2; 52 // 目录 53 pub const DT_DIR: u16 = 4; 54 // 块设备 55 pub const DT_BLK: u16 = 6; 56 // 常规文件 57 pub const DT_REG: u16 = 8; 58 // 符号链接 59 pub const DT_LNK: u16 = 10; 60 // 是一个socket 61 pub const DT_SOCK: u16 = 12; 62 // 这个是抄Linux的,还不知道含义 63 pub const DT_WHT: u16 = 14; 64 pub const DT_MAX: u16 = 16; 65 66 impl FileType { 67 pub fn get_file_type_num(&self) -> u16 { 68 return match self { 69 FileType::File => DT_REG, 70 FileType::Dir => DT_DIR, 71 FileType::BlockDevice => DT_BLK, 72 FileType::CharDevice => DT_CHR, 73 FileType::Pipe => DT_FIFO, 74 FileType::SymLink => DT_LNK, 75 FileType::Socket => DT_SOCK, 76 }; 77 } 78 } 79 80 bitflags! { 81 /// @brief inode的状态(由poll方法返回) 82 pub struct PollStatus: u8 { 83 const WRITE = 1u8 << 0; 84 const READ = 1u8 << 1; 85 const ERROR = 1u8 << 2; 86 } 87 } 88 89 pub trait IndexNode: Any + Sync + Send + Debug { 90 /// @brief 打开文件 91 /// 92 /// @return 成功:Ok() 93 /// 失败:Err(错误码) 94 fn open(&self, _data: &mut FilePrivateData, _mode: &FileMode) -> Result<(), SystemError> { 95 // 若文件系统没有实现此方法,则返回“不支持” 96 return Err(SystemError::EOPNOTSUPP_OR_ENOTSUP); 97 } 98 99 /// @brief 关闭文件 100 /// 101 /// @return 成功:Ok() 102 /// 失败:Err(错误码) 103 fn close(&self, _data: &mut FilePrivateData) -> Result<(), SystemError> { 104 // 若文件系统没有实现此方法,则返回“不支持” 105 return Err(SystemError::EOPNOTSUPP_OR_ENOTSUP); 106 } 107 108 /// @brief 在inode的指定偏移量开始,读取指定大小的数据 109 /// 110 /// @param offset 起始位置在Inode中的偏移量 111 /// @param len 要读取的字节数 112 /// @param buf 缓冲区. 请注意,必须满足@buf.len()>=@len 113 /// @param _data 各文件系统系统所需私有信息 114 /// 115 /// @return 成功:Ok(读取的字节数) 116 /// 失败:Err(Posix错误码) 117 fn read_at( 118 &self, 119 offset: usize, 120 len: usize, 121 buf: &mut [u8], 122 _data: &mut FilePrivateData, 123 ) -> Result<usize, SystemError>; 124 125 /// @brief 在inode的指定偏移量开始,写入指定大小的数据(从buf的第0byte开始写入) 126 /// 127 /// @param offset 起始位置在Inode中的偏移量 128 /// @param len 要写入的字节数 129 /// @param buf 缓冲区. 请注意,必须满足@buf.len()>=@len 130 /// @param _data 各文件系统系统所需私有信息 131 /// 132 /// @return 成功:Ok(写入的字节数) 133 /// 失败:Err(Posix错误码) 134 fn write_at( 135 &self, 136 offset: usize, 137 len: usize, 138 buf: &[u8], 139 _data: &mut FilePrivateData, 140 ) -> Result<usize, SystemError>; 141 142 /// @brief 获取当前inode的状态。 143 /// 144 /// @return PollStatus结构体 145 fn poll(&self) -> Result<PollStatus, SystemError>; 146 147 /// @brief 获取inode的元数据 148 /// 149 /// @return 成功:Ok(inode的元数据) 150 /// 失败:Err(错误码) 151 fn metadata(&self) -> Result<Metadata, SystemError> { 152 // 若文件系统没有实现此方法,则返回“不支持” 153 return Err(SystemError::EOPNOTSUPP_OR_ENOTSUP); 154 } 155 156 /// @brief 设置inode的元数据 157 /// 158 /// @return 成功:Ok() 159 /// 失败:Err(错误码) 160 fn set_metadata(&self, _metadata: &Metadata) -> Result<(), SystemError> { 161 // 若文件系统没有实现此方法,则返回“不支持” 162 return Err(SystemError::EOPNOTSUPP_OR_ENOTSUP); 163 } 164 165 /// @brief 重新设置文件的大小 166 /// 167 /// 如果文件大小增加,则文件内容不变,但是文件的空洞部分会被填充为0 168 /// 如果文件大小减小,则文件内容会被截断 169 /// 170 /// @return 成功:Ok() 171 /// 失败:Err(错误码) 172 fn resize(&self, _len: usize) -> Result<(), SystemError> { 173 // 若文件系统没有实现此方法,则返回“不支持” 174 return Err(SystemError::EOPNOTSUPP_OR_ENOTSUP); 175 } 176 177 /// @brief 在当前目录下创建一个新的inode 178 /// 179 /// @param name 目录项的名字 180 /// @param file_type 文件类型 181 /// @param mode 权限 182 /// 183 /// @return 创建成功:返回Ok(新的inode的Arc指针) 184 /// @return 创建失败:返回Err(错误码) 185 fn create( 186 &self, 187 name: &str, 188 file_type: FileType, 189 mode: u32, 190 ) -> Result<Arc<dyn IndexNode>, SystemError> { 191 // 若文件系统没有实现此方法,则默认调用其create_with_data方法。如果仍未实现,则会得到一个Err(-EOPNOTSUPP_OR_ENOTSUP)的返回值 192 return self.create_with_data(name, file_type, mode, 0); 193 } 194 195 /// @brief 在当前目录下创建一个新的inode,并传入一个简单的data字段,方便进行初始化。 196 /// 197 /// @param name 目录项的名字 198 /// @param file_type 文件类型 199 /// @param mode 权限 200 /// @param data 用于初始化该inode的数据。(为0则表示忽略此字段)对于不同的文件系统来说,代表的含义可能不同。 201 /// 202 /// @return 创建成功:返回Ok(新的inode的Arc指针) 203 /// @return 创建失败:返回Err(错误码) 204 fn create_with_data( 205 &self, 206 _name: &str, 207 _file_type: FileType, 208 _mode: u32, 209 _data: usize, 210 ) -> Result<Arc<dyn IndexNode>, SystemError> { 211 // 若文件系统没有实现此方法,则返回“不支持” 212 return Err(SystemError::EOPNOTSUPP_OR_ENOTSUP); 213 } 214 215 /// @brief 在当前目录下,创建一个名为Name的硬链接,指向另一个IndexNode 216 /// 217 /// @param name 硬链接的名称 218 /// @param other 要被指向的IndexNode的Arc指针 219 /// 220 /// @return 成功:Ok() 221 /// 失败:Err(错误码) 222 fn link(&self, _name: &str, _other: &Arc<dyn IndexNode>) -> Result<(), SystemError> { 223 // 若文件系统没有实现此方法,则返回“不支持” 224 return Err(SystemError::EOPNOTSUPP_OR_ENOTSUP); 225 } 226 227 /// @brief 在当前目录下,删除一个名为Name的硬链接 228 /// 229 /// @param name 硬链接的名称 230 /// 231 /// @return 成功:Ok() 232 /// 失败:Err(错误码) 233 fn unlink(&self, _name: &str) -> Result<(), SystemError> { 234 // 若文件系统没有实现此方法,则返回“不支持” 235 return Err(SystemError::EOPNOTSUPP_OR_ENOTSUP); 236 } 237 238 /// @brief 删除文件夹 239 /// 240 /// @param name 文件夹名称 241 /// 242 /// @return 成功 Ok(()) 243 /// @return 失败 Err(错误码) 244 fn rmdir(&self, _name: &str) -> Result<(), SystemError> { 245 return Err(SystemError::EOPNOTSUPP_OR_ENOTSUP); 246 } 247 248 /// @brief 将指定名称的子目录项的文件内容,移动到target这个目录下。如果_old_name所指向的inode与_target的相同,那么则直接执行重命名的操作。 249 /// 250 /// @param old_name 旧的名字 251 /// 252 /// @param target 移动到指定的inode 253 /// 254 /// @param new_name 新的文件名 255 /// 256 /// @return 成功: Ok() 257 /// 失败: Err(错误码) 258 fn move_( 259 &self, 260 _old_name: &str, 261 _target: &Arc<dyn IndexNode>, 262 _new_name: &str, 263 ) -> Result<(), SystemError> { 264 // 若文件系统没有实现此方法,则返回“不支持” 265 return Err(SystemError::EOPNOTSUPP_OR_ENOTSUP); 266 } 267 268 /// @brief 寻找一个名为Name的inode 269 /// 270 /// @param name 要寻找的inode的名称 271 /// 272 /// @return 成功:Ok() 273 /// 失败:Err(错误码) 274 fn find(&self, _name: &str) -> Result<Arc<dyn IndexNode>, SystemError> { 275 // 若文件系统没有实现此方法,则返回“不支持” 276 return Err(SystemError::EOPNOTSUPP_OR_ENOTSUP); 277 } 278 279 /// @brief 根据inode号,获取子目录项的名字 280 /// 281 /// @param ino inode号 282 /// 283 /// @return 成功:Ok() 284 /// 失败:Err(错误码) 285 fn get_entry_name(&self, _ino: InodeId) -> Result<String, SystemError> { 286 // 若文件系统没有实现此方法,则返回“不支持” 287 return Err(SystemError::EOPNOTSUPP_OR_ENOTSUP); 288 } 289 290 /// @brief 根据inode号,获取子目录项的名字和元数据 291 /// 292 /// @param ino inode号 293 /// 294 /// @return 成功:Ok(String, Metadata) 295 /// 失败:Err(错误码) 296 fn get_entry_name_and_metadata(&self, ino: InodeId) -> Result<(String, Metadata), SystemError> { 297 // 如果有条件,请在文件系统中使用高效的方式实现本接口,而不是依赖这个低效率的默认实现。 298 let name = self.get_entry_name(ino)?; 299 let entry = self.find(&name)?; 300 return Ok((name, entry.metadata()?)); 301 } 302 303 /// @brief io control接口 304 /// 305 /// @param cmd 命令 306 /// @param data 数据 307 /// 308 /// @return 成功:Ok() 309 /// 失败:Err(错误码) 310 fn ioctl(&self, _cmd: u32, _data: usize) -> Result<usize, SystemError> { 311 // 若文件系统没有实现此方法,则返回“不支持” 312 return Err(SystemError::EOPNOTSUPP_OR_ENOTSUP); 313 } 314 315 /// @brief 获取inode所在的文件系统的指针 316 fn fs(&self) -> Arc<dyn FileSystem>; 317 318 /// @brief 本函数用于实现动态转换。 319 /// 具体的文件系统在实现本函数时,最简单的方式就是:直接返回self 320 fn as_any_ref(&self) -> &dyn Any; 321 322 /// @brief 列出当前inode下的所有目录项的名字 323 fn list(&self) -> Result<Vec<String>, SystemError>; 324 325 /// @brief 在当前Inode下,挂载一个新的文件系统 326 /// 请注意!该函数只能被MountFS实现,其他文件系统不应实现这个函数 327 fn mount(&self, _fs: Arc<dyn FileSystem>) -> Result<Arc<MountFS>, SystemError> { 328 return Err(SystemError::EOPNOTSUPP_OR_ENOTSUP); 329 } 330 331 /// @brief 截断当前inode到指定的长度。如果当前文件长度小于len,则不操作。 332 /// 333 /// @param len 要被截断到的目标长度 334 fn truncate(&self, _len: usize) -> Result<(), SystemError> { 335 return Err(SystemError::EOPNOTSUPP_OR_ENOTSUP); 336 } 337 338 /// @brief 将当前inode的内容同步到具体设备上 339 fn sync(&self) -> Result<(), SystemError> { 340 return Ok(()); 341 } 342 } 343 344 impl DowncastArc for dyn IndexNode { 345 fn as_any_arc(self: Arc<Self>) -> Arc<dyn Any> { 346 self 347 } 348 } 349 350 impl dyn IndexNode { 351 /// @brief 将当前Inode转换为一个具体的结构体(类型由T指定) 352 /// 如果类型正确,则返回Some,否则返回None 353 pub fn downcast_ref<T: IndexNode>(&self) -> Option<&T> { 354 return self.as_any_ref().downcast_ref::<T>(); 355 } 356 357 /// @brief 查找文件(不考虑符号链接) 358 /// 359 /// @param path 文件路径 360 /// 361 /// @return Ok(Arc<dyn IndexNode>) 要寻找的目录项的inode 362 /// @return Err(SystemError) 错误码 363 pub fn lookup(&self, path: &str) -> Result<Arc<dyn IndexNode>, SystemError> { 364 return self.lookup_follow_symlink(path, 0); 365 } 366 367 /// @brief 查找文件(考虑符号链接) 368 /// 369 /// @param path 文件路径 370 /// @param max_follow_times 最大经过的符号链接的大小 371 /// 372 /// @return Ok(Arc<dyn IndexNode>) 要寻找的目录项的inode 373 /// @return Err(SystemError) 错误码 374 pub fn lookup_follow_symlink( 375 &self, 376 path: &str, 377 max_follow_times: usize, 378 ) -> Result<Arc<dyn IndexNode>, SystemError> { 379 if self.metadata()?.file_type != FileType::Dir { 380 return Err(SystemError::ENOTDIR); 381 } 382 383 // 处理绝对路径 384 // result: 上一个被找到的inode 385 // rest_path: 还没有查找的路径 386 let (mut result, mut rest_path) = if let Some(rest) = path.strip_prefix('/') { 387 (ROOT_INODE().clone(), String::from(rest)) 388 } else { 389 // 是相对路径 390 (self.find(".")?, String::from(path)) 391 }; 392 393 // 逐级查找文件 394 while !rest_path.is_empty() { 395 // 当前这一级不是文件夹 396 if result.metadata()?.file_type != FileType::Dir { 397 return Err(SystemError::ENOTDIR); 398 } 399 400 let name; 401 402 // 寻找“/” 403 match rest_path.find('/') { 404 Some(pos) => { 405 // 找到了,设置下一个要查找的名字 406 name = String::from(&rest_path[0..pos]); 407 // 剩余的路径字符串 408 rest_path = String::from(&rest_path[pos + 1..]); 409 } 410 None => { 411 name = rest_path; 412 rest_path = String::new(); 413 } 414 } 415 416 // 遇到连续多个"/"的情况 417 if name.is_empty() { 418 continue; 419 } 420 421 let inode = result.find(&name)?; 422 423 // 处理符号链接的问题 424 if inode.metadata()?.file_type == FileType::SymLink && max_follow_times > 0 { 425 let mut content = [0u8; 256]; 426 // 读取符号链接 427 let len = inode.read_at(0, 256, &mut content, &mut FilePrivateData::Unused)?; 428 429 // 将读到的数据转换为utf8字符串(先转为str,再转为String) 430 let link_path = String::from( 431 ::core::str::from_utf8(&content[..len]).map_err(|_| SystemError::ENOTDIR)?, 432 ); 433 434 let new_path = link_path + "/" + &rest_path; 435 // 继续查找符号链接 436 return result.lookup_follow_symlink(&new_path, max_follow_times - 1); 437 } else { 438 result = inode; 439 } 440 } 441 442 return Ok(result); 443 } 444 } 445 446 /// IndexNode的元数据 447 /// 448 /// 对应Posix2008中的sys/stat.h中的定义 https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_stat.h.html 449 #[derive(Debug, PartialEq, Eq, Clone)] 450 pub struct Metadata { 451 /// 当前inode所在的文件系统的设备号 452 pub dev_id: usize, 453 454 /// inode号 455 pub inode_id: InodeId, 456 457 /// Inode的大小 458 /// 文件:文件大小(单位:字节) 459 /// 目录:目录项中的文件、文件夹数量 460 pub size: i64, 461 462 /// Inode所在的文件系统中,每个块的大小 463 pub blk_size: usize, 464 465 /// Inode所占的块的数目 466 pub blocks: usize, 467 468 /// inode最后一次被访问的时间 469 pub atime: TimeSpec, 470 471 /// inode最后一次修改的时间 472 pub mtime: TimeSpec, 473 474 /// inode的创建时间 475 pub ctime: TimeSpec, 476 477 /// 文件类型 478 pub file_type: FileType, 479 480 /// 权限 481 pub mode: u32, 482 483 /// 硬链接的数量 484 pub nlinks: usize, 485 486 /// User ID 487 pub uid: usize, 488 489 /// Group ID 490 pub gid: usize, 491 492 /// 文件指向的设备的id(对于设备文件系统来说) 493 pub raw_dev: usize, 494 } 495 496 impl Default for Metadata { 497 fn default() -> Self { 498 return Self { 499 dev_id: 0, 500 inode_id: 0, 501 size: 0, 502 blk_size: 0, 503 blocks: 0, 504 atime: TimeSpec::default(), 505 mtime: TimeSpec::default(), 506 ctime: TimeSpec::default(), 507 file_type: FileType::File, 508 mode: 0, 509 nlinks: 1, 510 uid: 0, 511 gid: 0, 512 raw_dev: 0, 513 }; 514 } 515 } 516 517 /// @brief 所有文件系统都应该实现的trait 518 pub trait FileSystem: Any + Sync + Send + Debug { 519 /// @brief 获取当前文件系统的root inode的指针 520 fn root_inode(&self) -> Arc<dyn IndexNode>; 521 522 /// @brief 获取当前文件系统的信息 523 fn info(&self) -> FsInfo; 524 525 /// @brief 本函数用于实现动态转换。 526 /// 具体的文件系统在实现本函数时,最简单的方式就是:直接返回self 527 fn as_any_ref(&self) -> &dyn Any; 528 } 529 530 #[derive(Debug)] 531 pub struct FsInfo { 532 /// 文件系统所在的块设备的id 533 pub blk_dev_id: usize, 534 /// 文件名的最大长度 535 pub max_name_len: usize, 536 } 537 538 /// @brief 整合主设备号+次设备号 539 pub fn make_rawdev(major: usize, minor: usize) -> usize { 540 ((major & 0xffffff) << 8) | (minor & 0xff) 541 } 542 543 /// @brief 544 #[repr(C)] 545 #[derive(Debug)] 546 pub struct Dirent { 547 d_ino: u64, // 文件序列号 548 d_off: i64, // dir偏移量 549 d_reclen: u16, // 目录下的记录数 550 d_type: u8, // entry的类型 551 d_name: u8, // 文件entry的名字(是一个零长数组), 本字段仅用于占位 552 } 553 554 impl Metadata { 555 pub fn new(file_type: FileType, mode: u32) -> Self { 556 Metadata { 557 dev_id: 0, 558 inode_id: generate_inode_id(), 559 size: 0, 560 blk_size: 0, 561 blocks: 0, 562 atime: TimeSpec::default(), 563 mtime: TimeSpec::default(), 564 ctime: TimeSpec::default(), 565 file_type, 566 mode, 567 nlinks: 1, 568 uid: 0, 569 gid: 0, 570 raw_dev: 0, 571 } 572 } 573 } 574