From 91af7cf8a89c5b6c8def34ac683d6f3ffb4834c7 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Wed, 10 Apr 2019 18:23:25 -0700 Subject: [PATCH 1/8] add preopened fd and fix/improve fs syscalls (WIP) --- lib/wasi/src/lib.rs | 8 +- lib/wasi/src/state.rs | 157 +++++++++++++++++++++++++++++---- lib/wasi/src/syscalls/mod.rs | 139 ++++++++++++++++++++++------- lib/wasi/src/syscalls/types.rs | 2 +- src/bin/wasmer.rs | 9 +- 5 files changed, 262 insertions(+), 53 deletions(-) diff --git a/lib/wasi/src/lib.rs b/lib/wasi/src/lib.rs index dac093f5c6f..c08428dbebb 100644 --- a/lib/wasi/src/lib.rs +++ b/lib/wasi/src/lib.rs @@ -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>, envs: Vec>) -> ImportObject { +pub fn generate_import_object( + args: Vec>, + envs: Vec>, + preopened_files: Vec, +) -> ImportObject { let state_gen = move || { fn state_destructor(data: *mut c_void) { unsafe { @@ -26,7 +30,7 @@ pub fn generate_import_object(args: Vec>, envs: Vec>) -> ImportO } let state = Box::new(WasiState { - fs: WasiFs::new().unwrap(), + fs: WasiFs::new(&preopened_files).unwrap(), args: &args[..], envs: &envs[..], }); diff --git a/lib/wasi/src/state.rs b/lib/wasi/src/state.rs index 8e186011dea..43b2c93996d 100644 --- a/lib/wasi/src/state.rs +++ b/lib/wasi/src/state.rs @@ -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 { + 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 { + match self { + WasiFile::ZboxFile(zbf) => zbf.read(buf), + WasiFile::HostFile(hf) => hf.read(buf), + } + } + + fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { + 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 { + 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 { + 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, }, @@ -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, pub inodes: Arena, pub fd_map: HashMap, @@ -59,26 +134,58 @@ pub struct WasiFs { } impl WasiFs { - pub fn new() -> Result { + pub fn new(preopened_files: &[String]) -> Result { 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> { diff --git a/lib/wasi/src/syscalls/mod.rs b/lib/wasi/src/syscalls/mod.rs index 15f0f26798a..45b62634631 100644 --- a/lib/wasi/src/syscalls/mod.rs +++ b/lib/wasi/src/syscalls/mod.rs @@ -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)); + + 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]; - if let Ok(path_chars) = path.deref(memory, 0, path_len) { - debug!( - "=> path: {}", - path_chars - .iter() - .map(|c| c.get() as char) - .collect::() - ); - if true - /* check if dir */ - { - // get name - // write name - // if overflow __WASI_EOVERFLOW + // 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 { diff --git a/lib/wasi/src/syscalls/types.rs b/lib/wasi/src/syscalls/types.rs index b6f7bada2d2..5afd3148fdf 100644 --- a/lib/wasi/src/syscalls/types.rs +++ b/lib/wasi/src/syscalls/types.rs @@ -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, diff --git a/src/bin/wasmer.rs b/src/bin/wasmer.rs index 34550355805..b0676a074ad 100644 --- a/src/bin/wasmer.rs +++ b/src/bin/wasmer.rs @@ -66,9 +66,13 @@ struct Run { #[structopt(name = "--", raw(multiple = "true"))] args: Vec, - /// 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, + + /// WASI pre-opened file + #[structopt(long = "pre-open", group = "wasi")] + pre_opened_files: Vec, } #[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, ) From 19e830d25a193dfbdc190e4d187f22f9dd458217 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Thu, 18 Apr 2019 17:48:14 -0700 Subject: [PATCH 2/8] fix memory access in WasmPtr --- lib/wasi/src/ptr.rs | 9 ++++--- lib/wasi/src/state.rs | 47 ++++++++++++++++++++++++++++++------ lib/wasi/src/syscalls/mod.rs | 42 ++++++++++++++++++++------------ src/bin/wasmer.rs | 1 - 4 files changed, 71 insertions(+), 28 deletions(-) diff --git a/lib/wasi/src/ptr.rs b/lib/wasi/src/ptr.rs index da892d1bf69..32c19655eae 100644 --- a/lib/wasi/src/ptr.rs +++ b/lib/wasi/src/ptr.rs @@ -36,10 +36,11 @@ impl WasmPtr { return Err(__WASI_EFAULT); } unsafe { - let cell_ptr = memory - .view::() - .get_unchecked((self.offset() as usize) / mem::size_of::()) - as *const _; + let aligner = |ptr: usize, align: usize| ptr & !(align - 1); + let cell_ptr = aligner( + memory.view::().as_ptr().add(self.offset as usize) as usize, + mem::align_of::(), + ) as *const Cell; Ok(&*cell_ptr) } } diff --git a/lib/wasi/src/state.rs b/lib/wasi/src/state.rs index 43b2c93996d..6cb684b8563 100644 --- a/lib/wasi/src/state.rs +++ b/lib/wasi/src/state.rs @@ -16,6 +16,7 @@ use zbox::{init_env as zbox_init_env, FileType, OpenOptions, Repo, RepoOpener}; pub const MAX_SYMLINKS: usize = 100; +#[derive(Debug)] pub enum WasiFile { ZboxFile(zbox::File), HostFile(fs::File), @@ -90,6 +91,7 @@ impl Seek for WasiFile { } } +#[derive(Debug)] pub struct InodeVal { pub stat: __wasi_filestat_t, pub is_preopened: bool, @@ -98,6 +100,7 @@ pub struct InodeVal { } #[allow(dead_code)] +#[derive(Debug)] pub enum Kind { File { handle: WasiFile, @@ -155,9 +158,13 @@ impl WasiFs { 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() { + 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() { // it seems bad to open every file recursively; can do it lazily though Kind::Dir { handle: WasiFile::HostFile(cur_file), @@ -173,13 +180,36 @@ impl WasiFs { )); }; let inode_val = InodeVal { - stat: __wasi_filestat_t::default(), + stat: __wasi_filestat_t { + st_filetype: __WASI_FILETYPE_DIRECTORY, + st_size: cur_file_metadata.len(), + st_atim: cur_file_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: cur_file_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: cur_file_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: true, // this is incorrect name: file.clone(), 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"); @@ -304,7 +334,7 @@ impl WasiFs { debug!("fdstat: {:?}", fd); - dbg!(Ok(__wasi_fdstat_t { + Ok(__wasi_fdstat_t { fs_filetype: match self.inodes[fd.inode].kind { Kind::File { .. } => __WASI_FILETYPE_REGULAR_FILE, Kind::Dir { .. } => __WASI_FILETYPE_DIRECTORY, @@ -313,8 +343,8 @@ impl WasiFs { }, fs_flags: fd.flags, 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> { @@ -327,7 +357,8 @@ impl WasiFs { Ok(__wasi_prestat_t { pr_type: __WASI_PREOPENTYPE_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(), }) diff --git a/lib/wasi/src/syscalls/mod.rs b/lib/wasi/src/syscalls/mod.rs index 45b62634631..2416b3a7a5d 100644 --- a/lib/wasi/src/syscalls/mod.rs +++ b/lib/wasi/src/syscalls/mod.rs @@ -324,15 +324,18 @@ pub fn fd_datasync(ctx: &mut Ctx, fd: __wasi_fd_t) -> __wasi_errno_t { pub fn fd_fdstat_get( ctx: &mut Ctx, fd: __wasi_fd_t, - buf: WasmPtr<__wasi_fdstat_t>, + buf_ptr: WasmPtr<__wasi_fdstat_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 memory = ctx.memory(0); - 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); __WASI_ESUCCESS @@ -543,9 +546,19 @@ pub fn fd_prestat_dir_name( 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() { + let mut i = 0; + for c in inode_val.name.bytes() { path_chars[i].set(c); + i += 1 } + path_chars[i].set(0); + + debug!( + "=> result: \"{}\"", + ::std::str::from_utf8(unsafe { &*(&path_chars[..] as *const [_] as *const [u8]) }) + .unwrap() + ); + __WASI_ESUCCESS } else { __WASI_EOVERFLOW @@ -669,13 +682,10 @@ pub fn fd_read( for iov in iovs_arr_cell { let iov_inner = iov.get(); - let bytes = iov_inner.buf.deref(memory, 0, dbg!(iov_inner.buf_len))?; + let bytes = iov_inner.buf.deref(memory, 0, iov_inner.buf_len)?; let mut raw_bytes: &mut [u8] = unsafe { &mut *(bytes as *const [_] as *mut [_] as *mut [u8]) }; - bytes_read += dbg!(reader.read(raw_bytes).map_err(|e| { - dbg!(e); - __WASI_EIO - })? as u32); + bytes_read += reader.read(raw_bytes).map_err(|e| __WASI_EIO)? as u32; } Ok(bytes_read) } @@ -722,7 +732,7 @@ pub fn fd_read( } }; - nread_cell.set(dbg!(bytes_read)); + nread_cell.set(bytes_read); __WASI_ESUCCESS } @@ -819,7 +829,7 @@ pub fn fd_seek( // TODO: handle case if fd is a dir? match whence { - __WASI_WHENCE_CUR => fd_entry.offset = (dbg!(fd_entry.offset) as i64 + offset) as u64, + __WASI_WHENCE_CUR => fd_entry.offset = (fd_entry.offset as i64 + offset) as u64, __WASI_WHENCE_END => unimplemented!(), __WASI_WHENCE_SET => fd_entry.offset = offset as u64, _ => return __WASI_EINVAL, @@ -838,7 +848,7 @@ pub fn fd_seek( /// Errors: /// TODO: figure out which errors this should return /// - `__WASI_EPERM` -/// - `__WAIS_ENOTCAPABLE` +/// - `__WASI_ENOTCAPABLE` pub fn fd_sync(ctx: &mut Ctx, fd: __wasi_fd_t) -> __wasi_errno_t { debug!("wasi::fd_sync"); // TODO: check __WASI_RIGHT_FD_SYNC @@ -949,7 +959,7 @@ pub fn fd_write( } }; - nwritten_cell.set(dbg!(bytes_written)); + nwritten_cell.set(bytes_written); __WASI_ESUCCESS } @@ -1042,7 +1052,9 @@ pub fn path_filestat_get( return __WASI_ELOOP; } } - _ => return __WASI_ENOTDIR, + _ => { + return __WASI_ENOTDIR; + } } } } diff --git a/src/bin/wasmer.rs b/src/bin/wasmer.rs index 32692a59efb..54f58840588 100644 --- a/src/bin/wasmer.rs +++ b/src/bin/wasmer.rs @@ -123,7 +123,6 @@ impl FromStr for Backend { _ => Err(format!("The backend {} doesn't exist", s)), } } ->>>>>>> origin/master } #[derive(Debug, StructOpt)] From c75f055b0971c8d0fa2553459d07313ce104ca33 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Fri, 19 Apr 2019 09:25:29 -0700 Subject: [PATCH 3/8] disable ZFS again; clean up simple-path's control-flow-path in path_open --- lib/wasi/src/state.rs | 18 ++--- lib/wasi/src/syscalls/mod.rs | 138 ++++++++++++++--------------------- 2 files changed, 63 insertions(+), 93 deletions(-) diff --git a/lib/wasi/src/state.rs b/lib/wasi/src/state.rs index 6cb684b8563..72f31db773b 100644 --- a/lib/wasi/src/state.rs +++ b/lib/wasi/src/state.rs @@ -128,7 +128,7 @@ pub struct Fd { } pub struct WasiFs { - pub repo: Repo, + //pub repo: Repo, pub name_map: HashMap, pub inodes: Arena, pub fd_map: HashMap, @@ -141,14 +141,14 @@ impl WasiFs { 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 mut wasi_fs = Self { - repo: repo, + //repo: repo, name_map: HashMap::new(), inodes: inodes, fd_map: HashMap::new(), @@ -165,15 +165,11 @@ impl WasiFs { .expect("Could not find file"); let cur_file_metadata = cur_file.metadata().unwrap(); let kind = if cur_file_metadata.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 @@ -204,7 +200,7 @@ impl WasiFs { ..__wasi_filestat_t::default() }, is_preopened: true, - // this is incorrect + // TODO: handle nested paths name: file.clone(), kind, }; diff --git a/lib/wasi/src/syscalls/mod.rs b/lib/wasi/src/syscalls/mod.rs index 2416b3a7a5d..672370a0c30 100644 --- a/lib/wasi/src/syscalls/mod.rs +++ b/lib/wasi/src/syscalls/mod.rs @@ -1166,13 +1166,11 @@ pub fn path_open( ) -> __wasi_errno_t { debug!("wasi::path_open"); 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:) */ - { + if path_len > 1024 * 1024 { return __WASI_ENAMETOOLONG; } - // check for __WASI_RIGHT_PATH_OPEN somewhere, probably via dirfd let fd_cell = wasi_try!(fd.deref(memory)); let path_cells = wasi_try!(path.deref(memory, 0, path_len)); let state = get_wasi_state(ctx); @@ -1185,26 +1183,27 @@ pub fn path_open( 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) { return __WASI_EACCES; } - let path_vec = + let path_string = wasi_try!( ::std::str::from_utf8(unsafe { &*(path_cells as *const [_] as *const [u8]) }) .map_err(|_| __WASI_EINVAL) - ) + ); + let path_vec = path_string .split('/') .map(|str| str.to_string()) .collect::>(); + debug!("Path vec: {:#?}", path_vec); if path_vec.is_empty() { return __WASI_EINVAL; } let working_dir_inode = &mut state.fs.inodes[working_dir.inode]; - let mut cur_dir = working_dir; // TODO: refactor single path segment logic out and do traversing before @@ -1221,7 +1220,9 @@ pub fn path_open( if o_flags & __WASI_O_DIRECTORY != 0 { match &child_inode_val.kind { Kind::Dir { .. } => (), - Kind::Symlink { .. } => unimplemented!(), + Kind::Symlink { .. } => { + unimplemented!("Symlinks not yet supported in path_open") + } _ => return __WASI_ENOTDIR, } } @@ -1230,87 +1231,60 @@ pub fn path_open( .fs .create_fd(fs_rights_base, fs_rights_inheriting, fs_flags, child)) } else { - // 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], ) - - 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], + // if entry does not exist in parent directory, try to lazily + // load it; possibly creating or truncating it if flags set + let real_opened_file = { + let mut open_options = std::fs::OpenOptions::new(); + let open_options = open_options.read(true).write(true); + 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 { + open_options + }; + 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(&path_vec[0]).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: path_vec[0].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(path_vec[0].clone(), new_inode); + } + let new_fd = wasi_try!(state.fs.create_fd( fs_rights_base, fs_rights_inheriting, - fs_flags - ));*/ - } else { - // 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 - } + fs_flags, + new_inode, + )); + + new_fd } } else { - // working_dir is not a directory + // working_dir did not match on Kind::Dir return __WASI_ENOTDIR; } } else { + unimplemented!("Path_open is not implemented for anything other simple paths (i.e. file, not /path/to/file); this will be fixed soon -- if you're seeing this error message and it's not 2019/04/19 in America, pull from master and try again"); // traverse the pieces of the path // TODO: lots of testing on this for path_segment in &path_vec[..(path_vec.len() - 1)] { From 35f8d69d4ba956e10885b52d896c85e66ea6bf39 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Fri, 19 Apr 2019 09:26:47 -0700 Subject: [PATCH 4/8] fix typo; thanks Kerollmops! --- src/bin/wasmer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/wasmer.rs b/src/bin/wasmer.rs index 54f58840588..26ed97fec4d 100644 --- a/src/bin/wasmer.rs +++ b/src/bin/wasmer.rs @@ -79,7 +79,7 @@ struct Run { )] backend: Backend, - /// Emscripten symbol ma>p + /// Emscripten symbol map #[structopt(long = "em-symbol-map", parse(from_os_str), group = "emscripten")] em_symbol_map: Option, From 5e504b77cd27eee444bd9b69f10872e56df7b667 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Fri, 19 Apr 2019 12:48:29 -0700 Subject: [PATCH 5/8] add support for more complex paths in `path_open` --- lib/wasi/src/state.rs | 56 ++++++-- lib/wasi/src/syscalls/mod.rs | 251 +++++++++++++++++++++-------------- src/bin/wasmer.rs | 8 +- 3 files changed, 199 insertions(+), 116 deletions(-) diff --git a/lib/wasi/src/state.rs b/lib/wasi/src/state.rs index 72f31db773b..5b3b5e4154f 100644 --- a/lib/wasi/src/state.rs +++ b/lib/wasi/src/state.rs @@ -99,6 +99,49 @@ pub struct InodeVal { 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)] #[derive(Debug)] pub enum Kind { @@ -409,19 +452,6 @@ 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> { diff --git a/lib/wasi/src/syscalls/mod.rs b/lib/wasi/src/syscalls/mod.rs index 672370a0c30..455d210d424 100644 --- a/lib/wasi/src/syscalls/mod.rs +++ b/lib/wasi/src/syscalls/mod.rs @@ -685,12 +685,11 @@ pub fn fd_read( let bytes = iov_inner.buf.deref(memory, 0, 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(|e| __WASI_EIO)? as u32; + bytes_read += reader.read(raw_bytes).map_err(|_| __WASI_EIO)? as u32; } Ok(bytes_read) } - debug!("think-fish"); let bytes_read = match fd { 0 => { let stdin = io::stdin(); @@ -825,8 +824,6 @@ 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, @@ -1190,119 +1187,175 @@ pub fn path_open( let path_string = 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) ); - let path_vec = path_string - .split('/') - .map(|str| str.to_string()) - .collect::>(); + let path = std::path::PathBuf::from(path_string); + let path_vec = wasi_try!(path + .components() + .map(|comp| { + comp.as_os_str() + .to_str() + .map(|inner_str| inner_str.to_string()) + .ok_or(__WASI_EINVAL) + }) + .collect::, __wasi_errno_t>>()); debug!("Path vec: {:#?}", path_vec); if path_vec.is_empty() { return __WASI_EINVAL; } - let working_dir_inode = &mut state.fs.inodes[working_dir.inode]; - let mut cur_dir = working_dir; - - // TODO: refactor single path segment logic out and do traversing before - // as necessary - let out_fd = if path_vec.len() == 1 { - // just the file or dir - if let Kind::Dir { entries, .. } = &mut working_dir_inode.kind { - if let Some(child) = entries.get(&path_vec[0]).cloned() { - let child_inode_val = &state.fs.inodes[child]; - // early return based on flags - if o_flags & __WASI_O_EXCL != 0 { - return __WASI_EEXIST; - } - if o_flags & __WASI_O_DIRECTORY != 0 { - match &child_inode_val.kind { - Kind::Dir { .. } => (), - Kind::Symlink { .. } => { - unimplemented!("Symlinks not yet supported in path_open") - } - _ => return __WASI_ENOTDIR, - } - } - // do logic on child - wasi_try!(state - .fs - .create_fd(fs_rights_base, fs_rights_inheriting, fs_flags, child)) - } else { - // if entry does not exist in parent directory, try to lazily - // load it; possibly creating or truncating it if flags set - let real_opened_file = { - let mut open_options = std::fs::OpenOptions::new(); - let open_options = open_options.read(true).write(true); - 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 { - open_options - }; - 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(&path_vec[0]).map_err(|_| __WASI_EIO)); - debug!("Opening host file {}", &path_string); + let mut cur_dir_inode = working_dir.inode; + let mut cumulative_path = std::path::PathBuf::from("."); - 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: path_vec[0].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(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 { - // working_dir did not match on Kind::Dir - return __WASI_ENOTDIR; - } - } else { - unimplemented!("Path_open is not implemented for anything other simple paths (i.e. file, not /path/to/file); this will be fixed soon -- if you're seeing this error message and it's not 2019/04/19 in America, pull from master and try again"); - // traverse the pieces of the path - // TODO: lots of testing on this + // traverse path + if path_vec.len() > 1 { for path_segment in &path_vec[..(path_vec.len() - 1)] { - match &working_dir_inode.kind { + match &state.fs.inodes[cur_dir_inode].kind { Kind::Dir { entries, .. } => { if let Some(child) = entries.get(path_segment) { - unimplemented!(); + let inode_val = *child; + cur_dir_inode = inode_val; } else { - // Is __WASI_O_FLAG_CREAT recursive? - // ASSUMPTION: it's not - return __WASI_EINVAL; + // 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 + + let cur_dir = wasi_try!(open_options + .open(&cumulative_path) + .map_err(|_| __WASI_EINVAL)); + + // TODO: refactor and reuse + 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!(), + Kind::Symlink { .. } => unimplemented!("Symlinks not yet supported in `path_open`"), _ => return __WASI_ENOTDIR, } } - unimplemented!() + } + + 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]; + // early return based on flags + if o_flags & __WASI_O_EXCL != 0 { + return __WASI_EEXIST; + } + if o_flags & __WASI_O_DIRECTORY != 0 { + match &child_inode_val.kind { + Kind::Dir { .. } => (), + Kind::Symlink { .. } => { + unimplemented!("Symlinks not yet supported in path_open") + } + _ => return __WASI_ENOTDIR, + } + } + // do logic on child + wasi_try!(state + .fs + .create_fd(fs_rights_base, fs_rights_inheriting, fs_flags, child)) + } else { + // if entry does not exist in parent directory, try to lazily + // load it; possibly creating or truncating it if flags set + let real_opened_file = { + let mut open_options = std::fs::OpenOptions::new(); + let open_options = open_options.read(true).write(true); + 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 { + open_options + }; + 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 { + // working_dir did not match on Kind::Dir + return __WASI_ENOTDIR; }; fd_cell.set(out_fd); diff --git a/src/bin/wasmer.rs b/src/bin/wasmer.rs index 26ed97fec4d..b9606a225a7 100644 --- a/src/bin/wasmer.rs +++ b/src/bin/wasmer.rs @@ -83,9 +83,9 @@ struct Run { #[structopt(long = "em-symbol-map", parse(from_os_str), group = "emscripten")] em_symbol_map: Option, - /// WASI pre-opened file - #[structopt(long = "pre-open", group = "wasi")] - pre_opened_files: Vec, + /// WASI pre-opened directory + #[structopt(long = "dir", multiple = true, group = "wasi")] + pre_opened_directories: Vec, #[structopt(long = "command-name", hidden = true)] command_name: Option, @@ -320,7 +320,7 @@ fn execute_wasm(options: &Run) -> Result<(), String> { env::vars() .map(|(k, v)| format!("{}={}", k, v).into_bytes()) .collect(), - options.pre_opened_files.clone(), + options.pre_opened_directories.clone(), ), None, ) From 52279afbe2ae185debe1527ed2a0d7fff28c80ef Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Fri, 19 Apr 2019 13:24:41 -0700 Subject: [PATCH 6/8] add comment and reduce code in pre-open loading section --- lib/wasi/src/ptr.rs | 1 + lib/wasi/src/state.rs | 33 ++++----------------------------- 2 files changed, 5 insertions(+), 29 deletions(-) diff --git a/lib/wasi/src/ptr.rs b/lib/wasi/src/ptr.rs index 32c19655eae..85a582df0c2 100644 --- a/lib/wasi/src/ptr.rs +++ b/lib/wasi/src/ptr.rs @@ -36,6 +36,7 @@ impl WasmPtr { return Err(__WASI_EFAULT); } unsafe { + // clears bits below aligment amount (assumes power of 2) to align pointer let aligner = |ptr: usize, align: usize| ptr & !(align - 1); let cell_ptr = aligner( memory.view::().as_ptr().add(self.offset as usize) as usize, diff --git a/lib/wasi/src/state.rs b/lib/wasi/src/state.rs index 5b3b5e4154f..8594009a0e5 100644 --- a/lib/wasi/src/state.rs +++ b/lib/wasi/src/state.rs @@ -218,35 +218,10 @@ impl WasiFs { file )); }; - let inode_val = InodeVal { - stat: __wasi_filestat_t { - st_filetype: __WASI_FILETYPE_DIRECTORY, - st_size: cur_file_metadata.len(), - st_atim: cur_file_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: cur_file_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: cur_file_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: true, - // TODO: handle nested paths - name: file.clone(), - kind, - }; + // 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 From b141d7f80eba3cad28d7bed5606d1cd7140f1963 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Fri, 19 Apr 2019 13:25:23 -0700 Subject: [PATCH 7/8] fix typo --- lib/wasi/src/state.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/wasi/src/state.rs b/lib/wasi/src/state.rs index 8594009a0e5..d703e2032d0 100644 --- a/lib/wasi/src/state.rs +++ b/lib/wasi/src/state.rs @@ -220,7 +220,7 @@ impl WasiFs { }; // TODO: handle nested pats in `file` let inode_val = - InodeVal::from_file_metadata(cur_file_metadata, file.clone(), true, kind); + 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(); From eef2bc6a21acee244ac1cd4b819e5197dcd520ae Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Fri, 19 Apr 2019 14:07:41 -0700 Subject: [PATCH 8/8] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 113ad0c3e0f..329a92f8cbf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ All PRs to the Wasmer repository must add to this file. Blocks of changes will separated by version increments. ## **[Unreleased]** +- [#343](https://github.com/wasmerio/wasmer/pull/343) Implement preopened files for WASI and fix aligment issue when accessing WASI memory - [#355](https://github.com/wasmerio/wasmer/pull/355) Misc changes to `Cargo.toml`s for publishing - [#352](https://github.com/wasmerio/wasmer/pull/352) Bump version numbers to 0.3.0 - [#351](https://github.com/wasmerio/wasmer/pull/351) Add hidden option to specify wasm program name (can be used to improve error messages)