async_wasi/snapshots/common/vfs/
mod.rs

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, // this is a read field, the rest are file fields
67    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; // 0b1
121        const DSYNC    = wasi_types::__wasi_fdflags_t::__WASI_FDFLAGS_DSYNC; // 0b10
122        const NONBLOCK = wasi_types::__wasi_fdflags_t::__WASI_FDFLAGS_NONBLOCK; // 0b100
123        const RSYNC    = wasi_types::__wasi_fdflags_t::__WASI_FDFLAGS_RSYNC; // 0b1000
124        const SYNC     = wasi_types::__wasi_fdflags_t::__WASI_FDFLAGS_SYNC; // 0b10000
125    }
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}