kernel/fs/vfs_v2/drivers/overlayfs/mod.rs
1//! OverlayFS v2 - Overlay filesystem implementation for VFS v2
2//!
3//! This module provides a union/overlay view of multiple filesystems, allowing
4//! files and directories from multiple source filesystems to appear as a single
5//! unified filesystem hierarchy.
6//!
7//! ## Features
8//!
9//! - **Multi-layer support**: Combines an optional upper layer (read-write) with
10//! multiple lower layers (read-only) in priority order
11//! - **Copy-up semantics**: Modifications to lower layer files are copied to the
12//! upper layer before modification
13//! - **Whiteout support**: Files can be hidden or deleted from view using special
14//! whiteout entries
15//! - **Mount point aware**: Handles crossing mount boundaries correctly when
16//! resolving paths across layers
17//!
18//! ## Usage
19//!
20//! ```rust,no_run
21//! // Create overlay with upper and lower layers
22//! let overlay = OverlayFS::new(
23//! Some((upper_mount, upper_entry)), // Upper layer for writes
24//! vec![(lower_mount, lower_entry)], // Lower layers (read-only)
25//! "my_overlay".to_string()
26//! )?;
27//! ```
28//!
29//! ## Cross-VFS Support
30//!
31//! - **Cross-VFS overlays supported**: Upper and lower layers can come from
32//! different VFS managers, enabling flexible overlay configurations
33//! - **Seamless integration**: Mount points from different VFS managers are
34//! unified transparently through the overlay interface
35//!
36//! ## Limitations
37//!
38//! - Upper layer is required for write operations
39//! - Whiteout files follow the `.wh.filename` convention
40
41use alloc::boxed::Box;
42use alloc::string::ToString;
43use alloc::{collections::BTreeSet, format, string::String, sync::Arc, vec::Vec};
44use core::any::Any;
45use spin::RwLock;
46
47use crate::driver_initcall;
48use crate::fs::vfs_v2::core::{
49 DirectoryEntryInternal, FileSystemId, FileSystemOperations, VfsEntry, VfsNode,
50};
51use crate::fs::vfs_v2::mount_tree::MountPoint;
52use crate::fs::{
53 FileMetadata, FileObject, FileSystemDriver, FileSystemError, FileSystemErrorKind, FileType,
54 SeekFrom, VfsManager, get_fs_driver_manager,
55};
56use crate::object::capability::{ControlOps, MemoryMappingOps, StreamError, StreamOps};
57use crate::vm::vmem::MemoryArea;
58
59/// OverlayFS implementation for VFS v2
60///
61/// This filesystem provides a unified view of multiple underlying filesystems
62/// by layering them on top of each other. Files and directories from all layers
63/// are merged, with the upper layer taking precedence for writes and the lower
64/// layers providing fallback content.
65///
66/// ## Layer Resolution
67///
68/// When resolving files or directories:
69/// 1. Check upper layer first (if present and not whiteout)
70/// 2. Check lower layers in priority order
71/// 3. Return first match found
72///
73/// ## Write Operations
74///
75/// All write operations are performed on the upper layer. If a file exists
76/// only in lower layers, it is first copied to the upper layer (copy-up)
77/// before modification.
78#[derive(Clone)]
79pub struct OverlayFS {
80 /// Unique filesystem identifier
81 fs_id: FileSystemId,
82 /// Upper layer for write operations (may be None for read-only overlay)
83 upper: Option<(Arc<MountPoint>, Arc<VfsEntry>)>,
84 /// Lower layers (in priority order, highest priority first)
85 lower_layers: Vec<(Arc<MountPoint>, Arc<VfsEntry>)>,
86 /// Filesystem name
87 name: String,
88 /// Root node (composite of all layers)
89 root_node: Arc<OverlayNode>,
90}
91
92/// A composite node that represents a file/directory across overlay layers
93///
94/// OverlayNode serves as a virtual representation of a file or directory that
95/// may exist in one or more layers of the overlay filesystem. It handles the
96/// resolution of operations across these layers according to overlay semantics.
97///
98/// ## Design
99///
100/// Each OverlayNode represents a specific path in the overlay and delegates
101/// operations to the appropriate underlying filesystem layers. The node itself
102/// doesn't store file content but rather coordinates access to the real nodes
103/// in the upper and lower layers.
104pub struct OverlayNode {
105 /// Node name
106 name: String,
107 /// Reference to overlay filesystem
108 overlay_fs: RwLock<Option<Arc<OverlayFS>>>,
109 /// Path in the overlay
110 path: String,
111 /// File type (resolved from layers)
112 file_type: FileType,
113 /// File ID
114 file_id: u64,
115}
116
117impl OverlayNode {
118 pub fn new(name: String, path: String, file_type: FileType, file_id: u64) -> Arc<Self> {
119 Arc::new(Self {
120 name,
121 overlay_fs: RwLock::new(None),
122 path,
123 file_type,
124 file_id,
125 })
126 }
127
128 pub fn set_overlay_fs(&self, fs: Arc<OverlayFS>) {
129 *self.overlay_fs.write() = Some(fs);
130 }
131}
132
133impl Clone for OverlayNode {
134 fn clone(&self) -> Self {
135 let cloned = Self {
136 name: self.name.clone(),
137 overlay_fs: RwLock::new(None),
138 path: self.path.clone(),
139 file_type: self.file_type.clone(),
140 file_id: self.file_id,
141 };
142
143 // Copy the overlay_fs reference if it exists
144 if let Some(fs) = self.overlay_fs.read().as_ref() {
145 *cloned.overlay_fs.write() = Some(Arc::clone(fs));
146 }
147
148 cloned
149 }
150}
151
152impl VfsNode for OverlayNode {
153 fn id(&self) -> u64 {
154 self.file_id
155 }
156
157 fn filesystem(&self) -> Option<alloc::sync::Weak<dyn FileSystemOperations>> {
158 self.overlay_fs
159 .read()
160 .as_ref()
161 .map(|fs| Arc::downgrade(fs) as alloc::sync::Weak<dyn FileSystemOperations>)
162 }
163
164 fn metadata(&self) -> Result<FileMetadata, FileSystemError> {
165 if let Some(ref fs) = *self.overlay_fs.read() {
166 fs.get_metadata_for_path(&self.path)
167 } else {
168 Err(FileSystemError::new(
169 FileSystemErrorKind::NotSupported,
170 "No filesystem reference",
171 ))
172 }
173 }
174
175 fn read_link(&self) -> Result<String, FileSystemError> {
176 if let Some(ref fs) = *self.overlay_fs.read() {
177 fs.read_link_for_path(&self.path)
178 } else {
179 Err(FileSystemError::new(
180 FileSystemErrorKind::NotSupported,
181 "No filesystem reference",
182 ))
183 }
184 }
185
186 fn as_any(&self) -> &dyn Any {
187 self
188 }
189}
190
191impl OverlayFS {
192 /// Create a new OverlayFS instance with specified layers
193 ///
194 /// # Arguments
195 ///
196 /// * `upper` - Optional upper layer for write operations (mount point and entry)
197 /// * `lower_layers` - Vector of lower layers in priority order (highest priority first)
198 /// * `name` - Name identifier for this overlay filesystem
199 ///
200 /// # Returns
201 ///
202 /// Returns an Arc<OverlayFS> on success, or FileSystemError on failure
203 ///
204 /// # Example
205 ///
206 /// ```rust,no_run
207 /// let overlay = OverlayFS::new(
208 /// Some((upper_mount, upper_entry)), // Read-write upper layer
209 /// vec![
210 /// (layer1_mount, layer1_entry), // Higher priority lower layer
211 /// (layer2_mount, layer2_entry), // Lower priority layer
212 /// ],
213 /// "system_overlay".to_string()
214 /// )?;
215 /// ```
216 pub fn new(
217 upper: Option<(Arc<MountPoint>, Arc<VfsEntry>)>,
218 lower_layers: Vec<(Arc<MountPoint>, Arc<VfsEntry>)>,
219 name: String,
220 ) -> Result<Arc<Self>, FileSystemError> {
221 let root_node = OverlayNode::new("/".to_string(), "/".to_string(), FileType::Directory, 1);
222 let overlay = Arc::new(Self {
223 fs_id: FileSystemId::new(),
224 upper,
225 lower_layers,
226 name,
227 root_node: root_node.clone(),
228 });
229 root_node.set_overlay_fs(overlay.clone());
230 Ok(overlay)
231 }
232
233 /// Create a new OverlayFS from VFS paths
234 ///
235 /// This is a convenience method that resolves VFS paths to create an overlay.
236 /// This approach follows the "normal filesystem" pattern - create the overlay
237 /// instance, then mount it like any other filesystem.
238 ///
239 /// # Arguments
240 /// * `vfs_manager` - VFS manager to resolve paths in
241 /// * `upper_path` - Optional path for the upper (writable) layer
242 /// * `lower_paths` - Vector of paths for lower (read-only) layers
243 /// * `name` - Name for the overlay instance
244 ///
245 /// # Example
246 /// ```rust,no_run
247 /// // Create overlay from paths
248 /// let overlay = OverlayFS::new_from_paths(
249 /// &vfs_manager,
250 /// Some("/tmp/overlay"), // Upper layer
251 /// vec!["/system", "/base"], // Lower layers
252 /// "container_overlay"
253 /// )?;
254 ///
255 /// // Mount like any other filesystem
256 /// vfs_manager.mount(overlay, "/merged", 0)?;
257 /// ```
258 pub fn new_from_paths(
259 vfs_manager: &crate::fs::vfs_v2::manager::VfsManager,
260 upper_path: Option<&str>,
261 lower_paths: Vec<&str>,
262 name: &str,
263 ) -> Result<Arc<Self>, FileSystemError> {
264 // Resolve upper layer if provided
265 let upper = if let Some(path) = upper_path {
266 let (entry, mount) = vfs_manager.mount_tree.resolve_path(path)?;
267 Some((mount, entry))
268 } else {
269 None
270 };
271
272 // Resolve lower layers
273 let mut lower_layers = Vec::new();
274 for path in lower_paths {
275 let (entry, mount) = vfs_manager.mount_tree.resolve_path(path)?;
276 lower_layers.push((mount, entry));
277 }
278
279 // Create overlay with resolved layers
280 Self::new(upper, lower_layers, name.to_string())
281 }
282
283 /// Create a new OverlayFS from paths across multiple VFS managers (Cross-VFS)
284 ///
285 /// This method enables true cross-VFS overlays where upper and lower layers
286 /// can come from completely different VFS manager instances. This is perfect
287 /// for container scenarios where the base system is in one VFS and the
288 /// container overlay is in another.
289 ///
290 /// # Arguments
291 /// * `upper_vfs_and_path` - Optional tuple of (vfs_manager, path) for upper layer
292 /// * `lower_vfs_and_paths` - Vector of (vfs_manager, path) tuples for lower layers
293 /// * `name` - Name for the overlay instance
294 ///
295 /// # Example
296 /// ```rust,no_run
297 /// // Cross-VFS overlay: base system from global VFS, overlay in container VFS
298 /// let base_vfs = get_global_vfs_manager();
299 /// let container_vfs = VfsManager::new();
300 ///
301 /// let overlay = OverlayFS::new_from_paths_and_vfs(
302 /// Some((&container_vfs, "/upper")), // Upper in container VFS
303 /// vec![
304 /// (&base_vfs, "/system"), // Base system from global VFS
305 /// (&container_vfs, "/config"), // Config from container VFS
306 /// ],
307 /// "cross_vfs_overlay"
308 /// )?;
309 ///
310 /// // Mount in container VFS like any other filesystem
311 /// container_vfs.mount(overlay, "/merged", 0)?;
312 /// ```
313 pub fn new_from_paths_and_vfs(
314 upper_vfs_and_path: Option<(&Arc<VfsManager>, &str)>,
315 lower_vfs_and_paths: Vec<(&Arc<VfsManager>, &str)>,
316 name: &str,
317 ) -> Result<Arc<Self>, FileSystemError> {
318 // Resolve upper layer from its VFS
319 let upper = if let Some((upper_vfs, upper_path)) = upper_vfs_and_path {
320 let (entry, mount) = upper_vfs.mount_tree.resolve_path(upper_path)?;
321 Some((mount, entry))
322 } else {
323 None
324 };
325
326 // Resolve lower layers from their respective VFS managers
327 let mut lower_layers = Vec::new();
328 for (lower_vfs, lower_path) in lower_vfs_and_paths {
329 let (entry, mount) = lower_vfs.mount_tree.resolve_path(lower_path)?;
330 lower_layers.push((mount, entry));
331 }
332
333 // Create overlay - the internal implementation already supports cross-VFS!
334 Self::new(upper, lower_layers, name.to_string())
335 }
336
337 /// Get FileSystemOperations from MountPoint
338 ///
339 /// Helper method to extract the filesystem operations from a mount point.
340 /// This is used internally to access the underlying filesystem operations
341 /// for each layer.
342 fn fs_from_mount(
343 mount: &Arc<MountPoint>,
344 ) -> Result<Arc<dyn FileSystemOperations>, FileSystemError> {
345 let filesystem = mount.root.node().filesystem().ok_or_else(|| {
346 FileSystemError::new(
347 FileSystemErrorKind::BrokenFileSystem,
348 "Mount point has no filesystem",
349 )
350 })?;
351
352 let fs_ops = filesystem.upgrade().ok_or_else(|| {
353 FileSystemError::new(
354 FileSystemErrorKind::BrokenFileSystem,
355 "Filesystem operations are no longer available",
356 )
357 })?;
358
359 Ok(fs_ops)
360 }
361
362 /// Get metadata for a path by checking layers in priority order
363 ///
364 /// This method implements the core overlay resolution logic:
365 /// 1. Check if the path is hidden by a whiteout file
366 /// 2. Check the upper layer first (if present)
367 /// 3. Fall back to lower layers in priority order
368 ///
369 /// # Arguments
370 ///
371 /// * `path` - The path to resolve within the overlay
372 ///
373 /// # Returns
374 ///
375 /// Returns FileMetadata for the first matching file found, or NotFound error
376 /// if the file doesn't exist in any layer or is hidden by whiteout.
377 fn get_metadata_for_path(&self, path: &str) -> Result<FileMetadata, FileSystemError> {
378 // Check for whiteout first
379 if self.is_whiteout(path) {
380 return Err(FileSystemError::new(
381 FileSystemErrorKind::NotFound,
382 "File is hidden by whiteout",
383 ));
384 }
385
386 // Check upper layer first
387 if let Some((ref upper_fs, ref upper_node)) = self.upper {
388 if let Ok(node) = self.resolve_in_layer(upper_fs, upper_node, path) {
389 return node.metadata();
390 }
391 }
392
393 // Check lower layers
394 for (lower_fs, lower_node) in &self.lower_layers {
395 if let Ok(node) = self.resolve_in_layer(lower_fs, lower_node, path) {
396 return node.metadata();
397 }
398 }
399
400 Err(FileSystemError::new(
401 FileSystemErrorKind::NotFound,
402 "File not found in any layer",
403 ))
404 }
405
406 /// Read the target of a symbolic link at the specified path
407 ///
408 /// This method searches through the overlay layers to find a symbolic link
409 /// at the given path and returns its target. It follows the same priority
410 /// order as other operations: upper layer first, then lower layers.
411 ///
412 /// # Arguments
413 ///
414 /// * `path` - The path to the symbolic link to read
415 ///
416 /// # Returns
417 ///
418 /// Returns the target path of the symbolic link, or an error if the path
419 /// is not found or is not a symbolic link.
420 fn read_link_for_path(&self, path: &str) -> Result<String, FileSystemError> {
421 // Check for whiteout first
422 if self.is_whiteout(path) {
423 return Err(FileSystemError::new(
424 FileSystemErrorKind::NotFound,
425 "File is hidden by whiteout",
426 ));
427 }
428
429 // Check upper layer first
430 if let Some((ref upper_fs, ref upper_node)) = self.upper {
431 if let Ok(node) = self.resolve_in_layer(upper_fs, upper_node, path) {
432 return node.read_link();
433 }
434 }
435
436 // Check lower layers
437 for (lower_fs, lower_node) in &self.lower_layers {
438 if let Ok(node) = self.resolve_in_layer(lower_fs, lower_node, path) {
439 return node.read_link();
440 }
441 }
442
443 Err(FileSystemError::new(
444 FileSystemErrorKind::NotFound,
445 "Symbolic link not found in any layer",
446 ))
447 }
448
449 /// Resolve a path in a specific layer, starting from the given node
450 ///
451 /// This method performs path resolution within a single overlay layer,
452 /// handling mount boundary crossings correctly. It walks down the path
453 /// components, following mount points as needed.
454 ///
455 /// # Arguments
456 ///
457 /// * `mount` - The mount point to start resolution from
458 /// * `entry` - The VFS entry to start resolution from
459 /// * `path` - The path to resolve (relative to the entry)
460 ///
461 /// # Returns
462 ///
463 /// Returns the resolved VfsNode, or an error if the path cannot be resolved
464 /// in this layer.
465 fn resolve_in_layer(
466 &self,
467 mount: &Arc<MountPoint>,
468 entry: &Arc<VfsEntry>,
469 path: &str,
470 ) -> Result<Arc<dyn VfsNode>, FileSystemError> {
471 let mut current_mount = mount.clone();
472 let mut current_node = entry.node();
473
474 let parts: Vec<&str> = path
475 .trim_start_matches('/')
476 .split('/')
477 .filter(|s| !s.is_empty())
478 .collect();
479 if parts.is_empty() {
480 return Ok(current_node);
481 }
482
483 for part in parts {
484 let current_fs = current_node
485 .filesystem()
486 .and_then(|w| w.upgrade())
487 .ok_or_else(|| {
488 FileSystemError::new(
489 FileSystemErrorKind::NotSupported,
490 "Node has no filesystem",
491 )
492 })?;
493
494 let next_node = current_fs.lookup(¤t_node, &part.to_string())?;
495
496 let child_mount_opt = current_mount.children.read().get(&next_node.id()).cloned();
497
498 if let Some(child_mount) = child_mount_opt {
499 current_mount = child_mount.clone();
500 current_node = child_mount.root.node();
501 } else {
502 current_node = next_node;
503 }
504 }
505
506 Ok(current_node)
507 }
508
509 /// Check if a file is hidden by a whiteout file
510 ///
511 /// Whiteout files are special files in the upper layer that indicate
512 /// a file from a lower layer should be hidden. They follow the naming
513 /// convention `.wh.filename` where `filename` is the name of the file
514 /// to be hidden.
515 ///
516 /// # Arguments
517 ///
518 /// * `path` - The path to check for whiteout
519 ///
520 /// # Returns
521 ///
522 /// Returns true if the file is hidden by a whiteout, false otherwise.
523 fn is_whiteout(&self, path: &str) -> bool {
524 if let Some((ref upper_fs, ref upper_node)) = self.upper {
525 let whiteout_name = format!(".wh.{}", path.split('/').last().unwrap_or(path));
526 let parent_path = if let Some(pos) = path.rfind('/') {
527 &path[..pos]
528 } else {
529 "/"
530 };
531 let whiteout_path = if parent_path == "/" {
532 format!("/{}", whiteout_name)
533 } else {
534 format!("{}/{}", parent_path, whiteout_name)
535 };
536
537 self.resolve_in_layer(upper_fs, upper_node, &whiteout_path)
538 .is_ok()
539 } else {
540 false
541 }
542 }
543
544 /// Get upper layer, error if not available
545 ///
546 /// Returns the upper layer mount point and entry, or an error if the
547 /// overlay filesystem is read-only (no upper layer configured).
548 /// This is used by write operations that require an upper layer.
549 ///
550 /// # Returns
551 ///
552 /// Returns (MountPoint, VfsEntry) tuple for upper layer, or PermissionDenied
553 /// error if no upper layer is available.
554 fn get_upper_layer(&self) -> Result<(Arc<MountPoint>, Arc<VfsEntry>), FileSystemError> {
555 self.upper.as_ref().map(|fs| fs.clone()).ok_or_else(|| {
556 FileSystemError::new(
557 FileSystemErrorKind::PermissionDenied,
558 "Overlay is read-only (no upper layer)",
559 )
560 })
561 }
562
563 /// Create a whiteout file to hide a file from lower layers
564 fn create_whiteout(&self, path: &str) -> Result<(), FileSystemError> {
565 let upper = self.get_upper_layer()?;
566 let whiteout_name = format!(".wh.{}", path.split('/').last().unwrap_or(path));
567 let parent_path = if let Some(pos) = path.rfind('/') {
568 &path[..pos]
569 } else {
570 "/"
571 };
572 let whiteout_path = if parent_path == "/" {
573 format!("/{}", whiteout_name)
574 } else {
575 format!("{}/{}", parent_path, whiteout_name)
576 };
577 // Create parent directories if needed
578 self.ensure_parent_dirs(&whiteout_path)?;
579 let parent_node = self.resolve_in_layer(&upper.0, &upper.1, parent_path)?;
580 let fs = Self::fs_from_mount(&upper.0)?;
581 fs.create(&parent_node, &whiteout_name, FileType::RegularFile, 0o644)
582 .map(|_| ())
583 }
584
585 /// Perform copy-up operation: copy a file from lower layer to upper layer
586 fn copy_up(&self, path: &str) -> Result<(), FileSystemError> {
587 let upper = self.get_upper_layer()?;
588 let upper_fs = Self::fs_from_mount(&upper.0)?;
589 // Check if file already exists in upper layer
590 if self.resolve_in_layer(&upper.0, &upper.1, path).is_ok() {
591 return Ok(());
592 }
593 // Find the file in lower layers
594 for (lower_mount, lower_node) in &self.lower_layers {
595 if let Ok(lower_node) = self.resolve_in_layer(lower_mount, lower_node, path) {
596 let metadata = lower_node.metadata()?;
597 // Ensure parent directories exist in upper layer
598 self.ensure_parent_dirs(path)?;
599 let parent_path = if let Some(pos) = path.rfind('/') {
600 &path[..pos]
601 } else {
602 "/"
603 };
604 let filename = path.split('/').last().unwrap_or(path);
605 let parent_node = self.resolve_in_layer(&upper.0, &upper.1, parent_path)?;
606 match metadata.file_type {
607 FileType::Directory => {
608 upper_fs.create(
609 &parent_node,
610 &filename.to_string(),
611 FileType::Directory,
612 0o755,
613 )?;
614 }
615 FileType::RegularFile => {
616 // Create file and copy content
617 let new_node = upper_fs.create(
618 &parent_node,
619 &filename.to_string(),
620 FileType::RegularFile,
621 0o644,
622 )?;
623 // Copy file content
624 let lower_fs = Self::fs_from_mount(lower_mount)?;
625 if let Ok(source_file) = lower_fs.open(&lower_node, 0) {
626 // Read-only
627 if let Ok(dest_file) = upper_fs.open(&new_node, 1) {
628 // Write-only
629 let _ = dest_file.seek(SeekFrom::Start(0));
630 let mut buffer = alloc::vec![0u8; crate::environment::PAGE_SIZE];
631 loop {
632 match source_file.read(&mut buffer) {
633 Ok(bytes_read) if bytes_read > 0 => {
634 if dest_file.write(&buffer[..bytes_read]).is_err() {
635 break;
636 }
637 }
638 _ => break,
639 }
640 }
641 }
642 }
643 }
644 _ => {
645 // For other file types, create a placeholder
646 upper_fs.create(
647 &parent_node,
648 &filename.to_string(),
649 metadata.file_type,
650 0o644,
651 )?;
652 }
653 }
654 return Ok(());
655 }
656 }
657 Err(FileSystemError::new(
658 FileSystemErrorKind::NotFound,
659 "File not found for copy-up",
660 ))
661 }
662
663 /// Ensure parent directories exist in upper layer
664 fn ensure_parent_dirs(&self, path: &str) -> Result<(), FileSystemError> {
665 let upper = self.get_upper_layer()?;
666 let _upper_fs = Self::fs_from_mount(&upper.0)?;
667 let parent_path = if let Some(pos) = path.rfind('/') {
668 &path[..pos]
669 } else {
670 return Ok(());
671 };
672 if parent_path.is_empty() || parent_path == "/" {
673 return Ok(());
674 }
675 // Try to resolve parent - if it fails, create it
676 if self
677 .resolve_in_layer(&upper.0, &upper.1, parent_path)
678 .is_err()
679 {
680 self.ensure_parent_dirs(parent_path)?;
681 let grandparent_path = if let Some(pos) = parent_path.rfind('/') {
682 &parent_path[..pos]
683 } else {
684 "/"
685 };
686 let dirname = parent_path.split('/').last().unwrap_or(parent_path);
687 let grandparent_node = self.resolve_in_layer(
688 &upper.0,
689 &upper.1,
690 if grandparent_path.is_empty() {
691 "/"
692 } else {
693 grandparent_path
694 },
695 )?;
696 let upper_fs = Self::fs_from_mount(&upper.0)?;
697 upper_fs.create(
698 &grandparent_node,
699 &dirname.to_string(),
700 FileType::Directory,
701 0o755,
702 )?;
703 }
704 Ok(())
705 }
706
707 /// Check if file exists only in lower layers (not in upper)
708 fn file_exists_in_lower_only(&self, path: &str) -> bool {
709 // Check if exists in upper
710 if let Some((ref upper_fs, ref upper_node)) = self.upper {
711 if self.resolve_in_layer(upper_fs, upper_node, path).is_ok() {
712 return false;
713 }
714 }
715
716 // Check if exists in any lower layer
717 for (lower_fs, lower_node) in &self.lower_layers {
718 if self.resolve_in_layer(lower_fs, lower_node, path).is_ok() {
719 return true;
720 }
721 }
722
723 false
724 }
725
726 /// Create an OverlayFS from an option string
727 /// example: option = Some("upper=tmpfs,lower=cpiofs")
728 pub fn create_from_option_string(
729 _option: Option<&str>,
730 upper: Option<(Arc<MountPoint>, Arc<VfsEntry>)>,
731 lower_layers: Vec<(Arc<MountPoint>, Arc<VfsEntry>)>,
732 ) -> Arc<dyn FileSystemOperations> {
733 // Parse options if provided
734 let name = "overlayfs".to_string();
735 OverlayFS::new(upper, lower_layers, name).expect("Failed to create OverlayFS")
736 as Arc<dyn FileSystemOperations>
737 }
738}
739
740impl FileSystemOperations for OverlayFS {
741 fn fs_id(&self) -> FileSystemId {
742 self.fs_id
743 }
744
745 fn lookup(
746 &self,
747 parent_node: &Arc<dyn VfsNode>,
748 name: &String,
749 ) -> Result<Arc<dyn VfsNode>, FileSystemError> {
750 let overlay_parent = parent_node
751 .as_any()
752 .downcast_ref::<OverlayNode>()
753 .ok_or_else(|| {
754 FileSystemError::new(
755 FileSystemErrorKind::NotSupported,
756 "Invalid node type for OverlayFS",
757 )
758 })?;
759
760 let child_path = if overlay_parent.path == "/" {
761 format!("/{}", name)
762 } else {
763 format!("{}/{}", overlay_parent.path, name)
764 };
765
766 // Handle special directory entries
767 if name == "." {
768 return Ok(parent_node.clone());
769 }
770 if name == ".." {
771 let parent_path = if let Some(pos) = overlay_parent.path.rfind('/') {
772 if pos == 0 {
773 "/"
774 } else {
775 &overlay_parent.path[..pos]
776 }
777 } else {
778 "/"
779 };
780 let parent_name = parent_path.split('/').last().unwrap_or("/");
781 let node = OverlayNode::new(
782 parent_name.to_string(),
783 parent_path.to_string(),
784 FileType::Directory,
785 0,
786 );
787 if let Some(ref fs) = *overlay_parent.overlay_fs.read() {
788 node.set_overlay_fs(Arc::clone(fs));
789 }
790 return Ok(node);
791 }
792
793 // Check for whiteout
794 if self.is_whiteout(&child_path) {
795 return Err(FileSystemError::new(
796 FileSystemErrorKind::NotFound,
797 "File is hidden by whiteout",
798 ));
799 }
800
801 // Try upper layer first
802 if let Some((ref upper_fs, ref upper_node)) = self.upper {
803 if let Ok(_) = self.resolve_in_layer(upper_fs, upper_node, &child_path) {
804 let metadata = self.get_metadata_for_path(&child_path)?;
805 let node = OverlayNode::new(
806 name.clone(),
807 child_path.clone(),
808 metadata.file_type,
809 metadata.file_id,
810 );
811 if let Some(ref fs) = *overlay_parent.overlay_fs.read() {
812 node.set_overlay_fs(Arc::clone(fs));
813 }
814 return Ok(node);
815 }
816 }
817
818 // Try lower layers
819 for (lower_fs, lower_node) in &self.lower_layers {
820 if let Ok(_) = self.resolve_in_layer(lower_fs, lower_node, &child_path) {
821 let metadata = self.get_metadata_for_path(&child_path)?;
822 let node = OverlayNode::new(
823 name.clone(),
824 child_path.clone(),
825 metadata.file_type,
826 metadata.file_id,
827 );
828 if let Some(ref fs) = *overlay_parent.overlay_fs.read() {
829 node.set_overlay_fs(Arc::clone(fs));
830 }
831 return Ok(node);
832 }
833 }
834
835 Err(FileSystemError::new(
836 FileSystemErrorKind::NotFound,
837 "File not found",
838 ))
839 }
840
841 fn open(
842 &self,
843 overlay_node: &Arc<dyn VfsNode>,
844 flags: u32,
845 ) -> Result<Arc<dyn FileObject>, FileSystemError> {
846 // Downcast to OverlayNode
847 let overlay_node_ref = overlay_node
848 .as_any()
849 .downcast_ref::<OverlayNode>()
850 .ok_or_else(|| {
851 FileSystemError::new(
852 FileSystemErrorKind::NotSupported,
853 "Invalid node type for OverlayFS",
854 )
855 })?;
856
857 // Get metadata using path instead of node metadata to avoid filesystem reference issues
858 let metadata = self.get_metadata_for_path(&overlay_node_ref.path)?;
859
860 // If this is a directory, return OverlayDirectoryObject
861 if metadata.file_type == FileType::Directory {
862 return Ok(Arc::new(OverlayDirectoryObject::new(
863 Arc::new(self.clone()),
864 overlay_node_ref.path.clone(),
865 )));
866 }
867
868 // Check if this is a write operation
869 let is_write_operation = (flags & 0x3) != 0; // O_WRONLY=1, O_RDWR=2
870 // If writing to a file that exists only in lower layer, copy it up first
871 if is_write_operation && self.file_exists_in_lower_only(&overlay_node_ref.path) {
872 self.copy_up(&overlay_node_ref.path)?;
873 }
874 // Try upper layer first
875 if let Some((ref upper_mount, ref upper_node)) = self.upper {
876 if let Ok(upper_node) =
877 self.resolve_in_layer(upper_mount, upper_node, &overlay_node_ref.path)
878 {
879 let fs = Self::fs_from_mount(upper_mount)?;
880 if let Ok(file) = fs.open(&upper_node, flags) {
881 return Ok(file);
882 }
883 }
884 }
885 // For write operations, we need an upper layer
886 if is_write_operation {
887 return Err(FileSystemError::new(
888 FileSystemErrorKind::PermissionDenied,
889 "Cannot write to read-only overlay",
890 ));
891 }
892 // Try lower layers for read operations
893 for (lower_mount, lower_node) in &self.lower_layers {
894 if let Ok(lower_node) =
895 self.resolve_in_layer(lower_mount, lower_node, &overlay_node_ref.path)
896 {
897 let fs = Self::fs_from_mount(lower_mount)?;
898 if let Ok(file) = fs.open(&lower_node, flags) {
899 return Ok(file);
900 }
901 }
902 }
903 Err(FileSystemError::new(
904 FileSystemErrorKind::NotFound,
905 "File not found",
906 ))
907 }
908
909 fn create(
910 &self,
911 parent_node: &Arc<dyn VfsNode>,
912 name: &String,
913 file_type: FileType,
914 mode: u32,
915 ) -> Result<Arc<dyn VfsNode>, FileSystemError> {
916 let upper = self.get_upper_layer()?;
917 let upper_fs = Self::fs_from_mount(&upper.0)?;
918 let overlay_parent = parent_node
919 .as_any()
920 .downcast_ref::<OverlayNode>()
921 .ok_or_else(|| {
922 FileSystemError::new(
923 FileSystemErrorKind::NotSupported,
924 "Invalid node type for OverlayFS",
925 )
926 })?;
927 let child_path = if overlay_parent.path == "/" {
928 format!("/{}", name)
929 } else {
930 format!("{}/{}", overlay_parent.path, name)
931 };
932 // Ensure parent exists in upper layer (copy-up if needed)
933 if self.file_exists_in_lower_only(&overlay_parent.path) {
934 self.copy_up(&overlay_parent.path)?;
935 }
936 // Remove any existing whiteout
937 if self.is_whiteout(&child_path) {
938 // Remove whiteout file
939 let whiteout_name = format!(".wh.{}", name);
940 let parent_path = if let Some(pos) = overlay_parent.path.rfind('/') {
941 &overlay_parent.path[..pos]
942 } else {
943 "/"
944 };
945 if let Ok(whiteout_parent) = self.resolve_in_layer(&upper.0, &upper.1, parent_path) {
946 if upper_fs.remove(&whiteout_parent, &whiteout_name).is_err() {
947 return Err(FileSystemError::new(
948 FileSystemErrorKind::NotFound,
949 "Whiteout file not found",
950 ));
951 }
952 // Successfully removed whiteout file
953 }
954 }
955 let upper_parent = self.resolve_in_layer(&upper.0, &upper.1, &overlay_parent.path)?;
956 let fs = Self::fs_from_mount(&upper.0)?;
957 // let new_node = fs.create(&upper_parent, name, file_type, mode)?;
958 let new_node = match fs.create(&upper_parent, name, file_type, mode) {
959 Ok(node) => node,
960 Err(e) => {
961 // crate::println!(
962 // "OverlayFS: Failed to create file '{}' in upper layer: {}",
963 // name,
964 // e.message
965 // );
966 return Err(e);
967 }
968 };
969
970 // Return overlay node
971 let metadata = new_node.metadata()?;
972 let overlay_node = OverlayNode::new(
973 name.clone(),
974 child_path,
975 metadata.file_type,
976 metadata.file_id,
977 );
978 if let Some(ref fs) = *overlay_parent.overlay_fs.read() {
979 overlay_node.set_overlay_fs(Arc::clone(fs));
980 }
981 Ok(overlay_node)
982 }
983
984 fn remove(&self, parent_node: &Arc<dyn VfsNode>, name: &String) -> Result<(), FileSystemError> {
985 let overlay_parent = parent_node
986 .as_any()
987 .downcast_ref::<OverlayNode>()
988 .ok_or_else(|| {
989 FileSystemError::new(
990 FileSystemErrorKind::NotSupported,
991 "Invalid node type for OverlayFS",
992 )
993 })?;
994
995 let child_path = if overlay_parent.path == "/" {
996 format!("/{}", name)
997 } else {
998 format!("{}/{}", overlay_parent.path, name)
999 };
1000
1001 // If file exists in upper layer, remove it
1002 if let Some((ref upper_mount, ref upper_entry)) = self.upper {
1003 if let Ok(upper_parent) =
1004 self.resolve_in_layer(upper_mount, upper_entry, &overlay_parent.path)
1005 {
1006 let fs = Self::fs_from_mount(upper_mount)?;
1007 if fs.remove(&upper_parent, name).is_ok() {
1008 // If file also exists in lower layers, create whiteout
1009 for (lower_mount, lower_entry) in &self.lower_layers {
1010 if self
1011 .resolve_in_layer(lower_mount, lower_entry, &child_path)
1012 .is_ok()
1013 {
1014 self.create_whiteout(&child_path)?;
1015 break;
1016 }
1017 }
1018 return Ok(());
1019 }
1020 }
1021 }
1022
1023 // If file exists only in lower layers, create whiteout
1024 for (lower_mount, lower_node) in &self.lower_layers {
1025 if self
1026 .resolve_in_layer(lower_mount, lower_node, &child_path)
1027 .is_ok()
1028 {
1029 self.create_whiteout(&child_path)?;
1030 return Ok(());
1031 }
1032 }
1033
1034 Err(FileSystemError::new(
1035 FileSystemErrorKind::NotFound,
1036 "File not found",
1037 ))
1038 }
1039
1040 fn root_node(&self) -> Arc<dyn VfsNode> {
1041 Arc::clone(&self.root_node) as Arc<dyn VfsNode>
1042 }
1043
1044 fn name(&self) -> &str {
1045 &self.name
1046 }
1047
1048 fn is_read_only(&self) -> bool {
1049 self.upper.is_none()
1050 }
1051
1052 fn readdir(
1053 &self,
1054 node: &Arc<dyn VfsNode>,
1055 ) -> Result<Vec<DirectoryEntryInternal>, FileSystemError> {
1056 let overlay_node = node.as_any().downcast_ref::<OverlayNode>().ok_or_else(|| {
1057 FileSystemError::new(
1058 FileSystemErrorKind::NotSupported,
1059 "Invalid node type for OverlayFS",
1060 )
1061 })?;
1062
1063 let mut entries = Vec::new();
1064 let mut seen_names = BTreeSet::new();
1065
1066 // Get parent directory file_id for ".."
1067 let parent_file_id = if overlay_node.path == "/" {
1068 // Root directory's parent is itself
1069 overlay_node.file_id
1070 } else {
1071 // Get parent by looking up ".." from current directory
1072 match self.lookup(node, &"..".to_string()) {
1073 Ok(parent_node) => parent_node.id(),
1074 Err(_) => overlay_node.file_id, // Fallback to current if parent can't be resolved
1075 }
1076 };
1077
1078 // Add "." and ".." entries
1079 entries.push(DirectoryEntryInternal {
1080 name: ".".to_string(),
1081 file_type: FileType::Directory,
1082 file_id: overlay_node.file_id,
1083 });
1084 entries.push(DirectoryEntryInternal {
1085 name: "..".to_string(),
1086 file_type: FileType::Directory,
1087 file_id: parent_file_id,
1088 });
1089 seen_names.insert(".".to_string());
1090 seen_names.insert("..".to_string());
1091
1092 // Read from upper layer first
1093 if let Some((ref upper_mount, ref upper_node)) = self.upper {
1094 if let Ok(upper_node) =
1095 self.resolve_in_layer(upper_mount, upper_node, &overlay_node.path)
1096 {
1097 let fs = upper_node
1098 .filesystem()
1099 .and_then(|w| w.upgrade())
1100 .ok_or_else(|| {
1101 FileSystemError::new(
1102 FileSystemErrorKind::NotSupported,
1103 "Node has no filesystem",
1104 )
1105 })?;
1106 if let Ok(upper_entries) = fs.readdir(&upper_node) {
1107 for entry in upper_entries {
1108 // Skip whiteout files themselves and . .. entries
1109 if entry.name.starts_with(".wh.") || entry.name == "." || entry.name == ".."
1110 {
1111 continue;
1112 }
1113 if !seen_names.contains(&entry.name) {
1114 seen_names.insert(entry.name.clone());
1115 entries.push(entry);
1116 }
1117 }
1118 }
1119 }
1120 }
1121
1122 // Read from lower layers (skip entries already seen in upper layers)
1123 for (lower_mount, lower_node) in &self.lower_layers {
1124 if let Ok(lower_node) =
1125 self.resolve_in_layer(lower_mount, lower_node, &overlay_node.path)
1126 {
1127 let fs = lower_node
1128 .filesystem()
1129 .and_then(|w| w.upgrade())
1130 .ok_or_else(|| {
1131 FileSystemError::new(
1132 FileSystemErrorKind::NotSupported,
1133 "Node has no filesystem",
1134 )
1135 })?;
1136 if let Ok(lower_entries) = fs.readdir(&lower_node) {
1137 for entry in lower_entries {
1138 // Skip . .. entries
1139 if entry.name == "." || entry.name == ".." {
1140 continue;
1141 }
1142 let entry_full_path = if overlay_node.path == "/" {
1143 format!("/{}", entry.name)
1144 } else {
1145 format!("{}/{}", overlay_node.path, entry.name)
1146 };
1147 // Only add if not already seen and not hidden by whiteout
1148 if !seen_names.contains(&entry.name) && !self.is_whiteout(&entry_full_path)
1149 {
1150 seen_names.insert(entry.name.clone());
1151 entries.push(entry);
1152 }
1153 }
1154 }
1155 }
1156 }
1157 entries.sort_by(|a, b| a.file_id.cmp(&b.file_id)); // Sort entries by file_id
1158 Ok(entries)
1159 }
1160
1161 fn as_any(&self) -> &dyn Any {
1162 self
1163 }
1164}
1165
1166/// File object for OverlayFS directory operations
1167///
1168/// OverlayDirectoryObject handles reading directory entries from overlayfs,
1169/// merging entries from upper and lower layers while respecting whiteout files.
1170pub struct OverlayDirectoryObject {
1171 overlay_fs: Arc<OverlayFS>,
1172 path: String, // Store path instead of node
1173 position: RwLock<u64>,
1174}
1175
1176impl OverlayDirectoryObject {
1177 pub fn new(overlay_fs: Arc<OverlayFS>, path: String) -> Self {
1178 Self {
1179 overlay_fs,
1180 path,
1181 position: RwLock::new(0),
1182 }
1183 }
1184
1185 /// Collect all directory entries from all layers, handling whiteouts and merging
1186 fn collect_directory_entries(
1187 &self,
1188 ) -> Result<Vec<crate::fs::DirectoryEntryInternal>, FileSystemError> {
1189 let mut special_entries = Vec::new();
1190 let mut all_entries = Vec::new();
1191
1192 let mut seen_names = BTreeSet::new();
1193
1194 // Get current directory node by resolving path components
1195 let current_dir_node = {
1196 let mut current = self.overlay_fs.root_node();
1197 if self.path != "/" {
1198 for component in self.path.trim_start_matches('/').split('/') {
1199 if !component.is_empty() {
1200 current = self.overlay_fs.lookup(¤t, &component.to_string())?;
1201 }
1202 }
1203 }
1204 current
1205 };
1206 let current_file_id = current_dir_node.id();
1207
1208 // Get parent directory file_id for ".."
1209 let parent_file_id = if self.path == "/" {
1210 // Root directory's parent is itself
1211 current_file_id
1212 } else {
1213 // Get parent by looking up ".." from current directory
1214 match self.overlay_fs.lookup(¤t_dir_node, &"..".to_string()) {
1215 Ok(parent_node) => parent_node.id(),
1216 Err(_) => current_file_id, // Fallback to current if parent can't be resolved
1217 }
1218 };
1219
1220 // Add "." and ".." entries first
1221 special_entries.push(crate::fs::DirectoryEntryInternal {
1222 name: ".".to_string(),
1223 file_type: FileType::Directory,
1224 file_id: current_file_id,
1225 size: 0,
1226 metadata: None,
1227 });
1228 special_entries.push(crate::fs::DirectoryEntryInternal {
1229 name: "..".to_string(),
1230 file_type: FileType::Directory,
1231 file_id: parent_file_id,
1232 size: 0,
1233 metadata: None,
1234 });
1235 seen_names.insert(".".to_string());
1236 seen_names.insert("..".to_string());
1237
1238 // Check upper layer first
1239 if let Some((ref upper_mount, ref upper_node)) = self.overlay_fs.upper {
1240 if let Ok(upper_dir_node) =
1241 self.overlay_fs
1242 .resolve_in_layer(upper_mount, upper_node, &self.path)
1243 {
1244 // Try to get filesystem from mount and read directory
1245 if let Ok(upper_fs) = Self::try_fs_from_mount(upper_mount) {
1246 if let Ok(upper_entries) = upper_fs.readdir(&upper_dir_node) {
1247 for entry in upper_entries {
1248 if entry.name == "." || entry.name == ".." {
1249 continue; // Skip, already added
1250 }
1251
1252 // Check for whiteout
1253 if entry.name.starts_with(".wh.") {
1254 // Hide the corresponding file from lower layers
1255 let hidden_name = &entry.name[4..]; // Remove ".wh." prefix
1256 seen_names.insert(hidden_name.to_string());
1257 continue; // Don't add the whiteout file itself
1258 }
1259
1260 if !seen_names.contains(&entry.name) {
1261 all_entries.push(crate::fs::DirectoryEntryInternal {
1262 name: entry.name.clone(),
1263 file_type: entry.file_type,
1264 file_id: entry.file_id,
1265 size: 0,
1266 metadata: None,
1267 });
1268 seen_names.insert(entry.name);
1269 }
1270 }
1271 }
1272 }
1273 }
1274 }
1275
1276 // Check lower layers
1277 for (lower_mount, lower_node) in &self.overlay_fs.lower_layers {
1278 if let Ok(lower_dir_node) =
1279 self.overlay_fs
1280 .resolve_in_layer(lower_mount, lower_node, &self.path)
1281 {
1282 if let Ok(lower_fs) = Self::try_fs_from_mount(lower_mount) {
1283 if let Ok(lower_entries) = lower_fs.readdir(&lower_dir_node) {
1284 for entry in lower_entries {
1285 if entry.name == "." || entry.name == ".." {
1286 continue; // Skip, already added
1287 }
1288
1289 // Only add if not already seen (upper layer takes precedence)
1290 if !seen_names.contains(&entry.name) {
1291 all_entries.push(crate::fs::DirectoryEntryInternal {
1292 name: entry.name.clone(),
1293 file_type: entry.file_type,
1294 file_id: entry.file_id,
1295 size: 0,
1296 metadata: None,
1297 });
1298 seen_names.insert(entry.name);
1299 }
1300 }
1301 }
1302 }
1303 }
1304 }
1305
1306 // Sort entries by file_id to maintain consistent order
1307 all_entries.sort_by(|a, b| a.file_id.cmp(&b.file_id));
1308 special_entries.extend(all_entries);
1309 Ok(special_entries)
1310 }
1311
1312 /// Safe version of fs_from_mount that returns Result instead of panicking
1313 fn try_fs_from_mount(
1314 mount: &Arc<MountPoint>,
1315 ) -> Result<Arc<dyn FileSystemOperations>, FileSystemError> {
1316 let filesystem = mount.root.node().filesystem().ok_or_else(|| {
1317 FileSystemError::new(
1318 FileSystemErrorKind::BrokenFileSystem,
1319 "Mount point has no filesystem",
1320 )
1321 })?;
1322
1323 let fs_ops = filesystem.upgrade().ok_or_else(|| {
1324 FileSystemError::new(
1325 FileSystemErrorKind::BrokenFileSystem,
1326 "Filesystem operations are no longer available",
1327 )
1328 })?;
1329
1330 Ok(fs_ops)
1331 }
1332}
1333
1334impl StreamOps for OverlayDirectoryObject {
1335 fn read(&self, buffer: &mut [u8]) -> Result<usize, StreamError> {
1336 // Collect all directory entries from all layers (simplified version)
1337 let all_entries = self
1338 .collect_directory_entries()
1339 .map_err(StreamError::from)?;
1340
1341 let position = *self.position.read() as usize;
1342
1343 if position >= all_entries.len() {
1344 return Ok(0); // EOF
1345 }
1346
1347 // Get current entry
1348 let fs_entry = &all_entries[position];
1349
1350 // Convert to binary format
1351 let dir_entry = crate::fs::DirectoryEntry::from_internal(fs_entry);
1352
1353 // Calculate actual entry size
1354 let entry_size = dir_entry.entry_size();
1355
1356 // Check buffer size
1357 if buffer.len() < entry_size {
1358 return Err(StreamError::InvalidArgument); // Buffer too small
1359 }
1360
1361 // Treat struct as byte array
1362 let entry_bytes =
1363 unsafe { core::slice::from_raw_parts(&dir_entry as *const _ as *const u8, entry_size) };
1364
1365 // Copy to buffer
1366 buffer[..entry_size].copy_from_slice(entry_bytes);
1367
1368 // Move to next entry
1369 *self.position.write() += 1;
1370
1371 Ok(entry_size)
1372 }
1373
1374 fn write(&self, _buffer: &[u8]) -> Result<usize, StreamError> {
1375 // Directories cannot be written to directly
1376 Err(StreamError::from(FileSystemError::new(
1377 FileSystemErrorKind::IsADirectory,
1378 "Cannot write to directory",
1379 )))
1380 }
1381}
1382
1383impl ControlOps for OverlayDirectoryObject {
1384 // Overlay directories don't support control operations
1385 fn control(&self, _command: u32, _arg: usize) -> Result<i32, &'static str> {
1386 Err("Control operations not supported on overlay directories")
1387 }
1388}
1389
1390impl MemoryMappingOps for OverlayDirectoryObject {
1391 fn get_mapping_info(
1392 &self,
1393 _offset: usize,
1394 _length: usize,
1395 ) -> Result<(usize, usize, bool), &'static str> {
1396 Err("Memory mapping not supported for directories")
1397 }
1398
1399 fn on_mapped(&self, _vaddr: usize, _paddr: usize, _length: usize, _offset: usize) {
1400 // Directories don't support memory mapping
1401 }
1402
1403 fn on_unmapped(&self, _vaddr: usize, _length: usize) {
1404 // Directories don't support memory mapping
1405 }
1406
1407 fn supports_mmap(&self) -> bool {
1408 false
1409 }
1410}
1411
1412impl FileObject for OverlayDirectoryObject {
1413 fn seek(&self, _whence: crate::fs::SeekFrom) -> Result<u64, StreamError> {
1414 // Seeking in directories not supported for now
1415 Err(StreamError::NotSupported)
1416 }
1417
1418 fn metadata(&self) -> Result<crate::fs::FileMetadata, StreamError> {
1419 // Get metadata for the directory path
1420 self.overlay_fs
1421 .get_metadata_for_path(&self.path)
1422 .map_err(StreamError::from)
1423 }
1424
1425 fn truncate(&self, _size: u64) -> Result<(), StreamError> {
1426 Err(StreamError::from(FileSystemError::new(
1427 FileSystemErrorKind::IsADirectory,
1428 "Cannot truncate directory",
1429 )))
1430 }
1431
1432 fn as_any(&self) -> &dyn Any {
1433 self
1434 }
1435}
1436
1437impl crate::object::capability::selectable::Selectable for OverlayDirectoryObject {
1438 fn wait_until_ready(
1439 &self,
1440 _interest: crate::object::capability::selectable::ReadyInterest,
1441 _trapframe: &mut crate::arch::Trapframe,
1442 _timeout_ticks: Option<u64>,
1443 ) -> crate::object::capability::selectable::SelectWaitOutcome {
1444 crate::object::capability::selectable::SelectWaitOutcome::Ready
1445 }
1446}
1447
1448/// Driver for creating OverlayFS instances
1449///
1450/// This driver implements the FileSystemDriver trait to allow OverlayFS
1451/// to be created through the standard filesystem driver infrastructure.
1452/// Currently, OverlayFS instances are typically created programmatically
1453/// rather than through driver parameters due to the complexity of specifying
1454/// multiple layer mount points.
1455pub struct OverlayFSDriver;
1456
1457impl FileSystemDriver for OverlayFSDriver {
1458 fn create_from_memory(
1459 &self,
1460 _memory_area: &MemoryArea,
1461 ) -> Result<Arc<dyn FileSystemOperations>, FileSystemError> {
1462 Ok(OverlayFS::create_from_option_string(None, None, Vec::new()))
1463 }
1464
1465 fn create_from_params(
1466 &self,
1467 _params: &dyn crate::fs::params::FileSystemParams,
1468 ) -> Result<Arc<dyn FileSystemOperations>, FileSystemError> {
1469 Ok(OverlayFS::create_from_option_string(None, None, Vec::new()))
1470 }
1471
1472 fn name(&self) -> &'static str {
1473 "overlayfs"
1474 }
1475
1476 fn filesystem_type(&self) -> crate::fs::FileSystemType {
1477 crate::fs::FileSystemType::Virtual
1478 }
1479}
1480
1481/// Register the OverlayFS driver with the filesystem driver manager
1482///
1483/// This function is called during kernel initialization to make the OverlayFS
1484/// driver available for use. It's automatically invoked by the driver_initcall
1485/// mechanism.
1486fn register_driver() {
1487 let fs_driver_manager = get_fs_driver_manager();
1488 fs_driver_manager.register_driver(Box::new(OverlayFSDriver));
1489}
1490
1491driver_initcall!(register_driver);
1492
1493// ========================================================================
1494// Implementation Notes and Usage Examples
1495// ========================================================================
1496//
1497// ## Creating an Overlay
1498//
1499// ```rust,no_run
1500// // Mount base filesystem
1501// let base_fs = create_base_filesystem()?;
1502// vfs.mount(base_fs, "/base", 0)?;
1503//
1504// // Mount overlay filesystem
1505// let overlay_fs = create_overlay_filesystem()?;
1506// vfs.mount(overlay_fs, "/overlay", 0)?;
1507//
1508// // Create overlay combining them
1509// let (base_mount, base_entry) = vfs.resolve_path("/base")?;
1510// let (overlay_mount, overlay_entry) = vfs.resolve_path("/overlay")?;
1511//
1512// let overlay = OverlayFS::new(
1513// Some((overlay_mount, overlay_entry)), // Upper (writable)
1514// vec![(base_mount, base_entry)], // Lower (read-only)
1515// "system_overlay".to_string()
1516// )?;
1517//
1518// vfs.mount(overlay, "/merged", 0)?;
1519// ```
1520//
1521// ## Key Behaviors
1522//
1523// - **Read operations**: Check upper first, then lower layers in order
1524// - **Write operations**: Always go to upper layer (copy-up if needed)
1525// - **Delete operations**: Create whiteout files in upper layer
1526// - **Directory listing**: Merge all layers, respecting whiteouts
1527//
1528// ## Whiteout Files
1529//
1530// To hide `/merged/file.txt`, create `/overlay/.wh.file.txt` in upper layer.
1531// This follows the standard overlay filesystem whiteout convention.
1532//
1533
1534// ========================================================================
1535// Usage Examples - Normal Filesystem Approach
1536// ========================================================================
1537//
1538// ## Example 1: Basic Overlay (Same VFS)
1539//
1540// ```rust,no_run
1541// // Setup base filesystem
1542// let base_fs = crate::fs::vfs_v2::drivers::tmpfs::TmpFS::new(0);
1543// vfs.mount(base_fs, "/base", 0)?;
1544//
1545// // Setup upper filesystem for writes
1546// let upper_fs = crate::fs::vfs_v2::drivers::tmpfs::TmpFS::new(0);
1547// vfs.mount(upper_fs, "/upper", 0)?;
1548//
1549// // Create overlay combining them - NORMAL FILESYSTEM APPROACH
1550// let overlay = OverlayFS::new_from_paths(
1551// &vfs,
1552// Some("/upper"), // Upper layer (writable)
1553// vec!["/base"], // Lower layers (read-only)
1554// "my_overlay"
1555// )?;
1556//
1557// // Mount like any other filesystem!
1558// vfs.mount(overlay, "/merged", 0)?;
1559// ```
1560//
1561// ## Example 2: Multi-layer Overlay
1562//
1563// ```rust,no_run
1564// // Mount multiple base layers
1565// vfs.mount(system_fs, "/system", 0)?; // System files
1566// vfs.mount(config_fs, "/config", 0)?; // Configuration
1567// vfs.mount(overlay_fs, "/overlay", 0)?; // Overlay workspace
1568//
1569// // Create multi-layer overlay
1570// let overlay = OverlayFS::new_from_paths(
1571// &vfs,
1572// Some("/overlay"), // Writable layer
1573// vec!["/config", "/system"], // Read-only layers (priority order)
1574// "container_overlay"
1575// )?;
1576//
1577// // Mount normally
1578// vfs.mount(overlay, "/merged", 0)?;
1579// ```
1580//
1581// ## Benefits of Normal Filesystem Approach
1582//
1583// - **Consistent API**: Uses standard mount()/unmount() operations
1584// - **No special VFS methods**: No need for create_and_mount_overlay() etc.
1585// - **Flexible**: Can be combined with other filesystem operations
1586// - **Maintainable**: Less complexity in VfsManager
1587// - **Testable**: Easy to unit test overlay creation independently
1588//
1589
1590// ========================================================================
1591// Usage Examples - Including Cross-VFS Support
1592// ========================================================================
1593//
1594// ## Example 1: Same-VFS Overlay
1595//
1596// ```rust,no_run
1597// // Setup base filesystem
1598// let base_fs = crate::fs::vfs_v2::drivers::tmpfs::TmpFS::new(0);
1599// vfs.mount(base_fs, "/base", 0)?;
1600//
1601// // Setup upper filesystem for writes
1602// let upper_fs = crate::fs::vfs_v2::drivers::tmpfs::TmpFS::new(0);
1603// vfs.mount(upper_fs, "/upper", 0)?;
1604//
1605// // Create overlay combining them - NORMAL FILESYSTEM APPROACH
1606// let overlay = OverlayFS::new_from_paths(
1607// &vfs,
1608// Some("/upper"), // Upper layer (writable)
1609// vec!["/base"], // Lower layers (read-only)
1610// "my_overlay"
1611// )?;
1612//
1613// // Mount like any other filesystem!
1614// vfs.mount(overlay, "/merged", 0)?;
1615// ```
1616//
1617// ## Example 2: Cross-VFS Overlay (Container Scenario)
1618//
1619// ```rust,no_run
1620// // Get global VFS with base system
1621// let base_vfs = get_global_vfs_manager();
1622//
1623// // Create container VFS
1624// let container_vfs = VfsManager::new();
1625//
1626// // Mount container-specific filesystems
1627// let overlay_fs = TmpFS::new(0);
1628// container_vfs.mount(overlay_fs, "/overlay", 0)?;
1629//
1630// let config_fs = TmpFS::new(0);
1631// container_vfs.mount(config_fs, "/config", 0)?;
1632//
1633// // Create cross-VFS overlay: base system from global VFS, overlay from container VFS
1634// let overlay = OverlayFS::new_from_paths_and_vfs(
1635// Some((&container_vfs, "/overlay")), // Upper in container VFS (writable)
1636// vec![
1637// (&container_vfs, "/config"), // Container config (higher priority)
1638// (&base_vfs, "/system"), // Global system files (lower priority)
1639// ],
1640// "container_overlay"
1641// )?;
1642//
1643// // Mount in container VFS - completely seamless!
1644// container_vfs.mount(overlay, "/", 0)?;
1645// ```
1646//
1647// ## Example 3: Multi-layer Same-VFS Overlay
1648//
1649// ```rust,no_run
1650// // Mount multiple base layers
1651// vfs.mount(system_fs, "/system", 0)?; // System files
1652// vfs.mount(config_fs, "/config", 0)?; // Configuration
1653// vfs.mount(overlay_fs, "/overlay", 0)?; // Overlay workspace
1654//
1655// // Create multi-layer overlay
1656// let overlay = OverlayFS::new_from_paths(
1657// &vfs,
1658// Some("/overlay"), // Writable layer
1659// vec!["/config", "/system"], // Read-only layers (priority order)
1660// "container_overlay"
1661// )?;
1662//
1663// // Mount normally
1664// vfs.mount(overlay, "/merged", 0)?;
1665// ```
1666//
1667// ## Benefits of This Approach
1668//
1669// - **Cross-VFS Support**: Layers can come from different VFS managers
1670// - **Consistent API**: Uses standard mount()/unmount() operations
1671// - **No special VFS methods**: No need for create_and_mount_overlay() etc.
1672// - **Flexible**: Can be combined with other filesystem operations
1673// - **Container-friendly**: Perfect for namespace isolation
1674// - **Maintainable**: Less complexity in VfsManager
1675// - **Testable**: Easy to unit test overlay creation independently
1676//
1677
1678#[cfg(test)]
1679mod tests;