use alloc::collections::BTreeMap;
use alloc::sync::{Arc, Weak};
use axfs_vfs::{VfsDirEntry, VfsNodeAttr, VfsNodeOps, VfsNodeRef, VfsNodeType};
use axfs_vfs::{VfsError, VfsResult};
use spin::RwLock;
pub struct DirNode {
parent: RwLock<Weak<dyn VfsNodeOps>>,
children: RwLock<BTreeMap<&'static str, VfsNodeRef>>,
}
impl DirNode {
pub(super) fn new(parent: Option<&VfsNodeRef>) -> Arc<Self> {
let parent = parent.map_or(Weak::<Self>::new() as _, Arc::downgrade);
Arc::new(Self {
parent: RwLock::new(parent),
children: RwLock::new(BTreeMap::new()),
})
}
pub(super) fn set_parent(&self, parent: Option<&VfsNodeRef>) {
*self.parent.write() = parent.map_or(Weak::<Self>::new() as _, Arc::downgrade);
}
pub fn mkdir(self: &Arc<Self>, name: &'static str) -> Arc<Self> {
let parent = self.clone() as VfsNodeRef;
let node = Self::new(Some(&parent));
self.children.write().insert(name, node.clone());
node
}
pub fn add(&self, name: &'static str, node: VfsNodeRef) {
self.children.write().insert(name, node);
}
}
impl VfsNodeOps for DirNode {
fn get_attr(&self) -> VfsResult<VfsNodeAttr> {
Ok(VfsNodeAttr::new_dir(4096, 0))
}
fn parent(&self) -> Option<VfsNodeRef> {
self.parent.read().upgrade()
}
fn lookup(self: Arc<Self>, path: &str) -> VfsResult<VfsNodeRef> {
let (name, rest) = split_path(path);
let node = match name {
"" | "." => Ok(self.clone() as VfsNodeRef),
".." => self.parent().ok_or(VfsError::NotFound),
_ => self
.children
.read()
.get(name)
.cloned()
.ok_or(VfsError::NotFound),
}?;
if let Some(rest) = rest {
node.lookup(rest)
} else {
Ok(node)
}
}
fn read_dir(&self, start_idx: usize, dirents: &mut [VfsDirEntry]) -> VfsResult<usize> {
let children = self.children.read();
let mut children = children.iter().skip(start_idx.max(2) - 2);
for (i, ent) in dirents.iter_mut().enumerate() {
match i + start_idx {
0 => *ent = VfsDirEntry::new(".", VfsNodeType::Dir),
1 => *ent = VfsDirEntry::new("..", VfsNodeType::Dir),
_ => {
if let Some((name, node)) = children.next() {
*ent = VfsDirEntry::new(name, node.get_attr().unwrap().file_type());
} else {
return Ok(i);
}
}
}
}
Ok(dirents.len())
}
fn create(&self, path: &str, ty: VfsNodeType) -> VfsResult {
log::debug!("create {:?} at devfs: {}", ty, path);
let (name, rest) = split_path(path);
if let Some(rest) = rest {
match name {
"" | "." => self.create(rest, ty),
".." => self.parent().ok_or(VfsError::NotFound)?.create(rest, ty),
_ => self
.children
.read()
.get(name)
.ok_or(VfsError::NotFound)?
.create(rest, ty),
}
} else if name.is_empty() || name == "." || name == ".." {
Ok(()) } else {
Err(VfsError::PermissionDenied) }
}
fn remove(&self, path: &str) -> VfsResult {
log::debug!("remove at devfs: {}", path);
let (name, rest) = split_path(path);
if let Some(rest) = rest {
match name {
"" | "." => self.remove(rest),
".." => self.parent().ok_or(VfsError::NotFound)?.remove(rest),
_ => self
.children
.read()
.get(name)
.ok_or(VfsError::NotFound)?
.remove(rest),
}
} else {
Err(VfsError::PermissionDenied) }
}
axfs_vfs::impl_vfs_dir_default! {}
}
fn split_path(path: &str) -> (&str, Option<&str>) {
let trimmed_path = path.trim_start_matches('/');
trimmed_path.find('/').map_or((trimmed_path, None), |n| {
(&trimmed_path[..n], Some(&trimmed_path[n + 1..]))
})
}