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::{Once, RwLock};
14
15use crate::fs::{DeviceFileInfo, FileMetadata, FileSystemError, FileSystemErrorKind, FileType};
16use crate::object::KernelObject;
17
18use super::{
19    core::{DirectoryEntryInternal, FileSystemOperations, VfsEntry},
20    mount_tree::{
21        MountOptionsV2, MountPoint, MountTree, MountType, VfsEntryRef, VfsManagerId, VfsResult,
22    },
23};
24
25/// Filesystem ID type
26pub type FSId = u64;
27
28/// Path resolution options for VFS operations
29#[derive(Debug, Clone)]
30pub struct PathResolutionOptions {
31    /// Don't follow symbolic links in the final component (like lstat behavior)
32    pub no_follow: bool,
33}
34
35impl PathResolutionOptions {
36    /// Create options with no_follow flag set (don't follow final symlink)
37    pub fn no_follow() -> Self {
38        Self { no_follow: true }
39    }
40}
41
42impl Default for PathResolutionOptions {
43    fn default() -> Self {
44        Self { no_follow: false }
45    }
46}
47
48// Helper function to create FileSystemError
49fn vfs_error(kind: FileSystemErrorKind, message: &str) -> FileSystemError {
50    FileSystemError::new(kind, message)
51}
52
53/// VFS Manager v2 - Enhanced VFS architecture implementation
54///
55/// This manager provides advanced VFS functionality with proper mount tree
56/// management, enhanced caching, and better support for containerization.
57pub struct VfsManager {
58    /// Unique identifier for this VfsManager instance
59    pub id: VfsManagerId,
60    /// Mount tree for hierarchical mount point management
61    pub mount_tree: MountTree,
62    /// Current working directory: (VfsEntry, MountPoint) pair
63    pub cwd: RwLock<Option<(Arc<VfsEntry>, Arc<MountPoint>)>>,
64    /// Strong references to all currently mounted filesystems
65    pub mounted_filesystems: RwLock<Vec<Arc<dyn FileSystemOperations>>>,
66}
67
68static GLOBAL_VFS_MANAGER: Once<Arc<VfsManager>> = Once::new();
69
70impl VfsManager {
71    /// Create a new VFS manager instance with a dummy root
72    pub fn new() -> Self {
73        // Create a dummy root filesystem for initialization
74        use super::drivers::tmpfs::TmpFS;
75        let root_fs: Arc<dyn FileSystemOperations> = TmpFS::new(0); // 0 = unlimited memory
76        let root_node = root_fs.root_node();
77        let dummy_root_entry = VfsEntry::new(None, "/".to_string(), root_node);
78
79        let mount_tree = MountTree::new(dummy_root_entry.clone(), root_fs.clone());
80
81        Self {
82            id: VfsManagerId::new(),
83            mount_tree,
84            cwd: RwLock::new(None),
85            mounted_filesystems: RwLock::new(vec![root_fs.clone()]),
86        }
87    }
88
89    /// Create a new VFS manager instance with a specified root filesystem
90    pub fn new_with_root(root_fs: Arc<dyn FileSystemOperations>) -> Self {
91        let root_node = root_fs.root_node();
92        let dummy_root_entry = VfsEntry::new(None, "/".to_string(), root_node);
93        let mount_tree = MountTree::new(dummy_root_entry.clone(), root_fs.clone());
94        Self {
95            id: VfsManagerId::new(),
96            mount_tree,
97            cwd: RwLock::new(None),
98            mounted_filesystems: RwLock::new(vec![root_fs.clone()]),
99        }
100    }
101
102    /// Mount a filesystem at the specified path
103    ///
104    /// This will mount the given filesystem at the specified mount point.
105    /// If the mount point is "/", it will replace the root filesystem.
106    ///
107    /// # Arguments
108    /// * `filesystem` - The filesystem to mount.
109    /// * `mount_point_str` - The path where the filesystem should be mounted.
110    /// * `flags` - Flags for the mount operation (e.g., read-only).
111    ///
112    /// # Errors
113    /// Returns an error if the mount point is invalid, the filesystem cannot be mounted,
114    /// or if the mount operation fails.
115    ///
116    pub fn mount(
117        &self,
118        filesystem: Arc<dyn FileSystemOperations>,
119        mount_point_str: &str,
120        flags: u32,
121    ) -> Result<(), FileSystemError> {
122        if mount_point_str == "/" {
123            // Remove the existing root FS from the list
124            let old_root_fs = self
125                .mount_tree
126                .root_mount
127                .read()
128                .root
129                .node()
130                .filesystem()
131                .and_then(|w| w.upgrade());
132            if let Some(old_fs) = old_root_fs {
133                let old_ptr = Arc::as_ptr(&old_fs) as *const () as usize;
134                self.mounted_filesystems
135                    .write()
136                    .retain(|fs| Arc::as_ptr(fs) as *const () as usize != old_ptr);
137            }
138            // Set the new root
139            let new_root_node = filesystem.root_node();
140            let new_root_entry = VfsEntry::new(None, "/".to_string(), new_root_node);
141            let new_root_mount =
142                MountPoint::new_regular("/".to_string(), new_root_entry, filesystem.clone());
143            self.mount_tree.replace_root(new_root_mount);
144            // Push the new FS
145            self.mounted_filesystems.write().push(filesystem.clone());
146            return Ok(());
147        }
148        let _mount_options = MountOptionsV2 {
149            readonly: (flags & 0x01) != 0,
150            flags,
151        };
152        let (target_entry, target_mount_point) = self.resolve_path(mount_point_str)?;
153        self.mount_tree
154            .mount(target_entry, target_mount_point, filesystem.clone())?;
155        self.mounted_filesystems.write().push(filesystem);
156        Ok(())
157    }
158
159    /// Unmount a mount point at the specified path
160    ///
161    /// This will remove the mount point from the mount tree and clean up any
162    /// associated resources.
163    ///
164    /// # Arguments
165    /// * `mount_point_str` - The path of the mount point to unmount.
166    ///
167    /// # Errors
168    /// Returns an error if the mount point is not valid or if the unmount operation fails.
169    ///
170    pub fn unmount(&self, mount_point_str: &str) -> Result<(), FileSystemError> {
171        let (entry, mount_point) = self.resolve_mount_point(mount_point_str)?;
172        if !self.mount_tree.is_mount_point(&entry, &mount_point) {
173            return Err(vfs_error(
174                FileSystemErrorKind::InvalidPath,
175                "Path is not a mount point",
176            ));
177        }
178        let unmounted_mount = self.mount_tree.unmount(&entry, &mount_point)?;
179        // Identify the unmounted fs and remove it from the holding list
180        // If mount_point is a bind mount, we do not remove the filesystem
181        if !unmounted_mount.is_bind_mount() {
182            if let Some(fs) = unmounted_mount.filesystem.clone().or_else(|| {
183                unmounted_mount
184                    .root
185                    .node()
186                    .filesystem()
187                    .and_then(|w| w.upgrade())
188            }) {
189                let fs_ptr = Arc::as_ptr(&fs) as *const () as usize;
190                self.mounted_filesystems
191                    .write()
192                    .retain(|fs| Arc::as_ptr(fs) as *const () as usize != fs_ptr);
193            }
194        }
195        Ok(())
196    }
197
198    /// Bind mount a directory from source_path to target_path
199    ///
200    /// This will create a bind mount where the source directory is mounted
201    /// at the target path.
202    ///
203    /// # Arguments
204    /// * `source_path` - The path of the source directory to bind mount.
205    /// * `target_path` - The path where the source directory should be mounted.
206    ///
207    /// # Errors
208    /// Returns an error if the source is not a directory, the target is already a mount point,
209    /// or if the source is not a valid directory.
210    ///
211    pub fn bind_mount(&self, source_path: &str, target_path: &str) -> Result<(), FileSystemError> {
212        // Resolve the target mount point
213        let (target_entry, target_mount_point) = self.resolve_path(target_path)?;
214        // Resolve the source entry
215        let (source_entry, source_mount_point) = self.resolve_path(source_path)?;
216        // Check if source is a valid entry
217        if !source_entry.node().is_directory()? {
218            return Err(vfs_error(
219                FileSystemErrorKind::NotADirectory,
220                "Source path must be a directory",
221            ));
222        }
223        // Check if target is not already a mount point
224        if self
225            .mount_tree
226            .is_mount_point(&target_entry, &target_mount_point)
227        {
228            return Err(vfs_error(
229                FileSystemErrorKind::InvalidPath,
230                "Target path is already a mount point",
231            ));
232        }
233        // Check if source is a directory (bind mounts only support directories)
234        if !source_entry.node().is_directory()? {
235            return Err(vfs_error(
236                FileSystemErrorKind::NotADirectory,
237                "Source path must be a directory",
238            ));
239        }
240        // Create the bind mount entry
241        self.bind_mount_entry(
242            source_entry,
243            source_mount_point,
244            target_entry,
245            target_mount_point,
246        )
247    }
248
249    /// Bind mount a directory from another VFS instance
250    ///
251    /// This will create a bind mount where the source directory from another VFS
252    /// is mounted at the target path in this VFS.
253    ///
254    /// # Arguments
255    /// * `source_vfs` - The source VFS instance containing the directory to bind
256    /// * `source_path` - The path of the source directory in the source VFS.
257    /// * `target_path` - The path where the source directory should be mounted in this
258    /// VFS.
259    ///
260    /// # Errors
261    /// Returns an error if the source path does not exist, the target is already a mount point,
262    /// or if the source is not a valid directory.
263    ///
264    pub fn bind_mount_from(
265        &self,
266        source_vfs: &Arc<VfsManager>,
267        source_path: &str,
268        target_path: &str,
269    ) -> Result<(), FileSystemError> {
270        // Resolve the source and target paths
271        let (source_entry, source_mount_point) = source_vfs.resolve_path(source_path)?;
272        let (target_entry, target_mount_point) = self.resolve_path(target_path)?;
273
274        // Create the bind mount entry
275        self.bind_mount_entry(
276            source_entry,
277            source_mount_point,
278            target_entry,
279            target_mount_point,
280        )
281    }
282
283    pub(crate) fn bind_mount_from_entry(
284        &self,
285        source_entry: Arc<VfsEntry>,
286        source_mount_point: Arc<MountPoint>,
287        target_path: &str,
288    ) -> Result<(), FileSystemError> {
289        let (target_entry, target_mount_point) = self.resolve_path(target_path)?;
290        self.bind_mount_entry(
291            source_entry,
292            source_mount_point,
293            target_entry,
294            target_mount_point,
295        )
296    }
297
298    /// Create a new VFS manager that starts with the same mount tree as `source`,
299    /// but does not share mount tree structures (deep copy of mount topology).
300    ///
301    /// Notes:
302    /// - Underlying filesystem objects may still be shared (same backing FS),
303    ///   but mount/unmount/bind mount operations will be isolated between namespaces.
304    /// - This intentionally reconstructs mounts by replaying mount/bind-mount operations.
305    pub fn clone_mount_namespace_deep(
306        source: &Arc<VfsManager>,
307    ) -> Result<Arc<VfsManager>, FileSystemError> {
308        fn with_context(e: FileSystemError, ctx: &str) -> FileSystemError {
309            FileSystemError::new(e.kind, alloc::format!("{}: {}", ctx, e.message))
310        }
311
312        fn collect_mounts(mount: &Arc<MountPoint>, out: &mut Vec<Arc<MountPoint>>) {
313            out.push(mount.clone());
314            let children = mount.children.read();
315            for child in children.values() {
316                collect_mounts(child, out);
317            }
318        }
319
320        fn mount_namespace_path(mount: &Arc<MountPoint>) -> Result<String, FileSystemError> {
321            if mount.is_root_mount() {
322                return Ok("/".to_string());
323            }
324
325            let parent_mount = mount.get_parent().ok_or_else(|| {
326                FileSystemError::new(
327                    FileSystemErrorKind::InvalidPath,
328                    "deep-clone: orphan mount (missing parent)",
329                )
330            })?;
331            let parent_entry = mount.parent_entry.as_ref().ok_or_else(|| {
332                FileSystemError::new(
333                    FileSystemErrorKind::InvalidPath,
334                    "deep-clone: orphan mount (missing parent_entry)",
335                )
336            })?;
337
338            namespace_path_of_entry(&parent_mount, parent_entry)
339        }
340
341        fn namespace_path_of_entry(
342            mount: &Arc<MountPoint>,
343            entry: &Arc<VfsEntry>,
344        ) -> Result<String, FileSystemError> {
345            let base = mount_namespace_path(mount)?;
346
347            if Arc::ptr_eq(entry, &mount.root) {
348                return Ok(base);
349            }
350
351            let mut components: Vec<String> = Vec::new();
352            let mut current = Some(entry.clone());
353            while let Some(e) = current {
354                if Arc::ptr_eq(&e, &mount.root) {
355                    break;
356                }
357                components.push(e.name().clone());
358                current = e.parent();
359            }
360            components.reverse();
361
362            if components.is_empty() {
363                Ok(base)
364            } else if base == "/" {
365                Ok(alloc::format!("/{}", components.join("/")))
366            } else {
367                Ok(alloc::format!("{}/{}", base, components.join("/")))
368            }
369        }
370
371        fn find_containing_mount(
372            all_mounts: &[Arc<MountPoint>],
373            entry: &Arc<VfsEntry>,
374        ) -> Option<Arc<MountPoint>> {
375            let mut root = entry.clone();
376            while let Some(parent) = root.parent() {
377                root = parent;
378            }
379
380            all_mounts
381                .iter()
382                .find(|m| Arc::ptr_eq(&m.root, &root))
383                .cloned()
384        }
385
386        fn ensure_dir_path(vfs: &Arc<VfsManager>, path: &str) -> Result<(), FileSystemError> {
387            if path == "/" {
388                return Ok(());
389            }
390
391            // If it already exists, we're done.
392            if vfs.resolve_path(path).is_ok() {
393                return Ok(());
394            }
395
396            // Create intermediate directories one by one: /a, /a/b, ...
397            let mut current = String::new();
398            for part in path.split('/').filter(|p| !p.is_empty()) {
399                if current.is_empty() {
400                    current.push('/');
401                    current.push_str(part);
402                } else {
403                    current.push('/');
404                    current.push_str(part);
405                }
406
407                if vfs.resolve_path(&current).is_ok() {
408                    continue;
409                }
410
411                match vfs.create_dir(&current) {
412                    Ok(()) => {}
413                    Err(e) if e.kind == FileSystemErrorKind::AlreadyExists => {}
414                    Err(e) => return Err(e),
415                }
416            }
417
418            Ok(())
419        }
420
421        let source_root_mount = source.mount_tree.root_mount.read().clone();
422        let source_root_fs = source_root_mount
423            .filesystem
424            .clone()
425            .or_else(|| {
426                source_root_mount
427                    .root
428                    .node()
429                    .filesystem()
430                    .and_then(|w| w.upgrade())
431            })
432            .ok_or_else(|| {
433                FileSystemError::new(
434                    FileSystemErrorKind::NotSupported,
435                    "deep-clone root mount: no filesystem reference",
436                )
437            })?;
438
439        let new_vfs = Arc::new(VfsManager::new_with_root(source_root_fs));
440
441        let mut all_mounts: Vec<Arc<MountPoint>> = Vec::new();
442        collect_mounts(&source_root_mount, &mut all_mounts);
443
444        // 1) Recreate regular mounts (excluding the root mount).
445        let mut regular_mounts: Vec<(usize, String, Arc<dyn FileSystemOperations>)> = Vec::new();
446        for mount in &all_mounts {
447            if mount.is_root_mount() {
448                continue;
449            }
450            if !matches!(mount.mount_type, MountType::Regular) {
451                continue;
452            }
453
454            let target_path = mount_namespace_path(mount)?;
455            let depth = target_path.matches('/').count();
456            let fs = mount
457                .filesystem
458                .clone()
459                .or_else(|| mount.root.node().filesystem().and_then(|w| w.upgrade()))
460                .ok_or_else(|| {
461                    FileSystemError::new(
462                        FileSystemErrorKind::NotSupported,
463                        alloc::format!(
464                            "deep-clone mount replay: no filesystem reference for '{}'",
465                            target_path
466                        ),
467                    )
468                })?;
469            regular_mounts.push((depth, target_path, fs));
470        }
471        regular_mounts.sort_by_key(|(depth, path, _)| (*depth, path.clone()));
472        for (_depth, target_path, fs) in regular_mounts {
473            // Some mount points may not exist as directories in the underlying FS.
474            // Ensure the target directory exists before mounting.
475            let _ = ensure_dir_path(&new_vfs, &target_path);
476            new_vfs.mount(fs, &target_path, 0).map_err(|e| {
477                with_context(
478                    e,
479                    &alloc::format!("deep-clone mount replay at '{}'", target_path),
480                )
481            })?;
482        }
483
484        // 2) Recreate bind mounts after regular mounts are in place.
485        let mut bind_mounts: Vec<(usize, String, String)> = Vec::new();
486        for mount in &all_mounts {
487            if mount.is_root_mount() {
488                continue;
489            }
490            if !matches!(mount.mount_type, MountType::Bind) {
491                continue;
492            }
493
494            let target_path = mount_namespace_path(mount)?;
495            let depth = target_path.matches('/').count();
496
497            let source_entry = mount.root.clone();
498            let containing_mount =
499                find_containing_mount(&all_mounts, &source_entry).ok_or_else(|| {
500                    FileSystemError::new(
501                        FileSystemErrorKind::InvalidPath,
502                        "Bind source mount not found",
503                    )
504                })?;
505            let source_path = namespace_path_of_entry(&containing_mount, &source_entry)?;
506
507            bind_mounts.push((depth, source_path, target_path));
508        }
509        bind_mounts.sort_by_key(|(depth, source_path, target_path)| {
510            (*depth, target_path.clone(), source_path.clone())
511        });
512        for (_depth, source_path, target_path) in bind_mounts {
513            // Some systems may contain redundant self-binds (e.g. "/dev" -> "/dev")
514            // or bind mounts that effectively overlap existing mounts. Our mount tree does not
515            // support stacking multiple mounts at the same target, so treat these as no-ops.
516            if source_path == target_path {
517                continue;
518            }
519
520            // Ensure bind target exists before creating the bind mount.
521            let _ = ensure_dir_path(&new_vfs, &target_path);
522            match new_vfs.bind_mount(&source_path, &target_path) {
523                Ok(()) => {}
524                Err(e)
525                    if e.kind == FileSystemErrorKind::InvalidPath
526                        && e.message.contains("Target path is already a mount point") =>
527                {
528                    // Unsupported to stack a bind mount over an existing mount in this VFS.
529                    // Keep going to provide best-effort deep clone.
530                }
531                Err(e) => {
532                    return Err(with_context(
533                        e,
534                        &alloc::format!(
535                            "deep-clone bind-mount replay '{}' -> '{}'",
536                            source_path,
537                            target_path
538                        ),
539                    ));
540                }
541            }
542        }
543
544        Ok(new_vfs)
545    }
546
547    /// Create a bind mount from source_entry to target_entry
548    fn bind_mount_entry(
549        &self,
550        source_entry: Arc<VfsEntry>,
551        source_mount_point: Arc<MountPoint>,
552        target_entry: Arc<VfsEntry>,
553        target_mount_point: Arc<MountPoint>,
554    ) -> Result<(), FileSystemError> {
555        // Create a new MountPoint for the bind mount
556        let bind_mount = MountPoint::new_bind(target_entry.name().clone(), source_entry);
557        // Set parent/parent_entry
558        unsafe {
559            let mut_ptr = Arc::as_ptr(&bind_mount) as *mut MountPoint;
560            (*mut_ptr).parent = Some(Arc::downgrade(&target_mount_point));
561            (*mut_ptr).parent_entry = Some(target_entry.clone());
562        }
563        // NOTE: Do not clone mount-children from the source mount point.
564        // Cloning `MountPoint` children across VFS instances leaves their `parent` weak refs
565        // pointing at the source tree, which can later be dropped (e.g. during pivot_root),
566        // producing orphan mounts and panics in mount-tree traversal.
567        let _ = source_mount_point;
568        // Add as child to target_mount_point
569        target_mount_point
570            .children
571            .write()
572            .insert(target_entry.node().id(), bind_mount);
573        Ok(())
574    }
575
576    /// Open a file at the specified path
577    ///
578    /// This will resolve the path using the MountTreeV2 and open the file
579    /// using the filesystem associated with the resolved VfsEntry.
580    ///
581    /// # Arguments
582    /// * `path` - The path of the file to open.
583    /// * `flags` - Flags for opening the file (e.g., read, write
584    /// * `O_CREAT`, etc.).
585    ///
586    /// # Errors
587    /// Returns an error if the path does not exist, is not a file, or if
588    /// the filesystem cannot be resolved.
589    ///
590    pub fn open(&self, path: &str, flags: u32) -> Result<KernelObject, FileSystemError> {
591        // Use MountTreeV2 to resolve filesystem and relative path, then open
592        let (entry, mount_point) = self.resolve_path(path)?;
593        let node = entry.node();
594        let filesystem = node.filesystem().and_then(|w| w.upgrade()).ok_or_else(|| {
595            FileSystemError::new(FileSystemErrorKind::NotSupported, "No filesystem reference")
596        })?;
597
598        // Get the underlying FileSystem implementation
599        let inner_file_obj = filesystem.open(&node, flags)?;
600
601        // Wrap with VFS-layer information
602        let vfs_file_obj =
603            super::core::VfsFileObject::new(inner_file_obj, entry, mount_point, path.to_string());
604
605        Ok(KernelObject::File(Arc::new(vfs_file_obj)))
606    }
607
608    /// Create a file at the specified path
609    ///
610    /// This will create a new file in the filesystem at the given path.
611    ///
612    /// # Arguments
613    /// * `path` - The path where the file should be created.
614    /// * `file_type` - The type of file to create (e.g., regular
615    /// file, directory, etc.).
616    ///
617    /// # Errors
618    /// Returns an error if the parent directory does not exist, the filesystem cannot be resolved,
619    /// or if the file cannot be created.
620    ///
621    pub fn create_file(&self, path: &str, file_type: FileType) -> Result<(), FileSystemError> {
622        // Split path into parent and filename
623        let (parent_path, filename) = self.split_parent_child(path)?;
624
625        // Resolve parent directory using MountTreeV2
626        let parent_entry = self.resolve_path(&parent_path)?.0;
627        let parent_node = parent_entry.node();
628        debug_assert!(
629            parent_node.filesystem().is_some(),
630            "VfsManager::create_file - parent_node.filesystem() is None for path '{}'",
631            parent_path
632        );
633
634        // Create file using filesystem
635        let filesystem = parent_node
636            .filesystem()
637            .and_then(|w| w.upgrade())
638            .ok_or_else(|| {
639                FileSystemError::new(FileSystemErrorKind::NotSupported, "No filesystem reference")
640            })?;
641        let new_node = filesystem.create(
642            &parent_node,
643            &filename,
644            file_type,
645            0o644, // Default permissions
646        )?;
647
648        // Create VfsEntry and add to parent cache
649        let new_entry = VfsEntry::new(
650            Some(Arc::downgrade(&parent_entry)),
651            filename.clone(),
652            new_node,
653        );
654
655        parent_entry.add_child(filename, new_entry);
656
657        Ok(())
658    }
659
660    /// Create a directory at the specified path
661    ///
662    /// This will create a new directory in the filesystem at the given path.
663    ///
664    /// # Arguments
665    /// * `path` - The path where the directory should be created.
666    ///
667    /// # Errors
668    /// Returns an error if the parent directory does not exist, the filesystem cannot be resolved,
669    /// or if the directory cannot be created.
670    ///
671    pub fn create_dir(&self, path: &str) -> Result<(), FileSystemError> {
672        self.create_file(path, FileType::Directory)
673    }
674
675    /// Create a symbolic link at the specified path
676    ///
677    /// This will create a new symbolic link in the filesystem at the given path,
678    /// pointing to the specified target path.
679    ///
680    /// # Arguments
681    /// * `path` - The path where the symbolic link should be created.
682    /// * `target_path` - The path that the symbolic link should point to.
683    ///
684    /// # Errors
685    /// Returns an error if the parent directory does not exist, the filesystem cannot be resolved,
686    /// or if the symbolic link cannot be created.
687    ///
688    pub fn create_symlink(&self, path: &str, target_path: &str) -> Result<(), FileSystemError> {
689        self.create_file(path, FileType::SymbolicLink(target_path.to_string()))
690    }
691
692    /// Remove a file at the specified path
693    ///
694    /// This will remove the file from the filesystem and update the mount tree.
695    ///
696    /// # Arguments
697    /// * `path` - The path of the file to remove.
698    ///
699    /// # Errors
700    /// Returns an error if the path does not exist, is not a file, or if
701    /// the filesystem cannot be resolved.
702    ///
703    pub fn remove(&self, path: &str) -> Result<(), FileSystemError> {
704        // Resolve the entry to be removed - use no_follow to follow intermediate symlinks
705        // but not the final component (like POSIX rm behavior)
706        let options = PathResolutionOptions::no_follow();
707        let (entry_to_remove, mount_point) = self.resolve_path_with_options(path, &options)?;
708
709        // Check if the entry is involved in any mount, which would make it busy
710        if self
711            .mount_tree
712            .is_entry_used_in_mount(&entry_to_remove, &mount_point)
713        {
714            return Err(vfs_error(
715                FileSystemErrorKind::NotSupported,
716                "Resource is busy",
717            ));
718        }
719
720        // Split path into parent and filename
721        let (parent_path, filename) = self.split_parent_child(path)?;
722
723        // Resolve parent directory using MountTreeV2 (follow all symlinks for parent path)
724        let parent_entry = self.resolve_path(&parent_path)?.0;
725        let parent_node = parent_entry.node();
726
727        // Remove from filesystem
728        let filesystem = parent_node
729            .filesystem()
730            .and_then(|w| w.upgrade())
731            .ok_or_else(|| {
732                FileSystemError::new(FileSystemErrorKind::NotSupported, "No filesystem reference")
733            })?;
734        filesystem.remove(&parent_node, &filename)?;
735
736        // Remove from parent cache
737        let _ = parent_entry.remove_child(&filename);
738
739        Ok(())
740    }
741
742    /// Get metadata for a file at the specified path
743    ///
744    /// This will resolve the path using the MountTreeV2 and return the metadata
745    /// for the file represented by the resolved VfsEntry.
746    ///
747    /// # Arguments
748    /// * `path` - The path of the file to get metadata for.
749    ///
750    /// # Errors
751    /// Returns an error if the path does not exist, is not a file, or if
752    /// the filesystem cannot be resolved.
753    ///
754    pub fn metadata(&self, path: &str) -> Result<FileMetadata, FileSystemError> {
755        // Resolve path to VfsEntry
756        let entry = self.resolve_path(path)?.0;
757
758        // Get VfsNode and return metadata
759        let node = entry.node();
760
761        node.metadata()
762    }
763
764    /// Read directory entries at the specified path
765    ///
766    /// This will resolve the path using the MountTreeV2 and return a list of
767    /// directory entries for the directory represented by the resolved VfsEntry.
768    ///
769    /// # Arguments
770    /// * `path` - The path of the directory to read.
771    ///
772    /// # Errors
773    /// Returns an error if the path does not exist, is not a directory, or if
774    /// the filesystem cannot be resolved.
775    ///
776    pub fn readdir(&self, path: &str) -> Result<Vec<DirectoryEntryInternal>, FileSystemError> {
777        // Resolve path to VfsEntry
778        let entry = self.resolve_path(path)?.0;
779
780        // Get VfsNode
781        let node = entry.node();
782
783        // Check if it's a directory
784        if !node.is_directory()? {
785            return Err(FileSystemError::new(
786                FileSystemErrorKind::NotADirectory,
787                "Not a directory",
788            ));
789        }
790
791        // Get filesystem from node
792        let fs_ref = node.filesystem().ok_or_else(|| {
793            FileSystemError::new(
794                FileSystemErrorKind::NotSupported,
795                "Node has no filesystem reference",
796            )
797        })?;
798
799        let filesystem = fs_ref.upgrade().ok_or_else(|| {
800            FileSystemError::new(
801                FileSystemErrorKind::NotSupported,
802                "Filesystem reference is dead",
803            )
804        })?;
805
806        // Call filesystem's readdir
807        filesystem.readdir(&node)
808    }
809
810    /// Set current working directory by path
811    ///
812    /// This will change the current working directory to the specified path.
813    ///
814    /// # Arguments
815    /// * `path` - The path to set as the current working directory.
816    ///
817    /// # Errors
818    /// Returns an error if the path does not exist, is not a directory, or if
819    /// the filesystem cannot be resolved.
820    ///
821    pub fn set_cwd_by_path(&self, path: &str) -> Result<(), FileSystemError> {
822        let (entry, mount_point) = self.resolve_path(path)?;
823
824        // Verify it's a directory
825        let node = entry.node();
826
827        if !node.is_directory()? {
828            return Err(FileSystemError::new(
829                FileSystemErrorKind::NotADirectory,
830                "Not a directory",
831            ));
832        }
833
834        self.set_cwd(entry, mount_point);
835        Ok(())
836    }
837
838    /// Set current working directory
839    ///
840    /// This sets the current working directory to the specified VfsEntry and MountPoint.
841    /// The entry must be a directory.
842    ///
843    /// # Arguments
844    /// * `entry` - The VfsEntry representing the directory
845    /// * `mount_point` - The MountPoint where the directory is mounted
846    ///
847    pub fn set_cwd(&self, entry: Arc<VfsEntry>, mount_point: Arc<MountPoint>) {
848        *self.cwd.write() = Some((entry, mount_point));
849    }
850
851    /// Get current working directory
852    ///
853    /// This returns the current working directory as an `Arc<VfsEntry>`.
854    ///
855    /// If the current working directory is not set, it returns `None`.
856    ///
857    /// # Returns
858    /// An `Option<Arc<VfsEntry>>` containing the current working directory entry,
859    /// or `None` if the current working directory is not set.
860    ///
861    pub fn get_cwd(&self) -> Option<(Arc<VfsEntry>, Arc<MountPoint>)> {
862        self.cwd.read().clone()
863    }
864
865    /// Get current working directory as path string
866    ///
867    /// This returns the current working directory as a path string.
868    /// If the current working directory is not set, it returns "/".
869    ///
870    /// # Returns
871    /// A `String` containing the current working directory path.
872    ///
873    pub fn get_cwd_path(&self) -> String {
874        if let Some((entry, mount_point)) = self.get_cwd() {
875            self.build_absolute_path(&entry, &mount_point)
876        } else {
877            "/".to_string()
878        }
879    }
880
881    /// Build absolute path from VfsEntry and MountPoint
882    ///
883    /// This safely constructs the absolute path for a given VfsEntry by using
884    /// MountPoint information, avoiding potential issues with Weak references.
885    ///
886    /// # Arguments
887    /// * `entry` - The VfsEntry to build the path for
888    /// * `mount_point` - The MountPoint containing this entry
889    ///
890    /// # Returns
891    /// A `String` containing the absolute path
892    pub fn build_absolute_path(
893        &self,
894        entry: &Arc<VfsEntry>,
895        mount_point: &Arc<MountPoint>,
896    ) -> String {
897        // Build relative path within the mount point
898        let mut path_components = Vec::new();
899        let mut current = Some(entry.clone());
900        let mount_root = &mount_point.root;
901
902        // Traverse up to the mount root
903        while let Some(entry) = current {
904            // Stop if we've reached the mount root
905            if Arc::ptr_eq(&entry, mount_root) {
906                break;
907            }
908
909            path_components.push(entry.name().clone());
910            current = entry.parent();
911        }
912
913        // Get the mount path using MountTree's method
914        let mount_path = self.mount_tree.get_mount_absolute_path(mount_point);
915
916        if path_components.is_empty() {
917            // This is the mount root itself
918            mount_path
919        } else {
920            path_components.reverse();
921            let relative_path = path_components.join("/");
922
923            if mount_path == "/" {
924                alloc::format!("/{}", relative_path)
925            } else {
926                alloc::format!("{}/{}", mount_path, relative_path)
927            }
928        }
929    }
930
931    /// Create a device file
932    ///
933    /// This will create a new device file in the filesystem at the given path.
934    ///
935    /// # Arguments
936    /// * `path` - The path where the device file should be created.
937    /// * `device_info` - Information about the device file to create (e.g.,
938    /// device type, major/minor numbers, etc.).
939    ///
940    /// # Errors
941    /// Returns an error if the parent directory does not exist, the filesystem cannot be resolved,
942    /// or if the device file cannot be created.
943    ///
944    pub fn create_device_file(
945        &self,
946        path: &str,
947        device_info: DeviceFileInfo,
948    ) -> Result<(), FileSystemError> {
949        let file_type = match device_info.device_type {
950            crate::device::DeviceType::Char => FileType::CharDevice(device_info),
951            crate::device::DeviceType::Block => FileType::BlockDevice(device_info),
952            _ => {
953                return Err(FileSystemError::new(
954                    FileSystemErrorKind::NotSupported,
955                    "Unsupported device type",
956                ));
957            }
958        };
959
960        self.create_file(path, file_type)
961    }
962
963    /// Resolve a path to both VfsEntry and MountPoint
964    ///
965    /// Automatically handles both absolute paths (starting with '/') and relative paths
966    /// (resolved from current working directory). This is the main path resolution API.
967    ///
968    /// Returns both VfsEntry and MountPoint for consistency with other resolution APIs.
969    pub fn resolve_path(
970        &self,
971        path: &str,
972    ) -> Result<(Arc<VfsEntry>, Arc<MountPoint>), FileSystemError> {
973        self.resolve_path_with_options(path, &PathResolutionOptions::default())
974    }
975
976    /// Resolve a path with specified options
977    pub fn resolve_path_with_options(
978        &self,
979        path: &str,
980        options: &PathResolutionOptions,
981    ) -> Result<(Arc<VfsEntry>, Arc<MountPoint>), FileSystemError> {
982        // Check if the path is absolute
983        if path.starts_with('/') {
984            // Absolute path - resolve from root
985            self.mount_tree.resolve_path_with_options(path, options)
986        } else {
987            // Relative path - resolve from current working directory
988            let cwd = self.get_cwd();
989            if let Some((base_entry, base_mount)) = cwd {
990                // Resolve relative to current working directory
991                self.resolve_path_from_with_options(&base_entry, &base_mount, path, options)
992            } else {
993                Err(FileSystemError::new(
994                    FileSystemErrorKind::InvalidPath,
995                    "Relative path resolution requires a current working directory",
996                ))
997            }
998        }
999    }
1000
1001    /// Resolve a path from a specific base directory (for *at system calls)
1002    ///
1003    /// This method resolves a path starting from the specified base directory.
1004    /// It's specifically designed for *at system calls (openat, fstatat, etc.).
1005    ///
1006    /// Returns both VfsEntry and MountPoint for efficient use.
1007    pub fn resolve_path_from(
1008        &self,
1009        base_entry: &Arc<VfsEntry>,
1010        base_mount: &Arc<MountPoint>,
1011        path: &str,
1012    ) -> Result<(Arc<VfsEntry>, Arc<MountPoint>), FileSystemError> {
1013        self.resolve_path_from_with_options(
1014            base_entry,
1015            base_mount,
1016            path,
1017            &PathResolutionOptions::default(),
1018        )
1019    }
1020
1021    /// Resolve a path from an optional base directory with options
1022    pub fn resolve_path_from_with_options(
1023        &self,
1024        base_entry: &Arc<VfsEntry>,
1025        base_mount: &Arc<MountPoint>,
1026        path: &str,
1027        options: &PathResolutionOptions,
1028    ) -> Result<(Arc<VfsEntry>, Arc<MountPoint>), FileSystemError> {
1029        if path.starts_with('/') {
1030            // Absolute path - ignore base and resolve from root
1031            self.mount_tree.resolve_path_with_options(path, options)
1032        } else {
1033            // Relative path with explicit base (for *at syscalls)
1034            self.mount_tree.resolve_path_from_with_options(
1035                Some(base_entry),
1036                Some(base_mount),
1037                path,
1038                options,
1039            )
1040        }
1041    }
1042
1043    /// Resolve a path to mount point (returns VfsEntryRef instead of Arc<VfsEntry>)
1044    ///
1045    /// This is useful for operations that need to work with mount point information
1046    /// but don't necessarily need strong references to entries.
1047    ///
1048    /// # Arguments
1049    /// * `path` - The path to resolve
1050    ///
1051    /// # Returns
1052    /// Returns a tuple of (VfsEntryRef, Arc<MountPoint>) on success
1053    pub fn resolve_mount_point(&self, path: &str) -> VfsResult<(VfsEntryRef, Arc<MountPoint>)> {
1054        self.resolve_mount_point_with_options(path, &PathResolutionOptions::default())
1055    }
1056
1057    /// Resolve a path to mount point with specified options
1058    pub fn resolve_mount_point_with_options(
1059        &self,
1060        path: &str,
1061        options: &PathResolutionOptions,
1062    ) -> VfsResult<(VfsEntryRef, Arc<MountPoint>)> {
1063        // Check if the path is absolute
1064        if path.starts_with('/') {
1065            // Absolute path - resolve from root
1066            self.mount_tree
1067                .resolve_mount_point_with_options(path, options)
1068        } else {
1069            // Relative path - resolve from current working directory
1070            let cwd = self.get_cwd();
1071            if let Some((base_entry, base_mount)) = cwd {
1072                // Resolve relative to current working directory
1073                self.resolve_mount_point_from_with_options(&base_entry, &base_mount, path, options)
1074            } else {
1075                Err(vfs_error(
1076                    FileSystemErrorKind::InvalidPath,
1077                    "Relative path resolution requires a current working directory",
1078                ))
1079            }
1080        }
1081    }
1082
1083    pub fn resolve_mount_point_from(
1084        &self,
1085        base_entry: &Arc<VfsEntry>,
1086        base_mount: &Arc<MountPoint>,
1087        path: &str,
1088        options: &PathResolutionOptions,
1089    ) -> VfsResult<(VfsEntryRef, Arc<MountPoint>)> {
1090        self.resolve_mount_point_from_with_options(base_entry, base_mount, path, options)
1091    }
1092
1093    pub fn resolve_mount_point_from_with_options(
1094        &self,
1095        base_entry: &Arc<VfsEntry>,
1096        base_mount: &Arc<MountPoint>,
1097        path: &str,
1098        options: &PathResolutionOptions,
1099    ) -> VfsResult<(VfsEntryRef, Arc<MountPoint>)> {
1100        if path.starts_with('/') {
1101            // Absolute path - resolve from root
1102            self.mount_tree
1103                .resolve_mount_point_with_options(path, options)
1104        } else {
1105            // Relative path with explicit base (for *at syscalls)
1106            self.mount_tree.resolve_mount_point_from_with_options(
1107                Some(base_entry),
1108                Some(base_mount),
1109                path,
1110                options,
1111            )
1112        }
1113    }
1114
1115    /// Create a hard link
1116    ///
1117    /// This will create a hard link where the source file is linked to the target path.
1118    /// Both paths will refer to the same underlying file data.
1119    ///
1120    /// # Arguments
1121    /// * `source_path` - Path of the existing file to link to
1122    /// * `target_path` - Path where the hard link should be created
1123    ///
1124    /// # Errors
1125    /// Returns an error if the source doesn't exist, target already exists,
1126    /// filesystems don't match, or hard links aren't supported.
1127    ///
1128    pub fn create_hardlink(
1129        &self,
1130        source_path: &str,
1131        target_path: &str,
1132    ) -> Result<(), FileSystemError> {
1133        // Resolve source file
1134        let (source_entry, _source_mount) = self.resolve_path(source_path)?;
1135
1136        let source_node = source_entry.node();
1137
1138        // Check that source is a regular file (most filesystems don't support directory hard links)
1139        if source_node.is_directory()? {
1140            return Err(vfs_error(
1141                FileSystemErrorKind::InvalidOperation,
1142                "Cannot create hard link to directory",
1143            ));
1144        }
1145
1146        // Split target path into parent and filename
1147        let (target_parent_path, target_filename) = self.split_parent_child(target_path)?;
1148
1149        // Resolve target parent directory
1150        let (target_parent_entry, _target_mount) = self.resolve_path(&target_parent_path)?;
1151        let target_parent_node = target_parent_entry.node();
1152
1153        // Check that target parent is a directory
1154        if !target_parent_node.is_directory()? {
1155            return Err(vfs_error(
1156                FileSystemErrorKind::NotADirectory,
1157                "Target parent is not a directory",
1158            ));
1159        }
1160
1161        // Get filesystems for both source and target
1162        let source_fs = source_node
1163            .filesystem()
1164            .and_then(|w| w.upgrade())
1165            .ok_or_else(|| {
1166                FileSystemError::new(
1167                    FileSystemErrorKind::NotSupported,
1168                    "No filesystem reference for source",
1169                )
1170            })?;
1171
1172        let target_fs = target_parent_node
1173            .filesystem()
1174            .and_then(|w| w.upgrade())
1175            .ok_or_else(|| {
1176                FileSystemError::new(
1177                    FileSystemErrorKind::NotSupported,
1178                    "No filesystem reference for target",
1179                )
1180            })?;
1181
1182        // Check that both files are on the same filesystem (hard links can't cross filesystem boundaries)
1183        if !Arc::ptr_eq(&source_fs, &target_fs) {
1184            return Err(vfs_error(
1185                FileSystemErrorKind::CrossDevice,
1186                "Hard links cannot cross filesystem boundaries",
1187            ));
1188        }
1189
1190        // Check if target already exists
1191        if target_parent_entry.get_child(&target_filename).is_some() {
1192            return Err(vfs_error(
1193                FileSystemErrorKind::FileExists,
1194                "Target file already exists",
1195            ));
1196        }
1197
1198        // Create the hard link
1199        let link_node =
1200            source_fs.create_hardlink(&target_parent_node, &target_filename, &source_node)?;
1201
1202        // Create VfsEntry and add to parent cache
1203        let link_entry = VfsEntry::new(
1204            Some(Arc::downgrade(&target_parent_entry)),
1205            target_filename.clone(),
1206            link_node,
1207        );
1208        target_parent_entry.add_child(target_filename, link_entry);
1209
1210        Ok(())
1211    }
1212
1213    // Helper methods
1214
1215    /// Split a path into parent directory and filename
1216    fn split_parent_child(&self, path: &str) -> Result<(String, String), FileSystemError> {
1217        // Simple path normalization: remove trailing slash except for root
1218        let normalized = if path != "/" && path.ends_with('/') {
1219            path.trim_end_matches('/').to_string()
1220        } else {
1221            path.to_string()
1222        };
1223
1224        if normalized == "/" {
1225            return Err(FileSystemError::new(
1226                FileSystemErrorKind::InvalidPath,
1227                "Cannot split root path",
1228            ));
1229        }
1230
1231        if let Some(last_slash) = normalized.rfind('/') {
1232            let parent = if last_slash == 0 {
1233                "/".to_string()
1234            } else {
1235                normalized[..last_slash].to_string()
1236            };
1237            let filename = normalized[last_slash + 1..].to_string();
1238            Ok((parent, filename))
1239        } else {
1240            Err(FileSystemError::new(
1241                FileSystemErrorKind::InvalidPath,
1242                "Invalid path format",
1243            ))
1244        }
1245    }
1246
1247    /// Open a file relative to a given base entry and mount (for *at syscalls)
1248    ///
1249    /// # Arguments
1250    /// * `base_entry` - Base VfsEntry to resolve relative path from
1251    /// * `base_mount` - Base MountPoint for the base entry
1252    /// * `path` - Relative or absolute path
1253    /// * `flags` - Open flags
1254    ///
1255    /// # Returns
1256    /// KernelObject::File(VfsFileObject)
1257    /// Open a file with optional base directory (unified openat implementation)
1258    ///
1259    /// If base_entry and base_mount are None, behaves like regular open().
1260    /// If base is provided, resolves relative paths from that base (for *at syscalls).
1261    pub fn open_from(
1262        &self,
1263        base_entry: &Arc<VfsEntry>,
1264        base_mount: &Arc<MountPoint>,
1265        path: &str,
1266        flags: u32,
1267    ) -> Result<KernelObject, FileSystemError> {
1268        let (entry, mount_point) = self.resolve_path_from(base_entry, base_mount, path)?;
1269        let node = entry.node();
1270        let filesystem = node.filesystem().and_then(|w| w.upgrade()).ok_or_else(|| {
1271            FileSystemError::new(FileSystemErrorKind::NotSupported, "No filesystem reference")
1272        })?;
1273        let inner_file_obj = filesystem.open(&node, flags)?;
1274        let vfs_file_obj =
1275            super::core::VfsFileObject::new(inner_file_obj, entry, mount_point, path.to_string());
1276        Ok(KernelObject::File(Arc::new(vfs_file_obj)))
1277    }
1278
1279    /// Resolve a relative path to an absolute path using the current working directory
1280    ///
1281    /// If the path is already absolute, returns it as-is.
1282    /// If the path is relative, combines it with the current working directory.
1283    ///
1284    /// # Arguments
1285    /// * `path` - The path to resolve (relative or absolute)
1286    ///
1287    /// # Returns
1288    /// An absolute path string
1289    pub fn resolve_path_to_absolute(&self, path: &str) -> String {
1290        if path.starts_with('/') {
1291            // Already absolute path
1292            path.to_string()
1293        } else {
1294            // Relative path - combine with current working directory
1295            self.get_cwd_path() + "/" + path
1296        }
1297    }
1298}
1299
1300/// Initialize the global VFS manager (Arc) so it can be retrieved later
1301pub fn init_global_vfs_manager() -> Arc<VfsManager> {
1302    GLOBAL_VFS_MANAGER
1303        .call_once(|| Arc::new(VfsManager::new()))
1304        .clone()
1305}
1306
1307/// Retrieve the global VFS manager (Arc)
1308pub fn get_global_vfs_manager() -> Arc<VfsManager> {
1309    GLOBAL_VFS_MANAGER
1310        .get()
1311        .expect("global VFS manager not initialized")
1312        .clone()
1313}