Add serialization for WASI state - wip

This commit is contained in:
Mark McCaskey
2019-08-26 17:11:56 -07:00
parent 854b2013ea
commit 51faeed2cf
7 changed files with 197 additions and 60 deletions

3
Cargo.lock generated
View File

@ -535,6 +535,7 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -1651,11 +1652,13 @@ dependencies = [
name = "wasmer-wasi"
version = "0.6.0"
dependencies = [
"bincode 1.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"generational-arena 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
"wasmer-runtime-core 0.6.0",
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",

View File

@ -8,14 +8,16 @@ repository = "https://github.com/wasmerio/wasmer"
edition = "2018"
[dependencies]
wasmer-runtime-core = { path = "../runtime-core", version = "0.6.0" }
libc = "0.2.60"
rand = "0.7.0"
# wasmer-runtime-abi = { path = "../runtime-abi" }
generational-arena = "0.2.2"
log = "0.4.8"
bincode = "1"
byteorder = "1.3.2"
generational-arena = { version = "0.2.2", features = ["serde"] }
libc = "0.2.60"
log = "0.4.8"
rand = "0.7.0"
time = "0.1.42"
serde = { version = "1", features = ["derive"] }
# wasmer-runtime-abi = { path = "../runtime-abi" }
wasmer-runtime-core = { path = "../runtime-core", version = "0.6.0" }
[target.'cfg(windows)'.dependencies]
winapi = "0.3.7"

View File

@ -49,8 +49,8 @@ pub fn generate_import_object(
let state = Box::new(WasiState {
fs: WasiFs::new(&preopened_files, &mapped_dirs).unwrap(),
args: &args[..],
envs: &envs[..],
args,
envs,
});
(

View File

@ -8,6 +8,7 @@ pub use self::types::*;
use crate::syscalls::types::*;
use generational_arena::Arena;
pub use generational_arena::Index as Inode;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::{
borrow::Borrow,
@ -35,7 +36,7 @@ pub unsafe fn get_wasi_state(ctx: &mut Ctx) -> &mut WasiState {
pub const MAX_SYMLINKS: u32 = 128;
/// A file that Wasi knows about that may or may not be open
#[derive(Debug)]
#[derive(Debug, Serialize, Deserialize)]
pub struct InodeVal {
pub stat: __wasi_filestat_t,
pub is_preopened: bool,
@ -43,28 +44,10 @@ 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)]
#[derive(Debug, Serialize, Deserialize)]
pub enum Kind {
File {
#[serde(skip)]
/// the open file, if it's open
handle: Option<Box<dyn WasiFile>>,
/// The path on the host system where the file is located
@ -106,15 +89,24 @@ pub enum Kind {
},
}
#[derive(Debug)]
#[derive(Debug, Serialize, Deserialize)]
pub struct Fd {
pub rights: __wasi_rights_t,
pub rights_inheriting: __wasi_rights_t,
pub flags: __wasi_fdflags_t,
pub offset: u64,
pub open_flags: u16,
pub inode: Inode,
}
impl Fd {
pub const READ: u16 = 1;
pub const WRITE: u16 = 2;
pub const APPEND: u16 = 4;
pub const TRUNCATE: u16 = 8;
pub const CREATE: u16 = 16;
}
#[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
@ -176,7 +168,7 @@ impl WasiFs {
& (!__WASI_RIGHT_PATH_REMOVE_DIRECTORY)*/;
let inode = wasi_fs.create_virtual_root();
let fd = wasi_fs
.create_fd(root_rights, root_rights, 0, inode)
.create_fd(root_rights, root_rights, 0, Fd::READ, inode)
.expect("Could not create root fd");
wasi_fs.preopen_fds.push(fd);
inode
@ -211,7 +203,13 @@ impl WasiFs {
)
})?;
let fd = wasi_fs
.create_fd(default_rights, default_rights, 0, inode)
.create_fd(
default_rights,
default_rights,
0,
Fd::READ | Fd::WRITE,
inode,
)
.expect("Could not open fd");
if let Kind::Root { entries } = &mut wasi_fs.inodes[root_inode].kind {
// todo handle collisions
@ -249,7 +247,13 @@ impl WasiFs {
)
})?;
let fd = wasi_fs
.create_fd(default_rights, default_rights, 0, inode)
.create_fd(
default_rights,
default_rights,
0,
Fd::READ | Fd::WRITE,
inode,
)
.expect("Could not open fd");
if let Kind::Root { entries } = &mut wasi_fs.inodes[root_inode].kind {
// todo handle collisions
@ -276,6 +280,7 @@ impl WasiFs {
&mut self,
base: __wasi_fd_t,
file: Box<dyn WasiFile>,
open_flags: u16,
name: String,
rights: __wasi_rights_t,
rights_inheriting: __wasi_rights_t,
@ -312,7 +317,7 @@ impl WasiFs {
_ => unreachable!("Dir or Root became not Dir or Root"),
}
self.create_fd(rights, rights_inheriting, flags, inode)
self.create_fd(rights, rights_inheriting, flags, open_flags, inode)
.map_err(WasiFsError::from_wasi_err)
}
_ => Err(WasiFsError::BaseNotDirectory),
@ -754,10 +759,13 @@ impl WasiFs {
let inode = &mut self.inodes[fd.inode];
match &mut inode.kind {
Kind::File {
handle: Some(handle),
..
} => handle.flush().map_err(|_| __WASI_EIO)?,
Kind::File { handle, .. } => {
if let Some(file) = handle {
file.flush().map_err(|_| __WASI_EIO)?
} else {
return Err(__WASI_EIO);
}
}
// TODO: verify this behavior
Kind::Dir { .. } => return Err(__WASI_EISDIR),
Kind::Symlink { .. } => unimplemented!(),
@ -810,6 +818,7 @@ impl WasiFs {
rights: __wasi_rights_t,
rights_inheriting: __wasi_rights_t,
flags: __wasi_fdflags_t,
open_flags: u16,
inode: Inode,
) -> Result<__wasi_fd_t, __wasi_errno_t> {
let idx = self.next_fd.get();
@ -821,6 +830,7 @@ impl WasiFs {
rights_inheriting,
flags,
offset: 0,
open_flags,
inode,
},
);
@ -922,13 +932,80 @@ impl WasiFs {
..__wasi_filestat_t::default()
})
}
pub(crate) fn unfreeze(bytes: &[u8]) -> Option<(WasiFs, &[u8])> {
unimplemented!()
Some((Self {
preopen_fds,
name_map,
inodes,
fd_map,
next_fd,
inode_counter,
orphan_fds,
stdout,
stderr,
stdin
}, unimplemented!()))
}
pub(crate) fn freeze(&self) -> Option<Vec<u8>> {
let mut out = vec![];
// store pointer to stdout, stderr, and stdin here I guess
// hmmm
out.append(&mut bincode::serialize(&self.preopen_fds).ok()?);
out.append(&mut bincode::serialize(&self.name_map).ok()?);
out.append(&mut bincode::serialize(&self.inodes).ok()?);
out.append(&mut bincode::serialize(&self.fd_map).ok()?);
out.append(&mut bincode::serialize(&self.next_fd).ok()?);
out.append(&mut bincode::serialize(&self.inode_counter).ok()?);
out.append(&mut bincode::serialize(&self.orphan_fds).ok()?);
out.append(&mut self.stdout.to_bytes()?);
out.append(&mut self.stderr.to_bytes()?);
out.append(&mut self.stdin.to_bytes()?);
Some(out)
}
}
#[derive(Debug)]
pub struct WasiState<'a> {
pub struct WasiState {
pub fs: WasiFs,
pub args: &'a [Vec<u8>],
pub envs: &'a [Vec<u8>],
pub args: Vec<Vec<u8>>,
pub envs: Vec<Vec<u8>>,
}
impl WasiState {
/// Turn the WasiState into bytes
pub fn freeze(&self) -> Option<Vec<u8>> {
let wasi_fs = self.wasi_fs.freeze()?;
let args = bincode::serialize(&self.args).ok()?;
let envs = bincode::serialize(&self.envs).ok()?;
Some(
wasi_fs
.into_iter()
.chain(args.into_iter())
.chain(envs.into_iter())
.collect(),
)
}
/// Get a WasiState from bytes
pub fn unfreeze<F>(bytes: &[u8], deserialize_fns: Vec<Box<F>>) -> Option<Self>
where
F: Fn(&[u8]) -> Option<Box<dyn WasiFile>>,
{
let (wasi_fs, remaining_bytes) = WasiFs::unfreeze(bytes)?;
let (args, envs): (Vec<Vec<u8>>, Vec<Vec<u8>>) =
bincode::deserialize(remaining_bytes).ok()?;
Some(Self {
fs: wasi_fs,
args,
envs,
})
}
}
pub fn host_file_type_to_wasi_file_type(file_type: fs::FileType) -> __wasi_filetype_t {

View File

@ -4,7 +4,7 @@ use crate::syscalls::types::*;
use std::convert::TryInto;
use std::{
fs,
io::{self, Read, Seek, Write},
io::{self, Read, Seek, SeekFrom, Write},
path::PathBuf,
time::SystemTime,
};
@ -178,6 +178,29 @@ pub trait WasiFile: std::fmt::Debug + Write + Read + Seek {
fn get_raw_fd(&self) -> Option<i32> {
None
}
/// Try to turn the file into bytes
fn to_bytes(&self) -> Option<Vec<u8>>;
}
/// Tries to read a WASI file out of bytes
/// with default parsers for stdin/stdout/stderr and `HostFile`
pub fn wasi_file_from_bytes(bytes: &[u8]) -> Option<Box<dyn WasiFile>> {
Some(if b"stdout"[..] == bytes[.."stdout".len()] {
Box::new(Stdout(std::io::stdout()))
} else if b"stderr"[..] == bytes[.."stderr".len()] {
Box::new(Stderr(std::io::stderr()))
} else if b"stdin"[..] == bytes[.."stdin".len()] {
Box::new(Stdin(std::io::stdin()))
} else if b"host_file"[..] == bytes[.."host_file".len()] {
unimplemented!();
} else {
return None;
})
}
pub(crate) fn serialize_file<E: Default>(file: Box<dyn WasiFile>) -> Result<Vec<u8>, E> {
file.to_bytes().ok_or_else(|| Default::default())
}
#[derive(Debug, Clone)]
@ -485,6 +508,23 @@ impl WasiFile for HostFile {
"HostFile::get_raw_fd in WasiFile is not implemented for non-Unix-like targets yet"
);
}
fn to_bytes(&self) -> Option<Vec<u8>> {
let mut out = vec![];
for c in "host_file".chars() {
out.push(c as u8);
}
let cursor = self.inner.seek(SeekFrom::Current(0)).ok()?;
// store r/w/append info here? or is this handeled somewhere else?
for b in cursor.to_le_bytes().into_iter() {
out.push(*b);
}
for b in self.host_path.to_string_lossy().bytes() {
out.push(b);
}
Some(out)
}
}
impl From<io::Error> for WasiFsError {
@ -591,6 +631,10 @@ impl WasiFile for Stdout {
"Stdout::get_raw_fd in WasiFile is not implemented for non-Unix-like targets yet"
);
}
fn to_bytes(&self) -> Option<Vec<u8>> {
Some("stdout".chars().map(|c| c as u8).collect())
}
}
#[derive(Debug)]
@ -670,6 +714,10 @@ impl WasiFile for Stderr {
"Stderr::get_raw_fd in WasiFile is not implemented for non-Unix-like targets yet"
);
}
fn to_bytes(&self) -> Option<Vec<u8>> {
Some("stderr".chars().map(|c| c as u8).collect())
}
}
#[derive(Debug)]
@ -773,6 +821,10 @@ impl WasiFile for Stdin {
"Stdin::get_raw_fd in WasiFile is not implemented for non-Unix-like targets yet"
);
}
fn to_bytes(&self) -> Option<Vec<u8>> {
Some("stdin".chars().map(|c| c as u8).collect())
}
}
/*

View File

@ -683,23 +683,11 @@ pub fn fd_pread(
match &mut state.fs.inodes[inode].kind {
Kind::File { handle, .. } => {
if let Some(h) = handle {
let current_pos =
wasi_try!(h.seek(std::io::SeekFrom::Current(0)).ok(), __WASI_EIO);
wasi_try!(
h.seek(std::io::SeekFrom::Start(offset as u64)).ok(),
__WASI_EIO
);
let bytes_read = wasi_try!(read_bytes(h, memory, iov_cells));
// reborrow so we can seek it back (the &mut gets moved into `read_bytes`
// and we can't use it after)
// If you're in the future and there's a nicer way to do this, please
// clean up this code
if let Some(h) = handle {
wasi_try!(
h.seek(std::io::SeekFrom::Start(current_pos)).ok(),
__WASI_EIO
);
}
bytes_read
} else {
return __WASI_EINVAL;
@ -1675,6 +1663,7 @@ pub fn path_open(
if let Ok(m) = maybe_inode {
&state.fs.inodes[m];
}
let mut open_flags = 0;
// TODO: traverse rights of dirs properly
// COMMENTED OUT: WASI isn't giving appropriate rights here when opening
@ -1702,7 +1691,16 @@ pub fn path_open(
.write(adjusted_rights & __WASI_RIGHT_FD_WRITE != 0)
.create(o_flags & __WASI_O_CREAT != 0)
.truncate(o_flags & __WASI_O_TRUNC != 0);
open_flags |= Fd::READ;
if adjusted_rights & __WASI_RIGHT_FD_WRITE != 0 {
open_flags |= Fd::WRITE;
}
if o_flags & __WASI_O_CREAT != 0 {
open_flags |= Fd::CREATE;
}
if o_flags & __WASI_O_TRUNC != 0 {
open_flags |= Fd::TRUNCATE;
}
*handle = Some(Box::new(HostFile::new(
wasi_try!(open_options.open(&path).map_err(|_| __WASI_EIO)),
path.to_path_buf(),
@ -1762,6 +1760,7 @@ pub fn path_open(
// write access is required for creating a file
.write(true)
.create_new(true);
open_flags |= Fd::READ | Fd::WRITE | Fd::CREATE | Fd::TRUNCATE;
Some(Box::new(HostFile::new(
wasi_try!(open_options.open(&new_file_host_path).map_err(|e| {
@ -1800,10 +1799,13 @@ pub fn path_open(
// TODO: check and reduce these
// TODO: ensure a mutable fd to root can never be opened
let out_fd =
wasi_try!(state
.fs
.create_fd(adjusted_rights, fs_rights_inheriting, fs_flags, inode));
let out_fd = wasi_try!(state.fs.create_fd(
adjusted_rights,
fs_rights_inheriting,
fs_flags,
open_flags,
inode
));
fd_cell.set(out_fd);

View File

@ -2,6 +2,7 @@
use crate::ptr::{Array, WasmPtr};
use byteorder::{ReadBytesExt, WriteBytesExt, LE};
use serde::{Deserialize, Serialize};
use std::fmt;
use std::mem;
use wasmer_runtime_core::types::ValueType;
@ -316,7 +317,7 @@ pub type __wasi_filedelta_t = i64;
pub type __wasi_filesize_t = u64;
#[derive(Copy, Clone, PartialEq, Eq)]
#[derive(Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[repr(C)]
pub struct __wasi_filestat_t {
pub st_dev: __wasi_device_t,