use crate::dev::DevError;
use alloc::{boxed::Box, string::String, sync::Arc, vec::Vec};
use core::any::Any;
use core::fmt;
use core::future::Future;
use core::pin::Pin;
use core::result;
use core::str;
pub trait INode: Any + Sync + Send {
fn read_at(&self, offset: usize, buf: &mut [u8]) -> Result<usize>;
fn write_at(&self, offset: usize, buf: &[u8]) -> Result<usize>;
fn poll(&self) -> Result<PollStatus>;
fn async_poll<'a>(
&'a self,
) -> Pin<Box<dyn Future<Output = Result<PollStatus>> + Send + Sync + 'a>> {
let f = async move || self.poll();
Box::pin(f())
}
fn metadata(&self) -> Result<Metadata> {
Err(FsError::NotSupported)
}
fn set_metadata(&self, _metadata: &Metadata) -> Result<()> {
Err(FsError::NotSupported)
}
fn sync_all(&self) -> Result<()> {
Err(FsError::NotSupported)
}
fn sync_data(&self) -> Result<()> {
Err(FsError::NotSupported)
}
fn resize(&self, _len: usize) -> Result<()> {
Err(FsError::NotSupported)
}
fn create(&self, name: &str, type_: FileType, mode: u32) -> Result<Arc<dyn INode>> {
self.create2(name, type_, mode, 0)
}
fn create2(
&self,
name: &str,
type_: FileType,
mode: u32,
_data: usize,
) -> Result<Arc<dyn INode>> {
self.create(name, type_, mode)
}
fn link(&self, _name: &str, _other: &Arc<dyn INode>) -> Result<()> {
Err(FsError::NotSupported)
}
fn unlink(&self, _name: &str) -> Result<()> {
Err(FsError::NotSupported)
}
fn move_(&self, _old_name: &str, _target: &Arc<dyn INode>, _new_name: &str) -> Result<()> {
Err(FsError::NotSupported)
}
fn find(&self, _name: &str) -> Result<Arc<dyn INode>> {
Err(FsError::NotSupported)
}
fn get_entry(&self, _id: usize) -> Result<String> {
Err(FsError::NotSupported)
}
fn get_entry_with_metadata(&self, id: usize) -> Result<(Metadata, String)> {
let name = self.get_entry(id)?;
let entry = self.find(&name)?;
Ok((entry.metadata()?, name))
}
fn io_control(&self, _cmd: u32, _data: usize) -> Result<usize> {
Err(FsError::NotSupported)
}
fn mmap(&self, _area: MMapArea) -> Result<()> {
Err(FsError::NotSupported)
}
fn fs(&self) -> Arc<dyn FileSystem> {
unimplemented!();
}
fn as_any_ref(&self) -> &dyn Any;
}
impl dyn INode {
pub fn downcast_ref<T: INode>(&self) -> Option<&T> {
self.as_any_ref().downcast_ref::<T>()
}
pub fn list(&self) -> Result<Vec<String>> {
let info = self.metadata()?;
if info.type_ != FileType::Dir {
return Err(FsError::NotDir);
}
Ok((0..)
.map(|i| self.get_entry(i))
.take_while(|result| result.is_ok())
.filter_map(|result| result.ok())
.collect())
}
pub fn lookup(&self, path: &str) -> Result<Arc<dyn INode>> {
self.lookup_follow(path, 0)
}
pub fn lookup_follow(&self, path: &str, mut follow_times: usize) -> Result<Arc<dyn INode>> {
if self.metadata()?.type_ != FileType::Dir {
return Err(FsError::NotDir);
}
let mut result = self.find(".")?;
let mut rest_path = String::from(path);
while rest_path != "" {
if result.metadata()?.type_ != FileType::Dir {
return Err(FsError::NotDir);
}
if let Some('/') = rest_path.chars().next() {
result = self.fs().root_inode();
rest_path = String::from(&rest_path[1..]);
continue;
}
let name;
match rest_path.find('/') {
None => {
name = rest_path;
rest_path = String::new();
}
Some(pos) => {
name = String::from(&rest_path[0..pos]);
rest_path = String::from(&rest_path[pos + 1..]);
}
};
if name == "" {
continue;
}
let inode = result.find(&name)?;
if inode.metadata()?.type_ == FileType::SymLink && follow_times > 0 {
follow_times -= 1;
let mut content = [0u8; 256];
let len = inode.read_at(0, &mut content)?;
let path = str::from_utf8(&content[..len]).map_err(|_| FsError::NotDir)?;
rest_path = {
let mut new_path = String::from(path);
if let Some('/') = new_path.chars().last() {
new_path += &rest_path;
} else {
new_path += "/";
new_path += &rest_path;
}
new_path
};
} else {
result = inode
}
}
Ok(result)
}
}
pub enum IOCTLError {
NotValidFD = 9,
NotValidMemory = 14,
NotValidParam = 22,
NotCharDevice = 25,
}
#[derive(Debug, Default)]
pub struct PollStatus {
pub read: bool,
pub write: bool,
pub error: bool,
}
#[derive(Debug)]
pub struct MMapArea {
pub start_vaddr: usize,
pub end_vaddr: usize,
pub prot: usize,
pub flags: usize,
pub offset: usize,
}
#[derive(Debug, Eq, PartialEq, Clone)]
pub struct Metadata {
pub dev: usize,
pub inode: usize,
pub size: usize,
pub blk_size: usize,
pub blocks: usize,
pub atime: Timespec,
pub mtime: Timespec,
pub ctime: Timespec,
pub type_: FileType,
pub mode: u16,
pub nlinks: usize,
pub uid: usize,
pub gid: usize,
pub rdev: usize,
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
pub struct Timespec {
pub sec: i64,
pub nsec: i32,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum FileType {
File,
Dir,
SymLink,
CharDevice,
BlockDevice,
NamedPipe,
Socket,
}
#[derive(Debug)]
pub struct FsInfo {
pub bsize: usize,
pub frsize: usize,
pub blocks: usize,
pub bfree: usize,
pub bavail: usize,
pub files: usize,
pub ffree: usize,
pub namemax: usize,
}
#[derive(Debug, Eq, PartialEq)]
pub enum FsError {
NotSupported,
NotFile,
IsDir,
NotDir,
EntryNotFound,
EntryExist,
NotSameFs,
InvalidParam,
NoDeviceSpace,
DirRemoved,
DirNotEmpty,
WrongFs,
DeviceError,
IOCTLError,
NoDevice,
Again,
SymLoop,
Busy,
Interrupted,
}
impl fmt::Display for FsError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self)
}
}
impl From<DevError> for FsError {
fn from(_: DevError) -> Self {
FsError::DeviceError
}
}
pub type Result<T> = result::Result<T, FsError>;
pub trait FileSystem: Sync + Send {
fn sync(&self) -> Result<()>;
fn root_inode(&self) -> Arc<dyn INode>;
fn info(&self) -> FsInfo;
}
pub fn make_rdev(major: usize, minor: usize) -> usize {
((major & 0xfff) << 8) | (minor & 0xff)
}