kernel/fs/vfs_v2/
manager.rs

1//! VFS Manager v2 - Enhanced Virtual File System Management
2//!
3//! This module provides the next-generation VFS management system for Scarlet,
4//! built on the improved VFS v2 architecture with enhanced mount tree management,
5//! VfsEntry-based caching, and better isolation support.
6
7use alloc::{
8    string::{String, ToString},
9    sync::Arc,
10    vec,
11    vec::Vec,
12};
13use spin::{RwLock, Once};
14
15use crate::fs::{
16    FileSystemError, FileSystemErrorKind, FileMetadata, FileType, 
17    DeviceFileInfo
18};
19use crate::object::KernelObject;
20
21use super::{
22    core::{VfsEntry, FileSystemOperations, DirectoryEntryInternal},
23    mount_tree::{MountTree, MountOptionsV2, MountPoint, VfsManagerId},
24};
25
26/// Filesystem ID type
27pub type FSId = u64;
28
29/// Path resolution options for VFS operations
30#[derive(Debug, Clone)]
31pub struct PathResolutionOptions {
32    /// Don't follow symbolic links in the final component (like lstat behavior)
33    pub no_follow: bool,
34}
35
36impl PathResolutionOptions {
37    /// Create options with no_follow flag set (don't follow final symlink)
38    pub fn no_follow() -> Self {
39        Self {
40            no_follow: true,
41        }
42    }
43}
44
45impl Default for PathResolutionOptions {
46    fn default() -> Self {
47        Self {
48            no_follow: false,
49        }
50    }
51}
52
53// Helper function to create FileSystemError
54fn vfs_error(kind: FileSystemErrorKind, message: &str) -> FileSystemError {
55    FileSystemError::new(kind, message)
56}
57
58/// VFS Manager v2 - Enhanced VFS architecture implementation
59/// 
60/// This manager provides advanced VFS functionality with proper mount tree
61/// management, enhanced caching, and better support for containerization.
62pub struct VfsManager {
63    /// Unique identifier for this VfsManager instance
64    pub id: VfsManagerId,
65    /// Mount tree for hierarchical mount point management
66    pub mount_tree: MountTree,
67    /// Current working directory
68    pub cwd: RwLock<Option<Arc<VfsEntry>>>,
69    /// Strong references to all currently mounted filesystems
70    pub mounted_filesystems: RwLock<Vec<Arc<dyn FileSystemOperations>>>,
71}
72
73static GLOBAL_VFS_MANAGER: Once<Arc<VfsManager>> = Once::new();
74
75impl VfsManager {
76    /// Create a new VFS manager instance with a dummy root
77    pub fn new() -> Self {
78        // Create a dummy root filesystem for initialization
79        use super::drivers::tmpfs::TmpFS;
80        let root_fs: Arc<dyn FileSystemOperations> = TmpFS::new(0); // 0 = unlimited memory
81        let root_node = root_fs.root_node();
82        let dummy_root_entry = VfsEntry::new(None, "/".to_string(), root_node);
83        
84        let mount_tree = MountTree::new(dummy_root_entry.clone());
85        
86        Self {
87            id: VfsManagerId::new(),
88            mount_tree,
89            cwd: RwLock::new(None),
90            mounted_filesystems: RwLock::new(vec![root_fs.clone()]),
91        }
92    }
93
94    /// Create a new VFS manager instance with a specified root filesystem
95    pub fn new_with_root(root_fs: Arc<dyn FileSystemOperations>) -> Self {
96        let root_node = root_fs.root_node();
97        let dummy_root_entry = VfsEntry::new(None, "/".to_string(), root_node);
98        let mount_tree = MountTree::new(dummy_root_entry.clone());
99        Self {
100            id: VfsManagerId::new(),
101            mount_tree,
102            cwd: RwLock::new(None),
103            mounted_filesystems: RwLock::new(vec![root_fs.clone()]),
104        }
105    }
106    
107    /// Mount a filesystem at the specified path
108    /// 
109    /// This will mount the given filesystem at the specified mount point.
110    /// If the mount point is "/", it will replace the root filesystem.
111    /// 
112    /// # Arguments
113    /// * `filesystem` - The filesystem to mount.
114    /// * `mount_point_str` - The path where the filesystem should be mounted.
115    /// * `flags` - Flags for the mount operation (e.g., read-only).
116    /// 
117    /// # Errors
118    /// Returns an error if the mount point is invalid, the filesystem cannot be mounted,
119    /// or if the mount operation fails.
120    /// 
121    pub fn mount(
122        &self,
123        filesystem: Arc<dyn FileSystemOperations>,
124        mount_point_str: &str,
125        flags: u32,
126    ) -> Result<(), FileSystemError> {
127        if mount_point_str == "/" {
128            // Remove the existing root FS from the list
129            let old_root_fs = self.mount_tree.root_mount.read().root.node().filesystem()
130                .and_then(|w| w.upgrade());
131            if let Some(old_fs) = old_root_fs {
132                let old_ptr = Arc::as_ptr(&old_fs) as *const () as usize;
133                self.mounted_filesystems.write().retain(|fs| Arc::as_ptr(fs) as *const () as usize != old_ptr);
134            }
135            // Set the new root
136            let new_root_node = filesystem.root_node();
137            let new_root_entry = VfsEntry::new(None, "/".to_string(), new_root_node);
138            let new_root_mount = MountPoint::new_regular("/".to_string(), new_root_entry);
139            self.mount_tree.replace_root(new_root_mount);
140            // Push the new FS
141            self.mounted_filesystems.write().push(filesystem.clone());
142            return Ok(());
143        }
144        let _mount_options = MountOptionsV2 {
145            readonly: (flags & 0x01) != 0,
146            flags,
147        };
148        let (target_entry, target_mount_point) = self.mount_tree.resolve_path(mount_point_str)?;
149        self.mount_tree.mount(target_entry, target_mount_point, filesystem.clone())?;
150        self.mounted_filesystems.write().push(filesystem);
151        Ok(())
152    }
153
154    /// Unmount a mount point at the specified path
155    /// 
156    /// This will remove the mount point from the mount tree and clean up any
157    /// associated resources.
158    /// 
159    /// # Arguments
160    /// * `mount_point_str` - The path of the mount point to unmount.
161    /// 
162    /// # Errors
163    /// Returns an error if the mount point is not valid or if the unmount operation fails.
164    /// 
165    pub fn unmount(&self, mount_point_str: &str) -> Result<(), FileSystemError> {
166        let (entry, mount_point) = self.mount_tree.resolve_mount_point(mount_point_str)?;
167        if !self.mount_tree.is_mount_point(&entry, &mount_point) {
168            return Err(vfs_error(FileSystemErrorKind::InvalidPath, "Path is not a mount point"));
169        }
170        let unmounted_mount = self.mount_tree.unmount(&entry, &mount_point)?;
171        // Identify the unmounted fs and remove it from the holding list
172        // If mount_point is a bind mount, we do not remove the filesystem
173        if !unmounted_mount.is_bind_mount() {
174            if let Some(fs) = unmounted_mount.root.node().filesystem().unwrap().upgrade() {
175                let fs_ptr = Arc::as_ptr(&fs) as *const () as usize;
176                self.mounted_filesystems.write().retain(|fs| Arc::as_ptr(fs) as *const () as usize != fs_ptr);
177            }
178        }
179        Ok(())
180    }
181
182    /// Bind mount a directory from source_path to target_path
183    /// 
184    /// This will create a bind mount where the source directory is mounted
185    /// at the target path.
186    /// 
187    /// # Arguments
188    /// * `source_path` - The path of the source directory to bind mount.
189    /// * `target_path` - The path where the source directory should be mounted.
190    /// 
191    /// # Errors
192    /// Returns an error if the source is not a directory, the target is already a mount point,
193    /// or if the source is not a valid directory.
194    /// 
195    pub fn bind_mount(
196        &self,
197        source_path: &str,
198        target_path: &str
199    ) -> Result<(), FileSystemError> {
200        // Resolve the target mount point
201        let (target_entry, target_mount_point) = self.mount_tree.resolve_path(target_path)?;
202        // Resolve the source entry
203        let (source_entry, source_mount_point) = self.mount_tree.resolve_path(source_path)?;
204        // Check if source is a valid entry
205        if !source_entry.node().is_directory()? {
206            return Err(vfs_error(FileSystemErrorKind::NotADirectory, "Source path must be a directory"));
207        }
208        // Check if target is not already a mount point
209        if self.mount_tree.is_mount_point(&target_entry, &target_mount_point) {
210            return Err(vfs_error(FileSystemErrorKind::InvalidPath, "Target path is already a mount point"));
211        }
212        // Check if source is a directory (bind mounts only support directories)
213        if !source_entry.node().is_directory()? {
214            return Err(vfs_error(FileSystemErrorKind::NotADirectory, "Source path must be a directory"));
215        }
216        // Create the bind mount entry
217        self.bind_mount_entry(
218            source_entry,
219            source_mount_point,
220            target_entry,
221            target_mount_point
222        )
223    }
224
225    /// Bind mount a directory from another VFS instance
226    /// 
227    /// This will create a bind mount where the source directory from another VFS
228    /// is mounted at the target path in this VFS.
229    /// 
230    /// # Arguments
231    /// * `source_vfs` - The source VFS instance containing the directory to bind
232    /// * `source_path` - The path of the source directory in the source VFS.
233    /// * `target_path` - The path where the source directory should be mounted in this
234    /// VFS.
235    /// 
236    /// # Errors
237    /// Returns an error if the source path does not exist, the target is already a mount point,
238    /// or if the source is not a valid directory.
239    /// 
240    pub fn bind_mount_from(
241        &self,
242        source_vfs: &Arc<VfsManager>,
243        source_path: &str,
244        target_path: &str,
245    ) -> Result<(), FileSystemError> {
246        // Resolve the source and target paths
247        let (source_entry, source_mount_point) = source_vfs.mount_tree.resolve_path(source_path)?;
248        let (target_entry, target_mount_point) = self.mount_tree.resolve_path(target_path)?;
249
250        // Create the bind mount entry
251        self.bind_mount_entry(
252            source_entry,
253            source_mount_point,
254            target_entry,
255            target_mount_point
256        )
257    }
258
259    /// Create a bind mount from source_entry to target_entry
260    fn bind_mount_entry(
261        &self,
262        source_entry: Arc<VfsEntry>,
263        source_mount_point: Arc<MountPoint>,
264        target_entry: Arc<VfsEntry>,
265        target_mount_point: Arc<MountPoint>,
266    ) -> Result<(), FileSystemError> {
267        // Create a new MountPoint for the bind mount
268        let bind_mount = MountPoint::new_bind(target_entry.name().clone(), source_entry);
269        // Set parent/parent_entry
270        unsafe {
271            let mut_ptr = Arc::as_ptr(&bind_mount) as *mut MountPoint;
272            (*mut_ptr).parent = Some(Arc::downgrade(&target_mount_point));
273            (*mut_ptr).parent_entry = Some(target_entry.clone());
274        }
275        // Connect the bind mount to the source mount point
276        *(bind_mount.children.write()) = source_mount_point.children.read().clone();
277        // Add as child to target_mount_point
278        target_mount_point.children.write().insert(target_entry.node().id(), bind_mount);
279        Ok(())
280    }
281    
282    /// Open a file at the specified path
283    /// 
284    /// This will resolve the path using the MountTreeV2 and open the file
285    /// using the filesystem associated with the resolved VfsEntry.
286    /// 
287    /// # Arguments
288    /// * `path` - The path of the file to open.
289    /// * `flags` - Flags for opening the file (e.g., read, write
290    /// * `O_CREAT`, etc.).
291    /// 
292    /// # Errors
293    /// Returns an error if the path does not exist, is not a file, or if
294    /// the filesystem cannot be resolved.
295    /// 
296    pub fn open(&self, path: &str, flags: u32) -> Result<KernelObject, FileSystemError> {
297        // Use MountTreeV2 to resolve filesystem and relative path, then open
298        let entry = self.mount_tree.resolve_path(path)?.0;
299        let node = entry.node();
300        let filesystem = node.filesystem()
301            .and_then(|w| w.upgrade())
302            .ok_or_else(|| FileSystemError::new(FileSystemErrorKind::NotSupported, "No filesystem reference"))?;
303        let file_obj = filesystem.open(&node, flags)?;
304        Ok(KernelObject::File(file_obj))
305    }
306    
307    /// Create a file at the specified path
308    /// 
309    /// This will create a new file in the filesystem at the given path.
310    ///
311    /// # Arguments
312    /// * `path` - The path where the file should be created.
313    /// * `file_type` - The type of file to create (e.g., regular
314    /// file, directory, etc.).
315    /// 
316    /// # Errors
317    /// Returns an error if the parent directory does not exist, the filesystem cannot be resolved,
318    /// or if the file cannot be created.
319    /// 
320    pub fn create_file(&self, path: &str, file_type: FileType) -> Result<(), FileSystemError> {
321        // Split path into parent and filename
322        let (parent_path, filename) = self.split_parent_child(path)?;
323        
324        // Resolve parent directory using MountTreeV2
325        let parent_entry = self.mount_tree.resolve_path(&parent_path)?.0;
326        let parent_node = parent_entry.node();
327        debug_assert!(parent_node.filesystem().is_some(), "VfsManager::create_file - parent_node.filesystem() is None for path '{}'", parent_path);
328        
329        // Create file using filesystem
330        let filesystem = parent_node.filesystem()
331            .and_then(|w| w.upgrade())
332            .ok_or_else(|| FileSystemError::new(FileSystemErrorKind::NotSupported, "No filesystem reference"))?;
333        let new_node = filesystem.create(
334            &parent_node,
335            &filename,
336            file_type,
337            0o644, // Default permissions
338        )?;
339        
340        // Create VfsEntry and add to parent cache
341        let new_entry = VfsEntry::new(
342            Some(Arc::downgrade(&parent_entry)),
343            filename.clone(),
344            new_node,
345        );
346        
347        
348        parent_entry.add_child(filename, new_entry);
349    
350        
351        Ok(())
352    }
353    
354    /// Create a directory at the specified path
355    /// 
356    /// This will create a new directory in the filesystem at the given path.
357    /// 
358    /// # Arguments
359    /// * `path` - The path where the directory should be created.
360    /// 
361    /// # Errors
362    /// Returns an error if the parent directory does not exist, the filesystem cannot be resolved,
363    /// or if the directory cannot be created.
364    /// 
365    pub fn create_dir(&self, path: &str) -> Result<(), FileSystemError> {
366        self.create_file(path, FileType::Directory)
367    }
368    
369    /// Create a symbolic link at the specified path
370    /// 
371    /// This will create a new symbolic link in the filesystem at the given path,
372    /// pointing to the specified target path.
373    /// 
374    /// # Arguments
375    /// * `path` - The path where the symbolic link should be created.
376    /// * `target_path` - The path that the symbolic link should point to.
377    /// 
378    /// # Errors
379    /// Returns an error if the parent directory does not exist, the filesystem cannot be resolved,
380    /// or if the symbolic link cannot be created.
381    /// 
382    pub fn create_symlink(&self, path: &str, target_path: &str) -> Result<(), FileSystemError> {
383        self.create_file(path, FileType::SymbolicLink(target_path.to_string()))
384    }
385    
386    /// Remove a file at the specified path
387    /// 
388    /// This will remove the file from the filesystem and update the mount tree.
389    /// 
390    /// # Arguments
391    /// * `path` - The path of the file to remove.
392    /// 
393    /// # Errors
394    /// Returns an error if the path does not exist, is not a file, or if
395    /// the filesystem cannot be resolved.
396    /// 
397    pub fn remove(&self, path: &str) -> Result<(), FileSystemError> {
398        // Resolve the entry to be removed - use no_follow to follow intermediate symlinks
399        // but not the final component (like POSIX rm behavior)
400        let options = PathResolutionOptions::no_follow();
401        let (entry_to_remove, mount_point) = self.mount_tree.resolve_path_with_options(path, &options)?;
402
403        // Check if the entry is involved in any mount, which would make it busy
404        if self.mount_tree.is_entry_used_in_mount(&entry_to_remove, &mount_point) {
405            return Err(vfs_error(FileSystemErrorKind::NotSupported, "Resource is busy"));
406        }
407
408        // Split path into parent and filename
409        let (parent_path, filename) = self.split_parent_child(path)?;
410        
411        // Resolve parent directory using MountTreeV2 (follow all symlinks for parent path)
412        let parent_entry = self.mount_tree.resolve_path(&parent_path)?.0;
413        let parent_node = parent_entry.node();
414        
415        // Remove from filesystem
416        let filesystem = parent_node.filesystem()
417            .and_then(|w| w.upgrade())
418            .ok_or_else(|| FileSystemError::new(FileSystemErrorKind::NotSupported, "No filesystem reference"))?;
419        filesystem.remove(&parent_node, &filename)?;
420        
421        // Remove from parent cache
422        let _ = parent_entry.remove_child(&filename);
423
424        Ok(())
425    }
426    
427    /// Get metadata for a file at the specified path
428    /// 
429    /// This will resolve the path using the MountTreeV2 and return the metadata
430    /// for the file represented by the resolved VfsEntry.
431    /// 
432    /// # Arguments
433    /// * `path` - The path of the file to get metadata for.
434    /// 
435    /// # Errors
436    /// Returns an error if the path does not exist, is not a file, or if
437    /// the filesystem cannot be resolved.
438    /// 
439    pub fn metadata(&self, path: &str) -> Result<FileMetadata, FileSystemError> {
440        // Resolve path to VfsEntry
441        let entry = self.mount_tree.resolve_path(path)?.0;
442        
443        // Get VfsNode and return metadata
444        let node = entry.node();
445        
446        node.metadata()
447    }
448    
449    /// Read directory entries at the specified path
450    /// 
451    /// This will resolve the path using the MountTreeV2 and return a list of
452    /// directory entries for the directory represented by the resolved VfsEntry.
453    /// 
454    /// # Arguments
455    /// * `path` - The path of the directory to read.
456    /// 
457    /// # Errors
458    /// Returns an error if the path does not exist, is not a directory, or if
459    /// the filesystem cannot be resolved.
460    /// 
461    pub fn readdir(&self, path: &str) -> Result<Vec<DirectoryEntryInternal>, FileSystemError> {
462        // Resolve path to VfsEntry
463        let entry = self.mount_tree.resolve_path(path)?.0;
464        
465        // Get VfsNode
466        let node = entry.node();
467        
468        // Check if it's a directory
469        if !node.is_directory()? {
470            return Err(FileSystemError::new(
471                FileSystemErrorKind::NotADirectory,
472                "Not a directory"
473            ));
474        }
475        
476        // Get filesystem from node
477        let fs_ref = node.filesystem()
478            .ok_or_else(|| FileSystemError::new(
479                FileSystemErrorKind::NotSupported,
480                "Node has no filesystem reference"
481            ))?;
482            
483        let filesystem = fs_ref.upgrade()
484            .ok_or_else(|| FileSystemError::new(
485                FileSystemErrorKind::NotSupported,
486                "Filesystem reference is dead"
487            ))?;
488        
489        // Call filesystem's readdir
490        filesystem.readdir(&node)
491    }
492    
493    /// Set current working directory
494    /// 
495    /// This will change the current working directory to the specified path.
496    /// 
497    /// # Arguments
498    /// * `path` - The path to set as the current working directory.
499    /// 
500    /// # Errors
501    /// Returns an error if the path does not exist, is not a directory, or if
502    /// the filesystem cannot be resolved.
503    /// 
504    pub fn set_cwd(&self, path: &str) -> Result<(), FileSystemError> {
505        let entry = self.mount_tree.resolve_path(path)?.0;
506        
507        // Verify it's a directory
508        let node = entry.node();
509        
510        if !node.is_directory()? {
511            return Err(FileSystemError::new(
512                FileSystemErrorKind::NotADirectory,
513                "Not a directory"
514            ));
515        }
516        
517        *self.cwd.write() = Some(entry);
518        Ok(())
519    }
520    
521    /// Get current working directory
522    /// 
523    /// This returns the current working directory as an `Arc<VfsEntry>`.
524    /// 
525    /// If the current working directory is not set, it returns `None`.
526    ///
527    /// # Returns
528    /// An `Option<Arc<VfsEntry>>` containing the current working directory entry,
529    /// or `None` if the current working directory is not set.
530    /// 
531    pub fn get_cwd(&self) -> Option<Arc<VfsEntry>> {
532        self.cwd.read().clone()
533    }
534    
535    /// Create a device file
536    /// 
537    /// This will create a new device file in the filesystem at the given path.
538    /// 
539    /// # Arguments
540    /// * `path` - The path where the device file should be created.
541    /// * `device_info` - Information about the device file to create (e.g.,
542    /// device type, major/minor numbers, etc.).
543    /// 
544    /// # Errors
545    /// Returns an error if the parent directory does not exist, the filesystem cannot be resolved,
546    /// or if the device file cannot be created.
547    /// 
548    pub fn create_device_file(
549        &self,
550        path: &str,
551        device_info: DeviceFileInfo,
552    ) -> Result<(), FileSystemError> {
553        let file_type = match device_info.device_type {
554            crate::device::DeviceType::Char => FileType::CharDevice(device_info),
555            crate::device::DeviceType::Block => FileType::BlockDevice(device_info),
556            _ => return Err(FileSystemError::new(
557                FileSystemErrorKind::NotSupported,
558                "Unsupported device type"
559            )),
560        };
561        
562        self.create_file(path, file_type)
563    }
564
565    pub fn resolve_path(&self, path: &str) -> Result<Arc<VfsEntry>, FileSystemError> {
566        self.resolve_path_with_options(path, &PathResolutionOptions::default())
567    }
568
569    /// Resolve a path with specified options
570    pub fn resolve_path_with_options(&self, path: &str, options: &PathResolutionOptions) -> Result<Arc<VfsEntry>, FileSystemError> {
571        // Use MountTreeV2 to resolve the path with options
572        let (entry, _mount_point) = self.mount_tree.resolve_path_with_options(path, options)?;
573        Ok(entry)
574    }
575    
576    /// Create a hard link
577    /// 
578    /// This will create a hard link where the source file is linked to the target path.
579    /// Both paths will refer to the same underlying file data.
580    /// 
581    /// # Arguments
582    /// * `source_path` - Path of the existing file to link to
583    /// * `target_path` - Path where the hard link should be created
584    /// 
585    /// # Errors
586    /// Returns an error if the source doesn't exist, target already exists, 
587    /// filesystems don't match, or hard links aren't supported.
588    /// 
589    pub fn create_hardlink(
590        &self,
591        source_path: &str,
592        target_path: &str,
593    ) -> Result<(), FileSystemError> {
594        // Resolve source file
595        let (source_entry, _source_mount) = self.mount_tree.resolve_path(source_path)?;
596        let source_node = source_entry.node();
597        
598        // Check that source is a regular file (most filesystems don't support directory hard links)
599        if source_node.is_directory()? {
600            return Err(vfs_error(
601                FileSystemErrorKind::InvalidOperation, 
602                "Cannot create hard link to directory"
603            ));
604        }
605        
606        // Split target path into parent and filename
607        let (target_parent_path, target_filename) = self.split_parent_child(target_path)?;
608        
609        // Resolve target parent directory
610        let (target_parent_entry, _target_mount) = self.mount_tree.resolve_path(&target_parent_path)?;
611        let target_parent_node = target_parent_entry.node();
612        
613        // Check that target parent is a directory
614        if !target_parent_node.is_directory()? {
615            return Err(vfs_error(
616                FileSystemErrorKind::NotADirectory,
617                "Target parent is not a directory"
618            ));
619        }
620        
621        // Get filesystems for both source and target
622        let source_fs = source_node.filesystem()
623            .and_then(|w| w.upgrade())
624            .ok_or_else(|| FileSystemError::new(FileSystemErrorKind::NotSupported, "No filesystem reference for source"))?;
625        
626        let target_fs = target_parent_node.filesystem()
627            .and_then(|w| w.upgrade())
628            .ok_or_else(|| FileSystemError::new(FileSystemErrorKind::NotSupported, "No filesystem reference for target"))?;
629        
630        // Check that both files are on the same filesystem (hard links can't cross filesystem boundaries)
631        if !Arc::ptr_eq(&source_fs, &target_fs) {
632            return Err(vfs_error(
633                FileSystemErrorKind::CrossDevice,
634                "Hard links cannot cross filesystem boundaries"
635            ));
636        }
637        
638        // Check if target already exists
639        if target_parent_entry.get_child(&target_filename).is_some() {
640            return Err(vfs_error(
641                FileSystemErrorKind::FileExists,
642                "Target file already exists"
643            ));
644        }
645        
646        // Create the hard link
647        let link_node = source_fs.create_hardlink(&target_parent_node, &target_filename, &source_node)?;
648        
649        // Create VfsEntry and add to parent cache
650        let link_entry = VfsEntry::new(
651            Some(Arc::downgrade(&target_parent_entry)),
652            target_filename.clone(),
653            link_node,
654        );
655        target_parent_entry.add_child(target_filename, link_entry);
656        
657        Ok(())
658    }
659
660    // Helper methods
661    
662    /// Split a path into parent directory and filename
663    fn split_parent_child(&self, path: &str) -> Result<(String, String), FileSystemError> {
664        // Simple path normalization: remove trailing slash except for root
665        let normalized = if path != "/" && path.ends_with('/') {
666            path.trim_end_matches('/').to_string()
667        } else {
668            path.to_string()
669        };
670        
671        if normalized == "/" {
672            return Err(FileSystemError::new(
673                FileSystemErrorKind::InvalidPath,
674                "Cannot split root path"
675            ));
676        }
677        
678        if let Some(last_slash) = normalized.rfind('/') {
679            let parent = if last_slash == 0 {
680                "/".to_string()
681            } else {
682                normalized[..last_slash].to_string()
683            };
684            let filename = normalized[last_slash + 1..].to_string();
685            Ok((parent, filename))
686        } else {
687            Err(FileSystemError::new(
688                FileSystemErrorKind::InvalidPath,
689                "Invalid path format"
690            ))
691        }
692    }
693}
694
695/// Initialize the global VFS manager (Arc) so it can be retrieved later
696pub fn init_global_vfs_manager() -> Arc<VfsManager> {
697    GLOBAL_VFS_MANAGER.call_once(|| Arc::new(VfsManager::new())).clone()
698}
699
700/// Retrieve the global VFS manager (Arc)
701pub fn get_global_vfs_manager() -> Arc<VfsManager> {
702    GLOBAL_VFS_MANAGER.get().expect("global VFS manager not initialized").clone()
703}
704