Skip to main content

wasmtime_wasi/p3/filesystem/
mod.rs

1mod host;
2
3use crate::TrappableError;
4use crate::filesystem::{
5    WasiFilesystem, WasiFilesystemNamed, WasiFilesystemNamedView, WasiFilesystemView,
6};
7use crate::p3::Interface;
8use crate::p3::bindings::filesystem::{preopens, types};
9use crate::{NamedId, WasiCtxNamedView};
10use wasmtime::component::{Component, Linker};
11
12pub type FilesystemResult<T> = Result<T, FilesystemError>;
13pub type FilesystemError = TrappableError<types::ErrorCode>;
14
15/// Add all WASI interfaces from this module into the `linker` provided.
16///
17/// This function will add all interfaces implemented by this module to the
18/// [`Linker`], which corresponds to the `wasi:filesystem/imports` world supported by
19/// this module.
20///
21/// This is low-level API for advanced use cases,
22/// [`wasmtime_wasi::p3::add_to_linker`](crate::p3::add_to_linker) can be used instead
23/// to add *all* wasip3 interfaces (including the ones from this module) to the `linker`.
24///
25/// # Example
26///
27/// ```
28/// use wasmtime::{Engine, Result, Store, Config};
29/// use wasmtime::component::{Linker, ResourceTable};
30/// use wasmtime_wasi::filesystem::{WasiFilesystemCtx, WasiFilesystemCtxView, WasiFilesystemView};
31///
32/// fn main() -> Result<()> {
33///     let mut config = Config::new();
34///     config.wasm_component_model_async(true);
35///     let engine = Engine::new(&config)?;
36///
37///     let mut linker = Linker::<MyState>::new(&engine);
38///     wasmtime_wasi::p3::filesystem::add_to_linker(&mut linker)?;
39///     // ... add any further functionality to `linker` if desired ...
40///
41///     let mut store = Store::new(
42///         &engine,
43///         MyState::default(),
44///     );
45///
46///     // ... use `linker` to instantiate within `store` ...
47///
48///     Ok(())
49/// }
50///
51/// #[derive(Default)]
52/// struct MyState {
53///     filesystem: WasiFilesystemCtx,
54///     table: ResourceTable,
55/// }
56///
57/// impl WasiFilesystemView for MyState {
58///     fn filesystem(&mut self) -> WasiFilesystemCtxView<'_> {
59///         WasiFilesystemCtxView {
60///             ctx: &mut self.filesystem,
61///             table: &mut self.table,
62///         }
63///     }
64/// }
65/// ```
66pub fn add_to_linker<T>(linker: &mut Linker<T>) -> wasmtime::Result<()>
67where
68    T: WasiFilesystemView + 'static,
69{
70    types::add_to_linker::<_, WasiFilesystem>(linker, T::filesystem)?;
71    preopens::add_to_linker::<_, WasiFilesystem>(linker, T::filesystem)?;
72    Ok(())
73}
74
75/// Convenience function to add `wasi:filesystem` interfaces into `linker` for any
76/// named imports of `wasi:filesystem` interfaces.
77///
78/// This function is similar to [`add_to_linker`] except that it's specifically
79/// designed to work with named imports of `wasi:filesystem` interfaces that
80/// components may have. This requires a [`Component`] parameter to be passed in
81/// when populating the [`Linker`] provided to see what the [`Component`]
82/// actually imports.
83///
84/// Like [`add_to_linker`] this is a bit low level and you may want to possibly
85/// invoke [`wasmtime_wasi::p3::add_named_to_linker`] instead. Alternatively if
86/// this isn't low level enough you can additionally invoke bindgen-generated
87/// `add_to_linker` functions directly from within the
88/// [`named_imports::wasi::filesystem`] module.
89///
90/// [`wasmtime_wasi::p3::add_named_to_linker`]: crate::p3::add_named_to_linker
91/// [`named_imports::wasi::filesystem`]: crate::p3::bindings::named_imports::wasi::filesystem
92///
93/// The `lookup` function provided here is invoked for every named import found
94/// for a particular interface. The [`Interface`] given is what's being bound,
95/// and the `&str` argument is the name that the component imports it as. The
96/// embedder can then decide how it would like to allocate a [`NamedId`] for
97/// this import. If `Ok` is returned then the linker is populated with this
98/// name, and imported functions will pass the [`NamedId`] later to the
99/// implementation of [`WasiFilesystemNamedView`] on `T` when invoked. If `Err` is
100/// returned then the error will cause this entire function to fail and this
101/// function call will return the same error.
102///
103/// # Example
104///
105/// ```
106/// use std::collections::HashMap;
107/// use wasmtime::component::{Component, Linker, ResourceTable};
108/// use wasmtime::{Engine, Result, Store, Config};
109/// use wasmtime_wasi::NamedId;
110/// use wasmtime_wasi::filesystem::{WasiFilesystemCtx, WasiFilesystemCtxView, WasiFilesystemNamedView};
111///
112/// fn main() -> Result<()> {
113///     let engine = Engine::default();
114///     let component = Component::new(&engine, "(component)")?;
115///
116///     let mut linker = Linker::<MyState>::new(&engine);
117///
118///     // ... add default functionality to `linker` as needed ...
119///
120///     // and then additionally fill in any specific named imports `component`
121///     // might have for `wasi:filesystem` interfaces.
122///     let mut name_map = HashMap::new();
123///     wasmtime_wasi::p3::filesystem::add_named_to_linker(&mut linker, &component, |_i, name| {
124///         let len = name_map.len();
125///         Ok(NamedId(*name_map.entry(name.to_string()).or_insert(len)))
126///     })?;
127///
128///     // Here a `WasiFilesystemCtx` is allocated per-named-import and will then be
129///     // referred to internally by the [`NamedId`] allocated above. You could
130///     // also use `name_map` to configure each context differently.
131///     let mut my_state = MyState::default();
132///     for _ in 0..name_map.len() {
133///         my_state.contexts.push(WasiFilesystemCtx::default());
134///     }
135///     let mut store = Store::new(&engine, my_state);
136///     let instance = linker.instantiate(&mut store, &component)?;
137///
138///     // ... work with `instance` ...
139///
140///     Ok(())
141/// }
142///
143/// #[derive(Default)]
144/// struct MyState {
145///     table: ResourceTable,
146///     contexts: Vec<WasiFilesystemCtx>,
147/// }
148///
149/// impl WasiFilesystemNamedView for MyState {
150///     fn filesystem(&mut self, id: NamedId) -> WasiFilesystemCtxView<'_> {
151///         WasiFilesystemCtxView {
152///             ctx: &mut self.contexts[id.0],
153///             table: &mut self.table,
154///         }
155///     }
156/// }
157/// ```
158pub fn add_named_to_linker<T>(
159    linker: &mut Linker<T>,
160    component: &Component,
161    mut lookup: impl FnMut(Interface, &str) -> wasmtime::Result<NamedId>,
162) -> wasmtime::Result<()>
163where
164    T: WasiFilesystemNamedView + 'static,
165{
166    use crate::p3::bindings::named_imports::wasi::filesystem::{preopens, types};
167    types::add_to_linker::<_, WasiFilesystemNamed<T>>(
168        linker,
169        component,
170        |name| lookup(Interface::FilesystemTypes, name),
171        |x| WasiCtxNamedView(x),
172    )?;
173    preopens::add_to_linker::<_, WasiFilesystemNamed<T>>(
174        linker,
175        component,
176        |name| lookup(Interface::FilesystemPreopens, name),
177        |x| WasiCtxNamedView(x),
178    )?;
179    Ok(())
180}
181
182impl<'a> From<&'a std::io::Error> for types::ErrorCode {
183    fn from(err: &'a std::io::Error) -> Self {
184        crate::filesystem::ErrorCode::from(err).into()
185    }
186}
187
188impl From<std::io::Error> for types::ErrorCode {
189    fn from(err: std::io::Error) -> Self {
190        Self::from(&err)
191    }
192}
193
194impl From<std::io::Error> for FilesystemError {
195    fn from(error: std::io::Error) -> Self {
196        types::ErrorCode::from(error).into()
197    }
198}
199
200impl From<crate::filesystem::ErrorCode> for types::ErrorCode {
201    fn from(error: crate::filesystem::ErrorCode) -> Self {
202        match error {
203            crate::filesystem::ErrorCode::Access => Self::Access,
204            crate::filesystem::ErrorCode::Already => Self::Already,
205            crate::filesystem::ErrorCode::BadDescriptor => Self::BadDescriptor,
206            crate::filesystem::ErrorCode::Busy => Self::Busy,
207            crate::filesystem::ErrorCode::Exist => Self::Exist,
208            crate::filesystem::ErrorCode::FileTooLarge => Self::FileTooLarge,
209            crate::filesystem::ErrorCode::IllegalByteSequence => Self::IllegalByteSequence,
210            crate::filesystem::ErrorCode::InProgress => Self::InProgress,
211            crate::filesystem::ErrorCode::Interrupted => Self::Interrupted,
212            crate::filesystem::ErrorCode::Invalid => Self::Invalid,
213            crate::filesystem::ErrorCode::Io => Self::Io,
214            crate::filesystem::ErrorCode::IsDirectory => Self::IsDirectory,
215            crate::filesystem::ErrorCode::Loop => Self::Loop,
216            crate::filesystem::ErrorCode::TooManyLinks => Self::TooManyLinks,
217            crate::filesystem::ErrorCode::NameTooLong => Self::NameTooLong,
218            crate::filesystem::ErrorCode::NoEntry => Self::NoEntry,
219            crate::filesystem::ErrorCode::InsufficientMemory => Self::InsufficientMemory,
220            crate::filesystem::ErrorCode::InsufficientSpace => Self::InsufficientSpace,
221            crate::filesystem::ErrorCode::NotDirectory => Self::NotDirectory,
222            crate::filesystem::ErrorCode::NotEmpty => Self::NotEmpty,
223            crate::filesystem::ErrorCode::Unsupported => Self::Unsupported,
224            crate::filesystem::ErrorCode::Overflow => Self::Overflow,
225            crate::filesystem::ErrorCode::NotPermitted => Self::NotPermitted,
226            crate::filesystem::ErrorCode::Pipe => Self::Pipe,
227            crate::filesystem::ErrorCode::InvalidSeek => Self::InvalidSeek,
228        }
229    }
230}
231
232impl From<crate::filesystem::ErrorCode> for FilesystemError {
233    fn from(error: crate::filesystem::ErrorCode) -> Self {
234        types::ErrorCode::from(error).into()
235    }
236}
237
238impl From<wasmtime::component::ResourceTableError> for FilesystemError {
239    fn from(error: wasmtime::component::ResourceTableError) -> Self {
240        Self::trap(error)
241    }
242}
243
244impl From<types::Advice> for crate::filesystem::Advice {
245    fn from(advice: types::Advice) -> Self {
246        match advice {
247            types::Advice::Normal => Self::Normal,
248            types::Advice::Sequential => Self::Sequential,
249            types::Advice::Random => Self::Random,
250            types::Advice::WillNeed => Self::WillNeed,
251            types::Advice::DontNeed => Self::DontNeed,
252            types::Advice::NoReuse => Self::NoReuse,
253        }
254    }
255}
256
257impl From<types::OpenFlags> for crate::filesystem::OpenFlags {
258    fn from(flags: types::OpenFlags) -> Self {
259        let mut out = Self::empty();
260        if flags.contains(types::OpenFlags::CREATE) {
261            out |= Self::CREATE;
262        }
263        if flags.contains(types::OpenFlags::DIRECTORY) {
264            out |= Self::DIRECTORY;
265        }
266        if flags.contains(types::OpenFlags::EXCLUSIVE) {
267            out |= Self::EXCLUSIVE;
268        }
269        if flags.contains(types::OpenFlags::TRUNCATE) {
270            out |= Self::TRUNCATE;
271        }
272        out
273    }
274}
275
276impl From<types::PathFlags> for crate::filesystem::PathFlags {
277    fn from(flags: types::PathFlags) -> Self {
278        let mut out = Self::empty();
279        if flags.contains(types::PathFlags::SYMLINK_FOLLOW) {
280            out |= Self::SYMLINK_FOLLOW;
281        }
282        out
283    }
284}
285
286impl From<crate::filesystem::DescriptorFlags> for types::DescriptorFlags {
287    fn from(flags: crate::filesystem::DescriptorFlags) -> Self {
288        let mut out = Self::empty();
289        if flags.contains(crate::filesystem::DescriptorFlags::READ) {
290            out |= Self::READ;
291        }
292        if flags.contains(crate::filesystem::DescriptorFlags::WRITE) {
293            out |= Self::WRITE;
294        }
295        if flags.contains(crate::filesystem::DescriptorFlags::FILE_INTEGRITY_SYNC) {
296            out |= Self::FILE_INTEGRITY_SYNC;
297        }
298        if flags.contains(crate::filesystem::DescriptorFlags::DATA_INTEGRITY_SYNC) {
299            out |= Self::DATA_INTEGRITY_SYNC;
300        }
301        if flags.contains(crate::filesystem::DescriptorFlags::REQUESTED_WRITE_SYNC) {
302            out |= Self::REQUESTED_WRITE_SYNC;
303        }
304        if flags.contains(crate::filesystem::DescriptorFlags::MUTATE_DIRECTORY) {
305            out |= Self::MUTATE_DIRECTORY;
306        }
307        out
308    }
309}
310
311impl From<types::DescriptorFlags> for crate::filesystem::DescriptorFlags {
312    fn from(flags: types::DescriptorFlags) -> Self {
313        let mut out = Self::empty();
314        if flags.contains(types::DescriptorFlags::READ) {
315            out |= Self::READ;
316        }
317        if flags.contains(types::DescriptorFlags::WRITE) {
318            out |= Self::WRITE;
319        }
320        if flags.contains(types::DescriptorFlags::FILE_INTEGRITY_SYNC) {
321            out |= Self::FILE_INTEGRITY_SYNC;
322        }
323        if flags.contains(types::DescriptorFlags::DATA_INTEGRITY_SYNC) {
324            out |= Self::DATA_INTEGRITY_SYNC;
325        }
326        if flags.contains(types::DescriptorFlags::REQUESTED_WRITE_SYNC) {
327            out |= Self::REQUESTED_WRITE_SYNC;
328        }
329        if flags.contains(types::DescriptorFlags::MUTATE_DIRECTORY) {
330            out |= Self::MUTATE_DIRECTORY;
331        }
332        out
333    }
334}
335
336impl From<crate::filesystem::MetadataHashValue> for types::MetadataHashValue {
337    fn from(
338        crate::filesystem::MetadataHashValue { lower, upper }: crate::filesystem::MetadataHashValue,
339    ) -> Self {
340        Self { lower, upper }
341    }
342}
343
344impl From<crate::filesystem::DescriptorStat> for types::DescriptorStat {
345    fn from(
346        crate::filesystem::DescriptorStat {
347            type_,
348            link_count,
349            size,
350            data_access_timestamp,
351            data_modification_timestamp,
352            status_change_timestamp,
353        }: crate::filesystem::DescriptorStat,
354    ) -> Self {
355        Self {
356            type_: type_.into(),
357            link_count,
358            size,
359            data_access_timestamp: data_access_timestamp.map(Into::into),
360            data_modification_timestamp: data_modification_timestamp.map(Into::into),
361            status_change_timestamp: status_change_timestamp.map(Into::into),
362        }
363    }
364}
365
366impl From<crate::filesystem::DescriptorType> for types::DescriptorType {
367    fn from(ty: crate::filesystem::DescriptorType) -> Self {
368        match ty {
369            crate::filesystem::DescriptorType::Unknown => Self::Other(None),
370            crate::filesystem::DescriptorType::BlockDevice => Self::BlockDevice,
371            crate::filesystem::DescriptorType::CharacterDevice => Self::CharacterDevice,
372            crate::filesystem::DescriptorType::Directory => Self::Directory,
373            crate::filesystem::DescriptorType::SymbolicLink => Self::SymbolicLink,
374            crate::filesystem::DescriptorType::RegularFile => Self::RegularFile,
375        }
376    }
377}
378
379impl From<crate::filesystem::primitives::FileType> for types::DescriptorType {
380    fn from(ft: crate::filesystem::primitives::FileType) -> Self {
381        crate::filesystem::DescriptorType::from(ft).into()
382    }
383}