sausage-vfs: review fixes (fallible growth, symlink flags, stream cancel, sized reads, timestamps, error kinds, linker shadowing)
API: Layer::stat(path, follow), Layer::readlink, Open::follow, Stat::symlink and Entry::symlink. Process::idle documents the shutdown order: abort, await the task, await idle, release locks.
This commit is contained in:
@@ -9,6 +9,11 @@ use std::{
|
||||
fn main() {
|
||||
match std::env::var("VFS_TOPIC").unwrap().as_str() {
|
||||
"basic" => basic(),
|
||||
"review_streams" => review_streams(),
|
||||
"review_times" => review_times(),
|
||||
"review_paths" => review_paths(),
|
||||
"review_links" => review_links(),
|
||||
"review_preopen" => review_preopen(),
|
||||
"trees" => trees(),
|
||||
"options" => options(),
|
||||
"rename" => rename(),
|
||||
@@ -255,3 +260,121 @@ fn temporary() {
|
||||
assert!(!original.exists());
|
||||
assert_eq!(fs::read("/persisted").unwrap(), b"persisted");
|
||||
}
|
||||
|
||||
/// Dropped output streams finish writes and small reads do not cache later bytes.
|
||||
fn review_streams() {
|
||||
use wasip2::filesystem::{
|
||||
preopens,
|
||||
types::{DescriptorFlags, OpenFlags, PathFlags},
|
||||
};
|
||||
let root = preopens::get_directories().remove(0).0;
|
||||
let file = root
|
||||
.open_at(
|
||||
PathFlags::empty(),
|
||||
"stream-review",
|
||||
OpenFlags::CREATE,
|
||||
DescriptorFlags::READ | DescriptorFlags::WRITE,
|
||||
)
|
||||
.unwrap();
|
||||
let output = file.write_via_stream(0).unwrap();
|
||||
assert!(output.check_write().unwrap() > 0);
|
||||
output.write(b"abcd").unwrap();
|
||||
drop(output);
|
||||
assert_eq!(fs::read("/stream-review").unwrap(), b"abcd");
|
||||
let input = file.read_via_stream(0).unwrap();
|
||||
assert_eq!(input.blocking_read(1).unwrap(), b"a");
|
||||
file.write_via_stream(1)
|
||||
.unwrap()
|
||||
.blocking_write_and_flush(b"Z")
|
||||
.unwrap();
|
||||
assert_eq!(input.blocking_read(1).unwrap(), b"Z");
|
||||
}
|
||||
/// Timestamp operations accept directories and return overflow rather than panicking.
|
||||
fn review_times() {
|
||||
use wasip2::clocks::wall_clock::Datetime;
|
||||
use wasip2::filesystem::{
|
||||
preopens,
|
||||
types::{ErrorCode, NewTimestamp, PathFlags},
|
||||
};
|
||||
let root = preopens::get_directories().remove(0).0;
|
||||
fs::create_dir("/times").unwrap();
|
||||
root.set_times_at(
|
||||
PathFlags::SYMLINK_FOLLOW,
|
||||
"times",
|
||||
NewTimestamp::NoChange,
|
||||
NewTimestamp::Timestamp(Datetime {
|
||||
seconds: 123,
|
||||
nanoseconds: 0,
|
||||
}),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
fs::metadata("/times").unwrap().modified().unwrap(),
|
||||
std::time::UNIX_EPOCH + std::time::Duration::from_secs(123)
|
||||
);
|
||||
assert_eq!(
|
||||
root.set_times(
|
||||
NewTimestamp::Timestamp(Datetime {
|
||||
seconds: u64::MAX,
|
||||
nanoseconds: u32::MAX
|
||||
}),
|
||||
NewTimestamp::NoChange
|
||||
),
|
||||
Err(ErrorCode::Overflow)
|
||||
);
|
||||
}
|
||||
/// Trailing slashes require folders and readlink rejects ordinary files with invalid.
|
||||
fn review_paths() {
|
||||
use wasip2::filesystem::{preopens, types::ErrorCode};
|
||||
fs::write("/file", b"x").unwrap();
|
||||
assert_eq!(
|
||||
fs::read("/file/").unwrap_err().kind(),
|
||||
ErrorKind::NotADirectory
|
||||
);
|
||||
let root = preopens::get_directories().remove(0).0;
|
||||
assert_eq!(root.readlink_at("file"), Err(ErrorCode::Invalid));
|
||||
assert_eq!(root.remove_directory_at("run"), Err(ErrorCode::Busy));
|
||||
}
|
||||
/// Removing an internal link leaves its target intact and external links cannot escape.
|
||||
fn review_links() {
|
||||
use wasip2::filesystem::{
|
||||
preopens,
|
||||
types::{DescriptorFlags, DescriptorType, OpenFlags, PathFlags},
|
||||
};
|
||||
let root = preopens::get_directories().remove(0).0;
|
||||
assert_eq!(
|
||||
root.stat_at(PathFlags::empty(), "link").unwrap().type_,
|
||||
DescriptorType::SymbolicLink
|
||||
);
|
||||
assert_eq!(root.readlink_at("link").unwrap(), "target");
|
||||
assert!(
|
||||
root.open_at(
|
||||
PathFlags::empty(),
|
||||
"link",
|
||||
OpenFlags::DIRECTORY,
|
||||
DescriptorFlags::READ
|
||||
)
|
||||
.is_err()
|
||||
);
|
||||
assert_eq!(
|
||||
root.open_at(
|
||||
PathFlags::SYMLINK_FOLLOW,
|
||||
"link",
|
||||
OpenFlags::DIRECTORY,
|
||||
DescriptorFlags::READ
|
||||
)
|
||||
.unwrap()
|
||||
.get_type()
|
||||
.unwrap(),
|
||||
DescriptorType::Directory
|
||||
);
|
||||
fs::remove_dir_all("/link").unwrap();
|
||||
assert_eq!(fs::read("/target/keep").unwrap(), b"keep");
|
||||
assert!(fs::read("/outside-link/secret").is_err());
|
||||
}
|
||||
/// The component sees exactly one virtual preopen rather than native WASI preopens.
|
||||
fn review_preopen() {
|
||||
let dirs = wasip2::filesystem::preopens::get_directories();
|
||||
assert_eq!(dirs.len(), 1);
|
||||
assert_eq!(dirs[0].1, "/");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user