wasmer_wasix/utils/
mod.rs

1mod dummy_waker;
2mod owned_mutex_guard;
3pub mod store;
4mod thread_parker;
5
6#[cfg(feature = "js")]
7pub(crate) mod web;
8
9pub use self::{dummy_waker::WasiDummyWaker, thread_parker::WasiParkingLot};
10
11pub(crate) use owned_mutex_guard::{
12    OwnedRwLockReadGuard, OwnedRwLockWriteGuard, read_owned, write_owned,
13};
14
15use std::collections::BTreeSet;
16
17use wasmer::Module;
18use wasmer_wasix_types::wasi::Errno;
19
20/// Check if a provided module is compiled for some version of WASI.
21/// Use [`get_wasi_version`] to find out which version of WASI the module is.
22pub fn is_wasi_module(module: &Module) -> bool {
23    get_wasi_version(module, false).is_some()
24}
25
26/// Returns if the module is WASIX or not
27pub fn is_wasix_module(module: &Module) -> bool {
28    match get_wasi_versions(module, false).ok_or(false) {
29        Ok(wasi_versions) => {
30            wasi_versions.contains(&WasiVersion::Wasix32v1)
31                || wasi_versions.contains(&WasiVersion::Wasix64v1)
32        }
33        Err(_) => false,
34    }
35}
36
37pub fn map_io_err(err: std::io::Error) -> Errno {
38    From::<std::io::Error>::from(err)
39}
40
41#[cfg(feature = "journal")]
42pub fn map_snapshot_err(err: anyhow::Error) -> Errno {
43    tracing::warn!("unknown snapshot error: {}", err);
44    Errno::Unknown
45}
46
47/// The version of WASI. This is determined by the imports namespace
48/// string.
49#[derive(Debug, Clone, Copy, Eq)]
50pub enum WasiVersion {
51    /// `wasi_unstable`.
52    Snapshot0,
53
54    /// `wasi_snapshot_preview1`.
55    Snapshot1,
56
57    /// `wasix_32v1`.
58    Wasix32v1,
59
60    /// `wasix_64v1`.
61    Wasix64v1,
62
63    /// Latest version.
64    ///
65    /// It's a “floating” version, i.e. it's an alias to the latest
66    /// version (for the moment, `Snapshot1`). Using this version is a
67    /// way to ensure that modules will run only if they come with the
68    /// latest WASI version (in case of security issues for instance),
69    /// by just updating the runtime.
70    ///
71    /// Note that this version is never returned by an API. It is
72    /// provided only by the user.
73    Latest,
74}
75
76impl WasiVersion {
77    /// Get the version as its namespace str as it appears in Wasm modules.
78    pub const fn get_namespace_str(&self) -> &'static str {
79        match *self {
80            WasiVersion::Snapshot0 => SNAPSHOT0_NAMESPACE,
81            WasiVersion::Snapshot1 => SNAPSHOT1_NAMESPACE,
82            WasiVersion::Wasix32v1 => WASIX_32V1_NAMESPACE,
83            WasiVersion::Wasix64v1 => WASIX_64V1_NAMESPACE,
84            WasiVersion::Latest => SNAPSHOT1_NAMESPACE,
85        }
86    }
87}
88
89impl PartialEq<WasiVersion> for WasiVersion {
90    fn eq(&self, other: &Self) -> bool {
91        matches!(
92            (*self, *other),
93            (Self::Snapshot1, Self::Latest)
94                | (Self::Latest, Self::Snapshot1)
95                | (Self::Latest, Self::Latest)
96                | (Self::Snapshot0, Self::Snapshot0)
97                | (Self::Snapshot1, Self::Snapshot1)
98                | (Self::Wasix32v1, Self::Wasix32v1)
99                | (Self::Wasix64v1, Self::Wasix64v1)
100        )
101    }
102}
103
104impl PartialOrd for WasiVersion {
105    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
106        Some(self.cmp(other))
107    }
108}
109
110impl Ord for WasiVersion {
111    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
112        if self == other {
113            return std::cmp::Ordering::Equal;
114        }
115        match (*self, *other) {
116            (Self::Snapshot1, Self::Snapshot0) => std::cmp::Ordering::Greater,
117            (Self::Wasix32v1, Self::Snapshot1) | (Self::Wasix32v1, Self::Snapshot0) => {
118                std::cmp::Ordering::Greater
119            }
120            (Self::Wasix64v1, Self::Wasix32v1)
121            | (Self::Wasix64v1, Self::Snapshot1)
122            | (Self::Wasix64v1, Self::Snapshot0) => std::cmp::Ordering::Greater,
123            (Self::Latest, Self::Wasix64v1)
124            | (Self::Latest, Self::Wasix32v1)
125            | (Self::Latest, Self::Snapshot1)
126            | (Self::Latest, Self::Snapshot0) => std::cmp::Ordering::Greater,
127            // they are not equal and not greater so they must be less
128            (_, _) => std::cmp::Ordering::Less,
129        }
130    }
131}
132
133/// Namespace for the `Snapshot0` version.
134const SNAPSHOT0_NAMESPACE: &str = "wasi_unstable";
135
136/// Namespace for the `Snapshot1` version.
137const SNAPSHOT1_NAMESPACE: &str = "wasi_snapshot_preview1";
138
139/// Namespace for the `wasix` version.
140const WASIX_32V1_NAMESPACE: &str = "wasix_32v1";
141
142/// Namespace for the `wasix` version.
143const WASIX_64V1_NAMESPACE: &str = "wasix_64v1";
144
145/// Namespace for the `wasix` version.
146const WASIX_HTTP_V1_NAMESPACE: &str = "wasix_http_client_v1";
147
148/// Detect the version of WASI being used based on the import
149/// namespaces.
150///
151/// A strict detection expects that all imports live in a single WASI
152/// namespace. A non-strict detection expects that at least one WASI
153/// namespace exists to detect the version. Note that the strict
154/// detection is faster than the non-strict one.
155pub fn get_wasi_version(module: &Module, strict: bool) -> Option<WasiVersion> {
156    let mut imports = module.imports().functions().map(|f| f.module().to_owned());
157
158    if strict {
159        let first_module = imports.next()?;
160        if imports.all(|module| module == first_module) {
161            match first_module.as_str() {
162                SNAPSHOT0_NAMESPACE => Some(WasiVersion::Snapshot0),
163                SNAPSHOT1_NAMESPACE => Some(WasiVersion::Snapshot1),
164                WASIX_32V1_NAMESPACE => Some(WasiVersion::Wasix32v1),
165                WASIX_64V1_NAMESPACE => Some(WasiVersion::Wasix64v1),
166                _ => None,
167            }
168        } else {
169            None
170        }
171    } else {
172        // Check that at least a WASI namespace exists, and use the
173        // first one in the list to detect the WASI version.
174        imports.find_map(|module| match module.as_str() {
175            SNAPSHOT0_NAMESPACE => Some(WasiVersion::Snapshot0),
176            SNAPSHOT1_NAMESPACE => Some(WasiVersion::Snapshot1),
177            WASIX_32V1_NAMESPACE => Some(WasiVersion::Wasix32v1),
178            WASIX_64V1_NAMESPACE => Some(WasiVersion::Wasix64v1),
179            _ => None,
180        })
181    }
182}
183
184/// Like [`get_wasi_version`] but detects multiple WASI versions in a single module.
185/// Thus `strict` behaves differently in this function as multiple versions are
186/// always supported. `strict` indicates whether non-WASI imports should trigger a
187/// failure or be ignored.
188pub fn get_wasi_versions(module: &Module, strict: bool) -> Option<BTreeSet<WasiVersion>> {
189    let mut out = BTreeSet::new();
190    let imports = module.imports().functions().map(|f| f.module().to_owned());
191
192    let mut non_wasi_seen = false;
193    for ns in imports {
194        match ns.as_str() {
195            SNAPSHOT0_NAMESPACE => {
196                out.insert(WasiVersion::Snapshot0);
197            }
198            SNAPSHOT1_NAMESPACE => {
199                out.insert(WasiVersion::Snapshot1);
200            }
201            WASIX_32V1_NAMESPACE => {
202                out.insert(WasiVersion::Wasix32v1);
203            }
204            WASIX_64V1_NAMESPACE => {
205                out.insert(WasiVersion::Wasix64v1);
206            }
207            WASIX_HTTP_V1_NAMESPACE => {
208                out.insert(WasiVersion::Wasix64v1);
209            }
210            _ => {
211                non_wasi_seen = true;
212            }
213        }
214    }
215    if strict && non_wasi_seen {
216        None
217    } else {
218        Some(out)
219    }
220}
221
222#[cfg(test)]
223mod test {
224    use super::*;
225
226    #[test]
227    fn wasi_version_equality() {
228        assert_eq!(WasiVersion::Snapshot0, WasiVersion::Snapshot0);
229        assert_eq!(WasiVersion::Wasix64v1, WasiVersion::Wasix64v1);
230        assert_eq!(WasiVersion::Wasix32v1, WasiVersion::Wasix32v1);
231        assert_eq!(WasiVersion::Snapshot1, WasiVersion::Snapshot1);
232        assert_eq!(WasiVersion::Snapshot1, WasiVersion::Latest);
233        assert_eq!(WasiVersion::Latest, WasiVersion::Snapshot1);
234        assert_eq!(WasiVersion::Latest, WasiVersion::Latest);
235        assert!(WasiVersion::Wasix32v1 != WasiVersion::Wasix64v1);
236        assert!(WasiVersion::Wasix64v1 != WasiVersion::Wasix32v1);
237        assert!(WasiVersion::Snapshot1 != WasiVersion::Wasix64v1);
238        assert!(WasiVersion::Wasix64v1 != WasiVersion::Snapshot1);
239        assert!(WasiVersion::Snapshot1 != WasiVersion::Wasix32v1);
240        assert!(WasiVersion::Wasix32v1 != WasiVersion::Snapshot1);
241        assert!(WasiVersion::Snapshot0 != WasiVersion::Snapshot1);
242        assert!(WasiVersion::Snapshot1 != WasiVersion::Snapshot0);
243        assert!(WasiVersion::Snapshot0 != WasiVersion::Latest);
244        assert!(WasiVersion::Latest != WasiVersion::Snapshot0);
245        assert!(WasiVersion::Snapshot0 != WasiVersion::Latest);
246        assert!(WasiVersion::Latest != WasiVersion::Snapshot0);
247        assert!(WasiVersion::Wasix32v1 != WasiVersion::Latest);
248        assert!(WasiVersion::Wasix64v1 != WasiVersion::Latest);
249    }
250
251    #[test]
252    fn wasi_version_ordering() {
253        assert!(WasiVersion::Snapshot0 <= WasiVersion::Snapshot0);
254        assert!(WasiVersion::Snapshot1 <= WasiVersion::Snapshot1);
255        assert!(WasiVersion::Wasix32v1 <= WasiVersion::Wasix32v1);
256        assert!(WasiVersion::Wasix64v1 <= WasiVersion::Wasix64v1);
257        assert!(WasiVersion::Latest <= WasiVersion::Latest);
258        assert!(WasiVersion::Snapshot0 >= WasiVersion::Snapshot0);
259        assert!(WasiVersion::Snapshot1 >= WasiVersion::Snapshot1);
260        assert!(WasiVersion::Wasix32v1 >= WasiVersion::Wasix32v1);
261        assert!(WasiVersion::Wasix64v1 >= WasiVersion::Wasix64v1);
262        assert!(WasiVersion::Latest >= WasiVersion::Latest);
263
264        assert!(WasiVersion::Snapshot0 < WasiVersion::Latest);
265        assert!(WasiVersion::Snapshot0 < WasiVersion::Wasix32v1);
266        assert!(WasiVersion::Snapshot0 < WasiVersion::Wasix64v1);
267        assert!(WasiVersion::Snapshot0 < WasiVersion::Snapshot1);
268        assert!(WasiVersion::Latest > WasiVersion::Snapshot0);
269        assert!(WasiVersion::Wasix32v1 > WasiVersion::Snapshot0);
270        assert!(WasiVersion::Wasix64v1 > WasiVersion::Snapshot0);
271        assert!(WasiVersion::Snapshot1 > WasiVersion::Snapshot0);
272
273        assert!(WasiVersion::Snapshot1 < WasiVersion::Wasix32v1);
274        assert!(WasiVersion::Snapshot1 < WasiVersion::Wasix64v1);
275        assert!(WasiVersion::Wasix32v1 > WasiVersion::Snapshot1);
276        assert!(WasiVersion::Wasix64v1 > WasiVersion::Snapshot1);
277
278        assert!(WasiVersion::Wasix32v1 < WasiVersion::Latest);
279        assert!(WasiVersion::Wasix32v1 > WasiVersion::Snapshot1);
280        assert!(WasiVersion::Wasix64v1 < WasiVersion::Latest);
281        assert!(WasiVersion::Wasix64v1 > WasiVersion::Snapshot1);
282        assert!(WasiVersion::Latest > WasiVersion::Wasix32v1);
283        assert!(WasiVersion::Snapshot1 < WasiVersion::Wasix32v1);
284        assert!(WasiVersion::Latest > WasiVersion::Wasix64v1);
285        assert!(WasiVersion::Snapshot1 < WasiVersion::Wasix32v1);
286
287        assert!(WasiVersion::Wasix32v1 < WasiVersion::Wasix64v1);
288        assert!(WasiVersion::Wasix64v1 > WasiVersion::Wasix32v1);
289    }
290}