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
impl VfsManager
Sourcepub fn new() -> Self
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());
Sourcepub fn register_fs(&mut self, fs: Box<dyn VirtualFileSystem>) -> usize
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")?;
Sourcepub fn create_and_register_block_fs(
&mut self,
driver_name: &str,
block_device: Box<dyn BlockDevice>,
block_size: usize,
) -> Result<usize>
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 useblock_device
- The block device that will store the filesystem datablock_size
- The block size for I/O operations (typically 512, 1024, or 4096 bytes)
§Returns
Ok(usize)
- The filesystem ID assigned by this VfsManagerErr(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")?;
Sourcepub fn create_and_register_memory_fs(
&mut self,
driver_name: &str,
memory_area: &MemoryArea,
) -> Result<usize>
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 usememory_area
- The memory region containing filesystem data or available for use
§Returns
Ok(usize)
- The filesystem ID assigned by this VfsManagerErr(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, "/")?;
Sourcepub fn create_and_register_fs_with_params(
&mut self,
driver_name: &str,
params: &dyn FileSystemParams,
) -> Result<usize>
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 driverparams
- 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", ¶ms)?;
Sourcepub fn bind_mount(
&mut self,
source_path: &str,
target_path: &str,
read_only: bool,
) -> Result<()>
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 fromtarget_path
- The target mount point where the source will be accessibleread_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)?;
Sourcepub fn bind_mount_from(
&mut self,
source_vfs: &Arc<VfsManager>,
source_path: &str,
target_path: &str,
read_only: bool,
) -> Result<()>
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 pathsource_path
- The source path in the source VFS managertarget_path
- The target mount point in this VFS managerread_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)?;
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 fromtarget_path
- The target mount point
§Returns
Result<()>
- Ok if the shared bind mount was successful, Err otherwise
Thread-safe bind mount for use from system calls
This method can be called on a shared VfsManager (Arc
§Arguments
source_path
- The source path to bind fromtarget_path
- The target mount point where the source will be accessibleread_only
- Whether the bind mount should be read-only
§Returns
Result<()>
- Ok if the bind mount was successful, Err otherwise
Sourcepub fn is_bind_mount(&self, path: &str) -> bool
pub fn is_bind_mount(&self, path: &str) -> bool
Sourcefn normalize_path(path: &str) -> String
fn normalize_path(path: &str) -> String
Sourcefn with_resolve_path<F, T>(&self, path: &str, f: F) -> Result<T>where
F: FnOnce(&FileSystemRef, &str) -> Result<T>,
fn with_resolve_path<F, T>(&self, path: &str, f: F) -> Result<T>where
F: FnOnce(&FileSystemRef, &str) -> Result<T>,
Sourcefn resolve_path(&self, path: &str) -> Result<(FileSystemRef, String)>
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
- Path Normalization: Remove
.
and..
components, validate against directory traversal - Mount Point Lookup: Find the most specific mount point for the given path
- Bind Mount Resolution: Transparently handle bind mounts by resolving to source
- 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 pathString
: Path relative to the filesystem root (always starts with/
)
§Errors
FileSystemErrorKind::NotFound
- No filesystem mounted for the pathFileSystemErrorKind::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
Sourcepub fn to_absolute_path(task: &Task, path: &str) -> Result<String>
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 directorypath
- 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");
Sourcepub fn open(&self, path: &str, flags: u32) -> Result<File>
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 openflags
- 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)?;
Sourcepub fn read_dir(&self, path: &str) -> Result<Vec<DirectoryEntry>>
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);
}
Sourcepub fn create_file(&self, path: &str, file_type: FileType) -> Result<()>
pub fn create_file(&self, path: &str, file_type: FileType) -> Result<()>
Sourcepub fn create_dir(&self, path: &str) -> Result<()>
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")?;
Sourcepub fn remove(&self, path: &str) -> Result<()>
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")?;
Sourcepub fn metadata(&self, path: &str) -> Result<FileMetadata>
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);
Sourcepub fn create_regular_file(&self, path: &str) -> Result<()>
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")?;
Sourcepub fn create_char_device(
&self,
path: &str,
device_info: DeviceFileInfo,
) -> Result<()>
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 createddevice_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)?;
Sourcepub fn create_block_device(
&self,
path: &str,
device_info: DeviceFileInfo,
) -> Result<()>
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 createddevice_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)?;
Sourcepub fn create_pipe(&self, path: &str) -> Result<()>
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")?;
Sourcepub fn create_symlink(&self, path: &str) -> Result<()>
pub fn create_symlink(&self, path: &str) -> Result<()>
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")?;
Sourcepub fn create_socket(&self, path: &str) -> Result<()>
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")?;
Sourcepub fn create_device_file(
&self,
path: &str,
device_info: DeviceFileInfo,
) -> Result<()>
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 createdevice_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)?;
Sourcepub fn mount_count(&self) -> usize
pub fn mount_count(&self) -> usize
Get the number of mount points
Sourcepub fn has_mount_point(&self, path: &str) -> bool
pub fn has_mount_point(&self, path: &str) -> bool
Check if a specific mount point exists
Sourcepub fn list_mount_points(&self) -> Vec<String>
pub fn list_mount_points(&self) -> Vec<String>
List all mount points