Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Port some more wasmtime filesystem tests #86

Merged
merged 1 commit into from
Sep 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 128 additions & 0 deletions tests/rust/src/bin/dir_fd_op_failures.rs
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,
zoraaver marked this conversation as resolved.
Show resolved Hide resolved
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);
}
}
30 changes: 30 additions & 0 deletions tests/rust/src/bin/path_open_nonblock.rs
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) }
}
191 changes: 191 additions & 0 deletions tests/rust/src/bin/path_open_preopen.rs
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
}
Loading