383 lines
9.5 KiB
Rust

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<Vnode>;
pub type VnodeWeak = Weak<Vnode>;
pub struct VnodeDump {
node: VnodeRef,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum VnodeKind {
Directory,
Regular,
Char,
Block,
}
pub(crate) struct TreeNode {
parent: Option<VnodeWeak>,
children: Vec<VnodeRef>,
}
pub struct Vnode {
name: String,
tree: RefCell<TreeNode>,
kind: VnodeKind,
data: RefCell<Option<Box<dyn VnodeImpl>>>,
fs: RefCell<Option<Rc<dyn Filesystem>>>,
target: RefCell<Option<VnodeRef>>,
}
pub trait VnodeImpl {
fn create(&mut self, _at: &VnodeRef, _name: &str, _kind: VnodeKind) -> Result<VnodeRef, Error> {
Err(Error::NotImplemented)
}
fn open(
&mut self,
_node: &VnodeRef,
_opts: OpenOptions,
_mode: FileMode,
) -> Result<u64, Error> {
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<usize, Error> {
Err(Error::NotImplemented)
}
fn write(&mut self, _node: &VnodeRef, _pos: u64, _data: &[u8]) -> Result<usize, Error> {
Err(Error::NotImplemented)
}
fn size(&mut self, _node: &VnodeRef) -> Result<u64, Error> {
Err(Error::NotImplemented)
}
}
impl Vnode {
pub fn new<S: Into<String>>(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<VnodeRef> {
self.target.borrow().clone()
}
#[inline]
pub fn data(&self) -> RefMut<Option<Box<dyn VnodeImpl>>> {
self.data.borrow_mut()
}
#[inline]
pub fn fs(&self) -> Option<Rc<dyn Filesystem>> {
self.fs.borrow().clone()
}
#[inline]
pub fn set_target(&self, target: Option<VnodeRef>) {
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<dyn VnodeImpl>) {
self.data.borrow_mut().replace(data);
}
pub fn set_fs(&self, data: Rc<dyn Filesystem>) {
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<VnodeRef> {
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<VnodeRef, Error> {
// 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<FileRef, Error> {
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<VnodeRef, Error> {
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<usize, Error> {
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<usize, Error> {
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<u64, Error> {
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)
}
}