axfs_ng_vfs/node/
dir.rs

1use alloc::{borrow::ToOwned, string::String, sync::Arc};
2use core::{
3    mem,
4    ops::{Deref, DerefMut},
5};
6
7use hashbrown::HashMap;
8
9use super::DirEntry;
10use crate::{
11    MetadataUpdate, Mountpoint, Mutex, MutexGuard, NodeOps, NodePermission, NodeType, VfsError,
12    VfsResult,
13    path::{DOT, DOTDOT, MAX_NAME_LEN, verify_entry_name},
14};
15
16/// A trait for a sink that can receive directory entries.
17pub trait DirEntrySink {
18    /// Accept a directory entry, returns `false` if the sink is full.
19    ///
20    /// `offset` is the offset of the next entry to be read.
21    ///
22    /// It's not recommended to operate on the node inside the `accept`
23    /// function, since some filesystem may impose a lock while iterating the
24    /// directory, and operating on the node may cause deadlock.
25    fn accept(&mut self, name: &str, ino: u64, node_type: NodeType, offset: u64) -> bool;
26}
27
28impl<F: FnMut(&str, u64, NodeType, u64) -> bool> DirEntrySink for F {
29    fn accept(&mut self, name: &str, ino: u64, node_type: NodeType, offset: u64) -> bool {
30        self(name, ino, node_type, offset)
31    }
32}
33
34type DirChildren = HashMap<String, DirEntry>;
35
36pub trait DirNodeOps: NodeOps {
37    /// Reads directory entries.
38    ///
39    /// Returns the number of entries read.
40    ///
41    /// Implementations should ensure that `.` and `..` are present in the
42    /// result.
43    fn read_dir(&self, offset: u64, sink: &mut dyn DirEntrySink) -> VfsResult<usize>;
44
45    /// Lookups a directory entry by name.
46    fn lookup(&self, name: &str) -> VfsResult<DirEntry>;
47
48    /// Returns whether directory entries can be cached.
49    ///
50    /// Some filesystems (like '/proc') may not support caching directory
51    /// entries, as they may change frequently or not be backed by persistent
52    /// storage.
53    ///
54    /// If this returns `false`, the directory will not be cached in dentry and
55    /// each call to [`DirNode::lookup`] will end up calling [`lookup`].
56    /// Implementations should take care to handle cases where [`lookup`] is
57    /// called multiple times for the same name.
58    fn is_cacheable(&self) -> bool {
59        true
60    }
61
62    /// Creates a directory entry.
63    fn create(
64        &self,
65        name: &str,
66        node_type: NodeType,
67        permission: NodePermission,
68    ) -> VfsResult<DirEntry>;
69
70    /// Creates a link to a node.
71    fn link(&self, name: &str, node: &DirEntry) -> VfsResult<DirEntry>;
72
73    /// Unlinks a directory entry by name.
74    ///
75    /// If the entry is a non-empty directory, it should return `ENOTEMPTY`
76    /// error.
77    fn unlink(&self, name: &str) -> VfsResult<()>;
78
79    /// Renames a directory entry, replacing the original entry (dst) if it
80    /// already exists.
81    ///
82    /// If src and dst link to the same file, this should do nothing and return
83    /// `Ok(())`.
84    ///
85    /// The caller should ensure:
86    /// - If `src` is a directory, `dst` must not exist or be an empty
87    ///   directory.
88    /// - If `src` is not a directory, `dst` must not exist or not be a
89    ///   directory.
90    fn rename(&self, src_name: &str, dst_dir: &DirNode, dst_name: &str) -> VfsResult<()>;
91}
92
93/// Options for opening (or creating) a directory entry.
94///
95/// See [`DirNode::open_file`] for more details.
96#[derive(Debug, Clone)]
97pub struct OpenOptions {
98    pub create: bool,
99    pub create_new: bool,
100    pub node_type: NodeType,
101    pub permission: NodePermission,
102    pub user: Option<(u32, u32)>, // (uid, gid)
103}
104
105impl Default for OpenOptions {
106    fn default() -> Self {
107        Self {
108            create: false,
109            create_new: false,
110            node_type: NodeType::RegularFile,
111            permission: NodePermission::default(),
112            user: None,
113        }
114    }
115}
116
117pub struct DirNode {
118    ops: Arc<dyn DirNodeOps>,
119    cache: Mutex<DirChildren>,
120    pub(crate) mountpoint: Mutex<Option<Arc<Mountpoint>>>,
121}
122
123impl Deref for DirNode {
124    type Target = dyn NodeOps;
125
126    fn deref(&self) -> &Self::Target {
127        &*self.ops
128    }
129}
130
131impl From<DirNode> for Arc<dyn NodeOps> {
132    fn from(node: DirNode) -> Self {
133        node.ops.clone()
134    }
135}
136
137impl DirNode {
138    pub fn new(ops: Arc<dyn DirNodeOps>) -> Self {
139        Self {
140            ops,
141            cache: Mutex::default(),
142            mountpoint: Mutex::default(),
143        }
144    }
145
146    pub fn inner(&self) -> &Arc<dyn DirNodeOps> {
147        &self.ops
148    }
149
150    pub fn downcast<T: DirNodeOps>(&self) -> VfsResult<Arc<T>> {
151        self.ops
152            .clone()
153            .into_any()
154            .downcast()
155            .map_err(|_| VfsError::InvalidInput)
156    }
157
158    fn forget_entry(children: &mut DirChildren, name: &str) {
159        if let Some(entry) = children.remove(name)
160            && let Ok(dir) = entry.as_dir()
161        {
162            dir.forget();
163        }
164    }
165
166    fn lookup_locked(&self, name: &str, children: &mut DirChildren) -> VfsResult<DirEntry> {
167        use hashbrown::hash_map::Entry;
168        match children.entry(name.to_owned()) {
169            Entry::Occupied(e) => Ok(e.get().clone()),
170            Entry::Vacant(e) => {
171                let node = self.ops.lookup(name)?;
172                if self.ops.is_cacheable() {
173                    e.insert(node.clone());
174                }
175                Ok(node)
176            }
177        }
178    }
179
180    /// Looks up a directory entry by name.
181    pub fn lookup(&self, name: &str) -> VfsResult<DirEntry> {
182        if name.len() > MAX_NAME_LEN {
183            return Err(VfsError::NameTooLong);
184        }
185        // Fast path
186        if self.ops.is_cacheable() {
187            self.lookup_locked(name, &mut self.cache.lock())
188        } else {
189            self.ops.lookup(name)
190        }
191    }
192
193    /// Looks up a directory entry by name in cache.
194    pub fn lookup_cache(&self, name: &str) -> Option<DirEntry> {
195        if self.ops.is_cacheable() {
196            self.cache.lock().get(name).cloned()
197        } else {
198            None
199        }
200    }
201
202    /// Inserts a directory entry into the cache.
203    pub fn insert_cache(&self, name: String, entry: DirEntry) -> Option<DirEntry> {
204        if self.ops.is_cacheable() {
205            self.cache.lock().insert(name, entry)
206        } else {
207            None
208        }
209    }
210
211    pub fn read_dir(&self, offset: u64, sink: &mut dyn DirEntrySink) -> VfsResult<usize> {
212        self.ops.read_dir(offset, sink)
213    }
214
215    /// Creates a link to a node.
216    pub fn link(&self, name: &str, node: &DirEntry) -> VfsResult<DirEntry> {
217        verify_entry_name(name)?;
218
219        self.ops.link(name, node).inspect(|entry| {
220            self.cache.lock().insert(name.to_owned(), entry.clone());
221        })
222    }
223
224    /// Unlinks a directory entry by name.
225    pub fn unlink(&self, name: &str, is_dir: bool) -> VfsResult<()> {
226        verify_entry_name(name)?;
227
228        let mut children = self.cache.lock();
229        let entry = self.lookup_locked(name, &mut children)?;
230        match (entry.is_dir(), is_dir) {
231            (true, false) => return Err(VfsError::IsADirectory),
232            (false, true) => return Err(VfsError::NotADirectory),
233            _ => {}
234        }
235
236        self.ops.unlink(name).inspect(|_| {
237            Self::forget_entry(&mut children, name);
238        })
239    }
240
241    /// Returns whether the directory contains children.
242    pub fn has_children(&self) -> VfsResult<bool> {
243        let mut has_children = false;
244        self.read_dir(0, &mut |name: &str, _, _, _| {
245            if name != DOT && name != DOTDOT {
246                has_children = true;
247                false
248            } else {
249                true
250            }
251        })?;
252        Ok(has_children)
253    }
254
255    fn create_locked(
256        &self,
257        name: &str,
258        node_type: NodeType,
259        permission: NodePermission,
260        children: &mut DirChildren,
261    ) -> VfsResult<DirEntry> {
262        let entry = self.ops.create(name, node_type, permission)?;
263        children.insert(name.to_owned(), entry.clone());
264        Ok(entry)
265    }
266
267    /// Creates a directory entry.
268    pub fn create(
269        &self,
270        name: &str,
271        node_type: NodeType,
272        permission: NodePermission,
273    ) -> VfsResult<DirEntry> {
274        verify_entry_name(name)?;
275        self.create_locked(name, node_type, permission, &mut self.cache.lock())
276    }
277
278    fn lock_both_cache<'a>(
279        &'a self,
280        other: &'a Self,
281    ) -> (
282        MutexGuard<'a, DirChildren>,
283        Option<MutexGuard<'a, DirChildren>>,
284    ) {
285        let src_children = self.cache.lock();
286        let dst_children = if core::ptr::eq(self, other) {
287            None
288        } else {
289            Some(other.cache.lock())
290        };
291        (src_children, dst_children)
292    }
293
294    /// Renames a directory entry.
295    pub fn rename(&self, src_name: &str, dst_dir: &Self, dst_name: &str) -> VfsResult<()> {
296        verify_entry_name(src_name)?;
297        verify_entry_name(dst_name)?;
298
299        let (mut src_children, mut dst_children) = self.lock_both_cache(dst_dir);
300
301        let src = self.lookup_locked(src_name, &mut src_children)?;
302        if let Ok(dst) = dst_dir.lookup_locked(
303            dst_name,
304            dst_children
305                .as_mut()
306                .map_or_else(|| src_children.deref_mut(), DerefMut::deref_mut),
307        ) {
308            if src.node_type() == NodeType::Directory {
309                if let Ok(dir) = dst.as_dir()
310                    && dir.has_children()?
311                {
312                    return Err(VfsError::DirectoryNotEmpty);
313                }
314            } else if dst.node_type() == NodeType::Directory {
315                return Err(VfsError::IsADirectory);
316            }
317        }
318        drop(src_children);
319        drop(dst_children);
320
321        self.ops.rename(src_name, dst_dir, dst_name).inspect(|_| {
322            let (mut src_children, mut dst_children) = self.lock_both_cache(dst_dir);
323            Self::forget_entry(&mut src_children, src_name);
324            Self::forget_entry(
325                dst_children
326                    .as_mut()
327                    .map_or_else(|| src_children.deref_mut(), DerefMut::deref_mut),
328                dst_name,
329            );
330        })
331    }
332
333    /// Opens (or creates) a file in the directory.
334    pub fn open_file(&self, name: &str, options: &OpenOptions) -> VfsResult<DirEntry> {
335        verify_entry_name(name)?;
336
337        let mut children = self.cache.lock();
338        match self.lookup_locked(name, &mut children) {
339            Ok(val) => {
340                if options.create_new {
341                    return Err(VfsError::AlreadyExists);
342                }
343                return Ok(val);
344            }
345            Err(err) if err.canonicalize() == VfsError::NotFound && options.create => {}
346            Err(err) => return Err(err),
347        }
348        let entry =
349            self.create_locked(name, options.node_type, options.permission, &mut children)?;
350        if options.user.is_some() {
351            entry.update_metadata(MetadataUpdate {
352                owner: options.user,
353                ..Default::default()
354            })?;
355        }
356        Ok(entry)
357    }
358
359    pub fn mountpoint(&self) -> Option<Arc<Mountpoint>> {
360        self.mountpoint.lock().clone()
361    }
362
363    pub fn is_mountpoint(&self) -> bool {
364        self.mountpoint.lock().is_some()
365    }
366
367    /// Clears the cache of directory entries & user data, allowing them to be
368    /// released.
369    pub(crate) fn forget(&self) {
370        for (_, child) in mem::take(self.cache.lock().deref_mut()) {
371            if let Ok(dir) = child.as_dir() {
372                dir.forget();
373            }
374        }
375    }
376}