use crate::environment::is_uhyve;
use crate::synch::spinlock::Spinlock;
use alloc::borrow::ToOwned;
use alloc::boxed::Box;
use alloc::collections::BTreeMap;
use alloc::string::String;
use alloc::vec::Vec;
use core::ops::Deref;
pub static FILESYSTEM: Spinlock<Filesystem> = Spinlock::new(Filesystem::new());
pub struct Filesystem {
mounts: BTreeMap<String, Box<dyn PosixFileSystem + Send>>,
files: BTreeMap<u64, Box<dyn PosixFile + Send>>,
}
impl Filesystem {
pub const fn new() -> Self {
Self {
mounts: BTreeMap::new(),
files: BTreeMap::new(),
}
}
fn assign_new_fd(&self) -> u64 {
if let Some((fd, _)) = self.files.iter().next_back() {
fd + 1
} else {
3 }
}
fn add_file(&mut self, file: Box<dyn PosixFile + Send>) -> u64 {
let fd = self.assign_new_fd();
self.files.insert(fd, file);
fd
}
fn parse_path<'a, 'b>(
&'a self,
path: &'b str,
) -> Result<(&'a (dyn PosixFileSystem + Send), &'b str), FileError> {
let mut pathsplit = path.splitn(3, '/');
if path.starts_with('/') {
pathsplit.next();
let mount = pathsplit.next().unwrap();
let internal_path = pathsplit.next().unwrap();
if let Some(fs) = self.mounts.get(mount) {
return Ok((fs.deref(), internal_path));
}
warn!(
"Trying to open file on non-existing mount point '{}'!",
mount
);
} else {
let mount = if !is_uhyve() {
option_env!("HERMIT_WD").unwrap_or("root")
} else {
"."
};
let internal_path = pathsplit.next().unwrap();
debug!(
"Assume that the directory '{}' is used as mount point!",
mount
);
if let Some(fs) = self.mounts.get(mount) {
return Ok((fs.deref(), internal_path));
}
info!(
"Trying to open file on non-existing mount point '{}'!",
mount
);
}
Err(FileError::ENOENT())
}
pub fn open(&mut self, path: &str, perms: FilePerms) -> Result<u64, FileError> {
debug!("Opening file {} {:?}", path, perms);
let (fs, internal_path) = self.parse_path(path)?;
let file = fs.open(internal_path, perms)?;
Ok(self.add_file(file))
}
pub fn close(&mut self, fd: u64) {
debug!("Closing fd {}", fd);
if let Some(file) = self.files.get_mut(&fd) {
file.close().unwrap(); }
self.files.remove(&fd);
}
pub fn unlink(&mut self, path: &str) -> Result<(), FileError> {
info!("Unlinking file {}", path);
let (fs, internal_path) = self.parse_path(path)?;
fs.unlink(internal_path)?;
Ok(())
}
pub fn mount(
&mut self,
mntpath: &str,
mntobj: Box<dyn PosixFileSystem + Send>,
) -> Result<(), ()> {
info!("Mounting {}", mntpath);
if mntpath.contains('/') {
warn!(
"Trying to mount at '{}', but slashes in name are not supported!",
mntpath
);
return Err(());
}
if self.mounts.contains_key(mntpath) {
warn!("Mountpoint already exists!");
return Err(());
}
self.mounts.insert(mntpath.to_owned(), mntobj);
Ok(())
}
pub fn fd_op(&mut self, fd: u64, f: impl FnOnce(&mut Box<dyn PosixFile + Send>)) {
f(self.files.get_mut(&fd).unwrap());
}
}
#[derive(Debug)]
pub enum FileError {
ENOENT(),
ENOSYS(),
}
pub trait PosixFileSystem {
fn open(&self, _path: &str, _perms: FilePerms) -> Result<Box<dyn PosixFile + Send>, FileError>;
fn unlink(&self, _path: &str) -> Result<(), FileError>;
}
pub trait PosixFile {
fn close(&mut self) -> Result<(), FileError>;
fn read(&mut self, len: u32) -> Result<Vec<u8>, FileError>;
fn write(&mut self, buf: &[u8]) -> Result<u64, FileError>;
fn lseek(&mut self, offset: isize, whence: SeekWhence) -> Result<usize, FileError>;
}
#[derive(Clone, Copy, Debug, Default)]
pub struct FilePerms {
pub write: bool,
pub creat: bool,
pub excl: bool,
pub trunc: bool,
pub append: bool,
pub directio: bool,
pub raw: u32,
pub mode: u32,
}
pub enum SeekWhence {
Set,
Cur,
End,
}