mirror of
https://github.com/fluencelabs/wasmer
synced 2025-04-21 16:32:13 +00:00
Merge #343
343: add preopened fd and fix/improve fs syscalls; fix wasi memory access r=MarkMcCaskey a=MarkMcCaskey resolves #356 Co-authored-by: Mark McCaskey <mark@wasmer.io> Co-authored-by: Syrus Akbary <me@syrusakbary.com> Co-authored-by: Mark McCaskey <markmccaskey@users.noreply.github.com>
This commit is contained in:
commit
f7e4a09832
@ -5,6 +5,7 @@ All PRs to the Wasmer repository must add to this file.
|
|||||||
Blocks of changes will separated by version increments.
|
Blocks of changes will separated by version increments.
|
||||||
|
|
||||||
## **[Unreleased]**
|
## **[Unreleased]**
|
||||||
|
- [#343](https://github.com/wasmerio/wasmer/pull/343) Implement preopened files for WASI and fix aligment issue when accessing WASI memory
|
||||||
- [#367](https://github.com/wasmerio/wasmer/pull/367) Add caching support to the LLVM backend.
|
- [#367](https://github.com/wasmerio/wasmer/pull/367) Add caching support to the LLVM backend.
|
||||||
- [#366](https://github.com/wasmerio/wasmer/pull/366) Remove `UserTrapper` trait to fix [#365](https://github.com/wasmerio/wasmer/issues/365).
|
- [#366](https://github.com/wasmerio/wasmer/pull/366) Remove `UserTrapper` trait to fix [#365](https://github.com/wasmerio/wasmer/issues/365).
|
||||||
- [#348](https://github.com/wasmerio/wasmer/pull/348) Refactor internal runtime ↔️ backend abstraction.
|
- [#348](https://github.com/wasmerio/wasmer/pull/348) Refactor internal runtime ↔️ backend abstraction.
|
||||||
|
@ -17,7 +17,11 @@ pub use self::utils::is_wasi_module;
|
|||||||
|
|
||||||
use wasmer_runtime_core::{func, import::ImportObject, imports};
|
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 || {
|
let state_gen = move || {
|
||||||
fn state_destructor(data: *mut c_void) {
|
fn state_destructor(data: *mut c_void) {
|
||||||
unsafe {
|
unsafe {
|
||||||
@ -26,7 +30,7 @@ pub fn generate_import_object(args: Vec<Vec<u8>>, envs: Vec<Vec<u8>>) -> ImportO
|
|||||||
}
|
}
|
||||||
|
|
||||||
let state = Box::new(WasiState {
|
let state = Box::new(WasiState {
|
||||||
fs: WasiFs::new().unwrap(),
|
fs: WasiFs::new(&preopened_files).unwrap(),
|
||||||
args: &args[..],
|
args: &args[..],
|
||||||
envs: &envs[..],
|
envs: &envs[..],
|
||||||
});
|
});
|
||||||
|
@ -36,10 +36,12 @@ impl<T: Copy + ValueType> WasmPtr<T, Item> {
|
|||||||
return Err(__WASI_EFAULT);
|
return Err(__WASI_EFAULT);
|
||||||
}
|
}
|
||||||
unsafe {
|
unsafe {
|
||||||
let cell_ptr = memory
|
// clears bits below aligment amount (assumes power of 2) to align pointer
|
||||||
.view::<T>()
|
let aligner = |ptr: usize, align: usize| ptr & !(align - 1);
|
||||||
.get_unchecked((self.offset() as usize) / mem::size_of::<T>())
|
let cell_ptr = aligner(
|
||||||
as *const _;
|
memory.view::<u8>().as_ptr().add(self.offset as usize) as usize,
|
||||||
|
mem::align_of::<T>(),
|
||||||
|
) as *const Cell<T>;
|
||||||
Ok(&*cell_ptr)
|
Ok(&*cell_ptr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,14 +7,91 @@ use generational_arena::{Arena, Index as Inode};
|
|||||||
use hashbrown::hash_map::{Entry, HashMap};
|
use hashbrown::hash_map::{Entry, HashMap};
|
||||||
use std::{
|
use std::{
|
||||||
cell::Cell,
|
cell::Cell,
|
||||||
io::{self, Write},
|
fs,
|
||||||
|
io::{self, Read, Seek, Write},
|
||||||
time::SystemTime,
|
time::SystemTime,
|
||||||
};
|
};
|
||||||
use wasmer_runtime_core::debug;
|
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 const MAX_SYMLINKS: usize = 100;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct InodeVal {
|
pub struct InodeVal {
|
||||||
pub stat: __wasi_filestat_t,
|
pub stat: __wasi_filestat_t,
|
||||||
pub is_preopened: bool,
|
pub is_preopened: bool,
|
||||||
@ -22,13 +99,57 @@ pub struct InodeVal {
|
|||||||
pub kind: Kind,
|
pub kind: Kind,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl InodeVal {
|
||||||
|
// TODO: clean this up
|
||||||
|
pub fn from_file_metadata(
|
||||||
|
metadata: &std::fs::Metadata,
|
||||||
|
name: String,
|
||||||
|
is_preopened: bool,
|
||||||
|
kind: Kind,
|
||||||
|
) -> Self {
|
||||||
|
InodeVal {
|
||||||
|
stat: __wasi_filestat_t {
|
||||||
|
st_filetype: if metadata.is_dir() {
|
||||||
|
__WASI_FILETYPE_DIRECTORY
|
||||||
|
} else {
|
||||||
|
__WASI_FILETYPE_REGULAR_FILE
|
||||||
|
},
|
||||||
|
st_size: metadata.len(),
|
||||||
|
st_atim: metadata
|
||||||
|
.accessed()
|
||||||
|
.ok()
|
||||||
|
.and_then(|sys_time| sys_time.duration_since(SystemTime::UNIX_EPOCH).ok())
|
||||||
|
.map(|duration| duration.as_nanos() as u64)
|
||||||
|
.unwrap_or(0),
|
||||||
|
st_ctim: metadata
|
||||||
|
.created()
|
||||||
|
.ok()
|
||||||
|
.and_then(|sys_time| sys_time.duration_since(SystemTime::UNIX_EPOCH).ok())
|
||||||
|
.map(|duration| duration.as_nanos() as u64)
|
||||||
|
.unwrap_or(0),
|
||||||
|
st_mtim: metadata
|
||||||
|
.modified()
|
||||||
|
.ok()
|
||||||
|
.and_then(|sys_time| sys_time.duration_since(SystemTime::UNIX_EPOCH).ok())
|
||||||
|
.map(|duration| duration.as_nanos() as u64)
|
||||||
|
.unwrap_or(0),
|
||||||
|
..__wasi_filestat_t::default()
|
||||||
|
},
|
||||||
|
is_preopened,
|
||||||
|
name,
|
||||||
|
kind,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum Kind {
|
pub enum Kind {
|
||||||
File {
|
File {
|
||||||
handle: File,
|
handle: WasiFile,
|
||||||
},
|
},
|
||||||
Dir {
|
Dir {
|
||||||
handle: File,
|
handle: WasiFile,
|
||||||
/// The entries of a directory are lazily filled.
|
/// The entries of a directory are lazily filled.
|
||||||
entries: HashMap<String, Inode>,
|
entries: HashMap<String, Inode>,
|
||||||
},
|
},
|
||||||
@ -40,7 +161,7 @@ pub enum Kind {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Fd {
|
pub struct Fd {
|
||||||
pub rights: __wasi_rights_t,
|
pub rights: __wasi_rights_t,
|
||||||
pub rights_inheriting: __wasi_rights_t,
|
pub rights_inheriting: __wasi_rights_t,
|
||||||
@ -50,7 +171,7 @@ pub struct Fd {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct WasiFs {
|
pub struct WasiFs {
|
||||||
// pub repo: Repo,
|
//pub repo: Repo,
|
||||||
pub name_map: HashMap<String, Inode>,
|
pub name_map: HashMap<String, Inode>,
|
||||||
pub inodes: Arena<InodeVal>,
|
pub inodes: Arena<InodeVal>,
|
||||||
pub fd_map: HashMap<u32, Fd>,
|
pub fd_map: HashMap<u32, Fd>,
|
||||||
@ -59,26 +180,56 @@ pub struct WasiFs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl WasiFs {
|
impl WasiFs {
|
||||||
pub fn new() -> Result<Self, String> {
|
pub fn new(preopened_files: &[String]) -> Result<Self, String> {
|
||||||
debug!("wasi::fs::init");
|
debug!("wasi::fs::init");
|
||||||
zbox_init_env();
|
zbox_init_env();
|
||||||
debug!("wasi::fs::repo");
|
debug!("wasi::fs::repo");
|
||||||
// let repo = RepoOpener::new()
|
/*let repo = RepoOpener::new()
|
||||||
// .create(true)
|
.create(true)
|
||||||
// .open("mem://wasmer-test-fs", "")
|
.open("mem://wasmer-test-fs", "")
|
||||||
// .map_err(|e| e.to_string())?;
|
.map_err(|e| e.to_string())?;*/
|
||||||
debug!("wasi::fs::inodes");
|
debug!("wasi::fs::inodes");
|
||||||
let inodes = Arena::new();
|
let inodes = Arena::new();
|
||||||
let res = Ok(Self {
|
let mut wasi_fs = Self {
|
||||||
// repo: repo,
|
//repo: repo,
|
||||||
name_map: HashMap::new(),
|
name_map: HashMap::new(),
|
||||||
inodes: inodes,
|
inodes: inodes,
|
||||||
fd_map: HashMap::new(),
|
fd_map: HashMap::new(),
|
||||||
next_fd: Cell::new(3),
|
next_fd: Cell::new(3),
|
||||||
inode_counter: Cell::new(1000),
|
inode_counter: Cell::new(1000),
|
||||||
});
|
};
|
||||||
|
for file in preopened_files {
|
||||||
|
debug!("Attempting to preopen {}", &file);
|
||||||
|
// TODO: think about this
|
||||||
|
let default_rights = 0x1FFFFFFF; // all rights
|
||||||
|
let cur_file: fs::File = fs::OpenOptions::new()
|
||||||
|
.read(true)
|
||||||
|
.open(file)
|
||||||
|
.expect("Could not find file");
|
||||||
|
let cur_file_metadata = cur_file.metadata().unwrap();
|
||||||
|
let kind = if cur_file_metadata.is_dir() {
|
||||||
|
Kind::Dir {
|
||||||
|
handle: WasiFile::HostFile(cur_file),
|
||||||
|
entries: Default::default(),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Err(format!(
|
||||||
|
"WASI only supports pre-opened directories right now; found \"{}\"",
|
||||||
|
file
|
||||||
|
));
|
||||||
|
};
|
||||||
|
// TODO: handle nested pats in `file`
|
||||||
|
let inode_val =
|
||||||
|
InodeVal::from_file_metadata(&cur_file_metadata, file.clone(), true, kind);
|
||||||
|
|
||||||
|
let inode = wasi_fs.inodes.insert(inode_val);
|
||||||
|
wasi_fs.inodes[inode].stat.st_ino = wasi_fs.inode_counter.get();
|
||||||
|
wasi_fs
|
||||||
|
.create_fd(default_rights, default_rights, 0, inode)
|
||||||
|
.expect("Could not open fd");
|
||||||
|
}
|
||||||
debug!("wasi::fs::end");
|
debug!("wasi::fs::end");
|
||||||
res
|
Ok(wasi_fs)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@ -195,6 +346,8 @@ impl WasiFs {
|
|||||||
pub fn fdstat(&self, fd: __wasi_fd_t) -> Result<__wasi_fdstat_t, __wasi_errno_t> {
|
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)?;
|
let fd = self.fd_map.get(&fd).ok_or(__WASI_EBADF)?;
|
||||||
|
|
||||||
|
debug!("fdstat: {:?}", fd);
|
||||||
|
|
||||||
Ok(__wasi_fdstat_t {
|
Ok(__wasi_fdstat_t {
|
||||||
fs_filetype: match self.inodes[fd.inode].kind {
|
fs_filetype: match self.inodes[fd.inode].kind {
|
||||||
Kind::File { .. } => __WASI_FILETYPE_REGULAR_FILE,
|
Kind::File { .. } => __WASI_FILETYPE_REGULAR_FILE,
|
||||||
@ -204,20 +357,22 @@ impl WasiFs {
|
|||||||
},
|
},
|
||||||
fs_flags: fd.flags,
|
fs_flags: fd.flags,
|
||||||
fs_rights_base: fd.rights,
|
fs_rights_base: fd.rights,
|
||||||
fs_rights_inheriting: fd.rights, // TODO(lachlan): Is this right?
|
fs_rights_inheriting: fd.rights_inheriting, // TODO(lachlan): Is this right?
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn prestat_fd(&self, fd: __wasi_fd_t) -> Result<__wasi_prestat_t, __wasi_errno_t> {
|
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)?;
|
let fd = self.fd_map.get(&fd).ok_or(__WASI_EBADF)?;
|
||||||
|
|
||||||
|
debug!("in prestat_fd {:?}", fd);
|
||||||
let inode_val = &self.inodes[fd.inode];
|
let inode_val = &self.inodes[fd.inode];
|
||||||
|
|
||||||
if inode_val.is_preopened {
|
if inode_val.is_preopened {
|
||||||
Ok(__wasi_prestat_t {
|
Ok(__wasi_prestat_t {
|
||||||
pr_type: __WASI_PREOPENTYPE_DIR,
|
pr_type: __WASI_PREOPENTYPE_DIR,
|
||||||
u: PrestatEnum::Dir {
|
u: PrestatEnum::Dir {
|
||||||
pr_name_len: inode_val.name.len() as u32,
|
// REVIEW:
|
||||||
|
pr_name_len: inode_val.name.len() as u32 + 1,
|
||||||
}
|
}
|
||||||
.untagged(),
|
.untagged(),
|
||||||
})
|
})
|
||||||
|
@ -8,11 +8,11 @@ pub mod windows;
|
|||||||
use self::types::*;
|
use self::types::*;
|
||||||
use crate::{
|
use crate::{
|
||||||
ptr::{Array, WasmPtr},
|
ptr::{Array, WasmPtr},
|
||||||
state::{Fd, Kind, WasiState, MAX_SYMLINKS},
|
state::{Fd, InodeVal, Kind, WasiFile, WasiState, MAX_SYMLINKS},
|
||||||
};
|
};
|
||||||
use rand::{thread_rng, Rng};
|
use rand::{thread_rng, Rng};
|
||||||
use std::cell::Cell;
|
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};
|
use wasmer_runtime_core::{debug, memory::Memory, vm::Ctx};
|
||||||
|
|
||||||
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||||
@ -324,18 +324,21 @@ pub fn fd_datasync(ctx: &mut Ctx, fd: __wasi_fd_t) -> __wasi_errno_t {
|
|||||||
pub fn fd_fdstat_get(
|
pub fn fd_fdstat_get(
|
||||||
ctx: &mut Ctx,
|
ctx: &mut Ctx,
|
||||||
fd: __wasi_fd_t,
|
fd: __wasi_fd_t,
|
||||||
buf: WasmPtr<__wasi_fdstat_t>,
|
buf_ptr: WasmPtr<__wasi_fdstat_t>,
|
||||||
) -> __wasi_errno_t {
|
) -> __wasi_errno_t {
|
||||||
debug!("wasi::fd_fdstat_get: fd={}", fd);
|
debug!(
|
||||||
|
"wasi::fd_fdstat_get: fd={}, buf_ptr={}",
|
||||||
|
fd,
|
||||||
|
buf_ptr.offset()
|
||||||
|
);
|
||||||
let mut state = get_wasi_state(ctx);
|
let mut state = get_wasi_state(ctx);
|
||||||
let memory = ctx.memory(0);
|
let memory = ctx.memory(0);
|
||||||
|
|
||||||
let stat = wasi_try!(state.fs.fdstat(fd));
|
let stat = wasi_try!(state.fs.fdstat(fd));
|
||||||
|
let buf = wasi_try!(buf_ptr.deref(memory));
|
||||||
|
|
||||||
let buf = wasi_try!(buf.deref(memory));
|
|
||||||
buf.set(stat);
|
buf.set(stat);
|
||||||
|
|
||||||
__WASI_EFAULT
|
__WASI_ESUCCESS
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ### `fd_fdstat_set_flags()`
|
/// ### `fd_fdstat_set_flags()`
|
||||||
@ -532,27 +535,36 @@ pub fn fd_prestat_dir_name(
|
|||||||
fd, path_len
|
fd, path_len
|
||||||
);
|
);
|
||||||
let memory = ctx.memory(0);
|
let memory = ctx.memory(0);
|
||||||
|
let path_chars = wasi_try!(path.deref(memory, 0, path_len));
|
||||||
|
|
||||||
|
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 {
|
||||||
|
let mut i = 0;
|
||||||
|
for c in inode_val.name.bytes() {
|
||||||
|
path_chars[i].set(c);
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
path_chars[i].set(0);
|
||||||
|
|
||||||
if let Ok(path_chars) = path.deref(memory, 0, path_len) {
|
|
||||||
debug!(
|
debug!(
|
||||||
"=> path: {}",
|
"=> result: \"{}\"",
|
||||||
path_chars
|
::std::str::from_utf8(unsafe { &*(&path_chars[..] as *const [_] as *const [u8]) })
|
||||||
.iter()
|
.unwrap()
|
||||||
.map(|c| c.get() as char)
|
|
||||||
.collect::<String>()
|
|
||||||
);
|
);
|
||||||
if true
|
|
||||||
/* check if dir */
|
|
||||||
{
|
|
||||||
// get name
|
|
||||||
// write name
|
|
||||||
// if overflow __WASI_EOVERFLOW
|
|
||||||
__WASI_ESUCCESS
|
__WASI_ESUCCESS
|
||||||
} else {
|
} else {
|
||||||
__WASI_ENOTDIR
|
__WASI_EOVERFLOW
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
__WASI_EFAULT
|
__WASI_ENOTDIR
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -612,7 +624,7 @@ pub fn fd_pwrite(
|
|||||||
|
|
||||||
let bytes_written = match &mut inode.kind {
|
let bytes_written = match &mut inode.kind {
|
||||||
Kind::File { handle } => {
|
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))
|
wasi_try!(write_bytes(handle, memory, iovs_arr_cell))
|
||||||
}
|
}
|
||||||
Kind::Dir { .. } => {
|
Kind::Dir { .. } => {
|
||||||
@ -699,7 +711,10 @@ pub fn fd_read(
|
|||||||
let inode = &mut state.fs.inodes[fd_entry.inode];
|
let inode = &mut state.fs.inodes[fd_entry.inode];
|
||||||
|
|
||||||
let bytes_read = match &mut inode.kind {
|
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 { .. } => {
|
Kind::Dir { .. } => {
|
||||||
// TODO: verify
|
// TODO: verify
|
||||||
return __WASI_EISDIR;
|
return __WASI_EISDIR;
|
||||||
@ -798,7 +813,7 @@ pub fn fd_seek(
|
|||||||
whence: __wasi_whence_t,
|
whence: __wasi_whence_t,
|
||||||
newoffset: WasmPtr<__wasi_filesize_t>,
|
newoffset: WasmPtr<__wasi_filesize_t>,
|
||||||
) -> __wasi_errno_t {
|
) -> __wasi_errno_t {
|
||||||
debug!("wasi::fd_seek: fd={}", fd);
|
debug!("wasi::fd_seek: fd={}, offset={}", fd, offset);
|
||||||
let memory = ctx.memory(0);
|
let memory = ctx.memory(0);
|
||||||
let state = get_wasi_state(ctx);
|
let state = get_wasi_state(ctx);
|
||||||
let new_offset_cell = wasi_try!(newoffset.deref(memory));
|
let new_offset_cell = wasi_try!(newoffset.deref(memory));
|
||||||
@ -830,7 +845,7 @@ pub fn fd_seek(
|
|||||||
/// Errors:
|
/// Errors:
|
||||||
/// TODO: figure out which errors this should return
|
/// TODO: figure out which errors this should return
|
||||||
/// - `__WASI_EPERM`
|
/// - `__WASI_EPERM`
|
||||||
/// - `__WAIS_ENOTCAPABLE`
|
/// - `__WASI_ENOTCAPABLE`
|
||||||
pub fn fd_sync(ctx: &mut Ctx, fd: __wasi_fd_t) -> __wasi_errno_t {
|
pub fn fd_sync(ctx: &mut Ctx, fd: __wasi_fd_t) -> __wasi_errno_t {
|
||||||
debug!("wasi::fd_sync");
|
debug!("wasi::fd_sync");
|
||||||
// TODO: check __WASI_RIGHT_FD_SYNC
|
// TODO: check __WASI_RIGHT_FD_SYNC
|
||||||
@ -920,7 +935,11 @@ pub fn fd_write(
|
|||||||
let inode = &mut state.fs.inodes[fd_entry.inode];
|
let inode = &mut state.fs.inodes[fd_entry.inode];
|
||||||
|
|
||||||
let bytes_written = match &mut inode.kind {
|
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 { .. } => {
|
Kind::Dir { .. } => {
|
||||||
// TODO: verify
|
// TODO: verify
|
||||||
return __WASI_EISDIR;
|
return __WASI_EISDIR;
|
||||||
@ -1030,7 +1049,9 @@ pub fn path_filestat_get(
|
|||||||
return __WASI_ELOOP;
|
return __WASI_ELOOP;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => return __WASI_ENOTDIR,
|
_ => {
|
||||||
|
return __WASI_ENOTDIR;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1142,13 +1163,11 @@ pub fn path_open(
|
|||||||
) -> __wasi_errno_t {
|
) -> __wasi_errno_t {
|
||||||
debug!("wasi::path_open");
|
debug!("wasi::path_open");
|
||||||
let memory = ctx.memory(0);
|
let memory = ctx.memory(0);
|
||||||
if path_len > 1024
|
|
||||||
/* TODO: find actual upper bound on name size (also this is a path, not a name :think-fish:) */
|
/* TODO: find actual upper bound on name size (also this is a path, not a name :think-fish:) */
|
||||||
{
|
if path_len > 1024 * 1024 {
|
||||||
return __WASI_ENAMETOOLONG;
|
return __WASI_ENAMETOOLONG;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for __WASI_RIGHT_PATH_OPEN somewhere, probably via dirfd
|
|
||||||
let fd_cell = wasi_try!(fd.deref(memory));
|
let fd_cell = wasi_try!(fd.deref(memory));
|
||||||
let path_cells = wasi_try!(path.deref(memory, 0, path_len));
|
let path_cells = wasi_try!(path.deref(memory, 0, path_len));
|
||||||
let state = get_wasi_state(ctx);
|
let state = get_wasi_state(ctx);
|
||||||
@ -1161,34 +1180,112 @@ pub fn path_open(
|
|||||||
|
|
||||||
let working_dir = wasi_try!(state.fs.fd_map.get(&dirfd).ok_or(__WASI_EBADF));
|
let working_dir = wasi_try!(state.fs.fd_map.get(&dirfd).ok_or(__WASI_EBADF));
|
||||||
|
|
||||||
// ASSUMPTION: open rights cascade down
|
// ASSUMPTION: open rights apply recursively
|
||||||
if !has_rights(working_dir.rights, __WASI_RIGHT_PATH_OPEN) {
|
if !has_rights(working_dir.rights, __WASI_RIGHT_PATH_OPEN) {
|
||||||
return __WASI_EACCES;
|
return __WASI_EACCES;
|
||||||
}
|
}
|
||||||
|
|
||||||
let path_vec =
|
let path_string =
|
||||||
wasi_try!(
|
wasi_try!(
|
||||||
::std::str::from_utf8(unsafe { &*(path_cells as *const [_] as *const [u8]) })
|
std::str::from_utf8(unsafe { &*(path_cells as *const [_] as *const [u8]) })
|
||||||
.map_err(|_| __WASI_EINVAL)
|
.map_err(|_| __WASI_EINVAL)
|
||||||
)
|
);
|
||||||
.split('/')
|
let path = std::path::PathBuf::from(path_string);
|
||||||
.map(|str| str.to_string())
|
let path_vec = wasi_try!(path
|
||||||
.collect::<Vec<String>>();
|
.components()
|
||||||
|
.map(|comp| {
|
||||||
|
comp.as_os_str()
|
||||||
|
.to_str()
|
||||||
|
.map(|inner_str| inner_str.to_string())
|
||||||
|
.ok_or(__WASI_EINVAL)
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<String>, __wasi_errno_t>>());
|
||||||
|
debug!("Path vec: {:#?}", path_vec);
|
||||||
|
|
||||||
if path_vec.is_empty() {
|
if path_vec.is_empty() {
|
||||||
return __WASI_EINVAL;
|
return __WASI_EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
let working_dir_inode = &mut state.fs.inodes[working_dir.inode];
|
let mut cur_dir_inode = working_dir.inode;
|
||||||
|
let mut cumulative_path = std::path::PathBuf::from(".");
|
||||||
|
|
||||||
let mut cur_dir = working_dir;
|
// traverse path
|
||||||
|
if path_vec.len() > 1 {
|
||||||
|
for path_segment in &path_vec[..(path_vec.len() - 1)] {
|
||||||
|
match &state.fs.inodes[cur_dir_inode].kind {
|
||||||
|
Kind::Dir { entries, .. } => {
|
||||||
|
if let Some(child) = entries.get(path_segment) {
|
||||||
|
let inode_val = *child;
|
||||||
|
cur_dir_inode = inode_val;
|
||||||
|
} else {
|
||||||
|
// attempt to lazily load or create
|
||||||
|
if path_segment == ".." {
|
||||||
|
unimplemented!(
|
||||||
|
"\"..\" in paths in `path_open` has not been implemented yet"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// lazily load
|
||||||
|
cumulative_path.push(path_segment);
|
||||||
|
let mut open_options = std::fs::OpenOptions::new();
|
||||||
|
let open_options = open_options.read(true);
|
||||||
|
// ASSUMPTION: __WASI_O_CREAT applies recursively
|
||||||
|
let open_options = if o_flags & __WASI_O_CREAT != 0 {
|
||||||
|
open_options.create(true)
|
||||||
|
} else {
|
||||||
|
open_options
|
||||||
|
};
|
||||||
|
// TODO: handle __WASI_O_TRUNC on directories
|
||||||
|
|
||||||
// TODO: refactor single path segment logic out and do traversing before
|
let cur_dir = wasi_try!(open_options
|
||||||
// as necessary
|
.open(&cumulative_path)
|
||||||
let out_fd = if path_vec.len() == 1 {
|
.map_err(|_| __WASI_EINVAL));
|
||||||
// just the file or dir
|
|
||||||
if let Kind::Dir { entries, .. } = &mut working_dir_inode.kind {
|
// TODO: refactor and reuse
|
||||||
if let Some(child) = entries.get(&path_vec[0]).cloned() {
|
let cur_file_metadata = cur_dir.metadata().unwrap();
|
||||||
|
let kind = if cur_file_metadata.is_dir() {
|
||||||
|
Kind::Dir {
|
||||||
|
handle: WasiFile::HostFile(cur_dir),
|
||||||
|
entries: Default::default(),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return __WASI_ENOTDIR;
|
||||||
|
};
|
||||||
|
let inode_val = InodeVal::from_file_metadata(
|
||||||
|
&cur_file_metadata,
|
||||||
|
path_segment.clone(),
|
||||||
|
false,
|
||||||
|
kind,
|
||||||
|
);
|
||||||
|
|
||||||
|
let new_inode = state.fs.inodes.insert(inode_val);
|
||||||
|
let inode_idx = state.fs.inode_counter.get();
|
||||||
|
state.fs.inode_counter.replace(inode_idx + 1);
|
||||||
|
// reborrow to insert entry
|
||||||
|
if let Kind::Dir { entries, .. } = &mut state.fs.inodes[cur_dir_inode].kind
|
||||||
|
{
|
||||||
|
assert!(entries.insert(path_segment.clone(), new_inode).is_none());
|
||||||
|
state.fs.inodes[new_inode].stat.st_ino = state.fs.inode_counter.get();
|
||||||
|
cur_dir_inode = new_inode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Kind::Symlink { .. } => unimplemented!("Symlinks not yet supported in `path_open`"),
|
||||||
|
_ => return __WASI_ENOTDIR,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let file_name = path_vec.last().unwrap();
|
||||||
|
|
||||||
|
debug!(
|
||||||
|
"Looking for file {} in directory {:#?}",
|
||||||
|
file_name, cumulative_path
|
||||||
|
);
|
||||||
|
cumulative_path.push(file_name);
|
||||||
|
let file_path = cumulative_path;
|
||||||
|
|
||||||
|
let out_fd = if let Kind::Dir { entries, .. } = &mut state.fs.inodes[cur_dir_inode].kind {
|
||||||
|
if let Some(child) = entries.get(file_name).cloned() {
|
||||||
let child_inode_val = &state.fs.inodes[child];
|
let child_inode_val = &state.fs.inodes[child];
|
||||||
// early return based on flags
|
// early return based on flags
|
||||||
if o_flags & __WASI_O_EXCL != 0 {
|
if o_flags & __WASI_O_EXCL != 0 {
|
||||||
@ -1197,7 +1294,9 @@ pub fn path_open(
|
|||||||
if o_flags & __WASI_O_DIRECTORY != 0 {
|
if o_flags & __WASI_O_DIRECTORY != 0 {
|
||||||
match &child_inode_val.kind {
|
match &child_inode_val.kind {
|
||||||
Kind::Dir { .. } => (),
|
Kind::Dir { .. } => (),
|
||||||
Kind::Symlink { .. } => unimplemented!(),
|
Kind::Symlink { .. } => {
|
||||||
|
unimplemented!("Symlinks not yet supported in path_open")
|
||||||
|
}
|
||||||
_ => return __WASI_ENOTDIR,
|
_ => return __WASI_ENOTDIR,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1206,40 +1305,57 @@ pub fn path_open(
|
|||||||
.fs
|
.fs
|
||||||
.create_fd(fs_rights_base, fs_rights_inheriting, fs_flags, child))
|
.create_fd(fs_rights_base, fs_rights_inheriting, fs_flags, child))
|
||||||
} else {
|
} else {
|
||||||
// entry does not exist in parent directory
|
// if entry does not exist in parent directory, try to lazily
|
||||||
// check to see if we should create it
|
// load it; possibly creating or truncating it if flags set
|
||||||
if o_flags & __WASI_O_CREAT != 0 {
|
let real_opened_file = {
|
||||||
// insert in to directory and set values
|
let mut open_options = std::fs::OpenOptions::new();
|
||||||
//entries.insert(path_segment[0], )
|
let open_options = open_options.read(true).write(true);
|
||||||
unimplemented!()
|
let open_options = if o_flags & __WASI_O_CREAT != 0 {
|
||||||
|
debug!(
|
||||||
|
"File {} may be created when opened if it does not exist",
|
||||||
|
&path_string
|
||||||
|
);
|
||||||
|
open_options.create(true)
|
||||||
} else {
|
} else {
|
||||||
// no entry and can't create it
|
open_options
|
||||||
return __WASI_ENOENT;
|
};
|
||||||
|
let open_options = if o_flags & __WASI_O_TRUNC != 0 {
|
||||||
|
debug!("File {} will be truncated when opened", &path_string);
|
||||||
|
open_options.truncate(true)
|
||||||
|
} else {
|
||||||
|
open_options
|
||||||
|
};
|
||||||
|
let real_open_file =
|
||||||
|
wasi_try!(open_options.open(&file_path).map_err(|_| __WASI_EIO));
|
||||||
|
debug!("Opening host file {}", &path_string);
|
||||||
|
|
||||||
|
real_open_file
|
||||||
|
};
|
||||||
|
// record lazily loaded or newly created fd
|
||||||
|
let new_inode = state.fs.inodes.insert(InodeVal {
|
||||||
|
stat: __wasi_filestat_t::default(),
|
||||||
|
is_preopened: false,
|
||||||
|
name: file_name.clone(),
|
||||||
|
kind: Kind::File {
|
||||||
|
handle: WasiFile::HostFile(real_opened_file),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
// reborrow to insert entry
|
||||||
|
if let Kind::Dir { entries, .. } = &mut state.fs.inodes[working_dir.inode].kind {
|
||||||
|
entries.insert(file_name.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 {
|
} else {
|
||||||
// working_dir is not a directory
|
// working_dir did not match on Kind::Dir
|
||||||
return __WASI_ENOTDIR;
|
return __WASI_ENOTDIR;
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// traverse the pieces of the path
|
|
||||||
// TODO: lots of testing on this
|
|
||||||
for path_segment in &path_vec[..(path_vec.len() - 1)] {
|
|
||||||
match &working_dir_inode.kind {
|
|
||||||
Kind::Dir { entries, .. } => {
|
|
||||||
if let Some(child) = entries.get(path_segment) {
|
|
||||||
unimplemented!();
|
|
||||||
} else {
|
|
||||||
// Is __WASI_O_FLAG_CREAT recursive?
|
|
||||||
// ASSUMPTION: it's not
|
|
||||||
return __WASI_EINVAL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Kind::Symlink { .. } => unimplemented!(),
|
|
||||||
_ => return __WASI_ENOTDIR,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
unimplemented!()
|
|
||||||
};
|
};
|
||||||
|
|
||||||
fd_cell.set(out_fd);
|
fd_cell.set(out_fd);
|
||||||
|
@ -264,7 +264,7 @@ pub type __wasi_filedelta_t = i64;
|
|||||||
|
|
||||||
pub type __wasi_filesize_t = u64;
|
pub type __wasi_filesize_t = u64;
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct __wasi_filestat_t {
|
pub struct __wasi_filestat_t {
|
||||||
pub st_dev: __wasi_device_t,
|
pub st_dev: __wasi_device_t,
|
||||||
|
@ -80,9 +80,13 @@ struct Run {
|
|||||||
backend: Backend,
|
backend: Backend,
|
||||||
|
|
||||||
/// Emscripten symbol map
|
/// Emscripten symbol map
|
||||||
#[structopt(long = "em-symbol-map", parse(from_os_str))]
|
#[structopt(long = "em-symbol-map", parse(from_os_str), group = "emscripten")]
|
||||||
em_symbol_map: Option<PathBuf>,
|
em_symbol_map: Option<PathBuf>,
|
||||||
|
|
||||||
|
/// WASI pre-opened directory
|
||||||
|
#[structopt(long = "dir", multiple = true, group = "wasi")]
|
||||||
|
pre_opened_directories: Vec<String>,
|
||||||
|
|
||||||
#[structopt(long = "command-name", hidden = true)]
|
#[structopt(long = "command-name", hidden = true)]
|
||||||
command_name: Option<String>,
|
command_name: Option<String>,
|
||||||
|
|
||||||
@ -316,6 +320,7 @@ fn execute_wasm(options: &Run) -> Result<(), String> {
|
|||||||
env::vars()
|
env::vars()
|
||||||
.map(|(k, v)| format!("{}={}", k, v).into_bytes())
|
.map(|(k, v)| format!("{}={}", k, v).into_bytes())
|
||||||
.collect(),
|
.collect(),
|
||||||
|
options.pre_opened_directories.clone(),
|
||||||
),
|
),
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user