Add start of wasi fs

This commit is contained in:
Lachlan Sneff 2019-04-01 15:15:20 -07:00
parent 48b5918895
commit 68f1123ad6
4 changed files with 205 additions and 5 deletions

View File

@ -5,4 +5,9 @@ authors = ["The Wasmer Engineering Team <engineering@wasmer.io>"]
edition = "2018"
[dependencies]
wasmer-runtime-core = { path = "../runtime-core", version = "0.2.1" }
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"

View File

@ -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<Vec<u8>>, envs: Vec<Vec<u8>>) -> ImportO
}
let state = Box::new(WasiState {
fs: WasiFs::new().unwrap(),
args: &args[..],
envs: &envs[..],
});

View File

@ -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<Inode>,
},
Symlink {
forwarded: Inode,
},
Buffer {
buffer: Vec<u8>,
},
}
pub struct Fd {
rights: __wasi_rights_t,
flags: __wasi_fdflags_t,
offset: u64,
inode: Inode,
}
pub struct WasiFs {
repo: Repo,
name_map: HashMap<String, Inode>,
inodes: Arena<InodeVal>,
fd_map: HashMap<u32, Fd>,
next_fd: Cell<u32>,
inode_counter: Cell<u64>,
}
impl WasiFs {
pub fn new() -> Result<Self, String> {
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<Inode> {
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<u8>],
pub envs: &'a [Vec<u8>],
}

View File

@ -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,