use std::any::Any;
use std::cmp::min;
use std::io::{IoSlice, IoSliceMut, SeekFrom};
use std::ops::{Deref, DerefMut};
use std::sync::Arc;
use wasi_common::file::{Advice, FdFlags, FileType, Filestat};
use wasi_common::{Error, ErrorExt, ErrorKind, SystemTimeSpec, WasiDir, WasiFile};
use wasmtime_vfs_ledger::InodeId;
use wasmtime_vfs_memory::{Data, Inode, Link, Node, Open, State};
pub struct File(Link<Vec<u8>>);
impl Deref for File {
type Target = Link<Vec<u8>>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for File {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
#[async_trait::async_trait]
impl Node for File {
fn to_any(self: Arc<Self>) -> Arc<dyn Any + Send + Sync> {
self
}
fn parent(&self) -> Option<Arc<dyn Node>> {
self.parent.upgrade()
}
fn filetype(&self) -> FileType {
FileType::RegularFile
}
fn id(&self) -> Arc<InodeId> {
self.inode.id.clone()
}
async fn open_dir(self: Arc<Self>) -> Result<Box<dyn WasiDir>, Error> {
Err(Error::not_dir())
}
async fn open_file(
self: Arc<Self>,
_path: &str,
dir: bool,
read: bool,
write: bool,
flags: FdFlags,
) -> Result<Box<dyn WasiFile>, Error> {
if dir {
return Err(Error::not_dir());
}
Ok(Box::new(OpenFile(Open {
root: self.root(),
link: self,
state: State::from(flags).into(),
write,
read,
})))
}
}
impl File {
#[allow(clippy::new_ret_no_self)]
pub fn new(parent: Arc<dyn Node>) -> Arc<dyn Node> {
Self::with_data(parent, [])
}
pub fn with_data(parent: Arc<dyn Node>, data: impl Into<Vec<u8>>) -> Arc<dyn Node> {
let id = parent.id().device().create_inode();
let inode = Inode {
data: Data::from(data.into()).into(),
id,
};
Arc::new(Self(Link {
parent: Arc::downgrade(&parent),
inode: inode.into(),
}))
}
}
struct OpenFile(Open<File>);
impl Deref for OpenFile {
type Target = Open<File>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[async_trait::async_trait]
impl WasiFile for OpenFile {
fn as_any(&self) -> &dyn Any {
self
}
async fn get_filetype(&mut self) -> Result<FileType, Error> {
Ok(FileType::RegularFile)
}
async fn datasync(&mut self) -> Result<(), Error> {
Ok(())
}
async fn sync(&mut self) -> Result<(), Error> {
Ok(())
}
async fn get_fdflags(&mut self) -> Result<FdFlags, Error> {
Ok(self.state.read().await.flags)
}
async fn set_fdflags(&mut self, flags: FdFlags) -> Result<(), Error> {
if !self.write {
return Err(Error::io()); }
self.state.write().await.flags = flags;
Ok(())
}
async fn get_filestat(&mut self) -> Result<Filestat, Error> {
let ilock = self.link.inode.data.read().await;
Ok(Filestat {
device_id: **self.link.inode.id.device(),
inode: **self.link.inode.id,
filetype: FileType::RegularFile,
nlink: Arc::strong_count(&self.link.inode) as u64,
size: ilock.content.len() as u64,
atim: Some(ilock.access),
mtim: Some(ilock.modify),
ctim: Some(ilock.create),
})
}
async fn set_filestat_size(&mut self, size: u64) -> Result<(), Error> {
let size: usize = size.try_into().map_err(|_| Error::invalid_argument())?;
if !self.write {
return Err(Error::io()); }
self.link.inode.data.write().await.content.resize(size, 0);
Ok(())
}
async fn advise(&mut self, _offset: u64, _len: u64, _advice: Advice) -> Result<(), Error> {
Ok(())
}
async fn allocate(&mut self, offset: u64, len: u64) -> Result<(), Error> {
if !self.write {
return Err(Error::io()); }
let offset: usize = offset.try_into().map_err(|_| Error::invalid_argument())?;
let len: usize = len.try_into().map_err(|_| Error::invalid_argument())?;
offset
.checked_add(len)
.ok_or_else(Error::invalid_argument)?;
Ok(())
}
async fn set_times(
&mut self,
atime: Option<SystemTimeSpec>,
mtime: Option<SystemTimeSpec>,
) -> Result<(), Error> {
if !self.write {
return Err(Error::io()); }
self.link.inode.data.write().await.set_times(atime, mtime)
}
async fn read_vectored<'a>(&mut self, bufs: &mut [IoSliceMut<'a>]) -> Result<u64, Error> {
if !self.read {
return Err(Error::io()); }
let mut total = 0;
let mut olock = self.state.write().await;
let ilock = self.link.inode.data.read().await;
for buf in bufs {
let len = min(buf.len(), ilock.content.len() - olock.pos);
buf[..len].copy_from_slice(&ilock.content[olock.pos..][..len]);
total += len as u64;
olock.pos += len;
}
Ok(total)
}
async fn read_vectored_at<'a>(
&mut self,
bufs: &mut [IoSliceMut<'a>],
offset: u64,
) -> Result<u64, Error> {
if !self.read {
return Err(Error::io()); }
let mut position: usize = offset.try_into().map_err(|_| Error::invalid_argument())?;
let mut total = 0;
let data = &self.link.inode.data.read().await.content[..];
for buf in bufs {
let len = min(buf.len(), data.len() - position);
buf[..len].copy_from_slice(&data[position..][..len]);
total += len as u64;
position += len;
}
Ok(total)
}
async fn write_vectored<'a>(&mut self, bufs: &[IoSlice<'a>]) -> Result<u64, Error> {
if !self.write {
return Err(Error::io()); }
let mut total = 0;
let mut olock = self.state.write().await;
let mut ilock = self.link.inode.data.write().await;
for buf in bufs {
let pos = match olock.flags.contains(FdFlags::APPEND) {
true => ilock.content.len(),
false => olock.pos,
};
if pos + buf.len() > ilock.content.len() {
ilock.content.resize(pos + buf.len(), 0);
}
ilock.content[pos..][..buf.len()].copy_from_slice(buf);
total += buf.len() as u64;
if !olock.flags.contains(FdFlags::APPEND) {
olock.pos += buf.len();
}
}
Ok(total)
}
async fn write_vectored_at<'a>(
&mut self,
bufs: &[IoSlice<'a>],
offset: u64,
) -> Result<u64, Error> {
if !self.write {
return Err(Error::io()); }
let mut pos: usize = offset.try_into().map_err(|_| Error::invalid_argument())?;
let mut total = 0;
let mut ilock = self.link.inode.data.write().await;
for buf in bufs {
if pos + buf.len() > ilock.content.len() {
ilock.content.resize(pos + buf.len(), 0);
}
ilock.content[pos..][..buf.len()].copy_from_slice(buf);
total += buf.len() as u64;
pos += buf.len();
}
Ok(total)
}
async fn seek(&mut self, pos: SeekFrom) -> Result<u64, Error> {
let mut olock = self.state.write().await;
let ilock = self.link.inode.data.read().await;
let cur = match pos {
SeekFrom::Current(_) => i64::try_from(olock.pos),
SeekFrom::Start(_) => Ok(0),
SeekFrom::End(_) => i64::try_from(ilock.content.len()),
};
let off = match pos {
SeekFrom::Current(off) => Ok(off),
SeekFrom::Start(off) => i64::try_from(off),
SeekFrom::End(off) => Ok(off),
};
let pos = cur.map_err(|_| ErrorKind::Inval)? + off.map_err(|_| ErrorKind::Inval)?;
let pos = usize::try_from(pos).map_err(|_| ErrorKind::Inval)?;
olock.pos = pos;
Ok(pos as u64)
}
async fn peek(&mut self, buf: &mut [u8]) -> Result<u64, Error> {
if !self.read {
return Err(Error::io()); }
let mut total = 0;
let olock = self.state.read().await;
let ilock = self.link.inode.data.read().await;
let len = min(buf.len(), ilock.content.len() - olock.pos);
buf[..len].copy_from_slice(&ilock.content[olock.pos..][..len]);
total += len as u64;
Ok(total)
}
async fn num_ready_bytes(&self) -> Result<u64, Error> {
if !self.read {
return Err(Error::io()); }
let olock = self.state.read().await;
let ilock = self.link.inode.data.read().await;
let len = min(ilock.content.len(), olock.pos);
let len = ilock.content.len() - len;
Ok(len as u64)
}
async fn readable(&self) -> Result<(), Error> {
Ok(())
}
async fn writable(&self) -> Result<(), Error> {
Ok(())
}
}