Skip to main content

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    /// Whether this *process* has registered a signal handler callback.
139    ///
140    /// `WasiModuleInstanceHandles::signal_set` only records whether the
141    /// instance doing the asking registered one, and every spawned thread gets
142    /// its own instance. Signals are delivered to all of a process's threads,
143    /// so a per-instance flag makes sibling threads believe the program handles
144    /// no signals and apply the runtime's default disposition -- terminating a
145    /// process that does in fact have a handler installed.
146    pub signal_handler_registered: std::sync::atomic::AtomicBool,
147
148    // TODO: should not be here, since this requires active work to resolve.
149    // State should only hold active runtime state that can be reproducibly re-created.
150    pub preopen: Vec<String>,
151}
152
153impl WasiState {
154    // fn new(fs: WasiFs, inodes: Arc<RwLock<WasiInodes>>) -> Self {
155    //     WasiState {
156    //         fs,
157    //         secret: rand::thread_rng().gen::<[u8; 32]>(),
158    //         inodes,
159    //         args: Vec::new(),
160    //         preopen: Vec::new(),
161    //         threading: Default::default(),
162    //         futexs: Default::default(),
163    //         clock_offset: Default::default(),
164    //         envs: Vec::new(),
165    //     }
166    // }
167}
168
169// Implementations of direct to FS calls so that we can easily change their implementation
170impl WasiState {
171    pub(crate) fn fs_read_dir<P: AsRef<Path>>(
172        &self,
173        path: P,
174    ) -> Result<virtual_fs::ReadDir, Errno> {
175        self.fs
176            .root_fs
177            .read_dir(path.as_ref())
178            .map_err(fs_error_into_wasi_err)
179    }
180
181    pub(crate) fn fs_create_dir<P: AsRef<Path>>(&self, path: P) -> Result<(), Errno> {
182        self.fs
183            .root_fs
184            .create_dir(path.as_ref())
185            .map_err(fs_error_into_wasi_err)
186    }
187
188    pub(crate) fn fs_remove_dir<P: AsRef<Path>>(&self, path: P) -> Result<(), Errno> {
189        self.fs
190            .root_fs
191            .remove_dir(path.as_ref())
192            .map_err(fs_error_into_wasi_err)
193    }
194
195    pub(crate) async fn fs_rename<P: AsRef<Path>, Q: AsRef<Path>>(
196        &self,
197        from: P,
198        to: Q,
199    ) -> Result<(), Errno> {
200        self.fs
201            .root_fs
202            .rename(from.as_ref(), to.as_ref())
203            .await
204            .map_err(fs_error_into_wasi_err)
205    }
206
207    pub(crate) fn fs_remove_file<P: AsRef<Path>>(&self, path: P) -> Result<(), Errno> {
208        self.fs
209            .root_fs
210            .remove_file(path.as_ref())
211            .map_err(fs_error_into_wasi_err)
212    }
213
214    pub(crate) fn fs_new_open_options(&self) -> OpenOptions<'_> {
215        self.fs.root_fs.new_open_options()
216    }
217
218    /// Get the `VirtualFile` object at stdout
219    pub fn stdout(&self) -> Result<Option<Box<dyn VirtualFile + Send + Sync + 'static>>, FsError> {
220        self.std_dev_get(__WASI_STDOUT_FILENO)
221    }
222
223    /// Get the `VirtualFile` object at stderr
224    pub fn stderr(&self) -> Result<Option<Box<dyn VirtualFile + Send + Sync + 'static>>, FsError> {
225        self.std_dev_get(__WASI_STDERR_FILENO)
226    }
227
228    /// Get the `VirtualFile` object at stdin
229    pub fn stdin(&self) -> Result<Option<Box<dyn VirtualFile + Send + Sync + 'static>>, FsError> {
230        self.std_dev_get(__WASI_STDIN_FILENO)
231    }
232
233    /// Internal helper function to get a standard device handle.
234    /// Expects one of `__WASI_STDIN_FILENO`, `__WASI_STDOUT_FILENO`, `__WASI_STDERR_FILENO`.
235    fn std_dev_get(
236        &self,
237        fd: WasiFd,
238    ) -> Result<Option<Box<dyn VirtualFile + Send + Sync + 'static>>, FsError> {
239        let ret = WasiStateFileGuard::new(self, fd)?.map(|a| {
240            let ret = Box::new(a);
241            let ret: Box<dyn VirtualFile + Send + Sync + 'static> = ret;
242            ret
243        });
244        Ok(ret)
245    }
246
247    /// Forking the WasiState is used when either fork or vfork is called
248    pub fn fork(&self) -> Self {
249        WasiState {
250            fs: self.fs.fork(),
251            secret: self.secret,
252            inodes: self.inodes.clone(),
253            futexs: Default::default(),
254            clock_offset: Mutex::new(self.clock_offset.lock().unwrap().clone()),
255            args: Mutex::new(self.args.lock().unwrap().clone()),
256            envs: Mutex::new(self.envs.lock().unwrap().clone()),
257            signals: Mutex::new(self.signals.lock().unwrap().clone()),
258            // A forked process re-registers from its own instance.
259            signal_handler_registered: std::sync::atomic::AtomicBool::new(false),
260            preopen: self.preopen.clone(),
261        }
262    }
263}