diff --git a/src/fs/mod.rs b/src/fs/mod.rs index 0e52c293f8232e7be07ff4d15c2d7e2b0612ef30..f371830d6f57c04bc768c952d643fa9986e458a6 100644 --- a/src/fs/mod.rs +++ b/src/fs/mod.rs @@ -3,3 +3,4 @@ //! Contains modules for the Virtual File System (vfs) and an ext2 implementations pub mod node; +pub mod path; diff --git a/src/fs/node.rs b/src/fs/node.rs index 04f304b5e56f295aa0de8fb62de2bb8992b4bdf5..15debe701217edac14648301af3c8ca9b4b090dd 100644 --- a/src/fs/node.rs +++ b/src/fs/node.rs @@ -1,3 +1,127 @@ //! Interface for files, directories and symbolic links //! //! The general structure of the file systems is the Node Graph in Unix style. +//! +//! The node named "/" is a special node called "root" of the filesystem. + +use alloc::boxed::Box; +use alloc::string::String; +use alloc::sync::Arc; +use alloc::vec::Vec; + +use spin::Mutex; +use x86_64::structures::paging::page::PageRangeInclusive; + +use super::path::Path; +use crate::kernel::device::block::Manipulation; + +/// Type alias for a thread-safe shared reference behind a mutex +pub type Ref<T> = Arc<Mutex<T>>; + +/// Enumeration of possible node kinds : file, directory or symbolic link +#[derive(Debug, Clone, Copy)] +pub enum Kind { + /// Storage unit of a filesystem + File, + + /// Node containing other nodes + Directory, + + /// Node pointing towards an other node in the filesystem + Symlink, +} + +/// Enumeration of possible nodes +#[allow(unused)] +pub enum Node { + /// A reference to a file + File(Ref<dyn File>), + + /// A reference to a directory + Directory(Ref<dyn Directory>), + + /// A reference to a symbolic link + Symlink(Ref<dyn Symlink>), +} + +impl From<Node> for Kind { + fn from(value: Node) -> Self { + match value { + Node::File(_) => Self::File, + Node::Directory(_) => Self::Directory, + Node::Symlink(_) => Self::Symlink, + } + } +} + +/// General interface for every Interface in a filesystem +pub trait Interface { + /// Returns the name of the node + /// + /// A node's name cannot contain the character `/` + fn name(&self) -> String; + + /// Returns the kind of the node + fn kind(&self) -> Kind; + + /// Returns the parent directory of the node + /// + /// The parent directory of the root "/" should by itself + fn get_parent_dir(&self) -> Ref<dyn Directory>; + + /// Sets the parent directory of the node + fn set_parent_dir(&mut self, new_parent: Box<dyn Directory>); + + /// Returns whether the node is the root of the filesystem or not + fn is_root(&self) -> bool { + self.name() == "/" + } + + /// Returns the absolute path of the node + fn absolute_path(&self) -> Path { + if self.is_root() { + Path::new_absolute() + } else { + let mut parent_path = self.get_parent_dir().lock().absolute_path(); + parent_path.push(&self.name()); + parent_path + } + } +} + +impl PartialEq for dyn Interface { + fn eq(&self, other: &Self) -> bool { + self.absolute_path() == other.absolute_path() + } +} + +impl Eq for dyn Interface {} + +/// General interface for directories +pub trait Directory: Interface { + /// Returns the child with the corresponding name + fn get(&self, name: &str) -> Option<Node>; + + /// Inserts the given `node` in the directory + /// + /// If a node with the same name is already present in this directory, that node is replaced and return + fn insert(&mut self, node: Node) -> Option<Node>; + + /// Removes the given `node` in the directory then returns it + fn remove(&mut self, node: Node) -> Option<Node>; + + /// Lists the nodes in this directory + fn children(&self) -> Vec<Node>; +} + +/// General interface for files +pub trait File: Interface + Manipulation { + /// Returns a memory region on which the file is mounted + fn as_pages(&self) -> anyhow::Result<PageRangeInclusive>; +} + +/// General interface for symbolic links +pub trait Symlink: Interface { + /// Returns the node pointed by the symbolic link + fn pointed_node(&self) -> Node; +} diff --git a/src/fs/path.rs b/src/fs/path.rs new file mode 100644 index 0000000000000000000000000000000000000000..098cd7317e3efbe8739d4c982775b4ae75cee6eb --- /dev/null +++ b/src/fs/path.rs @@ -0,0 +1,133 @@ +//! Interface to use paths in the filesystem + +use alloc::borrow::ToOwned; +use alloc::string::String; + +use derive_more::{Deref, DerefMut}; + +use super::node::{Interface, Ref}; + +/// Memory allocated non-empty path structure +#[derive(Debug, Deref, DerefMut, Clone, PartialEq, Eq)] +pub struct Path(String); + +impl From<Ref<dyn Interface>> for Path { + fn from(value: Ref<dyn Interface>) -> Self { + let node = value.lock(); + if node.is_root() { + Self::new_absolute() + } else { + let mut parent_path = Self::from(node.get_parent_dir() as Ref<dyn Interface>); + parent_path.push(&node.name()); + parent_path + } + } +} + +impl Path { + /// Returns a new absolute path (the path of the filesystem's root) + pub fn new_absolute() -> Self { + Self("/".to_owned()) + } + + /// Returns a new relative path (the path of the "current" node) + #[allow(unused)] + pub fn new_relative() -> Self { + Self(".".to_owned()) + } + + /// Returns whether `self` is relative or not + #[allow(unused)] + pub fn is_relative(&self) -> bool { + // SAFETY: it is ensure in this structure that no empty path can be created + unsafe { self.chars().next().unwrap_unchecked() == '.' } + } + + /// Returns whether `self` is absolute or not + #[allow(unused)] + pub fn is_absolute(&self) -> bool { + !self.is_relative() + } + + /// Returns whether `self` represents the root or not + /// + /// Is `is_relative` if `true`, then this function will always return `false`. + #[allow(unused)] + pub fn is_root(&self) -> bool { + self.is_absolute() && self.ends_with('/') + } + + /// Extends `self` with a node represented as a [`String`] + pub fn push(&mut self, node: &str) { + if self.0 != "/" { + self.extend_one('/'); + } + self.extend(node.chars()); + } + + /// Truncates `self` to its parent dir + /// + /// Returns the name of the node truncated, and [`None`] if it does nothing + #[allow(unused)] + pub fn pop(&mut self) -> Option<String> { + if self.0 == "." || self.0 == "/" { + return None; + }; + + let binding = self.clone(); + let relative = self.is_relative(); + let mut ancestors = binding.split('/'); + + // SAFETY: the path is non empty and not reduced to "/" or "." + let node = unsafe { ancestors.next_back().unwrap_unchecked() }.to_owned(); + if relative { + ancestors.next(); + }; + + self.clone_from(&if relative { Self::new_relative() } else { Self::new_absolute() }); + ancestors.for_each(|ancestor| self.push(ancestor)); + + Some(node) + } +} + +#[cfg(test)] +mod test { + use super::Path; + + #[test_case] + fn absolute() { + let root = Path::new_absolute(); + assert!(root.is_absolute()); + assert!(root.is_root()); + assert_eq!(&root.0, "/"); + } + + #[test_case] + fn relative() { + let current = Path::new_relative(); + assert!(current.is_relative()); + assert!(!current.is_root()); + assert_eq!(¤t.0, "."); + } + + #[test_case] + fn absolute_manipulation() { + let mut path = Path::new_absolute(); + path.push("bin"); + path.push("ls"); + assert_eq!(&path.0, "/bin/ls"); + assert_eq!(&path.pop().unwrap(), "ls"); + assert_eq!(&path.0, "/bin"); + } + + #[test_case] + fn relative_manipulation() { + let mut path = Path::new_relative(); + path.push("foo"); + path.push("bar"); + assert_eq!(&path.0, "./foo/bar"); + assert_eq!(&path.pop().unwrap(), "bar"); + assert_eq!(&path.0, "./foo"); + } +} diff --git a/src/kernel/device/mod.rs b/src/kernel/device/mod.rs index f0648a72e04fb934920369841d99b5144518e4ba..b5238fdf3d052fc3cc096736f3983e62cb1ccd9e 100644 --- a/src/kernel/device/mod.rs +++ b/src/kernel/device/mod.rs @@ -7,12 +7,11 @@ use conquer_once::spin::OnceCell; use log::info; use spin::Mutex; -use crate::kernel::device::ata::{IdeController, BLOCK_SIZE}; -use crate::println; +use crate::kernel::device::ata::IdeController; #[allow(clippy::same_name_method)] mod ata; -mod block; +pub mod block; mod pci; pub mod storage; diff --git a/src/main.rs b/src/main.rs index af1b0a65bb733811ce76e6aa807c060c9c464609..ab39a49a51c6eb9cc21d3032ef1829b9125e3b05 100644 --- a/src/main.rs +++ b/src/main.rs @@ -58,8 +58,10 @@ #![feature(const_trait_impl)] #![feature(custom_test_frameworks)] #![feature(error_in_core)] +#![feature(extend_one)] #![feature(no_coverage)] #![feature(strict_provenance)] +#![feature(trait_upcasting)] #![reexport_test_harness_main = "test_main"] #![test_runner(test::runner)]