-
Notifications
You must be signed in to change notification settings - Fork 26
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Port some more wasmtime filesystem tests
To close the testing gaps on any filesystem functions, port some more of the wasmtime tests. A few test cases in dir_fd_op_failures seemed to be specific to wasmtime; namely fd_sync, fd_datasync, fd_fdstat_set_flags and fd_advise. These functions return errors when called on a directory fd on wasmtime but on WAMR (and POSIX) they return 0 so for the moment have removed them.
- Loading branch information
Showing
10 changed files
with
573 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
use std::{env, process}; | ||
use wasi_tests::{assert_errno, open_scratch_directory}; | ||
|
||
unsafe fn test_fd_dir_ops(dir_fd: wasi::Fd) { | ||
let stat = wasi::fd_filestat_get(dir_fd).expect("failed to fdstat"); | ||
assert_eq!(stat.filetype, wasi::FILETYPE_DIRECTORY); | ||
|
||
let (pr_fd, pr_name_len) = (3..) | ||
.map_while(|fd| wasi::fd_prestat_get(fd).ok().map(|stat| (fd, stat))) | ||
.find_map(|(fd, wasi::Prestat { tag, u })| { | ||
(tag == wasi::PREOPENTYPE_DIR.raw()).then_some((fd, u.dir.pr_name_len)) | ||
}) | ||
.expect("failed to find preopen directory"); | ||
|
||
let mut pr_name = vec![]; | ||
let r = wasi::fd_prestat_dir_name(pr_fd, pr_name.as_mut_ptr(), 0); | ||
assert_eq!(r, Err(wasi::ERRNO_NAMETOOLONG)); | ||
|
||
// Test that passing a larger than necessary buffer works correctly | ||
let mut pr_name = vec![0; pr_name_len + 1]; | ||
let r = wasi::fd_prestat_dir_name(pr_fd, pr_name.as_mut_ptr(), pr_name_len + 1); | ||
assert_eq!(r, Ok(())); | ||
|
||
let mut read_buf = vec![0; 128].into_boxed_slice(); | ||
let iovec = wasi::Iovec { | ||
buf: read_buf.as_mut_ptr(), | ||
buf_len: read_buf.len(), | ||
}; | ||
|
||
// On posix, this fails with ERRNO_ISDIR | ||
assert_errno!( | ||
wasi::fd_read(dir_fd, &[iovec]).expect_err("fd_read error"), | ||
wasi::ERRNO_BADF, | ||
wasi::ERRNO_NOTCAPABLE | ||
); | ||
|
||
// On posix, this fails with ERRNO_ISDIR | ||
assert_errno!( | ||
wasi::fd_pread(dir_fd, &[iovec], 0).expect_err("fd_pread error"), | ||
wasi::ERRNO_BADF, | ||
wasi::ERRNO_NOTCAPABLE | ||
); | ||
|
||
let write_buf = vec![0; 128].into_boxed_slice(); | ||
let ciovec = wasi::Ciovec { | ||
buf: write_buf.as_ptr(), | ||
buf_len: write_buf.len(), | ||
}; | ||
|
||
assert_errno!( | ||
wasi::fd_write(dir_fd, &[ciovec]).expect_err("fd_write error"), | ||
wasi::ERRNO_BADF, | ||
wasi::ERRNO_NOTCAPABLE | ||
); | ||
|
||
assert_errno!( | ||
wasi::fd_pwrite(dir_fd, &[ciovec], 0).expect_err("fd_pwrite error"), | ||
wasi::ERRNO_BADF, | ||
wasi::ERRNO_NOTCAPABLE | ||
); | ||
|
||
// Divergence from posix: lseek(dirfd) will return 0 | ||
assert_errno!( | ||
wasi::fd_seek(dir_fd, 0, wasi::WHENCE_CUR).expect_err("fd_seek WHENCE_CUR error"), | ||
wasi::ERRNO_BADF, | ||
wasi::ERRNO_NOTCAPABLE | ||
); | ||
|
||
// Divergence from posix: lseek(dirfd) will return 0 | ||
assert_errno!( | ||
wasi::fd_seek(dir_fd, 0, wasi::WHENCE_SET).expect_err("fd_seek WHENCE_SET error"), | ||
wasi::ERRNO_BADF, | ||
wasi::ERRNO_NOTCAPABLE | ||
); | ||
|
||
// Divergence from posix: lseek(dirfd) will return 0 | ||
assert_errno!( | ||
wasi::fd_seek(dir_fd, 0, wasi::WHENCE_END).expect_err("fd_seek WHENCE_END error"), | ||
wasi::ERRNO_BADF, | ||
wasi::ERRNO_NOTCAPABLE | ||
); | ||
|
||
// Tell isnt in posix, its basically lseek with WHENCE_CUR above | ||
assert_errno!( | ||
wasi::fd_tell(dir_fd).expect_err("fd_tell error"), | ||
wasi::ERRNO_BADF, | ||
wasi::ERRNO_NOTCAPABLE | ||
); | ||
|
||
// fallocate(dirfd, FALLOC_FL_ZERO_RANGE, 0, 1) will fail with errno EBADF on linux. | ||
// not available on mac os. | ||
assert_errno!( | ||
wasi::fd_allocate(dir_fd, 0, 0).expect_err("fd_allocate error"), | ||
wasi::ERRNO_BADF, | ||
wasi::ERRNO_NOTCAPABLE | ||
); | ||
|
||
// ftruncate(dirfd, 1) will fail with errno EINVAL on posix. | ||
assert_errno!( | ||
wasi::fd_filestat_set_size(dir_fd, 0).expect_err("fd_filestat_set_size error"), | ||
wasi::ERRNO_BADF, | ||
wasi::ERRNO_NOTCAPABLE | ||
); | ||
} | ||
|
||
fn main() { | ||
let mut args = env::args(); | ||
let prog = args.next().unwrap(); | ||
let arg = if let Some(arg) = args.next() { | ||
arg | ||
} else { | ||
eprintln!("usage: {} <scratch directory>", 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) | ||
} | ||
}; | ||
|
||
unsafe { | ||
test_fd_dir_ops(dir_fd); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
use std::{env, process}; | ||
use wasi_tests::open_scratch_directory; | ||
|
||
unsafe fn try_path_open(dir_fd: wasi::Fd) { | ||
let _fd = | ||
wasi::path_open(dir_fd, 0, ".", 0, 0, 0, wasi::FDFLAGS_NONBLOCK).expect("opening the 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: {} <scratch directory>", 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 { try_path_open(dir_fd) } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,191 @@ | ||
use std::{env, process}; | ||
|
||
unsafe fn find_first_preopened_fd(path: &str) -> Result<wasi::Fd, String> { | ||
let max_fd = (1 << 31) - 1; | ||
|
||
for i in 3..=max_fd { | ||
match wasi::fd_prestat_get(i) { | ||
Ok(prestat) => { | ||
if prestat.tag != wasi::PREOPENTYPE_DIR.raw() { | ||
continue; | ||
} | ||
let mut dst = Vec::with_capacity(prestat.u.dir.pr_name_len); | ||
|
||
if wasi::fd_prestat_dir_name(i, dst.as_mut_ptr(), dst.capacity()).is_err() { | ||
continue; | ||
} | ||
dst.set_len(prestat.u.dir.pr_name_len); | ||
|
||
if dst == path.as_bytes() { | ||
return Ok(i); | ||
} | ||
} | ||
Err(_) => continue, | ||
}; | ||
} | ||
|
||
Err(format!("Failed to find a preopened directory")) | ||
} | ||
|
||
unsafe fn path_open_preopen(preopened_fd: wasi::Fd) { | ||
let prestat = wasi::fd_prestat_get(preopened_fd).expect("fd is a preopen"); | ||
assert_eq!( | ||
prestat.tag, | ||
wasi::PREOPENTYPE_DIR.raw(), | ||
"prestat is a directory" | ||
); | ||
let mut dst = Vec::with_capacity(prestat.u.dir.pr_name_len); | ||
wasi::fd_prestat_dir_name(preopened_fd, dst.as_mut_ptr(), dst.capacity()) | ||
.expect("get preopen dir name"); | ||
dst.set_len(prestat.u.dir.pr_name_len); | ||
|
||
let fdstat = wasi::fd_fdstat_get(preopened_fd).expect("get fdstat"); | ||
|
||
println!( | ||
"preopen dir: {:?} base {:?} inheriting {:?}", | ||
String::from_utf8_lossy(&dst), | ||
fdstat.fs_rights_base, | ||
fdstat.fs_rights_inheriting | ||
); | ||
for (right, name) in directory_base_rights() { | ||
assert!( | ||
(fdstat.fs_rights_base & right) == right, | ||
"fs_rights_base does not have required right `{name}`" | ||
); | ||
} | ||
for (right, name) in directory_inheriting_rights() { | ||
assert!( | ||
(fdstat.fs_rights_inheriting & right) == right, | ||
"fs_rights_inheriting does not have required right `{name}`" | ||
); | ||
} | ||
|
||
// Open with same rights it has now: | ||
let _ = wasi::path_open( | ||
preopened_fd, | ||
0, | ||
".", | ||
0, | ||
fdstat.fs_rights_base, | ||
fdstat.fs_rights_inheriting, | ||
0, | ||
) | ||
.expect("open with same rights"); | ||
|
||
// Open with an empty set of rights: | ||
let _ = wasi::path_open(preopened_fd, 0, ".", 0, 0, 0, 0).expect("open with empty rights"); | ||
|
||
// Open OFLAGS_DIRECTORY with an empty set of rights: | ||
let _ = wasi::path_open(preopened_fd, 0, ".", wasi::OFLAGS_DIRECTORY, 0, 0, 0) | ||
.expect("open with O_DIRECTORY empty rights"); | ||
|
||
// Open OFLAGS_DIRECTORY with just the read right: | ||
let _ = wasi::path_open( | ||
preopened_fd, | ||
0, | ||
".", | ||
wasi::OFLAGS_DIRECTORY, | ||
wasi::RIGHTS_FD_READ, | ||
0, | ||
0, | ||
) | ||
.expect("open with O_DIRECTORY and read right"); | ||
|
||
if !wasi_tests::TESTCONFIG.errno_expect_windows() { | ||
// Open OFLAGS_DIRECTORY and read/write rights should fail with isdir: | ||
let err = wasi::path_open( | ||
preopened_fd, | ||
0, | ||
".", | ||
wasi::OFLAGS_DIRECTORY, | ||
wasi::RIGHTS_FD_READ | wasi::RIGHTS_FD_WRITE, | ||
0, | ||
0, | ||
) | ||
.err() | ||
.expect("open with O_DIRECTORY and read/write should fail"); | ||
assert_eq!( | ||
err, | ||
wasi::ERRNO_ISDIR, | ||
"opening directory read/write should fail with ISDIR" | ||
); | ||
} else { | ||
// Open OFLAGS_DIRECTORY and read/write rights will succeed, only on windows: | ||
let _ = wasi::path_open( | ||
preopened_fd, | ||
0, | ||
".", | ||
wasi::OFLAGS_DIRECTORY, | ||
wasi::RIGHTS_FD_READ | wasi::RIGHTS_FD_WRITE, | ||
0, | ||
0, | ||
) | ||
.expect("open with O_DIRECTORY and read/write should succeed on windows"); | ||
} | ||
} | ||
|
||
fn main() { | ||
let mut args = env::args(); | ||
let prog = args.next().unwrap(); | ||
|
||
let arg = if let Some(arg) = args.next() { | ||
arg | ||
} else { | ||
eprintln!("usage: {} <scratch directory>", prog); | ||
process::exit(1); | ||
}; | ||
|
||
let preopened_fd = unsafe { find_first_preopened_fd(arg.as_str()).unwrap() }; | ||
|
||
// Run the tests. | ||
unsafe { path_open_preopen(preopened_fd) } | ||
} | ||
|
||
// Hard-code the set of rights expected for a preopened directory. This is | ||
// more brittle than we wanted to test for, but various userland | ||
// implementations expect (at least) this set of rights to be present on all | ||
// directories: | ||
|
||
fn directory_base_rights() -> Vec<(wasi::Rights, &'static str)> { | ||
vec![ | ||
(wasi::RIGHTS_PATH_CREATE_DIRECTORY, "PATH_CREATE_DIRECTORY"), | ||
(wasi::RIGHTS_PATH_CREATE_FILE, "PATH_CREATE_FILE"), | ||
(wasi::RIGHTS_PATH_LINK_SOURCE, "PATH_LINK_SOURCE"), | ||
(wasi::RIGHTS_PATH_LINK_TARGET, "PATH_LINK_TARGET"), | ||
(wasi::RIGHTS_PATH_OPEN, "PATH_OPEN"), | ||
(wasi::RIGHTS_FD_READDIR, "FD_READDIR"), | ||
(wasi::RIGHTS_PATH_READLINK, "PATH_READLINK"), | ||
(wasi::RIGHTS_PATH_RENAME_SOURCE, "PATH_RENAME_SOURCE"), | ||
(wasi::RIGHTS_PATH_RENAME_TARGET, "PATH_RENAME_TARGET"), | ||
(wasi::RIGHTS_PATH_SYMLINK, "PATH_SYMLINK"), | ||
(wasi::RIGHTS_PATH_REMOVE_DIRECTORY, "PATH_REMOVE_DIRECTORY"), | ||
(wasi::RIGHTS_PATH_UNLINK_FILE, "PATH_UNLINK_FILE"), | ||
(wasi::RIGHTS_PATH_FILESTAT_GET, "PATH_FILESTAT_GET"), | ||
( | ||
wasi::RIGHTS_PATH_FILESTAT_SET_TIMES, | ||
"PATH_FILESTAT_SET_TIMES", | ||
), | ||
(wasi::RIGHTS_FD_FILESTAT_GET, "FD_FILESTAT_GET"), | ||
(wasi::RIGHTS_FD_FILESTAT_SET_TIMES, "FD_FILESTAT_SET_TIMES"), | ||
] | ||
} | ||
|
||
pub(crate) fn directory_inheriting_rights() -> Vec<(wasi::Rights, &'static str)> { | ||
let mut rights = directory_base_rights(); | ||
rights.extend_from_slice(&[ | ||
(wasi::RIGHTS_FD_DATASYNC, "FD_DATASYNC"), | ||
(wasi::RIGHTS_FD_READ, "FD_READ"), | ||
(wasi::RIGHTS_FD_SEEK, "FD_SEEK"), | ||
(wasi::RIGHTS_FD_FDSTAT_SET_FLAGS, "FD_FDSTAT_SET_FLAGS"), | ||
(wasi::RIGHTS_FD_SYNC, "FD_SYNC"), | ||
(wasi::RIGHTS_FD_TELL, "FD_TELL"), | ||
(wasi::RIGHTS_FD_WRITE, "FD_WRITE"), | ||
(wasi::RIGHTS_FD_ADVISE, "FD_ADVISE"), | ||
(wasi::RIGHTS_FD_ALLOCATE, "FD_ALLOCATE"), | ||
(wasi::RIGHTS_FD_FILESTAT_GET, "FD_FILESTAT_GET"), | ||
(wasi::RIGHTS_FD_FILESTAT_SET_SIZE, "FD_FILESTAT_SET_SIZE"), | ||
(wasi::RIGHTS_FD_FILESTAT_SET_TIMES, "FD_FILESTAT_SET_TIMES"), | ||
(wasi::RIGHTS_POLL_FD_READWRITE, "POLL_FD_READWRITE"), | ||
]); | ||
rights | ||
} |
Oops, something went wrong.