restructure cap-std impls into their own crate

This commit is contained in:
Pat Hickey
2021-01-20 19:09:15 -08:00
parent 016ed8966a
commit f3e40e2fc4
19 changed files with 597 additions and 485 deletions

17
Cargo.lock generated
View File

@@ -2263,6 +2263,7 @@ dependencies = [
"target-lexicon",
"tempfile",
"wasi-c2",
"wasi-c2-cap-std-sync",
"wasi-c2-wasmtime",
"wasmtime",
"wat",
@@ -2529,6 +2530,22 @@ dependencies = [
"yanix 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "wasi-c2-cap-std-sync"
version = "0.22.0"
dependencies = [
"cap-fs-ext",
"cap-rand",
"cap-std",
"cap-time-ext",
"fs-set-times",
"libc",
"system-interface",
"tracing",
"wasi-c2",
"yanix 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "wasi-c2-wasmtime"
version = "0.22.0"

View File

@@ -73,6 +73,7 @@ members = [
"crates/wiggle/wasmtime",
"crates/wasi-c2",
"crates/wasi-c2/wasmtime",
"crates/wasi-c2/cap-std-sync",
"examples/fib-debug/wasm",
"examples/wasi/wasm",
"examples/wasi-fs/wasm",

View File

@@ -13,6 +13,7 @@ cfg-if = "1.0"
[dev-dependencies]
wasi-c2 = { path = "../wasi-c2", version = "0.22.0" }
wasi-c2-wasmtime = { path = "../wasi-c2/wasmtime", version = "0.22.0" }
wasi-c2-cap-std-sync = { path = "../wasi-c2/cap-std-sync", version = "0.22.0" }
wasmtime = { path = "../wasmtime", version = "0.22.0" }
target-lexicon = "0.11.0"
pretty_env_logger = "0.4.0"

View File

@@ -1,30 +1,34 @@
use anyhow::Context;
use std::fs::File;
use std::path::Path;
use wasi_c2::virt::pipe::{ReadPipe, WritePipe};
use wasi_c2::pipe::{ReadPipe, WritePipe};
use wasi_c2_cap_std_sync::WasiCtxBuilder;
use wasmtime::{Linker, Module, Store};
pub fn instantiate(data: &[u8], bin_name: &str, workspace: Option<&Path>) -> anyhow::Result<()> {
/*
let stdout = WritePipe::new_in_memory();
let stderr = WritePipe::new_in_memory();
*/
let r = {
let store = Store::default();
// Create our wasi context.
// Additionally register any preopened directories if we have them.
let mut builder = wasi_c2::WasiCtx::builder();
let mut builder = WasiCtxBuilder::new();
builder = builder.arg(bin_name)?.arg(".")?.inherit_stdio();
builder = builder
.arg(bin_name)?
.arg(".")?
.stdin(Box::new(ReadPipe::from(Vec::new())))
.stdout(Box::new(stdout.clone()))
.stderr(Box::new(stderr.clone()));
if let Some(workspace) = workspace {
println!("preopen: {:?}", workspace);
let dirfd =
File::open(workspace).context(format!("error while preopening {:?}", workspace))?;
let preopen_dir = unsafe { cap_std::fs::Dir::from_std_file(dirfd) };
builder = builder.preopened_dir(Box::new(preopen_dir), ".")?;
builder = builder.preopened_dir(preopen_dir, ".")?;
}
let snapshot1 = wasi_c2_wasmtime::Wasi::new(&store, builder.build()?);
@@ -43,7 +47,6 @@ pub fn instantiate(data: &[u8], bin_name: &str, workspace: Option<&Path>) -> any
match r {
Ok(()) => Ok(()),
Err(trap) => {
/*
let stdout = stdout
.try_into_inner()
.expect("sole ref to stdout")
@@ -58,7 +61,6 @@ pub fn instantiate(data: &[u8], bin_name: &str, workspace: Option<&Path>) -> any
if !stderr.is_empty() {
println!("guest stderr:\n{}\n===", String::from_utf8_lossy(&stderr));
}
*/
Err(trap.context(format!("error while testing Wasm module '{}'", bin_name,)))
}
}

View File

@@ -0,0 +1,25 @@
[package]
name = "wasi-c2-cap-std-sync"
version = "0.22.0"
authors = ["The Wasmtime Project Developers"]
description = "WASI implementation in Rust"
license = "Apache-2.0 WITH LLVM-exception"
categories = ["wasm"]
keywords = ["webassembly", "wasm"]
repository = "https://github.com/bytecodealliance/wasmtime"
readme = "README.md"
edition = "2018"
include = ["src/**/*", "LICENSE" ]
publish = false
[dependencies]
wasi-c2 = { path = "../" }
cap-std = "0.9"
cap-fs-ext = "0.9"
cap-time-ext = "0.9"
cap-rand = "0.9"
fs-set-times = "0.2.2"
yanix = "0.22"
system-interface = { version = "0.5", features = ["cap_std_impls"] }
tracing = "0.1.19"
libc = "0.2"

View File

@@ -0,0 +1,222 @@
use crate::file::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,
};
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::Ilseq)?;
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) -> Result<Filestat, Error> {
let meta = self.0.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::NotCapable)?;
self.0
.rename(Path::new(src_path), &dest_dir.0, Path::new(dest_path))?;
Ok(())
}
fn hard_link(
&self,
src_path: &str,
symlink_follow: bool,
target_dir: &dyn WasiDir,
target_path: &str,
) -> Result<(), Error> {
let target_dir = target_dir
.as_any()
.downcast_ref::<Self>()
.ok_or(Error::NotCapable)?;
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<SystemTimeSpec>,
mtime: Option<SystemTimeSpec>,
) -> Result<(), Error> {
self.0.set_times(Path::new(path), atime, mtime)?;
Ok(())
}
}

View File

@@ -0,0 +1,136 @@
use cap_fs_ext::MetadataExt;
use fs_set_times::SetTimes;
use std::any::Any;
use std::io;
use system_interface::fs::{Advice, FileIoExt};
use system_interface::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> {
// XXX get_fdflags is not implemented but lets lie rather than panic:
Ok(FdFlags::empty())
}
fn set_fdflags(&self, _fdflags: FdFlags) -> Result<(), Error> {
todo!("set_fdflags is not implemented")
}
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(())
}
}
impl FileIoExt for File {
fn advise(&self, offset: u64, len: u64, advice: Advice) -> io::Result<()> {
self.0.advise(offset, len, advice)
}
fn allocate(&self, offset: u64, len: u64) -> io::Result<()> {
self.0.allocate(offset, len)
}
fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
self.0.read(buf)
}
fn read_exact(&self, buf: &mut [u8]) -> io::Result<()> {
self.0.read_exact(buf)
}
fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
self.0.read_at(buf, offset)
}
fn read_exact_at(&self, buf: &mut [u8], offset: u64) -> io::Result<()> {
self.0.read_exact_at(buf, offset)
}
fn read_vectored(&self, bufs: &mut [io::IoSliceMut]) -> io::Result<usize> {
self.0.read_vectored(bufs)
}
fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> {
self.0.read_to_end(buf)
}
fn read_to_string(&self, buf: &mut String) -> io::Result<usize> {
self.0.read_to_string(buf)
}
fn write(&self, buf: &[u8]) -> io::Result<usize> {
self.0.write(buf)
}
fn write_all(&self, buf: &[u8]) -> io::Result<()> {
self.0.write_all(buf)
}
fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
self.0.write_at(buf, offset)
}
fn write_all_at(&self, buf: &[u8], offset: u64) -> io::Result<()> {
self.0.write_all_at(buf, offset)
}
fn write_vectored(&self, bufs: &[io::IoSlice]) -> io::Result<usize> {
self.0.write_vectored(bufs)
}
fn write_fmt(&self, fmt: std::fmt::Arguments) -> io::Result<()> {
self.0.write_fmt(fmt)
}
fn flush(&self) -> io::Result<()> {
self.0.flush()
}
fn seek(&self, pos: std::io::SeekFrom) -> io::Result<u64> {
self.0.seek(pos)
}
fn stream_position(&self) -> io::Result<u64> {
self.0.stream_position()
}
fn peek(&self, buf: &mut [u8]) -> io::Result<usize> {
self.0.peek(buf)
}
}
impl SetTimes for File {
fn set_times(
&self,
atime: Option<fs_set_times::SystemTimeSpec>,
mtime: Option<fs_set_times::SystemTimeSpec>,
) -> io::Result<()> {
self.0.set_times(atime, mtime)
}
}
impl ReadReady for File {
fn num_ready_bytes(&self) -> io::Result<u64> {
self.0.num_ready_bytes()
}
}

