From f9beef276d1580197dfbbb007e7c92fe882ef2d6 Mon Sep 17 00:00:00 2001 From: Mark McCaskey Date: Thu, 16 May 2019 17:35:13 -0700 Subject: [PATCH] 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