From 3793fa3b0915e890b13455d9fdc1285e9d2b203a Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 7 Nov 2019 13:58:57 +0100 Subject: [PATCH] Merge wasi-misc-tests repo as a subdir (#174) * Initial checkin. * Update to rust-lang libc. * Add a .gitignore file. * Factor out functions for cleaning up files and directories. * Fix a typo in a comment. * Print a "Success!" message if all tests passed. * Factor out code for creating directories. * Add wrappers around WASI functions. These wrappers handle converting from &str to pointer+length and handle unsafe. * More refactoring. * Refactor a fd_close helper. * Move utility functions into a separate file. * cargo update * Add a basic test for random_get. * Test that directories aren't resizable. * Test clearing __WASI_RIGHT_PATH_FILESTAT_SET_SIZE. Ensure that clearing __WASI_RIGHT_PATH_FILESTAT_SET_SIZE succeeds before testing file truncation. * cargo update * Modularise tests for easier use with wasi-common crate * Add a Code of Conduct and CONTRIBUTING.md. * Fix typo * Add testcase for fd_allocate * Add positive test for fd_renumber * Assert bufused in readlink_no_buffer testcase * Add positive readlink testcase * Add testcase for fd_seek and fd_tell * Add fd_p{read, write} test * Add README * Add cases with trailing slashes to interesting_paths * Split nofollow_errors testcase into two * nofollow_errors now operators on symlinks to existing resources * dangling_symlink covers danling symlinks tests * Factor out a `create_file` helper function. * Switch from the error crate to `std::io::Error::last_os_error()`. * Use `create_file` in the readlink test too. * Add a test for fd_filestat_set_* * Minor refactoring Add missing cleanup_file calls to file_pread_pwrite and file_seek_tell. * Add testcase for unbuffered fd_write; fixes #11 * Add testcase for path_rename * Use the wasi crate. Switch from depending on libc to depending on the new wasi crate to provide the low-level WASI interfaces. See also https://github.com/rust-lang/libc/pull/1461. * Add a test for path_filestat_* * Add a test for fd_readdir * Use expect instead of unwrap * Add a check for ino. * Fix the build * Don't assume a specific order of dirents * Better test * Test cookie value * Fix file types * Fix the test * Fix the test * Fix the test * Cleanup * Minor formatting tidying in README.md. * Fix miscellaneous clippy warnings. * Rename the crate to wasi-misc-tests. * Update to wasi 0.7.0. This switches from using the libc wasi bindings to using the wasi crate's bindings. This eliminates a git dependency on libc, updates to the new-style bindings which use Result where possible, and treats functions that operate on raw file descriptors as unsafe. * Add various tests for trailing-slash behavior. * Sync new testcases with latest upstream * Fix path_filestat testcase * Add smoke test for fd_advise This test is a true smoke test as it only tests whether issuing an advise call to the host's kernel doesn't yield an error. The consequence of issuing such a syscall is not tested. * Check if CLOCK_MONOTONIC is actually monotonic * Refactor the inequality assertions for more debuggable errors. * Bump libc from 0.2.62 to 0.2.65 Bumps [libc](https://github.com/rust-lang/libc) from 0.2.62 to 0.2.65. - [Release notes](https://github.com/rust-lang/libc/releases) - [Commits](https://github.com/rust-lang/libc/compare/0.2.62...0.2.65) Signed-off-by: dependabot-preview[bot] * Fix compilation error * Enable Actions and add rust.yml (#35) * Enable Actions and add rust.yml This commit enables Github Actions and adds corresponding configuration in rust.yml file. * Update rust.yml * Fix formatting * Add empty .rustfmt.toml config file * Add badge to README * Update README * Clean up Github Actions and README * Add test case for `poll_oneoff` syscall (#38) * Add test case for `poll_oneoff` syscall This commit adds a test case for `poll_oneoff` syscall. In particular, it builds on the excellent test use case provided by @dunnock in their repo [poll_oneoff_tests] (thanks!), and tests: * simple timeout * stdin read with timeout * fd read and fd write polls [poll_oneoff_tests]: https://github.com/dunnock/poll_oneoff_tests * Apply suggestions and negative test for bad fd Co-authored-by: Maxim Vorobjov * Add smoke test for STDOUT/ERR readwrite poll * Add comment on stdin/out/err * Add a test for `*at`-style functions returning `ENOTDIR` when `dirfd` is not a dir. * Remove misc_testsuite submodule * Add "publish=false" to Cargo.toml; remove LICENSE --- .gitmodules | 3 - Cargo.toml | 23 +- build.rs | 20 +- misc_testsuite | 1 - tests/wasm_tests.rs | 2 +- wasi-misc-tests/Cargo.toml | 11 + wasi-misc-tests/README.md | 4 + wasi-misc-tests/src/bin/big_random_buf.rs | 18 ++ wasi-misc-tests/src/bin/clock_time_get.rs | 37 +++ wasi-misc-tests/src/bin/close_preopen.rs | 83 ++++++ wasi-misc-tests/src/bin/dangling_symlink.rs | 62 ++++ wasi-misc-tests/src/bin/directory_seek.rs | 90 ++++++ wasi-misc-tests/src/bin/fd_advise.rs | 98 +++++++ wasi-misc-tests/src/bin/fd_filestat_set.rs | 120 ++++++++ wasi-misc-tests/src/bin/fd_readdir.rs | 189 ++++++++++++ wasi-misc-tests/src/bin/file_allocate.rs | 125 ++++++++ wasi-misc-tests/src/bin/file_pread_pwrite.rs | 131 +++++++++ wasi-misc-tests/src/bin/file_seek_tell.rs | 133 +++++++++ .../src/bin/file_unbuffered_write.rs | 116 ++++++++ wasi-misc-tests/src/bin/interesting_paths.rs | 173 +++++++++++ wasi-misc-tests/src/bin/isatty.rs | 63 ++++ wasi-misc-tests/src/bin/nofollow_errors.rs | 177 ++++++++++++ wasi-misc-tests/src/bin/path_filestat.rs | 184 ++++++++++++ .../src/bin/path_open_dirfd_not_dir.rs | 67 +++++ wasi-misc-tests/src/bin/path_rename.rs | 259 +++++++++++++++++ .../src/bin/path_rename_trailing_slashes.rs | 68 +++++ .../src/bin/path_symlink_trailing_slashes.rs | 81 ++++++ wasi-misc-tests/src/bin/poll_oneoff.rs | 271 ++++++++++++++++++ wasi-misc-tests/src/bin/readlink.rs | 72 +++++ wasi-misc-tests/src/bin/readlink_no_buffer.rs | 51 ++++ .../bin/remove_directory_trailing_slashes.rs | 68 +++++ .../src/bin/remove_nonempty_directory.rs | 50 ++++ wasi-misc-tests/src/bin/renumber.rs | 131 +++++++++ wasi-misc-tests/src/bin/sched_yield.rs | 10 + wasi-misc-tests/src/bin/symlink_loop.rs | 47 +++ wasi-misc-tests/src/bin/truncation_rights.rs | 157 ++++++++++ .../src/bin/unlink_file_trailing_slashes.rs | 67 +++++ wasi-misc-tests/src/lib.rs | 25 ++ wasi-misc-tests/src/utils.rs | 54 ++++ wasi-misc-tests/src/wasi_wrappers.rs | 217 ++++++++++++++ 40 files changed, 3525 insertions(+), 33 deletions(-) delete mode 160000 misc_testsuite create mode 100644 wasi-misc-tests/Cargo.toml create mode 100644 wasi-misc-tests/README.md create mode 100644 wasi-misc-tests/src/bin/big_random_buf.rs create mode 100644 wasi-misc-tests/src/bin/clock_time_get.rs create mode 100644 wasi-misc-tests/src/bin/close_preopen.rs create mode 100644 wasi-misc-tests/src/bin/dangling_symlink.rs create mode 100644 wasi-misc-tests/src/bin/directory_seek.rs create mode 100644 wasi-misc-tests/src/bin/fd_advise.rs create mode 100644 wasi-misc-tests/src/bin/fd_filestat_set.rs create mode 100644 wasi-misc-tests/src/bin/fd_readdir.rs create mode 100644 wasi-misc-tests/src/bin/file_allocate.rs create mode 100644 wasi-misc-tests/src/bin/file_pread_pwrite.rs create mode 100644 wasi-misc-tests/src/bin/file_seek_tell.rs create mode 100644 wasi-misc-tests/src/bin/file_unbuffered_write.rs create mode 100644 wasi-misc-tests/src/bin/interesting_paths.rs create mode 100644 wasi-misc-tests/src/bin/isatty.rs create mode 100644 wasi-misc-tests/src/bin/nofollow_errors.rs create mode 100644 wasi-misc-tests/src/bin/path_filestat.rs create mode 100644 wasi-misc-tests/src/bin/path_open_dirfd_not_dir.rs create mode 100644 wasi-misc-tests/src/bin/path_rename.rs create mode 100644 wasi-misc-tests/src/bin/path_rename_trailing_slashes.rs create mode 100644 wasi-misc-tests/src/bin/path_symlink_trailing_slashes.rs create mode 100644 wasi-misc-tests/src/bin/poll_oneoff.rs create mode 100644 wasi-misc-tests/src/bin/readlink.rs create mode 100644 wasi-misc-tests/src/bin/readlink_no_buffer.rs create mode 100644 wasi-misc-tests/src/bin/remove_directory_trailing_slashes.rs create mode 100644 wasi-misc-tests/src/bin/remove_nonempty_directory.rs create mode 100644 wasi-misc-tests/src/bin/renumber.rs create mode 100644 wasi-misc-tests/src/bin/sched_yield.rs create mode 100644 wasi-misc-tests/src/bin/symlink_loop.rs create mode 100644 wasi-misc-tests/src/bin/truncation_rights.rs create mode 100644 wasi-misc-tests/src/bin/unlink_file_trailing_slashes.rs create mode 100644 wasi-misc-tests/src/lib.rs create mode 100644 wasi-misc-tests/src/utils.rs create mode 100644 wasi-misc-tests/src/wasi_wrappers.rs diff --git a/.gitmodules b/.gitmodules index 137cc0e19b42..e8599ca429f3 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,3 @@ -[submodule "misc_testsuite"] - path = misc_testsuite - url = https://github.com/cranestation/wasi-misc-tests [submodule "WASI"] path = WASI url = https://github.com/WebAssembly/WASI diff --git a/Cargo.toml b/Cargo.toml index e934400fe211..ba56fb68b71f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,11 +1,7 @@ [package] name = "wasi-common" version = "0.5.0" -authors = [ - "Adam C. Foltzer ", - "Frank Denis ", - "Jakub Konka ", - "Dan Gohman "] +authors = ["The Wasmtime Project Developers"] edition = "2018" license = "Apache-2.0 WITH LLVM-exception" description = "WASI implementation in Rust" @@ -41,18 +37,15 @@ cpu-time = "1.0" [dev-dependencies] -wasmtime-runtime = { git = "https://github.com/acfoltzer/wasmtime", rev = "d99e2fb" } -wasmtime-environ = { git = "https://github.com/acfoltzer/wasmtime", rev = "d99e2fb" } -wasmtime-jit = { git = "https://github.com/acfoltzer/wasmtime", rev = "d99e2fb" } -wasmtime-wasi = { git = "https://github.com/acfoltzer/wasmtime", rev = "d99e2fb" } -wasmtime-api = { git = "https://github.com/acfoltzer/wasmtime", rev = "d99e2fb" } -cranelift-codegen = "0.47.0" +wasmtime-runtime = { git = "https://github.com/CraneStation/wasmtime", rev = "ab3cd94" } +wasmtime-environ = { git = "https://github.com/CraneStation/wasmtime", rev = "ab3cd94" } +wasmtime-jit = { git = "https://github.com/CraneStation/wasmtime", rev = "ab3cd94" } +wasmtime-wasi = { git = "https://github.com/CraneStation/wasmtime", rev = "ab3cd94" } +wasmtime-api = { git = "https://github.com/CraneStation/wasmtime", rev = "ab3cd94" } +cranelift-codegen = "0.49" target-lexicon = "0.8.1" pretty_env_logger = "0.3.0" tempfile = "3.1.0" -# this is just a temp fix to make the tests build and run; I hope this to be -# completely removed when we merge `wasi-common` into `wasmtime` -faerie = "=0.11.0" [patch."https://github.com/CraneStation/wasi-common"] wasi-common = { path = "." } @@ -66,4 +59,4 @@ crate-type = ["rlib", "staticlib", "cdylib"] [workspace] members = ["wasi-common-cbindgen"] -exclude = ["winx", "misc_testsuite"] +exclude = ["wasi-misc-tests", "wig", "winx", "WASI/tools/witx"] diff --git a/build.rs b/build.rs index ec379f7364d9..1a34fe59e0a7 100644 --- a/build.rs +++ b/build.rs @@ -21,8 +21,8 @@ mod wasm_tests { pub(crate) fn build_and_generate_tests() { // Validate if any of test sources are present and if they changed - let bin_tests = std::fs::read_dir("misc_testsuite/src/bin") - .expect("wasm_tests feature requires initialized misc_testsuite: `git submodule update --init`?"); + // This should always work since there is no submodule to init anymore + let bin_tests = std::fs::read_dir("wasi-misc-tests/src/bin").unwrap(); for test in bin_tests { if let Ok(test_file) = test { let test_file_path = test_file @@ -33,23 +33,17 @@ mod wasm_tests { println!("cargo:rerun-if-changed={}", test_file_path); } } - // Build tests to OUT_DIR (target/*/build/wasi-common-*/out/wasm32-wasi/release/*.wasm) let out_dir = PathBuf::from( env::var("OUT_DIR").expect("The OUT_DIR environment variable must be set"), ); - let mut out = File::create(out_dir.join("misc_testsuite_tests.rs")) + let mut out = File::create(out_dir.join("wasi_misc_tests.rs")) .expect("error generating test source file"); - build_tests("misc_testsuite", &out_dir).expect("building tests"); - test_directory(&mut out, "misc_testsuite", &out_dir).expect("generating tests"); + build_tests("wasi-misc-tests", &out_dir).expect("building tests"); + test_directory(&mut out, "wasi-misc-tests", &out_dir).expect("generating tests"); } fn build_tests(testsuite: &str, out_dir: &Path) -> io::Result<()> { - // if the submodule has not been checked out, the build will stall - if !Path::new(&format!("{}/Cargo.toml", testsuite)).exists() { - panic!("Testsuite {} not checked out", testsuite); - } - let mut cmd = Command::new("cargo"); cmd.args(&[ "build", @@ -191,7 +185,7 @@ mod wasm_tests { } else { /// Ignore tests that aren't supported yet. fn ignore(testsuite: &str, name: &str) -> bool { - if testsuite == "misc_testsuite" { + if testsuite == "wasi-misc-tests" { match name { "readlink_no_buffer" => true, "dangling_symlink" => true, @@ -211,7 +205,7 @@ mod wasm_tests { /// Mark tests which do not require preopens fn no_preopens(testsuite: &str, name: &str) -> bool { - if testsuite == "misc_testsuite" { + if testsuite == "wasi-misc-tests" { match name { "big_random_buf" => true, "clock_time_get" => true, diff --git a/misc_testsuite b/misc_testsuite deleted file mode 160000 index f67722870634..000000000000 --- a/misc_testsuite +++ /dev/null @@ -1 +0,0 @@ -Subproject commit f677228706347c05793a7ee2dbd91629c61d043f diff --git a/tests/wasm_tests.rs b/tests/wasm_tests.rs index bccefd5b3d8d..0a5515d52bbb 100644 --- a/tests/wasm_tests.rs +++ b/tests/wasm_tests.rs @@ -13,4 +13,4 @@ fn setup_log() { }) } -include!(concat!(env!("OUT_DIR"), "/misc_testsuite_tests.rs")); +include!(concat!(env!("OUT_DIR"), "/wasi_misc_tests.rs")); diff --git a/wasi-misc-tests/Cargo.toml b/wasi-misc-tests/Cargo.toml new file mode 100644 index 000000000000..a8693af0ea81 --- /dev/null +++ b/wasi-misc-tests/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "wasi-misc-tests" +version = "0.1.0" +authors = ["The Wasmtime Project Developers"] +edition = "2018" +publish = false + +[dependencies] +libc = "0.2.65" +wasi = "0.7.0" +more-asserts = "0.2.1" diff --git a/wasi-misc-tests/README.md b/wasi-misc-tests/README.md new file mode 100644 index 000000000000..ded59fb00020 --- /dev/null +++ b/wasi-misc-tests/README.md @@ -0,0 +1,4 @@ +This is the `wasi-misc-test` crate, which contains source code for the system-level WASI tests. + +Building these tests requires `wasm32-wasi` target installed + diff --git a/wasi-misc-tests/src/bin/big_random_buf.rs b/wasi-misc-tests/src/bin/big_random_buf.rs new file mode 100644 index 000000000000..5cbbc11928dd --- /dev/null +++ b/wasi-misc-tests/src/bin/big_random_buf.rs @@ -0,0 +1,18 @@ +use wasi::wasi_unstable; + +fn test_big_random_buf() { + let mut buf = Vec::new(); + buf.resize(1024, 0); + assert!( + wasi_unstable::random_get(&mut buf).is_ok(), + "calling get_random on a large buffer" + ); + // Chances are pretty good that at least *one* byte will be non-zero in + // any meaningful random function producing 1024 u8 values. + assert!(buf.iter().any(|x| *x != 0), "random_get returned all zeros"); +} + +fn main() { + // Run the tests. + test_big_random_buf() +} diff --git a/wasi-misc-tests/src/bin/clock_time_get.rs b/wasi-misc-tests/src/bin/clock_time_get.rs new file mode 100644 index 000000000000..5de9b5c1aa55 --- /dev/null +++ b/wasi-misc-tests/src/bin/clock_time_get.rs @@ -0,0 +1,37 @@ +use more_asserts::assert_le; +use wasi::wasi_unstable; +use wasi_misc_tests::wasi_wrappers::wasi_clock_time_get; + +unsafe fn test_clock_time_get() { + // Test that clock_time_get succeeds. Even in environments where it's not + // desirable to expose high-precision timers, it should still succeed. + // clock_res_get is where information about precision can be provided. + let mut time: wasi_unstable::Timestamp = 0; + let status = wasi_clock_time_get(wasi_unstable::CLOCK_MONOTONIC, 1, &mut time); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "clock_time_get with a precision of 1" + ); + + let status = wasi_clock_time_get(wasi_unstable::CLOCK_MONOTONIC, 0, &mut time); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "clock_time_get with a precision of 0" + ); + let first_time = time; + + let status = wasi_clock_time_get(wasi_unstable::CLOCK_MONOTONIC, 0, &mut time); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "clock_time_get with a precision of 0" + ); + assert_le!(first_time, time, "CLOCK_MONOTONIC should be monotonic"); +} + +fn main() { + // Run the tests. + unsafe { test_clock_time_get() } +} diff --git a/wasi-misc-tests/src/bin/close_preopen.rs b/wasi-misc-tests/src/bin/close_preopen.rs new file mode 100644 index 000000000000..000be994185c --- /dev/null +++ b/wasi-misc-tests/src/bin/close_preopen.rs @@ -0,0 +1,83 @@ +use libc; +use more_asserts::assert_gt; +use std::{env, mem, process}; +use wasi::wasi_unstable; +use wasi_misc_tests::open_scratch_directory; +use wasi_misc_tests::wasi_wrappers::wasi_fd_fdstat_get; + +unsafe fn test_close_preopen(dir_fd: wasi_unstable::Fd) { + let pre_fd: wasi_unstable::Fd = (libc::STDERR_FILENO + 1) as wasi_unstable::Fd; + + assert_gt!(dir_fd, pre_fd, "dir_fd number"); + + // Try to close a preopened directory handle. + assert_eq!( + wasi_unstable::fd_close(pre_fd), + Err(wasi_unstable::ENOTSUP), + "closing a preopened file descriptor", + ); + + // Try to renumber over a preopened directory handle. + assert_eq!( + wasi_unstable::fd_renumber(dir_fd, pre_fd), + Err(wasi_unstable::ENOTSUP), + "renumbering over a preopened file descriptor", + ); + + // Ensure that dir_fd is still open. + let mut dir_fdstat: wasi_unstable::FdStat = mem::zeroed(); + let mut status = wasi_fd_fdstat_get(dir_fd, &mut dir_fdstat); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "calling fd_fdstat on the scratch directory" + ); + assert_eq!( + dir_fdstat.fs_filetype, + wasi_unstable::FILETYPE_DIRECTORY, + "expected the scratch directory to be a directory", + ); + + // Try to renumber a preopened directory handle. + assert_eq!( + wasi_unstable::fd_renumber(pre_fd, dir_fd), + Err(wasi_unstable::ENOTSUP), + "renumbering over a preopened file descriptor", + ); + + // Ensure that dir_fd is still open. + status = wasi_fd_fdstat_get(dir_fd, &mut dir_fdstat); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "calling fd_fdstat on the scratch directory" + ); + assert_eq!( + dir_fdstat.fs_filetype, + wasi_unstable::FILETYPE_DIRECTORY, + "expected the scratch directory to be a directory", + ); +} + +fn main() { + let mut args = env::args(); + let prog = args.next().unwrap(); + let arg = if let Some(arg) = args.next() { + arg + } else { + eprintln!("usage: {} ", prog); + process::exit(1); + }; + + // Open scratch directory + let dir_fd = match open_scratch_directory(&arg) { + Ok(dir_fd) => dir_fd, + Err(err) => { + eprintln!("{}", err); + process::exit(1) + } + }; + + // Run the tests. + unsafe { test_close_preopen(dir_fd) } +} diff --git a/wasi-misc-tests/src/bin/dangling_symlink.rs b/wasi-misc-tests/src/bin/dangling_symlink.rs new file mode 100644 index 000000000000..8e30fc4ee75a --- /dev/null +++ b/wasi-misc-tests/src/bin/dangling_symlink.rs @@ -0,0 +1,62 @@ +use std::{env, process}; +use wasi::wasi_unstable; +use wasi_misc_tests::open_scratch_directory; +use wasi_misc_tests::utils::cleanup_file; +use wasi_misc_tests::wasi_wrappers::{wasi_path_open, wasi_path_symlink}; + +unsafe fn test_dangling_symlink(dir_fd: wasi_unstable::Fd) { + // First create a dangling symlink. + assert!( + wasi_path_symlink("target", dir_fd, "symlink").is_ok(), + "creating a symlink" + ); + + // Try to open it as a directory with O_NOFOLLOW. + let mut file_fd: wasi_unstable::Fd = wasi_unstable::Fd::max_value() - 1; + let status = wasi_path_open( + dir_fd, + 0, + "symlink", + wasi_unstable::O_DIRECTORY, + 0, + 0, + 0, + &mut file_fd, + ); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ELOOP, + "opening a dangling symlink as a directory", + ); + assert_eq!( + file_fd, + wasi_unstable::Fd::max_value(), + "failed open should set the file descriptor to -1", + ); + + // Clean up. + cleanup_file(dir_fd, "symlink"); +} + +fn main() { + let mut args = env::args(); + let prog = args.next().unwrap(); + let arg = if let Some(arg) = args.next() { + arg + } else { + eprintln!("usage: {} ", prog); + process::exit(1); + }; + + // Open scratch directory + let dir_fd = match open_scratch_directory(&arg) { + Ok(dir_fd) => dir_fd, + Err(err) => { + eprintln!("{}", err); + process::exit(1) + } + }; + + // Run the tests. + unsafe { test_dangling_symlink(dir_fd) } +} diff --git a/wasi-misc-tests/src/bin/directory_seek.rs b/wasi-misc-tests/src/bin/directory_seek.rs new file mode 100644 index 000000000000..01783e4a2772 --- /dev/null +++ b/wasi-misc-tests/src/bin/directory_seek.rs @@ -0,0 +1,90 @@ +use libc; +use more_asserts::assert_gt; +use std::{env, mem, process}; +use wasi::wasi_unstable; +use wasi_misc_tests::open_scratch_directory; +use wasi_misc_tests::utils::{cleanup_dir, close_fd, create_dir}; +use wasi_misc_tests::wasi_wrappers::{wasi_fd_fdstat_get, wasi_fd_seek, wasi_path_open}; + +unsafe fn test_directory_seek(dir_fd: wasi_unstable::Fd) { + // Create a directory in the scratch directory. + create_dir(dir_fd, "dir"); + + // Open the directory and attempt to request rights for seeking. + let mut fd: wasi_unstable::Fd = wasi_unstable::Fd::max_value() - 1; + let mut status = wasi_path_open( + dir_fd, + 0, + "dir", + 0, + wasi_unstable::RIGHT_FD_SEEK, + 0, + 0, + &mut fd, + ); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "opening a file" + ); + assert_gt!( + fd, + libc::STDERR_FILENO as wasi_unstable::Fd, + "file descriptor range check", + ); + + // Attempt to seek. + let mut newoffset = 1; + status = wasi_fd_seek(fd, 0, wasi_unstable::WHENCE_CUR, &mut newoffset); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ENOTCAPABLE, + "seek on a directory" + ); + + // Check if we obtained the right to seek. + let mut fdstat: wasi_unstable::FdStat = mem::zeroed(); + status = wasi_fd_fdstat_get(fd, &mut fdstat); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "calling fd_fdstat on a directory" + ); + assert_eq!( + fdstat.fs_filetype, + wasi_unstable::FILETYPE_DIRECTORY, + "expected the scratch directory to be a directory", + ); + assert_eq!( + (fdstat.fs_rights_base & wasi_unstable::RIGHT_FD_SEEK), + 0, + "directory has the seek right", + ); + + // Clean up. + close_fd(fd); + cleanup_dir(dir_fd, "dir"); +} + +fn main() { + let mut args = env::args(); + let prog = args.next().unwrap(); + let arg = if let Some(arg) = args.next() { + arg + } else { + eprintln!("usage: {} ", prog); + process::exit(1); + }; + + // Open scratch directory + let dir_fd = match open_scratch_directory(&arg) { + Ok(dir_fd) => dir_fd, + Err(err) => { + eprintln!("{}", err); + process::exit(1) + } + }; + + // Run the tests. + unsafe { test_directory_seek(dir_fd) } +} diff --git a/wasi-misc-tests/src/bin/fd_advise.rs b/wasi-misc-tests/src/bin/fd_advise.rs new file mode 100644 index 000000000000..2b48b11bbbc4 --- /dev/null +++ b/wasi-misc-tests/src/bin/fd_advise.rs @@ -0,0 +1,98 @@ +use libc; +use more_asserts::assert_gt; +use std::{env, process}; +use wasi::wasi_unstable; +use wasi_misc_tests::open_scratch_directory; +use wasi_misc_tests::utils::{cleanup_file, close_fd}; +use wasi_misc_tests::wasi_wrappers::{wasi_fd_advise, wasi_fd_filestat_get, wasi_path_open}; + +unsafe fn test_fd_advise(dir_fd: wasi_unstable::Fd) { + // Create a file in the scratch directory. + let mut file_fd = wasi_unstable::Fd::max_value() - 1; + let status = wasi_path_open( + dir_fd, + 0, + "file", + wasi_unstable::O_CREAT, + wasi_unstable::RIGHT_FD_READ | wasi_unstable::RIGHT_FD_WRITE, + 0, + 0, + &mut file_fd, + ); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "opening a file" + ); + assert_gt!( + file_fd, + libc::STDERR_FILENO as wasi_unstable::Fd, + "file descriptor range check", + ); + + // Check file size + let mut stat = wasi_unstable::FileStat { + st_dev: 0, + st_ino: 0, + st_filetype: 0, + st_nlink: 0, + st_size: 0, + st_atim: 0, + st_mtim: 0, + st_ctim: 0, + }; + let status = wasi_fd_filestat_get(file_fd, &mut stat); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "reading file stats" + ); + assert_eq!(stat.st_size, 0, "file size should be 0"); + + // Allocate some size + assert!( + wasi_unstable::fd_allocate(file_fd, 0, 100).is_ok(), + "allocating size" + ); + + let status = wasi_fd_filestat_get(file_fd, &mut stat); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "reading file stats after initial allocation" + ); + assert_eq!(stat.st_size, 100, "file size should be 100"); + + // Advise the kernel + let status = wasi_fd_advise(file_fd, 10, 50, wasi_unstable::ADVICE_NORMAL); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "advising the kernel" + ); + + close_fd(file_fd); + cleanup_file(dir_fd, "file"); +} +fn main() { + let mut args = env::args(); + let prog = args.next().unwrap(); + let arg = if let Some(arg) = args.next() { + arg + } else { + eprintln!("usage: {} ", prog); + process::exit(1); + }; + + // Open scratch directory + let dir_fd = match open_scratch_directory(&arg) { + Ok(dir_fd) => dir_fd, + Err(err) => { + eprintln!("{}", err); + process::exit(1) + } + }; + + // Run the tests. + unsafe { test_fd_advise(dir_fd) } +} diff --git a/wasi-misc-tests/src/bin/fd_filestat_set.rs b/wasi-misc-tests/src/bin/fd_filestat_set.rs new file mode 100644 index 000000000000..90c39b99b1f5 --- /dev/null +++ b/wasi-misc-tests/src/bin/fd_filestat_set.rs @@ -0,0 +1,120 @@ +use libc; +use more_asserts::assert_gt; +use std::{env, process}; +use wasi::wasi_unstable; +use wasi_misc_tests::open_scratch_directory; +use wasi_misc_tests::utils::{cleanup_file, close_fd}; +use wasi_misc_tests::wasi_wrappers::{wasi_fd_filestat_get, wasi_path_open}; + +unsafe fn test_fd_filestat_set(dir_fd: wasi_unstable::Fd) { + // Create a file in the scratch directory. + let mut file_fd = wasi_unstable::Fd::max_value() - 1; + let status = wasi_path_open( + dir_fd, + 0, + "file", + wasi_unstable::O_CREAT, + wasi_unstable::RIGHT_FD_READ | wasi_unstable::RIGHT_FD_WRITE, + 0, + 0, + &mut file_fd, + ); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "opening a file" + ); + assert_gt!( + file_fd, + libc::STDERR_FILENO as wasi_unstable::Fd, + "file descriptor range check", + ); + + // Check file size + let mut stat = wasi_unstable::FileStat { + st_dev: 0, + st_ino: 0, + st_filetype: 0, + st_nlink: 0, + st_size: 0, + st_atim: 0, + st_mtim: 0, + st_ctim: 0, + }; + let status = wasi_fd_filestat_get(file_fd, &mut stat); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "reading file stats" + ); + assert_eq!(stat.st_size, 0, "file size should be 0"); + + // Check fd_filestat_set_size + assert!( + wasi_unstable::fd_filestat_set_size(file_fd, 100).is_ok(), + "fd_filestat_set_size" + ); + + let status = wasi_fd_filestat_get(file_fd, &mut stat); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "reading file stats after fd_filestat_set_size" + ); + assert_eq!(stat.st_size, 100, "file size should be 100"); + + // Check fd_filestat_set_times + let old_atim = stat.st_atim; + let new_mtim = stat.st_mtim - 100; + assert!( + wasi_unstable::fd_filestat_set_times( + file_fd, + new_mtim, + new_mtim, + wasi_unstable::FILESTAT_SET_MTIM, + ) + .is_ok(), + "fd_filestat_set_times" + ); + + let status = wasi_fd_filestat_get(file_fd, &mut stat); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "reading file stats after fd_filestat_set_times" + ); + assert_eq!( + stat.st_size, 100, + "file size should remain unchanged at 100" + ); + assert_eq!(stat.st_mtim, new_mtim, "mtim should change"); + assert_eq!(stat.st_atim, old_atim, "atim should not change"); + + // let status = wasi_fd_filestat_set_times(file_fd, new_mtim, new_mtim, wasi_unstable::FILESTAT_SET_MTIM | wasi_unstable::FILESTAT_SET_MTIM_NOW); + // assert_eq!(status, wasi_unstable::EINVAL, "ATIM & ATIM_NOW can't both be set"); + + close_fd(file_fd); + cleanup_file(dir_fd, "file"); +} +fn main() { + let mut args = env::args(); + let prog = args.next().unwrap(); + let arg = if let Some(arg) = args.next() { + arg + } else { + eprintln!("usage: {} ", prog); + process::exit(1); + }; + + // Open scratch directory + let dir_fd = match open_scratch_directory(&arg) { + Ok(dir_fd) => dir_fd, + Err(err) => { + eprintln!("{}", err); + process::exit(1) + } + }; + + // Run the tests. + unsafe { test_fd_filestat_set(dir_fd) } +} diff --git a/wasi-misc-tests/src/bin/fd_readdir.rs b/wasi-misc-tests/src/bin/fd_readdir.rs new file mode 100644 index 000000000000..30ff87a953cd --- /dev/null +++ b/wasi-misc-tests/src/bin/fd_readdir.rs @@ -0,0 +1,189 @@ +use libc; +use more_asserts::assert_gt; +use std::{cmp::min, env, mem, process, slice, str}; +use wasi::wasi_unstable; +use wasi_misc_tests::open_scratch_directory; +use wasi_misc_tests::wasi_wrappers::{wasi_fd_filestat_get, wasi_fd_readdir, wasi_path_open}; + +const BUF_LEN: usize = 256; + +#[derive(Debug)] +struct DirEntry { + dirent: wasi_unstable::Dirent, + name: String, +} + +// Manually reading the output from fd_readdir is tedious and repetitive, +// so encapsulate it into an iterator +struct ReadDir<'a> { + buf: &'a [u8], +} + +impl<'a> ReadDir<'a> { + fn from_slice(buf: &'a [u8]) -> Self { + Self { buf } + } +} + +impl<'a> Iterator for ReadDir<'a> { + type Item = DirEntry; + + fn next(&mut self) -> Option { + unsafe { + if self.buf.is_empty() { + return None; + } + + // Read the data + let dirent_ptr = self.buf.as_ptr() as *const wasi_unstable::Dirent; + let dirent = *dirent_ptr; + let name_ptr = dirent_ptr.offset(1) as *const u8; + // NOTE Linux syscall returns a NULL-terminated name, but WASI doesn't + let namelen = dirent.d_namlen as usize; + let slice = slice::from_raw_parts(name_ptr, namelen); + let name = str::from_utf8(slice).expect("invalid utf8").to_owned(); + + // Update the internal state + let delta = mem::size_of_val(&dirent) + namelen; + self.buf = &self.buf[delta..]; + + DirEntry { dirent, name }.into() + } + } +} + +unsafe fn exec_fd_readdir( + fd: wasi_unstable::Fd, + cookie: wasi_unstable::DirCookie, +) -> Vec { + let mut buf: [u8; BUF_LEN] = [0; BUF_LEN]; + let mut bufused = 0; + let status = wasi_fd_readdir(fd, &mut buf, BUF_LEN, cookie, &mut bufused); + assert_eq!(status, wasi_unstable::raw::__WASI_ESUCCESS, "fd_readdir"); + + let sl = slice::from_raw_parts(buf.as_ptr(), min(BUF_LEN, bufused)); + let dirs: Vec<_> = ReadDir::from_slice(sl).collect(); + dirs +} + +unsafe fn test_fd_readdir(dir_fd: wasi_unstable::Fd) { + let mut stat: wasi_unstable::FileStat = mem::zeroed(); + let status = wasi_fd_filestat_get(dir_fd, &mut stat); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "reading scratch directory stats" + ); + + // Check the behavior in an empty directory + let mut dirs = exec_fd_readdir(dir_fd, wasi_unstable::DIRCOOKIE_START); + dirs.sort_by_key(|d| d.name.clone()); + assert_eq!(dirs.len(), 2, "expected two entries in an empty directory"); + let mut dirs = dirs.into_iter(); + + // the first entry should be `.` + let dir = dirs.next().expect("first entry is None"); + assert_eq!(dir.name, ".", "first name"); + assert_eq!( + dir.dirent.d_type, + wasi_unstable::FILETYPE_DIRECTORY, + "first type" + ); + assert_eq!(dir.dirent.d_ino, stat.st_ino); + assert_eq!(dir.dirent.d_namlen, 1); + + // the second entry should be `..` + let dir = dirs.next().expect("second entry is None"); + assert_eq!(dir.name, "..", "second name"); + assert_eq!( + dir.dirent.d_type, + wasi_unstable::FILETYPE_DIRECTORY, + "second type" + ); + + assert!( + dirs.next().is_none(), + "the directory should be seen as empty" + ); + + // Add a file and check the behavior + let mut file_fd = wasi_unstable::Fd::max_value() - 1; + let status = wasi_path_open( + dir_fd, + 0, + "file", + wasi_unstable::O_CREAT, + wasi_unstable::RIGHT_FD_READ | wasi_unstable::RIGHT_FD_WRITE, + 0, + 0, + &mut file_fd, + ); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "opening a file" + ); + assert_gt!( + file_fd, + libc::STDERR_FILENO as wasi_unstable::Fd, + "file descriptor range check", + ); + + let status = wasi_fd_filestat_get(file_fd, &mut stat); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "reading file stats" + ); + + // Execute another readdir + let mut dirs = exec_fd_readdir(dir_fd, wasi_unstable::DIRCOOKIE_START); + assert_eq!(dirs.len(), 3, "expected three entries"); + // Save the data about the last entry. We need to do it before sorting. + let lastfile_cookie = dirs[1].dirent.d_next; + let lastfile_name = dirs[2].name.clone(); + dirs.sort_by_key(|d| d.name.clone()); + let mut dirs = dirs.into_iter(); + + let dir = dirs.next().expect("first entry is None"); + assert_eq!(dir.name, ".", "first name"); + let dir = dirs.next().expect("second entry is None"); + assert_eq!(dir.name, "..", "second name"); + let dir = dirs.next().expect("third entry is None"); + // check the file info + assert_eq!(dir.name, "file", "file name doesn't match"); + assert_eq!( + dir.dirent.d_type, + wasi_unstable::FILETYPE_REGULAR_FILE, + "type for the real file" + ); + assert_eq!(dir.dirent.d_ino, stat.st_ino); + + // check if cookie works as expected + let dirs = exec_fd_readdir(dir_fd, lastfile_cookie); + assert_eq!(dirs.len(), 1, "expected one entry"); + assert_eq!(dirs[0].name, lastfile_name, "name of the only entry"); +} + +fn main() { + let mut args = env::args(); + let prog = args.next().unwrap(); + let arg = if let Some(arg) = args.next() { + arg + } else { + eprintln!("usage: {} ", prog); + process::exit(1); + }; + + // Open scratch directory + let dir_fd = match open_scratch_directory(&arg) { + Ok(dir_fd) => dir_fd, + Err(err) => { + eprintln!("{}", err); + process::exit(1) + } + }; + + // Run the tests. + unsafe { test_fd_readdir(dir_fd) } +} diff --git a/wasi-misc-tests/src/bin/file_allocate.rs b/wasi-misc-tests/src/bin/file_allocate.rs new file mode 100644 index 000000000000..c1e3d6a07dea --- /dev/null +++ b/wasi-misc-tests/src/bin/file_allocate.rs @@ -0,0 +1,125 @@ +use libc; +use more_asserts::assert_gt; +use std::{env, process}; +use wasi::wasi_unstable; +use wasi_misc_tests::open_scratch_directory; +use wasi_misc_tests::utils::{cleanup_file, close_fd}; +use wasi_misc_tests::wasi_wrappers::{wasi_fd_filestat_get, wasi_path_open}; + +unsafe fn test_file_allocate(dir_fd: wasi_unstable::Fd) { + // Create a file in the scratch directory. + let mut file_fd = wasi_unstable::Fd::max_value() - 1; + let status = wasi_path_open( + dir_fd, + 0, + "file", + wasi_unstable::O_CREAT, + wasi_unstable::RIGHT_FD_READ | wasi_unstable::RIGHT_FD_WRITE, + 0, + 0, + &mut file_fd, + ); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "opening a file" + ); + assert_gt!( + file_fd, + libc::STDERR_FILENO as wasi_unstable::Fd, + "file descriptor range check", + ); + + // Check file size + let mut stat = wasi_unstable::FileStat { + st_dev: 0, + st_ino: 0, + st_filetype: 0, + st_nlink: 0, + st_size: 0, + st_atim: 0, + st_mtim: 0, + st_ctim: 0, + }; + let status = wasi_fd_filestat_get(file_fd, &mut stat); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "reading file stats" + ); + assert_eq!(stat.st_size, 0, "file size should be 0"); + + // Allocate some size + assert!( + wasi_unstable::fd_allocate(file_fd, 0, 100).is_ok(), + "allocating size" + ); + + let status = wasi_fd_filestat_get(file_fd, &mut stat); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "reading file stats after initial allocation" + ); + assert_eq!(stat.st_size, 100, "file size should be 100"); + + // Allocate should not modify if less than current size + assert!( + wasi_unstable::fd_allocate(file_fd, 10, 10).is_ok(), + "allocating size less than current size" + ); + + let status = wasi_fd_filestat_get(file_fd, &mut stat); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "reading file stats after additional allocation was not required" + ); + assert_eq!( + stat.st_size, 100, + "file size should remain unchanged at 100" + ); + + // Allocate should modify if offset+len > current_len + assert!( + wasi_unstable::fd_allocate(file_fd, 90, 20).is_ok(), + "allocating size larger than current size" + ); + + let status = wasi_fd_filestat_get(file_fd, &mut stat); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "reading file stats after additional allocation was required" + ); + assert_eq!( + stat.st_size, 110, + "file size should increase from 100 to 110" + ); + + close_fd(file_fd); + cleanup_file(dir_fd, "file"); +} + +fn main() { + let mut args = env::args(); + let prog = args.next().unwrap(); + let arg = if let Some(arg) = args.next() { + arg + } else { + eprintln!("usage: {} ", prog); + process::exit(1); + }; + + // Open scratch directory + let dir_fd = match open_scratch_directory(&arg) { + Ok(dir_fd) => dir_fd, + Err(err) => { + eprintln!("{}", err); + process::exit(1) + } + }; + + // Run the tests. + unsafe { test_file_allocate(dir_fd) } +} diff --git a/wasi-misc-tests/src/bin/file_pread_pwrite.rs b/wasi-misc-tests/src/bin/file_pread_pwrite.rs new file mode 100644 index 000000000000..8887ead5de19 --- /dev/null +++ b/wasi-misc-tests/src/bin/file_pread_pwrite.rs @@ -0,0 +1,131 @@ +use libc; +use more_asserts::assert_gt; +use std::{env, process}; +use wasi::wasi_unstable; +use wasi_misc_tests::open_scratch_directory; +use wasi_misc_tests::utils::{cleanup_file, close_fd}; +use wasi_misc_tests::wasi_wrappers::{wasi_fd_pread, wasi_fd_pwrite, wasi_path_open}; + +unsafe fn test_file_pread_pwrite(dir_fd: wasi_unstable::Fd) { + // Create a file in the scratch directory. + let mut file_fd = wasi_unstable::Fd::max_value() - 1; + let mut status = wasi_path_open( + dir_fd, + 0, + "file", + wasi_unstable::O_CREAT, + wasi_unstable::RIGHT_FD_READ | wasi_unstable::RIGHT_FD_WRITE, + 0, + 0, + &mut file_fd, + ); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "opening a file" + ); + assert_gt!( + file_fd, + libc::STDERR_FILENO as wasi_unstable::Fd, + "file descriptor range check", + ); + + let contents = &[0u8, 1, 2, 3]; + let ciovec = wasi_unstable::CIoVec { + buf: contents.as_ptr() as *const libc::c_void, + buf_len: contents.len(), + }; + let mut nwritten = 0; + status = wasi_fd_pwrite(file_fd, &mut [ciovec], 0, &mut nwritten); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "writing bytes at offset 0" + ); + assert_eq!(nwritten, 4, "nwritten bytes check"); + + let contents = &mut [0u8; 4]; + let iovec = wasi_unstable::IoVec { + buf: contents.as_mut_ptr() as *mut libc::c_void, + buf_len: contents.len(), + }; + let mut nread = 0; + status = wasi_fd_pread(file_fd, &[iovec], 0, &mut nread); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "reading bytes at offset 0" + ); + assert_eq!(nread, 4, "nread bytes check"); + assert_eq!(contents, &[0u8, 1, 2, 3], "written bytes equal read bytes"); + + let contents = &mut [0u8; 4]; + let iovec = wasi_unstable::IoVec { + buf: contents.as_mut_ptr() as *mut libc::c_void, + buf_len: contents.len(), + }; + let mut nread = 0; + status = wasi_fd_pread(file_fd, &[iovec], 2, &mut nread); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "reading bytes at offset 2" + ); + assert_eq!(nread, 2, "nread bytes check"); + assert_eq!(contents, &[2u8, 3, 0, 0], "file cursor was overwritten"); + + let contents = &[1u8, 0]; + let ciovec = wasi_unstable::CIoVec { + buf: contents.as_ptr() as *const libc::c_void, + buf_len: contents.len(), + }; + let mut nwritten = 0; + status = wasi_fd_pwrite(file_fd, &mut [ciovec], 2, &mut nwritten); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "writing bytes at offset 2" + ); + assert_eq!(nwritten, 2, "nwritten bytes check"); + + let contents = &mut [0u8; 4]; + let iovec = wasi_unstable::IoVec { + buf: contents.as_mut_ptr() as *mut libc::c_void, + buf_len: contents.len(), + }; + let mut nread = 0; + status = wasi_fd_pread(file_fd, &[iovec], 0, &mut nread); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "reading bytes at offset 0" + ); + assert_eq!(nread, 4, "nread bytes check"); + assert_eq!(contents, &[0u8, 1, 1, 0], "file cursor was overwritten"); + + close_fd(file_fd); + cleanup_file(dir_fd, "file"); +} + +fn main() { + let mut args = env::args(); + let prog = args.next().unwrap(); + let arg = if let Some(arg) = args.next() { + arg + } else { + eprintln!("usage: {} ", prog); + process::exit(1); + }; + + // Open scratch directory + let dir_fd = match open_scratch_directory(&arg) { + Ok(dir_fd) => dir_fd, + Err(err) => { + eprintln!("{}", err); + process::exit(1) + } + }; + + // Run the tests. + unsafe { test_file_pread_pwrite(dir_fd) } +} diff --git a/wasi-misc-tests/src/bin/file_seek_tell.rs b/wasi-misc-tests/src/bin/file_seek_tell.rs new file mode 100644 index 000000000000..fa7f0cd22d46 --- /dev/null +++ b/wasi-misc-tests/src/bin/file_seek_tell.rs @@ -0,0 +1,133 @@ +use libc; +use more_asserts::assert_gt; +use std::{env, process}; +use wasi::wasi_unstable; +use wasi_misc_tests::open_scratch_directory; +use wasi_misc_tests::utils::{cleanup_file, close_fd}; +use wasi_misc_tests::wasi_wrappers::{wasi_fd_seek, wasi_fd_tell, wasi_fd_write, wasi_path_open}; + +unsafe fn test_file_seek_tell(dir_fd: wasi_unstable::Fd) { + // Create a file in the scratch directory. + let mut file_fd = wasi_unstable::Fd::max_value() - 1; + let mut status = wasi_path_open( + dir_fd, + 0, + "file", + wasi_unstable::O_CREAT, + wasi_unstable::RIGHT_FD_READ | wasi_unstable::RIGHT_FD_WRITE, + 0, + 0, + &mut file_fd, + ); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "opening a file" + ); + assert_gt!( + file_fd, + libc::STDERR_FILENO as wasi_unstable::Fd, + "file descriptor range check", + ); + + // Check current offset + let mut offset: wasi_unstable::FileSize = 0; + status = wasi_fd_tell(file_fd, &mut offset); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "getting initial file offset" + ); + assert_eq!(offset, 0, "current offset should be 0"); + + // Write to file + let buf = &[0u8; 100]; + let iov = wasi_unstable::CIoVec { + buf: buf.as_ptr() as *const _, + buf_len: buf.len(), + }; + let iovs = &[iov]; + let mut nwritten = 0; + status = wasi_fd_write(file_fd, iovs, &mut nwritten); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "writing to a file" + ); + assert_eq!(nwritten, 100, "should write 100 bytes to file"); + + // Check current offset + status = wasi_fd_tell(file_fd, &mut offset); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "getting file offset after writing" + ); + assert_eq!(offset, 100, "offset after writing should be 100"); + + // Seek to middle of the file + let mut newoffset = 1; + status = wasi_fd_seek(file_fd, -50, wasi_unstable::WHENCE_CUR, &mut newoffset); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "seeking to the middle of a file" + ); + assert_eq!( + newoffset, 50, + "offset after seeking to the middle should be at 50" + ); + + // Seek to the beginning of the file + status = wasi_fd_seek(file_fd, 0, wasi_unstable::WHENCE_SET, &mut newoffset); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "seeking to the beginning of the file" + ); + assert_eq!( + newoffset, 0, + "offset after seeking to the beginning of the file should be at 0" + ); + + // Seek beyond the file should be possible + status = wasi_fd_seek(file_fd, 1000, wasi_unstable::WHENCE_CUR, &mut newoffset); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "seeking beyond the end of the file" + ); + + // Seek before byte 0 is an error though + status = wasi_fd_seek(file_fd, -2000, wasi_unstable::WHENCE_CUR, &mut newoffset); + assert_eq!( + status, + wasi_unstable::raw::__WASI_EINVAL, + "seeking before byte 0 is an error" + ); + + close_fd(file_fd); + cleanup_file(dir_fd, "file"); +} +fn main() { + let mut args = env::args(); + let prog = args.next().unwrap(); + let arg = if let Some(arg) = args.next() { + arg + } else { + eprintln!("usage: {} ", prog); + process::exit(1); + }; + + // Open scratch directory + let dir_fd = match open_scratch_directory(&arg) { + Ok(dir_fd) => dir_fd, + Err(err) => { + eprintln!("{}", err); + process::exit(1) + } + }; + + // Run the tests. + unsafe { test_file_seek_tell(dir_fd) } +} diff --git a/wasi-misc-tests/src/bin/file_unbuffered_write.rs b/wasi-misc-tests/src/bin/file_unbuffered_write.rs new file mode 100644 index 000000000000..270df3db83f2 --- /dev/null +++ b/wasi-misc-tests/src/bin/file_unbuffered_write.rs @@ -0,0 +1,116 @@ +use libc; +use more_asserts::assert_gt; +use std::{env, process}; +use wasi::wasi_unstable; +use wasi_misc_tests::open_scratch_directory; +use wasi_misc_tests::utils::{cleanup_file, close_fd, create_file}; +use wasi_misc_tests::wasi_wrappers::{wasi_fd_read, wasi_fd_write, wasi_path_open}; + +unsafe fn test_file_unbuffered_write(dir_fd: wasi_unstable::Fd) { + // Create file + create_file(dir_fd, "file"); + + // Open file for reading + let mut fd_read = wasi_unstable::Fd::max_value() - 1; + let mut status = wasi_path_open( + dir_fd, + 0, + "file", + 0, + wasi_unstable::RIGHT_FD_READ, + 0, + 0, + &mut fd_read, + ); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "opening a file" + ); + assert_gt!( + fd_read, + libc::STDERR_FILENO as wasi_unstable::Fd, + "file descriptor range check", + ); + + // Open the same file but for writing + let mut fd_write = wasi_unstable::Fd::max_value() - 1; + status = wasi_path_open( + dir_fd, + 0, + "file", + 0, + wasi_unstable::RIGHT_FD_WRITE, + 0, + 0, + &mut fd_write, + ); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "opening a file" + ); + assert_gt!( + fd_write, + libc::STDERR_FILENO as wasi_unstable::Fd, + "file descriptor range check", + ); + + // Write to file + let contents = &[1u8]; + let ciovec = wasi_unstable::CIoVec { + buf: contents.as_ptr() as *const libc::c_void, + buf_len: contents.len(), + }; + let mut nwritten = 0; + status = wasi_fd_write(fd_write, &[ciovec], &mut nwritten); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "writing byte to file" + ); + assert_eq!(nwritten, 1, "nwritten bytes check"); + + // Read from file + let contents = &mut [0u8; 1]; + let iovec = wasi_unstable::IoVec { + buf: contents.as_mut_ptr() as *mut libc::c_void, + buf_len: contents.len(), + }; + let mut nread = 0; + status = wasi_fd_read(fd_read, &[iovec], &mut nread); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "reading bytes from file" + ); + assert_eq!(nread, 1, "nread bytes check"); + assert_eq!(contents, &[1u8], "written bytes equal read bytes"); + + // Clean up + close_fd(fd_write); + close_fd(fd_read); + cleanup_file(dir_fd, "file"); +} +fn main() { + let mut args = env::args(); + let prog = args.next().unwrap(); + let arg = if let Some(arg) = args.next() { + arg + } else { + eprintln!("usage: {} ", prog); + process::exit(1); + }; + + // Open scratch directory + let dir_fd = match open_scratch_directory(&arg) { + Ok(dir_fd) => dir_fd, + Err(err) => { + eprintln!("{}", err); + process::exit(1) + } + }; + + // Run the tests. + unsafe { test_file_unbuffered_write(dir_fd) } +} diff --git a/wasi-misc-tests/src/bin/interesting_paths.rs b/wasi-misc-tests/src/bin/interesting_paths.rs new file mode 100644 index 000000000000..cfb68bc81a18 --- /dev/null +++ b/wasi-misc-tests/src/bin/interesting_paths.rs @@ -0,0 +1,173 @@ +use libc; +use more_asserts::assert_gt; +use std::{env, process}; +use wasi::wasi_unstable; +use wasi_misc_tests::open_scratch_directory; +use wasi_misc_tests::utils::{close_fd, create_dir, create_file}; +use wasi_misc_tests::wasi_wrappers::{ + wasi_path_open, wasi_path_remove_directory, wasi_path_unlink_file, +}; + +unsafe fn test_interesting_paths(dir_fd: wasi_unstable::Fd, arg: &str) { + // Create a directory in the scratch directory. + create_dir(dir_fd, "dir"); + + // Create a directory in the directory we just created. + create_dir(dir_fd, "dir/nested"); + + // Create a file in the nested directory. + create_file(dir_fd, "dir/nested/file"); + + // Now open it with an absolute path. + let mut file_fd: wasi_unstable::Fd = wasi_unstable::Fd::max_value() - 1; + let mut status = wasi_path_open(dir_fd, 0, "/dir/nested/file", 0, 0, 0, 0, &mut file_fd); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ENOTCAPABLE, + "opening a file with an absolute path" + ); + assert_eq!( + file_fd, + wasi_unstable::Fd::max_value(), + "failed open should set the file descriptor to -1", + ); + + // Now open it with a path containing "..". + status = wasi_path_open( + dir_fd, + 0, + "dir/.//nested/../../dir/nested/../nested///./file", + 0, + 0, + 0, + 0, + &mut file_fd, + ); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "opening a file with \"..\" in the path" + ); + assert_gt!( + file_fd, + libc::STDERR_FILENO as wasi_unstable::Fd, + "file descriptor range check", + ); + close_fd(file_fd); + + // Now open it with a trailing NUL. + status = wasi_path_open(dir_fd, 0, "dir/nested/file\0", 0, 0, 0, 0, &mut file_fd); + assert_eq!( + status, + wasi_unstable::raw::__WASI_EILSEQ, + "opening a file with a trailing NUL" + ); + assert_eq!( + file_fd, + wasi_unstable::Fd::max_value(), + "failed open should set the file descriptor to -1", + ); + + // Now open it with a trailing slash. + status = wasi_path_open(dir_fd, 0, "dir/nested/file/", 0, 0, 0, 0, &mut file_fd); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ENOTDIR, + "opening a file with a trailing slash" + ); + assert_eq!( + file_fd, + wasi_unstable::Fd::max_value(), + "failed open should set the file descriptor to -1", + ); + + // Now open it with trailing slashes. + status = wasi_path_open(dir_fd, 0, "dir/nested/file///", 0, 0, 0, 0, &mut file_fd); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ENOTDIR, + "opening a file with trailing slashes" + ); + assert_eq!( + file_fd, + wasi_unstable::Fd::max_value(), + "failed open should set the file descriptor to -1", + ); + + // Now open the directory with a trailing slash. + status = wasi_path_open(dir_fd, 0, "dir/nested/", 0, 0, 0, 0, &mut file_fd); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "opening a directory with a trailing slash" + ); + assert_gt!( + file_fd, + libc::STDERR_FILENO as wasi_unstable::Fd, + "file descriptor range check", + ); + close_fd(file_fd); + + // Now open the directory with trailing slashes. + status = wasi_path_open(dir_fd, 0, "dir/nested///", 0, 0, 0, 0, &mut file_fd); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "opening a directory with trailing slashes" + ); + assert_gt!( + file_fd, + libc::STDERR_FILENO as wasi_unstable::Fd, + "file descriptor range check", + ); + close_fd(file_fd); + + // Now open it with a path containing too many ".."s. + let bad_path = format!("dir/nested/../../../{}/dir/nested/file", arg); + status = wasi_path_open(dir_fd, 0, &bad_path, 0, 0, 0, 0, &mut file_fd); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ENOTCAPABLE, + "opening a file with too many \"..\"s in the path" + ); + assert_eq!( + file_fd, + wasi_unstable::Fd::max_value(), + "failed open should set the file descriptor to -1", + ); + assert!( + wasi_path_unlink_file(dir_fd, "dir/nested/file").is_ok(), + "unlink_file on a symlink should succeed" + ); + assert!( + wasi_path_remove_directory(dir_fd, "dir/nested").is_ok(), + "remove_directory on a directory should succeed" + ); + assert!( + wasi_path_remove_directory(dir_fd, "dir").is_ok(), + "remove_directory on a directory should succeed" + ); +} + +fn main() { + let mut args = env::args(); + let prog = args.next().unwrap(); + let arg = if let Some(arg) = args.next() { + arg + } else { + eprintln!("usage: {} ", prog); + process::exit(1); + }; + + // Open scratch directory + let dir_fd = match open_scratch_directory(&arg) { + Ok(dir_fd) => dir_fd, + Err(err) => { + eprintln!("{}", err); + process::exit(1) + } + }; + + // Run the tests. + unsafe { test_interesting_paths(dir_fd, &arg) } +} diff --git a/wasi-misc-tests/src/bin/isatty.rs b/wasi-misc-tests/src/bin/isatty.rs new file mode 100644 index 000000000000..945998a203d2 --- /dev/null +++ b/wasi-misc-tests/src/bin/isatty.rs @@ -0,0 +1,63 @@ +use libc; +use more_asserts::assert_gt; +use std::{env, process}; +use wasi::wasi_unstable; +use wasi_misc_tests::open_scratch_directory; +use wasi_misc_tests::utils::{cleanup_file, close_fd}; +use wasi_misc_tests::wasi_wrappers::wasi_path_open; + +unsafe fn test_isatty(dir_fd: wasi_unstable::Fd) { + // Create a file in the scratch directory and test if it's a tty. + let mut file_fd: wasi_unstable::Fd = wasi_unstable::Fd::max_value() - 1; + let status = wasi_path_open( + dir_fd, + 0, + "file", + wasi_unstable::O_CREAT, + 0, + 0, + 0, + &mut file_fd, + ); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "opening a file" + ); + assert_gt!( + file_fd, + libc::STDERR_FILENO as wasi_unstable::Fd, + "file descriptor range check", + ); + assert_eq!( + libc::isatty(file_fd as std::os::raw::c_int), + 0, + "file is a tty" + ); + close_fd(file_fd); + + cleanup_file(dir_fd, "file"); +} + +fn main() { + let mut args = env::args(); + let prog = args.next().unwrap(); + let arg = if let Some(arg) = args.next() { + arg + } else { + eprintln!("usage: {} ", prog); + process::exit(1); + }; + + // Open scratch directory + let dir_fd = match open_scratch_directory(&arg) { + Ok(dir_fd) => dir_fd, + Err(err) => { + eprintln!("{}", err); + process::exit(1) + } + }; + + // Run the tests. + unsafe { test_isatty(dir_fd) } +} diff --git a/wasi-misc-tests/src/bin/nofollow_errors.rs b/wasi-misc-tests/src/bin/nofollow_errors.rs new file mode 100644 index 000000000000..5dd31f66d0dc --- /dev/null +++ b/wasi-misc-tests/src/bin/nofollow_errors.rs @@ -0,0 +1,177 @@ +use libc; +use more_asserts::assert_gt; +use std::{env, process}; +use wasi::wasi_unstable; +use wasi_misc_tests::open_scratch_directory; +use wasi_misc_tests::utils::{cleanup_file, close_fd, create_dir, create_file}; +use wasi_misc_tests::wasi_wrappers::{ + wasi_path_open, wasi_path_remove_directory, wasi_path_symlink, +}; + +unsafe fn test_nofollow_errors(dir_fd: wasi_unstable::Fd) { + // Create a directory for the symlink to point to. + create_dir(dir_fd, "target"); + + // Create a symlink. + assert!( + wasi_path_symlink("target", dir_fd, "symlink").is_ok(), + "creating a symlink" + ); + + // Try to open it as a directory with O_NOFOLLOW again. + let mut file_fd: wasi_unstable::Fd = wasi_unstable::Fd::max_value() - 1; + let mut status = wasi_path_open( + dir_fd, + 0, + "symlink", + wasi_unstable::O_DIRECTORY, + 0, + 0, + 0, + &mut file_fd, + ); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ELOOP, + "opening a directory symlink as a directory", + ); + assert_eq!( + file_fd, + wasi_unstable::Fd::max_value(), + "failed open should set the file descriptor to -1", + ); + + // Try to open it with just O_NOFOLLOW. + status = wasi_path_open(dir_fd, 0, "symlink", 0, 0, 0, 0, &mut file_fd); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ELOOP, + "opening a symlink with O_NOFOLLOW should return ELOOP", + ); + assert_eq!( + file_fd, + wasi_unstable::Fd::max_value(), + "failed open should set the file descriptor to -1", + ); + + // Try to open it as a directory without O_NOFOLLOW. + status = wasi_path_open( + dir_fd, + wasi_unstable::LOOKUP_SYMLINK_FOLLOW, + "symlink", + wasi_unstable::O_DIRECTORY, + 0, + 0, + 0, + &mut file_fd, + ); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "opening a symlink as a directory" + ); + assert_gt!( + file_fd, + libc::STDERR_FILENO as wasi_unstable::Fd, + "file descriptor range check", + ); + close_fd(file_fd); + + // Replace the target directory with a file. + cleanup_file(dir_fd, "symlink"); + + assert!( + wasi_path_remove_directory(dir_fd, "target").is_ok(), + "remove_directory on a directory should succeed" + ); + create_file(dir_fd, "target"); + + assert!( + wasi_path_symlink("target", dir_fd, "symlink").is_ok(), + "creating a symlink" + ); + + // Try to open it as a directory with O_NOFOLLOW again. + status = wasi_path_open( + dir_fd, + 0, + "symlink", + wasi_unstable::O_DIRECTORY, + 0, + 0, + 0, + &mut file_fd, + ); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ELOOP, + "opening a directory symlink as a directory", + ); + assert_eq!( + file_fd, + wasi_unstable::Fd::max_value(), + "failed open should set the file descriptor to -1", + ); + + // Try to open it with just O_NOFOLLOW. + status = wasi_path_open(dir_fd, 0, "symlink", 0, 0, 0, 0, &mut file_fd); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ELOOP, + "opening a symlink with O_NOFOLLOW should return ELOOP", + ); + assert_eq!( + file_fd, + wasi_unstable::Fd::max_value(), + "failed open should set the file descriptor to -1", + ); + + // Try to open it as a directory without O_NOFOLLOW. + status = wasi_path_open( + dir_fd, + wasi_unstable::LOOKUP_SYMLINK_FOLLOW, + "symlink", + wasi_unstable::O_DIRECTORY, + 0, + 0, + 0, + &mut file_fd, + ); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ENOTDIR, + "opening a symlink to a file as a directory", + ); + assert_eq!( + file_fd, + wasi_unstable::Fd::max_value(), + "failed open should set the file descriptor to -1", + ); + + // Clean up. + cleanup_file(dir_fd, "target"); + cleanup_file(dir_fd, "symlink"); +} + +fn main() { + let mut args = env::args(); + let prog = args.next().unwrap(); + let arg = if let Some(arg) = args.next() { + arg + } else { + eprintln!("usage: {} ", prog); + process::exit(1); + }; + + // Open scratch directory + let dir_fd = match open_scratch_directory(&arg) { + Ok(dir_fd) => dir_fd, + Err(err) => { + eprintln!("{}", err); + process::exit(1) + } + }; + + // Run the tests. + unsafe { test_nofollow_errors(dir_fd) } +} diff --git a/wasi-misc-tests/src/bin/path_filestat.rs b/wasi-misc-tests/src/bin/path_filestat.rs new file mode 100644 index 000000000000..2c831466e4e2 --- /dev/null +++ b/wasi-misc-tests/src/bin/path_filestat.rs @@ -0,0 +1,184 @@ +use libc; +use more_asserts::assert_gt; +use std::{env, process}; +use wasi::wasi_unstable; +use wasi_misc_tests::open_scratch_directory; +use wasi_misc_tests::utils::{cleanup_file, close_fd}; +use wasi_misc_tests::wasi_wrappers::{ + wasi_fd_fdstat_get, wasi_path_filestat_get, wasi_path_filestat_set_times, wasi_path_open, +}; + +unsafe fn test_path_filestat(dir_fd: wasi_unstable::Fd) { + let mut fdstat: wasi_unstable::FdStat = std::mem::zeroed(); + let status = wasi_fd_fdstat_get(dir_fd, &mut fdstat); + assert_eq!(status, wasi_unstable::raw::__WASI_ESUCCESS, "fd_fdstat_get"); + + assert_ne!( + fdstat.fs_rights_base & wasi_unstable::RIGHT_PATH_FILESTAT_GET, + 0, + "the scratch directory should have RIGHT_PATH_FILESTAT_GET as base right", + ); + assert_ne!( + fdstat.fs_rights_inheriting & wasi_unstable::RIGHT_PATH_FILESTAT_GET, + 0, + "the scratch directory should have RIGHT_PATH_FILESTAT_GET as base right", + ); + + // Create a file in the scratch directory. + let mut file_fd = wasi_unstable::Fd::max_value() - 1; + let filename = "file"; + let status = wasi_path_open( + dir_fd, + 0, + filename, + wasi_unstable::O_CREAT, + wasi_unstable::RIGHT_FD_READ + | wasi_unstable::RIGHT_FD_WRITE + | wasi_unstable::RIGHT_PATH_FILESTAT_GET, + 0, + 0, + &mut file_fd, + ); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "opening a file" + ); + assert_gt!( + file_fd, + libc::STDERR_FILENO as wasi_unstable::Fd, + "file descriptor range check", + ); + + let status = wasi_fd_fdstat_get(file_fd, &mut fdstat); + assert_eq!(status, wasi_unstable::raw::__WASI_ESUCCESS, "fd_fdstat_get"); + + assert_eq!( + fdstat.fs_rights_base & wasi_unstable::RIGHT_PATH_FILESTAT_GET, + 0, + "files shouldn't have rights for path_* syscalls even if manually given", + ); + assert_eq!( + fdstat.fs_rights_inheriting & wasi_unstable::RIGHT_PATH_FILESTAT_GET, + 0, + "files shouldn't have rights for path_* syscalls even if manually given", + ); + + // Check file size + let mut stat = wasi_unstable::FileStat { + st_dev: 0, + st_ino: 0, + st_filetype: 0, + st_nlink: 0, + st_size: 0, + st_atim: 0, + st_mtim: 0, + st_ctim: 0, + }; + let status = wasi_path_filestat_get(dir_fd, 0, filename, filename.len(), &mut stat); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "reading file stats" + ); + assert_eq!(stat.st_size, 0, "file size should be 0"); + + // Check path_filestat_set_times + let old_atim = stat.st_atim; + let new_mtim = stat.st_mtim - 100; + assert!( + wasi_path_filestat_set_times( + dir_fd, + 0, + filename, + // on purpose: the syscall should not touch atim, because + // neither of the ATIM flags is set + new_mtim, + new_mtim, + wasi_unstable::FILESTAT_SET_MTIM, + ) + .is_ok(), + "path_filestat_set_times should succeed" + ); + + let status = wasi_path_filestat_get(dir_fd, 0, filename, filename.len(), &mut stat); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "reading file stats after path_filestat_set_times" + ); + assert_eq!(stat.st_mtim, new_mtim, "mtim should change"); + assert_eq!(stat.st_atim, old_atim, "atim should not change"); + + assert_eq!( + wasi_path_filestat_set_times( + dir_fd, + 0, + filename, + new_mtim, + new_mtim, + wasi_unstable::FILESTAT_SET_MTIM | wasi_unstable::FILESTAT_SET_MTIM_NOW, + ), + Err(wasi_unstable::EINVAL), + "MTIM & MTIM_NOW can't both be set" + ); + + // check if the times were untouched + let status = wasi_path_filestat_get(dir_fd, 0, filename, filename.len(), &mut stat); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "reading file stats after EINVAL fd_filestat_set_times" + ); + assert_eq!(stat.st_mtim, new_mtim, "mtim should not change"); + assert_eq!(stat.st_atim, old_atim, "atim should not change"); + + let new_atim = old_atim - 100; + assert_eq!( + wasi_path_filestat_set_times( + dir_fd, + 0, + filename, + new_atim, + new_atim, + wasi_unstable::FILESTAT_SET_ATIM | wasi_unstable::FILESTAT_SET_ATIM_NOW, + ), + Err(wasi_unstable::EINVAL), + "ATIM & ATIM_NOW can't both be set" + ); + + // check if the times were untouched + let status = wasi_path_filestat_get(dir_fd, 0, filename, filename.len(), &mut stat); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "reading file stats after EINVAL path_filestat_set_times" + ); + assert_eq!(stat.st_mtim, new_mtim, "mtim should not change"); + assert_eq!(stat.st_atim, old_atim, "atim should not change"); + + close_fd(file_fd); + cleanup_file(dir_fd, "file"); +} +fn main() { + let mut args = env::args(); + let prog = args.next().unwrap(); + let arg = if let Some(arg) = args.next() { + arg + } else { + eprintln!("usage: {} ", prog); + process::exit(1); + }; + + // Open scratch directory + let dir_fd = match open_scratch_directory(&arg) { + Ok(dir_fd) => dir_fd, + Err(err) => { + eprintln!("{}", err); + process::exit(1) + } + }; + + // Run the tests. + unsafe { test_path_filestat(dir_fd) } +} diff --git a/wasi-misc-tests/src/bin/path_open_dirfd_not_dir.rs b/wasi-misc-tests/src/bin/path_open_dirfd_not_dir.rs new file mode 100644 index 000000000000..2b99971d9c9a --- /dev/null +++ b/wasi-misc-tests/src/bin/path_open_dirfd_not_dir.rs @@ -0,0 +1,67 @@ +use std::{env, process}; +use wasi::wasi_unstable; +use wasi_misc_tests::open_scratch_directory; +use wasi_misc_tests::utils::close_fd; +use wasi_misc_tests::wasi_wrappers::wasi_path_open; + +unsafe fn test_dirfd_not_dir(dir_fd: wasi_unstable::Fd) { + // Open a file. + let mut file_fd: wasi_unstable::Fd = wasi_unstable::Fd::max_value() - 1; + let mut status = wasi_path_open( + dir_fd, + 0, + "file", + wasi_unstable::O_CREAT, + 0, + 0, + 0, + &mut file_fd, + ); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "opening a file" + ); + + // Now try to open a file underneath it as if it were a directory. + let mut new_file_fd: wasi_unstable::Fd = wasi_unstable::Fd::max_value() - 1; + status = wasi_path_open( + file_fd, + 0, + "foo", + wasi_unstable::O_CREAT, + 0, + 0, + 0, + &mut new_file_fd, + ); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ENOTDIR, + "non-directory base fd should get ENOTDIR" + ); + close_fd(file_fd); +} + +fn main() { + let mut args = env::args(); + let prog = args.next().unwrap(); + let arg = if let Some(arg) = args.next() { + arg + } else { + eprintln!("usage: {} ", prog); + process::exit(1); + }; + + // Open scratch directory + let dir_fd = match open_scratch_directory(&arg) { + Ok(dir_fd) => dir_fd, + Err(err) => { + eprintln!("{}", err); + process::exit(1) + } + }; + + // Run the tests. + unsafe { test_dirfd_not_dir(dir_fd) } +} diff --git a/wasi-misc-tests/src/bin/path_rename.rs b/wasi-misc-tests/src/bin/path_rename.rs new file mode 100644 index 000000000000..f7e5b7af444a --- /dev/null +++ b/wasi-misc-tests/src/bin/path_rename.rs @@ -0,0 +1,259 @@ +use libc; +use more_asserts::assert_gt; +use std::{env, process}; +use wasi::wasi_unstable; +use wasi_misc_tests::open_scratch_directory; +use wasi_misc_tests::utils::{cleanup_dir, cleanup_file, close_fd, create_dir, create_file}; +use wasi_misc_tests::wasi_wrappers::{wasi_path_open, wasi_path_rename}; + +unsafe fn test_path_rename(dir_fd: wasi_unstable::Fd) { + // First, try renaming a dir to nonexistent path + // Create source directory + create_dir(dir_fd, "source"); + + // Try renaming the directory + assert!( + wasi_path_rename(dir_fd, "source", dir_fd, "target").is_ok(), + "renaming a directory" + ); + + // Check that source directory doesn't exist anymore + let mut fd: wasi_unstable::Fd = wasi_unstable::Fd::max_value() - 1; + let mut status = wasi_path_open( + dir_fd, + 0, + "source", + wasi_unstable::O_DIRECTORY, + 0, + 0, + 0, + &mut fd, + ); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ENOENT, + "opening a nonexistent path as a directory" + ); + assert_eq!( + fd, + wasi_unstable::Fd::max_value(), + "failed open should set the file descriptor to -1", + ); + + // Check that target directory exists + status = wasi_path_open( + dir_fd, + 0, + "target", + wasi_unstable::O_DIRECTORY, + 0, + 0, + 0, + &mut fd, + ); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "opening renamed path as a directory" + ); + assert_gt!( + fd, + libc::STDERR_FILENO as wasi_unstable::Fd, + "file descriptor range check", + ); + + close_fd(fd); + cleanup_dir(dir_fd, "target"); + + // Now, try renaming renaming a dir to existing empty dir + create_dir(dir_fd, "source"); + create_dir(dir_fd, "target"); + + assert!( + wasi_path_rename(dir_fd, "source", dir_fd, "target").is_ok(), + "renaming a directory" + ); + + // Check that source directory doesn't exist anymore + fd = wasi_unstable::Fd::max_value() - 1; + status = wasi_path_open( + dir_fd, + 0, + "source", + wasi_unstable::O_DIRECTORY, + 0, + 0, + 0, + &mut fd, + ); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ENOENT, + "opening a nonexistent path as a directory" + ); + assert_eq!( + fd, + wasi_unstable::Fd::max_value(), + "failed open should set the file descriptor to -1", + ); + + // Check that target directory exists + status = wasi_path_open( + dir_fd, + 0, + "target", + wasi_unstable::O_DIRECTORY, + 0, + 0, + 0, + &mut fd, + ); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "opening renamed path as a directory" + ); + assert_gt!( + fd, + libc::STDERR_FILENO as wasi_unstable::Fd, + "file descriptor range check", + ); + + close_fd(fd); + cleanup_dir(dir_fd, "target"); + + // Now, try renaming a dir to existing non-empty dir + create_dir(dir_fd, "source"); + create_dir(dir_fd, "target"); + create_file(dir_fd, "target/file"); + + assert_eq!( + wasi_path_rename(dir_fd, "source", dir_fd, "target"), + Err(wasi_unstable::ENOTEMPTY), + "renaming directory to a nonempty directory" + ); + + // Try renaming dir to a file + assert_eq!( + wasi_path_rename(dir_fd, "source", dir_fd, "target/file"), + Err(wasi_unstable::ENOTDIR), + "renaming directory to a file" + ); + + cleanup_file(dir_fd, "target/file"); + cleanup_dir(dir_fd, "target"); + cleanup_dir(dir_fd, "source"); + + // Now, try renaming a file to a nonexistent path + create_file(dir_fd, "source"); + + assert!( + wasi_path_rename(dir_fd, "source", dir_fd, "target").is_ok(), + "renaming a file" + ); + + // Check that source file doesn't exist anymore + fd = wasi_unstable::Fd::max_value() - 1; + status = wasi_path_open(dir_fd, 0, "source", 0, 0, 0, 0, &mut fd); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ENOENT, + "opening a nonexistent path" + ); + assert_eq!( + fd, + wasi_unstable::Fd::max_value(), + "failed open should set the file descriptor to -1", + ); + + // Check that target file exists + status = wasi_path_open(dir_fd, 0, "target", 0, 0, 0, 0, &mut fd); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "opening renamed path" + ); + assert_gt!( + fd, + libc::STDERR_FILENO as wasi_unstable::Fd, + "file descriptor range check", + ); + + close_fd(fd); + cleanup_file(dir_fd, "target"); + + // Now, try renaming file to an existing file + create_file(dir_fd, "source"); + create_file(dir_fd, "target"); + + assert!( + wasi_path_rename(dir_fd, "source", dir_fd, "target").is_ok(), + "renaming file to another existing file" + ); + + // Check that source file doesn't exist anymore + fd = wasi_unstable::Fd::max_value() - 1; + status = wasi_path_open(dir_fd, 0, "source", 0, 0, 0, 0, &mut fd); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ENOENT, + "opening a nonexistent path" + ); + assert_eq!( + fd, + wasi_unstable::Fd::max_value(), + "failed open should set the file descriptor to -1", + ); + + // Check that target file exists + status = wasi_path_open(dir_fd, 0, "target", 0, 0, 0, 0, &mut fd); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "opening renamed path" + ); + assert_gt!( + fd, + libc::STDERR_FILENO as wasi_unstable::Fd, + "file descriptor range check", + ); + + close_fd(fd); + cleanup_file(dir_fd, "target"); + + // Try renaming to an (empty) directory instead + create_file(dir_fd, "source"); + create_dir(dir_fd, "target"); + + assert_eq!( + wasi_path_rename(dir_fd, "source", dir_fd, "target"), + Err(wasi_unstable::EISDIR), + "renaming file to existing directory" + ); + + cleanup_dir(dir_fd, "target"); + cleanup_file(dir_fd, "source"); +} + +fn main() { + let mut args = env::args(); + let prog = args.next().unwrap(); + let arg = if let Some(arg) = args.next() { + arg + } else { + eprintln!("usage: {} ", prog); + process::exit(1); + }; + + // Open scratch directory + let dir_fd = match open_scratch_directory(&arg) { + Ok(dir_fd) => dir_fd, + Err(err) => { + eprintln!("{}", err); + process::exit(1) + } + }; + + // Run the tests. + unsafe { test_path_rename(dir_fd) } +} diff --git a/wasi-misc-tests/src/bin/path_rename_trailing_slashes.rs b/wasi-misc-tests/src/bin/path_rename_trailing_slashes.rs new file mode 100644 index 000000000000..17a9785735ad --- /dev/null +++ b/wasi-misc-tests/src/bin/path_rename_trailing_slashes.rs @@ -0,0 +1,68 @@ +use std::{env, process}; +use wasi::wasi_unstable; +use wasi_misc_tests::open_scratch_directory; +use wasi_misc_tests::utils::{cleanup_dir, cleanup_file, create_dir, create_file}; +use wasi_misc_tests::wasi_wrappers::wasi_path_rename; + +unsafe fn test_path_rename_trailing_slashes(dir_fd: wasi_unstable::Fd) { + // Test renaming a file with a trailing slash in the name. + create_file(dir_fd, "source"); + assert_eq!( + wasi_path_rename(dir_fd, "source/", dir_fd, "target"), + Err(wasi_unstable::ENOTDIR), + "renaming a file with a trailing slash in the source name" + ); + assert_eq!( + wasi_path_rename(dir_fd, "source", dir_fd, "target/"), + Err(wasi_unstable::ENOTDIR), + "renaming a file with a trailing slash in the destination name" + ); + assert_eq!( + wasi_path_rename(dir_fd, "source/", dir_fd, "target/"), + Err(wasi_unstable::ENOTDIR), + "renaming a file with a trailing slash in the source and destination names" + ); + cleanup_file(dir_fd, "source"); + + // Test renaming a directory with a trailing slash in the name. + create_dir(dir_fd, "source"); + assert_eq!( + wasi_path_rename(dir_fd, "source/", dir_fd, "target"), + Ok(()), + "renaming a directory with a trailing slash in the source name" + ); + assert_eq!( + wasi_path_rename(dir_fd, "target", dir_fd, "source/"), + Ok(()), + "renaming a directory with a trailing slash in the destination name" + ); + assert_eq!( + wasi_path_rename(dir_fd, "source/", dir_fd, "target/"), + Ok(()), + "renaming a directory with a trailing slash in the source and destination names" + ); + cleanup_dir(dir_fd, "target"); +} + +fn main() { + let mut args = env::args(); + let prog = args.next().unwrap(); + let arg = if let Some(arg) = args.next() { + arg + } else { + eprintln!("usage: {} ", prog); + process::exit(1); + }; + + // Open scratch directory + let dir_fd = match open_scratch_directory(&arg) { + Ok(dir_fd) => dir_fd, + Err(err) => { + eprintln!("{}", err); + process::exit(1) + } + }; + + // Run the tests. + unsafe { test_path_rename_trailing_slashes(dir_fd) } +} diff --git a/wasi-misc-tests/src/bin/path_symlink_trailing_slashes.rs b/wasi-misc-tests/src/bin/path_symlink_trailing_slashes.rs new file mode 100644 index 000000000000..c10a61c7238a --- /dev/null +++ b/wasi-misc-tests/src/bin/path_symlink_trailing_slashes.rs @@ -0,0 +1,81 @@ +use std::{env, process}; +use wasi::wasi_unstable; +use wasi_misc_tests::open_scratch_directory; +use wasi_misc_tests::utils::{cleanup_dir, cleanup_file, create_dir, create_file}; +use wasi_misc_tests::wasi_wrappers::wasi_path_symlink; + +unsafe fn test_path_symlink_trailing_slashes(dir_fd: wasi_unstable::Fd) { + // Link destination shouldn't end with a slash. + assert_eq!( + wasi_path_symlink("source", dir_fd, "target/"), + Err(wasi_unstable::ENOENT), + "link destination ending with a slash" + ); + + // Without the trailing slash, this should succeed. + assert_eq!( + wasi_path_symlink("source", dir_fd, "target"), + Ok(()), + "link destination ending with a slash" + ); + cleanup_file(dir_fd, "target"); + + // Link destination already exists, target has trailing slash. + create_dir(dir_fd, "target"); + assert_eq!( + wasi_path_symlink("source", dir_fd, "target/"), + Err(wasi_unstable::EEXIST), + "link destination already exists" + ); + cleanup_dir(dir_fd, "target"); + + // Link destination already exists, target has no trailing slash. + create_dir(dir_fd, "target"); + assert_eq!( + wasi_path_symlink("source", dir_fd, "target"), + Err(wasi_unstable::EEXIST), + "link destination already exists" + ); + cleanup_dir(dir_fd, "target"); + + // Link destination already exists, target has trailing slash. + create_file(dir_fd, "target"); + assert_eq!( + wasi_path_symlink("source", dir_fd, "target/"), + Err(wasi_unstable::EEXIST), + "link destination already exists" + ); + cleanup_file(dir_fd, "target"); + + // Link destination already exists, target has no trailing slash. + create_file(dir_fd, "target"); + assert_eq!( + wasi_path_symlink("source", dir_fd, "target"), + Err(wasi_unstable::EEXIST), + "link destination already exists" + ); + cleanup_file(dir_fd, "target"); +} + +fn main() { + let mut args = env::args(); + let prog = args.next().unwrap(); + let arg = if let Some(arg) = args.next() { + arg + } else { + eprintln!("usage: {} ", prog); + process::exit(1); + }; + + // Open scratch directory + let dir_fd = match open_scratch_directory(&arg) { + Ok(dir_fd) => dir_fd, + Err(err) => { + eprintln!("{}", err); + process::exit(1) + } + }; + + // Run the tests. + unsafe { test_path_symlink_trailing_slashes(dir_fd) } +} diff --git a/wasi-misc-tests/src/bin/poll_oneoff.rs b/wasi-misc-tests/src/bin/poll_oneoff.rs new file mode 100644 index 000000000000..c303c08926b3 --- /dev/null +++ b/wasi-misc-tests/src/bin/poll_oneoff.rs @@ -0,0 +1,271 @@ +use libc; +use more_asserts::assert_gt; +use std::{env, mem::MaybeUninit, process}; +use wasi::wasi_unstable; +use wasi_misc_tests::{ + open_scratch_directory, + utils::{cleanup_file, close_fd}, + wasi_wrappers::wasi_path_open, +}; + +const CLOCK_ID: wasi_unstable::Userdata = 0x0123_45678; + +unsafe fn poll_oneoff_impl( + in_: &[wasi_unstable::Subscription], + nexpected: usize, +) -> Vec { + let mut out: Vec = Vec::new(); + out.resize_with(in_.len(), || { + MaybeUninit::::zeroed().assume_init() + }); + let res = wasi_unstable::poll_oneoff(&in_, out.as_mut_slice()); + let res = res.expect("poll_oneoff should succeed"); + assert_eq!( + res, nexpected, + "poll_oneoff should return {} events", + nexpected + ); + out +} + +unsafe fn test_timeout() { + let clock = wasi_unstable::raw::__wasi_subscription_u_clock_t { + identifier: CLOCK_ID, + clock_id: wasi_unstable::CLOCK_MONOTONIC, + timeout: 5_000_000u64, // 5 milliseconds + precision: 0, + flags: 0, + }; + let in_ = [wasi_unstable::Subscription { + userdata: CLOCK_ID, + type_: wasi_unstable::EVENTTYPE_CLOCK, + u: wasi_unstable::raw::__wasi_subscription_u { clock }, + }]; + let out = poll_oneoff_impl(&in_, 1); + let event = &out[0]; + assert_eq!( + event.userdata, CLOCK_ID, + "the event.userdata should contain clock_id specified by the user" + ); + assert_eq!( + event.error, + wasi_unstable::raw::__WASI_ESUCCESS, + "the event.error should be set to ESUCCESS" + ); + assert_eq!( + event.type_, + wasi_unstable::EVENTTYPE_CLOCK, + "the event.type_ should equal clock" + ); +} + +unsafe fn test_stdin_read() { + let clock = wasi_unstable::raw::__wasi_subscription_u_clock_t { + identifier: CLOCK_ID, + clock_id: wasi_unstable::CLOCK_MONOTONIC, + timeout: 5_000_000u64, // 5 milliseconds + precision: 0, + flags: 0, + }; + let fd_readwrite = wasi_unstable::raw::__wasi_subscription_u_fd_readwrite_t { + fd: wasi_unstable::STDIN_FD, + }; + let in_ = [ + wasi_unstable::Subscription { + userdata: CLOCK_ID, + type_: wasi_unstable::EVENTTYPE_CLOCK, + u: wasi_unstable::raw::__wasi_subscription_u { clock }, + }, + wasi_unstable::Subscription { + userdata: 1, + type_: wasi_unstable::EVENTTYPE_FD_READ, + u: wasi_unstable::raw::__wasi_subscription_u { fd_readwrite }, + }, + ]; + let out = poll_oneoff_impl(&in_, 1); + let event = &out[0]; + assert_eq!( + event.userdata, CLOCK_ID, + "the event.userdata should contain clock_id specified by the user" + ); + assert_eq!( + event.error, + wasi_unstable::raw::__WASI_ESUCCESS, + "the event.error should be set to ESUCCESS" + ); + assert_eq!( + event.type_, + wasi_unstable::EVENTTYPE_CLOCK, + "the event.type_ should equal clock" + ); +} + +unsafe fn test_stdout_stderr_write() { + let stdout_readwrite = wasi_unstable::raw::__wasi_subscription_u_fd_readwrite_t { + fd: wasi_unstable::STDOUT_FD, + }; + let stderr_readwrite = wasi_unstable::raw::__wasi_subscription_u_fd_readwrite_t { + fd: wasi_unstable::STDERR_FD, + }; + let in_ = [ + wasi_unstable::Subscription { + userdata: 1, + type_: wasi_unstable::EVENTTYPE_FD_WRITE, + u: wasi_unstable::raw::__wasi_subscription_u { + fd_readwrite: stdout_readwrite, + }, + }, + wasi_unstable::Subscription { + userdata: 2, + type_: wasi_unstable::EVENTTYPE_FD_WRITE, + u: wasi_unstable::raw::__wasi_subscription_u { + fd_readwrite: stderr_readwrite, + }, + }, + ]; + let out = poll_oneoff_impl(&in_, 2); + assert_eq!( + out[0].userdata, 1, + "the event.userdata should contain fd userdata specified by the user" + ); + assert_eq!( + out[0].error, + wasi_unstable::raw::__WASI_ESUCCESS, + "the event.error should be set to {}", + wasi_unstable::raw::__WASI_ESUCCESS + ); + assert_eq!( + out[0].type_, + wasi_unstable::EVENTTYPE_FD_WRITE, + "the event.type_ should equal FD_WRITE" + ); + assert_eq!( + out[1].userdata, 2, + "the event.userdata should contain fd userdata specified by the user" + ); + assert_eq!( + out[1].error, + wasi_unstable::raw::__WASI_ESUCCESS, + "the event.error should be set to {}", + wasi_unstable::raw::__WASI_ESUCCESS + ); + assert_eq!( + out[1].type_, + wasi_unstable::EVENTTYPE_FD_WRITE, + "the event.type_ should equal FD_WRITE" + ); +} + +unsafe fn test_fd_readwrite(fd: wasi_unstable::Fd, error_code: wasi_unstable::raw::__wasi_errno_t) { + let fd_readwrite = wasi_unstable::raw::__wasi_subscription_u_fd_readwrite_t { fd }; + let in_ = [ + wasi_unstable::Subscription { + userdata: 1, + type_: wasi_unstable::EVENTTYPE_FD_READ, + u: wasi_unstable::raw::__wasi_subscription_u { fd_readwrite }, + }, + wasi_unstable::Subscription { + userdata: 2, + type_: wasi_unstable::EVENTTYPE_FD_WRITE, + u: wasi_unstable::raw::__wasi_subscription_u { fd_readwrite }, + }, + ]; + let out = poll_oneoff_impl(&in_, 2); + assert_eq!( + out[0].userdata, 1, + "the event.userdata should contain fd userdata specified by the user" + ); + assert_eq!( + out[0].error, error_code, + "the event.error should be set to {}", + error_code + ); + assert_eq!( + out[0].type_, + wasi_unstable::EVENTTYPE_FD_READ, + "the event.type_ should equal FD_READ" + ); + assert_eq!( + out[1].userdata, 2, + "the event.userdata should contain fd userdata specified by the user" + ); + assert_eq!( + out[1].error, error_code, + "the event.error should be set to {}", + error_code + ); + assert_eq!( + out[1].type_, + wasi_unstable::EVENTTYPE_FD_WRITE, + "the event.type_ should equal FD_WRITE" + ); +} + +unsafe fn test_fd_readwrite_valid_fd(dir_fd: wasi_unstable::Fd) { + // Create a file in the scratch directory. + let mut file_fd = wasi_unstable::Fd::max_value() - 1; + let status = wasi_path_open( + dir_fd, + 0, + "file", + wasi_unstable::O_CREAT, + wasi_unstable::RIGHT_FD_READ | wasi_unstable::RIGHT_FD_WRITE, + 0, + 0, + &mut file_fd, + ); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "opening a file" + ); + assert_gt!( + file_fd, + libc::STDERR_FILENO as wasi_unstable::Fd, + "file descriptor range check", + ); + + test_fd_readwrite(file_fd, wasi_unstable::raw::__WASI_ESUCCESS); + + close_fd(file_fd); + cleanup_file(dir_fd, "file"); +} + +unsafe fn test_fd_readwrite_invalid_fd() { + test_fd_readwrite( + wasi_unstable::Fd::max_value(), + wasi_unstable::raw::__WASI_EBADF, + ) +} + +unsafe fn test_poll_oneoff(dir_fd: wasi_unstable::Fd) { + test_timeout(); + // NB we assume that stdin/stdout/stderr are valid and open + // for the duration of the test case + test_stdin_read(); + test_stdout_stderr_write(); + test_fd_readwrite_valid_fd(dir_fd); + test_fd_readwrite_invalid_fd(); +} +fn main() { + let mut args = env::args(); + let prog = args.next().unwrap(); + let arg = if let Some(arg) = args.next() { + arg + } else { + eprintln!("usage: {} ", prog); + process::exit(1); + }; + + // Open scratch directory + let dir_fd = match open_scratch_directory(&arg) { + Ok(dir_fd) => dir_fd, + Err(err) => { + eprintln!("{}", err); + process::exit(1) + } + }; + + // Run the tests. + unsafe { test_poll_oneoff(dir_fd) } +} diff --git a/wasi-misc-tests/src/bin/readlink.rs b/wasi-misc-tests/src/bin/readlink.rs new file mode 100644 index 000000000000..23ee02d56b8c --- /dev/null +++ b/wasi-misc-tests/src/bin/readlink.rs @@ -0,0 +1,72 @@ +use std::{env, process}; +use wasi::wasi_unstable; +use wasi_misc_tests::open_scratch_directory; +use wasi_misc_tests::utils::{cleanup_file, create_file}; +use wasi_misc_tests::wasi_wrappers::{wasi_path_readlink, wasi_path_symlink}; + +unsafe fn test_readlink(dir_fd: wasi_unstable::Fd) { + // Create a file in the scratch directory. + create_file(dir_fd, "target"); + + // Create a symlink + assert!( + wasi_path_symlink("target", dir_fd, "symlink").is_ok(), + "creating a symlink" + ); + + // Read link into the buffer + let buf = &mut [0u8; 10]; + let mut bufused: usize = 0; + let mut status = wasi_path_readlink(dir_fd, "symlink", buf, &mut bufused); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "readlink should succeed" + ); + assert_eq!(bufused, 6, "should use 6 bytes of the buffer"); + assert_eq!(&buf[..6], b"target", "buffer should contain 'target'"); + assert_eq!( + &buf[6..], + &[0u8; 4], + "the remaining bytes should be untouched" + ); + + // Read link into smaller buffer than the actual link's length + let buf = &mut [0u8; 4]; + let mut bufused: usize = 0; + status = wasi_path_readlink(dir_fd, "symlink", buf, &mut bufused); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "readlink should succeed" + ); + assert_eq!(bufused, 4, "should use all 4 bytes of the buffer"); + assert_eq!(buf, b"targ", "buffer should contain 'targ'"); + + // Clean up. + cleanup_file(dir_fd, "target"); + cleanup_file(dir_fd, "symlink"); +} + +fn main() { + let mut args = env::args(); + let prog = args.next().unwrap(); + let arg = if let Some(arg) = args.next() { + arg + } else { + eprintln!("usage: {} ", prog); + process::exit(1); + }; + + // Open scratch directory + let dir_fd = match open_scratch_directory(&arg) { + Ok(dir_fd) => dir_fd, + Err(err) => { + eprintln!("{}", err); + process::exit(1) + } + }; + + // Run the tests. + unsafe { test_readlink(dir_fd) } +} diff --git a/wasi-misc-tests/src/bin/readlink_no_buffer.rs b/wasi-misc-tests/src/bin/readlink_no_buffer.rs new file mode 100644 index 000000000000..695aff69519e --- /dev/null +++ b/wasi-misc-tests/src/bin/readlink_no_buffer.rs @@ -0,0 +1,51 @@ +use std::{env, process}; +use wasi::wasi_unstable; +use wasi_misc_tests::open_scratch_directory; +use wasi_misc_tests::utils::cleanup_file; +use wasi_misc_tests::wasi_wrappers::{wasi_path_readlink, wasi_path_symlink}; + +unsafe fn test_readlink_no_buffer(dir_fd: wasi_unstable::Fd) { + // First create a dangling symlink. + assert!( + wasi_path_symlink("target", dir_fd, "symlink").is_ok(), + "creating a symlink" + ); + + // Readlink it into a non-existent buffer. + let mut bufused: usize = 1; + let status = wasi_path_readlink(dir_fd, "symlink", &mut [], &mut bufused); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "readlink with a 0-sized buffer should succeed" + ); + assert_eq!( + bufused, 0, + "readlink with a 0-sized buffer should return 'bufused' 0" + ); + + // Clean up. + cleanup_file(dir_fd, "symlink"); +} +fn main() { + let mut args = env::args(); + let prog = args.next().unwrap(); + let arg = if let Some(arg) = args.next() { + arg + } else { + eprintln!("usage: {} ", prog); + process::exit(1); + }; + + // Open scratch directory + let dir_fd = match open_scratch_directory(&arg) { + Ok(dir_fd) => dir_fd, + Err(err) => { + eprintln!("{}", err); + process::exit(1) + } + }; + + // Run the tests. + unsafe { test_readlink_no_buffer(dir_fd) } +} diff --git a/wasi-misc-tests/src/bin/remove_directory_trailing_slashes.rs b/wasi-misc-tests/src/bin/remove_directory_trailing_slashes.rs new file mode 100644 index 000000000000..85f83eb0fd70 --- /dev/null +++ b/wasi-misc-tests/src/bin/remove_directory_trailing_slashes.rs @@ -0,0 +1,68 @@ +use std::{env, process}; +use wasi::wasi_unstable; +use wasi_misc_tests::open_scratch_directory; +use wasi_misc_tests::utils::{cleanup_file, create_dir, create_file}; +use wasi_misc_tests::wasi_wrappers::wasi_path_remove_directory; + +unsafe fn test_remove_directory_trailing_slashes(dir_fd: wasi_unstable::Fd) { + // Create a directory in the scratch directory. + create_dir(dir_fd, "dir"); + + // Test that removing it succeeds. + assert_eq!( + wasi_path_remove_directory(dir_fd, "dir"), + Ok(()), + "remove_directory on a directory should succeed" + ); + + create_dir(dir_fd, "dir"); + + // Test that removing it with a trailing flash succeeds. + assert_eq!( + wasi_path_remove_directory(dir_fd, "dir/"), + Ok(()), + "remove_directory with a trailing slash on a directory should succeed" + ); + + // Create a temporary file. + create_file(dir_fd, "file"); + + // Test that removing it with no trailing flash fails. + assert_eq!( + wasi_path_remove_directory(dir_fd, "file"), + Err(wasi_unstable::ENOTDIR), + "remove_directory without a trailing slash on a file should fail" + ); + + // Test that removing it with a trailing flash fails. + assert_eq!( + wasi_path_remove_directory(dir_fd, "file/"), + Err(wasi_unstable::ENOTDIR), + "remove_directory with a trailing slash on a file should fail" + ); + + cleanup_file(dir_fd, "file"); +} + +fn main() { + let mut args = env::args(); + let prog = args.next().unwrap(); + let arg = if let Some(arg) = args.next() { + arg + } else { + eprintln!("usage: {} ", prog); + process::exit(1); + }; + + // Open scratch directory + let dir_fd = match open_scratch_directory(&arg) { + Ok(dir_fd) => dir_fd, + Err(err) => { + eprintln!("{}", err); + process::exit(1) + } + }; + + // Run the tests. + unsafe { test_remove_directory_trailing_slashes(dir_fd) } +} diff --git a/wasi-misc-tests/src/bin/remove_nonempty_directory.rs b/wasi-misc-tests/src/bin/remove_nonempty_directory.rs new file mode 100644 index 000000000000..8f990d69583e --- /dev/null +++ b/wasi-misc-tests/src/bin/remove_nonempty_directory.rs @@ -0,0 +1,50 @@ +use std::{env, process}; +use wasi::wasi_unstable; +use wasi_misc_tests::open_scratch_directory; +use wasi_misc_tests::utils::{cleanup_dir, create_dir}; +use wasi_misc_tests::wasi_wrappers::wasi_path_remove_directory; + +unsafe fn test_remove_nonempty_directory(dir_fd: wasi_unstable::Fd) { + // Create a directory in the scratch directory. + create_dir(dir_fd, "dir"); + + // Create a directory in the directory we just created. + create_dir(dir_fd, "dir/nested"); + + // Test that attempting to unlink the first directory returns the expected error code. + assert_eq!( + wasi_path_remove_directory(dir_fd, "dir"), + Err(wasi_unstable::ENOTEMPTY), + "remove_directory on a directory should return ENOTEMPTY", + ); + + // Removing the directories. + assert!( + wasi_path_remove_directory(dir_fd, "dir/nested").is_ok(), + "remove_directory on a nested directory should succeed", + ); + cleanup_dir(dir_fd, "dir"); +} + +fn main() { + let mut args = env::args(); + let prog = args.next().unwrap(); + let arg = if let Some(arg) = args.next() { + arg + } else { + eprintln!("usage: {} ", prog); + process::exit(1); + }; + + // Open scratch directory + let dir_fd = match open_scratch_directory(&arg) { + Ok(dir_fd) => dir_fd, + Err(err) => { + eprintln!("{}", err); + process::exit(1) + } + }; + + // Run the tests. + unsafe { test_remove_nonempty_directory(dir_fd) } +} diff --git a/wasi-misc-tests/src/bin/renumber.rs b/wasi-misc-tests/src/bin/renumber.rs new file mode 100644 index 000000000000..7e4a097c0a1d --- /dev/null +++ b/wasi-misc-tests/src/bin/renumber.rs @@ -0,0 +1,131 @@ +use libc; +use more_asserts::assert_gt; +use std::{env, mem, process}; +use wasi::wasi_unstable; +use wasi_misc_tests::open_scratch_directory; +use wasi_misc_tests::utils::close_fd; +use wasi_misc_tests::wasi_wrappers::{wasi_fd_fdstat_get, wasi_path_open}; + +unsafe fn test_renumber(dir_fd: wasi_unstable::Fd) { + let pre_fd: wasi_unstable::Fd = (libc::STDERR_FILENO + 1) as wasi_unstable::Fd; + + assert_gt!(dir_fd, pre_fd, "dir_fd number"); + + // Create a file in the scratch directory. + let mut fd_from = wasi_unstable::Fd::max_value() - 1; + let mut status = wasi_path_open( + dir_fd, + 0, + "file1", + wasi_unstable::O_CREAT, + wasi_unstable::RIGHT_FD_READ | wasi_unstable::RIGHT_FD_WRITE, + 0, + 0, + &mut fd_from, + ); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "opening a file" + ); + assert_gt!( + fd_from, + libc::STDERR_FILENO as wasi_unstable::Fd, + "file descriptor range check", + ); + + // Get fd_from fdstat attributes + let mut fdstat_from: wasi_unstable::FdStat = mem::zeroed(); + status = wasi_fd_fdstat_get(fd_from, &mut fdstat_from); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "calling fd_fdstat on the open file descriptor" + ); + + // Create another file in the scratch directory. + let mut fd_to = wasi_unstable::Fd::max_value() - 1; + status = wasi_path_open( + dir_fd, + 0, + "file2", + wasi_unstable::O_CREAT, + wasi_unstable::RIGHT_FD_READ | wasi_unstable::RIGHT_FD_WRITE, + 0, + 0, + &mut fd_to, + ); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "opening a file" + ); + assert_gt!( + fd_to, + libc::STDERR_FILENO as wasi_unstable::Fd, + "file descriptor range check", + ); + + // Renumber fd of file1 into fd of file2 + assert!( + wasi_unstable::fd_renumber(fd_from, fd_to).is_ok(), + "renumbering two descriptors", + ); + + // Ensure that fd_from is closed + assert_eq!( + wasi_unstable::fd_close(fd_from), + Err(wasi_unstable::EBADF), + "closing already closed file descriptor" + ); + + // Ensure that fd_to is still open. + let mut fdstat_to: wasi_unstable::FdStat = mem::zeroed(); + status = wasi_fd_fdstat_get(fd_to, &mut fdstat_to); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "calling fd_fdstat on the open file descriptor" + ); + assert_eq!( + fdstat_from.fs_filetype, fdstat_to.fs_filetype, + "expected fd_to have the same fdstat as fd_from" + ); + assert_eq!( + fdstat_from.fs_flags, fdstat_to.fs_flags, + "expected fd_to have the same fdstat as fd_from" + ); + assert_eq!( + fdstat_from.fs_rights_base, fdstat_to.fs_rights_base, + "expected fd_to have the same fdstat as fd_from" + ); + assert_eq!( + fdstat_from.fs_rights_inheriting, fdstat_to.fs_rights_inheriting, + "expected fd_to have the same fdstat as fd_from" + ); + + close_fd(fd_to); +} + +fn main() { + let mut args = env::args(); + let prog = args.next().unwrap(); + let arg = if let Some(arg) = args.next() { + arg + } else { + eprintln!("usage: {} ", prog); + process::exit(1); + }; + + // Open scratch directory + let dir_fd = match open_scratch_directory(&arg) { + Ok(dir_fd) => dir_fd, + Err(err) => { + eprintln!("{}", err); + process::exit(1) + } + }; + + // Run the tests. + unsafe { test_renumber(dir_fd) } +} diff --git a/wasi-misc-tests/src/bin/sched_yield.rs b/wasi-misc-tests/src/bin/sched_yield.rs new file mode 100644 index 000000000000..d498f8849505 --- /dev/null +++ b/wasi-misc-tests/src/bin/sched_yield.rs @@ -0,0 +1,10 @@ +use wasi::wasi_unstable; + +fn test_sched_yield() { + assert!(wasi_unstable::sched_yield().is_ok(), "sched_yield"); +} + +fn main() { + // Run tests + test_sched_yield() +} diff --git a/wasi-misc-tests/src/bin/symlink_loop.rs b/wasi-misc-tests/src/bin/symlink_loop.rs new file mode 100644 index 000000000000..6972f5ad625f --- /dev/null +++ b/wasi-misc-tests/src/bin/symlink_loop.rs @@ -0,0 +1,47 @@ +use std::{env, process}; +use wasi::wasi_unstable; +use wasi_misc_tests::open_scratch_directory; +use wasi_misc_tests::utils::cleanup_file; +use wasi_misc_tests::wasi_wrappers::{wasi_path_open, wasi_path_symlink}; + +unsafe fn test_symlink_loop(dir_fd: wasi_unstable::Fd) { + // Create a self-referencing symlink. + assert!( + wasi_path_symlink("symlink", dir_fd, "symlink").is_ok(), + "creating a symlink" + ); + + // Try to open it. + let mut file_fd: wasi_unstable::Fd = wasi_unstable::Fd::max_value() - 1; + assert_eq!( + wasi_path_open(dir_fd, 0, "symlink", 0, 0, 0, 0, &mut file_fd), + wasi_unstable::raw::__WASI_ELOOP, + "opening a self-referencing symlink", + ); + + // Clean up. + cleanup_file(dir_fd, "symlink"); +} + +fn main() { + let mut args = env::args(); + let prog = args.next().unwrap(); + let arg = if let Some(arg) = args.next() { + arg + } else { + eprintln!("usage: {} ", prog); + process::exit(1); + }; + + // Open scratch directory + let dir_fd = match open_scratch_directory(&arg) { + Ok(dir_fd) => dir_fd, + Err(err) => { + eprintln!("{}", err); + process::exit(1) + } + }; + + // Run the tests. + unsafe { test_symlink_loop(dir_fd) } +} diff --git a/wasi-misc-tests/src/bin/truncation_rights.rs b/wasi-misc-tests/src/bin/truncation_rights.rs new file mode 100644 index 000000000000..e400c4e2a51b --- /dev/null +++ b/wasi-misc-tests/src/bin/truncation_rights.rs @@ -0,0 +1,157 @@ +use std::{env, mem, process}; +use wasi::wasi_unstable; +use wasi_misc_tests::open_scratch_directory; +use wasi_misc_tests::utils::{cleanup_file, close_fd, create_file}; +use wasi_misc_tests::wasi_wrappers::{wasi_fd_fdstat_get, wasi_path_open}; + +unsafe fn test_truncation_rights(dir_fd: wasi_unstable::Fd) { + // Create a file in the scratch directory. + create_file(dir_fd, "file"); + + // Get the rights for the scratch directory. + let mut dir_fdstat: wasi_unstable::FdStat = mem::zeroed(); + let mut status = wasi_fd_fdstat_get(dir_fd, &mut dir_fdstat); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "calling fd_fdstat on the scratch directory" + ); + assert_eq!( + dir_fdstat.fs_filetype, + wasi_unstable::FILETYPE_DIRECTORY, + "expected the scratch directory to be a directory", + ); + assert_eq!( + dir_fdstat.fs_flags, 0, + "expected the scratch directory to have no special flags", + ); + assert_eq!( + dir_fdstat.fs_rights_base & wasi_unstable::RIGHT_FD_FILESTAT_SET_SIZE, + 0, + "directories shouldn't have the fd_filestat_set_size right", + ); + + // If we have the right to set sizes from paths, test that it works. + if (dir_fdstat.fs_rights_base & wasi_unstable::RIGHT_PATH_FILESTAT_SET_SIZE) == 0 { + eprintln!("implementation doesn't support setting file sizes, skipping"); + } else { + // Test that we can truncate the file. + let mut file_fd: wasi_unstable::Fd = wasi_unstable::Fd::max_value() - 1; + status = wasi_path_open( + dir_fd, + 0, + "file", + wasi_unstable::O_TRUNC, + 0, + 0, + 0, + &mut file_fd, + ); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "truncating a file" + ); + close_fd(file_fd); + + let mut rights_base: wasi_unstable::Rights = dir_fdstat.fs_rights_base; + let mut rights_inheriting: wasi_unstable::Rights = dir_fdstat.fs_rights_inheriting; + + if (rights_inheriting & wasi_unstable::RIGHT_FD_FILESTAT_SET_SIZE) == 0 { + eprintln!("implementation doesn't support setting file sizes through file descriptors, skipping"); + } else { + rights_inheriting &= !wasi_unstable::RIGHT_FD_FILESTAT_SET_SIZE; + assert!( + wasi_unstable::fd_fdstat_set_rights(dir_fd, rights_base, rights_inheriting).is_ok(), + "droping fd_filestat_set_size inheriting right on a directory", + ); + } + + // Test that we can truncate the file without the + // wasi_unstable::RIGHT_FD_FILESTAT_SET_SIZE right. + status = wasi_path_open( + dir_fd, + 0, + "file", + wasi_unstable::O_TRUNC, + 0, + 0, + 0, + &mut file_fd, + ); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "truncating a file without fd_filestat_set_size right", + ); + close_fd(file_fd); + + rights_base &= !wasi_unstable::RIGHT_PATH_FILESTAT_SET_SIZE; + assert!( + wasi_unstable::fd_fdstat_set_rights(dir_fd, rights_base, rights_inheriting).is_ok(), + "droping path_filestat_set_size base right on a directory", + ); + + // Test that clearing wasi_unstable::RIGHT_PATH_FILESTAT_SET_SIZE actually + // took effect. + status = wasi_fd_fdstat_get(dir_fd, &mut dir_fdstat); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "reading the fdstat from a directory", + ); + assert_eq!( + (dir_fdstat.fs_rights_base & wasi_unstable::RIGHT_PATH_FILESTAT_SET_SIZE), + 0, + "reading the fdstat from a directory", + ); + + // Test that we can't truncate the file without the + // wasi_unstable::RIGHT_PATH_FILESTAT_SET_SIZE right. + status = wasi_path_open( + dir_fd, + 0, + "file", + wasi_unstable::O_TRUNC, + 0, + 0, + 0, + &mut file_fd, + ); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ENOTCAPABLE, + "truncating a file without path_filestat_set_size right", + ); + assert_eq!( + file_fd, + wasi_unstable::Fd::max_value(), + "failed open should set the file descriptor to -1", + ); + } + + cleanup_file(dir_fd, "file"); +} + +fn main() { + let mut args = env::args(); + let prog = args.next().unwrap(); + let arg = if let Some(arg) = args.next() { + arg + } else { + eprintln!("usage: {} ", prog); + process::exit(1); + }; + + // Open scratch directory + let dir_fd = match open_scratch_directory(&arg) { + Ok(dir_fd) => dir_fd, + Err(err) => { + eprintln!("{}", err); + process::exit(1) + } + }; + + // Run the tests. + unsafe { test_truncation_rights(dir_fd) } +} diff --git a/wasi-misc-tests/src/bin/unlink_file_trailing_slashes.rs b/wasi-misc-tests/src/bin/unlink_file_trailing_slashes.rs new file mode 100644 index 000000000000..5d7b3aa69a30 --- /dev/null +++ b/wasi-misc-tests/src/bin/unlink_file_trailing_slashes.rs @@ -0,0 +1,67 @@ +use std::{env, process}; +use wasi::wasi_unstable; +use wasi_misc_tests::open_scratch_directory; +use wasi_misc_tests::utils::{cleanup_dir, create_dir, create_file}; +use wasi_misc_tests::wasi_wrappers::wasi_path_unlink_file; + +unsafe fn test_unlink_file_trailing_slashes(dir_fd: wasi_unstable::Fd) { + // Create a directory in the scratch directory. + create_dir(dir_fd, "dir"); + + // Test that unlinking it fails. + assert_eq!( + wasi_path_unlink_file(dir_fd, "dir"), + Err(wasi_unstable::EISDIR), + "unlink_file on a directory should fail" + ); + + // Test that unlinking it with a trailing flash fails. + assert_eq!( + wasi_path_unlink_file(dir_fd, "dir/"), + Err(wasi_unstable::EISDIR), + "unlink_file on a directory should fail" + ); + + // Clean up. + cleanup_dir(dir_fd, "dir"); + + // Create a temporary file. + create_file(dir_fd, "file"); + + // Test that unlinking it with a trailing flash fails. + assert_eq!( + wasi_path_unlink_file(dir_fd, "file/"), + Err(wasi_unstable::ENOTDIR), + "unlink_file with a trailing slash should fail" + ); + + // Test that unlinking it with no trailing flash succeeds. + assert_eq!( + wasi_path_unlink_file(dir_fd, "file"), + Ok(()), + "unlink_file with no trailing slash should succeed" + ); +} + +fn main() { + let mut args = env::args(); + let prog = args.next().unwrap(); + let arg = if let Some(arg) = args.next() { + arg + } else { + eprintln!("usage: {} ", prog); + process::exit(1); + }; + + // Open scratch directory + let dir_fd = match open_scratch_directory(&arg) { + Ok(dir_fd) => dir_fd, + Err(err) => { + eprintln!("{}", err); + process::exit(1) + } + }; + + // Run the tests. + unsafe { test_unlink_file_trailing_slashes(dir_fd) } +} diff --git a/wasi-misc-tests/src/lib.rs b/wasi-misc-tests/src/lib.rs new file mode 100644 index 000000000000..5960d09f5c8a --- /dev/null +++ b/wasi-misc-tests/src/lib.rs @@ -0,0 +1,25 @@ +pub mod utils; +pub mod wasi_wrappers; + +use libc; +use std::ffi::CString; +use std::io; +use wasi::wasi_unstable; + +pub fn open_scratch_directory(path: &str) -> Result { + // Open the scratch directory. + let dir_fd: wasi_unstable::Fd = unsafe { + let cstr = CString::new(path.as_bytes()).unwrap(); + libc::open(cstr.as_ptr(), libc::O_RDONLY | libc::O_DIRECTORY) + } as wasi_unstable::Fd; + + if (dir_fd as std::os::raw::c_int) < 0 { + Err(format!( + "error opening scratch directory '{}': {}", + path, + io::Error::last_os_error() + )) + } else { + Ok(dir_fd) + } +} diff --git a/wasi-misc-tests/src/utils.rs b/wasi-misc-tests/src/utils.rs new file mode 100644 index 000000000000..b9c1f1620c19 --- /dev/null +++ b/wasi-misc-tests/src/utils.rs @@ -0,0 +1,54 @@ +use crate::wasi_wrappers::*; +use more_asserts::assert_gt; +use wasi::wasi_unstable; + +pub unsafe fn create_dir(dir_fd: wasi_unstable::Fd, dir_name: &str) { + assert!( + wasi_path_create_directory(dir_fd, dir_name).is_ok(), + "creating a directory" + ); +} + +pub unsafe fn cleanup_dir(dir_fd: wasi_unstable::Fd, dir_name: &str) { + assert!( + wasi_path_remove_directory(dir_fd, dir_name).is_ok(), + "remove_directory on an empty directory should succeed" + ); +} + +/// Create an empty file with the given name. +pub unsafe fn create_file(dir_fd: wasi_unstable::Fd, file_name: &str) { + let mut file_fd = wasi_unstable::Fd::max_value() - 1; + let status = wasi_path_open( + dir_fd, + 0, + file_name, + wasi_unstable::O_CREAT, + 0, + 0, + 0, + &mut file_fd, + ); + assert_eq!( + status, + wasi_unstable::raw::__WASI_ESUCCESS, + "creating a file" + ); + assert_gt!( + file_fd, + libc::STDERR_FILENO as wasi_unstable::Fd, + "file descriptor range check", + ); + close_fd(file_fd); +} + +pub unsafe fn cleanup_file(dir_fd: wasi_unstable::Fd, file_name: &str) { + assert!( + wasi_path_unlink_file(dir_fd, file_name).is_ok(), + "unlink_file on a symlink should succeed" + ); +} + +pub unsafe fn close_fd(fd: wasi_unstable::Fd) { + assert!(wasi_unstable::fd_close(fd).is_ok(), "closing a file"); +} diff --git a/wasi-misc-tests/src/wasi_wrappers.rs b/wasi-misc-tests/src/wasi_wrappers.rs new file mode 100644 index 000000000000..584dfb09c0e6 --- /dev/null +++ b/wasi-misc-tests/src/wasi_wrappers.rs @@ -0,0 +1,217 @@ +//! Minimal wrappers around WASI functions to allow use of `&str` rather than +//! pointer-length pairs. +//! +//! Where possible, we use the idiomatic wasi_unstable wrappers rather than the +//! raw interfaces, however for functions with out parameters, we use the raw +//! interfaces so that we can test whether they are stored to. In the future, +//! WASI should switch to multi-value and eliminate out parameters altogether. + +use wasi::wasi_unstable; + +pub unsafe fn wasi_path_create_directory( + dir_fd: wasi_unstable::Fd, + dir_name: &str, +) -> Result<(), wasi_unstable::Error> { + wasi_unstable::path_create_directory(dir_fd, dir_name.as_bytes()) +} + +pub unsafe fn wasi_path_remove_directory( + dir_fd: wasi_unstable::Fd, + dir_name: &str, +) -> Result<(), wasi_unstable::Error> { + wasi_unstable::path_remove_directory(dir_fd, dir_name.as_bytes()) +} + +pub unsafe fn wasi_path_unlink_file( + dir_fd: wasi_unstable::Fd, + file_name: &str, +) -> Result<(), wasi_unstable::Error> { + wasi_unstable::path_unlink_file(dir_fd, file_name.as_bytes()) +} + +#[allow(clippy::too_many_arguments)] +pub unsafe fn wasi_path_open( + dirfd: wasi_unstable::Fd, + dirflags: wasi_unstable::LookupFlags, + path: &str, + oflags: wasi_unstable::OFlags, + fs_rights_base: wasi_unstable::Rights, + fs_rights_inheriting: wasi_unstable::Rights, + fs_flags: wasi_unstable::FdFlags, + fd: &mut wasi_unstable::Fd, +) -> wasi_unstable::Errno { + wasi_unstable::raw::__wasi_path_open( + dirfd, + dirflags, + path.as_ptr(), + path.len(), + oflags, + fs_rights_base, + fs_rights_inheriting, + fs_flags, + fd, + ) +} + +pub unsafe fn wasi_path_symlink( + old_path: &str, + dirfd: wasi_unstable::Fd, + new_path: &str, +) -> Result<(), wasi_unstable::Error> { + wasi_unstable::path_symlink(old_path.as_bytes(), dirfd, new_path.as_bytes()) +} + +pub unsafe fn wasi_path_readlink( + dirfd: wasi_unstable::Fd, + path: &str, + buf: &mut [u8], + bufused: &mut usize, +) -> wasi_unstable::Errno { + wasi_unstable::raw::__wasi_path_readlink( + dirfd, + path.as_ptr(), + path.len(), + buf.as_mut_ptr(), + buf.len(), + bufused, + ) +} + +pub unsafe fn wasi_path_rename( + old_dirfd: wasi_unstable::Fd, + old_path: &str, + new_dirfd: wasi_unstable::Fd, + new_path: &str, +) -> Result<(), wasi_unstable::Error> { + wasi_unstable::path_rename( + old_dirfd, + old_path.as_bytes(), + new_dirfd, + new_path.as_bytes(), + ) +} + +pub unsafe fn wasi_fd_fdstat_get( + fd: wasi_unstable::Fd, + fdstat: &mut wasi_unstable::FdStat, +) -> wasi_unstable::Errno { + wasi_unstable::raw::__wasi_fd_fdstat_get(fd, fdstat) +} + +pub unsafe fn wasi_fd_seek( + fd: wasi_unstable::Fd, + offset: wasi_unstable::FileDelta, + whence: wasi_unstable::Whence, + newoffset: &mut wasi_unstable::FileSize, +) -> wasi_unstable::Errno { + wasi_unstable::raw::__wasi_fd_seek(fd, offset, whence, newoffset) +} + +pub unsafe fn wasi_fd_tell( + fd: wasi_unstable::Fd, + offset: &mut wasi_unstable::FileSize, +) -> wasi_unstable::Errno { + wasi_unstable::raw::__wasi_fd_tell(fd, offset) +} + +pub unsafe fn wasi_clock_time_get( + clock_id: wasi_unstable::ClockId, + precision: wasi_unstable::Timestamp, + time: &mut wasi_unstable::Timestamp, +) -> wasi_unstable::Errno { + wasi_unstable::raw::__wasi_clock_time_get(clock_id, precision, time) +} + +pub unsafe fn wasi_fd_filestat_get( + fd: wasi_unstable::Fd, + filestat: &mut wasi_unstable::FileStat, +) -> wasi_unstable::Errno { + wasi_unstable::raw::__wasi_fd_filestat_get(fd, filestat) +} + +pub unsafe fn wasi_fd_write( + fd: wasi_unstable::Fd, + iovs: &[wasi_unstable::CIoVec], + nwritten: &mut libc::size_t, +) -> wasi_unstable::Errno { + wasi_unstable::raw::__wasi_fd_write(fd, iovs.as_ptr(), iovs.len(), nwritten) +} + +pub unsafe fn wasi_fd_read( + fd: wasi_unstable::Fd, + iovs: &[wasi_unstable::IoVec], + nread: &mut libc::size_t, +) -> wasi_unstable::Errno { + wasi_unstable::raw::__wasi_fd_read(fd, iovs.as_ptr(), iovs.len(), nread) +} + +pub unsafe fn wasi_fd_pread( + fd: wasi_unstable::Fd, + iovs: &[wasi_unstable::IoVec], + offset: wasi_unstable::FileSize, + nread: &mut usize, +) -> wasi_unstable::Errno { + wasi_unstable::raw::__wasi_fd_pread(fd, iovs.as_ptr(), iovs.len(), offset, nread) +} + +pub unsafe fn wasi_fd_pwrite( + fd: wasi_unstable::Fd, + iovs: &mut [wasi_unstable::CIoVec], + offset: wasi_unstable::FileSize, + nwritten: &mut usize, +) -> wasi_unstable::Errno { + wasi_unstable::raw::__wasi_fd_pwrite(fd, iovs.as_ptr(), iovs.len(), offset, nwritten) +} + +pub unsafe fn wasi_path_filestat_get( + fd: wasi_unstable::Fd, + dirflags: wasi_unstable::LookupFlags, + path: &str, + path_len: usize, + filestat: &mut wasi_unstable::FileStat, +) -> wasi_unstable::Errno { + wasi_unstable::raw::__wasi_path_filestat_get(fd, dirflags, path.as_ptr(), path_len, filestat) +} + +pub unsafe fn wasi_path_filestat_set_times( + fd: wasi_unstable::Fd, + dirflags: wasi_unstable::LookupFlags, + path: &str, + st_atim: wasi_unstable::Timestamp, + st_mtim: wasi_unstable::Timestamp, + fst_flags: wasi_unstable::FstFlags, +) -> Result<(), wasi_unstable::Error> { + wasi_unstable::path_filestat_set_times( + fd, + dirflags, + path.as_bytes(), + st_atim, + st_mtim, + fst_flags, + ) +} + +pub unsafe fn wasi_fd_readdir( + fd: wasi_unstable::Fd, + buf: &mut [u8], + buf_len: usize, + cookie: wasi_unstable::DirCookie, + buf_used: &mut usize, +) -> wasi_unstable::Errno { + wasi_unstable::raw::__wasi_fd_readdir( + fd, + buf.as_mut_ptr() as *mut libc::c_void, + buf_len, + cookie, + buf_used, + ) +} + +pub unsafe fn wasi_fd_advise( + fd: wasi_unstable::Fd, + offset: wasi_unstable::FileSize, + len: wasi_unstable::FileSize, + advice: wasi_unstable::Advice, +) -> wasi_unstable::Errno { + wasi_unstable::raw::__wasi_fd_advise(fd, offset, len, advice) +}