Struct VfsManager

Source
pub struct VfsManager {
    filesystems: RwLock<BTreeMap<usize, FileSystemRef>>,
    mount_tree: RwLock<MountTree>,
    next_fs_id: RwLock<usize>,
}
Expand description

VFS manager for per-task or shared filesystem management.

VfsManager provides flexible virtual filesystem management supporting both process isolation and filesystem sharing scenarios.

§Architecture

Each VfsManager instance maintains:

  • Independent mount point namespace using hierarchical MountTree
  • Reference-counted filesystem objects that can be shared between managers
  • Thread-safe operations via RwLock protection
  • Security-enhanced path resolution with protection against directory traversal

§Usage Scenarios

§1. Container Isolation

Each container gets its own VfsManager with completely isolated mount points:

let mut container_vfs = VfsManager::new();
let fs_index = container_vfs.register_fs(container_fs);
container_vfs.mount(fs_index, "/");
task.vfs = Some(Arc::new(container_vfs));

§2. Shared Filesystem Access

Multiple tasks can share VfsManager objects using Arc:

let shared_vfs = Arc::new(original_vfs); // Shares filesystem objects and mount points
let fs_index = shared_vfs.register_fs(shared_fs);
shared_vfs.mount(fs_index, "/mnt/shared"); // Shared mount points

§Performance Improvements

The new MountTree implementation provides:

  • O(log k) path resolution where k is path depth
  • Efficient mount point hierarchy management
  • Security-enhanced path normalization
  • Reduced memory usage through Trie structure

§Thread Safety

All internal data structures use RwLock for thread-safe concurrent access. VfsManager can be shared between threads using Arc for cases requiring shared filesystem access across multiple tasks.

Fields§

§filesystems: RwLock<BTreeMap<usize, FileSystemRef>>§mount_tree: RwLock<MountTree>§next_fs_id: RwLock<usize>

Implementations§

Source§

impl VfsManager

Source

pub fn new() -> Self

Create a new VFS manager instance

This method creates a new VfsManager with empty filesystem registry and mount tree. Each VfsManager instance provides an isolated filesystem namespace, making it suitable for containerization and process isolation scenarios.

§Returns
  • Self - A new VfsManager instance ready for filesystem registration and mounting
§Examples
// Create isolated VFS for a container
let container_vfs = VfsManager::new();
 
// Create shared VFS for multiple tasks
let shared_vfs = Arc::new(VfsManager::new());
Source

pub fn register_fs(&mut self, fs: Box<dyn VirtualFileSystem>) -> usize

Register a filesystem with the VFS manager

This method adds a filesystem instance to the VfsManager’s registry, assigning it a unique ID for future operations. The filesystem remains available for mounting until it’s actually mounted on a mount point.

§Arguments
  • fs - The filesystem instance to register (must implement VirtualFileSystem)
§Returns
  • usize - Unique filesystem ID for use in mount operations
§Thread Safety

This method is thread-safe and can be called concurrently from multiple threads.

§Examples
// Register a block-based filesystem
let device = Box::new(SomeBlockDevice::new());
let fs = Box::new(SomeFileSystem::new("myfs", device, 512));
let fs_id = vfs_manager.register_fs(fs);
 
// Later mount the filesystem
vfs_manager.mount(fs_id, "/mnt")?;
Source

pub fn create_and_register_block_fs( &mut self, driver_name: &str, block_device: Box<dyn BlockDevice>, block_size: usize, ) -> Result<usize>

Create and register a block device-based filesystem

This convenience method combines filesystem creation and registration in a single operation. It uses the global FileSystemDriverManager to create a filesystem from the specified block device and automatically registers it with this VfsManager.

§Arguments
  • driver_name - The name of the registered filesystem driver to use
  • block_device - The block device that will store the filesystem data
  • block_size - The block size for I/O operations (typically 512, 1024, or 4096 bytes)
§Returns
  • Ok(usize) - The filesystem ID assigned by this VfsManager
  • Err(FileSystemError) - If driver not found, creation fails, or registration fails
§Example
let device = get_block_device();
let fs_id = vfs_manager.create_and_register_block_fs("ext4", device, 4096)?;
vfs_manager.mount(fs_id, "/mnt")?;
Source

