From 66e71fca5e3816bb7cef650cbf60ec14f672c0e2 Mon Sep 17 00:00:00 2001 From: Syrus Akbary Date: Mon, 6 Sep 2021 10:21:03 +0200 Subject: [PATCH 1/6] Added WASI test generator command. Added new dir path rename wasi testcase --- Cargo.lock | 46 +++++++++++++++++- Cargo.toml | 2 + Makefile | 4 ++ tests/wasi-wast/Cargo.toml | 2 +- tests/wasi-wast/src/wasi_version.rs | 2 +- tests/wasi-wast/src/wasitests.rs | 2 +- .../wasi/snapshot1/fd_rename_path.wasm | Bin 0 -> 63501 bytes .../wasi/snapshot1/fd_rename_path.wast | 7 +++ tests/wasi-wast/wasi/tests/fd_rename_path.rs | 11 +++++ 9 files changed, 71 insertions(+), 5 deletions(-) create mode 100755 tests/wasi-wast/wasi/snapshot1/fd_rename_path.wasm create mode 100644 tests/wasi-wast/wasi/snapshot1/fd_rename_path.wast create mode 100644 tests/wasi-wast/wasi/tests/fd_rename_path.rs diff --git a/Cargo.lock b/Cargo.lock index df08919502a..f16024dd151 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -891,6 +891,26 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" +[[package]] +name = "gumdrop" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46571f5d540478cf70d2a42dd0d6d8e9f4b9cc7531544b93311e657b86568a0b" +dependencies = [ + "gumdrop_derive", +] + +[[package]] +name = "gumdrop_derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915ef07c710d84733522461de2a734d4d62a3fd39a4d4f404c2f385ef8618d05" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "half" version = "1.7.1" @@ -2364,6 +2384,18 @@ version = "0.10.2+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" +[[package]] +name = "wasi-test-generator" +version = "2.0.0" +dependencies = [ + "glob", + "gumdrop", + "serde", + "serde_json", + "tempfile", + "wast 24.0.0", +] + [[package]] name = "wasm-bindgen" version = "0.2.75" @@ -2926,7 +2958,7 @@ dependencies = [ "wasmer", "wasmer-vfs", "wasmer-wasi", - "wast", + "wast 37.0.0", ] [[package]] @@ -2948,6 +2980,7 @@ dependencies = [ "test-generator", "tracing", "tracing-subscriber", + "wasi-test-generator", "wasmer", "wasmer-cache", "wasmer-compiler", @@ -3003,6 +3036,15 @@ dependencies = [ "wasmparser 0.80.0", ] +[[package]] +name = "wast" +version = "24.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ff1e3bd3ad0b2ee7784add89c30dc96b89a54b43e5d6d95d774eda1863b3500" +dependencies = [ + "leb128", +] + [[package]] name = "wast" version = "37.0.0" @@ -3018,7 +3060,7 @@ version = "1.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2ab2cc8d9a69d1ab28a41d9149bb06bb927aba8fc9d56625f8b597a564c83f50" dependencies = [ - "wast", + "wast 37.0.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index a19e0789477..44449c8945e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,7 @@ wasmer-engine-dylib = { version = "2.0.0", path = "lib/engine-dylib", optional = wasmer-engine-staticlib = { version = "2.0.0", path = "lib/engine-staticlib", optional = true } wasmer-wasi = { version = "2.0.0", path = "lib/wasi", optional = true } wasmer-wast = { version = "2.0.0", path = "tests/lib/wast", optional = true } +wasi-test-generator = { version = "2.0.0", path = "tests/wasi-wast", optional = true } wasmer-cache = { version = "2.0.0", path = "lib/cache", optional = true } wasmer-types = { version = "2.0.0", path = "lib/types" } wasmer-middlewares = { version = "2.0.0", path = "lib/middlewares", optional = true } @@ -50,6 +51,7 @@ members = [ "lib/wasi-types", "lib/wasi-experimental-io-devices", "lib/types", + "tests/wasi-wast", "tests/lib/wast", "tests/lib/compiler-test-derive", "tests/integration/cli", diff --git a/Makefile b/Makefile index 9c0d92e5c28..36b61be76ee 100644 --- a/Makefile +++ b/Makefile @@ -573,6 +573,10 @@ test-integration: test-integration-ios: cargo test -p wasmer-integration-tests-ios +generate-wasi-tests: +# Uncomment the following for installing the toolchain +# cargo run -p wasi-test-generator -- -s + cargo run -p wasi-test-generator -- -g ##### # # Packaging. diff --git a/tests/wasi-wast/Cargo.toml b/tests/wasi-wast/Cargo.toml index 76ae5b76f53..bc8022faa02 100644 --- a/tests/wasi-wast/Cargo.toml +++ b/tests/wasi-wast/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wasi-test-generator" -version = "0.17.0" +version = "2.0.0" description = "Tests for our WASI implementation" license = "MIT" authors = ["Wasmer Engineering Team "] diff --git a/tests/wasi-wast/src/wasi_version.rs b/tests/wasi-wast/src/wasi_version.rs index 10abf6785e8..88dd4c61b20 100644 --- a/tests/wasi-wast/src/wasi_version.rs +++ b/tests/wasi-wast/src/wasi_version.rs @@ -16,7 +16,7 @@ impl WasiVersion { pub fn get_compiler_toolchain(&self) -> &'static str { match self { WasiVersion::Unstable => "nightly-2019-09-13", - WasiVersion::Snapshot1 => "nightly-2019-12-18", + WasiVersion::Snapshot1 => "1.53.0", } } diff --git a/tests/wasi-wast/src/wasitests.rs b/tests/wasi-wast/src/wasitests.rs index 72fa7ab0b81..39e8a426a74 100644 --- a/tests/wasi-wast/src/wasitests.rs +++ b/tests/wasi-wast/src/wasitests.rs @@ -147,7 +147,7 @@ fn compile_wasm_for_version( .create(true) .open(&temp_wasi_rs_file_name) .unwrap(); - actual_file.write_all(b"#![feature(wasi_ext)]\n").unwrap(); + // actual_file.write_all(b"#![feature(wasi_ext)]\n").unwrap(); actual_file.write_all(file_contents.as_bytes()).unwrap(); } diff --git a/tests/wasi-wast/wasi/snapshot1/fd_rename_path.wasm b/tests/wasi-wast/wasi/snapshot1/fd_rename_path.wasm new file mode 100755 index 0000000000000000000000000000000000000000..e3396e3ec611953df56067ec6761f8277dfd259d GIT binary patch literal 63501 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 BY+(QZ literal 0 HcmV?d00001 diff --git a/tests/wasi-wast/wasi/snapshot1/fd_rename_path.wast b/tests/wasi-wast/wasi/snapshot1/fd_rename_path.wast new file mode 100644 index 00000000000..97046bb5783 --- /dev/null +++ b/tests/wasi-wast/wasi/snapshot1/fd_rename_path.wast @@ -0,0 +1,7 @@ +;; This file was generated by https://github.com/wasmerio/wasi-tests + +(wasi_test "fd_rename_path.wasm" + (preopens "test_fs") + (assert_return (i64.const 101)) + (assert_stderr "thread 'main' panicked at 'assertion failed: fs::rename(old_path, new_path).is_ok()', /Users/syrus/Development/wasmer/tests/wasi-wast/wasi/tests/fd_rename_path.rs:10:3\nnote: run with `RUST_BACKTRACE=1` environment variable to display a backtrace\n") +) diff --git a/tests/wasi-wast/wasi/tests/fd_rename_path.rs b/tests/wasi-wast/wasi/tests/fd_rename_path.rs new file mode 100644 index 00000000000..66e9c7b537f --- /dev/null +++ b/tests/wasi-wast/wasi/tests/fd_rename_path.rs @@ -0,0 +1,11 @@ +// WASI: +// dir: test_fs + +use std::fs; + +fn main() { + let old_path = "test_fs/oldname"; + let new_path = "test_fs/newname"; + + assert!(fs::rename(old_path, new_path).is_ok()); +} From c889c693e881ff422930db7aa1beccd9f7587866 Mon Sep 17 00:00:00 2001 From: Syrus Akbary Date: Mon, 6 Sep 2021 10:22:22 +0200 Subject: [PATCH 2/6] Added new test-wasi command --- Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Makefile b/Makefile index 36b61be76ee..b9c8e9b90f3 100644 --- a/Makefile +++ b/Makefile @@ -564,6 +564,9 @@ test-capi-integration-%: test-wasi-unit: cargo test --manifest-path lib/wasi/Cargo.toml --release +test-wasi: + cargo test --release --tests $(compiler_features) -- wasi::wasitests::snapshot1 + test-examples: cargo test --release $(compiler_features) --features wasi --examples From 08580b533102adee694f4be7c2d0cd551891a690 Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Mon, 6 Sep 2021 13:55:31 +0200 Subject: [PATCH 3/6] wip Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- lib/vfs/src/host_fs.rs | 9 + lib/vfs/src/lib.rs | 2 + lib/vfs/src/mem_fs/filesystem.rs | 4 + lib/wasi/src/state/mod.rs | 14 ++ lib/wasi/src/syscalls/mod.rs | 161 +++++++++++++----- .../wasi/test_fs/oldname/fakefile.txt | 0 tests/wasi-wast/wasi/tests/fd_rename_path.rs | 7 +- 7 files changed, 151 insertions(+), 46 deletions(-) create mode 100644 tests/wasi-wast/wasi/test_fs/oldname/fakefile.txt diff --git a/lib/vfs/src/host_fs.rs b/lib/vfs/src/host_fs.rs index 19475768e51..75ba9585b0f 100644 --- a/lib/vfs/src/host_fs.rs +++ b/lib/vfs/src/host_fs.rs @@ -100,6 +100,15 @@ impl crate::FileSystem for FileSystem { fs::rename(from, to).map_err(Into::into) } + fn rename_dir(&self, from: &Path, to: &Path) -> Result<()> { + let metadata = self.metadata(from)?; + if metadata.is_dir() { + fs::rename(from, to).map_err(Into::into) + } else { + Err(FsError::BaseNotDirectory) + } + } + fn remove_file(&self, path: &Path) -> Result<()> { fs::remove_file(path).map_err(Into::into) } diff --git a/lib/vfs/src/lib.rs b/lib/vfs/src/lib.rs index d69f52e8c13..df2f8335dfb 100644 --- a/lib/vfs/src/lib.rs +++ b/lib/vfs/src/lib.rs @@ -27,6 +27,7 @@ pub trait FileSystem: fmt::Debug + Send + Sync + 'static { fn create_dir(&self, path: &Path) -> Result<()>; fn remove_dir(&self, path: &Path) -> Result<()>; fn rename(&self, from: &Path, to: &Path) -> Result<()>; + fn rename_dir(&self, from: &Path, to: &Path) -> Result<()>; fn metadata(&self, path: &Path) -> Result; /// This method gets metadata without following symlinks in the path. /// Currently identical to `metadata` because symlinks aren't implemented @@ -35,6 +36,7 @@ pub trait FileSystem: fmt::Debug + Send + Sync + 'static { self.metadata(path) } fn remove_file(&self, path: &Path) -> Result<()>; + fn new_open_options(&self) -> OpenOptions; } diff --git a/lib/vfs/src/mem_fs/filesystem.rs b/lib/vfs/src/mem_fs/filesystem.rs index d22bec69003..8564240171d 100644 --- a/lib/vfs/src/mem_fs/filesystem.rs +++ b/lib/vfs/src/mem_fs/filesystem.rs @@ -224,6 +224,10 @@ impl crate::FileSystem for FileSystem { Ok(()) } + fn rename_dir(&self, from: &Path, to: &Path) -> Result<()> { + unimplemented!("not already implemented for mem_fs") + } + fn metadata(&self, path: &Path) -> Result { // Read lock. let fs = self.inner.try_read().map_err(|_| FsError::Lock)?; diff --git a/lib/wasi/src/state/mod.rs b/lib/wasi/src/state/mod.rs index fa3fdf22376..f17641b30af 100644 --- a/lib/wasi/src/state/mod.rs +++ b/lib/wasi/src/state/mod.rs @@ -207,6 +207,9 @@ impl FileSystem for FallbackFileSystem { fn rename(&self, _from: &Path, _to: &Path) -> Result<(), FsError> { Self::fail(); } + fn rename_dir(&self, _from: &Path, _to: &Path) -> Result<(), FsError> { + Self::fail(); + } fn metadata(&self, _path: &Path) -> Result { Self::fail(); } @@ -1514,6 +1517,17 @@ impl WasiState { .map_err(fs_error_into_wasi_err) } + pub(crate) fn fs_rename_dir, Q: AsRef>( + &self, + from: P, + to: Q, + ) -> Result<(), __wasi_errno_t> { + self.fs + .fs_backing + .rename_dir(from.as_ref(), to.as_ref()) + .map_err(fs_error_into_wasi_err) + } + pub(crate) fn fs_remove_file>(&self, path: P) -> Result<(), __wasi_errno_t> { self.fs .fs_backing diff --git a/lib/wasi/src/syscalls/mod.rs b/lib/wasi/src/syscalls/mod.rs index ac6a546c0d7..6ce60132bec 100644 --- a/lib/wasi/src/syscalls/mod.rs +++ b/lib/wasi/src/syscalls/mod.rs @@ -2029,6 +2029,7 @@ pub fn path_rename( new_path: WasmPtr, new_path_len: u32, ) -> __wasi_errno_t { + println!("!!!!!!!"); debug!( "wasi::path_rename: old_fd = {}, new_fd = {}", old_fd, new_fd @@ -2040,6 +2041,7 @@ pub fn path_rename( let target_path = std::path::Path::new(&target_str); debug!("=> rename from {} to {}", &source_str, &target_str); + println!("check rights"); { let source_fd = wasi_try!(state.fs.get_fd(old_fd)); if !has_rights(source_fd.rights, __WASI_RIGHT_PATH_RENAME_SOURCE) { @@ -2053,6 +2055,7 @@ pub fn path_rename( let (source_parent_inode, source_entry_name) = wasi_try!(state.fs.get_parent_inode_at_path(old_fd, source_path, true)); + println!("rights checked --- {:?}", source_entry_name); let (target_parent_inode, target_entry_name) = wasi_try!(state.fs.get_parent_inode_at_path(new_fd, target_path, true)); @@ -2070,56 +2073,128 @@ pub fn path_rename( unreachable!("Fatal internal logic error: parent of inode is not a directory") } }; - let source_entry = match &mut state.fs.inodes[source_parent_inode].kind { - Kind::Dir { entries, .. } => wasi_try!(entries.remove(&source_entry_name), __WASI_EINVAL), - Kind::Root { .. } => return __WASI_ENOTCAPABLE, - Kind::Symlink { .. } | Kind::File { .. } | Kind::Buffer { .. } => { - unreachable!("Fatal internal logic error: parent of inode is not a directory") - } - }; + println!( + "host_adjusted_target_path = {:?}", + host_adjusted_target_path + ); + // let source_entry = + match &mut state.fs.inodes[source_parent_inode].kind { + Kind::Dir { entries, .. } => { + println!("it's a dir -- entries --- {:#?}", entries); + // if entries.contains_key(&source_entry_name).is_some() { + match entries.remove(&source_entry_name) { + Some(source_entry) => { + match &mut state.fs.inodes[source_entry].kind { + Kind::File { + handle, ref path, .. + } => { + // TODO: investigate why handle is not always there, it probably should be. + // My best guess is the fact that a handle means currently open and a path + // just means reference to host file on disk. But ideally those concepts + // could just be unified even if there's a `Box` which just + // implements the logic of "I'm not actually a file, I'll try to be as needed". + let result = if let Some(h) = handle { + state.fs_rename(&source_path, &host_adjusted_target_path) + } else { + let path_clone = path.clone(); + let out = state.fs_rename(&path_clone, &host_adjusted_target_path); + if let Kind::File { ref mut path, .. } = + &mut state.fs.inodes[source_entry].kind + { + *path = host_adjusted_target_path; + } else { + unreachable!() + } + out + }; + // if the above operation failed we have to revert the previous change and then fail + if let Err(e) = result { + if let Kind::Dir { entries, .. } = + &mut state.fs.inodes[source_parent_inode].kind + { + entries.insert(source_entry_name, source_entry); + return e; + } + } + } + Kind::Dir { ref path, .. } => { + // path seems to be sourcepath + println!("source_path {:?}", path); + println!("target_path {:?}", host_adjusted_target_path); + let cloned_path = path.clone(); + // wasi_try!( + state + .fs_rename_dir(cloned_path, &host_adjusted_target_path) + .expect("cannot rename the directory"); + // __WASI_EINVAL + // ); + // if let Err(e) = out { + // // Will see + // println!("ERROR {}", e); + // } + unreachable!() + } + Kind::Buffer { .. } => {} + Kind::Symlink { .. } => {} + Kind::Root { .. } => unreachable!("The root can not be moved"), + } - match &mut state.fs.inodes[source_entry].kind { - Kind::File { - handle, ref path, .. - } => { - // TODO: investigate why handle is not always there, it probably should be. - // My best guess is the fact that a handle means currently open and a path - // just means reference to host file on disk. But ideally those concepts - // could just be unified even if there's a `Box` which just - // implements the logic of "I'm not actually a file, I'll try to be as needed". - let result = if let Some(h) = handle { - state.fs_rename(&source_path, &host_adjusted_target_path) - } else { - let path_clone = path.clone(); - let out = state.fs_rename(&path_clone, &host_adjusted_target_path); - if let Kind::File { ref mut path, .. } = &mut state.fs.inodes[source_entry].kind { - *path = host_adjusted_target_path; - } else { - unreachable!() + if let Kind::Dir { entries, .. } = + &mut state.fs.inodes[target_parent_inode].kind + { + let result = entries.insert(target_entry_name, source_entry); + assert!( + result.is_none(), + "Fatal error: race condition on filesystem detected or internal logic error" + ); + } } - out - }; - // if the above operation failed we have to revert the previous change and then fail - if let Err(e) = result { - if let Kind::Dir { entries, .. } = &mut state.fs.inodes[source_parent_inode].kind { - entries.insert(source_entry_name, source_entry); - return e; + None => { + // QUICK and VERY dirty + let source_path = match &state.fs.inodes[source_parent_inode].kind { + Kind::Dir { entries, path, .. } => { + if entries.contains_key(&source_entry_name) { + return __WASI_EEXIST; + } + let mut out_path = path.clone(); + out_path.push(std::path::Path::new(&source_entry_name)); + out_path + } + Kind::Root { .. } => return __WASI_ENOTCAPABLE, + Kind::Symlink { .. } | Kind::File { .. } | Kind::Buffer { .. } => { + unreachable!( + "Fatal internal logic error: parent of inode is not a directory" + ) + } + }; + // Should be a directory + // path seems to be sourcepath + println!("source_path {:?}", source_path); + println!("target_path {:?}", host_adjusted_target_path); + // let cloned_path = path.clone(); + // wasi_try!( + state + .fs_rename_dir(&source_path, &host_adjusted_target_path) + .expect("cannot rename the directory"); + // __WASI_EINVAL + // ); + // if let Err(e) = out { + // // Will see + // println!("ERROR {}", e); + // } } } + // } else { + // wasi_try!(entries.remove(&source_entry_name), __WASI_EINVAL) + // } + } + Kind::Root { .. } => return __WASI_ENOTCAPABLE, + Kind::Symlink { .. } | Kind::File { .. } | Kind::Buffer { .. } => { + unreachable!("Fatal internal logic error: parent of inode is not a directory") } - Kind::Dir { path, .. } => unimplemented!("wasi::path_rename on Directories"), - Kind::Buffer { .. } => {} - Kind::Symlink { .. } => {} - Kind::Root { .. } => unreachable!("The root can not be moved"), } - if let Kind::Dir { entries, .. } = &mut state.fs.inodes[target_parent_inode].kind { - let result = entries.insert(target_entry_name, source_entry); - assert!( - result.is_none(), - "Fatal error: race condition on filesystem detected or internal logic error" - ); - } + println!("OKKKKK"); __WASI_ESUCCESS } diff --git a/tests/wasi-wast/wasi/test_fs/oldname/fakefile.txt b/tests/wasi-wast/wasi/test_fs/oldname/fakefile.txt new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/wasi-wast/wasi/tests/fd_rename_path.rs b/tests/wasi-wast/wasi/tests/fd_rename_path.rs index 66e9c7b537f..b802c1743e1 100644 --- a/tests/wasi-wast/wasi/tests/fd_rename_path.rs +++ b/tests/wasi-wast/wasi/tests/fd_rename_path.rs @@ -4,8 +4,9 @@ use std::fs; fn main() { - let old_path = "test_fs/oldname"; - let new_path = "test_fs/newname"; + let old_path = "test_fs/oldname"; + let new_path = "test_fs/newname"; - assert!(fs::rename(old_path, new_path).is_ok()); + assert!(fs::rename(old_path, new_path).is_ok()); + assert!(fs::rename(new_path, old_path).is_ok()); } From 8596a9fa1141debc8f3dec92256b9d4552183bab Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Mon, 6 Sep 2021 18:10:07 +0200 Subject: [PATCH 4/6] wip, remove_dir bug Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- lib/c-api/wasmer.h | 9 + lib/wasi/src/syscalls/mod.rs | 168 ++++++------------ .../wasi/snapshot1/fd_rename_path.wasm | Bin 63501 -> 72348 bytes .../wasi/snapshot1/fd_rename_path.wast | 3 +- tests/wasi-wast/wasi/tests/fd_rename_path.rs | 10 +- 5 files changed, 68 insertions(+), 122 deletions(-) diff --git a/lib/c-api/wasmer.h b/lib/c-api/wasmer.h index 4142d4bd9a6..91e59e4a053 100644 --- a/lib/c-api/wasmer.h +++ b/lib/c-api/wasmer.h @@ -62,9 +62,18 @@ # define DEPRECATED(message) __declspec(deprecated(message)) #endif +// The `universal` feature has been enabled for this build. +#define WASMER_UNIVERSAL_ENABLED + +// The `compiler` feature has been enabled for this build. +#define WASMER_COMPILER_ENABLED + // The `wasi` feature has been enabled for this build. #define WASMER_WASI_ENABLED +// The `middlewares` feature has been enabled for this build. +#define WASMER_MIDDLEWARES_ENABLED + // This file corresponds to the following Wasmer version. #define WASMER_VERSION "2.0.0" #define WASMER_VERSION_MAJOR 2 diff --git a/lib/wasi/src/syscalls/mod.rs b/lib/wasi/src/syscalls/mod.rs index 6ce60132bec..fde1e2ff0f5 100644 --- a/lib/wasi/src/syscalls/mod.rs +++ b/lib/wasi/src/syscalls/mod.rs @@ -2029,7 +2029,6 @@ pub fn path_rename( new_path: WasmPtr, new_path_len: u32, ) -> __wasi_errno_t { - println!("!!!!!!!"); debug!( "wasi::path_rename: old_fd = {}, new_fd = {}", old_fd, new_fd @@ -2041,7 +2040,6 @@ pub fn path_rename( let target_path = std::path::Path::new(&target_str); debug!("=> rename from {} to {}", &source_str, &target_str); - println!("check rights"); { let source_fd = wasi_try!(state.fs.get_fd(old_fd)); if !has_rights(source_fd.rights, __WASI_RIGHT_PATH_RENAME_SOURCE) { @@ -2055,7 +2053,6 @@ pub fn path_rename( let (source_parent_inode, source_entry_name) = wasi_try!(state.fs.get_parent_inode_at_path(old_fd, source_path, true)); - println!("rights checked --- {:?}", source_entry_name); let (target_parent_inode, target_entry_name) = wasi_try!(state.fs.get_parent_inode_at_path(new_fd, target_path, true)); @@ -2073,128 +2070,67 @@ pub fn path_rename( unreachable!("Fatal internal logic error: parent of inode is not a directory") } }; - println!( - "host_adjusted_target_path = {:?}", - host_adjusted_target_path - ); - // let source_entry = - match &mut state.fs.inodes[source_parent_inode].kind { + + let source_entry = match &mut state.fs.inodes[source_parent_inode].kind { Kind::Dir { entries, .. } => { - println!("it's a dir -- entries --- {:#?}", entries); - // if entries.contains_key(&source_entry_name).is_some() { - match entries.remove(&source_entry_name) { - Some(source_entry) => { - match &mut state.fs.inodes[source_entry].kind { - Kind::File { - handle, ref path, .. - } => { - // TODO: investigate why handle is not always there, it probably should be. - // My best guess is the fact that a handle means currently open and a path - // just means reference to host file on disk. But ideally those concepts - // could just be unified even if there's a `Box` which just - // implements the logic of "I'm not actually a file, I'll try to be as needed". - let result = if let Some(h) = handle { - state.fs_rename(&source_path, &host_adjusted_target_path) - } else { - let path_clone = path.clone(); - let out = state.fs_rename(&path_clone, &host_adjusted_target_path); - if let Kind::File { ref mut path, .. } = - &mut state.fs.inodes[source_entry].kind - { - *path = host_adjusted_target_path; - } else { - unreachable!() - } - out - }; - // if the above operation failed we have to revert the previous change and then fail - if let Err(e) = result { - if let Kind::Dir { entries, .. } = - &mut state.fs.inodes[source_parent_inode].kind - { - entries.insert(source_entry_name, source_entry); - return e; - } - } - } - Kind::Dir { ref path, .. } => { - // path seems to be sourcepath - println!("source_path {:?}", path); - println!("target_path {:?}", host_adjusted_target_path); - let cloned_path = path.clone(); - // wasi_try!( - state - .fs_rename_dir(cloned_path, &host_adjusted_target_path) - .expect("cannot rename the directory"); - // __WASI_EINVAL - // ); - // if let Err(e) = out { - // // Will see - // println!("ERROR {}", e); - // } - unreachable!() - } - Kind::Buffer { .. } => {} - Kind::Symlink { .. } => {} - Kind::Root { .. } => unreachable!("The root can not be moved"), - } + wasi_try!(entries.remove(&source_entry_name), __WASI_EINVAL) + } + Kind::Root { .. } => return __WASI_ENOTCAPABLE, + Kind::Symlink { .. } | Kind::File { .. } | Kind::Buffer { .. } => { + unreachable!("Fatal internal logic error: parent of inode is not a directory") + } + }; - if let Kind::Dir { entries, .. } = - &mut state.fs.inodes[target_parent_inode].kind - { - let result = entries.insert(target_entry_name, source_entry); - assert!( - result.is_none(), - "Fatal error: race condition on filesystem detected or internal logic error" - ); - } + match &mut state.fs.inodes[source_entry].kind { + Kind::File { + handle, ref path, .. + } => { + // TODO: investigate why handle is not always there, it probably should be. + // My best guess is the fact that a handle means currently open and a path + // just means reference to host file on disk. But ideally those concepts + // could just be unified even if there's a `Box` which just + // implements the logic of "I'm not actually a file, I'll try to be as needed". + let result = if let Some(h) = handle { + state.fs_rename(&source_path, &host_adjusted_target_path) + } else { + let path_clone = path.clone(); + let out = state.fs_rename(&path_clone, &host_adjusted_target_path); + if let Kind::File { ref mut path, .. } = &mut state.fs.inodes[source_entry].kind { + *path = host_adjusted_target_path; + } else { + unreachable!() } - None => { - // QUICK and VERY dirty - let source_path = match &state.fs.inodes[source_parent_inode].kind { - Kind::Dir { entries, path, .. } => { - if entries.contains_key(&source_entry_name) { - return __WASI_EEXIST; - } - let mut out_path = path.clone(); - out_path.push(std::path::Path::new(&source_entry_name)); - out_path - } - Kind::Root { .. } => return __WASI_ENOTCAPABLE, - Kind::Symlink { .. } | Kind::File { .. } | Kind::Buffer { .. } => { - unreachable!( - "Fatal internal logic error: parent of inode is not a directory" - ) - } - }; - // Should be a directory - // path seems to be sourcepath - println!("source_path {:?}", source_path); - println!("target_path {:?}", host_adjusted_target_path); - // let cloned_path = path.clone(); - // wasi_try!( - state - .fs_rename_dir(&source_path, &host_adjusted_target_path) - .expect("cannot rename the directory"); - // __WASI_EINVAL - // ); - // if let Err(e) = out { - // // Will see - // println!("ERROR {}", e); - // } + out + }; + // if the above operation failed we have to revert the previous change and then fail + if let Err(e) = result { + if let Kind::Dir { entries, .. } = &mut state.fs.inodes[source_parent_inode].kind { + entries.insert(source_entry_name, source_entry); + return e; } } - // } else { - // wasi_try!(entries.remove(&source_entry_name), __WASI_EINVAL) - // } } - Kind::Root { .. } => return __WASI_ENOTCAPABLE, - Kind::Symlink { .. } | Kind::File { .. } | Kind::Buffer { .. } => { - unreachable!("Fatal internal logic error: parent of inode is not a directory") + Kind::Dir { ref path, .. } => { + let cloned_path = path.clone(); + if let Err(e) = state.fs_rename(cloned_path, &host_adjusted_target_path) { + return e; + } + if let Kind::Dir { path, .. } = &mut state.fs.inodes[source_entry].kind { + *path = host_adjusted_target_path; + } } + Kind::Buffer { .. } => {} + Kind::Symlink { .. } => {} + Kind::Root { .. } => unreachable!("The root can not be moved"), } - println!("OKKKKK"); + if let Kind::Dir { entries, .. } = &mut state.fs.inodes[target_parent_inode].kind { + let result = entries.insert(target_entry_name, source_entry); + assert!( + result.is_none(), + "Fatal error: race condition on filesystem detected or internal logic error" + ); + } __WASI_ESUCCESS } diff --git a/tests/wasi-wast/wasi/snapshot1/fd_rename_path.wasm b/tests/wasi-wast/wasi/snapshot1/fd_rename_path.wasm index e3396e3ec611953df56067ec6761f8277dfd259d..2b0a82ffe9d35aed2999220885702aeea83d5461 100755 GIT binary patch delta 33654 zcmb`w3z!^Lng3sx?w+3Mo|(%1GD)cJT$qI1P)I@oI5m(!j6gt?iy#CtKqmK@;o_=v zAQ2-*1q<1zs8MkfT~Hu^r~wB>MTv?6iWmi5)t=UxW##wzzNdO-5+nNjpMUby zRGs@d@BO^zt?Ku^7~XbYuE|@`yVmzS&wu;PO_MLk)%fRK?AQEjFAjO+=6M$f?!m8J zY~|eZs#hC$@GdS`hH|ne@8a-Ub)~<4E%#sVb&CUJIeya`e zulLlx?mz4|mU6iu2!=I!enUe8>l^%+_gHTjhCzu6LEaVkzV9hf%I8AQYsiJ6m(LZO z!n_w23b|rKu~=+sq9h-L`Jg~+{&|H$F&EI6mkY{XV{?w4{hU{$nx79Fy?j3Jx8&(T z<9S}L*$X4jYw-&?Ki5hf&nxG=T&`H~{9Mkbdgu+WPqWd><^2u*nl*V^4=aodYksn6 zQ@>ZLdC3Dm9&&G(pD(NhB0X;lywXJ%UD357y)&3t`?jB3yJFQk?>m0udstfHmsYow zn}wXfy;Zy!C{>sXVmWA^cEIRhM+a{mznj@!8KYmL4Q8U(WIOn{#oPXh)-h9q^=U(vEqpw)iy?XShHEYLD828SW zt^N)EyZuM}kNf}Vf5qQc^FQggjJe(aod3`MH~c&NJN?i5-}1ldH$D2(@L~U@z%SOK zcTDz@XmvH1;w51v3_A2bw7+o}2T8Fz%C|HMQTpeHr8t=76&L3^;-Eb!SN+8Aj(V!O z#mzaY=VCu9H0!Ry?xf}|^t_-J=PG`(1#qUR+Fa z^MW$KDkkObO5h3#U?rFM(TJ)C#J%LVU;E5|7Up?gT;DlI~fiGkINOY_5GT-pgV zVn5xG98vS56H6iw`aa;A82%IQ^`J8A1i1Rmfu&*=Ts0q!0%%K&JE?d(9bfg&UIql2#WXsH*sZFz`1J&`<|k^$_TXg8o=RKlT>)h)QEGojPfF zEp7k_QD-Hvxx`I_QxY96u{nhad>4s*=LJP>e)5iuqAjLH6`&K&@*+3@RFD_82Sua# z7}SYF;px>{8h7!B0LGYLfF-^qSYqwqxPwXUZ!8oP{!L0t`;Is=r^Th8IO53kkt62R zo^$ZabwslhzqbMt`$?|5lINE1hR>UW`kY~sIA@DT6y@Vw5}bk@(sC*Yr8-4Jkg%xA zax6T6E3y%w@o2W_Tx63kAsQsP(w_K;nyuUjmwYdo&|N2HXjimpL?Cu5Q@ZJs%8(?> zb@KZ$Lhp?8&AoYM93GoEN(+QK>F0P}tPD2&Xq~O+@X?a}o2sdLvssW7& z+z@wT;i*u3`{YfI^TGkHKt78<)c;sPB}_j!t-XePbPxkEA=@@;!oWwhCNB#_6O!$$&On4V%2@;YWhgM`Z>;7Qdgu*AQIOO` zFZGV<=oUd$8Ynd?S_~OAh`t(F{*X)|uHn@Xw@Z_d)QY96O7-6B#H8+%8uOxpBPe0i zNQrU&eo|@}LQ0UYSQIcCi*h%926V7!)oV8VCUc#f72-}4%EZDBYqxxmDT_SrkR&}S zxm{_Kvrh`^Gm=fpfc8pAPSaehOB(1#Ewh-uE3EpB>8|kjqsuUhDwIOcFAYOHwFf!r zD5i%FES>73%1jrBnjs94wzZ5;KQ(%I4GpQvs&xUipaAR-iDqwSHJ7~NZ%lF$;n0X9 z6|Zx_*5eW8P)}$3))TyDdn#@{(Q9N5ZMM2(JBlC<95zBXv7Z-$rdm!ac0ly+qE;=L z7+pf6s5|)v$z(!wiJJmilX*hwt--ZCv`SzJdm?DB5*Xh|njmj&VKf_3dhvmYk z;F0`7o!)yLU>wFM?E-wUn;wiM;&3{K>HIJzH}0-BsAoUsR@4A=P!#|fgW+)%;>ZjHD0bra;uIbfKsJN6Df7ZTX)i*myGPL7NqSZlxUd4O3E5G)#CEb3i2mas<&Giw>%8mB;aq~|2EE^Km;;CFM$d1ywHED@G#tP!*dXsu>TTZ#! zC@Zma@lHfYF>cx!H`bd*xoRPK<&|(_bI|EknmTi6rr2K`=TLpJDSOK~7;|x)U)*Ds zC#2JP{SNG(Od+p5FgBQjkAb-=u#M~nMO8zZ09@Kx2@b9< z5VJTBdIVfR(Lp*2#V3FZU}W%;K#6mUe20TMemeg2lEENu(y{~LNf7c60vy=~ueK?9 zQ7hesUNh>Ax>i)eRh~N1gL)oQ1{{)L3Aa> ziI<3OudarSHeS37i7=ZuSk{JBPut#7x+*t)#8-SjWCW3tW_6FB>qvjPcUF4WC#R=# zzgw*By`Nd7l0)!ivX7LkusG~aHu-4D*jpWa)@Qb)rg*K?igwe5rW`%&llG4GG!;MU z_eXc!K>gU2BUkftB+@w-guU2$yso7y#+}Vi%zA5UYd-PrM2~CI2 zF%YN^;)eNN+m`tuSjlyS9a=Uf7_-B;7h88AU9i$?v_UhoDFrgKynF@is_{^3h|G;l zzW|b{6tw3V<>U9K4}X3{Eub>BoI!+$8pF89bSM4yIdg>C(}S_H-Zh~y*uRl-ic)#g zg9t{Y#w;&h=q=!>I6rLX)*zu|EfG&+c>>F{oXgmw`S2pJ}%_6FqHY&+ zkbZa8xb&`XjH;RL@>jPE55io&&`@k_DwUgCTBBjZ(RZTY0x@ zqB4fLjDBTA%7|RX7nCtf8PKEp*rAM8Wwf}A4`elJe!1D@Y*tQLRmv{oe?Lk_qcVVl zI{A$y zWOveP_Iy%ivza~agK-hP+Dy?0GcFrL=}8O%GdX&LcqIv9FPYgbOPR`@;rw6%8F%a! zV^Sm`&<9(+HU3C4`m3=+^NYkIkP0bhyl!!S|zs!%0 zu21hSeZcQXlk!ym#`N;?B>%nX4drM3Bh$;8k6*sATIfW9Y+fz_BW;avMwh%FmAZdZ zu2M+YEeeC-8Q8{h1MPZ$UH0N{=!~|W;J3hzE!}f`uNp?Dr+;XEQ=_x%icxR6yye!? zZLqb0D7rQMkCxMh-8+~T+T-r@e@0YmNAYOvd3QsQ8xXs`c;46=dZ-{eZ95hqMX~M? zhj+2fpg56fncV9VI34tq)PQ`@kvzgPjZrztH{7GtXyR2oarx?MUM`a0q>jO=W=SRe zuD5E?%vKX84gxAOm3O=c5Xe;42A^i?R>CU%!K@JwQyiSeWFBzM(GK~P6}W&qA-0M6ZM$m)!5Ks_`p2k^yHdYGTh0DILd z*Jvkb@6#NmA_vRj!cHJ)n!eCk+=>n`gqx_v*2a=R$b~@=!jfTgPBgINyU;*G60m$p zhv;-O%3GH-vx^}Q0nxD2Wu0Ps?QlsAr+|b4uEC`BlxEQm_|YG2SB7PeRYbM+2;7R} zt%eLO%rh+;rHYJ~y%3u3EXFHT$Hh`}vQWpFn*;fRxDq%T#cu|1RvOgNP!->@6Az-A z)iWM;g9XW>?*djI5Ujwnx`yVkNr0<$c~Fmdd$YM5uE$dRn!HhQ9D*r-bKKBbykUz( zo^UcSPBnvCD6dLGIxIK7b`U^B(q?(`2l}dyXf(XX4R4dA*vLEa7x`{t=FM~DpK${o zNRivXN@O#M&sqn_`6c%6uQ|xYCWY@jZ2R8_Xtc4t+-XJjAozz_%dV~Iic!bap4`%} zR1PnId!pe3pQOzW+8ix2utR%4RP~TOv2nD#EMgkLMjR~aEltC5@`U4PZD*4$F0q)& zDxcMbG-3&^j6S?<=WgN_O@>*Nu9`5j$wA4h_@(s42{EwlpD>lbznU;7W!h^aorU+koxdFZ4` z@tB=l3F_71WHqeI^p}2^j+s;$Cr>lp4(DT*R(6#R{4hOp(ujcDWs@e=tX(#$V+MMc z?tvR4?8gM{o{$?vOck_T>xY@4LrZjddUj>E|CzK{J@(?oGBV17IYP8l36rgG zh_obrah{yRg1#1dm>bBl9RO>3=v8x_wKD;yl5wune~0Z%mN&ch#)e6ZaW^9USoMvy zy*JR)9Kh4woxCJay-bGlt@z3DnMcx377ThoeTkr%v~V8Kb_1<-AP;ErU&z{IesYJp z!MRjdse6<*?~}%si|PKr&(+fPle;lpo|(J|h8sO)@dR@|jz4@p(eQ|Cfz5A#m zYTLJ2c2vo+8%W;mQo~3^faASZHk=g1_^&Rdnk}W|iNUgFPK#WsnX)oIU6*Ph#c9>2 zU1}7mrc$!qQW*TnWmXdCaBCr1GWYYcADDD#u_h z)kKOTn@_uxdch?6kV|PIDC}1p1V&e60tm94ho#?}IlQ&3EpOmRBS`kAzn@vH37V?r zHg=__<)riRw}B+H^bB2Qr9F=Mgxaw^vR}L3=gC?g+59Yn4Xs9jhx%AsK92F{!xC)!w*0m?=!_AWoIJ&1DEII5XL? zPv!+PWh^)YkUD4yInElR+x#kihfR-or!+I3wM2zG4;y#>XS0ZF8l~#1nQeA)_|ZRd zJDlmkrHOYsCFTVMvd-Zm|0zIAWE@$ez%|ky*iT-!u~aS5;)spaVp8bqagFKeIe#he z8rnrj7d7icVpyhWyK~XfM_f|ls?H(H*2#<394^Dit|gw#+0X=OyTz_C2sTki$hfUxYs=;?VdHSc3s|Y$hNtdt=Qf}*@}wnQyG&1vV$x@C0-S1 zcWf0f$7)H!BROnXfs!g`Q05jhT^;0T^4Qq`&LQR)!4bagSe@j0@ES|VvZ;i@Lotni ziXMzBWU*-LNtLKOE<_(@C=MrWo2RE?f`knjYc96QV3Q4n&wdWA=ZNyDFKR{1}y+?C_Vibth3@ zJ>9yoiaTX#TkLfwWBYoNeJ}bwNIvA}zULWE^s#TB9*~;*9?$~jh_<^EhL%n=%oXQFz)NaV&#VNo>ZH)jJ z@}FHDgkwN$4`KtF!LDZF`EMhA5Z$z8C7`YBmGqxcMO;`frKcMGMp=2!X^)GL2H=z; zRdPw~4Q#bZAM~FfkeW$01v3zS0i6p7XLI9JbUBg<2!(>gxp4>;hRaDYSc3HznK8Q@ zLSnykZi!u8-kpV9Z}u!Sh%=Rj(2}1tuqljvb`iKo!;ZvSpHTH(m-N{%q|fZI@DYGazpSt`n2!VsljlC+ zBx#s5&kNq5L6S#3SWGhr;nR7+sbC)ap70bh=5|+$(%$F;z)@`?TBq6o64+Q1$ilo~ zRf&fh3l`a8!Zjqt)zyY<0${n)co4C4pjk%R9!?}v2}B!B+ShYn3@LNsle7xW8iBqk zK?8~kAO(B{qEmTYg6_WjRRzb7dnFt8wLF$$52gLJ_%y?cN~mYk7MqFZ8anhJpMm-7iRAJ{F+ya%uv-FWYQ*^?(X-J z*Ba+1FH$MlONQCmNg3zo2p#^gBu9CIp_qOMQD(JpdB{f>>9~~8n|BL)fLi<|ac0Ox zVAk%$gVY&rA!bNE98sv7#lVvWwXFIBD2G=;a(i&L?uxY<=&|$qTk#jZ=enXgB#aO8 zkfl{X1=RQ{ZkBLEREw7WG z$7KOP+X3C_xHp-zL}I%Db1w3xGPiJ{H-%(DVK&&0GII-J&LWu{TrA<{E%HxxRpu%s zHZKh5*+TLHu3Aa_=uA<5T)+gpBFRgk6_Q;F9|KqgO|_~%Int`;%(fMkA(X1JQ(ofU zIFJh)Rh!6tk$b+L%VBPK#r*J8^0)dL5o6~}jkc8#nk7bD1@v8awd~HN#Ri3BI~k>{ z3*3>(O{80fcDWS+eMMY`2V`%1F&@F}sU>b2TJhBR;Z`J|n+DWqkq9QJ-%Y$~Q4c2nF|i@(y1*bXBu~U>;7bDF`370lMn{u1 zAZ&v9Jaz&(CuXYjoYYL>oYGv-8A-iBebw{Hw1aN4+`Tcn4qGpfA#iACS(}W}EM@bm z=O3o8nmUskHS5d{v214c#9{^q`inAOMu?k)Ilru(JpnGoj37kxC$A}2I2CHNfn-zE zSH+IeEz9c2?#j(ltOb9Th>;baBS9|pqq|irNk9#yX3>@i*<}0Sh>nuZTxEoM^zmIS zuMU@2dSpDj#yLcFG|p-j55*hoF}xlb0s;vr_8Mr-#EJ+DO_UP|Sv^WoFw&gUW`w&5 zXq>Z*0vzF6%nVTu+x+SX=g7t->j&`&e6lkwJjHn!j5EakAwh5SYrfr)otysrO2g)O zbm!O`wrr`4iifipM3#$Op(E4DfC+E|Ci%YgSe5+^cXPw{O^&&GLk2PwDC$L%z)Dov zU$hnmY0d56)`-d+R*#cW7?OB|K!?QCyL>07ws4oA+TaA$8Yif(aC}v1l(fTybH^d# zxtcMa!zsqxL}RHU%bR3R;WD&fxTFU%;IC*)8odd_b#8a1Y}G0{8k*Q$X$>amhr`ql+A(thC!f#rEye(s3tvjFg-!(SmKJ7I?QTusA<)$DAsFnR)T2I@mGz>Kt7C) zbZMkdGoKKj*i;DMI>m}Y3p%XGNHZt=v#(>QYO{KWP!xFWW2A!rd`u>|N98(6E%IpC zNGIV^p7OAg*27peo2NBmdX3IY2_Tr0W{{ViCGudF5J+fStpROmAcM zJb+LU4`{`D)Sp8*z~m@J?8gr&3%W)2Sq}(&5@tDz_a|}G%@Gb6prU9`=x}XYSq|lm z-P1j9K5C>f83ny0!(3Voz>TRC&zr{yCNTz$3HuoFf4x_!DEU-c%&RSTQ@8|n(y3)b zTwKZ#C}7cP8izoxv7ErzK^A2$?o76S0E60MhG>!Ox`~vI{OE;~vw8}MnqojC!ijh69B`X*nPuoxd^@Psm9pyb9v{d@_IN8cEL`gaKBQ;*=eL z!gkGk7}Sax);=GA=96)v-ML1~mWF@SVY|VLwuGie+@Q-cx1w_>F#M!yd7(E=ozL;eNUVdyq8dQ&C`4mlS}cXKnP!}`!*I=29an9_kS}yc45|3Yja&P> z2aa>dA^2!1TL^MD)v1<)lA{2kLXC_7j}RXrn~3=q6-E?bSxf zQzXJtX^xC!XL(a(H_Y;;RvH-^cmmDsAE?zCRuuQN*w+}AJ(q3EMd;!V$K}jePLxLn zxEwwvAmuOkHioCg;{@zhhdYx`lpOoc0^Ny!PZn5?6}%>vLF&vSE5~xoHcW@N8=cuW zMH}No%|;F3dPYhG@$jMcWl+j&N7i5_uvwDYWInF=#dw5So1+v-Q2LP{z?0gU;&gE* z@gRfg6VyvY9~fYccv(QY!c5IKn6t1RC0v+kJ`2jN#rd5T|KR$ZXaO{M;@UX36G+J! z3m_w%Y7EAi<~q&122F}H)gW_xZ!k0^N5pNM+X6LcACm!NFt*mR0Y^=n@WA{dajx`- zqThnF!JXv{O}ucKp;=l6mSt!X5F$e}A)Fo(!PG5Q8>KZHrT@xp)d1&bTOucOa7tSR zeNGoZr`(2+Ix=p&isDME4S)sa&#HM7oXi>&0FJj9PBdT^s(cA$+dU4bR*6%OFy*0I zRpxl6RrM(CA89_~yudh540mvm^oz=zR?T`qtHL${LUw>F>$m~F&kyoFP|8NnI6rG! z$=mkh@Z(cxoSz?I{5#u3~y$&8Xso)gJ$&r9BPNa zsBTu*hc{u$@P!yeiktDSaC%`59{3R(QFahd&>@~kF&L%?YEDn!T{eW84w@dGOb@?q zdel@8+d~KZPL~on*etJ&7t3qOn7L}OxVeF9X8JLbv%MHo69T;q(TaQwkdRIzfGF3H zBPx4h+SEhn1=0lCBAYE3B(vq%bth<6RKfx6jq|e7R7Y_f5(wt`jM$Q(qnSC% zsf||)Kwe$3`$_1^{HzQ`n6FseDH4{q4weErA*Ya`iCBL_Q^@^me7kJ6#O5qR?hYfRBSIXkwd1q}x_5E}B#Yfe;;LZnZI!s&V5^4w79> zGna%IytmT&g1)(8>}{u7hedwJASy2IOwgFnv>yf329K&4`8x9;5~u>(~jse3hkUe zMAADD7`J7s*PcsnF)%UHF)j^ng)o?H0#rUl=_v#0nmd}9Igml*Iu(u?OdqfG@q=j& zD9GS1cyN3Gj53gthUf#^*Tl>+Smd7$ZH3AAyo0opb5S!Jf0=bb)Vn#Y$+d$??-)JlO#;17TY;7~aDaodyUV1@(B(F4Z zaKT@U0FV#eO1Vm~oJ#0#H)V9ltLDVONjVgCLsv;ASNyPpdZBnyd##H6dbYa_lC46O z)|rd;r0+Urat+mtp_OfY7=b44z(~GGji=)i$yt<1(cP4?^OGRiQM5CVrJ6swrs zgN|4;IG`$_5Ct8`1h+%hLrg`nj4>tVxy*>68&=z==(XH>GzaqV5SwtQKNd-&7JpHv zZZ}$Y>dib3%VaM>{;R8a?#F&h`Mjx!!>+Hd?|A1zj1Cw!nIspJ*dT2$oS7=A_;yEG%i$%eJLp24^BW+_Uav-(ycx{ z0m<=ngI-MwquUiHrnWUU2y)!K$Gx(4(A#ktIb=0%QVm%JLtbh#jiTl^st~(#Xkn?E zy*g)75WQ*Quyh>jndi4oh=VTd)L{d%=K*kq^nQbF34%O#y4j~EF$mv0K~*}z9-d~Jb5 z&Fl>{0p6a$TW4?UQ%>aV}-0r!JI*G)YZ4?9NyC{y$}#L1)#2T&qI zxJbMvZ5Up)Y!}^| z$)X2mbl5Rb0N_T*b%X?yiG$v@o@6@9=-`%_AYN-Zi?~pm8`H6!i~S4Jw{|Y9ILDcc z2@P1<5x7YDY?}lVrLS}zcf|92^hLo>zU-N~ROa=4)0=9B-aO}|%L=Zi^y?=rd=rmI z%CTCA+rpH7lMDEIlYD6OYJNkd69q(-CF9%c z-y<1l;-c|m8pB0urD*&Q2k8%%jA^gG)<&-ugD*R#2rl98tmrN)N~2T8PIdyq#HG5D z;)96=GDt*Qz0ga?cO2s%NYCh)6Fj*+-PAFq)=zu&YR)0`9$oI0o*5YoAHV}aHjrMk zy(LtCRS2%7Us1dM555_M;&!hCU57~&F`%HRSB2{D3yJFqSo823$gV5Z;BatBppNJ; zM)=&eSrDASlY`XQBIhs=iW zqF}&Dw3o?dYH>k2{?M9O)~;ry1(3do+aYHlut1f=tW~c*)z`^9G0a$42AN0Qj9Nm^ z55l2GQ@l55@rL>2eec$S$JT)fI9rWl@o&kA&Ks^LONGx1#NVo%v}l7u5j~tru1;4I z_?Z09@a`gume8?-th|B(Ga|rRm>zcMMRJ`B@P{y?#cZkcs>(>+iq#RA51ZU}E2(jC z8oh&c#0@|`COge=plj;Zvnj2b!SpM3_!5HLkmUOL6taWE1ymD5rIp zJovu{ejWA>sZkqJ#aD9-7kb4klSjt7;9l#k_XAAOI{{`;CD43qd^f3P^m=lF3?rLK z%o$UwK&Uxm7iR0i6jq+*i9;7pmkmbP8}P5i3i`%Bmw2(7j@RPVxwSz7bytSPAZK-D zIAfPpGAa(tSatT(aFh1#dDM{S;nqe+O^Uy;(1Rczrc`ZM9N3cV$kh5=T~ft{jVK+htV6?9Yvv zQ7;)Yd$AW?hGuSLVJM`yag-Ji0!{fsW%{$|N-_QO^fA~tY!3qb<`$4?Orf|(;oJp4 z`sPeUt0_EGS`Dl1&gy6aDC5yvDx*vB2p2byk4M|e`Hshf9!u4J5DkAI^h7l90t`4m|Y_)@gO++25}*p#>csCKe` z^_#o@r2vg4VWlb2d-J%In-Y%~^n@@%jY^3`M&idPvG!;RpJu{RY?E+^TXX~zFn#hQ zI}{Waw?v=7J!yM0-9}2bHF%ZLEXO1M>A)X9{;S`8`g4E$vv+1&GZnQ)HA#w4`Hoxr zJx)Mr#uiey@?h)5G=BL#x7Y4@;NiVDo_Ln~ri*CMc6qec77l>MSi*H(bQAqKHN_06 zD`ImjZTmgMJj{g@J^VMuFHf z56|F18iu>h_CkFZEf;m_RhpTyXQQh8IiVApMK-?(9r33vjQQEZ5-5sKl^sK*1CZRs z!Yn^w1QKGcV(LIJbA&b(YC#o zP-wm9s@7DO$NjF34q8sNgo6Zcz^inDLKcWtP3{#2x&&o4LwxW(5tO$?Dq9~=EvOKg zvCumsZeR=eoa@%UwIz(ICw-hCdCoctI`rlT)SA`f(LTXuy%C@YjnV7iQdsLji_xq_ ztDa@sQH_?KEd~l4sKCs?5APEg8wgF(_x@^qDL|7KCXHdE*U1nzYMm*gmSwtNKB&dv zox7_&Q{USHJqTD4_o?bycWB+@zxqF@dvqn;GtP?^o+8kmo;zyvs3D}OEyw-y7bf|Y6Zz(>#}a+EZ}{Ax>NM=;~$na(9RR; z`BKE)tJc03XCJi@!yGz0%a8A4*pZkgc`nI&ms#aVUT1N5ig=cM=G{~BB*r78^4}b4 z3ohTmiKV|kb@HSG_!~i#JDSAULUYDJCmSxkopd0byL55=-D%cxYr1La0^Usi($e$F zBXCw@u2}?oI_dQjgBP}^C%nEB%XHK0PdcQ{wDiF1haVw%joVW(Wx@nfOe6wYRBsPyZnA7@V=UvfL*0|>yIUUZFp=N2#ZNLa6cQb8Z zGw~D)xJ^CglWXJfYCgDWhWl+#q-ULZd~nMX=}l+eaLiyg(u=RsTCeof+rR#-b0^y% zY%zB*0@EOZK{kXR`+Zg7p4~jZan@_csoa6>7Cq@xxq5NSPd}XV8(X#F1COe);Uwh8fvh^&; zGn;hfIg93MP={m{*%Ok}0L43$s0Be-Hzo@1HZiY|uBnlks2Y%&cXUMUDLq zVG`%BN?|OpB)Z?QF?srPPkk=mt2rHp;&`_-SE`1*7J=P@YdFXIe4N&p*D0AR5H*#8 zVI9dMZd{3eFWK{>^zL&f&6H=VUdL!y##e5mJ|G%sEpB1FT?{R!e>!(|>opKxjZdY- z$S&9-U2xv~mU_oH*3|Di&ujCqNk4pE%-=7c*Be z8}CpuN0H>8UwP#fLL;D?NW<_qz3TibT{1`gp|YIa2G#y7?Fr^eNL1+KH3v<+;3oc) z^V@5;WorR}F??~Fq92VXok)}7cbqHIG|shxLH`B6X~Q2;?7sA$4X5o6DMIDt)L}ahbRyMyv&xBZn|J(0LpH? zV4iN@yWr+wLZ*e68NKo+-EiT=2^q(Uy^XoFp-GI+;gk&*j!FA3d|lhUH0?fRJbDCG zRj%gi!a(9<{9C^+-g@?}V;XJ}fMC1-d+X=Q)}+FlE~@i`t`PyZM=$zH)1Ig3iqIvh zz3<}jV$#oE{9m=hF4(Len{}pz*sDJY%mJn(nB@bou4>kWcTx#>b;?h6OuS)<-=hI-W zI^W=WMTp1bVq%!g#)&U9h$18@KC&_nUj9+KXXPZpaNo*t0?7ZY{N7=~_p7e2Omg50 z393OmX2KCG&K674uU@uK9ewO_>*#^YA3ZX|!%)fUnzDP#*2R2+^xJ36DH{RU=f}bQ zFR!>zQZiXJGC&TVv#L6T3NRqbRr{8WRDv*k%POOSKd%|2f~&hXOWMBNed2@+9wM6o zd=60I7gmo=*ig|CFef0$QJ+CbnZ{upb>{0;21r#@F+i$cSiSs2TWhsJp0teH$75*6 zxgq50B+j+5?=%=Y7I5Smq$jL7*6D>w=?B+z3ywpVGhywu%>$G488_XzcF|$+6s~)) z?C|6V9U_#y>x@udSoi3$rtS5IwPM+tEQ@7|h_KymUX&+F#v+t+9|4s9>-v+952`rwXT~t94}G){SD`hx++#c zQm#&?f)C>0gpIKi5*F!X8j##|8zoMXu zvv-g<*7ts=(anO{rk#DW3fD{Q_oi3&omd|(LrZ^OWuSK~P?4U#L~dLv(tEkjdVl!J z!QP*}(nxRRRWtvB^o*{=gIe(&SB-z&08amY!m~bZQ6DV+j;qF{lddia!be>_YsgIH z9jw|X-0Doyd-Y$;^v%~CN>#tOX3=3p5j!*Sdr%ieC%o;!X^zGMD9p$S+YKX}cav4e zna(DSYtEHoHfD&}h7Jw|4LuTu;wkq@CbuL_(aft6!E2TU;hJkai50DD#m~0UXK-t(+ zuz*%5e%uS7|4#uBtX9Y!@Bsk~p#&ZbKo3~2g;F~D|aoB5;cRU$yp&n`XMG=eQAP)NRJDN8h@ z-`_N@b_*kbJr1BUNbnsU;*}bi(PrKUb|?Fim(?Z?{;LEnfrMhR-8GY`q#iisC`MOS zrTG*OM@Wq_%@V2um~a|MCE7xCQ+-Un@m!-*C{XW2By$#d7_oXp=`()4YK5kV0%ST4 zmmzo|unaPl;~X2m(u)0A#3r#2?CuKlVA2ADOxl6p>1bxc_1_eU2!W`T!;S!VI-gU3 zVxL$)|z6i;ijw_qAYA+BTGt6EVDhoRT`pib`&R~nc=1CXj|QTSN( z%34$0G7xBs6d>+MNj!y`8$Z{ee*Bo@6j%?^#z)^+;RWMnCT{Z@SoBP3>zX)IA+r^I z3)xpsipU^5O(E=iKyUWD6JU7j&vzmdk005Ic0p=K8eyZNwx1g`h&mRYHG&z}2@p*)FYKX5+B0nWcf0N$`;) z6POszv_Qjw&kEYfDTAfAjX$GgAH`5-IOv|a>9w_?9Y`u=k$yO=Oyw}JbQ32Q-wyZ+ zoO3*KQXh0MH)aQIEi)-+vy?W+zL(;Q`I0y)+P<8(tj+Xj7EBGkB5{=cVnrI48)`(e z=yNR^o-&n0+HLujfUBUP67N(f-!nJ2QK_R;yt~ASN+u z3eNJQk);Y8%Gh*^+X1#w)9^^`ne8CA zJnpm^+#VqYIA``s!|{0V(U4?Vvh5x;!TbHR^_Jtu*{6#`d1)2G@X~HM*z~emYr685 zY5s@O_ueuwxb2?wbGJ+nKIEs9ZkcsRi>+TCY|-{1TI}xp(3!a-_C2WVv5CDe!tw-5 z93ArV<`eq6=fe+8)8~>Ab;*gHhYB? zb?ZxNbf#DgbZT_3l6b#M}2zwP8PzXfc` zQJmuFJA{luNPR9kEKv3w43xt|?u9R@dk1SEH|#_7+JewN7Q%*9qW-k;<8gVrVMND2 zK<(I%|H*$gJ^B-8`p@p({E4l8xZQ6~Ke=nf{K1%@+nA&UOUsq7n4le&p^z%aUw+0I zjIcdD_mf*kAO7?B>3@In!qX04Bw8IZO=Ft+m-GWFYi-J?OtL?f%S#XAlU7sFI$zbNhyGl6GpcALS*?^wJxeRE{VFslKo%*@!r;#~V^8oMLjx9C1HN;0**W z4)fh+BKgPrg~u^*Ku)QO3PMX*=ftU&9!Y23*%5sB{&d})GeiO(xpVF0R}Aq|DT6;9 zz!bnohSrbXpU(LF$$p%!{rp+MZTjVdO7OA!)8{`wIeq6{pZ33yR_>m}-;TQ%`d`@n z_Pc97w?Ex;w(sAbF2DD*%B~0cohs9Z)`P@?%fyMS4(fp7(ueN-1$B4ax4bl@Zu+PD zE(o^0J3a6I72xJ8_fI`$8jbE8__YVx>(>#0*Z_oBaod*CF;p{1NiV14zc`+6rz~FR zVQR~4$nEa_;urnK2kg6S`RGgOj4$0@c%CUnH}8J(OHccS1NNNW^OZTli;t!M@|BB% z1COOA@0qUD`aPp{d-tB#1^f4=kM5Z{`M`HFj$>taBUv9COdgTkVySe@gVkV5f4bA0TKew~&Is=OMmpx9{Le(dX3?$`(DiutguoxywJJxrs^q@f+zE9_ncSqgA&P;`$IS>!2ZaeM>_$JjNoH zG&iLa9y%s?>^teTU%lw;=aJ6Q2PKKapNLpb-`Rgc+W4jB^jrPaF?_;Nw9n@oS)h)!^;xR0l>VT9 zRE-ddB+Rk|2R?KvyAs8|&jVuA4u*W}6Rss%1BU|NIU(Qv$AMlHtG{5%M;Uytz!!V| zi{=#J)_>jHHio4ZAy`T5h(g7lV$hbdTNCkGfoUov6iRmqS~68=RG(H4EQD~DU5 z(|@P+-MX1)j^;LP*t`YPCHcRhy>C_&y2t8r>;PoQhyk%v_;4Hn4ai~kz(MEep>yo_ zwqzhl7k=%3g1ukf{o1cT5pYk7-+Yq{i;KQ_f^HxG=0EGU>{}=6cJsG3>o)q)M7Q%E zU9HF2-QHuAQCYMQf4r9Y!!&p`z)m6pGAReJSz zEA?NvnKGziu_ph@FWjW>dF+zl=l7?-eJs|kb?>>lt=fCyw4Wn^yXSb`BJHyg+SnHT z$R5a%EjPcCb7UOy3pwd`_pbi?2D;$8YyQ59`@cJ*x!)g9{(BxvCx7q#f6W5<*7q)4 z{%RJ;(OzaK{k;~5SNdq$_x)+Xy^p20egF944xpf;?)2I3FO$}o`M7C~GajEDJn&fB z_xRM@cE9wS^ka|5!NZTGUwyoC>~|2C2@um!;_VWh+1qLLBlP4cRnTXH6}*yFNJsqO zlo46R<7#d_jtS3tytT2*g{vd^3oOS)~)Z0-Ou>OeOGkF zG;}}ZJEw2$hB>qOv!wUDH+9anzAsw4e&wpmR;8ExaANHpUc~oVJg>-gZV zyn5X-^`;K3t6u{CD|MTxcLdi6F0E77NUl*_qdo7$g9^rwIhJc2*U$#zNw;xLI3#~! z`l}xv)vTKO=W|U;Cp|G|*3g{#;Po0Y4;8M6OOvUl|M0||6V#%P3V~tDkhGRMbT-ot zNgtX1>WOpwccdpidFimHgUFjm`G1C6(!2THVfFhIcZ$7_ zat8$7pSaYfolBtAtfq6#;5v$HW|p4CeKyy&^wy`2nm#=j**Y#;!7ntgjQiHt(SP+7 z>(_vdo~|zMZ0fY75B+#_y6>q`oGkz0sn?FXD{FTRca6A}YdBZm?h}8s$geH#=~>^i zdeyp>WNFa?sxIV;x3;Udcg5vhmR|?fT=JJu{fypA*RSk47*MeCL5q3~>9~D;Z``Fp z;^}?9kiZlQ5nq?(_cjYry8XwM;EqE2l^<9A$J3wx_^8^;6p9}104%!K)BgGj zszdD0glIR3yt;k^Q0#YNxyhV`bub^I;8w^(ju*T z`}0W0-bsTQ=O5DGy@#Z4C4DGu;tBE=r5k=Ws=V&fIqTP5+QpJCD5jr&`t<>~-#@)@ z+6BeP_-ZRy7E>}yub%gN@)|9jp7E0z!Rs2+xBuj=bIxsyymOU%jpseUvs&n1%q6m5 zr@Jpj%DpsRB_X3vO=msR^qS|XE4t2D->av^1LFF6T3A+{=Nr>i&&(TM&la&t5RiTM zGt-*3H$_(K&ZhJi&&+7*&yr6yrK5gY4GuJ=$NjW=kZ3qQz3HcI8vh`g1is$MT#LDu zaGk<+D%aBVj-O5*zpWJ6l5XKH;`uappzJZ|uYdaH+M^^-CwYLuC7b>ocuC&W)B8z_ z1YXZomp99Jegl`7tIkBH@vMHd#5ygX!Sm2M%MVGPDL|i<_V1gYzpWg3ui z>vi8m`c3P)uGxTY=~}sX{bgsMYkHQhgUfoZ+(6bjJt(g8yL#52U0b!bYvmbN_AOm^ z)rvK%R-U|~Z$Ej?3BKlHQV!MQEzXMZ+Lw;%j$cGDv* zks*|1!*p z%D&5HE%07!O)Jm7KA01wYoDDRTpXnzdv;#1HAuyMY2_R6iJF^ne9#Fm+yW(COzYYQqVpwz2Jqjrmav3|J3{v zE-i1uI48AtaaaDXap{vUENps&WK8n;ap~A!FFC6`-iht0+y$!n+(p_aaTf<&%=Opm zTtOZ6yf7XdZQFR}Hf{C_HRBQlF6COuh1T(6{_UVy%(K)?g=@$7H20g=Sd%)ygt!aq zlKxUGD--pXt?iq$?#i`@#-rQPmA`prtVXFfxt!-KxK?qc=l@$z3)68Qq; zHz%a+`)4$bo#>2#*%Q+X_HQk`e-8rmwdsHCAM?TAHl)ShjtTbeNh`mdddV9mIRoQ* zno3aC({JNh+NYj==OO7n(gI4oy!()H0nwGGyM8-w_%9enDtSM5$>%G^r!W3?#`p~CF$*Ck|bBYG{9I^ZH z-(8zOVcAq?l8AsM3hQYRu_jSZpT+ZuTtm^+oxb%m8LK0xl03m@uq9SiKD;3b<10S_&wMu!YK~zHURqI5d ziZxbrp+Sq5wpe;BT9m4^q9;~Gt$@(ugSKd~_4fFnEj`j+YdL!V|8MP?3>15MF8p@( zdarMN@Aa*I2a^zr?K<2%a7e-Ab6;gxs^Cy_{~Adzr9z`-f} z0Cl0`1S;hxe9x}{;vA>kcYHtT0dU_}=!1rREvSxD;`j-5g_<)bfie0d2YNZBJ3D)v zAm`{G{6gLy5+?AD%)O=j1+y->%=wKfy-_Jgt6+9eT9&TXtvl6(suMS;@2Rg0JU2LU zO!HBrj~Lt7)VI$M)q|@3@;BA+bNUY$G41%N(;foCyU(z-|th{tVza=WTOWmnf0O=2_@2gkTx74F5=<}GG@|1d7{Yw42>QT?A zUFz4W?AcY`FICD3Z&YEY3bM-04Rv%lKkE#0B1ik<+%#V4w(zpZG4H3ds>GC+PZ?br zDIK;Aa{{0-nB%%&9=C=6!z;4xTK%b7sJ;0WuH&j8a?PxZ8ygn~krOTs^y5H`o}gd7 zp~ngDQ%(@1Fi)hy*+H2kA~&34Uh313I~v(~*U7N*dF8iAtN&1y0rH#k`PZ3uj&OiT z6u+i|my>P^z+esAo%vaBnB(S9<%Z9ya;G}-bgE6p*`XpY^6K3b|D;2kZSb7&UWi8Z zZIPG5`odqJS-o4I^)bDto7*y_c(u&WlzGV4yJIs+zM3;7d^G}8j~HWHyqYluUiEl6 zk>57XZPb3ocUwF`ROD-aI)*I`*Qu-zetFKBUXJOcXW+Kv+!&+8Y7B+Ul6BY(Lu=KR zRT$sRMSi2>WE0vs1vLp`Kdk~319j6-;$)O|P$HdN+bQx#J4zq7Fv$4mF=SyT75Pz0 zCp+5h0NGqZ?^Y)gX+BDe2lMlTDMBDCRHH!w4A!0j88A#*hS_WTWAW4coOTj1&_oGl zmkwy#w*!mc>nsFEVfOYQ)XQTRC#Gsf@Oo^Nv-B*ljTm3E(`R30pfHb(0}CH56qX}uBzIi%H3+MfM6%ws-~8M z0IYRQ*V|ROKzppn2vvgd9%lnQ0FXS>oOU5MkstmXY|E84XM7Ma&??Q93xgiOp8x|L zz~!vn?ocu>D2TF4QHCgG@h6o5H-cp-S1v^6B{kl|3_iFLc_1c@mrH^FsRFjKQq*## zUOdHt>Dfzq!qpFlncnk|n2xkc*s)q~XS8=`K};(M?r zwgboaQL;@1>9|CN>x(n5t6-gLSp{~5%;;*fe!vF@tDe4yD{ zb)_0_RzERZb(^+s?H4IC{PH2=VJ?JY>m5h`{{2UOmz-YiP=`Pz zU_R(d?F1QjD%AzeEGN?{^37K-AC^;*JISfxa6~{l>yjBFm!d^84*m2aMJU27R6@Pm z4{iD+9T+!PJHYL}zuo`)KYs7QzkTAIRs#mMGJq~R1zo+$^f=Hd=$5j*)4jc?qVxV+ z*5n@D_{^4X9D8~@xgGY3?eRiyU?K7-HZD*Gm7z!JE{rFR0U;DAi}?0!V+h?a@H)`1 zcUyEq_<;y!Ac&cyq^NYj$9kvJmUZ;IS=$ows^3M@>L?W@$9prQNS8~vdzdUT?6ZJyrgJH zh;${kWs0PlX=O+i`#-1u2IwHFL=%+TIQImSmW5hPavZ(WuEVk$Gs14YO_ADVn`A-T z6C`67>(clrJpr0fzw5N=^+fVqB9h$Zj&@)cbWTC|T~3t(cplVM)|=#<8hPce-fqYB zTf#aa1c#!P(7R+LEX@HP^UaV9>ws1ntP~Sy2ZQ#m#BDinErdF98%ZE8i-uXGC|(V? z%(jR%?l^a5WWt8(99Baa8V9?W5zcMQ0t~DO6b0*m_Ou_!P#u#Y8rwi(0P8YW@18)~ z^PT;y4ll~^=((3RmAG^f{Qb3=g$ zahiYVy4;0XM;i(yh8Cp9GQgi8Hh~mlrB_?jYY(oRUSl($2va(T)wwPD$4HZJK!L|> z5(GFY>&1>Z*Y34OX7AwGSUJH(O3{mm@}^<$%|xGX zy%ERssU4kLwUqp!{Wcw^Fv<^Bq5mW3e+2piTR?z+00#voB;32gf;bMMS0&r1;Y{qa|)}$ z-XrpK*X`!f%*2{?TmgLhHXWzn7`Oud+vc=k{Y-WC$Ew!6lAZTYjN&On|E30SIAK_z z{z;=thhG@3fF=dHdM(?m*Ju2rxkF)4WbVvub>sT~gGE+=vdjFkZm>5vIL*v>ykW$l zn<(t9#F|I0Fok2z8_@`C-7=y9e>aR6j=#SgF%p0M`g6)E#KvhAmYa*}kG}w}!xOQC z04{($SbeD90Wo4RhRA&$l%o1(iCa*SP*q&$l#H8i4%uMGyllL?)s!`4#;_-3Uq+zi zb8Mq{3a~PjK3Le1?&h3^!6h=vJ6p~54Fk^DLgQ40xvHQ%$-Z58#O&M4+NcuFUiH~k z>+)4jFv7gskohu<_4!V)(a(DrSU)!2?q^sxY{{?;k;I5rDGP0GF~S8gX*H7*CB}QH zC}D0%jqr~S!ina`sS)PQ)aYE19@lD-STHapb|OkA+Mq(pp+>0@ohBTjM>0i1M-p1GxTNDf98;%?QcqibVr3iS5s zQdwgf&8#j>A?3H>4=4?XECP2=a!}2!Qc7T@ zAAt@)C9D=xV!DA;EFT+*a;2A}5Rrzg-wOLdqCQbJC2x>ml@ByWji|LKtVgPf)~H@ksGs^e1{tsS;YG~k zy|1MlmSGv0yBmqeUFGM@g2pyD<2xHCLre7=JvGOgC;=(dVtK>GBGCvTB*F?vXE|IW zFd>-(v=SzYDIU^|M~Fd%9Ab6COIDAvTHG97 zk*e^W5=8R?X_RjXnE($9<01e_msx3VAvhGk+TM}Mc#G^DLO2JhSOe`1@hG-7vLw`s2CP6^3RD z%+%?JH*gb=1ekTt4p}{@!eAbKYbhu45kLl2s9Vy>z{`=oR9<{x2JFMctnuY%IVSW# z@o3oNAiz^l{a7VX1rD29UIGifI9h<~)DTdH z{SqgG1_{O{5ONX;hRa`&(HLybg=m9IGqsh6*}`=x1YH5q81-kO59WDd)g; z%|-d+iUwhq?x@WxPkt2z-iWjCFaE-BQ^^xD=sT2vJgmPXMTQ zkLMdG;A&PZBe27ey0JflSE7qW-zmKXdjm){CTexZV?U|%YVmA2Uk;JU;|T#GT8uO@ z#hBv>lBzP00nLEQ|ZNfd?1)j2pw5mYbeBM5_Jxrl0s z!WA5f&SqV=`U(UClmrzS1Ow6G{?#eFFVMo9lyI80dR!t}k-%-*4${ zSrO>}`wi*Gy7EB3Z*%3yr31ao<|>ebdV9g<4o42r+)a{8mj-$*Q#ntwA{FR6dz(Sx z{(g%LKLB;fK>x|+(#RnY{Ck@#Mb5`$HdlrmVs$Ur9LI!2{lw-72t@XKHrGFcpqO9{ zw#U5D)VH!sLbLS!h_mJ9qo(XR=%zGXi5kilX21MUVve9{7;jKhNQc0Yo^o0YQl zHxt=uP!%tzAN>wITiR*W>p6t;%ne74JF+Oihlb(HDhTrv*pT1h!OcmPqVC*{z!(C1 z=Iws{aBh-kKMG+u4truz>@&e=N>^G&qa2@&YFK;jiz+Fqv~JLnu8l1qc0)g@XMASn(V(X1}<0$mT8&Vw6jh&eKY{_T|b5RM=bCe^M6x0#zl^0b93WJj$hch#sL`~OteUV1gs2~ zMd1Jt&RxMNc@E}7oDy+rTM)c4&4X%l>Qwal!B+7luo(K#IDmorB)wp4!3rRlEPhco zAwh7&!kyC*#^F*B3(q9z_`;UAhRcpo6=+(~)}$aN4@qm#7#?;K5~)+u+W{7%MzV5t zL%7({ozO$!cS-s}U6feM@D%ZqXiY6cc$ibI&47n_ngOpz&9A^|y$8a$w|~B! zjxACyyq~B5^*A;K1xK5e*4f2Xw1QYYy&7}G7gwo8X4@AByFJgCKYsC1oCSU7 z>}bNSF2ErCy&rm457j-0+6wGRpsE#6l~_uZkv=pPwukxK*;6pyxN`;%?0M#f6ihu~ zJQe)LHt~_uf^&G}bkjNabI!VR7XkQt&piSVCS-Lu{IL*%{Q=?I=hm}b@TE1w3s6CJ zOX3;Hcn82}Uj5P;!G6Gx39%V^z_gs7>9hL%PU_cIsLa;oot>-SH*?OLR7uqZr4}xZ z%N{y!S+MT5|G%D-a%&F=(#aaA5X7;woaurKp#l_k19}f*FSUA^#7lrr256-@_Igxf zE)N(G$H4{$*rU&4Pt`b5{vbil8>r41oXUw83@x+B8Vy^@LJAO$D(KZfOBPs8qRm}P z0me!KL5`!Tg6fFFU=U>qQgI}S(*Rwf2Fa6ggh~}6RJhes6rxge#V}(bM5Q#matTHv zL^aHT0OxPfK!{$z!63wkQu_oKE8C!zv7pDHZwVUF|3RbUh)MWEa@K>Q5YAAULsiRI zlV@(2Jt_xg;=L5SQq@8XvAg)gRpi5JZtT`!Z)^c}^%iV}fhFRZ2;+3P*TBuBD3O2C zbKP?3X?tBF-DH=E6-@si5&7j}*#?c!6ZlHlrI4)IFra z^PJ$j3J1sL)Jw*yO=jsOP44!)%wv}fo3`n$9w=d~A8Y436<*~0F9!ay6N~~Brg7xJ z1xxSlf$1v@ax?6gjY#D~(Gn2?bO#;MV+Us)JX|OG26&@^I8N7XS`P|Hq-7;px8al$VkrzEjVT7Lh z(cF1S?mTgTUo%fYh7G79b8#OHsjCILV40Ck&5*-pC+x7+HM~}KLg02fS7_yiS7Eyd zJcrl9e}sR)!#&zL34llLj2aj|P8GcG=Q#q)u!lNh+pvEb1<2*fOBvVTQR2iZdaVc( zrBSeE1yO_>X1dHgIB$|FHUDScWG(=~15*%L|JsG-E;LX%zragp!@(h=O@ zKBeaLizgRo?g{p|{-Qyedqc^Eacn4=@X>=HQGoRG3s*ZxV+bSe7Z9Ow;B6J=7A_PK zv$(cZIlZn>2SN~4z_{fQ!K4bmwjw9iE|cE zEkC$9p<^i2^raoO_nQZSEfkzj*JRoo&Ju9w{t z3M4v$GV_}&kH8J(K3Db69oV1}cc*lIut7oz?x*Ov96Z!@g{yU7d2KhLt{Bfh{Oo}b zlj9upZe0q->yc=v0EVJ)N_YVV>q0RW*(c{0g5=ucg){ixlblMd17Q-Y7{g?Cjr&^_&ze|O2U&IWQoc!HAIb;quN-6xRqKb_?8e>KH6O{w+iXU+Nd;>BF7?Bh~1w;>wYkQynRztxFIVh)UN+*y%>8wTKSHJwt`7vbZ^xTUhS4I+;`}gL2pe!2xP#lpY=o!wFLn zOcf(S_oem5)u86XOH#Zi%SZ}a(T|T3la+Psu)przU8k^vz-3`Fg?K+H##E&%s4Ff~ z)Vw>vzm4{GOam8Y4uK~Iy>`RY1>8M_%?mTLcR0B*B(qPVi*n13tEbX&>QoqK=&Xi zaTL92M^AjOosGKyi2%UNYGg$E^ePtfc8Ui-RjF{w3t%V`=i>!ncQPA;H;QtiYs zGGuTauFOMCO+d&}t9FjNlB2x?2taq{Yq7Dsdx8pHTBm{oGkOA z4{(!`-37OoIQ)v77!-%`nf=h!Fap;{-c>kzlZ!`P+svwKr?^YEnO|Ld)7ai&{)gUT zPiUgif5lhN9xCgFd0!3)$+9K5j)|ksNOfd!7gVM;mDe3Lko6XCOPS)S0YlWC+sv1) zyTo0$-E6+@Y8PX+eC<{jgh%_`$RTx@5efh;2P`yc7dmuleU8#fa)Q(mWai`=G9e2^et2N$T1ls4 zL`k<5z2Yn)o>wt!^CH>^-yA~F31dOfnK3_KI=S{>VHWYtb57>#Q{83T%*9`i&i-fZ z0GIU;qhy<&0GH7Uon0JM_>AIZMY!G#{Z5Q?O=kDk$GLb_-E>DE@IZ9IWKMu(XFYYiW-8w6V9)8dP&^s|y zM8vjRpN=8-4uIUvL+**9BRU@K`g@w`}%RvnoR`6`}H@E#uD1MSE^bfaJQ?ZsV-!;6KcJaqE_VfV9z_N;{ z6o$ezGnWlCL+@C7C=CzdV2LgS~vF2HZP?i-!7;Suxi_Z^!Dz>?mS-JdDUhus(F za|v6XgQwk}pRbqf|1e*}w}pcr{&wR(aIi2a6$96Q>gs`3O9S-(4GYU4c@{o(^)QlP z@#@hpos zqLJFZw>fvKr*OvD0&XHt3fJ=R4DM89SrV?Z>L5Qx?hHBSK{zaO=Ue^)n&>Stz9k{R z!{}gPQyH9C14EK-hw2GCib7yIH9zBEHjFtx0vr3dt7NB(aDRDzmOO%!@~am@wmp8J z1y{nIzYyJUd&(n@uElXso>kcfPK~_7>GSYFxnMYOjN5Z?>=9daQc^7FYg9D(h0`jKK%D%5kA*Ff7kVaV}z0yb-48B17BhCVNpZK1D^7@axKkV=%RV zTm-kh;va)7Fwp^q@Q{eIldz$5d)k2j9Vlr=e!p*S3D2i(biq|+Ie3S1rC7`yfo32(zL9 zq`~dvlfqGv_A-hzwUou|G&G@GQHl@K<)3260jIrOt|(S$5sELaeF-Z=0z6&-i*l?* zoX=k1a>K8oKEAV-pBsb6fu$Fm@H$ws&GI@E(IvggdZav%vqnppgcleJdn*Z;D-3!Y zm%|N+R6Qq()ky&zVAXKBx%2zO`U`o)Ri*{hdqH#E`3tk<`-5|_-NeTo5CAy{1ZT&f zU>#0~k9mQIaIt`|V^BGQ2tqzQ7HslVNsP!6#N@buOp>_df8q?^N7E!HNL`Gp*MlVC z&4=0JeEq28B$F`LtsR(KiXa3S4g0|u7uh6P4xt{r+89xy?&Gj!AagcAt}R=tm)UOI ze{ckD2*a!5Ff(r)uwHm7KWv73NE`z%SJl!(=8Vd665zd;;(nSi%MaZiLW7f#fj;8xL&BqGKW*zofM&<0rCttzWv zHgd9|hafrhzT%c-bC4Z-k)Fo|QvtecwKs4xcC4J#+N)P%zk!&F#wyD&KTk+pOP*if*hbBoa8 zh8vfrV6i*#z8dvVjY>cD@xJ%GMKFPfXaNAI00|!u4pi$&G6WL2Mfq_+8Y)q$R*r}S z=BIqbmvN3^Y!3}Kw6FVZB6lOtbrE0%mtl#7k4+iGk)D_YmTNwVD?9*IFA;K2u1re5 zC{A0|AD2rXfJGfAL((f^4i;Xf_j%hWQWG)T{NNRfM9vv^&)I(x(g^VCKAjLa^t-9%2qg;{l@| zKWgMZc7Ys_jUcL`1mdr?f~U>4s>SZTs`q>A96!_;D3I!&tv$j&xN8k7h65`n=!?+Y zx*kXO(fjd|1HRYei}W}|z+F&chY`;fK@+ShA{L|-)k_Ka$Omn?0f4lEN)n-kiKQQz zAeO-0z5jv6ehm6kyh(SA0lVO8>Su+M0Nfy@KgMy{iZ?>AKst~f_Sa&sPvQ+~;Wyhy z)EokJyzYdtXjr-7g@U)*s}#!Hx;f^Qy0Ir0VHS7ufn$3;M5B}5{zG7370`9y$1Qp< z%Joq~551e|O+*#0p_oK{W*WX?;R3Bble-sGP0&d6gvAs>a(FO75CvMg5@7|t@{Z*G zo5}zI=F<9RbNI$d197wtRPr(@&z|3Xm>P*^&*kRgjdf~`xqagxx9cJE@WuvYGS7@T zsKZA;?(Hz-C+M(Y+D}gN-7OoTN3B1k3~!TN{7q1cULkoCB=?NnwBu>3{#4IyOH7zqie&IofMQK)i0# ztqD8E(w`Q_Ffa5R-~Y{rfb>zg70O!>m;;{x_~D^ezwWVvR(}W#?K}`eeb!)1itf=+ z{E+kTHBS))y_NsQd>#29WgV}gFe%CH0 zcrSh<6Sxw;Ya92NxEZ_aIj0UOEJh{)i{ttsmPw6E*+l3GyD>_tS+n`n<{txdB6_&` z0fkegpCifHxt5v$E>0h;KVSLELuTj;@8P3@@)wVB_pCQ3z1ZNcUvI8@@%&RCvs(qY zTl;^65!_G;u;JvjZ^lgez!Sk{g1_zYr}b>X^jJGVri1{)7pu~LX5EMD&Cr*oy8C}* z+FqKR<1b|>tKtC=e|b9zd1G_&&-Amomx^wN?plqK?R{Cr;)5AWQ-zW0-mVrSa~^51UV3 z>R-N7rb&vJ?lhU74-9qzE4;Fw)UTSUKR@{h-1>o{l#g5vXly%DsLw<7#-d)&{=5a6 z$A5XAdce$o`E%8_TY?%37bA5i5J#bab}=u$JQ7Vme0hxfA~3n-Dtu}Atu1#Yx1dS5 z+Kk(JL2{QQH+*aBD3x0Q!L(O7apuacy6A1}L;+&3RTZDrM+~=v7V@olwcEl}0)|7Z zn{1+e`HdFnTze9a!|5uyj0XJjOdk7Cr2a}Gdt?!}TDDs7!AbnTu^ht}Gaub%8D3g+ zLL@a$qKnv*yaSzIr9WnO{^&+gWst=l4tqP1}yHJcF&6At~c;T|rdaRaAgqV>a znV&!6Giwp@%!|QUP#h=?J=6j%&2m$~+}x6$^z*JlPrZOs#}pXa`M zlR5m2S&+c1-WWa!jw-G!;F?Nj*oq{fTYozU2!<_Qe$CQg9ICB;U5^VSI%T%MQ9Y5- zg;F#H75(54W8Hp;m13*`j@kbD;TtMnKJRLOTF zZI0X3VuGn>f`_5*wV^>4=3;aLxii$nm4oVzMq;G-<;-de$s?*wbXBZvM2s-GxFeNC1A*G zk;I31XJlape(zJ}>bFL^_w|@HZ%yofUl04>SHK{}1geDR?f01%-kfa4{3fm5GnH@Y z5${POF>CpOZP5V(uQXS^HOhVRd9(dD6Zr~uUvTEDaB0Hta3P-luB#<0-y&GwM!&;) zac{`9#jWxCJfG3pJiR+p1}pT5CpR0-d%J5B9zW7BWp5u->d`Rccg#F9^XMg+sXE&huA@b~!*18oKe7AH1O>uraGZ7#q<^=A}$N z9ue;XbqUW^P762|vba;Yl(%*KY80!lNloPvq{;GEELqBh{g_D zZSU{?iM@!A57iH#BEmMousC#sr2whnRX0x%p@a)p+*&P;y4 zre9ZKZo+JHrtSTjnso9*pp9NPu|rL%&B>M z&kQqjb4gq`Irj38a3ECNWI)Td* ze#2g|_$Tch>i%G}S-5w&zg7kBntS&~?jxJcroEY?e!3Zp=M0DOd)eHpn4Ps~(cJcqOD?}Gnm6l`OXtp+ z6wO;SX;S-K*43lS1y{`KxVSMwYe_YXxn$7=m(Q6-CP6R&Qc z-Er;wqsJY2+3Y#jcFbwMc)^6L=PYV&?%Ukle9>i1XEu%LSa8L)CtW`6ifLy2e>Gg> zIG5okVP`vzQ-;UG(+7_qKYj5&3{O8i#pQ=1-yhF_gUYLrABd;=pz<0s=)+Ov9D)B7 zo?7#T51U38mkdH_aegrJL-2%n2v1}l`LO92wn)V#8N9Pa`JqU&J`eq{gYv`8pnYFd z$D76bX7^d`h7M=^5KCsC)#kl@$Enq(;V)l0@ft65*!MoXiILfObG3iR!#;I*(s($} z2t4(88t{ya^P})?#B-+E`j=6OKcjcXWd1q|-%6eQ*P{lW=_8NYTD;kF86M(!%7%OX zI#~^l>Mvgu&24YLygh1|qPTd+*^&tH`-hJ6M#7x_Q8uv`*;*nBqz_fc=Ba`LRqQg z%`ZM4={{LvKKb~|O|O=O&exn+l}|vQC7DbccJib2+4HPZh_)=R*4qGxz_! z_Q(s-mNdw7WpD~L@Lz#*%m@2x)aOjo ze-Eurm4&jh5N~3B2;Ss6%zf#9pOrfv*;A0c0&^3UA7CDeMLxd=d5X&Mc=Dn$73pbs z2pW+_MxBo51Uv;0kUkMlahuN{l%GL-pJZ~MRGafZ8RS+4=DJUgbk7XT`cLWxEt8Mt z9p{wAmrfm@Yn+|0G$gWf)`@V=_$XN66Pz+G~ zsyf&GS-QJfsdMZ6@=&mNEZ$t+C6`^XxFcGycu`06mATR4j(MXeI2V?8uUG2x?zhXk z_bW9fu^nwALeQO7s;X?k;*Pmjk7-}@QF-?qR~_5;^A(|Bj3Q8u=kpcaKX%nfcUi>~ zyIuTvqWg<~>vl`kw+}xRl|&t%nRrgab9(pVrRvl{OAiknV8=NR*r$SIQ~o0poY9>v zQ>VH^`*$xYQ^)o{slO#$77Pnvbeu6`oZI?$AD31Gy5B0p;n2$d-5->x)9cP*CH`@Y zbMcUQy3m>bss@BoUNxY5cAzGejYU2}`I!T{*9L0Z>DM7o*1ijGVsS0r6!#5ylbJ5W z^I2_nq7CPq6!p%xe*gij8$+1#YU4{tpNHpsJQ4ndRTc)Sh76vts@tU1kKCZC5@H?=0%uXI*yD+-UA)bD~Qw zn=|+7=wg%;m*c9tpQunH%a$Qe<#2y>_XibfS@M&|p;eCRzM)d}?_OT1O1kf^R8{V# zC%V^Hs^MSRRAbG>jp#u<=kwPfO_Pw%f9;@r2lB*CzP|0C`niSr?(v}-*Y~tqtLe|f zo6zkX(47m_$m%ZSE0DhzZ{q2Gytx|GKO3q^*?hn4D5r$~6>m!ZCwM1qyCHqln7)mJ zLT5PgH1^acjf0-JppUvfam;7Z=Tb Y;;aGc2=8$I23Sx@?&3ebAFc@hAJR2ytN;K2 diff --git a/tests/wasi-wast/wasi/snapshot1/fd_rename_path.wast b/tests/wasi-wast/wasi/snapshot1/fd_rename_path.wast index 97046bb5783..fd3e59714b3 100644 --- a/tests/wasi-wast/wasi/snapshot1/fd_rename_path.wast +++ b/tests/wasi-wast/wasi/snapshot1/fd_rename_path.wast @@ -2,6 +2,5 @@ (wasi_test "fd_rename_path.wasm" (preopens "test_fs") - (assert_return (i64.const 101)) - (assert_stderr "thread 'main' panicked at 'assertion failed: fs::rename(old_path, new_path).is_ok()', /Users/syrus/Development/wasmer/tests/wasi-wast/wasi/tests/fd_rename_path.rs:10:3\nnote: run with `RUST_BACKTRACE=1` environment variable to display a backtrace\n") + (assert_return (i64.const 0)) ) diff --git a/tests/wasi-wast/wasi/tests/fd_rename_path.rs b/tests/wasi-wast/wasi/tests/fd_rename_path.rs index b802c1743e1..8261958b656 100644 --- a/tests/wasi-wast/wasi/tests/fd_rename_path.rs +++ b/tests/wasi-wast/wasi/tests/fd_rename_path.rs @@ -2,11 +2,13 @@ // dir: test_fs use std::fs; +use std::path::PathBuf; fn main() { - let old_path = "test_fs/oldname"; - let new_path = "test_fs/newname"; + let old_path = PathBuf::from("test_fs/wasitests/dirtorename"); + let new_path = PathBuf::from("test_fs/wasitests/dirrenamed"); + fs::create_dir_all(&old_path).expect("cannot create the directory"); - assert!(fs::rename(old_path, new_path).is_ok()); - assert!(fs::rename(new_path, old_path).is_ok()); + fs::rename(old_path, &new_path).expect("cannot rename the directory"); + fs::remove_dir(&new_path).expect("cannot remove the directory"); } From e0e12f9d9ff41a512e44bd497324eeb0f71abfe2 Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Tue, 7 Sep 2021 13:55:24 +0200 Subject: [PATCH 5/6] feat(wasi): add rename for a directory + fix remove_dir Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- lib/vfs/src/host_fs.rs | 9 --------- lib/vfs/src/lib.rs | 1 - lib/vfs/src/mem_fs/filesystem.rs | 4 ---- lib/wasi/src/state/mod.rs | 14 -------------- lib/wasi/src/syscalls/mod.rs | 2 +- tests/compilers/config.rs | 2 +- tests/wasi-wast/README.md | 9 +++++++++ tests/wasi-wast/src/main.rs | 6 +++++- tests/wasi-wast/src/wasitests.rs | 12 ++++++++++-- .../wasi/snapshot1/fd_rename_path.wasm | Bin 72348 -> 72348 bytes .../wasi/test_fs/oldname/fakefile.txt | 0 11 files changed, 26 insertions(+), 33 deletions(-) delete mode 100644 tests/wasi-wast/wasi/test_fs/oldname/fakefile.txt diff --git a/lib/vfs/src/host_fs.rs b/lib/vfs/src/host_fs.rs index 75ba9585b0f..19475768e51 100644 --- a/lib/vfs/src/host_fs.rs +++ b/lib/vfs/src/host_fs.rs @@ -100,15 +100,6 @@ impl crate::FileSystem for FileSystem { fs::rename(from, to).map_err(Into::into) } - fn rename_dir(&self, from: &Path, to: &Path) -> Result<()> { - let metadata = self.metadata(from)?; - if metadata.is_dir() { - fs::rename(from, to).map_err(Into::into) - } else { - Err(FsError::BaseNotDirectory) - } - } - fn remove_file(&self, path: &Path) -> Result<()> { fs::remove_file(path).map_err(Into::into) } diff --git a/lib/vfs/src/lib.rs b/lib/vfs/src/lib.rs index df2f8335dfb..b3fa148167f 100644 --- a/lib/vfs/src/lib.rs +++ b/lib/vfs/src/lib.rs @@ -27,7 +27,6 @@ pub trait FileSystem: fmt::Debug + Send + Sync + 'static { fn create_dir(&self, path: &Path) -> Result<()>; fn remove_dir(&self, path: &Path) -> Result<()>; fn rename(&self, from: &Path, to: &Path) -> Result<()>; - fn rename_dir(&self, from: &Path, to: &Path) -> Result<()>; fn metadata(&self, path: &Path) -> Result; /// This method gets metadata without following symlinks in the path. /// Currently identical to `metadata` because symlinks aren't implemented diff --git a/lib/vfs/src/mem_fs/filesystem.rs b/lib/vfs/src/mem_fs/filesystem.rs index 8564240171d..d22bec69003 100644 --- a/lib/vfs/src/mem_fs/filesystem.rs +++ b/lib/vfs/src/mem_fs/filesystem.rs @@ -224,10 +224,6 @@ impl crate::FileSystem for FileSystem { Ok(()) } - fn rename_dir(&self, from: &Path, to: &Path) -> Result<()> { - unimplemented!("not already implemented for mem_fs") - } - fn metadata(&self, path: &Path) -> Result { // Read lock. let fs = self.inner.try_read().map_err(|_| FsError::Lock)?; diff --git a/lib/wasi/src/state/mod.rs b/lib/wasi/src/state/mod.rs index f17641b30af..fa3fdf22376 100644 --- a/lib/wasi/src/state/mod.rs +++ b/lib/wasi/src/state/mod.rs @@ -207,9 +207,6 @@ impl FileSystem for FallbackFileSystem { fn rename(&self, _from: &Path, _to: &Path) -> Result<(), FsError> { Self::fail(); } - fn rename_dir(&self, _from: &Path, _to: &Path) -> Result<(), FsError> { - Self::fail(); - } fn metadata(&self, _path: &Path) -> Result { Self::fail(); } @@ -1517,17 +1514,6 @@ impl WasiState { .map_err(fs_error_into_wasi_err) } - pub(crate) fn fs_rename_dir, Q: AsRef>( - &self, - from: P, - to: Q, - ) -> Result<(), __wasi_errno_t> { - self.fs - .fs_backing - .rename_dir(from.as_ref(), to.as_ref()) - .map_err(fs_error_into_wasi_err) - } - pub(crate) fn fs_remove_file>(&self, path: P) -> Result<(), __wasi_errno_t> { self.fs .fs_backing diff --git a/lib/wasi/src/syscalls/mod.rs b/lib/wasi/src/syscalls/mod.rs index fde1e2ff0f5..b428447fe70 100644 --- a/lib/wasi/src/syscalls/mod.rs +++ b/lib/wasi/src/syscalls/mod.rs @@ -1990,7 +1990,7 @@ pub fn path_remove_directory( ), } - if state.fs_remove_dir(path_str).is_err() { + if state.fs_remove_dir(host_path_to_remove).is_err() { // reinsert to prevent FS from being in bad state if let Kind::Dir { ref mut entries, .. diff --git a/tests/compilers/config.rs b/tests/compilers/config.rs index 406eea2eac8..8274ed20044 100644 --- a/tests/compilers/config.rs +++ b/tests/compilers/config.rs @@ -125,7 +125,7 @@ impl Config { self.add_middlewares(&mut compiler); Box::new(compiler) } - #[allow(dead_code)] + #[allow(unreachable_patterns)] compiler => { panic!( "The {:?} Compiler is not enabled. Enable it via the features", diff --git a/tests/wasi-wast/README.md b/tests/wasi-wast/README.md index 36968c6318f..a17321f8b4a 100644 --- a/tests/wasi-wast/README.md +++ b/tests/wasi-wast/README.md @@ -12,6 +12,9 @@ In order to run the test generator properly you will need: ## Usage ``` +Positional arguments: + free if you want to specify specific tests to generate + Optional arguments: -a, --all-versions Whether or not to do operations for all versions of WASI or just the latest. -g, --generate-wasm Whether or not the Wasm will be generated. @@ -26,6 +29,12 @@ cargo run -- -as # set up the toolchains for all targets cargo run -- -ag # generate the WASI tests for all targets ``` +If you want to generate specific tests (it's faster when you're developing) you can use this command: + +```bash +cargo run -- -g fd_rename_path # If you want to run the test in fd_rename_path.rs +``` + ## Updating in Wasmer Run diff --git a/tests/wasi-wast/src/main.rs b/tests/wasi-wast/src/main.rs index ce2a43e3f7a..fabdf052478 100644 --- a/tests/wasi-wast/src/main.rs +++ b/tests/wasi-wast/src/main.rs @@ -14,6 +14,9 @@ use gumdrop::Options; #[derive(Debug, Options)] pub struct TestGenOptions { + /// if you want to specify specific tests to generate + #[options(free)] + free: Vec, /// Whether or not to do operations for all versions of WASI or just the latest. all_versions: bool, /// Whether or not the Wasm will be generated. @@ -48,6 +51,7 @@ fn main() { // Generate the WASI Wasm files if generate_wasm { - build(wasi_versions); + let specific_tests: Vec<&str> = opts.free.iter().map(|st| st.as_str()).collect(); + build(wasi_versions, &specific_tests); } } diff --git a/tests/wasi-wast/src/wasitests.rs b/tests/wasi-wast/src/wasitests.rs index 39e8a426a74..f4361cb4e50 100644 --- a/tests/wasi-wast/src/wasitests.rs +++ b/tests/wasi-wast/src/wasitests.rs @@ -246,13 +246,21 @@ fn compile(temp_dir: &Path, file: &str, wasi_versions: &[WasiVersion]) { } const WASI_TEST_SRC_DIR: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/wasi/tests/*.rs"); -pub fn build(wasi_versions: &[WasiVersion]) { +pub fn build(wasi_versions: &[WasiVersion], specific_tests: &[&str]) { let temp_dir = tempfile::TempDir::new().unwrap(); for entry in glob(WASI_TEST_SRC_DIR).unwrap() { match entry { Ok(path) => { let test = path.to_str().unwrap(); - compile(temp_dir.path(), test, wasi_versions); + if !specific_tests.is_empty() { + if let Some(filename) = path.file_stem().map(|f| f.to_str()).flatten() { + if specific_tests.contains(&filename) { + compile(temp_dir.path(), test, wasi_versions); + } + } + } else { + compile(temp_dir.path(), test, wasi_versions); + } } Err(e) => println!("{:?}", e), } diff --git a/tests/wasi-wast/wasi/snapshot1/fd_rename_path.wasm b/tests/wasi-wast/wasi/snapshot1/fd_rename_path.wasm index 2b0a82ffe9d35aed2999220885702aeea83d5461..de66cdd18c9c053f5319a8ea4d50a91ba93e6a95 100755 GIT binary patch delta 21 dcmbQUm1WLWmJNoV*i3ShERsDon}15x003lG2-5%n delta 21 dcmbQUm1WLWmJNoV*xZUEL!AsZn}15x003lo2*v;a diff --git a/tests/wasi-wast/wasi/test_fs/oldname/fakefile.txt b/tests/wasi-wast/wasi/test_fs/oldname/fakefile.txt deleted file mode 100644 index e69de29bb2d..00000000000 From 1a71dc222745f1d56a48f2922c1096cd723c3db6 Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Tue, 7 Sep 2021 18:06:13 +0200 Subject: [PATCH 6/6] feat(wasi): add rename for a directory + fix remove_dir Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- Makefile | 2 +- lib/c-api/wasmer.h | 9 --------- tests/wasi-wast/src/wasitests.rs | 1 - 3 files changed, 1 insertion(+), 11 deletions(-) diff --git a/Makefile b/Makefile index b9c8e9b90f3..b73dc17d508 100644 --- a/Makefile +++ b/Makefile @@ -565,7 +565,7 @@ test-wasi-unit: cargo test --manifest-path lib/wasi/Cargo.toml --release test-wasi: - cargo test --release --tests $(compiler_features) -- wasi::wasitests::snapshot1 + cargo test --release --tests $(compiler_features) -- wasi::wasitests test-examples: cargo test --release $(compiler_features) --features wasi --examples diff --git a/lib/c-api/wasmer.h b/lib/c-api/wasmer.h index 91e59e4a053..4142d4bd9a6 100644 --- a/lib/c-api/wasmer.h +++ b/lib/c-api/wasmer.h @@ -62,18 +62,9 @@ # define DEPRECATED(message) __declspec(deprecated(message)) #endif -// The `universal` feature has been enabled for this build. -#define WASMER_UNIVERSAL_ENABLED - -// The `compiler` feature has been enabled for this build. -#define WASMER_COMPILER_ENABLED - // The `wasi` feature has been enabled for this build. #define WASMER_WASI_ENABLED -// The `middlewares` feature has been enabled for this build. -#define WASMER_MIDDLEWARES_ENABLED - // This file corresponds to the following Wasmer version. #define WASMER_VERSION "2.0.0" #define WASMER_VERSION_MAJOR 2 diff --git a/tests/wasi-wast/src/wasitests.rs b/tests/wasi-wast/src/wasitests.rs index f4361cb4e50..9ac9a861a2b 100644 --- a/tests/wasi-wast/src/wasitests.rs +++ b/tests/wasi-wast/src/wasitests.rs @@ -147,7 +147,6 @@ fn compile_wasm_for_version( .create(true) .open(&temp_wasi_rs_file_name) .unwrap(); - // actual_file.write_all(b"#![feature(wasi_ext)]\n").unwrap(); actual_file.write_all(file_contents.as_bytes()).unwrap(); }