diff --git a/lib/wasi/Cargo.toml b/lib/wasi/Cargo.toml index 17a2f14f4..8657fa92d 100644 --- a/lib/wasi/Cargo.toml +++ b/lib/wasi/Cargo.toml @@ -5,4 +5,9 @@ authors = ["The Wasmer Engineering Team "] edition = "2018" [dependencies] -wasmer-runtime-core = { path = "../runtime-core", version = "0.2.1" } \ No newline at end of file +wasmer-runtime-core = { path = "../runtime-core", version = "0.2.1" } +# wasmer-runtime-abi = { path = "../runtime-abi" } +hashbrown = "0.1.8" +generational-arena = "0.2.2" +zbox = "0.6.1" +log = "0.4.6" \ No newline at end of file diff --git a/lib/wasi/src/lib.rs b/lib/wasi/src/lib.rs index 3dcb2f7cb..27e945502 100644 --- a/lib/wasi/src/lib.rs +++ b/lib/wasi/src/lib.rs @@ -1,9 +1,13 @@ + +#[macro_use] +extern crate log; + mod ptr; mod state; mod syscalls; mod utils; -use self::state::WasiState; +use self::state::{WasiState, WasiFs}; use self::syscalls::*; use std::ffi::c_void; @@ -21,6 +25,7 @@ pub fn generate_import_object(args: Vec>, envs: Vec>) -> ImportO } let state = Box::new(WasiState { + fs: WasiFs::new().unwrap(), args: &args[..], envs: &envs[..], }); diff --git a/lib/wasi/src/state.rs b/lib/wasi/src/state.rs index e7cafc1ba..3dd97b39b 100644 --- a/lib/wasi/src/state.rs +++ b/lib/wasi/src/state.rs @@ -1,5 +1,182 @@ +// use wasmer_runtime_abi::vfs::{ +// vfs::Vfs, +// file_like::{FileLike, Metadata}; +// }; +use std::{ + cell::{Cell, RefCell}, + ops::{Index, IndexMut}, + rc::Rc, + time::SystemTime, +}; +use generational_arena::{Arena, Index as Inode}; +use hashbrown::hash_map::{HashMap, Entry}; +use zbox::{ + RepoOpener, + Repo, + File, + FileType, + OpenOptions, +}; +use crate::syscalls::types::*; + +pub const MAX_SYMLINKS: usize = 100; + +pub struct InodeVal { + stat: __wasi_filestat_t, + is_preopened: bool, + name: String, + kind: Kind, +} + +pub enum Kind { + File { + handle: File, + }, + Dir { + handle: File, + /// The entries of a directory are lazily filled. + entries: Vec, + }, + Symlink { + forwarded: Inode, + }, + Buffer { + buffer: Vec, + }, +} + +pub struct Fd { + rights: __wasi_rights_t, + flags: __wasi_fdflags_t, + offset: u64, + inode: Inode, +} + +pub struct WasiFs { + repo: Repo, + name_map: HashMap, + inodes: Arena, + fd_map: HashMap, + next_fd: Cell, + inode_counter: Cell, +} + +impl WasiFs { + pub fn new() -> Result { + Ok(Self { + repo: RepoOpener::new().create(true).open("mem://📂", "very unsafe pwd").map_err(|e| e.to_string())?, + name_map: HashMap::new(), + inodes: Arena::new(), + fd_map: HashMap::new(), + next_fd: Cell::new(3), + inode_counter: Cell::new(1000), + }) + } + + fn get_inode(&mut self, path: &str) -> Option { + Some(match self.name_map.entry(path.to_string()) { + Entry::Occupied(o) => *o.get(), + Entry::Vacant(v) => { + let file = if let Ok(file) = OpenOptions::new().read(true).write(true).create(false).open(&mut self.repo, path) { + file + } else { + return None; + }; + + let metadata = file.metadata().unwrap(); + let inode_index = { + let index = self.inode_counter.get(); + self.inode_counter.replace(index + 1) + }; + + let systime_to_nanos = |systime: SystemTime| { + let duration = systime.duration_since(SystemTime::UNIX_EPOCH).expect("should always be after unix epoch"); + duration.as_nanos() as u64 + }; + + let inode = self.inodes.insert(InodeVal { + stat: __wasi_filestat_t { + st_dev: 0, + st_ino: inode_index, + st_filetype: match metadata.file_type() { + FileType::File => __WASI_FILETYPE_REGULAR_FILE, + FileType::Dir => __WASI_FILETYPE_DIRECTORY, + }, + st_nlink: 0, + st_size: metadata.len() as u64, + st_atim: systime_to_nanos(SystemTime::now()), + st_mtim: systime_to_nanos(metadata.modified()), + st_ctim: systime_to_nanos(metadata.created()), + }, + is_preopened: false, + name: path.to_string(), + kind: match metadata.file_type() { + FileType::File => Kind::File { + handle: file, + }, + FileType::Dir => Kind::Dir { + handle: file, + entries: Vec::new(), + }, + }, + }); + v.insert(inode); + inode + }, + }) + } + + fn filestat_inode(&self, inode: Inode, flags: __wasi_lookupflags_t) -> Result<__wasi_filestat_t, __wasi_errno_t> { + let inode_val = &self.inodes[inode]; + if let (true, Kind::Symlink { mut forwarded }) = (flags & __WASI_LOOKUP_SYMLINK_FOLLOW != 0, &inode_val.kind) { + // Time to follow the symlink. + let mut counter = 0; + + while counter <= MAX_SYMLINKS { + let inode_val = &self.inodes[forwarded]; + if let &Kind::Symlink { forwarded: new_forwarded } = &inode_val.kind { + counter += 1; + forwarded = new_forwarded; + } else { + return Ok(inode_val.stat); + } + } + + Err(__WASI_EMLINK) + } else { + Ok(inode_val.stat) + } + } + + pub fn filestat_path( + &mut self, + preopened_fd: __wasi_fd_t, + flags: __wasi_lookupflags_t, + path: &str, + ) -> Result<__wasi_filestat_t, __wasi_errno_t> { + warn!("Should use preopned_fd: {}", preopened_fd); + let inode = if let Some(inode) = self.get_inode(path) { + inode + } else { + return Err(__WASI_EINVAL); + }; + + self.filestat_inode(inode, flags) + } + + pub fn filestat_fd(&self, fd: __wasi_fd_t) -> Result<__wasi_filestat_t, __wasi_errno_t> { + let fd = if let Some(fd) = self.fd_map.get(&fd) { + fd + } else { + return Err(__WASI_EBADF); + }; + + self.filestat_inode(fd.inode, 0) + } +} + pub struct WasiState<'a> { - // vfs: Vfs, + pub fs: WasiFs, pub args: &'a [Vec], pub envs: &'a [Vec], } diff --git a/lib/wasi/src/syscalls/mod.rs b/lib/wasi/src/syscalls/mod.rs index d72cbf466..3e5442b32 100644 --- a/lib/wasi/src/syscalls/mod.rs +++ b/lib/wasi/src/syscalls/mod.rs @@ -1,6 +1,6 @@ #![allow(unused)] -mod types; +pub mod types; use self::types::*; use crate::{ @@ -185,7 +185,20 @@ pub fn fd_fdstat_get( fd: __wasi_fd_t, buf: WasmPtr<__wasi_fdstat_t>, ) -> __wasi_errno_t { - unimplemented!() + let mut state = get_wasi_state(ctx); + let memory = ctx.memory(0); + + let stat = match state.fs.filestat_fd(fd) { + Ok(stat) => stat, + Err(errno) => return errno, + }; + + if let Some(buf) = buf.deref(memory) { + buf.set(stat); + __WASI_ESUCCESS + } else { + __WASI_EFAULT + } } pub fn fd_fdstat_set_flags( ctx: &mut Ctx,