kernel/fs/vfs_v2/
mount_tree.rs

1//! VFS v2 Mount Tree Implementation
2//!
3//! This module provides a new mount tree architecture for VFS v2 that supports:
4//! - Hierarchical mount points with parent-child relationships
5//! - Bind mounts and overlay mounts
6//! - Proper path resolution across mount boundaries
7//! - Efficient mount point lookup and traversal
8
9use alloc::collections::BTreeMap;
10use alloc::format;
11use alloc::string::{String, ToString};
12use alloc::sync::{Arc, Weak};
13use alloc::vec::Vec;
14use core::sync::atomic::{AtomicU64, Ordering};
15use spin::RwLock;
16
17use super::core::{FileSystemOperations, VfsEntry};
18use super::manager::{PathResolutionOptions, VfsManager};
19use crate::fs::{FileSystemError, FileSystemErrorKind};
20
21pub type VfsResult<T> = Result<T, FileSystemError>;
22pub type VfsEntryRef = Arc<VfsEntry>;
23pub type VfsEntryWeakRef = Weak<VfsEntry>;
24
25// Helper function to create FileSystemError
26fn vfs_error(kind: FileSystemErrorKind, message: &str) -> FileSystemError {
27    FileSystemError::new(kind, message)
28}
29
30/// Unique identifier for mount points
31#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
32pub struct MountId(u64);
33
34impl MountId {
35    fn new() -> Self {
36        static COUNTER: AtomicU64 = AtomicU64::new(1);
37        Self(COUNTER.fetch_add(1, Ordering::Relaxed))
38    }
39}
40
41/// Unique identifier for VfsManager instances
42#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
43pub struct VfsManagerId(u64);
44
45impl VfsManagerId {
46    pub fn new() -> Self {
47        static COUNTER: AtomicU64 = AtomicU64::new(1);
48        Self(COUNTER.fetch_add(1, Ordering::Relaxed))
49    }
50}
51
52/// Type of mount operation
53#[derive(Debug, Clone)]
54pub enum MountType {
55    /// Regular mount
56    Regular,
57    /// Bind mount (mount existing directory at another location)
58    Bind,
59    /// Overlay mount (overlay multiple directories)
60    Overlay {
61        /// The list of overlay layers (top to bottom priority)
62        layers: Vec<VfsEntryRef>,
63    },
64}
65
66/// Mount options (for compatibility with manager_v2.rs)
67#[derive(Debug, Clone, Default)]
68pub struct MountOptionsV2 {
69    pub readonly: bool,
70    pub flags: u32,
71}
72
73/// Mount point information
74#[derive(Debug)]
75pub struct MountPoint {
76    /// Unique mount ID
77    pub id: MountId,
78    /// Type of mount
79    pub mount_type: MountType,
80    /// Mount path (relative to parent mount)
81    pub path: String,
82    /// Root entry of the mounted filesystem
83    pub root: VfsEntryRef,
84    /// Strong reference to the mounted filesystem (Regular mounts only)
85    pub filesystem: Option<Arc<dyn FileSystemOperations>>,
86    /// Parent mount (weak reference to avoid cycles)
87    pub parent: Option<Weak<MountPoint>>,
88    /// Parent entry (strong reference to the VFS entry at the mount point to ensure it stays alive)
89    pub parent_entry: Option<VfsEntryRef>,
90    /// Child mounts: shared map of VfsEntry ID to MountPoint
91    pub children: Arc<RwLock<BTreeMap<u64, Arc<MountPoint>>>>,
92}
93
94impl MountPoint {
95    /// Create a new regular mount point
96    pub fn new_regular(
97        path: String,
98        root: VfsEntryRef,
99        filesystem: Arc<dyn FileSystemOperations>,
100    ) -> Arc<Self> {
101        Arc::new(Self {
102            id: MountId::new(),
103            mount_type: MountType::Regular,
104            path,
105            root,
106            filesystem: Some(filesystem),
107            parent: None,
108            parent_entry: None,
109            children: Arc::new(RwLock::new(BTreeMap::new())),
110        })
111    }
112
113    /// Create a new bind mount point
114    pub fn new_bind(path: String, source: VfsEntryRef) -> Arc<Self> {
115        Arc::new(Self {
116            id: MountId::new(),
117            mount_type: MountType::Bind,
118            path,
119            root: source,
120            filesystem: None,
121            parent: None,
122            parent_entry: None,
123            children: Arc::new(RwLock::new(BTreeMap::new())),
124        })
125    }
126
127    /// Create a new overlay mount point
128    pub fn new_overlay(path: String, layers: Vec<VfsEntryRef>) -> VfsResult<Arc<Self>> {
129        if layers.is_empty() {
130            return Err(vfs_error(
131                FileSystemErrorKind::InvalidPath,
132                "Overlay mount requires at least one layer",
133            ));
134        }
135
136        // Use the top layer as the root
137        let root = layers[0].clone();
138
139        Ok(Arc::new(Self {
140            id: MountId::new(),
141            mount_type: MountType::Overlay {
142                layers: layers.clone(),
143            },
144            path,
145            root,
146            filesystem: None,
147            parent: None,
148            parent_entry: None,
149            children: Arc::new(RwLock::new(BTreeMap::new())),
150        }))
151    }
152
153    /// Get the parent mount point
154    pub fn get_parent(&self) -> Option<Arc<MountPoint>> {
155        self.parent.as_ref().and_then(|weak| weak.upgrade())
156    }
157
158    /// Check if this is the root mount
159    pub fn is_root_mount(&self) -> bool {
160        self.parent.is_none()
161    }
162
163    /// Get child mount by VfsEntry
164    pub fn get_child(&self, entry: &VfsEntryRef) -> Option<Arc<MountPoint>> {
165        let key = entry.node().id();
166        self.children.read().get(&key).cloned()
167    }
168
169    /// Add a child mount by VfsEntry
170    pub fn add_child(
171        self: &Arc<Self>,
172        entry: &VfsEntryRef,
173        child: Arc<MountPoint>,
174    ) -> VfsResult<()> {
175        // Set parent reference in child
176        let mut_child: *const MountPoint = Arc::as_ptr(&child);
177        unsafe {
178            let mut_child = mut_child as *mut MountPoint;
179            (*mut_child).parent = Some(Arc::downgrade(self));
180            (*mut_child).parent_entry = Some(entry.clone());
181        }
182        let key = entry.node().id();
183        self.children.write().insert(key, child);
184        Ok(())
185    }
186
187    /// Remove a child mount by VfsEntry
188    pub fn remove_child(&self, entry: &VfsEntryRef) -> Option<Arc<MountPoint>> {
189        let key = entry.node().id();
190        self.children.write().remove(&key)
191    }
192
193    /// List all child mount IDs
194    pub fn list_children(&self) -> Vec<u64> {
195        self.children.read().keys().cloned().collect()
196    }
197
198    /// Check if this mount point is a bind mount
199    pub fn is_bind_mount(&self) -> bool {
200        matches!(self.mount_type, MountType::Bind { .. })
201    }
202
203    /// Get the bind source entry (for regular bind mounts only)
204    pub fn get_bind_source(&self) -> Option<VfsEntryRef> {
205        match &self.mount_type {
206            MountType::Bind { .. } => Some(self.root.clone()),
207            _ => None,
208        }
209    }
210
211    /// Get cross-VFS bind information
212    pub fn get_cross_vfs_info(&self) -> Option<(Weak<VfsManager>, &str, u64)> {
213        match &self.mount_type {
214            MountType::Bind { .. } => None,
215            _ => None,
216        }
217    }
218}
219
220/// Mount tree manager for VFS v2
221#[derive(Debug)]
222pub struct MountTree {
223    /// Root mount point (can be updated when mounting at "/")
224    pub root_mount: RwLock<Arc<MountPoint>>,
225}
226
227impl MountTree {
228    /// Create a new mount tree with the given root
229    pub fn new(root_entry: VfsEntryRef, root_fs: Arc<dyn FileSystemOperations>) -> Self {
230        let root_mount = MountPoint::new_regular("/".to_string(), root_entry, root_fs);
231        let root_id = root_mount.id;
232
233        let mut mounts = BTreeMap::new();
234        mounts.insert(root_id, Arc::downgrade(&root_mount));
235
236        Self {
237            root_mount: RwLock::new(root_mount.clone()),
238        }
239    }
240
241    /// Create a bind mount.
242    ///
243    /// # Arguments
244    /// * `source_entry` - The VFS entry to be mounted.
245    /// * `target_entry` - The VFS entry where the source will be mounted.
246    /// * `target_mount_point` - The mount point containing the target entry.
247    pub fn bind_mount(
248        &self,
249        source_entry: VfsEntryRef,
250        target_entry: VfsEntryRef,
251        target_mount_point: Arc<MountPoint>,
252    ) -> VfsResult<MountId> {
253        // Create a new bind mount point. The name of the mount point is the name of the target entry.
254        let bind_mount = MountPoint::new_bind(target_entry.name().clone(), source_entry);
255        let mount_id = bind_mount.id;
256
257        // Add the new mount as a child of the target's containing mount point, attached to the target entry.
258        target_mount_point.add_child(&target_entry, bind_mount.clone())?;
259
260        Ok(mount_id)
261    }
262
263    /// Mount a filesystem at a specific entry in the mount tree.
264    pub fn mount(
265        &self,
266        target_entry: VfsEntryRef,
267        target_mount_point: Arc<MountPoint>,
268        filesystem: Arc<dyn FileSystemOperations>,
269    ) -> VfsResult<MountId> {
270        // The root of the new filesystem.
271        let new_fs_root_node = filesystem.root_node();
272
273        // Create a VfsEntry for the root of the new filesystem.
274        let new_fs_root_entry = VfsEntry::new(None, "/".to_string(), new_fs_root_node);
275
276        // Create a new mount point for the filesystem.
277        let new_mount = MountPoint::new_regular(
278            target_entry.name().clone(),
279            new_fs_root_entry,
280            filesystem.clone(),
281        );
282        let mount_id = new_mount.id;
283
284        // Add the new mount as a child to the target's mount point.
285        target_mount_point.add_child(&target_entry, new_mount.clone())?;
286
287        Ok(mount_id)
288    }
289
290    /// Replaces the root mount point.
291    pub fn replace_root(&self, new_root: Arc<MountPoint>) {
292        *self.root_mount.write() = new_root.clone();
293    }
294
295    /// Check if a path is a mount point
296    ///
297    /// # Arguments
298    /// * `entry_to_check` - The VFS entry to check if it is a mount point.
299    /// * `mount_point_to_check` - The mount point to check against.
300    ///
301    /// # Notes
302    /// `entry_to_check` and `mount_point_to_check` should be in the same mount point.
303    pub fn is_mount_point(
304        &self,
305        entry_to_check: &VfsEntryRef,
306        mount_point_to_check: &Arc<MountPoint>,
307    ) -> bool {
308        // let node_to_check = entry_to_check.node();
309        // let node_id = node_to_check.id();
310
311        // let fs_ptr_to_check = match node_to_check.filesystem().and_then(|w| w.upgrade()) {
312        //     Some(fs) => Arc::as_ptr(&fs) as *const (),
313        //     None => return false,
314        // };
315
316        // for mount in self.mounts.read().values().filter_map(|w| w.upgrade()) {
317        //     if let Some(parent_entry) = &mount.parent_entry {
318        //         if parent_entry.node().id() == node_id {
319        //             let parent_fs_ptr = parent_entry.node().filesystem().and_then(|w| w.upgrade())
320        //                 .map(|fs| Arc::as_ptr(&fs) as *const ());
321        //             if parent_fs_ptr == Some(fs_ptr_to_check) {
322        //                 return true;
323        //             }
324        //         }
325        //     }
326        //
327
328        let children = mount_point_to_check.children.read();
329        children.contains_key(&entry_to_check.node().id())
330    }
331
332    /// Check if an entry is a source for a bind mount
333    pub fn is_bind_source(&self, entry_to_check: &VfsEntryRef) -> bool {
334        let node_to_check = entry_to_check.node();
335        let node_id = node_to_check.id();
336
337        let fs_ptr_to_check = match node_to_check.filesystem().and_then(|w| w.upgrade()) {
338            Some(fs) => Arc::as_ptr(&fs) as *const (),
339            None => return false,
340        };
341
342        false
343    }
344
345    /// Check if an entry is used in a mount (either as a mount point or a bind source)
346    pub fn is_entry_used_in_mount(
347        &self,
348        entry_to_check: &VfsEntryRef,
349        mount_point_to_check: &Arc<MountPoint>,
350    ) -> bool {
351        // self.is_mount_point(entry_to_check, mount_point_to_check) || self.is_bind_source(entry_to_check)
352        self.is_mount_point(entry_to_check, mount_point_to_check)
353    }
354
355    /// Unmount a filesystem
356    pub fn unmount(
357        &self,
358        entry: &VfsEntryRef,
359        parent_mount_point: &Arc<MountPoint>,
360    ) -> VfsResult<Arc<MountPoint>> {
361        let removed_mount = parent_mount_point.remove_child(&entry);
362        match removed_mount {
363            Some(mount) => Ok(mount),
364            None => Err(vfs_error(
365                FileSystemErrorKind::NotFound,
366                "Mount point not found for unmount",
367            )),
368        }
369    }
370
371    /// Resolve a path to a VFS entry (automatically handles absolute/relative)
372    pub fn resolve_path(&self, path: &str) -> VfsResult<(VfsEntryRef, Arc<MountPoint>)> {
373        self.resolve_path_from(None, None, path)
374    }
375
376    /// Resolve a path with specified options
377    pub fn resolve_path_with_options(
378        &self,
379        path: &str,
380        options: &PathResolutionOptions,
381    ) -> VfsResult<(VfsEntryRef, Arc<MountPoint>)> {
382        self.resolve_path_from_with_options(None, None, path, options)
383    }
384
385    // /// Resolve a path from a base entry and mount point
386    pub fn resolve_path_from(
387        &self,
388        base_entry: Option<&VfsEntryRef>,
389        base_mount: Option<&Arc<MountPoint>>,
390        path: &str,
391    ) -> VfsResult<(VfsEntryRef, Arc<MountPoint>)> {
392        self.resolve_path_from_with_options(
393            base_entry,
394            base_mount,
395            path,
396            &PathResolutionOptions::default(),
397        )
398    }
399
400    /// Unified path resolution from an optional base directory with options
401    pub fn resolve_path_from_with_options(
402        &self,
403        base_entry: Option<&VfsEntryRef>,
404        base_mount: Option<&Arc<MountPoint>>,
405        path: &str,
406        options: &PathResolutionOptions,
407    ) -> VfsResult<(VfsEntryRef, Arc<MountPoint>)> {
408        if path.starts_with('/') {
409            // Absolute path - ignore base and resolve from root
410            self.resolve_path_internal(path, false, options)
411        } else if let (Some(entry), Some(mount)) = (base_entry, base_mount) {
412            // Relative path with explicit base
413            self.resolve_path_from_internal(entry, mount, path, false, options)
414        } else {
415            Err(vfs_error(
416                FileSystemErrorKind::InvalidPath,
417                "Relative path resolution requires base entry and mount",
418            ))
419        }
420    }
421
422    /// Resolve a path to the mount point entry (not the mounted content)
423    /// This is used for unmount operations where we need the actual mount point
424    pub fn resolve_mount_point(&self, path: &str) -> VfsResult<(VfsEntryRef, Arc<MountPoint>)> {
425        // Use the same resolution logic but ensure we resolve to the mount point
426        self.resolve_mount_point_with_options(path, &PathResolutionOptions::default())
427    }
428
429    /// Resolve a path to the mount point entry with options
430    pub fn resolve_mount_point_with_options(
431        &self,
432        path: &str,
433        options: &PathResolutionOptions,
434    ) -> VfsResult<(VfsEntryRef, Arc<MountPoint>)> {
435        self.resolve_mount_point_from_with_options(None, None, path, options)
436    }
437
438    pub fn resolve_mount_point_from(
439        &self,
440        base_entry: &VfsEntryRef,
441        base_mount: &Arc<MountPoint>,
442        path: &str,
443    ) -> VfsResult<(VfsEntryRef, Arc<MountPoint>)> {
444        self.resolve_mount_point_from_with_options(
445            Some(base_entry),
446            Some(base_mount),
447            path,
448            &PathResolutionOptions::default(),
449        )
450    }
451
452    pub fn resolve_mount_point_from_with_options(
453        &self,
454        base_entry: Option<&VfsEntryRef>,
455        base_mount: Option<&Arc<MountPoint>>,
456        path: &str,
457        options: &PathResolutionOptions,
458    ) -> VfsResult<(VfsEntryRef, Arc<MountPoint>)> {
459        if path.starts_with('/') {
460            // Absolute path - ignore base and resolve from root
461            self.resolve_path_internal(path, true, options)
462        } else if let (Some(entry), Some(mount)) = (base_entry, base_mount) {
463            // Relative path with explicit base
464            self.resolve_path_from_internal(entry, mount, path, true, options)
465        } else {
466            Err(vfs_error(
467                FileSystemErrorKind::InvalidPath,
468                "Relative path resolution requires base entry and mount",
469            ))
470        }
471    }
472
473    fn resolve_path_internal(
474        &self,
475        path: &str,
476        resolve_mount: bool,
477        options: &PathResolutionOptions,
478    ) -> VfsResult<(VfsEntryRef, Arc<MountPoint>)> {
479        // Check the path is a valid absolute path
480        assert!(
481            path.starts_with('/'),
482            "resolve_path_internal: Path must be absolute, got '{}'",
483            path
484        );
485
486        let root_mount = self.root_mount.read();
487        let root_entry = root_mount.root.clone();
488        self.resolve_path_from_internal(&root_entry, &root_mount, path, resolve_mount, options)
489    }
490
491    fn resolve_path_from_internal(
492        &self,
493        base_entry: &VfsEntryRef,
494        base_mount: &Arc<MountPoint>,
495        path: &str,
496        resolve_mount: bool,
497        options: &PathResolutionOptions,
498    ) -> VfsResult<(VfsEntryRef, Arc<MountPoint>)> {
499        if path.is_empty() {
500            return Ok((base_entry.clone(), base_mount.clone()));
501        } else if path == "/" {
502            // Special case for root path,
503            return Ok((
504                self.root_mount.read().root.clone(),
505                self.root_mount.read().clone(),
506            ));
507        }
508
509        let components = self.parse_path(path);
510        let mut current_mount = base_mount.clone();
511        let mut current_entry = base_entry.clone();
512
513        let mut resolved_path = String::new();
514        for (i, component) in components.iter().enumerate() {
515            let is_final_component = i == components.len() - 1;
516
517            if component == ".." {
518                // Handle parent directory traversal (same as original implementation)
519                let is_at_mount_root = current_entry.node().id() == current_mount.root.node().id();
520
521                if is_at_mount_root {
522                    let parent_info = current_mount
523                        .get_parent()
524                        .zip(current_mount.parent_entry.clone());
525                    match parent_info {
526                        Some((parent_mount, parent_entry)) => {
527                            current_mount = parent_mount;
528                            current_entry = self.resolve_component(parent_entry, &"..")?;
529                        }
530                        None => {
531                            // No parent mount - stay at current mount root
532                        }
533                    }
534                } else {
535                    current_entry = self.resolve_component(current_entry, &component)?;
536                }
537            } else {
538                // Regular path traversal with symlink handling based on options
539                let should_follow_symlinks = if is_final_component {
540                    // For the final component, check no_follow option
541                    !options.no_follow
542                } else {
543                    // For intermediate components, always follow symlinks
544                    true
545                };
546
547                if should_follow_symlinks {
548                    // Use normal component resolution (which follows symlinks)
549                    current_entry = self.resolve_component(current_entry, &component)?;
550                } else {
551                    // Don't follow symlinks - use direct filesystem lookup
552                    current_entry = self.resolve_component_no_symlink(current_entry, &component)?;
553                }
554
555                // Handle mount points (same as original implementation)
556                if resolve_mount && is_final_component {
557                    if let Some(_child_mount) = current_mount.get_child(&current_entry) {
558                        return Ok((current_entry, current_mount));
559                    }
560                } else {
561                    if let Some(child_mount) = current_mount.get_child(&current_entry) {
562                        current_mount = child_mount;
563                        current_entry = current_mount.root.clone();
564                    }
565                }
566            }
567
568            resolved_path.push('/');
569            resolved_path.push_str(&component);
570        }
571
572        Ok((current_entry, current_mount))
573    }
574
575    /// Resolve a single path component without following symlinks
576    fn resolve_component_no_symlink(
577        &self,
578        entry: VfsEntryRef,
579        component: &str,
580    ) -> VfsResult<VfsEntryRef> {
581        // Handle special cases
582        if component == "." {
583            return Ok(entry);
584        }
585
586        // Check cache first (fast path)
587        let component_string = component.to_string();
588        if let Some(cached_child) = entry.get_child(&component_string) {
589            return Ok(cached_child);
590        }
591
592        // Cache miss - perform filesystem lookup without symlink resolution
593        let parent_node = entry.node();
594        debug_assert!(
595            parent_node.filesystem().is_some(),
596            "resolve_component_no_symlink: parent_node.filesystem() is None"
597        );
598        let filesystem = parent_node
599            .filesystem()
600            .and_then(|w| w.upgrade())
601            .ok_or_else(|| {
602                vfs_error(FileSystemErrorKind::NotSupported, "No filesystem reference")
603            })?;
604
605        // Ask filesystem to lookup the component
606        let child_node = filesystem
607            .lookup(&parent_node, &component_string)
608            .map_err(|e| vfs_error(e.kind, &e.message))?;
609
610        // Don't resolve symlinks - just create VfsEntry as-is
611        let child_entry = VfsEntry::new(
612            Some(Arc::downgrade(&entry)),
613            component_string.clone(),
614            child_node,
615        );
616
617        // Add to parent's cache
618        entry.add_child(component_string, child_entry.clone());
619
620        Ok(child_entry)
621    }
622
623    // Helper methods
624
625    /// Parse a path into components
626    pub fn parse_path(&self, path: &str) -> Vec<String> {
627        path.split('/')
628            .filter(|s| !s.is_empty() && *s != ".")
629            .map(|s| s.to_string())
630            .collect()
631    }
632
633    /// Get the full path of a mount point
634    fn get_mount_path(&self, mount: &Arc<MountPoint>) -> String {
635        if mount.is_root_mount() {
636            return "/".to_string();
637        }
638
639        let mut components = Vec::new();
640        let mut current = Some(mount.clone());
641
642        while let Some(mount) = current {
643            if !mount.is_root_mount() {
644                components.push(mount.path.clone());
645                current = mount.get_parent();
646            } else {
647                break;
648            }
649        }
650
651        components.reverse();
652        if components.is_empty() {
653            "/".to_string()
654        } else {
655            format!("/{}", components.join("/"))
656        }
657    }
658
659    /// Get the full absolute path of a mount point
660    ///
661    /// This method constructs the complete absolute path for a given mount point
662    /// by traversing the mount tree hierarchy.
663    ///
664    /// # Arguments
665    /// * `mount` - The mount point to get the path for
666    ///
667    /// # Returns
668    /// A `String` containing the absolute mount path
669    pub fn get_mount_absolute_path(&self, mount: &Arc<MountPoint>) -> String {
670        self.get_mount_path(mount)
671    }
672
673    /// Resolve a single path component within a VFS entry
674    fn resolve_component(&self, entry: VfsEntryRef, component: &str) -> VfsResult<VfsEntryRef> {
675        self.resolve_component_with_depth(entry, component, 0)
676    }
677
678    /// Resolve a single path component with symlink depth tracking
679    fn resolve_component_with_depth(
680        &self,
681        entry: VfsEntryRef,
682        component: &str,
683        symlink_depth: u32,
684    ) -> VfsResult<VfsEntryRef> {
685        const MAX_SYMLINK_DEPTH: u32 = 32; // Prevent infinite symlink loops
686
687        if symlink_depth > MAX_SYMLINK_DEPTH {
688            return Err(vfs_error(
689                FileSystemErrorKind::InvalidPath,
690                "Too many symbolic links",
691            ));
692        }
693
694        // Handle special cases
695        if component == "." {
696            return Ok(entry);
697        }
698
699        // Check cache first (fast path)
700        let component_string = component.to_string();
701        if let Some(cached_child) = entry.get_child(&component_string) {
702            // Check if cached entry is a symlink that needs resolution
703            if cached_child.node().is_symlink()? {
704                let link_target = cached_child
705                    .node()
706                    .read_link()
707                    .map_err(|e| vfs_error(e.kind, &e.message))?;
708                return self.resolve_symlink_target_with_depth(
709                    &entry,
710                    &link_target,
711                    symlink_depth + 1,
712                );
713            }
714            return Ok(cached_child);
715        }
716
717        // Cache miss - perform filesystem lookup
718        let parent_node = entry.node();
719        debug_assert!(
720            parent_node.filesystem().is_some(),
721            "resolve_component: parent_node.filesystem() is None"
722        );
723        let filesystem = parent_node
724            .filesystem()
725            .and_then(|w| w.upgrade())
726            .ok_or_else(|| {
727                vfs_error(FileSystemErrorKind::NotSupported, "No filesystem reference")
728            })?;
729        // Ask filesystem to lookup the component
730        let child_node = filesystem
731            .lookup(&parent_node, &component_string)
732            .map_err(|e| vfs_error(e.kind, &e.message))?;
733
734        // Check if the resolved node is a symbolic link
735        if child_node.is_symlink()? {
736            // Resolve the symbolic link
737            let link_target = child_node
738                .read_link()
739                .map_err(|e| vfs_error(e.kind, &e.message))?;
740
741            // Recursively resolve the link target
742            return self.resolve_symlink_target_with_depth(&entry, &link_target, symlink_depth + 1);
743        }
744
745        // Create new VfsEntry for the child
746        let child_entry = VfsEntry::new(
747            Some(Arc::downgrade(&entry)),
748            component_string.clone(),
749            child_node,
750        );
751
752        // Add to parent's cache
753        entry.add_child(component_string, child_entry.clone());
754
755        Ok(child_entry)
756    }
757
758    /// Resolve a symbolic link target
759    fn resolve_symlink_target(
760        &self,
761        base_entry: &VfsEntryRef,
762        target: &str,
763    ) -> VfsResult<VfsEntryRef> {
764        self.resolve_symlink_target_with_depth(base_entry, target, 0)
765    }
766
767    /// Resolve a symbolic link target with depth tracking
768    fn resolve_symlink_target_with_depth(
769        &self,
770        base_entry: &VfsEntryRef,
771        target: &str,
772        symlink_depth: u32,
773    ) -> VfsResult<VfsEntryRef> {
774        const MAX_SYMLINK_DEPTH: u32 = 32; // Prevent infinite symlink loops
775
776        if symlink_depth > MAX_SYMLINK_DEPTH {
777            return Err(vfs_error(
778                FileSystemErrorKind::InvalidPath,
779                "Too many symbolic links",
780            ));
781        }
782
783        if target.starts_with('/') {
784            // Absolute path - resolve from root
785            let (resolved_entry, _mount) = self.resolve_path_internal(
786                target,
787                false,
788                &PathResolutionOptions { no_follow: false },
789            )?;
790            Ok(resolved_entry)
791        } else {
792            // Relative path - resolve from current directory
793            let components = self.parse_path(target);
794            let mut current_entry = base_entry.clone();
795
796            for component in components {
797                current_entry =
798                    self.resolve_component_with_depth(current_entry, &component, symlink_depth)?;
799            }
800
801            Ok(current_entry)
802        }
803    }
804
805    // /// Resolve cross-VFS path for bind mounts
806    // fn resolve_cross_vfs_path(
807    //     &self,
808    //     mount_point: &MountPoint,
809    //     relative_path: &str
810    // ) -> VfsResult<(VfsEntryRef, Arc<MountPoint>)> {
811    //     if let Some((source_vfs, source_path, _cache_timeout)) = mount_point.get_cross_vfs_info() {
812    //         let source_vfs = source_vfs.upgrade()
813    //             .ok_or_else(|| vfs_error(FileSystemErrorKind::NotFound, "Source VFS no longer available"))?;
814
815    //         let full_source_path = if relative_path.is_empty() || relative_path == "/" {
816    //             source_path.to_string()
817    //         } else {
818    //             format!("{}/{}", source_path.trim_end_matches('/'), relative_path.trim_start_matches('/'))
819    //         };
820
821    //         // Delegate to source VFS for complete resolution (including child mounts)
822    //         source_vfs.resolve_path_cross_vfs(&full_source_path)
823    //     } else {
824    //         Err(vfs_error(FileSystemErrorKind::NotSupported, "Not a cross-VFS mount"))
825    //     }
826    // }
827}