pub fn create_and_register_memory_fs( &mut self, driver_name: &str, memory_area: &MemoryArea, ) -> Result<usize>

Create and register a memory-based filesystem

This convenience method combines filesystem creation and registration in a single operation for memory-based filesystems like tmpfs or initramfs. It uses the global FileSystemDriverManager to create a filesystem and automatically registers it.

§Arguments
  • driver_name - The name of the registered filesystem driver to use
  • memory_area - The memory region containing filesystem data or available for use
§Returns
  • Ok(usize) - The filesystem ID assigned by this VfsManager
  • Err(FileSystemError) - If driver not found, creation fails, or registration fails
§Example
let memory_area = MemoryArea::new(initramfs_addr, initramfs_size);
let fs_id = vfs_manager.create_and_register_memory_fs("cpiofs", &memory_area)?;
vfs_manager.mount(fs_id, "/")?;
Source

pub fn create_and_register_fs_with_params( &mut self, driver_name: &str, params: &dyn FileSystemParams, ) -> Result<usize>

Create and register a file system with structured parameters

This method allows creating file systems with structured configuration parameters. It uses dynamic dispatch to handle different parameter types, enabling future dynamic filesystem module loading.

§Arguments
  • driver_name - The name of the file system driver
  • params - Parameter structure implementing FileSystemParams
§Returns
  • Result<usize> - The ID of the registered file system
§Errors
  • FileSystemError - If the driver is not found or if the file system cannot be created
§Example
use crate::fs::params::TmpFSParams;
 
let params = TmpFSParams::with_memory_limit(1048576); // 1MB limit
let fs_id = manager.create_and_register_fs_with_params("tmpfs", &params)?;
Source

pub fn mount(&mut self, fs_id: usize, mount_point: &str) -> Result<()>

Mount a file system at a specified mount point

§Arguments
  • fs_id - The ID of the file system to mount
  • mount_point - The mount point for the file system
§Returns
  • Result<()> - Ok if the mount was successful, Err if there was an error
Source

pub fn unmount(&mut self, mount_point: &str) -> Result<()>

Unmount a file system from a specified mount point

§Arguments
  • mount_point - The mount point to unmount
§Returns
  • Result<()> - Ok if the unmount was successful, Err if there was an error
Source

pub fn bind_mount( &mut self, source_path: &str, target_path: &str, read_only: bool, ) -> Result<()>

Bind mount a source path to a target path

This creates a bind mount where the target path will provide access to the same content as the source path. The bind mount can be read-only or read-write.

§Arguments
  • source_path - The source path to bind from
  • target_path - The target mount point where the source will be accessible
  • read_only - Whether the bind mount should be read-only
§Returns
  • Result<()> - Ok if the bind mount was successful, Err otherwise
§Example
// Bind mount /mnt/source to /mnt/target as read-only
vfs_manager.bind_mount("/mnt/source", "/mnt/target", true)?;
Source

pub fn bind_mount_from( &mut self, source_vfs: &Arc<VfsManager>, source_path: &str, target_path: &str, read_only: bool, ) -> Result<()>

Bind mount from another VFS manager

This creates a bind mount where the target path in this VFS manager will provide access to content from a different VFS manager instance. This is useful for sharing filesystem content between containers.

§Arguments
  • source_vfs - The source VFS manager containing the source path
  • source_path - The source path in the source VFS manager
  • target_path - The target mount point in this VFS manager
  • read_only - Whether the bind mount should be read-only
§Returns
  • Result<()> - Ok if the bind mount was successful, Err otherwise
§Example
// Bind mount /data from host_vfs to /mnt/shared in container_vfs
container_vfs.bind_mount_from(&host_vfs, "/data", "/mnt/shared", false)?;
Source

pub fn bind_mount_shared( &mut self, source_path: &str, target_path: &str, ) -> Result<()>

Create a shared bind mount

This creates a shared bind mount where changes to mount propagation will be shared between the source and target. This is useful for scenarios where you want mount events to propagate between namespaces.

§Arguments
  • source_path - The source path to bind from
  • target_path - The target mount point
§Returns
  • Result<()> - Ok if the shared bind mount was successful, Err otherwise
Source

pub fn bind_mount_shared_ref( &self, source_path: &str, target_path: &str, read_only: bool, ) -> Result<()>

