use super::file::{File, FileError, FileType};
use super::inode::Inode;
use super::manager::{MountPoint, FS_MANAGER};
use super::{BlkFS, BlkInode, RamInode};
use alloc::string::String;
use alloc::sync::Arc;
use alloc::vec::Vec;
use spin::Mutex;
pub fn split_path(path: &str) -> Vec<&str> {
path.split('/').filter(|s| !s.is_empty()).collect()
}
fn find_mount(normalized: &str) -> Option<(MountPoint, String)> {
let mgr = FS_MANAGER.lock();
let mount = mgr
.mounts
.iter()
.filter(|m| {
let mp = m.path.trim_start_matches('/');
if mp.is_empty() {
return false;
}
normalized == mp
|| normalized.starts_with(mp) && normalized.as_bytes().get(mp.len()) == Some(&b'/')
})
.max_by_key(|m| m.path.len())?;
let mp = mount.path.trim_start_matches('/');
let remainder = if normalized.len() > mp.len() {
normalized[mp.len() + 1..].trim_start_matches('/').into()
} else {
String::new()
};
let result = MountPoint {
path: mount.path.clone(),
fs_type: mount.fs_type,
blkfs: mount.blkfs.clone(),
virtualfs: mount.virtualfs.clone(),
};
Some((result, remainder))
}
pub fn ramfs_resolve(path: &str) -> Result<Arc<Mutex<RamInode>>, FileError> {
let mgr = FS_MANAGER.lock();
let ramfs = mgr.ramfs();
let parts = split_path(path.trim_start_matches('/'));
if parts.is_empty() {
return Ok(mgr.root());
}
let mut current = mgr.root();
for &part in &parts {
current = ramfs.lookup(current, part)?;
}
Ok(current)
}
pub fn ramfs_parent_and_name(path: &str) -> Result<(Arc<Mutex<RamInode>>, String), FileError> {
let parts = split_path(path.trim_start_matches('/'));
if parts.is_empty() {
return Err(FileError::InvalidOperation);
}
let filename = String::from(parts[parts.len() - 1]);
let mgr = FS_MANAGER.lock();
let ramfs = mgr.ramfs();
let mut parent = mgr.root();
for &part in parts.iter().take(parts.len().saturating_sub(1)) {
parent = ramfs.lookup(parent, part)?;
}
Ok((parent, filename))
}
pub fn blkfs_resolve(blkfs: &BlkFS, path: &str) -> Result<BlkInode, FileError> {
let parts = split_path(path);
if parts.is_empty() {
return blkfs.root_inode();
}
let mut current = blkfs.root_inode()?;
for &part in &parts {
current = blkfs.lookup(¤t, part)?;
}
Ok(current)
}
pub fn blkfs_parent_and_name(blkfs: &BlkFS, path: &str) -> Result<(BlkInode, String), FileError> {
let parts = split_path(path);
if parts.is_empty() {
return Err(FileError::InvalidOperation);
}
let filename = String::from(parts[parts.len() - 1]);
let mut parent = blkfs.root_inode()?;
for &part in parts.iter().take(parts.len().saturating_sub(1)) {
parent = blkfs.lookup(&parent, part)?;
}
Ok((parent, filename))
}
pub fn open_file(path: &str) -> Result<Arc<Mutex<dyn File>>, FileError> {
let normalized = path.trim_start_matches('/');
if normalized.is_empty() {
return Err(FileError::IsDirectory);
}
if let Some((mount, remainder)) = find_mount(normalized) {
if let Some(virtualfs) = mount.virtualfs {
return virtualfs.open_file(&remainder);
}
let blkfs = mount.blkfs.ok_or(FileError::InvalidOperation)?;
if remainder.is_empty() {
return Err(FileError::IsDirectory);
}
let inode = blkfs_resolve(&blkfs, &remainder)?;
if inode.file_type() == FileType::Directory {
return Err(FileError::IsDirectory);
}
let file = blkfs.open_file(inode)?;
Ok(Arc::new(Mutex::new(file)))
} else {
let inode = ramfs_resolve(normalized)?;
{
let locked = inode.lock();
if locked.file_type() == FileType::Directory {
return Err(FileError::IsDirectory);
}
}
let ramfs = FS_MANAGER.lock().ramfs();
let file = ramfs.open_file(inode)?;
Ok(Arc::new(Mutex::new(file)))
}
}
pub fn create_file(path: &str) -> Result<Arc<Mutex<dyn File>>, FileError> {
let normalized = path.trim_start_matches('/');
if normalized.is_empty() {
return Err(FileError::InvalidOperation);
}
if let Some((mount, remainder)) = find_mount(normalized) {
if let Some(virtualfs) = mount.virtualfs {
return virtualfs.create_file(&remainder);
}
let blkfs = mount.blkfs.ok_or(FileError::InvalidOperation)?;
if remainder.is_empty() {
return Err(FileError::InvalidOperation);
}
let (mut parent, filename) = blkfs_parent_and_name(&blkfs, &remainder)?;
let inode = blkfs.create_file(&mut parent, &filename)?;
let file = blkfs.open_file(inode)?;
Ok(Arc::new(Mutex::new(file)))
} else {
let (parent, filename) = ramfs_parent_and_name(normalized)?;
let ramfs = FS_MANAGER.lock().ramfs();
let inode = ramfs.create_file(parent, filename)?;
let file = ramfs.open_file(inode)?;
Ok(Arc::new(Mutex::new(file)))
}
}
pub fn create_dir(path: &str) -> Result<(), FileError> {
let normalized = path.trim_start_matches('/');
if normalized.is_empty() {
return Err(FileError::InvalidOperation);
}
if let Some((mount, remainder)) = find_mount(normalized) {
if let Some(virtualfs) = mount.virtualfs {
return virtualfs.create_dir(&remainder);
}
let blkfs = mount.blkfs.ok_or(FileError::InvalidOperation)?;
if remainder.is_empty() {
return Err(FileError::InvalidOperation);
}
let (mut parent, dirname) = blkfs_parent_and_name(&blkfs, &remainder)?;
blkfs.create_directory(&mut parent, &dirname)?;
Ok(())
} else {
let (parent, dirname) = ramfs_parent_and_name(normalized)?;
let ramfs = FS_MANAGER.lock().ramfs();
ramfs.create_directory(parent, dirname)?;
Ok(())
}
}
pub fn remove_file(path: &str) -> Result<(), FileError> {
let normalized = path.trim_start_matches('/');
if normalized.is_empty() {
return Err(FileError::InvalidOperation);
}
if let Some((mount, remainder)) = find_mount(normalized) {
if let Some(virtualfs) = mount.virtualfs {
return virtualfs.remove_file(&remainder);
}
let blkfs = mount.blkfs.ok_or(FileError::InvalidOperation)?;
if remainder.is_empty() {
return Err(FileError::InvalidOperation);
}
let (mut parent, filename) = blkfs_parent_and_name(&blkfs, &remainder)?;
parent.remove_entry(&filename)?;
Ok(())
} else {
let (parent, filename) = ramfs_parent_and_name(normalized)?;
let mut parent_locked = parent.lock();
parent_locked.remove_entry(&filename)?;
Ok(())
}
}
pub fn stat_path(path: &str) -> Result<super::file::FileMetadata, FileError> {
let normalized = path.trim_start_matches('/');
if normalized.is_empty() {
return Err(FileError::InvalidOperation);
}
if let Some((mount, remainder)) = find_mount(normalized) {
if let Some(virtualfs) = mount.virtualfs {
return virtualfs.stat_path(&remainder);
}
let blkfs = mount.blkfs.ok_or(FileError::InvalidOperation)?;
let inode = if remainder.is_empty() {
blkfs.root_inode()?
} else {
blkfs_resolve(&blkfs, &remainder)?
};
Ok(super::file::FileMetadata {
file_type: super::inode::Inode::file_type(&inode),
size: super::inode::Inode::size(&inode),
mode: super::inode::Inode::mode(&inode),
created: 0,
modified: 0,
})
} else {
let inode = ramfs_resolve(normalized)?;
let locked = inode.lock();
Ok(locked.metadata())
}
}
pub fn readdir_path(path: &str) -> Result<alloc::vec::Vec<alloc::string::String>, FileError> {
let normalized = path.trim_start_matches('/');
if normalized.is_empty() {
return Err(FileError::InvalidOperation);
}
if let Some((mount, remainder)) = find_mount(normalized) {
if let Some(virtualfs) = mount.virtualfs {
return virtualfs.readdir_path(&remainder);
}
let blkfs = mount.blkfs.ok_or(FileError::InvalidOperation)?;
let inode = if remainder.is_empty() {
blkfs.root_inode()?
} else {
blkfs_resolve(&blkfs, &remainder)?
};
if inode.file_type() != super::file::FileType::Directory {
return Err(FileError::NotDirectory);
}
inode.list_entries()
} else {
let inode = ramfs_resolve(normalized)?;
let locked = inode.lock();
if locked.file_type() != super::file::FileType::Directory {
return Err(FileError::NotDirectory);
}
locked.list_entries()
}
}