move wasi-c2 into wasi-common
This commit is contained in:
46
crates/wasi-common/cap-std-sync/src/clocks.rs
Normal file
46
crates/wasi-common/cap-std-sync/src/clocks.rs
Normal file
@@ -0,0 +1,46 @@
|
||||
use cap_std::time::{Duration, Instant, SystemTime};
|
||||
use cap_time_ext::{MonotonicClockExt, SystemClockExt};
|
||||
use wasi_c2::clocks::{WasiClocks, WasiMonotonicClock, WasiSystemClock};
|
||||
|
||||
pub struct SystemClock(cap_std::time::SystemClock);
|
||||
|
||||
impl SystemClock {
|
||||
pub unsafe fn new() -> Self {
|
||||
SystemClock(cap_std::time::SystemClock::new())
|
||||
}
|
||||
}
|
||||
impl WasiSystemClock for SystemClock {
|
||||
fn resolution(&self) -> Duration {
|
||||
self.0.resolution()
|
||||
}
|
||||
fn now(&self, precision: Duration) -> SystemTime {
|
||||
self.0.now_with(precision)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MonotonicClock(cap_std::time::MonotonicClock);
|
||||
impl MonotonicClock {
|
||||
pub unsafe fn new() -> Self {
|
||||
MonotonicClock(cap_std::time::MonotonicClock::new())
|
||||
}
|
||||
}
|
||||
impl WasiMonotonicClock for MonotonicClock {
|
||||
fn resolution(&self) -> Duration {
|
||||
self.0.resolution()
|
||||
}
|
||||
fn now(&self, precision: Duration) -> Instant {
|
||||
self.0.now_with(precision)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clocks() -> WasiClocks {
|
||||
let system = Box::new(unsafe { SystemClock::new() });
|
||||
let monotonic = unsafe { cap_std::time::MonotonicClock::new() };
|
||||
let creation_time = monotonic.now();
|
||||
let monotonic = Box::new(MonotonicClock(monotonic));
|
||||
WasiClocks {
|
||||
system,
|
||||
monotonic,
|
||||
creation_time,
|
||||
}
|
||||
}
|
||||
249
crates/wasi-common/cap-std-sync/src/dir.rs
Normal file
249
crates/wasi-common/cap-std-sync/src/dir.rs
Normal file
@@ -0,0 +1,249 @@
|
||||
use crate::file::{filetype_from, File};
|
||||
use cap_fs_ext::{DirExt, MetadataExt, SystemTimeSpec};
|
||||
use std::any::Any;
|
||||
use std::convert::TryInto;
|
||||
use std::path::{Path, PathBuf};
|
||||
use wasi_c2::{
|
||||
dir::{ReaddirCursor, ReaddirEntity, WasiDir},
|
||||
file::{FdFlags, FileCaps, FileType, Filestat, OFlags, WasiFile},
|
||||
Error, ErrorExt,
|
||||
};
|
||||
|
||||
pub struct Dir(cap_std::fs::Dir);
|
||||
|
||||
impl Dir {
|
||||
pub fn from_cap_std(dir: cap_std::fs::Dir) -> Self {
|
||||
Dir(dir)
|
||||
}
|
||||
}
|
||||
|
||||
impl WasiDir for Dir {
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
fn open_file(
|
||||
&self,
|
||||
symlink_follow: bool,
|
||||
path: &str,
|
||||
oflags: OFlags,
|
||||
caps: FileCaps,
|
||||
fdflags: FdFlags,
|
||||
) -> Result<Box<dyn WasiFile>, Error> {
|
||||
use cap_fs_ext::{FollowSymlinks, OpenOptionsFollowExt};
|
||||
|
||||
let mut opts = cap_std::fs::OpenOptions::new();
|
||||
|
||||
if oflags.contains(OFlags::CREATE | OFlags::EXCLUSIVE) {
|
||||
opts.create_new(true);
|
||||
opts.write(true);
|
||||
} else if oflags.contains(OFlags::CREATE) {
|
||||
opts.create(true);
|
||||
opts.write(true);
|
||||
}
|
||||
if oflags.contains(OFlags::TRUNCATE) {
|
||||
opts.truncate(true);
|
||||
}
|
||||
if caps.contains(FileCaps::WRITE)
|
||||
|| caps.contains(FileCaps::DATASYNC)
|
||||
|| caps.contains(FileCaps::ALLOCATE)
|
||||
|| caps.contains(FileCaps::FILESTAT_SET_SIZE)
|
||||
{
|
||||
opts.write(true);
|
||||
} else {
|
||||
// If not opened write, open read. This way the OS lets us open the file.
|
||||
// If FileCaps::READ is not set, read calls will be rejected at the
|
||||
// get_cap check.
|
||||
opts.read(true);
|
||||
}
|
||||
if caps.contains(FileCaps::READ) {
|
||||
opts.read(true);
|
||||
}
|
||||
if fdflags.contains(FdFlags::APPEND) {
|
||||
opts.append(true);
|
||||
}
|
||||
// XXX what about rest of fdflags - dsync, sync become oflags.
|
||||
// what do we do with nonblock?
|
||||
// what do we do with rsync?
|
||||
|
||||
if symlink_follow {
|
||||
opts.follow(FollowSymlinks::Yes);
|
||||
} else {
|
||||
opts.follow(FollowSymlinks::No);
|
||||
}
|
||||
|
||||
let f = self.0.open_with(Path::new(path), &opts)?;
|
||||
Ok(Box::new(File::from_cap_std(f)))
|
||||
}
|
||||
|
||||
fn open_dir(&self, symlink_follow: bool, path: &str) -> Result<Box<dyn WasiDir>, Error> {
|
||||
let d = if symlink_follow {
|
||||
self.0.open_dir(Path::new(path))?
|
||||
} else {
|
||||
self.0.open_dir_nofollow(Path::new(path))?
|
||||
};
|
||||
Ok(Box::new(Dir::from_cap_std(d)))
|
||||
}
|
||||
|
||||
fn create_dir(&self, path: &str) -> Result<(), Error> {
|
||||
self.0.create_dir(Path::new(path))?;
|
||||
Ok(())
|
||||
}
|
||||
fn readdir(
|
||||
&self,
|
||||
cursor: ReaddirCursor,
|
||||
) -> Result<Box<dyn Iterator<Item = Result<(ReaddirEntity, String), Error>>>, Error> {
|
||||
// cap_std's read_dir does not include . and .., we should prepend these.
|
||||
// Why does the Ok contain a tuple? We can't construct a cap_std::fs::DirEntry, and we don't
|
||||
// have enough info to make a ReaddirEntity yet.
|
||||
let dir_meta = self.0.dir_metadata()?;
|
||||
let rd = vec![
|
||||
{
|
||||
let name = ".".to_owned();
|
||||
let namelen = name.as_bytes().len().try_into().expect("1 wont overflow");
|
||||
Ok((FileType::Directory, dir_meta.ino(), namelen, name))
|
||||
},
|
||||
{
|
||||
// XXX if parent dir is mounted it *might* be possible to give its inode, but we
|
||||
// don't know that in this context.
|
||||
let name = "..".to_owned();
|
||||
let namelen = name.as_bytes().len().try_into().expect("2 wont overflow");
|
||||
Ok((FileType::Directory, dir_meta.ino(), namelen, name))
|
||||
},
|
||||
]
|
||||
.into_iter()
|
||||
.chain(
|
||||
// Now process the `DirEntry`s:
|
||||
self.0.entries()?.map(|entry| {
|
||||
let entry = entry?;
|
||||
let meta = entry.metadata()?;
|
||||
let inode = meta.ino();
|
||||
let filetype = filetype_from(&meta.file_type());
|
||||
let name = entry
|
||||
.file_name()
|
||||
.into_string()
|
||||
.map_err(|_| Error::illegal_byte_sequence().context("filename"))?;
|
||||
let namelen = name.as_bytes().len().try_into()?;
|
||||
Ok((filetype, inode, namelen, name))
|
||||
}),
|
||||
)
|
||||
// Enumeration of the iterator makes it possible to define the ReaddirCursor
|
||||
.enumerate()
|
||||
.map(|(ix, r)| match r {
|
||||
Ok((filetype, inode, namelen, name)) => Ok((
|
||||
ReaddirEntity {
|
||||
next: ReaddirCursor::from(ix as u64 + 1),
|
||||
filetype,
|
||||
inode,
|
||||
namelen,
|
||||
},
|
||||
name,
|
||||
)),
|
||||
Err(e) => Err(e),
|
||||
})
|
||||
.skip(u64::from(cursor) as usize);
|
||||
|
||||
Ok(Box::new(rd))
|
||||
}
|
||||
|
||||
fn symlink(&self, src_path: &str, dest_path: &str) -> Result<(), Error> {
|
||||
self.0.symlink(src_path, dest_path)?;
|
||||
Ok(())
|
||||
}
|
||||
fn remove_dir(&self, path: &str) -> Result<(), Error> {
|
||||
self.0.remove_dir(Path::new(path))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn unlink_file(&self, path: &str) -> Result<(), Error> {
|
||||
self.0.remove_file(Path::new(path))?;
|
||||
Ok(())
|
||||
}
|
||||
fn read_link(&self, path: &str) -> Result<PathBuf, Error> {
|
||||
let link = self.0.read_link(Path::new(path))?;
|
||||
Ok(link)
|
||||
}
|
||||
fn get_filestat(&self) -> Result<Filestat, Error> {
|
||||
let meta = self.0.dir_metadata()?;
|
||||
Ok(Filestat {
|
||||
device_id: meta.dev(),
|
||||
inode: meta.ino(),
|
||||
filetype: filetype_from(&meta.file_type()),
|
||||
nlink: meta.nlink(),
|
||||
size: meta.len(),
|
||||
atim: meta.accessed().map(|t| Some(t.into_std())).unwrap_or(None),
|
||||
mtim: meta.modified().map(|t| Some(t.into_std())).unwrap_or(None),
|
||||
ctim: meta.created().map(|t| Some(t.into_std())).unwrap_or(None),
|
||||
})
|
||||
}
|
||||
fn get_path_filestat(&self, path: &str, follow_symlinks: bool) -> Result<Filestat, Error> {
|
||||
let meta = if follow_symlinks {
|
||||
self.0.metadata(Path::new(path))?
|
||||
} else {
|
||||
self.0.symlink_metadata(Path::new(path))?
|
||||
};
|
||||
Ok(Filestat {
|
||||
device_id: meta.dev(),
|
||||
inode: meta.ino(),
|
||||
filetype: filetype_from(&meta.file_type()),
|
||||
nlink: meta.nlink(),
|
||||
size: meta.len(),
|
||||
atim: meta.accessed().map(|t| Some(t.into_std())).unwrap_or(None),
|
||||
mtim: meta.modified().map(|t| Some(t.into_std())).unwrap_or(None),
|
||||
ctim: meta.created().map(|t| Some(t.into_std())).unwrap_or(None),
|
||||
})
|
||||
}
|
||||
fn rename(&self, src_path: &str, dest_dir: &dyn WasiDir, dest_path: &str) -> Result<(), Error> {
|
||||
let dest_dir = dest_dir
|
||||
.as_any()
|
||||
.downcast_ref::<Self>()
|
||||
.ok_or(Error::badf().context("failed downcast to cap-std Dir"))?;
|
||||
self.0
|
||||
.rename(Path::new(src_path), &dest_dir.0, Path::new(dest_path))?;
|
||||
Ok(())
|
||||
}
|
||||
fn hard_link(
|
||||
&self,
|
||||
src_path: &str,
|
||||
target_dir: &dyn WasiDir,
|
||||
target_path: &str,
|
||||
) -> Result<(), Error> {
|
||||
let target_dir = target_dir
|
||||
.as_any()
|
||||
.downcast_ref::<Self>()
|
||||
.ok_or(Error::badf().context("failed downcast to cap-std Dir"))?;
|
||||
let src_path = Path::new(src_path);
|
||||
let target_path = Path::new(target_path);
|
||||
self.0.hard_link(src_path, &target_dir.0, target_path)?;
|
||||
Ok(())
|
||||
}
|
||||
fn set_times(
|
||||
&self,
|
||||
path: &str,
|
||||
atime: Option<wasi_c2::SystemTimeSpec>,
|
||||
mtime: Option<wasi_c2::SystemTimeSpec>,
|
||||
follow_symlinks: bool,
|
||||
) -> Result<(), Error> {
|
||||
if follow_symlinks {
|
||||
self.0.set_times(
|
||||
Path::new(path),
|
||||
convert_systimespec(atime),
|
||||
convert_systimespec(mtime),
|
||||
)?;
|
||||
} else {
|
||||
self.0.set_symlink_times(
|
||||
Path::new(path),
|
||||
convert_systimespec(atime),
|
||||
convert_systimespec(mtime),
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_systimespec(t: Option<wasi_c2::SystemTimeSpec>) -> Option<SystemTimeSpec> {
|
||||
match t {
|
||||
Some(wasi_c2::SystemTimeSpec::Absolute(t)) => Some(SystemTimeSpec::Absolute(t)),
|
||||
Some(wasi_c2::SystemTimeSpec::SymbolicNow) => Some(SystemTimeSpec::SymbolicNow),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
193
crates/wasi-common/cap-std-sync/src/file.rs
Normal file
193
crates/wasi-common/cap-std-sync/src/file.rs
Normal file
@@ -0,0 +1,193 @@
|
||||
use cap_fs_ext::MetadataExt;
|
||||
use fs_set_times::{SetTimes, SystemTimeSpec};
|
||||
use std::any::Any;
|
||||
use std::convert::TryInto;
|
||||
use std::io;
|
||||
use system_interface::{
|
||||
fs::{Advice, FileIoExt, GetSetFdFlags},
|
||||
io::ReadReady,
|
||||
};
|
||||
use wasi_c2::{
|
||||
file::{FdFlags, FileType, Filestat, WasiFile},
|
||||
Error,
|
||||
};
|
||||
|
||||
pub struct File(cap_std::fs::File);
|
||||
|
||||
impl File {
|
||||
pub fn from_cap_std(file: cap_std::fs::File) -> Self {
|
||||
File(file)
|
||||
}
|
||||
}
|
||||
|
||||
impl WasiFile for File {
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
fn datasync(&self) -> Result<(), Error> {
|
||||
self.0.sync_data()?;
|
||||
Ok(())
|
||||
}
|
||||
fn sync(&self) -> Result<(), Error> {
|
||||
self.0.sync_all()?;
|
||||
Ok(())
|
||||
}
|
||||
fn get_filetype(&self) -> Result<FileType, Error> {
|
||||
let meta = self.0.metadata()?;
|
||||
Ok(filetype_from(&meta.file_type()))
|
||||
}
|
||||
fn get_fdflags(&self) -> Result<FdFlags, Error> {
|
||||
let fdflags = self.0.get_fd_flags()?;
|
||||
Ok(from_sysif_fdflags(fdflags))
|
||||
}
|
||||
fn set_fdflags(&mut self, fdflags: FdFlags) -> Result<(), Error> {
|
||||
Ok(self.0.set_fd_flags(to_sysif_fdflags(fdflags))?)
|
||||
}
|
||||
fn get_filestat(&self) -> Result<Filestat, Error> {
|
||||
let meta = self.0.metadata()?;
|
||||
Ok(Filestat {
|
||||
device_id: meta.dev(),
|
||||
inode: meta.ino(),
|
||||
filetype: filetype_from(&meta.file_type()),
|
||||
nlink: meta.nlink(),
|
||||
size: meta.len(),
|
||||
atim: meta.accessed().map(|t| Some(t.into_std())).unwrap_or(None),
|
||||
mtim: meta.modified().map(|t| Some(t.into_std())).unwrap_or(None),
|
||||
ctim: meta.created().map(|t| Some(t.into_std())).unwrap_or(None),
|
||||
})
|
||||
}
|
||||
fn set_filestat_size(&self, size: u64) -> Result<(), Error> {
|
||||
self.0.set_len(size)?;
|
||||
Ok(())
|
||||
}
|
||||
fn advise(&self, offset: u64, len: u64, advice: Advice) -> Result<(), Error> {
|
||||
self.0.advise(offset, len, advice)?;
|
||||
Ok(())
|
||||
}
|
||||
fn allocate(&self, offset: u64, len: u64) -> Result<(), Error> {
|
||||
self.0.allocate(offset, len)?;
|
||||
Ok(())
|
||||
}
|
||||
fn set_times(
|
||||
&self,
|
||||
atime: Option<wasi_c2::SystemTimeSpec>,
|
||||
mtime: Option<wasi_c2::SystemTimeSpec>,
|
||||
) -> Result<(), Error> {
|
||||
self.0
|
||||
.set_times(convert_systimespec(atime), convert_systimespec(mtime))?;
|
||||
Ok(())
|
||||
}
|
||||
fn read_vectored(&self, bufs: &mut [io::IoSliceMut]) -> Result<u64, Error> {
|
||||
let n = self.0.read_vectored(bufs)?;
|
||||
Ok(n.try_into()?)
|
||||
}
|
||||
fn read_vectored_at(&self, bufs: &mut [io::IoSliceMut], offset: u64) -> Result<u64, Error> {
|
||||
let n = self.0.read_vectored_at(bufs, offset)?;
|
||||
Ok(n.try_into()?)
|
||||
}
|
||||
fn write_vectored(&self, bufs: &[io::IoSlice]) -> Result<u64, Error> {
|
||||
let n = self.0.write_vectored(bufs)?;
|
||||
Ok(n.try_into()?)
|
||||
}
|
||||
fn write_vectored_at(&self, bufs: &[io::IoSlice], offset: u64) -> Result<u64, Error> {
|
||||
let n = self.0.write_vectored_at(bufs, offset)?;
|
||||
Ok(n.try_into()?)
|
||||
}
|
||||
fn seek(&self, pos: std::io::SeekFrom) -> Result<u64, Error> {
|
||||
Ok(self.0.seek(pos)?)
|
||||
}
|
||||
fn peek(&self, buf: &mut [u8]) -> Result<u64, Error> {
|
||||
let n = self.0.peek(buf)?;
|
||||
Ok(n.try_into()?)
|
||||
}
|
||||
fn num_ready_bytes(&self) -> Result<u64, Error> {
|
||||
Ok(self.0.num_ready_bytes()?)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn filetype_from(ft: &cap_std::fs::FileType) -> FileType {
|
||||
use cap_fs_ext::FileTypeExt;
|
||||
if ft.is_dir() {
|
||||
FileType::Directory
|
||||
} else if ft.is_symlink() {
|
||||
FileType::SymbolicLink
|
||||
} else if ft.is_socket() {
|
||||
if ft.is_block_device() {
|
||||
FileType::SocketDgram
|
||||
} else {
|
||||
FileType::SocketStream
|
||||
}
|
||||
} else if ft.is_block_device() {
|
||||
FileType::BlockDevice
|
||||
} else if ft.is_char_device() {
|
||||
FileType::CharacterDevice
|
||||
} else if ft.is_file() {
|
||||
FileType::RegularFile
|
||||
} else {
|
||||
FileType::Unknown
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
use std::os::windows::io::{AsRawHandle, RawHandle};
|
||||
#[cfg(windows)]
|
||||
impl AsRawHandle for File {
|
||||
fn as_raw_handle(&self) -> RawHandle {
|
||||
self.0.as_raw_handle()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
#[cfg(unix)]
|
||||
impl AsRawFd for File {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.0.as_raw_fd()
|
||||
}
|
||||
}
|
||||
pub fn convert_systimespec(t: Option<wasi_c2::SystemTimeSpec>) -> Option<SystemTimeSpec> {
|
||||
match t {
|
||||
Some(wasi_c2::SystemTimeSpec::Absolute(t)) => Some(SystemTimeSpec::Absolute(t.into_std())),
|
||||
Some(wasi_c2::SystemTimeSpec::SymbolicNow) => Some(SystemTimeSpec::SymbolicNow),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_sysif_fdflags(f: wasi_c2::file::FdFlags) -> system_interface::fs::FdFlags {
|
||||
let mut out = system_interface::fs::FdFlags::empty();
|
||||
if f.contains(wasi_c2::file::FdFlags::APPEND) {
|
||||
out |= system_interface::fs::FdFlags::APPEND;
|
||||
}
|
||||
if f.contains(wasi_c2::file::FdFlags::DSYNC) {
|
||||
out |= system_interface::fs::FdFlags::DSYNC;
|
||||
}
|
||||
if f.contains(wasi_c2::file::FdFlags::NONBLOCK) {
|
||||
out |= system_interface::fs::FdFlags::NONBLOCK;
|
||||
}
|
||||
if f.contains(wasi_c2::file::FdFlags::RSYNC) {
|
||||
out |= system_interface::fs::FdFlags::RSYNC;
|
||||
}
|
||||
if f.contains(wasi_c2::file::FdFlags::SYNC) {
|
||||
out |= system_interface::fs::FdFlags::SYNC;
|
||||
}
|
||||
out
|
||||
}
|
||||
pub fn from_sysif_fdflags(f: system_interface::fs::FdFlags) -> wasi_c2::file::FdFlags {
|
||||
let mut out = wasi_c2::file::FdFlags::empty();
|
||||
if f.contains(system_interface::fs::FdFlags::APPEND) {
|
||||
out |= wasi_c2::file::FdFlags::APPEND;
|
||||
}
|
||||
if f.contains(system_interface::fs::FdFlags::DSYNC) {
|
||||
out |= wasi_c2::file::FdFlags::DSYNC;
|
||||
}
|
||||
if f.contains(system_interface::fs::FdFlags::NONBLOCK) {
|
||||
out |= wasi_c2::file::FdFlags::NONBLOCK;
|
||||
}
|
||||
if f.contains(system_interface::fs::FdFlags::RSYNC) {
|
||||
out |= wasi_c2::file::FdFlags::RSYNC;
|
||||
}
|
||||
if f.contains(system_interface::fs::FdFlags::SYNC) {
|
||||
out |= wasi_c2::file::FdFlags::SYNC;
|
||||
}
|
||||
out
|
||||
}
|
||||
61
crates/wasi-common/cap-std-sync/src/lib.rs
Normal file
61
crates/wasi-common/cap-std-sync/src/lib.rs
Normal file
@@ -0,0 +1,61 @@
|
||||
pub mod clocks;
|
||||
pub mod dir;
|
||||
pub mod file;
|
||||
pub mod sched;
|
||||
pub mod stdio;
|
||||
|
||||
use cap_rand::RngCore;
|
||||
use std::cell::RefCell;
|
||||
use std::path::Path;
|
||||
use std::rc::Rc;
|
||||
use wasi_c2::{table::Table, Error, WasiCtx, WasiFile};
|
||||
|
||||
pub struct WasiCtxBuilder(wasi_c2::WasiCtxBuilder);
|
||||
|
||||
impl WasiCtxBuilder {
|
||||
pub fn new() -> Self {
|
||||
WasiCtxBuilder(WasiCtx::builder(
|
||||
random(),
|
||||
clocks::clocks(),
|
||||
Box::new(sched::SyncSched::new()),
|
||||
Rc::new(RefCell::new(Table::new())),
|
||||
))
|
||||
}
|
||||
pub fn env(self, var: &str, value: &str) -> Result<Self, wasi_c2::StringArrayError> {
|
||||
let s = self.0.env(var, value)?;
|
||||
Ok(WasiCtxBuilder(s))
|
||||
}
|
||||
pub fn arg(self, arg: &str) -> Result<Self, wasi_c2::StringArrayError> {
|
||||
let s = self.0.arg(arg)?;
|
||||
Ok(WasiCtxBuilder(s))
|
||||
}
|
||||
pub fn stdin(self, f: Box<dyn WasiFile>) -> Self {
|
||||
WasiCtxBuilder(self.0.stdin(f))
|
||||
}
|
||||
pub fn stdout(self, f: Box<dyn WasiFile>) -> Self {
|
||||
WasiCtxBuilder(self.0.stdout(f))
|
||||
}
|
||||
pub fn stderr(self, f: Box<dyn WasiFile>) -> Self {
|
||||
WasiCtxBuilder(self.0.stderr(f))
|
||||
}
|
||||
pub fn inherit_stdio(self) -> Self {
|
||||
self.stdin(Box::new(crate::stdio::stdin()))
|
||||
.stdout(Box::new(crate::stdio::stdout()))
|
||||
.stderr(Box::new(crate::stdio::stderr()))
|
||||
}
|
||||
pub fn preopened_dir(
|
||||
self,
|
||||
dir: cap_std::fs::Dir,
|
||||
path: impl AsRef<Path>,
|
||||
) -> Result<Self, Error> {
|
||||
let dir = Box::new(crate::dir::Dir::from_cap_std(dir));
|
||||
Ok(WasiCtxBuilder(self.0.preopened_dir(dir, path)?))
|
||||
}
|
||||
pub fn build(self) -> Result<WasiCtx, Error> {
|
||||
self.0.build()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn random() -> RefCell<Box<dyn RngCore>> {
|
||||
RefCell::new(Box::new(unsafe { cap_rand::rngs::OsRng::default() }))
|
||||
}
|
||||
9
crates/wasi-common/cap-std-sync/src/sched.rs
Normal file
9
crates/wasi-common/cap-std-sync/src/sched.rs
Normal file
@@ -0,0 +1,9 @@
|
||||
#[cfg(unix)]
|
||||
mod unix;
|
||||
#[cfg(unix)]
|
||||
pub use unix::*;
|
||||
|
||||
#[cfg(windows)]
|
||||
mod windows;
|
||||
#[cfg(windows)]
|
||||
pub use windows::*;
|
||||
190
crates/wasi-common/cap-std-sync/src/sched/unix.rs
Normal file
190
crates/wasi-common/cap-std-sync/src/sched/unix.rs
Normal file
@@ -0,0 +1,190 @@
|
||||
use cap_std::time::Duration;
|
||||
use std::convert::TryInto;
|
||||
use std::ops::Deref;
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
use wasi_c2::{
|
||||
file::WasiFile,
|
||||
sched::{
|
||||
subscription::{RwEventFlags, Subscription},
|
||||
Poll, WasiSched,
|
||||
},
|
||||
Error, ErrorExt,
|
||||
};
|
||||
|
||||
use poll::{PollFd, PollFlags};
|
||||
|
||||
pub struct SyncSched;
|
||||
|
||||
impl SyncSched {
|
||||
pub fn new() -> Self {
|
||||
SyncSched
|
||||
}
|
||||
}
|
||||
|
||||
impl WasiSched for SyncSched {
|
||||
fn poll_oneoff<'a>(&self, poll: &'a Poll<'a>) -> Result<(), Error> {
|
||||
if poll.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
let mut pollfds = Vec::new();
|
||||
let timeout = poll.earliest_clock_deadline();
|
||||
for s in poll.rw_subscriptions() {
|
||||
match s {
|
||||
Subscription::Read(f) => {
|
||||
let raw_fd = wasi_file_raw_fd(f.file.deref()).ok_or(
|
||||
Error::invalid_argument().context("read subscription fd downcast failed"),
|
||||
)?;
|
||||
pollfds.push(unsafe { PollFd::new(raw_fd, PollFlags::POLLIN) });
|
||||
}
|
||||
|
||||
Subscription::Write(f) => {
|
||||
let raw_fd = wasi_file_raw_fd(f.file.deref()).ok_or(
|
||||
Error::invalid_argument().context("write subscription fd downcast failed"),
|
||||
)?;
|
||||
pollfds.push(unsafe { PollFd::new(raw_fd, PollFlags::POLLOUT) });
|
||||
}
|
||||
Subscription::MonotonicClock { .. } => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
let ready = loop {
|
||||
let poll_timeout = if let Some(t) = timeout {
|
||||
let duration = t.duration_until().unwrap_or(Duration::from_secs(0));
|
||||
(duration.as_millis() + 1) // XXX try always rounding up?
|
||||
.try_into()
|
||||
.map_err(|_| Error::overflow().context("poll timeout"))?
|
||||
} else {
|
||||
libc::c_int::max_value()
|
||||
};
|
||||
tracing::debug!(
|
||||
poll_timeout = tracing::field::debug(poll_timeout),
|
||||
poll_fds = tracing::field::debug(&pollfds),
|
||||
"poll"
|
||||
);
|
||||
match poll::poll(&mut pollfds, poll_timeout) {
|
||||
Ok(ready) => break ready,
|
||||
Err(_) => {
|
||||
let last_err = std::io::Error::last_os_error();
|
||||
if last_err.raw_os_error().unwrap() == libc::EINTR {
|
||||
continue;
|
||||
} else {
|
||||
return Err(last_err.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
if ready > 0 {
|
||||
for (rwsub, pollfd) in poll.rw_subscriptions().zip(pollfds.into_iter()) {
|
||||
if let Some(revents) = pollfd.revents() {
|
||||
let (nbytes, rwsub) = match rwsub {
|
||||
Subscription::Read(sub) => {
|
||||
let ready = sub.file.num_ready_bytes()?;
|
||||
(std::cmp::max(ready, 1), sub)
|
||||
}
|
||||
Subscription::Write(sub) => (0, sub),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
if revents.contains(PollFlags::POLLNVAL) {
|
||||
rwsub.error(Error::badf());
|
||||
} else if revents.contains(PollFlags::POLLERR) {
|
||||
rwsub.error(Error::io());
|
||||
} else if revents.contains(PollFlags::POLLHUP) {
|
||||
rwsub.complete(nbytes, RwEventFlags::HANGUP);
|
||||
} else {
|
||||
rwsub.complete(nbytes, RwEventFlags::empty());
|
||||
};
|
||||
}
|
||||
}
|
||||
} else {
|
||||
timeout
|
||||
.expect("timed out")
|
||||
.result()
|
||||
.expect("timer deadline is past")
|
||||
.unwrap()
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn sched_yield(&self) -> Result<(), Error> {
|
||||
std::thread::yield_now();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn wasi_file_raw_fd(f: &dyn WasiFile) -> Option<RawFd> {
|
||||
let a = f.as_any();
|
||||
if a.is::<crate::file::File>() {
|
||||
Some(a.downcast_ref::<crate::file::File>().unwrap().as_raw_fd())
|
||||
} else if a.is::<crate::stdio::Stdin>() {
|
||||
Some(a.downcast_ref::<crate::stdio::Stdin>().unwrap().as_raw_fd())
|
||||
} else if a.is::<crate::stdio::Stdout>() {
|
||||
Some(
|
||||
a.downcast_ref::<crate::stdio::Stdout>()
|
||||
.unwrap()
|
||||
.as_raw_fd(),
|
||||
)
|
||||
} else if a.is::<crate::stdio::Stderr>() {
|
||||
Some(
|
||||
a.downcast_ref::<crate::stdio::Stderr>()
|
||||
.unwrap()
|
||||
.as_raw_fd(),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
mod poll {
|
||||
use bitflags::bitflags;
|
||||
use std::convert::TryInto;
|
||||
use std::os::unix::io::RawFd;
|
||||
|
||||
bitflags! {
|
||||
pub struct PollFlags: libc::c_short {
|
||||
const POLLIN = libc::POLLIN;
|
||||
const POLLPRI = libc::POLLPRI;
|
||||
const POLLOUT = libc::POLLOUT;
|
||||
const POLLRDNORM = libc::POLLRDNORM;
|
||||
const POLLWRNORM = libc::POLLWRNORM;
|
||||
const POLLRDBAND = libc::POLLRDBAND;
|
||||
const POLLWRBAND = libc::POLLWRBAND;
|
||||
const POLLERR = libc::POLLERR;
|
||||
const POLLHUP = libc::POLLHUP;
|
||||
const POLLNVAL = libc::POLLNVAL;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
#[repr(C)]
|
||||
pub struct PollFd(libc::pollfd);
|
||||
|
||||
impl PollFd {
|
||||
pub unsafe fn new(fd: RawFd, events: PollFlags) -> Self {
|
||||
Self(libc::pollfd {
|
||||
fd,
|
||||
events: events.bits(),
|
||||
revents: PollFlags::empty().bits(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn revents(self) -> Option<PollFlags> {
|
||||
PollFlags::from_bits(self.0.revents)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn poll(fds: &mut [PollFd], timeout: libc::c_int) -> Result<usize, std::io::Error> {
|
||||
let nready = unsafe {
|
||||
libc::poll(
|
||||
fds.as_mut_ptr() as *mut libc::pollfd,
|
||||
fds.len() as libc::nfds_t,
|
||||
timeout,
|
||||
)
|
||||
};
|
||||
if nready == -1 {
|
||||
Err(std::io::Error::last_os_error())
|
||||
} else {
|
||||
// When poll doesn't fail, its return value is a non-negative int, which will
|
||||
// always be convertable to usize, so we can unwrap() here.
|
||||
Ok(nready.try_into().unwrap())
|
||||
}
|
||||
}
|
||||
}
|
||||
256
crates/wasi-common/cap-std-sync/src/sched/windows.rs
Normal file
256
crates/wasi-common/cap-std-sync/src/sched/windows.rs
Normal file
@@ -0,0 +1,256 @@
|
||||
use anyhow::Context;
|
||||
use std::ops::Deref;
|
||||
use std::os::windows::io::{AsRawHandle, RawHandle};
|
||||
use std::sync::mpsc::{self, Receiver, RecvTimeoutError, Sender, TryRecvError};
|
||||
use std::sync::Mutex;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
use wasi_c2::{
|
||||
file::WasiFile,
|
||||
sched::{
|
||||
subscription::{RwEventFlags, Subscription},
|
||||
Poll, WasiSched,
|
||||
},
|
||||
Error, ErrorExt,
|
||||
};
|
||||
pub struct SyncSched {}
|
||||
|
||||
impl SyncSched {
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
impl WasiSched for SyncSched {
|
||||
fn poll_oneoff<'a>(&self, poll: &'a Poll<'a>) -> Result<(), Error> {
|
||||
if poll.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut ready = false;
|
||||
let timeout = poll.earliest_clock_deadline();
|
||||
|
||||
let mut stdin_read_subs = Vec::new();
|
||||
let mut immediate_subs = Vec::new();
|
||||
for s in poll.rw_subscriptions() {
|
||||
match s {
|
||||
Subscription::Read(r) if r.file.as_any().is::<crate::stdio::Stdin>() => {
|
||||
stdin_read_subs.push(r);
|
||||
}
|
||||
Subscription::Read(rw) | Subscription::Write(rw) => {
|
||||
if wasi_file_raw_handle(rw.file.deref()).is_some() {
|
||||
immediate_subs.push(s);
|
||||
} else {
|
||||
return Err(Error::invalid_argument()
|
||||
.context("read/write subscription fd downcast failed"));
|
||||
}
|
||||
}
|
||||
Subscription::MonotonicClock { .. } => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
if !stdin_read_subs.is_empty() {
|
||||
let waitmode = if let Some(t) = timeout {
|
||||
if let Some(duration) = t.duration_until() {
|
||||
WaitMode::Timeout(duration)
|
||||
} else {
|
||||
WaitMode::Immediate
|
||||
}
|
||||
} else {
|
||||
if ready {
|
||||
WaitMode::Immediate
|
||||
} else {
|
||||
WaitMode::Infinite
|
||||
}
|
||||
};
|
||||
let state = STDIN_POLL
|
||||
.lock()
|
||||
.map_err(|_| Error::trap("failed to take lock of STDIN_POLL"))?
|
||||
.poll(waitmode)?;
|
||||
for readsub in stdin_read_subs.into_iter() {
|
||||
match state {
|
||||
PollState::Ready => {
|
||||
readsub.complete(1, RwEventFlags::empty());
|
||||
ready = true;
|
||||
}
|
||||
PollState::NotReady | PollState::TimedOut => {}
|
||||
PollState::Error(ref e) => {
|
||||
// Unfortunately, we need to deliver the Error to each of the
|
||||
// subscriptions, but there is no Clone on std::io::Error. So, we convert it to the
|
||||
// kind, and then back to std::io::Error, and finally to anyhow::Error.
|
||||
// When its time to turn this into an errno elsewhere, the error kind will
|
||||
// be inspected.
|
||||
let ekind = e.kind();
|
||||
let ioerror = std::io::Error::from(ekind);
|
||||
readsub.error(ioerror.into());
|
||||
ready = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for sub in immediate_subs {
|
||||
match sub {
|
||||
Subscription::Read(r) => {
|
||||
// XXX This doesnt strictly preserve the behavior in the earlier
|
||||
// implementation, which would always do complete(0) for reads from
|
||||
// stdout/err.
|
||||
match r.file.num_ready_bytes() {
|
||||
Ok(ready_bytes) => {
|
||||
r.complete(ready_bytes, RwEventFlags::empty());
|
||||
ready = true;
|
||||
}
|
||||
Err(e) => {
|
||||
r.error(e);
|
||||
ready = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
Subscription::Write(w) => {
|
||||
// Everything is always ready for writing, apparently?
|
||||
w.complete(0, RwEventFlags::empty());
|
||||
ready = true;
|
||||
}
|
||||
Subscription::MonotonicClock { .. } => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
if !ready {
|
||||
if let Some(t) = timeout {
|
||||
if let Some(duration) = t.duration_until() {
|
||||
thread::sleep(duration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
fn sched_yield(&self) -> Result<(), Error> {
|
||||
thread::yield_now();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn wasi_file_raw_handle(f: &dyn WasiFile) -> Option<RawHandle> {
|
||||
let a = f.as_any();
|
||||
if a.is::<crate::file::File>() {
|
||||
Some(
|
||||
a.downcast_ref::<crate::file::File>()
|
||||
.unwrap()
|
||||
.as_raw_handle(),
|
||||
)
|
||||
} else if a.is::<crate::stdio::Stdin>() {
|
||||
Some(
|
||||
a.downcast_ref::<crate::stdio::Stdin>()
|
||||
.unwrap()
|
||||
.as_raw_handle(),
|
||||
)
|
||||
} else if a.is::<crate::stdio::Stdout>() {
|
||||
Some(
|
||||
a.downcast_ref::<crate::stdio::Stdout>()
|
||||
.unwrap()
|
||||
.as_raw_handle(),
|
||||
)
|
||||
} else if a.is::<crate::stdio::Stderr>() {
|
||||
Some(
|
||||
a.downcast_ref::<crate::stdio::Stderr>()
|
||||
.unwrap()
|
||||
.as_raw_handle(),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
enum PollState {
|
||||
Ready,
|
||||
NotReady, // Not ready, but did not wait
|
||||
TimedOut, // Not ready, waited until timeout
|
||||
Error(std::io::Error),
|
||||
}
|
||||
|
||||
enum WaitMode {
|
||||
Timeout(Duration),
|
||||
Infinite,
|
||||
Immediate,
|
||||
}
|
||||
|
||||
struct StdinPoll {
|
||||
request_tx: Sender<()>,
|
||||
notify_rx: Receiver<PollState>,
|
||||
}
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref STDIN_POLL: Mutex<StdinPoll> = StdinPoll::new();
|
||||
}
|
||||
|
||||
impl StdinPoll {
|
||||
pub fn new() -> Mutex<Self> {
|
||||
let (request_tx, request_rx) = mpsc::channel();
|
||||
let (notify_tx, notify_rx) = mpsc::channel();
|
||||
thread::spawn(move || Self::event_loop(request_rx, notify_tx));
|
||||
Mutex::new(StdinPoll {
|
||||
request_tx,
|
||||
notify_rx,
|
||||
})
|
||||
}
|
||||
|
||||
// This function should not be used directly.
|
||||
// Correctness of this function crucially depends on the fact that
|
||||
// mpsc::Receiver is !Sync.
|
||||
fn poll(&self, wait_mode: WaitMode) -> Result<PollState, Error> {
|
||||
match self.notify_rx.try_recv() {
|
||||
// Clean up possibly unread result from previous poll.
|
||||
Ok(_) | Err(TryRecvError::Empty) => {}
|
||||
Err(TryRecvError::Disconnected) => {
|
||||
return Err(Error::trap("StdinPoll notify_rx channel closed"))
|
||||
}
|
||||
}
|
||||
|
||||
// Notify the worker thread to poll stdin
|
||||
self.request_tx
|
||||
.send(())
|
||||
.context("request_tx channel closed")?;
|
||||
|
||||
// Wait for the worker thread to send a readiness notification
|
||||
match wait_mode {
|
||||
WaitMode::Timeout(timeout) => match self.notify_rx.recv_timeout(timeout) {
|
||||
Ok(r) => Ok(r),
|
||||
Err(RecvTimeoutError::Timeout) => Ok(PollState::TimedOut),
|
||||
Err(RecvTimeoutError::Disconnected) => {
|
||||
Err(Error::trap("StdinPoll notify_rx channel closed"))
|
||||
}
|
||||
},
|
||||
WaitMode::Infinite => self
|
||||
.notify_rx
|
||||
.recv()
|
||||
.context("StdinPoll notify_rx channel closed"),
|
||||
WaitMode::Immediate => match self.notify_rx.try_recv() {
|
||||
Ok(r) => Ok(r),
|
||||
Err(TryRecvError::Empty) => Ok(PollState::NotReady),
|
||||
Err(TryRecvError::Disconnected) => {
|
||||
Err(Error::trap("StdinPoll notify_rx channel closed"))
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn event_loop(request_rx: Receiver<()>, notify_tx: Sender<PollState>) -> ! {
|
||||
use std::io::BufRead;
|
||||
loop {
|
||||
// Wait on a request:
|
||||
request_rx.recv().expect("request_rx channel");
|
||||
// Wait for data to appear in stdin. If fill_buf returns any slice, it means
|
||||
// that either:
|
||||
// (a) there is some data in stdin, if non-empty,
|
||||
// (b) EOF was recieved, if its empty
|
||||
// Linux returns `POLLIN` in both cases, so we imitate this behavior.
|
||||
let resp = match std::io::stdin().lock().fill_buf() {
|
||||
Ok(_) => PollState::Ready,
|
||||
Err(e) => PollState::Error(e),
|
||||
};
|
||||
// Notify about data in stdin. If the read on this channel has timed out, the
|
||||
// next poller will have to clean the channel.
|
||||
notify_tx.send(resp).expect("notify_tx channel");
|
||||
}
|
||||
}
|
||||
}
|
||||
217
crates/wasi-common/cap-std-sync/src/stdio.rs
Normal file
217
crates/wasi-common/cap-std-sync/src/stdio.rs
Normal file
@@ -0,0 +1,217 @@
|
||||
use crate::file::convert_systimespec;
|
||||
use fs_set_times::SetTimes;
|
||||
use std::any::Any;
|
||||
use std::convert::TryInto;
|
||||
use std::io;
|
||||
use std::io::{Read, Write};
|
||||
use system_interface::{fs::Advice, io::ReadReady};
|
||||
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
#[cfg(windows)]
|
||||
use std::os::windows::io::{AsRawHandle, RawHandle};
|
||||
use unsafe_io::AsUnsafeFile;
|
||||
use wasi_c2::{
|
||||
file::{FdFlags, FileType, Filestat, WasiFile},
|
||||
Error, ErrorExt,
|
||||
};
|
||||
|
||||
pub struct Stdin(std::io::Stdin);
|
||||
|
||||
pub fn stdin() -> Stdin {
|
||||
Stdin(std::io::stdin())
|
||||
}
|
||||
|
||||
impl WasiFile for Stdin {
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
fn datasync(&self) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
fn sync(&self) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
fn get_filetype(&self) -> Result<FileType, Error> {
|
||||
Ok(FileType::Unknown)
|
||||
}
|
||||
fn get_fdflags(&self) -> Result<FdFlags, Error> {
|
||||
Ok(FdFlags::empty())
|
||||
}
|
||||
fn set_fdflags(&mut self, _fdflags: FdFlags) -> Result<(), Error> {
|
||||
Err(Error::badf())
|
||||
}
|
||||
fn get_filestat(&self) -> Result<Filestat, Error> {
|
||||
let meta = self.0.as_file_view().metadata()?;
|
||||
Ok(Filestat {
|
||||
device_id: 0,
|
||||
inode: 0,
|
||||
filetype: self.get_filetype()?,
|
||||
nlink: 0,
|
||||
size: meta.len(),
|
||||
atim: meta.accessed().ok(),
|
||||
mtim: meta.modified().ok(),
|
||||
ctim: meta.created().ok(),
|
||||
})
|
||||
}
|
||||
fn set_filestat_size(&self, _size: u64) -> Result<(), Error> {
|
||||
Err(Error::badf())
|
||||
}
|
||||
fn advise(&self, _offset: u64, _len: u64, _advice: Advice) -> Result<(), Error> {
|
||||
Err(Error::badf())
|
||||
}
|
||||
fn allocate(&self, _offset: u64, _len: u64) -> Result<(), Error> {
|
||||
Err(Error::badf())
|
||||
}
|
||||
fn read_vectored(&self, bufs: &mut [io::IoSliceMut]) -> Result<u64, Error> {
|
||||
let n = self.0.as_file_view().read_vectored(bufs)?;
|
||||
Ok(n.try_into().map_err(|_| Error::range())?)
|
||||
}
|
||||
fn read_vectored_at(&self, _bufs: &mut [io::IoSliceMut], _offset: u64) -> Result<u64, Error> {
|
||||
Err(Error::seek_pipe())
|
||||
}
|
||||
fn write_vectored(&self, _bufs: &[io::IoSlice]) -> Result<u64, Error> {
|
||||
Err(Error::badf())
|
||||
}
|
||||
fn write_vectored_at(&self, _bufs: &[io::IoSlice], _offset: u64) -> Result<u64, Error> {
|
||||
Err(Error::badf())
|
||||
}
|
||||
fn seek(&self, _pos: std::io::SeekFrom) -> Result<u64, Error> {
|
||||
Err(Error::seek_pipe())
|
||||
}
|
||||
fn peek(&self, _buf: &mut [u8]) -> Result<u64, Error> {
|
||||
Err(Error::seek_pipe())
|
||||
}
|
||||
fn set_times(
|
||||
&self,
|
||||
atime: Option<wasi_c2::SystemTimeSpec>,
|
||||
mtime: Option<wasi_c2::SystemTimeSpec>,
|
||||
) -> Result<(), Error> {
|
||||
self.0
|
||||
.set_times(convert_systimespec(atime), convert_systimespec(mtime))?;
|
||||
Ok(())
|
||||
}
|
||||
fn num_ready_bytes(&self) -> Result<u64, Error> {
|
||||
Ok(self.0.num_ready_bytes()?)
|
||||
}
|
||||
}
|
||||
#[cfg(windows)]
|
||||
impl AsRawHandle for Stdin {
|
||||
fn as_raw_handle(&self) -> RawHandle {
|
||||
self.0.as_raw_handle()
|
||||
}
|
||||
}
|
||||
#[cfg(unix)]
|
||||
impl AsRawFd for Stdin {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.0.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! wasi_file_write_impl {
|
||||
($ty:ty) => {
|
||||
impl WasiFile for $ty {
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
fn datasync(&self) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
fn sync(&self) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
fn get_filetype(&self) -> Result<FileType, Error> {
|
||||
Ok(FileType::Unknown)
|
||||
}
|
||||
fn get_fdflags(&self) -> Result<FdFlags, Error> {
|
||||
Ok(FdFlags::APPEND)
|
||||
}
|
||||
fn set_fdflags(&mut self, _fdflags: FdFlags) -> Result<(), Error> {
|
||||
Err(Error::badf())
|
||||
}
|
||||
fn get_filestat(&self) -> Result<Filestat, Error> {
|
||||
let meta = self.0.as_file_view().metadata()?;
|
||||
Ok(Filestat {
|
||||
device_id: 0,
|
||||
inode: 0,
|
||||
filetype: self.get_filetype()?,
|
||||
nlink: 0,
|
||||
size: meta.len(),
|
||||
atim: meta.accessed().ok(),
|
||||
mtim: meta.modified().ok(),
|
||||
ctim: meta.created().ok(),
|
||||
})
|
||||
}
|
||||
fn set_filestat_size(&self, _size: u64) -> Result<(), Error> {
|
||||
Err(Error::badf())
|
||||
}
|
||||
fn advise(&self, _offset: u64, _len: u64, _advice: Advice) -> Result<(), Error> {
|
||||
Err(Error::badf())
|
||||
}
|
||||
fn allocate(&self, _offset: u64, _len: u64) -> Result<(), Error> {
|
||||
Err(Error::badf())
|
||||
}
|
||||
fn read_vectored(&self, _bufs: &mut [io::IoSliceMut]) -> Result<u64, Error> {
|
||||
Err(Error::badf())
|
||||
}
|
||||
fn read_vectored_at(
|
||||
&self,
|
||||
_bufs: &mut [io::IoSliceMut],
|
||||
_offset: u64,
|
||||
) -> Result<u64, Error> {
|
||||
Err(Error::badf())
|
||||
}
|
||||
fn write_vectored(&self, bufs: &[io::IoSlice]) -> Result<u64, Error> {
|
||||
let n = self.0.as_file_view().write_vectored(bufs)?;
|
||||
Ok(n.try_into().map_err(|c| Error::range().context(c))?)
|
||||
}
|
||||
fn write_vectored_at(&self, _bufs: &[io::IoSlice], _offset: u64) -> Result<u64, Error> {
|
||||
Err(Error::seek_pipe())
|
||||
}
|
||||
fn seek(&self, _pos: std::io::SeekFrom) -> Result<u64, Error> {
|
||||
Err(Error::seek_pipe())
|
||||
}
|
||||
fn peek(&self, _buf: &mut [u8]) -> Result<u64, Error> {
|
||||
Err(Error::badf())
|
||||
}
|
||||
fn set_times(
|
||||
&self,
|
||||
atime: Option<wasi_c2::SystemTimeSpec>,
|
||||
mtime: Option<wasi_c2::SystemTimeSpec>,
|
||||
) -> Result<(), Error> {
|
||||
self.0
|
||||
.set_times(convert_systimespec(atime), convert_systimespec(mtime))?;
|
||||
Ok(())
|
||||
}
|
||||
fn num_ready_bytes(&self) -> Result<u64, Error> {
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
#[cfg(windows)]
|
||||
impl AsRawHandle for $ty {
|
||||
fn as_raw_handle(&self) -> RawHandle {
|
||||
self.0.as_raw_handle()
|
||||
}
|
||||
}
|
||||
#[cfg(unix)]
|
||||
impl AsRawFd for $ty {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.0.as_raw_fd()
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub struct Stdout(std::io::Stdout);
|
||||
|
||||
pub fn stdout() -> Stdout {
|
||||
Stdout(std::io::stdout())
|
||||
}
|
||||
wasi_file_write_impl!(Stdout);
|
||||
|
||||
pub struct Stderr(std::io::Stderr);
|
||||
|
||||
pub fn stderr() -> Stderr {
|
||||
Stderr(std::io::stderr())
|
||||
}
|
||||
wasi_file_write_impl!(Stderr);
|
||||
Reference in New Issue
Block a user