mirror of
https://github.com/fluencelabs/wasmer
synced 2025-06-21 04:31:33 +00:00
add preopened fd and fix/improve fs syscalls (WIP)
This commit is contained in:
@ -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[..],
|
||||
});
|
||||
|
@ -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> {
|
||||
|
@ -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 {
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
)
|
||||
|
Reference in New Issue
Block a user