1use super::{
2 error::Errno,
3 types::{self as wasi_types},
4};
5use bitflags::bitflags;
6use std::{fmt::Debug, future::Future, io, path::Path, time::Duration};
7
8pub mod impls;
9pub mod virtual_sys;
10
11pub enum SystemTimeSpec {
12 SymbolicNow,
13 Absolute(Duration),
14}
15
16pub struct ReaddirEntity {
17 pub next: u64,
18 pub inode: u64,
19 pub name: String,
20 pub filetype: FileType,
21}
22
23impl From<&ReaddirEntity> for wasi_types::__wasi_dirent_t {
24 fn from(ent: &ReaddirEntity) -> Self {
25 wasi_types::__wasi_dirent_t {
26 d_next: ent.next.to_le(),
27 d_ino: ent.inode.to_le(),
28 d_namlen: (ent.name.len() as u32).to_le(),
29 d_type: ent.filetype.0,
30 }
31 }
32}
33
34#[derive(Debug, Clone)]
35pub struct FdStat {
36 pub filetype: FileType,
37 pub fs_rights_base: WASIRights,
38 pub fs_rights_inheriting: WASIRights,
39 pub flags: FdFlags,
40}
41
42impl From<&FdStat> for wasi_types::__wasi_fdstat_t {
43 fn from(fdstat: &FdStat) -> wasi_types::__wasi_fdstat_t {
44 use wasi_types::__wasi_fdstat_t;
45 __wasi_fdstat_t {
46 fs_filetype: fdstat.filetype.0,
47 fs_rights_base: fdstat.fs_rights_base.bits(),
48 fs_rights_inheriting: fdstat.fs_rights_inheriting.bits(),
49 fs_flags: fdstat.flags.bits(),
50 }
51 }
52}
53
54impl From<FdStat> for wasi_types::__wasi_fdstat_t {
55 fn from(fdstat: FdStat) -> wasi_types::__wasi_fdstat_t {
56 use wasi_types::__wasi_fdstat_t;
57 __wasi_fdstat_t::from(&fdstat)
58 }
59}
60
61#[derive(Debug, Clone, PartialEq, Eq)]
62pub struct Filestat {
63 pub filetype: FileType,
64 pub inode: u64,
65 pub nlink: u64,
66 pub size: u64, pub atim: Option<std::time::SystemTime>,
68 pub mtim: Option<std::time::SystemTime>,
69 pub ctim: Option<std::time::SystemTime>,
70}
71
72impl From<Filestat> for wasi_types::__wasi_filestat_t {
73 fn from(stat: Filestat) -> wasi_types::__wasi_filestat_t {
74 wasi_types::__wasi_filestat_t {
75 dev: 3,
76 ino: stat.inode,
77 filetype: stat.filetype.0,
78 nlink: stat.nlink,
79 size: stat.size,
80 atim: stat
81 .atim
82 .map(|t| t.duration_since(std::time::UNIX_EPOCH).unwrap().as_nanos() as u64)
83 .unwrap_or(0),
84 mtim: stat
85 .mtim
86 .map(|t| t.duration_since(std::time::UNIX_EPOCH).unwrap().as_nanos() as u64)
87 .unwrap_or(0),
88 ctim: stat
89 .ctim
90 .map(|t| t.duration_since(std::time::UNIX_EPOCH).unwrap().as_nanos() as u64)
91 .unwrap_or(0),
92 }
93 }
94}
95
96impl From<(u64, Filestat)> for wasi_types::__wasi_filestat_t {
97 fn from((dev, stat): (u64, Filestat)) -> Self {
98 let mut stat: Self = stat.into();
99 stat.dev = dev;
100 stat
101 }
102}
103
104#[derive(Debug, Copy, Clone, PartialEq, Eq)]
105pub struct FileType(pub wasi_types::__wasi_filetype_t::Type);
106impl FileType {
107 pub const UNKNOWN: FileType = FileType(0);
108 pub const BLOCK_DEVICE: FileType = FileType(1);
109 pub const CHARACTER_DEVICE: FileType = FileType(2);
110 pub const DIRECTORY: FileType = FileType(3);
111 pub const REGULAR_FILE: FileType = FileType(4);
112 pub const SOCKET_DGRAM: FileType = FileType(5);
113 pub const SOCKET_STREAM: FileType = FileType(6);
114 pub const SYMBOLIC_LINK: FileType = FileType(7);
115}
116
117bitflags! {
118 #[derive(Debug, Clone)]
119 pub struct FdFlags: wasi_types::__wasi_fdflags_t::Type {
120 const APPEND = wasi_types::__wasi_fdflags_t::__WASI_FDFLAGS_APPEND; const DSYNC = wasi_types::__wasi_fdflags_t::__WASI_FDFLAGS_DSYNC; const NONBLOCK = wasi_types::__wasi_fdflags_t::__WASI_FDFLAGS_NONBLOCK; const RSYNC = wasi_types::__wasi_fdflags_t::__WASI_FDFLAGS_RSYNC; const SYNC = wasi_types::__wasi_fdflags_t::__WASI_FDFLAGS_SYNC; }
126}
127
128bitflags! {
129 #[derive(PartialEq)]
130 pub struct SdFlags: wasi_types::__wasi_sdflags_t::Type {
131 const RD = wasi_types::__wasi_sdflags_t::__WASI_SDFLAGS_RD;
132 const WR = wasi_types::__wasi_sdflags_t::__WASI_SDFLAGS_WR;
133 }
134}
135
136impl From<SdFlags> for std::net::Shutdown {
137 fn from(val: SdFlags) -> Self {
138 use std::net::Shutdown;
139 if val == SdFlags::RD {
140 Shutdown::Read
141 } else if val == SdFlags::WR {
142 Shutdown::Write
143 } else {
144 Shutdown::Both
145 }
146 }
147}
148
149bitflags! {
150 pub struct SiFlags: wasi_types::__wasi_siflags_t {
151 }
152}
153
154bitflags! {
155 pub struct RiFlags: wasi_types::__wasi_riflags_t::Type {
156 const RECV_PEEK = wasi_types::__wasi_riflags_t::__WASI_RIFLAGS_RECV_PEEK;
157 const RECV_WAITALL = wasi_types::__wasi_riflags_t::__WASI_RIFLAGS_RECV_WAITALL;
158 }
159}
160
161bitflags! {
162 pub struct RoFlags: wasi_types::__wasi_roflags_t::Type {
163 const RECV_DATA_TRUNCATED = wasi_types::__wasi_roflags_t::__WASI_ROFLAGS_RECV_DATA_TRUNCATED;
164 }
165}
166
167bitflags! {
168 #[derive(Debug, Clone)]
169 pub struct OFlags: wasi_types::__wasi_oflags_t::Type {
170 const CREATE = wasi_types::__wasi_oflags_t::__WASI_OFLAGS_CREAT;
171 const DIRECTORY = wasi_types::__wasi_oflags_t::__WASI_OFLAGS_DIRECTORY;
172 const EXCLUSIVE = wasi_types::__wasi_oflags_t::__WASI_OFLAGS_EXCL;
173 const TRUNCATE = wasi_types::__wasi_oflags_t::__WASI_OFLAGS_TRUNC;
174 }
175}
176
177bitflags! {
178 #[derive(Debug, Clone)]
179 pub struct WASIRights : wasi_types::__wasi_rights_t::Type {
180 const FD_DATASYNC= 1;
181 const FD_READ = 2;
182 const FD_SEEK = 4;
183 const FD_FDSTAT_SET_FLAGS = 8;
184 const FD_SYNC = 16;
185 const FD_TELL = 32;
186 const FD_WRITE = 64;
187 const FD_ADVISE = 128;
188 const FD_ALLOCATE = 256;
189 const PATH_CREATE_DIRECTORY = 512;
190 const PATH_CREATE_FILE = 1024;
191 const PATH_LINK_SOURCE = 2048;
192 const PATH_LINK_TARGET = 4096;
193 const PATH_OPEN = 8192;
194 const FD_READDIR = 16384;
195 const PATH_READLINK = 32768;
196 const PATH_RENAME_SOURCE = 65536;
197 const PATH_RENAME_TARGET = 131072;
198 const PATH_FILESTAT_GET = 262144;
199 const PATH_FILESTAT_SET_SIZE = 524288;
200 const PATH_FILESTAT_SET_TIMES = 1048576;
201 const FD_FILESTAT_GET = 2097152;
202 const FD_FILESTAT_SET_SIZE = 4194304;
203 const FD_FILESTAT_SET_TIMES = 8388608;
204 const PATH_SYMLINK = 16777216;
205 const PATH_REMOVE_DIRECTORY = 33554432;
206 const PATH_UNLINK_FILE = 67108864;
207 const POLL_FD_READWRITE = 134217728;
208 const SOCK_SHUTDOWN = 268435456;
209 const SOCK_OPEN = 536870912;
210 const SOCK_CLOSE = 1073741824;
211 const SOCK_BIND = 2147483648;
212 const SOCK_RECV = 4294967296;
213 const SOCK_RECV_FROM = 8589934592;
214 const SOCK_SEND = 17179869184;
215 const SOCK_SEND_TO = 34359738368;
216 }
217}
218
219impl Default for WASIRights {
220 fn default() -> Self {
221 Self::empty()
222 }
223}
224
225impl WASIRights {
226 #[inline]
227 pub fn fd_all() -> Self {
228 WASIRights::FD_ADVISE
229 | WASIRights::FD_ALLOCATE
230 | WASIRights::FD_DATASYNC
231 | WASIRights::FD_SYNC
232 | WASIRights::FD_TELL
233 | WASIRights::FD_SEEK
234 | WASIRights::FD_READ
235 | WASIRights::FD_WRITE
236 | WASIRights::FD_FDSTAT_SET_FLAGS
237 | WASIRights::FD_FILESTAT_GET
238 | WASIRights::FD_FILESTAT_SET_SIZE
239 | WASIRights::FD_FILESTAT_SET_TIMES
240 }
241
242 #[inline]
243 pub fn dir_all() -> Self {
244 WASIRights::PATH_CREATE_DIRECTORY
245 | WASIRights::PATH_CREATE_FILE
246 | WASIRights::PATH_LINK_SOURCE
247 | WASIRights::PATH_LINK_TARGET
248 | WASIRights::PATH_OPEN
249 | WASIRights::FD_READDIR
250 | WASIRights::PATH_READLINK
251 | WASIRights::PATH_RENAME_SOURCE
252 | WASIRights::PATH_RENAME_TARGET
253 | WASIRights::PATH_SYMLINK
254 | WASIRights::PATH_REMOVE_DIRECTORY
255 | WASIRights::PATH_UNLINK_FILE
256 | WASIRights::PATH_FILESTAT_GET
257 | WASIRights::PATH_FILESTAT_SET_TIMES
258 | WASIRights::FD_FILESTAT_GET
259 | WASIRights::FD_FILESTAT_SET_TIMES
260 }
261
262 pub fn can(&self, other: Self) -> Result<(), Errno> {
263 if self.contains(other) {
264 Ok(())
265 } else {
266 Err(Errno::__WASI_ERRNO_NOTCAPABLE)
267 }
268 }
269}
270
271bitflags! {
272 pub struct Lookupflags: wasi_types::__wasi_lookupflags_t::Type {
273 const SYMLINK_FOLLOW = wasi_types::__wasi_lookupflags_t::__WASI_LOOKUPFLAGS_SYMLINK_FOLLOW;
274 }
275}
276
277#[derive(Debug, Clone)]
278pub enum Advice {
279 Normal,
280 Sequential,
281 Random,
282 WillNeed,
283 DontNeed,
284 NoReuse,
285}
286
287pub trait WasiNode {
288 fn fd_fdstat_get(&self) -> Result<FdStat, Errno>;
289
290 fn fd_fdstat_set_flags(&mut self, flags: FdFlags) -> Result<(), Errno> {
291 Err(Errno::__WASI_ERRNO_BADF)
292 }
293
294 fn fd_fdstat_set_rights(
295 &mut self,
296 fs_rights_base: WASIRights,
297 fs_rights_inheriting: WASIRights,
298 ) -> Result<(), Errno> {
299 Ok(())
300 }
301
302 fn fd_filestat_get(&self) -> Result<Filestat, Errno>;
303
304 fn fd_filestat_set_size(&mut self, size: wasi_types::__wasi_filesize_t) -> Result<(), Errno>;
305
306 fn fd_filestat_set_times(
307 &mut self,
308 atim: wasi_types::__wasi_timestamp_t,
309 mtim: wasi_types::__wasi_timestamp_t,
310 fst_flags: wasi_types::__wasi_fstflags_t::Type,
311 ) -> Result<(), Errno>;
312}
313
314pub trait WasiFile: WasiNode {
315 fn fd_advise(
316 &mut self,
317 offset: wasi_types::__wasi_filesize_t,
318 len: wasi_types::__wasi_filesize_t,
319 advice: Advice,
320 ) -> Result<(), Errno> {
321 Ok(())
322 }
323
324 fn fd_allocate(
325 &mut self,
326 offset: wasi_types::__wasi_filesize_t,
327 len: wasi_types::__wasi_filesize_t,
328 ) -> Result<(), Errno> {
329 Err(Errno::__WASI_ERRNO_BADF)
330 }
331
332 fn fd_datasync(&mut self) -> Result<(), Errno> {
333 Ok(())
334 }
335
336 fn fd_sync(&mut self) -> Result<(), Errno> {
337 Ok(())
338 }
339
340 fn fd_read(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> Result<usize, Errno>;
341
342 fn fd_pread(
343 &mut self,
344 bufs: &mut [io::IoSliceMut<'_>],
345 offset: wasi_types::__wasi_filesize_t,
346 ) -> Result<usize, Errno>;
347
348 fn fd_write(&mut self, bufs: &[io::IoSlice<'_>]) -> Result<usize, Errno>;
349
350 fn fd_pwrite(
351 &mut self,
352 bufs: &[io::IoSlice<'_>],
353 offset: wasi_types::__wasi_filesize_t,
354 ) -> Result<usize, Errno>;
355
356 fn fd_seek(
357 &mut self,
358 offset: wasi_types::__wasi_filedelta_t,
359 whence: wasi_types::__wasi_whence_t::Type,
360 ) -> Result<wasi_types::__wasi_filesize_t, Errno>;
361
362 fn fd_tell(&mut self) -> Result<wasi_types::__wasi_filesize_t, Errno>;
363}
364
365pub trait WasiDir: WasiNode {
366 fn get_readdir(&self, start: u64) -> Result<Vec<(String, u64, FileType)>, Errno>;
367
368 fn fd_readdir(&self, cursor: usize, write_buf: &mut [u8]) -> Result<usize, Errno> {
369 fn write_dirent(entity: &ReaddirEntity, write_buf: &mut [u8]) -> usize {
370 unsafe {
371 use wasi_types::__wasi_dirent_t;
372 const __wasi_dirent_t_size: usize = std::mem::size_of::<__wasi_dirent_t>();
373 let ent = __wasi_dirent_t::from(entity);
374 let ent_bytes_ptr = (&ent) as *const __wasi_dirent_t;
375 let ent_bytes =
376 std::slice::from_raw_parts(ent_bytes_ptr as *const u8, __wasi_dirent_t_size);
377 let dirent_copy_len = write_buf.len().min(__wasi_dirent_t_size);
378 write_buf[..dirent_copy_len].copy_from_slice(&ent_bytes[..dirent_copy_len]);
379 if dirent_copy_len < __wasi_dirent_t_size {
380 return dirent_copy_len;
381 }
382
383 let name_bytes = entity.name.as_bytes();
384 let name_len = name_bytes.len();
385 let name_copy_len = (write_buf.len() - dirent_copy_len).min(name_len);
386 write_buf[dirent_copy_len..dirent_copy_len + name_copy_len]
387 .copy_from_slice(&name_bytes[..name_copy_len]);
388
389 dirent_copy_len + name_copy_len
390 }
391 }
392
393 let buflen = write_buf.len();
394
395 let mut bufused = 0;
396 let mut next = cursor as u64;
397
398 for (name, inode, filetype) in self.get_readdir(next)? {
399 next += 1;
400 let entity = ReaddirEntity {
401 next,
402 inode,
403 name,
404 filetype,
405 };
406
407 let n = write_dirent(&entity, &mut write_buf[bufused..]);
408 bufused += n;
409 if bufused == buflen {
410 return Ok(bufused);
411 }
412 }
413
414 Ok(bufused)
415 }
416}
417
418pub trait WasiFileSys {
419 type Index: Sized;
420
421 fn path_open(
422 &mut self,
423 dir_ino: Self::Index,
424 path: &str,
425 oflags: OFlags,
426 fs_rights_base: WASIRights,
427 fs_rights_inheriting: WASIRights,
428 fdflags: FdFlags,
429 ) -> Result<Self::Index, Errno>;
430 fn path_rename(
431 &mut self,
432 old_dir: Self::Index,
433 old_path: &str,
434 new_dir: Self::Index,
435 new_path: &str,
436 ) -> Result<(), Errno>;
437 fn path_create_directory(&mut self, dir_ino: Self::Index, path: &str) -> Result<(), Errno>;
438 fn path_remove_directory(&mut self, dir_ino: Self::Index, path: &str) -> Result<(), Errno>;
439 fn path_unlink_file(&mut self, dir_ino: Self::Index, path: &str) -> Result<(), Errno>;
440 fn path_link_file(
441 &mut self,
442 old_dir: Self::Index,
443 old_path: &str,
444 new_dir: Self::Index,
445 new_path: &str,
446 ) -> Result<(), Errno>;
447 fn path_filestat_get(
448 &self,
449 dir_ino: Self::Index,
450 path: &str,
451 follow_symlinks: bool,
452 ) -> Result<Filestat, Errno>;
453
454 fn fclose(&mut self, ino: Self::Index) -> Result<(), Errno> {
455 Ok(())
456 }
457
458 fn get_mut_inode(&mut self, ino: usize) -> Result<&mut dyn WasiNode, Errno>;
459 fn get_inode(&self, ino: usize) -> Result<&dyn WasiNode, Errno>;
460
461 fn get_mut_file(&mut self, ino: usize) -> Result<&mut dyn WasiFile, Errno>;
462 fn get_file(&self, ino: usize) -> Result<&dyn WasiFile, Errno>;
463
464 fn get_mut_dir(&mut self, ino: usize) -> Result<&mut dyn WasiDir, Errno>;
465 fn get_dir(&self, ino: usize) -> Result<&dyn WasiDir, Errno>;
466}