diff --git a/Cargo.lock b/Cargo.lock index fc02405..0842d54 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,21 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "anyhow" version = "1.0.102" @@ -14,6 +29,38 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "cc" +version = "1.2.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "556e016178bb5662a08681bbe0f00f8e17631781a4dfc8c45e466e4b185ec27f" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "chrono" +version = "0.5.0-alpha.1" +source = "git+https://github.com/chronotope/chrono.git?branch=0.5.x#16c45fa8eab2c906ab50a70dea39949b28fdfb28" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "windows-targets", +] + [[package]] name = "chroot" version = "0.1.0" @@ -21,18 +68,54 @@ dependencies = [ "wit-bindgen", ] +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + [[package]] name = "equivalent" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + [[package]] name = "foldhash" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "slab", +] + [[package]] name = "hashbrown" version = "0.17.0" @@ -48,6 +131,30 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + [[package]] name = "id-arena" version = "2.3.0" @@ -72,12 +179,29 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" +[[package]] +name = "js-sys" +version = "0.3.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2025f20d7a4fa7785846e7b63d10a76d3f1cee98ee5cb79ea59703f95e42162" +dependencies = [ + "cfg-if", + "futures-util", + "wasm-bindgen", +] + [[package]] name = "leb128fmt" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + [[package]] name = "log" version = "0.4.25" @@ -101,6 +225,18 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + [[package]] name = "prettyplease" version = "0.2.29" @@ -136,6 +272,12 @@ dependencies = [ "wit-bindgen", ] +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + [[package]] name = "ryu" version = "1.0.19" @@ -189,6 +331,18 @@ dependencies = [ "serde", ] +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + [[package]] name = "syn" version = "2.0.98" @@ -204,6 +358,8 @@ dependencies = [ name = "tracing" version = "0.1.0" dependencies = [ + "chrono", + "heck", "wit-bindgen", ] @@ -219,6 +375,51 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" +[[package]] +name = "wasm-bindgen" +version = "0.2.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a254a4b10c19a76f09a27640e7ffbf9bc30bf67e16a3bf28aaefa4920fe81563" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24a40fc75b0ec6f3746ceb10d36f53a93dcd68a93b11b6445983945d79eba0dc" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "908f34bd9b9ce3d4caf07b72dfab63d61504d156856c6bd3cd87fa350cf3985b" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7acbf7616c27b194bbb550bf77ed0c2c3e5b7fd1260a93082b95fb7f47959b92" +dependencies = [ + "unicode-ident", +] + [[package]] name = "wasm-encoder" version = "0.251.0" @@ -253,11 +454,133 @@ dependencies = [ "semver", ] +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + [[package]] name = "wit-bindgen" version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a43552cfa071f246cfd99e5dbb23710dfe7336b3259e09339818483359470749" +source = "git+https://github.com/bytecodealliance/wit-bindgen.git?branch=main#646d3b4be2b0b02cfdace6fa0614c504ed53c801" dependencies = [ "bitflags", "wit-bindgen-rust-macro", @@ -266,8 +589,7 @@ dependencies = [ [[package]] name = "wit-bindgen-core" version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4738d1c9a78e97bc7f664bfafd5d8e67d7bb26faa5c41e6d628e8bbdad3ec351" +source = "git+https://github.com/bytecodealliance/wit-bindgen.git?branch=main#646d3b4be2b0b02cfdace6fa0614c504ed53c801" dependencies = [ "anyhow", "heck", @@ -277,8 +599,7 @@ dependencies = [ [[package]] name = "wit-bindgen-rust" version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1130ce1f531bc9f9a75922244aa773bf5e2117fda1ef4a86b9f98d6b8135eb46" +source = "git+https://github.com/bytecodealliance/wit-bindgen.git?branch=main#646d3b4be2b0b02cfdace6fa0614c504ed53c801" dependencies = [ "anyhow", "heck", @@ -293,8 +614,7 @@ dependencies = [ [[package]] name = "wit-bindgen-rust-macro" version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07296369e4d598e7e79b64eef66f724d83324ea671bcf677d78fc5cf92604ae5" +source = "git+https://github.com/bytecodealliance/wit-bindgen.git?branch=main#646d3b4be2b0b02cfdace6fa0614c504ed53c801" dependencies = [ "anyhow", "macro-string", diff --git a/Cargo.toml b/Cargo.toml index 08aa7bc..8f15ac0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,4 +5,6 @@ members = [ ] [workspace.dependencies] -wit-bindgen = "0.58.0" +chrono = { git = "https://github.com/chronotope/chrono.git", branch = "0.5.x" } +heck = "0.5" +wit-bindgen = { version = "0.58.0", git = "https://github.com/bytecodealliance/wit-bindgen.git", branch = "main" } diff --git a/Makefile b/Makefile index 17fb6a6..3ac7c7c 100644 --- a/Makefile +++ b/Makefile @@ -19,14 +19,17 @@ components: $(foreach component,$(COMPONENTS),lib/$(component).wasm $(foreach co define BUILD_COMPONENT +.PHONY: components/$1 +components/$1: lib/$1.wasm lib/$1.debug.wasm + lib/$1.wasm: Cargo.toml Cargo.lock wit/deps $(shell find components/$1 -type f) - cargo component build -p $1 --target wasm32-unknown-unknown --release - cp target/wasm32-unknown-unknown/release/$(subst -,_,$1).wasm lib/$1.wasm + cargo build -p $1 --target wasm32-unknown-unknown --release + wasm-tools component new target/wasm32-unknown-unknown/release/$(subst -,_,$1).wasm -o lib/$1.wasm cp components/$1/README.md lib/$1.wasm.md lib/$1.debug.wasm: Cargo.toml Cargo.lock wit/deps $(shell find components/$1 -type f) - cargo component build -p $1 --target wasm32-wasip2 - cp target/wasm32-wasip2/debug/$(subst -,_,$1).wasm lib/$1.debug.wasm + cargo build -p $1 --target wasm32-unknown-unknown + wasm-tools component new target/wasm32-unknown-unknown/debug/$(subst -,_,$1).wasm -o lib/$1.debug.wasm cp components/$1/README.md lib/$1.debug.wasm.md endef diff --git a/README.md b/README.md index b9b80f0..13c337c 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ A collection of utility components that remix wasi:filesystem types and interfac Prereqs: - a rust toolchain -- [`cargo component`](https://github.com/bytecodealliance/cargo-component) +- [`wasm-tools`](https://github.com/bytecodealliance/wasm-tools) - [`wkg`](https://github.com/bytecodealliance/wasm-pkg-tools) ```sh diff --git a/components/chroot/src/lib.rs b/components/chroot/src/lib.rs index 00716b1..bc0b8ab 100644 --- a/components/chroot/src/lib.rs +++ b/components/chroot/src/lib.rs @@ -1,14 +1,13 @@ #![no_main] +use std::path::{Component, Path, PathBuf}; + use exports::wasi::filesystem::preopens::Guest as Preopens; use exports::wasi::filesystem::types::{ Advice, Descriptor, DescriptorBorrow, DescriptorFlags, DescriptorStat, DescriptorType, - DirectoryEntry, DirectoryEntryStream, Error, ErrorCode, Filesize, Guest as Types, - GuestDescriptor, GuestDirectoryEntryStream, InputStream, MetadataHashValue, NewTimestamp, - OpenFlags, OutputStream, PathFlags, + DirectoryEntry, ErrorCode, Filesize, Guest as Types, GuestDescriptor, MetadataHashValue, + NewTimestamp, OpenFlags, PathFlags, }; -use std::path::Path; -use std::rc::Rc; use wasi::filesystem::preopens; use wasi::filesystem::types; @@ -28,7 +27,6 @@ fn prefix_path(path: String) -> String { String::from(Path::new("").join(path_prefix).join(path).to_str().unwrap()) } -#[derive(Debug, Clone)] struct FilesystemChroot {} impl Preopens for FilesystemChroot { @@ -41,382 +39,266 @@ impl Preopens for FilesystemChroot { let open_flags = types::OpenFlags::DIRECTORY; let flags = types::DescriptorFlags::READ; - let chroot = &prefix_path(String::from(path)); - // TODO should we create the directory if it doesn't exist - let chroot_fd = fd - .open_at(path_flags, chroot, open_flags, flags) - .expect(format!("chroot directory '{}' must exist", chroot).as_str()); + let root = prefix_path(String::from(path)); + + let fd = wit_bindgen::block_on(async { + fd.open_at(path_flags, root.clone(), open_flags, flags) + .await + // TODO should we create the directory if it doesn't exist + .expect(format!("chroot directory '{}' must exist", root.clone()).as_str()) + }); - vec![(descriptor_map(chroot_fd), String::from("/"))] + let chroot_fd = + Descriptor::new(FilesystemChrootDescriptor::new(fd, root.into(), "/".into())); + vec![(chroot_fd, String::from("/"))] } } impl Types for FilesystemChroot { type Descriptor = FilesystemChrootDescriptor; - type DirectoryEntryStream = FilesystemChrootDirectoryEntryStream; - - fn filesystem_error_code(err: &Error) -> Option { - types::filesystem_error_code(err).map(error_code_map) - } } -#[derive(Debug, Clone)] struct FilesystemChrootDescriptor { - fd: Rc, + fd: types::Descriptor, + root: PathBuf, + path: PathBuf, } impl FilesystemChrootDescriptor { - fn new(fd: types::Descriptor) -> Self { - Self { fd: Rc::new(fd) } + fn new(fd: types::Descriptor, root: PathBuf, path: PathBuf) -> Self { + Self { fd, root, path } + } + + fn internal_path(&self, path: String) -> Result { + let mut internal = self.path.clone(); + for component in PathBuf::from(path).components() { + if component == Component::RootDir { + // treat root dir as a relative path + internal = PathBuf::from(".") + } else if component == Component::ParentDir { + if !internal.pop() { + // attempt to escape root + return Err(ErrorCode::NotPermitted); + }; + } else if let Component::Normal(c) = component { + internal.push(c); + } + } + Ok(internal) + } + + fn external_path(&self, path: String) -> Result { + let path = self.root.clone().join(self.internal_path(path)?); + Ok(path.to_string_lossy().into_owned()) } } impl GuestDescriptor for FilesystemChrootDescriptor { - fn read_via_stream(&self, offset: Filesize) -> Result { - self.fd.read_via_stream(offset).map_err(error_code_map) + fn read_via_stream( + &self, + offset: Filesize, + ) -> ( + wit_bindgen::StreamReader, + wit_bindgen::FutureReader>, + ) { + self.fd.read_via_stream(offset) } - fn write_via_stream(&self, offset: Filesize) -> Result { - self.fd.write_via_stream(offset).map_err(error_code_map) + fn write_via_stream( + &self, + data: wit_bindgen::StreamReader, + offset: Filesize, + ) -> wit_bindgen::FutureReader> { + self.fd.write_via_stream(data, offset) } - fn append_via_stream(&self) -> Result { - self.fd.append_via_stream().map_err(error_code_map) + fn append_via_stream( + &self, + data: wit_bindgen::StreamReader, + ) -> wit_bindgen::FutureReader> { + self.fd.append_via_stream(data) } - fn advise(&self, offset: Filesize, length: Filesize, advice: Advice) -> Result<(), ErrorCode> { - let advice = advice_map_in(advice); - - self.fd - .advise(offset, length, advice) - .map_err(error_code_map) + async fn advise( + &self, + offset: Filesize, + length: Filesize, + advice: Advice, + ) -> Result<(), ErrorCode> { + self.fd.advise(offset, length, advice).await } - fn sync_data(&self) -> Result<(), ErrorCode> { - self.fd.sync_data().map_err(error_code_map) + async fn sync_data(&self) -> Result<(), ErrorCode> { + self.fd.sync_data().await } - fn get_flags(&self) -> Result { - self.fd - .get_flags() - .map(descriptor_flags_map) - .map_err(error_code_map) + async fn get_flags(&self) -> Result { + self.fd.get_flags().await } - fn get_type(&self) -> Result { - self.fd - .get_type() - .map(descriptor_type_map) - .map_err(error_code_map) + async fn get_type(&self) -> Result { + self.fd.get_type().await } - fn set_size(&self, size: Filesize) -> Result<(), ErrorCode> { - self.fd.set_size(size).map_err(error_code_map) + async fn set_size(&self, size: Filesize) -> Result<(), ErrorCode> { + self.fd.set_size(size).await } - fn set_times( + async fn set_times( &self, data_access_timestamp: NewTimestamp, data_modification_timestamp: NewTimestamp, ) -> Result<(), ErrorCode> { - let data_access_timestamp = new_timestamp_map_in(data_access_timestamp); - let data_modification_timestamp = new_timestamp_map_in(data_modification_timestamp); - self.fd .set_times(data_access_timestamp, data_modification_timestamp) - .map_err(error_code_map) + .await } - fn read(&self, length: Filesize, offset: Filesize) -> Result<(Vec, bool), ErrorCode> { - self.fd.read(length, offset).map_err(error_code_map) - } - - fn write(&self, buffer: Vec, offset: Filesize) -> Result { - self.fd - .write(buffer.as_slice(), offset) - .map_err(error_code_map) - } - - fn read_directory(&self) -> Result { - self.fd - .read_directory() - .map(directory_entry_stream_map) - .map_err(error_code_map) + fn read_directory( + &self, + ) -> ( + wit_bindgen::StreamReader, + wit_bindgen::FutureReader>, + ) { + self.fd.read_directory() } - fn sync(&self) -> Result<(), ErrorCode> { - self.fd.sync().map_err(error_code_map) + async fn sync(&self) -> Result<(), ErrorCode> { + self.fd.sync().await } - fn create_directory_at(&self, path: String) -> Result<(), ErrorCode> { - self.fd.create_directory_at(&path).map_err(error_code_map) + async fn create_directory_at(&self, path: String) -> Result<(), ErrorCode> { + let path = self.external_path(path)?; + self.fd.create_directory_at(path).await } - fn stat(&self) -> Result { - self.fd - .stat() - .map(descriptor_stat_map) - .map_err(error_code_map) + async fn stat(&self) -> Result { + self.fd.stat().await } - fn stat_at(&self, path_flags: PathFlags, path: String) -> Result { - let path_flags = types::PathFlags::from_bits(path_flags.bits()).unwrap(); - - self.fd - .stat_at(path_flags, &path) - .map(descriptor_stat_map) - .map_err(error_code_map) + async fn stat_at( + &self, + path_flags: PathFlags, + path: String, + ) -> Result { + let path = self.external_path(path)?; + self.fd.stat_at(path_flags, path).await } - fn set_times_at( + async fn set_times_at( &self, path_flags: PathFlags, path: String, data_access_timestamp: NewTimestamp, data_modification_timestamp: NewTimestamp, ) -> Result<(), ErrorCode> { - let path_flags = types::PathFlags::from_bits(path_flags.bits()).unwrap(); - let data_access_timestamp = new_timestamp_map_in(data_access_timestamp); - let data_modification_timestamp = new_timestamp_map_in(data_modification_timestamp); - + let path = self.external_path(path)?; self.fd .set_times_at( path_flags, - &path, + path, data_access_timestamp, data_modification_timestamp, ) - .map_err(error_code_map) + .await } - fn link_at( + async fn link_at( &self, old_path_flags: PathFlags, old_path: String, new_descriptor: DescriptorBorrow<'_>, new_path: String, ) -> Result<(), ErrorCode> { - let old_path_flags = types::PathFlags::from_bits(old_path_flags.bits()).unwrap(); - let new_descriptor: &Self = new_descriptor.get(); - + let old_path = self.external_path(old_path)?; + let new_descriptor: &FilesystemChrootDescriptor = new_descriptor.get(); + let new_path = self.external_path(new_path)?; self.fd - .link_at(old_path_flags, &old_path, &new_descriptor.fd, &new_path) - .map_err(error_code_map) + .link_at(old_path_flags, old_path, &new_descriptor.fd, new_path) + .await } - fn open_at( + async fn open_at( &self, path_flags: PathFlags, path: String, open_flags: OpenFlags, flags: DescriptorFlags, ) -> Result { - let path_flags = types::PathFlags::from_bits(path_flags.bits()).unwrap(); - let open_flags = types::OpenFlags::from_bits(open_flags.bits()).unwrap(); - let flags = types::DescriptorFlags::from_bits(flags.bits()).unwrap(); - + let internal_path = self.internal_path(path.clone())?; + let external_path = self.external_path(path.clone())?; self.fd - .open_at(path_flags, &path, open_flags, flags) - .map(descriptor_map) - .map_err(error_code_map) + .open_at(path_flags, external_path, open_flags, flags) + .await + .map(|fd| { + Descriptor::new(FilesystemChrootDescriptor::new( + fd, + self.root.clone(), + internal_path, + )) + }) } - fn readlink_at(&self, path: String) -> Result { - self.fd.readlink_at(&path).map_err(error_code_map) + async fn readlink_at(&self, path: String) -> Result { + let path = self.external_path(path)?; + self.fd.readlink_at(path).await } - fn remove_directory_at(&self, path: String) -> Result<(), ErrorCode> { - self.fd.remove_directory_at(&path).map_err(error_code_map) + async fn remove_directory_at(&self, path: String) -> Result<(), ErrorCode> { + let path = self.external_path(path)?; + self.fd.remove_directory_at(path).await } - fn rename_at( + async fn rename_at( &self, old_path: String, new_descriptor: DescriptorBorrow<'_>, new_path: String, ) -> Result<(), ErrorCode> { + let old_path = self.external_path(old_path)?; let new_descriptor: &Self = new_descriptor.get(); + let new_path = self.external_path(new_path)?; self.fd - .rename_at(&old_path, &new_descriptor.fd, &new_path) - .map_err(error_code_map) + .rename_at(old_path, &new_descriptor.fd, new_path) + .await } - fn symlink_at(&self, old_path: String, new_path: String) -> Result<(), ErrorCode> { - self.fd - .symlink_at(&old_path, &new_path) - .map_err(error_code_map) + async fn symlink_at(&self, old_path: String, new_path: String) -> Result<(), ErrorCode> { + let old_path = self.external_path(old_path)?; + let new_path = self.external_path(new_path)?; + self.fd.symlink_at(old_path, new_path).await } - fn unlink_file_at(&self, path: String) -> Result<(), ErrorCode> { - self.fd.unlink_file_at(&path).map_err(error_code_map) + async fn unlink_file_at(&self, path: String) -> Result<(), ErrorCode> { + let path = self.external_path(path)?; + self.fd.unlink_file_at(path).await } - fn is_same_object(&self, other: DescriptorBorrow<'_>) -> bool { + async fn is_same_object(&self, other: DescriptorBorrow<'_>) -> bool { let other: &Self = other.get(); - self.fd.is_same_object(&other.fd) + self.fd.is_same_object(&other.fd).await } - fn metadata_hash(&self) -> Result { - self.fd - .metadata_hash() - .map(metadata_hash_value_map) - .map_err(error_code_map) + async fn metadata_hash(&self) -> Result { + self.fd.metadata_hash().await } - fn metadata_hash_at( + async fn metadata_hash_at( &self, path_flags: PathFlags, path: String, ) -> Result { - let path_flags = types::PathFlags::from_bits(path_flags.bits()).unwrap(); - - self.fd - .metadata_hash_at(path_flags, &path) - .map(metadata_hash_value_map) - .map_err(error_code_map) - } -} - -#[derive(Debug, Clone)] -struct FilesystemChrootDirectoryEntryStream { - des: Rc, -} - -impl FilesystemChrootDirectoryEntryStream { - fn new(des: types::DirectoryEntryStream) -> Self { - Self { des: Rc::new(des) } - } -} - -impl GuestDirectoryEntryStream for FilesystemChrootDirectoryEntryStream { - fn read_directory_entry(&self) -> Result, ErrorCode> { - self.des - .read_directory_entry() - .map(|de| de.map(directory_entry_map)) - .map_err(error_code_map) - } -} - -fn advice_map_in(advice: Advice) -> types::Advice { - match advice { - Advice::Normal => types::Advice::Normal, - Advice::Sequential => types::Advice::Sequential, - Advice::Random => types::Advice::Random, - Advice::WillNeed => types::Advice::WillNeed, - Advice::DontNeed => types::Advice::DontNeed, - Advice::NoReuse => types::Advice::NoReuse, - } -} - -fn descriptor_map(descriptor: types::Descriptor) -> Descriptor { - Descriptor::new(FilesystemChrootDescriptor::new(descriptor)) -} - -fn descriptor_flags_map(descriptor_flags: types::DescriptorFlags) -> DescriptorFlags { - DescriptorFlags::from_bits(descriptor_flags.bits()).unwrap() -} - -fn descriptor_stat_map(descriptor_stat: types::DescriptorStat) -> DescriptorStat { - DescriptorStat { - type_: descriptor_type_map(descriptor_stat.type_), - link_count: descriptor_stat.link_count, - size: descriptor_stat.size, - data_access_timestamp: descriptor_stat.data_access_timestamp, - data_modification_timestamp: descriptor_stat.data_modification_timestamp, - status_change_timestamp: descriptor_stat.status_change_timestamp, - } -} - -fn descriptor_type_map(descriptor_type: types::DescriptorType) -> DescriptorType { - match descriptor_type { - types::DescriptorType::Unknown => DescriptorType::Unknown, - types::DescriptorType::BlockDevice => DescriptorType::BlockDevice, - types::DescriptorType::CharacterDevice => DescriptorType::CharacterDevice, - types::DescriptorType::Directory => DescriptorType::Directory, - types::DescriptorType::Fifo => DescriptorType::Fifo, - types::DescriptorType::SymbolicLink => DescriptorType::SymbolicLink, - types::DescriptorType::RegularFile => DescriptorType::RegularFile, - types::DescriptorType::Socket => DescriptorType::Socket, - } -} - -fn directory_entry_map(directory_entry: types::DirectoryEntry) -> DirectoryEntry { - DirectoryEntry { - name: directory_entry.name, - type_: descriptor_type_map(directory_entry.type_), - } -} - -fn directory_entry_stream_map( - directory_entry_stream: types::DirectoryEntryStream, -) -> DirectoryEntryStream { - DirectoryEntryStream::new(FilesystemChrootDirectoryEntryStream::new( - directory_entry_stream, - )) -} - -fn error_code_map(error_code: types::ErrorCode) -> ErrorCode { - match error_code { - types::ErrorCode::Access => ErrorCode::Access, - types::ErrorCode::WouldBlock => ErrorCode::WouldBlock, - types::ErrorCode::Already => ErrorCode::Already, - types::ErrorCode::BadDescriptor => ErrorCode::BadDescriptor, - types::ErrorCode::Busy => ErrorCode::Busy, - types::ErrorCode::Deadlock => ErrorCode::Deadlock, - types::ErrorCode::Quota => ErrorCode::Quota, - types::ErrorCode::Exist => ErrorCode::Exist, - types::ErrorCode::FileTooLarge => ErrorCode::FileTooLarge, - types::ErrorCode::IllegalByteSequence => ErrorCode::IllegalByteSequence, - types::ErrorCode::InProgress => ErrorCode::InProgress, - types::ErrorCode::Interrupted => ErrorCode::Interrupted, - types::ErrorCode::Invalid => ErrorCode::Invalid, - types::ErrorCode::Io => ErrorCode::Io, - types::ErrorCode::IsDirectory => ErrorCode::IsDirectory, - types::ErrorCode::Loop => ErrorCode::Loop, - types::ErrorCode::TooManyLinks => ErrorCode::TooManyLinks, - types::ErrorCode::MessageSize => ErrorCode::MessageSize, - types::ErrorCode::NameTooLong => ErrorCode::NameTooLong, - types::ErrorCode::NoDevice => ErrorCode::NoDevice, - types::ErrorCode::NoEntry => ErrorCode::NoEntry, - types::ErrorCode::NoLock => ErrorCode::NoLock, - types::ErrorCode::InsufficientMemory => ErrorCode::InsufficientMemory, - types::ErrorCode::InsufficientSpace => ErrorCode::InsufficientSpace, - types::ErrorCode::NotDirectory => ErrorCode::NotDirectory, - types::ErrorCode::NotEmpty => ErrorCode::NotEmpty, - types::ErrorCode::NotRecoverable => ErrorCode::NotRecoverable, - types::ErrorCode::Unsupported => ErrorCode::Unsupported, - types::ErrorCode::NoTty => ErrorCode::NoTty, - types::ErrorCode::NoSuchDevice => ErrorCode::NoSuchDevice, - types::ErrorCode::Overflow => ErrorCode::Overflow, - types::ErrorCode::NotPermitted => ErrorCode::NotPermitted, - types::ErrorCode::Pipe => ErrorCode::Pipe, - types::ErrorCode::ReadOnly => ErrorCode::ReadOnly, - types::ErrorCode::InvalidSeek => ErrorCode::InvalidSeek, - types::ErrorCode::TextFileBusy => ErrorCode::TextFileBusy, - types::ErrorCode::CrossDevice => ErrorCode::CrossDevice, - } -} - -fn metadata_hash_value_map(metadata_hash_value: types::MetadataHashValue) -> MetadataHashValue { - MetadataHashValue { - lower: metadata_hash_value.lower, - upper: metadata_hash_value.upper, - } -} - -fn new_timestamp_map_in(new_timestamp: NewTimestamp) -> types::NewTimestamp { - match new_timestamp { - NewTimestamp::NoChange => types::NewTimestamp::NoChange, - NewTimestamp::Now => types::NewTimestamp::Now, - NewTimestamp::Timestamp(datetime) => types::NewTimestamp::Timestamp(datetime), + let path = self.external_path(path)?; + self.fd.metadata_hash_at(path_flags, path).await } } wit_bindgen::generate!({ path: "../../wit", world: "filesystem", + merge_structurally_equal_types: true, generate_all }); diff --git a/components/readonly/src/lib.rs b/components/readonly/src/lib.rs index a601a48..0a81bfa 100644 --- a/components/readonly/src/lib.rs +++ b/components/readonly/src/lib.rs @@ -1,17 +1,16 @@ #![no_main] -use std::rc::Rc; +use std::path::PathBuf; use exports::wasi::filesystem::preopens::Guest as Preopens; use exports::wasi::filesystem::types::{ Advice, Descriptor, DescriptorBorrow, DescriptorFlags, DescriptorStat, DescriptorType, - DirectoryEntry, DirectoryEntryStream, Error, ErrorCode, Filesize, Guest as Types, InputStream, - MetadataHashValue, NewTimestamp, OpenFlags, OutputStream, PathFlags, + DirectoryEntry, ErrorCode, Filesize, Guest as Types, MetadataHashValue, NewTimestamp, + OpenFlags, PathFlags, }; use wasi::filesystem::preopens; use wasi::filesystem::types; -#[derive(Debug, Clone)] struct FilesystemReadOnly {} impl Preopens for FilesystemReadOnly { @@ -20,7 +19,7 @@ impl Preopens for FilesystemReadOnly { preopens::get_directories() .into_iter() .map(|(fd, path)| { - let fd = Descriptor::new(ReadOnlyDescriptor::new(fd)); + let fd = Descriptor::new(ReadOnlyDescriptor::new(fd, path.clone().into())); (fd, path) }) .collect() @@ -29,138 +28,81 @@ impl Preopens for FilesystemReadOnly { impl Types for FilesystemReadOnly { type Descriptor = ReadOnlyDescriptor; - type DirectoryEntryStream = ReadOnlyDirectoryEntryStream; - - #[doc = " Attempts to extract a filesystem-related `error-code` from the stream"] - #[doc = " `error` provided."] - #[doc = ""] - #[doc = " Stream operations which return `stream-error::last-operation-failed`"] - #[doc = " have a payload with more information about the operation that failed."] - #[doc = " This payload can be passed through to this function to see if there\'s"] - #[doc = " filesystem-related information about the error to return."] - #[doc = ""] - #[doc = " Note that this function is fallible because not all stream-related"] - #[doc = " errors are filesystem-related errors."] - fn filesystem_error_code(err: &Error) -> Option { - types::filesystem_error_code(err).map(error_code_map) - } } -#[derive(Debug, Clone)] struct ReadOnlyDescriptor { - fd: Rc, + fd: types::Descriptor, + path: PathBuf, } impl ReadOnlyDescriptor { - fn new(fd: types::Descriptor) -> Self { - Self { fd: Rc::new(fd) } + fn new(fd: types::Descriptor, path: PathBuf) -> Self { + Self { fd, path } } } impl exports::wasi::filesystem::types::GuestDescriptor for ReadOnlyDescriptor { - #[doc = " Return a stream for reading from a file, if available."] - #[doc = ""] - #[doc = " May fail with an error-code describing why the file cannot be read."] - #[doc = ""] - #[doc = " Multiple read, write, and append streams may be active on the same open"] - #[doc = " file and they do not interfere with each other."] - #[doc = ""] - #[doc = " Note: This allows using `read-stream`, which is similar to `read` in POSIX."] - fn read_via_stream(&self, offset: Filesize) -> Result { - self.fd.read_via_stream(offset).map_err(error_code_map) + fn read_via_stream( + &self, + offset: Filesize, + ) -> ( + wit_bindgen::StreamReader, + wit_bindgen::FutureReader>, + ) { + self.fd.read_via_stream(offset) } - #[doc = " Return a stream for writing to a file, if available."] - #[doc = ""] - #[doc = " May fail with an error-code describing why the file cannot be written."] - #[doc = ""] - #[doc = " Note: This allows using `write-stream`, which is similar to `write` in"] - #[doc = " POSIX."] - fn write_via_stream(&self, _offset: Filesize) -> Result { - Err(ErrorCode::ReadOnly) + fn write_via_stream( + &self, + _data: wit_bindgen::StreamReader, + _offset: Filesize, + ) -> wit_bindgen::FutureReader> { + let (tx, rx) = wit_future::new(|| Err(ErrorCode::ReadOnly)); + tx.write(Err(ErrorCode::ReadOnly)); + rx } - #[doc = " Return a stream for appending to a file, if available."] - #[doc = ""] - #[doc = " May fail with an error-code describing why the file cannot be appended."] - #[doc = ""] - #[doc = " Note: This allows using `write-stream`, which is similar to `write` with"] - #[doc = " `O_APPEND` in in POSIX."] - fn append_via_stream(&self) -> Result { - Err(ErrorCode::ReadOnly) + fn append_via_stream( + &self, + _data: wit_bindgen::StreamReader, + ) -> wit_bindgen::FutureReader> { + let (tx, rx) = wit_future::new(|| Err(ErrorCode::ReadOnly)); + tx.write(Err(ErrorCode::ReadOnly)); + rx } - #[doc = " Provide file advisory information on a descriptor."] - #[doc = ""] - #[doc = " This is similar to `posix_fadvise` in POSIX."] - fn advise(&self, offset: Filesize, length: Filesize, advice: Advice) -> Result<(), ErrorCode> { - let advice = advice_map_in(advice); - self.fd - .advise(offset, length, advice) - .map_err(error_code_map) + async fn advise( + &self, + offset: Filesize, + length: Filesize, + advice: Advice, + ) -> Result<(), ErrorCode> { + self.fd.advise(offset, length, advice).await } - #[doc = " Synchronize the data of a file to disk."] - #[doc = ""] - #[doc = " This function succeeds with no effect if the file descriptor is not"] - #[doc = " opened for writing."] - #[doc = ""] - #[doc = " Note: This is similar to `fdatasync` in POSIX."] - fn sync_data(&self) -> Result<(), ErrorCode> { + async fn sync_data(&self) -> Result<(), ErrorCode> { Err(ErrorCode::ReadOnly) } - #[doc = " Get flags associated with a descriptor."] - #[doc = ""] - #[doc = " Note: This returns similar flags to `fcntl(fd, F_GETFL)` in POSIX."] - #[doc = ""] - #[doc = " Note: This returns the value that was the `fs_flags` value returned"] - #[doc = " from `fdstat_get` in earlier versions of WASI."] - fn get_flags(&self) -> Result { - self.fd - .get_flags() - .map(descriptor_flags_map) - .map(|flags| { - flags - .difference(DescriptorFlags::WRITE) - .difference(DescriptorFlags::FILE_INTEGRITY_SYNC) - .difference(DescriptorFlags::DATA_INTEGRITY_SYNC) - .difference(DescriptorFlags::MUTATE_DIRECTORY) - }) - .map_err(error_code_map) + async fn get_flags(&self) -> Result { + self.fd.get_flags().await.map(|flags| { + flags + .difference(DescriptorFlags::WRITE) + .difference(DescriptorFlags::FILE_INTEGRITY_SYNC) + .difference(DescriptorFlags::DATA_INTEGRITY_SYNC) + .difference(DescriptorFlags::MUTATE_DIRECTORY) + }) } - #[doc = " Get the dynamic type of a descriptor."] - #[doc = ""] - #[doc = " Note: This returns the same value as the `type` field of the `fd-stat`"] - #[doc = " returned by `stat`, `stat-at` and similar."] - #[doc = ""] - #[doc = " Note: This returns similar flags to the `st_mode & S_IFMT` value provided"] - #[doc = " by `fstat` in POSIX."] - #[doc = ""] - #[doc = " Note: This returns the value that was the `fs_filetype` value returned"] - #[doc = " from `fdstat_get` in earlier versions of WASI."] - fn get_type(&self) -> Result { - self.fd - .get_type() - .map(descriptor_type_map) - .map_err(error_code_map) + async fn get_type(&self) -> Result { + self.fd.get_type().await } - #[doc = " Adjust the size of an open file. If this increases the file\'s size, the"] - #[doc = " extra bytes are filled with zeros."] - #[doc = ""] - #[doc = " Note: This was called `fd_filestat_set_size` in earlier versions of WASI."] - fn set_size(&self, _size: Filesize) -> Result<(), ErrorCode> { + async fn set_size(&self, _size: Filesize) -> Result<(), ErrorCode> { Err(ErrorCode::ReadOnly) } - #[doc = " Adjust the timestamps of an open file or directory."] - #[doc = ""] - #[doc = " Note: This is similar to `futimens` in POSIX."] - #[doc = ""] - #[doc = " Note: This was called `fd_filestat_set_times` in earlier versions of WASI."] - fn set_times( + async fn set_times( &self, _data_access_timestamp: NewTimestamp, _data_modification_timestamp: NewTimestamp, @@ -168,105 +110,36 @@ impl exports::wasi::filesystem::types::GuestDescriptor for ReadOnlyDescriptor { Err(ErrorCode::ReadOnly) } - #[doc = " Read from a descriptor, without using and updating the descriptor\'s offset."] - #[doc = ""] - #[doc = " This function returns a list of bytes containing the data that was"] - #[doc = " read, along with a bool which, when true, indicates that the end of the"] - #[doc = " file was reached. The returned list will contain up to `length` bytes; it"] - #[doc = " may return fewer than requested, if the end of the file is reached or"] - #[doc = " if the I/O operation is interrupted."] - #[doc = ""] - #[doc = " In the future, this may change to return a `stream`."] - #[doc = ""] - #[doc = " Note: This is similar to `pread` in POSIX."] - fn read(&self, length: Filesize, offset: Filesize) -> Result<(Vec, bool), ErrorCode> { - self.fd.read(length, offset).map_err(error_code_map) - } - - #[doc = " Write to a descriptor, without using and updating the descriptor\'s offset."] - #[doc = ""] - #[doc = " It is valid to write past the end of a file; the file is extended to the"] - #[doc = " extent of the write, with bytes between the previous end and the start of"] - #[doc = " the write set to zero."] - #[doc = ""] - #[doc = " In the future, this may change to take a `stream`."] - #[doc = ""] - #[doc = " Note: This is similar to `pwrite` in POSIX."] - fn write(&self, _buffer: Vec, _offset: Filesize) -> Result { - Err(ErrorCode::ReadOnly) - } - - #[doc = " Read directory entries from a directory."] - #[doc = ""] - #[doc = " On filesystems where directories contain entries referring to themselves"] - #[doc = " and their parents, often named `.` and `..` respectively, these entries"] - #[doc = " are omitted."] - #[doc = ""] - #[doc = " This always returns a new stream which starts at the beginning of the"] - #[doc = " directory. Multiple streams may be active on the same directory, and they"] - #[doc = " do not interfere with each other."] - fn read_directory(&self) -> Result { - self.fd - .read_directory() - .map(directory_entry_stream_map) - .map_err(error_code_map) + fn read_directory( + &self, + ) -> ( + wit_bindgen::StreamReader, + wit_bindgen::FutureReader>, + ) { + self.fd.read_directory() } - #[doc = " Synchronize the data and metadata of a file to disk."] - #[doc = ""] - #[doc = " This function succeeds with no effect if the file descriptor is not"] - #[doc = " opened for writing."] - #[doc = ""] - #[doc = " Note: This is similar to `fsync` in POSIX."] - fn sync(&self) -> Result<(), ErrorCode> { + async fn sync(&self) -> Result<(), ErrorCode> { Err(ErrorCode::ReadOnly) } - #[doc = " Create a directory."] - #[doc = ""] - #[doc = " Note: This is similar to `mkdirat` in POSIX."] - fn create_directory_at(&self, _path: String) -> Result<(), ErrorCode> { + async fn create_directory_at(&self, _path: String) -> Result<(), ErrorCode> { Err(ErrorCode::ReadOnly) } - #[doc = " Return the attributes of an open file or directory."] - #[doc = ""] - #[doc = " Note: This is similar to `fstat` in POSIX, except that it does not return"] - #[doc = " device and inode information. For testing whether two descriptors refer to"] - #[doc = " the same underlying filesystem object, use `is-same-object`. To obtain"] - #[doc = " additional data that can be used do determine whether a file has been"] - #[doc = " modified, use `metadata-hash`."] - #[doc = ""] - #[doc = " Note: This was called `fd_filestat_get` in earlier versions of WASI."] - fn stat(&self) -> Result { - self.fd - .stat() - .map(descriptor_stat_map) - .map_err(error_code_map) + async fn stat(&self) -> Result { + self.fd.stat().await } - #[doc = " Return the attributes of a file or directory."] - #[doc = ""] - #[doc = " Note: This is similar to `fstatat` in POSIX, except that it does not"] - #[doc = " return device and inode information. See the `stat` description for a"] - #[doc = " discussion of alternatives."] - #[doc = ""] - #[doc = " Note: This was called `path_filestat_get` in earlier versions of WASI."] - fn stat_at(&self, path_flags: PathFlags, path: String) -> Result { - let path_flags = types::PathFlags::from_bits(path_flags.bits()).unwrap(); - self.fd - .stat_at(path_flags, &path) - .map(descriptor_stat_map) - .map_err(error_code_map) + async fn stat_at( + &self, + path_flags: PathFlags, + path: String, + ) -> Result { + self.fd.stat_at(path_flags, path).await } - #[doc = " Adjust the timestamps of a file or directory."] - #[doc = ""] - #[doc = " Note: This is similar to `utimensat` in POSIX."] - #[doc = ""] - #[doc = " Note: This was called `path_filestat_set_times` in earlier versions of"] - #[doc = " WASI."] - fn set_times_at( + async fn set_times_at( &self, _path_flags: PathFlags, _path: String, @@ -276,10 +149,7 @@ impl exports::wasi::filesystem::types::GuestDescriptor for ReadOnlyDescriptor { Err(ErrorCode::ReadOnly) } - #[doc = " Create a hard link."] - #[doc = ""] - #[doc = " Note: This is similar to `linkat` in POSIX."] - fn link_at( + async fn link_at( &self, _old_path_flags: PathFlags, _old_path: String, @@ -289,25 +159,7 @@ impl exports::wasi::filesystem::types::GuestDescriptor for ReadOnlyDescriptor { Err(ErrorCode::ReadOnly) } - #[doc = " Open a file or directory."] - #[doc = ""] - #[doc = " The returned descriptor is not guaranteed to be the lowest-numbered"] - #[doc = " descriptor not currently open/ it is randomized to prevent applications"] - #[doc = " from depending on making assumptions about indexes, since this is"] - #[doc = " error-prone in multi-threaded contexts. The returned descriptor is"] - #[doc = " guaranteed to be less than 2**31."] - #[doc = ""] - #[doc = " If `flags` contains `descriptor-flags::mutate-directory`, and the base"] - #[doc = " descriptor doesn\'t have `descriptor-flags::mutate-directory` set,"] - #[doc = " `open-at` fails with `error-code::read-only`."] - #[doc = ""] - #[doc = " If `flags` contains `write` or `mutate-directory`, or `open-flags`"] - #[doc = " contains `truncate` or `create`, and the base descriptor doesn\'t have"] - #[doc = " `descriptor-flags::mutate-directory` set, `open-at` fails with"] - #[doc = " `error-code::read-only`."] - #[doc = ""] - #[doc = " Note: This is similar to `openat` in POSIX."] - fn open_at( + async fn open_at( &self, path_flags: PathFlags, path: String, @@ -325,38 +177,28 @@ impl exports::wasi::filesystem::types::GuestDescriptor for ReadOnlyDescriptor { return Err(ErrorCode::ReadOnly); } - let path_flags = types::PathFlags::from_bits(path_flags.bits()).unwrap(); - let open_flags = types::OpenFlags::from_bits(open_flags.bits()).unwrap(); - let flags = types::DescriptorFlags::from_bits(flags.bits()).unwrap(); - self.fd - .open_at(path_flags, &path, open_flags, flags) - .map(descriptor_map) - .map_err(error_code_map) + match self + .fd + .open_at(path_flags, path.clone(), open_flags, flags) + .await + { + Ok(fd) => Ok(Descriptor::new(ReadOnlyDescriptor::new( + fd, + self.path.clone().join(path), + ))), + Err(error_code) => Err(error_code), + } } - #[doc = " Read the contents of a symbolic link."] - #[doc = ""] - #[doc = " If the contents contain an absolute or rooted path in the underlying"] - #[doc = " filesystem, this function fails with `error-code::not-permitted`."] - #[doc = ""] - #[doc = " Note: This is similar to `readlinkat` in POSIX."] - fn readlink_at(&self, path: String) -> Result { - self.fd.readlink_at(&path).map_err(error_code_map) + async fn readlink_at(&self, path: String) -> Result { + self.fd.readlink_at(path).await } - #[doc = " Remove a directory."] - #[doc = ""] - #[doc = " Return `error-code::not-empty` if the directory is not empty."] - #[doc = ""] - #[doc = " Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX."] - fn remove_directory_at(&self, _path: String) -> Result<(), ErrorCode> { + async fn remove_directory_at(&self, _path: String) -> Result<(), ErrorCode> { Err(ErrorCode::ReadOnly) } - #[doc = " Rename a filesystem object."] - #[doc = ""] - #[doc = " Note: This is similar to `renameat` in POSIX."] - fn rename_at( + async fn rename_at( &self, _old_path: String, _new_descriptor: DescriptorBorrow<'_>, @@ -365,208 +207,36 @@ impl exports::wasi::filesystem::types::GuestDescriptor for ReadOnlyDescriptor { Err(ErrorCode::ReadOnly) } - #[doc = " Create a symbolic link (also known as a \"symlink\")."] - #[doc = ""] - #[doc = " If `old-path` starts with `/`, the function fails with"] - #[doc = " `error-code::not-permitted`."] - #[doc = ""] - #[doc = " Note: This is similar to `symlinkat` in POSIX."] - fn symlink_at(&self, _old_path: String, _new_path: String) -> Result<(), ErrorCode> { + async fn symlink_at(&self, _old_path: String, _new_path: String) -> Result<(), ErrorCode> { Err(ErrorCode::ReadOnly) } - #[doc = " Unlink a filesystem object that is not a directory."] - #[doc = ""] - #[doc = " Return `error-code::is-directory` if the path refers to a directory."] - #[doc = " Note: This is similar to `unlinkat(fd, path, 0)` in POSIX."] - fn unlink_file_at(&self, _path: String) -> Result<(), ErrorCode> { + async fn unlink_file_at(&self, _path: String) -> Result<(), ErrorCode> { Err(ErrorCode::ReadOnly) } - #[doc = " Test whether two descriptors refer to the same filesystem object."] - #[doc = ""] - #[doc = " In POSIX, this corresponds to testing whether the two descriptors have the"] - #[doc = " same device (`st_dev`) and inode (`st_ino` or `d_ino`) numbers."] - #[doc = " wasi-filesystem does not expose device and inode numbers, so this function"] - #[doc = " may be used instead."] - fn is_same_object(&self, other: DescriptorBorrow<'_>) -> bool { + async fn is_same_object(&self, other: DescriptorBorrow<'_>) -> bool { let other: &Self = other.get(); - - self.fd.is_same_object(&other.fd) + self.fd.is_same_object(&other.fd).await } - #[doc = " Return a hash of the metadata associated with a filesystem object referred"] - #[doc = " to by a descriptor."] - #[doc = ""] - #[doc = " This returns a hash of the last-modification timestamp and file size, and"] - #[doc = " may also include the inode number, device number, birth timestamp, and"] - #[doc = " other metadata fields that may change when the file is modified or"] - #[doc = " replaced. It may also include a secret value chosen by the"] - #[doc = " implementation and not otherwise exposed."] - #[doc = ""] - #[doc = " Implementations are encourated to provide the following properties:"] - #[doc = ""] - #[doc = " - If the file is not modified or replaced, the computed hash value should"] - #[doc = " usually not change."] - #[doc = " - If the object is modified or replaced, the computed hash value should"] - #[doc = " usually change."] - #[doc = " - The inputs to the hash should not be easily computable from the"] - #[doc = " computed hash."] - #[doc = ""] - #[doc = " However, none of these is required."] - fn metadata_hash(&self) -> Result { - self.fd - .metadata_hash() - .map(metadata_hash_value_map) - .map_err(error_code_map) + async fn metadata_hash(&self) -> Result { + self.fd.metadata_hash().await } - #[doc = " Return a hash of the metadata associated with a filesystem object referred"] - #[doc = " to by a directory descriptor and a relative path."] - #[doc = ""] - #[doc = " This performs the same hash computation as `metadata-hash`."] - fn metadata_hash_at( + async fn metadata_hash_at( &self, path_flags: PathFlags, path: String, ) -> Result { - let path_flags = types::PathFlags::from_bits(path_flags.bits()).unwrap(); - self.fd - .metadata_hash_at(path_flags, &path) - .map(metadata_hash_value_map) - .map_err(error_code_map) - } -} - -#[derive(Debug, Clone)] -struct ReadOnlyDirectoryEntryStream { - des: Rc, -} - -impl ReadOnlyDirectoryEntryStream { - fn new(des: types::DirectoryEntryStream) -> Self { - Self { des: Rc::new(des) } - } -} - -impl exports::wasi::filesystem::types::GuestDirectoryEntryStream for ReadOnlyDirectoryEntryStream { - #[doc = " Read a single directory entry from a `directory-entry-stream`."] - fn read_directory_entry(&self) -> Result, ErrorCode> { - self.des - .read_directory_entry() - .map(|de| de.map(directory_entry_map)) - .map_err(error_code_map) - } -} - -fn advice_map_in(advice: Advice) -> types::Advice { - match advice { - Advice::Normal => types::Advice::Normal, - Advice::Sequential => types::Advice::Sequential, - Advice::Random => types::Advice::Random, - Advice::WillNeed => types::Advice::WillNeed, - Advice::DontNeed => types::Advice::DontNeed, - Advice::NoReuse => types::Advice::NoReuse, - } -} - -fn descriptor_map(descriptor: types::Descriptor) -> Descriptor { - Descriptor::new(ReadOnlyDescriptor::new(descriptor)) -} - -fn descriptor_flags_map(descriptor_flags: types::DescriptorFlags) -> DescriptorFlags { - DescriptorFlags::from_bits(descriptor_flags.bits()).unwrap() -} - -fn descriptor_stat_map(descriptor_stat: types::DescriptorStat) -> DescriptorStat { - DescriptorStat { - type_: descriptor_type_map(descriptor_stat.type_), - link_count: descriptor_stat.link_count, - size: descriptor_stat.size, - data_access_timestamp: descriptor_stat.data_access_timestamp, - data_modification_timestamp: descriptor_stat.data_modification_timestamp, - status_change_timestamp: descriptor_stat.status_change_timestamp, - } -} - -fn descriptor_type_map(descriptor_type: types::DescriptorType) -> DescriptorType { - match descriptor_type { - types::DescriptorType::Unknown => DescriptorType::Unknown, - types::DescriptorType::BlockDevice => DescriptorType::BlockDevice, - types::DescriptorType::CharacterDevice => DescriptorType::CharacterDevice, - types::DescriptorType::Directory => DescriptorType::Directory, - types::DescriptorType::Fifo => DescriptorType::Fifo, - types::DescriptorType::SymbolicLink => DescriptorType::SymbolicLink, - types::DescriptorType::RegularFile => DescriptorType::RegularFile, - types::DescriptorType::Socket => DescriptorType::Socket, - } -} - -fn directory_entry_map(directory_entry: types::DirectoryEntry) -> DirectoryEntry { - DirectoryEntry { - name: directory_entry.name, - type_: descriptor_type_map(directory_entry.type_), - } -} - -fn directory_entry_stream_map( - directory_entry_stream: types::DirectoryEntryStream, -) -> DirectoryEntryStream { - DirectoryEntryStream::new(ReadOnlyDirectoryEntryStream::new(directory_entry_stream)) -} - -fn error_code_map(error_code: types::ErrorCode) -> ErrorCode { - match error_code { - types::ErrorCode::Access => ErrorCode::Access, - types::ErrorCode::WouldBlock => ErrorCode::WouldBlock, - types::ErrorCode::Already => ErrorCode::Already, - types::ErrorCode::BadDescriptor => ErrorCode::BadDescriptor, - types::ErrorCode::Busy => ErrorCode::Busy, - types::ErrorCode::Deadlock => ErrorCode::Deadlock, - types::ErrorCode::Quota => ErrorCode::Quota, - types::ErrorCode::Exist => ErrorCode::Exist, - types::ErrorCode::FileTooLarge => ErrorCode::FileTooLarge, - types::ErrorCode::IllegalByteSequence => ErrorCode::IllegalByteSequence, - types::ErrorCode::InProgress => ErrorCode::InProgress, - types::ErrorCode::Interrupted => ErrorCode::Interrupted, - types::ErrorCode::Invalid => ErrorCode::Invalid, - types::ErrorCode::Io => ErrorCode::Io, - types::ErrorCode::IsDirectory => ErrorCode::IsDirectory, - types::ErrorCode::Loop => ErrorCode::Loop, - types::ErrorCode::TooManyLinks => ErrorCode::TooManyLinks, - types::ErrorCode::MessageSize => ErrorCode::MessageSize, - types::ErrorCode::NameTooLong => ErrorCode::NameTooLong, - types::ErrorCode::NoDevice => ErrorCode::NoDevice, - types::ErrorCode::NoEntry => ErrorCode::NoEntry, - types::ErrorCode::NoLock => ErrorCode::NoLock, - types::ErrorCode::InsufficientMemory => ErrorCode::InsufficientMemory, - types::ErrorCode::InsufficientSpace => ErrorCode::InsufficientSpace, - types::ErrorCode::NotDirectory => ErrorCode::NotDirectory, - types::ErrorCode::NotEmpty => ErrorCode::NotEmpty, - types::ErrorCode::NotRecoverable => ErrorCode::NotRecoverable, - types::ErrorCode::Unsupported => ErrorCode::Unsupported, - types::ErrorCode::NoTty => ErrorCode::NoTty, - types::ErrorCode::NoSuchDevice => ErrorCode::NoSuchDevice, - types::ErrorCode::Overflow => ErrorCode::Overflow, - types::ErrorCode::NotPermitted => ErrorCode::NotPermitted, - types::ErrorCode::Pipe => ErrorCode::Pipe, - types::ErrorCode::ReadOnly => ErrorCode::ReadOnly, - types::ErrorCode::InvalidSeek => ErrorCode::InvalidSeek, - types::ErrorCode::TextFileBusy => ErrorCode::TextFileBusy, - types::ErrorCode::CrossDevice => ErrorCode::CrossDevice, - } -} - -fn metadata_hash_value_map(metadata_hash_value: types::MetadataHashValue) -> MetadataHashValue { - MetadataHashValue { - lower: metadata_hash_value.lower, - upper: metadata_hash_value.upper, + self.fd.metadata_hash_at(path_flags, path).await } } wit_bindgen::generate!({ path: "../../wit", world: "filesystem", + merge_structurally_equal_types: true, generate_all }); diff --git a/components/tracing/Cargo.toml b/components/tracing/Cargo.toml index b7d31ce..134a130 100644 --- a/components/tracing/Cargo.toml +++ b/components/tracing/Cargo.toml @@ -8,4 +8,6 @@ license = "Apache-2.0" crate-type = ["cdylib"] [dependencies] +chrono = { workspace = true } +heck = { workspace = true } wit-bindgen = { workspace = true } diff --git a/components/tracing/src/lib.rs b/components/tracing/src/lib.rs index afe5fdb..56d4aef 100644 --- a/components/tracing/src/lib.rs +++ b/components/tracing/src/lib.rs @@ -1,12 +1,16 @@ #![no_main] -use std::rc::Rc; +use std::fmt::Display; +use std::path::PathBuf; + +use chrono::DateTime; +use heck::ToKebabCase; use exports::wasi::filesystem::preopens::Guest as Preopens; use exports::wasi::filesystem::types::{ Advice, Descriptor, DescriptorBorrow, DescriptorFlags, DescriptorStat, DescriptorType, - DirectoryEntry, DirectoryEntryStream, Error, ErrorCode, Filesize, Guest as Types, InputStream, - MetadataHashValue, NewTimestamp, OpenFlags, OutputStream, PathFlags, + DirectoryEntry, ErrorCode, Filesize, Guest as Types, MetadataHashValue, NewTimestamp, + OpenFlags, PathFlags, }; use wasi::filesystem::preopens; use wasi::filesystem::types; @@ -22,18 +26,18 @@ macro_rules! trace { }; } -#[derive(Debug, Clone)] struct FilesystemTracing {} impl Preopens for FilesystemTracing { - #[doc = " Return the set of preopened directories, and their path."] + #[doc = "/ Return the set of preopened directories, and their paths."] + #[allow(async_fn_in_trait)] fn get_directories() -> Vec<(Descriptor, String)> { trace!("CALL wasi:filesystem/preopens#get-directories"); preopens::get_directories() .into_iter() .map(|(fd, path)| { - let fd = Descriptor::new(TracingDescriptor::new(fd)); + let fd = Descriptor::new(TracingDescriptor::new(fd, PathBuf::from(path.clone()))); (fd, path) }) .collect() @@ -42,616 +46,539 @@ impl Preopens for FilesystemTracing { impl Types for FilesystemTracing { type Descriptor = TracingDescriptor; - type DirectoryEntryStream = TracingDirectoryEntryStream; - - #[doc = " Attempts to extract a filesystem-related `error-code` from the stream"] - #[doc = " `error` provided."] - #[doc = ""] - #[doc = " Stream operations which return `stream-error::last-operation-failed`"] - #[doc = " have a payload with more information about the operation that failed."] - #[doc = " This payload can be passed through to this function to see if there\'s"] - #[doc = " filesystem-related information about the error to return."] - #[doc = ""] - #[doc = " Note that this function is fallible because not all stream-related"] - #[doc = " errors are filesystem-related errors."] - fn filesystem_error_code(err: &Error) -> Option { - trace!("CALL wasi:filesystem/types#filesystem-error-code"); - - types::filesystem_error_code(err).map(error_code_map) - } } -#[derive(Debug, Clone)] struct TracingDescriptor { - fd: Rc, + fd: types::Descriptor, + path: PathBuf, } impl TracingDescriptor { - fn new(fd: types::Descriptor) -> Self { - Self { fd: Rc::new(fd) } + fn new(fd: types::Descriptor, path: PathBuf) -> Self { + Self { fd, path } } } impl exports::wasi::filesystem::types::GuestDescriptor for TracingDescriptor { - #[doc = " Return a stream for reading from a file, if available."] - #[doc = ""] - #[doc = " May fail with an error-code describing why the file cannot be read."] - #[doc = ""] - #[doc = " Multiple read, write, and append streams may be active on the same open"] - #[doc = " file and they do not interfere with each other."] - #[doc = ""] - #[doc = " Note: This allows using `read-stream`, which is similar to `read` in POSIX."] - fn read_via_stream(&self, offset: Filesize) -> Result { - trace!("CALL wasi:filesystem/types#descriptor.read-via-stream FD={self:?} OFFSET={offset}",); - - self.fd.read_via_stream(offset).map_err(error_code_map) - } - - #[doc = " Return a stream for writing to a file, if available."] - #[doc = ""] - #[doc = " May fail with an error-code describing why the file cannot be written."] - #[doc = ""] - #[doc = " Note: This allows using `write-stream`, which is similar to `write` in"] - #[doc = " POSIX."] - fn write_via_stream(&self, offset: Filesize) -> Result { - trace!( - "CALL wasi:filesystem/types#descriptor.write-via-stream FD={self:?} OFFSET={offset}", - ); - - self.fd.write_via_stream(offset).map_err(error_code_map) - } - - #[doc = " Return a stream for appending to a file, if available."] - #[doc = ""] - #[doc = " May fail with an error-code describing why the file cannot be appended."] - #[doc = ""] - #[doc = " Note: This allows using `write-stream`, which is similar to `write` with"] - #[doc = " `O_APPEND` in in POSIX."] - fn append_via_stream(&self) -> Result { - trace!("CALL wasi:filesystem/types#descriptor.append-via-stream FD={self:?}"); - - self.fd.append_via_stream().map_err(error_code_map) - } - - #[doc = " Provide file advisory information on a descriptor."] - #[doc = ""] - #[doc = " This is similar to `posix_fadvise` in POSIX."] - fn advise(&self, offset: Filesize, length: Filesize, advice: Advice) -> Result<(), ErrorCode> { - trace!("CALL wasi:filesystem/types#descriptor.advise FD={self:?}"); - - let advice = advice_map_in(advice); - self.fd - .advise(offset, length, advice) - .map_err(error_code_map) - } - - #[doc = " Synchronize the data of a file to disk."] - #[doc = ""] - #[doc = " This function succeeds with no effect if the file descriptor is not"] - #[doc = " opened for writing."] - #[doc = ""] - #[doc = " Note: This is similar to `fdatasync` in POSIX."] - fn sync_data(&self) -> Result<(), ErrorCode> { - trace!("CALL wasi:filesystem/types#descriptor.sync-data FD={self:?}"); - - self.fd.sync_data().map_err(error_code_map) - } - - #[doc = " Get flags associated with a descriptor."] - #[doc = ""] - #[doc = " Note: This returns similar flags to `fcntl(fd, F_GETFL)` in POSIX."] - #[doc = ""] - #[doc = " Note: This returns the value that was the `fs_flags` value returned"] - #[doc = " from `fdstat_get` in earlier versions of WASI."] - fn get_flags(&self) -> Result { - trace!("CALL wasi:filesystem/types#descriptor.get-flags FD={self:?}"); - - self.fd - .get_flags() - .map(descriptor_flags_map) - .map_err(error_code_map) - } - - #[doc = " Get the dynamic type of a descriptor."] - #[doc = ""] - #[doc = " Note: This returns the same value as the `type` field of the `fd-stat`"] - #[doc = " returned by `stat`, `stat-at` and similar."] - #[doc = ""] - #[doc = " Note: This returns similar flags to the `st_mode & S_IFMT` value provided"] - #[doc = " by `fstat` in POSIX."] - #[doc = ""] - #[doc = " Note: This returns the value that was the `fs_filetype` value returned"] - #[doc = " from `fdstat_get` in earlier versions of WASI."] - fn get_type(&self) -> Result { - trace!("CALL wasi:filesystem/types#descriptor.get-type FD={self:?}"); - - self.fd - .get_type() - .map(descriptor_type_map) - .map_err(error_code_map) - } - - #[doc = " Adjust the size of an open file. If this increases the file\'s size, the"] - #[doc = " extra bytes are filled with zeros."] - #[doc = ""] - #[doc = " Note: This was called `fd_filestat_set_size` in earlier versions of WASI."] - fn set_size(&self, size: Filesize) -> Result<(), ErrorCode> { - trace!("CALL wasi:filesystem/types#descriptor.set-size FD={self:?}"); + #[doc = "/ Return a stream for reading from a file."] + #[doc = "/"] + #[doc = "/ Multiple read, write, and append streams may be active on the same open"] + #[doc = "/ file and they do not interfere with each other."] + #[doc = "/"] + #[doc = "/ This function returns a `stream` which provides the data received from the"] + #[doc = "/ file, and a `future` providing additional error information in case an"] + #[doc = "/ error is encountered."] + #[doc = "/"] + #[doc = "/ If no error is encountered, `stream.read` on the `stream` will return"] + #[doc = "/ `read-status::closed` with no `error-context` and the future resolves to"] + #[doc = "/ the value `ok`. If an error is encountered, `stream.read` on the"] + #[doc = "/ `stream` returns `read-status::closed` with an `error-context` and the future"] + #[doc = "/ resolves to `err` with an `error-code`."] + #[doc = "/"] + #[doc = "/ Note: This is similar to `pread` in POSIX."] + #[allow(async_fn_in_trait)] + fn read_via_stream( + &self, + offset: Filesize, + ) -> ( + wit_bindgen::StreamReader, + wit_bindgen::FutureReader>, + ) { + trace!("CALL wasi:filesystem/types#descriptor.read-via-stream FD={self} OFFSET={offset}"); + + self.fd.read_via_stream(offset) + } + + #[doc = "/ Return a stream for writing to a file, if available."] + #[doc = "/"] + #[doc = "/ May fail with an error-code describing why the file cannot be written."] + #[doc = "/"] + #[doc = "/ It is valid to write past the end of a file; the file is extended to the"] + #[doc = "/ extent of the write, with bytes between the previous end and the start of"] + #[doc = "/ the write set to zero."] + #[doc = "/"] + #[doc = "/ This function returns once either full contents of the stream are"] + #[doc = "/ written or an error is encountered."] + #[doc = "/"] + #[doc = "/ Note: This is similar to `pwrite` in POSIX."] + #[allow(async_fn_in_trait)] + fn write_via_stream( + &self, + data: wit_bindgen::StreamReader, + offset: Filesize, + ) -> wit_bindgen::FutureReader> { + trace!("CALL wasi:filesystem/types#descriptor.write-via-stream FD={self} OFFSET={offset}"); + + self.fd.write_via_stream(data, offset) + } + + #[doc = "/ Return a stream for appending to a file, if available."] + #[doc = "/"] + #[doc = "/ May fail with an error-code describing why the file cannot be appended."] + #[doc = "/"] + #[doc = "/ This function returns once either full contents of the stream are"] + #[doc = "/ written or an error is encountered."] + #[doc = "/"] + #[doc = "/ Note: This is similar to `write` with `O_APPEND` in POSIX."] + #[allow(async_fn_in_trait)] + fn append_via_stream( + &self, + data: wit_bindgen::StreamReader, + ) -> wit_bindgen::FutureReader> { + trace!("CALL wasi:filesystem/types#descriptor.append-via-stream FD={self}",); - self.fd.set_size(size).map_err(error_code_map) + self.fd.append_via_stream(data) } - #[doc = " Adjust the timestamps of an open file or directory."] - #[doc = ""] - #[doc = " Note: This is similar to `futimens` in POSIX."] - #[doc = ""] - #[doc = " Note: This was called `fd_filestat_set_times` in earlier versions of WASI."] - fn set_times( + #[doc = "/ Provide file advisory information on a descriptor."] + #[doc = "/"] + #[doc = "/ This is similar to `posix_fadvise` in POSIX."] + #[allow(async_fn_in_trait)] + async fn advise( + &self, + offset: Filesize, + length: Filesize, + advice: Advice, + ) -> Result<(), ErrorCode> { + trace!("CALL wasi:filesystem/types#descriptor.advise FD={self} OFFSET={offset} LENGTH={length} ADVICE={advice}"); + + self.fd.advise(offset, length, advice).await + } + + #[doc = "/ Synchronize the data of a file to disk."] + #[doc = "/"] + #[doc = "/ This function succeeds with no effect if the file descriptor is not"] + #[doc = "/ opened for writing."] + #[doc = "/"] + #[doc = "/ Note: This is similar to `fdatasync` in POSIX."] + #[allow(async_fn_in_trait)] + async fn sync_data(&self) -> Result<(), ErrorCode> { + trace!("CALL wasi:filesystem/types#descriptor.sync-data FD={self}"); + + self.fd.sync_data().await + } + + #[doc = "/ Get flags associated with a descriptor."] + #[doc = "/"] + #[doc = "/ Note: This returns similar flags to `fcntl(fd, F_GETFL)` in POSIX."] + #[doc = "/"] + #[doc = "/ Note: This returns the value that was the `fs_flags` value returned"] + #[doc = "/ from `fdstat_get` in earlier versions of WASI."] + #[allow(async_fn_in_trait)] + async fn get_flags(&self) -> Result { + trace!("CALL wasi:filesystem/types#descriptor.get-flags FD={self}"); + + self.fd.get_flags().await + } + + #[doc = "/ Get the dynamic type of a descriptor."] + #[doc = "/"] + #[doc = "/ Note: This returns the same value as the `type` field of the `fd-stat`"] + #[doc = "/ returned by `stat`, `stat-at` and similar."] + #[doc = "/"] + #[doc = "/ Note: This returns similar flags to the `st_mode & S_IFMT` value provided"] + #[doc = "/ by `fstat` in POSIX."] + #[doc = "/"] + #[doc = "/ Note: This returns the value that was the `fs_filetype` value returned"] + #[doc = "/ from `fdstat_get` in earlier versions of WASI."] + #[allow(async_fn_in_trait)] + async fn get_type(&self) -> Result { + trace!("CALL wasi:filesystem/types#descriptor.get-type FD={self}"); + + self.fd.get_type().await + } + + #[doc = "/ Adjust the size of an open file. If this increases the file\'s size, the"] + #[doc = "/ extra bytes are filled with zeros."] + #[doc = "/"] + #[doc = "/ Note: This was called `fd_filestat_set_size` in earlier versions of WASI."] + #[allow(async_fn_in_trait)] + async fn set_size(&self, size: Filesize) -> Result<(), ErrorCode> { + trace!("CALL wasi:filesystem/types#descriptor.set-size FD={self} SIZE={size}"); + + self.fd.set_size(size).await + } + + #[doc = "/ Adjust the timestamps of an open file or directory."] + #[doc = "/"] + #[doc = "/ Note: This is similar to `futimens` in POSIX."] + #[doc = "/"] + #[doc = "/ Note: This was called `fd_filestat_set_times` in earlier versions of WASI."] + #[allow(async_fn_in_trait)] + async fn set_times( &self, data_access_timestamp: NewTimestamp, data_modification_timestamp: NewTimestamp, ) -> Result<(), ErrorCode> { - trace!("CALL wasi:filesystem/types#descriptor.set-times FD={self:?}"); + trace!("CALL wasi:filesystem/types#descriptor.set-times FD={self} ACCESS-TIMESTAMP={data_access_timestamp} MODIFICATION-TIMESTAMP={data_modification_timestamp}"); - let data_access_timestamp = new_timestamp_map_in(data_access_timestamp); - let data_modification_timestamp = new_timestamp_map_in(data_modification_timestamp); self.fd .set_times(data_access_timestamp, data_modification_timestamp) - .map_err(error_code_map) - } - - #[doc = " Read from a descriptor, without using and updating the descriptor\'s offset."] - #[doc = ""] - #[doc = " This function returns a list of bytes containing the data that was"] - #[doc = " read, along with a bool which, when true, indicates that the end of the"] - #[doc = " file was reached. The returned list will contain up to `length` bytes; it"] - #[doc = " may return fewer than requested, if the end of the file is reached or"] - #[doc = " if the I/O operation is interrupted."] - #[doc = ""] - #[doc = " In the future, this may change to return a `stream`."] - #[doc = ""] - #[doc = " Note: This is similar to `pread` in POSIX."] - fn read(&self, length: Filesize, offset: Filesize) -> Result<(Vec, bool), ErrorCode> { - trace!("CALL wasi:filesystem/types#descriptor.read FD={self:?}"); - - self.fd.read(length, offset).map_err(error_code_map) - } - - #[doc = " Write to a descriptor, without using and updating the descriptor\'s offset."] - #[doc = ""] - #[doc = " It is valid to write past the end of a file; the file is extended to the"] - #[doc = " extent of the write, with bytes between the previous end and the start of"] - #[doc = " the write set to zero."] - #[doc = ""] - #[doc = " In the future, this may change to take a `stream`."] - #[doc = ""] - #[doc = " Note: This is similar to `pwrite` in POSIX."] - fn write(&self, buffer: Vec, offset: Filesize) -> Result { - trace!("CALL wasi:filesystem/types#descriptor.write FD={self:?}"); - - self.fd.write(&buffer, offset).map_err(error_code_map) - } - - #[doc = " Read directory entries from a directory."] - #[doc = ""] - #[doc = " On filesystems where directories contain entries referring to themselves"] - #[doc = " and their parents, often named `.` and `..` respectively, these entries"] - #[doc = " are omitted."] - #[doc = ""] - #[doc = " This always returns a new stream which starts at the beginning of the"] - #[doc = " directory. Multiple streams may be active on the same directory, and they"] - #[doc = " do not interfere with each other."] - fn read_directory(&self) -> Result { - trace!("CALL wasi:filesystem/types#descriptor.read-directory FD={self:?}"); - - self.fd - .read_directory() - .map(directory_entry_stream_map) - .map_err(error_code_map) - } - - #[doc = " Synchronize the data and metadata of a file to disk."] - #[doc = ""] - #[doc = " This function succeeds with no effect if the file descriptor is not"] - #[doc = " opened for writing."] - #[doc = ""] - #[doc = " Note: This is similar to `fsync` in POSIX."] - fn sync(&self) -> Result<(), ErrorCode> { - trace!("CALL wasi:filesystem/types#descriptor.sync FD={self:?}"); - - self.fd.sync().map_err(error_code_map) - } - - #[doc = " Create a directory."] - #[doc = ""] - #[doc = " Note: This is similar to `mkdirat` in POSIX."] - fn create_directory_at(&self, path: String) -> Result<(), ErrorCode> { - trace!("CALL wasi:filesystem/types#descriptor.create-directory-at FD={self:?} PATH={path}",); + .await + } + + #[doc = "/ Read directory entries from a directory."] + #[doc = "/"] + #[doc = "/ On filesystems where directories contain entries referring to themselves"] + #[doc = "/ and their parents, often named `.` and `..` respectively, these entries"] + #[doc = "/ are omitted."] + #[doc = "/"] + #[doc = "/ This always returns a new stream which starts at the beginning of the"] + #[doc = "/ directory. Multiple streams may be active on the same directory, and they"] + #[doc = "/ do not interfere with each other."] + #[doc = "/"] + #[doc = "/ This function returns a future, which will resolve to an error code if"] + #[doc = "/ reading full contents of the directory fails."] + #[allow(async_fn_in_trait)] + fn read_directory( + &self, + ) -> ( + wit_bindgen::StreamReader, + wit_bindgen::FutureReader>, + ) { + trace!("CALL wasi:filesystem/types#descriptor.read-directory FD={self}"); + + self.fd.read_directory() + } + + #[doc = "/ Synchronize the data and metadata of a file to disk."] + #[doc = "/"] + #[doc = "/ This function succeeds with no effect if the file descriptor is not"] + #[doc = "/ opened for writing."] + #[doc = "/"] + #[doc = "/ Note: This is similar to `fsync` in POSIX."] + #[allow(async_fn_in_trait)] + async fn sync(&self) -> Result<(), ErrorCode> { + trace!("CALL wasi:filesystem/types#descriptor.sync FD={self}"); + + self.fd.sync().await + } + + #[doc = "/ Create a directory."] + #[doc = "/"] + #[doc = "/ Note: This is similar to `mkdirat` in POSIX."] + #[allow(async_fn_in_trait)] + async fn create_directory_at(&self, path: String) -> Result<(), ErrorCode> { + trace!("CALL wasi:filesystem/types#descriptor.create-directory-at FD={self} PATH={path}"); + + self.fd.create_directory_at(path).await + } + + #[doc = "/ Return the attributes of an open file or directory."] + #[doc = "/"] + #[doc = "/ Note: This is similar to `fstat` in POSIX, except that it does not return"] + #[doc = "/ device and inode information. For testing whether two descriptors refer to"] + #[doc = "/ the same underlying filesystem object, use `is-same-object`. To obtain"] + #[doc = "/ additional data that can be used do determine whether a file has been"] + #[doc = "/ modified, use `metadata-hash`."] + #[doc = "/"] + #[doc = "/ Note: This was called `fd_filestat_get` in earlier versions of WASI."] + #[allow(async_fn_in_trait)] + async fn stat(&self) -> Result { + trace!("CALL wasi:filesystem/types#descriptor.stat FD={self}"); + + self.fd.stat().await + } + + #[doc = "/ Return the attributes of a file or directory."] + #[doc = "/"] + #[doc = "/ Note: This is similar to `fstatat` in POSIX, except that it does not"] + #[doc = "/ return device and inode information. See the `stat` description for a"] + #[doc = "/ discussion of alternatives."] + #[doc = "/"] + #[doc = "/ Note: This was called `path_filestat_get` in earlier versions of WASI."] + #[allow(async_fn_in_trait)] + async fn stat_at( + &self, + path_flags: PathFlags, + path: String, + ) -> Result { + trace!("CALL wasi:filesystem/types#descriptor.stat-at FD={self} PATH-FLAGS={path_flags} PATH={path}"); - self.fd.create_directory_at(&path).map_err(error_code_map) + self.fd.stat_at(path_flags, path).await } - #[doc = " Return the attributes of an open file or directory."] - #[doc = ""] - #[doc = " Note: This is similar to `fstat` in POSIX, except that it does not return"] - #[doc = " device and inode information. For testing whether two descriptors refer to"] - #[doc = " the same underlying filesystem object, use `is-same-object`. To obtain"] - #[doc = " additional data that can be used do determine whether a file has been"] - #[doc = " modified, use `metadata-hash`."] - #[doc = ""] - #[doc = " Note: This was called `fd_filestat_get` in earlier versions of WASI."] - fn stat(&self) -> Result { - trace!("CALL wasi:filesystem/types#descriptor.stat FD={self:?}"); - - self.fd - .stat() - .map(descriptor_stat_map) - .map_err(error_code_map) - } - - #[doc = " Return the attributes of a file or directory."] - #[doc = ""] - #[doc = " Note: This is similar to `fstatat` in POSIX, except that it does not"] - #[doc = " return device and inode information. See the `stat` description for a"] - #[doc = " discussion of alternatives."] - #[doc = ""] - #[doc = " Note: This was called `path_filestat_get` in earlier versions of WASI."] - fn stat_at(&self, path_flags: PathFlags, path: String) -> Result { - trace!("CALL wasi:filesystem/types#descriptor.stat-at FD={self:?} PATH={path}"); - - let path_flags = types::PathFlags::from_bits(path_flags.bits()).unwrap(); - self.fd - .stat_at(path_flags, &path) - .map(descriptor_stat_map) - .map_err(error_code_map) - } - - #[doc = " Adjust the timestamps of a file or directory."] - #[doc = ""] - #[doc = " Note: This is similar to `utimensat` in POSIX."] - #[doc = ""] - #[doc = " Note: This was called `path_filestat_set_times` in earlier versions of"] - #[doc = " WASI."] - fn set_times_at( + #[doc = "/ Adjust the timestamps of a file or directory."] + #[doc = "/"] + #[doc = "/ Note: This is similar to `utimensat` in POSIX."] + #[doc = "/"] + #[doc = "/ Note: This was called `path_filestat_set_times` in earlier versions of"] + #[doc = "/ WASI."] + #[allow(async_fn_in_trait)] + async fn set_times_at( &self, path_flags: PathFlags, path: String, data_access_timestamp: NewTimestamp, data_modification_timestamp: NewTimestamp, ) -> Result<(), ErrorCode> { - trace!("CALL wasi:filesystem/types#descriptor.set-times-at FD={self:?} PATH={path}",); + trace!("CALL wasi:filesystem/types#descriptor.set-times-at FD={self} PATH-FLAGS={path_flags} PATH={path} ACCESS-TIMESTAMP={data_access_timestamp} MODIFICATION-TIMESTAMP={data_modification_timestamp}"); - let path_flags = types::PathFlags::from_bits(path_flags.bits()).unwrap(); - let data_access_timestamp = new_timestamp_map_in(data_access_timestamp); - let data_modification_timestamp = new_timestamp_map_in(data_modification_timestamp); self.fd .set_times_at( path_flags, - &path, + path, data_access_timestamp, data_modification_timestamp, ) - .map_err(error_code_map) - } - - #[doc = " Create a hard link."] - #[doc = ""] - #[doc = " Note: This is similar to `linkat` in POSIX."] - fn link_at( + .await + } + + #[doc = "/ Create a hard link."] + #[doc = "/"] + #[doc = "/ Fails with `error-code::no-entry` if the old path does not exist,"] + #[doc = "/ with `error-code::exist` if the new path already exists, and"] + #[doc = "/ `error-code::not-permitted` if the old path is not a file."] + #[doc = "/"] + #[doc = "/ Note: This is similar to `linkat` in POSIX."] + #[allow(async_fn_in_trait)] + async fn link_at( &self, old_path_flags: PathFlags, old_path: String, new_descriptor: DescriptorBorrow<'_>, new_path: String, ) -> Result<(), ErrorCode> { - trace!("CALL wasi:filesystem/types#descriptor.link-at FD={self:?} PATH={old_path}",); - - let old_path_flags = types::PathFlags::from_bits(old_path_flags.bits()).unwrap(); let new_descriptor: &Self = new_descriptor.get(); + trace!("CALL wasi:filesystem/types#descriptor.link-at FD={self} OLD-PATH-FLAGS={old_path_flags} OLD-PATH={old_path} NEW-DESCRIPTOR={new_descriptor} NEW-PATH={new_path}"); + self.fd - .link_at(old_path_flags, &old_path, &new_descriptor.fd, &new_path) - .map_err(error_code_map) - } - - #[doc = " Open a file or directory."] - #[doc = ""] - #[doc = " The returned descriptor is not guaranteed to be the lowest-numbered"] - #[doc = " descriptor not currently open/ it is randomized to prevent applications"] - #[doc = " from depending on making assumptions about indexes, since this is"] - #[doc = " error-prone in multi-threaded contexts. The returned descriptor is"] - #[doc = " guaranteed to be less than 2**31."] - #[doc = ""] - #[doc = " If `flags` contains `descriptor-flags::mutate-directory`, and the base"] - #[doc = " descriptor doesn\'t have `descriptor-flags::mutate-directory` set,"] - #[doc = " `open-at` fails with `error-code::read-only`."] - #[doc = ""] - #[doc = " If `flags` contains `write` or `mutate-directory`, or `open-flags`"] - #[doc = " contains `truncate` or `create`, and the base descriptor doesn\'t have"] - #[doc = " `descriptor-flags::mutate-directory` set, `open-at` fails with"] - #[doc = " `error-code::read-only`."] - #[doc = ""] - #[doc = " Note: This is similar to `openat` in POSIX."] - fn open_at( + .link_at(old_path_flags, old_path, &new_descriptor.fd, new_path) + .await + } + + #[doc = "/ Open a file or directory."] + #[doc = "/"] + #[doc = "/ If `flags` contains `descriptor-flags::mutate-directory`, and the base"] + #[doc = "/ descriptor doesn\'t have `descriptor-flags::mutate-directory` set,"] + #[doc = "/ `open-at` fails with `error-code::read-only`."] + #[doc = "/"] + #[doc = "/ If `flags` contains `write` or `mutate-directory`, or `open-flags`"] + #[doc = "/ contains `truncate` or `create`, and the base descriptor doesn\'t have"] + #[doc = "/ `descriptor-flags::mutate-directory` set, `open-at` fails with"] + #[doc = "/ `error-code::read-only`."] + #[doc = "/"] + #[doc = "/ Note: This is similar to `openat` in POSIX."] + #[allow(async_fn_in_trait)] + async fn open_at( &self, path_flags: PathFlags, path: String, open_flags: OpenFlags, flags: DescriptorFlags, ) -> Result { - trace!("CALL wasi:filesystem/types#descriptor.open-at FD={self:?} PATH={path}",); + trace!("CALL wasi:filesystem/types#descriptor.open-at FD={self} PATH-FLAGS={path_flags} PATH={path} OPEN-FLAGS={open_flags} FLAGS={flags}"); - let path_flags = types::PathFlags::from_bits(path_flags.bits()).unwrap(); - let open_flags = types::OpenFlags::from_bits(open_flags.bits()).unwrap(); - let flags = types::DescriptorFlags::from_bits(flags.bits()).unwrap(); self.fd - .open_at(path_flags, &path, open_flags, flags) - .map(descriptor_map) - .map_err(error_code_map) + .open_at(path_flags, path.clone(), open_flags, flags) + .await + .map(|fd| { + let path = self.path.clone().join(path); + Descriptor::new(TracingDescriptor::new(fd, path)) + }) } - #[doc = " Read the contents of a symbolic link."] - #[doc = ""] - #[doc = " If the contents contain an absolute or rooted path in the underlying"] - #[doc = " filesystem, this function fails with `error-code::not-permitted`."] - #[doc = ""] - #[doc = " Note: This is similar to `readlinkat` in POSIX."] - fn readlink_at(&self, path: String) -> Result { - trace!("CALL wasi:filesystem/types#descriptor.readlink-at FD={self:?} PATH={path}",); + #[doc = "/ Read the contents of a symbolic link."] + #[doc = "/"] + #[doc = "/ If the contents contain an absolute or rooted path in the underlying"] + #[doc = "/ filesystem, this function fails with `error-code::not-permitted`."] + #[doc = "/"] + #[doc = "/ Note: This is similar to `readlinkat` in POSIX."] + #[allow(async_fn_in_trait)] + async fn readlink_at(&self, path: String) -> Result { + trace!("CALL wasi:filesystem/types#descriptor.readlink-at FD={self} PATH={path}"); - self.fd.readlink_at(&path).map_err(error_code_map) + self.fd.readlink_at(path).await } - #[doc = " Remove a directory."] - #[doc = ""] - #[doc = " Return `error-code::not-empty` if the directory is not empty."] - #[doc = ""] - #[doc = " Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX."] - fn remove_directory_at(&self, path: String) -> Result<(), ErrorCode> { - trace!("CALL wasi:filesystem/types#descriptor.remove-directory-at FD={self:?} PATH={path}",); + #[doc = "/ Remove a directory."] + #[doc = "/"] + #[doc = "/ Return `error-code::not-empty` if the directory is not empty."] + #[doc = "/"] + #[doc = "/ Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX."] + #[allow(async_fn_in_trait)] + async fn remove_directory_at(&self, path: String) -> Result<(), ErrorCode> { + trace!("CALL wasi:filesystem/types#descriptor.remove-directory-at FD={self} PATH={path}"); - self.fd.remove_directory_at(&path).map_err(error_code_map) + self.fd.remove_directory_at(path).await } - #[doc = " Rename a filesystem object."] - #[doc = ""] - #[doc = " Note: This is similar to `renameat` in POSIX."] - fn rename_at( + #[doc = "/ Rename a filesystem object."] + #[doc = "/"] + #[doc = "/ Note: This is similar to `renameat` in POSIX."] + #[allow(async_fn_in_trait)] + async fn rename_at( &self, old_path: String, new_descriptor: DescriptorBorrow<'_>, new_path: String, ) -> Result<(), ErrorCode> { - trace!("CALL wasi:filesystem/types#descriptor.rename-at FD={self:?} PATH={old_path}",); - let new_descriptor: &Self = new_descriptor.get(); - self.fd - .rename_at(&old_path, &new_descriptor.fd, &new_path) - .map_err(error_code_map) - } - - #[doc = " Create a symbolic link (also known as a \"symlink\")."] - #[doc = ""] - #[doc = " If `old-path` starts with `/`, the function fails with"] - #[doc = " `error-code::not-permitted`."] - #[doc = ""] - #[doc = " Note: This is similar to `symlinkat` in POSIX."] - fn symlink_at(&self, old_path: String, new_path: String) -> Result<(), ErrorCode> { - trace!("CALL wasi:filesystem/types#descriptor.symlink-at FD={self:?} PATH={old_path}",); + trace!("CALL wasi:filesystem/types#descriptor.rename-at FD={self} OLD-PATH={old_path} NEW-DESCRIPTOR={new_descriptor} NEW-PATH={new_path}"); self.fd - .symlink_at(&old_path, &new_path) - .map_err(error_code_map) - } - - #[doc = " Unlink a filesystem object that is not a directory."] - #[doc = ""] - #[doc = " Return `error-code::is-directory` if the path refers to a directory."] - #[doc = " Note: This is similar to `unlinkat(fd, path, 0)` in POSIX."] - fn unlink_file_at(&self, path: String) -> Result<(), ErrorCode> { - trace!("CALL wasi:filesystem/types#descriptor.unlink-file-at FD={self:?} PATH={path}",); - - self.fd.unlink_file_at(&path).map_err(error_code_map) - } - - #[doc = " Test whether two descriptors refer to the same filesystem object."] - #[doc = ""] - #[doc = " In POSIX, this corresponds to testing whether the two descriptors have the"] - #[doc = " same device (`st_dev`) and inode (`st_ino` or `d_ino`) numbers."] - #[doc = " wasi-filesystem does not expose device and inode numbers, so this function"] - #[doc = " may be used instead."] - fn is_same_object(&self, other: DescriptorBorrow<'_>) -> bool { + .rename_at(old_path, &new_descriptor.fd, new_path) + .await + } + + #[doc = "/ Create a symbolic link (also known as a \"symlink\")."] + #[doc = "/"] + #[doc = "/ If `old-path` starts with `/`, the function fails with"] + #[doc = "/ `error-code::not-permitted`."] + #[doc = "/"] + #[doc = "/ Note: This is similar to `symlinkat` in POSIX."] + #[allow(async_fn_in_trait)] + async fn symlink_at(&self, old_path: String, new_path: String) -> Result<(), ErrorCode> { + trace!("CALL wasi:filesystem/types#descriptor.symlink-at FD={self} OLD-PATH={old_path} NEW-PATH={new_path}"); + + self.fd.symlink_at(old_path, new_path).await + } + + #[doc = "/ Unlink a filesystem object that is not a directory."] + #[doc = "/"] + #[doc = "/ This is similar to `unlinkat(fd, path, 0)` in POSIX."] + #[doc = "/"] + #[doc = "/ Error returns are as specified by POSIX."] + #[doc = "/"] + #[doc = "/ If the filesystem object is a directory, `error-code::access` or"] + #[doc = "/ `error-code::is-directory` may be returned instead of the"] + #[doc = "/ POSIX-specified `error-code::not-permitted`."] + #[allow(async_fn_in_trait)] + async fn unlink_file_at(&self, path: String) -> Result<(), ErrorCode> { + trace!("CALL wasi:filesystem/types#descriptor.unlink-file-at FD={self} PATH={path}"); + + self.fd.unlink_file_at(path).await + } + + #[doc = "/ Test whether two descriptors refer to the same filesystem object."] + #[doc = "/"] + #[doc = "/ In POSIX, this corresponds to testing whether the two descriptors have the"] + #[doc = "/ same device (`st_dev`) and inode (`st_ino` or `d_ino`) numbers."] + #[doc = "/ wasi-filesystem does not expose device and inode numbers, so this function"] + #[doc = "/ may be used instead."] + #[allow(async_fn_in_trait)] + async fn is_same_object(&self, other: DescriptorBorrow<'_>) -> bool { let other: &Self = other.get(); - trace!("CALL wasi:filesystem/types#descriptor.is-same-object FD1={self:?} FD2={other:?}",); - - self.fd.is_same_object(&other.fd) - } - - #[doc = " Return a hash of the metadata associated with a filesystem object referred"] - #[doc = " to by a descriptor."] - #[doc = ""] - #[doc = " This returns a hash of the last-modification timestamp and file size, and"] - #[doc = " may also include the inode number, device number, birth timestamp, and"] - #[doc = " other metadata fields that may change when the file is modified or"] - #[doc = " replaced. It may also include a secret value chosen by the"] - #[doc = " implementation and not otherwise exposed."] - #[doc = ""] - #[doc = " Implementations are encourated to provide the following properties:"] - #[doc = ""] - #[doc = " - If the file is not modified or replaced, the computed hash value should"] - #[doc = " usually not change."] - #[doc = " - If the object is modified or replaced, the computed hash value should"] - #[doc = " usually change."] - #[doc = " - The inputs to the hash should not be easily computable from the"] - #[doc = " computed hash."] - #[doc = ""] - #[doc = " However, none of these is required."] - fn metadata_hash(&self) -> Result { - trace!("CALL wasi:filesystem/types#descriptor.metadata-hash FD={self:?}"); - - self.fd - .metadata_hash() - .map(metadata_hash_value_map) - .map_err(error_code_map) - } - - #[doc = " Return a hash of the metadata associated with a filesystem object referred"] - #[doc = " to by a directory descriptor and a relative path."] - #[doc = ""] - #[doc = " This performs the same hash computation as `metadata-hash`."] - fn metadata_hash_at( + trace!("CALL wasi:filesystem/types#descriptor.is-same-object FD={self} OTHER={other}"); + + self.fd.is_same_object(&other.fd).await + } + + #[doc = "/ Return a hash of the metadata associated with a filesystem object referred"] + #[doc = "/ to by a descriptor."] + #[doc = "/"] + #[doc = "/ This returns a hash of the last-modification timestamp and file size, and"] + #[doc = "/ may also include the inode number, device number, birth timestamp, and"] + #[doc = "/ other metadata fields that may change when the file is modified or"] + #[doc = "/ replaced. It may also include a secret value chosen by the"] + #[doc = "/ implementation and not otherwise exposed."] + #[doc = "/"] + #[doc = "/ Implementations are encouraged to provide the following properties:"] + #[doc = "/"] + #[doc = "/ - If the file is not modified or replaced, the computed hash value should"] + #[doc = "/ usually not change."] + #[doc = "/ - If the object is modified or replaced, the computed hash value should"] + #[doc = "/ usually change."] + #[doc = "/ - The inputs to the hash should not be easily computable from the"] + #[doc = "/ computed hash."] + #[doc = "/"] + #[doc = "/ However, none of these is required."] + #[allow(async_fn_in_trait)] + async fn metadata_hash(&self) -> Result { + trace!("CALL wasi:filesystem/types#descriptor.metadata-hash FD={self}"); + + self.fd.metadata_hash().await + } + + #[doc = "/ Return a hash of the metadata associated with a filesystem object referred"] + #[doc = "/ to by a directory descriptor and a relative path."] + #[doc = "/"] + #[doc = "/ This performs the same hash computation as `metadata-hash`."] + #[allow(async_fn_in_trait)] + async fn metadata_hash_at( &self, path_flags: PathFlags, path: String, ) -> Result { - trace!("CALL wasi:filesystem/types#descriptor.metadata-hash-at FD={self:?} PATH={path}",); - - let path_flags = types::PathFlags::from_bits(path_flags.bits()).unwrap(); - self.fd - .metadata_hash_at(path_flags, &path) - .map(metadata_hash_value_map) - .map_err(error_code_map) - } -} - -#[derive(Debug, Clone)] -struct TracingDirectoryEntryStream { - des: Rc, -} + trace!("CALL wasi:filesystem/types#descriptor.metadata-hash-at FD={self} PATH-FLAGS={path_flags} PATH={path}"); -impl TracingDirectoryEntryStream { - fn new(des: types::DirectoryEntryStream) -> Self { - Self { des: Rc::new(des) } + self.fd.metadata_hash_at(path_flags, path).await } } -impl exports::wasi::filesystem::types::GuestDirectoryEntryStream for TracingDirectoryEntryStream { - #[doc = " Read a single directory entry from a `directory-entry-stream`."] - fn read_directory_entry(&self) -> Result, ErrorCode> { - trace!("CALL wasi:filesystem/types#read-directory-entry SID={self:?}"); - - self.des - .read_directory_entry() - .map(|de| de.map(directory_entry_map)) - .map_err(error_code_map) +impl Display for TracingDescriptor { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str( + &self + .path + .to_str() + .expect("path contains invalid unicode characters"), + ) } } -fn advice_map_in(advice: Advice) -> types::Advice { - match advice { - Advice::Normal => types::Advice::Normal, - Advice::Sequential => types::Advice::Sequential, - Advice::Random => types::Advice::Random, - Advice::WillNeed => types::Advice::WillNeed, - Advice::DontNeed => types::Advice::DontNeed, - Advice::NoReuse => types::Advice::NoReuse, +impl Display for types::Advice { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(&self.to_string().to_kebab_case()) } } -fn descriptor_map(descriptor: types::Descriptor) -> Descriptor { - Descriptor::new(TracingDescriptor::new(descriptor)) -} - -fn descriptor_flags_map(descriptor_flags: types::DescriptorFlags) -> DescriptorFlags { - DescriptorFlags::from_bits(descriptor_flags.bits()).unwrap() -} - -fn descriptor_stat_map(descriptor_stat: types::DescriptorStat) -> DescriptorStat { - DescriptorStat { - type_: descriptor_type_map(descriptor_stat.type_), - link_count: descriptor_stat.link_count, - size: descriptor_stat.size, - data_access_timestamp: descriptor_stat.data_access_timestamp, - data_modification_timestamp: descriptor_stat.data_modification_timestamp, - status_change_timestamp: descriptor_stat.status_change_timestamp, +impl Display for types::DescriptorFlags { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let names: Vec = self + .iter_names() + .map(|(name, _flags)| name.to_kebab_case()) + .collect(); + f.write_fmt(format_args!("({})", &names.join("|"))) } } -fn descriptor_type_map(descriptor_type: types::DescriptorType) -> DescriptorType { - match descriptor_type { - types::DescriptorType::Unknown => DescriptorType::Unknown, - types::DescriptorType::BlockDevice => DescriptorType::BlockDevice, - types::DescriptorType::CharacterDevice => DescriptorType::CharacterDevice, - types::DescriptorType::Directory => DescriptorType::Directory, - types::DescriptorType::Fifo => DescriptorType::Fifo, - types::DescriptorType::SymbolicLink => DescriptorType::SymbolicLink, - types::DescriptorType::RegularFile => DescriptorType::RegularFile, - types::DescriptorType::Socket => DescriptorType::Socket, +impl Display for types::OpenFlags { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let names: Vec = self + .iter_names() + .map(|(name, _flags)| name.to_kebab_case()) + .collect(); + f.write_fmt(format_args!("({})", &names.join("|"))) } } -fn directory_entry_map(directory_entry: types::DirectoryEntry) -> DirectoryEntry { - DirectoryEntry { - name: directory_entry.name, - type_: descriptor_type_map(directory_entry.type_), +impl Display for types::PathFlags { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let names: Vec = self + .iter_names() + .map(|(name, _flags)| name.to_kebab_case()) + .collect(); + f.write_fmt(format_args!("({})", &names.join("|"))) } } -fn directory_entry_stream_map( - directory_entry_stream: types::DirectoryEntryStream, -) -> DirectoryEntryStream { - DirectoryEntryStream::new(TracingDirectoryEntryStream::new(directory_entry_stream)) -} - -fn error_code_map(error_code: types::ErrorCode) -> ErrorCode { - match error_code { - types::ErrorCode::Access => ErrorCode::Access, - types::ErrorCode::WouldBlock => ErrorCode::WouldBlock, - types::ErrorCode::Already => ErrorCode::Already, - types::ErrorCode::BadDescriptor => ErrorCode::BadDescriptor, - types::ErrorCode::Busy => ErrorCode::Busy, - types::ErrorCode::Deadlock => ErrorCode::Deadlock, - types::ErrorCode::Quota => ErrorCode::Quota, - types::ErrorCode::Exist => ErrorCode::Exist, - types::ErrorCode::FileTooLarge => ErrorCode::FileTooLarge, - types::ErrorCode::IllegalByteSequence => ErrorCode::IllegalByteSequence, - types::ErrorCode::InProgress => ErrorCode::InProgress, - types::ErrorCode::Interrupted => ErrorCode::Interrupted, - types::ErrorCode::Invalid => ErrorCode::Invalid, - types::ErrorCode::Io => ErrorCode::Io, - types::ErrorCode::IsDirectory => ErrorCode::IsDirectory, - types::ErrorCode::Loop => ErrorCode::Loop, - types::ErrorCode::TooManyLinks => ErrorCode::TooManyLinks, - types::ErrorCode::MessageSize => ErrorCode::MessageSize, - types::ErrorCode::NameTooLong => ErrorCode::NameTooLong, - types::ErrorCode::NoDevice => ErrorCode::NoDevice, - types::ErrorCode::NoEntry => ErrorCode::NoEntry, - types::ErrorCode::NoLock => ErrorCode::NoLock, - types::ErrorCode::InsufficientMemory => ErrorCode::InsufficientMemory, - types::ErrorCode::InsufficientSpace => ErrorCode::InsufficientSpace, - types::ErrorCode::NotDirectory => ErrorCode::NotDirectory, - types::ErrorCode::NotEmpty => ErrorCode::NotEmpty, - types::ErrorCode::NotRecoverable => ErrorCode::NotRecoverable, - types::ErrorCode::Unsupported => ErrorCode::Unsupported, - types::ErrorCode::NoTty => ErrorCode::NoTty, - types::ErrorCode::NoSuchDevice => ErrorCode::NoSuchDevice, - types::ErrorCode::Overflow => ErrorCode::Overflow, - types::ErrorCode::NotPermitted => ErrorCode::NotPermitted, - types::ErrorCode::Pipe => ErrorCode::Pipe, - types::ErrorCode::ReadOnly => ErrorCode::ReadOnly, - types::ErrorCode::InvalidSeek => ErrorCode::InvalidSeek, - types::ErrorCode::TextFileBusy => ErrorCode::TextFileBusy, - types::ErrorCode::CrossDevice => ErrorCode::CrossDevice, +impl Display for types::NewTimestamp { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + types::NewTimestamp::NoChange => f.write_str("no-change"), + types::NewTimestamp::Now => f.write_str("now"), + types::NewTimestamp::Timestamp(instant) => { + f.write_fmt(format_args!("timestamp<{instant}>")) + } + } } } -fn metadata_hash_value_map(metadata_hash_value: types::MetadataHashValue) -> MetadataHashValue { - MetadataHashValue { - lower: metadata_hash_value.lower, - upper: metadata_hash_value.upper, - } -} +impl Display for types::Instant { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let date = DateTime::from_timestamp(self.seconds as i64, self.nanoseconds) + .unwrap() + .format("%Y-%m-%d %H:%M:%S.%3fZ"); -fn new_timestamp_map_in(timestamp: NewTimestamp) -> types::NewTimestamp { - match timestamp { - NewTimestamp::NoChange => types::NewTimestamp::NoChange, - NewTimestamp::Now => types::NewTimestamp::Now, - NewTimestamp::Timestamp(dt) => types::NewTimestamp::Timestamp(dt), + f.write_fmt(format_args!("{date}")) } } wit_bindgen::generate!({ path: "../../wit", world: "filesystem", + merge_structurally_equal_types: true, generate_all }); diff --git a/rust-toolchain.toml b/rust-toolchain.toml index d41f3c6..aa8b8f1 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] channel = "1.96" -targets = [ "wasm32-unknown-unknown", "wasm32-wasip2" ] +targets = [ "wasm32-unknown-unknown" ] diff --git a/wit/deps/wasi-clocks-0.2.6/package.wit b/wit/deps/wasi-clocks-0.2.6/package.wit deleted file mode 100644 index 68b5a40..0000000 --- a/wit/deps/wasi-clocks-0.2.6/package.wit +++ /dev/null @@ -1,13 +0,0 @@ -package wasi:clocks@0.2.6; - -interface wall-clock { - record datetime { - seconds: u64, - nanoseconds: u32, - } - - now: func() -> datetime; - - resolution: func() -> datetime; -} - diff --git a/wit/deps/wasi-clocks-0.3.0/package.wit b/wit/deps/wasi-clocks-0.3.0/package.wit new file mode 100644 index 0000000..2594e02 --- /dev/null +++ b/wit/deps/wasi-clocks-0.3.0/package.wit @@ -0,0 +1,19 @@ +package wasi:clocks@0.3.0; + +interface types { + type duration = u64; +} + +interface system-clock { + use types.{duration}; + + record instant { + seconds: s64, + nanoseconds: u32, + } + + now: func() -> instant; + + get-resolution: func() -> duration; +} + diff --git a/wit/deps/wasi-filesystem-0.2.6/package.wit b/wit/deps/wasi-filesystem-0.3.0/package.wit similarity index 72% rename from wit/deps/wasi-filesystem-0.2.6/package.wit rename to wit/deps/wasi-filesystem-0.3.0/package.wit index 9f4a828..e4a778f 100644 --- a/wit/deps/wasi-filesystem-0.2.6/package.wit +++ b/wit/deps/wasi-filesystem-0.3.0/package.wit @@ -1,12 +1,9 @@ -package wasi:filesystem@0.2.6; +package wasi:filesystem@0.3.0; /// WASI filesystem is a filesystem API primarily intended to let users run WASI /// programs that access their files on their existing filesystems, without /// significant overhead. /// -/// It is intended to be roughly portable between Unix-family platforms and -/// Windows, though it does not hide many of the major differences. -/// /// Paths are passed as interface-type `string`s, meaning they must consist of /// a sequence of Unicode Scalar Values (USVs). Some filesystems may contain /// paths which are not accessible by this API. @@ -23,26 +20,35 @@ package wasi:filesystem@0.2.6; /// For more information about WASI path resolution and sandboxing, see /// [WASI filesystem path resolution]. /// +/// Though this package presents a portable interface modelled on POSIX, it +/// prioritizes compatibility over portability: allowing users to access their +/// files on their machine is more important than exposing a single semantics +/// across all platforms. Notably, depending on the underlying operating system +/// and file system: +/// * Paths may be case-folded or not. +/// * Deleting (unlinking) a file may fail if there are other file descriptors +/// open. +/// * Durability and atomicity of changes to underlying files when there are +/// concurrent writers. +/// +/// Users that need well-defined, portable semantics should use a key-value +/// store or a database instead. +/// /// [WASI filesystem path resolution]: https://github.com/WebAssembly/wasi-filesystem/blob/main/path-resolution.md -@since(version = 0.2.0) +@since(version = 0.3.0) interface types { - @since(version = 0.2.0) - use wasi:io/streams@0.2.6.{input-stream, output-stream, error}; - @since(version = 0.2.0) - use wasi:clocks/wall-clock@0.2.6.{datetime}; + @since(version = 0.3.0) + use wasi:clocks/system-clock@0.3.0.{instant}; /// File size or length of a region within a file. - @since(version = 0.2.0) + @since(version = 0.3.0) type filesize = u64; /// The type of a filesystem object referenced by a descriptor. /// /// Note: This was called `filetype` in earlier versions of WASI. - @since(version = 0.2.0) - enum descriptor-type { - /// The type of the descriptor or file is unknown or is different from - /// any of the other types specified. - unknown, + @since(version = 0.3.0) + variant descriptor-type { /// The descriptor refers to a block device inode. block-device, /// The descriptor refers to a character device inode. @@ -57,12 +63,15 @@ interface types { regular-file, /// The descriptor refers to a socket. socket, + /// The type of the descriptor or file is different from any of the + /// other types specified. + other(option), } /// Descriptor flags. /// /// Note: This was called `fdflags` in earlier versions of WASI. - @since(version = 0.2.0) + @since(version = 0.3.0) flags descriptor-flags { /// Read mode: Data can be read. read, @@ -104,7 +113,7 @@ interface types { } /// Flags determining the method of how paths are resolved. - @since(version = 0.2.0) + @since(version = 0.3.0) flags path-flags { /// As long as the resolved path corresponds to a symbolic link, it is /// expanded. @@ -112,7 +121,7 @@ interface types { } /// Open flags used by `open-at`. - @since(version = 0.2.0) + @since(version = 0.3.0) flags open-flags { /// Create file if it does not exist, similar to `O_CREAT` in POSIX. create, @@ -125,13 +134,13 @@ interface types { } /// Number of hard links to an inode. - @since(version = 0.2.0) + @since(version = 0.3.0) type link-count = u64; /// File attributes. /// /// Note: This was called `filestat` in earlier versions of WASI. - @since(version = 0.2.0) + @since(version = 0.3.0) record descriptor-stat { /// File type. %type: descriptor-type, @@ -144,21 +153,21 @@ interface types { /// /// If the `option` is none, the platform doesn't maintain an access /// timestamp for this file. - data-access-timestamp: option, + data-access-timestamp: option, /// Last data modification timestamp. /// /// If the `option` is none, the platform doesn't maintain a /// modification timestamp for this file. - data-modification-timestamp: option, + data-modification-timestamp: option, /// Last file status-change timestamp. /// /// If the `option` is none, the platform doesn't maintain a /// status-change timestamp for this file. - status-change-timestamp: option, + status-change-timestamp: option, } /// When setting a timestamp, this gives the value to set it to. - @since(version = 0.2.0) + @since(version = 0.3.0) variant new-timestamp { /// Leave the timestamp set to its previous value. no-change, @@ -166,10 +175,11 @@ interface types { /// with the filesystem. now, /// Set the timestamp to the given value. - timestamp(datetime), + timestamp(instant), } /// A directory entry. + @since(version = 0.3.0) record directory-entry { /// The type of the file referred to by this directory entry. %type: descriptor-type, @@ -181,11 +191,10 @@ interface types { /// Not all of these error codes are returned by the functions provided by this /// API; some are used in higher-level library layers, and others are provided /// merely for alignment with POSIX. - enum error-code { + @since(version = 0.3.0) + variant error-code { /// Permission denied, similar to `EACCES` in POSIX. access, - /// Resource unavailable, or operation would block, similar to `EAGAIN` and `EWOULDBLOCK` in POSIX. - would-block, /// Connection already in progress, similar to `EALREADY` in POSIX. already, /// Bad descriptor, similar to `EBADF` in POSIX. @@ -256,10 +265,14 @@ interface types { text-file-busy, /// Cross-device link, similar to `EXDEV` in POSIX. cross-device, + /// A catch-all for errors not captured by the existing variants. + /// Implementations can use this to extend the error type without + /// breaking existing code. + other(option), } /// File or memory access pattern advisory information. - @since(version = 0.2.0) + @since(version = 0.3.0) enum advice { /// The application has no advice to give on its behavior with respect /// to the specified data. @@ -283,7 +296,7 @@ interface types { /// A 128-bit hash value, split into parts because wasm doesn't have a /// 128-bit integer type. - @since(version = 0.2.0) + @since(version = 0.3.0) record metadata-hash-value { /// 64 bits of a 128-bit hash value. lower: u64, @@ -294,55 +307,71 @@ interface types { /// A descriptor is a reference to a filesystem object, which may be a file, /// directory, named pipe, special file, or other object on which filesystem /// calls may be made. - @since(version = 0.2.0) + @since(version = 0.3.0) resource descriptor { - /// Return a stream for reading from a file, if available. - /// - /// May fail with an error-code describing why the file cannot be read. + /// Return a stream for reading from a file. /// /// Multiple read, write, and append streams may be active on the same open /// file and they do not interfere with each other. /// - /// Note: This allows using `read-stream`, which is similar to `read` in POSIX. - @since(version = 0.2.0) - read-via-stream: func(offset: filesize) -> result; + /// This function returns a `stream` which provides the data received from the + /// file, and a `future` providing additional error information in case an + /// error is encountered. + /// + /// If no error is encountered, `stream.read` on the `stream` will return + /// `read-status::closed` with no `error-context` and the future resolves to + /// the value `ok`. If an error is encountered, `stream.read` on the + /// `stream` returns `read-status::closed` with an `error-context` and the future + /// resolves to `err` with an `error-code`. + /// + /// Note: This is similar to `pread` in POSIX. + @since(version = 0.3.0) + read-via-stream: func(offset: filesize) -> tuple, future>>; /// Return a stream for writing to a file, if available. /// /// May fail with an error-code describing why the file cannot be written. /// - /// Note: This allows using `write-stream`, which is similar to `write` in - /// POSIX. - @since(version = 0.2.0) - write-via-stream: func(offset: filesize) -> result; + /// It is valid to write past the end of a file; the file is extended to the + /// extent of the write, with bytes between the previous end and the start of + /// the write set to zero. + /// + /// This function returns once either full contents of the stream are + /// written or an error is encountered. + /// + /// Note: This is similar to `pwrite` in POSIX. + @since(version = 0.3.0) + write-via-stream: func(data: stream, offset: filesize) -> future>; /// Return a stream for appending to a file, if available. /// /// May fail with an error-code describing why the file cannot be appended. /// - /// Note: This allows using `write-stream`, which is similar to `write` with - /// `O_APPEND` in POSIX. - @since(version = 0.2.0) - append-via-stream: func() -> result; + /// This function returns once either full contents of the stream are + /// written or an error is encountered. + /// + /// Note: This is similar to `write` with `O_APPEND` in POSIX. + @since(version = 0.3.0) + append-via-stream: func(data: stream) -> future>; /// Provide file advisory information on a descriptor. /// /// This is similar to `posix_fadvise` in POSIX. - @since(version = 0.2.0) - advise: func(offset: filesize, length: filesize, advice: advice) -> result<_, error-code>; + @since(version = 0.3.0) + advise: async func(offset: filesize, length: filesize, advice: advice) -> result<_, error-code>; /// Synchronize the data of a file to disk. /// /// This function succeeds with no effect if the file descriptor is not /// opened for writing. /// /// Note: This is similar to `fdatasync` in POSIX. - @since(version = 0.2.0) - sync-data: func() -> result<_, error-code>; + @since(version = 0.3.0) + sync-data: async func() -> result<_, error-code>; /// Get flags associated with a descriptor. /// /// Note: This returns similar flags to `fcntl(fd, F_GETFL)` in POSIX. /// /// Note: This returns the value that was the `fs_flags` value returned /// from `fdstat_get` in earlier versions of WASI. - @since(version = 0.2.0) - get-flags: func() -> result; + @since(version = 0.3.0) + get-flags: async func() -> result; /// Get the dynamic type of a descriptor. /// /// Note: This returns the same value as the `type` field of the `fd-stat` @@ -353,45 +382,21 @@ interface types { /// /// Note: This returns the value that was the `fs_filetype` value returned /// from `fdstat_get` in earlier versions of WASI. - @since(version = 0.2.0) - get-type: func() -> result; + @since(version = 0.3.0) + get-type: async func() -> result; /// Adjust the size of an open file. If this increases the file's size, the /// extra bytes are filled with zeros. /// /// Note: This was called `fd_filestat_set_size` in earlier versions of WASI. - @since(version = 0.2.0) - set-size: func(size: filesize) -> result<_, error-code>; + @since(version = 0.3.0) + set-size: async func(size: filesize) -> result<_, error-code>; /// Adjust the timestamps of an open file or directory. /// /// Note: This is similar to `futimens` in POSIX. /// /// Note: This was called `fd_filestat_set_times` in earlier versions of WASI. - @since(version = 0.2.0) - set-times: func(data-access-timestamp: new-timestamp, data-modification-timestamp: new-timestamp) -> result<_, error-code>; - /// Read from a descriptor, without using and updating the descriptor's offset. - /// - /// This function returns a list of bytes containing the data that was - /// read, along with a bool which, when true, indicates that the end of the - /// file was reached. The returned list will contain up to `length` bytes; it - /// may return fewer than requested, if the end of the file is reached or - /// if the I/O operation is interrupted. - /// - /// In the future, this may change to return a `stream`. - /// - /// Note: This is similar to `pread` in POSIX. - @since(version = 0.2.0) - read: func(length: filesize, offset: filesize) -> result, bool>, error-code>; - /// Write to a descriptor, without using and updating the descriptor's offset. - /// - /// It is valid to write past the end of a file; the file is extended to the - /// extent of the write, with bytes between the previous end and the start of - /// the write set to zero. - /// - /// In the future, this may change to take a `stream`. - /// - /// Note: This is similar to `pwrite` in POSIX. - @since(version = 0.2.0) - write: func(buffer: list, offset: filesize) -> result; + @since(version = 0.3.0) + set-times: async func(data-access-timestamp: new-timestamp, data-modification-timestamp: new-timestamp) -> result<_, error-code>; /// Read directory entries from a directory. /// /// On filesystems where directories contain entries referring to themselves @@ -401,21 +406,24 @@ interface types { /// This always returns a new stream which starts at the beginning of the /// directory. Multiple streams may be active on the same directory, and they /// do not interfere with each other. - @since(version = 0.2.0) - read-directory: func() -> result; + /// + /// This function returns a future, which will resolve to an error code if + /// reading full contents of the directory fails. + @since(version = 0.3.0) + read-directory: func() -> tuple, future>>; /// Synchronize the data and metadata of a file to disk. /// /// This function succeeds with no effect if the file descriptor is not /// opened for writing. /// /// Note: This is similar to `fsync` in POSIX. - @since(version = 0.2.0) - sync: func() -> result<_, error-code>; + @since(version = 0.3.0) + sync: async func() -> result<_, error-code>; /// Create a directory. /// /// Note: This is similar to `mkdirat` in POSIX. - @since(version = 0.2.0) - create-directory-at: func(path: string) -> result<_, error-code>; + @since(version = 0.3.0) + create-directory-at: async func(path: string) -> result<_, error-code>; /// Return the attributes of an open file or directory. /// /// Note: This is similar to `fstat` in POSIX, except that it does not return @@ -425,8 +433,8 @@ interface types { /// modified, use `metadata-hash`. /// /// Note: This was called `fd_filestat_get` in earlier versions of WASI. - @since(version = 0.2.0) - stat: func() -> result; + @since(version = 0.3.0) + stat: async func() -> result; /// Return the attributes of a file or directory. /// /// Note: This is similar to `fstatat` in POSIX, except that it does not @@ -434,16 +442,16 @@ interface types { /// discussion of alternatives. /// /// Note: This was called `path_filestat_get` in earlier versions of WASI. - @since(version = 0.2.0) - stat-at: func(path-flags: path-flags, path: string) -> result; + @since(version = 0.3.0) + stat-at: async func(path-flags: path-flags, path: string) -> result; /// Adjust the timestamps of a file or directory. /// /// Note: This is similar to `utimensat` in POSIX. /// /// Note: This was called `path_filestat_set_times` in earlier versions of /// WASI. - @since(version = 0.2.0) - set-times-at: func(path-flags: path-flags, path: string, data-access-timestamp: new-timestamp, data-modification-timestamp: new-timestamp) -> result<_, error-code>; + @since(version = 0.3.0) + set-times-at: async func(path-flags: path-flags, path: string, data-access-timestamp: new-timestamp, data-modification-timestamp: new-timestamp) -> result<_, error-code>; /// Create a hard link. /// /// Fails with `error-code::no-entry` if the old path does not exist, @@ -451,8 +459,8 @@ interface types { /// `error-code::not-permitted` if the old path is not a file. /// /// Note: This is similar to `linkat` in POSIX. - @since(version = 0.2.0) - link-at: func(old-path-flags: path-flags, old-path: string, new-descriptor: borrow, new-path: string) -> result<_, error-code>; + @since(version = 0.3.0) + link-at: async func(old-path-flags: path-flags, old-path: string, new-descriptor: borrow, new-path: string) -> result<_, error-code>; /// Open a file or directory. /// /// If `flags` contains `descriptor-flags::mutate-directory`, and the base @@ -465,50 +473,55 @@ interface types { /// `error-code::read-only`. /// /// Note: This is similar to `openat` in POSIX. - @since(version = 0.2.0) - open-at: func(path-flags: path-flags, path: string, open-flags: open-flags, %flags: descriptor-flags) -> result; + @since(version = 0.3.0) + open-at: async func(path-flags: path-flags, path: string, open-flags: open-flags, %flags: descriptor-flags) -> result; /// Read the contents of a symbolic link. /// /// If the contents contain an absolute or rooted path in the underlying /// filesystem, this function fails with `error-code::not-permitted`. /// /// Note: This is similar to `readlinkat` in POSIX. - @since(version = 0.2.0) - readlink-at: func(path: string) -> result; + @since(version = 0.3.0) + readlink-at: async func(path: string) -> result; /// Remove a directory. /// /// Return `error-code::not-empty` if the directory is not empty. /// /// Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX. - @since(version = 0.2.0) - remove-directory-at: func(path: string) -> result<_, error-code>; + @since(version = 0.3.0) + remove-directory-at: async func(path: string) -> result<_, error-code>; /// Rename a filesystem object. /// /// Note: This is similar to `renameat` in POSIX. - @since(version = 0.2.0) - rename-at: func(old-path: string, new-descriptor: borrow, new-path: string) -> result<_, error-code>; + @since(version = 0.3.0) + rename-at: async func(old-path: string, new-descriptor: borrow, new-path: string) -> result<_, error-code>; /// Create a symbolic link (also known as a "symlink"). /// /// If `old-path` starts with `/`, the function fails with /// `error-code::not-permitted`. /// /// Note: This is similar to `symlinkat` in POSIX. - @since(version = 0.2.0) - symlink-at: func(old-path: string, new-path: string) -> result<_, error-code>; + @since(version = 0.3.0) + symlink-at: async func(old-path: string, new-path: string) -> result<_, error-code>; /// Unlink a filesystem object that is not a directory. /// - /// Return `error-code::is-directory` if the path refers to a directory. - /// Note: This is similar to `unlinkat(fd, path, 0)` in POSIX. - @since(version = 0.2.0) - unlink-file-at: func(path: string) -> result<_, error-code>; + /// This is similar to `unlinkat(fd, path, 0)` in POSIX. + /// + /// Error returns are as specified by POSIX. + /// + /// If the filesystem object is a directory, `error-code::access` or + /// `error-code::is-directory` may be returned instead of the + /// POSIX-specified `error-code::not-permitted`. + @since(version = 0.3.0) + unlink-file-at: async func(path: string) -> result<_, error-code>; /// Test whether two descriptors refer to the same filesystem object. /// /// In POSIX, this corresponds to testing whether the two descriptors have the /// same device (`st_dev`) and inode (`st_ino` or `d_ino`) numbers. /// wasi-filesystem does not expose device and inode numbers, so this function /// may be used instead. - @since(version = 0.2.0) - is-same-object: func(other: borrow) -> bool; + @since(version = 0.3.0) + is-same-object: async func(other: borrow) -> bool; /// Return a hash of the metadata associated with a filesystem object referred /// to by a descriptor. /// @@ -528,60 +541,35 @@ interface types { /// computed hash. /// /// However, none of these is required. - @since(version = 0.2.0) - metadata-hash: func() -> result; + @since(version = 0.3.0) + metadata-hash: async func() -> result; /// Return a hash of the metadata associated with a filesystem object referred /// to by a directory descriptor and a relative path. /// /// This performs the same hash computation as `metadata-hash`. - @since(version = 0.2.0) - metadata-hash-at: func(path-flags: path-flags, path: string) -> result; - } - - /// A stream of directory entries. - @since(version = 0.2.0) - resource directory-entry-stream { - /// Read a single directory entry from a `directory-entry-stream`. - @since(version = 0.2.0) - read-directory-entry: func() -> result, error-code>; + @since(version = 0.3.0) + metadata-hash-at: async func(path-flags: path-flags, path: string) -> result; } - - /// Attempts to extract a filesystem-related `error-code` from the stream - /// `error` provided. - /// - /// Stream operations which return `stream-error::last-operation-failed` - /// have a payload with more information about the operation that failed. - /// This payload can be passed through to this function to see if there's - /// filesystem-related information about the error to return. - /// - /// Note that this function is fallible because not all stream-related - /// errors are filesystem-related errors. - @since(version = 0.2.0) - filesystem-error-code: func(err: borrow) -> option; } -@since(version = 0.2.0) +@since(version = 0.3.0) interface preopens { - @since(version = 0.2.0) + @since(version = 0.3.0) use types.{descriptor}; /// Return the set of preopened directories, and their paths. - @since(version = 0.2.0) + @since(version = 0.3.0) get-directories: func() -> list>; } -@since(version = 0.2.0) +@since(version = 0.3.0) world imports { - @since(version = 0.2.0) - import wasi:io/error@0.2.6; - @since(version = 0.2.0) - import wasi:io/poll@0.2.6; - @since(version = 0.2.0) - import wasi:io/streams@0.2.6; - @since(version = 0.2.0) - import wasi:clocks/wall-clock@0.2.6; - @since(version = 0.2.0) + @since(version = 0.3.0) + import wasi:clocks/types@0.3.0; + @since(version = 0.3.0) + import wasi:clocks/system-clock@0.3.0; + @since(version = 0.3.0) import types; - @since(version = 0.2.0) + @since(version = 0.3.0) import preopens; } diff --git a/wit/deps/wasi-io-0.2.6/package.wit b/wit/deps/wasi-io-0.2.6/package.wit deleted file mode 100644 index 72fefbe..0000000 --- a/wit/deps/wasi-io-0.2.6/package.wit +++ /dev/null @@ -1,48 +0,0 @@ -package wasi:io@0.2.6; - -interface error { - resource error { - to-debug-string: func() -> string; - } -} - -interface poll { - resource pollable { - ready: func() -> bool; - block: func(); - } - - poll: func(in: list>) -> list; -} - -interface streams { - use error.{error}; - use poll.{pollable}; - - variant stream-error { - last-operation-failed(error), - closed, - } - - resource input-stream { - read: func(len: u64) -> result, stream-error>; - blocking-read: func(len: u64) -> result, stream-error>; - skip: func(len: u64) -> result; - blocking-skip: func(len: u64) -> result; - subscribe: func() -> pollable; - } - - resource output-stream { - check-write: func() -> result; - write: func(contents: list) -> result<_, stream-error>; - blocking-write-and-flush: func(contents: list) -> result<_, stream-error>; - flush: func() -> result<_, stream-error>; - blocking-flush: func() -> result<_, stream-error>; - subscribe: func() -> pollable; - write-zeroes: func(len: u64) -> result<_, stream-error>; - blocking-write-zeroes-and-flush: func(len: u64) -> result<_, stream-error>; - splice: func(src: borrow, len: u64) -> result; - blocking-splice: func(src: borrow, len: u64) -> result; - } -} - diff --git a/wit/worlds.wit b/wit/worlds.wit index bad82d8..d527e48 100644 --- a/wit/worlds.wit +++ b/wit/worlds.wit @@ -3,8 +3,8 @@ package componentized:filesystem; world filesystem { import wasi:config/store@0.2.0-rc.1; import wasi:logging/logging@0.1.0-draft; - import wasi:filesystem/preopens@0.2.6; - export wasi:filesystem/preopens@0.2.6; - import wasi:filesystem/types@0.2.6; - export wasi:filesystem/types@0.2.6; + import wasi:filesystem/preopens@0.3.0; + export wasi:filesystem/preopens@0.3.0; + import wasi:filesystem/types@0.3.0; + export wasi:filesystem/types@0.3.0; } diff --git a/wkg.lock b/wkg.lock index 0e909a6..d82b52c 100644 --- a/wkg.lock +++ b/wkg.lock @@ -16,9 +16,9 @@ name = "wasi:filesystem" registry = "wasi.dev" [[packages.versions]] -requirement = "=0.2.6" -version = "0.2.6" -digest = "sha256:7b735005e020ee3d727d995c05849c59bbf084b51a054013c0f0796c571e1ab0" +requirement = "=0.3.0" +version = "0.3.0" +digest = "sha256:42921410cddcdce2e009fd65c9a763bfb3fb61e2c0a64ff71647cc4405470378" [[packages]] name = "wasi:logging"