From f9beef276d1580197dfbbb007e7c92fe882ef2d6 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Thu, 16 May 2019 17:35:13 -0700 Subject: [PATCH 1/3] implement map-dir for WASI; fix bug in path_open --- lib/wasi/src/lib.rs | 4 ++- lib/wasi/src/state.rs | 55 +++++++++++++++++++++++++----------- lib/wasi/src/syscalls/mod.rs | 6 ++-- src/bin/wasmer.rs | 29 ++++++++++++++++++- 4 files changed, 74 insertions(+), 20 deletions(-) diff --git a/lib/wasi/src/lib.rs b/lib/wasi/src/lib.rs index 86f9ceaac4a..55459b74bee 100644 --- a/lib/wasi/src/lib.rs +++ b/lib/wasi/src/lib.rs @@ -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; @@ -29,6 +30,7 @@ pub fn generate_import_object( args: Vec>, envs: Vec>, preopened_files: Vec, + mapped_dirs: Vec<(PathBuf, String)>, ) -> ImportObject { let state_gen = move || { fn state_destructor(data: *mut c_void) { @@ -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[..], }); diff --git a/lib/wasi/src/state.rs b/lib/wasi/src/state.rs index 9a57e97a33f..d8d366e6bb6 100644 --- a/lib/wasi/src/state.rs +++ b/lib/wasi/src/state.rs @@ -175,21 +175,20 @@ pub struct WasiFs { } impl WasiFs { - pub fn new(preopened_dirs: &[String]) -> Result { - /*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: &[(PathBuf, String)], + ) -> Result { 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 @@ -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 (src_dir, dest_dir) in mapped_dirs { + debug!("Attempting to open {:?} at {}", src_dir, dest_dir); + // TODO: think about this + let default_rights = 0x1FFFFFFF; // all rights + let cur_dir_metadata = src_dir + .metadata() + .expect("mapped dir not at previously verified location"); + let kind = if cur_dir_metadata.is_dir() { + Kind::Dir { + parent: None, + path: src_dir.clone(), + entries: Default::default(), + } + } else { + return Err(format!( + "WASI only supports pre-opened directories right now; found \"{:?}\"", + &src_dir, + )); + }; + // TODO: handle nested pats in `file` + let inode_val = + InodeVal::from_file_metadata(&cur_dir_metadata, dest_dir.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) } @@ -424,24 +453,18 @@ impl WasiFs { 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[cur_inode].kind { + return Some(path.to_string_lossy().to_string()); } } - path_segments.reverse(); + /*path_segments.reverse(); Some( path_segments .iter() .skip(1) .fold(path_segments.first()?.clone(), |a, b| a + "/" + b), - ) + )*/ } } diff --git a/lib/wasi/src/syscalls/mod.rs b/lib/wasi/src/syscalls/mod.rs index f313b966479..4e5e72e67d3 100644 --- a/lib/wasi/src/syscalls/mod.rs +++ b/lib/wasi/src/syscalls/mod.rs @@ -1465,6 +1465,7 @@ pub fn path_open( let file_path = cumulative_path; let out_fd = if let Kind::Dir { entries, .. } = &mut state.fs.inodes[cur_dir_inode].kind { + debug!("Hm"); if let Some(child) = entries.get(file_name).cloned() { let child_inode_val = &state.fs.inodes[child]; // early return based on flags @@ -1485,10 +1486,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), diff --git a/src/bin/wasmer.rs b/src/bin/wasmer.rs index 8231c60b410..ca14f49a01c 100644 --- a/src/bin/wasmer.rs +++ b/src/bin/wasmer.rs @@ -97,7 +97,11 @@ struct Run { #[structopt(long = "dir", multiple = true, group = "wasi")] pre_opened_directories: Vec, - // Custom code loader + /// Map a host directory to a different location for the wasm module + #[structopt(long = "map-dir", multiple = true)] + mapped_dirs: Vec, + + /// Custom code loader #[structopt( long = "loader", raw(possible_values = "LoaderName::variants()", case_insensitive = "true") @@ -227,6 +231,28 @@ 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 &[source, dest] = &entry.split(':').collect::>()[..] { + let pb = PathBuf::from(&source); + if let Ok(pb_metadata) = pb.metadata() { + if !pb_metadata.is_dir() { + return Err(format!("\"{}\" exists, but it is not a directory", &source)); + } + } else { + return Err(format!("Directory \"{}\" does not exist", &source)); + } + md.push((pb, dest.to_string())); + 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 = read_file_contents(wasm_path).map_err(|err| { @@ -445,6 +471,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 From be4dd453c2d97fd53b19771b20556afb256c68da Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Mon, 20 May 2019 16:39:02 -0700 Subject: [PATCH 2/3] clean up and fix bugs --- CHANGELOG.md | 4 ++-- lib/wasi/src/state.rs | 19 +++---------------- lib/wasi/src/syscalls/mod.rs | 2 +- lib/wasi/tests/wasitests/_common.rs | 2 +- src/bin/wasmer.rs | 2 +- 5 files changed, 8 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b4051de1a5..ad600bdc154 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/lib/wasi/src/state.rs b/lib/wasi/src/state.rs index 0ca18c85c79..3b03162ba4d 100644 --- a/lib/wasi/src/state.rs +++ b/lib/wasi/src/state.rs @@ -448,23 +448,10 @@ impl WasiFs { } pub fn get_base_path_for_directory(&self, directory: Inode) -> Option { - let mut path_segments = vec![]; - let mut cur_inode = directory; - loop { - path_segments.push(self.inodes[cur_inode].name.clone()); - - if let Kind::Dir { path, .. } = &self.inodes[cur_inode].kind { - return Some(path.to_string_lossy().to_string()); - } + 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 } } diff --git a/lib/wasi/src/syscalls/mod.rs b/lib/wasi/src/syscalls/mod.rs index 7574c87b56c..87610eceacc 100644 --- a/lib/wasi/src/syscalls/mod.rs +++ b/lib/wasi/src/syscalls/mod.rs @@ -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); @@ -1466,7 +1467,6 @@ pub fn path_open( let file_path = cumulative_path; let out_fd = if let Kind::Dir { entries, .. } = &mut state.fs.inodes[cur_dir_inode].kind { - debug!("Hm"); if let Some(child) = entries.get(file_name).cloned() { let child_inode_val = &state.fs.inodes[child]; // early return based on flags diff --git a/lib/wasi/tests/wasitests/_common.rs b/lib/wasi/tests/wasitests/_common.rs index 1fbd783f42d..a3f45947e0c 100644 --- a/lib/wasi/tests/wasitests/_common.rs +++ b/lib/wasi/tests/wasitests/_common.rs @@ -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) diff --git a/src/bin/wasmer.rs b/src/bin/wasmer.rs index ca14f49a01c..1c232e1e6be 100644 --- a/src/bin/wasmer.rs +++ b/src/bin/wasmer.rs @@ -98,7 +98,7 @@ struct Run { pre_opened_directories: Vec, /// Map a host directory to a different location for the wasm module - #[structopt(long = "map-dir", multiple = true)] + #[structopt(long = "mapdir", multiple = true)] mapped_dirs: Vec, /// Custom code loader From 7cc967e709250af50473f9c8dcf3be4e9d518c03 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Mon, 20 May 2019 16:46:08 -0700 Subject: [PATCH 3/3] flip order of args in mapdir --- lib/wasi/src/lib.rs | 2 +- lib/wasi/src/state.rs | 14 +++++++------- src/bin/wasmer.rs | 13 ++++++++----- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/lib/wasi/src/lib.rs b/lib/wasi/src/lib.rs index 55459b74bee..8621dc55e8b 100644 --- a/lib/wasi/src/lib.rs +++ b/lib/wasi/src/lib.rs @@ -30,7 +30,7 @@ pub fn generate_import_object( args: Vec>, envs: Vec>, preopened_files: Vec, - mapped_dirs: Vec<(PathBuf, String)>, + mapped_dirs: Vec<(String, PathBuf)>, ) -> ImportObject { let state_gen = move || { fn state_destructor(data: *mut c_void) { diff --git a/lib/wasi/src/state.rs b/lib/wasi/src/state.rs index 3b03162ba4d..979d63d4d98 100644 --- a/lib/wasi/src/state.rs +++ b/lib/wasi/src/state.rs @@ -177,7 +177,7 @@ pub struct WasiFs { impl WasiFs { pub fn new( preopened_dirs: &[String], - mapped_dirs: &[(PathBuf, String)], + mapped_dirs: &[(String, PathBuf)], ) -> Result { debug!("wasi::fs::inodes"); let inodes = Arena::new(); @@ -218,28 +218,28 @@ impl WasiFs { .expect("Could not open fd"); } debug!("wasi::fs::mapped_dirs"); - for (src_dir, dest_dir) in mapped_dirs { - debug!("Attempting to open {:?} at {}", src_dir, dest_dir); + 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 = src_dir + 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: src_dir.clone(), + path: real_dir.clone(), entries: Default::default(), } } else { return Err(format!( "WASI only supports pre-opened directories right now; found \"{:?}\"", - &src_dir, + &real_dir, )); }; // TODO: handle nested pats in `file` let inode_val = - InodeVal::from_file_metadata(&cur_dir_metadata, dest_dir.clone(), true, kind); + 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(); diff --git a/src/bin/wasmer.rs b/src/bin/wasmer.rs index 1c232e1e6be..bc4da1c8512 100644 --- a/src/bin/wasmer.rs +++ b/src/bin/wasmer.rs @@ -234,16 +234,19 @@ fn execute_wasm(options: &Run) -> Result<(), String> { let mapped_dirs = { let mut md = vec![]; for entry in options.mapped_dirs.iter() { - if let &[source, dest] = &entry.split(':').collect::>()[..] { - let pb = PathBuf::from(&source); + if let &[alias, real_dir] = &entry.split(':').collect::>()[..] { + 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", &source)); + return Err(format!( + "\"{}\" exists, but it is not a directory", + &real_dir + )); } } else { - return Err(format!("Directory \"{}\" does not exist", &source)); + return Err(format!("Directory \"{}\" does not exist", &real_dir)); } - md.push((pb, dest.to_string())); + md.push((alias.to_string(), pb)); continue; } return Err(format!(