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(¤t).is_ok() {
408 continue;
409 }
410
411 match vfs.create_dir(¤t) {
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}