Thread-safe bind mount for use from system calls

This method can be called on a shared VfsManager (Arc) from system call context where &mut self is not available.

§Arguments
  • source_path - The source path to bind from
  • target_path - The target mount point where the source will be accessible
  • read_only - Whether the bind mount should be read-only
§Returns
  • Result<()> - Ok if the bind mount was successful, Err otherwise
Source

pub fn is_bind_mount(&self, path: &str) -> bool

Check if a path is a bind mount

§Arguments
  • path - The path to check
§Returns
  • bool - True if the path is a bind mount, false otherwise
Source

fn normalize_path(path: &str) -> String

Normalize a path

§Arguments
  • path - The path to normalize
§Returns
  • String - The normalized path
Source

fn with_resolve_path<F, T>(&self, path: &str, f: F) -> Result<T>
where F: FnOnce(&FileSystemRef, &str) -> Result<T>,

Execute a function with the resolved file system and path

§Arguments
  • path - The path to resolve
  • f - The function to execute with the resolved file system and path
§Returns
  • Result<T> - The result of the function execution
§Errors
  • FileSystemError - If no file system is mounted for the specified path
Source

fn resolve_path(&self, path: &str) -> Result<(FileSystemRef, String)>

Resolve the path to the file system and relative path

This method performs path resolution within the VfsManager’s mount tree, handling bind mounts, security validation, and path normalization.

§Path Resolution Process
  1. Path Normalization: Remove . and .. components, validate against directory traversal
  2. Mount Point Lookup: Find the most specific mount point for the given path
  3. Bind Mount Resolution: Transparently handle bind mounts by resolving to source
  4. Relative Path Calculation: Calculate the path relative to the filesystem root
§Arguments
  • path - The absolute path to resolve (must start with /)
§Returns
  • Result<(FileSystemRef, String)> - Tuple containing:
    • FileSystemRef: Arc-wrapped filesystem that handles this path
    • String: Path relative to the filesystem root (always starts with /)
§Errors
  • FileSystemErrorKind::NotFound - No filesystem mounted for the path
  • FileSystemErrorKind::InvalidPath - Path validation failed (e.g., directory traversal attempt)
§Examples
// Mount filesystem at /mnt
let fs_id = vfs.register_fs(filesystem);
vfs.mount(fs_id, "/mnt")?;
 
// Resolve paths
let (fs, rel_path) = vfs.resolve_path("/mnt/dir/file.txt")?;
assert_eq!(rel_path, "/dir/file.txt");
 
// Bind mount example
vfs.bind_mount("/mnt/data", "/data", false)?;
let (fs2, rel_path2) = vfs.resolve_path("/data/file.txt")?;
// fs2 points to the same filesystem as fs, rel_path2 is "/data/file.txt"
§Security

This method includes protection against directory traversal attacks:

  • Normalizes .. and . components
  • Prevents escaping mount point boundaries
  • Validates all path components for security
§Arguments
  • path - The path to resolve (must be absolute)
§Returns
  • Result<(FileSystemRef, String)> - The resolved file system and relative path
§Errors
  • FileSystemError - If no file system is mounted for the specified path
Source

pub fn to_absolute_path(task: &Task, path: &str) -> Result<String>

Get absolute path from relative path and current working directory

§Arguments

Convert a relative path to an absolute path using the task’s current working directory

This method provides path resolution for system calls that accept relative paths. It combines the task’s current working directory with the relative path to create an absolute path suitable for VFS operations.

§Arguments
  • task - The task containing the current working directory
  • path - The relative path to convert (if already absolute, returns as-is)
§Returns
  • Result<String> - The absolute path ready for VFS operations
§Examples
// If task cwd is "/home/user" and path is "documents/file.txt"
let abs_path = VfsManager::to_absolute_path(&task, "documents/file.txt")?;
assert_eq!(abs_path, "/home/user/documents/file.txt");
 
// Absolute paths are returned unchanged
let abs_path = VfsManager::to_absolute_path(&task, "/etc/config")?;
assert_eq!(abs_path, "/etc/config");
Source

pub fn open(&self, path: &str, flags: u32) -> Result<File>

Open a file for reading/writing

