383 lines
9.5 KiB
Rust
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)
|
|
}
|
|
}
|