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, string::{String, ToString}, sync::{Arc, Weak}, vec::Vec
10};
11use spin::RwLock;
12use core::{any::Any, fmt::Debug};
13use core::fmt;
14
15use crate::fs::{FileSystemError, FileSystemErrorKind, FileMetadata, FileObject, FileType};
16
17/// DirectoryEntry structure used by readdir
18#[derive(Debug, Clone)]
19pub struct DirectoryEntryInternal {
20    pub name: String,
21    pub file_type: FileType,
22    pub file_id: u64,
23}
24
25/// Reference to a filesystem instance
26pub type FileSystemRef = Arc<dyn FileSystemOperations>;
27
28/// VfsEntry represents a node in the VFS path hierarchy (similar to Linux dentry)
29/// 
30/// This structure represents the VFS's in-memory filesystem hierarchy graph.
31/// It serves as:
32/// - A "name" representation within a directory
33/// - A "link" that constructs parent-child relationships in the VFS graph
34/// - A cache for fast re-access to already resolved paths
35/// 
36/// VfsEntry is designed to be thread-safe and can be shared across threads.
37pub struct VfsEntry {
38    /// Weak reference to parent VfsEntry (prevents circular references)
39    parent: RwLock<Weak<VfsEntry>>,
40
41    /// Name of this VfsEntry (e.g., "user", "file.txt")
42    name: String,
43
44    /// Reference to the corresponding file entity (VfsNode)
45    node: Arc<dyn VfsNode>,
46
47    /// Cache of child VfsEntries for fast lookup (using Weak to prevent memory leaks)
48    children: RwLock<BTreeMap<String, Weak<VfsEntry>>>,
49}
50
51impl VfsEntry {
52    /// Create a new VfsEntry
53    pub fn new(
54        parent: Option<Weak<VfsEntry>>,
55        name: String,
56        node: Arc<dyn VfsNode>,
57    ) -> Arc<Self> {
58        // Verify that node has filesystem reference when creating VfsEntry
59        debug_assert!(node.filesystem().is_some(), "VfsEntry::new - node.filesystem() is None for name '{}'", name);
60        debug_assert!(node.filesystem().unwrap().upgrade().is_some(), "VfsEntry::new - node.filesystem().upgrade() failed for name '{}'", name);
61        
62        Arc::new(Self {
63            parent: RwLock::new(parent.unwrap_or_else(|| Weak::new())),
64            name,
65            node,
66            children: RwLock::new(BTreeMap::new()),
67        })
68    }
69
70    /// Get the name of this entry
71    pub fn name(&self) -> &String {
72        &self.name
73    }
74
75    /// Get the VfsNode for this entry
76    pub fn node(&self) -> Arc<dyn VfsNode> {
77        Arc::clone(&self.node)
78    }
79
80    /// Get parent VfsEntry if it exists
81    pub fn parent(&self) -> Option<Arc<VfsEntry>> {
82        self.parent.read().upgrade()
83    }
84
85    pub fn set_parent(&self, parent: Weak<VfsEntry>) {
86        *self.parent.write() = parent;
87    }
88
89    /// Add a child to the cache
90    pub fn add_child(self: &Arc<Self>, name: String, child: Arc<VfsEntry>) {
91        child.set_parent(Arc::downgrade(self));
92        let mut children = self.children.write();
93        children.insert(name, Arc::downgrade(&child));
94    }
95
96    /// Get a child from the cache
97    pub fn get_child(&self, name: &String) -> Option<Arc<VfsEntry>> {
98        let mut children = self.children.write();
99        
100        // Try to upgrade the weak reference
101        if let Some(weak_ref) = children.get(name) {
102            if let Some(strong_ref) = weak_ref.upgrade() {
103                return Some(strong_ref);
104            } else {
105                // Clean up dead weak reference
106                children.remove(name);
107            }
108        }
109        
110        None
111    }
112
113    /// Remove a child from the cache
114    pub fn remove_child(&self, name: &String) -> Option<Arc<VfsEntry>> {
115        let mut children = self.children.write();
116        if let Some(weak_ref) = children.remove(name) {
117            weak_ref.upgrade()
118        } else {
119            None
120        }
121    }
122
123    /// Clean up expired weak references in the cache
124    pub fn cleanup_cache(&self) {
125        let mut children = self.children.write();
126        children.retain(|_, weak_ref| weak_ref.strong_count() > 0);
127    }
128}
129
130impl Clone for VfsEntry {
131    fn clone(&self) -> Self {
132         Self {
133            parent: RwLock::new(self.parent.read().clone()),
134            name: self.name.clone(),
135            node: Arc::clone(&self.node),
136            children: RwLock::new(self.children.read().clone()),
137        }
138    }
139}
140
141impl fmt::Debug for VfsEntry {
142    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
143        f.debug_struct("VfsEntry")
144            .field("name", &self.name)
145            .field("node", &self.node)
146            .field("children_count", &self.children.read().len())
147            .finish()
148    }
149}
150
151/// VfsNode trait represents the "entity" interface for files and directories
152///
153/// This trait provides only basic APIs for file/directory attributes, type checks, fs reference, and downcasting.
154/// All operation APIs (lookup, create, remove, open, etc.) are consolidated in FileSystemOperations for clear separation of concerns.
155pub trait VfsNode: Send + Sync + Any {
156    /// Returns the unique identifier in the filesystem
157    fn id(&self) -> u64;
158
159    /// Returns a (Weak) reference to the filesystem this node belongs to
160    fn filesystem(&self) -> Option<Weak<dyn FileSystemOperations>>;
161
162    /// Get metadata for this node
163    fn metadata(&self) -> Result<FileMetadata, FileSystemError>;
164
165    /// Get the file type of this node
166    fn file_type(&self) -> Result<FileType, FileSystemError> {
167        Ok(self.metadata()?.file_type)
168    }
169
170    /// Helper for downcasting
171    fn as_any(&self) -> &dyn Any;
172
173    /// Returns true if this node is a directory
174    fn is_directory(&self) -> Result<bool, FileSystemError> {
175        Ok(self.file_type()? == FileType::Directory)
176    }
177
178    /// Returns true if this node is a symbolic link
179    fn is_symlink(&self) -> Result<bool, FileSystemError> {
180        Ok(matches!(self.file_type()?, FileType::SymbolicLink(_)))
181    }
182
183    /// Read the target of a symbolic link (returns error if not a symlink)
184    fn read_link(&self) -> Result<String, FileSystemError> {
185        Err(FileSystemError::new(
186            crate::fs::FileSystemErrorKind::NotSupported,
187            "Not a symbolic link"
188        ))
189    }
190}
191
192// Impl debug for VfsNode
193impl fmt::Debug for dyn VfsNode {
194    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
195        f.debug_struct("VfsNode")
196            .field("id", &self.id())
197            .field("file_type", &self.file_type().unwrap_or(FileType::Unknown))
198            .field("metadata", &self.metadata())
199            .field("filesystem", &self.filesystem().and_then(|fs| fs.upgrade().map(|fs| fs.name().to_string())))
200            .finish()
201    }
202}
203
204/// FileSystemOperations trait defines the driver API for filesystem operations
205/// 
206/// This trait consolidates filesystem operations that were previously scattered
207/// across different interfaces. It provides a clean contract between VFS and
208/// filesystem drivers.
209pub trait FileSystemOperations: Send + Sync {
210    /// Look up a child node by name within a parent directory
211    /// 
212    /// This is the heart of the new driver API. It takes a parent directory's
213    /// VfsNode and a name, returning the child's VfsNode.
214    fn lookup(
215        &self,
216        parent_node: &Arc<dyn VfsNode>,
217        name: &String,
218    ) -> Result<Arc<dyn VfsNode>, FileSystemError>;
219
220    /// Open a file represented by a VfsNode
221    /// 
222    /// This method takes a VfsNode (file entity) and opens it, returning
223    /// a stateful FileObject for read/write operations.
224    fn open(
225        &self,
226        node: &Arc<dyn VfsNode>,
227        flags: u32,
228    ) -> Result<Arc<dyn FileObject>, FileSystemError>;
229
230    /// Create a new file in the specified directory
231    fn create(
232        &self,
233        parent_node: &Arc<dyn VfsNode>,
234        name: &String,
235        file_type: FileType,
236        mode: u32,
237    ) -> Result<Arc<dyn VfsNode>, FileSystemError>;
238
239    /// Remove a file from the specified directory
240    fn remove(
241        &self,
242        parent_node: &Arc<dyn VfsNode>,
243        name: &String,
244    ) -> Result<(), FileSystemError>;
245
246    /// Read directory entries from a directory node
247    fn readdir(
248        &self,
249        node: &Arc<dyn VfsNode>,
250    ) -> Result<Vec<DirectoryEntryInternal>, FileSystemError>;
251
252    /// Get the root VfsNode for this filesystem
253    fn root_node(&self) -> Arc<dyn VfsNode>;
254
255    /// Get filesystem name
256    fn name(&self) -> &str;
257
258    /// Check if filesystem is read-only
259    fn is_read_only(&self) -> bool {
260        false
261    }
262
263    /// Create a hard link to an existing file
264    /// 
265    /// This method creates a hard link from `link_name` in `link_parent` to the existing
266    /// file represented by `target_node`. Both the link and target will refer to the
267    /// same underlying file data.
268    /// 
269    /// # Arguments
270    /// * `link_parent` - Parent directory where the link will be created
271    /// * `link_name` - Name for the new hard link
272    /// * `target_node` - Existing file to link to
273    /// 
274    /// # Returns
275    /// Returns the VfsNode representing the new hard link on success
276    /// 
277    /// # Errors
278    /// * `NotSupported` - Filesystem doesn't support hard links
279    /// * `InvalidOperation` - Target is a directory (most filesystems don't support directory hard links)
280    /// * `CrossDevice` - Target and link are on different filesystems
281    /// * `FileExists` - Link name already exists in parent directory
282    fn create_hardlink(
283        &self,
284        link_parent: &Arc<dyn VfsNode>,
285        link_name: &String,
286        target_node: &Arc<dyn VfsNode>,
287    ) -> Result<Arc<dyn VfsNode>, FileSystemError> {
288        // Default implementation: not supported
289        let _ = (link_parent, link_name, target_node);
290        Err(FileSystemError::new(
291            FileSystemErrorKind::NotSupported,
292            "Hard links not supported by this filesystem"
293        ))
294    }
295
296}
297
298impl fmt::Debug for dyn FileSystemOperations {
299    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
300        f.debug_struct("FileSystemOperations")
301            .field("name", &self.name())
302            .field("root", &self.root_node())
303            .finish()
304    }
305}