View File

@@ -0,0 +1,68 @@
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::{clocks::WasiClocks, table::Table, Error, WasiCtx, WasiFile};
pub struct WasiCtxBuilder(wasi_c2::WasiCtxBuilder);
impl WasiCtxBuilder {
pub fn new() -> Self {
WasiCtxBuilder(WasiCtx::builder(
random(),
clocks(),
Box::new(sched::SyncSched),
Rc::new(RefCell::new(Table::new())),
))
}
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 clocks() -> WasiClocks {
let system = Box::new(unsafe { cap_std::time::SystemClock::new() });
let monotonic = unsafe { cap_std::time::MonotonicClock::new() };
let creation_time = monotonic.now();
let monotonic = Box::new(monotonic);
WasiClocks {
system,
monotonic,
creation_time,
}
}
pub fn random() -> RefCell<Box<dyn RngCore>> {
RefCell::new(Box::new(unsafe { cap_rand::rngs::OsRng::default() }))
}

View File

@@ -3,18 +3,17 @@ pub use unix::*;
#[cfg(unix)]
mod unix {
use crate::file::WasiFile;
use crate::sched::subscription::{RwEventFlags, Subscription};
use crate::sched::{Poll, WasiSched};
use crate::Error;
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;
use wasi_c2::sched::subscription::{RwEventFlags, Subscription};
use wasi_c2::sched::{Poll, WasiSched};
use wasi_c2::Error;
use yanix::poll::{PollFd, PollFlags};
#[derive(Default)]
pub struct SyncSched {}
pub struct SyncSched;
impl WasiSched for SyncSched {
fn poll_oneoff<'a>(&self, poll: &'a Poll<'a>) -> Result<(), Error> {
@@ -106,8 +105,9 @@ mod unix {
fn wasi_file_raw_fd(f: &dyn WasiFile) -> Option<RawFd> {
let a = f.as_any();
if a.is::<cap_std::fs::File>() {
Some(a.downcast_ref::<cap_std::fs::File>().unwrap().as_raw_fd())
if a.is::<crate::file::File>() {
/* DISABLED UNTIL AsRawFd can be implemented properly
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>() {
@@ -122,6 +122,8 @@ mod unix {
.unwrap()
.as_raw_fd(),
)
*/
None
} else {
None
}

View File

@@ -0,0 +1,63 @@
use wasi_c2::pipe::{ReadPipe, WritePipe};
pub type Stdin = ReadPipe<std::io::Stdin>;
pub fn stdin() -> Stdin {
ReadPipe::new(std::io::stdin())
}
pub type Stdout = WritePipe<std::io::Stdout>;
pub fn stdout() -> Stdout {
WritePipe::new(std::io::stdout())
}
pub type Stderr = WritePipe<std::io::Stderr>;
pub fn stderr() -> Stderr {
WritePipe::new(std::io::stderr())
}
/*
#[cfg(windows)]
mod windows {
use super::*;
use std::os::windows::io::{AsRawHandle, RawHandle};
impl AsRawHandle for Stdin {
fn as_raw_handle(&self) -> RawHandle {
self.borrow().as_raw_handle()
}
}
impl AsRawHandle for Stdout {
fn as_raw_handle(&self) -> RawHandle {
self.borrow().as_raw_handle()
}
}
impl AsRawHandle for Stderr {
fn as_raw_handle(&self) -> RawHandle {
self.borrow().as_raw_handle()
}
}
}
#[cfg(unix)]
mod unix {
use super::*;
use std::os::unix::io::{AsRawFd, RawFd};
impl AsRawFd for Stdin {
fn as_raw_fd(&self) -> RawFd {
self.borrow().as_raw_fd()
}
}
impl AsRawFd for Stdout {
fn as_raw_fd(&self) -> RawFd {
self.borrow().as_raw_fd()
}
}
impl AsRawFd for Stderr {
fn as_raw_fd(&self) -> RawFd {
self.borrow().as_raw_fd()
}
}
}
*/

View File

@@ -28,3 +28,9 @@ impl WasiMonotonicClock for MonotonicClock {
self.now_with(precision)
}
}
pub struct WasiClocks {
pub system: Box<dyn WasiSystemClock>,
pub monotonic: Box<dyn WasiMonotonicClock>,
pub creation_time: cap_std::time::Instant,
}

View File

@@ -1,7 +1,7 @@
use crate::clocks::{WasiMonotonicClock, WasiSystemClock};
use crate::clocks::WasiClocks;
use crate::dir::{DirCaps, DirEntry, WasiDir};
use crate::file::{FileCaps, FileEntry, WasiFile};
use crate::sched::{SyncSched, WasiSched};
use crate::sched::WasiSched;
use crate::string_array::{StringArray, StringArrayError};
use crate::table::Table;
use crate::Error;
@@ -11,28 +11,29 @@ use std::path::{Path, PathBuf};
use std::rc::Rc;
pub struct WasiCtx {
pub(crate) args: StringArray,
pub(crate) env: StringArray,
pub(crate) random: RefCell<Box<dyn RngCore>>,
pub(crate) clocks: WasiCtxClocks,
pub(crate) sched: Box<dyn WasiSched>,
table: Rc<RefCell<Table>>,
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() -> WasiCtxBuilder {
WasiCtxBuilder(WasiCtx::new())
}
pub fn new() -> Self {
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: RefCell::new(Box::new(unsafe { cap_rand::rngs::OsRng::default() })),
clocks: WasiCtxClocks::default(),
sched: Box::new(SyncSched::default()),
table: Rc::new(RefCell::new(Table::new())),
}
random,
clocks,
sched,
table,
})
}
pub fn insert_file(&self, fd: u32, file: Box<dyn WasiFile>, caps: FileCaps) {
@@ -98,12 +99,6 @@ impl WasiCtxBuilder {
self
}
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: Box<dyn WasiDir>,
@@ -119,29 +114,4 @@ impl WasiCtxBuilder {
)))?;
Ok(self)
}
pub fn random(self, random: Box<dyn RngCore>) -> Self {
self.0.random.replace(random);
self
}
}
pub struct WasiCtxClocks {
pub(crate) system: Box<dyn WasiSystemClock>,
pub(crate) monotonic: Box<dyn WasiMonotonicClock>,
pub(crate) creation_time: cap_std::time::Instant,
}
impl Default for WasiCtxClocks {
fn default() -> WasiCtxClocks {
let system = Box::new(unsafe { cap_std::time::SystemClock::new() });
let monotonic = unsafe { cap_std::time::MonotonicClock::new() };
let creation_time = monotonic.now();
let monotonic = Box::new(monotonic);
WasiCtxClocks {
system,
monotonic,
creation_time,
}
}
}

