kernel/fs/mount_tree.rs
1//! Mount Tree Implementation
2//!
3//! This module provides a Trie-based mount point management system
4//! for efficient path resolution and hierarchical mount point organization.
5
6use alloc::{collections::BTreeMap, string::String, sync::Arc, vec::Vec};
7use super::*;
8
9/// Mount point management using Trie structure
10///
11/// MountTree provides efficient hierarchical mount point management using a Trie
12/// data structure. This enables O(log k) path resolution where k is the path depth,
13/// making filesystem operations scale well with complex mount hierarchies.
14///
15/// # Features
16///
17/// - **Efficient Path Resolution**: Trie-based lookup for fast mount point discovery
18/// - **Bind Mount Support**: Advanced bind mounting with cross-VFS capability
19/// - **Security**: Enhanced path normalization preventing directory traversal attacks
20/// - **Caching**: Optional path caching for frequently accessed mount points
21/// - **Thread Safety**: All operations are thread-safe using RwLock protection
22///
23/// # Architecture
24///
25/// The mount tree consists of:
26/// - Root node representing the filesystem root "/"
27/// - Internal nodes for directory components
28/// - Leaf nodes containing actual mount points
29/// - Path cache for performance optimization
30///
31/// # Usage
32///
33/// ```rust
34/// let mut mount_tree = MountTree::new();
35///
36/// // Mount a filesystem
37/// mount_tree.mount("/mnt/data", mount_point)?;
38///
39/// // Resolve a path to its mount point
40/// let (mount_node, relative_path) = mount_tree.resolve("/mnt/data/file.txt")?;
41/// ```
42#[derive(Clone)]
43pub struct MountTree {
44 root: Arc<MountNode>,
45 /// Cache for fast lookup
46 path_cache: BTreeMap<String, Arc<MountPoint>>,
47}
48
49/// Mount tree node representing a single component in the filesystem hierarchy
50///
51/// Each MountNode represents a directory component in the filesystem path.
52/// Nodes can optionally contain mount points and have child nodes for
53/// subdirectories. The tree structure enables efficient path traversal
54/// and mount point resolution.
55///
56/// # Thread Safety
57///
58/// All fields are protected by RwLock to ensure thread-safe access in
59/// a multi-threaded kernel environment.
60pub struct MountNode {
61 /// Path component
62 #[allow(dead_code)]
63 pub component: RwLock<String>,
64 /// Mount information if this node is a mount point
65 mount_point: RwLock<Option<Arc<MountPoint>>>,
66 /// Child nodes
67 children: RwLock<BTreeMap<String, Arc<MountNode>>>,
68}
69
70impl MountNode {
71 /// Create a new mount node with the specified path component
72 ///
73 /// # Arguments
74 ///
75 /// * `component` - The path component name for this node
76 ///
77 /// # Returns
78 ///
79 /// A new MountNode instance with the given component name
80 fn new(component: String) -> Self {
81 Self {
82 component: RwLock::new(component),
83 mount_point: RwLock::new(None),
84 children: RwLock::new(BTreeMap::new()),
85 }
86 }
87
88 /// Get the mount point associated with this node
89 ///
90 /// # Returns
91 /// * `Ok(Arc<MountPoint>)` - The mount point if it exists.
92 /// * `Err(FileSystemError)` - If no mount point is found.
93 ///
94 pub fn get_mount_point(&self) -> Result<Arc<MountPoint>> {
95 let mount_guard = self.mount_point.read();
96 if let Some(mount_point) = &*mount_guard {
97 Ok(mount_point.clone())
98 } else {
99 Err(FileSystemError {
100 kind: FileSystemErrorKind::NotFound,
101 message: "No mount point found".to_string(),
102 })
103 }
104 }
105
106 /// Resolve path within this mount node and its children (transparent resolution)
107 ///
108 /// This method resolves the given path components starting from this node.
109 /// For bind mounts, it recursively resolves through the source mount tree
110 /// to find the deepest actual mount that handles the requested path.
111 ///
112 /// This is an internal method that operates on Arc<MountNode>
113 ///
114 /// # Arguments
115 /// * `self_arc` - Arc reference to this node
116 /// * `components` - The path components to resolve.
117 /// * `depth` - Current recursion depth to prevent infinite loops.
118 ///
119 /// # Returns
120 /// * `Ok(Some((Arc<MountNode>, String)))` - A tuple containing the resolved mount node and the relative path.
121 /// * `Ok(None)` - If this node is the final target (self-resolution).
122 /// * `Err(FileSystemError)` - If the path is invalid or no mount point is found.
123 ///
124 fn resolve_internal(self_arc: Arc<MountNode>, components: &[String], depth: usize) -> Result<Option<(Arc<MountNode>, String)>> {
125 // Prevent infinite recursion in bind mount chains
126 if depth > 32 {
127 return Err(FileSystemError {
128 kind: FileSystemErrorKind::NotSupported,
129 message: "Too many bind mount redirections".to_string(),
130 });
131 }
132
133 // crate::println!("Resolving internal mount point for path: {}, depth: {}", components.join("/"), depth);
134
135 // If no components left, check if this node is a mount point
136 if components.is_empty() {
137 // crate::println!("No components left, checking if this node is a mount point");
138 let mount_guard = self_arc.mount_point.read();
139 if mount_guard.is_some() {
140 // This node is a mount point and is the target
141 return Ok(None);
142 } else {
143 // No mount point at this location
144 return Err(FileSystemError {
145 kind: FileSystemErrorKind::NotFound,
146 message: "No mount point found".to_string(),
147 });
148 }
149 }
150
151 // Start with self
152 let mut current_node = self_arc.clone();
153 let mut best_match_node: Option<Arc<MountNode>> = None;
154 let mut match_depth = 0;
155
156 // Check if current node is a mount point
157 {
158 let mount_guard = current_node.mount_point.read();
159 if mount_guard.is_some() {
160 best_match_node = Some(current_node.clone());
161 match_depth = 0;
162
163 // crate::println!("Current node is a mount point: {}", current_node.component.read().as_str());
164 }
165 }
166
167 // Traverse components to find the deepest mount point
168 for (depth_idx, component) in components.iter().enumerate() {
169 // Move to next node
170 let next_node = {
171 let children_guard = current_node.children.read();
172 if let Some(child) = children_guard.get(component) {
173 child.clone()
174 } else {
175 break;
176 }
177 };
178 current_node = next_node;
179
180 // Check if the moved-to node is a mount point
181 {
182 let mount_guard = current_node.mount_point.read();
183 if mount_guard.is_some() {
184 best_match_node = Some(current_node.clone());
185 match_depth = depth_idx + 1; // +1 for depth after movement
186 }
187 }
188 }
189
190 let resolved_node = best_match_node.ok_or(FileSystemError {
191 kind: FileSystemErrorKind::NotFound,
192 message: "No mount point found in this subtree".to_string(),
193 })?;
194
195 // Build relative path
196 let relative_components = &components[match_depth..];
197 let relative_path = if relative_components.is_empty() {
198 "/".to_string()
199 } else {
200 format!("/{}", relative_components.join("/"))
201 };
202
203 // Check if this is a bind mount and needs further resolution
204 let mount_point = resolved_node.get_mount_point()?;
205 match &mount_point.mount_type {
206 MountType::Bind { source_mount_node, source_relative_path, .. } => {
207 // Construct the full source path
208 let full_source_path = match (relative_path.as_str(), source_relative_path.as_str()) {
209 ("/", _) => source_relative_path.clone(),
210 (_, "/") => relative_path.clone(),
211 (rel, src) => {
212 let src_trimmed = src.trim_end_matches('/');
213 let rel_trimmed = rel.trim_start_matches('/');
214 format!("{}/{}", src_trimmed, rel_trimmed)
215 }
216 };
217
218 // Split the full source path into components
219 let source_components: Vec<String> = if full_source_path == "/" {
220 Vec::new()
221 } else {
222 full_source_path.trim_start_matches('/')
223 .split('/')
224 .filter(|s| !s.is_empty())
225 .map(|s| s.to_string())
226 .collect()
227 };
228
229 // Recursively resolve through the source mount node
230 let result = MountNode::resolve_internal(source_mount_node.clone(), &source_components, depth + 1)?;
231 let (final_node, final_relative_path) = match result {
232 Some((node, path)) => (node, path),
233 None => {
234 // If None, use the source mount node itself
235 (source_mount_node.clone(), "/".to_string())
236 }
237 };
238
239 // Verify that the final node is not another bind mount to the same location
240 // to prevent infinite loops
241 let final_mount_point = final_node.get_mount_point()?;
242 match &final_mount_point.mount_type {
243 MountType::Bind { .. } => {
244 // If we still have a bind mount, check if it's different from where we started
245 if Arc::ptr_eq(&final_node, &resolved_node) {
246 return Err(FileSystemError {
247 kind: FileSystemErrorKind::NotSupported,
248 message: "Circular bind mount detected".to_string(),
249 });
250 }
251 }
252 _ => {}
253 }
254
255 Ok(Some((final_node, final_relative_path)))
256 }
257 _ => {
258 // Regular mount or overlay - return as-is
259 Ok(Some((resolved_node, relative_path)))
260 }
261 }
262 }
263
264 /// Resolve path within this mount node and its children (non-transparent resolution)
265 ///
266 /// This method resolves the given path components starting from this node without
267 /// resolving through bind mounts. This is useful for mount management operations
268 /// where you need to work with the bind mount node itself.
269 ///
270 /// This is an internal method that operates on Arc<MountNode>
271 ///
272 /// # Arguments
273 /// * `self_arc` - Arc reference to this node
274 /// * `components` - The path components to resolve.
275 ///
276 /// # Returns
277 /// * `Ok(Some((Arc<MountNode>, String)))` - A tuple containing the mount node and the relative path.
278 /// * `Ok(None)` - If this node is the final target (self-resolution).
279 /// * `Err(FileSystemError)` - If the path is invalid or no mount point is found.
280 ///
281 fn resolve_non_transparent_internal(self_arc: Arc<MountNode>, components: &[String]) -> Result<Option<(Arc<MountNode>, String)>> {
282 // If no components left, check if this node is a mount point
283 if components.is_empty() {
284 let mount_guard = self_arc.mount_point.read();
285 if mount_guard.is_some() {
286 // This node is a mount point and is the target
287 return Ok(None);
288 } else {
289 // No mount point at this location
290 return Err(FileSystemError {
291 kind: FileSystemErrorKind::NotFound,
292 message: "No mount point found".to_string(),
293 });
294 }
295 }
296
297 // Start with self
298 let mut current_node = self_arc.clone();
299 let mut best_match_node: Option<Arc<MountNode>> = None;
300 let mut match_depth = 0;
301
302 // Check if current node is a mount point
303 {
304 let mount_guard = current_node.mount_point.read();
305 if mount_guard.is_some() {
306 best_match_node = Some(current_node.clone());
307 match_depth = 0;
308 }
309 }
310
311 // Traverse components to find the deepest mount point
312 for (depth, component) in components.iter().enumerate() {
313 // Move to next node
314 let next_node = {
315 let children_guard = current_node.children.read();
316 if let Some(child) = children_guard.get(component) {
317 child.clone()
318 } else {
319 break;
320 }
321 };
322 current_node = next_node;
323
324 // Check if the moved-to node is a mount point
325 {
326 let mount_guard = current_node.mount_point.read();
327 if mount_guard.is_some() {
328 best_match_node = Some(current_node.clone());
329 match_depth = depth + 1; // +1 for depth after movement
330 }
331 }
332 }
333
334 let resolved_node = best_match_node.ok_or(FileSystemError {
335 kind: FileSystemErrorKind::NotFound,
336 message: "No mount point found in this subtree".to_string(),
337 })?;
338
339 // Build relative path
340 let relative_components = &components[match_depth..];
341 let relative_path = if relative_components.is_empty() {
342 "/".to_string()
343 } else {
344 format!("/{}", relative_components.join("/"))
345 };
346
347 // Return the mount node without resolving bind mounts
348 Ok(Some((resolved_node, relative_path)))
349 }
350}
351
352/// Extended mount point information
353///
354/// MountPoint contains comprehensive information about a mounted filesystem,
355/// including metadata, mount options, and relationship information for
356/// hierarchical mount management.
357///
358/// # Features
359///
360/// - **Filesystem Integration**: Direct reference to the mounted filesystem
361/// - **Mount Hierarchy**: Parent/child relationships for mount tree management
362/// - **Security Options**: Configurable mount options for access control
363/// - **Bind Mount Support**: Resolves bind mount chains to actual filesystems
364/// - **Metadata Tracking**: Mount time and filesystem identification
365///
366/// # Thread Safety
367///
368/// MountPoint is designed to be shared safely between threads using Arc
369/// wrapper when stored in the mount tree.
370#[derive(Clone)]
371pub struct MountPoint {
372 /// Absolute mount path in the filesystem hierarchy
373 pub path: String,
374 /// Reference to the actual filesystem implementation
375 pub fs: super::FileSystemRef,
376 /// VfsManager-assigned filesystem identifier
377 pub fs_id: usize,
378 /// Type of mount (regular, bind, overlay)
379 pub mount_type: MountType,
380 /// Security and behavior options for this mount
381 pub mount_options: MountOptions,
382 /// Parent mount path (None for root mount)
383 pub parent: Option<String>,
384 /// List of child mount paths
385 pub children: Vec<String>,
386 /// Timestamp when this mount was created
387 pub mount_time: u64,
388}
389
390impl MountPoint {
391 /// Resolve filesystem and internal path from MountPoint
392 ///
393 /// This method resolves bind mount chains to find the actual filesystem
394 /// that handles the requested path. For regular mounts, it returns the
395 /// filesystem directly. For bind mounts, it recursively follows the
396 /// bind chain to the source filesystem.
397 ///
398 /// # Arguments
399 ///
400 /// * `relative_path` - Path relative to this mount point
401 ///
402 /// # Returns
403 ///
404 /// * `Ok((FileSystemRef, String))` - The actual filesystem and the resolved path within it
405 /// * `Err(FileSystemError)` - If bind mount resolution fails or exceeds recursion limit
406 ///
407 /// # Note
408 ///
409 /// This method only supports Regular, Tmpfs, and Overlay mounts.
410 /// Bind mounts are resolved transparently to their source filesystems.
411 pub fn resolve_fs(&self, relative_path: &str) -> Result<(super::FileSystemRef, String)> {
412 self.resolve_fs_with_depth(relative_path, 0)
413 }
414
415 /// Internal method for bind mount resolution with recursion depth tracking
416 ///
417 /// This method prevents infinite recursion in circular bind mount chains
418 /// by limiting the maximum recursion depth to 32 levels.
419 ///
420 /// # Arguments
421 ///
422 /// * `relative_path` - Path relative to this mount point
423 /// * `depth` - Current recursion depth for loop detection
424 ///
425 /// # Returns
426 ///
427 /// * `Ok((FileSystemRef, String))` - The actual filesystem and resolved path
428 /// * `Err(FileSystemError)` - If recursion limit exceeded or resolution fails
429 fn resolve_fs_with_depth(&self, relative_path: &str, depth: usize) -> Result<(super::FileSystemRef, String)> {
430 // Prevent circular references
431 if depth > 32 {
432 return Err(FileSystemError {
433 kind: FileSystemErrorKind::NotSupported,
434 message: "Too many bind mount redirections".to_string(),
435 });
436 }
437
438 match &self.mount_type {
439 MountType::Regular | MountType::Overlay { .. } => {
440 // Regular mount: return filesystem as-is
441 Ok((self.fs.clone(), relative_path.to_string()))
442 }
443
444 MountType::Bind { source_mount_node, source_relative_path, .. } => {
445 // Combine paths
446 let full_source_path = match (relative_path, source_relative_path.as_str()) {
447 ("/", _) => source_relative_path.clone(),
448 (_, "/") => relative_path.to_string().clone(),
449 (rel, src) => {
450 let src_trimmed = src.trim_end_matches('/');
451 let rel_trimmed = rel.trim_start_matches('/');
452 format!("{}/{}", src_trimmed, rel_trimmed)
453 }
454 };
455
456 // Recursively resolve with source MountNode
457 let source_mount_point = source_mount_node.get_mount_point()?;
458 source_mount_point.resolve_fs_with_depth(&full_source_path, depth + 1)
459 }
460 }
461 }
462}
463
464/// Mount type classification for different mount strategies
465///
466/// This enum defines the various mount types supported by the VFS system,
467/// each with different behaviors and resource handling approaches.
468#[derive(Clone)]
469pub enum MountType {
470 /// Regular filesystem mount
471 ///
472 /// Standard mount where the filesystem directly handles all operations
473 /// at the mount point. This is the most common mount type.
474 Regular,
475
476 /// Bind mount - maps one directory tree to another location
477 ///
478 /// Bind mounts allow the same filesystem content to be accessible
479 /// from multiple mount points. They support:
480 /// - Cross-VFS sharing for container resource sharing
481 /// - Read-only restrictions for security
482 /// - Shared propagation for namespace management
483 Bind {
484 /// Source mount node that provides the actual filesystem
485 source_mount_node: Arc<MountNode>,
486 /// Relative path within the source filesystem
487 source_relative_path: String,
488 /// Type of bind mount (read-only, read-write, shared)
489 bind_type: BindType,
490 },
491
492 /// Overlay filesystem mount
493 ///
494 /// Combines multiple filesystem layers into a unified view,
495 /// typically used for container images and copy-on-write scenarios.
496 Overlay {
497 /// Lower filesystem layers (read-only)
498 lower_layers: Vec<String>,
499 /// Upper layer for writes
500 upper_layer: String,
501 /// Working directory for overlay operations
502 work_dir: String,
503 },
504}
505
506/// Bind mount type specifying access and propagation behavior
507///
508/// Different bind types provide various levels of access control
509/// and mount propagation for container and namespace isolation.
510#[derive(Clone, Copy, PartialEq, Eq)]
511pub enum BindType {
512 /// Read-only bind mount - prevents write operations
513 ReadOnly,
514 /// Read-write bind mount - allows full access
515 ReadWrite,
516 /// Shared bind mount - propagates mount events to other namespaces
517 Shared,
518}
519
520/// Mount options controlling filesystem behavior and security
521///
522/// These options provide fine-grained control over filesystem access
523/// and can be used to enhance security in containerized environments.
524#[derive(Clone)]
525pub struct MountOptions {
526 /// Prevent write operations on this mount
527 pub read_only: bool,
528 /// Disable execution of binaries on this mount
529 pub no_exec: bool,
530 /// Disable set-uid/set-gid bits on this mount
531 pub no_suid: bool,
532 /// Disable device file access on this mount
533 pub no_dev: bool,
534 /// Force synchronous I/O operations
535 pub sync: bool,
536}
537
538impl Default for MountOptions {
539 fn default() -> Self {
540 Self {
541 read_only: false,
542 no_exec: false,
543 no_suid: false,
544 no_dev: false,
545 sync: false,
546 }
547 }
548}
549
550impl MountTree {
551 /// Create a new empty mount tree
552 ///
553 /// Initializes a new mount tree with an empty root node and no cached paths.
554 /// This creates the foundation for a new filesystem namespace.
555 ///
556 /// # Returns
557 ///
558 /// A new MountTree instance ready for mount operations
559 pub fn new() -> Self {
560 Self {
561 root: Arc::new(MountNode::new("".to_string())),
562 path_cache: BTreeMap::new(),
563 }
564 }
565
566 /// Add mount point (VfsManager interface)
567 ///
568 /// This method provides the primary interface for VfsManager to add new
569 /// mount points to the tree. It normalizes the path and delegates to
570 /// the internal insert method.
571 ///
572 /// # Arguments
573 ///
574 /// * `path` - Absolute path where the filesystem should be mounted
575 /// * `mount_point` - MountPoint structure containing mount information
576 ///
577 /// # Returns
578 ///
579 /// * `Ok(())` - Mount operation succeeded
580 /// * `Err(FileSystemError)` - If path is invalid or mount point already exists
581 pub fn mount(&mut self, path: &str, mount_point: MountPoint) -> Result<()> {
582 self.insert(path, mount_point)
583 }
584
585 /// Internal method to add mount point to the tree
586 ///
587 /// This method handles the actual insertion logic, creating intermediate
588 /// nodes as needed and validating that mount points don't conflict.
589 ///
590 /// # Arguments
591 ///
592 /// * `path` - Normalized absolute path for the mount
593 /// * `mount_point` - MountPoint structure to insert
594 ///
595 /// # Returns
596 ///
597 /// * `Ok(())` - Mount point successfully added
598 /// * `Err(FileSystemError)` - If mount point already exists at the path
599 pub fn insert(&mut self, path: &str, mount_point: MountPoint) -> Result<()> {
600 let normalized = Self::normalize_path(path)?;
601 let components = self.split_path(&normalized);
602
603 // Traverse path using Trie structure
604 let mut current_arc = self.root.clone();
605 for component in &components {
606 let next_arc = {
607 let mut children = current_arc.children.write();
608 children.entry(component.clone())
609 .or_insert_with(|| Arc::new(MountNode::new(component.clone())))
610 .clone()
611 };
612 current_arc = next_arc;
613 }
614
615 // Return error if mount point already exists
616 if current_arc.mount_point.read().is_some() {
617 return Err(FileSystemError {
618 kind: FileSystemErrorKind::AlreadyExists,
619 message: format!("Mount point {} already exists", path),
620 });
621 }
622
623 // Set mount point
624 let mount_point_arc = Arc::new(mount_point);
625 *current_arc.mount_point.write() = Some(mount_point_arc.clone());
626 self.path_cache.insert(normalized, mount_point_arc);
627
628 Ok(())
629 }
630
631 /// Find mount point by path (transparent resolution - resolves through bind mounts)
632 ///
633 /// This method resolves the given path to its corresponding mount point.
634 /// For bind mounts, it recursively resolves through the source mount tree
635 /// to find the deepest actual mount that handles the requested path.
636 ///
637 /// # Arguments
638 /// * `path` - The absolute path to resolve.
639 ///
640 /// # Returns
641 /// * `Ok((Arc<MountNode>, String))` - A tuple containing the resolved mount node and the relative path.
642 /// * `Err(FileSystemError)` - If the path is invalid or no mount point is found.
643 ///
644 pub fn resolve(&self, path: &str) -> Result<(Arc<MountNode>, String)> {
645 let normalized = Self::normalize_path(path)?;
646 let components = self.split_path(&normalized);
647
648 let result = MountNode::resolve_internal(self.root.clone(), &components, 0)?;
649 match result {
650 Some((node, path)) => Ok((node, path)),
651 None => {
652 // If None, use the root node itself with empty path
653 Ok((self.root.clone(), "/".to_string()))
654 }
655 }
656 }
657
658 /// Find mount point by path (non-transparent resolution - stops at bind mount nodes)
659 ///
660 /// This method resolves the given path to its corresponding mount point without
661 /// resolving through bind mounts. This is useful for mount management operations
662 /// where you need to work with the bind mount node itself.
663 ///
664 /// # Arguments
665 /// * `path` - The absolute path to resolve.
666 ///
667 /// # Returns
668 /// * `Ok((Arc<MountNode>, String))` - A tuple containing the mount node and the relative path.
669 /// * `Err(FileSystemError)` - If the path is invalid or no mount point is found.
670 ///
671 pub fn resolve_non_transparent(&self, path: &str) -> Result<(Arc<MountNode>, String)> {
672 let normalized = Self::normalize_path(path)?;
673 let components = self.split_path(&normalized);
674
675 let result = MountNode::resolve_non_transparent_internal(self.root.clone(), &components)?;
676 match result {
677 Some((node, path)) => Ok((node, path)),
678 None => {
679 // If None, use the root node itself with empty path
680 Ok((self.root.clone(), String::new()))
681 }
682 }
683 }
684
685 /// Remove mount point from the tree
686 ///
687 /// Removes a mount point at the specified path and returns the removed
688 /// MountPoint for cleanup. This operation also removes the path from
689 /// the internal cache.
690 ///
691 /// # Arguments
692 ///
693 /// * `path` - Absolute path of the mount point to remove
694 ///
695 /// # Returns
696 ///
697 /// * `Ok(Arc<MountPoint>)` - The removed mount point
698 /// * `Err(FileSystemError)` - If no mount point exists at the path
699 pub fn remove(&mut self, path: &str) -> Result<Arc<MountPoint>> {
700 let normalized = Self::normalize_path(path)?;
701 let components = self.split_path(&normalized);
702
703 // Traverse path to find node
704 let mut current_arc = self.root.clone();
705 for component in &components {
706 let next_arc = {
707 let children = current_arc.children.read();
708 children.get(component)
709 .ok_or(FileSystemError {
710 kind: FileSystemErrorKind::NotFound,
711 message: format!("Mount point {} not found", path),
712 })?
713 .clone()
714 };
715 current_arc = next_arc;
716 }
717
718 // Remove mount point
719 let mount_point = current_arc.mount_point.write()
720 .take()
721 .ok_or(FileSystemError {
722 kind: FileSystemErrorKind::NotFound,
723 message: format!("No mount point at {}", path),
724 })?;
725
726 self.path_cache.remove(&normalized);
727
728 Ok(mount_point)
729 }
730
731 /// List all mount points in the tree
732 ///
733 /// Returns a vector of all mount point paths currently registered
734 /// in the mount tree. This is useful for debugging and system
735 /// introspection.
736 ///
737 /// # Returns
738 ///
739 /// Vector of mount point paths in no particular order
740 pub fn list_all(&self) -> Vec<String> {
741 let mut paths = Vec::new();
742 self.collect_mount_paths(&self.root, String::new(), &mut paths);
743 paths
744 }
745
746 /// Get number of mount points
747 ///
748 /// Returns the total number of mount points currently registered
749 /// in this mount tree.
750 ///
751 /// # Returns
752 ///
753 /// Number of active mount points
754 pub fn len(&self) -> usize {
755 self.path_cache.len()
756 }
757
758 /// Secure path normalization with directory traversal protection
759 ///
760 /// This method normalizes filesystem paths by resolving "." and ".."
761 /// components while preventing directory traversal attacks that could
762 /// escape the filesystem root. It ensures all paths are absolute
763 /// and properly formatted.
764 ///
765 /// # Arguments
766 ///
767 /// * `path` - Input path to normalize
768 ///
769 /// # Returns
770 ///
771 /// * `Ok(String)` - Normalized absolute path
772 /// * `Err(FileSystemError)` - If path is not absolute or invalid
773 ///
774 /// # Security
775 ///
776 /// This method prevents:
777 /// - Relative path traversal (../../../etc/passwd)
778 /// - Root directory escape attempts
779 /// - Malformed path components
780 ///
781 /// # Examples
782 ///
783 /// ```rust
784 /// assert_eq!(MountTree::normalize_path("/a/b/../c")?, "/a/c");
785 /// assert_eq!(MountTree::normalize_path("/a/./b")?, "/a/b");
786 /// assert_eq!(MountTree::normalize_path("/../..")?, "/");
787 /// ```
788 pub fn normalize_path(path: &str) -> Result<String> {
789 if !path.starts_with('/') {
790 return Err(FileSystemError {
791 kind: FileSystemErrorKind::InvalidPath,
792 message: "Path must be absolute".to_string(),
793 });
794 }
795
796 let mut normalized_components = Vec::new();
797 let components: Vec<&str> = path.split('/').filter(|s| !s.is_empty()).collect();
798
799 for component in components {
800 match component {
801 "." => continue,
802 ".." => {
803 // Move to parent directory (cannot go above root)
804 if !normalized_components.is_empty() {
805 normalized_components.pop();
806 }
807 }
808 comp => normalized_components.push(comp),
809 }
810 }
811
812 if normalized_components.is_empty() {
813 Ok("/".to_string())
814 } else {
815 Ok(format!("/{}", normalized_components.join("/")))
816 }
817 }
818
819 /// Split normalized path into components
820 ///
821 /// Converts a normalized path string into a vector of path components
822 /// for tree traversal. Root path "/" becomes an empty vector.
823 ///
824 /// # Arguments
825 ///
826 /// * `path` - Normalized absolute path
827 ///
828 /// # Returns
829 ///
830 /// Vector of path components, empty for root path
831 fn split_path(&self, path: &str) -> Vec<String> {
832 if path == "/" {
833 return Vec::new();
834 }
835 path.trim_start_matches('/')
836 .split('/')
837 .filter(|s| !s.is_empty())
838 .map(|s| s.to_string())
839 .collect()
840 }
841
842 /// Recursively collect mount paths from tree nodes
843 ///
844 /// Internal method for traversing the mount tree and collecting
845 /// all mount point paths for the list_all() operation.
846 ///
847 /// # Arguments
848 ///
849 /// * `node` - Current node being examined
850 /// * `current_path` - Path accumulated up to this node
851 /// * `paths` - Vector to collect found mount paths
852 fn collect_mount_paths(&self, node: &MountNode, current_path: String, paths: &mut Vec<String>) {
853 // Check if this node has a mount point
854 let mount_guard = node.mount_point.read();
855 if mount_guard.is_some() {
856 let path = if current_path.is_empty() { "/".to_string() } else { current_path.clone() };
857 paths.push(path);
858 }
859 // Guard is dropped here
860
861 // Iterate through children
862 let children_guard = node.children.read();
863 for (component, child) in children_guard.iter() {
864 let child_path = if current_path.is_empty() {
865 format!("/{}", component)
866 } else {
867 format!("{}/{}", current_path, component)
868 };
869 self.collect_mount_paths(child, child_path, paths);
870 }
871 // Guard is dropped here
872 }
873}