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!(&current.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)]