add preopened fd and fix/improve fs syscalls (WIP)

This commit is contained in:
Mark McCaskey
2019-04-10 18:23:25 -07:00
parent 870faf9838
commit 91af7cf8a8
5 changed files with 262 additions and 53 deletions

View File

@ -17,7 +17,11 @@ pub use self::utils::is_wasi_module;
use wasmer_runtime_core::{func, import::ImportObject, imports};
pub fn generate_import_object(args: Vec<Vec<u8>>, envs: Vec<Vec<u8>>) -> ImportObject {
pub fn generate_import_object(
args: Vec<Vec<u8>>,
envs: Vec<Vec<u8>>,
preopened_files: Vec<String>,
) -> ImportObject {
let state_gen = move || {
fn state_destructor(data: *mut c_void) {
unsafe {
@ -26,7 +30,7 @@ pub fn generate_import_object(args: Vec<Vec<u8>>, envs: Vec<Vec<u8>>) -> ImportO
}
let state = Box::new(WasiState {
fs: WasiFs::new().unwrap(),
fs: WasiFs::new(&preopened_files).unwrap(),
args: &args[..],
envs: &envs[..],
});

View File

@ -7,14 +7,89 @@ use generational_arena::{Arena, Index as Inode};
use hashbrown::hash_map::{Entry, HashMap};
use std::{
cell::Cell,
io::{self, Write},
fs,
io::{self, Read, Seek, Write},
time::SystemTime,
};
use wasmer_runtime_core::debug;
use zbox::{init_env as zbox_init_env, File, FileType, OpenOptions, Repo, RepoOpener};
use zbox::{init_env as zbox_init_env, FileType, OpenOptions, Repo, RepoOpener};
pub const MAX_SYMLINKS: usize = 100;
pub enum WasiFile {
ZboxFile(zbox::File),
HostFile(fs::File),
}
impl Write for WasiFile {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
match self {
WasiFile::ZboxFile(zbf) => zbf.write(buf),
WasiFile::HostFile(hf) => hf.write(buf),
}
}
fn flush(&mut self) -> io::Result<()> {
match self {
WasiFile::ZboxFile(zbf) => zbf.flush(),
WasiFile::HostFile(hf) => hf.flush(),
}
}
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
match self {
WasiFile::ZboxFile(zbf) => zbf.write_all(buf),
WasiFile::HostFile(hf) => hf.write_all(buf),
}
}
fn write_fmt(&mut self, fmt: ::std::fmt::Arguments) -> io::Result<()> {
match self {
WasiFile::ZboxFile(zbf) => zbf.write_fmt(fmt),
WasiFile::HostFile(hf) => hf.write_fmt(fmt),
}
}
}
impl Read for WasiFile {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
match self {
WasiFile::ZboxFile(zbf) => zbf.read(buf),
WasiFile::HostFile(hf) => hf.read(buf),
}
}
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
match self {
WasiFile::ZboxFile(zbf) => zbf.read_to_end(buf),
WasiFile::HostFile(hf) => hf.read_to_end(buf),
}
}
fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
match self {
WasiFile::ZboxFile(zbf) => zbf.read_to_string(buf),
WasiFile::HostFile(hf) => hf.read_to_string(buf),
}
}
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
match self {
WasiFile::ZboxFile(zbf) => zbf.read_exact(buf),
WasiFile::HostFile(hf) => hf.read_exact(buf),
}
}
}
impl Seek for WasiFile {
fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
match self {
WasiFile::ZboxFile(zbf) => zbf.seek(pos),
WasiFile::HostFile(hf) => hf.seek(pos),
}
}
}
pub struct InodeVal {
pub stat: __wasi_filestat_t,
pub is_preopened: bool,
@ -25,10 +100,10 @@ pub struct InodeVal {
#[allow(dead_code)]
pub enum Kind {
File {
handle: File,
handle: WasiFile,
},
Dir {
handle: File,
handle: WasiFile,
/// The entries of a directory are lazily filled.
entries: HashMap<String, Inode>,
},
@ -40,7 +115,7 @@ pub enum Kind {
},
}
#[derive(Clone)]
#[derive(Clone, Debug)]
pub struct Fd {
pub rights: __wasi_rights_t,
pub rights_inheriting: __wasi_rights_t,
@ -50,7 +125,7 @@ pub struct Fd {
}
pub struct WasiFs {
// pub repo: Repo,
pub repo: Repo,
pub name_map: HashMap<String, Inode>,
pub inodes: Arena<InodeVal>,
pub fd_map: HashMap<u32, Fd>,
@ -59,26 +134,58 @@ pub struct WasiFs {
}
impl WasiFs {
pub fn new() -> Result<Self, String> {
pub fn new(preopened_files: &[String]) -> Result<Self, String> {
debug!("wasi::fs::init");
zbox_init_env();
debug!("wasi::fs::repo");
// let repo = RepoOpener::new()
// .create(true)
// .open("mem://wasmer-test-fs", "")
// .map_err(|e| e.to_string())?;
let repo = RepoOpener::new()
.create(true)
.open("mem://wasmer-test-fs", "")
.map_err(|e| e.to_string())?;
debug!("wasi::fs::inodes");
let inodes = Arena::new();
let res = Ok(Self {
// repo: repo,
let mut wasi_fs = Self {
repo: repo,
name_map: HashMap::new(),
inodes: inodes,
fd_map: HashMap::new(),
next_fd: Cell::new(3),
inode_counter: Cell::new(1000),
});
};
for file in preopened_files {
debug!("Attempting to preopen {}", &file);
// TODO: think about this
let default_rights = 0x1FFFFFFF;
let cur_file: fs::File = fs::File::open(file).expect("Could not find file");
let kind = if cur_file.metadata().unwrap().is_dir() {
// it seems bad to open every file recursively; can do it lazily though
Kind::Dir {
handle: WasiFile::HostFile(cur_file),
entries: Default::default(),
}
} else {
/*Kind::File {
handle: WasiFile::HostFile(cur_file),
}*/
return Err(format!(
"WASI only supports pre-opened directories right now; found \"{}\"",
file
));
};
let inode_val = InodeVal {
stat: __wasi_filestat_t::default(),
is_preopened: true,
// this is incorrect
name: file.clone(),
kind,
};
let inode = wasi_fs.inodes.insert(inode_val);
wasi_fs
.create_fd(default_rights, default_rights, 0, inode)
.expect("Could not open fd");
}
debug!("wasi::fs::end");
res
Ok(wasi_fs)
}
#[allow(dead_code)]
@ -195,7 +302,9 @@ impl WasiFs {
pub fn fdstat(&self, fd: __wasi_fd_t) -> Result<__wasi_fdstat_t, __wasi_errno_t> {
let fd = self.fd_map.get(&fd).ok_or(__WASI_EBADF)?;
Ok(__wasi_fdstat_t {
debug!("fdstat: {:?}", fd);
dbg!(Ok(__wasi_fdstat_t {
fs_filetype: match self.inodes[fd.inode].kind {
Kind::File { .. } => __WASI_FILETYPE_REGULAR_FILE,
Kind::Dir { .. } => __WASI_FILETYPE_DIRECTORY,
@ -205,12 +314,13 @@ impl WasiFs {
fs_flags: fd.flags,
fs_rights_base: fd.rights,
fs_rights_inheriting: fd.rights, // TODO(lachlan): Is this right?
})
}))
}
pub fn prestat_fd(&self, fd: __wasi_fd_t) -> Result<__wasi_prestat_t, __wasi_errno_t> {
let fd = self.fd_map.get(&fd).ok_or(__WASI_EBADF)?;
debug!("in prestat_fd {:?}", fd);
let inode_val = &self.inodes[fd.inode];
if inode_val.is_preopened {
@ -272,6 +382,19 @@ impl WasiFs {
);
Ok(idx)
}
/*pub fn create_file_at_fd(
&mut self,
parent: __wasi_fd_t,
path: String,
fs_rights_base: __wasi_rights_t,
fs_rights_inheriting: __wasi_rights_t,
fs_flags: fs_flags,
) -> Result<__wasi_fd_t, __wasi_errno_t> {
let fd = self.fd_map.get(&fd).ok_or(__WASI_EBADF)?;
Ok()
}*/
}
pub struct WasiState<'a> {

View File

@ -8,11 +8,11 @@ pub mod windows;
use self::types::*;
use crate::{
ptr::{Array, WasmPtr},
state::{Fd, Kind, WasiState, MAX_SYMLINKS},
state::{Fd, InodeVal, Kind, WasiFile, WasiState, MAX_SYMLINKS},
};
use rand::{thread_rng, Rng};
use std::cell::Cell;
use std::io::{self, Read, Write};
use std::io::{self, Read, Seek, Write};
use wasmer_runtime_core::{debug, memory::Memory, vm::Ctx};
#[cfg(any(target_os = "linux", target_os = "macos"))]
@ -335,7 +335,7 @@ pub fn fd_fdstat_get(
let buf = wasi_try!(buf.deref(memory));
buf.set(stat);
__WASI_EFAULT
__WASI_ESUCCESS
}
/// ### `fd_fdstat_set_flags()`
@ -532,27 +532,26 @@ pub fn fd_prestat_dir_name(
fd, path_len
);
let memory = ctx.memory(0);
let path_chars = wasi_try!(path.deref(memory, 0, path_len));
if let Ok(path_chars) = path.deref(memory, 0, path_len) {
debug!(
"=> path: {}",
path_chars
.iter()
.map(|c| c.get() as char)
.collect::<String>()
);
if true
/* check if dir */
{
// get name
// write name
// if overflow __WASI_EOVERFLOW
let state = get_wasi_state(ctx);
let real_fd = wasi_try!(state.fs.fd_map.get(&fd).ok_or(__WASI_EBADF));
let inode_val = &state.fs.inodes[real_fd.inode];
// check inode-val.is_preopened?
if let Kind::Dir { .. } = inode_val.kind {
// TODO: verify this: null termination, etc
if inode_val.name.len() <= path_len as usize {
for (i, c) in inode_val.name.bytes().enumerate() {
path_chars[i].set(c);
}
__WASI_ESUCCESS
} else {
__WASI_ENOTDIR
__WASI_EOVERFLOW
}
} else {
__WASI_EFAULT
__WASI_ENOTDIR
}
}
@ -612,7 +611,7 @@ pub fn fd_pwrite(
let bytes_written = match &mut inode.kind {
Kind::File { handle } => {
// TODO: adjust by offset
handle.seek(::std::io::SeekFrom::Start(offset as u64));
wasi_try!(write_bytes(handle, memory, iovs_arr_cell))
}
Kind::Dir { .. } => {
@ -670,14 +669,18 @@ pub fn fd_read(
for iov in iovs_arr_cell {
let iov_inner = iov.get();
let bytes = iov_inner.buf.deref(memory, 0, iov_inner.buf_len)?;
let bytes = iov_inner.buf.deref(memory, 0, dbg!(iov_inner.buf_len))?;
let mut raw_bytes: &mut [u8] =
unsafe { &mut *(bytes as *const [_] as *mut [_] as *mut [u8]) };
bytes_read += reader.read(raw_bytes).map_err(|_| __WASI_EIO)? as u32;
bytes_read += dbg!(reader.read(raw_bytes).map_err(|e| {
dbg!(e);
__WASI_EIO
})? as u32);
}
Ok(bytes_read)
}
debug!("think-fish");
let bytes_read = match fd {
0 => {
let stdin = io::stdin();
@ -699,7 +702,10 @@ pub fn fd_read(
let inode = &mut state.fs.inodes[fd_entry.inode];
let bytes_read = match &mut inode.kind {
Kind::File { handle } => wasi_try!(read_bytes(handle, memory, iovs_arr_cell)),
Kind::File { handle } => {
handle.seek(::std::io::SeekFrom::Start(offset as u64));
wasi_try!(read_bytes(handle, memory, iovs_arr_cell))
}
Kind::Dir { .. } => {
// TODO: verify
return __WASI_EISDIR;
@ -716,7 +722,7 @@ pub fn fd_read(
}
};
nread_cell.set(bytes_read);
nread_cell.set(dbg!(bytes_read));
__WASI_ESUCCESS
}
@ -798,7 +804,7 @@ pub fn fd_seek(
whence: __wasi_whence_t,
newoffset: WasmPtr<__wasi_filesize_t>,
) -> __wasi_errno_t {
debug!("wasi::fd_seek: fd={}", fd);
debug!("wasi::fd_seek: fd={}, offset={}", fd, offset);
let memory = ctx.memory(0);
let state = get_wasi_state(ctx);
let new_offset_cell = wasi_try!(newoffset.deref(memory));
@ -809,9 +815,11 @@ pub fn fd_seek(
return __WASI_EACCES;
}
debug!("Hmml");
// TODO: handle case if fd is a dir?
match whence {
__WASI_WHENCE_CUR => fd_entry.offset = (fd_entry.offset as i64 + offset) as u64,
__WASI_WHENCE_CUR => fd_entry.offset = (dbg!(fd_entry.offset) as i64 + offset) as u64,
__WASI_WHENCE_END => unimplemented!(),
__WASI_WHENCE_SET => fd_entry.offset = offset as u64,
_ => return __WASI_EINVAL,
@ -920,7 +928,11 @@ pub fn fd_write(
let inode = &mut state.fs.inodes[fd_entry.inode];
let bytes_written = match &mut inode.kind {
Kind::File { handle } => wasi_try!(write_bytes(handle, memory, iovs_arr_cell)),
Kind::File { handle } => {
handle.seek(::std::io::SeekFrom::Start(offset as u64));
wasi_try!(write_bytes(handle, memory, iovs_arr_cell))
}
Kind::Dir { .. } => {
// TODO: verify
return __WASI_EISDIR;
@ -937,7 +949,7 @@ pub fn fd_write(
}
};
nwritten_cell.set(bytes_written);
nwritten_cell.set(dbg!(bytes_written));
__WASI_ESUCCESS
}
@ -1209,12 +1221,77 @@ pub fn path_open(
// entry does not exist in parent directory
// check to see if we should create it
if o_flags & __WASI_O_CREAT != 0 {
// REVIEW: the code in this branch was written while very tired
// insert in to directory and set values
//entries.insert(path_segment[0], )
unimplemented!()
let real_opened_file = wasi_try!(::std::fs::OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(&path_vec[0])
.map_err(|_| __WASI_EIO));
debug!("Creating host file {}", &path_vec[0]);
let new_inode = state.fs.inodes.insert(InodeVal {
stat: __wasi_filestat_t::default(),
is_preopened: false,
name: path_vec[0].clone(),
kind: Kind::File {
handle: WasiFile::HostFile(real_opened_file),
},
});
// reborrow
if let Kind::Dir { entries, .. } = &mut state.fs.inodes[working_dir.inode].kind
{
entries.insert(path_vec[0].clone(), new_inode);
}
let new_fd = wasi_try!(state.fs.create_fd(
fs_rights_base,
fs_rights_inheriting,
fs_flags,
new_inode,
));
new_fd
// TODO: technically this could just not be lazily loaded...
/*wasi_try!(state.fs.create_file_at_fd(
dirfd,
path_vec[0],
fs_rights_base,
fs_rights_inheriting,
fs_flags
));*/
} else {
// no entry and can't create it
return __WASI_ENOENT;
// attempt to load it
let real_opened_file = wasi_try!(::std::fs::OpenOptions::new()
.read(true)
.write(true)
.open(&path_vec[0])
.map_err(|_| __WASI_ENOENT));
debug!("Opening host file {}", &path_vec[0]);
let new_inode = state.fs.inodes.insert(InodeVal {
stat: __wasi_filestat_t::default(),
is_preopened: false,
name: path_vec[0].clone(),
kind: Kind::File {
handle: WasiFile::HostFile(real_opened_file),
},
});
// reborrow
if let Kind::Dir { entries, .. } = &mut state.fs.inodes[working_dir.inode].kind
{
entries.insert(path_vec[0].clone(), new_inode);
}
let new_fd = wasi_try!(state.fs.create_fd(
fs_rights_base,
fs_rights_inheriting,
fs_flags,
new_inode,
));
new_fd
}
}
} else {

View File

@ -264,7 +264,7 @@ pub type __wasi_filedelta_t = i64;
pub type __wasi_filesize_t = u64;
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
#[repr(C)]
pub struct __wasi_filestat_t {
pub st_dev: __wasi_device_t,

View File

@ -66,9 +66,13 @@ struct Run {
#[structopt(name = "--", raw(multiple = "true"))]
args: Vec<String>,
/// Emscripten symbol map
#[structopt(long = "em-symbol-map", parse(from_os_str))]
/// Emscripten symbol ma>p
#[structopt(long = "em-symbol-map", parse(from_os_str), group = "emscripten")]
em_symbol_map: Option<PathBuf>,
/// WASI pre-opened file
#[structopt(long = "pre-open", group = "wasi")]
pre_opened_files: Vec<String>,
}
#[derive(Debug, StructOpt)]
@ -248,6 +252,7 @@ fn execute_wasm(options: &Run) -> Result<(), String> {
env::vars()
.map(|(k, v)| format!("{}={}", k, v).into_bytes())
.collect(),
options.pre_opened_files.clone(),
),
None,
)