Merge branch 'master' into feature/no-mem-uninit

This commit is contained in:
Syrus Akbary 2019-07-30 22:28:56 -07:00 committed by GitHub
commit 6a4091e78e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 544 additions and 206 deletions

View File

@ -5,6 +5,7 @@ All PRs to the Wasmer repository must add to this file.
Blocks of changes will separated by version increments.
## **[Unreleased]**
- [#595](https://github.com/wasmerio/wasmer/pull/595) Add unstable public API for interfacing with the WASI file system in plugin-like usecases
- [#598](https://github.com/wasmerio/wasmer/pull/598) LLVM Backend is now supported in Windows
- [#599](https://github.com/wasmerio/wasmer/pull/599) Fix llvm backend failures in fat spec tests and simd_binaryen spec test.
- [#579](https://github.com/wasmerio/wasmer/pull/579) Fix bug in caching with LLVM and Singlepass backends.

Binary file not shown.

View File

@ -40,4 +40,6 @@ In this example, we instantiate a system with an extended (WASI)[wasi] ABI, allo
Because the Rust WASI doesn't support the crate type of `cdylib`, we have to include a main function which we don't use. This is being discussed [here](https://github.com/WebAssembly/WASI/issues/24).
We call the main function to initialize WASI's libpreopen internal datastructures and have the module call back into the host to set swap out the modules implementation of stdout. The host then provides a wrapper around stdout, allowing the guest's writes to stdout to be formatted in a host-appropriate manner.
[wasi]: https://hacks.mozilla.org/2019/03/standardizing-wasi-a-webassembly-system-interface/

View File

@ -1,5 +1,6 @@
extern "C" {
fn it_works() -> i32;
fn initialize();
}
#[no_mangle]
@ -9,4 +10,6 @@ pub fn plugin_entrypoint(n: i32) -> i32 {
result + n
}
pub fn main() {}
pub fn main() {
unsafe { initialize() };
}

View File

@ -1,6 +1,10 @@
use wasmer_runtime::{func, imports, instantiate};
use wasmer_runtime_core::vm::Ctx;
use wasmer_wasi::generate_import_object;
use wasmer_wasi::{
generate_import_object,
state::{self, WasiFile},
types,
};
static PLUGIN_LOCATION: &'static str = "examples/plugin-for-example.wasm";
@ -9,6 +13,107 @@ fn it_works(_ctx: &mut Ctx) -> i32 {
5
}
#[derive(Debug)]
pub struct LoggingWrapper {
pub wasm_module_name: String,
}
// std io trait boiler plate so we can implement WasiFile
// LoggingWrapper is a write-only type so we just want to immediately
// fail when reading or Seeking
impl std::io::Read for LoggingWrapper {
fn read(&mut self, _buf: &mut [u8]) -> std::io::Result<usize> {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
"can not read from logging wrapper",
))
}
fn read_to_end(&mut self, _buf: &mut Vec<u8>) -> std::io::Result<usize> {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
"can not read from logging wrapper",
))
}
fn read_to_string(&mut self, _buf: &mut String) -> std::io::Result<usize> {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
"can not read from logging wrapper",
))
}
fn read_exact(&mut self, _buf: &mut [u8]) -> std::io::Result<()> {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
"can not read from logging wrapper",
))
}
}
impl std::io::Seek for LoggingWrapper {
fn seek(&mut self, _pos: std::io::SeekFrom) -> std::io::Result<u64> {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
"can not seek logging wrapper",
))
}
}
impl std::io::Write for LoggingWrapper {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
let stdout = std::io::stdout();
let mut out = stdout.lock();
out.write(b"[")?;
out.write(self.wasm_module_name.as_bytes())?;
out.write(b"]: ")?;
out.write(buf)
}
fn flush(&mut self) -> std::io::Result<()> {
std::io::stdout().flush()
}
fn write_all(&mut self, buf: &[u8]) -> std::io::Result<()> {
let stdout = std::io::stdout();
let mut out = stdout.lock();
out.write(b"[")?;
out.write(self.wasm_module_name.as_bytes())?;
out.write(b"]: ")?;
out.write_all(buf)
}
fn write_fmt(&mut self, fmt: std::fmt::Arguments) -> std::io::Result<()> {
let stdout = std::io::stdout();
let mut out = stdout.lock();
out.write(b"[")?;
out.write(self.wasm_module_name.as_bytes())?;
out.write(b"]: ")?;
out.write_fmt(fmt)
}
}
// the WasiFile methods aren't relevant for a write-only Stdout-like implementation
impl WasiFile for LoggingWrapper {
fn last_accessed(&self) -> u64 {
0
}
fn last_modified(&self) -> u64 {
0
}
fn created_time(&self) -> u64 {
0
}
fn size(&self) -> u64 {
0
}
}
/// Called by the program when it wants to set itself up
fn initialize(ctx: &mut Ctx) {
let state = state::get_wasi_state(ctx);
let wasi_file_inner = LoggingWrapper {
wasm_module_name: "example module name".to_string(),
};
// swap stdout with our new wasifile
let _old_stdout = state
.fs
.swap_file(types::__WASI_STDOUT_FILENO, Box::new(wasi_file_inner))
.unwrap();
}
fn main() {
// Load the plugin data
let wasm_bytes = std::fs::read(PLUGIN_LOCATION).expect(&format!(
@ -22,6 +127,7 @@ fn main() {
let custom_imports = imports! {
"env" => {
"it_works" => func!(it_works),
"initialize" => func!(initialize),
},
};
// The WASI imports object contains all required import functions for a WASI module to run.
@ -30,6 +136,8 @@ fn main() {
let instance =
instantiate(&wasm_bytes[..], &base_imports).expect("failed to instantiate wasm module");
let main = instance.func::<(), ()>("_start").unwrap();
main.call().expect("Could not initialize");
// get a reference to the function "plugin_entrypoint" which takes an i32 and returns an i32
let entry_point = instance.func::<(i32), i32>("plugin_entrypoint").unwrap();
// call the "entry_point" function in WebAssembly with the number "2" as the i32 argument

View File

@ -6,11 +6,12 @@ extern crate winapi;
#[macro_use]
mod macros;
mod ptr;
mod state;
pub mod state;
mod syscalls;
mod utils;
use self::state::{WasiFs, WasiState};
pub use self::syscalls::types;
use self::syscalls::*;
use std::ffi::c_void;

View File

@ -1,3 +1,6 @@
//! WARNING: the API exposed here is unstable and very experimental. Certain thins will not
//! yet and may be broken in patch releases. If you're using this and have any specific needs,
//! please let us know here https://github.com/wasmerio/wasmer/issues/583 or by filing an issue.
// use wasmer_runtime_abi::vfs::{
// vfs::Vfs,
// file_like::{FileLike, Metadata};
@ -5,7 +8,7 @@
use crate::syscalls::types::*;
use generational_arena::Arena;
pub use generational_arena::Index as Inode;
use hashbrown::hash_map::{Entry, HashMap};
use hashbrown::hash_map::HashMap;
use std::{
borrow::Borrow,
cell::Cell,
@ -14,80 +17,310 @@ use std::{
path::{Path, PathBuf},
time::SystemTime,
};
use wasmer_runtime_core::debug;
use wasmer_runtime_core::{debug, vm::Ctx};
/// the fd value of the virtual root
pub const VIRTUAL_ROOT_FD: __wasi_fd_t = 4;
/// all the rights enabled
pub const ALL_RIGHTS: __wasi_rights_t = 0x1FFFFFFF;
/// Get WasiState from a Ctx
pub fn get_wasi_state(ctx: &mut Ctx) -> &mut WasiState {
unsafe { &mut *(ctx.data as *mut WasiState) }
}
/// A completely aribtrary "big enough" number used as the upper limit for
/// the number of symlinks that can be traversed when resolving a path
pub const MAX_SYMLINKS: u32 = 128;
/// Error type for external users
#[derive(Debug, PartialEq, Eq)]
#[allow(dead_code)]
// dead code beacuse this is for external use
pub enum WasiFsError {
/// The fd given as a base was not a directory so the operation was not possible
BaseNotDirectory,
/// Expected a file but found not a file
NotAFile,
/// The fd given was not usable
InvalidFd,
/// File exists
AlreadyExists,
/// Something failed when doing IO. These errors can generally not be handled.
/// It may work if tried again.
IOError,
/// A WASI error without an external name. If you encounter this it means
/// that there's probably a bug on our side (maybe as simple as forgetting to wrap
/// this error, but perhaps something broke)
UnknownError(__wasi_errno_t),
}
impl WasiFsError {
pub fn from_wasi_err(err: __wasi_errno_t) -> WasiFsError {
match err {
__WASI_EBADF => WasiFsError::InvalidFd,
__WASI_EEXIST => WasiFsError::AlreadyExists,
__WASI_EIO => WasiFsError::IOError,
_ => WasiFsError::UnknownError(err),
}
}
}
/// This trait relies on your file closing when it goes out of scope via `Drop`
pub trait WasiFile: std::fmt::Debug + Write + Read + Seek {
/// the last time the file was accessed in nanoseconds as a UNIX timestamp
fn last_accessed(&self) -> u64;
/// the last time the file was modified in nanoseconds as a UNIX timestamp
fn last_modified(&self) -> u64;
/// the time at which the file was created in nanoseconds as a UNIX timestamp
fn created_time(&self) -> u64;
/// the size of the file in bytes
fn size(&self) -> u64;
}
impl WasiFile for fs::File {
fn last_accessed(&self) -> u64 {
self.metadata()
.unwrap()
.accessed()
.ok()
.and_then(|ct| ct.duration_since(SystemTime::UNIX_EPOCH).ok())
.map(|ct| ct.as_nanos() as u64)
.unwrap_or(0)
}
fn last_modified(&self) -> u64 {
self.metadata()
.unwrap()
.modified()
.ok()
.and_then(|ct| ct.duration_since(SystemTime::UNIX_EPOCH).ok())
.map(|ct| ct.as_nanos() as u64)
.unwrap_or(0)
}
fn created_time(&self) -> u64 {
self.metadata()
.unwrap()
.created()
.ok()
.and_then(|ct| ct.duration_since(SystemTime::UNIX_EPOCH).ok())
.map(|ct| ct.as_nanos() as u64)
.unwrap_or(0)
}
fn size(&self) -> u64 {
self.metadata().unwrap().len()
}
}
#[derive(Debug)]
pub enum WasiFile {
HostFile(fs::File),
pub struct Stdout(std::io::Stdout);
impl Read for Stdout {
fn read(&mut self, _buf: &mut [u8]) -> io::Result<usize> {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
"can not read from stdout",
))
}
impl WasiFile {
pub fn close(self) {}
fn read_to_end(&mut self, _buf: &mut Vec<u8>) -> io::Result<usize> {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
"can not read from stdout",
))
}
impl Write for WasiFile {
fn read_to_string(&mut self, _buf: &mut String) -> io::Result<usize> {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
"can not read from stdout",
))
}
fn read_exact(&mut self, _buf: &mut [u8]) -> io::Result<()> {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
"can not read from stdout",
))
}
}
impl Seek for Stdout {
fn seek(&mut self, _pos: io::SeekFrom) -> io::Result<u64> {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
"can not seek stdout",
))
}
}
impl Write for Stdout {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
match self {
WasiFile::HostFile(hf) => hf.write(buf),
self.0.write(buf)
}
}
fn flush(&mut self) -> io::Result<()> {
match self {
WasiFile::HostFile(hf) => hf.flush(),
self.0.flush()
}
}
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
match self {
WasiFile::HostFile(hf) => hf.write_all(buf),
self.0.write_all(buf)
}
}
fn write_fmt(&mut self, fmt: ::std::fmt::Arguments) -> io::Result<()> {
match self {
WasiFile::HostFile(hf) => hf.write_fmt(fmt),
}
self.0.write_fmt(fmt)
}
}
impl Read for WasiFile {
impl WasiFile for Stdout {
fn last_accessed(&self) -> u64 {
0
}
fn last_modified(&self) -> u64 {
0
}
fn created_time(&self) -> u64 {
0
}
fn size(&self) -> u64 {
0
}
}
#[derive(Debug)]
pub struct Stderr(std::io::Stderr);
impl Read for Stderr {
fn read(&mut self, _buf: &mut [u8]) -> io::Result<usize> {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
"can not read from stderr",
))
}
fn read_to_end(&mut self, _buf: &mut Vec<u8>) -> io::Result<usize> {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
"can not read from stderr",
))
}
fn read_to_string(&mut self, _buf: &mut String) -> io::Result<usize> {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
"can not read from stderr",
))
}
fn read_exact(&mut self, _buf: &mut [u8]) -> io::Result<()> {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
"can not read from stderr",
))
}
}
impl Seek for Stderr {
fn seek(&mut self, _pos: io::SeekFrom) -> io::Result<u64> {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
"can not seek stderr",
))
}
}
impl Write for Stderr {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.0.write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.0.flush()
}
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
self.0.write_all(buf)
}
fn write_fmt(&mut self, fmt: ::std::fmt::Arguments) -> io::Result<()> {
self.0.write_fmt(fmt)
}
}
impl WasiFile for Stderr {
fn last_accessed(&self) -> u64 {
0
}
fn last_modified(&self) -> u64 {
0
}
fn created_time(&self) -> u64 {
0
}
fn size(&self) -> u64 {
0
}
}
#[derive(Debug)]
pub struct Stdin(std::io::Stdin);
impl Read for Stdin {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
match self {
WasiFile::HostFile(hf) => hf.read(buf),
self.0.read(buf)
}
}
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
match self {
WasiFile::HostFile(hf) => hf.read_to_end(buf),
self.0.read_to_end(buf)
}
}
fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
match self {
WasiFile::HostFile(hf) => hf.read_to_string(buf),
self.0.read_to_string(buf)
}
}
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
match self {
WasiFile::HostFile(hf) => hf.read_exact(buf),
self.0.read_exact(buf)
}
}
impl Seek for Stdin {
fn seek(&mut self, _pos: io::SeekFrom) -> io::Result<u64> {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
"can not seek stdin",
))
}
}
impl Write for Stdin {
fn write(&mut self, _buf: &[u8]) -> io::Result<usize> {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
"can not write to stdin",
))
}
fn flush(&mut self) -> io::Result<()> {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
"can not write to stdin",
))
}
fn write_all(&mut self, _buf: &[u8]) -> io::Result<()> {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
"can not write to stdin",
))
}
fn write_fmt(&mut self, _fmt: ::std::fmt::Arguments) -> io::Result<()> {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
"can not write to stdin",
))
}
}
impl Seek for WasiFile {
fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
match self {
WasiFile::HostFile(hf) => hf.seek(pos),
impl WasiFile for Stdin {
fn last_accessed(&self) -> u64 {
0
}
fn last_modified(&self) -> u64 {
0
}
fn created_time(&self) -> u64 {
0
}
fn size(&self) -> u64 {
0
}
}
/*
TODO: Think about using this
trait WasiFdBacking: std::fmt::Debug {
fn get_stat(&self) -> &__wasi_filestat_t;
fn get_stat_mut(&mut self) -> &mut __wasi_filestat_t;
fn is_preopened(&self) -> bool;
fn get_name(&self) -> &str;
}
*/
/// A file that Wasi knows about that may or may not be open
#[derive(Debug)]
@ -98,12 +331,30 @@ pub struct InodeVal {
pub kind: Kind,
}
/*impl WasiFdBacking for InodeVal {
fn get_stat(&self) -> &__wasi_filestat_t {
&self.stat
}
fn get_stat_mut(&mut self) -> &mut __wasi_filestat_t {
&mut self.stat
}
fn is_preopened(&self) -> bool {
self.is_preopened
}
fn get_name(&self) -> &str {
self.name.as_ref()
}
}*/
#[allow(dead_code)]
#[derive(Debug)]
pub enum Kind {
File {
/// the open file, if it's open
handle: Option<WasiFile>,
handle: Option<Box<dyn WasiFile>>,
/// the path to the file
path: PathBuf,
},
@ -152,6 +403,8 @@ pub struct Fd {
}
#[derive(Debug)]
/// Warning, modifying these fields directly may cause invariants to break and
/// should be considered unsafe. These fields may be made private in a future release
pub struct WasiFs {
//pub repo: Repo,
pub preopen_fds: Vec<u32>,
@ -160,6 +413,10 @@ pub struct WasiFs {
pub fd_map: HashMap<u32, Fd>,
pub next_fd: Cell<u32>,
inode_counter: Cell<u64>,
pub stdout: Box<dyn WasiFile>,
pub stderr: Box<dyn WasiFile>,
pub stdin: Box<dyn WasiFile>,
}
impl WasiFs {
@ -176,6 +433,10 @@ impl WasiFs {
fd_map: HashMap::new(),
next_fd: Cell::new(3),
inode_counter: Cell::new(1024),
stdin: Box::new(Stdin(io::stdin())),
stdout: Box::new(Stdout(io::stdout())),
stderr: Box::new(Stderr(io::stderr())),
};
// create virtual root
let root_inode = {
@ -291,117 +552,97 @@ impl WasiFs {
next
}
/// Opens a user-supplied file in the directory specified with the
/// name and flags given
// dead code because this is an API for external use
#[allow(dead_code)]
fn get_inode(&mut self, path: &str) -> Option<Inode> {
Some(match self.name_map.entry(path.to_string()) {
Entry::Occupied(o) => *o.get(),
Entry::Vacant(_v) => {
return None;
// let file = if let Ok(file) = OpenOptions::new()
// .read(true)
// .write(true)
// .create(false)
// .open(&mut self.repo, path)
// {
// file
// } else {
// return None;
// };
// let metadata = file.metadata().unwrap();
// let inode_index = {
// let index = self.inode_counter.get();
// self.inode_counter.replace(index + 1)
// };
// let systime_to_nanos = |systime: SystemTime| {
// let duration = systime
// .duration_since(SystemTime::UNIX_EPOCH)
// .expect("should always be after unix epoch");
// duration.as_nanos() as u64
// };
// let inode = self.inodes.insert(InodeVal {
// stat: __wasi_filestat_t {
// st_dev: 0,
// st_ino: inode_index,
// st_filetype: match metadata.file_type() {
// FileType::File => __WASI_FILETYPE_REGULAR_FILE,
// FileType::Dir => __WASI_FILETYPE_DIRECTORY,
// },
// st_nlink: 0,
// st_size: metadata.content_len() as u64,
// st_atim: systime_to_nanos(SystemTime::now()),
// st_mtim: systime_to_nanos(metadata.modified_at()),
// st_ctim: systime_to_nanos(metadata.created_at()),
// },
// is_preopened: false,
// name: path.to_string(),
// kind: match metadata.file_type() {
// FileType::File => Kind::File { handle: file },
// FileType::Dir => Kind::Dir {
// handle: file,
// entries: HashMap::new(),
// },
// },
// });
// v.insert(inode);
// inode
}
})
}
/*
#[allow(dead_code)]
fn filestat_inode(
&self,
inode: Inode,
flags: __wasi_lookupflags_t,
) -> Result<__wasi_filestat_t, __wasi_errno_t> {
let inode_val = &self.inodes[inode];
if let (
true,
Kind::Symlink {
mut forwarded,
path,
},
) = (flags & __WASI_LOOKUP_SYMLINK_FOLLOW != 0, &inode_val.kind)
{
// Time to follow the symlink.
let mut counter = 0;
while counter <= MAX_SYMLINKS {
let inode_val = &self.inodes[forwarded];
if let &Kind::Symlink {
forwarded: new_forwarded,
} = &inode_val.kind
{
counter += 1;
forwarded = new_forwarded;
} else {
return Ok(inode_val.stat);
}
}
Err(__WASI_EMLINK)
} else {
Ok(inode_val.stat)
}
}
#[allow(dead_code)]
pub fn filestat_path(
pub fn open_file_at(
&mut self,
preopened_fd: __wasi_fd_t,
flags: __wasi_lookupflags_t,
path: &str,
) -> Result<__wasi_filestat_t, __wasi_errno_t> {
warn!("Should use preopned_fd: {}", preopened_fd);
let inode = self.get_inode(path).ok_or(__WASI_EINVAL)?;
base: __wasi_fd_t,
file: Box<dyn WasiFile>,
name: String,
rights: __wasi_rights_t,
rights_inheriting: __wasi_rights_t,
flags: __wasi_fdflags_t,
) -> Result<__wasi_fd_t, WasiFsError> {
let base_fd = self.get_fd(base).map_err(WasiFsError::from_wasi_err)?;
// TODO: check permissions here? probably not, but this should be
// an explicit choice, so justify it in a comment when we remove this one
let base_inode = base_fd.inode;
self.filestat_inode(inode, flags)
match &self.inodes[base_inode].kind {
Kind::Dir { ref entries, .. } | Kind::Root { ref entries } => {
if let Some(_entry) = entries.get(&name) {
// TODO: eventually change the logic here to allow overwrites
return Err(WasiFsError::AlreadyExists);
}
let kind = Kind::File {
handle: Some(file),
path: PathBuf::from(""),
};
let inode = self
.create_inode(kind, false, name.clone())
.map_err(|_| WasiFsError::IOError)?;
// reborrow to insert
match &mut self.inodes[base_inode].kind {
Kind::Dir {
ref mut entries, ..
}
| Kind::Root { ref mut entries } => {
entries.insert(name, inode).ok_or(WasiFsError::IOError)?;
}
_ => unreachable!("Dir or Root became not Dir or Root"),
}
self.create_fd(rights, rights_inheriting, flags, inode)
.map_err(WasiFsError::from_wasi_err)
}
_ => Err(WasiFsError::BaseNotDirectory),
}
}
/// Change the backing of a given file descriptor
/// Returns the old backing
/// TODO: add examples
#[allow(dead_code)]
pub fn swap_file(
&mut self,
fd: __wasi_fd_t,
file: Box<dyn WasiFile>,
) -> Result<Option<Box<dyn WasiFile>>, WasiFsError> {
match fd {
__WASI_STDIN_FILENO => {
let mut ret = file;
std::mem::swap(&mut self.stdin, &mut ret);
Ok(Some(ret))
}
__WASI_STDOUT_FILENO => {
let mut ret = file;
std::mem::swap(&mut self.stdout, &mut ret);
Ok(Some(ret))
}
__WASI_STDERR_FILENO => {
let mut ret = file;
std::mem::swap(&mut self.stderr, &mut ret);
Ok(Some(ret))
}
_ => {
let base_fd = self.get_fd(fd).map_err(WasiFsError::from_wasi_err)?;
let base_inode = base_fd.inode;
match &mut self.inodes[base_inode].kind {
Kind::File { ref mut handle, .. } => {
let mut ret = Some(file);
std::mem::swap(handle, &mut ret);
Ok(ret)
}
_ => return Err(WasiFsError::NotAFile),
}
}
}
}
*/
fn get_inode_at_path_inner(
&mut self,
@ -666,9 +907,9 @@ impl WasiFs {
pub fn flush(&mut self, fd: __wasi_fd_t) -> Result<(), __wasi_errno_t> {
match fd {
0 => (),
1 => io::stdout().flush().map_err(|_| __WASI_EIO)?,
2 => io::stderr().flush().map_err(|_| __WASI_EIO)?,
__WASI_STDIN_FILENO => (),
__WASI_STDOUT_FILENO => self.stdout.flush().map_err(|_| __WASI_EIO)?,
__WASI_STDERR_FILENO => self.stderr.flush().map_err(|_| __WASI_EIO)?,
_ => {
let fd = self.fd_map.get(&fd).ok_or(__WASI_EBADF)?;
if fd.rights & __WASI_RIGHT_FD_DATASYNC == 0 {
@ -717,7 +958,7 @@ impl WasiFs {
rights_inheriting: __wasi_rights_t,
flags: __wasi_fdflags_t,
inode: Inode,
) -> Result<u32, __wasi_errno_t> {
) -> Result<__wasi_fd_t, __wasi_errno_t> {
let idx = self.next_fd.get();
self.next_fd.set(idx + 1);
self.fd_map.insert(
@ -762,7 +1003,17 @@ impl WasiFs {
pub fn get_stat_for_kind(&self, kind: &Kind) -> Option<__wasi_filestat_t> {
let md = match kind {
Kind::File { handle, path } => match handle {
Some(WasiFile::HostFile(hf)) => hf.metadata().ok()?,
Some(wf) => {
return Some(__wasi_filestat_t {
st_filetype: __WASI_FILETYPE_REGULAR_FILE,
st_size: wf.size(),
st_atim: wf.last_accessed(),
st_mtim: wf.last_modified(),
st_ctim: wf.created_time(),
..__wasi_filestat_t::default()
})
}
None => path.metadata().ok()?,
},
Kind::Dir { path, .. } => path.metadata().ok()?,

View File

@ -9,7 +9,7 @@ use self::types::*;
use crate::{
ptr::{Array, WasmPtr},
state::{
host_file_type_to_wasi_file_type, Fd, Inode, InodeVal, Kind, WasiFile, WasiState,
self, host_file_type_to_wasi_file_type, Fd, Inode, InodeVal, Kind, WasiFile, WasiState,
MAX_SYMLINKS,
},
ExitCode,
@ -27,9 +27,10 @@ pub use unix::*;
#[cfg(any(target_os = "windows"))]
pub use windows::*;
/// This function is not safe
#[allow(clippy::mut_from_ref)]
fn get_wasi_state(ctx: &Ctx) -> &mut WasiState {
unsafe { &mut *(ctx.data as *mut WasiState) }
pub(crate) fn get_wasi_state(ctx: &Ctx) -> &mut WasiState {
unsafe { state::get_wasi_state(&mut *(ctx as *const Ctx as *mut Ctx)) }
}
fn write_bytes<T: Write>(
@ -327,11 +328,6 @@ pub fn fd_close(ctx: &mut Ctx, fd: __wasi_fd_t) -> __wasi_errno_t {
Kind::File { ref mut handle, .. } => {
let mut empty_handle = None;
std::mem::swap(handle, &mut empty_handle);
if let Some(handle_inner) = empty_handle {
handle_inner.close()
} else {
return __WASI_EINVAL;
}
}
Kind::Dir { .. } => return __WASI_EISDIR,
Kind::Root { .. } => return __WASI_EACCES,
@ -642,24 +638,13 @@ pub fn fd_pwrite(
let memory = ctx.memory(0);
let iovs_arr_cell = wasi_try!(iovs.deref(memory, 0, iovs_len));
let nwritten_cell = wasi_try!(nwritten.deref(memory));
let state = get_wasi_state(ctx);
let bytes_written = match fd {
0 => return __WASI_EINVAL,
1 => {
let stdout = io::stdout();
let mut handle = stdout.lock();
wasi_try!(write_bytes(handle, memory, iovs_arr_cell))
}
2 => {
let stderr = io::stderr();
let mut handle = stderr.lock();
wasi_try!(write_bytes(handle, memory, iovs_arr_cell))
}
__WASI_STDIN_FILENO => return __WASI_EINVAL,
__WASI_STDOUT_FILENO => wasi_try!(write_bytes(&mut state.fs.stdout, memory, iovs_arr_cell)),
__WASI_STDERR_FILENO => wasi_try!(write_bytes(&mut state.fs.stderr, memory, iovs_arr_cell)),
_ => {
let state = get_wasi_state(ctx);
let fd_entry = wasi_try!(state.fs.fd_map.get_mut(&fd).ok_or(__WASI_EBADF));
if !has_rights(fd_entry.rights, __WASI_RIGHT_FD_WRITE) {
@ -740,17 +725,12 @@ pub fn fd_read(
}
Ok(bytes_read)
}
let state = get_wasi_state(ctx);
let bytes_read = match fd {
0 => {
let stdin = io::stdin();
let mut handle = stdin.lock();
wasi_try!(read_bytes(handle, memory, iovs_arr_cell))
}
1 | 2 => return __WASI_EINVAL,
__WASI_STDIN_FILENO => wasi_try!(read_bytes(&mut state.fs.stdin, memory, iovs_arr_cell)),
__WASI_STDOUT_FILENO | __WASI_STDERR_FILENO => return __WASI_EINVAL,
_ => {
let state = get_wasi_state(ctx);
let fd_entry = wasi_try!(state.fs.fd_map.get_mut(&fd).ok_or(__WASI_EBADF));
if !has_rights(fd_entry.rights, __WASI_RIGHT_FD_READ) {
@ -1062,22 +1042,12 @@ pub fn fd_write(
let memory = ctx.memory(0);
let iovs_arr_cell = wasi_try!(iovs.deref(memory, 0, iovs_len));
let nwritten_cell = wasi_try!(nwritten.deref(memory));
let state = get_wasi_state(ctx);
let bytes_written = match fd {
0 => return __WASI_EINVAL,
1 => {
let stdout = io::stdout();
let mut handle = stdout.lock();
wasi_try!(write_bytes(handle, memory, iovs_arr_cell))
}
2 => {
let stderr = io::stderr();
let mut handle = stderr.lock();
wasi_try!(write_bytes(handle, memory, iovs_arr_cell))
}
__WASI_STDIN_FILENO => return __WASI_EINVAL,
__WASI_STDOUT_FILENO => wasi_try!(write_bytes(&mut state.fs.stdout, memory, iovs_arr_cell)),
__WASI_STDERR_FILENO => wasi_try!(write_bytes(&mut state.fs.stderr, memory, iovs_arr_cell)),
_ => {
let state = get_wasi_state(ctx);
let fd_entry = wasi_try!(state.fs.fd_map.get_mut(&fd).ok_or(__WASI_EBADF));
@ -1436,7 +1406,7 @@ pub fn path_open(
.create(o_flags & __WASI_O_CREAT != 0)
.truncate(o_flags & __WASI_O_TRUNC != 0);
*handle = Some(WasiFile::HostFile(wasi_try!(open_options
*handle = Some(Box::new(wasi_try!(open_options
.open(&path)
.map_err(|_| __WASI_EIO))));
}
@ -1495,12 +1465,14 @@ pub fn path_open(
.write(true)
.create_new(true);
Some(WasiFile::HostFile(wasi_try!(open_options
.open(&new_file_host_path)
.map_err(|e| {
Some(
Box::new(wasi_try!(open_options.open(&new_file_host_path).map_err(
|e| {
debug!("Error opening file {}", e);
__WASI_EIO
}))))
}
))) as Box<dyn WasiFile>,
)
};
let new_inode = {