wasmer_wasix/state/
mod.rs

1//! WARNING: the API exposed here is unstable and very experimental.  Certain things are not ready
2//! yet and may be broken in patch releases.  If you're using this and have any specific needs,
3//! please [let us know here](https://github.com/wasmerio/wasmer/issues/583) or by filing an issue.
4//!
5//! Wasmer always has a virtual root directory located at `/` at which all pre-opened directories can
6//! be found.  It's possible to traverse between preopened directories this way as well (for example
7//! `preopen-dir1/../preopen-dir2`).
8//!
9//! A preopened directory is a directory or directory + name combination passed into the
10//! `generate_import_object` function.  These are directories that the caller has given
11//! the WASI module permission to access.
12//!
13//! You can implement `VirtualFile` for your own types to get custom behavior and extend WASI, see the
14//! [WASI plugin example](https://github.com/wasmerio/wasmer/blob/main/examples/plugin.rs).
15
16#![allow(clippy::cognitive_complexity, clippy::too_many_arguments)]
17
18mod builder;
19pub mod context_switching;
20mod env;
21mod func_env;
22mod handles;
23mod linker;
24mod types;
25
26use std::{
27    collections::{BTreeMap, HashMap},
28    path::Path,
29    sync::Mutex,
30    task::Waker,
31    time::Duration,
32};
33
34#[cfg(feature = "enable-serde")]
35use serde::{Deserialize, Serialize};
36use virtual_fs::{FileOpener, FileSystem, FsError, OpenOptions, VirtualFile};
37use wasmer_wasix_types::wasi::{
38    Disposition, Errno, Fd as WasiFd, Rights, Signal, Snapshot0Clockid,
39};
40
41pub use self::{
42    builder::*,
43    env::{WasiEnv, WasiEnvInit, WasiModuleInstanceHandles, WasiModuleTreeHandles},
44    func_env::WasiFunctionEnv,
45    types::*,
46};
47pub use crate::fs::{InodeGuard, InodeWeakGuard};
48use crate::{
49    fs::{WasiFs, WasiFsRoot, WasiInodes, WasiStateFileGuard, fs_error_into_wasi_err},
50    syscalls::types::*,
51    utils::WasiParkingLot,
52};
53pub(crate) use handles::*;
54pub(crate) use linker::*;
55
56/// all the rights enabled
57pub const ALL_RIGHTS: Rights = Rights::all();
58
59#[allow(dead_code)]
60struct WasiStateOpener {
61    root_fs: WasiFsRoot,
62}
63
64impl FileOpener for WasiStateOpener {
65    fn open(
66        &self,
67        path: &Path,
68        conf: &virtual_fs::OpenOptionsConfig,
69    ) -> virtual_fs::Result<Box<dyn VirtualFile + Send + Sync + 'static>> {
70        let mut new_options = self.root_fs.new_open_options();
71        new_options.options(conf.clone());
72        new_options.open(path)
73    }
74}
75
76/// Represents a futex which will make threads wait for completion in a more
77/// CPU efficient manner
78#[derive(Debug, Default)]
79pub struct WasiFutex {
80    pub(crate) wakers: BTreeMap<u64, Option<Waker>>,
81}
82
83/// Structure that holds the state of BUS calls to this process and from
84/// this process. BUS calls are the equivalent of RPC's with support
85/// for all the major serializers
86#[derive(Debug, Default)]
87pub struct WasiBusState {
88    poll_waker: WasiParkingLot,
89}
90
91impl WasiBusState {
92    /// Gets a reference to the waker that can be used for
93    /// asynchronous calls
94    // TODO: review allow...
95    #[allow(dead_code)]
96    pub fn get_poll_waker(&self) -> Waker {
97        self.poll_waker.get_waker()
98    }
99
100    /// Wakes one of the reactors thats currently waiting
101    // TODO: review allow...
102    #[allow(dead_code)]
103    pub fn poll_wake(&self) {
104        self.poll_waker.wake()
105    }
106
107    /// Will wait until either the reactor is triggered
108    /// or the timeout occurs
109    // TODO: review allow...
110    #[allow(dead_code)]
111    pub fn poll_wait(&self, timeout: Duration) -> bool {
112        self.poll_waker.wait(timeout)
113    }
114}
115
116/// Stores the state of the futexes
117#[derive(Debug, Default)]
118#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
119pub(crate) struct WasiFutexState {
120    pub poller_seed: u64,
121    pub futexes: HashMap<u64, WasiFutex>,
122}
123
124/// Top level data type containing all* the state with which WASI can
125/// interact.
126///
127/// * The contents of files are not stored and may be modified by
128///   other, concurrently running programs.  Data such as the contents
129///   of directories are lazily loaded.
130#[derive(Debug)]
131#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
132pub(crate) struct WasiState {
133    pub secret: [u8; 32],
134
135    pub fs: WasiFs,
136    pub inodes: WasiInodes,
137    pub futexs: Mutex<WasiFutexState>,
138    pub clock_offset: Mutex<HashMap<Snapshot0Clockid, i64>>,
139    pub args: Mutex<Vec<String>>,
140    pub envs: Mutex<Vec<Vec<u8>>>,
141    pub signals: Mutex<HashMap<Signal, Disposition>>,
142
143    // TODO: should not be here, since this requires active work to resolve.
144    // State should only hold active runtime state that can be reproducibly re-created.
145    pub preopen: Vec<String>,
146}
147
148impl WasiState {
149    // fn new(fs: WasiFs, inodes: Arc<RwLock<WasiInodes>>) -> Self {
150    //     WasiState {
151    //         fs,
152    //         secret: rand::thread_rng().gen::<[u8; 32]>(),
153    //         inodes,
154    //         args: Vec::new(),
155    //         preopen: Vec::new(),
156    //         threading: Default::default(),
157    //         futexs: Default::default(),
158    //         clock_offset: Default::default(),
159    //         envs: Vec::new(),
160    //     }
161    // }
162}
163
164// Implementations of direct to FS calls so that we can easily change their implementation
165impl WasiState {
166    pub(crate) fn fs_read_dir<P: AsRef<Path>>(
167        &self,
168        path: P,
169    ) -> Result<virtual_fs::ReadDir, Errno> {
170        self.fs
171            .root_fs
172            .read_dir(path.as_ref())
173            .map_err(fs_error_into_wasi_err)
174    }
175
176    pub(crate) fn fs_create_dir<P: AsRef<Path>>(&self, path: P) -> Result<(), Errno> {
177        self.fs
178            .root_fs
179            .create_dir(path.as_ref())
180            .map_err(fs_error_into_wasi_err)
181    }
182
183    pub(crate) fn fs_remove_dir<P: AsRef<Path>>(&self, path: P) -> Result<(), Errno> {
184        self.fs
185            .root_fs
186            .remove_dir(path.as_ref())
187            .map_err(fs_error_into_wasi_err)
188    }
189
190    pub(crate) async fn fs_rename<P: AsRef<Path>, Q: AsRef<Path>>(
191        &self,
192        from: P,
193        to: Q,
194    ) -> Result<(), Errno> {
195        self.fs
196            .root_fs
197            .rename(from.as_ref(), to.as_ref())
198            .await
199            .map_err(fs_error_into_wasi_err)
200    }
201
202    pub(crate) fn fs_remove_file<P: AsRef<Path>>(&self, path: P) -> Result<(), Errno> {
203        self.fs
204            .root_fs
205            .remove_file(path.as_ref())
206            .map_err(fs_error_into_wasi_err)
207    }
208
209    pub(crate) fn fs_new_open_options(&self) -> OpenOptions<'_> {
210        self.fs.root_fs.new_open_options()
211    }
212
213    /// Turn the WasiState into bytes
214    #[cfg(feature = "enable-serde")]
215    pub fn freeze(&self) -> Option<Vec<u8>> {
216        bincode::serialize(self).ok()
217    }
218
219    /// Get a WasiState from bytes
220    #[cfg(feature = "enable-serde")]
221    pub fn unfreeze(bytes: &[u8]) -> Option<Self> {
222        bincode::deserialize(bytes).ok()
223    }
224
225    /// Get the `VirtualFile` object at stdout
226    pub fn stdout(&self) -> Result<Option<Box<dyn VirtualFile + Send + Sync + 'static>>, FsError> {
227        self.std_dev_get(__WASI_STDOUT_FILENO)
228    }
229
230    /// Get the `VirtualFile` object at stderr
231    pub fn stderr(&self) -> Result<Option<Box<dyn VirtualFile + Send + Sync + 'static>>, FsError> {
232        self.std_dev_get(__WASI_STDERR_FILENO)
233    }
234
235    /// Get the `VirtualFile` object at stdin
236    pub fn stdin(&self) -> Result<Option<Box<dyn VirtualFile + Send + Sync + 'static>>, FsError> {
237        self.std_dev_get(__WASI_STDIN_FILENO)
238    }
239
240    /// Internal helper function to get a standard device handle.
241    /// Expects one of `__WASI_STDIN_FILENO`, `__WASI_STDOUT_FILENO`, `__WASI_STDERR_FILENO`.
242    fn std_dev_get(
243        &self,
244        fd: WasiFd,
245    ) -> Result<Option<Box<dyn VirtualFile + Send + Sync + 'static>>, FsError> {
246        let ret = WasiStateFileGuard::new(self, fd)?.map(|a| {
247            let ret = Box::new(a);
248            let ret: Box<dyn VirtualFile + Send + Sync + 'static> = ret;
249            ret
250        });
251        Ok(ret)
252    }
253
254    /// Forking the WasiState is used when either fork or vfork is called
255    pub fn fork(&self) -> Self {
256        WasiState {
257            fs: self.fs.fork(),
258            secret: self.secret,
259            inodes: self.inodes.clone(),
260            futexs: Default::default(),
261            clock_offset: Mutex::new(self.clock_offset.lock().unwrap().clone()),
262            args: Mutex::new(self.args.lock().unwrap().clone()),
263            envs: Mutex::new(self.envs.lock().unwrap().clone()),
264            signals: Mutex::new(self.signals.lock().unwrap().clone()),
265            preopen: self.preopen.clone(),
266        }
267    }
268}