use crate::syscalls::types::*;
use serde::{de, Deserialize, Serialize};
use std::any::Any;
#[cfg(unix)]
use std::convert::TryInto;
use std::fmt;
use std::{
collections::VecDeque,
fs,
io::{self, Read, Seek, Write},
path::PathBuf,
time::SystemTime,
};
use thiserror::Error;
use tracing::debug;
#[derive(Error, Copy, Clone, Debug, PartialEq, Eq)]
#[allow(dead_code)]
pub enum WasiFsError {
#[error("fd not a directory")]
BaseNotDirectory,
#[error("fd not a file")]
NotAFile,
#[error("invalid fd")]
InvalidFd,
#[error("file exists")]
AlreadyExists,
#[error("io error")]
IOError,
#[error("address is in use")]
AddressInUse,
#[error("address could not be found")]
AddressNotAvailable,
#[error("broken pipe (was closed)")]
BrokenPipe,
#[error("connection aborted")]
ConnectionAborted,
#[error("connection refused")]
ConnectionRefused,
#[error("connection reset")]
ConnectionReset,
#[error("operation interrupted")]
Interrupted,
#[error("invalid internal data")]
InvalidData,
#[error("invalid input")]
InvalidInput,
#[error("connection is not open")]
NotConnected,
#[error("entity not found")]
EntityNotFound,
#[error("can't access device")]
NoDevice,
#[error("permission denied")]
PermissionDenied,
#[error("time out")]
TimedOut,
#[error("unexpected eof")]
UnexpectedEof,
#[error("blocking operation. try again")]
WouldBlock,
#[error("write returned 0")]
WriteZero,
#[error("unknown error: {0}")]
UnknownError(__wasi_errno_t),
}
impl WasiFsError {
pub fn from_wasi_err(err: __wasi_errno_t) -> WasiFsError {
match err {
__WASI_EBADF => WasiFsError::InvalidFd,
__WASI_EEXIST => WasiFsError::AlreadyExists,
__WASI_EIO => WasiFsError::IOError,
__WASI_EADDRINUSE => WasiFsError::AddressInUse,
__WASI_EADDRNOTAVAIL => WasiFsError::AddressNotAvailable,
__WASI_EPIPE => WasiFsError::BrokenPipe,
__WASI_ECONNABORTED => WasiFsError::ConnectionAborted,
__WASI_ECONNREFUSED => WasiFsError::ConnectionRefused,
__WASI_ECONNRESET => WasiFsError::ConnectionReset,
__WASI_EINTR => WasiFsError::Interrupted,
__WASI_EINVAL => WasiFsError::InvalidInput,
__WASI_ENOTCONN => WasiFsError::NotConnected,
__WASI_ENODEV => WasiFsError::NoDevice,
__WASI_ENOENT => WasiFsError::EntityNotFound,
__WASI_EPERM => WasiFsError::PermissionDenied,
__WASI_ETIMEDOUT => WasiFsError::TimedOut,
__WASI_EPROTO => WasiFsError::UnexpectedEof,
__WASI_EAGAIN => WasiFsError::WouldBlock,
__WASI_ENOSPC => WasiFsError::WriteZero,
_ => WasiFsError::UnknownError(err),
}
}
pub fn into_wasi_err(self) -> __wasi_errno_t {
match self {
WasiFsError::AlreadyExists => __WASI_EEXIST,
WasiFsError::AddressInUse => __WASI_EADDRINUSE,
WasiFsError::AddressNotAvailable => __WASI_EADDRNOTAVAIL,
WasiFsError::BaseNotDirectory => __WASI_ENOTDIR,
WasiFsError::BrokenPipe => __WASI_EPIPE,
WasiFsError::ConnectionAborted => __WASI_ECONNABORTED,
WasiFsError::ConnectionRefused => __WASI_ECONNREFUSED,
WasiFsError::ConnectionReset => __WASI_ECONNRESET,
WasiFsError::Interrupted => __WASI_EINTR,
WasiFsError::InvalidData => __WASI_EIO,
WasiFsError::InvalidFd => __WASI_EBADF,
WasiFsError::InvalidInput => __WASI_EINVAL,
WasiFsError::IOError => __WASI_EIO,
WasiFsError::NoDevice => __WASI_ENODEV,
WasiFsError::NotAFile => __WASI_EINVAL,
WasiFsError::NotConnected => __WASI_ENOTCONN,
WasiFsError::EntityNotFound => __WASI_ENOENT,
WasiFsError::PermissionDenied => __WASI_EPERM,
WasiFsError::TimedOut => __WASI_ETIMEDOUT,
WasiFsError::UnexpectedEof => __WASI_EPROTO,
WasiFsError::WouldBlock => __WASI_EAGAIN,
WasiFsError::WriteZero => __WASI_ENOSPC,
WasiFsError::UnknownError(ec) => ec,
}
}
}
#[typetag::serde(tag = "type")]
pub trait WasiFile: fmt::Debug + Send + Write + Read + Seek + 'static + Upcastable {
fn last_accessed(&self) -> __wasi_timestamp_t;
fn last_modified(&self) -> __wasi_timestamp_t;
fn created_time(&self) -> __wasi_timestamp_t;
fn set_last_accessed(&self, _last_accessed: __wasi_timestamp_t) {
debug!("{:?} did nothing in WasiFile::set_last_accessed due to using the default implementation", self);
}
fn set_last_modified(&self, _last_modified: __wasi_timestamp_t) {
debug!("{:?} did nothing in WasiFile::set_last_modified due to using the default implementation", self);
}
fn set_created_time(&self, _created_time: __wasi_timestamp_t) {
debug!(
"{:?} did nothing in WasiFile::set_created_time to using the default implementation",
self
);
}
fn size(&self) -> u64;
fn set_len(&mut self, _new_size: __wasi_filesize_t) -> Result<(), WasiFsError>;
fn unlink(&mut self) -> Result<(), WasiFsError>;
fn sync_to_disk(&self) -> Result<(), WasiFsError> {
Ok(())
}
fn rename_file(&self, _new_name: &std::path::Path) -> Result<(), WasiFsError> {
panic!("Default implementation for now as this method is unstable; this default implementation or this entire method may be removed in a future release.");
}
fn bytes_available(&self) -> Result<usize, WasiFsError>;
fn get_raw_fd(&self) -> Option<i32> {
None
}
}
pub trait Upcastable {
fn upcast_any_ref(&'_ self) -> &'_ dyn Any;
fn upcast_any_mut(&'_ mut self) -> &'_ mut dyn Any;
fn upcast_any_box(self: Box<Self>) -> Box<dyn Any>;
}
impl<T: Any + fmt::Debug + 'static> Upcastable for T {
#[inline]
fn upcast_any_ref(&'_ self) -> &'_ dyn Any {
self
}
#[inline]
fn upcast_any_mut(&'_ mut self) -> &'_ mut dyn Any {
self
}
#[inline]
fn upcast_any_box(self: Box<Self>) -> Box<dyn Any> {
self
}
}
impl dyn WasiFile + 'static {
#[inline]
pub fn downcast_ref<T: 'static>(&'_ self) -> Option<&'_ T> {
self.upcast_any_ref().downcast_ref::<T>()
}
#[inline]
pub fn downcast_mut<T: 'static>(&'_ mut self) -> Option<&'_ mut T> {
self.upcast_any_mut().downcast_mut::<T>()
}
}
#[derive(Debug, Clone)]
pub enum PollEvent {
PollIn = 1,
PollOut = 2,
PollError = 4,
PollHangUp = 8,
PollInvalid = 16,
}
impl PollEvent {
fn from_i16(raw_num: i16) -> Option<PollEvent> {
Some(match raw_num {
1 => PollEvent::PollIn,
2 => PollEvent::PollOut,
4 => PollEvent::PollError,
8 => PollEvent::PollHangUp,
16 => PollEvent::PollInvalid,
_ => return None,
})
}
}
#[derive(Debug, Clone)]
pub struct PollEventBuilder {
inner: PollEventSet,
}
pub type PollEventSet = i16;
#[derive(Debug)]
pub struct PollEventIter {
pes: PollEventSet,
i: usize,
}
impl Iterator for PollEventIter {
type Item = PollEvent;
fn next(&mut self) -> Option<Self::Item> {
if self.pes == 0 || self.i > 15 {
None
} else {
while self.i < 16 {
let result = PollEvent::from_i16(self.pes & (1 << self.i));
self.pes &= !(1 << self.i);
self.i += 1;
if let Some(r) = result {
return Some(r);
}
}
unreachable!("Internal logic error in PollEventIter");
}
}
}
pub fn iterate_poll_events(pes: PollEventSet) -> PollEventIter {
PollEventIter { pes, i: 0 }
}
#[cfg(unix)]
fn poll_event_set_to_platform_poll_events(mut pes: PollEventSet) -> i16 {
let mut out = 0;
for i in 0..16 {
out |= match PollEvent::from_i16(pes & (1 << i)) {
Some(PollEvent::PollIn) => libc::POLLIN,
Some(PollEvent::PollOut) => libc::POLLOUT,
Some(PollEvent::PollError) => libc::POLLERR,
Some(PollEvent::PollHangUp) => libc::POLLHUP,
Some(PollEvent::PollInvalid) => libc::POLLNVAL,
_ => 0,
};
pes &= !(1 << i);
}
out
}
#[cfg(unix)]
fn platform_poll_events_to_pollevent_set(mut num: i16) -> PollEventSet {
let mut peb = PollEventBuilder::new();
for i in 0..16 {
peb = match num & (1 << i) {
libc::POLLIN => peb.add(PollEvent::PollIn),
libc::POLLOUT => peb.add(PollEvent::PollOut),
libc::POLLERR => peb.add(PollEvent::PollError),
libc::POLLHUP => peb.add(PollEvent::PollHangUp),
libc::POLLNVAL => peb.add(PollEvent::PollInvalid),
_ => peb,
};
num &= !(1 << i);
}
peb.build()
}
impl PollEventBuilder {
pub fn new() -> PollEventBuilder {
PollEventBuilder { inner: 0 }
}
pub fn add(mut self, event: PollEvent) -> PollEventBuilder {
self.inner |= event as PollEventSet;
self
}
pub fn build(self) -> PollEventSet {
self.inner
}
}
#[cfg(unix)]
pub(crate) fn poll(
selfs: &[&dyn WasiFile],
events: &[PollEventSet],
seen_events: &mut [PollEventSet],
) -> Result<u32, WasiFsError> {
if !(selfs.len() == events.len() && events.len() == seen_events.len()) {
return Err(WasiFsError::InvalidInput);
}
let mut fds = selfs
.iter()
.enumerate()
.filter_map(|(i, s)| s.get_raw_fd().map(|rfd| (i, rfd)))
.map(|(i, host_fd)| libc::pollfd {
fd: host_fd,
events: poll_event_set_to_platform_poll_events(events[i]),
revents: 0,
})
.collect::<Vec<_>>();
let result = unsafe { libc::poll(fds.as_mut_ptr(), selfs.len() as _, 1) };
if result < 0 {
return Err(WasiFsError::IOError);
}
for (i, fd) in fds.into_iter().enumerate() {
seen_events[i] = platform_poll_events_to_pollevent_set(fd.revents);
}
Ok(result.try_into().unwrap())
}
#[cfg(not(unix))]
pub(crate) fn poll(
_selfs: &[&dyn WasiFile],
_events: &[PollEventSet],
_seen_events: &mut [PollEventSet],
) -> Result<(), WasiFsError> {
unimplemented!("HostFile::poll in WasiFile is not implemented for non-Unix-like targets yet");
}
pub trait WasiPath {}
#[derive(Debug, Serialize)]
pub struct HostFile {
#[serde(skip_serializing)]
pub inner: fs::File,
pub host_path: PathBuf,
flags: u16,
}
impl<'de> Deserialize<'de> for HostFile {
fn deserialize<D>(deserializer: D) -> Result<HostFile, D::Error>
where
D: serde::Deserializer<'de>,
{
#[derive(Deserialize)]
#[serde(field_identifier, rename_all = "snake_case")]
enum Field {
HostPath,
Flags,
}
struct HostFileVisitor;
impl<'de> de::Visitor<'de> for HostFileVisitor {
type Value = HostFile;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("struct HostFile")
}
fn visit_seq<V>(self, mut seq: V) -> Result<Self::Value, V::Error>
where
V: de::SeqAccess<'de>,
{
let host_path = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(0, &self))?;
let flags = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(1, &self))?;
let inner = std::fs::OpenOptions::new()
.read(flags & HostFile::READ != 0)
.write(flags & HostFile::WRITE != 0)
.append(flags & HostFile::APPEND != 0)
.open(&host_path)
.map_err(|_| de::Error::custom("Could not open file on this system"))?;
Ok(HostFile {
inner,
host_path,
flags,
})
}
fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error>
where
V: de::MapAccess<'de>,
{
let mut host_path = None;
let mut flags = None;
while let Some(key) = map.next_key()? {
match key {
Field::HostPath => {
if host_path.is_some() {
return Err(de::Error::duplicate_field("host_path"));
}
host_path = Some(map.next_value()?);
}
Field::Flags => {
if flags.is_some() {
return Err(de::Error::duplicate_field("flags"));
}
flags = Some(map.next_value()?);
}
}
}
let host_path = host_path.ok_or_else(|| de::Error::missing_field("host_path"))?;
let flags = flags.ok_or_else(|| de::Error::missing_field("flags"))?;
let inner = std::fs::OpenOptions::new()
.read(flags & HostFile::READ != 0)
.write(flags & HostFile::WRITE != 0)
.append(flags & HostFile::APPEND != 0)
.open(&host_path)
.map_err(|_| de::Error::custom("Could not open file on this system"))?;
Ok(HostFile {
inner,
host_path,
flags,
})
}
}
const FIELDS: &[&str] = &["host_path", "flags"];
deserializer.deserialize_struct("HostFile", FIELDS, HostFileVisitor)
}
}
impl HostFile {
const READ: u16 = 1;
const WRITE: u16 = 2;
const APPEND: u16 = 4;
pub fn new(file: fs::File, host_path: PathBuf, read: bool, write: bool, append: bool) -> Self {
let mut flags = 0;
if read {
flags |= Self::READ;
}
if write {
flags |= Self::WRITE;
}
if append {
flags |= Self::APPEND;
}
Self {
inner: file,
host_path,
flags,
}
}
pub fn metadata(&self) -> fs::Metadata {
self.inner.metadata().unwrap()
}
}
impl Read for HostFile {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.inner.read(buf)
}
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
self.inner.read_to_end(buf)
}
fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
self.inner.read_to_string(buf)
}
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
self.inner.read_exact(buf)
}
}
impl Seek for HostFile {
fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
self.inner.seek(pos)
}
}
impl Write for HostFile {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.inner.write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.inner.flush()
}
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
self.inner.write_all(buf)
}
fn write_fmt(&mut self, fmt: ::std::fmt::Arguments) -> io::Result<()> {
self.inner.write_fmt(fmt)
}
}
#[typetag::serde]
impl WasiFile for HostFile {
fn last_accessed(&self) -> u64 {
self.metadata()
.accessed()
.ok()
.and_then(|ct| ct.duration_since(SystemTime::UNIX_EPOCH).ok())
.map(|ct| ct.as_nanos() as u64)
.unwrap_or(0)
}
fn set_last_accessed(&self, _last_accessed: __wasi_timestamp_t) {
}
fn last_modified(&self) -> u64 {
self.metadata()
.modified()
.ok()
.and_then(|ct| ct.duration_since(SystemTime::UNIX_EPOCH).ok())
.map(|ct| ct.as_nanos() as u64)
.unwrap_or(0)
}
fn set_last_modified(&self, _last_modified: __wasi_timestamp_t) {
}
fn created_time(&self) -> u64 {
self.metadata()
.created()
.ok()
.and_then(|ct| ct.duration_since(SystemTime::UNIX_EPOCH).ok())
.map(|ct| ct.as_nanos() as u64)
.unwrap_or(0)
}
fn set_created_time(&self, _created_time: __wasi_timestamp_t) {
}
fn size(&self) -> u64 {
self.metadata().len()
}
fn set_len(&mut self, new_size: __wasi_filesize_t) -> Result<(), WasiFsError> {
fs::File::set_len(&self.inner, new_size).map_err(Into::into)
}
fn unlink(&mut self) -> Result<(), WasiFsError> {
std::fs::remove_file(&self.host_path).map_err(Into::into)
}
fn sync_to_disk(&self) -> Result<(), WasiFsError> {
self.inner.sync_all().map_err(Into::into)
}
fn rename_file(&self, new_name: &std::path::Path) -> Result<(), WasiFsError> {
std::fs::rename(&self.host_path, new_name).map_err(Into::into)
}
fn bytes_available(&self) -> Result<usize, WasiFsError> {
let host_fd = self.get_raw_fd().unwrap();
host_file_bytes_available(host_fd)
}
#[cfg(unix)]
fn get_raw_fd(&self) -> Option<i32> {
use std::os::unix::io::AsRawFd;
Some(self.inner.as_raw_fd())
}
#[cfg(not(unix))]
fn get_raw_fd(&self) -> Option<i32> {
unimplemented!(
"HostFile::get_raw_fd in WasiFile is not implemented for non-Unix-like targets yet"
);
}
}
impl From<io::Error> for WasiFsError {
fn from(io_error: io::Error) -> Self {
match io_error.kind() {
io::ErrorKind::AddrInUse => WasiFsError::AddressInUse,
io::ErrorKind::AddrNotAvailable => WasiFsError::AddressNotAvailable,
io::ErrorKind::AlreadyExists => WasiFsError::AlreadyExists,
io::ErrorKind::BrokenPipe => WasiFsError::BrokenPipe,
io::ErrorKind::ConnectionAborted => WasiFsError::ConnectionAborted,
io::ErrorKind::ConnectionRefused => WasiFsError::ConnectionRefused,
io::ErrorKind::ConnectionReset => WasiFsError::ConnectionReset,
io::ErrorKind::Interrupted => WasiFsError::Interrupted,
io::ErrorKind::InvalidData => WasiFsError::InvalidData,
io::ErrorKind::InvalidInput => WasiFsError::InvalidInput,
io::ErrorKind::NotConnected => WasiFsError::NotConnected,
io::ErrorKind::NotFound => WasiFsError::EntityNotFound,
io::ErrorKind::PermissionDenied => WasiFsError::PermissionDenied,
io::ErrorKind::TimedOut => WasiFsError::TimedOut,
io::ErrorKind::UnexpectedEof => WasiFsError::UnexpectedEof,
io::ErrorKind::WouldBlock => WasiFsError::WouldBlock,
io::ErrorKind::WriteZero => WasiFsError::WriteZero,
io::ErrorKind::Other => WasiFsError::IOError,
_ => WasiFsError::UnknownError(__WASI_EIO),
}
}
}
#[cfg(unix)]
fn host_file_bytes_available(host_fd: i32) -> Result<usize, WasiFsError> {
let mut bytes_found = 0 as libc::c_int;
let result = unsafe { libc::ioctl(host_fd, libc::FIONREAD, &mut bytes_found) };
match result {
0 => Ok(bytes_found.try_into().unwrap_or(0)),
libc::EBADF => Err(WasiFsError::InvalidFd),
libc::EFAULT => Err(WasiFsError::InvalidData),
libc::EINVAL => Err(WasiFsError::InvalidInput),
_ => Err(WasiFsError::IOError),
}
}
#[cfg(not(unix))]
fn host_file_bytes_available(_raw_fd: i32) -> Result<usize, WasiFsError> {
unimplemented!("host_file_bytes_available not yet implemented for non-Unix-like targets. This probably means the program tried to use wasi::poll_oneoff")
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Stdout;
impl Read for Stdout {
fn read(&mut self, _buf: &mut [u8]) -> io::Result<usize> {
Err(io::Error::new(
io::ErrorKind::Other,
"can not read from stdout",
))
}
fn read_to_end(&mut self, _buf: &mut Vec<u8>) -> io::Result<usize> {
Err(io::Error::new(
io::ErrorKind::Other,
"can not read from stdout",
))
}
fn read_to_string(&mut self, _buf: &mut String) -> io::Result<usize> {
Err(io::Error::new(
io::ErrorKind::Other,
"can not read from stdout",
))
}
fn read_exact(&mut self, _buf: &mut [u8]) -> io::Result<()> {
Err(io::Error::new(
io::ErrorKind::Other,
"can not read from stdout",
))
}
}
impl Seek for Stdout {
fn seek(&mut self, _pos: io::SeekFrom) -> io::Result<u64> {
Err(io::Error::new(io::ErrorKind::Other, "can not seek stdout"))
}
}
impl Write for Stdout {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
io::stdout().write(buf)
}
fn flush(&mut self) -> io::Result<()> {
io::stdout().flush()
}
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
io::stdout().write_all(buf)
}
fn write_fmt(&mut self, fmt: ::std::fmt::Arguments) -> io::Result<()> {
io::stdout().write_fmt(fmt)
}
}
#[typetag::serde]
impl WasiFile for Stdout {
fn last_accessed(&self) -> u64 {
0
}
fn last_modified(&self) -> u64 {
0
}
fn created_time(&self) -> u64 {
0
}
fn size(&self) -> u64 {
0
}
fn set_len(&mut self, _new_size: __wasi_filesize_t) -> Result<(), WasiFsError> {
debug!("Calling WasiFile::set_len on stdout; this is probably a bug");
Err(WasiFsError::PermissionDenied)
}
fn unlink(&mut self) -> Result<(), WasiFsError> {
Ok(())
}
fn bytes_available(&self) -> Result<usize, WasiFsError> {
let host_fd = self.get_raw_fd().unwrap();
host_file_bytes_available(host_fd)
}
#[cfg(unix)]
fn get_raw_fd(&self) -> Option<i32> {
use std::os::unix::io::AsRawFd;
Some(io::stdout().as_raw_fd())
}
#[cfg(not(unix))]
fn get_raw_fd(&self) -> Option<i32> {
unimplemented!(
"Stdout::get_raw_fd in WasiFile is not implemented for non-Unix-like targets yet"
);
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Stderr;
impl Read for Stderr {
fn read(&mut self, _buf: &mut [u8]) -> io::Result<usize> {
Err(io::Error::new(
io::ErrorKind::Other,
"can not read from stderr",
))
}
fn read_to_end(&mut self, _buf: &mut Vec<u8>) -> io::Result<usize> {
Err(io::Error::new(
io::ErrorKind::Other,
"can not read from stderr",
))
}
fn read_to_string(&mut self, _buf: &mut String) -> io::Result<usize> {
Err(io::Error::new(
io::ErrorKind::Other,
"can not read from stderr",
))
}
fn read_exact(&mut self, _buf: &mut [u8]) -> io::Result<()> {
Err(io::Error::new(
io::ErrorKind::Other,
"can not read from stderr",
))
}
}
impl Seek for Stderr {
fn seek(&mut self, _pos: io::SeekFrom) -> io::Result<u64> {
Err(io::Error::new(io::ErrorKind::Other, "can not seek stderr"))
}
}
impl Write for Stderr {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
io::stderr().write(buf)
}
fn flush(&mut self) -> io::Result<()> {
io::stderr().flush()
}
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
io::stderr().write_all(buf)
}
fn write_fmt(&mut self, fmt: ::std::fmt::Arguments) -> io::Result<()> {
io::stderr().write_fmt(fmt)
}
}
#[typetag::serde]
impl WasiFile for Stderr {
fn last_accessed(&self) -> u64 {
0
}
fn last_modified(&self) -> u64 {
0
}
fn created_time(&self) -> u64 {
0
}
fn size(&self) -> u64 {
0
}
fn set_len(&mut self, _new_size: __wasi_filesize_t) -> Result<(), WasiFsError> {
debug!("Calling WasiFile::set_len on stderr; this is probably a bug");
Err(WasiFsError::PermissionDenied)
}
fn unlink(&mut self) -> Result<(), WasiFsError> {
Ok(())
}
fn bytes_available(&self) -> Result<usize, WasiFsError> {
let host_fd = self.get_raw_fd().unwrap();
host_file_bytes_available(host_fd)
}
#[cfg(unix)]
fn get_raw_fd(&self) -> Option<i32> {
use std::os::unix::io::AsRawFd;
Some(io::stderr().as_raw_fd())
}
#[cfg(not(unix))]
fn get_raw_fd(&self) -> Option<i32> {
unimplemented!(
"Stderr::get_raw_fd in WasiFile is not implemented for non-Unix-like targets yet"
);
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Stdin;
impl Read for Stdin {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
io::stdin().read(buf)
}
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
io::stdin().read_to_end(buf)
}
fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
io::stdin().read_to_string(buf)
}
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
io::stdin().read_exact(buf)
}
}
impl Seek for Stdin {
fn seek(&mut self, _pos: io::SeekFrom) -> io::Result<u64> {
Err(io::Error::new(io::ErrorKind::Other, "can not seek stdin"))
}
}
impl Write for Stdin {
fn write(&mut self, _buf: &[u8]) -> io::Result<usize> {
Err(io::Error::new(
io::ErrorKind::Other,
"can not write to stdin",
))
}
fn flush(&mut self) -> io::Result<()> {
Err(io::Error::new(
io::ErrorKind::Other,
"can not write to stdin",
))
}
fn write_all(&mut self, _buf: &[u8]) -> io::Result<()> {
Err(io::Error::new(
io::ErrorKind::Other,
"can not write to stdin",
))
}
fn write_fmt(&mut self, _fmt: ::std::fmt::Arguments) -> io::Result<()> {
Err(io::Error::new(
io::ErrorKind::Other,
"can not write to stdin",
))
}
}
#[typetag::serde]
impl WasiFile for Stdin {
fn last_accessed(&self) -> u64 {
0
}
fn last_modified(&self) -> u64 {
0
}
fn created_time(&self) -> u64 {
0
}
fn size(&self) -> u64 {
0
}
fn set_len(&mut self, _new_size: __wasi_filesize_t) -> Result<(), WasiFsError> {
debug!("Calling WasiFile::set_len on stdin; this is probably a bug");
Err(WasiFsError::PermissionDenied)
}
fn unlink(&mut self) -> Result<(), WasiFsError> {
Ok(())
}
fn bytes_available(&self) -> Result<usize, WasiFsError> {
let host_fd = self.get_raw_fd().unwrap();
host_file_bytes_available(host_fd)
}
#[cfg(unix)]
fn get_raw_fd(&self) -> Option<i32> {
use std::os::unix::io::AsRawFd;
Some(io::stdin().as_raw_fd())
}
#[cfg(not(unix))]
fn get_raw_fd(&self) -> Option<i32> {
unimplemented!(
"Stdin::get_raw_fd in WasiFile is not implemented for non-Unix-like targets yet"
);
}
}
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct Pipe {
buffer: VecDeque<u8>,
}
impl Pipe {
pub fn new() -> Self {
Self::default()
}
}
impl Read for Pipe {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let amt = std::cmp::min(buf.len(), self.buffer.len());
for (i, byte) in self.buffer.drain(..amt).enumerate() {
buf[i] = byte;
}
Ok(amt)
}
}
impl Write for Pipe {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.buffer.extend(buf);
Ok(buf.len())
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
impl Seek for Pipe {
fn seek(&mut self, _pos: io::SeekFrom) -> io::Result<u64> {
Err(io::Error::new(
io::ErrorKind::Other,
"can not seek in a pipe",
))
}
}
#[typetag::serde]
impl WasiFile for Pipe {
fn last_accessed(&self) -> u64 {
0
}
fn last_modified(&self) -> u64 {
0
}
fn created_time(&self) -> u64 {
0
}
fn size(&self) -> u64 {
self.buffer.len() as u64
}
fn set_len(&mut self, len: u64) -> Result<(), WasiFsError> {
Ok(self.buffer.resize(len as usize, 0))
}
fn unlink(&mut self) -> Result<(), WasiFsError> {
Ok(())
}
fn bytes_available(&self) -> Result<usize, WasiFsError> {
Ok(self.buffer.len())
}
}