use bitmask_enum::bitmask;
use futures::{future::poll_fn, AsyncRead, AsyncSeek, AsyncWrite, Stream};
use std::{
io::{Result, SeekFrom},
ops::Deref,
path::{Path, PathBuf},
sync::{Arc, OnceLock},
task::{Context, Poll},
};
pub use std::fs::{FileType, Metadata, Permissions};
#[bitmask(u8)]
pub enum FileOpenMode {
Append,
Writable,
Readable,
Create,
CreateNew,
Truncate,
}
#[cfg(windows)]
pub mod windows {
use std::io::ErrorKind;
use super::*;
pub struct NamedPipeListener(Box<dyn syscall::windows::DriverNamedPipeListener>);
impl Deref for NamedPipeListener {
type Target = dyn syscall::windows::DriverNamedPipeListener;
fn deref(&self) -> &Self::Target {
&*self.0
}
}
impl<F: syscall::windows::DriverNamedPipeListener + 'static> From<F> for NamedPipeListener {
fn from(value: F) -> Self {
Self(Box::new(value))
}
}
impl NamedPipeListener {
pub fn as_raw_ptr(&self) -> &dyn syscall::windows::DriverNamedPipeListener {
&*self.0
}
pub async fn accept(&self) -> Result<NamedPipeStream> {
poll_fn(|cx| self.as_raw_ptr().poll_next(cx)).await
}
}
impl Stream for NamedPipeListener {
type Item = Result<NamedPipeStream>;
fn poll_next(
self: std::pin::Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Option<Self::Item>> {
match self.as_raw_ptr().poll_next(cx) {
Poll::Ready(Ok(stream)) => Poll::Ready(Some(Ok(stream))),
Poll::Ready(Err(err)) => {
if err.kind() == ErrorKind::BrokenPipe {
Poll::Ready(None)
} else {
Poll::Ready(Some(Err(err)))
}
}
Poll::Pending => Poll::Pending,
}
}
}
pub struct NamedPipeStream(Arc<Box<dyn syscall::windows::DriverNamedPipeStream>>);
impl Deref for NamedPipeStream {
type Target = dyn syscall::windows::DriverNamedPipeStream;
fn deref(&self) -> &Self::Target {
&**self.0
}
}
impl<F: syscall::windows::DriverNamedPipeStream + 'static> From<F> for NamedPipeStream {
fn from(value: F) -> Self {
Self(Arc::new(Box::new(value)))
}
}
impl NamedPipeStream {
pub fn as_raw_ptr(&self) -> &dyn syscall::windows::DriverNamedPipeStream {
&**self.0
}
}
impl AsyncRead for NamedPipeStream {
fn poll_read(
self: std::pin::Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut [u8],
) -> Poll<Result<usize>> {
self.as_raw_ptr().poll_read(cx, buf)
}
}
impl AsyncWrite for NamedPipeStream {
fn poll_write(
self: std::pin::Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<Result<usize>> {
self.as_raw_ptr().poll_write(cx, buf)
}
fn poll_flush(self: std::pin::Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Result<()>> {
Poll::Ready(Ok(()))
}
fn poll_close(self: std::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<()>> {
self.as_raw_ptr().poll_close(cx)
}
}
}
pub mod syscall {
use super::*;
#[cfg(windows)]
pub mod windows {
use super::*;
pub trait DriverNamedPipeListener: Sync + Send {
fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<()>>;
fn poll_next(
&self,
cx: &mut Context<'_>,
) -> Poll<Result<crate::fs::windows::NamedPipeStream>>;
}
pub trait DriverNamedPipeStream: Sync + Send {
fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<()>>;
fn poll_write(&self, cx: &mut Context<'_>, buf: &[u8]) -> Poll<Result<usize>>;
fn poll_read(&self, cx: &mut Context<'_>, buf: &mut [u8]) -> Poll<Result<usize>>;
fn poll_close(&self, cx: &mut Context<'_>) -> Poll<Result<()>>;
}
}
pub trait Driver: Sync + Send {
fn open_file(&self, path: &Path, mode: FileOpenMode) -> Result<File>;
fn canonicalize(&self, path: &Path) -> Result<PathBuf>;
fn poll_copy(&self, cx: &mut Context<'_>, from: &Path, to: &Path) -> Poll<Result<u64>>;
fn poll_create_dir(&self, cx: &mut Context<'_>, path: &Path) -> Poll<Result<()>>;
fn poll_create_dir_all(&self, cx: &mut Context<'_>, path: &Path) -> Poll<Result<()>>;
fn poll_hard_link(&self, cx: &mut Context<'_>, from: &Path, to: &Path) -> Poll<Result<()>>;
fn poll_metadata(&self, cx: &mut Context<'_>, path: &Path) -> Poll<Result<Metadata>>;
fn poll_read_link(&self, cx: &mut Context<'_>, path: &Path) -> Poll<Result<PathBuf>>;
fn poll_remove_dir(&self, cx: &mut Context<'_>, path: &Path) -> Poll<Result<()>>;
fn poll_remove_dir_all(&self, cx: &mut Context<'_>, path: &Path) -> Poll<Result<()>>;
fn poll_remove_file(&self, cx: &mut Context<'_>, path: &Path) -> Poll<Result<()>>;
fn poll_rename(&self, cx: &mut Context<'_>, from: &Path, to: &Path) -> Poll<Result<()>>;
fn poll_set_permissions(
&self,
cx: &mut Context<'_>,
path: &Path,
perm: &Permissions,
) -> Poll<Result<()>>;
fn poll_symlink_metadata(
&self,
cx: &mut Context<'_>,
path: &Path,
) -> Poll<Result<Metadata>>;
fn read_dir(&self, path: &Path) -> Result<ReadDir>;
#[cfg(any(windows))]
fn named_pipe_client_open(
&self,
addr: &std::ffi::OsStr,
) -> Result<crate::fs::windows::NamedPipeStream>;
#[cfg(any(windows))]
fn named_pipe_server_create(
&self,
addr: &std::ffi::OsStr,
) -> Result<crate::fs::windows::NamedPipeListener>;
}
pub trait DriverDirEntry: Sync + Send {
fn name(&self) -> String;
fn path(&self) -> PathBuf;
fn meta(&self) -> Result<Metadata>;
fn file_type(&self) -> Result<FileType>;
}
pub trait DriverFile: Sync + Send {
fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<()>>;
fn poll_write(&self, cx: &mut Context<'_>, buf: &[u8]) -> Poll<Result<usize>>;
fn poll_read(&self, cx: &mut Context<'_>, buf: &mut [u8]) -> Poll<Result<usize>>;
fn poll_flush(&self, cx: &mut Context<'_>) -> Poll<Result<()>>;
fn poll_seek(&self, cx: &mut Context<'_>, pos: SeekFrom) -> Poll<Result<u64>>;
fn poll_meta(&self, cx: &mut Context<'_>) -> Poll<Result<Metadata>>;
fn poll_set_permissions(
&self,
cx: &mut Context<'_>,
perm: &Permissions,
) -> Poll<Result<()>>;
fn poll_set_len(&self, cx: &mut Context<'_>, size: u64) -> Poll<Result<()>>;
}
pub trait DriverReadDir: Sync + Send {
fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<()>>;
fn poll_next(&self, cx: &mut Context<'_>) -> Poll<Option<Result<DirEntry>>>;
}
}
pub struct DirEntry(Box<dyn syscall::DriverDirEntry>);
impl Deref for DirEntry {
type Target = dyn syscall::DriverDirEntry;
fn deref(&self) -> &Self::Target {
&*self.0
}
}
impl<F: syscall::DriverDirEntry + 'static> From<F> for DirEntry {
fn from(value: F) -> Self {
Self(Box::new(value))
}
}
impl DirEntry {
pub fn as_raw_ptr(&self) -> &dyn syscall::DriverDirEntry {
&*self.0
}
}
pub struct ReadDir(Box<dyn syscall::DriverReadDir>);
impl Deref for ReadDir {
type Target = dyn syscall::DriverReadDir;
fn deref(&self) -> &Self::Target {
&*self.0
}
}
impl<F: syscall::DriverReadDir + 'static> From<F> for ReadDir {
fn from(value: F) -> Self {
Self(Box::new(value))
}
}
impl ReadDir {
pub fn as_raw_ptr(&self) -> &dyn syscall::DriverReadDir {
&*self.0
}
pub async fn new<P: AsRef<Path>>(path: P) -> Result<Self> {
Self::new_with(path, get_fs_driver()).await
}
pub async fn new_with<P: AsRef<Path>>(path: P, driver: &dyn syscall::Driver) -> Result<Self> {
let readdir = driver.read_dir(path.as_ref())?;
poll_fn(|cx| readdir.as_raw_ptr().poll_ready(cx))
.await
.map(|_| readdir)
}
}
impl Stream for ReadDir {
type Item = Result<DirEntry>;
fn poll_next(self: std::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
self.as_raw_ptr().poll_next(cx)
}
}
pub struct File(Arc<Box<dyn syscall::DriverFile>>);
impl Deref for File {
type Target = dyn syscall::DriverFile;
fn deref(&self) -> &Self::Target {
&**self.0
}
}
impl<F: syscall::DriverFile + 'static> From<F> for File {
fn from(value: F) -> Self {
Self(Arc::new(Box::new(value)))
}
}
impl File {
pub fn as_raw_ptr(&self) -> &dyn syscall::DriverFile {
&**self.0
}
pub async fn open<P: AsRef<Path>>(path: P, mode: FileOpenMode) -> Result<Self> {
Self::open_with(path, mode, get_fs_driver()).await
}
pub async fn open_with<P: AsRef<Path>>(
path: P,
mode: FileOpenMode,
driver: &dyn syscall::Driver,
) -> Result<Self> {
let file = driver.open_file(path.as_ref(), mode)?;
poll_fn(|cx| file.as_raw_ptr().poll_ready(cx)).await?;
Ok(file)
}
pub async fn meta(&self) -> Result<Metadata> {
poll_fn(|cx| self.as_raw_ptr().poll_meta(cx)).await
}
pub async fn set_permissions(&self, perm: &Permissions) -> Result<()> {
poll_fn(|cx| self.as_raw_ptr().poll_set_permissions(cx, perm)).await
}
pub async fn set_len(&self, len: u64) -> Result<()> {
poll_fn(|cx| self.as_raw_ptr().poll_set_len(cx, len)).await
}
}
impl AsyncRead for File {
fn poll_read(
self: std::pin::Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut [u8],
) -> Poll<Result<usize>> {
self.as_raw_ptr().poll_read(cx, buf)
}
}
impl AsyncWrite for File {
fn poll_write(
self: std::pin::Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<Result<usize>> {
self.as_raw_ptr().poll_write(cx, buf)
}
fn poll_flush(self: std::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<()>> {
self.as_raw_ptr().poll_flush(cx)
}
fn poll_close(self: std::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<()>> {
self.poll_flush(cx)
}
}
impl AsyncSeek for File {
fn poll_seek(
self: std::pin::Pin<&mut Self>,
cx: &mut Context<'_>,
pos: SeekFrom,
) -> Poll<Result<u64>> {
self.as_raw_ptr().poll_seek(cx, pos)
}
}
pub async fn canonicalize<P: AsRef<Path>>(path: P) -> Result<PathBuf> {
get_fs_driver().canonicalize(path.as_ref())
}
pub async fn copy<F: AsRef<Path>, T: AsRef<Path>>(from: F, to: T) -> Result<u64> {
poll_fn(|cx| get_fs_driver().poll_copy(cx, from.as_ref(), to.as_ref())).await
}
pub async fn create_dir<P: AsRef<Path>>(path: P) -> Result<()> {
poll_fn(|cx| get_fs_driver().poll_create_dir(cx, path.as_ref())).await
}
pub async fn create_dir_all<P: AsRef<Path>>(path: P) -> Result<()> {
poll_fn(|cx| get_fs_driver().poll_create_dir_all(cx, path.as_ref())).await
}
pub async fn hard_link<F: AsRef<Path>, T: AsRef<Path>>(from: F, to: T) -> Result<()> {
poll_fn(|cx| get_fs_driver().poll_hard_link(cx, from.as_ref(), to.as_ref())).await
}
pub async fn metadata<P: AsRef<Path>>(path: P) -> Result<Metadata> {
poll_fn(|cx| get_fs_driver().poll_metadata(cx, path.as_ref())).await
}
pub async fn read_link<P: AsRef<Path>>(path: P) -> Result<PathBuf> {
poll_fn(|cx| get_fs_driver().poll_read_link(cx, path.as_ref())).await
}
pub async fn remove_dir<P: AsRef<Path>>(path: P) -> Result<()> {
poll_fn(|cx| get_fs_driver().poll_remove_dir(cx, path.as_ref())).await
}
pub async fn remove_dir_all<P: AsRef<Path>>(path: P) -> Result<()> {
poll_fn(|cx| get_fs_driver().poll_remove_dir_all(cx, path.as_ref())).await
}
pub async fn remove_file<P: AsRef<Path>>(path: P) -> Result<()> {
poll_fn(|cx| get_fs_driver().poll_remove_file(cx, path.as_ref())).await
}
pub async fn rename<F: AsRef<Path>, T: AsRef<Path>>(from: F, to: T) -> Result<()> {
poll_fn(|cx| get_fs_driver().poll_rename(cx, from.as_ref(), to.as_ref())).await
}
pub async fn set_permissions<P: AsRef<Path>>(path: P, perm: &Permissions) -> Result<()> {
poll_fn(|cx| get_fs_driver().poll_set_permissions(cx, path.as_ref(), perm)).await
}
pub async fn symlink_metadata<P: AsRef<Path>>(path: P) -> Result<Metadata> {
poll_fn(|cx| get_fs_driver().poll_symlink_metadata(cx, path.as_ref())).await
}
pub async fn is_dir<P: AsRef<Path>>(path: P) -> bool {
metadata(path).await.map(|m| m.is_dir()).unwrap_or(false)
}
pub struct FileSystem<'a> {
driver: &'a dyn syscall::Driver,
}
impl<'a> From<&'a dyn syscall::Driver> for FileSystem<'a> {
fn from(value: &'a dyn syscall::Driver) -> Self {
Self { driver: value }
}
}
impl<'a> FileSystem<'a> {
pub async fn is_dir<P: AsRef<Path>>(&self, path: P) -> bool {
self.metadata(path)
.await
.map(|m| m.is_dir())
.unwrap_or(false)
}
pub async fn is_file<P: AsRef<Path>>(&self, path: P) -> bool {
self.metadata(path)
.await
.map(|m| m.is_file())
.unwrap_or(false)
}
pub async fn open_file<P: AsRef<Path>>(&self, path: P, mode: FileOpenMode) -> Result<File> {
File::open_with(path, mode, self.driver).await
}
pub async fn canonicalize<P: AsRef<Path>>(&self, path: P) -> Result<PathBuf> {
self.driver.canonicalize(path.as_ref())
}
pub async fn copy<F: AsRef<Path>, T: AsRef<Path>>(&self, from: F, to: T) -> Result<u64> {
poll_fn(|cx| self.driver.poll_copy(cx, from.as_ref(), to.as_ref())).await
}
pub async fn create_dir<P: AsRef<Path>>(&self, path: P) -> Result<()> {
poll_fn(|cx| self.driver.poll_create_dir(cx, path.as_ref())).await
}
pub async fn create_dir_all<P: AsRef<Path>>(&self, path: P) -> Result<()> {
poll_fn(|cx| self.driver.poll_create_dir_all(cx, path.as_ref())).await
}
pub async fn hard_link<F: AsRef<Path>, T: AsRef<Path>>(&self, from: F, to: T) -> Result<()> {
poll_fn(|cx| self.driver.poll_hard_link(cx, from.as_ref(), to.as_ref())).await
}
pub async fn metadata<P: AsRef<Path>>(&self, path: P) -> Result<Metadata> {
poll_fn(|cx| self.driver.poll_metadata(cx, path.as_ref())).await
}
pub async fn read_link<P: AsRef<Path>>(&self, path: P) -> Result<PathBuf> {
poll_fn(|cx| self.driver.poll_read_link(cx, path.as_ref())).await
}
pub async fn remove_dir<P: AsRef<Path>>(&self, path: P) -> Result<()> {
poll_fn(|cx| self.driver.poll_remove_dir(cx, path.as_ref())).await
}
pub async fn remove_dir_all<P: AsRef<Path>>(&self, path: P) -> Result<()> {
poll_fn(|cx| self.driver.poll_remove_dir_all(cx, path.as_ref())).await
}
pub async fn remove_file<P: AsRef<Path>>(&self, path: P) -> Result<()> {
poll_fn(|cx| self.driver.poll_remove_file(cx, path.as_ref())).await
}
pub async fn rename<F: AsRef<Path>, T: AsRef<Path>>(&self, from: F, to: T) -> Result<()> {
poll_fn(|cx| self.driver.poll_rename(cx, from.as_ref(), to.as_ref())).await
}
pub async fn set_permissions<P: AsRef<Path>>(&self, path: P, perm: &Permissions) -> Result<()> {
poll_fn(|cx| self.driver.poll_set_permissions(cx, path.as_ref(), perm)).await
}
pub async fn symlink_metadata<P: AsRef<Path>>(&self, path: P) -> Result<Metadata> {
poll_fn(|cx| self.driver.poll_symlink_metadata(cx, path.as_ref())).await
}
}
static FIFLE_SYSTEM_DRIVER: OnceLock<Box<dyn syscall::Driver>> = OnceLock::new();
pub fn get_fs_driver() -> &'static dyn syscall::Driver {
FIFLE_SYSTEM_DRIVER
.get()
.expect("Call register_network_driver first.")
.as_ref()
}
pub fn register_fs_driver<E: syscall::Driver + 'static>(driver: E) {
if FIFLE_SYSTEM_DRIVER.set(Box::new(driver)).is_err() {
panic!("Multiple calls to register_network_driver are not permitted!!!");
}
}