1use crate::random_file::RandomFile;
2use crate::{FileSystem, VirtualFile};
3use std::path::{Path, PathBuf};
4use tracing::*;
5
6use super::ZeroFile;
7use super::{DeviceFile, NullFile};
8use crate::tmp_fs::TmpFileSystem;
9
10pub struct RootFileSystemBuilder {
11 default_root_dirs: bool,
12 default_dev_files: bool,
13 add_wasmer_command: bool,
14 stdin: Option<Box<dyn VirtualFile + Send + Sync>>,
15 stdout: Option<Box<dyn VirtualFile + Send + Sync>>,
16 stderr: Option<Box<dyn VirtualFile + Send + Sync>>,
17 tty: Option<Box<dyn VirtualFile + Send + Sync>>,
18}
19
20impl Default for RootFileSystemBuilder {
21 fn default() -> Self {
22 Self {
23 default_root_dirs: true,
24 default_dev_files: true,
25 add_wasmer_command: true,
26 stdin: None,
27 stdout: None,
28 stderr: None,
29 tty: None,
30 }
31 }
32}
33
34impl RootFileSystemBuilder {
35 pub fn new() -> Self {
36 Self::default()
37 }
38
39 pub fn with_stdin(mut self, file: Box<dyn VirtualFile + Send + Sync>) -> Self {
40 self.stdin.replace(file);
41 self
42 }
43
44 pub fn with_stdout(mut self, file: Box<dyn VirtualFile + Send + Sync>) -> Self {
45 self.stdout.replace(file);
46 self
47 }
48
49 pub fn with_stderr(mut self, file: Box<dyn VirtualFile + Send + Sync>) -> Self {
50 self.stderr.replace(file);
51 self
52 }
53
54 pub fn with_tty(mut self, file: Box<dyn VirtualFile + Send + Sync>) -> Self {
55 self.tty.replace(file);
56 self
57 }
58
59 pub fn default_root_dirs(mut self, val: bool) -> Self {
60 self.default_root_dirs = val;
61 self
62 }
63
64 pub fn build(self) -> TmpFileSystem {
65 self.build_ext(&[])
66 }
67
68 pub fn build_ext(self, mapped_dirs: &[&str]) -> TmpFileSystem {
69 let tmp = TmpFileSystem::new();
70
71 if self.default_root_dirs {
72 let default_dirs = ["/.app", "/.private", "/bin", "/dev", "/etc", "/tmp"]
73 .into_iter()
74 .filter(|d| !mapped_dirs.contains(d))
75 .collect::<Vec<_>>();
76
77 for root_dir in &default_dirs {
78 if let Err(err) = tmp.create_dir(Path::new(root_dir)) {
79 debug!("failed to create dir [{}] - {}", root_dir, err);
80 }
81 }
82 }
83 if self.add_wasmer_command {
84 let _ = tmp
85 .new_open_options_ext()
86 .insert_device_file(PathBuf::from("/bin/wasmer"), Box::<NullFile>::default());
87 }
88 if self.default_dev_files {
89 let _ = tmp
90 .new_open_options_ext()
91 .insert_device_file(PathBuf::from("/dev/null"), Box::<NullFile>::default());
92 let _ = tmp
93 .new_open_options_ext()
94 .insert_device_file(PathBuf::from("/dev/zero"), Box::<ZeroFile>::default());
95 let _ = tmp
96 .new_open_options_ext()
97 .insert_device_file(PathBuf::from("/dev/urandom"), Box::<RandomFile>::default());
98 let _ = tmp.new_open_options_ext().insert_device_file(
99 PathBuf::from("/dev/stdin"),
100 self.stdin
101 .unwrap_or_else(|| Box::new(DeviceFile::new(DeviceFile::STDIN))),
102 );
103 let _ = tmp.new_open_options_ext().insert_device_file(
104 PathBuf::from("/dev/stdout"),
105 self.stdout
106 .unwrap_or_else(|| Box::new(DeviceFile::new(DeviceFile::STDOUT))),
107 );
108 let _ = tmp.new_open_options_ext().insert_device_file(
109 PathBuf::from("/dev/stderr"),
110 self.stderr
111 .unwrap_or_else(|| Box::new(DeviceFile::new(DeviceFile::STDERR))),
112 );
113 let _ = tmp.new_open_options_ext().insert_device_file(
114 PathBuf::from("/dev/tty"),
115 self.tty.unwrap_or_else(|| Box::<NullFile>::default()),
116 );
117
118 let _ = tmp.create_dir(Path::new("/dev/shm"));
119 }
120 tmp
121 }
122}
123
124#[cfg(test)]
125mod test_builder {
126 use crate::{FileSystem, RootFileSystemBuilder};
127 use std::path::Path;
128 use tokio::io::{AsyncReadExt, AsyncWriteExt};
129
130 #[tokio::test]
131 async fn test_root_file_system() {
132 let root_fs = RootFileSystemBuilder::new().build();
133 let mut dev_null = root_fs
134 .new_open_options()
135 .read(true)
136 .write(true)
137 .open("/dev/null")
138 .unwrap();
139 assert_eq!(dev_null.write(b"hello").await.unwrap(), 5);
140 let mut buf = Vec::new();
141 dev_null.read_to_end(&mut buf).await.unwrap();
142 assert!(buf.is_empty());
143 assert!(dev_null.get_special_fd().is_none());
144
145 let mut dev_zero = root_fs
146 .new_open_options()
147 .read(true)
148 .write(true)
149 .open("/dev/zero")
150 .unwrap();
151 assert_eq!(dev_zero.write(b"hello").await.unwrap(), 5);
152 let mut buf = vec![1; 10];
153 dev_zero.read_exact(&mut buf[..]).await.unwrap();
154 assert_eq!(buf, vec![0; 10]);
155 assert!(dev_zero.get_special_fd().is_none());
156
157 let mut dev_tty = root_fs
158 .new_open_options()
159 .read(true)
160 .write(true)
161 .open("/dev/tty")
162 .unwrap();
163 assert_eq!(dev_tty.write(b"hello").await.unwrap(), 5);
164 let mut buf = Vec::new();
165 dev_tty.read_to_end(&mut buf).await.unwrap();
166 assert!(buf.is_empty());
167 assert!(dev_tty.get_special_fd().is_none());
168
169 root_fs
170 .new_open_options()
171 .read(true)
172 .open("/bin/wasmer")
173 .unwrap();
174
175 let dev_stdin = root_fs
176 .new_open_options()
177 .read(true)
178 .write(true)
179 .open("/dev/stdin")
180 .unwrap();
181 assert_eq!(dev_stdin.get_special_fd().unwrap(), 0);
182 let dev_stdout = root_fs
183 .new_open_options()
184 .read(true)
185 .write(true)
186 .open("/dev/stdout")
187 .unwrap();
188 assert_eq!(dev_stdout.get_special_fd().unwrap(), 1);
189 let dev_stderr = root_fs
190 .new_open_options()
191 .read(true)
192 .write(true)
193 .open("/dev/stderr")
194 .unwrap();
195 assert_eq!(dev_stderr.get_special_fd().unwrap(), 2);
196
197 let dev_shm_metadata = root_fs.metadata(Path::new("/dev/shm")).unwrap();
198 assert!(dev_shm_metadata.is_dir());
199 }
200}