use super::blkfs::BlkFS;
use super::block_dev::BlockDeviceHandle;
use super::disk_format::{detect_disk_format, DiskFormat};
use super::file::{FileError, FileType};
use super::inode::Inode;
use super::ramfs::{RamFS, RamInode};
use super::virtualfs::{default_devtmpfs, default_procfs, default_sysfs, VirtualFileSystem};
use alloc::string::String;
use alloc::sync::Arc;
use alloc::vec::Vec;
use lazy_static::lazy_static;
use spin::Mutex;
const BASE_DIRS: [&str; 7] = ["home", "tmp", "dev", "mnt", "proc", "sys", "run"];
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BlkFsMountMode {
OpenOrCreate,
OpenOnly,
Format,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct BlkFsMountOptions {
pub mode: BlkFsMountMode,
}
impl BlkFsMountOptions {
pub const fn open_or_create() -> Self {
Self {
mode: BlkFsMountMode::OpenOrCreate,
}
}
pub const fn open_only() -> Self {
Self {
mode: BlkFsMountMode::OpenOnly,
}
}
pub const fn format() -> Self {
Self {
mode: BlkFsMountMode::Format,
}
}
}
impl Default for BlkFsMountOptions {
fn default() -> Self {
Self::open_or_create()
}
}
pub struct MountPoint {
pub path: String,
pub fs_type: FsType,
pub blkfs: Option<Arc<BlkFS>>,
pub virtualfs: Option<Arc<dyn VirtualFileSystem>>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FsType {
RamFS,
BlkFS,
ProcFS,
SysFS,
DevTmpFS,
}
impl FsType {
fn matches_virtualfs_name(self, name: &str) -> bool {
matches!(
(self, name),
(FsType::ProcFS, "procfs") | (FsType::SysFS, "sysfs") | (FsType::DevTmpFS, "devtmpfs")
)
}
}
pub struct FileSystemManager {
pub root: Arc<Mutex<RamInode>>,
pub ramfs: Arc<RamFS>,
pub mounts: Vec<MountPoint>,
}
impl FileSystemManager {
pub fn new(ramfs: Arc<RamFS>) -> Self {
let root = ramfs.root();
FileSystemManager {
root,
ramfs,
mounts: Vec::new(),
}
}
pub fn root(&self) -> Arc<Mutex<RamInode>> {
self.root.clone()
}
pub fn ramfs(&self) -> Arc<RamFS> {
self.ramfs.clone()
}
fn ensure_ramfs_directory(&self, name: &str) -> Result<bool, &'static str> {
if name.is_empty() || name.contains('/') {
return Err("Invalid directory name");
}
match self.ramfs.lookup(self.root.clone(), name) {
Ok(inode) => {
if inode.lock().file_type() == FileType::Directory {
Ok(false)
} else {
Err("Path exists but is not a directory")
}
}
Err(FileError::NotFound) => self
.ramfs
.create_directory(self.root.clone(), String::from(name))
.map(|_| true)
.map_err(|_| "Failed to create directory"),
Err(_) => Err("Failed to inspect directory"),
}
}
fn rollback_created_ramfs_directory(&self, name: &str, created: bool) {
if created {
let _ = self.ramfs.remove(self.root.clone(), name);
}
}
fn normalize_mount_path(path: &str) -> Result<(String, String), &'static str> {
let trimmed = path.trim();
if !trimmed.starts_with('/') {
return Err("Mount path must be absolute");
}
let dir_name = trimmed.trim_matches('/');
if dir_name.is_empty() {
return Err("Cannot mount over root");
}
if dir_name.contains('/') {
return Err("Nested mount paths are not supported");
}
let mut mount_path = String::from("/");
mount_path.push_str(dir_name);
Ok((mount_path, String::from(dir_name)))
}
pub fn mount_blkfs(&mut self, path: &str, dev: BlockDeviceHandle) -> Result<(), &'static str> {
self.mount_blkfs_with_options(path, dev, BlkFsMountOptions::default())
}
pub fn mount_blkfs_with_options(
&mut self,
path: &str,
dev: BlockDeviceHandle,
options: BlkFsMountOptions,
) -> Result<(), &'static str> {
let (mount_path, dir_name) = Self::normalize_mount_path(path)?;
if self.mounts.iter().any(|m| m.path == mount_path) {
return Err("Mount point already exists");
}
let created_mount_dir = self.ensure_ramfs_directory(&dir_name)?;
let format =
match detect_disk_format(&dev).map_err(|_| "Failed to inspect block device format") {
Ok(format) => format,
Err(err) => {
self.rollback_created_ramfs_directory(&dir_name, created_mount_dir);
return Err(err);
}
};
let blkfs_result = match options.mode {
BlkFsMountMode::OpenOrCreate => match format {
DiskFormat::BlkFS => BlkFS::open(dev).map_err(|_| "Failed to open BlkFS"),
DiskFormat::Blank => BlkFS::create(dev).map_err(|_| "Failed to create BlkFS"),
other => Err(unsupported_disk_format_error(other)),
},
BlkFsMountMode::OpenOnly => match format {
DiskFormat::BlkFS => BlkFS::open(dev).map_err(|_| "Failed to open BlkFS"),
other => Err(unsupported_disk_format_error(other)),
},
BlkFsMountMode::Format => BlkFS::create(dev).map_err(|_| "Failed to create BlkFS"),
};
let blkfs = match blkfs_result {
Ok(blkfs) => Arc::new(blkfs),
Err(err) => {
self.rollback_created_ramfs_directory(&dir_name, created_mount_dir);
return Err(err);
}
};
self.mounts.push(MountPoint {
path: mount_path,
fs_type: FsType::BlkFS,
blkfs: Some(blkfs),
virtualfs: None,
});
Ok(())
}
pub fn mount_virtualfs(
&mut self,
path: &str,
fs_type: FsType,
virtualfs: Arc<dyn VirtualFileSystem>,
) -> Result<(), &'static str> {
let (mount_path, dir_name) = Self::normalize_mount_path(path)?;
if !matches!(fs_type, FsType::ProcFS | FsType::SysFS | FsType::DevTmpFS) {
return Err("Invalid virtual filesystem type");
}
if !fs_type.matches_virtualfs_name(virtualfs.name()) {
return Err("Virtual filesystem type mismatch");
}
if self.mounts.iter().any(|m| m.path == mount_path) {
return Err("Mount point already exists");
}
self.ensure_ramfs_directory(&dir_name)?;
self.mounts.push(MountPoint {
path: mount_path,
fs_type,
blkfs: None,
virtualfs: Some(virtualfs),
});
Ok(())
}
fn ensure_virtualfs_mounted(
&mut self,
path: &str,
fs_type: FsType,
virtualfs: Arc<dyn VirtualFileSystem>,
) -> Result<(), &'static str> {
let (mount_path, _) = Self::normalize_mount_path(path)?;
if self.mounts.iter().any(|m| m.path == mount_path) {
return Ok(());
}
self.mount_virtualfs(path, fs_type, virtualfs)
}
pub fn get_blkfs(&self, path: &str) -> Option<Arc<BlkFS>> {
self.mounts
.iter()
.find(|m| m.path == path && m.fs_type == FsType::BlkFS)
.and_then(|m| m.blkfs.clone())
}
pub fn umount(&mut self, path: &str) -> Result<(), &'static str> {
let (mount_path, _) = Self::normalize_mount_path(path)?;
let idx = self
.mounts
.iter()
.position(|m| m.path == mount_path)
.ok_or("Mount point not found")?;
if let Some(blkfs) = self.mounts[idx].blkfs.as_ref() {
blkfs
.sync()
.map_err(|_| "Failed to flush mounted filesystem")?;
}
self.mounts.remove(idx);
Ok(())
}
}
fn unsupported_disk_format_error(format: DiskFormat) -> &'static str {
match format {
DiskFormat::Blank => "Device is blank (not BlkFS)",
DiskFormat::BlkFS => "Failed to open BlkFS",
DiskFormat::InvalidBlkFS => "Invalid BlkFS superblock",
DiskFormat::TooSmall => "Device is too small for BlkFS",
DiskFormat::MasterBootRecord => "Unsupported MBR partition table",
DiskFormat::GuidPartitionTable => "Unsupported GPT partition table",
DiskFormat::FatFamily => "Unsupported FAT filesystem",
DiskFormat::ExtFamily => "Unsupported ext-family filesystem",
DiskFormat::Unknown => "Unsupported or unknown disk format",
}
}
lazy_static! {
pub static ref FS_MANAGER: Mutex<FileSystemManager> = {
let ramfs = Arc::new(RamFS::new());
let manager = FileSystemManager::new(ramfs);
Mutex::new(manager)
};
}
pub fn init_filesystem() {
let mut manager = FS_MANAGER.lock();
for dir in BASE_DIRS {
manager
.ensure_ramfs_directory(dir)
.expect("Failed to initialize base directory");
}
manager
.ensure_virtualfs_mounted("/proc", FsType::ProcFS, default_procfs())
.expect("Failed to mount procfs");
manager
.ensure_virtualfs_mounted("/sys", FsType::SysFS, default_sysfs())
.expect("Failed to mount sysfs");
manager
.ensure_virtualfs_mounted("/dev", FsType::DevTmpFS, default_devtmpfs())
.expect("Failed to mount devtmpfs");
}