move wasi-c2 into wasi-common
This commit is contained in:
22
crates/wasi-common/src/clocks.rs
Normal file
22
crates/wasi-common/src/clocks.rs
Normal file
@@ -0,0 +1,22 @@
|
||||
use cap_std::time::{Duration, Instant, SystemTime};
|
||||
|
||||
pub enum SystemTimeSpec {
|
||||
SymbolicNow,
|
||||
Absolute(SystemTime),
|
||||
}
|
||||
|
||||
pub trait WasiSystemClock {
|
||||
fn resolution(&self) -> Duration;
|
||||
fn now(&self, precision: Duration) -> SystemTime;
|
||||
}
|
||||
|
||||
pub trait WasiMonotonicClock {
|
||||
fn resolution(&self) -> Duration;
|
||||
fn now(&self, precision: Duration) -> Instant;
|
||||
}
|
||||
|
||||
pub struct WasiClocks {
|
||||
pub system: Box<dyn WasiSystemClock>,
|
||||
pub monotonic: Box<dyn WasiMonotonicClock>,
|
||||
pub creation_time: cap_std::time::Instant,
|
||||
}
|
||||
122
crates/wasi-common/src/ctx.rs
Normal file
122
crates/wasi-common/src/ctx.rs
Normal file
@@ -0,0 +1,122 @@
|
||||
use crate::clocks::WasiClocks;
|
||||
use crate::dir::{DirCaps, DirEntry, WasiDir};
|
||||
use crate::file::{FileCaps, FileEntry, WasiFile};
|
||||
use crate::sched::WasiSched;
|
||||
use crate::string_array::{StringArray, StringArrayError};
|
||||
use crate::table::Table;
|
||||
use crate::Error;
|
||||
use cap_rand::RngCore;
|
||||
use std::cell::{RefCell, RefMut};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::rc::Rc;
|
||||
|
||||
pub struct WasiCtx {
|
||||
pub args: StringArray,
|
||||
pub env: StringArray,
|
||||
pub random: RefCell<Box<dyn RngCore>>,
|
||||
pub clocks: WasiClocks,
|
||||
pub sched: Box<dyn WasiSched>,
|
||||
pub table: Rc<RefCell<Table>>,
|
||||
}
|
||||
|
||||
impl WasiCtx {
|
||||
pub fn builder(
|
||||
random: RefCell<Box<dyn RngCore>>,
|
||||
clocks: WasiClocks,
|
||||
sched: Box<dyn WasiSched>,
|
||||
table: Rc<RefCell<Table>>,
|
||||
) -> WasiCtxBuilder {
|
||||
WasiCtxBuilder(WasiCtx {
|
||||
args: StringArray::new(),
|
||||
env: StringArray::new(),
|
||||
random,
|
||||
clocks,
|
||||
sched,
|
||||
table,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn insert_file(&self, fd: u32, file: Box<dyn WasiFile>, caps: FileCaps) {
|
||||
self.table()
|
||||
.insert_at(fd, Box::new(FileEntry::new(caps, file)));
|
||||
}
|
||||
|
||||
pub fn insert_dir(
|
||||
&self,
|
||||
fd: u32,
|
||||
dir: Box<dyn WasiDir>,
|
||||
caps: DirCaps,
|
||||
file_caps: FileCaps,
|
||||
path: PathBuf,
|
||||
) {
|
||||
self.table().insert_at(
|
||||
fd,
|
||||
Box::new(DirEntry::new(caps, file_caps, Some(path), dir)),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn table(&self) -> RefMut<Table> {
|
||||
self.table.borrow_mut()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WasiCtxBuilder(WasiCtx);
|
||||
|
||||
impl WasiCtxBuilder {
|
||||
pub fn build(self) -> Result<WasiCtx, Error> {
|
||||
Ok(self.0)
|
||||
}
|
||||
|
||||
pub fn arg(mut self, arg: &str) -> Result<Self, StringArrayError> {
|
||||
self.0.args.push(arg.to_owned())?;
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
pub fn env(mut self, var: &str, value: &str) -> Result<Self, StringArrayError> {
|
||||
self.0.env.push(format!("{}={}", var, value))?;
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
pub fn stdin(self, f: Box<dyn WasiFile>) -> Self {
|
||||
self.0.insert_file(
|
||||
0,
|
||||
f,
|
||||
FileCaps::READ | FileCaps::POLL_READWRITE, // XXX fixme: more rights are ok, but this is read-only
|
||||
);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn stdout(self, f: Box<dyn WasiFile>) -> Self {
|
||||
self.0.insert_file(
|
||||
1,
|
||||
f,
|
||||
FileCaps::WRITE | FileCaps::POLL_READWRITE, // XXX fixme: more rights are ok, but this is append only
|
||||
);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn stderr(self, f: Box<dyn WasiFile>) -> Self {
|
||||
self.0.insert_file(
|
||||
2,
|
||||
f,
|
||||
FileCaps::WRITE | FileCaps::POLL_READWRITE, // XXX fixme: more rights are ok, but this is append only
|
||||
);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn preopened_dir(
|
||||
self,
|
||||
dir: Box<dyn WasiDir>,
|
||||
path: impl AsRef<Path>,
|
||||
) -> Result<Self, Error> {
|
||||
let caps = DirCaps::all();
|
||||
let file_caps = FileCaps::all();
|
||||
self.0.table().push(Box::new(DirEntry::new(
|
||||
caps,
|
||||
file_caps,
|
||||
Some(path.as_ref().to_owned()),
|
||||
dir,
|
||||
)))?;
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
182
crates/wasi-common/src/dir.rs
Normal file
182
crates/wasi-common/src/dir.rs
Normal file
@@ -0,0 +1,182 @@
|
||||
use crate::file::{FdFlags, FileCaps, FileType, Filestat, OFlags, WasiFile};
|
||||
use crate::{Error, ErrorExt, SystemTimeSpec};
|
||||
use bitflags::bitflags;
|
||||
use std::any::Any;
|
||||
use std::cell::Ref;
|
||||
use std::ops::Deref;
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub trait WasiDir {
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
fn open_file(
|
||||
&self,
|
||||
symlink_follow: bool,
|
||||
path: &str,
|
||||
oflags: OFlags,
|
||||
caps: FileCaps,
|
||||
fdflags: FdFlags,
|
||||
) -> Result<Box<dyn WasiFile>, Error>;
|
||||
fn open_dir(&self, symlink_follow: bool, path: &str) -> Result<Box<dyn WasiDir>, Error>;
|
||||
fn create_dir(&self, path: &str) -> Result<(), Error>;
|
||||
fn readdir(
|
||||
&self,
|
||||
cursor: ReaddirCursor,
|
||||
) -> Result<Box<dyn Iterator<Item = Result<(ReaddirEntity, String), Error>>>, Error>;
|
||||
fn symlink(&self, old_path: &str, new_path: &str) -> Result<(), Error>;
|
||||
fn remove_dir(&self, path: &str) -> Result<(), Error>;
|
||||
fn unlink_file(&self, path: &str) -> Result<(), Error>;
|
||||
fn read_link(&self, path: &str) -> Result<PathBuf, Error>;
|
||||
fn get_filestat(&self) -> Result<Filestat, Error>;
|
||||
fn get_path_filestat(&self, path: &str, follow_symlinks: bool) -> Result<Filestat, Error>;
|
||||
fn rename(&self, path: &str, dest_dir: &dyn WasiDir, dest_path: &str) -> Result<(), Error>;
|
||||
fn hard_link(
|
||||
&self,
|
||||
path: &str,
|
||||
target_dir: &dyn WasiDir,
|
||||
target_path: &str,
|
||||
) -> Result<(), Error>;
|
||||
fn set_times(
|
||||
&self,
|
||||
path: &str,
|
||||
atime: Option<SystemTimeSpec>,
|
||||
mtime: Option<SystemTimeSpec>,
|
||||
follow_symlinks: bool,
|
||||
) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
pub(crate) struct DirEntry {
|
||||
caps: DirCaps,
|
||||
file_caps: FileCaps,
|
||||
preopen_path: Option<PathBuf>, // precondition: PathBuf is valid unicode
|
||||
dir: Box<dyn WasiDir>,
|
||||
}
|
||||
|
||||
impl DirEntry {
|
||||
pub fn new(
|
||||
caps: DirCaps,
|
||||
file_caps: FileCaps,
|
||||
preopen_path: Option<PathBuf>,
|
||||
dir: Box<dyn WasiDir>,
|
||||
) -> Self {
|
||||
DirEntry {
|
||||
caps,
|
||||
file_caps,
|
||||
preopen_path,
|
||||
dir,
|
||||
}
|
||||
}
|
||||
pub fn capable_of_dir(&self, caps: DirCaps) -> Result<(), Error> {
|
||||
if self.caps.contains(caps) {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::not_capable().context(format!("desired {:?}, has {:?}", caps, self.caps,)))
|
||||
}
|
||||
}
|
||||
pub fn capable_of_file(&self, caps: FileCaps) -> Result<(), Error> {
|
||||
if self.file_caps.contains(caps) {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::not_capable()
|
||||
.context(format!("desired {:?}, has {:?}", caps, self.file_caps)))
|
||||
}
|
||||
}
|
||||
pub fn drop_caps_to(&mut self, caps: DirCaps, file_caps: FileCaps) -> Result<(), Error> {
|
||||
self.capable_of_dir(caps)?;
|
||||
self.capable_of_file(file_caps)?;
|
||||
self.caps = caps;
|
||||
self.file_caps = file_caps;
|
||||
Ok(())
|
||||
}
|
||||
pub fn child_dir_caps(&self, desired_caps: DirCaps) -> DirCaps {
|
||||
self.caps & desired_caps
|
||||
}
|
||||
pub fn child_file_caps(&self, desired_caps: FileCaps) -> FileCaps {
|
||||
self.file_caps & desired_caps
|
||||
}
|
||||
pub fn get_dir_fdstat(&self) -> DirFdStat {
|
||||
DirFdStat {
|
||||
dir_caps: self.caps,
|
||||
file_caps: self.file_caps,
|
||||
}
|
||||
}
|
||||
pub fn preopen_path(&self) -> &Option<PathBuf> {
|
||||
&self.preopen_path
|
||||
}
|
||||
}
|
||||
|
||||
pub trait DirEntryExt<'a> {
|
||||
fn get_cap(self, caps: DirCaps) -> Result<Ref<'a, dyn WasiDir>, Error>;
|
||||
}
|
||||
|
||||
impl<'a> DirEntryExt<'a> for Ref<'a, DirEntry> {
|
||||
fn get_cap(self, caps: DirCaps) -> Result<Ref<'a, dyn WasiDir>, Error> {
|
||||
self.capable_of_dir(caps)?;
|
||||
Ok(Ref::map(self, |r| r.dir.deref()))
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
pub struct DirCaps: u32 {
|
||||
const CREATE_DIRECTORY = 0b1;
|
||||
const CREATE_FILE = 0b10;
|
||||
const LINK_SOURCE = 0b100;
|
||||
const LINK_TARGET = 0b1000;
|
||||
const OPEN = 0b10000;
|
||||
const READDIR = 0b100000;
|
||||
const READLINK = 0b1000000;
|
||||
const RENAME_SOURCE = 0b10000000;
|
||||
const RENAME_TARGET = 0b100000000;
|
||||
const SYMLINK = 0b1000000000;
|
||||
const REMOVE_DIRECTORY = 0b10000000000;
|
||||
const UNLINK_FILE = 0b100000000000;
|
||||
const PATH_FILESTAT_GET = 0b1000000000000;
|
||||
const PATH_FILESTAT_SET_TIMES = 0b10000000000000;
|
||||
const FILESTAT_GET = 0b100000000000000;
|
||||
const FILESTAT_SET_TIMES = 0b1000000000000000;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DirFdStat {
|
||||
pub file_caps: FileCaps,
|
||||
pub dir_caps: DirCaps,
|
||||
}
|
||||
|
||||
pub(crate) trait TableDirExt {
|
||||
fn get_dir(&self, fd: u32) -> Result<Ref<DirEntry>, Error>;
|
||||
fn is_preopen(&self, fd: u32) -> bool;
|
||||
}
|
||||
|
||||
impl TableDirExt for crate::table::Table {
|
||||
fn get_dir(&self, fd: u32) -> Result<Ref<DirEntry>, Error> {
|
||||
self.get(fd)
|
||||
}
|
||||
fn is_preopen(&self, fd: u32) -> bool {
|
||||
if self.is::<DirEntry>(fd) {
|
||||
let dir_entry: std::cell::Ref<DirEntry> = self.get(fd).unwrap();
|
||||
dir_entry.preopen_path.is_some()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ReaddirEntity {
|
||||
pub next: ReaddirCursor,
|
||||
pub inode: u64,
|
||||
pub namelen: u32,
|
||||
pub filetype: FileType,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct ReaddirCursor(u64);
|
||||
impl From<u64> for ReaddirCursor {
|
||||
fn from(c: u64) -> ReaddirCursor {
|
||||
ReaddirCursor(c)
|
||||
}
|
||||
}
|
||||
impl From<ReaddirCursor> for u64 {
|
||||
fn from(c: ReaddirCursor) -> u64 {
|
||||
c.0
|
||||
}
|
||||
}
|
||||
110
crates/wasi-common/src/error.rs
Normal file
110
crates/wasi-common/src/error.rs
Normal file
@@ -0,0 +1,110 @@
|
||||
pub use anyhow::Error;
|
||||
|
||||
/// Internal error type for the `wasi-common` crate.
|
||||
/// Contains variants of the WASI `$errno` type are added according to what is actually used internally by
|
||||
/// the crate. Not all values are represented presently.
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum ErrorKind {
|
||||
/// Errno::TooBig: Argument list too long
|
||||
#[error("TooBig: Argument list too long")]
|
||||
TooBig,
|
||||
/// Errno::Badf: Bad file descriptor
|
||||
#[error("Badf: Bad file descriptor")]
|
||||
Badf,
|
||||
/// Errno::Exist: File exists
|
||||
#[error("Exist: File exists")]
|
||||
Exist,
|
||||
/// Errno::Ilseq: Illegal byte sequence
|
||||
#[error("Ilseq: Illegal byte sequence")]
|
||||
Ilseq,
|
||||
/// Errno::Inval: Invalid argument
|
||||
#[error("Inval: Invalid argument")]
|
||||
Inval,
|
||||
/// Errno::Io: I/O error
|
||||
#[error("Io: I/O error")]
|
||||
Io,
|
||||
/// Errno::Nametoolong: Filename too long
|
||||
#[error("Nametoolong: Filename too long")]
|
||||
Nametoolong,
|
||||
/// Errno::Notdir: Not a directory or a symbolic link to a directory.
|
||||
#[error("Notdir: Not a directory or a symbolic link to a directory")]
|
||||
Notdir,
|
||||
/// Errno::Notsup: Not supported, or operation not supported on socket.
|
||||
#[error("Notsup: Not supported, or operation not supported on socket")]
|
||||
Notsup,
|
||||
/// Errno::Overflow: Value too large to be stored in data type.
|
||||
#[error("Overflow: Value too large to be stored in data type")]
|
||||
Overflow,
|
||||
/// Errno::Range: Result too large
|
||||
#[error("Range: Result too large")]
|
||||
Range,
|
||||
/// Errno::Spipe: Invalid seek
|
||||
#[error("Spipe: Invalid seek")]
|
||||
Spipe,
|
||||
/// Errno::NotCapable: Not capable
|
||||
#[error("Not capable")]
|
||||
NotCapable,
|
||||
}
|
||||
|
||||
pub trait ErrorExt {
|
||||
fn trap(msg: impl Into<String>) -> Self;
|
||||
fn too_big() -> Self;
|
||||
fn badf() -> Self;
|
||||
fn exist() -> Self;
|
||||
fn illegal_byte_sequence() -> Self;
|
||||
fn invalid_argument() -> Self;
|
||||
fn io() -> Self;
|
||||
fn name_too_long() -> Self;
|
||||
fn not_dir() -> Self;
|
||||
fn not_supported() -> Self;
|
||||
fn overflow() -> Self;
|
||||
fn range() -> Self;
|
||||
fn seek_pipe() -> Self;
|
||||
fn not_capable() -> Self;
|
||||
}
|
||||
|
||||
impl ErrorExt for Error {
|
||||
fn trap(msg: impl Into<String>) -> Self {
|
||||
anyhow::anyhow!(msg.into())
|
||||
}
|
||||
fn too_big() -> Self {
|
||||
ErrorKind::TooBig.into()
|
||||
}
|
||||
fn badf() -> Self {
|
||||
ErrorKind::Badf.into()
|
||||
}
|
||||
fn exist() -> Self {
|
||||
ErrorKind::Exist.into()
|
||||
}
|
||||
fn illegal_byte_sequence() -> Self {
|
||||
ErrorKind::Ilseq.into()
|
||||
}
|
||||
fn invalid_argument() -> Self {
|
||||
ErrorKind::Inval.into()
|
||||
}
|
||||
fn io() -> Self {
|
||||
ErrorKind::Io.into()
|
||||
}
|
||||
fn name_too_long() -> Self {
|
||||
ErrorKind::Nametoolong.into()
|
||||
}
|
||||
fn not_dir() -> Self {
|
||||
ErrorKind::Notdir.into()
|
||||
}
|
||||
fn not_supported() -> Self {
|
||||
ErrorKind::Notsup.into()
|
||||
}
|
||||
fn overflow() -> Self {
|
||||
ErrorKind::Overflow.into()
|
||||
}
|
||||
fn range() -> Self {
|
||||
ErrorKind::Range.into()
|
||||
}
|
||||
fn seek_pipe() -> Self {
|
||||
ErrorKind::Spipe.into()
|
||||
}
|
||||
fn not_capable() -> Self {
|
||||
ErrorKind::NotCapable.into()
|
||||
}
|
||||
}
|
||||
172
crates/wasi-common/src/file.rs
Normal file
172
crates/wasi-common/src/file.rs
Normal file
@@ -0,0 +1,172 @@
|
||||
use crate::{Error, ErrorExt, SystemTimeSpec};
|
||||
use bitflags::bitflags;
|
||||
use std::any::Any;
|
||||
use std::cell::{Ref, RefMut};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
pub trait WasiFile {
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
fn datasync(&self) -> Result<(), Error>; // write op
|
||||
fn sync(&self) -> Result<(), Error>; // file op
|
||||
fn get_filetype(&self) -> Result<FileType, Error>; // file op
|
||||
fn get_fdflags(&self) -> Result<FdFlags, Error>; // file op
|
||||
fn set_fdflags(&mut self, flags: FdFlags) -> Result<(), Error>; // file op
|
||||
fn get_filestat(&self) -> Result<Filestat, Error>; // split out get_length as a read & write op, rest is a file op
|
||||
fn set_filestat_size(&self, _size: u64) -> Result<(), Error>; // write op
|
||||
fn advise(
|
||||
&self,
|
||||
offset: u64,
|
||||
len: u64,
|
||||
advice: system_interface::fs::Advice,
|
||||
) -> Result<(), Error>; // file op
|
||||
fn allocate(&self, offset: u64, len: u64) -> Result<(), Error>; // write op
|
||||
fn set_times(
|
||||
&self,
|
||||
atime: Option<SystemTimeSpec>,
|
||||
mtime: Option<SystemTimeSpec>,
|
||||
) -> Result<(), Error>;
|
||||
fn read_vectored(&self, bufs: &mut [std::io::IoSliceMut]) -> Result<u64, Error>; // read op
|
||||
fn read_vectored_at(&self, bufs: &mut [std::io::IoSliceMut], offset: u64)
|
||||
-> Result<u64, Error>; // file op
|
||||
fn write_vectored(&self, bufs: &[std::io::IoSlice]) -> Result<u64, Error>; // write op
|
||||
fn write_vectored_at(&self, bufs: &[std::io::IoSlice], offset: u64) -> Result<u64, Error>; // file op
|
||||
fn seek(&self, pos: std::io::SeekFrom) -> Result<u64, Error>; // file op that generates a new stream from a file will supercede this
|
||||
fn peek(&self, buf: &mut [u8]) -> Result<u64, Error>; // read op
|
||||
fn num_ready_bytes(&self) -> Result<u64, Error>; // read op
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum FileType {
|
||||
Unknown,
|
||||
BlockDevice,
|
||||
CharacterDevice,
|
||||
Directory,
|
||||
RegularFile,
|
||||
SocketDgram,
|
||||
SocketStream,
|
||||
SymbolicLink,
|
||||
Pipe,
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
pub struct FdFlags: u32 {
|
||||
const APPEND = 0b1;
|
||||
const DSYNC = 0b10;
|
||||
const NONBLOCK = 0b100;
|
||||
const RSYNC = 0b1000;
|
||||
const SYNC = 0b10000;
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
pub struct OFlags: u32 {
|
||||
const CREATE = 0b1;
|
||||
const DIRECTORY = 0b10;
|
||||
const EXCLUSIVE = 0b100;
|
||||
const TRUNCATE = 0b1000;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Filestat {
|
||||
pub device_id: u64,
|
||||
pub inode: u64,
|
||||
pub filetype: FileType,
|
||||
pub nlink: u64,
|
||||
pub size: u64, // this is a read field, the rest are file fields
|
||||
pub atim: Option<std::time::SystemTime>,
|
||||
pub mtim: Option<std::time::SystemTime>,
|
||||
pub ctim: Option<std::time::SystemTime>,
|
||||
}
|
||||
|
||||
pub(crate) trait TableFileExt {
|
||||
fn get_file(&self, fd: u32) -> Result<Ref<FileEntry>, Error>;
|
||||
fn get_file_mut(&self, fd: u32) -> Result<RefMut<FileEntry>, Error>;
|
||||
}
|
||||
impl TableFileExt for crate::table::Table {
|
||||
fn get_file(&self, fd: u32) -> Result<Ref<FileEntry>, Error> {
|
||||
self.get(fd)
|
||||
}
|
||||
fn get_file_mut(&self, fd: u32) -> Result<RefMut<FileEntry>, Error> {
|
||||
self.get_mut(fd)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct FileEntry {
|
||||
caps: FileCaps,
|
||||
file: Box<dyn WasiFile>,
|
||||
}
|
||||
|
||||
impl FileEntry {
|
||||
pub fn new(caps: FileCaps, file: Box<dyn WasiFile>) -> Self {
|
||||
FileEntry { caps, file }
|
||||
}
|
||||
|
||||
pub fn capable_of(&self, caps: FileCaps) -> Result<(), Error> {
|
||||
if self.caps.contains(caps) {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::not_capable().context(format!("desired {:?}, has {:?}", caps, self.caps,)))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn drop_caps_to(&mut self, caps: FileCaps) -> Result<(), Error> {
|
||||
self.capable_of(caps)?;
|
||||
self.caps = caps;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_fdstat(&self) -> Result<FdStat, Error> {
|
||||
Ok(FdStat {
|
||||
filetype: self.file.get_filetype()?,
|
||||
caps: self.caps,
|
||||
flags: self.file.get_fdflags()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub trait FileEntryExt<'a> {
|
||||
fn get_cap(self, caps: FileCaps) -> Result<Ref<'a, dyn WasiFile>, Error>;
|
||||
}
|
||||
|
||||
impl<'a> FileEntryExt<'a> for Ref<'a, FileEntry> {
|
||||
fn get_cap(self, caps: FileCaps) -> Result<Ref<'a, dyn WasiFile>, Error> {
|
||||
self.capable_of(caps)?;
|
||||
Ok(Ref::map(self, |r| r.file.deref()))
|
||||
}
|
||||
}
|
||||
pub trait FileEntryMutExt<'a> {
|
||||
fn get_cap(self, caps: FileCaps) -> Result<RefMut<'a, dyn WasiFile>, Error>;
|
||||
}
|
||||
|
||||
impl<'a> FileEntryMutExt<'a> for RefMut<'a, FileEntry> {
|
||||
fn get_cap(self, caps: FileCaps) -> Result<RefMut<'a, dyn WasiFile>, Error> {
|
||||
self.capable_of(caps)?;
|
||||
Ok(RefMut::map(self, |r| r.file.deref_mut()))
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
pub struct FileCaps : u32 {
|
||||
const DATASYNC = 0b1;
|
||||
const READ = 0b10;
|
||||
const SEEK = 0b100;
|
||||
const FDSTAT_SET_FLAGS = 0b1000;
|
||||
const SYNC = 0b10000;
|
||||
const TELL = 0b100000;
|
||||
const WRITE = 0b1000000;
|
||||
const ADVISE = 0b10000000;
|
||||
const ALLOCATE = 0b100000000;
|
||||
const FILESTAT_GET = 0b1000000000;
|
||||
const FILESTAT_SET_SIZE = 0b10000000000;
|
||||
const FILESTAT_SET_TIMES = 0b100000000000;
|
||||
const POLL_READWRITE = 0b1000000000000;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FdStat {
|
||||
pub filetype: FileType,
|
||||
pub caps: FileCaps,
|
||||
pub flags: FdFlags,
|
||||
}
|
||||
20
crates/wasi-common/src/lib.rs
Normal file
20
crates/wasi-common/src/lib.rs
Normal file
@@ -0,0 +1,20 @@
|
||||
#![cfg_attr(feature = "nightly", feature(windows_by_handle))]
|
||||
|
||||
pub mod clocks;
|
||||
mod ctx;
|
||||
pub mod dir;
|
||||
mod error;
|
||||
pub mod file;
|
||||
pub mod pipe;
|
||||
pub mod random;
|
||||
pub mod sched;
|
||||
pub mod snapshots;
|
||||
mod string_array;
|
||||
pub mod table;
|
||||
|
||||
pub use clocks::SystemTimeSpec;
|
||||
pub use ctx::{WasiCtx, WasiCtxBuilder};
|
||||
pub use dir::{DirCaps, ReaddirCursor, ReaddirEntity, WasiDir};
|
||||
pub use error::{Error, ErrorExt, ErrorKind};
|
||||
pub use file::{FdFlags, FileCaps, Filestat, OFlags, WasiFile};
|
||||
pub use string_array::StringArrayError;
|
||||
306
crates/wasi-common/src/pipe.rs
Normal file
306
crates/wasi-common/src/pipe.rs
Normal file
@@ -0,0 +1,306 @@
|
||||
// This is mostly stubs
|
||||
#![allow(unused_variables, dead_code)]
|
||||
//! Virtual pipes.
|
||||
//!
|
||||
//! These types provide easy implementations of `WasiFile` that mimic much of the behavior of Unix
|
||||
//! pipes. These are particularly helpful for redirecting WASI stdio handles to destinations other
|
||||
//! than OS files.
|
||||
//!
|
||||
//! Some convenience constructors are included for common backing types like `Vec<u8>` and `String`,
|
||||
//! but the virtual pipes can be instantiated with any `Read` or `Write` type.
|
||||
//!
|
||||
use crate::{
|
||||
file::{FdFlags, FileType, Filestat, WasiFile},
|
||||
Error, ErrorExt, SystemTimeSpec,
|
||||
};
|
||||
use std::any::Any;
|
||||
use std::convert::TryInto;
|
||||
use std::io::{self, Read, Write};
|
||||
use std::sync::{Arc, RwLock};
|
||||
use system_interface::fs::Advice;
|
||||
|
||||
/// A virtual pipe read end.
|
||||
///
|
||||
/// A variety of `From` impls are provided so that common pipe types are easy to create. For example:
|
||||
///
|
||||
/// ```
|
||||
/// # use wasi_c2::WasiCtx;
|
||||
/// # use wasi_c2::virt::pipe::ReadPipe;
|
||||
/// let stdin = ReadPipe::from("hello from stdin!");
|
||||
/// let ctx = WasiCtx::builder().stdin(Box::new(stdin)).build();
|
||||
/// ```
|
||||
#[derive(Debug)]
|
||||
pub struct ReadPipe<R: Read> {
|
||||
reader: Arc<RwLock<R>>,
|
||||
}
|
||||
|
||||
impl<R: Read> Clone for ReadPipe<R> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
reader: self.reader.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Read> ReadPipe<R> {
|
||||
/// Create a new pipe from a `Read` type.
|
||||
///
|
||||
/// All `Handle` read operations delegate to reading from this underlying reader.
|
||||
pub fn new(r: R) -> Self {
|
||||
Self::from_shared(Arc::new(RwLock::new(r)))
|
||||
}
|
||||
|
||||
/// Create a new pipe from a shareable `Read` type.
|
||||
///
|
||||
/// All `Handle` read operations delegate to reading from this underlying reader.
|
||||
pub fn from_shared(reader: Arc<RwLock<R>>) -> Self {
|
||||
Self { reader }
|
||||
}
|
||||
|
||||
/// Try to convert this `ReadPipe<R>` back to the underlying `R` type.
|
||||
///
|
||||
/// This will fail with `Err(self)` if multiple references to the underlying `R` exist.
|
||||
pub fn try_into_inner(mut self) -> Result<R, Self> {
|
||||
match Arc::try_unwrap(self.reader) {
|
||||
Ok(rc) => Ok(RwLock::into_inner(rc).unwrap()),
|
||||
Err(reader) => {
|
||||
self.reader = reader;
|
||||
Err(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
fn borrow(&self) -> std::sync::RwLockWriteGuard<R> {
|
||||
RwLock::write(&self.reader).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<u8>> for ReadPipe<io::Cursor<Vec<u8>>> {
|
||||
fn from(r: Vec<u8>) -> Self {
|
||||
Self::new(io::Cursor::new(r))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&[u8]> for ReadPipe<io::Cursor<Vec<u8>>> {
|
||||
fn from(r: &[u8]) -> Self {
|
||||
Self::from(r.to_vec())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for ReadPipe<io::Cursor<String>> {
|
||||
fn from(r: String) -> Self {
|
||||
Self::new(io::Cursor::new(r))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for ReadPipe<io::Cursor<String>> {
|
||||
fn from(r: &str) -> Self {
|
||||
Self::from(r.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Read + Any> WasiFile for ReadPipe<R> {
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
fn datasync(&self) -> Result<(), Error> {
|
||||
Ok(()) // trivial: no implementation needed
|
||||
}
|
||||
fn sync(&self) -> Result<(), Error> {
|
||||
Ok(()) // trivial
|
||||
}
|
||||
fn get_filetype(&self) -> Result<FileType, Error> {
|
||||
Ok(FileType::Pipe)
|
||||
}
|
||||
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> {
|
||||
Ok(Filestat {
|
||||
device_id: 0,
|
||||
inode: 0,
|
||||
filetype: self.get_filetype()?,
|
||||
nlink: 0,
|
||||
size: 0, // XXX no way to get a size out of a Read :(
|
||||
atim: None,
|
||||
mtim: None,
|
||||
ctim: None,
|
||||
})
|
||||
}
|
||||
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.borrow().read_vectored(bufs)?;
|
||||
Ok(n.try_into()?)
|
||||
}
|
||||
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> {
|
||||
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::badf())
|
||||
}
|
||||
fn peek(&self, buf: &mut [u8]) -> Result<u64, Error> {
|
||||
Err(Error::badf())
|
||||
}
|
||||
fn set_times(
|
||||
&self,
|
||||
atime: Option<SystemTimeSpec>,
|
||||
mtime: Option<SystemTimeSpec>,
|
||||
) -> Result<(), Error> {
|
||||
Err(Error::badf())
|
||||
}
|
||||
fn num_ready_bytes(&self) -> Result<u64, Error> {
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
|
||||
/// A virtual pipe write end.
|
||||
///
|
||||
/// ```
|
||||
/// # use wasi_c2::WasiCtx;
|
||||
/// # use wasi_c2::virt::pipe::WritePipe;
|
||||
/// let stdout = WritePipe::new_in_memory();
|
||||
/// let ctx = WasiCtx::builder().stdout(Box::new(stdout.clone())).build();
|
||||
/// // use ctx in an instance, then make sure it is dropped:
|
||||
/// drop(ctx);
|
||||
/// let contents: Vec<u8> = stdout.try_into_inner().expect("sole remaining reference to WritePipe").into_inner();
|
||||
/// println!("contents of stdout: {:?}", contents);
|
||||
/// ```
|
||||
#[derive(Debug)]
|
||||
pub struct WritePipe<W: Write> {
|
||||
writer: Arc<RwLock<W>>,
|
||||
}
|
||||
|
||||
impl<W: Write> Clone for WritePipe<W> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
writer: self.writer.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> WritePipe<W> {
|
||||
/// Create a new pipe from a `Write` type.
|
||||
///
|
||||
/// All `Handle` write operations delegate to writing to this underlying writer.
|
||||
pub fn new(w: W) -> Self {
|
||||
Self::from_shared(Arc::new(RwLock::new(w)))
|
||||
}
|
||||
|
||||
/// Create a new pipe from a shareable `Write` type.
|
||||
///
|
||||
/// All `Handle` write operations delegate to writing to this underlying writer.
|
||||
pub fn from_shared(writer: Arc<RwLock<W>>) -> Self {
|
||||
Self { writer }
|
||||
}
|
||||
|
||||
/// Try to convert this `WritePipe<W>` back to the underlying `W` type.
|
||||
///
|
||||
/// This will fail with `Err(self)` if multiple references to the underlying `W` exist.
|
||||
pub fn try_into_inner(mut self) -> Result<W, Self> {
|
||||
match Arc::try_unwrap(self.writer) {
|
||||
Ok(rc) => Ok(RwLock::into_inner(rc).unwrap()),
|
||||
Err(writer) => {
|
||||
self.writer = writer;
|
||||
Err(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn borrow(&self) -> std::sync::RwLockWriteGuard<W> {
|
||||
RwLock::write(&self.writer).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl WritePipe<io::Cursor<Vec<u8>>> {
|
||||
/// Create a new writable virtual pipe backed by a `Vec<u8>` buffer.
|
||||
pub fn new_in_memory() -> Self {
|
||||
Self::new(io::Cursor::new(vec![]))
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write + Any> WasiFile for WritePipe<W> {
|
||||
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::Pipe)
|
||||
}
|
||||
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> {
|
||||
Ok(Filestat {
|
||||
device_id: 0,
|
||||
inode: 0,
|
||||
filetype: self.get_filetype()?,
|
||||
nlink: 0,
|
||||
size: 0, // XXX no way to get a size out of a Write :(
|
||||
atim: None,
|
||||
mtim: None,
|
||||
ctim: None,
|
||||
})
|
||||
}
|
||||
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.borrow().write_vectored(bufs)?;
|
||||
Ok(n.try_into()?)
|
||||
}
|
||||
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::badf())
|
||||
}
|
||||
fn peek(&self, buf: &mut [u8]) -> Result<u64, Error> {
|
||||
Err(Error::badf())
|
||||
}
|
||||
fn set_times(
|
||||
&self,
|
||||
atime: Option<SystemTimeSpec>,
|
||||
mtime: Option<SystemTimeSpec>,
|
||||
) -> Result<(), Error> {
|
||||
Err(Error::badf())
|
||||
}
|
||||
fn num_ready_bytes(&self) -> Result<u64, Error> {
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
46
crates/wasi-common/src/random.rs
Normal file
46
crates/wasi-common/src/random.rs
Normal file
@@ -0,0 +1,46 @@
|
||||
use cap_rand::RngCore;
|
||||
|
||||
/// Implement `WasiRandom` using a deterministic cycle of bytes.
|
||||
pub struct Deterministic {
|
||||
cycle: std::iter::Cycle<std::vec::IntoIter<u8>>,
|
||||
}
|
||||
|
||||
impl Deterministic {
|
||||
pub fn new(bytes: Vec<u8>) -> Self {
|
||||
Deterministic {
|
||||
cycle: bytes.into_iter().cycle(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RngCore for Deterministic {
|
||||
fn next_u32(&mut self) -> u32 {
|
||||
todo!()
|
||||
}
|
||||
fn next_u64(&mut self) -> u64 {
|
||||
todo!()
|
||||
}
|
||||
fn fill_bytes(&mut self, buf: &mut [u8]) {
|
||||
for b in buf.iter_mut() {
|
||||
*b = self.cycle.next().expect("infinite sequence");
|
||||
}
|
||||
}
|
||||
fn try_fill_bytes(&mut self, buf: &mut [u8]) -> Result<(), cap_rand::Error> {
|
||||
self.fill_bytes(buf);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn deterministic() {
|
||||
let mut det = Deterministic::new(vec![1, 2, 3, 4]);
|
||||
let mut buf = vec![0; 1024];
|
||||
det.try_fill_bytes(&mut buf).expect("get randomness");
|
||||
for (ix, b) in buf.iter().enumerate() {
|
||||
assert_eq!(*b, (ix % 4) as u8 + 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
87
crates/wasi-common/src/sched.rs
Normal file
87
crates/wasi-common/src/sched.rs
Normal file
@@ -0,0 +1,87 @@
|
||||
use crate::clocks::WasiMonotonicClock;
|
||||
use crate::file::WasiFile;
|
||||
use crate::Error;
|
||||
use cap_std::time::{Duration, Instant};
|
||||
use std::cell::Ref;
|
||||
pub mod subscription;
|
||||
|
||||
use subscription::{MonotonicClockSubscription, RwSubscription, Subscription, SubscriptionResult};
|
||||
|
||||
pub trait WasiSched {
|
||||
fn poll_oneoff(&self, poll: &Poll) -> Result<(), Error>;
|
||||
fn sched_yield(&self) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
pub struct Userdata(u64);
|
||||
impl From<u64> for Userdata {
|
||||
fn from(u: u64) -> Userdata {
|
||||
Userdata(u)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Userdata> for u64 {
|
||||
fn from(u: Userdata) -> u64 {
|
||||
u.0
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Poll<'a> {
|
||||
subs: Vec<(Subscription<'a>, Userdata)>,
|
||||
}
|
||||
|
||||
impl<'a> Poll<'a> {
|
||||
pub fn new() -> Self {
|
||||
Self { subs: Vec::new() }
|
||||
}
|
||||
pub fn subscribe_monotonic_clock(
|
||||
&mut self,
|
||||
clock: &'a dyn WasiMonotonicClock,
|
||||
deadline: Instant,
|
||||
precision: Duration,
|
||||
ud: Userdata,
|
||||
) {
|
||||
self.subs.push((
|
||||
Subscription::MonotonicClock(MonotonicClockSubscription {
|
||||
clock,
|
||||
deadline,
|
||||
precision,
|
||||
}),
|
||||
ud,
|
||||
));
|
||||
}
|
||||
pub fn subscribe_read(&mut self, file: Ref<'a, dyn WasiFile>, ud: Userdata) {
|
||||
self.subs
|
||||
.push((Subscription::Read(RwSubscription::new(file)), ud));
|
||||
}
|
||||
pub fn subscribe_write(&mut self, file: Ref<'a, dyn WasiFile>, ud: Userdata) {
|
||||
self.subs
|
||||
.push((Subscription::Write(RwSubscription::new(file)), ud));
|
||||
}
|
||||
pub fn results(self) -> Vec<(SubscriptionResult, Userdata)> {
|
||||
self.subs
|
||||
.into_iter()
|
||||
.filter_map(|(s, ud)| SubscriptionResult::from_subscription(s).map(|r| (r, ud)))
|
||||
.collect()
|
||||
}
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.subs.is_empty()
|
||||
}
|
||||
pub fn earliest_clock_deadline(&'a self) -> Option<&MonotonicClockSubscription<'a>> {
|
||||
let mut subs = self
|
||||
.subs
|
||||
.iter()
|
||||
.filter_map(|(s, _ud)| match s {
|
||||
Subscription::MonotonicClock(t) => Some(t),
|
||||
_ => None,
|
||||
})
|
||||
.collect::<Vec<&MonotonicClockSubscription<'a>>>();
|
||||
subs.sort_by(|a, b| a.deadline.cmp(&b.deadline));
|
||||
subs.into_iter().next() // First element is earliest
|
||||
}
|
||||
pub fn rw_subscriptions(&'a self) -> impl Iterator<Item = &Subscription<'a>> {
|
||||
self.subs.iter().filter_map(|(s, _ud)| match s {
|
||||
Subscription::Read { .. } | Subscription::Write { .. } => Some(s),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
}
|
||||
79
crates/wasi-common/src/sched/subscription.rs
Normal file
79
crates/wasi-common/src/sched/subscription.rs
Normal file
@@ -0,0 +1,79 @@
|
||||
use crate::clocks::WasiMonotonicClock;
|
||||
use crate::file::WasiFile;
|
||||
use crate::Error;
|
||||
use bitflags::bitflags;
|
||||
use cap_std::time::{Duration, Instant};
|
||||
use std::cell::{Cell, Ref};
|
||||
|
||||
bitflags! {
|
||||
pub struct RwEventFlags: u32 {
|
||||
const HANGUP = 0b1;
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RwSubscription<'a> {
|
||||
pub file: Ref<'a, dyn WasiFile>,
|
||||
status: Cell<Option<Result<(u64, RwEventFlags), Error>>>,
|
||||
}
|
||||
|
||||
impl<'a> RwSubscription<'a> {
|
||||
pub fn new(file: Ref<'a, dyn WasiFile>) -> Self {
|
||||
Self {
|
||||
file,
|
||||
status: Cell::new(None),
|
||||
}
|
||||
}
|
||||
pub fn complete(&self, size: u64, flags: RwEventFlags) {
|
||||
self.status.set(Some(Ok((size, flags))))
|
||||
}
|
||||
pub fn error(&self, error: Error) {
|
||||
self.status.set(Some(Err(error)))
|
||||
}
|
||||
pub fn result(self) -> Option<Result<(u64, RwEventFlags), Error>> {
|
||||
self.status.into_inner()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MonotonicClockSubscription<'a> {
|
||||
pub clock: &'a dyn WasiMonotonicClock,
|
||||
pub deadline: Instant,
|
||||
pub precision: Duration,
|
||||
}
|
||||
|
||||
impl<'a> MonotonicClockSubscription<'a> {
|
||||
pub fn now(&self) -> Instant {
|
||||
self.clock.now(self.precision)
|
||||
}
|
||||
pub fn duration_until(&self) -> Option<Duration> {
|
||||
self.deadline.checked_duration_since(self.now())
|
||||
}
|
||||
pub fn result(&self) -> Option<Result<(), Error>> {
|
||||
if self.now().checked_duration_since(self.deadline).is_some() {
|
||||
Some(Ok(()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Subscription<'a> {
|
||||
Read(RwSubscription<'a>),
|
||||
Write(RwSubscription<'a>),
|
||||
MonotonicClock(MonotonicClockSubscription<'a>),
|
||||
}
|
||||
|
||||
pub enum SubscriptionResult {
|
||||
Read(Result<(u64, RwEventFlags), Error>),
|
||||
Write(Result<(u64, RwEventFlags), Error>),
|
||||
MonotonicClock(Result<(), Error>),
|
||||
}
|
||||
|
||||
impl SubscriptionResult {
|
||||
pub fn from_subscription(s: Subscription) -> Option<SubscriptionResult> {
|
||||
match s {
|
||||
Subscription::Read(s) => s.result().map(SubscriptionResult::Read),
|
||||
Subscription::Write(s) => s.result().map(SubscriptionResult::Write),
|
||||
Subscription::MonotonicClock(s) => s.result().map(SubscriptionResult::MonotonicClock),
|
||||
}
|
||||
}
|
||||
}
|
||||
1
crates/wasi-common/src/snapshots/mod.rs
Normal file
1
crates/wasi-common/src/snapshots/mod.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub mod preview_1;
|
||||
1505
crates/wasi-common/src/snapshots/preview_1.rs
Normal file
1505
crates/wasi-common/src/snapshots/preview_1.rs
Normal file
File diff suppressed because it is too large
Load Diff
74
crates/wasi-common/src/string_array.rs
Normal file
74
crates/wasi-common/src/string_array.rs
Normal file
@@ -0,0 +1,74 @@
|
||||
use crate::{Error, ErrorExt};
|
||||
use wiggle::GuestPtr;
|
||||
|
||||
pub struct StringArray {
|
||||
elems: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum StringArrayError {
|
||||
#[error("Number of elements exceeds 2^32")]
|
||||
NumberElements,
|
||||
#[error("Element size exceeds 2^32")]
|
||||
ElementSize,
|
||||
#[error("Cumulative size exceeds 2^32")]
|
||||
CumulativeSize,
|
||||
}
|
||||
|
||||
impl StringArray {
|
||||
pub fn new() -> Self {
|
||||
StringArray { elems: Vec::new() }
|
||||
}
|
||||
|
||||
pub fn push(&mut self, elem: String) -> Result<(), StringArrayError> {
|
||||
if self.elems.len() + 1 > std::u32::MAX as usize {
|
||||
return Err(StringArrayError::NumberElements);
|
||||
}
|
||||
if elem.as_bytes().len() + 1 > std::u32::MAX as usize {
|
||||
return Err(StringArrayError::ElementSize);
|
||||
}
|
||||
if self.cumulative_size() as usize + elem.as_bytes().len() + 1 > std::u32::MAX as usize {
|
||||
return Err(StringArrayError::CumulativeSize);
|
||||
}
|
||||
self.elems.push(elem);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn number_elements(&self) -> u32 {
|
||||
self.elems.len() as u32
|
||||
}
|
||||
|
||||
pub fn cumulative_size(&self) -> u32 {
|
||||
self.elems
|
||||
.iter()
|
||||
.map(|e| e.as_bytes().len() + 1)
|
||||
.sum::<usize>() as u32
|
||||
}
|
||||
|
||||
pub fn write_to_guest<'a>(
|
||||
&self,
|
||||
buffer: &GuestPtr<'a, u8>,
|
||||
element_heads: &GuestPtr<'a, GuestPtr<'a, u8>>,
|
||||
) -> Result<(), Error> {
|
||||
let element_heads = element_heads.as_array(self.number_elements());
|
||||
let buffer = buffer.as_array(self.cumulative_size());
|
||||
let mut cursor = 0;
|
||||
for (elem, head) in self.elems.iter().zip(element_heads.iter()) {
|
||||
let bytes = elem.as_bytes();
|
||||
let len = bytes.len() as u32;
|
||||
{
|
||||
let elem_buffer = buffer
|
||||
.get_range(cursor..(cursor + len))
|
||||
.ok_or(Error::invalid_argument())?; // Elements don't fit in buffer provided
|
||||
elem_buffer.copy_from_slice(bytes)?;
|
||||
}
|
||||
buffer
|
||||
.get(cursor + len)
|
||||
.ok_or(Error::invalid_argument())?
|
||||
.write(0)?; // 0 terminate
|
||||
head?.write(buffer.get(cursor).expect("already validated"))?;
|
||||
cursor += len + 1;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
105
crates/wasi-common/src/table.rs
Normal file
105
crates/wasi-common/src/table.rs
Normal file
@@ -0,0 +1,105 @@
|
||||
use crate::{Error, ErrorExt};
|
||||
use std::any::Any;
|
||||
use std::cell::{Ref, RefCell, RefMut};
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub struct Table {
|
||||
map: HashMap<u32, RefCell<Box<dyn Any>>>,
|
||||
next_key: u32,
|
||||
}
|
||||
|
||||
impl Table {
|
||||
pub fn new() -> Self {
|
||||
Table {
|
||||
map: HashMap::new(),
|
||||
next_key: 3, // 0, 1 and 2 are reserved for stdio
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert_at(&mut self, key: u32, a: Box<dyn Any>) {
|
||||
self.map.insert(key, RefCell::new(a));
|
||||
}
|
||||
|
||||
pub fn push(&mut self, a: Box<dyn Any>) -> Result<u32, Error> {
|
||||
loop {
|
||||
let key = self.next_key;
|
||||
// XXX this is not correct. The table may still have empty entries, but our
|
||||
// linear search strategy is quite bad
|
||||
self.next_key = self
|
||||
.next_key
|
||||
.checked_add(1)
|
||||
.ok_or_else(|| Error::trap("out of keys in table"))?;
|
||||
if self.map.contains_key(&key) {
|
||||
continue;
|
||||
}
|
||||
self.map.insert(key, RefCell::new(a));
|
||||
return Ok(key);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn contains_key(&self, key: u32) -> bool {
|
||||
self.map.contains_key(&key)
|
||||
}
|
||||
|
||||
pub fn is<T: Any + Sized>(&self, key: u32) -> bool {
|
||||
if let Some(refcell) = self.map.get(&key) {
|
||||
if let Ok(refmut) = refcell.try_borrow_mut() {
|
||||
refmut.is::<T>()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get<T: Any + Sized>(&self, key: u32) -> Result<Ref<T>, Error> {
|
||||
if let Some(refcell) = self.map.get(&key) {
|
||||
if let Ok(r) = refcell.try_borrow() {
|
||||
if r.is::<T>() {
|
||||
Ok(Ref::map(r, |r| r.downcast_ref::<T>().unwrap()))
|
||||
} else {
|
||||
Err(Error::badf().context("element is a different type"))
|
||||
}
|
||||
} else {
|
||||
Err(Error::trap("table get of mutably borrowed element"))
|
||||
}
|
||||
} else {
|
||||
Err(Error::badf().context("key not in table"))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_mut<T: Any + Sized>(&self, key: u32) -> Result<RefMut<T>, Error> {
|
||||
if let Some(refcell) = self.map.get(&key) {
|
||||
if let Ok(r) = refcell.try_borrow_mut() {
|
||||
if r.is::<T>() {
|
||||
Ok(RefMut::map(r, |r| r.downcast_mut::<T>().unwrap()))
|
||||
} else {
|
||||
Err(Error::badf().context("element is a different type"))
|
||||
}
|
||||
} else {
|
||||
Err(Error::trap("table get_mut of borrowed element"))
|
||||
}
|
||||
} else {
|
||||
Err(Error::badf().context("key not in table"))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn delete(&mut self, key: u32) -> Option<Box<dyn Any>> {
|
||||
self.map.remove(&key).map(|rc| RefCell::into_inner(rc))
|
||||
}
|
||||
|
||||
pub fn update_in_place<T, F>(&mut self, key: u32, f: F) -> Result<(), Error>
|
||||
where
|
||||
T: Any + Sized,
|
||||
F: FnOnce(T) -> Result<T, Error>,
|
||||
{
|
||||
let entry = self.delete(key).ok_or(Error::badf())?;
|
||||
let downcast = entry
|
||||
.downcast::<T>()
|
||||
.map_err(|_| Error::exist().context("element is a different type"))?;
|
||||
let new = f(*downcast)?;
|
||||
self.insert_at(key, Box::new(new));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user