This method opens a file through the VFS layer, automatically resolving the path to the appropriate filesystem and handling mount points and bind mounts transparently.

§Arguments
  • path - The absolute path to the file to open
  • flags - File open flags (read, write, create, etc.)
§Returns
  • Result<File> - A file handle for performing I/O operations
§Errors
  • FileSystemError - If the file cannot be opened or the path is invalid
§Examples
// Open an existing file for reading
let file = vfs.open("/etc/config.txt", OpenFlags::RDONLY)?;
 
// Create and open a new file for writing
let file = vfs.open("/tmp/output.txt", OpenFlags::WRONLY | OpenFlags::CREATE)?;
Source

pub fn read_dir(&self, path: &str) -> Result<Vec<DirectoryEntry>>

Read directory entries

This method reads all entries from a directory, returning a vector of directory entry structures containing file names, types, and metadata.

§Arguments
  • path - The absolute path to the directory to read
§Returns
  • Result<Vec<DirectoryEntry>> - Vector of directory entries
§Errors
  • FileSystemError - If the directory cannot be read or doesn’t exist
§Examples
// List files in a directory
let entries = vfs.read_dir("/home/user")?;
for entry in entries {
    println!("{}: {:?}", entry.name, entry.file_type);
}
Source

pub fn create_file(&self, path: &str, file_type: FileType) -> Result<()>

Create a file with specified type

§Arguments
  • path - The path to the file to create
  • file_type - The type of file to create
§Returns
  • Result<()> - Ok if the file was created successfully, Err otherwise
Source

pub fn create_dir(&self, path: &str) -> Result<()>

Create a directory at the specified path

This method creates a new directory in the filesystem, handling parent directory creation if necessary (depending on filesystem implementation).

§Arguments
  • path - The absolute path where the directory should be created
§Returns
  • Result<()> - Ok if the directory was created successfully
§Errors
  • FileSystemError - If the directory cannot be created or already exists
§Examples
// Create a new directory
vfs.create_dir("/tmp/new_directory")?;
 
// Create nested directories (if supported by filesystem)
vfs.create_dir("/tmp/path/to/directory")?;
Source

pub fn remove(&self, path: &str) -> Result<()>

Remove a file or directory

This method removes a file or directory from the filesystem. For directories, the behavior depends on the filesystem implementation (some may require the directory to be empty).

§Arguments
  • path - The absolute path to the file or directory to remove
§Returns
  • Result<()> - Ok if the item was removed successfully
§Errors
  • FileSystemError - If the item cannot be removed or doesn’t exist
§Examples
// Remove a file
vfs.remove("/tmp/old_file.txt")?;
 
// Remove a directory
vfs.remove("/tmp/empty_directory")?;
Source

pub fn metadata(&self, path: &str) -> Result<FileMetadata>

Get file or directory metadata

This method retrieves metadata information about a file or directory, including file type, size, permissions, and timestamps.

§Arguments
  • path - The absolute path to the file or directory
§Returns
  • Result<FileMetadata> - Metadata structure containing file information
§Errors
  • FileSystemError - If the file doesn’t exist or metadata cannot be retrieved
§Examples
// Get file metadata
let metadata = vfs.metadata("/etc/config.txt")?;
println!("File size: {} bytes", metadata.size);
println!("File type: {:?}", metadata.file_type);
Source

pub fn create_regular_file(&self, path: &str) -> Result<()>

Create a regular file

This method creates a new regular file at the specified path. It’s a convenience method that creates a file with FileType::RegularFile.

§Arguments
  • path - The absolute path where the file should be created
§Returns
  • Result<()> - Ok if the file was created successfully
§Errors
  • FileSystemError - If the file cannot be created or already exists
§Examples
// Create a new regular file
vfs.create_regular_file("/tmp/new_file.txt")?;
Source

pub fn create_char_device( &self, path: &str, device_info: DeviceFileInfo, ) -> Result<()>

Create a character device file

This method creates a character device file in the filesystem. Character devices provide unbuffered access to hardware devices and are accessed through character-based I/O operations.

In Scarlet’s device architecture, devices are identified by a unique device ID rather than traditional major/minor number pairs, providing a simplified and unified device identification system.

§Arguments
  • path - The absolute path where the character device file should be created
  • device_info - Device information including unique device ID and type
