async_wasi/snapshots/common/vfs/
virtual_sys.rs

1use std::{
2    io::{Read, Seek, Write},
3    path::{Path, PathBuf},
4    time::Duration,
5};
6
7use futures::future::ok;
8use libc::hostent;
9use slab::Slab;
10
11use crate::snapshots::env::{wasi_types, Errno};
12
13use super::{
14    Advice, FdFlags, FdStat, FileType, Filestat, OFlags, SystemTimeSpec, WASIRights, WasiDir,
15    WasiFile, WasiFileSys, WasiNode,
16};
17
18pub trait WasiVirtualDir: WasiDir {
19    fn create(ino: usize) -> Self;
20    fn add_sub_dir<P: AsRef<Path>>(&mut self, path: &P, ino: usize) -> Result<(), Errno>;
21    fn remove_sub_dir<P: AsRef<Path>>(&mut self, path: &P) -> Result<(), Errno>;
22
23    fn link_inode<P: AsRef<Path>>(&mut self, path: &P, ino: usize) -> Result<(), Errno>;
24    fn unlink_inode<P: AsRef<Path>>(&mut self, path: &P) -> Result<(), Errno>;
25    fn find_inode<P: AsRef<Path>>(&self, path: &P) -> Option<usize>;
26    fn is_empty(&self) -> bool;
27
28    fn is_open(&self) -> bool;
29    fn open(&mut self);
30    fn close(&mut self) -> usize;
31    fn mark_remove(&mut self);
32}
33
34pub trait WasiVirtualFile: WasiFile {
35    fn create(ino: usize) -> Self;
36
37    fn set_ino(&mut self, ino: usize);
38
39    fn inc_link(&mut self) -> Result<usize, Errno>;
40
41    fn dec_link(&mut self) -> Result<usize, Errno>;
42
43    fn is_open(&self) -> bool;
44    fn open(&mut self);
45    fn close(&mut self) -> usize;
46}
47
48pub enum Inode<D: WasiVirtualDir, F: WasiVirtualFile> {
49    Dir(D),
50    File(F),
51}
52
53impl<D: WasiVirtualDir, F: WasiVirtualFile> WasiNode for Inode<D, F> {
54    fn fd_fdstat_get(&self) -> Result<FdStat, Errno> {
55        match self {
56            Inode::Dir(dir) => dir.fd_fdstat_get(),
57            Inode::File(file) => file.fd_fdstat_get(),
58        }
59    }
60
61    fn fd_fdstat_set_flags(&mut self, flags: FdFlags) -> Result<(), Errno> {
62        match self {
63            Inode::Dir(dir) => dir.fd_fdstat_set_flags(flags),
64            Inode::File(file) => file.fd_fdstat_set_flags(flags),
65        }
66    }
67
68    fn fd_fdstat_set_rights(
69        &mut self,
70        fs_rights_base: WASIRights,
71        fs_rights_inheriting: WASIRights,
72    ) -> Result<(), Errno> {
73        match self {
74            Inode::Dir(dir) => dir.fd_fdstat_set_rights(fs_rights_base, fs_rights_inheriting),
75            Inode::File(file) => file.fd_fdstat_set_rights(fs_rights_base, fs_rights_inheriting),
76        }
77    }
78
79    fn fd_filestat_get(&self) -> Result<Filestat, Errno> {
80        match self {
81            Inode::Dir(dir) => dir.fd_filestat_get(),
82            Inode::File(file) => file.fd_filestat_get(),
83        }
84    }
85
86    fn fd_filestat_set_size(&mut self, size: wasi_types::__wasi_filesize_t) -> Result<(), Errno> {
87        match self {
88            Inode::Dir(dir) => dir.fd_filestat_set_size(size),
89            Inode::File(file) => file.fd_filestat_set_size(size),
90        }
91    }
92
93    fn fd_filestat_set_times(
94        &mut self,
95        atim: wasi_types::__wasi_timestamp_t,
96        mtim: wasi_types::__wasi_timestamp_t,
97        fst_flags: wasi_types::__wasi_fstflags_t::Type,
98    ) -> Result<(), Errno> {
99        match self {
100            Inode::Dir(dir) => dir.fd_filestat_set_times(atim, mtim, fst_flags),
101            Inode::File(file) => file.fd_filestat_set_times(atim, mtim, fst_flags),
102        }
103    }
104}
105
106// VFS
107pub struct WasiVirtualSys<D: WasiVirtualDir, F: WasiVirtualFile> {
108    inodes: slab::Slab<Inode<D, F>>,
109    dir_rights: WASIRights,
110    file_rights: WASIRights,
111}
112
113impl<D: WasiVirtualDir, F: WasiVirtualFile> Default for WasiVirtualSys<D, F> {
114    fn default() -> Self {
115        Self::new()
116    }
117}
118
119impl<D: WasiVirtualDir, F: WasiVirtualFile> WasiVirtualSys<D, F> {
120    pub fn new() -> Self {
121        let mut inodes = Slab::new();
122        inodes.insert(Inode::Dir(D::create(0)));
123        Self {
124            inodes,
125            dir_rights: WASIRights::dir_all(),
126            file_rights: WASIRights::fd_all(),
127        }
128    }
129
130    pub fn create_file<P: AsRef<Path>>(
131        &mut self,
132        dir_ino: usize,
133        path: &P,
134        mut new_file: F,
135    ) -> Result<usize, Errno> {
136        new_file.inc_link();
137        let new_ino = self.inodes.vacant_key();
138        new_file.set_ino(new_ino);
139        let new_ino = self.inodes.insert(Inode::File(new_file));
140
141        if let Some(Inode::Dir(dir)) = self.inodes.get_mut(dir_ino) {
142            let r = dir.link_inode(path, new_ino);
143            if r.is_err() {
144                self.inodes.remove(new_ino);
145            }
146            r?;
147            Ok(new_ino)
148        } else {
149            self.inodes.remove(new_ino);
150            Err(Errno::__WASI_ERRNO_NOTDIR)
151        }
152    }
153
154    pub fn find_inode_index<P: AsRef<Path>>(
155        &self,
156        dir_ino: usize,
157        path: &P,
158    ) -> Result<usize, Errno> {
159        let mut ino = dir_ino;
160        let path = path.as_ref();
161        let path_iter = path.iter();
162        for entry in path.iter() {
163            let entry = entry.to_str().ok_or(Errno::__WASI_ERRNO_ILSEQ)?;
164            log::trace!("WasiVirtualSys find_inode_index {ino} {entry}");
165
166            if let Inode::Dir(dir) = self.inodes.get(ino).ok_or(Errno::__WASI_ERRNO_NOENT)? {
167                ino = dir.find_inode(&entry).ok_or(Errno::__WASI_ERRNO_NOENT)?;
168            } else {
169                return Err(Errno::__WASI_ERRNO_NOTDIR);
170            }
171        }
172        log::trace!("WasiVirtualSys find_inode_index return {ino}");
173        Ok(ino)
174    }
175
176    pub fn create_file_inode<P: AsRef<Path>>(
177        &mut self,
178        dir_ino: usize,
179        path: &P,
180    ) -> Result<usize, Errno> {
181        let mut new_file = F::create(self.inodes.vacant_key());
182        new_file.inc_link();
183        let new_ino = self.inodes.insert(Inode::File(new_file));
184
185        if let Some(Inode::Dir(dir)) = self.inodes.get_mut(dir_ino) {
186            let r = dir.link_inode(path, new_ino);
187            if r.is_err() {
188                self.inodes.remove(new_ino);
189            }
190            r?;
191            Ok(new_ino)
192        } else {
193            self.inodes.remove(new_ino);
194            Err(Errno::__WASI_ERRNO_NOTDIR)
195        }
196    }
197
198    pub fn create_dir_inode<P: AsRef<Path>>(
199        &mut self,
200        dir_ino: usize,
201        path: &P,
202    ) -> Result<usize, Errno> {
203        let mut new_dir = D::create(self.inodes.vacant_key());
204        new_dir.add_sub_dir(&"..", dir_ino);
205        let new_ino = self.inodes.insert(Inode::Dir(new_dir));
206
207        if let Some(Inode::Dir(dir)) = self.inodes.get_mut(dir_ino) {
208            let r = dir.add_sub_dir(path, new_ino);
209            if r.is_err() {
210                self.inodes.remove(new_ino);
211            }
212            r?;
213            Ok(new_ino)
214        } else {
215            self.inodes.remove(new_ino);
216            Err(Errno::__WASI_ERRNO_NOTDIR)
217        }
218    }
219}
220
221impl<D: WasiVirtualDir, F: WasiVirtualFile> WasiFileSys for WasiVirtualSys<D, F> {
222    type Index = usize;
223
224    fn path_open(
225        &mut self,
226        dir_ino: Self::Index,
227        path: &str,
228        oflags: OFlags,
229        fs_rights_base: WASIRights,
230        fs_rights_inheriting: WASIRights,
231        fdflags: FdFlags,
232    ) -> Result<usize, Errno> {
233        let path: &Path = path.as_ref();
234
235        log::trace!("WasiVirtualSys path_open {oflags:?} {path:?} {dir_ino}");
236
237        if fdflags.intersects(FdFlags::DSYNC | FdFlags::SYNC | FdFlags::RSYNC) {
238            return Err(Errno::__WASI_ERRNO_NOSYS);
239        }
240
241        if oflags.intersects(OFlags::DIRECTORY)
242            && (oflags.contains(OFlags::CREATE)
243                || oflags.contains(OFlags::EXCLUSIVE)
244                || oflags.contains(OFlags::TRUNCATE))
245        {
246            return Err(Errno::__WASI_ERRNO_INVAL);
247        }
248
249        let read = fs_rights_base.contains(WASIRights::FD_READ);
250        let write = fs_rights_base.contains(WASIRights::FD_WRITE);
251
252        let inode = self.find_inode_index(dir_ino, &path);
253        match inode {
254            Ok(ino) => match self.inodes.get_mut(ino).ok_or(Errno::__WASI_ERRNO_NOENT)? {
255                Inode::Dir(dir) => {
256                    dir.open();
257                    Ok(ino)
258                }
259                Inode::File(file) => {
260                    if oflags.intersects(OFlags::DIRECTORY) {
261                        return Err(Errno::__WASI_ERRNO_NOTDIR);
262                    }
263
264                    if oflags.intersects(OFlags::CREATE | OFlags::EXCLUSIVE) {
265                        return Err(Errno::__WASI_ERRNO_EXIST);
266                    }
267
268                    file.open();
269
270                    Ok(ino)
271                }
272            },
273            Err(e) => {
274                if oflags.intersects(OFlags::DIRECTORY) {
275                    return Err(e);
276                }
277
278                if oflags.intersects(OFlags::CREATE) {
279                    let parent = match path.parent() {
280                        Some(p) => self.find_inode_index(dir_ino, &p)?,
281                        None => dir_ino,
282                    };
283
284                    let ino = self.create_file_inode(
285                        parent,
286                        &path.file_name().ok_or(Errno::__WASI_ERRNO_INVAL)?,
287                    )?;
288                    if let Some(Inode::File(file)) = self.inodes.get_mut(ino) {
289                        file.open();
290                    }
291
292                    return Ok(ino);
293                }
294
295                Err(Errno::__WASI_ERRNO_EXIST)
296            }
297        }
298    }
299
300    fn path_rename(
301        &mut self,
302        old_dir: usize,
303        old_path: &str,
304        new_dir: usize,
305        new_path: &str,
306    ) -> Result<(), Errno> {
307        self.path_link_file(old_dir, old_path, new_dir, new_path)?;
308        self.path_unlink_file(old_dir, old_path)?;
309        Ok(())
310    }
311
312    fn path_create_directory(&mut self, dir_ino: Self::Index, path: &str) -> Result<(), Errno> {
313        let path: &Path = path.as_ref();
314        self.dir_rights.can(WASIRights::PATH_CREATE_DIRECTORY)?;
315
316        let mut ino = dir_ino;
317        let path_iter = path.iter();
318        for entry in path.iter() {
319            let entry = entry.to_str().ok_or(Errno::__WASI_ERRNO_ILSEQ)?;
320
321            match self.inodes.get(ino) {
322                Some(Inode::Dir(dir)) => {
323                    if let Some(sub_ino) = dir.find_inode(&entry) {
324                        ino = sub_ino;
325                    } else {
326                        ino = self.create_dir_inode(ino, &entry)?;
327                    }
328                }
329                Some(Inode::File(_)) => {
330                    return Err(Errno::__WASI_ERRNO_NOTDIR);
331                }
332                None => {
333                    return Err(Errno::__WASI_ERRNO_NOENT);
334                }
335            }
336        }
337        Ok(())
338    }
339
340    fn fclose(&mut self, ino: Self::Index) -> Result<(), Errno> {
341        let i = match self.inodes.get_mut(ino).ok_or(Errno::__WASI_ERRNO_BADF)? {
342            Inode::Dir(dir) => dir.close(),
343            Inode::File(file) => file.close(),
344        };
345        log::trace!("WasiVirtualSys path_open {ino} close_r={i}");
346        if i == 0 {
347            self.inodes.remove(ino);
348        }
349        Ok(())
350    }
351
352    fn path_remove_directory(&mut self, dir_ino: Self::Index, path: &str) -> Result<(), Errno> {
353        self.dir_rights.can(WASIRights::PATH_REMOVE_DIRECTORY)?;
354        let inode = self.find_inode_index(dir_ino, &path)?;
355        if let (Inode::Dir(dir), Inode::Dir(parent_dir)) = self
356            .inodes
357            .get2_mut(inode, dir_ino)
358            .ok_or(Errno::__WASI_ERRNO_NOENT)?
359        {
360            if dir.is_empty() {
361                parent_dir.remove_sub_dir(&path)?;
362
363                if !dir.is_open() {
364                    self.inodes.remove(inode);
365                } else {
366                    dir.mark_remove();
367                }
368
369                Ok(())
370            } else {
371                Err(Errno::__WASI_ERRNO_NOTEMPTY)
372            }
373        } else {
374            Err(Errno::__WASI_ERRNO_NOTDIR)
375        }
376    }
377
378    fn path_unlink_file(&mut self, dir_ino: Self::Index, path: &str) -> Result<(), Errno> {
379        log::trace!("WasiVirtualSys path_unlink_file {dir_ino} {path}");
380        self.dir_rights.can(WASIRights::PATH_UNLINK_FILE)?;
381
382        let path: &Path = path.as_ref();
383        let parent_dir_ino = if let Some(parent) = path.parent() {
384            self.find_inode_index(dir_ino, &parent)?
385        } else {
386            dir_ino
387        };
388
389        let file_name = path
390            .file_name()
391            .ok_or(Errno::__WASI_ERRNO_INVAL)?
392            .to_str()
393            .ok_or(Errno::__WASI_ERRNO_ILSEQ)?;
394
395        let file_ino = if let Inode::Dir(dir) = self
396            .inodes
397            .get_mut(parent_dir_ino)
398            .ok_or(Errno::__WASI_ERRNO_BADF)?
399        {
400            let file_ino = dir
401                .find_inode(&file_name)
402                .ok_or(Errno::__WASI_ERRNO_NOENT)?;
403            dir.unlink_inode(&file_name)?;
404            file_ino
405        } else {
406            return Err(Errno::__WASI_ERRNO_NOTDIR);
407        };
408
409        if let Inode::File(file) = self
410            .inodes
411            .get_mut(file_ino)
412            .ok_or(Errno::__WASI_ERRNO_BADF)?
413        {
414            let link = file.dec_link()?;
415            log::trace!("WasiVirtualSys path_unlink_file {file_ino} nlink = {link}");
416
417            if link == 0 && !file.is_open() {
418                self.inodes.try_remove(file_ino);
419            }
420            Ok(())
421        } else {
422            Err(Errno::__WASI_ERRNO_ISDIR)
423        }
424    }
425
426    fn path_link_file(
427        &mut self,
428        old_dir: usize,
429        old_path: &str,
430        new_dir: usize,
431        new_path: &str,
432    ) -> Result<(), Errno> {
433        log::trace!("WasiVirtualSys path_link_file ({old_dir} {old_path})  ({new_dir} {new_path})");
434
435        let old_inode = self.find_inode_index(old_dir, &old_path)?;
436
437        let new_path: &Path = new_path.as_ref();
438        let parent_dir_ino = if let Some(parent) = new_path.parent() {
439            self.find_inode_index(new_dir, &parent)?
440        } else {
441            new_dir
442        };
443
444        let file_name = new_path
445            .file_name()
446            .ok_or(Errno::__WASI_ERRNO_INVAL)?
447            .to_str()
448            .ok_or(Errno::__WASI_ERRNO_ILSEQ)?;
449
450        if let Inode::Dir(dir) = self
451            .inodes
452            .get_mut(parent_dir_ino)
453            .ok_or(Errno::__WASI_ERRNO_BADF)?
454        {
455            dir.link_inode(&file_name, old_inode)?;
456        } else {
457            return Err(Errno::__WASI_ERRNO_NOTDIR);
458        };
459
460        if let Inode::File(file) = self
461            .inodes
462            .get_mut(old_inode)
463            .ok_or(Errno::__WASI_ERRNO_BADF)?
464        {
465            let nlink = file.inc_link()?;
466            log::trace!("WasiVirtualSys path_link_file {old_inode} nlink = {nlink}");
467        } else {
468            return Err(Errno::__WASI_ERRNO_ISDIR);
469        };
470
471        Ok(())
472    }
473
474    fn path_filestat_get(
475        &self,
476        dir_ino: Self::Index,
477        path: &str,
478        follow_symlinks: bool,
479    ) -> Result<Filestat, Errno> {
480        let path: &Path = path.as_ref();
481
482        self.dir_rights.can(WASIRights::PATH_FILESTAT_GET)?;
483        let inode = self.find_inode_index(dir_ino, &path)?;
484        self.inodes
485            .get(inode)
486            .ok_or(Errno::__WASI_ERRNO_NOENT)?
487            .fd_filestat_get()
488    }
489
490    fn get_mut_inode(&mut self, ino: usize) -> Result<&mut dyn WasiNode, Errno> {
491        Ok(self.inodes.get_mut(ino).ok_or(Errno::__WASI_ERRNO_BADF)?)
492    }
493
494    fn get_inode(&self, ino: usize) -> Result<&dyn WasiNode, Errno> {
495        Ok(self.inodes.get(ino).ok_or(Errno::__WASI_ERRNO_BADF)?)
496    }
497
498    fn get_mut_file(&mut self, ino: usize) -> Result<&mut dyn WasiFile, Errno> {
499        if let Inode::File(f) = self.inodes.get_mut(ino).ok_or(Errno::__WASI_ERRNO_BADF)? {
500            Ok(f)
501        } else {
502            Err(Errno::__WASI_ERRNO_ISDIR)
503        }
504    }
505
506    fn get_file(&self, ino: usize) -> Result<&dyn WasiFile, Errno> {
507        if let Inode::File(f) = self.inodes.get(ino).ok_or(Errno::__WASI_ERRNO_BADF)? {
508            Ok(f)
509        } else {
510            Err(Errno::__WASI_ERRNO_ISDIR)
511        }
512    }
513
514    fn get_mut_dir(&mut self, ino: usize) -> Result<&mut dyn WasiDir, Errno> {
515        if let Inode::Dir(dir) = self.inodes.get_mut(ino).ok_or(Errno::__WASI_ERRNO_BADF)? {
516            Ok(dir)
517        } else {
518            Err(Errno::__WASI_ERRNO_NOTDIR)
519        }
520    }
521
522    fn get_dir(&self, ino: usize) -> Result<&dyn WasiDir, Errno> {
523        if let Inode::Dir(dir) = self.inodes.get(ino).ok_or(Errno::__WASI_ERRNO_BADF)? {
524            Ok(dir)
525        } else {
526            Err(Errno::__WASI_ERRNO_NOTDIR)
527        }
528    }
529}
530
531// Real Disk
532
533fn get_file_ino(metadata: &std::fs::Metadata) -> u64 {
534    #[cfg(unix)]
535    {
536        use std::os::unix::prelude::MetadataExt;
537        metadata.ino()
538    }
539    #[cfg(not(unix))]
540    {
541        0
542    }
543}
544
545fn get_file_nlink(metadata: &std::fs::Metadata) -> u64 {
546    #[cfg(unix)]
547    {
548        use std::os::unix::prelude::MetadataExt;
549        metadata.nlink()
550    }
551    #[cfg(not(unix))]
552    {
553        1
554    }
555}
556
557fn systimespec(
558    set: bool,
559    ts: wasi_types::__wasi_timestamp_t,
560    now: bool,
561) -> Result<Option<SystemTimeSpec>, Errno> {
562    if set && now {
563        Err(Errno::__WASI_ERRNO_INVAL)
564    } else if set {
565        Ok(Some(SystemTimeSpec::Absolute(Duration::from_nanos(ts))))
566    } else if now {
567        Ok(Some(SystemTimeSpec::SymbolicNow))
568    } else {
569        Ok(None)
570    }
571}
572
573#[derive(Debug)]
574pub struct DiskDir {
575    // absolutize
576    pub real_path: PathBuf,
577    pub dir_rights: WASIRights,
578    pub file_rights: WASIRights,
579}
580
581impl DiskDir {
582    pub fn get_absolutize_path<P: AsRef<Path>>(&self, sub_path: &P) -> Result<PathBuf, Errno> {
583        use path_absolutize::*;
584        let new_path = self.real_path.join(sub_path);
585        let absolutize = new_path
586            .absolutize_virtually(&self.real_path)
587            .or(Err(Errno::__WASI_ERRNO_NOENT))?;
588        Ok(absolutize.to_path_buf())
589    }
590}
591
592impl WasiNode for DiskDir {
593    fn fd_fdstat_get(&self) -> Result<FdStat, Errno> {
594        Ok(FdStat {
595            filetype: FileType::DIRECTORY,
596            fs_rights_base: self.dir_rights.clone(),
597            fs_rights_inheriting: self.file_rights.clone(),
598            flags: FdFlags::empty(),
599        })
600    }
601
602    fn fd_fdstat_set_flags(&mut self, flags: FdFlags) -> Result<(), Errno> {
603        Err(Errno::__WASI_ERRNO_BADF)
604    }
605
606    fn fd_fdstat_set_rights(
607        &mut self,
608        fs_rights_base: WASIRights,
609        fs_rights_inheriting: WASIRights,
610    ) -> Result<(), Errno> {
611        self.dir_rights.can(fs_rights_base.clone())?;
612        self.file_rights.can(fs_rights_inheriting.clone())?;
613
614        self.dir_rights = fs_rights_base;
615        self.file_rights = fs_rights_inheriting;
616
617        Ok(())
618    }
619
620    fn fd_filestat_get(&self) -> Result<Filestat, Errno> {
621        self.dir_rights.can(WASIRights::FD_FILESTAT_GET)?;
622        let meta = std::fs::metadata(&self.real_path)?;
623        let filetype = if meta.is_symlink() {
624            FileType::SYMBOLIC_LINK
625        } else {
626            FileType::DIRECTORY
627        };
628
629        let nlink = get_file_nlink(&meta);
630        let inode = get_file_ino(&meta);
631
632        Ok(Filestat {
633            filetype,
634            nlink,
635            inode,
636            size: meta.len(),
637            atim: meta.accessed().ok(),
638            mtim: meta.modified().ok(),
639            ctim: meta.created().ok(),
640        })
641    }
642
643    fn fd_filestat_set_size(&mut self, size: wasi_types::__wasi_filesize_t) -> Result<(), Errno> {
644        Err(Errno::__WASI_ERRNO_BADF)
645    }
646
647    fn fd_filestat_set_times(
648        &mut self,
649        atim: wasi_types::__wasi_timestamp_t,
650        mtim: wasi_types::__wasi_timestamp_t,
651        fst_flags: wasi_types::__wasi_fstflags_t::Type,
652    ) -> Result<(), Errno> {
653        use wasi_types::__wasi_fstflags_t;
654        self.dir_rights.can(WASIRights::FD_FILESTAT_SET_TIMES)?;
655        Err(Errno::__WASI_ERRNO_NOSYS)
656    }
657}
658
659impl WasiDir for DiskDir {
660    fn get_readdir(&self, mut index: u64) -> Result<Vec<(String, u64, FileType)>, Errno> {
661        self.dir_rights.can(WASIRights::FD_READDIR)?;
662
663        let mut dirs = vec![];
664        if index == 0 {
665            let dir_meta = std::fs::metadata(&self.real_path)?;
666            let dir_ino = get_file_ino(&dir_meta);
667            dirs.push((".".to_string(), dir_ino, FileType::DIRECTORY));
668            index += 1;
669        }
670
671        if index == 1 {
672            let dir_ino = if let Some(parent) = self.real_path.parent() {
673                let dir_meta = std::fs::metadata(parent)?;
674                get_file_ino(&dir_meta)
675            } else {
676                0
677            };
678            dirs.push(("..".to_string(), dir_ino, FileType::DIRECTORY));
679            index += 1;
680        }
681
682        let read_dir = self.real_path.read_dir()?;
683
684        for dir_entity in read_dir.into_iter().skip((index - 2) as usize) {
685            let dir_entity = dir_entity?;
686            let name = dir_entity
687                .file_name()
688                .into_string()
689                .map_err(|_| Errno::__WASI_ERRNO_ILSEQ)?;
690            let metadata = dir_entity.metadata()?;
691            let inode = get_file_ino(&metadata);
692
693            let filetype = if metadata.is_dir() {
694                FileType::DIRECTORY
695            } else if metadata.is_symlink() {
696                FileType::SYMBOLIC_LINK
697            } else {
698                FileType::REGULAR_FILE
699            };
700
701            dirs.push((name, inode, filetype));
702        }
703
704        Ok(dirs)
705    }
706}
707
708#[derive(Debug)]
709pub struct DiskFile {
710    pub fd: std::fs::File,
711    pub flags: FdFlags,
712    pub right: WASIRights,
713}
714
715impl WasiNode for DiskFile {
716    fn fd_fdstat_get(&self) -> Result<FdStat, Errno> {
717        let meta = self.fd.metadata()?;
718        let fd_flags = FdStat {
719            filetype: if meta.is_symlink() {
720                FileType::SYMBOLIC_LINK
721            } else {
722                FileType::REGULAR_FILE
723            },
724            fs_rights_base: self.right.clone(),
725            fs_rights_inheriting: WASIRights::empty(),
726            flags: self.flags.clone(),
727        };
728        Ok(fd_flags)
729    }
730
731    fn fd_fdstat_set_flags(&mut self, flags: FdFlags) -> Result<(), Errno> {
732        self.right.can(WASIRights::FD_FDSTAT_SET_FLAGS)?;
733        if flags.contains(FdFlags::NONBLOCK)
734            && flags.intersects(FdFlags::DSYNC | FdFlags::SYNC | FdFlags::RSYNC)
735        {
736            return Err(Errno::__WASI_ERRNO_INVAL);
737        }
738        if flags.contains(FdFlags::APPEND) {
739            return Err(Errno::__WASI_ERRNO_NOSYS);
740        }
741        self.flags = flags;
742        Ok(())
743    }
744
745    fn fd_fdstat_set_rights(
746        &mut self,
747        fs_rights_base: WASIRights,
748        _fs_rights_inheriting: WASIRights,
749    ) -> Result<(), Errno> {
750        self.right.can(fs_rights_base.clone())?;
751        self.right = fs_rights_base;
752        Ok(())
753    }
754
755    fn fd_filestat_get(&self) -> Result<Filestat, Errno> {
756        self.right.can(WASIRights::FD_FILESTAT_GET)?;
757        let meta = self.fd.metadata()?;
758        let filetype = if meta.is_symlink() {
759            FileType::SYMBOLIC_LINK
760        } else {
761            FileType::REGULAR_FILE
762        };
763
764        let nlink = get_file_nlink(&meta);
765        let inode = get_file_ino(&meta);
766
767        Ok(Filestat {
768            filetype,
769            nlink,
770            inode,
771            size: meta.len(),
772            atim: meta.accessed().ok(),
773            mtim: meta.modified().ok(),
774            ctim: meta.created().ok(),
775        })
776    }
777
778    fn fd_filestat_set_size(&mut self, size: wasi_types::__wasi_filesize_t) -> Result<(), Errno> {
779        self.right.can(WASIRights::FD_FILESTAT_SET_SIZE)?;
780        self.fd.set_len(size)?;
781        Ok(())
782    }
783
784    fn fd_filestat_set_times(
785        &mut self,
786        atim: wasi_types::__wasi_timestamp_t,
787        mtim: wasi_types::__wasi_timestamp_t,
788        fst_flags: wasi_types::__wasi_fstflags_t::Type,
789    ) -> Result<(), Errno> {
790        use wasi_types::__wasi_fstflags_t;
791
792        self.right.can(WASIRights::FD_FILESTAT_SET_TIMES)?;
793
794        let set_atim = (fst_flags & __wasi_fstflags_t::__WASI_FSTFLAGS_ATIM) > 0;
795        let set_atim_now = (fst_flags & __wasi_fstflags_t::__WASI_FSTFLAGS_ATIM_NOW) > 0;
796        let set_mtim = (fst_flags & __wasi_fstflags_t::__WASI_FSTFLAGS_MTIM) > 0;
797        let set_mtim_now = (fst_flags & __wasi_fstflags_t::__WASI_FSTFLAGS_MTIM_NOW) > 0;
798
799        let atim = systimespec(set_atim, atim, set_atim_now)?;
800        let mtim = systimespec(set_mtim, mtim, set_mtim_now)?;
801
802        #[cfg(unix)]
803        {
804            use std::os::unix::prelude::AsRawFd;
805            let fd = self.fd.as_raw_fd();
806            let times = [
807                {
808                    match atim {
809                        Some(SystemTimeSpec::Absolute(atim)) => libc::timespec {
810                            tv_sec: atim.as_secs() as i64,
811                            tv_nsec: atim.subsec_nanos() as i64,
812                        },
813                        Some(SystemTimeSpec::SymbolicNow) => libc::timespec {
814                            tv_sec: 0,
815                            tv_nsec: libc::UTIME_NOW,
816                        },
817                        None => libc::timespec {
818                            tv_sec: 0,
819                            tv_nsec: libc::UTIME_OMIT,
820                        },
821                    }
822                },
823                {
824                    match mtim {
825                        Some(SystemTimeSpec::Absolute(mtim)) => libc::timespec {
826                            tv_sec: mtim.as_secs() as i64,
827                            tv_nsec: mtim.subsec_nanos() as i64,
828                        },
829                        Some(SystemTimeSpec::SymbolicNow) => libc::timespec {
830                            tv_sec: 0,
831                            tv_nsec: libc::UTIME_NOW,
832                        },
833                        None => libc::timespec {
834                            tv_sec: 0,
835                            tv_nsec: libc::UTIME_OMIT,
836                        },
837                    }
838                },
839            ];
840            if unsafe { libc::futimens(fd, times.as_ptr()) } < 0 {
841                Err(std::io::Error::last_os_error())?;
842            }
843            Ok(())
844        }
845        #[cfg(not(unix))]
846        {
847            Err(Errno::__WASI_ERRNO_NOSYS)
848        }
849    }
850}
851
852impl WasiFile for DiskFile {
853    fn fd_advise(
854        &mut self,
855        offset: wasi_types::__wasi_filesize_t,
856        len: wasi_types::__wasi_filesize_t,
857        advice: Advice,
858    ) -> Result<(), Errno> {
859        Ok(())
860    }
861
862    fn fd_allocate(
863        &mut self,
864        offset: wasi_types::__wasi_filesize_t,
865        len: wasi_types::__wasi_filesize_t,
866    ) -> Result<(), Errno> {
867        self.right.can(WASIRights::FD_ALLOCATE)?;
868        let f = &mut self.fd;
869        let metadata = f.metadata()?;
870        let file_len = metadata.len();
871        let new_len = offset + len;
872        if new_len > file_len {
873            let old_seek = f.stream_position()?;
874            f.set_len(new_len)?;
875            f.seek(std::io::SeekFrom::Start(old_seek))?;
876        }
877        Ok(())
878    }
879
880    fn fd_datasync(&mut self) -> Result<(), Errno> {
881        self.right.can(WASIRights::FD_DATASYNC)?;
882        self.fd.sync_data()?;
883        Ok(())
884    }
885
886    fn fd_sync(&mut self) -> Result<(), Errno> {
887        self.right.can(WASIRights::FD_SYNC)?;
888        self.fd.sync_all()?;
889        Ok(())
890    }
891
892    fn fd_read(&mut self, bufs: &mut [std::io::IoSliceMut<'_>]) -> Result<usize, Errno> {
893        self.right.can(WASIRights::FD_READ)?;
894        Ok(self.fd.read_vectored(bufs)?)
895    }
896
897    fn fd_pread(
898        &mut self,
899        bufs: &mut [std::io::IoSliceMut<'_>],
900        offset: wasi_types::__wasi_filesize_t,
901    ) -> Result<usize, Errno> {
902        use std::io::SeekFrom;
903
904        self.right.can(WASIRights::FD_READ | WASIRights::FD_SEEK)?;
905
906        let old_seek = self.fd.stream_position()?;
907        self.fd.seek(SeekFrom::Start(offset))?;
908        let r = self.fd.read_vectored(bufs);
909        self.fd.seek(SeekFrom::Start(old_seek))?;
910        Ok(r?)
911    }
912
913    fn fd_write(&mut self, bufs: &[std::io::IoSlice<'_>]) -> Result<usize, Errno> {
914        self.right.can(WASIRights::FD_WRITE)?;
915        Ok(self.fd.write_vectored(bufs)?)
916    }
917
918    fn fd_pwrite(
919        &mut self,
920        bufs: &[std::io::IoSlice<'_>],
921        offset: wasi_types::__wasi_filesize_t,
922    ) -> Result<usize, Errno> {
923        use std::io::SeekFrom;
924
925        self.right.can(WASIRights::FD_WRITE | WASIRights::FD_SEEK)?;
926
927        let old_seek = self.fd.stream_position()?;
928        self.fd.seek(SeekFrom::Start(offset))?;
929        let r = self.fd.write_vectored(bufs);
930        self.fd.seek(SeekFrom::Start(old_seek))?;
931        Ok(r?)
932    }
933
934    fn fd_seek(
935        &mut self,
936        offset: wasi_types::__wasi_filedelta_t,
937        whence: wasi_types::__wasi_whence_t::Type,
938    ) -> Result<wasi_types::__wasi_filesize_t, Errno> {
939        use std::io::SeekFrom;
940
941        let required_rigth =
942            if offset == 0 && whence == wasi_types::__wasi_whence_t::__WASI_WHENCE_CUR {
943                WASIRights::FD_TELL
944            } else {
945                WASIRights::FD_TELL | WASIRights::FD_SEEK
946            };
947
948        self.right.can(required_rigth)?;
949
950        let pos = match whence {
951            wasi_types::__wasi_whence_t::__WASI_WHENCE_CUR => SeekFrom::Current(offset),
952            wasi_types::__wasi_whence_t::__WASI_WHENCE_END => SeekFrom::End(offset),
953            wasi_types::__wasi_whence_t::__WASI_WHENCE_SET => SeekFrom::Start(offset as u64),
954            _ => return Err(Errno::__WASI_ERRNO_INVAL),
955        };
956
957        Ok(self.fd.seek(pos)?)
958    }
959
960    fn fd_tell(&mut self) -> Result<wasi_types::__wasi_filesize_t, Errno> {
961        use std::io::SeekFrom;
962        self.right.can(WASIRights::FD_TELL)?;
963        Ok(self.fd.stream_position()?)
964    }
965}
966
967pub enum DiskInode {
968    Dir(DiskDir),
969    File(DiskFile),
970}
971
972impl WasiNode for DiskInode {
973    fn fd_fdstat_get(&self) -> Result<FdStat, Errno> {
974        match self {
975            DiskInode::Dir(inode) => inode.fd_fdstat_get(),
976            DiskInode::File(inode) => inode.fd_fdstat_get(),
977        }
978    }
979
980    fn fd_fdstat_set_flags(&mut self, flags: FdFlags) -> Result<(), Errno> {
981        match self {
982            DiskInode::Dir(inode) => inode.fd_fdstat_set_flags(flags),
983            DiskInode::File(inode) => inode.fd_fdstat_set_flags(flags),
984        }
985    }
986
987    fn fd_fdstat_set_rights(
988        &mut self,
989        fs_rights_base: WASIRights,
990        fs_rights_inheriting: WASIRights,
991    ) -> Result<(), Errno> {
992        match self {
993            DiskInode::Dir(inode) => {
994                inode.fd_fdstat_set_rights(fs_rights_base, fs_rights_inheriting)
995            }
996            DiskInode::File(inode) => {
997                inode.fd_fdstat_set_rights(fs_rights_base, fs_rights_inheriting)
998            }
999        }
1000    }
1001
1002    fn fd_filestat_get(&self) -> Result<Filestat, Errno> {
1003        match self {
1004            DiskInode::Dir(inode) => inode.fd_filestat_get(),
1005            DiskInode::File(inode) => inode.fd_filestat_get(),
1006        }
1007    }
1008
1009    fn fd_filestat_set_size(&mut self, size: wasi_types::__wasi_filesize_t) -> Result<(), Errno> {
1010        match self {
1011            DiskInode::Dir(inode) => inode.fd_filestat_set_size(size),
1012            DiskInode::File(inode) => inode.fd_filestat_set_size(size),
1013        }
1014    }
1015
1016    fn fd_filestat_set_times(
1017        &mut self,
1018        atim: wasi_types::__wasi_timestamp_t,
1019        mtim: wasi_types::__wasi_timestamp_t,
1020        fst_flags: wasi_types::__wasi_fstflags_t::Type,
1021    ) -> Result<(), Errno> {
1022        match self {
1023            DiskInode::Dir(inode) => inode.fd_filestat_set_times(atim, mtim, fst_flags),
1024            DiskInode::File(inode) => inode.fd_filestat_set_times(atim, mtim, fst_flags),
1025        }
1026    }
1027}
1028
1029pub struct DiskFileSys {
1030    real_path: PathBuf,
1031    inodes: slab::Slab<DiskInode>,
1032    dir_rights: WASIRights,
1033    file_rights: WASIRights,
1034}
1035
1036impl DiskFileSys {
1037    pub fn new(host_path: PathBuf) -> std::io::Result<Self> {
1038        let host_path = host_path.canonicalize()?;
1039        let mut inodes = Slab::new();
1040
1041        inodes.insert(DiskInode::Dir(DiskDir {
1042            real_path: host_path.clone(),
1043            dir_rights: WASIRights::dir_all(),
1044            file_rights: WASIRights::fd_all(),
1045        }));
1046
1047        Ok(DiskFileSys {
1048            inodes,
1049            real_path: host_path,
1050            dir_rights: WASIRights::dir_all(),
1051            file_rights: WASIRights::fd_all(),
1052        })
1053    }
1054
1055    pub fn get_absolutize_path<P: AsRef<Path>>(&self, sub_path: &P) -> Result<PathBuf, Errno> {
1056        use path_absolutize::*;
1057        let new_path = self.real_path.join(sub_path);
1058        let absolutize = new_path
1059            .absolutize_virtually(&self.real_path)
1060            .or(Err(Errno::__WASI_ERRNO_NOENT))?;
1061        Ok(absolutize.to_path_buf())
1062    }
1063}
1064
1065impl WasiFileSys for DiskFileSys {
1066    type Index = usize;
1067
1068    fn path_open(
1069        &mut self,
1070        dir_ino: Self::Index,
1071        path: &str,
1072        oflags: OFlags,
1073        fs_rights_base: WASIRights,
1074        fs_rights_inheriting: WASIRights,
1075        fdflags: FdFlags,
1076    ) -> Result<Self::Index, Errno> {
1077        if fdflags.intersects(FdFlags::DSYNC | FdFlags::SYNC | FdFlags::RSYNC) {
1078            return Err(Errno::__WASI_ERRNO_NOSYS);
1079        }
1080
1081        if oflags.intersects(OFlags::DIRECTORY)
1082            && (oflags.contains(OFlags::CREATE)
1083                || oflags.contains(OFlags::EXCLUSIVE)
1084                || oflags.contains(OFlags::TRUNCATE))
1085        {
1086            return Err(Errno::__WASI_ERRNO_INVAL);
1087        }
1088
1089        let parent_dir = match self.inodes.get(dir_ino).ok_or(Errno::__WASI_ERRNO_BADF)? {
1090            DiskInode::Dir(dir) => dir,
1091            _ => return Err(Errno::__WASI_ERRNO_NOTDIR),
1092        };
1093
1094        let path = parent_dir.get_absolutize_path(&path)?;
1095        if path == self.real_path {
1096            return Ok(0);
1097        }
1098
1099        log::trace!("DiskFileSys path_open({path:?},{oflags:?})");
1100
1101        let path_meta = std::fs::metadata(&path).ok();
1102        match path_meta {
1103            Some(meta) if meta.is_dir() => {
1104                let dir_rights = self.dir_rights.clone() & fs_rights_base;
1105                let file_rights = self.file_rights.clone() & fs_rights_inheriting;
1106                let ino = self.inodes.insert(DiskInode::Dir(DiskDir {
1107                    real_path: path,
1108                    dir_rights,
1109                    file_rights,
1110                }));
1111                return Ok(ino);
1112            }
1113            _ => {
1114                if oflags.contains(OFlags::DIRECTORY) {
1115                    return Err(Errno::__WASI_ERRNO_NOTDIR);
1116                }
1117            }
1118        }
1119
1120        let read = fs_rights_base.contains(WASIRights::FD_READ);
1121        let write = fs_rights_base.contains(WASIRights::FD_WRITE);
1122
1123        let mut opts = std::fs::OpenOptions::new();
1124
1125        if oflags.contains(OFlags::CREATE | OFlags::EXCLUSIVE) {
1126            opts.create_new(true);
1127            opts.write(true);
1128        } else if oflags.contains(OFlags::CREATE) {
1129            opts.create(true);
1130            opts.write(true);
1131        }
1132
1133        if oflags.contains(OFlags::TRUNCATE) {
1134            opts.truncate(true);
1135        }
1136        if read {
1137            opts.read(true);
1138        }
1139
1140        if write {
1141            opts.write(true);
1142        } else {
1143            opts.read(true);
1144        }
1145
1146        if fdflags.contains(FdFlags::APPEND) {
1147            opts.append(true);
1148        }
1149
1150        let fd = opts.open(path)?;
1151
1152        let ino = self.inodes.insert(DiskInode::File(DiskFile {
1153            fd,
1154            flags: fdflags,
1155            right: fs_rights_base,
1156        }));
1157
1158        Ok(ino)
1159    }
1160
1161    fn path_rename(
1162        &mut self,
1163        old_dir: usize,
1164        old_path: &str,
1165        new_dir: usize,
1166        new_path: &str,
1167    ) -> Result<(), Errno> {
1168        let old_parent_dir = match self.inodes.get(old_dir).ok_or(Errno::__WASI_ERRNO_BADF)? {
1169            DiskInode::Dir(dir) => dir,
1170            _ => return Err(Errno::__WASI_ERRNO_NOTDIR),
1171        };
1172
1173        let old_path = old_parent_dir.get_absolutize_path(&old_path)?;
1174
1175        let new_parent_dir = match self.inodes.get(new_dir).ok_or(Errno::__WASI_ERRNO_BADF)? {
1176            DiskInode::Dir(dir) => dir,
1177            _ => return Err(Errno::__WASI_ERRNO_NOTDIR),
1178        };
1179        let new_path = new_parent_dir.get_absolutize_path(&new_path)?;
1180
1181        Ok(std::fs::rename(old_path, new_path)?)
1182    }
1183
1184    fn path_create_directory(&mut self, dir_ino: Self::Index, path: &str) -> Result<(), Errno> {
1185        self.dir_rights.can(WASIRights::PATH_CREATE_DIRECTORY)?;
1186        let parent_dir = match self.inodes.get(dir_ino).ok_or(Errno::__WASI_ERRNO_BADF)? {
1187            DiskInode::Dir(dir) => dir,
1188            _ => return Err(Errno::__WASI_ERRNO_NOTDIR),
1189        };
1190        let new_path = parent_dir.get_absolutize_path(&path)?;
1191        std::fs::DirBuilder::new()
1192            .recursive(true)
1193            .create(new_path)?;
1194        Ok(())
1195    }
1196
1197    fn path_remove_directory(&mut self, dir_ino: Self::Index, path: &str) -> Result<(), Errno> {
1198        self.dir_rights.can(WASIRights::PATH_REMOVE_DIRECTORY)?;
1199        let parent_dir = match self.inodes.get(dir_ino).ok_or(Errno::__WASI_ERRNO_BADF)? {
1200            DiskInode::Dir(dir) => dir,
1201            _ => return Err(Errno::__WASI_ERRNO_NOTDIR),
1202        };
1203        let new_path = parent_dir.get_absolutize_path(&path)?;
1204        log::trace!("DiskFileSys path_remove_directory {new_path:?}");
1205        std::fs::remove_dir(new_path)?;
1206        Ok(())
1207    }
1208
1209    fn path_unlink_file(&mut self, dir_ino: Self::Index, path: &str) -> Result<(), Errno> {
1210        self.dir_rights.can(WASIRights::PATH_REMOVE_DIRECTORY)?;
1211        let parent_dir = match self.inodes.get(dir_ino).ok_or(Errno::__WASI_ERRNO_BADF)? {
1212            DiskInode::Dir(dir) => dir,
1213            _ => return Err(Errno::__WASI_ERRNO_NOTDIR),
1214        };
1215        let new_path = parent_dir.get_absolutize_path(&path)?;
1216        std::fs::remove_file(new_path)?;
1217        Ok(())
1218    }
1219
1220    fn path_link_file(
1221        &mut self,
1222        old_dir: Self::Index,
1223        old_path: &str,
1224        new_dir: Self::Index,
1225        new_path: &str,
1226    ) -> Result<(), Errno> {
1227        Err(Errno::__WASI_ERRNO_NOSYS)
1228    }
1229
1230    fn path_filestat_get(
1231        &self,
1232        dir_ino: Self::Index,
1233        path: &str,
1234        follow_symlinks: bool,
1235    ) -> Result<Filestat, Errno> {
1236        self.dir_rights.can(WASIRights::PATH_FILESTAT_GET)?;
1237
1238        let parent_dir = match self.inodes.get(dir_ino).ok_or(Errno::__WASI_ERRNO_BADF)? {
1239            DiskInode::Dir(dir) => dir,
1240            _ => return Err(Errno::__WASI_ERRNO_NOTDIR),
1241        };
1242        let new_path = parent_dir.get_absolutize_path(&path)?;
1243
1244        let meta = if follow_symlinks {
1245            std::fs::metadata(new_path)?
1246        } else {
1247            std::fs::symlink_metadata(new_path)?
1248        };
1249
1250        let filetype = if meta.is_symlink() {
1251            FileType::SYMBOLIC_LINK
1252        } else if meta.is_dir() {
1253            FileType::DIRECTORY
1254        } else {
1255            FileType::REGULAR_FILE
1256        };
1257
1258        let nlink = get_file_nlink(&meta);
1259        let inode = get_file_ino(&meta);
1260
1261        Ok(Filestat {
1262            filetype,
1263            inode,
1264            nlink,
1265            size: meta.len(),
1266            atim: meta.accessed().ok(),
1267            mtim: meta.modified().ok(),
1268            ctim: meta.created().ok(),
1269        })
1270    }
1271
1272    fn fclose(&mut self, ino: Self::Index) -> Result<(), Errno> {
1273        self.inodes.try_remove(ino);
1274        Ok(())
1275    }
1276
1277    fn get_mut_inode(&mut self, ino: usize) -> Result<&mut dyn WasiNode, Errno> {
1278        Ok(self.inodes.get_mut(ino).ok_or(Errno::__WASI_ERRNO_BADF)?)
1279    }
1280
1281    fn get_inode(&self, ino: usize) -> Result<&dyn WasiNode, Errno> {
1282        Ok(self.inodes.get(ino).ok_or(Errno::__WASI_ERRNO_BADF)?)
1283    }
1284
1285    fn get_mut_file(&mut self, ino: usize) -> Result<&mut dyn WasiFile, Errno> {
1286        match self.inodes.get_mut(ino).ok_or(Errno::__WASI_ERRNO_BADF)? {
1287            DiskInode::File(f) => Ok(f),
1288            _ => Err(Errno::__WASI_ERRNO_ISDIR),
1289        }
1290    }
1291
1292    fn get_file(&self, ino: usize) -> Result<&dyn WasiFile, Errno> {
1293        match self.inodes.get(ino).ok_or(Errno::__WASI_ERRNO_BADF)? {
1294            DiskInode::File(f) => Ok(f),
1295            _ => Err(Errno::__WASI_ERRNO_ISDIR),
1296        }
1297    }
1298
1299    fn get_mut_dir(&mut self, ino: usize) -> Result<&mut dyn WasiDir, Errno> {
1300        match self.inodes.get_mut(ino).ok_or(Errno::__WASI_ERRNO_BADF)? {
1301            DiskInode::Dir(dir) => Ok(dir),
1302            _ => Err(Errno::__WASI_ERRNO_NOTDIR),
1303        }
1304    }
1305
1306    fn get_dir(&self, ino: usize) -> Result<&dyn WasiDir, Errno> {
1307        match self.inodes.get(ino).ok_or(Errno::__WASI_ERRNO_BADF)? {
1308            DiskInode::Dir(dir) => Ok(dir),
1309            _ => Err(Errno::__WASI_ERRNO_NOTDIR),
1310        }
1311    }
1312}
1313
1314// pipeline
1315pub struct OutPipeline<W: Write>(W);
1316impl<W: Write> From<W> for OutPipeline<W> {
1317    fn from(value: W) -> Self {
1318        Self(value)
1319    }
1320}
1321impl<W: Write> WasiNode for OutPipeline<W> {
1322    fn fd_fdstat_get(&self) -> Result<FdStat, Errno> {
1323        Ok(FdStat {
1324            filetype: FileType::CHARACTER_DEVICE,
1325            fs_rights_base: WASIRights::FD_WRITE | WASIRights::POLL_FD_READWRITE,
1326            fs_rights_inheriting: WASIRights::empty(),
1327            flags: FdFlags::APPEND,
1328        })
1329    }
1330
1331    fn fd_fdstat_set_flags(&mut self, flags: FdFlags) -> Result<(), Errno> {
1332        Err(Errno::__WASI_ERRNO_BADF)
1333    }
1334
1335    fn fd_fdstat_set_rights(
1336        &mut self,
1337        fs_rights_base: WASIRights,
1338        _fs_rights_inheriting: WASIRights,
1339    ) -> Result<(), Errno> {
1340        Err(Errno::__WASI_ERRNO_BADF)
1341    }
1342
1343    fn fd_filestat_get(&self) -> Result<Filestat, Errno> {
1344        Ok(Filestat {
1345            filetype: FileType::CHARACTER_DEVICE,
1346            nlink: 0,
1347            inode: 0,
1348            size: 0,
1349            atim: None,
1350            mtim: None,
1351            ctim: None,
1352        })
1353    }
1354
1355    fn fd_filestat_set_size(&mut self, size: wasi_types::__wasi_filesize_t) -> Result<(), Errno> {
1356        Err(Errno::__WASI_ERRNO_BADF)
1357    }
1358
1359    fn fd_filestat_set_times(
1360        &mut self,
1361        atim: wasi_types::__wasi_timestamp_t,
1362        mtim: wasi_types::__wasi_timestamp_t,
1363        fst_flags: wasi_types::__wasi_fstflags_t::Type,
1364    ) -> Result<(), Errno> {
1365        Err(Errno::__WASI_ERRNO_BADF)
1366    }
1367}
1368impl<W: Write> WasiFile for OutPipeline<W> {
1369    fn fd_advise(
1370        &mut self,
1371        offset: wasi_types::__wasi_filesize_t,
1372        len: wasi_types::__wasi_filesize_t,
1373        advice: Advice,
1374    ) -> Result<(), Errno> {
1375        Ok(())
1376    }
1377
1378    fn fd_allocate(
1379        &mut self,
1380        offset: wasi_types::__wasi_filesize_t,
1381        len: wasi_types::__wasi_filesize_t,
1382    ) -> Result<(), Errno> {
1383        Err(Errno::__WASI_ERRNO_BADF)
1384    }
1385
1386    fn fd_datasync(&mut self) -> Result<(), Errno> {
1387        self.0.flush()?;
1388        Ok(())
1389    }
1390
1391    fn fd_sync(&mut self) -> Result<(), Errno> {
1392        self.0.flush()?;
1393        Ok(())
1394    }
1395
1396    fn fd_read(&mut self, bufs: &mut [std::io::IoSliceMut<'_>]) -> Result<usize, Errno> {
1397        Err(Errno::__WASI_ERRNO_BADF)
1398    }
1399
1400    fn fd_pread(
1401        &mut self,
1402        bufs: &mut [std::io::IoSliceMut<'_>],
1403        offset: wasi_types::__wasi_filesize_t,
1404    ) -> Result<usize, Errno> {
1405        Err(Errno::__WASI_ERRNO_BADF)
1406    }
1407
1408    fn fd_write(&mut self, bufs: &[std::io::IoSlice<'_>]) -> Result<usize, Errno> {
1409        Ok(self.0.write_vectored(bufs)?)
1410    }
1411
1412    fn fd_pwrite(
1413        &mut self,
1414        bufs: &[std::io::IoSlice<'_>],
1415        offset: wasi_types::__wasi_filesize_t,
1416    ) -> Result<usize, Errno> {
1417        Err(Errno::__WASI_ERRNO_SPIPE)
1418    }
1419
1420    fn fd_seek(
1421        &mut self,
1422        offset: wasi_types::__wasi_filedelta_t,
1423        whence: wasi_types::__wasi_whence_t::Type,
1424    ) -> Result<wasi_types::__wasi_filesize_t, Errno> {
1425        Err(Errno::__WASI_ERRNO_SPIPE)
1426    }
1427
1428    fn fd_tell(&mut self) -> Result<wasi_types::__wasi_filesize_t, Errno> {
1429        Err(Errno::__WASI_ERRNO_SPIPE)
1430    }
1431}
1432
1433pub struct InPipline<R: Read>(R);
1434impl<R: Read> From<R> for InPipline<R> {
1435    fn from(value: R) -> Self {
1436        Self(value)
1437    }
1438}
1439impl<R: Read> WasiNode for InPipline<R> {
1440    fn fd_fdstat_get(&self) -> Result<FdStat, Errno> {
1441        Ok(FdStat {
1442            filetype: FileType::CHARACTER_DEVICE,
1443            fs_rights_base: WASIRights::FD_READ | WASIRights::POLL_FD_READWRITE,
1444            fs_rights_inheriting: WASIRights::empty(),
1445            flags: FdFlags::empty(),
1446        })
1447    }
1448
1449    fn fd_fdstat_set_flags(&mut self, flags: FdFlags) -> Result<(), Errno> {
1450        Err(Errno::__WASI_ERRNO_BADF)
1451    }
1452
1453    fn fd_fdstat_set_rights(
1454        &mut self,
1455        fs_rights_base: WASIRights,
1456        _fs_rights_inheriting: WASIRights,
1457    ) -> Result<(), Errno> {
1458        Err(Errno::__WASI_ERRNO_BADF)
1459    }
1460
1461    fn fd_filestat_get(&self) -> Result<Filestat, Errno> {
1462        Ok(Filestat {
1463            filetype: FileType::CHARACTER_DEVICE,
1464            nlink: 0,
1465            inode: 0,
1466            size: 0,
1467            atim: None,
1468            mtim: None,
1469            ctim: None,
1470        })
1471    }
1472
1473    fn fd_filestat_set_size(&mut self, size: wasi_types::__wasi_filesize_t) -> Result<(), Errno> {
1474        Err(Errno::__WASI_ERRNO_BADF)
1475    }
1476
1477    fn fd_filestat_set_times(
1478        &mut self,
1479        atim: wasi_types::__wasi_timestamp_t,
1480        mtim: wasi_types::__wasi_timestamp_t,
1481        fst_flags: wasi_types::__wasi_fstflags_t::Type,
1482    ) -> Result<(), Errno> {
1483        Err(Errno::__WASI_ERRNO_BADF)
1484    }
1485}
1486impl<R: Read> WasiFile for InPipline<R> {
1487    fn fd_advise(
1488        &mut self,
1489        offset: wasi_types::__wasi_filesize_t,
1490        len: wasi_types::__wasi_filesize_t,
1491        advice: Advice,
1492    ) -> Result<(), Errno> {
1493        Ok(())
1494    }
1495
1496    fn fd_allocate(
1497        &mut self,
1498        offset: wasi_types::__wasi_filesize_t,
1499        len: wasi_types::__wasi_filesize_t,
1500    ) -> Result<(), Errno> {
1501        Err(Errno::__WASI_ERRNO_BADF)
1502    }
1503
1504    fn fd_datasync(&mut self) -> Result<(), Errno> {
1505        Ok(())
1506    }
1507
1508    fn fd_sync(&mut self) -> Result<(), Errno> {
1509        Ok(())
1510    }
1511
1512    fn fd_read(&mut self, bufs: &mut [std::io::IoSliceMut<'_>]) -> Result<usize, Errno> {
1513        Ok(self.0.read_vectored(bufs)?)
1514    }
1515
1516    fn fd_pread(
1517        &mut self,
1518        bufs: &mut [std::io::IoSliceMut<'_>],
1519        offset: wasi_types::__wasi_filesize_t,
1520    ) -> Result<usize, Errno> {
1521        Err(Errno::__WASI_ERRNO_SPIPE)
1522    }
1523
1524    fn fd_write(&mut self, bufs: &[std::io::IoSlice<'_>]) -> Result<usize, Errno> {
1525        Err(Errno::__WASI_ERRNO_BADF)
1526    }
1527
1528    fn fd_pwrite(
1529        &mut self,
1530        bufs: &[std::io::IoSlice<'_>],
1531        offset: wasi_types::__wasi_filesize_t,
1532    ) -> Result<usize, Errno> {
1533        Err(Errno::__WASI_ERRNO_BADF)
1534    }
1535
1536    fn fd_seek(
1537        &mut self,
1538        offset: wasi_types::__wasi_filedelta_t,
1539        whence: wasi_types::__wasi_whence_t::Type,
1540    ) -> Result<wasi_types::__wasi_filesize_t, Errno> {
1541        Err(Errno::__WASI_ERRNO_SPIPE)
1542    }
1543
1544    fn fd_tell(&mut self) -> Result<wasi_types::__wasi_filesize_t, Errno> {
1545        Err(Errno::__WASI_ERRNO_SPIPE)
1546    }
1547}
1548
1549pub struct StdioSys<IN, OUT, ERR>
1550where
1551    IN: std::io::Read,
1552    OUT: std::io::Write,
1553    ERR: std::io::Write,
1554{
1555    stdin: InPipline<IN>,
1556    stdout: OutPipeline<OUT>,
1557    stderr: OutPipeline<ERR>,
1558}
1559
1560impl<IN, OUT, ERR> StdioSys<IN, OUT, ERR>
1561where
1562    IN: std::io::Read,
1563    OUT: std::io::Write,
1564    ERR: std::io::Write,
1565{
1566    pub fn new(stdin: IN, stdout: OUT, stderr: ERR) -> Self {
1567        Self {
1568            stdin: InPipline(stdin),
1569            stdout: OutPipeline(stdout),
1570            stderr: OutPipeline(stderr),
1571        }
1572    }
1573}
1574
1575impl<IN, OUT, ERR> WasiFileSys for StdioSys<IN, OUT, ERR>
1576where
1577    IN: std::io::Read,
1578    OUT: std::io::Write,
1579    ERR: std::io::Write,
1580{
1581    type Index = usize;
1582
1583    fn path_open(
1584        &mut self,
1585        dir_ino: usize,
1586        path: &str,
1587        oflags: OFlags,
1588        fs_rights_base: WASIRights,
1589        fs_rights_inheriting: WASIRights,
1590        fdflags: FdFlags,
1591    ) -> Result<Self::Index, Errno> {
1592        Err(Errno::__WASI_ERRNO_BADF)
1593    }
1594
1595    fn path_rename(
1596        &mut self,
1597        old_dir: usize,
1598        old_path: &str,
1599        new_dir: usize,
1600        new_path: &str,
1601    ) -> Result<(), Errno> {
1602        Err(Errno::__WASI_ERRNO_BADF)
1603    }
1604
1605    fn path_create_directory(&mut self, dir_ino: usize, path: &str) -> Result<(), Errno> {
1606        Err(Errno::__WASI_ERRNO_BADF)
1607    }
1608
1609    fn path_remove_directory(&mut self, dir_ino: usize, path: &str) -> Result<(), Errno> {
1610        Err(Errno::__WASI_ERRNO_BADF)
1611    }
1612
1613    fn path_unlink_file(&mut self, dir_ino: Self::Index, path: &str) -> Result<(), Errno> {
1614        Err(Errno::__WASI_ERRNO_BADF)
1615    }
1616
1617    fn path_link_file(
1618        &mut self,
1619        old_dir: Self::Index,
1620        old_path: &str,
1621        new_dir: Self::Index,
1622        new_path: &str,
1623    ) -> Result<(), Errno> {
1624        Err(Errno::__WASI_ERRNO_BADF)
1625    }
1626
1627    fn path_filestat_get(
1628        &self,
1629        dir_ino: usize,
1630        path: &str,
1631        follow_symlinks: bool,
1632    ) -> Result<Filestat, Errno> {
1633        Err(Errno::__WASI_ERRNO_BADF)
1634    }
1635
1636    fn get_mut_inode(&mut self, ino: usize) -> Result<&mut dyn WasiNode, Errno> {
1637        match ino {
1638            0 => Ok(&mut self.stdin),
1639            1 => Ok(&mut self.stdout),
1640            2 => Ok(&mut self.stderr),
1641            _ => Err(Errno::__WASI_ERRNO_BADF),
1642        }
1643    }
1644
1645    fn get_inode(&self, ino: usize) -> Result<&dyn WasiNode, Errno> {
1646        match ino {
1647            0 => Ok(&self.stdin),
1648            1 => Ok(&self.stdout),
1649            2 => Ok(&self.stderr),
1650            _ => Err(Errno::__WASI_ERRNO_BADF),
1651        }
1652    }
1653
1654    fn get_mut_file(&mut self, ino: usize) -> Result<&mut dyn WasiFile, Errno> {
1655        match ino {
1656            0 => Ok(&mut self.stdin),
1657            1 => Ok(&mut self.stdout),
1658            2 => Ok(&mut self.stderr),
1659            _ => Err(Errno::__WASI_ERRNO_BADF),
1660        }
1661    }
1662
1663    fn get_file(&self, ino: usize) -> Result<&dyn WasiFile, Errno> {
1664        match ino {
1665            0 => Ok(&self.stdin),
1666            1 => Ok(&self.stdout),
1667            2 => Ok(&self.stderr),
1668            _ => Err(Errno::__WASI_ERRNO_BADF),
1669        }
1670    }
1671
1672    fn get_mut_dir(&mut self, ino: usize) -> Result<&mut dyn WasiDir, Errno> {
1673        Err(Errno::__WASI_ERRNO_NOTDIR)
1674    }
1675
1676    fn get_dir(&self, ino: usize) -> Result<&dyn WasiDir, Errno> {
1677        Err(Errno::__WASI_ERRNO_NOTDIR)
1678    }
1679}