kernel/fs/vfs_v2/
core.rs

1//! Core VFS v2 types and traits
2//!
3//! This module defines the fundamental types and traits for the new VFS architecture:
4//! - VfsEntry: Represents path hierarchy nodes with caching
5//! - VfsNode: Abstract interface for file entities
6//! - FileSystemOperations: Driver API for filesystem operations
7
8use alloc::{
9    collections::BTreeMap,
10    string::{String, ToString},
11    sync::{Arc, Weak},
12    vec::Vec,
13};
14use core::fmt;
15use core::sync::atomic::{AtomicU64, Ordering};
16use core::{any::Any, fmt::Debug};
17use spin::RwLock;
18
19use super::mount_tree::MountPoint;
20use crate::fs::{
21    FileMetadata, FileObject, FileSystemError, FileSystemErrorKind, FileType, SeekFrom,
22};
23use crate::object::capability::selectable::{
24    ReadyInterest, ReadySet, SelectWaitOutcome, Selectable,
25};
26use crate::object::capability::{ControlOps, MemoryMappingOps, StreamError, StreamOps};
27
28/// DirectoryEntry structure used by readdir
29#[derive(Debug, Clone)]
30pub struct DirectoryEntryInternal {
31    pub name: String,
32    pub file_type: FileType,
33    pub file_id: u64,
34}
35
36/// Globally unique identifier for a filesystem instance.
37#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
38pub struct FileSystemId(u64);
39
40impl FileSystemId {
41    /// Generate a new unique FileSystemId.
42    pub fn new() -> Self {
43        static NEXT_ID: AtomicU64 = AtomicU64::new(1);
44        Self(NEXT_ID.fetch_add(1, Ordering::Relaxed))
45    }
46
47    /// Get the raw u64 value.
48    #[inline]
49    pub const fn get(self) -> u64 {
50        self.0
51    }
52}
53
54/// Reference to a filesystem instance
55pub type FileSystemRef = Arc<dyn FileSystemOperations>;
56
57/// VfsEntry represents a node in the VFS path hierarchy (similar to Linux dentry)
58///
59/// This structure represents the VFS's in-memory filesystem hierarchy graph.
60/// It serves as:
61/// - A "name" representation within a directory
62/// - A "link" that constructs parent-child relationships in the VFS graph
63/// - A cache for fast re-access to already resolved paths
64///
65/// VfsEntry is designed to be thread-safe and can be shared across threads.
66pub struct VfsEntry {
67    /// Weak reference to parent VfsEntry (prevents circular references)
68    parent: RwLock<Weak<VfsEntry>>,
69
70    /// Name of this VfsEntry (e.g., "user", "file.txt")
71    name: String,
72
73    /// Reference to the corresponding file entity (VfsNode)
74    node: Arc<dyn VfsNode>,
75
76    /// Cache of child VfsEntries for fast lookup (using Weak to prevent memory leaks)
77    children: RwLock<BTreeMap<String, Weak<VfsEntry>>>,
78}
79
80impl VfsEntry {
81    /// Create a new VfsEntry
82    pub fn new(parent: Option<Weak<VfsEntry>>, name: String, node: Arc<dyn VfsNode>) -> Arc<Self> {
83        // Verify that node has filesystem reference when creating VfsEntry
84        debug_assert!(
85            node.filesystem().is_some(),
86            "VfsEntry::new - node.filesystem() is None for name '{}'",
87            name
88        );
89        debug_assert!(
90            node.filesystem().unwrap().upgrade().is_some(),
91            "VfsEntry::new - node.filesystem().upgrade() failed for name '{}'",
92            name
93        );
94
95        Arc::new(Self {
96            parent: RwLock::new(parent.unwrap_or_else(|| Weak::new())),
97            name,
98            node,
99            children: RwLock::new(BTreeMap::new()),
100        })
101    }
102
103    /// Get the name of this entry
104    pub fn name(&self) -> &String {
105        &self.name
106    }
107
108    /// Get the VfsNode for this entry
109    pub fn node(&self) -> Arc<dyn VfsNode> {
110        Arc::clone(&self.node)
111    }
112
113    /// Get parent VfsEntry if it exists
114    pub fn parent(&self) -> Option<Arc<VfsEntry>> {
115        self.parent.read().upgrade()
116    }
117
118    pub fn set_parent(&self, parent: Weak<VfsEntry>) {
119        *self.parent.write() = parent;
120    }
121
122    /// Add a child to the cache
123    pub fn add_child(self: &Arc<Self>, name: String, child: Arc<VfsEntry>) {
124        child.set_parent(Arc::downgrade(self));
125        let mut children = self.children.write();
126        children.insert(name, Arc::downgrade(&child));
127    }
128
129    /// Get a child from the cache
130    pub fn get_child(&self, name: &String) -> Option<Arc<VfsEntry>> {
131        let mut children = self.children.write();
132
133        // Try to upgrade the weak reference
134        if let Some(weak_ref) = children.get(name) {
135            if let Some(strong_ref) = weak_ref.upgrade() {
136                return Some(strong_ref);
137            } else {
138                // Clean up dead weak reference
139                children.remove(name);
140            }
141        }
142
143        None
144    }
145
146    /// Remove a child from the cache
147    pub fn remove_child(&self, name: &String) -> Option<Arc<VfsEntry>> {
148        let mut children = self.children.write();
149        if let Some(weak_ref) = children.remove(name) {
150            weak_ref.upgrade()
151        } else {
152            None
153        }
154    }
155
156    /// Clean up expired weak references in the cache
157    pub fn cleanup_cache(&self) {
158        let mut children = self.children.write();
159        children.retain(|_, weak_ref| weak_ref.strong_count() > 0);
160    }
161}
162
163impl Clone for VfsEntry {
164    fn clone(&self) -> Self {
165        Self {
166            parent: RwLock::new(self.parent.read().clone()),
167            name: self.name.clone(),
168            node: Arc::clone(&self.node),
169            children: RwLock::new(self.children.read().clone()),
170        }
171    }
172}
173
174impl fmt::Debug for VfsEntry {
175    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
176        f.debug_struct("VfsEntry")
177            .field("name", &self.name)
178            .field("node", &self.node)
179            .field("children_count", &self.children.read().len())
180            .finish()
181    }
182}
183
184/// VfsNode trait represents the "entity" interface for files and directories
185///
186/// This trait provides only basic APIs for file/directory attributes, type checks, fs reference, and downcasting.
187/// All operation APIs (lookup, create, remove, open, etc.) are consolidated in FileSystemOperations for clear separation of concerns.
188pub trait VfsNode: Send + Sync + Any {
189    /// Returns the unique identifier in the filesystem
190    fn id(&self) -> u64;
191
192    /// Returns a (Weak) reference to the filesystem this node belongs to
193    fn filesystem(&self) -> Option<Weak<dyn FileSystemOperations>>;
194
195    /// Get metadata for this node
196    fn metadata(&self) -> Result<FileMetadata, FileSystemError>;
197
198    /// Get the file type of this node
199    fn file_type(&self) -> Result<FileType, FileSystemError> {
200        Ok(self.metadata()?.file_type)
201    }
202
203    /// Helper for downcasting
204    fn as_any(&self) -> &dyn Any;
205
206    /// Returns true if this node is a directory
207    fn is_directory(&self) -> Result<bool, FileSystemError> {
208        Ok(self.file_type()? == FileType::Directory)
209    }
210
211    /// Returns true if this node is a symbolic link
212    fn is_symlink(&self) -> Result<bool, FileSystemError> {
213        Ok(matches!(self.file_type()?, FileType::SymbolicLink(_)))
214    }
215
216    /// Read the target of a symbolic link (returns error if not a symlink)
217    fn read_link(&self) -> Result<String, FileSystemError> {
218        Err(FileSystemError::new(
219            crate::fs::FileSystemErrorKind::NotSupported,
220            "Not a symbolic link",
221        ))
222    }
223}
224
225// Impl debug for VfsNode
226impl fmt::Debug for dyn VfsNode {
227    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
228        f.debug_struct("VfsNode")
229            .field("id", &self.id())
230            .field("file_type", &self.file_type().unwrap_or(FileType::Unknown))
231            .field("metadata", &self.metadata())
232            .field(
233                "filesystem",
234                &self
235                    .filesystem()
236                    .and_then(|fs| fs.upgrade().map(|fs| fs.name().to_string())),
237            )
238            .finish()
239    }
240}
241
242/// FileSystemOperations trait defines the driver API for filesystem operations
243///
244/// This trait consolidates filesystem operations that were previously scattered
245/// across different interfaces. It provides a clean contract between VFS and
246/// filesystem drivers.
247pub trait FileSystemOperations: Send + Sync {
248    /// Get the unique filesystem identifier.
249    ///
250    /// This ID is stable for the lifetime of the filesystem mount and is used
251    /// in combination with file identifiers to create globally unique cache keys.
252    fn fs_id(&self) -> FileSystemId;
253
254    /// Look up a child node by name within a parent directory
255    ///
256    /// This is the heart of the new driver API. It takes a parent directory's
257    /// VfsNode and a name, returning the child's VfsNode.
258    fn lookup(
259        &self,
260        parent_node: &Arc<dyn VfsNode>,
261        name: &String,
262    ) -> Result<Arc<dyn VfsNode>, FileSystemError>;
263
264    /// Open a file represented by a VfsNode
265    ///
266    /// This method takes a VfsNode (file entity) and opens it, returning
267    /// a stateful FileObject for read/write operations.
268    fn open(
269        &self,
270        node: &Arc<dyn VfsNode>,
271        flags: u32,
272    ) -> Result<Arc<dyn FileObject>, FileSystemError>;
273
274    /// Create a new file in the specified directory
275    fn create(
276        &self,
277        parent_node: &Arc<dyn VfsNode>,
278        name: &String,
279        file_type: FileType,
280        mode: u32,
281    ) -> Result<Arc<dyn VfsNode>, FileSystemError>;
282
283    /// Remove a file from the specified directory
284    fn remove(&self, parent_node: &Arc<dyn VfsNode>, name: &String) -> Result<(), FileSystemError>;
285
286    /// Read directory entries from a directory node
287    fn readdir(
288        &self,
289        node: &Arc<dyn VfsNode>,
290    ) -> Result<Vec<DirectoryEntryInternal>, FileSystemError>;
291
292    /// Get the root VfsNode for this filesystem
293    fn root_node(&self) -> Arc<dyn VfsNode>;
294
295    /// Get filesystem name
296    fn name(&self) -> &str;
297
298    /// Check if filesystem is read-only
299    fn is_read_only(&self) -> bool {
300        false
301    }
302
303    /// Access to Any trait for downcasting
304    fn as_any(&self) -> &dyn Any;
305
306    /// Create a hard link to an existing file
307    ///
308    /// This method creates a hard link from `link_name` in `link_parent` to the existing
309    /// file represented by `target_node`. Both the link and target will refer to the
310    /// same underlying file data.
311    ///
312    /// # Arguments
313    /// * `link_parent` - Parent directory where the link will be created
314    /// * `link_name` - Name for the new hard link
315    /// * `target_node` - Existing file to link to
316    ///
317    /// # Returns
318    /// Returns the VfsNode representing the new hard link on success
319    ///
320    /// # Errors
321    /// * `NotSupported` - Filesystem doesn't support hard links
322    /// * `InvalidOperation` - Target is a directory (most filesystems don't support directory hard links)
323    /// * `CrossDevice` - Target and link are on different filesystems
324    /// * `FileExists` - Link name already exists in parent directory
325    fn create_hardlink(
326        &self,
327        link_parent: &Arc<dyn VfsNode>,
328        link_name: &String,
329        target_node: &Arc<dyn VfsNode>,
330    ) -> Result<Arc<dyn VfsNode>, FileSystemError> {
331        // Default implementation: not supported
332        let _ = (link_parent, link_name, target_node);
333        Err(FileSystemError::new(
334            FileSystemErrorKind::NotSupported,
335            "Hard links not supported by this filesystem",
336        ))
337    }
338}
339
340impl fmt::Debug for dyn FileSystemOperations {
341    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
342        f.debug_struct("FileSystemOperations")
343            .field("name", &self.name())
344            .field("root", &self.root_node())
345            .finish()
346    }
347}
348
349/// VfsFileObject wraps a filesystem-specific FileObject with VFS-layer information
350///
351/// This wrapper provides the VFS layer with access to path hierarchy information
352/// while delegating actual file operations to the underlying FileSystem implementation.
353pub struct VfsFileObject {
354    /// The underlying FileObject from the filesystem implementation
355    inner: Arc<dyn FileObject>,
356    /// The VfsEntry this FileObject was created from (for *at syscalls)
357    vfs_entry: Arc<VfsEntry>,
358    /// The mount point containing this VfsEntry
359    mount_point: Arc<MountPoint>,
360    /// The original path used to open this file (for debugging/logging)
361    original_path: String,
362}
363
364impl VfsFileObject {
365    /// Create a new VfsFileObject
366    pub fn new(
367        inner: Arc<dyn FileObject>,
368        vfs_entry: Arc<VfsEntry>,
369        mount_point: Arc<MountPoint>,
370        original_path: String,
371    ) -> Self {
372        Self {
373            inner,
374            vfs_entry,
375            mount_point,
376            original_path,
377        }
378    }
379
380    /// Get the VfsEntry this FileObject was created from
381    pub fn get_vfs_entry(&self) -> &Arc<VfsEntry> {
382        &self.vfs_entry
383    }
384
385    /// Get the mount point containing this VfsEntry
386    pub fn get_mount_point(&self) -> &Arc<MountPoint> {
387        &self.mount_point
388    }
389
390    /// Get the original path used to open this file
391    pub fn get_original_path(&self) -> &str {
392        &self.original_path
393    }
394
395    /// Enable downcasting for VfsFileObject detection
396    pub fn as_any(&self) -> &dyn Any {
397        self
398    }
399}
400
401impl StreamOps for VfsFileObject {
402    fn read(&self, buffer: &mut [u8]) -> Result<usize, StreamError> {
403        self.inner.read(buffer)
404    }
405
406    fn write(&self, buffer: &[u8]) -> Result<usize, StreamError> {
407        self.inner.write(buffer)
408    }
409}
410
411impl ControlOps for VfsFileObject {
412    fn control(&self, command: u32, arg: usize) -> Result<i32, &'static str> {
413        self.inner.control(command, arg)
414    }
415}
416
417impl MemoryMappingOps for VfsFileObject {
418    fn get_mapping_info(
419        &self,
420        offset: usize,
421        length: usize,
422    ) -> Result<(usize, usize, bool), &'static str> {
423        self.inner.get_mapping_info(offset, length)
424    }
425
426    fn on_mapped(&self, vaddr: usize, paddr: usize, length: usize, offset: usize) {
427        self.inner.on_mapped(vaddr, paddr, length, offset);
428    }
429
430    fn on_unmapped(&self, vaddr: usize, length: usize) {
431        self.inner.on_unmapped(vaddr, length);
432    }
433
434    fn supports_mmap(&self) -> bool {
435        self.inner.supports_mmap()
436    }
437
438    fn mmap_owner_name(&self) -> alloc::string::String {
439        alloc::format!("vfs:{}", self.get_original_path())
440    }
441}
442
443impl FileObject for VfsFileObject {
444    fn read_at(&self, offset: u64, buffer: &mut [u8]) -> Result<usize, StreamError> {
445        self.inner.read_at(offset, buffer)
446    }
447
448    fn write_at(&self, offset: u64, buffer: &[u8]) -> Result<usize, StreamError> {
449        self.inner.write_at(offset, buffer)
450    }
451
452    fn seek(&self, whence: SeekFrom) -> Result<u64, StreamError> {
453        self.inner.seek(whence)
454    }
455
456    fn metadata(&self) -> Result<FileMetadata, StreamError> {
457        self.inner.metadata()
458    }
459
460    fn truncate(&self, size: u64) -> Result<(), StreamError> {
461        self.inner.truncate(size)
462    }
463
464    fn as_any(&self) -> &dyn Any {
465        self
466    }
467}
468
469impl Selectable for VfsFileObject {
470    fn current_ready(&self, interest: ReadyInterest) -> ReadySet {
471        self.inner.current_ready(interest)
472    }
473
474    fn wait_until_ready(
475        &self,
476        interest: ReadyInterest,
477        trapframe: &mut crate::arch::Trapframe,
478        timeout_ticks: Option<u64>,
479    ) -> SelectWaitOutcome {
480        self.inner
481            .wait_until_ready(interest, trapframe, timeout_ticks)
482    }
483
484    fn set_nonblocking(&self, enabled: bool) {
485        self.inner.set_nonblocking(enabled)
486    }
487
488    fn is_nonblocking(&self) -> bool {
489        self.inner.is_nonblocking()
490    }
491}