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
34use virtual_fs::{FileOpener, FileSystem, FsError, OpenOptions, VirtualFile};
35use wasmer_wasix_types::wasi::{
36    Disposition, Errno, Fd as WasiFd, Rights, Signal, Snapshot0Clockid,
37};
38
39pub use self::{
40    builder::*,
41    env::{WasiEnv, WasiEnvInit, WasiModuleInstanceHandles, WasiModuleTreeHandles},
42    func_env::WasiFunctionEnv,
43    types::*,
44};
45pub use crate::fs::{InodeGuard, InodeWeakGuard};
46use crate::{
47    fs::{WasiFs, WasiFsRoot, WasiInodes, WasiStateFileGuard, fs_error_into_wasi_err},
48    syscalls::types::*,
49    utils::WasiParkingLot,
50};
51pub(crate) use handles::*;
52pub(crate) use linker::*;
53
54/// all the rights enabled
55pub const ALL_RIGHTS: Rights = Rights::all();
56
57#[allow(dead_code)]
58struct WasiStateOpener {
59    root_fs: WasiFsRoot,
60}
61
62impl FileOpener for WasiStateOpener {
63    fn open(
64        &self,
65        path: &Path,
66        conf: &virtual_fs::OpenOptionsConfig,
67    ) -> virtual_fs::Result<Box<dyn VirtualFile + Send + Sync + 'static>> {
68        let mut new_options = self.root_fs.new_open_options();
69        new_options.options(conf.clone());
70        new_options.open(path)
71    }
72}
73
74/// Represents a futex which will make threads wait for completion in a more
75/// CPU efficient manner
76#[derive(Debug, Default)]
77pub struct WasiFutex {
78    pub(crate) wakers: BTreeMap<u64, Option<Waker>>,
79}
80
81/// Structure that holds the state of BUS calls to this process and from
82/// this process. BUS calls are the equivalent of RPC's with support
83/// for all the major serializers
84#[derive(Debug, Default)]
85pub struct WasiBusState {
86    poll_waker: WasiParkingLot,
87}
88
89impl WasiBusState {
90    /// Gets a reference to the waker that can be used for
91    /// asynchronous calls
92    // TODO: review allow...
93    #[allow(dead_code)]
94    pub fn get_poll_waker(&self) -> Waker {
95        self.poll_waker.get_waker()
96    }
97
98    /// Wakes one of the reactors thats currently waiting
99    // TODO: review allow...
100    #[allow(dead_code)]
101    pub fn poll_wake(&self) {
102        self.poll_waker.wake()
103    }
104
105    /// Will wait until either the reactor is triggered
106    /// or the timeout occurs
107    // TODO: review allow...
108    #[allow(dead_code)]
109    pub fn poll_wait(&self, timeout: Duration) -> bool {
110        self.poll_waker.wait(timeout)
111    }
112}
113
114/// Stores the state of the futexes
115#[derive(Debug, Default)]
116pub(crate) struct WasiFutexState {
117    pub poller_seed: u64,
118    pub futexes: HashMap<u64, WasiFutex>,
119}
120
121/// Top level data type containing all* the state with which WASI can
122/// interact.
123///
124/// * The contents of files are not stored and may be modified by
125///   other, concurrently running programs.  Data such as the contents
126///   of directories are lazily loaded.
127#[derive(Debug)]
128pub(crate) struct WasiState {
129    pub secret: [u8; 32],
130
131    pub fs: WasiFs,
132    pub inodes: WasiInodes,
133    pub futexs: Mutex<WasiFutexState>,
134    pub clock_offset: Mutex<HashMap<Snapshot0Clockid, i64>>,
135    pub args: Mutex<Vec<String>>,
136    pub envs: Mutex<Vec<Vec<u8>>>,
137    pub signals: Mutex<HashMap<Signal, Disposition>>,
138
139    // TODO: should not be here, since this requires active work to resolve.
140    // State should only hold active runtime state that can be reproducibly re-created.
141    pub preopen: Vec<String>,
142}
143
144impl WasiState {
145    // fn new(fs: WasiFs, inodes: Arc<RwLock<WasiInodes>>) -> Self {
146    //     WasiState {
147    //         fs,
148    //         secret: rand::thread_rng().gen::<[u8; 32]>(),
149    //         inodes,
150    //         args: Vec::new(),
151    //         preopen: Vec::new(),
152    //         threading: Default::default(),
153    //         futexs: Default::default(),
154    //         clock_offset: Default::default(),
155    //         envs: Vec::new(),
156    //     }
157    // }
158}
159
160// Implementations of direct to FS calls so that we can easily change their implementation
161impl WasiState {
162    pub(crate) fn fs_read_dir<P: AsRef<Path>>(
163        &self,
164        path: P,
165    ) -> Result<virtual_fs::ReadDir, Errno> {
166        self.fs
167            .root_fs
168            .read_dir(path.as_ref())
169            .map_err(fs_error_into_wasi_err)
170    }
171
172    pub(crate) fn fs_create_dir<P: AsRef<Path>>(&self, path: P) -> Result<(), Errno> {
173        self.fs
174            .root_fs
175            .create_dir(path.as_ref())
176            .map_err(fs_error_into_wasi_err)
177    }
178
179    pub(crate) fn fs_remove_dir<P: AsRef<Path>>(&self, path: P) -> Result<(), Errno> {
180        self.fs
181            .root_fs
182            .remove_dir(path.as_ref())
183            .map_err(fs_error_into_wasi_err)
184    }
185
186    pub(crate) async fn fs_rename<P: AsRef<Path>, Q: AsRef<Path>>(
187        &self,
188        from: P,
189        to: Q,
190    ) -> Result<(), Errno> {
191        self.fs
192            .root_fs
193            .rename(from.as_ref(), to.as_ref())
194            .await
195            .map_err(fs_error_into_wasi_err)
196    }
197
198    pub(crate) fn fs_remove_file<P: AsRef<Path>>(&self, path: P) -> Result<(), Errno> {
199        self.fs
200            .root_fs
201            .remove_file(path.as_ref())
202            .map_err(fs_error_into_wasi_err)
203    }
204
205    pub(crate) fn fs_new_open_options(&self) -> OpenOptions<'_> {
206        self.fs.root_fs.new_open_options()
207    }
208
209    /// Get the `VirtualFile` object at stdout
210    pub fn stdout(&self) -> Result<Option<Box<dyn VirtualFile + Send + Sync + 'static>>, FsError> {
211        self.std_dev_get(__WASI_STDOUT_FILENO)
212    }
213
214    /// Get the `VirtualFile` object at stderr
215    pub fn stderr(&self) -> Result<Option<Box<dyn VirtualFile + Send + Sync + 'static>>, FsError> {
216        self.std_dev_get(__WASI_STDERR_FILENO)
217    }
218
219    /// Get the `VirtualFile` object at stdin
220    pub fn stdin(&self) -> Result<Option<Box<dyn VirtualFile + Send + Sync + 'static>>, FsError> {
221        self.std_dev_get(__WASI_STDIN_FILENO)
222    }
223
224    /// Internal helper function to get a standard device handle.
225    /// Expects one of `__WASI_STDIN_FILENO`, `__WASI_STDOUT_FILENO`, `__WASI_STDERR_FILENO`.
226    fn std_dev_get(
227        &self,
228        fd: WasiFd,
229    ) -> Result<Option<Box<dyn VirtualFile + Send + Sync + 'static>>, FsError> {
230        let ret = WasiStateFileGuard::new(self, fd)?.map(|a| {
231            let ret = Box::new(a);
232            let ret: Box<dyn VirtualFile + Send + Sync + 'static> = ret;
233            ret
234        });
235        Ok(ret)
236    }
237
238    /// Forking the WasiState is used when either fork or vfork is called
239    pub fn fork(&self) -> Self {
240        WasiState {
241            fs: self.fs.fork(),
242            secret: self.secret,
243            inodes: self.inodes.clone(),
244            futexs: Default::default(),
245            clock_offset: Mutex::new(self.clock_offset.lock().unwrap().clone()),
246            args: Mutex::new(self.args.lock().unwrap().clone()),
247            envs: Mutex::new(self.envs.lock().unwrap().clone()),
248            signals: Mutex::new(self.signals.lock().unwrap().clone()),
249            preopen: self.preopen.clone(),
250        }
251    }
252}