wasi_vfs/
wasi_snapshot_preview1.rs

1/// In this module, we can't use WASI wrappers provided by `wasi` crate
2/// because some types used in their parameters are defined as structs and
3/// their constructor is private. It wouldn't be a problem if the parameters
4/// are derived only from constant definitions (like `CLOCKID_MONOTONIC`), but
5/// we need to map given raw integers to the struct types. So we can't use those
6/// wrapper until they will provide public interface to make them from raw integers.
7use core::slice;
8use std::{
9    ffi::{CStr, OsStr},
10    mem::MaybeUninit,
11    path::Path,
12};
13use wasi::{
14    CiovecArray, Dircookie, Event, Fd, Fdflags, Fdstat, Filedelta, Filesize, Filestat, Fstflags,
15    IovecArray, Lookupflags, Oflags, Prestat, PrestatDir, PrestatU, Rights, Size, Subscription,
16    Timestamp,
17};
18
19use crate::{
20    embed::{Node, NodeDirBody, NodeFileBody, NodeIdTrait, Storage},
21    BackingFd, Error, FileSystem, UserFd,
22};
23
24pub(crate) unsafe fn fd_advise<S: Storage>(
25    fs: &mut FileSystem<S>,
26    fd: UserFd,
27    offset: Filesize,
28    len: Filesize,
29    advice: i32,
30) -> Result<(), Error> {
31    let fd = fs.get_backing_fd(fd)?;
32    match fd {
33        BackingFd::Virtual(vfd) => Err(wasi::ERRNO_NOTSUP.into()),
34        BackingFd::Wasi(fd) => {
35            let ret = wasi::wasi_snapshot_preview1::fd_advise(
36                fd as i32,
37                offset as i64,
38                len as i64,
39                advice as i32,
40            );
41            match ret {
42                0 => Ok(()),
43                _ => Err(Error(ret as u16)),
44            }
45        }
46    }
47}
48
49pub(crate) unsafe fn fd_allocate<S: Storage>(
50    fs: &mut FileSystem<S>,
51    fd: UserFd,
52    offset: Filesize,
53    len: Filesize,
54) -> Result<(), Error> {
55    let fd = fs.get_backing_fd(fd)?;
56    match fd {
57        BackingFd::Virtual(vfd) => Err(wasi::ERRNO_NOTSUP.into()),
58        BackingFd::Wasi(fd) => {
59            let ret =
60                wasi::wasi_snapshot_preview1::fd_allocate(fd as i32, offset as i64, len as i64);
61            match ret {
62                0 => Ok(()),
63                _ => Err(Error(ret as u16)),
64            }
65        }
66    }
67}
68
69pub(crate) unsafe fn fd_close<S: Storage>(fs: &mut FileSystem<S>, fd: UserFd) -> Result<(), Error> {
70    let fd = fs.get_backing_fd(fd)?;
71    match fd {
72        BackingFd::Virtual(vfd) => Ok(fs.embedded_fs.close_file(vfd)?),
73        BackingFd::Wasi(fd) => {
74            let ret = wasi::wasi_snapshot_preview1::fd_close(fd as i32);
75            match ret {
76                0 => Ok(()),
77                _ => Err(Error(ret as u16)),
78            }
79        }
80    }
81}
82
83pub(crate) unsafe fn fd_datasync<S: Storage>(
84    fs: &mut FileSystem<S>,
85    fd: UserFd,
86) -> Result<(), Error> {
87    let fd = fs.get_backing_fd(fd)?;
88    match fd {
89        BackingFd::Virtual(vfd) => Err(wasi::ERRNO_NOTSUP.into()),
90        BackingFd::Wasi(fd) => {
91            let ret = wasi::wasi_snapshot_preview1::fd_datasync(fd as i32);
92            match ret {
93                0 => Ok(()),
94                _ => Err(Error(ret as u16)),
95            }
96        }
97    }
98}
99
100pub(crate) unsafe fn fd_fdstat_get<S: Storage>(
101    fs: &mut FileSystem<S>,
102    fd: UserFd,
103) -> Result<Fdstat, Error> {
104    let fd = fs.get_backing_fd(fd)?;
105    match fd {
106        BackingFd::Virtual(vfd) => {
107            let stat = fs.embedded_fs.get_fd_stat(vfd)?;
108            Ok(stat)
109        }
110        BackingFd::Wasi(fd) => {
111            let mut rp0 = MaybeUninit::<Fdstat>::uninit();
112            let ret =
113                wasi::wasi_snapshot_preview1::fd_fdstat_get(fd as i32, rp0.as_mut_ptr() as i32);
114            match ret {
115                0 => Ok(rp0.assume_init()),
116                _ => Err(Error(ret as u16)),
117            }
118        }
119    }
120}
121
122pub(crate) unsafe fn fd_fdstat_set_flags<S: Storage>(
123    fs: &mut FileSystem<S>,
124    fd: UserFd,
125    flags: Fdflags,
126) -> Result<(), Error> {
127    let fd = fs.get_backing_fd(fd)?;
128    match fd {
129        BackingFd::Virtual(vfd) => {
130            let entry = fs.embedded_fs.get_fd_entry_mut(vfd)?;
131            entry.flags = flags;
132            Ok(())
133        }
134        BackingFd::Wasi(fd) => {
135            let ret = wasi::wasi_snapshot_preview1::fd_fdstat_set_flags(fd as i32, flags as i32);
136            match ret {
137                0 => Ok(()),
138                _ => Err(Error(ret as u16)),
139            }
140        }
141    }
142}
143
144pub(crate) unsafe fn fd_fdstat_set_rights<S: Storage>(
145    fs: &mut FileSystem<S>,
146    fd: UserFd,
147    fs_rights_base: Rights,
148    fs_rights_inheriting: Rights,
149) -> Result<(), Error> {
150    let fd = fs.get_backing_fd(fd)?;
151    match fd {
152        BackingFd::Virtual(vfd) => Err(wasi::ERRNO_NOTSUP.into()),
153        BackingFd::Wasi(fd) => {
154            let ret = wasi::wasi_snapshot_preview1::fd_fdstat_set_rights(
155                fd as i32,
156                fs_rights_base as i64,
157                fs_rights_inheriting as i64,
158            );
159            match ret {
160                0 => Ok(()),
161                _ => Err(Error(ret as u16)),
162            }
163        }
164    }
165}
166
167pub(crate) unsafe fn fd_filestat_get<S: Storage>(
168    fs: &mut FileSystem<S>,
169    fd: UserFd,
170) -> Result<Filestat, Error> {
171    let fd = fs.get_backing_fd(fd)?;
172    match fd {
173        BackingFd::Virtual(vfd) => {
174            let fd_entry = fs.embedded_fs.get_fd_entry(vfd)?;
175            Ok(fs.embedded_fs.get_filestat_from_node_id(fd_entry.node_id))
176        }
177        BackingFd::Wasi(fd) => {
178            let mut rp0 = MaybeUninit::<Filestat>::uninit();
179            let ret =
180                wasi::wasi_snapshot_preview1::fd_filestat_get(fd as i32, rp0.as_mut_ptr() as i32);
181            match ret {
182                0 => Ok(core::ptr::read(rp0.as_mut_ptr() as i32 as *const Filestat)),
183                _ => Err(Error(ret as u16)),
184            }
185        }
186    }
187}
188
189pub(crate) unsafe fn fd_filestat_set_size<S: Storage>(
190    fs: &mut FileSystem<S>,
191    fd: UserFd,
192    size: Filesize,
193) -> Result<(), Error> {
194    let fd = fs.get_backing_fd(fd)?;
195    match fd {
196        BackingFd::Virtual(vfd) => Err(wasi::ERRNO_NOTSUP.into()),
197        BackingFd::Wasi(fd) => {
198            let ret = wasi::wasi_snapshot_preview1::fd_filestat_set_size(fd as i32, size as i64);
199            match ret {
200                0 => Ok(()),
201                _ => Err(Error(ret as u16)),
202            }
203        }
204    }
205}
206
207pub(crate) unsafe fn fd_filestat_set_times<S: Storage>(
208    fs: &mut FileSystem<S>,
209    fd: UserFd,
210    atim: Timestamp,
211    mtim: Timestamp,
212    fst_flags: Fstflags,
213) -> Result<(), Error> {
214    let fd = fs.get_backing_fd(fd)?;
215    match fd {
216        BackingFd::Virtual(vfd) => Err(wasi::ERRNO_NOTSUP.into()),
217        BackingFd::Wasi(fd) => {
218            let ret = wasi::wasi_snapshot_preview1::fd_filestat_set_times(
219                fd as i32,
220                atim as i64,
221                mtim as i64,
222                fst_flags as i32,
223            );
224            match ret {
225                0 => Ok(()),
226                _ => Err(Error(ret as u16)),
227            }
228        }
229    }
230}
231
232pub(crate) unsafe fn fd_pread<S: Storage>(
233    fs: &mut FileSystem<S>,
234    fd: UserFd,
235    iovs: IovecArray<'_>,
236    offset: Filesize,
237) -> Result<Size, Error> {
238    let fd = fs.get_backing_fd(fd)?;
239    match fd {
240        BackingFd::Virtual(vfd) => Err(wasi::ERRNO_NOTSUP.into()),
241        BackingFd::Wasi(fd) => {
242            let mut rp0 = MaybeUninit::<Size>::uninit();
243            let ret = wasi::wasi_snapshot_preview1::fd_pread(
244                fd as i32,
245                iovs.as_ptr() as i32,
246                iovs.len() as i32,
247                offset as i64,
248                rp0.as_mut_ptr() as i32,
249            );
250            match ret {
251                0 => Ok(core::ptr::read(rp0.as_mut_ptr() as i32 as *const Size)),
252                _ => Err(Error(ret as u16)),
253            }
254        }
255    }
256}
257
258pub(crate) unsafe fn fd_prestat_get<S: Storage>(
259    fs: &mut FileSystem<S>,
260    fd: UserFd,
261) -> Result<Prestat, Error> {
262    let fd = fs.get_backing_fd(fd)?;
263    match fd {
264        BackingFd::Virtual(vfd) => {
265            if let Some(dir) = fs.embedded_fs.get_preopened_dir_path(vfd) {
266                let stat = Prestat {
267                    tag: wasi::PREOPENTYPE_DIR.raw(),
268                    u: PrestatU {
269                        dir: PrestatDir {
270                            pr_name_len: dir.as_bytes().len(),
271                        },
272                    },
273                };
274                Ok(stat)
275            } else {
276                Err(wasi::ERRNO_BADF.into())
277            }
278        }
279        BackingFd::Wasi(fd) => {
280            let mut rp0 = MaybeUninit::<Prestat>::uninit();
281            let ret =
282                wasi::wasi_snapshot_preview1::fd_prestat_get(fd as i32, rp0.as_mut_ptr() as i32);
283            match ret {
284                0 => Ok(core::ptr::read(rp0.as_mut_ptr() as i32 as *const Prestat)),
285                _ => Err(Error(ret as u16)),
286            }
287        }
288    }
289}
290
291pub(crate) unsafe fn fd_prestat_dir_name<S: Storage>(
292    fs: &mut FileSystem<S>,
293    fd: UserFd,
294    path: *mut u8,
295    path_len: u32,
296) -> Result<(), Error> {
297    let fd = fs.get_backing_fd(fd)?;
298    match fd {
299        BackingFd::Virtual(vfd) => {
300            if let Some(dir) = fs.embedded_fs.get_preopened_dir_path(vfd) {
301                let path = slice::from_raw_parts_mut(path, path_len as usize);
302                for (offset, byte) in dir.as_bytes().iter().enumerate() {
303                    path[offset] = *byte;
304                }
305                Ok(())
306            } else {
307                Err(wasi::ERRNO_BADF.into())
308            }
309        }
310        BackingFd::Wasi(fd) => {
311            let ret = wasi::wasi_snapshot_preview1::fd_prestat_dir_name(
312                fd as i32,
313                path as i32,
314                path_len as i32,
315            );
316            match ret {
317                0 => Ok(()),
318                _ => Err(Error(ret as u16)),
319            }
320        }
321    }
322}
323
324pub(crate) unsafe fn fd_pwrite<S: Storage>(
325    fs: &mut FileSystem<S>,
326    fd: UserFd,
327    iovs: CiovecArray<'_>,
328    offset: Filesize,
329) -> Result<Size, Error> {
330    let fd = fs.get_backing_fd(fd)?;
331    match fd {
332        BackingFd::Virtual(vfd) => Err(wasi::ERRNO_NOTSUP.into()),
333        BackingFd::Wasi(fd) => {
334            let mut rp0 = MaybeUninit::<Size>::uninit();
335            let ret = wasi::wasi_snapshot_preview1::fd_pwrite(
336                fd as i32,
337                iovs.as_ptr() as i32,
338                iovs.len() as i32,
339                offset as i64,
340                rp0.as_mut_ptr() as i32,
341            );
342            match ret {
343                0 => Ok(core::ptr::read(rp0.as_mut_ptr() as i32 as *const Size)),
344                _ => Err(Error(ret as u16)),
345            }
346        }
347    }
348}
349
350pub(crate) unsafe fn fd_read<S: Storage>(
351    fs: &mut FileSystem<S>,
352    fd: UserFd,
353    iovs: IovecArray<'_>,
354) -> Result<Size, Error> {
355    let fd = fs.get_backing_fd(fd)?;
356    match fd {
357        BackingFd::Virtual(vfd) => {
358            let node = fs.embedded_fs.get_node(vfd)?;
359            match node {
360                Node::Dir { .. } => Err(wasi::ERRNO_ISDIR.into()),
361                Node::File(body) => {
362                    let open = fs.embedded_fs.get_fd_entry(vfd)?;
363                    let mut cursor = std::io::Cursor::new(body.content());
364                    cursor.set_position(open.offset as u64);
365                    let read_bytes = read_bytes(cursor, iovs)?;
366                    let open = fs.embedded_fs.get_fd_entry_mut(vfd)?;
367                    open.offset += read_bytes;
368                    Ok(read_bytes)
369                }
370            }
371        }
372        BackingFd::Wasi(fd) => {
373            let mut rp0 = MaybeUninit::<Size>::uninit();
374            let ret = wasi::wasi_snapshot_preview1::fd_read(
375                fd as i32,
376                iovs.as_ptr() as i32,
377                iovs.len() as i32,
378                rp0.as_mut_ptr() as i32,
379            );
380            match ret {
381                0 => Ok(core::ptr::read(rp0.as_mut_ptr() as i32 as *const Size)),
382                _ => Err(Error(ret as u16)),
383            }
384        }
385    }
386}
387
388pub(crate) unsafe fn fd_readdir<S: Storage>(
389    fs: &mut FileSystem<S>,
390    fd: UserFd,
391    buf: *mut u8,
392    buf_len: u32,
393    cookie: Dircookie,
394) -> Result<Size, Error> {
395    let fd = fs.get_backing_fd(fd)?;
396    match fd {
397        BackingFd::Virtual(vfd) => {
398            let node = fs.embedded_fs.get_node(vfd)?;
399            let entries = match node {
400                Node::Dir(body) => body.entries(),
401                Node::File { .. } => {
402                    return Err(wasi::ERRNO_NOTDIR.into());
403                }
404            };
405            let mut bufused = 0;
406            let mut current_cookie = cookie;
407            let mut buf = buf;
408            let buf_len = buf_len as usize;
409            for entry in entries.skip(cookie as usize) {
410                current_cookie += 1;
411                let name_len = entry.name.len();
412                let node_id = fs.embedded_fs.get_node_id_by_link(entry.link_id);
413                let node_stat = fs.embedded_fs.get_filestat_from_node_id(node_id);
414                let dirent = wasi::Dirent {
415                    d_next: current_cookie,
416                    d_ino: node_id.ino() as u64,
417                    d_namlen: name_len as u32,
418                    d_type: node_stat.filetype,
419                };
420
421                // 1. Copy dirent to the buffer
422                let dirent_len = std::mem::size_of::<wasi::Dirent>();
423                let dirent_copy_len = std::cmp::min(dirent_len, buf_len - bufused);
424                // copy dirent even though the buffer doesn't have enough remaining space
425                std::ptr::copy(&dirent as *const _ as *const u8, buf, dirent_copy_len);
426                // bail out if the remaining buffer space is not enough
427                if dirent_copy_len < dirent_len {
428                    // return the number of bytes stored in the buffer
429                    return Ok(buf_len);
430                }
431                buf = buf.add(dirent_copy_len);
432                bufused += dirent_copy_len;
433
434                // 2. Copy name string to the buffer
435                let name_copy_len = std::cmp::min(name_len, buf_len - bufused);
436                // same truncation rule applied as above
437                std::ptr::copy(entry.name.as_ptr(), buf, name_copy_len);
438
439                if name_copy_len < name_len {
440                    return Ok(buf_len);
441                }
442                buf = buf.add(name_len);
443                bufused += name_copy_len;
444            }
445            Ok(bufused)
446        }
447        BackingFd::Wasi(fd) => {
448            let mut rp0 = MaybeUninit::<Size>::uninit();
449            let ret = wasi::wasi_snapshot_preview1::fd_readdir(
450                fd as i32,
451                buf as i32,
452                buf_len as i32,
453                cookie as i64,
454                rp0.as_mut_ptr() as i32,
455            );
456            match ret {
457                0 => Ok(core::ptr::read(rp0.as_mut_ptr() as i32 as *const Size)),
458                _ => Err(Error(ret as u16)),
459            }
460        }
461    }
462}
463
464pub(crate) unsafe fn fd_renumber<S: Storage>(
465    fs: &mut FileSystem<S>,
466    fd: UserFd,
467    to: UserFd,
468) -> Result<(), Error> {
469    let fd = fs.get_backing_fd(fd)?;
470    let to = fs.get_backing_fd(to)?;
471    match (fd, to) {
472        (BackingFd::Wasi(fd), BackingFd::Wasi(to)) => {
473            let ret = wasi::wasi_snapshot_preview1::fd_renumber(fd as i32, to as i32);
474            match ret {
475                0 => Ok(()),
476                _ => Err(Error(ret as u16)),
477            }
478        }
479        (_, _) => Err(wasi::ERRNO_NOTSUP.into()),
480    }
481}
482
483pub(crate) unsafe fn fd_seek<S: Storage>(
484    fs: &mut FileSystem<S>,
485    fd: UserFd,
486    offset: Filedelta,
487    whence: i32,
488) -> Result<Filesize, Error> {
489    let fd = fs.get_backing_fd(fd)?;
490    fn compute_new_offset(base: usize, offset: Filedelta) -> Result<usize, Error> {
491        let new_offset = if offset >= 0 {
492            base + (offset as usize)
493        } else {
494            let neg_offset = (-offset) as usize;
495            if neg_offset <= base {
496                base - neg_offset
497            } else {
498                return Err(wasi::ERRNO_INVAL.into());
499            }
500        };
501        Ok(new_offset)
502    }
503    match fd {
504        BackingFd::Virtual(vfd) => {
505            let whence: wasi::Whence = std::mem::transmute(whence as u8);
506            match whence {
507                wasi::WHENCE_SET => {
508                    let fd_entry = fs.embedded_fs.get_fd_entry_mut(vfd)?;
509                    fd_entry.offset = offset as usize;
510                    Ok(offset as Filesize)
511                }
512                wasi::WHENCE_CUR => {
513                    let fd_entry = fs.embedded_fs.get_fd_entry_mut(vfd)?;
514                    let absolute_offset = compute_new_offset(fd_entry.offset, offset)?;
515                    fd_entry.offset = absolute_offset;
516                    Ok(absolute_offset as Filesize)
517                }
518                wasi::WHENCE_END => {
519                    let fd_entry = fs.embedded_fs.get_fd_entry(vfd)?;
520                    let node = fs.embedded_fs.get_node(vfd)?;
521                    match node {
522                        Node::File(body) => {
523                            let content_len = body.content().len();
524                            let fd_entry = fs.embedded_fs.get_fd_entry_mut(vfd)?;
525                            let absolute_offset = compute_new_offset(content_len, offset)?;
526                            fd_entry.offset = absolute_offset;
527                            Ok(absolute_offset as Filesize)
528                        }
529                        Node::Dir { .. } => Err(wasi::ERRNO_INVAL.into()),
530                    }
531                }
532                _ => Err(wasi::ERRNO_INVAL.into()),
533            }
534        }
535        BackingFd::Wasi(fd) => {
536            let mut rp0 = MaybeUninit::<Filesize>::uninit();
537            let ret = wasi::wasi_snapshot_preview1::fd_seek(
538                fd as i32,
539                offset,
540                whence as i32,
541                rp0.as_mut_ptr() as i32,
542            );
543            match ret {
544                0 => Ok(core::ptr::read(rp0.as_mut_ptr() as i32 as *const Filesize)),
545                _ => Err(Error(ret as u16)),
546            }
547        }
548    }
549}
550
551pub(crate) unsafe fn fd_sync<S: Storage>(fs: &mut FileSystem<S>, fd: UserFd) -> Result<(), Error> {
552    let fd = fs.get_backing_fd(fd)?;
553    match fd {
554        BackingFd::Virtual(vfd) => Err(wasi::ERRNO_NOTSUP.into()),
555        BackingFd::Wasi(fd) => {
556            let ret = wasi::wasi_snapshot_preview1::fd_sync(fd as i32);
557            match ret {
558                0 => Ok(()),
559                _ => Err(Error(ret as u16)),
560            }
561        }
562    }
563}
564
565pub(crate) unsafe fn fd_tell<S: Storage>(
566    fs: &mut FileSystem<S>,
567    fd: UserFd,
568) -> Result<Filesize, Error> {
569    let fd = fs.get_backing_fd(fd)?;
570    match fd {
571        BackingFd::Virtual(vfd) => {
572            let node = fs.embedded_fs.get_node(vfd)?;
573            let open = fs.embedded_fs.get_fd_entry_mut(vfd)?;
574            Ok(open.offset as u64)
575        }
576        BackingFd::Wasi(fd) => {
577            let mut rp0 = MaybeUninit::<Filesize>::uninit();
578            let ret = wasi::wasi_snapshot_preview1::fd_tell(fd as i32, rp0.as_mut_ptr() as i32);
579            match ret {
580                0 => Ok(core::ptr::read(rp0.as_mut_ptr() as i32 as *const Filesize)),
581                _ => Err(Error(ret as u16)),
582            }
583        }
584    }
585}
586
587pub(crate) unsafe fn fd_write<S: Storage>(
588    fs: &mut FileSystem<S>,
589    fd: UserFd,
590    iovs: CiovecArray<'_>,
591) -> Result<Size, Error> {
592    let fd = fs.get_backing_fd(fd)?;
593    match fd {
594        BackingFd::Virtual(vfd) => Err(wasi::ERRNO_NOTSUP.into()),
595        BackingFd::Wasi(fd) => {
596            let mut rp0 = MaybeUninit::<Size>::uninit();
597            let ret = wasi::wasi_snapshot_preview1::fd_write(
598                fd as i32,
599                iovs.as_ptr() as i32,
600                iovs.len() as i32,
601                rp0.as_mut_ptr() as i32,
602            );
603            match ret {
604                0 => Ok(rp0.assume_init()),
605                _ => Err(Error(ret as u16)),
606            }
607        }
608    }
609}
610
611pub(crate) unsafe fn path_create_directory<S: Storage>(
612    fs: &mut FileSystem<S>,
613    fd: UserFd,
614    path: &CStr,
615) -> Result<(), Error> {
616    let fd = fs.get_backing_fd(fd)?;
617    match fd {
618        BackingFd::Virtual(vfd) => Err(wasi::ERRNO_NOTSUP.into()),
619        BackingFd::Wasi(fd) => {
620            let ret = wasi::wasi_snapshot_preview1::path_create_directory(
621                fd as i32,
622                path.as_ptr() as i32,
623                path.to_bytes().len() as i32,
624            );
625            match ret {
626                0 => Ok(()),
627                _ => Err(Error(ret as u16)),
628            }
629        }
630    }
631}
632
633pub(crate) unsafe fn path_filestat_get<S: Storage>(
634    fs: &mut FileSystem<S>,
635    fd: UserFd,
636    flags: Lookupflags,
637    path: &CStr,
638) -> Result<Filestat, Error> {
639    let fd = fs.get_backing_fd(fd)?;
640    match fd {
641        BackingFd::Virtual(vfd) => {
642            let path = cstr_to_path(path)?;
643            Ok(fs.embedded_fs.get_filestat_at_path(vfd, path)?)
644        }
645        BackingFd::Wasi(fd) => {
646            let mut rp0 = MaybeUninit::<Filestat>::uninit();
647            let ret = wasi::wasi_snapshot_preview1::path_filestat_get(
648                fd as i32,
649                flags as i32,
650                path.as_ptr() as i32,
651                path.to_bytes().len() as i32,
652                rp0.as_mut_ptr() as i32,
653            );
654            match ret {
655                0 => Ok(core::ptr::read(rp0.as_mut_ptr() as i32 as *const Filestat)),
656                _ => Err(Error(ret as u16)),
657            }
658        }
659    }
660}
661
662pub(crate) unsafe fn path_filestat_set_times<S: Storage>(
663    fs: &mut FileSystem<S>,
664    fd: UserFd,
665    flags: Lookupflags,
666    path: &CStr,
667    atim: Timestamp,
668    mtim: Timestamp,
669    fst_flags: Fstflags,
670) -> Result<(), Error> {
671    let fd = fs.get_backing_fd(fd)?;
672    match fd {
673        BackingFd::Virtual(vfd) => Err(wasi::ERRNO_NOTSUP.into()),
674        BackingFd::Wasi(fd) => {
675            let ret = wasi::wasi_snapshot_preview1::path_filestat_set_times(
676                fd as i32,
677                flags as i32,
678                path.as_ptr() as i32,
679                path.to_bytes().len() as i32,
680                atim as i64,
681                mtim as i64,
682                fst_flags as i32,
683            );
684            match ret {
685                0 => Ok(()),
686                _ => Err(Error(ret as u16)),
687            }
688        }
689    }
690}
691
692pub(crate) unsafe fn path_link<S: Storage>(
693    fs: &mut FileSystem<S>,
694    old_fd: UserFd,
695    old_flags: Lookupflags,
696    old_path: &CStr,
697    new_fd: UserFd,
698    new_path: &CStr,
699) -> Result<(), Error> {
700    let old_fd = fs.get_backing_fd(old_fd)?;
701    let new_fd = fs.get_backing_fd(new_fd)?;
702    match (old_fd, new_fd) {
703        (BackingFd::Wasi(old_fd), BackingFd::Wasi(new_fd)) => {
704            let ret = wasi::wasi_snapshot_preview1::path_link(
705                old_fd as i32,
706                old_flags as i32,
707                old_path.as_ptr() as i32,
708                old_path.to_bytes().len() as i32,
709                new_fd as i32,
710                new_path.as_ptr() as i32,
711                new_path.to_bytes().len() as i32,
712            );
713            match ret {
714                0 => Ok(()),
715                _ => Err(Error(ret as u16)),
716            }
717        }
718        (_, _) => Err(wasi::ERRNO_NOTSUP.into()),
719    }
720}
721
722pub(crate) unsafe fn path_open<S: Storage>(
723    fs: &mut FileSystem<S>,
724    fd: UserFd,
725    dirflags: Lookupflags,
726    path: &CStr,
727    oflags: Oflags,
728    fs_rights_base: Rights,
729    fs_rights_inheriting: Rights,
730    fdflags: Fdflags,
731) -> Result<UserFd, Error> {
732    let fd = fs.get_backing_fd(fd)?;
733    match fd {
734        BackingFd::Virtual(vfd) => {
735            let path = cstr_to_path(path)?;
736            let new_vfd = fs.embedded_fs.open_file(vfd, path, fdflags)?;
737            Ok(fs.issue_user_fd(BackingFd::Virtual(new_vfd)))
738        }
739        BackingFd::Wasi(fd) => {
740            let mut rp0 = MaybeUninit::<Fd>::uninit();
741            let ret = wasi::wasi_snapshot_preview1::path_open(
742                fd as i32,
743                dirflags as i32,
744                path.as_ptr() as i32,
745                path.to_bytes().len() as i32,
746                oflags as i32,
747                fs_rights_base as i64,
748                fs_rights_inheriting as i64,
749                fdflags as i32,
750                rp0.as_mut_ptr() as i32,
751            );
752            match ret {
753                0 => {
754                    let new_fd = BackingFd::Wasi(rp0.assume_init());
755                    Ok(fs.issue_user_fd(new_fd))
756                }
757                _ => Err(Error(ret as u16)),
758            }
759        }
760    }
761}
762
763pub(crate) unsafe fn path_readlink<S: Storage>(
764    fs: &mut FileSystem<S>,
765    fd: UserFd,
766    path: &CStr,
767    buf: *mut u8,
768    buf_len: u32,
769) -> Result<Size, Error> {
770    let fd = fs.get_backing_fd(fd)?;
771    match fd {
772        BackingFd::Virtual(vfd) => Err(wasi::ERRNO_INVAL.into()),
773        BackingFd::Wasi(fd) => {
774            let mut rp0 = MaybeUninit::<Size>::uninit();
775            let ret = wasi::wasi_snapshot_preview1::path_readlink(
776                fd as i32,
777                path.as_ptr() as i32,
778                path.to_bytes().len() as i32,
779                buf as i32,
780                buf_len as i32,
781                rp0.as_mut_ptr() as i32,
782            );
783            match ret {
784                0 => Ok(core::ptr::read(rp0.as_mut_ptr() as i32 as *const Size)),
785                _ => Err(Error(ret as u16)),
786            }
787        }
788    }
789}
790
791pub(crate) unsafe fn path_remove_directory<S: Storage>(
792    fs: &mut FileSystem<S>,
793    fd: UserFd,
794    path: &CStr,
795) -> Result<(), Error> {
796    let fd = fs.get_backing_fd(fd)?;
797    match fd {
798        BackingFd::Virtual(vfd) => Err(wasi::ERRNO_NOTSUP.into()),
799        BackingFd::Wasi(fd) => {
800            let ret = wasi::wasi_snapshot_preview1::path_remove_directory(
801                fd as i32,
802                path.as_ptr() as i32,
803                path.to_bytes().len() as i32,
804            );
805            match ret {
806                0 => Ok(()),
807                _ => Err(Error(ret as u16)),
808            }
809        }
810    }
811}
812
813pub(crate) unsafe fn path_rename<S: Storage>(
814    fs: &mut FileSystem<S>,
815    fd: UserFd,
816    old_path: &CStr,
817    new_fd: UserFd,
818    new_path: &CStr,
819) -> Result<(), Error> {
820    let fd = fs.get_backing_fd(fd)?;
821    let new_fd = fs.get_backing_fd(new_fd)?;
822    match (fd, new_fd) {
823        (BackingFd::Wasi(fd), BackingFd::Wasi(new_fd)) => {
824            let ret = wasi::wasi_snapshot_preview1::path_rename(
825                fd as i32,
826                old_path.as_ptr() as i32,
827                old_path.to_bytes().len() as i32,
828                new_fd as i32,
829                new_path.as_ptr() as i32,
830                new_path.to_bytes().len() as i32,
831            );
832            match ret {
833                0 => Ok(()),
834                _ => Err(Error(ret as u16)),
835            }
836        }
837        (_, _) => Err(wasi::ERRNO_NOTSUP.into()),
838    }
839}
840
841pub(crate) unsafe fn path_symlink<S: Storage>(
842    fs: &mut FileSystem<S>,
843    old_path: &CStr,
844    fd: UserFd,
845    new_path: &CStr,
846) -> Result<(), Error> {
847    let fd = fs.get_backing_fd(fd)?;
848    match fd {
849        BackingFd::Virtual(vfd) => Err(wasi::ERRNO_NOTSUP.into()),
850        BackingFd::Wasi(fd) => {
851            let ret = wasi::wasi_snapshot_preview1::path_symlink(
852                old_path.as_ptr() as i32,
853                old_path.to_bytes().len() as i32,
854                fd as i32,
855                new_path.as_ptr() as i32,
856                new_path.to_bytes().len() as i32,
857            );
858            match ret {
859                0 => Ok(()),
860                _ => Err(Error(ret as u16)),
861            }
862        }
863    }
864}
865
866pub(crate) unsafe fn path_unlink_file<S: Storage>(
867    fs: &mut FileSystem<S>,
868    fd: UserFd,
869    path: &CStr,
870) -> Result<(), Error> {
871    let fd = fs.get_backing_fd(fd)?;
872    match fd {
873        BackingFd::Virtual(vfd) => Err(wasi::ERRNO_NOTSUP.into()),
874        BackingFd::Wasi(fd) => {
875            let ret = wasi::wasi_snapshot_preview1::path_unlink_file(
876                fd as i32,
877                path.as_ptr() as i32,
878                path.to_bytes().len() as i32,
879            );
880            match ret {
881                0 => Ok(()),
882                _ => Err(Error(ret as u16)),
883            }
884        }
885    }
886}
887
888pub(crate) unsafe fn poll_oneoff<S: Storage>(
889    fs: &mut FileSystem<S>,
890    in_: *const Subscription,
891    out: *mut Event,
892    nsubscriptions: u32,
893) -> Result<Size, Error> {
894    let in_ = slice::from_raw_parts(in_, nsubscriptions as usize);
895    let mut new_in = Vec::<Subscription>::new();
896    for sub in in_ {
897        match sub.u.tag {
898        0 /* EVENTTYPE_CLOCK */ => {
899            new_in.push(*sub);
900        }
901        1 | 2 /* EVENTTYPE_FD_READ | EVENTTYPE_FD_WRITE */ => {
902            let mut new_sub = *sub;
903            let fd = if sub.u.tag == 1 {
904                sub.u.u.fd_read.file_descriptor
905            } else {
906                sub.u.u.fd_write.file_descriptor
907            };
908            let fd = fs.get_backing_fd(fd)?;
909            let new_fd = match fd {
910                BackingFd::Virtual(_) => return Err(wasi::ERRNO_NOTSUP.into()),
911                BackingFd::Wasi(fd) => fd,
912            };
913
914            if sub.u.tag == 1 {
915                new_sub.u.u.fd_read.file_descriptor = new_fd;
916            } else {
917                new_sub.u.u.fd_write.file_descriptor = new_fd;
918            }
919
920            new_in.push(new_sub);
921        }
922        _ => return Err(wasi::ERRNO_INVAL.into()),
923        }
924    }
925    let mut rp0 = MaybeUninit::<Fdstat>::uninit();
926    let ret = wasi::wasi_snapshot_preview1::poll_oneoff(
927        new_in.as_ptr() as i32,
928        out as i32,
929        nsubscriptions as i32,
930        rp0.as_mut_ptr() as i32,
931    );
932
933    match ret {
934        0 => Ok(core::ptr::read(rp0.as_mut_ptr() as i32 as *const Size)),
935        _ => Err(Error(ret as u16)),
936    }
937}
938
939fn read_bytes<R: std::io::Read>(mut src: R, iovs: wasi::IovecArray) -> Result<usize, wasi::Errno> {
940    let mut bytes_read = 0;
941    for iov in iovs {
942        unsafe {
943            let buf = slice::from_raw_parts_mut(iov.buf, iov.buf_len);
944            bytes_read += src.read(buf).map_err(|_| wasi::ERRNO_IO)?;
945        }
946    }
947    Ok(bytes_read)
948}
949
950fn cstr_to_path(path: &CStr) -> Result<&Path, wasi::Errno> {
951    let os_str: &OsStr = unsafe { std::mem::transmute(path.to_bytes()) };
952    Ok(Path::new(os_str))
953}