View File

@@ -4,9 +4,8 @@ use bitflags::bitflags;
use cap_fs_ext::SystemTimeSpec;
use std::any::Any;
use std::cell::Ref;
use std::convert::TryInto;
use std::ops::Deref;
use std::path::{Path, PathBuf};
use std::path::PathBuf;
pub trait WasiDir {
fn as_any(&self) -> &dyn Any;
@@ -187,211 +186,3 @@ impl From<ReaddirCursor> for u64 {
c.0
}
}
impl WasiDir for cap_std::fs::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.open_with(Path::new(path), &opts)?;
Ok(Box::new(f))
}
fn open_dir(&self, symlink_follow: bool, path: &str) -> Result<Box<dyn WasiDir>, Error> {
let d = if symlink_follow {
self.open_dir(Path::new(path))?
} else {
use cap_fs_ext::DirExt;
self.open_dir_nofollow(Path::new(path))?
};
Ok(Box::new(d))
}
fn create_dir(&self, path: &str) -> Result<(), Error> {
self.create_dir(Path::new(path))?;
Ok(())
}
fn readdir(
&self,
cursor: ReaddirCursor,
) -> Result<Box<dyn Iterator<Item = Result<(ReaddirEntity, String), Error>>>, Error> {
use cap_fs_ext::MetadataExt;
// 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.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.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::Ilseq)?;
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.symlink(Path::new(src_path), Path::new(dest_path))?;
Ok(())
}
fn remove_dir(&self, path: &str) -> Result<(), Error> {
self.remove_dir(Path::new(path))?;
Ok(())
}
fn unlink_file(&self, path: &str) -> Result<(), Error> {
self.remove_file(Path::new(path))?;
Ok(())
}
fn read_link(&self, path: &str) -> Result<PathBuf, Error> {
let link = self.read_link(Path::new(path))?;
Ok(link)
}
fn get_filestat(&self) -> Result<Filestat, Error> {
let meta = self.dir_metadata()?;
use cap_fs_ext::MetadataExt;
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) -> Result<Filestat, Error> {
let meta = self.metadata(Path::new(path))?;
use cap_fs_ext::MetadataExt;
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::NotCapable)?;
self.rename(Path::new(src_path), dest_dir, Path::new(dest_path))?;
Ok(())
}
fn hard_link(
&self,
src_path: &str,
symlink_follow: bool,
target_dir: &dyn WasiDir,
target_path: &str,
) -> Result<(), Error> {
let target_dir = target_dir
.as_any()
.downcast_ref::<Self>()
.ok_or(Error::NotCapable)?;
let src_path = Path::new(src_path);
let target_path = Path::new(target_path);
self.hard_link(src_path, target_dir, target_path)?;
Ok(())
}
fn set_times(
&self,
path: &str,
atime: Option<SystemTimeSpec>,
mtime: Option<SystemTimeSpec>,
) -> Result<(), Error> {
cap_fs_ext::DirExt::set_times(self, Path::new(path), atime, mtime)?;
Ok(())
}
}

