axfs_ramfs/
dir.rs

1use alloc::collections::BTreeMap;
2use alloc::sync::{Arc, Weak};
3use alloc::{string::String, vec::Vec};
4
5use axfs_vfs::{VfsDirEntry, VfsNodeAttr, VfsNodeOps, VfsNodeRef, VfsNodeType};
6use axfs_vfs::{VfsError, VfsResult};
7use spin::RwLock;
8
9use crate::file::FileNode;
10
11/// The directory node in the RAM filesystem.
12///
13/// It implements [`axfs_vfs::VfsNodeOps`].
14pub struct DirNode {
15    this: Weak<DirNode>,
16    parent: RwLock<Weak<dyn VfsNodeOps>>,
17    children: RwLock<BTreeMap<String, VfsNodeRef>>,
18}
19
20impl DirNode {
21    pub(super) fn new(parent: Option<Weak<dyn VfsNodeOps>>) -> Arc<Self> {
22        Arc::new_cyclic(|this| Self {
23            this: this.clone(),
24            parent: RwLock::new(parent.unwrap_or_else(|| Weak::<Self>::new())),
25            children: RwLock::new(BTreeMap::new()),
26        })
27    }
28
29    pub(super) fn set_parent(&self, parent: Option<&VfsNodeRef>) {
30        *self.parent.write() = parent.map_or(Weak::<Self>::new() as _, Arc::downgrade);
31    }
32
33    /// Returns a string list of all entries in this directory.
34    pub fn get_entries(&self) -> Vec<String> {
35        self.children.read().keys().cloned().collect()
36    }
37
38    /// Checks whether a node with the given name exists in this directory.
39    pub fn exist(&self, name: &str) -> bool {
40        self.children.read().contains_key(name)
41    }
42
43    /// Creates a new node with the given name and type in this directory.
44    pub fn create_node(&self, name: &str, ty: VfsNodeType) -> VfsResult {
45        if self.exist(name) {
46            log::error!("AlreadyExists {name}");
47            return Err(VfsError::AlreadyExists);
48        }
49        let node: VfsNodeRef = match ty {
50            VfsNodeType::File => Arc::new(FileNode::new()),
51            VfsNodeType::Dir => Self::new(Some(self.this.clone())),
52            _ => return Err(VfsError::Unsupported),
53        };
54        self.children.write().insert(name.into(), node);
55        Ok(())
56    }
57
58    /// Removes a node by the given name in this directory.
59    pub fn remove_node(&self, name: &str) -> VfsResult {
60        let mut children = self.children.write();
61        let node = children.get(name).ok_or(VfsError::NotFound)?;
62        if let Some(dir) = node.as_any().downcast_ref::<DirNode>() {
63            if !dir.children.read().is_empty() {
64                return Err(VfsError::DirectoryNotEmpty);
65            }
66        }
67        children.remove(name);
68        Ok(())
69    }
70}
71
72impl VfsNodeOps for DirNode {
73    fn get_attr(&self) -> VfsResult<VfsNodeAttr> {
74        Ok(VfsNodeAttr::new_dir(4096, 0))
75    }
76
77    fn parent(&self) -> Option<VfsNodeRef> {
78        self.parent.read().upgrade()
79    }
80
81    fn lookup(self: Arc<Self>, path: &str) -> VfsResult<VfsNodeRef> {
82        let (name, rest) = split_path(path);
83        let node = match name {
84            "" | "." => Ok(self.clone() as VfsNodeRef),
85            ".." => self.parent().ok_or(VfsError::NotFound),
86            _ => self
87                .children
88                .read()
89                .get(name)
90                .cloned()
91                .ok_or(VfsError::NotFound),
92        }?;
93
94        if let Some(rest) = rest {
95            node.lookup(rest)
96        } else {
97            Ok(node)
98        }
99    }
100
101    fn read_dir(&self, start_idx: usize, dirents: &mut [VfsDirEntry]) -> VfsResult<usize> {
102        let children = self.children.read();
103        let mut children = children.iter().skip(start_idx.max(2) - 2);
104        for (i, ent) in dirents.iter_mut().enumerate() {
105            match i + start_idx {
106                0 => *ent = VfsDirEntry::new(".", VfsNodeType::Dir),
107                1 => *ent = VfsDirEntry::new("..", VfsNodeType::Dir),
108                _ => {
109                    if let Some((name, node)) = children.next() {
110                        *ent = VfsDirEntry::new(name, node.get_attr().unwrap().file_type());
111                    } else {
112                        return Ok(i);
113                    }
114                }
115            }
116        }
117        Ok(dirents.len())
118    }
119
120    fn create(&self, path: &str, ty: VfsNodeType) -> VfsResult {
121        log::debug!("create {ty:?} at ramfs: {path}");
122        let (name, rest) = split_path(path);
123        if let Some(rest) = rest {
124            match name {
125                "" | "." => self.create(rest, ty),
126                ".." => self.parent().ok_or(VfsError::NotFound)?.create(rest, ty),
127                _ => {
128                    let subdir = self
129                        .children
130                        .read()
131                        .get(name)
132                        .ok_or(VfsError::NotFound)?
133                        .clone();
134                    subdir.create(rest, ty)
135                }
136            }
137        } else if name.is_empty() || name == "." || name == ".." {
138            Ok(()) // already exists
139        } else {
140            self.create_node(name, ty)
141        }
142    }
143
144    fn remove(&self, path: &str) -> VfsResult {
145        log::debug!("remove at ramfs: {path}");
146        let (name, rest) = split_path(path);
147        if let Some(rest) = rest {
148            match name {
149                "" | "." => self.remove(rest),
150                ".." => self.parent().ok_or(VfsError::NotFound)?.remove(rest),
151                _ => {
152                    let subdir = self
153                        .children
154                        .read()
155                        .get(name)
156                        .ok_or(VfsError::NotFound)?
157                        .clone();
158                    subdir.remove(rest)
159                }
160            }
161        } else if name.is_empty() || name == "." || name == ".." {
162            Err(VfsError::InvalidInput) // remove '.' or '..
163        } else {
164            self.remove_node(name)
165        }
166    }
167
168    axfs_vfs::impl_vfs_dir_default! {}
169}
170
171fn split_path(path: &str) -> (&str, Option<&str>) {
172    let trimmed_path = path.trim_start_matches('/');
173    trimmed_path.find('/').map_or((trimmed_path, None), |n| {
174        (&trimmed_path[..n], Some(&trimmed_path[n + 1..]))
175    })
176}