Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add preopened fd and fix/improve fs syscalls; fix wasi memory access #343

Merged
merged 11 commits into from
Apr 19, 2019
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
- [#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).
- [#348](https://github.com/wasmerio/wasmer/pull/348) Refactor internal runtime ↔️ backend abstraction.
Expand Down
8 changes: 6 additions & 2 deletions lib/wasi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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[..],
});
Expand Down
10 changes: 6 additions & 4 deletions lib/wasi/src/ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,12 @@ impl<T: Copy + ValueType> WasmPtr<T, Item> {
return Err(__WASI_EFAULT);
}
unsafe {
let cell_ptr = memory
.view::<T>()
.get_unchecked((self.offset() as usize) / mem::size_of::<T>())
as *const _;
// clears bits below aligment amount (assumes power of 2) to align pointer
let aligner = |ptr: usize, align: usize| ptr & !(align - 1);
MarkMcCaskey marked this conversation as resolved.
Show resolved Hide resolved
let cell_ptr = aligner(
memory.view::<u8>().as_ptr().add(self.offset as usize) as usize,
mem::align_of::<T>(),
) as *const Cell<T>;
Ok(&*cell_ptr)
}
}
Expand Down
189 changes: 172 additions & 17 deletions lib/wasi/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,28 +7,149 @@ 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;

#[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 stat: __wasi_filestat_t,
pub is_preopened: bool,
pub name: String,
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 {
File {
handle: File,
handle: WasiFile,
},
Dir {
handle: File,
handle: WasiFile,
/// The entries of a directory are lazily filled.
entries: HashMap<String, Inode>,
},
Expand All @@ -40,7 +161,7 @@ pub enum Kind {
},
}

#[derive(Clone)]
#[derive(Clone, Debug)]
pub struct Fd {
pub rights: __wasi_rights_t,
pub rights_inheriting: __wasi_rights_t,
Expand All @@ -50,7 +171,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>,
Expand All @@ -59,26 +180,56 @@ 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; // 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");
res
Ok(wasi_fs)
}

#[allow(dead_code)]
Expand Down Expand Up @@ -195,6 +346,8 @@ 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)?;

debug!("fdstat: {:?}", fd);

Ok(__wasi_fdstat_t {
fs_filetype: match self.inodes[fd.inode].kind {
Kind::File { .. } => __WASI_FILETYPE_REGULAR_FILE,
Expand All @@ -204,20 +357,22 @@ 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> {
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 {
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(),
})
Expand Down
Loading