From 83c5beefd3c2a924f5dc0feb5746ef4ecac992e1 Mon Sep 17 00:00:00 2001 From: Scott Andrews Date: Fri, 15 May 2026 00:22:52 -0400 Subject: [PATCH 1/4] Filesystem gate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Intercept wasi:filesystem calls allowing per call access control decisions. The 'gate' is a component that virtualizes the wasi:filesystem interfaces and also import a latch interface. The latch defines the access control check and returns a decision. For any call, a latch can either allow, deny or abstain. Denials include an error-code as the reason. Latches can be composed together to define more advanced behavior. The latch-N components compose N latches together. An allow or deny decision is returned immediately while an abstain decision allows the next latch to have an opinion. For example, if we want to make a component's access to the filesystem be read-only while other parts of the runtime need read-write, we can compose the ro-consume with a gate, while the rw-consumer has full access to the filesystem. The latch is itself a composition of two other latches, the first is the read-only latch which denies calls that would modify the filesystem. The allow latch approves calls that have otherwise not been denied. The composition enabled additional behavior be added, such as denying access to certain directories, or any other. behavior a use chooses to implement. Replace the latch2 component with latch3 when there is a third latch to orchestrate. ``` host ↗ ↖ ↗ latch-readonly rw-consumer gate → latch2 ↑ ↘ latch-allow ro-consumer ``` Signed-off-by: Scott Andrews --- Cargo.lock | 49 ++ README.md | 9 +- components/gate/Cargo.toml | 11 + components/gate/README.md | 3 + components/gate/src/lib.rs | 938 +++++++++++++++++++++++++++ components/latch-2/Cargo.toml | 11 + components/latch-2/README.md | 3 + components/latch-2/src/lib.rs | 273 ++++++++ components/latch-3/Cargo.toml | 11 + components/latch-3/README.md | 3 + components/latch-3/src/lib.rs | 273 ++++++++ components/latch-4/Cargo.toml | 11 + components/latch-4/README.md | 3 + components/latch-4/src/lib.rs | 273 ++++++++ components/latch-allow/Cargo.toml | 11 + components/latch-allow/README.md | 3 + components/latch-allow/src/lib.rs | 19 + components/latch-deny/Cargo.toml | 11 + components/latch-deny/README.md | 3 + components/latch-deny/src/lib.rs | 22 + components/latch-readonly/Cargo.toml | 11 + components/latch-readonly/README.md | 3 + components/latch-readonly/src/lib.rs | 71 ++ wit/latch.wit | 165 +++++ wit/worlds.wit | 26 + 25 files changed, 2215 insertions(+), 1 deletion(-) create mode 100644 components/gate/Cargo.toml create mode 100644 components/gate/README.md create mode 100644 components/gate/src/lib.rs create mode 100644 components/latch-2/Cargo.toml create mode 100644 components/latch-2/README.md create mode 100644 components/latch-2/src/lib.rs create mode 100644 components/latch-3/Cargo.toml create mode 100644 components/latch-3/README.md create mode 100644 components/latch-3/src/lib.rs create mode 100644 components/latch-4/Cargo.toml create mode 100644 components/latch-4/README.md create mode 100644 components/latch-4/src/lib.rs create mode 100644 components/latch-allow/Cargo.toml create mode 100644 components/latch-allow/README.md create mode 100644 components/latch-allow/src/lib.rs create mode 100644 components/latch-deny/Cargo.toml create mode 100644 components/latch-deny/README.md create mode 100644 components/latch-deny/src/lib.rs create mode 100644 components/latch-readonly/Cargo.toml create mode 100644 components/latch-readonly/README.md create mode 100644 components/latch-readonly/src/lib.rs create mode 100644 wit/latch.wit diff --git a/Cargo.lock b/Cargo.lock index 1c127f2..06048e8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -33,6 +33,13 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" +[[package]] +name = "gate" +version = "0.1.0" +dependencies = [ + "wit-bindgen", +] + [[package]] name = "hashbrown" version = "0.17.0" @@ -72,6 +79,48 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" +[[package]] +name = "latch-2" +version = "0.1.0" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "latch-3" +version = "0.1.0" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "latch-4" +version = "0.1.0" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "latch-allow" +version = "0.1.0" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "latch-deny" +version = "0.1.0" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "latch-readonly" +version = "0.1.0" +dependencies = [ + "wit-bindgen", +] + [[package]] name = "leb128fmt" version = "0.1.0" diff --git a/README.md b/README.md index b9b80f0..255e085 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,14 @@ A collection of utility components that remix wasi:filesystem types and interfac ## Components - [`chroot`](./components/chroot/) -- [`readonly`](./components/readonly/) +- [`gate`](./components/gate/) +- [`latch-2`](./components/latch-2/) +- [`latch-3`](./components/latch-3/) +- [`latch-4`](./components/latch-4/) +- [`latch-allow`](./components/latch-allow/) +- [`latch-deny`](./components/latch-deny/) +- [`latch-readonly`](./components/latch-readonly/) +- ~~[`readonly`](./components/readonly/)~~ (deprecated, favor gate with readonly latch) - [`tracing`](./components/tracing/) ## Build diff --git a/components/gate/Cargo.toml b/components/gate/Cargo.toml new file mode 100644 index 0000000..51d9cae --- /dev/null +++ b/components/gate/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "gate" +version = "0.1.0" +edition = "2021" +license = "Apache-2.0" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +wit-bindgen = { workspace = true } diff --git a/components/gate/README.md b/components/gate/README.md new file mode 100644 index 0000000..2a982e1 --- /dev/null +++ b/components/gate/README.md @@ -0,0 +1,3 @@ +# `gate` + +Filesystem gate access control. diff --git a/components/gate/src/lib.rs b/components/gate/src/lib.rs new file mode 100644 index 0000000..bc1b48e --- /dev/null +++ b/components/gate/src/lib.rs @@ -0,0 +1,938 @@ +#![no_main] + +use std::rc::Rc; + +use crate::componentized::filesystem::latch::Decision::{Abstain, Allow, Deny}; +use crate::componentized::filesystem::latch::{self, check, DescriptorOperation, Operation}; +use crate::exports::wasi::filesystem::preopens::Guest as Preopens; +use crate::exports::wasi::filesystem::types::{ + Advice, Descriptor, DescriptorBorrow, DescriptorFlags, DescriptorStat, DescriptorType, + DirectoryEntry, DirectoryEntryStream, Error, ErrorCode, Filesize, Guest as Types, InputStream, + MetadataHashValue, NewTimestamp, OpenFlags, OutputStream, PathFlags, +}; +use crate::wasi::filesystem::preopens; +use crate::wasi::filesystem::types; +use crate::wasi::logging::logging::{log, Level}; + +#[macro_export] +macro_rules! warn { + ($dst:expr, $($arg:tt)*) => { + log(Level::Warn, "componentized-gate", &format!($dst, $($arg)*)); + }; + ($dst:expr) => { + log(Level::Warn, "componentized-gate", &format!($dst)); + }; +} + +#[derive(Debug, Clone)] +struct FilesystemGate {} + +impl Preopens for FilesystemGate { + #[doc = " Return the set of preopened directories, and their path."] + fn get_directories() -> Vec<(Descriptor, String)> { + preopens::get_directories() + .into_iter() + .map(|(fd, path)| { + let fd = Descriptor::new(GateDescriptor::new(fd)); + (fd, path) + }) + .collect() + } +} + +impl Types for FilesystemGate { + type Descriptor = GateDescriptor; + type DirectoryEntryStream = GateDirectoryEntryStream; + + #[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 GateDescriptor { + fd: Rc, +} + +impl GateDescriptor { + fn new(fd: types::Descriptor) -> Self { + Self { fd: Rc::new(fd) } + } +} + +impl exports::wasi::filesystem::types::GuestDescriptor for GateDescriptor { + #[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 { + match check(&Operation::Descriptor(( + &self.fd, + DescriptorOperation::ReadViaStream(latch::DescriptorReadViaStreamArgs { offset }), + ))) { + Allow => self.fd.read_via_stream(offset).map_err(error_code_map), + Deny(code) => { + warn!("Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.read-via-stream FD={self:?} OFFSET={offset}"); + Err(error_code_map(code)) + } + Abstain => panic!("missing latch decision"), + } + } + + #[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 { + match check(&Operation::Descriptor(( + &self.fd, + DescriptorOperation::WriteViaStream(latch::DescriptorWriteViaStreamArgs { offset }), + ))) { + Allow => self.fd.write_via_stream(offset).map_err(error_code_map), + Deny(code) => { + warn!("Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.write-via-stream FD={self:?} OFFSET={offset}"); + Err(error_code_map(code)) + } + Abstain => panic!("missing latch decision"), + } + } + + #[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 { + match check(&Operation::Descriptor(( + &self.fd, + DescriptorOperation::AppendViaStream, + ))) { + Allow => self.fd.append_via_stream().map_err(error_code_map), + Deny(code) => { + warn!("Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.append-via-stream FD={self:?}"); + Err(error_code_map(code)) + } + Abstain => panic!("missing latch decision"), + } + } + + #[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); + + match check(&Operation::Descriptor(( + &self.fd, + DescriptorOperation::Advise(latch::DescriptorAdviseArgs { + offset, + length, + advice, + }), + ))) { + Allow => self + .fd + .advise(offset, length, advice) + .map_err(error_code_map), + Deny(code) => { + warn!("Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.advise FD={self:?}"); + Err(error_code_map(code)) + } + Abstain => panic!("missing latch decision"), + } + } + + #[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> { + match check(&Operation::Descriptor(( + &self.fd, + DescriptorOperation::SyncData, + ))) { + Allow => self.fd.sync_data().map_err(error_code_map), + Deny(code) => { + warn!("Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.sync-data FD={self:?}"); + Err(error_code_map(code)) + } + Abstain => panic!("missing latch decision"), + } + } + + #[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 { + match check(&Operation::Descriptor(( + &self.fd, + DescriptorOperation::GetFlags, + ))) { + Allow => self + .fd + .get_flags() + .map(descriptor_flags_map) + .map_err(error_code_map), + Deny(code) => { + warn!("Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.get-flags FD={self:?}"); + Err(error_code_map(code)) + } + Abstain => panic!("missing latch decision"), + } + } + + #[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 { + match check(&Operation::Descriptor(( + &self.fd, + DescriptorOperation::GetType, + ))) { + Allow => self + .fd + .get_type() + .map(descriptor_type_map) + .map_err(error_code_map), + Deny(code) => { + warn!("Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.get-type FD={self:?}"); + Err(error_code_map(code)) + } + Abstain => panic!("missing latch decision"), + } + } + + #[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> { + match check(&Operation::Descriptor(( + &self.fd, + DescriptorOperation::SetSize(latch::DescriptorSetSizeArgs { size }), + ))) { + Allow => self.fd.set_size(size).map_err(error_code_map), + Deny(code) => { + warn!("Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.set-size FD={self:?} SIZE={size}"); + Err(error_code_map(code)) + } + Abstain => panic!("missing latch decision"), + } + } + + #[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( + &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); + + match check(&Operation::Descriptor(( + &self.fd, + DescriptorOperation::SetTimes(latch::DescriptorSetTimesArgs { + data_access_timestamp, + data_modification_timestamp, + }), + ))) { + Allow => self + .fd + .set_times(data_access_timestamp, data_modification_timestamp) + .map_err(error_code_map), + Deny(code) => { + warn!("Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.set-times FD={self:?} ACCESS-TIME={data_access_timestamp:?} MODIFIED-TIME={data_modification_timestamp:?}"); + Err(error_code_map(code)) + } + Abstain => panic!("missing latch decision"), + } + } + + #[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> { + match check(&Operation::Descriptor(( + &self.fd, + DescriptorOperation::Read(latch::DescriptorReadArgs { length, offset }), + ))) { + Allow => self.fd.read(length, offset).map_err(error_code_map), + Deny(code) => { + warn!("Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.read FD={self:?} LENGTH={length} OFFSET={offset}"); + Err(error_code_map(code)) + } + Abstain => panic!("missing latch decision"), + } + } + + #[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 { + let buffer_length: u64 = buffer + .len() + .try_into() + .expect("buffer length 64-bits or less"); + match check(&Operation::Descriptor(( + &self.fd, + DescriptorOperation::Write(latch::DescriptorWriteArgs { + buffer_length, + offset, + }), + ))) { + Allow => self.fd.write(&buffer, offset).map_err(error_code_map), + Deny(code) => { + warn!("Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.write FD={self:?} BUFFER-LENGTH={buffer_length} OFFSET={offset}"); + Err(error_code_map(code)) + } + Abstain => panic!("missing latch decision"), + } + } + + #[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 { + match check(&Operation::Descriptor(( + &self.fd, + DescriptorOperation::ReadDirectory, + ))) { + Allow => self + .fd + .read_directory() + .map(directory_entry_stream_map) + .map_err(error_code_map), + Deny(code) => { + warn!("Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.read-directory FD={self:?}"); + Err(error_code_map(code)) + } + Abstain => panic!("missing latch decision"), + } + } + + #[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> { + match check(&Operation::Descriptor(( + &self.fd, + DescriptorOperation::Sync, + ))) { + Allow => self.fd.sync().map_err(error_code_map), + Deny(code) => { + warn!("Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.sync FD={self:?}"); + Err(error_code_map(code)) + } + Abstain => panic!("missing latch decision"), + } + } + + #[doc = " Create a directory."] + #[doc = ""] + #[doc = " Note: This is similar to `mkdirat` in POSIX."] + fn create_directory_at(&self, path: String) -> Result<(), ErrorCode> { + match check(&Operation::Descriptor(( + &self.fd, + DescriptorOperation::CreateDirectoryAt(latch::DescriptorCreateDirectoryAtArgs { + path: path.clone(), + }), + ))) { + Allow => self.fd.create_directory_at(&path).map_err(error_code_map), + Deny(code) => { + warn!("Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.create-directory-at FD={self:?} PATH={path}"); + Err(error_code_map(code)) + } + Abstain => panic!("missing latch decision"), + } + } + + #[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 { + match check(&Operation::Descriptor(( + &self.fd, + DescriptorOperation::Stat, + ))) { + Allow => self + .fd + .stat() + .map(descriptor_stat_map) + .map_err(error_code_map), + Deny(code) => { + warn!("Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.stat FD={self:?}"); + Err(error_code_map(code)) + } + Abstain => panic!("missing latch decision"), + } + } + + #[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(); + + match check(&Operation::Descriptor(( + &self.fd, + DescriptorOperation::StatAt(latch::DescriptorStatAtArgs { + path_flags, + path: path.clone(), + }), + ))) { + Allow => self + .fd + .stat_at(path_flags, &path) + .map(descriptor_stat_map) + .map_err(error_code_map), + Deny(code) => { + warn!("Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.stat-at FD={self:?} PATH={path}"); + Err(error_code_map(code)) + } + Abstain => panic!("missing latch decision"), + } + } + + #[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( + &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); + match check(&Operation::Descriptor(( + &self.fd, + DescriptorOperation::SetTimesAt(latch::DescriptorSetTimesAtArgs { + path_flags, + path: path.clone(), + data_access_timestamp, + data_modification_timestamp, + }), + ))) { + Allow => self + .fd + .set_times_at( + path_flags, + &path, + data_access_timestamp, + data_modification_timestamp, + ) + .map_err(error_code_map), + Deny(code) => { + warn!("Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.set-times-at FD={self:?} PATH={path} ACCESS-TIME={data_access_timestamp:?} MODIFIED-TIME={data_modification_timestamp:?}"); + Err(error_code_map(code)) + } + Abstain => panic!("missing latch decision"), + } + } + + #[doc = " Create a hard link."] + #[doc = ""] + #[doc = " Note: This is similar to `linkat` in POSIX."] + 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(); + match check(&Operation::Descriptor(( + &self.fd, + DescriptorOperation::LinkAt(latch::DescriptorLinkAtArgs { + old_path_flags, + old_path: old_path.clone(), + new_descriptor: &new_descriptor.get::().fd, + new_path: new_path.clone(), + }), + ))) { + Allow => self + .fd + .link_at( + old_path_flags, + &old_path, + &new_descriptor.get::().fd, + &new_path, + ) + .map_err(error_code_map), + Deny(code) => { + warn!( + "Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.link-at FD={self:?} OLD-PATH={old_path} NEW-PATH={new_path}", + ); + Err(error_code_map(code)) + } + Abstain => panic!("missing latch decision"), + } + } + + #[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( + &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(); + match check(&Operation::Descriptor(( + &self.fd, + DescriptorOperation::OpenAt(latch::DescriptorOpenAtArgs { + path_flags, + path: path.clone(), + open_flags, + flags, + }), + ))) { + Allow => self + .fd + .open_at(path_flags, &path, open_flags, flags) + .map(descriptor_map) + .map_err(error_code_map), + Deny(code) => { + warn!("Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.open-at FD={self:?} PATH={path}"); + Err(error_code_map(code)) + } + Abstain => panic!("missing latch decision"), + } + } + + #[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 { + match check(&Operation::Descriptor(( + &self.fd, + DescriptorOperation::ReadlinkAt(latch::DescriptorReadlinkAtArgs { path: path.clone() }), + ))) { + Allow => self.fd.readlink_at(&path).map_err(error_code_map), + Deny(code) => { + warn!( + "Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.readlink-at FD={self:?} PATH={path}", + ); + Err(error_code_map(code)) + } + Abstain => panic!("missing latch decision"), + } + } + + #[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> { + match check(&Operation::Descriptor(( + &self.fd, + DescriptorOperation::RemoveDirectoryAt(latch::DescriptorRemoveDirectoryAtArgs { + path: path.clone(), + }), + ))) { + Allow => self.fd.remove_directory_at(&path).map_err(error_code_map), + Deny(code) => { + warn!("Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.remove-directory-at FD={self:?} PATH={path}"); + Err(error_code_map(code)) + } + Abstain => panic!("missing latch decision"), + } + } + + #[doc = " Rename a filesystem object."] + #[doc = ""] + #[doc = " Note: This is similar to `renameat` in POSIX."] + fn rename_at( + &self, + old_path: String, + new_descriptor: DescriptorBorrow<'_>, + new_path: String, + ) -> Result<(), ErrorCode> { + match check(&Operation::Descriptor(( + &self.fd, + DescriptorOperation::RenameAt(latch::DescriptorRenameAtArgs { + old_path: old_path.clone(), + new_descriptor: &new_descriptor.get::().fd, + new_path: new_path.clone(), + }), + ))) { + Allow => { + let new_descriptor: &Self = new_descriptor.get(); + self.fd + .rename_at(&old_path, &new_descriptor.fd, &new_path) + .map_err(error_code_map) + } + Deny(code) => { + warn!( + "Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.rename-at FD={self:?} OLD-PATH={old_path} NEW-PATH={new_path}", + ); + Err(error_code_map(code)) + } + Abstain => panic!("missing latch decision"), + } + } + + #[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> { + match check(&Operation::Descriptor(( + &self.fd, + DescriptorOperation::SymlinkAt(latch::DescriptorSymlinkAtArgs { + old_path: old_path.clone(), + new_path: new_path.clone(), + }), + ))) { + Allow => self + .fd + .symlink_at(&old_path, &new_path) + .map_err(error_code_map), + Deny(code) => { + warn!( + "Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.symlink-at FD={self:?} OLD-PATH={old_path} NEW-PATH={new_path}", + ); + Err(error_code_map(code)) + } + Abstain => panic!("missing latch decision"), + } + } + + #[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> { + match check(&Operation::Descriptor(( + &self.fd, + DescriptorOperation::UnlinkFileAt(latch::DescriptorUnlinkFileAtArgs { + path: path.clone(), + }), + ))) { + Allow => self.fd.unlink_file_at(&path).map_err(error_code_map), + Deny(code) => { + warn!( + "Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.unlink-file-at FD={self:?} PATH={path}", + ); + Err(error_code_map(code)) + } + Abstain => panic!("missing latch decision"), + } + } + + #[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 { + let other: &Self = other.get(); + 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 { + match check(&Operation::Descriptor(( + &self.fd, + DescriptorOperation::MetadataHash, + ))) { + Allow => self + .fd + .metadata_hash() + .map(metadata_hash_value_map) + .map_err(error_code_map), + Deny(code) => { + warn!("Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.metadata-hash FD={self:?}"); + Err(error_code_map(code)) + } + Abstain => panic!("missing latch decision"), + } + } + + #[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( + &self, + path_flags: PathFlags, + path: String, + ) -> Result { + let path_flags = types::PathFlags::from_bits(path_flags.bits()).unwrap(); + match check(&Operation::Descriptor(( + &self.fd, + DescriptorOperation::MetadataHashAt(latch::DescriptorMetadataHashAtArgs { + path_flags, + path: path.clone(), + }), + ))) { + Allow => self + .fd + .metadata_hash_at(path_flags, &path) + .map(metadata_hash_value_map) + .map_err(error_code_map), + Deny(code) => { + warn!("Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.metadata-hash-at FD={self:?} PATH={path}"); + Err(error_code_map(code)) + } + Abstain => panic!("missing latch decision"), + } + } +} + +#[derive(Debug, Clone)] +struct GateDirectoryEntryStream { + des: Rc, +} + +impl GateDirectoryEntryStream { + fn new(des: types::DirectoryEntryStream) -> Self { + Self { des: Rc::new(des) } + } +} + +impl exports::wasi::filesystem::types::GuestDirectoryEntryStream for GateDirectoryEntryStream { + #[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(GateDescriptor::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(GateDirectoryEntryStream::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(timestamp: NewTimestamp) -> types::NewTimestamp { + match timestamp { + NewTimestamp::NoChange => types::NewTimestamp::NoChange, + NewTimestamp::Now => types::NewTimestamp::Now, + NewTimestamp::Timestamp(dt) => types::NewTimestamp::Timestamp(dt), + } +} + +wit_bindgen::generate!({ + path: "../../wit", + world: "filesystem", + generate_all +}); + +export!(FilesystemGate); diff --git a/components/latch-2/Cargo.toml b/components/latch-2/Cargo.toml new file mode 100644 index 0000000..6fc6748 --- /dev/null +++ b/components/latch-2/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "latch-2" +version = "0.1.0" +edition = "2021" +license = "Apache-2.0" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +wit-bindgen = { workspace = true } diff --git a/components/latch-2/README.md b/components/latch-2/README.md new file mode 100644 index 0000000..7d3ce33 --- /dev/null +++ b/components/latch-2/README.md @@ -0,0 +1,3 @@ +# `latch-2` + +Filesystem latch that aggregates two other filesystem latches. diff --git a/components/latch-2/src/lib.rs b/components/latch-2/src/lib.rs new file mode 100644 index 0000000..f0ce617 --- /dev/null +++ b/components/latch-2/src/lib.rs @@ -0,0 +1,273 @@ +#![no_main] + +use crate::{ + componentized::filesystem::{latch, latch1, latch2}, + exports::componentized::filesystem::latch::{ + Decision, DescriptorAdviseArgs, DescriptorCreateDirectoryAtArgs, DescriptorLinkAtArgs, + DescriptorMetadataHashAtArgs, DescriptorOpenAtArgs, DescriptorOperation, + DescriptorReadArgs, DescriptorReadViaStreamArgs, DescriptorReadlinkAtArgs, + DescriptorRemoveDirectoryAtArgs, DescriptorRenameAtArgs, DescriptorSetSizeArgs, + DescriptorSetTimesArgs, DescriptorSetTimesAtArgs, DescriptorStatAtArgs, + DescriptorSymlinkAtArgs, DescriptorUnlinkFileAtArgs, DescriptorWriteArgs, + DescriptorWriteViaStreamArgs, Guest as Latch, Operation, + }, +}; + +struct AggregateLatch {} + +impl Latch for AggregateLatch { + fn check(operation: Operation) -> Decision { + let operation = operation_map(operation); + let checks = vec![latch1::check, latch2::check]; + for check in checks { + match check(&operation) { + latch::Decision::Abstain => {} + latch::Decision::Allow => return Decision::Allow, + latch::Decision::Deny(error_code) => return Decision::Deny(error_code), + } + } + Decision::Abstain + } +} + +fn operation_map(operation: Operation) -> latch::Operation { + match operation { + Operation::Descriptor((descriptor, descriptor_operation)) => latch::Operation::Descriptor( + (descriptor, descriptor_operation_map(descriptor_operation)), + ), + } +} + +fn descriptor_operation_map( + descriptor_operation: DescriptorOperation, +) -> latch::DescriptorOperation { + match descriptor_operation { + DescriptorOperation::ReadViaStream(descriptor_read_via_stream_args) => { + latch::DescriptorOperation::ReadViaStream(descriptor_read_via_stream_args_map( + descriptor_read_via_stream_args, + )) + } + DescriptorOperation::WriteViaStream(descriptor_write_via_stream_args) => { + latch::DescriptorOperation::WriteViaStream(descriptor_write_via_stream_args_map( + descriptor_write_via_stream_args, + )) + } + DescriptorOperation::AppendViaStream => latch::DescriptorOperation::AppendViaStream, + DescriptorOperation::Advise(descriptor_advise_args) => { + latch::DescriptorOperation::Advise(descriptor_advise_args_map(descriptor_advise_args)) + } + DescriptorOperation::SyncData => latch::DescriptorOperation::SyncData, + DescriptorOperation::GetFlags => latch::DescriptorOperation::GetFlags, + DescriptorOperation::GetType => latch::DescriptorOperation::GetType, + DescriptorOperation::SetSize(descriptor_set_size_args) => { + latch::DescriptorOperation::SetSize(descriptor_set_size_args_map( + descriptor_set_size_args, + )) + } + DescriptorOperation::SetTimes(descriptor_set_times_args) => { + latch::DescriptorOperation::SetTimes(descriptor_set_times_args_map( + descriptor_set_times_args, + )) + } + DescriptorOperation::Read(descriptor_read_args) => { + latch::DescriptorOperation::Read(descriptor_read_args_map(descriptor_read_args)) + } + DescriptorOperation::Write(descriptor_write_args) => { + latch::DescriptorOperation::Write(descriptor_write_args_map(descriptor_write_args)) + } + DescriptorOperation::ReadDirectory => latch::DescriptorOperation::ReadDirectory, + DescriptorOperation::Sync => latch::DescriptorOperation::Sync, + DescriptorOperation::CreateDirectoryAt(descriptor_create_directory_at_args) => { + latch::DescriptorOperation::CreateDirectoryAt(descriptor_create_directory_at_args_map( + descriptor_create_directory_at_args, + )) + } + DescriptorOperation::Stat => latch::DescriptorOperation::Stat, + DescriptorOperation::StatAt(descriptor_stat_at_args) => { + latch::DescriptorOperation::StatAt(descriptor_stat_at_args_map(descriptor_stat_at_args)) + } + DescriptorOperation::SetTimesAt(descriptor_set_times_at_args) => { + latch::DescriptorOperation::SetTimesAt(descriptor_set_times_at_args_map( + descriptor_set_times_at_args, + )) + } + DescriptorOperation::LinkAt(descriptor_link_at_args) => { + latch::DescriptorOperation::LinkAt(descriptor_link_at_args_map(descriptor_link_at_args)) + } + DescriptorOperation::OpenAt(descriptor_open_at_args) => { + latch::DescriptorOperation::OpenAt(descriptor_open_at_args_map(descriptor_open_at_args)) + } + DescriptorOperation::ReadlinkAt(descriptor_readlink_at_args) => { + latch::DescriptorOperation::ReadlinkAt(descriptor_readlink_at_args_map( + descriptor_readlink_at_args, + )) + } + DescriptorOperation::RemoveDirectoryAt(descriptor_remove_directory_at_args) => { + latch::DescriptorOperation::RemoveDirectoryAt(descriptor_remove_directory_at_args_map( + descriptor_remove_directory_at_args, + )) + } + DescriptorOperation::RenameAt(descriptor_rename_at_args) => { + latch::DescriptorOperation::RenameAt(descriptor_rename_at_args_map( + descriptor_rename_at_args, + )) + } + DescriptorOperation::SymlinkAt(descriptor_symlink_at_args) => { + latch::DescriptorOperation::SymlinkAt(descriptor_symlink_at_args_map( + descriptor_symlink_at_args, + )) + } + DescriptorOperation::UnlinkFileAt(descriptor_unlink_file_at_args) => { + latch::DescriptorOperation::UnlinkFileAt(descriptor_unlink_file_at_args_map( + descriptor_unlink_file_at_args, + )) + } + DescriptorOperation::MetadataHash => latch::DescriptorOperation::MetadataHash, + DescriptorOperation::MetadataHashAt(descriptor_metadata_hash_at_args) => { + latch::DescriptorOperation::MetadataHashAt(descriptor_metadata_hash_at_args_map( + descriptor_metadata_hash_at_args, + )) + } + } +} + +fn descriptor_read_via_stream_args_map( + args: DescriptorReadViaStreamArgs, +) -> latch::DescriptorReadViaStreamArgs { + latch::DescriptorReadViaStreamArgs { + offset: args.offset, + } +} + +fn descriptor_write_via_stream_args_map( + args: DescriptorWriteViaStreamArgs, +) -> latch::DescriptorWriteViaStreamArgs { + latch::DescriptorWriteViaStreamArgs { + offset: args.offset, + } +} + +fn descriptor_advise_args_map(args: DescriptorAdviseArgs) -> latch::DescriptorAdviseArgs { + latch::DescriptorAdviseArgs { + offset: args.offset, + length: args.length, + advice: args.advice, + } +} + +fn descriptor_set_size_args_map(args: DescriptorSetSizeArgs) -> latch::DescriptorSetSizeArgs { + latch::DescriptorSetSizeArgs { size: args.size } +} + +fn descriptor_set_times_args_map(args: DescriptorSetTimesArgs) -> latch::DescriptorSetTimesArgs { + latch::DescriptorSetTimesArgs { + data_access_timestamp: args.data_access_timestamp, + data_modification_timestamp: args.data_modification_timestamp, + } +} + +fn descriptor_read_args_map(args: DescriptorReadArgs) -> latch::DescriptorReadArgs { + latch::DescriptorReadArgs { + length: args.length, + offset: args.offset, + } +} + +fn descriptor_write_args_map(args: DescriptorWriteArgs) -> latch::DescriptorWriteArgs { + latch::DescriptorWriteArgs { + buffer_length: args.buffer_length, + offset: args.offset, + } +} + +fn descriptor_create_directory_at_args_map( + args: DescriptorCreateDirectoryAtArgs, +) -> latch::DescriptorCreateDirectoryAtArgs { + latch::DescriptorCreateDirectoryAtArgs { path: args.path } +} + +fn descriptor_stat_at_args_map(args: DescriptorStatAtArgs) -> latch::DescriptorStatAtArgs { + latch::DescriptorStatAtArgs { + path: args.path, + path_flags: args.path_flags, + } +} + +fn descriptor_set_times_at_args_map( + args: DescriptorSetTimesAtArgs, +) -> latch::DescriptorSetTimesAtArgs { + latch::DescriptorSetTimesAtArgs { + data_access_timestamp: args.data_access_timestamp, + data_modification_timestamp: args.data_modification_timestamp, + path: args.path, + path_flags: args.path_flags, + } +} + +fn descriptor_link_at_args_map(args: DescriptorLinkAtArgs) -> latch::DescriptorLinkAtArgs { + latch::DescriptorLinkAtArgs { + new_descriptor: args.new_descriptor, + new_path: args.new_path, + old_path: args.old_path, + old_path_flags: args.old_path_flags, + } +} + +fn descriptor_open_at_args_map(args: DescriptorOpenAtArgs) -> latch::DescriptorOpenAtArgs { + latch::DescriptorOpenAtArgs { + flags: args.flags, + open_flags: args.open_flags, + path: args.path, + path_flags: args.path_flags, + } +} + +fn descriptor_readlink_at_args_map( + args: DescriptorReadlinkAtArgs, +) -> latch::DescriptorReadlinkAtArgs { + latch::DescriptorReadlinkAtArgs { path: args.path } +} + +fn descriptor_remove_directory_at_args_map( + args: DescriptorRemoveDirectoryAtArgs, +) -> latch::DescriptorRemoveDirectoryAtArgs { + latch::DescriptorRemoveDirectoryAtArgs { path: args.path } +} + +fn descriptor_rename_at_args_map(args: DescriptorRenameAtArgs) -> latch::DescriptorRenameAtArgs { + latch::DescriptorRenameAtArgs { + new_descriptor: args.new_descriptor, + new_path: args.new_path, + old_path: args.old_path, + } +} + +fn descriptor_symlink_at_args_map(args: DescriptorSymlinkAtArgs) -> latch::DescriptorSymlinkAtArgs { + latch::DescriptorSymlinkAtArgs { + new_path: args.new_path, + old_path: args.old_path, + } +} + +fn descriptor_unlink_file_at_args_map( + args: DescriptorUnlinkFileAtArgs, +) -> latch::DescriptorUnlinkFileAtArgs { + latch::DescriptorUnlinkFileAtArgs { path: args.path } +} + +fn descriptor_metadata_hash_at_args_map( + args: DescriptorMetadataHashAtArgs, +) -> latch::DescriptorMetadataHashAtArgs { + latch::DescriptorMetadataHashAtArgs { + path: args.path, + path_flags: args.path_flags, + } +} + +wit_bindgen::generate!({ + path: "../../wit", + world: "filesystem-latch2", + generate_all +}); + +export!(AggregateLatch); diff --git a/components/latch-3/Cargo.toml b/components/latch-3/Cargo.toml new file mode 100644 index 0000000..a348e23 --- /dev/null +++ b/components/latch-3/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "latch-3" +version = "0.1.0" +edition = "2021" +license = "Apache-2.0" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +wit-bindgen = { workspace = true } diff --git a/components/latch-3/README.md b/components/latch-3/README.md new file mode 100644 index 0000000..0438bd8 --- /dev/null +++ b/components/latch-3/README.md @@ -0,0 +1,3 @@ +# `latch-3` + +Filesystem latch that aggregates three other filesystem latches. diff --git a/components/latch-3/src/lib.rs b/components/latch-3/src/lib.rs new file mode 100644 index 0000000..7127cf5 --- /dev/null +++ b/components/latch-3/src/lib.rs @@ -0,0 +1,273 @@ +#![no_main] + +use crate::{ + componentized::filesystem::{latch, latch1, latch2, latch3}, + exports::componentized::filesystem::latch::{ + Decision, DescriptorAdviseArgs, DescriptorCreateDirectoryAtArgs, DescriptorLinkAtArgs, + DescriptorMetadataHashAtArgs, DescriptorOpenAtArgs, DescriptorOperation, + DescriptorReadArgs, DescriptorReadViaStreamArgs, DescriptorReadlinkAtArgs, + DescriptorRemoveDirectoryAtArgs, DescriptorRenameAtArgs, DescriptorSetSizeArgs, + DescriptorSetTimesArgs, DescriptorSetTimesAtArgs, DescriptorStatAtArgs, + DescriptorSymlinkAtArgs, DescriptorUnlinkFileAtArgs, DescriptorWriteArgs, + DescriptorWriteViaStreamArgs, Guest as Latch, Operation, + }, +}; + +struct AggregateLatch {} + +impl Latch for AggregateLatch { + fn check(operation: Operation) -> Decision { + let operation = operation_map(operation); + let checks = vec![latch1::check, latch2::check, latch3::check]; + for check in checks { + match check(&operation) { + latch::Decision::Abstain => {} + latch::Decision::Allow => return Decision::Allow, + latch::Decision::Deny(error_code) => return Decision::Deny(error_code), + } + } + Decision::Abstain + } +} + +fn operation_map(operation: Operation) -> latch::Operation { + match operation { + Operation::Descriptor((descriptor, descriptor_operation)) => latch::Operation::Descriptor( + (descriptor, descriptor_operation_map(descriptor_operation)), + ), + } +} + +fn descriptor_operation_map( + descriptor_operation: DescriptorOperation, +) -> latch::DescriptorOperation { + match descriptor_operation { + DescriptorOperation::ReadViaStream(descriptor_read_via_stream_args) => { + latch::DescriptorOperation::ReadViaStream(descriptor_read_via_stream_args_map( + descriptor_read_via_stream_args, + )) + } + DescriptorOperation::WriteViaStream(descriptor_write_via_stream_args) => { + latch::DescriptorOperation::WriteViaStream(descriptor_write_via_stream_args_map( + descriptor_write_via_stream_args, + )) + } + DescriptorOperation::AppendViaStream => latch::DescriptorOperation::AppendViaStream, + DescriptorOperation::Advise(descriptor_advise_args) => { + latch::DescriptorOperation::Advise(descriptor_advise_args_map(descriptor_advise_args)) + } + DescriptorOperation::SyncData => latch::DescriptorOperation::SyncData, + DescriptorOperation::GetFlags => latch::DescriptorOperation::GetFlags, + DescriptorOperation::GetType => latch::DescriptorOperation::GetType, + DescriptorOperation::SetSize(descriptor_set_size_args) => { + latch::DescriptorOperation::SetSize(descriptor_set_size_args_map( + descriptor_set_size_args, + )) + } + DescriptorOperation::SetTimes(descriptor_set_times_args) => { + latch::DescriptorOperation::SetTimes(descriptor_set_times_args_map( + descriptor_set_times_args, + )) + } + DescriptorOperation::Read(descriptor_read_args) => { + latch::DescriptorOperation::Read(descriptor_read_args_map(descriptor_read_args)) + } + DescriptorOperation::Write(descriptor_write_args) => { + latch::DescriptorOperation::Write(descriptor_write_args_map(descriptor_write_args)) + } + DescriptorOperation::ReadDirectory => latch::DescriptorOperation::ReadDirectory, + DescriptorOperation::Sync => latch::DescriptorOperation::Sync, + DescriptorOperation::CreateDirectoryAt(descriptor_create_directory_at_args) => { + latch::DescriptorOperation::CreateDirectoryAt(descriptor_create_directory_at_args_map( + descriptor_create_directory_at_args, + )) + } + DescriptorOperation::Stat => latch::DescriptorOperation::Stat, + DescriptorOperation::StatAt(descriptor_stat_at_args) => { + latch::DescriptorOperation::StatAt(descriptor_stat_at_args_map(descriptor_stat_at_args)) + } + DescriptorOperation::SetTimesAt(descriptor_set_times_at_args) => { + latch::DescriptorOperation::SetTimesAt(descriptor_set_times_at_args_map( + descriptor_set_times_at_args, + )) + } + DescriptorOperation::LinkAt(descriptor_link_at_args) => { + latch::DescriptorOperation::LinkAt(descriptor_link_at_args_map(descriptor_link_at_args)) + } + DescriptorOperation::OpenAt(descriptor_open_at_args) => { + latch::DescriptorOperation::OpenAt(descriptor_open_at_args_map(descriptor_open_at_args)) + } + DescriptorOperation::ReadlinkAt(descriptor_readlink_at_args) => { + latch::DescriptorOperation::ReadlinkAt(descriptor_readlink_at_args_map( + descriptor_readlink_at_args, + )) + } + DescriptorOperation::RemoveDirectoryAt(descriptor_remove_directory_at_args) => { + latch::DescriptorOperation::RemoveDirectoryAt(descriptor_remove_directory_at_args_map( + descriptor_remove_directory_at_args, + )) + } + DescriptorOperation::RenameAt(descriptor_rename_at_args) => { + latch::DescriptorOperation::RenameAt(descriptor_rename_at_args_map( + descriptor_rename_at_args, + )) + } + DescriptorOperation::SymlinkAt(descriptor_symlink_at_args) => { + latch::DescriptorOperation::SymlinkAt(descriptor_symlink_at_args_map( + descriptor_symlink_at_args, + )) + } + DescriptorOperation::UnlinkFileAt(descriptor_unlink_file_at_args) => { + latch::DescriptorOperation::UnlinkFileAt(descriptor_unlink_file_at_args_map( + descriptor_unlink_file_at_args, + )) + } + DescriptorOperation::MetadataHash => latch::DescriptorOperation::MetadataHash, + DescriptorOperation::MetadataHashAt(descriptor_metadata_hash_at_args) => { + latch::DescriptorOperation::MetadataHashAt(descriptor_metadata_hash_at_args_map( + descriptor_metadata_hash_at_args, + )) + } + } +} + +fn descriptor_read_via_stream_args_map( + args: DescriptorReadViaStreamArgs, +) -> latch::DescriptorReadViaStreamArgs { + latch::DescriptorReadViaStreamArgs { + offset: args.offset, + } +} + +fn descriptor_write_via_stream_args_map( + args: DescriptorWriteViaStreamArgs, +) -> latch::DescriptorWriteViaStreamArgs { + latch::DescriptorWriteViaStreamArgs { + offset: args.offset, + } +} + +fn descriptor_advise_args_map(args: DescriptorAdviseArgs) -> latch::DescriptorAdviseArgs { + latch::DescriptorAdviseArgs { + offset: args.offset, + length: args.length, + advice: args.advice, + } +} + +fn descriptor_set_size_args_map(args: DescriptorSetSizeArgs) -> latch::DescriptorSetSizeArgs { + latch::DescriptorSetSizeArgs { size: args.size } +} + +fn descriptor_set_times_args_map(args: DescriptorSetTimesArgs) -> latch::DescriptorSetTimesArgs { + latch::DescriptorSetTimesArgs { + data_access_timestamp: args.data_access_timestamp, + data_modification_timestamp: args.data_modification_timestamp, + } +} + +fn descriptor_read_args_map(args: DescriptorReadArgs) -> latch::DescriptorReadArgs { + latch::DescriptorReadArgs { + length: args.length, + offset: args.offset, + } +} + +fn descriptor_write_args_map(args: DescriptorWriteArgs) -> latch::DescriptorWriteArgs { + latch::DescriptorWriteArgs { + buffer_length: args.buffer_length, + offset: args.offset, + } +} + +fn descriptor_create_directory_at_args_map( + args: DescriptorCreateDirectoryAtArgs, +) -> latch::DescriptorCreateDirectoryAtArgs { + latch::DescriptorCreateDirectoryAtArgs { path: args.path } +} + +fn descriptor_stat_at_args_map(args: DescriptorStatAtArgs) -> latch::DescriptorStatAtArgs { + latch::DescriptorStatAtArgs { + path: args.path, + path_flags: args.path_flags, + } +} + +fn descriptor_set_times_at_args_map( + args: DescriptorSetTimesAtArgs, +) -> latch::DescriptorSetTimesAtArgs { + latch::DescriptorSetTimesAtArgs { + data_access_timestamp: args.data_access_timestamp, + data_modification_timestamp: args.data_modification_timestamp, + path: args.path, + path_flags: args.path_flags, + } +} + +fn descriptor_link_at_args_map(args: DescriptorLinkAtArgs) -> latch::DescriptorLinkAtArgs { + latch::DescriptorLinkAtArgs { + new_descriptor: args.new_descriptor, + new_path: args.new_path, + old_path: args.old_path, + old_path_flags: args.old_path_flags, + } +} + +fn descriptor_open_at_args_map(args: DescriptorOpenAtArgs) -> latch::DescriptorOpenAtArgs { + latch::DescriptorOpenAtArgs { + flags: args.flags, + open_flags: args.open_flags, + path: args.path, + path_flags: args.path_flags, + } +} + +fn descriptor_readlink_at_args_map( + args: DescriptorReadlinkAtArgs, +) -> latch::DescriptorReadlinkAtArgs { + latch::DescriptorReadlinkAtArgs { path: args.path } +} + +fn descriptor_remove_directory_at_args_map( + args: DescriptorRemoveDirectoryAtArgs, +) -> latch::DescriptorRemoveDirectoryAtArgs { + latch::DescriptorRemoveDirectoryAtArgs { path: args.path } +} + +fn descriptor_rename_at_args_map(args: DescriptorRenameAtArgs) -> latch::DescriptorRenameAtArgs { + latch::DescriptorRenameAtArgs { + new_descriptor: args.new_descriptor, + new_path: args.new_path, + old_path: args.old_path, + } +} + +fn descriptor_symlink_at_args_map(args: DescriptorSymlinkAtArgs) -> latch::DescriptorSymlinkAtArgs { + latch::DescriptorSymlinkAtArgs { + new_path: args.new_path, + old_path: args.old_path, + } +} + +fn descriptor_unlink_file_at_args_map( + args: DescriptorUnlinkFileAtArgs, +) -> latch::DescriptorUnlinkFileAtArgs { + latch::DescriptorUnlinkFileAtArgs { path: args.path } +} + +fn descriptor_metadata_hash_at_args_map( + args: DescriptorMetadataHashAtArgs, +) -> latch::DescriptorMetadataHashAtArgs { + latch::DescriptorMetadataHashAtArgs { + path: args.path, + path_flags: args.path_flags, + } +} + +wit_bindgen::generate!({ + path: "../../wit", + world: "filesystem-latch3", + generate_all +}); + +export!(AggregateLatch); diff --git a/components/latch-4/Cargo.toml b/components/latch-4/Cargo.toml new file mode 100644 index 0000000..00f20b4 --- /dev/null +++ b/components/latch-4/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "latch-4" +version = "0.1.0" +edition = "2021" +license = "Apache-2.0" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +wit-bindgen = { workspace = true } diff --git a/components/latch-4/README.md b/components/latch-4/README.md new file mode 100644 index 0000000..dd8d6f6 --- /dev/null +++ b/components/latch-4/README.md @@ -0,0 +1,3 @@ +# `latch-4` + +Filesystem latch that aggregates four other filesystem latches. diff --git a/components/latch-4/src/lib.rs b/components/latch-4/src/lib.rs new file mode 100644 index 0000000..1270f59 --- /dev/null +++ b/components/latch-4/src/lib.rs @@ -0,0 +1,273 @@ +#![no_main] + +use crate::{ + componentized::filesystem::{latch, latch1, latch2, latch3, latch4}, + exports::componentized::filesystem::latch::{ + Decision, DescriptorAdviseArgs, DescriptorCreateDirectoryAtArgs, DescriptorLinkAtArgs, + DescriptorMetadataHashAtArgs, DescriptorOpenAtArgs, DescriptorOperation, + DescriptorReadArgs, DescriptorReadViaStreamArgs, DescriptorReadlinkAtArgs, + DescriptorRemoveDirectoryAtArgs, DescriptorRenameAtArgs, DescriptorSetSizeArgs, + DescriptorSetTimesArgs, DescriptorSetTimesAtArgs, DescriptorStatAtArgs, + DescriptorSymlinkAtArgs, DescriptorUnlinkFileAtArgs, DescriptorWriteArgs, + DescriptorWriteViaStreamArgs, Guest as Latch, Operation, + }, +}; + +struct AggregateLatch {} + +impl Latch for AggregateLatch { + fn check(operation: Operation) -> Decision { + let operation = operation_map(operation); + let checks = vec![latch1::check, latch2::check, latch3::check, latch4::check]; + for check in checks { + match check(&operation) { + latch::Decision::Abstain => {} + latch::Decision::Allow => return Decision::Allow, + latch::Decision::Deny(error_code) => return Decision::Deny(error_code), + } + } + Decision::Abstain + } +} + +fn operation_map(operation: Operation) -> latch::Operation { + match operation { + Operation::Descriptor((descriptor, descriptor_operation)) => latch::Operation::Descriptor( + (descriptor, descriptor_operation_map(descriptor_operation)), + ), + } +} + +fn descriptor_operation_map( + descriptor_operation: DescriptorOperation, +) -> latch::DescriptorOperation { + match descriptor_operation { + DescriptorOperation::ReadViaStream(descriptor_read_via_stream_args) => { + latch::DescriptorOperation::ReadViaStream(descriptor_read_via_stream_args_map( + descriptor_read_via_stream_args, + )) + } + DescriptorOperation::WriteViaStream(descriptor_write_via_stream_args) => { + latch::DescriptorOperation::WriteViaStream(descriptor_write_via_stream_args_map( + descriptor_write_via_stream_args, + )) + } + DescriptorOperation::AppendViaStream => latch::DescriptorOperation::AppendViaStream, + DescriptorOperation::Advise(descriptor_advise_args) => { + latch::DescriptorOperation::Advise(descriptor_advise_args_map(descriptor_advise_args)) + } + DescriptorOperation::SyncData => latch::DescriptorOperation::SyncData, + DescriptorOperation::GetFlags => latch::DescriptorOperation::GetFlags, + DescriptorOperation::GetType => latch::DescriptorOperation::GetType, + DescriptorOperation::SetSize(descriptor_set_size_args) => { + latch::DescriptorOperation::SetSize(descriptor_set_size_args_map( + descriptor_set_size_args, + )) + } + DescriptorOperation::SetTimes(descriptor_set_times_args) => { + latch::DescriptorOperation::SetTimes(descriptor_set_times_args_map( + descriptor_set_times_args, + )) + } + DescriptorOperation::Read(descriptor_read_args) => { + latch::DescriptorOperation::Read(descriptor_read_args_map(descriptor_read_args)) + } + DescriptorOperation::Write(descriptor_write_args) => { + latch::DescriptorOperation::Write(descriptor_write_args_map(descriptor_write_args)) + } + DescriptorOperation::ReadDirectory => latch::DescriptorOperation::ReadDirectory, + DescriptorOperation::Sync => latch::DescriptorOperation::Sync, + DescriptorOperation::CreateDirectoryAt(descriptor_create_directory_at_args) => { + latch::DescriptorOperation::CreateDirectoryAt(descriptor_create_directory_at_args_map( + descriptor_create_directory_at_args, + )) + } + DescriptorOperation::Stat => latch::DescriptorOperation::Stat, + DescriptorOperation::StatAt(descriptor_stat_at_args) => { + latch::DescriptorOperation::StatAt(descriptor_stat_at_args_map(descriptor_stat_at_args)) + } + DescriptorOperation::SetTimesAt(descriptor_set_times_at_args) => { + latch::DescriptorOperation::SetTimesAt(descriptor_set_times_at_args_map( + descriptor_set_times_at_args, + )) + } + DescriptorOperation::LinkAt(descriptor_link_at_args) => { + latch::DescriptorOperation::LinkAt(descriptor_link_at_args_map(descriptor_link_at_args)) + } + DescriptorOperation::OpenAt(descriptor_open_at_args) => { + latch::DescriptorOperation::OpenAt(descriptor_open_at_args_map(descriptor_open_at_args)) + } + DescriptorOperation::ReadlinkAt(descriptor_readlink_at_args) => { + latch::DescriptorOperation::ReadlinkAt(descriptor_readlink_at_args_map( + descriptor_readlink_at_args, + )) + } + DescriptorOperation::RemoveDirectoryAt(descriptor_remove_directory_at_args) => { + latch::DescriptorOperation::RemoveDirectoryAt(descriptor_remove_directory_at_args_map( + descriptor_remove_directory_at_args, + )) + } + DescriptorOperation::RenameAt(descriptor_rename_at_args) => { + latch::DescriptorOperation::RenameAt(descriptor_rename_at_args_map( + descriptor_rename_at_args, + )) + } + DescriptorOperation::SymlinkAt(descriptor_symlink_at_args) => { + latch::DescriptorOperation::SymlinkAt(descriptor_symlink_at_args_map( + descriptor_symlink_at_args, + )) + } + DescriptorOperation::UnlinkFileAt(descriptor_unlink_file_at_args) => { + latch::DescriptorOperation::UnlinkFileAt(descriptor_unlink_file_at_args_map( + descriptor_unlink_file_at_args, + )) + } + DescriptorOperation::MetadataHash => latch::DescriptorOperation::MetadataHash, + DescriptorOperation::MetadataHashAt(descriptor_metadata_hash_at_args) => { + latch::DescriptorOperation::MetadataHashAt(descriptor_metadata_hash_at_args_map( + descriptor_metadata_hash_at_args, + )) + } + } +} + +fn descriptor_read_via_stream_args_map( + args: DescriptorReadViaStreamArgs, +) -> latch::DescriptorReadViaStreamArgs { + latch::DescriptorReadViaStreamArgs { + offset: args.offset, + } +} + +fn descriptor_write_via_stream_args_map( + args: DescriptorWriteViaStreamArgs, +) -> latch::DescriptorWriteViaStreamArgs { + latch::DescriptorWriteViaStreamArgs { + offset: args.offset, + } +} + +fn descriptor_advise_args_map(args: DescriptorAdviseArgs) -> latch::DescriptorAdviseArgs { + latch::DescriptorAdviseArgs { + offset: args.offset, + length: args.length, + advice: args.advice, + } +} + +fn descriptor_set_size_args_map(args: DescriptorSetSizeArgs) -> latch::DescriptorSetSizeArgs { + latch::DescriptorSetSizeArgs { size: args.size } +} + +fn descriptor_set_times_args_map(args: DescriptorSetTimesArgs) -> latch::DescriptorSetTimesArgs { + latch::DescriptorSetTimesArgs { + data_access_timestamp: args.data_access_timestamp, + data_modification_timestamp: args.data_modification_timestamp, + } +} + +fn descriptor_read_args_map(args: DescriptorReadArgs) -> latch::DescriptorReadArgs { + latch::DescriptorReadArgs { + length: args.length, + offset: args.offset, + } +} + +fn descriptor_write_args_map(args: DescriptorWriteArgs) -> latch::DescriptorWriteArgs { + latch::DescriptorWriteArgs { + buffer_length: args.buffer_length, + offset: args.offset, + } +} + +fn descriptor_create_directory_at_args_map( + args: DescriptorCreateDirectoryAtArgs, +) -> latch::DescriptorCreateDirectoryAtArgs { + latch::DescriptorCreateDirectoryAtArgs { path: args.path } +} + +fn descriptor_stat_at_args_map(args: DescriptorStatAtArgs) -> latch::DescriptorStatAtArgs { + latch::DescriptorStatAtArgs { + path: args.path, + path_flags: args.path_flags, + } +} + +fn descriptor_set_times_at_args_map( + args: DescriptorSetTimesAtArgs, +) -> latch::DescriptorSetTimesAtArgs { + latch::DescriptorSetTimesAtArgs { + data_access_timestamp: args.data_access_timestamp, + data_modification_timestamp: args.data_modification_timestamp, + path: args.path, + path_flags: args.path_flags, + } +} + +fn descriptor_link_at_args_map(args: DescriptorLinkAtArgs) -> latch::DescriptorLinkAtArgs { + latch::DescriptorLinkAtArgs { + new_descriptor: args.new_descriptor, + new_path: args.new_path, + old_path: args.old_path, + old_path_flags: args.old_path_flags, + } +} + +fn descriptor_open_at_args_map(args: DescriptorOpenAtArgs) -> latch::DescriptorOpenAtArgs { + latch::DescriptorOpenAtArgs { + flags: args.flags, + open_flags: args.open_flags, + path: args.path, + path_flags: args.path_flags, + } +} + +fn descriptor_readlink_at_args_map( + args: DescriptorReadlinkAtArgs, +) -> latch::DescriptorReadlinkAtArgs { + latch::DescriptorReadlinkAtArgs { path: args.path } +} + +fn descriptor_remove_directory_at_args_map( + args: DescriptorRemoveDirectoryAtArgs, +) -> latch::DescriptorRemoveDirectoryAtArgs { + latch::DescriptorRemoveDirectoryAtArgs { path: args.path } +} + +fn descriptor_rename_at_args_map(args: DescriptorRenameAtArgs) -> latch::DescriptorRenameAtArgs { + latch::DescriptorRenameAtArgs { + new_descriptor: args.new_descriptor, + new_path: args.new_path, + old_path: args.old_path, + } +} + +fn descriptor_symlink_at_args_map(args: DescriptorSymlinkAtArgs) -> latch::DescriptorSymlinkAtArgs { + latch::DescriptorSymlinkAtArgs { + new_path: args.new_path, + old_path: args.old_path, + } +} + +fn descriptor_unlink_file_at_args_map( + args: DescriptorUnlinkFileAtArgs, +) -> latch::DescriptorUnlinkFileAtArgs { + latch::DescriptorUnlinkFileAtArgs { path: args.path } +} + +fn descriptor_metadata_hash_at_args_map( + args: DescriptorMetadataHashAtArgs, +) -> latch::DescriptorMetadataHashAtArgs { + latch::DescriptorMetadataHashAtArgs { + path: args.path, + path_flags: args.path_flags, + } +} + +wit_bindgen::generate!({ + path: "../../wit", + world: "filesystem-latch4", + generate_all +}); + +export!(AggregateLatch); diff --git a/components/latch-allow/Cargo.toml b/components/latch-allow/Cargo.toml new file mode 100644 index 0000000..8127f2a --- /dev/null +++ b/components/latch-allow/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "latch-allow" +version = "0.1.0" +edition = "2021" +license = "Apache-2.0" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +wit-bindgen = { workspace = true } diff --git a/components/latch-allow/README.md b/components/latch-allow/README.md new file mode 100644 index 0000000..08965e4 --- /dev/null +++ b/components/latch-allow/README.md @@ -0,0 +1,3 @@ +# `latch-allow` + +Filesystem latch that implicitly allows all operations. diff --git a/components/latch-allow/src/lib.rs b/components/latch-allow/src/lib.rs new file mode 100644 index 0000000..2d6b414 --- /dev/null +++ b/components/latch-allow/src/lib.rs @@ -0,0 +1,19 @@ +#![no_main] + +use crate::exports::componentized::filesystem::latch::{Decision, Guest as Latch, Operation}; + +struct AllowLatch {} + +impl Latch for AllowLatch { + fn check(_: Operation) -> Decision { + Decision::Allow + } +} + +wit_bindgen::generate!({ + path: "../../wit", + world: "filesystem-latch", + generate_all +}); + +export!(AllowLatch); diff --git a/components/latch-deny/Cargo.toml b/components/latch-deny/Cargo.toml new file mode 100644 index 0000000..d13196b --- /dev/null +++ b/components/latch-deny/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "latch-deny" +version = "0.1.0" +edition = "2021" +license = "Apache-2.0" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +wit-bindgen = { workspace = true } diff --git a/components/latch-deny/README.md b/components/latch-deny/README.md new file mode 100644 index 0000000..d46f427 --- /dev/null +++ b/components/latch-deny/README.md @@ -0,0 +1,3 @@ +# `latch-deny` + +Filesystem latch that implicitly denies all operations. diff --git a/components/latch-deny/src/lib.rs b/components/latch-deny/src/lib.rs new file mode 100644 index 0000000..05dd59a --- /dev/null +++ b/components/latch-deny/src/lib.rs @@ -0,0 +1,22 @@ +#![no_main] + +use crate::{ + exports::componentized::filesystem::latch::{Decision, Guest as Latch, Operation}, + wasi::filesystem::types::ErrorCode, +}; + +struct DenyLatch {} + +impl Latch for DenyLatch { + fn check(_: Operation) -> Decision { + Decision::Deny(ErrorCode::NotPermitted) + } +} + +wit_bindgen::generate!({ + path: "../../wit", + world: "filesystem-latch", + generate_all +}); + +export!(DenyLatch); diff --git a/components/latch-readonly/Cargo.toml b/components/latch-readonly/Cargo.toml new file mode 100644 index 0000000..8e3f98b --- /dev/null +++ b/components/latch-readonly/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "latch-readonly" +version = "0.1.0" +edition = "2021" +license = "Apache-2.0" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +wit-bindgen = { workspace = true } diff --git a/components/latch-readonly/README.md b/components/latch-readonly/README.md new file mode 100644 index 0000000..79e5e96 --- /dev/null +++ b/components/latch-readonly/README.md @@ -0,0 +1,3 @@ +# `latch-readonly` + +Filesystem latch that denies operations which would mutate the filesystem. diff --git a/components/latch-readonly/src/lib.rs b/components/latch-readonly/src/lib.rs new file mode 100644 index 0000000..327e264 --- /dev/null +++ b/components/latch-readonly/src/lib.rs @@ -0,0 +1,71 @@ +#![no_main] + +use crate::{ + exports::componentized::filesystem::latch::{ + Decision::{self, Abstain, Deny}, + DescriptorOpenAtArgs, DescriptorOperation, Guest as Latch, Operation, + }, + wasi::filesystem::types::{DescriptorFlags, ErrorCode::ReadOnly, OpenFlags}, +}; + +struct ReadOnlyLatch {} + +impl Latch for ReadOnlyLatch { + fn check(operation: Operation) -> Decision { + match operation { + Operation::Descriptor((_, descriptor_operation)) => match descriptor_operation { + DescriptorOperation::ReadViaStream(_) => Abstain, + DescriptorOperation::WriteViaStream(_) => Deny(ReadOnly), + DescriptorOperation::AppendViaStream => Deny(ReadOnly), + DescriptorOperation::Advise(_) => Abstain, + DescriptorOperation::SyncData => Deny(ReadOnly), + DescriptorOperation::GetFlags => Abstain, + DescriptorOperation::GetType => Abstain, + DescriptorOperation::SetSize(_) => Deny(ReadOnly), + DescriptorOperation::SetTimes(_) => Deny(ReadOnly), + DescriptorOperation::Read(_) => Abstain, + DescriptorOperation::Write(_) => Deny(ReadOnly), + DescriptorOperation::ReadDirectory => Abstain, + DescriptorOperation::Sync => Deny(ReadOnly), + DescriptorOperation::CreateDirectoryAt(_) => Deny(ReadOnly), + DescriptorOperation::Stat => Abstain, + DescriptorOperation::StatAt(_) => Abstain, + DescriptorOperation::SetTimesAt(_) => Deny(ReadOnly), + DescriptorOperation::LinkAt(_) => Deny(ReadOnly), + DescriptorOperation::OpenAt(DescriptorOpenAtArgs { + open_flags, flags, .. + }) => { + if open_flags.intersects( + OpenFlags::CREATE + .union(OpenFlags::EXCLUSIVE) + .union(OpenFlags::TRUNCATE), + ) || flags.intersects( + DescriptorFlags::WRITE + .union(DescriptorFlags::FILE_INTEGRITY_SYNC) + .union(DescriptorFlags::DATA_INTEGRITY_SYNC) + .union(DescriptorFlags::REQUESTED_WRITE_SYNC), + ) { + Deny(ReadOnly) + } else { + Abstain + } + } + DescriptorOperation::ReadlinkAt(_) => Abstain, + DescriptorOperation::RemoveDirectoryAt(_) => Deny(ReadOnly), + DescriptorOperation::RenameAt(_) => Deny(ReadOnly), + DescriptorOperation::SymlinkAt(_) => Deny(ReadOnly), + DescriptorOperation::UnlinkFileAt(_) => Deny(ReadOnly), + DescriptorOperation::MetadataHash => Abstain, + DescriptorOperation::MetadataHashAt(_) => Abstain, + }, + } + } +} + +wit_bindgen::generate!({ + path: "../../wit", + world: "filesystem-latch", + generate_all +}); + +export!(ReadOnlyLatch); diff --git a/wit/latch.wit b/wit/latch.wit new file mode 100644 index 0000000..ac2c49f --- /dev/null +++ b/wit/latch.wit @@ -0,0 +1,165 @@ + +interface latch { + use wasi:filesystem/types@0.2.6.{advice, descriptor, descriptor-flags, error-code, filesize, open-flags, new-timestamp, path-flags}; + + record descriptor-get-directory-args { + path: string, + } + + record descriptor-read-via-stream-args { + offset: filesize, + } + + record descriptor-write-via-stream-args { + offset: filesize, + } + + record descriptor-advise-args { + offset: filesize, + length: filesize, + advice: advice, + } + + record descriptor-set-size-args { + size: filesize, + } + + record descriptor-set-times-args { + data-access-timestamp: new-timestamp, + data-modification-timestamp: new-timestamp, + } + + record descriptor-read-args { + length: filesize, + offset: filesize, + } + + record descriptor-write-args { + buffer-length: u64, + offset: filesize, + } + + record descriptor-create-directory-at-args { + path: string, + } + + record descriptor-stat-at-args { + path-flags: path-flags, + path: string, + } + + record descriptor-set-times-at-args { + path-flags: path-flags, + path: string, + data-access-timestamp: new-timestamp, + data-modification-timestamp: new-timestamp, + } + + record descriptor-link-at-args { + old-path-flags: path-flags, + old-path: string, + new-descriptor: borrow, + new-path: string, + } + + record descriptor-open-at-args { + path-flags: path-flags, + path: string, + open-flags: open-flags, + %flags: descriptor-flags, + } + + record descriptor-readlink-at-args { + path: string, + } + + record descriptor-remove-directory-at-args { + path: string, + } + + record descriptor-rename-at-args { + old-path: string, + new-descriptor: borrow, + new-path: string, + } + + record descriptor-symlink-at-args { + old-path: string, + new-path: string, + } + + record descriptor-unlink-file-at-args { + path: string, + } + + + record descriptor-metadata-hash-at-args { + path-flags: path-flags, + path: string, + } + + variant descriptor-operation { + read-via-stream(descriptor-read-via-stream-args), + write-via-stream(descriptor-write-via-stream-args), + append-via-stream, + advise(descriptor-advise-args), + sync-data, + get-flags, + get-type, + set-size(descriptor-set-size-args), + set-times(descriptor-set-times-args), + read(descriptor-read-args), + write(descriptor-write-args), + read-directory, + sync, + create-directory-at(descriptor-create-directory-at-args), + stat, + stat-at(descriptor-stat-at-args), + set-times-at( descriptor-set-times-at-args), + link-at(descriptor-link-at-args), + open-at(descriptor-open-at-args), + readlink-at(descriptor-readlink-at-args), + remove-directory-at(descriptor-remove-directory-at-args), + rename-at(descriptor-rename-at-args), + symlink-at(descriptor-symlink-at-args), + unlink-file-at(descriptor-unlink-file-at-args), + metadata-hash, + metadata-hash-at(descriptor-metadata-hash-at-args), + } + + variant operation { + descriptor(tuple, descriptor-operation>), + } + + variant decision { + abstain, + allow, + deny(error-code), + } + + check: func(operation: operation) -> decision; +} + +interface latch1 { + use latch.{operation, decision}; + + check: func(operation: operation) -> decision; +} + +interface latch2 { + use latch.{operation, decision}; + + check: func(operation: operation) -> decision; +} + +interface latch3 { + use latch.{operation, decision}; + + check: func(operation: operation) -> decision; +} + +interface latch4 { + use latch.{operation, decision}; + + check: func(operation: operation) -> decision; +} diff --git a/wit/worlds.wit b/wit/worlds.wit index bad82d8..1b231f5 100644 --- a/wit/worlds.wit +++ b/wit/worlds.wit @@ -1,6 +1,7 @@ package componentized:filesystem; world filesystem { + import latch; import wasi:config/store@0.2.0-rc.1; import wasi:logging/logging@0.1.0-draft; import wasi:filesystem/preopens@0.2.6; @@ -8,3 +9,28 @@ world filesystem { import wasi:filesystem/types@0.2.6; export wasi:filesystem/types@0.2.6; } + +world filesystem-latch { + export latch; +} + +world filesystem-latch2 { + import latch1; + import latch2; + export latch; +} + +world filesystem-latch3 { + import latch1; + import latch2; + import latch3; + export latch; +} + +world filesystem-latch4 { + import latch1; + import latch2; + import latch3; + import latch4; + export latch; +} From 4d833522848c68cdaa8ca528587f6dd69ec59ac3 Mon Sep 17 00:00:00 2001 From: Scott Andrews Date: Mon, 18 May 2026 13:51:16 -0400 Subject: [PATCH 2/4] centralize logic for latch-n components Signed-off-by: Scott Andrews --- Cargo.lock | 28 +- Cargo.toml | 2 + components/latch-2/src/lib.rs | 273 ------------------ components/latch-4/src/lib.rs | 273 ------------------ components/{latch-2 => latch-n2}/Cargo.toml | 3 +- components/{latch-2 => latch-n2}/README.md | 2 +- components/latch-n2/src/lib.rs | 18 ++ components/{latch-4 => latch-n3}/Cargo.toml | 3 +- components/{latch-3 => latch-n3}/README.md | 2 +- components/latch-n3/src/lib.rs | 18 ++ components/{latch-3 => latch-n4}/Cargo.toml | 3 +- components/{latch-4 => latch-n4}/README.md | 2 +- components/latch-n4/src/lib.rs | 18 ++ components/latch-n5/Cargo.toml | 12 + components/latch-n5/README.md | 3 + components/latch-n5/src/lib.rs | 26 ++ crates/latch-n/Cargo.toml | 8 + .../latch-3 => crates/latch-n}/src/lib.rs | 50 ++-- wit/worlds.wit | 17 +- 19 files changed, 168 insertions(+), 593 deletions(-) delete mode 100644 components/latch-2/src/lib.rs delete mode 100644 components/latch-4/src/lib.rs rename components/{latch-2 => latch-n2}/Cargo.toml (75%) rename components/{latch-2 => latch-n2}/README.md (83%) create mode 100644 components/latch-n2/src/lib.rs rename components/{latch-4 => latch-n3}/Cargo.toml (75%) rename components/{latch-3 => latch-n3}/README.md (83%) create mode 100644 components/latch-n3/src/lib.rs rename components/{latch-3 => latch-n4}/Cargo.toml (75%) rename components/{latch-4 => latch-n4}/README.md (83%) create mode 100644 components/latch-n4/src/lib.rs create mode 100644 components/latch-n5/Cargo.toml create mode 100644 components/latch-n5/README.md create mode 100644 components/latch-n5/src/lib.rs create mode 100644 crates/latch-n/Cargo.toml rename {components/latch-3 => crates/latch-n}/src/lib.rs (91%) diff --git a/Cargo.lock b/Cargo.lock index 06048e8..88a0af2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -80,37 +80,55 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" [[package]] -name = "latch-2" +name = "latch-allow" version = "0.1.0" dependencies = [ "wit-bindgen", ] [[package]] -name = "latch-3" +name = "latch-deny" version = "0.1.0" dependencies = [ "wit-bindgen", ] [[package]] -name = "latch-4" +name = "latch-n" version = "0.1.0" dependencies = [ "wit-bindgen", ] [[package]] -name = "latch-allow" +name = "latch-n2" version = "0.1.0" dependencies = [ + "latch-n", "wit-bindgen", ] [[package]] -name = "latch-deny" +name = "latch-n3" +version = "0.1.0" +dependencies = [ + "latch-n", + "wit-bindgen", +] + +[[package]] +name = "latch-n4" +version = "0.1.0" +dependencies = [ + "latch-n", + "wit-bindgen", +] + +[[package]] +name = "latch-n5" version = "0.1.0" dependencies = [ + "latch-n", "wit-bindgen", ] diff --git a/Cargo.toml b/Cargo.toml index b724f13..7a53530 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,9 @@ resolver = "2" members = [ "components/*", + "crates/*", ] [workspace.dependencies] +latch-n = { path = "./crates/latch-n" } wit-bindgen = "0.57.1" diff --git a/components/latch-2/src/lib.rs b/components/latch-2/src/lib.rs deleted file mode 100644 index f0ce617..0000000 --- a/components/latch-2/src/lib.rs +++ /dev/null @@ -1,273 +0,0 @@ -#![no_main] - -use crate::{ - componentized::filesystem::{latch, latch1, latch2}, - exports::componentized::filesystem::latch::{ - Decision, DescriptorAdviseArgs, DescriptorCreateDirectoryAtArgs, DescriptorLinkAtArgs, - DescriptorMetadataHashAtArgs, DescriptorOpenAtArgs, DescriptorOperation, - DescriptorReadArgs, DescriptorReadViaStreamArgs, DescriptorReadlinkAtArgs, - DescriptorRemoveDirectoryAtArgs, DescriptorRenameAtArgs, DescriptorSetSizeArgs, - DescriptorSetTimesArgs, DescriptorSetTimesAtArgs, DescriptorStatAtArgs, - DescriptorSymlinkAtArgs, DescriptorUnlinkFileAtArgs, DescriptorWriteArgs, - DescriptorWriteViaStreamArgs, Guest as Latch, Operation, - }, -}; - -struct AggregateLatch {} - -impl Latch for AggregateLatch { - fn check(operation: Operation) -> Decision { - let operation = operation_map(operation); - let checks = vec![latch1::check, latch2::check]; - for check in checks { - match check(&operation) { - latch::Decision::Abstain => {} - latch::Decision::Allow => return Decision::Allow, - latch::Decision::Deny(error_code) => return Decision::Deny(error_code), - } - } - Decision::Abstain - } -} - -fn operation_map(operation: Operation) -> latch::Operation { - match operation { - Operation::Descriptor((descriptor, descriptor_operation)) => latch::Operation::Descriptor( - (descriptor, descriptor_operation_map(descriptor_operation)), - ), - } -} - -fn descriptor_operation_map( - descriptor_operation: DescriptorOperation, -) -> latch::DescriptorOperation { - match descriptor_operation { - DescriptorOperation::ReadViaStream(descriptor_read_via_stream_args) => { - latch::DescriptorOperation::ReadViaStream(descriptor_read_via_stream_args_map( - descriptor_read_via_stream_args, - )) - } - DescriptorOperation::WriteViaStream(descriptor_write_via_stream_args) => { - latch::DescriptorOperation::WriteViaStream(descriptor_write_via_stream_args_map( - descriptor_write_via_stream_args, - )) - } - DescriptorOperation::AppendViaStream => latch::DescriptorOperation::AppendViaStream, - DescriptorOperation::Advise(descriptor_advise_args) => { - latch::DescriptorOperation::Advise(descriptor_advise_args_map(descriptor_advise_args)) - } - DescriptorOperation::SyncData => latch::DescriptorOperation::SyncData, - DescriptorOperation::GetFlags => latch::DescriptorOperation::GetFlags, - DescriptorOperation::GetType => latch::DescriptorOperation::GetType, - DescriptorOperation::SetSize(descriptor_set_size_args) => { - latch::DescriptorOperation::SetSize(descriptor_set_size_args_map( - descriptor_set_size_args, - )) - } - DescriptorOperation::SetTimes(descriptor_set_times_args) => { - latch::DescriptorOperation::SetTimes(descriptor_set_times_args_map( - descriptor_set_times_args, - )) - } - DescriptorOperation::Read(descriptor_read_args) => { - latch::DescriptorOperation::Read(descriptor_read_args_map(descriptor_read_args)) - } - DescriptorOperation::Write(descriptor_write_args) => { - latch::DescriptorOperation::Write(descriptor_write_args_map(descriptor_write_args)) - } - DescriptorOperation::ReadDirectory => latch::DescriptorOperation::ReadDirectory, - DescriptorOperation::Sync => latch::DescriptorOperation::Sync, - DescriptorOperation::CreateDirectoryAt(descriptor_create_directory_at_args) => { - latch::DescriptorOperation::CreateDirectoryAt(descriptor_create_directory_at_args_map( - descriptor_create_directory_at_args, - )) - } - DescriptorOperation::Stat => latch::DescriptorOperation::Stat, - DescriptorOperation::StatAt(descriptor_stat_at_args) => { - latch::DescriptorOperation::StatAt(descriptor_stat_at_args_map(descriptor_stat_at_args)) - } - DescriptorOperation::SetTimesAt(descriptor_set_times_at_args) => { - latch::DescriptorOperation::SetTimesAt(descriptor_set_times_at_args_map( - descriptor_set_times_at_args, - )) - } - DescriptorOperation::LinkAt(descriptor_link_at_args) => { - latch::DescriptorOperation::LinkAt(descriptor_link_at_args_map(descriptor_link_at_args)) - } - DescriptorOperation::OpenAt(descriptor_open_at_args) => { - latch::DescriptorOperation::OpenAt(descriptor_open_at_args_map(descriptor_open_at_args)) - } - DescriptorOperation::ReadlinkAt(descriptor_readlink_at_args) => { - latch::DescriptorOperation::ReadlinkAt(descriptor_readlink_at_args_map( - descriptor_readlink_at_args, - )) - } - DescriptorOperation::RemoveDirectoryAt(descriptor_remove_directory_at_args) => { - latch::DescriptorOperation::RemoveDirectoryAt(descriptor_remove_directory_at_args_map( - descriptor_remove_directory_at_args, - )) - } - DescriptorOperation::RenameAt(descriptor_rename_at_args) => { - latch::DescriptorOperation::RenameAt(descriptor_rename_at_args_map( - descriptor_rename_at_args, - )) - } - DescriptorOperation::SymlinkAt(descriptor_symlink_at_args) => { - latch::DescriptorOperation::SymlinkAt(descriptor_symlink_at_args_map( - descriptor_symlink_at_args, - )) - } - DescriptorOperation::UnlinkFileAt(descriptor_unlink_file_at_args) => { - latch::DescriptorOperation::UnlinkFileAt(descriptor_unlink_file_at_args_map( - descriptor_unlink_file_at_args, - )) - } - DescriptorOperation::MetadataHash => latch::DescriptorOperation::MetadataHash, - DescriptorOperation::MetadataHashAt(descriptor_metadata_hash_at_args) => { - latch::DescriptorOperation::MetadataHashAt(descriptor_metadata_hash_at_args_map( - descriptor_metadata_hash_at_args, - )) - } - } -} - -fn descriptor_read_via_stream_args_map( - args: DescriptorReadViaStreamArgs, -) -> latch::DescriptorReadViaStreamArgs { - latch::DescriptorReadViaStreamArgs { - offset: args.offset, - } -} - -fn descriptor_write_via_stream_args_map( - args: DescriptorWriteViaStreamArgs, -) -> latch::DescriptorWriteViaStreamArgs { - latch::DescriptorWriteViaStreamArgs { - offset: args.offset, - } -} - -fn descriptor_advise_args_map(args: DescriptorAdviseArgs) -> latch::DescriptorAdviseArgs { - latch::DescriptorAdviseArgs { - offset: args.offset, - length: args.length, - advice: args.advice, - } -} - -fn descriptor_set_size_args_map(args: DescriptorSetSizeArgs) -> latch::DescriptorSetSizeArgs { - latch::DescriptorSetSizeArgs { size: args.size } -} - -fn descriptor_set_times_args_map(args: DescriptorSetTimesArgs) -> latch::DescriptorSetTimesArgs { - latch::DescriptorSetTimesArgs { - data_access_timestamp: args.data_access_timestamp, - data_modification_timestamp: args.data_modification_timestamp, - } -} - -fn descriptor_read_args_map(args: DescriptorReadArgs) -> latch::DescriptorReadArgs { - latch::DescriptorReadArgs { - length: args.length, - offset: args.offset, - } -} - -fn descriptor_write_args_map(args: DescriptorWriteArgs) -> latch::DescriptorWriteArgs { - latch::DescriptorWriteArgs { - buffer_length: args.buffer_length, - offset: args.offset, - } -} - -fn descriptor_create_directory_at_args_map( - args: DescriptorCreateDirectoryAtArgs, -) -> latch::DescriptorCreateDirectoryAtArgs { - latch::DescriptorCreateDirectoryAtArgs { path: args.path } -} - -fn descriptor_stat_at_args_map(args: DescriptorStatAtArgs) -> latch::DescriptorStatAtArgs { - latch::DescriptorStatAtArgs { - path: args.path, - path_flags: args.path_flags, - } -} - -fn descriptor_set_times_at_args_map( - args: DescriptorSetTimesAtArgs, -) -> latch::DescriptorSetTimesAtArgs { - latch::DescriptorSetTimesAtArgs { - data_access_timestamp: args.data_access_timestamp, - data_modification_timestamp: args.data_modification_timestamp, - path: args.path, - path_flags: args.path_flags, - } -} - -fn descriptor_link_at_args_map(args: DescriptorLinkAtArgs) -> latch::DescriptorLinkAtArgs { - latch::DescriptorLinkAtArgs { - new_descriptor: args.new_descriptor, - new_path: args.new_path, - old_path: args.old_path, - old_path_flags: args.old_path_flags, - } -} - -fn descriptor_open_at_args_map(args: DescriptorOpenAtArgs) -> latch::DescriptorOpenAtArgs { - latch::DescriptorOpenAtArgs { - flags: args.flags, - open_flags: args.open_flags, - path: args.path, - path_flags: args.path_flags, - } -} - -fn descriptor_readlink_at_args_map( - args: DescriptorReadlinkAtArgs, -) -> latch::DescriptorReadlinkAtArgs { - latch::DescriptorReadlinkAtArgs { path: args.path } -} - -fn descriptor_remove_directory_at_args_map( - args: DescriptorRemoveDirectoryAtArgs, -) -> latch::DescriptorRemoveDirectoryAtArgs { - latch::DescriptorRemoveDirectoryAtArgs { path: args.path } -} - -fn descriptor_rename_at_args_map(args: DescriptorRenameAtArgs) -> latch::DescriptorRenameAtArgs { - latch::DescriptorRenameAtArgs { - new_descriptor: args.new_descriptor, - new_path: args.new_path, - old_path: args.old_path, - } -} - -fn descriptor_symlink_at_args_map(args: DescriptorSymlinkAtArgs) -> latch::DescriptorSymlinkAtArgs { - latch::DescriptorSymlinkAtArgs { - new_path: args.new_path, - old_path: args.old_path, - } -} - -fn descriptor_unlink_file_at_args_map( - args: DescriptorUnlinkFileAtArgs, -) -> latch::DescriptorUnlinkFileAtArgs { - latch::DescriptorUnlinkFileAtArgs { path: args.path } -} - -fn descriptor_metadata_hash_at_args_map( - args: DescriptorMetadataHashAtArgs, -) -> latch::DescriptorMetadataHashAtArgs { - latch::DescriptorMetadataHashAtArgs { - path: args.path, - path_flags: args.path_flags, - } -} - -wit_bindgen::generate!({ - path: "../../wit", - world: "filesystem-latch2", - generate_all -}); - -export!(AggregateLatch); diff --git a/components/latch-4/src/lib.rs b/components/latch-4/src/lib.rs deleted file mode 100644 index 1270f59..0000000 --- a/components/latch-4/src/lib.rs +++ /dev/null @@ -1,273 +0,0 @@ -#![no_main] - -use crate::{ - componentized::filesystem::{latch, latch1, latch2, latch3, latch4}, - exports::componentized::filesystem::latch::{ - Decision, DescriptorAdviseArgs, DescriptorCreateDirectoryAtArgs, DescriptorLinkAtArgs, - DescriptorMetadataHashAtArgs, DescriptorOpenAtArgs, DescriptorOperation, - DescriptorReadArgs, DescriptorReadViaStreamArgs, DescriptorReadlinkAtArgs, - DescriptorRemoveDirectoryAtArgs, DescriptorRenameAtArgs, DescriptorSetSizeArgs, - DescriptorSetTimesArgs, DescriptorSetTimesAtArgs, DescriptorStatAtArgs, - DescriptorSymlinkAtArgs, DescriptorUnlinkFileAtArgs, DescriptorWriteArgs, - DescriptorWriteViaStreamArgs, Guest as Latch, Operation, - }, -}; - -struct AggregateLatch {} - -impl Latch for AggregateLatch { - fn check(operation: Operation) -> Decision { - let operation = operation_map(operation); - let checks = vec![latch1::check, latch2::check, latch3::check, latch4::check]; - for check in checks { - match check(&operation) { - latch::Decision::Abstain => {} - latch::Decision::Allow => return Decision::Allow, - latch::Decision::Deny(error_code) => return Decision::Deny(error_code), - } - } - Decision::Abstain - } -} - -fn operation_map(operation: Operation) -> latch::Operation { - match operation { - Operation::Descriptor((descriptor, descriptor_operation)) => latch::Operation::Descriptor( - (descriptor, descriptor_operation_map(descriptor_operation)), - ), - } -} - -fn descriptor_operation_map( - descriptor_operation: DescriptorOperation, -) -> latch::DescriptorOperation { - match descriptor_operation { - DescriptorOperation::ReadViaStream(descriptor_read_via_stream_args) => { - latch::DescriptorOperation::ReadViaStream(descriptor_read_via_stream_args_map( - descriptor_read_via_stream_args, - )) - } - DescriptorOperation::WriteViaStream(descriptor_write_via_stream_args) => { - latch::DescriptorOperation::WriteViaStream(descriptor_write_via_stream_args_map( - descriptor_write_via_stream_args, - )) - } - DescriptorOperation::AppendViaStream => latch::DescriptorOperation::AppendViaStream, - DescriptorOperation::Advise(descriptor_advise_args) => { - latch::DescriptorOperation::Advise(descriptor_advise_args_map(descriptor_advise_args)) - } - DescriptorOperation::SyncData => latch::DescriptorOperation::SyncData, - DescriptorOperation::GetFlags => latch::DescriptorOperation::GetFlags, - DescriptorOperation::GetType => latch::DescriptorOperation::GetType, - DescriptorOperation::SetSize(descriptor_set_size_args) => { - latch::DescriptorOperation::SetSize(descriptor_set_size_args_map( - descriptor_set_size_args, - )) - } - DescriptorOperation::SetTimes(descriptor_set_times_args) => { - latch::DescriptorOperation::SetTimes(descriptor_set_times_args_map( - descriptor_set_times_args, - )) - } - DescriptorOperation::Read(descriptor_read_args) => { - latch::DescriptorOperation::Read(descriptor_read_args_map(descriptor_read_args)) - } - DescriptorOperation::Write(descriptor_write_args) => { - latch::DescriptorOperation::Write(descriptor_write_args_map(descriptor_write_args)) - } - DescriptorOperation::ReadDirectory => latch::DescriptorOperation::ReadDirectory, - DescriptorOperation::Sync => latch::DescriptorOperation::Sync, - DescriptorOperation::CreateDirectoryAt(descriptor_create_directory_at_args) => { - latch::DescriptorOperation::CreateDirectoryAt(descriptor_create_directory_at_args_map( - descriptor_create_directory_at_args, - )) - } - DescriptorOperation::Stat => latch::DescriptorOperation::Stat, - DescriptorOperation::StatAt(descriptor_stat_at_args) => { - latch::DescriptorOperation::StatAt(descriptor_stat_at_args_map(descriptor_stat_at_args)) - } - DescriptorOperation::SetTimesAt(descriptor_set_times_at_args) => { - latch::DescriptorOperation::SetTimesAt(descriptor_set_times_at_args_map( - descriptor_set_times_at_args, - )) - } - DescriptorOperation::LinkAt(descriptor_link_at_args) => { - latch::DescriptorOperation::LinkAt(descriptor_link_at_args_map(descriptor_link_at_args)) - } - DescriptorOperation::OpenAt(descriptor_open_at_args) => { - latch::DescriptorOperation::OpenAt(descriptor_open_at_args_map(descriptor_open_at_args)) - } - DescriptorOperation::ReadlinkAt(descriptor_readlink_at_args) => { - latch::DescriptorOperation::ReadlinkAt(descriptor_readlink_at_args_map( - descriptor_readlink_at_args, - )) - } - DescriptorOperation::RemoveDirectoryAt(descriptor_remove_directory_at_args) => { - latch::DescriptorOperation::RemoveDirectoryAt(descriptor_remove_directory_at_args_map( - descriptor_remove_directory_at_args, - )) - } - DescriptorOperation::RenameAt(descriptor_rename_at_args) => { - latch::DescriptorOperation::RenameAt(descriptor_rename_at_args_map( - descriptor_rename_at_args, - )) - } - DescriptorOperation::SymlinkAt(descriptor_symlink_at_args) => { - latch::DescriptorOperation::SymlinkAt(descriptor_symlink_at_args_map( - descriptor_symlink_at_args, - )) - } - DescriptorOperation::UnlinkFileAt(descriptor_unlink_file_at_args) => { - latch::DescriptorOperation::UnlinkFileAt(descriptor_unlink_file_at_args_map( - descriptor_unlink_file_at_args, - )) - } - DescriptorOperation::MetadataHash => latch::DescriptorOperation::MetadataHash, - DescriptorOperation::MetadataHashAt(descriptor_metadata_hash_at_args) => { - latch::DescriptorOperation::MetadataHashAt(descriptor_metadata_hash_at_args_map( - descriptor_metadata_hash_at_args, - )) - } - } -} - -fn descriptor_read_via_stream_args_map( - args: DescriptorReadViaStreamArgs, -) -> latch::DescriptorReadViaStreamArgs { - latch::DescriptorReadViaStreamArgs { - offset: args.offset, - } -} - -fn descriptor_write_via_stream_args_map( - args: DescriptorWriteViaStreamArgs, -) -> latch::DescriptorWriteViaStreamArgs { - latch::DescriptorWriteViaStreamArgs { - offset: args.offset, - } -} - -fn descriptor_advise_args_map(args: DescriptorAdviseArgs) -> latch::DescriptorAdviseArgs { - latch::DescriptorAdviseArgs { - offset: args.offset, - length: args.length, - advice: args.advice, - } -} - -fn descriptor_set_size_args_map(args: DescriptorSetSizeArgs) -> latch::DescriptorSetSizeArgs { - latch::DescriptorSetSizeArgs { size: args.size } -} - -fn descriptor_set_times_args_map(args: DescriptorSetTimesArgs) -> latch::DescriptorSetTimesArgs { - latch::DescriptorSetTimesArgs { - data_access_timestamp: args.data_access_timestamp, - data_modification_timestamp: args.data_modification_timestamp, - } -} - -fn descriptor_read_args_map(args: DescriptorReadArgs) -> latch::DescriptorReadArgs { - latch::DescriptorReadArgs { - length: args.length, - offset: args.offset, - } -} - -fn descriptor_write_args_map(args: DescriptorWriteArgs) -> latch::DescriptorWriteArgs { - latch::DescriptorWriteArgs { - buffer_length: args.buffer_length, - offset: args.offset, - } -} - -fn descriptor_create_directory_at_args_map( - args: DescriptorCreateDirectoryAtArgs, -) -> latch::DescriptorCreateDirectoryAtArgs { - latch::DescriptorCreateDirectoryAtArgs { path: args.path } -} - -fn descriptor_stat_at_args_map(args: DescriptorStatAtArgs) -> latch::DescriptorStatAtArgs { - latch::DescriptorStatAtArgs { - path: args.path, - path_flags: args.path_flags, - } -} - -fn descriptor_set_times_at_args_map( - args: DescriptorSetTimesAtArgs, -) -> latch::DescriptorSetTimesAtArgs { - latch::DescriptorSetTimesAtArgs { - data_access_timestamp: args.data_access_timestamp, - data_modification_timestamp: args.data_modification_timestamp, - path: args.path, - path_flags: args.path_flags, - } -} - -fn descriptor_link_at_args_map(args: DescriptorLinkAtArgs) -> latch::DescriptorLinkAtArgs { - latch::DescriptorLinkAtArgs { - new_descriptor: args.new_descriptor, - new_path: args.new_path, - old_path: args.old_path, - old_path_flags: args.old_path_flags, - } -} - -fn descriptor_open_at_args_map(args: DescriptorOpenAtArgs) -> latch::DescriptorOpenAtArgs { - latch::DescriptorOpenAtArgs { - flags: args.flags, - open_flags: args.open_flags, - path: args.path, - path_flags: args.path_flags, - } -} - -fn descriptor_readlink_at_args_map( - args: DescriptorReadlinkAtArgs, -) -> latch::DescriptorReadlinkAtArgs { - latch::DescriptorReadlinkAtArgs { path: args.path } -} - -fn descriptor_remove_directory_at_args_map( - args: DescriptorRemoveDirectoryAtArgs, -) -> latch::DescriptorRemoveDirectoryAtArgs { - latch::DescriptorRemoveDirectoryAtArgs { path: args.path } -} - -fn descriptor_rename_at_args_map(args: DescriptorRenameAtArgs) -> latch::DescriptorRenameAtArgs { - latch::DescriptorRenameAtArgs { - new_descriptor: args.new_descriptor, - new_path: args.new_path, - old_path: args.old_path, - } -} - -fn descriptor_symlink_at_args_map(args: DescriptorSymlinkAtArgs) -> latch::DescriptorSymlinkAtArgs { - latch::DescriptorSymlinkAtArgs { - new_path: args.new_path, - old_path: args.old_path, - } -} - -fn descriptor_unlink_file_at_args_map( - args: DescriptorUnlinkFileAtArgs, -) -> latch::DescriptorUnlinkFileAtArgs { - latch::DescriptorUnlinkFileAtArgs { path: args.path } -} - -fn descriptor_metadata_hash_at_args_map( - args: DescriptorMetadataHashAtArgs, -) -> latch::DescriptorMetadataHashAtArgs { - latch::DescriptorMetadataHashAtArgs { - path: args.path, - path_flags: args.path_flags, - } -} - -wit_bindgen::generate!({ - path: "../../wit", - world: "filesystem-latch4", - generate_all -}); - -export!(AggregateLatch); diff --git a/components/latch-2/Cargo.toml b/components/latch-n2/Cargo.toml similarity index 75% rename from components/latch-2/Cargo.toml rename to components/latch-n2/Cargo.toml index 6fc6748..ddd1054 100644 --- a/components/latch-2/Cargo.toml +++ b/components/latch-n2/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "latch-2" +name = "latch-n2" version = "0.1.0" edition = "2021" license = "Apache-2.0" @@ -8,4 +8,5 @@ license = "Apache-2.0" crate-type = ["cdylib"] [dependencies] +latch-n = { workspace = true } wit-bindgen = { workspace = true } diff --git a/components/latch-2/README.md b/components/latch-n2/README.md similarity index 83% rename from components/latch-2/README.md rename to components/latch-n2/README.md index 7d3ce33..d302838 100644 --- a/components/latch-2/README.md +++ b/components/latch-n2/README.md @@ -1,3 +1,3 @@ -# `latch-2` +# `latch-n2` Filesystem latch that aggregates two other filesystem latches. diff --git a/components/latch-n2/src/lib.rs b/components/latch-n2/src/lib.rs new file mode 100644 index 0000000..e157ad0 --- /dev/null +++ b/components/latch-n2/src/lib.rs @@ -0,0 +1,18 @@ +#![no_main] + +use latch_n::bindings::componentized::filesystem::{latch as latch0, latch1}; +use latch_n::bindings::exports::componentized::filesystem::latch::{ + Decision, Guest as Latch, Operation, +}; + +struct LatchN2 {} + +impl Latch for LatchN2 { + #[allow(async_fn_in_trait)] + fn check(operation: Operation<'_>) -> Decision { + let checks = vec![latch0::check, latch1::check]; + latch_n::check(operation, checks) + } +} + +latch_n::export!(LatchN2 with_types_in latch_n::bindings); diff --git a/components/latch-4/Cargo.toml b/components/latch-n3/Cargo.toml similarity index 75% rename from components/latch-4/Cargo.toml rename to components/latch-n3/Cargo.toml index 00f20b4..beb0e00 100644 --- a/components/latch-4/Cargo.toml +++ b/components/latch-n3/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "latch-4" +name = "latch-n3" version = "0.1.0" edition = "2021" license = "Apache-2.0" @@ -8,4 +8,5 @@ license = "Apache-2.0" crate-type = ["cdylib"] [dependencies] +latch-n = { workspace = true } wit-bindgen = { workspace = true } diff --git a/components/latch-3/README.md b/components/latch-n3/README.md similarity index 83% rename from components/latch-3/README.md rename to components/latch-n3/README.md index 0438bd8..8ff8624 100644 --- a/components/latch-3/README.md +++ b/components/latch-n3/README.md @@ -1,3 +1,3 @@ -# `latch-3` +# `latch-n3` Filesystem latch that aggregates three other filesystem latches. diff --git a/components/latch-n3/src/lib.rs b/components/latch-n3/src/lib.rs new file mode 100644 index 0000000..18f89a8 --- /dev/null +++ b/components/latch-n3/src/lib.rs @@ -0,0 +1,18 @@ +#![no_main] + +use latch_n::bindings::componentized::filesystem::{latch as latch0, latch1, latch2}; +use latch_n::bindings::exports::componentized::filesystem::latch::{ + Decision, Guest as Latch, Operation, +}; + +struct LatchN3 {} + +impl Latch for LatchN3 { + #[allow(async_fn_in_trait)] + fn check(operation: Operation<'_>) -> Decision { + let checks = vec![latch0::check, latch1::check, latch2::check]; + latch_n::check(operation, checks) + } +} + +latch_n::export!(LatchN3 with_types_in latch_n::bindings); diff --git a/components/latch-3/Cargo.toml b/components/latch-n4/Cargo.toml similarity index 75% rename from components/latch-3/Cargo.toml rename to components/latch-n4/Cargo.toml index a348e23..456bb99 100644 --- a/components/latch-3/Cargo.toml +++ b/components/latch-n4/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "latch-3" +name = "latch-n4" version = "0.1.0" edition = "2021" license = "Apache-2.0" @@ -8,4 +8,5 @@ license = "Apache-2.0" crate-type = ["cdylib"] [dependencies] +latch-n = { workspace = true } wit-bindgen = { workspace = true } diff --git a/components/latch-4/README.md b/components/latch-n4/README.md similarity index 83% rename from components/latch-4/README.md rename to components/latch-n4/README.md index dd8d6f6..f9586c5 100644 --- a/components/latch-4/README.md +++ b/components/latch-n4/README.md @@ -1,3 +1,3 @@ -# `latch-4` +# `latch-n4` Filesystem latch that aggregates four other filesystem latches. diff --git a/components/latch-n4/src/lib.rs b/components/latch-n4/src/lib.rs new file mode 100644 index 0000000..e733807 --- /dev/null +++ b/components/latch-n4/src/lib.rs @@ -0,0 +1,18 @@ +#![no_main] + +use latch_n::bindings::componentized::filesystem::{latch as latch0, latch1, latch2, latch3}; +use latch_n::bindings::exports::componentized::filesystem::latch::{ + Decision, Guest as Latch, Operation, +}; + +struct LatchN4 {} + +impl Latch for LatchN4 { + #[allow(async_fn_in_trait)] + fn check(operation: Operation<'_>) -> Decision { + let checks = vec![latch0::check, latch1::check, latch2::check, latch3::check]; + latch_n::check(operation, checks) + } +} + +latch_n::export!(LatchN4 with_types_in latch_n::bindings); diff --git a/components/latch-n5/Cargo.toml b/components/latch-n5/Cargo.toml new file mode 100644 index 0000000..0aeb5b3 --- /dev/null +++ b/components/latch-n5/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "latch-n5" +version = "0.1.0" +edition = "2021" +license = "Apache-2.0" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +latch-n = { workspace = true } +wit-bindgen = { workspace = true } diff --git a/components/latch-n5/README.md b/components/latch-n5/README.md new file mode 100644 index 0000000..407bcb2 --- /dev/null +++ b/components/latch-n5/README.md @@ -0,0 +1,3 @@ +# `latch-n5` + +Filesystem latch that aggregates five other filesystem latches. diff --git a/components/latch-n5/src/lib.rs b/components/latch-n5/src/lib.rs new file mode 100644 index 0000000..e76e136 --- /dev/null +++ b/components/latch-n5/src/lib.rs @@ -0,0 +1,26 @@ +#![no_main] + +use latch_n::bindings::componentized::filesystem::{ + latch as latch0, latch1, latch2, latch3, latch4, +}; +use latch_n::bindings::exports::componentized::filesystem::latch::{ + Decision, Guest as Latch, Operation, +}; + +struct LatchN5 {} + +impl Latch for LatchN5 { + #[allow(async_fn_in_trait)] + fn check(operation: Operation<'_>) -> Decision { + let checks = vec![ + latch0::check, + latch1::check, + latch2::check, + latch3::check, + latch4::check, + ]; + latch_n::check(operation, checks) + } +} + +latch_n::export!(LatchN5 with_types_in latch_n::bindings); diff --git a/crates/latch-n/Cargo.toml b/crates/latch-n/Cargo.toml new file mode 100644 index 0000000..89c2b9e --- /dev/null +++ b/crates/latch-n/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "latch-n" +version = "0.1.0" +edition = "2021" +license = "Apache-2.0" + +[dependencies] +wit-bindgen = { workspace = true } diff --git a/components/latch-3/src/lib.rs b/crates/latch-n/src/lib.rs similarity index 91% rename from components/latch-3/src/lib.rs rename to crates/latch-n/src/lib.rs index 7127cf5..34cc1b4 100644 --- a/components/latch-3/src/lib.rs +++ b/crates/latch-n/src/lib.rs @@ -1,7 +1,7 @@ #![no_main] -use crate::{ - componentized::filesystem::{latch, latch1, latch2, latch3}, +use crate::bindings::{ + componentized::filesystem::latch, exports::componentized::filesystem::latch::{ Decision, DescriptorAdviseArgs, DescriptorCreateDirectoryAtArgs, DescriptorLinkAtArgs, DescriptorMetadataHashAtArgs, DescriptorOpenAtArgs, DescriptorOperation, @@ -9,25 +9,23 @@ use crate::{ DescriptorRemoveDirectoryAtArgs, DescriptorRenameAtArgs, DescriptorSetSizeArgs, DescriptorSetTimesArgs, DescriptorSetTimesAtArgs, DescriptorStatAtArgs, DescriptorSymlinkAtArgs, DescriptorUnlinkFileAtArgs, DescriptorWriteArgs, - DescriptorWriteViaStreamArgs, Guest as Latch, Operation, + DescriptorWriteViaStreamArgs, Operation, }, }; -struct AggregateLatch {} - -impl Latch for AggregateLatch { - fn check(operation: Operation) -> Decision { - let operation = operation_map(operation); - let checks = vec![latch1::check, latch2::check, latch3::check]; - for check in checks { - match check(&operation) { - latch::Decision::Abstain => {} - latch::Decision::Allow => return Decision::Allow, - latch::Decision::Deny(error_code) => return Decision::Deny(error_code), - } +pub fn check( + operation: Operation, + checks: Vec) -> latch::Decision>, +) -> Decision { + let operation = operation_map(operation); + for check in checks { + match check(&operation) { + latch::Decision::Abstain => {} + latch::Decision::Allow => return Decision::Allow, + latch::Decision::Deny(error_code) => return Decision::Deny(error_code), } - Decision::Abstain } + Decision::Abstain } fn operation_map(operation: Operation) -> latch::Operation { @@ -264,10 +262,18 @@ fn descriptor_metadata_hash_at_args_map( } } -wit_bindgen::generate!({ - path: "../../wit", - world: "filesystem-latch3", - generate_all -}); +pub mod bindings { + wit_bindgen::generate!({ + path: "../../wit", + world: "filesystem-latch-n", + pub_export_macro: true, + generate_all + }); +} -export!(AggregateLatch); +#[macro_export] +macro_rules! export { + ($($t:tt)*) => { + $crate::bindings::export!($($t)*); + }; +} diff --git a/wit/worlds.wit b/wit/worlds.wit index 1b231f5..c04b7fc 100644 --- a/wit/worlds.wit +++ b/wit/worlds.wit @@ -14,23 +14,12 @@ world filesystem-latch { export latch; } -world filesystem-latch2 { - import latch1; - import latch2; +world filesystem-latch-n { export latch; -} - -world filesystem-latch3 { - import latch1; - import latch2; - import latch3; - export latch; -} - -world filesystem-latch4 { import latch1; import latch2; import latch3; import latch4; - export latch; + import wasi:filesystem/preopens@0.2.6; + import wasi:filesystem/types@0.2.6; } From 6ac8b02f227d966d6af606b1b82cfa47a5ce0022 Mon Sep 17 00:00:00 2001 From: Scott Andrews Date: Mon, 18 May 2026 13:57:56 -0400 Subject: [PATCH 3/4] rebuild components on changes in crates dir Signed-off-by: Scott Andrews --- Cargo.lock | 4 ---- Makefile | 4 ++-- components/latch-n2/Cargo.toml | 1 - components/latch-n3/Cargo.toml | 1 - components/latch-n4/Cargo.toml | 1 - components/latch-n5/Cargo.toml | 1 - 6 files changed, 2 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 88a0af2..ae38b67 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -105,7 +105,6 @@ name = "latch-n2" version = "0.1.0" dependencies = [ "latch-n", - "wit-bindgen", ] [[package]] @@ -113,7 +112,6 @@ name = "latch-n3" version = "0.1.0" dependencies = [ "latch-n", - "wit-bindgen", ] [[package]] @@ -121,7 +119,6 @@ name = "latch-n4" version = "0.1.0" dependencies = [ "latch-n", - "wit-bindgen", ] [[package]] @@ -129,7 +126,6 @@ name = "latch-n5" version = "0.1.0" dependencies = [ "latch-n", - "wit-bindgen", ] [[package]] diff --git a/Makefile b/Makefile index c9eb511..c15b9f3 100644 --- a/Makefile +++ b/Makefile @@ -19,12 +19,12 @@ components: $(foreach component,$(COMPONENTS),lib/$(component).wasm $(foreach co define BUILD_COMPONENT -lib/$1.wasm: Cargo.toml Cargo.lock wit/deps $(shell find components/$1 -type f) +lib/$1.wasm: Cargo.toml Cargo.lock wit/deps $(shell find components/$1 -type f) $(shell find crates -type f) cargo component build -p $1 --target wasm32-unknown-unknown --release cp target/wasm32-unknown-unknown/release/$(subst -,_,$1).wasm 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) +lib/$1.debug.wasm: Cargo.toml Cargo.lock wit/deps $(shell find components/$1 -type f) $(shell find crates -type f) cargo component build -p $1 --target wasm32-wasip2 cp target/wasm32-wasip2/debug/$(subst -,_,$1).wasm lib/$1.debug.wasm cp components/$1/README.md lib/$1.debug.wasm.md diff --git a/components/latch-n2/Cargo.toml b/components/latch-n2/Cargo.toml index ddd1054..4fbab2a 100644 --- a/components/latch-n2/Cargo.toml +++ b/components/latch-n2/Cargo.toml @@ -9,4 +9,3 @@ crate-type = ["cdylib"] [dependencies] latch-n = { workspace = true } -wit-bindgen = { workspace = true } diff --git a/components/latch-n3/Cargo.toml b/components/latch-n3/Cargo.toml index beb0e00..baf0a07 100644 --- a/components/latch-n3/Cargo.toml +++ b/components/latch-n3/Cargo.toml @@ -9,4 +9,3 @@ crate-type = ["cdylib"] [dependencies] latch-n = { workspace = true } -wit-bindgen = { workspace = true } diff --git a/components/latch-n4/Cargo.toml b/components/latch-n4/Cargo.toml index 456bb99..d6bc670 100644 --- a/components/latch-n4/Cargo.toml +++ b/components/latch-n4/Cargo.toml @@ -9,4 +9,3 @@ crate-type = ["cdylib"] [dependencies] latch-n = { workspace = true } -wit-bindgen = { workspace = true } diff --git a/components/latch-n5/Cargo.toml b/components/latch-n5/Cargo.toml index 0aeb5b3..1140d89 100644 --- a/components/latch-n5/Cargo.toml +++ b/components/latch-n5/Cargo.toml @@ -9,4 +9,3 @@ crate-type = ["cdylib"] [dependencies] latch-n = { workspace = true } -wit-bindgen = { workspace = true } From d07f7d098d46485c64abaaec5ab04e72d8584850 Mon Sep 17 00:00:00 2001 From: Scott Andrews Date: Mon, 18 May 2026 15:51:09 -0400 Subject: [PATCH 4/4] Gate check preopens and directory-entry-streams The preopens interface and directory-entry-stream resource are a bit different as we allow the call and check if the returned value should be passed back to the caller. This allows the gate to filter the preopened filesystems, or files in a directory. Signed-off-by: Scott Andrews --- components/gate/src/lib.rs | 167 ++++++++++++++++++--------- components/latch-readonly/src/lib.rs | 2 + crates/latch-n/src/lib.rs | 30 ++++- wit/latch.wit | 12 +- 4 files changed, 154 insertions(+), 57 deletions(-) diff --git a/components/gate/src/lib.rs b/components/gate/src/lib.rs index bc1b48e..aeeec0f 100644 --- a/components/gate/src/lib.rs +++ b/components/gate/src/lib.rs @@ -1,9 +1,13 @@ #![no_main] +use std::fmt::Display; +use std::path::PathBuf; use std::rc::Rc; use crate::componentized::filesystem::latch::Decision::{Abstain, Allow, Deny}; -use crate::componentized::filesystem::latch::{self, check, DescriptorOperation, Operation}; +use crate::componentized::filesystem::latch::{ + self, check, DescriptorOperation, DirectoryEntryStreamOperation, Operation, PreopensOperation, +}; use crate::exports::wasi::filesystem::preopens::Guest as Preopens; use crate::exports::wasi::filesystem::types::{ Advice, Descriptor, DescriptorBorrow, DescriptorFlags, DescriptorStat, DescriptorType, @@ -14,7 +18,6 @@ use crate::wasi::filesystem::preopens; use crate::wasi::filesystem::types; use crate::wasi::logging::logging::{log, Level}; -#[macro_export] macro_rules! warn { ($dst:expr, $($arg:tt)*) => { log(Level::Warn, "componentized-gate", &format!($dst, $($arg)*)); @@ -24,25 +27,44 @@ macro_rules! warn { }; } +macro_rules! trace { + ($dst:expr, $($arg:tt)*) => { + log(Level::Trace, "componentized-gate", &format!($dst, $($arg)*)); + }; + ($dst:expr) => { + log(Level::Trace, "componentized-gate", &format!($dst)); + }; +} + #[derive(Debug, Clone)] -struct FilesystemGate {} +struct GatedFilesystem {} -impl Preopens for FilesystemGate { +impl Preopens for GatedFilesystem { #[doc = " Return the set of preopened directories, and their path."] fn get_directories() -> Vec<(Descriptor, String)> { preopens::get_directories() .into_iter() + .filter(|(fs, path)| { + match check(&Operation::Preopens(PreopensOperation::GetDirectoriesItem((fs, path.clone())))) { + Allow => true, + Deny(code) => { + trace!("Denied REASON={code} OPERATION=wasi:filesystem/preopens#get-directories PATH={path}"); + false + } + Abstain => panic!("missing latch decision"), + } + }) .map(|(fd, path)| { - let fd = Descriptor::new(GateDescriptor::new(fd)); + let fd = Descriptor::new(GatedFileDescriptor::new(fd, PathBuf::from(path.clone()))); (fd, path) }) .collect() } } -impl Types for FilesystemGate { - type Descriptor = GateDescriptor; - type DirectoryEntryStream = GateDirectoryEntryStream; +impl Types for GatedFilesystem { + type Descriptor = GatedFileDescriptor; + type DirectoryEntryStream = GatedDirectoryEntryStream; #[doc = " Attempts to extract a filesystem-related `error-code` from the stream"] #[doc = " `error` provided."] @@ -60,17 +82,27 @@ impl Types for FilesystemGate { } #[derive(Debug, Clone)] -struct GateDescriptor { +struct GatedFileDescriptor { fd: Rc, + path: PathBuf, +} + +impl GatedFileDescriptor { + fn new(fd: types::Descriptor, path: PathBuf) -> Self { + Self { + fd: Rc::new(fd), + path, + } + } } -impl GateDescriptor { - fn new(fd: types::Descriptor) -> Self { - Self { fd: Rc::new(fd) } +impl Display for GatedFileDescriptor { + fn fmt(&self, d: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + d.write_fmt(format_args!("{}", self.path.display())) } } -impl exports::wasi::filesystem::types::GuestDescriptor for GateDescriptor { +impl exports::wasi::filesystem::types::GuestDescriptor for GatedFileDescriptor { #[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."] @@ -86,7 +118,7 @@ impl exports::wasi::filesystem::types::GuestDescriptor for GateDescriptor { ))) { Allow => self.fd.read_via_stream(offset).map_err(error_code_map), Deny(code) => { - warn!("Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.read-via-stream FD={self:?} OFFSET={offset}"); + warn!("Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.read-via-stream FD={self} OFFSET={offset}"); Err(error_code_map(code)) } Abstain => panic!("missing latch decision"), @@ -106,7 +138,7 @@ impl exports::wasi::filesystem::types::GuestDescriptor for GateDescriptor { ))) { Allow => self.fd.write_via_stream(offset).map_err(error_code_map), Deny(code) => { - warn!("Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.write-via-stream FD={self:?} OFFSET={offset}"); + warn!("Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.write-via-stream FD={self} OFFSET={offset}"); Err(error_code_map(code)) } Abstain => panic!("missing latch decision"), @@ -126,7 +158,7 @@ impl exports::wasi::filesystem::types::GuestDescriptor for GateDescriptor { ))) { Allow => self.fd.append_via_stream().map_err(error_code_map), Deny(code) => { - warn!("Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.append-via-stream FD={self:?}"); + warn!("Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.append-via-stream FD={self}"); Err(error_code_map(code)) } Abstain => panic!("missing latch decision"), @@ -152,7 +184,7 @@ impl exports::wasi::filesystem::types::GuestDescriptor for GateDescriptor { .advise(offset, length, advice) .map_err(error_code_map), Deny(code) => { - warn!("Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.advise FD={self:?}"); + warn!("Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.advise FD={self}"); Err(error_code_map(code)) } Abstain => panic!("missing latch decision"), @@ -172,7 +204,7 @@ impl exports::wasi::filesystem::types::GuestDescriptor for GateDescriptor { ))) { Allow => self.fd.sync_data().map_err(error_code_map), Deny(code) => { - warn!("Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.sync-data FD={self:?}"); + warn!("Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.sync-data FD={self}"); Err(error_code_map(code)) } Abstain => panic!("missing latch decision"), @@ -196,7 +228,7 @@ impl exports::wasi::filesystem::types::GuestDescriptor for GateDescriptor { .map(descriptor_flags_map) .map_err(error_code_map), Deny(code) => { - warn!("Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.get-flags FD={self:?}"); + warn!("Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.get-flags FD={self}"); Err(error_code_map(code)) } Abstain => panic!("missing latch decision"), @@ -224,7 +256,7 @@ impl exports::wasi::filesystem::types::GuestDescriptor for GateDescriptor { .map(descriptor_type_map) .map_err(error_code_map), Deny(code) => { - warn!("Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.get-type FD={self:?}"); + warn!("Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.get-type FD={self}"); Err(error_code_map(code)) } Abstain => panic!("missing latch decision"), @@ -242,7 +274,7 @@ impl exports::wasi::filesystem::types::GuestDescriptor for GateDescriptor { ))) { Allow => self.fd.set_size(size).map_err(error_code_map), Deny(code) => { - warn!("Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.set-size FD={self:?} SIZE={size}"); + warn!("Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.set-size FD={self} SIZE={size}"); Err(error_code_map(code)) } Abstain => panic!("missing latch decision"), @@ -274,7 +306,7 @@ impl exports::wasi::filesystem::types::GuestDescriptor for GateDescriptor { .set_times(data_access_timestamp, data_modification_timestamp) .map_err(error_code_map), Deny(code) => { - warn!("Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.set-times FD={self:?} ACCESS-TIME={data_access_timestamp:?} MODIFIED-TIME={data_modification_timestamp:?}"); + warn!("Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.set-times FD={self} ACCESS-TIME={data_access_timestamp:?} MODIFIED-TIME={data_modification_timestamp:?}"); Err(error_code_map(code)) } Abstain => panic!("missing latch decision"), @@ -299,7 +331,7 @@ impl exports::wasi::filesystem::types::GuestDescriptor for GateDescriptor { ))) { Allow => self.fd.read(length, offset).map_err(error_code_map), Deny(code) => { - warn!("Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.read FD={self:?} LENGTH={length} OFFSET={offset}"); + warn!("Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.read FD={self} LENGTH={length} OFFSET={offset}"); Err(error_code_map(code)) } Abstain => panic!("missing latch decision"), @@ -329,7 +361,7 @@ impl exports::wasi::filesystem::types::GuestDescriptor for GateDescriptor { ))) { Allow => self.fd.write(&buffer, offset).map_err(error_code_map), Deny(code) => { - warn!("Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.write FD={self:?} BUFFER-LENGTH={buffer_length} OFFSET={offset}"); + warn!("Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.write FD={self} BUFFER-LENGTH={buffer_length} OFFSET={offset}"); Err(error_code_map(code)) } Abstain => panic!("missing latch decision"), @@ -353,10 +385,10 @@ impl exports::wasi::filesystem::types::GuestDescriptor for GateDescriptor { Allow => self .fd .read_directory() - .map(directory_entry_stream_map) + .map(|des| directory_entry_stream_map(des, self.path.clone())) .map_err(error_code_map), Deny(code) => { - warn!("Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.read-directory FD={self:?}"); + warn!("Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.read-directory FD={self}"); Err(error_code_map(code)) } Abstain => panic!("missing latch decision"), @@ -376,7 +408,7 @@ impl exports::wasi::filesystem::types::GuestDescriptor for GateDescriptor { ))) { Allow => self.fd.sync().map_err(error_code_map), Deny(code) => { - warn!("Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.sync FD={self:?}"); + warn!("Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.sync FD={self}"); Err(error_code_map(code)) } Abstain => panic!("missing latch decision"), @@ -395,7 +427,7 @@ impl exports::wasi::filesystem::types::GuestDescriptor for GateDescriptor { ))) { Allow => self.fd.create_directory_at(&path).map_err(error_code_map), Deny(code) => { - warn!("Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.create-directory-at FD={self:?} PATH={path}"); + warn!("Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.create-directory-at FD={self} PATH={path}"); Err(error_code_map(code)) } Abstain => panic!("missing latch decision"), @@ -422,7 +454,7 @@ impl exports::wasi::filesystem::types::GuestDescriptor for GateDescriptor { .map(descriptor_stat_map) .map_err(error_code_map), Deny(code) => { - warn!("Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.stat FD={self:?}"); + warn!("Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.stat FD={self}"); Err(error_code_map(code)) } Abstain => panic!("missing latch decision"), @@ -452,7 +484,7 @@ impl exports::wasi::filesystem::types::GuestDescriptor for GateDescriptor { .map(descriptor_stat_map) .map_err(error_code_map), Deny(code) => { - warn!("Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.stat-at FD={self:?} PATH={path}"); + warn!("Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.stat-at FD={self} PATH={path}"); Err(error_code_map(code)) } Abstain => panic!("missing latch decision"), @@ -494,7 +526,7 @@ impl exports::wasi::filesystem::types::GuestDescriptor for GateDescriptor { ) .map_err(error_code_map), Deny(code) => { - warn!("Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.set-times-at FD={self:?} PATH={path} ACCESS-TIME={data_access_timestamp:?} MODIFIED-TIME={data_modification_timestamp:?}"); + warn!("Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.set-times-at FD={self} PATH={path} ACCESS-TIME={data_access_timestamp:?} MODIFIED-TIME={data_modification_timestamp:?}"); Err(error_code_map(code)) } Abstain => panic!("missing latch decision"), @@ -532,7 +564,7 @@ impl exports::wasi::filesystem::types::GuestDescriptor for GateDescriptor { .map_err(error_code_map), Deny(code) => { warn!( - "Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.link-at FD={self:?} OLD-PATH={old_path} NEW-PATH={new_path}", + "Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.link-at FD={self} OLD-PATH={old_path} NEW-PATH={new_path}", ); Err(error_code_map(code)) } @@ -579,11 +611,11 @@ impl exports::wasi::filesystem::types::GuestDescriptor for GateDescriptor { ))) { Allow => self .fd - .open_at(path_flags, &path, open_flags, flags) - .map(descriptor_map) + .open_at(path_flags, &path.clone(), open_flags, flags) + .map(|descriptor| descriptor_map(descriptor, self.path.join(path))) .map_err(error_code_map), Deny(code) => { - warn!("Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.open-at FD={self:?} PATH={path}"); + warn!("Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.open-at FD={self} PATH={path}"); Err(error_code_map(code)) } Abstain => panic!("missing latch decision"), @@ -604,7 +636,7 @@ impl exports::wasi::filesystem::types::GuestDescriptor for GateDescriptor { Allow => self.fd.readlink_at(&path).map_err(error_code_map), Deny(code) => { warn!( - "Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.readlink-at FD={self:?} PATH={path}", + "Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.readlink-at FD={self} PATH={path}", ); Err(error_code_map(code)) } @@ -626,7 +658,7 @@ impl exports::wasi::filesystem::types::GuestDescriptor for GateDescriptor { ))) { Allow => self.fd.remove_directory_at(&path).map_err(error_code_map), Deny(code) => { - warn!("Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.remove-directory-at FD={self:?} PATH={path}"); + warn!("Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.remove-directory-at FD={self} PATH={path}"); Err(error_code_map(code)) } Abstain => panic!("missing latch decision"), @@ -658,7 +690,7 @@ impl exports::wasi::filesystem::types::GuestDescriptor for GateDescriptor { } Deny(code) => { warn!( - "Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.rename-at FD={self:?} OLD-PATH={old_path} NEW-PATH={new_path}", + "Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.rename-at FD={self} OLD-PATH={old_path} NEW-PATH={new_path}", ); Err(error_code_map(code)) } @@ -686,7 +718,7 @@ impl exports::wasi::filesystem::types::GuestDescriptor for GateDescriptor { .map_err(error_code_map), Deny(code) => { warn!( - "Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.symlink-at FD={self:?} OLD-PATH={old_path} NEW-PATH={new_path}", + "Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.symlink-at FD={self} OLD-PATH={old_path} NEW-PATH={new_path}", ); Err(error_code_map(code)) } @@ -708,7 +740,7 @@ impl exports::wasi::filesystem::types::GuestDescriptor for GateDescriptor { Allow => self.fd.unlink_file_at(&path).map_err(error_code_map), Deny(code) => { warn!( - "Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.unlink-file-at FD={self:?} PATH={path}", + "Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.unlink-file-at FD={self} PATH={path}", ); Err(error_code_map(code)) } @@ -757,7 +789,7 @@ impl exports::wasi::filesystem::types::GuestDescriptor for GateDescriptor { .map(metadata_hash_value_map) .map_err(error_code_map), Deny(code) => { - warn!("Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.metadata-hash FD={self:?}"); + warn!("Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.metadata-hash FD={self}"); Err(error_code_map(code)) } Abstain => panic!("missing latch decision"), @@ -787,7 +819,7 @@ impl exports::wasi::filesystem::types::GuestDescriptor for GateDescriptor { .map(metadata_hash_value_map) .map_err(error_code_map), Deny(code) => { - warn!("Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.metadata-hash-at FD={self:?} PATH={path}"); + warn!("Denied REASON={code} OPERATION=wasi:filesystem/types#descriptor.metadata-hash-at FD={self} PATH={path}"); Err(error_code_map(code)) } Abstain => panic!("missing latch decision"), @@ -796,23 +828,47 @@ impl exports::wasi::filesystem::types::GuestDescriptor for GateDescriptor { } #[derive(Debug, Clone)] -struct GateDirectoryEntryStream { +struct GatedDirectoryEntryStream { des: Rc, + path: PathBuf, } -impl GateDirectoryEntryStream { - fn new(des: types::DirectoryEntryStream) -> Self { - Self { des: Rc::new(des) } +impl GatedDirectoryEntryStream { + fn new(des: types::DirectoryEntryStream, path: PathBuf) -> Self { + Self { + des: Rc::new(des), + path, + } } } -impl exports::wasi::filesystem::types::GuestDirectoryEntryStream for GateDirectoryEntryStream { +impl Display for GatedDirectoryEntryStream { + fn fmt(&self, d: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + d.write_fmt(format_args!("{}", self.path.display())) + } +} + +impl exports::wasi::filesystem::types::GuestDirectoryEntryStream for GatedDirectoryEntryStream { #[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) + match self.des.read_directory_entry() { + Ok(Some(de)) => { + match check(&Operation::DirectoryEntryStream(( + &self.des, + DirectoryEntryStreamOperation::ReadDirectoryEntry(de.clone()), + ))) { + Allow => Ok(Some(directory_entry_map(de))), + Deny(code) => { + trace!("Denied REASON={code} OPERATION=wasi:filesystem/types#directory-entry-stream.read-directory-entry STREAM={} ENTRY={}", self, de.name); + // continue reading the next entry transparently + self.read_directory_entry() + } + Abstain => panic!("missing latch decision"), + } + } + Ok(None) => Ok(None), + Err(code) => Err(error_code_map(code)), + } } } @@ -827,8 +883,8 @@ fn advice_map_in(advice: Advice) -> types::Advice { } } -fn descriptor_map(descriptor: types::Descriptor) -> Descriptor { - Descriptor::new(GateDescriptor::new(descriptor)) +fn descriptor_map(descriptor: types::Descriptor, path: PathBuf) -> Descriptor { + Descriptor::new(GatedFileDescriptor::new(descriptor, path)) } fn descriptor_flags_map(descriptor_flags: types::DescriptorFlags) -> DescriptorFlags { @@ -868,8 +924,9 @@ fn directory_entry_map(directory_entry: types::DirectoryEntry) -> DirectoryEntry fn directory_entry_stream_map( directory_entry_stream: types::DirectoryEntryStream, + path: PathBuf, ) -> DirectoryEntryStream { - DirectoryEntryStream::new(GateDirectoryEntryStream::new(directory_entry_stream)) + DirectoryEntryStream::new(GatedDirectoryEntryStream::new(directory_entry_stream, path)) } fn error_code_map(error_code: types::ErrorCode) -> ErrorCode { @@ -935,4 +992,4 @@ wit_bindgen::generate!({ generate_all }); -export!(FilesystemGate); +export!(GatedFilesystem); diff --git a/components/latch-readonly/src/lib.rs b/components/latch-readonly/src/lib.rs index 327e264..29e1d94 100644 --- a/components/latch-readonly/src/lib.rs +++ b/components/latch-readonly/src/lib.rs @@ -13,6 +13,7 @@ struct ReadOnlyLatch {} impl Latch for ReadOnlyLatch { fn check(operation: Operation) -> Decision { match operation { + Operation::Preopens(_) => Abstain, Operation::Descriptor((_, descriptor_operation)) => match descriptor_operation { DescriptorOperation::ReadViaStream(_) => Abstain, DescriptorOperation::WriteViaStream(_) => Deny(ReadOnly), @@ -58,6 +59,7 @@ impl Latch for ReadOnlyLatch { DescriptorOperation::MetadataHash => Abstain, DescriptorOperation::MetadataHashAt(_) => Abstain, }, + Operation::DirectoryEntryStream(_) => Abstain, } } } diff --git a/crates/latch-n/src/lib.rs b/crates/latch-n/src/lib.rs index 34cc1b4..234b3e3 100644 --- a/crates/latch-n/src/lib.rs +++ b/crates/latch-n/src/lib.rs @@ -9,7 +9,7 @@ use crate::bindings::{ DescriptorRemoveDirectoryAtArgs, DescriptorRenameAtArgs, DescriptorSetSizeArgs, DescriptorSetTimesArgs, DescriptorSetTimesAtArgs, DescriptorStatAtArgs, DescriptorSymlinkAtArgs, DescriptorUnlinkFileAtArgs, DescriptorWriteArgs, - DescriptorWriteViaStreamArgs, Operation, + DescriptorWriteViaStreamArgs, DirectoryEntryStreamOperation, Operation, PreopensOperation, }, }; @@ -30,9 +30,27 @@ pub fn check( fn operation_map(operation: Operation) -> latch::Operation { match operation { + Operation::Preopens(preopens_operation) => { + latch::Operation::Preopens(preopens_operation_map(preopens_operation)) + } Operation::Descriptor((descriptor, descriptor_operation)) => latch::Operation::Descriptor( (descriptor, descriptor_operation_map(descriptor_operation)), ), + Operation::DirectoryEntryStream(( + directory_entry_stream, + directory_entry_stream_operation, + )) => latch::Operation::DirectoryEntryStream(( + directory_entry_stream, + directory_entry_stream_operation_map(directory_entry_stream_operation), + )), + } +} + +fn preopens_operation_map(preopens_operation: PreopensOperation) -> latch::PreopensOperation { + match preopens_operation { + PreopensOperation::GetDirectoriesItem((fd, path)) => { + latch::PreopensOperation::GetDirectoriesItem((fd, path)) + } } } @@ -262,6 +280,16 @@ fn descriptor_metadata_hash_at_args_map( } } +fn directory_entry_stream_operation_map( + directory_entry_stream_operation: DirectoryEntryStreamOperation, +) -> latch::DirectoryEntryStreamOperation { + match directory_entry_stream_operation { + DirectoryEntryStreamOperation::ReadDirectoryEntry(directory_entry) => { + latch::DirectoryEntryStreamOperation::ReadDirectoryEntry(directory_entry) + } + } +} + pub mod bindings { wit_bindgen::generate!({ path: "../../wit", diff --git a/wit/latch.wit b/wit/latch.wit index ac2c49f..12a57b8 100644 --- a/wit/latch.wit +++ b/wit/latch.wit @@ -1,6 +1,6 @@ interface latch { - use wasi:filesystem/types@0.2.6.{advice, descriptor, descriptor-flags, error-code, filesize, open-flags, new-timestamp, path-flags}; + use wasi:filesystem/types@0.2.6.{advice, descriptor, descriptor-flags, directory-entry, directory-entry-stream, error-code, filesize, open-flags, new-timestamp, path-flags}; record descriptor-get-directory-args { path: string, @@ -98,6 +98,10 @@ interface latch { path: string, } + variant preopens-operation { + get-directories-item(tuple, string>), + } + variant descriptor-operation { read-via-stream(descriptor-read-via-stream-args), write-via-stream(descriptor-write-via-stream-args), @@ -127,8 +131,14 @@ interface latch { metadata-hash-at(descriptor-metadata-hash-at-args), } + variant directory-entry-stream-operation { + read-directory-entry(directory-entry), + } + variant operation { + preopens(preopens-operation), descriptor(tuple, descriptor-operation>), + directory-entry-stream(tuple, directory-entry-stream-operation>), } variant decision {