View File

@@ -166,46 +166,3 @@ pub struct FdStat {
pub caps: FileCaps,
pub flags: FdFlags,
}
impl WasiFile for cap_std::fs::File {
fn as_any(&self) -> &dyn Any {
self
}
fn datasync(&self) -> Result<(), Error> {
self.sync_data()?;
Ok(())
}
fn sync(&self) -> Result<(), Error> {
self.sync_all()?;
Ok(())
}
fn get_filetype(&self) -> Result<FileType, Error> {
let meta = self.metadata()?;
Ok(FileType::from(&meta.file_type()))
}
fn get_fdflags(&self) -> Result<FdFlags, Error> {
// XXX get_fdflags is not implemented but lets lie rather than panic:
Ok(FdFlags::empty())
}
fn set_fdflags(&self, _fdflags: FdFlags) -> Result<(), Error> {
todo!("set_fdflags is not implemented")
}
fn get_filestat(&self) -> Result<Filestat, Error> {
let meta = self.metadata()?;
use cap_fs_ext::MetadataExt;
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.set_len(size)?;
Ok(())
}
}

View File

@@ -2,16 +2,15 @@
pub mod clocks;
mod ctx;
mod dir;
pub mod dir;
mod error;
mod file;
pub mod file;
pub mod pipe;
pub mod random;
pub mod sched;
pub mod snapshots;
pub mod stdio;
mod string_array;
pub mod table;
pub mod virt;
pub use cap_fs_ext::SystemTimeSpec;
pub use ctx::{WasiCtx, WasiCtxBuilder};

