1 use crate::clocks::WasiClocks; 2 use crate::dir::{DirEntry, WasiDir}; 3 use crate::file::{FileAccessMode, FileEntry, WasiFile}; 4 use crate::sched::WasiSched; 5 use crate::string_array::StringArray; 6 use crate::table::Table; 7 use crate::{Error, StringArrayError}; 8 use cap_rand::RngCore; 9 use std::ops::Deref; 10 use std::path::{Path, PathBuf}; 11 use std::sync::{Arc, Mutex}; 12 13 /// An `Arc`-wrapper around the wasi-common context to allow mutable access to 14 /// the file descriptor table. This wrapper is only necessary due to the 15 /// signature of `fd_fdstat_set_flags`; if that changes, there are a variety of 16 /// improvements that can be made (TODO: 17 /// <https://github.com/bytecodealliance/wasmtime/issues/5643)>. 18 #[derive(Clone)] 19 pub struct WasiCtx(Arc<WasiCtxInner>); 20 21 pub struct WasiCtxInner { 22 pub args: StringArray, 23 pub env: StringArray, 24 // TODO: this mutex should not be necessary, it forces threads to serialize 25 // their access to randomness unnecessarily 26 // (https://github.com/bytecodealliance/wasmtime/issues/5660). 27 pub random: Mutex<Box<dyn RngCore + Send + Sync>>, 28 pub clocks: WasiClocks, 29 pub sched: Box<dyn WasiSched>, 30 pub table: Table, 31 } 32 33 impl WasiCtx { new( random: Box<dyn RngCore + Send + Sync>, clocks: WasiClocks, sched: Box<dyn WasiSched>, table: Table, ) -> Self34 pub fn new( 35 random: Box<dyn RngCore + Send + Sync>, 36 clocks: WasiClocks, 37 sched: Box<dyn WasiSched>, 38 table: Table, 39 ) -> Self { 40 let s = WasiCtx(Arc::new(WasiCtxInner { 41 args: StringArray::new(), 42 env: StringArray::new(), 43 random: Mutex::new(random), 44 clocks, 45 sched, 46 table, 47 })); 48 s.set_stdin(Box::new(crate::pipe::ReadPipe::new(std::io::empty()))); 49 s.set_stdout(Box::new(crate::pipe::WritePipe::new(std::io::sink()))); 50 s.set_stderr(Box::new(crate::pipe::WritePipe::new(std::io::sink()))); 51 s 52 } 53 insert_file(&self, fd: u32, file: Box<dyn WasiFile>, access_mode: FileAccessMode)54 pub fn insert_file(&self, fd: u32, file: Box<dyn WasiFile>, access_mode: FileAccessMode) { 55 self.table() 56 .insert_at(fd, Arc::new(FileEntry::new(file, access_mode))); 57 } 58 push_file( &self, file: Box<dyn WasiFile>, access_mode: FileAccessMode, ) -> Result<u32, Error>59 pub fn push_file( 60 &self, 61 file: Box<dyn WasiFile>, 62 access_mode: FileAccessMode, 63 ) -> Result<u32, Error> { 64 self.table() 65 .push(Arc::new(FileEntry::new(file, access_mode))) 66 } 67 insert_dir(&self, fd: u32, dir: Box<dyn WasiDir>, path: PathBuf)68 pub fn insert_dir(&self, fd: u32, dir: Box<dyn WasiDir>, path: PathBuf) { 69 self.table() 70 .insert_at(fd, Arc::new(DirEntry::new(Some(path), dir))); 71 } 72 push_dir(&self, dir: Box<dyn WasiDir>, path: PathBuf) -> Result<u32, Error>73 pub fn push_dir(&self, dir: Box<dyn WasiDir>, path: PathBuf) -> Result<u32, Error> { 74 self.table().push(Arc::new(DirEntry::new(Some(path), dir))) 75 } 76 table(&self) -> &Table77 pub fn table(&self) -> &Table { 78 &self.table 79 } 80 table_mut(&mut self) -> Option<&mut Table>81 pub fn table_mut(&mut self) -> Option<&mut Table> { 82 Arc::get_mut(&mut self.0).map(|c| &mut c.table) 83 } 84 push_arg(&mut self, arg: &str) -> Result<(), StringArrayError>85 pub fn push_arg(&mut self, arg: &str) -> Result<(), StringArrayError> { 86 let s = Arc::get_mut(&mut self.0).expect( 87 "`push_arg` should only be used during initialization before the context is cloned", 88 ); 89 s.args.push(arg.to_owned()) 90 } 91 push_env(&mut self, var: &str, value: &str) -> Result<(), StringArrayError>92 pub fn push_env(&mut self, var: &str, value: &str) -> Result<(), StringArrayError> { 93 let s = Arc::get_mut(&mut self.0).expect( 94 "`push_env` should only be used during initialization before the context is cloned", 95 ); 96 s.env.push(format!("{var}={value}"))?; 97 Ok(()) 98 } 99 set_stdin(&self, f: Box<dyn WasiFile>)100 pub fn set_stdin(&self, f: Box<dyn WasiFile>) { 101 self.insert_file(0, f, FileAccessMode::READ); 102 } 103 set_stdout(&self, f: Box<dyn WasiFile>)104 pub fn set_stdout(&self, f: Box<dyn WasiFile>) { 105 self.insert_file(1, f, FileAccessMode::WRITE); 106 } 107 set_stderr(&self, f: Box<dyn WasiFile>)108 pub fn set_stderr(&self, f: Box<dyn WasiFile>) { 109 self.insert_file(2, f, FileAccessMode::WRITE); 110 } 111 push_preopened_dir( &self, dir: Box<dyn WasiDir>, path: impl AsRef<Path>, ) -> Result<(), Error>112 pub fn push_preopened_dir( 113 &self, 114 dir: Box<dyn WasiDir>, 115 path: impl AsRef<Path>, 116 ) -> Result<(), Error> { 117 self.table() 118 .push(Arc::new(DirEntry::new(Some(path.as_ref().to_owned()), dir)))?; 119 Ok(()) 120 } 121 } 122 123 impl Deref for WasiCtx { 124 type Target = WasiCtxInner; deref(&self) -> &Self::Target125 fn deref(&self) -> &Self::Target { 126 &self.0 127 } 128 } 129