use core::{ cell::{RefCell, RefMut}, fmt, }; use alloc::{ boxed::Box, rc::{Rc, Weak}, string::String, vec::Vec, }; use yggdrasil_abi::{ error::Error, io::{FileMode, OpenOptions}, }; use crate::{ file::{File, FileFlags, FileRef}, fs::Filesystem, }; pub type VnodeRef = Rc; pub type VnodeWeak = Weak; pub struct VnodeDump { node: VnodeRef, } #[derive(Debug, Clone, Copy, PartialEq)] pub enum VnodeKind { Directory, Regular, Char, Block, } pub(crate) struct TreeNode { parent: Option, children: Vec, } pub struct Vnode { name: String, tree: RefCell, kind: VnodeKind, data: RefCell>>, fs: RefCell>>, target: RefCell>, } pub trait VnodeImpl { fn create(&mut self, _at: &VnodeRef, _name: &str, _kind: VnodeKind) -> Result { Err(Error::NotImplemented) } fn open( &mut self, _node: &VnodeRef, _opts: OpenOptions, _mode: FileMode, ) -> Result { Err(Error::NotImplemented) } fn close(&mut self, _node: &VnodeRef) -> Result<(), Error> { Err(Error::NotImplemented) } fn read(&mut self, _node: &VnodeRef, _pos: u64, _data: &mut [u8]) -> Result { Err(Error::NotImplemented) } fn write(&mut self, _node: &VnodeRef, _pos: u64, _data: &[u8]) -> Result { Err(Error::NotImplemented) } fn size(&mut self, _node: &VnodeRef) -> Result { Err(Error::NotImplemented) } } impl Vnode { pub fn new>(name: S, kind: VnodeKind) -> VnodeRef { Rc::new(Self { name: name.into(), tree: RefCell::new(TreeNode { parent: None, children: Vec::new(), }), kind, data: RefCell::new(None), fs: RefCell::new(None), target: RefCell::new(None), }) } #[inline] pub fn name(&self) -> &str { &self.name } #[inline] pub fn kind(&self) -> VnodeKind { self.kind } #[inline] pub fn target(&self) -> Option { self.target.borrow().clone() } #[inline] pub fn data(&self) -> RefMut>> { self.data.borrow_mut() } #[inline] pub fn fs(&self) -> Option> { self.fs.borrow().clone() } #[inline] pub fn set_target(&self, target: Option) { self.target.replace(target); } pub fn parent(self: &VnodeRef) -> VnodeRef { match &self.tree.borrow().parent { Some(parent) => parent.upgrade().unwrap(), None => self.clone(), } } pub fn set_data(&self, data: Box) { self.data.borrow_mut().replace(data); } pub fn set_fs(&self, data: Rc) { self.fs.replace(Some(data)); } #[inline] pub fn is_directory(&self) -> bool { self.kind == VnodeKind::Directory } #[inline] pub fn is_mountpoint(&self) -> bool { self.is_directory() && self.target.borrow().is_some() } // Cache tree operations pub fn add_child(self: &VnodeRef, child: VnodeRef) { let parent_weak = Rc::downgrade(self); let mut parent_borrow = self.tree.borrow_mut(); assert!(child .tree .borrow_mut() .parent .replace(parent_weak) .is_none()); parent_borrow.children.push(child); } pub fn dump(&self, f: &mut fmt::Formatter<'_>, depth: usize, indent: bool) -> fmt::Result { if indent { for _ in 0..depth { f.write_str(" ")?; } } write!(f, "{:?}", self.name)?; match self.kind { VnodeKind::Directory => { let tree = self.tree.borrow(); let target = self.target(); if let Some(target) = target { assert_eq!(tree.children.len(), 0); f.write_str(" -> ")?; return target.dump(f, depth, false); } if tree.children.is_empty() { f.write_str(" []")?; } else { f.write_str(" [\n")?; for child in tree.children.iter() { child.dump(f, depth + 1, true)?; f.write_str("\n")?; } for _ in 0..depth { f.write_str(" ")?; } f.write_str("]")?; } } _ => (), } Ok(()) } pub fn lookup(self: &VnodeRef, name: &str) -> Option { assert!(self.is_directory()); self.tree .borrow() .children .iter() .find(|e| e.name == name) .cloned() } // pub fn lookup_or_load(self: &VnodeRef, name: &str) -> Result { // Lookup in cache if let Some(node) = self.lookup(name) { return Ok(node); } // TODO load from FS Err(Error::DoesNotExist) } // Node operations pub fn open(self: &VnodeRef, flags: OpenOptions, mode: FileMode) -> Result { let mut open_flags = FileFlags::empty(); if flags.contains(OpenOptions::READ) { open_flags |= FileFlags::READ; } if flags.contains(OpenOptions::WRITE) { open_flags |= FileFlags::WRITE; } if self.kind == VnodeKind::Directory { return Err(Error::IsADirectory); } if let Some(ref mut data) = *self.data() { let pos = data.open(self, flags, mode)?; Ok(File::normal(self.clone(), pos, open_flags)) } else { todo!() } } pub fn close(self: &VnodeRef) -> Result<(), Error> { if let Some(ref mut data) = *self.data() { data.close(self) } else { todo!() } } pub fn create(self: &VnodeRef, name: &str, kind: VnodeKind) -> Result { if self.kind != VnodeKind::Directory { todo!(); } if name.contains('/') { return Err(Error::InvalidArgument); } match self.lookup_or_load(name) { Err(Error::DoesNotExist) => {} Ok(_) => return Err(Error::AlreadyExists), e => return e, }; if let Some(ref mut data) = *self.data() { let vnode = data.create(self, name, kind)?; if let Some(fs) = self.fs() { vnode.set_fs(fs); } self.add_child(vnode.clone()); Ok(vnode) } else { Err(Error::NotImplemented) } } pub fn write(self: &VnodeRef, pos: u64, buf: &[u8]) -> Result { if self.kind == VnodeKind::Directory { todo!(); } if let Some(ref mut data) = *self.data() { data.write(self, pos, buf) } else { todo!() } } pub fn read(self: &VnodeRef, pos: u64, buf: &mut [u8]) -> Result { if self.kind == VnodeKind::Directory { todo!(); } if let Some(ref mut data) = *self.data() { data.read(self, pos, buf) } else { todo!() } } pub fn size(self: &VnodeRef) -> Result { if let Some(ref mut data) = *self.data() { data.size(self) } else { todo!(); } } pub fn mount(self: &VnodeRef, fs_root: VnodeRef) -> Result<(), Error> { if !self.is_directory() { return Err(Error::NotADirectory); } if !fs_root.is_directory() { todo!("Filesystem root is not a directory"); } if self.target.borrow().is_some() { todo!("Target mountpoint is busy"); } { let mut child_borrow = fs_root.tree.borrow_mut(); if child_borrow.parent.is_some() { todo!("Filesystem is already mounted somewhere else"); } child_borrow.parent = Some(Rc::downgrade(self)); } self.target.replace(Some(fs_root)); Ok(()) } pub fn unmount_target(self: &VnodeRef) -> Result<(), Error> { if !self.is_directory() { return Err(Error::NotADirectory); } let Some(fs_root) = self.target.take() else { todo!(); }; { let mut target_borrow = fs_root.tree.borrow_mut(); let Some(parent) = target_borrow.parent.take() else { todo!() }; assert!(Rc::ptr_eq(self, &parent.upgrade().unwrap())); } Ok(()) } } impl fmt::Debug for Vnode { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let prefix = match self.kind { VnodeKind::Directory => "DIR ", VnodeKind::Regular => "REG ", VnodeKind::Char => "CHR ", VnodeKind::Block => "BLK ", }; write!(f, "[{} {}]", prefix, self.name) } } impl VnodeDump { pub fn new(node: VnodeRef) -> Self { Self { node } } } impl fmt::Debug for VnodeDump { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.node.dump(f, 0, true) } }