use crate::preview2::filesystem::TableFsExt;
use crate::preview2::preview2::filesystem::TableReaddirExt;
use crate::preview2::wasi::cli_base::{preopens, stderr, stdin, stdout};
use crate::preview2::wasi::clocks::monotonic_clock;
use crate::preview2::wasi::clocks::wall_clock;
use crate::preview2::wasi::filesystem::filesystem;
use crate::preview2::wasi::io::streams;
use crate::preview2::{wasi, TableError, WasiView};
use anyhow::{anyhow, bail, Context};
use std::borrow::Borrow;
use std::cell::Cell;
use std::collections::BTreeMap;
use std::mem::{size_of, size_of_val};
use std::ops::{Deref, DerefMut};
use std::slice;
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
use wiggle::tracing::instrument;
use wiggle::{GuestPtr, GuestSliceMut, GuestStrCow, GuestType};
#[derive(Clone, Debug)]
struct File {
fd: filesystem::Descriptor,
position: Arc<AtomicU64>,
append: bool,
blocking: bool,
}
#[derive(Clone, Debug)]
enum Descriptor {
Stdin(preopens::InputStream),
Stdout(preopens::OutputStream),
Stderr(preopens::OutputStream),
PreopenDirectory((filesystem::Descriptor, String)),
File(File),
}
#[derive(Debug, Default)]
pub struct WasiPreview1Adapter {
descriptors: Option<Descriptors>,
}
#[derive(Debug, Default)]
struct Descriptors {
used: BTreeMap<u32, Descriptor>,
free: Vec<u32>,
}
impl Deref for Descriptors {
type Target = BTreeMap<u32, Descriptor>;
fn deref(&self) -> &Self::Target {
&self.used
}
}
impl DerefMut for Descriptors {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.used
}
}
impl Descriptors {
async fn new(
preopens: &mut (impl preopens::Host + stdin::Host + stdout::Host + stderr::Host + ?Sized),
) -> Result<Self, types::Error> {
let stdin = preopens
.get_stdin()
.await
.context("failed to call `get-stdin`")
.map_err(types::Error::trap)?;
let stdout = preopens
.get_stdout()
.await
.context("failed to call `get-stdout`")
.map_err(types::Error::trap)?;
let stderr = preopens
.get_stderr()
.await
.context("failed to call `get-stderr`")
.map_err(types::Error::trap)?;
let directories = preopens
.get_directories()
.await
.context("failed to call `get-directories`")
.map_err(types::Error::trap)?;
let mut descriptors = Self::default();
descriptors.push(Descriptor::Stdin(stdin))?;
descriptors.push(Descriptor::Stdout(stdout))?;
descriptors.push(Descriptor::Stderr(stderr))?;
for dir in directories {
descriptors.push(Descriptor::PreopenDirectory(dir))?;
}
Ok(descriptors)
}
fn unused(&self) -> ErrnoResult<u32> {
match self.last_key_value() {
Some((fd, _)) => {
if let Some(fd) = fd.checked_add(1) {
return Ok(fd);
}
if self.len() == u32::MAX as usize {
return Err(types::Errno::Loop);
}
Ok((0..u32::MAX)
.rev()
.find(|fd| !self.contains_key(fd))
.expect("failed to find an unused file descriptor"))
}
None => Ok(0),
}
}
fn remove(&mut self, fd: types::Fd) -> Option<Descriptor> {
let fd = fd.into();
let desc = self.used.remove(&fd)?;
self.free.push(fd);
Some(desc)
}
fn push(&mut self, desc: Descriptor) -> ErrnoResult<u32> {
let fd = if let Some(fd) = self.free.pop() {
fd
} else {
self.unused()?
};
assert!(self.insert(fd, desc).is_none());
Ok(fd)
}
fn push_file(&mut self, file: File) -> ErrnoResult<u32> {
self.push(Descriptor::File(file))
}
}
impl WasiPreview1Adapter {
pub fn new() -> Self {
Self::default()
}
}
pub trait WasiPreview1View: Send + Sync + WasiView {
fn adapter(&self) -> &WasiPreview1Adapter;
fn adapter_mut(&mut self) -> &mut WasiPreview1Adapter;
}
struct Transaction<'a, T: WasiPreview1View + ?Sized> {
view: &'a mut T,
descriptors: Cell<Descriptors>,
}
impl<T: WasiPreview1View + ?Sized> Drop for Transaction<'_, T> {
fn drop(&mut self) {
let descriptors = self.descriptors.take();
self.view.adapter_mut().descriptors = Some(descriptors);
}
}
impl<T: WasiPreview1View + ?Sized> Transaction<'_, T> {
fn get_descriptor(&mut self, fd: types::Fd) -> ErrnoResult<&Descriptor> {
let fd = fd.into();
self.descriptors
.get_mut()
.get(&fd)
.ok_or(types::Errno::Badf)
}
fn get_file(&mut self, fd: types::Fd) -> ErrnoResult<&File> {
let fd = fd.into();
match self.descriptors.get_mut().get(&fd) {
Some(Descriptor::File(file @ File { fd, .. })) if self.view.table().is_file(*fd) => {
Ok(file)
}
_ => Err(types::Errno::Badf),
}
}
fn get_file_mut(&mut self, fd: types::Fd) -> ErrnoResult<&mut File> {
let fd = fd.into();
match self.descriptors.get_mut().get_mut(&fd) {
Some(Descriptor::File(file)) if self.view.table().is_file(file.fd) => Ok(file),
_ => Err(types::Errno::Badf),
}
}
fn get_seekable(&mut self, fd: types::Fd) -> ErrnoResult<&File> {
let fd = fd.into();
match self.descriptors.get_mut().get(&fd) {
Some(Descriptor::File(file @ File { fd, .. })) if self.view.table().is_file(*fd) => {
Ok(file)
}
Some(Descriptor::Stdin(..) | Descriptor::Stdout(..) | Descriptor::Stderr(..)) => {
Err(types::Errno::Spipe)
}
_ => Err(types::Errno::Badf),
}
}
fn get_fd(&mut self, fd: types::Fd) -> ErrnoResult<filesystem::Descriptor> {
match self.get_descriptor(fd)? {
Descriptor::File(File { fd, .. }) => Ok(*fd),
Descriptor::PreopenDirectory((fd, _)) => Ok(*fd),
Descriptor::Stdin(stream) => Ok(*stream),
Descriptor::Stdout(stream) | Descriptor::Stderr(stream) => Ok(*stream),
}
}
fn get_file_fd(&mut self, fd: types::Fd) -> ErrnoResult<filesystem::Descriptor> {
self.get_file(fd).map(|File { fd, .. }| *fd)
}
fn get_dir_fd(&mut self, fd: types::Fd) -> ErrnoResult<filesystem::Descriptor> {
let fd = fd.into();
match self.descriptors.get_mut().get(&fd) {
Some(Descriptor::File(File { fd, .. })) if self.view.table().is_dir(*fd) => Ok(*fd),
Some(Descriptor::PreopenDirectory((fd, _))) => Ok(*fd),
_ => Err(types::Errno::Badf),
}
}
}
#[wiggle::async_trait]
trait WasiPreview1ViewExt:
WasiPreview1View + preopens::Host + stdin::Host + stdout::Host + stderr::Host
{
async fn transact(&mut self) -> Result<Transaction<'_, Self>, types::Error> {
let descriptors = if let Some(descriptors) = self.adapter_mut().descriptors.take() {
descriptors
} else {
Descriptors::new(self).await?
}
.into();
Ok(Transaction {
view: self,
descriptors,
})
}
async fn get_fd(&mut self, fd: types::Fd) -> Result<filesystem::Descriptor, types::Error> {
let mut st = self.transact().await?;
let fd = st.get_fd(fd)?;
Ok(fd)
}
async fn get_file_fd(&mut self, fd: types::Fd) -> Result<filesystem::Descriptor, types::Error> {
let mut st = self.transact().await?;
let fd = st.get_file_fd(fd)?;
Ok(fd)
}
async fn get_dir_fd(&mut self, fd: types::Fd) -> Result<filesystem::Descriptor, types::Error> {
let mut st = self.transact().await?;
let fd = st.get_dir_fd(fd)?;
Ok(fd)
}
}
impl<T: WasiPreview1View + preopens::Host> WasiPreview1ViewExt for T {}
pub fn add_to_linker<
T: WasiPreview1View
+ wasi::cli_base::environment::Host
+ wasi::cli_base::exit::Host
+ wasi::cli_base::preopens::Host
+ wasi::filesystem::filesystem::Host
+ wasi::poll::poll::Host
+ wasi::random::random::Host
+ wasi::io::streams::Host
+ wasi::clocks::monotonic_clock::Host
+ wasi::clocks::wall_clock::Host,
>(
linker: &mut wasmtime::Linker<T>,
) -> anyhow::Result<()> {
wasi_snapshot_preview1::add_to_linker(linker, |t| t)
}
wiggle::from_witx!({
witx: ["$CARGO_MANIFEST_DIR/witx/wasi_snapshot_preview1.witx"],
errors: { errno => trappable Error },
async: *,
});
impl wiggle::GuestErrorType for types::Errno {
fn success() -> Self {
Self::Success
}
}
fn systimespec(
set: bool,
ts: types::Timestamp,
now: bool,
) -> ErrnoResult<filesystem::NewTimestamp> {
if set && now {
Err(types::Errno::Inval)
} else if set {
Ok(filesystem::NewTimestamp::Timestamp(filesystem::Datetime {
seconds: ts / 1_000_000_000,
nanoseconds: (ts % 1_000_000_000) as _,
}))
} else if now {
Ok(filesystem::NewTimestamp::Now)
} else {
Ok(filesystem::NewTimestamp::NoChange)
}
}
impl TryFrom<wall_clock::Datetime> for types::Timestamp {
type Error = types::Errno;
fn try_from(
wall_clock::Datetime {
seconds,
nanoseconds,
}: wall_clock::Datetime,
) -> Result<Self, Self::Error> {
types::Timestamp::from(seconds)
.checked_mul(1_000_000_000)
.and_then(|ns| ns.checked_add(nanoseconds.into()))
.ok_or(types::Errno::Overflow)
}
}
impl From<types::Lookupflags> for filesystem::PathFlags {
fn from(flags: types::Lookupflags) -> Self {
if flags.contains(types::Lookupflags::SYMLINK_FOLLOW) {
filesystem::PathFlags::SYMLINK_FOLLOW
} else {
filesystem::PathFlags::empty()
}
}
}
impl From<types::Oflags> for filesystem::OpenFlags {
fn from(flags: types::Oflags) -> Self {
let mut out = filesystem::OpenFlags::empty();
if flags.contains(types::Oflags::CREAT) {
out |= filesystem::OpenFlags::CREATE;
}
if flags.contains(types::Oflags::DIRECTORY) {
out |= filesystem::OpenFlags::DIRECTORY;
}
if flags.contains(types::Oflags::EXCL) {
out |= filesystem::OpenFlags::EXCLUSIVE;
}
if flags.contains(types::Oflags::TRUNC) {
out |= filesystem::OpenFlags::TRUNCATE;
}
out
}
}
impl From<types::Advice> for filesystem::Advice {
fn from(advice: types::Advice) -> Self {
match advice {
types::Advice::Normal => filesystem::Advice::Normal,
types::Advice::Sequential => filesystem::Advice::Sequential,
types::Advice::Random => filesystem::Advice::Random,
types::Advice::Willneed => filesystem::Advice::WillNeed,
types::Advice::Dontneed => filesystem::Advice::DontNeed,
types::Advice::Noreuse => filesystem::Advice::NoReuse,
}
}
}
impl TryFrom<filesystem::DescriptorType> for types::Filetype {
type Error = anyhow::Error;
fn try_from(ty: filesystem::DescriptorType) -> Result<Self, Self::Error> {
match ty {
filesystem::DescriptorType::RegularFile => Ok(types::Filetype::RegularFile),
filesystem::DescriptorType::Directory => Ok(types::Filetype::Directory),
filesystem::DescriptorType::BlockDevice => Ok(types::Filetype::BlockDevice),
filesystem::DescriptorType::CharacterDevice => Ok(types::Filetype::CharacterDevice),
filesystem::DescriptorType::Fifo => Ok(types::Filetype::Unknown),
filesystem::DescriptorType::Socket => {
bail!("sockets are not currently supported")
}
filesystem::DescriptorType::SymbolicLink => Ok(types::Filetype::SymbolicLink),
filesystem::DescriptorType::Unknown => Ok(types::Filetype::Unknown),
}
}
}
impl From<filesystem::ErrorCode> for types::Errno {
fn from(code: filesystem::ErrorCode) -> Self {
match code {
filesystem::ErrorCode::Access => types::Errno::Acces,
filesystem::ErrorCode::WouldBlock => types::Errno::Again,
filesystem::ErrorCode::Already => types::Errno::Already,
filesystem::ErrorCode::BadDescriptor => types::Errno::Badf,
filesystem::ErrorCode::Busy => types::Errno::Busy,
filesystem::ErrorCode::Deadlock => types::Errno::Deadlk,
filesystem::ErrorCode::Quota => types::Errno::Dquot,
filesystem::ErrorCode::Exist => types::Errno::Exist,
filesystem::ErrorCode::FileTooLarge => types::Errno::Fbig,
filesystem::ErrorCode::IllegalByteSequence => types::Errno::Ilseq,
filesystem::ErrorCode::InProgress => types::Errno::Inprogress,
filesystem::ErrorCode::Interrupted => types::Errno::Intr,
filesystem::ErrorCode::Invalid => types::Errno::Inval,
filesystem::ErrorCode::Io => types::Errno::Io,
filesystem::ErrorCode::IsDirectory => types::Errno::Isdir,
filesystem::ErrorCode::Loop => types::Errno::Loop,
filesystem::ErrorCode::TooManyLinks => types::Errno::Mlink,
filesystem::ErrorCode::MessageSize => types::Errno::Msgsize,
filesystem::ErrorCode::NameTooLong => types::Errno::Nametoolong,
filesystem::ErrorCode::NoDevice => types::Errno::Nodev,
filesystem::ErrorCode::NoEntry => types::Errno::Noent,
filesystem::ErrorCode::NoLock => types::Errno::Nolck,
filesystem::ErrorCode::InsufficientMemory => types::Errno::Nomem,
filesystem::ErrorCode::InsufficientSpace => types::Errno::Nospc,
filesystem::ErrorCode::Unsupported => types::Errno::Notsup,
filesystem::ErrorCode::NotDirectory => types::Errno::Notdir,
filesystem::ErrorCode::NotEmpty => types::Errno::Notempty,
filesystem::ErrorCode::NotRecoverable => types::Errno::Notrecoverable,
filesystem::ErrorCode::NoTty => types::Errno::Notty,
filesystem::ErrorCode::NoSuchDevice => types::Errno::Nxio,
filesystem::ErrorCode::Overflow => types::Errno::Overflow,
filesystem::ErrorCode::NotPermitted => types::Errno::Perm,
filesystem::ErrorCode::Pipe => types::Errno::Pipe,
filesystem::ErrorCode::ReadOnly => types::Errno::Rofs,
filesystem::ErrorCode::InvalidSeek => types::Errno::Spipe,
filesystem::ErrorCode::TextFileBusy => types::Errno::Txtbsy,
filesystem::ErrorCode::CrossDevice => types::Errno::Xdev,
}
}
}
impl From<filesystem::ErrorCode> for types::Error {
fn from(code: filesystem::ErrorCode) -> Self {
types::Errno::from(code).into()
}
}
impl TryFrom<filesystem::Error> for types::Errno {
type Error = anyhow::Error;
fn try_from(err: filesystem::Error) -> Result<Self, Self::Error> {
match err.downcast() {
Ok(code) => Ok(code.into()),
Err(e) => Err(e),
}
}
}
impl TryFrom<filesystem::Error> for types::Error {
type Error = anyhow::Error;
fn try_from(err: filesystem::Error) -> Result<Self, Self::Error> {
match err.downcast() {
Ok(code) => Ok(code.into()),
Err(e) => Err(e),
}
}
}
impl From<TableError> for types::Errno {
fn from(err: TableError) -> Self {
match err {
TableError::Full => types::Errno::Nomem,
TableError::NotPresent | TableError::WrongType => types::Errno::Badf,
}
}
}
impl From<TableError> for types::Error {
fn from(err: TableError) -> Self {
types::Errno::from(err).into()
}
}
type ErrnoResult<T> = Result<T, types::Errno>;
fn write_bytes<'a>(
ptr: impl Borrow<GuestPtr<'a, u8>>,
buf: impl AsRef<[u8]>,
) -> ErrnoResult<GuestPtr<'a, u8>> {
let buf = buf.as_ref();
let len = buf.len().try_into().or(Err(types::Errno::Inval))?;
let ptr = ptr.borrow();
ptr.as_array(len)
.copy_from_slice(buf)
.or(Err(types::Errno::Inval))?;
ptr.add(len).or(Err(types::Errno::Inval))
}
fn write_byte<'a>(ptr: impl Borrow<GuestPtr<'a, u8>>, byte: u8) -> ErrnoResult<GuestPtr<'a, u8>> {
let ptr = ptr.borrow();
ptr.write(byte).or(Err(types::Errno::Inval))?;
ptr.add(1).or(Err(types::Errno::Inval))
}
fn read_str<'a>(ptr: impl Borrow<GuestPtr<'a, str>>) -> ErrnoResult<GuestStrCow<'a>> {
ptr.borrow().as_cow().or(Err(types::Errno::Inval))
}
fn read_string<'a>(ptr: impl Borrow<GuestPtr<'a, str>>) -> ErrnoResult<String> {
read_str(ptr).map(|s| s.to_string())
}
fn first_non_empty_ciovec(ciovs: &types::CiovecArray<'_>) -> ErrnoResult<Option<Vec<u8>>> {
ciovs
.iter()
.map(|iov| {
let iov = iov
.or(Err(types::Errno::Inval))?
.read()
.or(Err(types::Errno::Inval))?;
if iov.buf_len == 0 {
return Ok(None);
}
iov.buf
.as_array(iov.buf_len)
.to_vec()
.or(Err(types::Errno::Inval))
.map(Some)
})
.find_map(Result::transpose)
.transpose()
}
fn first_non_empty_iovec<'a>(
iovs: &types::IovecArray<'a>,
) -> ErrnoResult<Option<GuestSliceMut<'a, u8>>> {
iovs.iter()
.map(|iov| {
let iov = iov
.or(Err(types::Errno::Inval))?
.read()
.or(Err(types::Errno::Inval))?;
if iov.buf_len == 0 {
return Ok(None);
}
iov.buf
.as_array(iov.buf_len)
.as_slice_mut()
.map_err(|_| types::Errno::Inval)
})
.find_map(Result::transpose)
.transpose()
}
#[wiggle::async_trait]
impl<
T: WasiPreview1View
+ wasi::cli_base::environment::Host
+ wasi::cli_base::exit::Host
+ wasi::cli_base::preopens::Host
+ wasi::filesystem::filesystem::Host
+ wasi::poll::poll::Host
+ wasi::random::random::Host
+ wasi::io::streams::Host
+ wasi::clocks::monotonic_clock::Host
+ wasi::clocks::wall_clock::Host,
> wasi_snapshot_preview1::WasiSnapshotPreview1 for T
{
#[instrument(skip(self))]
async fn args_get<'b>(
&mut self,
argv: &GuestPtr<'b, GuestPtr<'b, u8>>,
argv_buf: &GuestPtr<'b, u8>,
) -> Result<(), types::Error> {
self.get_arguments()
.await
.context("failed to call `get-arguments`")
.map_err(types::Error::trap)?
.into_iter()
.try_fold(
(*argv, *argv_buf),
|(argv, argv_buf), arg| -> ErrnoResult<_> {
argv.write(argv_buf).map_err(|_| types::Errno::Inval)?;
let argv = argv.add(1).map_err(|_| types::Errno::Inval)?;
let argv_buf = write_bytes(argv_buf, arg)?;
let argv_buf = write_byte(argv_buf, 0)?;
Ok((argv, argv_buf))
},
)?;
Ok(())
}
#[instrument(skip(self))]
async fn args_sizes_get(&mut self) -> Result<(types::Size, types::Size), types::Error> {
let args = self
.get_arguments()
.await
.context("failed to call `get-arguments`")
.map_err(types::Error::trap)?;
let num = args.len().try_into().map_err(|_| types::Errno::Overflow)?;
let len = args
.iter()
.map(|buf| buf.len() + 1) .sum::<usize>()
.try_into()
.map_err(|_| types::Errno::Overflow)?;
Ok((num, len))
}
#[instrument(skip(self))]
async fn environ_get<'b>(
&mut self,
environ: &GuestPtr<'b, GuestPtr<'b, u8>>,
environ_buf: &GuestPtr<'b, u8>,
) -> Result<(), types::Error> {
self.get_environment()
.await
.context("failed to call `get-environment`")
.map_err(types::Error::trap)?
.into_iter()
.try_fold(
(*environ, *environ_buf),
|(environ, environ_buf), (k, v)| -> ErrnoResult<_> {
environ
.write(environ_buf)
.map_err(|_| types::Errno::Inval)?;
let environ = environ.add(1).map_err(|_| types::Errno::Inval)?;
let environ_buf = write_bytes(environ_buf, k)?;
let environ_buf = write_byte(environ_buf, b'=')?;
let environ_buf = write_bytes(environ_buf, v)?;
let environ_buf = write_byte(environ_buf, 0)?;
Ok((environ, environ_buf))
},
)?;
Ok(())
}
#[instrument(skip(self))]
async fn environ_sizes_get(&mut self) -> Result<(types::Size, types::Size), types::Error> {
let environ = self
.get_environment()
.await
.context("failed to call `get-environment`")
.map_err(types::Error::trap)?;
let num = environ
.len()
.try_into()
.map_err(|_| types::Errno::Overflow)?;
let len = environ
.iter()
.map(|(k, v)| k.len() + 1 + v.len() + 1) .sum::<usize>()
.try_into()
.map_err(|_| types::Errno::Overflow)?;
Ok((num, len))
}
#[instrument(skip(self))]
async fn clock_res_get(
&mut self,
id: types::Clockid,
) -> Result<types::Timestamp, types::Error> {
let res = match id {
types::Clockid::Realtime => wall_clock::Host::resolution(self)
.await
.context("failed to call `wall_clock::resolution`")
.map_err(types::Error::trap)?
.try_into()?,
types::Clockid::Monotonic => monotonic_clock::Host::resolution(self)
.await
.context("failed to call `monotonic_clock::resolution`")
.map_err(types::Error::trap)?,
types::Clockid::ProcessCputimeId | types::Clockid::ThreadCputimeId => {
return Err(types::Errno::Badf.into())
}
};
Ok(res)
}
#[instrument(skip(self))]
async fn clock_time_get(
&mut self,
id: types::Clockid,
_precision: types::Timestamp,
) -> Result<types::Timestamp, types::Error> {
let now = match id {
types::Clockid::Realtime => wall_clock::Host::now(self)
.await
.context("failed to call `wall_clock::now`")
.map_err(types::Error::trap)?
.try_into()?,
types::Clockid::Monotonic => monotonic_clock::Host::now(self)
.await
.context("failed to call `monotonic_clock::now`")
.map_err(types::Error::trap)?,
types::Clockid::ProcessCputimeId | types::Clockid::ThreadCputimeId => {
return Err(types::Errno::Badf.into())
}
};
Ok(now)
}
#[instrument(skip(self))]
async fn fd_advise(
&mut self,
fd: types::Fd,
offset: types::Filesize,
len: types::Filesize,
advice: types::Advice,
) -> Result<(), types::Error> {
let fd = self.get_file_fd(fd).await?;
self.advise(fd, offset, len, advice.into())
.await
.map_err(|e| {
e.try_into()
.context("failed to call `advise`")
.unwrap_or_else(types::Error::trap)
})
}
#[instrument(skip(self))]
async fn fd_allocate(
&mut self,
fd: types::Fd,
_offset: types::Filesize,
_len: types::Filesize,
) -> Result<(), types::Error> {
self.get_file_fd(fd).await?;
Err(types::Errno::Notsup.into())
}
#[instrument(skip(self))]
async fn fd_close(&mut self, fd: types::Fd) -> Result<(), types::Error> {
let desc = self
.transact()
.await?
.descriptors
.get_mut()
.remove(fd)
.ok_or(types::Errno::Badf)?
.clone();
match desc {
Descriptor::Stdin(stream) => self
.drop_input_stream(stream)
.await
.context("failed to call `drop-input-stream`"),
Descriptor::Stdout(stream) | Descriptor::Stderr(stream) => self
.drop_output_stream(stream)
.await
.context("failed to call `drop-output-stream`"),
Descriptor::File(File { fd, .. }) | Descriptor::PreopenDirectory((fd, _)) => self
.drop_descriptor(fd)
.await
.context("failed to call `drop-descriptor`"),
}
.map_err(types::Error::trap)
}
#[instrument(skip(self))]
async fn fd_datasync(&mut self, fd: types::Fd) -> Result<(), types::Error> {
let fd = self.get_file_fd(fd).await?;
self.sync_data(fd).await.map_err(|e| {
e.try_into()
.context("failed to call `sync-data`")
.unwrap_or_else(types::Error::trap)
})
}
#[instrument(skip(self))]
async fn fd_fdstat_get(&mut self, fd: types::Fd) -> Result<types::Fdstat, types::Error> {
let (fd, blocking, append) = match self.transact().await?.get_descriptor(fd)? {
Descriptor::Stdin(..) => {
let fs_rights_base = types::Rights::FD_READ;
return Ok(types::Fdstat {
fs_filetype: types::Filetype::CharacterDevice,
fs_flags: types::Fdflags::empty(),
fs_rights_base,
fs_rights_inheriting: fs_rights_base,
});
}
Descriptor::Stdout(..) | Descriptor::Stderr(..) => {
let fs_rights_base = types::Rights::FD_WRITE;
return Ok(types::Fdstat {
fs_filetype: types::Filetype::CharacterDevice,
fs_flags: types::Fdflags::empty(),
fs_rights_base,
fs_rights_inheriting: fs_rights_base,
});
}
Descriptor::PreopenDirectory((fd, _)) => (*fd, false, false),
Descriptor::File(File {
fd,
blocking,
append,
..
}) => (*fd, *blocking, *append),
};
let flags = self.get_flags(fd).await.map_err(|e| {
e.try_into()
.context("failed to call `get-flags`")
.unwrap_or_else(types::Error::trap)
})?;
let fs_filetype = self
.get_type(fd)
.await
.map_err(|e| {
e.try_into()
.context("failed to call `get-type`")
.unwrap_or_else(types::Error::trap)
})?
.try_into()
.map_err(types::Error::trap)?;
let mut fs_flags = types::Fdflags::empty();
let mut fs_rights_base = types::Rights::all();
if !flags.contains(filesystem::DescriptorFlags::READ) {
fs_rights_base &= !types::Rights::FD_READ;
}
if !flags.contains(filesystem::DescriptorFlags::WRITE) {
fs_rights_base &= !types::Rights::FD_WRITE;
}
if flags.contains(filesystem::DescriptorFlags::DATA_INTEGRITY_SYNC) {
fs_flags |= types::Fdflags::DSYNC;
}
if flags.contains(filesystem::DescriptorFlags::REQUESTED_WRITE_SYNC) {
fs_flags |= types::Fdflags::RSYNC;
}
if flags.contains(filesystem::DescriptorFlags::FILE_INTEGRITY_SYNC) {
fs_flags |= types::Fdflags::SYNC;
}
if append {
fs_flags |= types::Fdflags::APPEND;
}
if !blocking {
fs_flags |= types::Fdflags::NONBLOCK;
}
Ok(types::Fdstat {
fs_filetype,
fs_flags,
fs_rights_base,
fs_rights_inheriting: fs_rights_base,
})
}
#[instrument(skip(self))]
async fn fd_fdstat_set_flags(
&mut self,
fd: types::Fd,
flags: types::Fdflags,
) -> Result<(), types::Error> {
let mut st = self.transact().await?;
let File {
append, blocking, ..
} = st.get_file_mut(fd)?;
if flags.contains(types::Fdflags::DSYNC)
|| flags.contains(types::Fdflags::SYNC)
|| flags.contains(types::Fdflags::RSYNC)
{
return Err(types::Errno::Inval.into());
}
*append = flags.contains(types::Fdflags::APPEND);
*blocking = !flags.contains(types::Fdflags::NONBLOCK);
Ok(())
}
#[instrument(skip(self))]
async fn fd_fdstat_set_rights(
&mut self,
fd: types::Fd,
_fs_rights_base: types::Rights,
_fs_rights_inheriting: types::Rights,
) -> Result<(), types::Error> {
self.get_fd(fd).await?;
Ok(())
}
#[instrument(skip(self))]
async fn fd_filestat_get(&mut self, fd: types::Fd) -> Result<types::Filestat, types::Error> {
let desc = self.transact().await?.get_descriptor(fd)?.clone();
match desc {
Descriptor::Stdin(..) | Descriptor::Stdout(..) | Descriptor::Stderr(..) => {
Ok(types::Filestat {
dev: 0,
ino: 0,
filetype: types::Filetype::CharacterDevice,
nlink: 0,
size: 0,
atim: 0,
mtim: 0,
ctim: 0,
})
}
Descriptor::PreopenDirectory((fd, _)) | Descriptor::File(File { fd, .. }) => {
let filesystem::DescriptorStat {
device: dev,
inode: ino,
type_,
link_count: nlink,
size,
data_access_timestamp,
data_modification_timestamp,
status_change_timestamp,
} = self.stat(fd).await.map_err(|e| {
e.try_into()
.context("failed to call `stat`")
.unwrap_or_else(types::Error::trap)
})?;
let filetype = type_.try_into().map_err(types::Error::trap)?;
let atim = data_access_timestamp.try_into()?;
let mtim = data_modification_timestamp.try_into()?;
let ctim = status_change_timestamp.try_into()?;
Ok(types::Filestat {
dev,
ino,
filetype,
nlink,
size,
atim,
mtim,
ctim,
})
}
}
}
#[instrument(skip(self))]
async fn fd_filestat_set_size(
&mut self,
fd: types::Fd,
size: types::Filesize,
) -> Result<(), types::Error> {
let fd = self.get_file_fd(fd).await?;
self.set_size(fd, size).await.map_err(|e| {
e.try_into()
.context("failed to call `set-size`")
.unwrap_or_else(types::Error::trap)
})
}
#[instrument(skip(self))]
async fn fd_filestat_set_times(
&mut self,
fd: types::Fd,
atim: types::Timestamp,
mtim: types::Timestamp,
fst_flags: types::Fstflags,
) -> Result<(), types::Error> {
let atim = systimespec(
fst_flags.contains(types::Fstflags::ATIM),
atim,
fst_flags.contains(types::Fstflags::ATIM_NOW),
)?;
let mtim = systimespec(
fst_flags.contains(types::Fstflags::MTIM),
mtim,
fst_flags.contains(types::Fstflags::MTIM_NOW),
)?;
let fd = self.get_fd(fd).await?;
self.set_times(fd, atim, mtim).await.map_err(|e| {
e.try_into()
.context("failed to call `set-times`")
.unwrap_or_else(types::Error::trap)
})
}
#[instrument(skip(self))]
async fn fd_read<'a>(
&mut self,
fd: types::Fd,
iovs: &types::IovecArray<'a>,
) -> Result<types::Size, types::Error> {
let desc = self.transact().await?.get_descriptor(fd)?.clone();
let (mut buf, read, end) = match desc {
Descriptor::File(File {
fd,
blocking,
position,
..
}) if self.table().is_file(fd) => {
let Some(buf) = first_non_empty_iovec(iovs)? else {
return Ok(0)
};
let pos = position.load(Ordering::Relaxed);
let stream = self.read_via_stream(fd, pos).await.map_err(|e| {
e.try_into()
.context("failed to call `read-via-stream`")
.unwrap_or_else(types::Error::trap)
})?;
let max = buf.len().try_into().unwrap_or(u64::MAX);
let (read, end) = if blocking {
self.blocking_read(stream, max)
} else {
streams::Host::read(self, stream, max)
}
.await
.map_err(|_| types::Errno::Io)?;
let n = read.len().try_into().or(Err(types::Errno::Overflow))?;
let pos = pos.checked_add(n).ok_or(types::Errno::Overflow)?;
position.store(pos, Ordering::Relaxed);
(buf, read, end)
}
Descriptor::Stdin(stream) => {
let Some(buf) = first_non_empty_iovec(iovs)? else {
return Ok(0)
};
let (read, end) =
streams::Host::read(self, stream, buf.len().try_into().unwrap_or(u64::MAX))
.await
.map_err(|_| types::Errno::Io)?;
(buf, read, end)
}
_ => return Err(types::Errno::Badf.into()),
};
if read.len() > buf.len() {
return Err(types::Errno::Range.into());
}
if !end && read.len() == 0 {
return Err(types::Errno::Intr.into());
}
let (buf, _) = buf.split_at_mut(read.len());
buf.copy_from_slice(&read);
let n = read.len().try_into().or(Err(types::Errno::Overflow))?;
Ok(n)
}
#[instrument(skip(self))]
async fn fd_pread<'a>(
&mut self,
fd: types::Fd,
iovs: &types::IovecArray<'a>,
offset: types::Filesize,
) -> Result<types::Size, types::Error> {
let desc = self.transact().await?.get_descriptor(fd)?.clone();
let (mut buf, read, end) = match desc {
Descriptor::File(File { fd, blocking, .. }) if self.table().is_file(fd) => {
let Some(buf) = first_non_empty_iovec(iovs)? else {
return Ok(0)
};
let stream = self.read_via_stream(fd, offset).await.map_err(|e| {
e.try_into()
.context("failed to call `read-via-stream`")
.unwrap_or_else(types::Error::trap)
})?;
let max = buf.len().try_into().unwrap_or(u64::MAX);
let (read, end) = if blocking {
self.blocking_read(stream, max)
} else {
streams::Host::read(self, stream, max)
}
.await
.map_err(|_| types::Errno::Io)?;
(buf, read, end)
}
Descriptor::Stdin(..) => {
return Err(types::Errno::Spipe.into());
}
_ => return Err(types::Errno::Badf.into()),
};
if read.len() > buf.len() {
return Err(types::Errno::Range.into());
}
if !end && read.len() == 0 {
return Err(types::Errno::Intr.into());
}
let (buf, _) = buf.split_at_mut(read.len());
buf.copy_from_slice(&read);
let n = read.len().try_into().or(Err(types::Errno::Overflow))?;
Ok(n)
}
#[instrument(skip(self))]
async fn fd_write<'a>(
&mut self,
fd: types::Fd,
ciovs: &types::CiovecArray<'a>,
) -> Result<types::Size, types::Error> {
let desc = self.transact().await?.get_descriptor(fd)?.clone();
let n = match desc {
Descriptor::File(File {
fd,
blocking,
append,
position,
}) if self.table().is_file(fd) => {
let Some(buf) = first_non_empty_ciovec(ciovs)? else {
return Ok(0)
};
let (stream, pos) = if append {
let stream = self.append_via_stream(fd).await.map_err(|e| {
e.try_into()
.context("failed to call `append-via-stream`")
.unwrap_or_else(types::Error::trap)
})?;
(stream, 0)
} else {
let position = position.load(Ordering::Relaxed);
let stream = self.write_via_stream(fd, position).await.map_err(|e| {
e.try_into()
.context("failed to call `write-via-stream`")
.unwrap_or_else(types::Error::trap)
})?;
(stream, position)
};
let n = if blocking {
self.blocking_write(stream, buf)
} else {
streams::Host::write(self, stream, buf)
}
.await
.map_err(|_| types::Errno::Io)?;
if !append {
let pos = pos.checked_add(n).ok_or(types::Errno::Overflow)?;
position.store(pos, Ordering::Relaxed);
}
n
}
Descriptor::Stdout(stream) | Descriptor::Stderr(stream) => {
let Some(buf) = first_non_empty_ciovec(ciovs)? else {
return Ok(0)
};
streams::Host::write(self, stream, buf)
.await
.map_err(|_| types::Errno::Io)?
}
_ => return Err(types::Errno::Badf.into()),
}
.try_into()
.or(Err(types::Errno::Overflow))?;
Ok(n)
}
#[instrument(skip(self))]
async fn fd_pwrite<'a>(
&mut self,
fd: types::Fd,
ciovs: &types::CiovecArray<'a>,
offset: types::Filesize,
) -> Result<types::Size, types::Error> {
let desc = self.transact().await?.get_descriptor(fd)?.clone();
let n = match desc {
Descriptor::File(File { fd, blocking, .. }) if self.table().is_file(fd) => {
let Some(buf) = first_non_empty_ciovec(ciovs)? else {
return Ok(0)
};
let stream = self.write_via_stream(fd, offset).await.map_err(|e| {
e.try_into()
.context("failed to call `write-via-stream`")
.unwrap_or_else(types::Error::trap)
})?;
if blocking {
self.blocking_write(stream, buf)
} else {
streams::Host::write(self, stream, buf)
}
.await
.map_err(|_| types::Errno::Io)?
}
Descriptor::Stdout(..) | Descriptor::Stderr(..) => {
return Err(types::Errno::Spipe.into());
}
_ => return Err(types::Errno::Badf.into()),
}
.try_into()
.or(Err(types::Errno::Overflow))?;
Ok(n)
}
#[instrument(skip(self))]
async fn fd_prestat_get(&mut self, fd: types::Fd) -> Result<types::Prestat, types::Error> {
if let Descriptor::PreopenDirectory((_, p)) = self.transact().await?.get_descriptor(fd)? {
let pr_name_len = p.len().try_into().or(Err(types::Errno::Overflow))?;
return Ok(types::Prestat::Dir(types::PrestatDir { pr_name_len }));
}
Err(types::Errno::Badf.into()) }
#[instrument(skip(self))]
async fn fd_prestat_dir_name<'a>(
&mut self,
fd: types::Fd,
path: &GuestPtr<'a, u8>,
path_max_len: types::Size,
) -> Result<(), types::Error> {
let path_max_len = path_max_len.try_into().or(Err(types::Errno::Overflow))?;
if let Descriptor::PreopenDirectory((_, p)) = self.transact().await?.get_descriptor(fd)? {
if p.len() > path_max_len {
return Err(types::Errno::Nametoolong.into());
}
write_bytes(path, p)?;
return Ok(());
}
Err(types::Errno::Notdir.into()) }
#[instrument(skip(self))]
async fn fd_renumber(&mut self, from: types::Fd, to: types::Fd) -> Result<(), types::Error> {
let mut st = self.transact().await?;
let descriptors = st.descriptors.get_mut();
let desc = descriptors.remove(from).ok_or(types::Errno::Badf)?;
descriptors.insert(to.into(), desc);
Ok(())
}
#[instrument(skip(self))]
async fn fd_seek(
&mut self,
fd: types::Fd,
offset: types::Filedelta,
whence: types::Whence,
) -> Result<types::Filesize, types::Error> {
let (fd, position) = {
let mut st = self.transact().await?;
let File { fd, position, .. } = st.get_seekable(fd)?;
(*fd, Arc::clone(&position))
};
let pos = match whence {
types::Whence::Set if offset >= 0 => offset as _,
types::Whence::Cur => position
.load(Ordering::Relaxed)
.checked_add_signed(offset)
.ok_or(types::Errno::Inval)?,
types::Whence::End => {
let filesystem::DescriptorStat { size, .. } = self.stat(fd).await.map_err(|e| {
e.try_into()
.context("failed to call `stat`")
.unwrap_or_else(types::Error::trap)
})?;
size.checked_add_signed(offset).ok_or(types::Errno::Inval)?
}
_ => return Err(types::Errno::Inval.into()),
};
position.store(pos, Ordering::Relaxed);
Ok(pos)
}
#[instrument(skip(self))]
async fn fd_sync(&mut self, fd: types::Fd) -> Result<(), types::Error> {
let fd = self.get_file_fd(fd).await?;
self.sync(fd).await.map_err(|e| {
e.try_into()
.context("failed to call `sync`")
.unwrap_or_else(types::Error::trap)
})
}
#[instrument(skip(self))]
async fn fd_tell(&mut self, fd: types::Fd) -> Result<types::Filesize, types::Error> {
let pos = self
.transact()
.await?
.get_seekable(fd)
.map(|File { position, .. }| position.load(Ordering::Relaxed))?;
Ok(pos)
}
#[instrument(skip(self))]
async fn fd_readdir<'a>(
&mut self,
fd: types::Fd,
buf: &GuestPtr<'a, u8>,
buf_len: types::Size,
cookie: types::Dircookie,
) -> Result<types::Size, types::Error> {
let fd = self.get_dir_fd(fd).await?;
let stream = self.read_directory(fd).await.map_err(|e| {
e.try_into()
.context("failed to call `read-directory`")
.unwrap_or_else(types::Error::trap)
})?;
let filesystem::DescriptorStat {
inode: fd_inode, ..
} = self.stat(fd).await.map_err(|e| {
e.try_into()
.context("failed to call `stat`")
.unwrap_or_else(types::Error::trap)
})?;
let cookie = cookie.try_into().map_err(|_| types::Errno::Overflow)?;
let head = [
(
types::Dirent {
d_next: 1u64.to_le(),
d_ino: fd_inode.to_le(),
d_type: types::Filetype::Directory,
d_namlen: 1u32.to_le(),
},
".".into(),
),
(
types::Dirent {
d_next: 2u64.to_le(),
d_ino: fd_inode.to_le(), d_type: types::Filetype::Directory,
d_namlen: 2u32.to_le(),
},
"..".into(),
),
]
.into_iter()
.map(Ok::<_, types::Error>);
let dir = self
.table_mut()
.delete_readdir(stream)?
.into_iter()
.zip(3u64..)
.map(|(entry, d_next)| {
let filesystem::DirectoryEntry { inode, type_, name } = entry.map_err(|e| {
e.try_into()
.context("failed to inspect `read-directory` entry")
.unwrap_or_else(types::Error::trap)
})?;
let d_type = type_.try_into().map_err(types::Error::trap)?;
let d_namlen: u32 = name.len().try_into().map_err(|_| types::Errno::Overflow)?;
Ok((
types::Dirent {
d_next: d_next.to_le(),
d_ino: inode.unwrap_or_default().to_le(),
d_type, d_namlen: d_namlen.to_le(),
},
name,
))
});
const DIRENT_SIZE: u32 = size_of::<types::Dirent>() as _;
assert_eq!(
types::Dirent::guest_size(),
DIRENT_SIZE,
"Dirent guest repr and host repr should match"
);
let mut buf = *buf;
let mut cap = buf_len;
for entry in head.chain(dir).skip(cookie) {
let (ref entry, mut path) = entry?;
assert_eq!(
1,
size_of_val(&entry.d_type),
"Dirent member d_type should be endian-invariant"
);
let entry_len = cap.min(DIRENT_SIZE);
let entry = entry as *const _ as _;
let entry = unsafe { slice::from_raw_parts(entry, entry_len as _) };
cap = cap.checked_sub(entry_len).unwrap();
buf = write_bytes(buf, entry)?;
if cap == 0 {
return Ok(buf_len);
}
if let Ok(cap) = cap.try_into() {
path.truncate(cap);
}
cap = cap.checked_sub(path.len() as _).unwrap();
buf = write_bytes(buf, path)?;
if cap == 0 {
return Ok(buf_len);
}
}
Ok(buf_len.checked_sub(cap).unwrap())
}
#[instrument(skip(self))]
async fn path_create_directory<'a>(
&mut self,
dirfd: types::Fd,
path: &GuestPtr<'a, str>,
) -> Result<(), types::Error> {
let dirfd = self.get_dir_fd(dirfd).await?;
let path = read_string(path)?;
self.create_directory_at(dirfd, path).await.map_err(|e| {
e.try_into()
.context("failed to call `create-directory-at`")
.unwrap_or_else(types::Error::trap)
})
}
#[instrument(skip(self))]
async fn path_filestat_get<'a>(
&mut self,
dirfd: types::Fd,
flags: types::Lookupflags,
path: &GuestPtr<'a, str>,
) -> Result<types::Filestat, types::Error> {
let dirfd = self.get_dir_fd(dirfd).await?;
let path = read_string(path)?;
let filesystem::DescriptorStat {
device: dev,
inode: ino,
type_,
link_count: nlink,
size,
data_access_timestamp,
data_modification_timestamp,
status_change_timestamp,
} = self.stat_at(dirfd, flags.into(), path).await.map_err(|e| {
e.try_into()
.context("failed to call `stat-at`")
.unwrap_or_else(types::Error::trap)
})?;
let filetype = type_.try_into().map_err(types::Error::trap)?;
let atim = data_access_timestamp.try_into()?;
let mtim = data_modification_timestamp.try_into()?;
let ctim = status_change_timestamp.try_into()?;
Ok(types::Filestat {
dev,
ino,
filetype,
nlink,
size,
atim,
mtim,
ctim,
})
}
#[instrument(skip(self))]
async fn path_filestat_set_times<'a>(
&mut self,
dirfd: types::Fd,
flags: types::Lookupflags,
path: &GuestPtr<'a, str>,
atim: types::Timestamp,
mtim: types::Timestamp,
fst_flags: types::Fstflags,
) -> Result<(), types::Error> {
let atim = systimespec(
fst_flags.contains(types::Fstflags::ATIM),
atim,
fst_flags.contains(types::Fstflags::ATIM_NOW),
)?;
let mtim = systimespec(
fst_flags.contains(types::Fstflags::MTIM),
mtim,
fst_flags.contains(types::Fstflags::MTIM_NOW),
)?;
let dirfd = self.get_dir_fd(dirfd).await?;
let path = read_string(path)?;
self.set_times_at(dirfd, flags.into(), path, atim, mtim)
.await
.map_err(|e| {
e.try_into()
.context("failed to call `set-times-at`")
.unwrap_or_else(types::Error::trap)
})
}
#[instrument(skip(self))]
async fn path_link<'a>(
&mut self,
src_fd: types::Fd,
src_flags: types::Lookupflags,
src_path: &GuestPtr<'a, str>,
target_fd: types::Fd,
target_path: &GuestPtr<'a, str>,
) -> Result<(), types::Error> {
let src_fd = self.get_dir_fd(src_fd).await?;
let target_fd = self.get_dir_fd(target_fd).await?;
let src_path = read_string(src_path)?;
let target_path = read_string(target_path)?;
self.link_at(src_fd, src_flags.into(), src_path, target_fd, target_path)
.await
.map_err(|e| {
e.try_into()
.context("failed to call `link-at`")
.unwrap_or_else(types::Error::trap)
})
}
#[instrument(skip(self))]
async fn path_open<'a>(
&mut self,
dirfd: types::Fd,
dirflags: types::Lookupflags,
path: &GuestPtr<'a, str>,
oflags: types::Oflags,
fs_rights_base: types::Rights,
_fs_rights_inheriting: types::Rights,
fdflags: types::Fdflags,
) -> Result<types::Fd, types::Error> {
let path = read_string(path)?;
let mut flags = filesystem::DescriptorFlags::empty();
if fs_rights_base.contains(types::Rights::FD_READ) {
flags |= filesystem::DescriptorFlags::READ;
}
if fs_rights_base.contains(types::Rights::FD_WRITE) {
flags |= filesystem::DescriptorFlags::WRITE;
}
if fdflags.contains(types::Fdflags::SYNC) {
flags |= filesystem::DescriptorFlags::FILE_INTEGRITY_SYNC;
}
if fdflags.contains(types::Fdflags::DSYNC) {
flags |= filesystem::DescriptorFlags::DATA_INTEGRITY_SYNC;
}
if fdflags.contains(types::Fdflags::RSYNC) {
flags |= filesystem::DescriptorFlags::REQUESTED_WRITE_SYNC;
}
let desc = self.transact().await?.get_descriptor(dirfd)?.clone();
let dirfd = match desc {
Descriptor::PreopenDirectory((fd, _)) => fd,
Descriptor::File(File { fd, .. }) if self.table().is_dir(fd) => fd,
Descriptor::File(File { fd, .. }) if !self.table().is_dir(fd) => {
return Err(types::Errno::Notdir.into());
}
_ => return Err(types::Errno::Badf.into()),
};
let fd = self
.open_at(
dirfd,
dirflags.into(),
path,
oflags.into(),
flags,
filesystem::Modes::READABLE | filesystem::Modes::WRITABLE,
)
.await
.map_err(|e| {
e.try_into()
.context("failed to call `open-at`")
.unwrap_or_else(types::Error::trap)
})?;
let fd = self
.transact()
.await?
.descriptors
.get_mut()
.push_file(File {
fd,
position: Default::default(),
append: fdflags.contains(types::Fdflags::APPEND),
blocking: !fdflags.contains(types::Fdflags::NONBLOCK),
})?;
Ok(fd.into())
}
#[instrument(skip(self))]
async fn path_readlink<'a>(
&mut self,
dirfd: types::Fd,
path: &GuestPtr<'a, str>,
buf: &GuestPtr<'a, u8>,
buf_len: types::Size,
) -> Result<types::Size, types::Error> {
let dirfd = self.get_dir_fd(dirfd).await?;
let path = read_string(path)?;
let mut path = self.readlink_at(dirfd, path).await.map_err(|e| {
e.try_into()
.context("failed to call `readlink-at`")
.unwrap_or_else(types::Error::trap)
})?;
if let Ok(buf_len) = buf_len.try_into() {
path.truncate(buf_len);
}
let n = path.len().try_into().map_err(|_| types::Errno::Overflow)?;
write_bytes(buf, &path)?;
Ok(n)
}
#[instrument(skip(self))]
async fn path_remove_directory<'a>(
&mut self,
dirfd: types::Fd,
path: &GuestPtr<'a, str>,
) -> Result<(), types::Error> {
let dirfd = self.get_dir_fd(dirfd).await?;
let path = read_string(path)?;
self.remove_directory_at(dirfd, path).await.map_err(|e| {
e.try_into()
.context("failed to call `remove-directory-at`")
.unwrap_or_else(types::Error::trap)
})
}
#[instrument(skip(self))]
async fn path_rename<'a>(
&mut self,
src_fd: types::Fd,
src_path: &GuestPtr<'a, str>,
dest_fd: types::Fd,
dest_path: &GuestPtr<'a, str>,
) -> Result<(), types::Error> {
let src_fd = self.get_dir_fd(src_fd).await?;
let dest_fd = self.get_dir_fd(dest_fd).await?;
let src_path = read_string(src_path)?;
let dest_path = read_string(dest_path)?;
self.rename_at(src_fd, src_path, dest_fd, dest_path)
.await
.map_err(|e| {
e.try_into()
.context("failed to call `rename-at`")
.unwrap_or_else(types::Error::trap)
})
}
#[instrument(skip(self))]
async fn path_symlink<'a>(
&mut self,
src_path: &GuestPtr<'a, str>,
dirfd: types::Fd,
dest_path: &GuestPtr<'a, str>,
) -> Result<(), types::Error> {
let dirfd = self.get_dir_fd(dirfd).await?;
let src_path = read_string(src_path)?;
let dest_path = read_string(dest_path)?;
self.symlink_at(dirfd, src_path, dest_path)
.await
.map_err(|e| {
e.try_into()
.context("failed to call `symlink-at`")
.unwrap_or_else(types::Error::trap)
})
}
#[instrument(skip(self))]
async fn path_unlink_file<'a>(
&mut self,
dirfd: types::Fd,
path: &GuestPtr<'a, str>,
) -> Result<(), types::Error> {
let dirfd = self.get_dir_fd(dirfd).await?;
let path = path.as_cow().map_err(|_| types::Errno::Inval)?.to_string();
self.unlink_file_at(dirfd, path).await.map_err(|e| {
e.try_into()
.context("failed to call `unlink-file-at`")
.unwrap_or_else(types::Error::trap)
})
}
#[allow(unused_variables)]
#[instrument(skip(self))]
async fn poll_oneoff<'a>(
&mut self,
subs: &GuestPtr<'a, types::Subscription>,
events: &GuestPtr<'a, types::Event>,
nsubscriptions: types::Size,
) -> Result<types::Size, types::Error> {
todo!()
}
#[instrument(skip(self))]
async fn proc_exit(&mut self, status: types::Exitcode) -> anyhow::Error {
let status = match status {
0 => Ok(()),
_ => Err(()),
};
match self.exit(status).await {
Err(e) => e,
Ok(()) => anyhow!("`exit` did not return an error"),
}
}
#[instrument(skip(self))]
async fn proc_raise(&mut self, _sig: types::Signal) -> Result<(), types::Error> {
Err(types::Errno::Notsup.into())
}
#[instrument(skip(self))]
async fn sched_yield(&mut self) -> Result<(), types::Error> {
Ok(())
}
#[instrument(skip(self))]
async fn random_get<'a>(
&mut self,
buf: &GuestPtr<'a, u8>,
buf_len: types::Size,
) -> Result<(), types::Error> {
let rand = self
.get_random_bytes(buf_len.into())
.await
.context("failed to call `get-random-bytes`")
.map_err(types::Error::trap)?;
write_bytes(buf, rand)?;
Ok(())
}
#[allow(unused_variables)]
#[instrument(skip(self))]
async fn sock_accept(
&mut self,
fd: types::Fd,
flags: types::Fdflags,
) -> Result<types::Fd, types::Error> {
todo!()
}
#[allow(unused_variables)]
#[instrument(skip(self))]
async fn sock_recv<'a>(
&mut self,
fd: types::Fd,
ri_data: &types::IovecArray<'a>,
ri_flags: types::Riflags,
) -> Result<(types::Size, types::Roflags), types::Error> {
todo!()
}
#[allow(unused_variables)]
#[instrument(skip(self))]
async fn sock_send<'a>(
&mut self,
fd: types::Fd,
si_data: &types::CiovecArray<'a>,
_si_flags: types::Siflags,
) -> Result<types::Size, types::Error> {
todo!()
}
#[allow(unused_variables)]
#[instrument(skip(self))]
async fn sock_shutdown(
&mut self,
fd: types::Fd,
how: types::Sdflags,
) -> Result<(), types::Error> {
todo!()
}
}