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

implement map-dir for WASI; fix bug in path_open #451

Merged
merged 9 commits into from
May 21, 2019
4 changes: 2 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ Blocks of changes will separated by version increments.

## **[Unreleased]**

- *empty*
- [#451](https://github.com/wasmerio/wasmer/pull/451) Add `--mapdir=src:dest` flag to rename host directories in the guest context
- [#457](https://github.com/wasmerio/wasmer/pull/457) Implement file metadata for WASI, fix bugs in WASI clock code for Unix platforms

## 0.4.2 - 2019-05-16

- [#457](https://github.com/wasmerio/wasmer/pull/457) Implement file metadata for WASI, fix bugs in WASI clock code for Unix platforms
- [#416](https://github.com/wasmerio/wasmer/pull/416) Remote code loading framework
- [#449](https://github.com/wasmerio/wasmer/pull/449) Fix bugs: opening host files in filestat and opening with write permissions unconditionally in path_open
- [#442](https://github.com/wasmerio/wasmer/pull/442) Misc. WASI FS fixes and implement readdir
Expand Down
2 changes: 1 addition & 1 deletion examples/plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ fn main() {
));

// WASI imports
let mut base_imports = generate_import_object(vec![], vec![], vec![]);
let mut base_imports = generate_import_object(vec![], vec![], vec![], vec![]);
// env is the default namespace for extern functions
let custom_imports = imports! {
"env" => {
Expand Down
4 changes: 3 additions & 1 deletion lib/wasi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use self::state::{WasiFs, WasiState};
use self::syscalls::*;

use std::ffi::c_void;
use std::path::PathBuf;

pub use self::utils::is_wasi_module;

Expand All @@ -29,6 +30,7 @@ pub fn generate_import_object(
args: Vec<Vec<u8>>,
envs: Vec<Vec<u8>>,
preopened_files: Vec<String>,
mapped_dirs: Vec<(String, PathBuf)>,
) -> ImportObject {
let state_gen = move || {
fn state_destructor(data: *mut c_void) {
Expand All @@ -38,7 +40,7 @@ pub fn generate_import_object(
}

let state = Box::new(WasiState {
fs: WasiFs::new(&preopened_files).unwrap(),
fs: WasiFs::new(&preopened_files, &mapped_dirs).unwrap(),
args: &args[..],
envs: &envs[..],
});
Expand Down
66 changes: 38 additions & 28 deletions lib/wasi/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,21 +175,20 @@ pub struct WasiFs {
}

impl WasiFs {
pub fn new(preopened_dirs: &[String]) -> Result<Self, String> {
/*let repo = RepoOpener::new()
.create(true)
.open("mem://wasmer-test-fs", "")
.map_err(|e| e.to_string())?;*/
pub fn new(
preopened_dirs: &[String],
mapped_dirs: &[(String, PathBuf)],
) -> Result<Self, String> {
debug!("wasi::fs::inodes");
let inodes = Arena::new();
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),
};
debug!("wasi::fs::preopen_dirs");
for dir in preopened_dirs {
debug!("Attempting to preopen {}", &dir);
// TODO: think about this
Expand Down Expand Up @@ -218,6 +217,36 @@ impl WasiFs {
.create_fd(default_rights, default_rights, 0, inode)
.expect("Could not open fd");
}
debug!("wasi::fs::mapped_dirs");
for (alias, real_dir) in mapped_dirs {
debug!("Attempting to open {:?} at {}", dest_dir, alias);
// TODO: think about this
let default_rights = 0x1FFFFFFF; // all rights
let cur_dir_metadata = real_dir
.metadata()
.expect("mapped dir not at previously verified location");
let kind = if cur_dir_metadata.is_dir() {
Kind::Dir {
parent: None,
path: real_dir.clone(),
entries: Default::default(),
}
} else {
return Err(format!(
"WASI only supports pre-opened directories right now; found \"{:?}\"",
&real_dir,
));
};
// TODO: handle nested pats in `file`
let inode_val =
InodeVal::from_file_metadata(&cur_dir_metadata, alias.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");
Ok(wasi_fs)
}
Expand Down Expand Up @@ -419,29 +448,10 @@ impl WasiFs {
}

pub fn get_base_path_for_directory(&self, directory: Inode) -> Option<String> {
let mut path_segments = vec![];
let mut cur_inode = directory;
loop {
path_segments.push(self.inodes[cur_inode].name.clone());

if let Kind::Dir { parent, .. } = &self.inodes[cur_inode].kind {
if let Some(p_inode) = parent {
cur_inode = *p_inode;
} else {
break;
}
} else {
return None;
}
if let Kind::Dir { path, .. } = &self.inodes[directory].kind {
return Some(path.to_string_lossy().to_string());
}

path_segments.reverse();
Some(
path_segments
.iter()
.skip(1)
.fold(path_segments.first()?.clone(), |a, b| a + "/" + b),
)
None
}
}

Expand Down
6 changes: 4 additions & 2 deletions lib/wasi/src/syscalls/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -795,6 +795,7 @@ pub fn fd_readdir(
for entry in entries.iter().skip(cookie as usize) {
cur_cookie += 1;
let entry_path = entry.path();
let entry_path = wasi_try!(entry_path.file_stem().ok_or(__WASI_EIO));
let entry_path_str = entry_path.to_string_lossy();
let namlen = entry_path_str.len();
debug!("Returning dirent for {}", entry_path_str);
Expand Down Expand Up @@ -1486,10 +1487,11 @@ pub fn path_open(
.fs
.create_fd(fs_rights_base, fs_rights_inheriting, fs_flags, child))
} else {
let file_metadata = wasi_try!(file_path.metadata().map_err(|_| __WASI_ENOENT));
debug!("Attempting to load file from host system");
let file_metadata = file_path.metadata();
// if entry does not exist in parent directory, try to lazily
// load it; possibly creating or truncating it if flags set
let kind = if file_metadata.is_dir() {
let kind = if file_metadata.is_ok() && file_metadata.unwrap().is_dir() {
// special dir logic
Kind::Dir {
parent: Some(cur_dir_inode),
Expand Down
2 changes: 1 addition & 1 deletion lib/wasi/tests/wasitests/_common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ macro_rules! assert_wasi_output {
let module = wasmer_runtime_core::compile_with(&wasm_bytes[..], &get_compiler())
.expect("WASM can't be compiled");

let import_object = generate_import_object(vec![], vec![], vec![".".to_string()]);
let import_object = generate_import_object(vec![], vec![], vec![".".to_string()], vec![]);

let instance = module
.instantiate(&import_object)
Expand Down
35 changes: 34 additions & 1 deletion src/bin/wasmer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,11 @@ struct Run {
#[structopt(long = "dir", multiple = true, group = "wasi")]
pre_opened_directories: Vec<String>,

// Custom code loader
/// Map a host directory to a different location for the wasm module
#[structopt(long = "mapdir", multiple = true)]
mapped_dirs: Vec<String>,

/// Custom code loader
#[structopt(
long = "loader",
raw(possible_values = "LoaderName::variants()", case_insensitive = "true")
Expand Down Expand Up @@ -227,6 +231,31 @@ fn execute_wasm(options: &Run) -> Result<(), String> {
#[cfg(not(target_os = "windows"))]
let disable_cache = options.disable_cache;

let mapped_dirs = {
let mut md = vec![];
for entry in options.mapped_dirs.iter() {
if let &[alias, real_dir] = &entry.split(':').collect::<Vec<&str>>()[..] {
let pb = PathBuf::from(&real_dir);
if let Ok(pb_metadata) = pb.metadata() {
if !pb_metadata.is_dir() {
return Err(format!(
"\"{}\" exists, but it is not a directory",
&real_dir
));
}
} else {
return Err(format!("Directory \"{}\" does not exist", &real_dir));
}
md.push((alias.to_string(), pb));
continue;
}
return Err(format!(
"Directory mappings must consist of two paths separate by a colon. Found {}",
&entry
));
}
md
};
let wasm_path = &options.path;

let mut wasm_binary: Vec<u8> = read_file_contents(wasm_path).map_err(|err| {
Expand Down Expand Up @@ -416,6 +445,9 @@ fn execute_wasm(options: &Run) -> Result<(), String> {
.instantiate(&import_object)
.map_err(|e| format!("Can't instantiate module: {:?}", e))?;

if !mapped_dirs.is_empty() {
eprintln!("WARN: mapdir is not implemented for emscripten targets");
}
wasmer_emscripten::run_emscripten_instance(
&module,
&mut instance,
Expand Down Expand Up @@ -445,6 +477,7 @@ fn execute_wasm(options: &Run) -> Result<(), String> {
.map(|(k, v)| format!("{}={}", k, v).into_bytes())
.collect(),
options.pre_opened_directories.clone(),
mapped_dirs,
);

let instance = module
Expand Down