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