View File

@@ -5,9 +5,6 @@ use cap_std::time::{Duration, Instant};
use std::cell::Ref;
pub mod subscription;
mod sync;
pub use sync::SyncSched;
use subscription::{MonotonicClockSubscription, RwSubscription, Subscription, SubscriptionResult};
pub trait WasiSched {

View File

@@ -1,144 +0,0 @@
use crate::file::{FdFlags, FileType, Filestat, WasiFile};
use crate::Error;
use std::any::Any;
#[cfg(unix)]
use std::os::unix::io::{AsRawFd, RawFd};
use system_interface::io::ReadReady;
pub struct Stdin(std::io::Stdin);
pub fn stdin() -> Stdin {
Stdin(std::io::stdin())
}
#[cfg(unix)]
impl AsRawFd for Stdin {
fn as_raw_fd(&self) -> RawFd {
self.0.as_raw_fd()
}
}
impl ReadReady for Stdin {
fn num_ready_bytes(&self) -> Result<u64, std::io::Error> {
self.0.num_ready_bytes()
}
}
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::CharacterDevice)
}
fn get_fdflags(&self) -> Result<FdFlags, Error> {
todo!()
}
fn set_fdflags(&self, _fdflags: FdFlags) -> Result<(), Error> {
todo!()
}
fn get_filestat(&self) -> Result<Filestat, Error> {
todo!()
}
fn set_filestat_size(&self, _size: u64) -> Result<(), Error> {
todo!()
}
}
pub struct Stdout(std::io::Stdout);
pub fn stdout() -> Stdout {
Stdout(std::io::stdout())
}
#[cfg(unix)]
impl AsRawFd for Stdout {
fn as_raw_fd(&self) -> RawFd {
self.0.as_raw_fd()
}
}
impl ReadReady for Stdout {
fn num_ready_bytes(&self) -> Result<u64, std::io::Error> {
Ok(0)
}
}
impl WasiFile for Stdout {
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::CharacterDevice)
}
fn get_fdflags(&self) -> Result<FdFlags, Error> {
todo!()
}
fn set_fdflags(&self, _fdflags: FdFlags) -> Result<(), Error> {
todo!()
}
fn get_filestat(&self) -> Result<Filestat, Error> {
todo!()
}
fn set_filestat_size(&self, _size: u64) -> Result<(), Error> {
todo!()
}
}
pub struct Stderr(std::io::Stderr);
pub fn stderr() -> Stderr {
Stderr(std::io::stderr())
}
#[cfg(unix)]
impl AsRawFd for Stderr {
fn as_raw_fd(&self) -> RawFd {
self.0.as_raw_fd()
}
}
impl ReadReady for Stderr {
fn num_ready_bytes(&self) -> Result<u64, std::io::Error> {
Ok(0)
}
}
impl WasiFile for Stderr {
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::CharacterDevice)
}
fn get_fdflags(&self) -> Result<FdFlags, Error> {
todo!()
}
fn set_fdflags(&self, _fdflags: FdFlags) -> Result<(), Error> {
todo!()
}
fn get_filestat(&self) -> Result<Filestat, Error> {
todo!()
}
fn set_filestat_size(&self, _size: u64) -> Result<(), Error> {
todo!()
}
}

View File

@@ -1 +0,0 @@
pub mod pipe;