use alloc::format;
use alloc::string::ToString;
use alloc::sync::Arc;
use alloc::vec::Vec;
use core::future::Future;
use crate::linux::io_uring::{
AT_FDCWD, AT_REMOVEDIR, AT_STATX_SYNC_AS_STAT, IoUring, S_IRUSR, S_IWUSR, S_IXUSR,
STATX_BASIC_STATS, Statx,
};
use crate::linux::sys::Errno;
use crate::{fs, linux};
pub mod file;
pub struct FileSystem;
impl fs::FileSystem<linux::runtime::Runtime, linux::runtime::Share> for FileSystem {
type File = file::File;
fn read_dir(
_path: &str,
) -> impl Future<Output = fs::Result<fs::ReadDir<linux::runtime::Runtime, linux::runtime::Share>>>
{
async move {
Err(fs::FileError::Io(
"Directory listing not yet implemented".into(),
))
}
}
fn create_dir(path: &str) -> impl Future<Output = fs::Result<()>> {
async move {
let ring = Arc::new(
IoUring::with_capacity(256)
.map_err(|e| fs::FileError::Io(format!("Failed to create io_uring: {}", e)))?,
);
let path_bytes = path.as_bytes();
let mut path_with_null = Vec::with_capacity(path_bytes.len() + 1);
path_with_null.extend_from_slice(path_bytes);
path_with_null.push(0);
ring.mkdirat(AT_FDCWD, &path_with_null, S_IRUSR | S_IWUSR | S_IXUSR)
.await
.await
.map_err(|e| match e {
crate::linux::io_uring::IoUringError::System(errno) => {
let Some(x) = Errno::from_raw(errno) else {
return fs::FileError::Io(format!("Failed to parse directory errno"));
};
match x {
Errno::Exist => fs::FileError::AlreadyExists {
path: path.to_string(),
},
Errno::Acces => fs::FileError::PermissionDenied {
path: path.to_string(),
},
Errno::NoSpc => fs::FileError::DiskFull,
Errno::NotDir => fs::FileError::NotADirectory {
path: path.to_string(),
},
Errno::NoEnt => fs::FileError::NotFound {
path: path.to_string(),
},
_ => fs::FileError::Io(format!(
"Failed to create directory: errno {}",
errno
)),
}
}
_ => fs::FileError::Io(format!("Failed to create directory: {}", e)),
})?;
Ok(())
}
}
fn remove_file(path: &str) -> impl Future<Output = fs::Result<()>> {
async move {
let ring = Arc::new(
IoUring::with_capacity(256)
.map_err(|e| fs::FileError::Io(format!("Failed to create io_uring: {}", e)))?,
);
let path_bytes = path.as_bytes();
let mut path_with_null = Vec::with_capacity(path_bytes.len() + 1);
path_with_null.extend_from_slice(path_bytes);
path_with_null.push(0);
ring.unlinkat(AT_FDCWD, &path_with_null, 0)
.await
.await
.map_err(|e| match e {
crate::linux::io_uring::IoUringError::System(errno) => {
let Some(x) = Errno::from_raw(errno) else {
return fs::FileError::Io(format!("Failed to parse remove file errno"));
};
match x {
Errno::NoEnt => fs::FileError::NotFound {
path: path.to_string(),
},
Errno::Acces => fs::FileError::PermissionDenied {
path: path.to_string(),
},
Errno::IsDir => fs::FileError::IsADirectory {
path: path.to_string(),
},
_ => {
fs::FileError::Io(format!("Failed to remove file: errno {}", errno))
}
}
}
_ => fs::FileError::Io(format!("Failed to remove file: {}", e)),
})?;
Ok(())
}
}
fn remove_dir(path: &str) -> impl Future<Output = fs::Result<()>> {
async move {
let ring = Arc::new(
IoUring::with_capacity(256)
.map_err(|e| fs::FileError::Io(format!("Failed to create io_uring: {}", e)))?,
);
let path_bytes = path.as_bytes();
let mut path_with_null = Vec::with_capacity(path_bytes.len() + 1);
path_with_null.extend_from_slice(path_bytes);
path_with_null.push(0);
ring.unlinkat(AT_FDCWD, &path_with_null, AT_REMOVEDIR)
.await
.await
.map_err(|e| match e {
crate::linux::io_uring::IoUringError::System(errno) => {
let Some(x) = Errno::from_raw(errno) else {
return fs::FileError::Io(format!(
"Failed to parse remove directory errno"
));
};
match x {
Errno::NoEnt => fs::FileError::NotFound {
path: path.to_string(),
},
Errno::Acces => fs::FileError::PermissionDenied {
path: path.to_string(),
},
Errno::NotDir => fs::FileError::NotADirectory {
path: path.to_string(),
},
Errno::NotEmpty => fs::FileError::Io("Directory not empty".into()),
_ => fs::FileError::Io(format!(
"Failed to remove directory: errno {}",
errno
)),
}
}
_ => fs::FileError::Io(format!("Failed to remove directory: {}", e)),
})?;
Ok(())
}
}
fn rename(from: &str, to: &str) -> impl Future<Output = fs::Result<()>> {
async move {
let ring = Arc::new(
IoUring::with_capacity(256)
.map_err(|e| fs::FileError::Io(format!("Failed to create io_uring: {}", e)))?,
);
let from_bytes = from.as_bytes();
let mut from_with_null = Vec::with_capacity(from_bytes.len() + 1);
from_with_null.extend_from_slice(from_bytes);
from_with_null.push(0);
let to_bytes = to.as_bytes();
let mut to_with_null = Vec::with_capacity(to_bytes.len() + 1);
to_with_null.extend_from_slice(to_bytes);
to_with_null.push(0);
ring.renameat(AT_FDCWD, &from_with_null, AT_FDCWD, &to_with_null)
.await
.await
.map_err(|e| match e {
crate::linux::io_uring::IoUringError::System(errno) => {
let Some(x) = Errno::from_raw(errno) else {
return fs::FileError::Io(format!("Failed to parse rename errno"));
};
match x {
Errno::NoEnt => fs::FileError::NotFound {
path: from.to_string(),
},
Errno::Acces => fs::FileError::PermissionDenied {
path: from.to_string(),
},
Errno::Exist => fs::FileError::AlreadyExists {
path: to.to_string(),
},
Errno::NoSpc => fs::FileError::DiskFull,
_ => fs::FileError::Io(format!("Failed to rename: errno {}", errno)),
}
}
_ => fs::FileError::Io(format!("Failed to rename: {}", e)),
})?;
Ok(())
}
}
fn metadata(path: &str) -> impl Future<Output = fs::Result<fs::file::Metadata>> {
async move {
let ring = Arc::new(
IoUring::with_capacity(256)
.map_err(|e| fs::FileError::Io(format!("Failed to create io_uring: {}", e)))?,
);
let mut statx_buf = unsafe { core::mem::zeroed::<Statx>() };
let path_bytes = path.as_bytes();
let mut path_with_null = Vec::with_capacity(path_bytes.len() + 1);
path_with_null.extend_from_slice(path_bytes);
path_with_null.push(0);
ring.statx(
AT_FDCWD,
&path_with_null,
AT_STATX_SYNC_AS_STAT,
STATX_BASIC_STATS,
&mut statx_buf,
)
.await
.await
.map_err(|e| match e {
crate::linux::io_uring::IoUringError::System(errno) => {
let Some(x) = Errno::from_raw(errno) else {
return fs::FileError::Io(format!("Failed to parse metadata errno"));
};
match x {
Errno::NoEnt => fs::FileError::NotFound {
path: path.to_string(),
},
Errno::Acces => fs::FileError::PermissionDenied {
path: path.to_string(),
},
_ => fs::FileError::Io(format!("Failed to get metadata: errno {}", errno)),
}
}
_ => fs::FileError::Io(format!("Failed to get metadata: {}", e)),
})?;
Ok(fs::file::Metadata {
len: statx_buf.stx_size,
is_file: (statx_buf.stx_mode & 0o170000) == 0o100000, is_dir: (statx_buf.stx_mode & 0o170000) == 0o040000, created: if statx_buf.stx_mask & crate::linux::io_uring::STATX_CTIME != 0 {
Some(statx_buf.stx_ctime.tv_sec as u64)
} else {
None
},
modified: if statx_buf.stx_mask & crate::linux::io_uring::STATX_MTIME != 0 {
Some(statx_buf.stx_mtime.tv_sec as u64)
} else {
None
},
accessed: if statx_buf.stx_mask & crate::linux::io_uring::STATX_ATIME != 0 {
Some(statx_buf.stx_atime.tv_sec as u64)
} else {
None
},
})
}
}
}