§Returns
  • Result<()> - Ok if the device file was created successfully
§Errors
  • FileSystemError - If the device file cannot be created
§Examples
// Create a character device file for /dev/tty
let device_info = DeviceFileInfo {
    device_id: 1,
    device_type: DeviceType::Char,
};
vfs.create_char_device("/dev/tty", device_info)?;
Source

pub fn create_block_device( &self, path: &str, device_info: DeviceFileInfo, ) -> Result<()>

Create a block device file

This method creates a block device file in the filesystem. Block devices provide buffered access to hardware devices and are accessed through block-based I/O operations.

In Scarlet’s device architecture, devices are identified by a unique device ID rather than traditional major/minor number pairs, enabling simplified device management and registration.

§Arguments
  • path - The absolute path where the block device file should be created
  • device_info - Device information including unique device ID and type
§Returns
  • Result<()> - Ok if the device file was created successfully
§Errors
  • FileSystemError - If the device file cannot be created
§Examples
// Create a block device file for /dev/sda
let device_info = DeviceFileInfo {
    device_id: 8,
    device_type: DeviceType::Block,
};
vfs.create_block_device("/dev/sda", device_info)?;
Source

pub fn create_pipe(&self, path: &str) -> Result<()>

Create a named pipe (FIFO)

This method creates a named pipe in the filesystem, which provides inter-process communication through a FIFO queue mechanism.

§Arguments
  • path - The absolute path where the pipe should be created
§Returns
  • Result<()> - Ok if the pipe was created successfully
§Errors
  • FileSystemError - If the pipe cannot be created
§Examples
// Create a named pipe for IPC
vfs.create_pipe("/tmp/my_pipe")?;

Create a symbolic link

This method creates a symbolic link (symlink) in the filesystem. A symbolic link is a file that contains a reference to another file or directory.

§Arguments
  • path - The absolute path where the symbolic link should be created
§Returns
  • Result<()> - Ok if the symbolic link was created successfully
§Errors
  • FileSystemError - If the symbolic link cannot be created
§Examples
// Create a symbolic link
vfs.create_symlink("/tmp/link_to_file")?;
Source

pub fn create_socket(&self, path: &str) -> Result<()>

Create a socket file

This method creates a Unix domain socket file in the filesystem. Socket files provide local inter-process communication endpoints.

§Arguments
  • path - The absolute path where the socket file should be created
§Returns
  • Result<()> - Ok if the socket file was created successfully
§Errors
  • FileSystemError - If the socket file cannot be created
§Examples
// Create a Unix domain socket
vfs.create_socket("/tmp/my_socket")?;
Source

pub fn create_device_file( &self, path: &str, device_info: DeviceFileInfo, ) -> Result<()>

Create a device file of any type

This is a convenience method that automatically determines the appropriate FileType based on the DeviceType in the DeviceFileInfo.

§Arguments
  • path - The path to the device file to create
  • device_info - Information about the device including its type
§Returns
  • Result<()> - Ok if the device file was created successfully, Err otherwise
§Example
use crate::device::{DeviceType, DeviceFileInfo};
 
let device_info = DeviceFileInfo {
    device_id: 1,
    device_type: DeviceType::Char,
};
 
vfs_manager.create_device_file("/dev/tty0", device_info)?;
Source

pub fn mount_count(&self) -> usize

Get the number of mount points

Source

pub fn has_mount_point(&self, path: &str) -> bool

Check if a specific mount point exists

Source

pub fn list_mount_points(&self) -> Vec<String>

List all mount points

Auto Trait Implementations§

§

impl !Freeze for VfsManager

§

impl !RefUnwindSafe for VfsManager

§

impl Send for VfsManager

§

impl Sync for VfsManager

§

impl Unpin for VfsManager

§

impl !UnwindSafe for VfsManager

Blanket Implementations§

§

impl<T> Any for T
where T: 'static + ?Sized,

§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
§

impl<T> Borrow<T> for T
where T: ?Sized,

§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
§

impl<T> BorrowMut<T> for T
where T: ?Sized,

§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<T> From<T> for T

§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T, U> Into<U> for T
where U: From<T>,

§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of [From]<T> for U chooses to do.

§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.