xref: /wasmtime-44.0.1/src/lib.rs (revision 4f84e641)
1 #![allow(unused_variables)] // TODO: remove this when more things are implemented
2 
3 use crate::bindings::{
4     exit, filesystem, monotonic_clock, network, poll, random, streams, wall_clock,
5 };
6 use core::cell::{Cell, RefCell, RefMut, UnsafeCell};
7 use core::cmp::min;
8 use core::ffi::c_void;
9 use core::hint::black_box;
10 use core::mem::{self, align_of, forget, size_of, ManuallyDrop, MaybeUninit};
11 use core::ops::{Deref, DerefMut};
12 use core::ptr::{self, null_mut};
13 use core::slice;
14 use poll::Pollable;
15 use wasi::*;
16 
17 #[cfg(all(feature = "command", feature = "reactor"))]
18 compile_error!("only one of the `command` and `reactor` features may be selected at a time");
19 
20 #[macro_use]
21 mod macros;
22 
23 mod descriptors;
24 use crate::descriptors::{Descriptor, Descriptors, StreamType, Streams};
25 
26 pub mod bindings {
27     #[cfg(feature = "command")]
28     wit_bindgen::generate!({
29         world: "command",
30         std_feature,
31         raw_strings,
32         // The generated definition of command will pull in std, so we are defining it
33         // manually below instead
34         skip: ["run", "get-directories", "get-environment"],
35     });
36 
37     #[cfg(feature = "reactor")]
38     wit_bindgen::generate!({
39         world: "reactor",
40         std_feature,
41         raw_strings,
42         skip: ["get-directories", "get-environment"],
43     });
44 }
45 
46 #[no_mangle]
47 #[cfg(feature = "command")]
48 pub unsafe extern "C" fn run() -> u32 {
49     #[link(wasm_import_module = "__main_module__")]
50     extern "C" {
51         fn _start();
52     }
53     _start();
54     0
55 }
56 
57 // The unwrap/expect methods in std pull panic when they fail, which pulls
58 // in unwinding machinery that we can't use in the adapter. Instead, use this
59 // extension trait to get postfixed upwrap on Option and Result.
60 trait TrappingUnwrap<T> {
61     fn trapping_unwrap(self) -> T;
62 }
63 
64 impl<T> TrappingUnwrap<T> for Option<T> {
65     fn trapping_unwrap(self) -> T {
66         match self {
67             Some(t) => t,
68             None => unreachable!(),
69         }
70     }
71 }
72 
73 impl<T, E> TrappingUnwrap<T> for Result<T, E> {
74     fn trapping_unwrap(self) -> T {
75         match self {
76             Ok(t) => t,
77             Err(_) => unreachable!(),
78         }
79     }
80 }
81 
82 #[no_mangle]
83 pub unsafe extern "C" fn cabi_import_realloc(
84     old_ptr: *mut u8,
85     old_size: usize,
86     align: usize,
87     new_size: usize,
88 ) -> *mut u8 {
89     if !old_ptr.is_null() || old_size != 0 {
90         unreachable!();
91     }
92     let mut ptr = null_mut::<u8>();
93     State::with(|state| {
94         ptr = state.import_alloc.alloc(align, new_size);
95         Ok(())
96     });
97     ptr
98 }
99 
100 /// Bump-allocated memory arena. This is a singleton - the
101 /// memory will be sized according to `bump_arena_size()`.
102 pub struct BumpArena {
103     data: MaybeUninit<[u8; bump_arena_size()]>,
104     position: Cell<usize>,
105 }
106 
107 impl BumpArena {
108     fn new() -> Self {
109         BumpArena {
110             data: MaybeUninit::uninit(),
111             position: Cell::new(0),
112         }
113     }
114     fn alloc(&self, align: usize, size: usize) -> *mut u8 {
115         let start = self.data.as_ptr() as usize;
116         let next = start + self.position.get();
117         let alloc = align_to(next, align);
118         let offset = alloc - start;
119         if offset + size > bump_arena_size() {
120             unreachable!("out of memory");
121         }
122         self.position.set(offset + size);
123         alloc as *mut u8
124     }
125 }
126 fn align_to(ptr: usize, align: usize) -> usize {
127     (ptr + (align - 1)) & !(align - 1)
128 }
129 
130 // Invariant: buffer not-null and arena is-some are never true at the same
131 // time. We did not use an enum to make this invalid behavior unrepresentable
132 // because we can't use RefCell to borrow() the variants of the enum - only
133 // Cell provides mutability without pulling in panic machinery - so it would
134 // make the accessors a lot more awkward to write.
135 pub struct ImportAlloc {
136     // When not-null, allocator should use this buffer/len pair at most once
137     // to satisfy allocations.
138     buffer: Cell<*mut u8>,
139     len: Cell<usize>,
140     // When not-empty, allocator should use this arena to satisfy allocations.
141     arena: Cell<Option<&'static BumpArena>>,
142 }
143 
144 impl ImportAlloc {
145     fn new() -> Self {
146         ImportAlloc {
147             buffer: Cell::new(std::ptr::null_mut()),
148             len: Cell::new(0),
149             arena: Cell::new(None),
150         }
151     }
152 
153     /// Expect at most one import allocation during execution of the provided closure.
154     /// Use the provided buffer to satisfy that import allocation. The user is responsible
155     /// for making sure allocated imports are not used beyond the lifetime of the buffer.
156     fn with_buffer<T>(&self, buffer: *mut u8, len: usize, f: impl FnOnce() -> T) -> T {
157         if self.arena.get().is_some() {
158             unreachable!("arena mode")
159         }
160         let prev = self.buffer.replace(buffer);
161         if !prev.is_null() {
162             unreachable!("overwrote another buffer")
163         }
164         self.len.set(len);
165         let r = f();
166         self.buffer.set(std::ptr::null_mut());
167         r
168     }
169 
170     /// Permit many import allocations during execution of the provided closure.
171     /// Use the provided BumpArena to satisfry those allocations. The user is responsible
172     /// for making sure allocated imports are not used beyond the lifetime of the arena.
173     fn with_arena<T>(&self, arena: &BumpArena, f: impl FnOnce() -> T) -> T {
174         if !self.buffer.get().is_null() {
175             unreachable!("buffer mode")
176         }
177         let prev = self.arena.replace(Some(unsafe {
178             // Safety: Need to erase the lifetime to store in the arena cell.
179             std::mem::transmute::<&'_ BumpArena, &'static BumpArena>(arena)
180         }));
181         if prev.is_some() {
182             unreachable!("overwrote another arena")
183         }
184         let r = f();
185         self.arena.set(None);
186         r
187     }
188 
189     /// To be used by cabi_import_realloc only!
190     fn alloc(&self, align: usize, size: usize) -> *mut u8 {
191         if let Some(arena) = self.arena.get() {
192             arena.alloc(align, size)
193         } else {
194             let buffer = self.buffer.get();
195             if buffer.is_null() {
196                 unreachable!("buffer not provided, or already used")
197             }
198             let buffer = buffer as usize;
199             let alloc = align_to(buffer, align);
200             if alloc.checked_add(size).trapping_unwrap()
201                 > buffer.checked_add(self.len.get()).trapping_unwrap()
202             {
203                 unreachable!("out of memory")
204             }
205             self.buffer.set(std::ptr::null_mut());
206             alloc as *mut u8
207         }
208     }
209 }
210 
211 /// This allocator is only used for the `run` entrypoint.
212 ///
213 /// The implementation here is a bump allocator into `State::long_lived_arena` which
214 /// traps when it runs out of data. This means that the total size of
215 /// arguments/env/etc coming into a component is bounded by the current 64k
216 /// (ish) limit. That's just an implementation limit though which can be lifted
217 /// by dynamically calling the main module's allocator as necessary for more data.
218 #[no_mangle]
219 pub unsafe extern "C" fn cabi_export_realloc(
220     old_ptr: *mut u8,
221     old_size: usize,
222     align: usize,
223     new_size: usize,
224 ) -> *mut u8 {
225     if !old_ptr.is_null() || old_size != 0 {
226         unreachable!();
227     }
228     let mut ret = null_mut::<u8>();
229     State::with_mut(|state| {
230         ret = state.long_lived_arena.alloc(align, new_size);
231         Ok(())
232     });
233     ret
234 }
235 
236 /// Read command-line argument data.
237 /// The size of the array should match that returned by `args_sizes_get`
238 #[no_mangle]
239 pub unsafe extern "C" fn args_get(mut argv: *mut *mut u8, mut argv_buf: *mut u8) -> Errno {
240     State::with(|state| {
241         for arg in state.get_args() {
242             // Copy the argument into `argv_buf` which must be sized
243             // appropriately by the caller.
244             ptr::copy_nonoverlapping(arg.ptr, argv_buf, arg.len);
245             *argv_buf.add(arg.len) = 0;
246 
247             // Copy the argument pointer into the `argv` buf
248             *argv = argv_buf;
249 
250             // Update our pointers past what's written to prepare for the
251             // next argument.
252             argv = argv.add(1);
253             argv_buf = argv_buf.add(arg.len + 1);
254         }
255         Ok(())
256     })
257 }
258 
259 /// Return command-line argument data sizes.
260 #[no_mangle]
261 pub unsafe extern "C" fn args_sizes_get(argc: *mut Size, argv_buf_size: *mut Size) -> Errno {
262     State::with(|state| {
263         let args = state.get_args();
264         *argc = args.len();
265         // Add one to each length for the terminating nul byte added by
266         // the `args_get` function.
267         *argv_buf_size = args.iter().map(|s| s.len + 1).sum();
268         Ok(())
269     })
270 }
271 
272 /// Read environment variable data.
273 /// The sizes of the buffers should match that returned by `environ_sizes_get`.
274 #[no_mangle]
275 pub unsafe extern "C" fn environ_get(environ: *mut *mut u8, environ_buf: *mut u8) -> Errno {
276     State::with(|state| {
277         let mut offsets = environ;
278         let mut buffer = environ_buf;
279         for var in state.get_environment() {
280             ptr::write(offsets, buffer);
281             offsets = offsets.add(1);
282 
283             ptr::copy_nonoverlapping(var.key.ptr, buffer, var.key.len);
284             buffer = buffer.add(var.key.len);
285 
286             ptr::write(buffer, b'=');
287             buffer = buffer.add(1);
288 
289             ptr::copy_nonoverlapping(var.value.ptr, buffer, var.value.len);
290             buffer = buffer.add(var.value.len);
291 
292             ptr::write(buffer, 0);
293             buffer = buffer.add(1);
294         }
295 
296         Ok(())
297     })
298 }
299 
300 /// Return environment variable data sizes.
301 #[no_mangle]
302 pub unsafe extern "C" fn environ_sizes_get(
303     environc: *mut Size,
304     environ_buf_size: *mut Size,
305 ) -> Errno {
306     if matches!(
307         get_allocation_state(),
308         AllocationState::StackAllocated | AllocationState::StateAllocated
309     ) {
310         State::with(|state| {
311             let vars = state.get_environment();
312             *environc = vars.len();
313             *environ_buf_size = {
314                 let mut sum = 0;
315                 for var in vars {
316                     sum += var.key.len + var.value.len + 2;
317                 }
318                 sum
319             };
320 
321             Ok(())
322         })
323     } else {
324         *environc = 0;
325         *environ_buf_size = 0;
326         ERRNO_SUCCESS
327     }
328 }
329 
330 /// Return the resolution of a clock.
331 /// Implementations are required to provide a non-zero value for supported clocks. For unsupported clocks,
332 /// return `errno::inval`.
333 /// Note: This is similar to `clock_getres` in POSIX.
334 #[no_mangle]
335 pub extern "C" fn clock_res_get(id: Clockid, resolution: &mut Timestamp) -> Errno {
336     State::with(|state| {
337         match id {
338             CLOCKID_MONOTONIC => {
339                 let res = monotonic_clock::resolution();
340                 *resolution = res;
341             }
342             CLOCKID_REALTIME => {
343                 let res = wall_clock::resolution();
344                 *resolution = Timestamp::from(res.seconds)
345                     .checked_mul(1_000_000_000)
346                     .and_then(|ns| ns.checked_add(res.nanoseconds.into()))
347                     .ok_or(ERRNO_OVERFLOW)?;
348             }
349             _ => unreachable!(),
350         }
351         Ok(())
352     })
353 }
354 
355 /// Return the time value of a clock.
356 /// Note: This is similar to `clock_gettime` in POSIX.
357 #[no_mangle]
358 pub unsafe extern "C" fn clock_time_get(
359     id: Clockid,
360     _precision: Timestamp,
361     time: &mut Timestamp,
362 ) -> Errno {
363     if matches!(
364         get_allocation_state(),
365         AllocationState::StackAllocated | AllocationState::StateAllocated
366     ) {
367         State::with(|state| {
368             match id {
369                 CLOCKID_MONOTONIC => {
370                     *time = monotonic_clock::now();
371                 }
372                 CLOCKID_REALTIME => {
373                     let res = wall_clock::now();
374                     *time = Timestamp::from(res.seconds)
375                         .checked_mul(1_000_000_000)
376                         .and_then(|ns| ns.checked_add(res.nanoseconds.into()))
377                         .ok_or(ERRNO_OVERFLOW)?;
378                 }
379                 _ => unreachable!(),
380             }
381             Ok(())
382         })
383     } else {
384         *time = Timestamp::from(0u64);
385         ERRNO_SUCCESS
386     }
387 }
388 
389 /// Provide file advisory information on a file descriptor.
390 /// Note: This is similar to `posix_fadvise` in POSIX.
391 #[no_mangle]
392 pub unsafe extern "C" fn fd_advise(
393     fd: Fd,
394     offset: Filesize,
395     len: Filesize,
396     advice: Advice,
397 ) -> Errno {
398     let advice = match advice {
399         ADVICE_NORMAL => filesystem::Advice::Normal,
400         ADVICE_SEQUENTIAL => filesystem::Advice::Sequential,
401         ADVICE_RANDOM => filesystem::Advice::Random,
402         ADVICE_WILLNEED => filesystem::Advice::WillNeed,
403         ADVICE_DONTNEED => filesystem::Advice::DontNeed,
404         ADVICE_NOREUSE => filesystem::Advice::NoReuse,
405         _ => return ERRNO_INVAL,
406     };
407     State::with(|state| {
408         let ds = state.descriptors();
409         let file = ds.get_seekable_file(fd)?;
410         filesystem::advise(file.fd, offset, len, advice)?;
411         Ok(())
412     })
413 }
414 
415 /// Force the allocation of space in a file.
416 /// Note: This is similar to `posix_fallocate` in POSIX.
417 #[no_mangle]
418 pub unsafe extern "C" fn fd_allocate(fd: Fd, offset: Filesize, len: Filesize) -> Errno {
419     State::with(|state| {
420         let ds = state.descriptors();
421         // For not-files, fail with BADF
422         let file = ds.get_file(fd)?;
423         // For all files, fail with NOTSUP, because this call does not exist in preview 2.
424         Err(wasi::ERRNO_NOTSUP)
425     })
426 }
427 
428 /// Close a file descriptor.
429 /// Note: This is similar to `close` in POSIX.
430 #[no_mangle]
431 pub unsafe extern "C" fn fd_close(fd: Fd) -> Errno {
432     State::with_mut(|state| {
433         // If there's a dirent cache entry for this file descriptor then drop
434         // it since the descriptor is being closed and future calls to
435         // `fd_readdir` should return an error.
436         if fd == state.dirent_cache.for_fd.get() {
437             drop(state.dirent_cache.stream.replace(None));
438         }
439 
440         let desc = state.descriptors_mut().close(fd)?;
441         Ok(())
442     })
443 }
444 
445 /// Synchronize the data of a file to disk.
446 /// Note: This is similar to `fdatasync` in POSIX.
447 #[no_mangle]
448 pub unsafe extern "C" fn fd_datasync(fd: Fd) -> Errno {
449     State::with(|state| {
450         let ds = state.descriptors();
451         let file = ds.get_file(fd)?;
452         filesystem::sync_data(file.fd)?;
453         Ok(())
454     })
455 }
456 
457 /// Get the attributes of a file descriptor.
458 /// Note: This returns similar flags to `fsync(fd, F_GETFL)` in POSIX, as well as additional fields.
459 #[no_mangle]
460 pub unsafe extern "C" fn fd_fdstat_get(fd: Fd, stat: *mut Fdstat) -> Errno {
461     State::with(|state| match state.descriptors().get(fd)? {
462         Descriptor::Streams(Streams {
463             type_: StreamType::File(file),
464             ..
465         }) => {
466             let flags = filesystem::get_flags(file.fd)?;
467             let type_ = filesystem::get_type(file.fd)?;
468 
469             let fs_filetype = type_.into();
470 
471             let mut fs_flags = 0;
472             let mut fs_rights_base = !0;
473             if !flags.contains(filesystem::DescriptorFlags::READ) {
474                 fs_rights_base &= !RIGHTS_FD_READ;
475             }
476             if !flags.contains(filesystem::DescriptorFlags::WRITE) {
477                 fs_rights_base &= !RIGHTS_FD_WRITE;
478             }
479             if flags.contains(filesystem::DescriptorFlags::DATA_INTEGRITY_SYNC) {
480                 fs_flags |= FDFLAGS_DSYNC;
481             }
482             if flags.contains(filesystem::DescriptorFlags::REQUESTED_WRITE_SYNC) {
483                 fs_flags |= FDFLAGS_RSYNC;
484             }
485             if flags.contains(filesystem::DescriptorFlags::FILE_INTEGRITY_SYNC) {
486                 fs_flags |= FDFLAGS_SYNC;
487             }
488             if file.append {
489                 fs_flags |= FDFLAGS_APPEND;
490             }
491             if !file.blocking {
492                 fs_flags |= FDFLAGS_NONBLOCK;
493             }
494             let fs_rights_inheriting = fs_rights_base;
495 
496             stat.write(Fdstat {
497                 fs_filetype,
498                 fs_flags,
499                 fs_rights_base,
500                 fs_rights_inheriting,
501             });
502             Ok(())
503         }
504         Descriptor::Streams(Streams {
505             input,
506             output,
507             type_: StreamType::Socket(_),
508         })
509         | Descriptor::Streams(Streams {
510             input,
511             output,
512             type_: StreamType::Stdio,
513         }) => {
514             let fs_filetype = FILETYPE_CHARACTER_DEVICE;
515             let fs_flags = 0;
516             let mut fs_rights_base = 0;
517             if input.get().is_some() {
518                 fs_rights_base |= RIGHTS_FD_READ;
519             }
520             if output.get().is_some() {
521                 fs_rights_base |= RIGHTS_FD_WRITE;
522             }
523             let fs_rights_inheriting = fs_rights_base;
524             stat.write(Fdstat {
525                 fs_filetype,
526                 fs_flags,
527                 fs_rights_base,
528                 fs_rights_inheriting,
529             });
530             Ok(())
531         }
532         Descriptor::Closed(_) => Err(ERRNO_BADF),
533     })
534 }
535 
536 /// Adjust the flags associated with a file descriptor.
537 /// Note: This is similar to `fcntl(fd, F_SETFL, flags)` in POSIX.
538 #[no_mangle]
539 pub unsafe extern "C" fn fd_fdstat_set_flags(fd: Fd, flags: Fdflags) -> Errno {
540     // Only support changing the NONBLOCK or APPEND flags.
541     if flags & !(FDFLAGS_NONBLOCK | FDFLAGS_APPEND) != 0 {
542         return wasi::ERRNO_INVAL;
543     }
544 
545     State::with_mut(|state| {
546         let mut ds = state.descriptors_mut();
547         let file = match ds.get_mut(fd)? {
548             Descriptor::Streams(Streams {
549                 type_: StreamType::File(file),
550                 ..
551             }) if !file.is_dir() => file,
552             _ => Err(wasi::ERRNO_BADF)?,
553         };
554         file.append = flags & FDFLAGS_APPEND == FDFLAGS_APPEND;
555         file.blocking = !(flags & FDFLAGS_NONBLOCK == FDFLAGS_NONBLOCK);
556         Ok(())
557     })
558 }
559 
560 /// Adjust the rights associated with a file descriptor.
561 /// This can only be used to remove rights, and returns `errno::notcapable` if called in a way that would attempt to add rights
562 #[no_mangle]
563 pub unsafe extern "C" fn fd_fdstat_set_rights(
564     fd: Fd,
565     fs_rights_base: Rights,
566     fs_rights_inheriting: Rights,
567 ) -> Errno {
568     unreachable!()
569 }
570 
571 /// Return the attributes of an open file.
572 #[no_mangle]
573 pub unsafe extern "C" fn fd_filestat_get(fd: Fd, buf: *mut Filestat) -> Errno {
574     State::with(|state| {
575         let ds = state.descriptors();
576         match ds.get(fd)? {
577             Descriptor::Streams(Streams {
578                 type_: StreamType::File(file),
579                 ..
580             }) => {
581                 let stat = filesystem::stat(file.fd)?;
582                 let filetype = stat.type_.into();
583                 *buf = Filestat {
584                     dev: stat.device,
585                     ino: stat.inode,
586                     filetype,
587                     nlink: stat.link_count,
588                     size: stat.size,
589                     atim: datetime_to_timestamp(stat.data_access_timestamp),
590                     mtim: datetime_to_timestamp(stat.data_modification_timestamp),
591                     ctim: datetime_to_timestamp(stat.status_change_timestamp),
592                 };
593                 Ok(())
594             }
595             // Stdio is all zero fields, except for filetype character device
596             Descriptor::Streams(Streams {
597                 type_: StreamType::Stdio,
598                 ..
599             }) => {
600                 *buf = Filestat {
601                     dev: 0,
602                     ino: 0,
603                     filetype: FILETYPE_CHARACTER_DEVICE,
604                     nlink: 0,
605                     size: 0,
606                     atim: 0,
607                     mtim: 0,
608                     ctim: 0,
609                 };
610                 Ok(())
611             }
612             _ => Err(wasi::ERRNO_BADF),
613         }
614     })
615 }
616 
617 /// Adjust the size of an open file. If this increases the file's size, the extra bytes are filled with zeros.
618 /// Note: This is similar to `ftruncate` in POSIX.
619 #[no_mangle]
620 pub unsafe extern "C" fn fd_filestat_set_size(fd: Fd, size: Filesize) -> Errno {
621     State::with(|state| {
622         let ds = state.descriptors();
623         let file = ds.get_file(fd)?;
624         filesystem::set_size(file.fd, size)?;
625         Ok(())
626     })
627 }
628 
629 fn systimespec(set: bool, ts: Timestamp, now: bool) -> Result<filesystem::NewTimestamp, Errno> {
630     if set && now {
631         Err(wasi::ERRNO_INVAL)
632     } else if set {
633         Ok(filesystem::NewTimestamp::Timestamp(filesystem::Datetime {
634             seconds: ts / 1_000_000_000,
635             nanoseconds: (ts % 1_000_000_000) as _,
636         }))
637     } else if now {
638         Ok(filesystem::NewTimestamp::Now)
639     } else {
640         Ok(filesystem::NewTimestamp::NoChange)
641     }
642 }
643 
644 /// Adjust the timestamps of an open file or directory.
645 /// Note: This is similar to `futimens` in POSIX.
646 #[no_mangle]
647 pub unsafe extern "C" fn fd_filestat_set_times(
648     fd: Fd,
649     atim: Timestamp,
650     mtim: Timestamp,
651     fst_flags: Fstflags,
652 ) -> Errno {
653     State::with(|state| {
654         let atim = systimespec(
655             fst_flags & FSTFLAGS_ATIM == FSTFLAGS_ATIM,
656             atim,
657             fst_flags & FSTFLAGS_ATIM_NOW == FSTFLAGS_ATIM_NOW,
658         )?;
659         let mtim = systimespec(
660             fst_flags & FSTFLAGS_MTIM == FSTFLAGS_MTIM,
661             mtim,
662             fst_flags & FSTFLAGS_MTIM_NOW == FSTFLAGS_MTIM_NOW,
663         )?;
664         let ds = state.descriptors();
665         let file = ds.get_file(fd)?;
666         filesystem::set_times(file.fd, atim, mtim)?;
667         Ok(())
668     })
669 }
670 
671 /// Read from a file descriptor, without using and updating the file descriptor's offset.
672 /// Note: This is similar to `preadv` in POSIX.
673 #[no_mangle]
674 pub unsafe extern "C" fn fd_pread(
675     fd: Fd,
676     mut iovs_ptr: *const Iovec,
677     mut iovs_len: usize,
678     offset: Filesize,
679     nread: *mut Size,
680 ) -> Errno {
681     // Advance to the first non-empty buffer.
682     while iovs_len != 0 && (*iovs_ptr).buf_len == 0 {
683         iovs_ptr = iovs_ptr.add(1);
684         iovs_len -= 1;
685     }
686     if iovs_len == 0 {
687         *nread = 0;
688         return ERRNO_SUCCESS;
689     }
690 
691     State::with(|state| {
692         let ptr = (*iovs_ptr).buf;
693         let len = (*iovs_ptr).buf_len;
694 
695         let ds = state.descriptors();
696         let file = ds.get_file(fd)?;
697         let (data, end) = state
698             .import_alloc
699             .with_buffer(ptr, len, || filesystem::read(file.fd, len as u64, offset))?;
700         assert_eq!(data.as_ptr(), ptr);
701         assert!(data.len() <= len);
702 
703         let len = data.len();
704         forget(data);
705         if !end && len == 0 {
706             Err(ERRNO_INTR)
707         } else {
708             *nread = len;
709             Ok(())
710         }
711     })
712 }
713 
714 /// Return a description of the given preopened file descriptor.
715 #[no_mangle]
716 pub unsafe extern "C" fn fd_prestat_get(fd: Fd, buf: *mut Prestat) -> Errno {
717     if matches!(
718         get_allocation_state(),
719         AllocationState::StackAllocated | AllocationState::StateAllocated
720     ) {
721         State::with(|state| {
722             if let Some(preopen) = state.descriptors().get_preopen(fd) {
723                 buf.write(Prestat {
724                     tag: 0,
725                     u: PrestatU {
726                         dir: PrestatDir {
727                             pr_name_len: preopen.path.len,
728                         },
729                     },
730                 });
731 
732                 Ok(())
733             } else {
734                 Err(ERRNO_BADF)
735             }
736         })
737     } else {
738         ERRNO_BADF
739     }
740 }
741 
742 /// Return a description of the given preopened file descriptor.
743 #[no_mangle]
744 pub unsafe extern "C" fn fd_prestat_dir_name(fd: Fd, path: *mut u8, path_len: Size) -> Errno {
745     State::with(|state| {
746         if let Some(preopen) = state.descriptors().get_preopen(fd) {
747             if preopen.path.len < path_len as usize {
748                 Err(ERRNO_NAMETOOLONG)
749             } else {
750                 ptr::copy_nonoverlapping(preopen.path.ptr, path, preopen.path.len);
751                 Ok(())
752             }
753         } else {
754             Err(ERRNO_NOTDIR)
755         }
756     })
757 }
758 
759 /// Write to a file descriptor, without using and updating the file descriptor's offset.
760 /// Note: This is similar to `pwritev` in POSIX.
761 #[no_mangle]
762 pub unsafe extern "C" fn fd_pwrite(
763     fd: Fd,
764     mut iovs_ptr: *const Ciovec,
765     mut iovs_len: usize,
766     offset: Filesize,
767     nwritten: *mut Size,
768 ) -> Errno {
769     // Advance to the first non-empty buffer.
770     while iovs_len != 0 && (*iovs_ptr).buf_len == 0 {
771         iovs_ptr = iovs_ptr.add(1);
772         iovs_len -= 1;
773     }
774     if iovs_len == 0 {
775         *nwritten = 0;
776         return ERRNO_SUCCESS;
777     }
778 
779     let ptr = (*iovs_ptr).buf;
780     let len = (*iovs_ptr).buf_len;
781 
782     State::with(|state| {
783         let ds = state.descriptors();
784         let file = ds.get_seekable_file(fd)?;
785         let bytes = filesystem::write(file.fd, slice::from_raw_parts(ptr, len), offset)?;
786         *nwritten = bytes as usize;
787         Ok(())
788     })
789 }
790 
791 /// Read from a file descriptor.
792 /// Note: This is similar to `readv` in POSIX.
793 #[no_mangle]
794 pub unsafe extern "C" fn fd_read(
795     fd: Fd,
796     mut iovs_ptr: *const Iovec,
797     mut iovs_len: usize,
798     nread: *mut Size,
799 ) -> Errno {
800     // Advance to the first non-empty buffer.
801     while iovs_len != 0 && (*iovs_ptr).buf_len == 0 {
802         iovs_ptr = iovs_ptr.add(1);
803         iovs_len -= 1;
804     }
805     if iovs_len == 0 {
806         *nread = 0;
807         return ERRNO_SUCCESS;
808     }
809 
810     let ptr = (*iovs_ptr).buf;
811     let len = (*iovs_ptr).buf_len;
812 
813     State::with(|state| {
814         match state.descriptors().get(fd)? {
815             Descriptor::Streams(streams) => {
816                 let blocking = if let StreamType::File(file) = &streams.type_ {
817                     file.blocking
818                 } else {
819                     false
820                 };
821 
822                 let read_len = u64::try_from(len).trapping_unwrap();
823                 let wasi_stream = streams.get_read_stream()?;
824                 let (data, end) = state
825                     .import_alloc
826                     .with_buffer(ptr, len, || {
827                         if blocking {
828                             streams::blocking_read(wasi_stream, read_len)
829                         } else {
830                             streams::read(wasi_stream, read_len)
831                         }
832                     })
833                     .map_err(|_| ERRNO_IO)?;
834 
835                 assert_eq!(data.as_ptr(), ptr);
836                 assert!(data.len() <= len);
837 
838                 // If this is a file, keep the current-position pointer up to date.
839                 if let StreamType::File(file) = &streams.type_ {
840                     file.position
841                         .set(file.position.get() + data.len() as filesystem::Filesize);
842                 }
843 
844                 let len = data.len();
845                 forget(data);
846                 if !end && len == 0 {
847                     Err(ERRNO_INTR)
848                 } else {
849                     *nread = len;
850                     Ok(())
851                 }
852             }
853             Descriptor::Closed(_) => Err(ERRNO_BADF),
854         }
855     })
856 }
857 
858 /// Read directory entries from a directory.
859 /// When successful, the contents of the output buffer consist of a sequence of
860 /// directory entries. Each directory entry consists of a `dirent` object,
861 /// followed by `dirent::d_namlen` bytes holding the name of the directory
862 /// entry.
863 /// This function fills the output buffer as much as possible, potentially
864 /// truncating the last directory entry. This allows the caller to grow its
865 /// read buffer size in case it's too small to fit a single large directory
866 /// entry, or skip the oversized directory entry.
867 #[no_mangle]
868 pub unsafe extern "C" fn fd_readdir(
869     fd: Fd,
870     buf: *mut u8,
871     buf_len: Size,
872     cookie: Dircookie,
873     bufused: *mut Size,
874 ) -> Errno {
875     let mut buf = slice::from_raw_parts_mut(buf, buf_len);
876     return State::with(|state| {
877         // First determine if there's an entry in the dirent cache to use. This
878         // is done to optimize the use case where a large directory is being
879         // used with a fixed-sized buffer to avoid re-invoking the `readdir`
880         // function and continuing to use the same iterator.
881         //
882         // This is a bit tricky since the requested state in this function call
883         // must match the prior state of the dirent stream, if any, so that's
884         // all validated here as well.
885         //
886         // Note that for the duration of this function the `cookie` specifier is
887         // the `n`th iteration of the `readdir` stream return value.
888         let prev_stream = state.dirent_cache.stream.replace(None);
889         let stream =
890             if state.dirent_cache.for_fd.get() == fd && state.dirent_cache.cookie.get() == cookie {
891                 prev_stream
892             } else {
893                 None
894             };
895 
896         // Compute the inode of `.` so that the iterator can produce an entry
897         // for it.
898         let ds = state.descriptors();
899         let dir = ds.get_dir(fd)?;
900         let stat = filesystem::stat(dir.fd)?;
901         let dot_inode = stat.inode;
902 
903         let mut iter;
904         match stream {
905             // All our checks passed and a dirent cache was available with a
906             // prior stream. Construct an iterator which will yield its first
907             // entry from cache and is additionally resuming at the `cookie`
908             // specified.
909             Some(stream) => {
910                 iter = DirectoryEntryIterator {
911                     stream,
912                     state,
913                     cookie,
914                     use_cache: true,
915                     dot_inode,
916                 }
917             }
918 
919             // Either a dirent stream wasn't previously available, a different
920             // cookie was requested, or a brand new directory is now being read.
921             // In these situations fall back to resuming reading the directory
922             // from scratch, and the `cookie` value indicates how many items
923             // need skipping.
924             None => {
925                 iter = DirectoryEntryIterator {
926                     state,
927                     cookie: wasi::DIRCOOKIE_START,
928                     use_cache: false,
929                     stream: DirectoryEntryStream(filesystem::read_directory(dir.fd)?),
930                     dot_inode,
931                 };
932 
933                 // Skip to the entry that is requested by the `cookie`
934                 // parameter.
935                 for _ in wasi::DIRCOOKIE_START..cookie {
936                     match iter.next() {
937                         Some(Ok(_)) => {}
938                         Some(Err(e)) => return Err(e),
939                         None => return Ok(()),
940                     }
941                 }
942             }
943         };
944 
945         while buf.len() > 0 {
946             let (dirent, name) = match iter.next() {
947                 Some(Ok(pair)) => pair,
948                 Some(Err(e)) => return Err(e),
949                 None => break,
950             };
951 
952             // Copy a `dirent` describing this entry into the destination `buf`,
953             // truncating it if it doesn't fit entirely.
954             let bytes = slice::from_raw_parts(
955                 (&dirent as *const wasi::Dirent).cast::<u8>(),
956                 size_of::<Dirent>(),
957             );
958             let dirent_bytes_to_copy = buf.len().min(bytes.len());
959             buf[..dirent_bytes_to_copy].copy_from_slice(&bytes[..dirent_bytes_to_copy]);
960             buf = &mut buf[dirent_bytes_to_copy..];
961 
962             // Copy the name bytes into the output `buf`, truncating it if it
963             // doesn't fit.
964             //
965             // Note that this might be a 0-byte copy if the `dirent` was
966             // truncated or fit entirely into the destination.
967             let name_bytes_to_copy = buf.len().min(name.len());
968             ptr::copy_nonoverlapping(name.as_ptr().cast(), buf.as_mut_ptr(), name_bytes_to_copy);
969 
970             buf = &mut buf[name_bytes_to_copy..];
971 
972             // If the buffer is empty then that means the value may be
973             // truncated, so save the state of the iterator in our dirent cache
974             // and return.
975             //
976             // Note that `cookie - 1` is stored here since `iter.cookie` stores
977             // the address of the next item, and we're rewinding one item since
978             // the current item is truncated and will want to resume from that
979             // in the future.
980             //
981             // Additionally note that this caching step is skipped if the name
982             // to store doesn't actually fit in the dirent cache's path storage.
983             // In that case there's not much we can do and let the next call to
984             // `fd_readdir` start from scratch.
985             if buf.len() == 0 && name.len() <= DIRENT_CACHE {
986                 let DirectoryEntryIterator { stream, cookie, .. } = iter;
987                 state.dirent_cache.stream.set(Some(stream));
988                 state.dirent_cache.for_fd.set(fd);
989                 state.dirent_cache.cookie.set(cookie - 1);
990                 state.dirent_cache.cached_dirent.set(dirent);
991                 ptr::copy(
992                     name.as_ptr().cast::<u8>(),
993                     (*state.dirent_cache.path_data.get()).as_mut_ptr() as *mut u8,
994                     name.len(),
995                 );
996                 break;
997             }
998         }
999 
1000         *bufused = buf_len - buf.len();
1001         Ok(())
1002     });
1003 
1004     struct DirectoryEntryIterator<'a> {
1005         state: &'a State,
1006         use_cache: bool,
1007         cookie: Dircookie,
1008         stream: DirectoryEntryStream,
1009         dot_inode: wasi::Inode,
1010     }
1011 
1012     impl<'a> Iterator for DirectoryEntryIterator<'a> {
1013         // Note the usage of `UnsafeCell<u8>` here to indicate that the data can
1014         // alias the storage within `state`.
1015         type Item = Result<(wasi::Dirent, &'a [UnsafeCell<u8>]), Errno>;
1016 
1017         fn next(&mut self) -> Option<Self::Item> {
1018             let current_cookie = self.cookie;
1019 
1020             self.cookie += 1;
1021 
1022             // Preview1 programs expect to see `.` and `..` in the traversal, but
1023             // Preview2 excludes them, so re-add them.
1024             match current_cookie {
1025                 0 => {
1026                     let dirent = wasi::Dirent {
1027                         d_next: self.cookie,
1028                         d_ino: self.dot_inode,
1029                         d_type: wasi::FILETYPE_DIRECTORY,
1030                         d_namlen: 1,
1031                     };
1032                     return Some(Ok((dirent, &self.state.dotdot[..1])));
1033                 }
1034                 1 => {
1035                     let dirent = wasi::Dirent {
1036                         d_next: self.cookie,
1037                         d_ino: 0,
1038                         d_type: wasi::FILETYPE_DIRECTORY,
1039                         d_namlen: 2,
1040                     };
1041                     return Some(Ok((dirent, &self.state.dotdot[..])));
1042                 }
1043                 _ => {}
1044             }
1045 
1046             if self.use_cache {
1047                 self.use_cache = false;
1048                 return Some(unsafe {
1049                     let dirent = self.state.dirent_cache.cached_dirent.as_ptr().read();
1050                     let ptr = (*(*self.state.dirent_cache.path_data.get()).as_ptr())
1051                         .as_ptr()
1052                         .cast();
1053                     let buffer = slice::from_raw_parts(ptr, dirent.d_namlen as usize);
1054                     Ok((dirent, buffer))
1055                 });
1056             }
1057             let entry = self.state.import_alloc.with_buffer(
1058                 self.state.path_buf.get().cast(),
1059                 PATH_MAX,
1060                 || filesystem::read_directory_entry(self.stream.0),
1061             );
1062             let entry = match entry {
1063                 Ok(Some(entry)) => entry,
1064                 Ok(None) => return None,
1065                 Err(e) => return Some(Err(e.into())),
1066             };
1067 
1068             let filesystem::DirectoryEntry { inode, type_, name } = entry;
1069             let name = ManuallyDrop::new(name);
1070             let dirent = wasi::Dirent {
1071                 d_next: self.cookie,
1072                 d_ino: inode.unwrap_or(0),
1073                 d_namlen: u32::try_from(name.len()).trapping_unwrap(),
1074                 d_type: type_.into(),
1075             };
1076             // Extend the lifetime of `name` to the `self.state` lifetime for
1077             // this iterator since the data for the name lives within state.
1078             let name = unsafe {
1079                 assert_eq!(name.as_ptr(), self.state.path_buf.get().cast());
1080                 slice::from_raw_parts(name.as_ptr().cast(), name.len())
1081             };
1082             Some(Ok((dirent, name)))
1083         }
1084     }
1085 }
1086 
1087 /// Atomically replace a file descriptor by renumbering another file descriptor.
1088 /// Due to the strong focus on thread safety, this environment does not provide
1089 /// a mechanism to duplicate or renumber a file descriptor to an arbitrary
1090 /// number, like `dup2()`. This would be prone to race conditions, as an actual
1091 /// file descriptor with the same number could be allocated by a different
1092 /// thread at the same time.
1093 /// This function provides a way to atomically renumber file descriptors, which
1094 /// would disappear if `dup2()` were to be removed entirely.
1095 #[no_mangle]
1096 pub unsafe extern "C" fn fd_renumber(fd: Fd, to: Fd) -> Errno {
1097     State::with_mut(|state| state.descriptors_mut().renumber(fd, to))
1098 }
1099 
1100 /// Move the offset of a file descriptor.
1101 /// Note: This is similar to `lseek` in POSIX.
1102 #[no_mangle]
1103 pub unsafe extern "C" fn fd_seek(
1104     fd: Fd,
1105     offset: Filedelta,
1106     whence: Whence,
1107     newoffset: *mut Filesize,
1108 ) -> Errno {
1109     State::with(|state| {
1110         let ds = state.descriptors();
1111         let stream = ds.get_seekable_stream(fd)?;
1112 
1113         // Seeking only works on files.
1114         if let StreamType::File(file) = &stream.type_ {
1115             if let filesystem::DescriptorType::Directory = file.descriptor_type {
1116                 // This isn't really the "right" errno, but it is consistient with wasmtime's
1117                 // preview 1 tests.
1118                 return Err(ERRNO_BADF);
1119             }
1120             let from = match whence {
1121                 WHENCE_SET if offset >= 0 => offset,
1122                 WHENCE_CUR => match (file.position.get() as i64).checked_add(offset) {
1123                     Some(pos) if pos >= 0 => pos,
1124                     _ => return Err(ERRNO_INVAL),
1125                 },
1126                 WHENCE_END => match (filesystem::stat(file.fd)?.size as i64).checked_add(offset) {
1127                     Some(pos) if pos >= 0 => pos,
1128                     _ => return Err(ERRNO_INVAL),
1129                 },
1130                 _ => return Err(ERRNO_INVAL),
1131             };
1132             stream.input.set(None);
1133             stream.output.set(None);
1134             file.position.set(from as filesystem::Filesize);
1135             *newoffset = from as filesystem::Filesize;
1136             Ok(())
1137         } else {
1138             Err(ERRNO_SPIPE)
1139         }
1140     })
1141 }
1142 
1143 /// Synchronize the data and metadata of a file to disk.
1144 /// Note: This is similar to `fsync` in POSIX.
1145 #[no_mangle]
1146 pub unsafe extern "C" fn fd_sync(fd: Fd) -> Errno {
1147     State::with(|state| {
1148         let ds = state.descriptors();
1149         let file = ds.get_file(fd)?;
1150         filesystem::sync(file.fd)?;
1151         Ok(())
1152     })
1153 }
1154 
1155 /// Return the current offset of a file descriptor.
1156 /// Note: This is similar to `lseek(fd, 0, SEEK_CUR)` in POSIX.
1157 #[no_mangle]
1158 pub unsafe extern "C" fn fd_tell(fd: Fd, offset: *mut Filesize) -> Errno {
1159     State::with(|state| {
1160         let ds = state.descriptors();
1161         let file = ds.get_seekable_file(fd)?;
1162         *offset = file.position.get() as Filesize;
1163         Ok(())
1164     })
1165 }
1166 
1167 /// Write to a file descriptor.
1168 /// Note: This is similar to `writev` in POSIX.
1169 #[no_mangle]
1170 pub unsafe extern "C" fn fd_write(
1171     fd: Fd,
1172     mut iovs_ptr: *const Ciovec,
1173     mut iovs_len: usize,
1174     nwritten: *mut Size,
1175 ) -> Errno {
1176     if matches!(
1177         get_allocation_state(),
1178         AllocationState::StackAllocated | AllocationState::StateAllocated
1179     ) {
1180         // Advance to the first non-empty buffer.
1181         while iovs_len != 0 && (*iovs_ptr).buf_len == 0 {
1182             iovs_ptr = iovs_ptr.add(1);
1183             iovs_len -= 1;
1184         }
1185         if iovs_len == 0 {
1186             *nwritten = 0;
1187             return ERRNO_SUCCESS;
1188         }
1189 
1190         let ptr = (*iovs_ptr).buf;
1191         let len = (*iovs_ptr).buf_len;
1192         let bytes = slice::from_raw_parts(ptr, len);
1193 
1194         State::with(|state| {
1195             let ds = state.descriptors();
1196             match ds.get(fd)? {
1197                 Descriptor::Streams(streams) => {
1198                     let wasi_stream = streams.get_write_stream()?;
1199 
1200                     let bytes = if let StreamType::File(file) = &streams.type_ {
1201                         if file.blocking {
1202                             streams::blocking_write(wasi_stream, bytes)
1203                         } else {
1204                             streams::write(wasi_stream, bytes)
1205                         }
1206                     } else {
1207                         streams::write(wasi_stream, bytes)
1208                     }
1209                     .map_err(|_| ERRNO_IO)?;
1210 
1211                     // If this is a file, keep the current-position pointer up to date.
1212                     if let StreamType::File(file) = &streams.type_ {
1213                         // But don't update if we're in append mode. Strictly speaking,
1214                         // we should set the position to the new end of the file, but
1215                         // we don't have an API to do that atomically.
1216                         if !file.append {
1217                             file.position
1218                                 .set(file.position.get() + filesystem::Filesize::from(bytes));
1219                         }
1220                     }
1221 
1222                     *nwritten = bytes as usize;
1223                     Ok(())
1224                 }
1225                 Descriptor::Closed(_) => Err(ERRNO_BADF),
1226             }
1227         })
1228     } else {
1229         *nwritten = 0;
1230         ERRNO_IO
1231     }
1232 }
1233 
1234 /// Create a directory.
1235 /// Note: This is similar to `mkdirat` in POSIX.
1236 #[no_mangle]
1237 pub unsafe extern "C" fn path_create_directory(
1238     fd: Fd,
1239     path_ptr: *const u8,
1240     path_len: usize,
1241 ) -> Errno {
1242     let path = slice::from_raw_parts(path_ptr, path_len);
1243 
1244     State::with(|state| {
1245         let ds = state.descriptors();
1246         let file = ds.get_dir(fd)?;
1247         filesystem::create_directory_at(file.fd, path)?;
1248         Ok(())
1249     })
1250 }
1251 
1252 /// Return the attributes of a file or directory.
1253 /// Note: This is similar to `stat` in POSIX.
1254 #[no_mangle]
1255 pub unsafe extern "C" fn path_filestat_get(
1256     fd: Fd,
1257     flags: Lookupflags,
1258     path_ptr: *const u8,
1259     path_len: usize,
1260     buf: *mut Filestat,
1261 ) -> Errno {
1262     let path = slice::from_raw_parts(path_ptr, path_len);
1263     let at_flags = at_flags_from_lookupflags(flags);
1264 
1265     State::with(|state| {
1266         let ds = state.descriptors();
1267         let file = ds.get_dir(fd)?;
1268         let stat = filesystem::stat_at(file.fd, at_flags, path)?;
1269         let filetype = stat.type_.into();
1270         *buf = Filestat {
1271             dev: stat.device,
1272             ino: stat.inode,
1273             filetype,
1274             nlink: stat.link_count,
1275             size: stat.size,
1276             atim: datetime_to_timestamp(stat.data_access_timestamp),
1277             mtim: datetime_to_timestamp(stat.data_modification_timestamp),
1278             ctim: datetime_to_timestamp(stat.status_change_timestamp),
1279         };
1280         Ok(())
1281     })
1282 }
1283 
1284 /// Adjust the timestamps of a file or directory.
1285 /// Note: This is similar to `utimensat` in POSIX.
1286 #[no_mangle]
1287 pub unsafe extern "C" fn path_filestat_set_times(
1288     fd: Fd,
1289     flags: Lookupflags,
1290     path_ptr: *const u8,
1291     path_len: usize,
1292     atim: Timestamp,
1293     mtim: Timestamp,
1294     fst_flags: Fstflags,
1295 ) -> Errno {
1296     let path = slice::from_raw_parts(path_ptr, path_len);
1297     let at_flags = at_flags_from_lookupflags(flags);
1298 
1299     State::with(|state| {
1300         let atim = systimespec(
1301             fst_flags & FSTFLAGS_ATIM == FSTFLAGS_ATIM,
1302             atim,
1303             fst_flags & FSTFLAGS_ATIM_NOW == FSTFLAGS_ATIM_NOW,
1304         )?;
1305         let mtim = systimespec(
1306             fst_flags & FSTFLAGS_MTIM == FSTFLAGS_MTIM,
1307             mtim,
1308             fst_flags & FSTFLAGS_MTIM_NOW == FSTFLAGS_MTIM_NOW,
1309         )?;
1310 
1311         let ds = state.descriptors();
1312         let file = ds.get_dir(fd)?;
1313         filesystem::set_times_at(file.fd, at_flags, path, atim, mtim)?;
1314         Ok(())
1315     })
1316 }
1317 
1318 /// Create a hard link.
1319 /// Note: This is similar to `linkat` in POSIX.
1320 #[no_mangle]
1321 pub unsafe extern "C" fn path_link(
1322     old_fd: Fd,
1323     old_flags: Lookupflags,
1324     old_path_ptr: *const u8,
1325     old_path_len: usize,
1326     new_fd: Fd,
1327     new_path_ptr: *const u8,
1328     new_path_len: usize,
1329 ) -> Errno {
1330     let old_path = slice::from_raw_parts(old_path_ptr, old_path_len);
1331     let new_path = slice::from_raw_parts(new_path_ptr, new_path_len);
1332     let at_flags = at_flags_from_lookupflags(old_flags);
1333 
1334     State::with(|state| {
1335         let old = state.descriptors().get_dir(old_fd)?.fd;
1336         let new = state.descriptors().get_dir(new_fd)?.fd;
1337         filesystem::link_at(old, at_flags, old_path, new, new_path)?;
1338         Ok(())
1339     })
1340 }
1341 
1342 /// Open a file or directory.
1343 /// The returned file descriptor is not guaranteed to be the lowest-numbered
1344 /// file descriptor not currently open; it is randomized to prevent
1345 /// applications from depending on making assumptions about indexes, since this
1346 /// is error-prone in multi-threaded contexts. The returned file descriptor is
1347 /// guaranteed to be less than 2**31.
1348 /// Note: This is similar to `openat` in POSIX.
1349 #[no_mangle]
1350 pub unsafe extern "C" fn path_open(
1351     fd: Fd,
1352     dirflags: Lookupflags,
1353     path_ptr: *const u8,
1354     path_len: usize,
1355     oflags: Oflags,
1356     fs_rights_base: Rights,
1357     fs_rights_inheriting: Rights,
1358     fdflags: Fdflags,
1359     opened_fd: *mut Fd,
1360 ) -> Errno {
1361     drop(fs_rights_inheriting);
1362 
1363     let path = slice::from_raw_parts(path_ptr, path_len);
1364     let at_flags = at_flags_from_lookupflags(dirflags);
1365     let o_flags = o_flags_from_oflags(oflags);
1366     let flags = descriptor_flags_from_flags(fs_rights_base, fdflags);
1367     let mode = filesystem::Modes::READABLE | filesystem::Modes::WRITEABLE;
1368     let append = fdflags & wasi::FDFLAGS_APPEND == wasi::FDFLAGS_APPEND;
1369 
1370     State::with_mut(|state| {
1371         let mut ds = state.descriptors_mut();
1372         let file = ds.get_dir(fd)?;
1373         let result = filesystem::open_at(file.fd, at_flags, path, o_flags, flags, mode)?;
1374         let descriptor_type = filesystem::get_type(result)?;
1375         let desc = Descriptor::Streams(Streams {
1376             input: Cell::new(None),
1377             output: Cell::new(None),
1378             type_: StreamType::File(File {
1379                 fd: result,
1380                 descriptor_type,
1381                 position: Cell::new(0),
1382                 append,
1383                 blocking: (fdflags & wasi::FDFLAGS_NONBLOCK) == 0,
1384             }),
1385         });
1386 
1387         let fd = ds.open(desc)?;
1388         *opened_fd = fd;
1389         Ok(())
1390     })
1391 }
1392 
1393 /// Read the contents of a symbolic link.
1394 /// Note: This is similar to `readlinkat` in POSIX.
1395 #[no_mangle]
1396 pub unsafe extern "C" fn path_readlink(
1397     fd: Fd,
1398     path_ptr: *const u8,
1399     path_len: usize,
1400     buf: *mut u8,
1401     buf_len: Size,
1402     bufused: *mut Size,
1403 ) -> Errno {
1404     let path = slice::from_raw_parts(path_ptr, path_len);
1405 
1406     State::with(|state| {
1407         // If the user gave us a buffer shorter than `PATH_MAX`, it may not be
1408         // long enough to accept the actual path. `cabi_realloc` can't fail,
1409         // so instead we handle this case specially.
1410         let use_state_buf = buf_len < PATH_MAX;
1411 
1412         let ds = state.descriptors();
1413         let file = ds.get_dir(fd)?;
1414         let path = if use_state_buf {
1415             state
1416                 .import_alloc
1417                 .with_buffer(state.path_buf.get().cast(), PATH_MAX, || {
1418                     filesystem::readlink_at(file.fd, path)
1419                 })?
1420         } else {
1421             state
1422                 .import_alloc
1423                 .with_buffer(buf, buf_len, || filesystem::readlink_at(file.fd, path))?
1424         };
1425 
1426         if use_state_buf {
1427             // Preview1 follows POSIX in truncating the returned path if it
1428             // doesn't fit.
1429             let len = min(path.len(), buf_len);
1430             ptr::copy_nonoverlapping(path.as_ptr().cast(), buf, len);
1431             *bufused = len;
1432         } else {
1433             *bufused = path.len();
1434         }
1435 
1436         // The returned string's memory was allocated in `buf`, so don't separately
1437         // free it.
1438         forget(path);
1439 
1440         Ok(())
1441     })
1442 }
1443 
1444 /// Remove a directory.
1445 /// Return `errno::notempty` if the directory is not empty.
1446 /// Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX.
1447 #[no_mangle]
1448 pub unsafe extern "C" fn path_remove_directory(
1449     fd: Fd,
1450     path_ptr: *const u8,
1451     path_len: usize,
1452 ) -> Errno {
1453     let path = slice::from_raw_parts(path_ptr, path_len);
1454 
1455     State::with(|state| {
1456         let ds = state.descriptors();
1457         let file = ds.get_dir(fd)?;
1458         filesystem::remove_directory_at(file.fd, path)?;
1459         Ok(())
1460     })
1461 }
1462 
1463 /// Rename a file or directory.
1464 /// Note: This is similar to `renameat` in POSIX.
1465 #[no_mangle]
1466 pub unsafe extern "C" fn path_rename(
1467     old_fd: Fd,
1468     old_path_ptr: *const u8,
1469     old_path_len: usize,
1470     new_fd: Fd,
1471     new_path_ptr: *const u8,
1472     new_path_len: usize,
1473 ) -> Errno {
1474     let old_path = slice::from_raw_parts(old_path_ptr, old_path_len);
1475     let new_path = slice::from_raw_parts(new_path_ptr, new_path_len);
1476 
1477     State::with(|state| {
1478         let ds = state.descriptors();
1479         let old = ds.get_dir(old_fd)?.fd;
1480         let new = ds.get_dir(new_fd)?.fd;
1481         filesystem::rename_at(old, old_path, new, new_path)?;
1482         Ok(())
1483     })
1484 }
1485 
1486 /// Create a symbolic link.
1487 /// Note: This is similar to `symlinkat` in POSIX.
1488 #[no_mangle]
1489 pub unsafe extern "C" fn path_symlink(
1490     old_path_ptr: *const u8,
1491     old_path_len: usize,
1492     fd: Fd,
1493     new_path_ptr: *const u8,
1494     new_path_len: usize,
1495 ) -> Errno {
1496     let old_path = slice::from_raw_parts(old_path_ptr, old_path_len);
1497     let new_path = slice::from_raw_parts(new_path_ptr, new_path_len);
1498 
1499     State::with(|state| {
1500         let ds = state.descriptors();
1501         let file = ds.get_dir(fd)?;
1502         filesystem::symlink_at(file.fd, old_path, new_path)?;
1503         Ok(())
1504     })
1505 }
1506 
1507 /// Unlink a file.
1508 /// Return `errno::isdir` if the path refers to a directory.
1509 /// Note: This is similar to `unlinkat(fd, path, 0)` in POSIX.
1510 #[no_mangle]
1511 pub unsafe extern "C" fn path_unlink_file(fd: Fd, path_ptr: *const u8, path_len: usize) -> Errno {
1512     let path = slice::from_raw_parts(path_ptr, path_len);
1513 
1514     State::with(|state| {
1515         let ds = state.descriptors();
1516         let file = ds.get_dir(fd)?;
1517         filesystem::unlink_file_at(file.fd, path)?;
1518         Ok(())
1519     })
1520 }
1521 
1522 struct Pollables {
1523     pointer: *mut Pollable,
1524     index: usize,
1525     length: usize,
1526 }
1527 
1528 impl Pollables {
1529     unsafe fn push(&mut self, pollable: Pollable) {
1530         assert!(self.index < self.length);
1531         *self.pointer.add(self.index) = pollable;
1532         self.index += 1;
1533     }
1534 }
1535 
1536 impl Drop for Pollables {
1537     fn drop(&mut self) {
1538         for i in 0..self.index {
1539             poll::drop_pollable(unsafe { *self.pointer.add(i) })
1540         }
1541     }
1542 }
1543 
1544 impl From<network::Error> for Errno {
1545     fn from(error: network::Error) -> Errno {
1546         match error {
1547             network::Error::Unknown => unreachable!(), // TODO
1548             network::Error::Again => ERRNO_AGAIN,
1549             /* TODO
1550             // Use a black box to prevent the optimizer from generating a
1551             // lookup table, which would require a static initializer.
1552             ConnectionAborted => black_box(ERRNO_CONNABORTED),
1553             ConnectionRefused => ERRNO_CONNREFUSED,
1554             ConnectionReset => ERRNO_CONNRESET,
1555             HostUnreachable => ERRNO_HOSTUNREACH,
1556             NetworkDown => ERRNO_NETDOWN,
1557             NetworkUnreachable => ERRNO_NETUNREACH,
1558             Timedout => ERRNO_TIMEDOUT,
1559             _ => unreachable!(),
1560             */
1561         }
1562     }
1563 }
1564 
1565 /// Concurrently poll for the occurrence of a set of events.
1566 #[no_mangle]
1567 pub unsafe extern "C" fn poll_oneoff(
1568     r#in: *const Subscription,
1569     out: *mut Event,
1570     nsubscriptions: Size,
1571     nevents: *mut Size,
1572 ) -> Errno {
1573     *nevents = 0;
1574 
1575     let subscriptions = slice::from_raw_parts(r#in, nsubscriptions);
1576 
1577     // We're going to split the `nevents` buffer into two non-overlapping
1578     // buffers: one to store the pollable handles, and the other to store
1579     // the bool results.
1580     //
1581     // First, we assert that this is possible:
1582     assert!(align_of::<Event>() >= align_of::<Pollable>());
1583     assert!(align_of::<Pollable>() >= align_of::<u8>());
1584     assert!(
1585         nsubscriptions
1586             .checked_mul(size_of::<Event>())
1587             .trapping_unwrap()
1588             >= nsubscriptions
1589                 .checked_mul(size_of::<Pollable>())
1590                 .trapping_unwrap()
1591                 .checked_add(
1592                     nsubscriptions
1593                         .checked_mul(size_of::<u8>())
1594                         .trapping_unwrap()
1595                 )
1596                 .trapping_unwrap()
1597     );
1598     // Store the pollable handles at the beginning, and the bool results at the
1599     // end, so that we don't clobber the bool results when writting the events.
1600     let pollables = out as *mut c_void as *mut Pollable;
1601     let results = out.add(nsubscriptions).cast::<u8>().sub(nsubscriptions);
1602 
1603     // Indefinite sleeping is not supported in preview1.
1604     if nsubscriptions == 0 {
1605         return ERRNO_INVAL;
1606     }
1607 
1608     State::with(|state| {
1609         const EVENTTYPE_CLOCK: u8 = wasi::EVENTTYPE_CLOCK.raw();
1610         const EVENTTYPE_FD_READ: u8 = wasi::EVENTTYPE_FD_READ.raw();
1611         const EVENTTYPE_FD_WRITE: u8 = wasi::EVENTTYPE_FD_WRITE.raw();
1612 
1613         let mut pollables = Pollables {
1614             pointer: pollables,
1615             index: 0,
1616             length: nsubscriptions,
1617         };
1618 
1619         for subscription in subscriptions {
1620             pollables.push(match subscription.u.tag {
1621                 EVENTTYPE_CLOCK => {
1622                     let clock = &subscription.u.u.clock;
1623                     let absolute = (clock.flags & SUBCLOCKFLAGS_SUBSCRIPTION_CLOCK_ABSTIME)
1624                         == SUBCLOCKFLAGS_SUBSCRIPTION_CLOCK_ABSTIME;
1625                     match clock.id {
1626                         CLOCKID_REALTIME => {
1627                             let timeout = if absolute {
1628                                 // Convert `clock.timeout` to `Datetime`.
1629                                 let mut datetime = wall_clock::Datetime {
1630                                     seconds: clock.timeout / 1_000_000_000,
1631                                     nanoseconds: (clock.timeout % 1_000_000_000) as _,
1632                                 };
1633 
1634                                 // Subtract `now`.
1635                                 let now = wall_clock::now();
1636                                 datetime.seconds -= now.seconds;
1637                                 if datetime.nanoseconds < now.nanoseconds {
1638                                     datetime.seconds -= 1;
1639                                     datetime.nanoseconds += 1_000_000_000;
1640                                 }
1641                                 datetime.nanoseconds -= now.nanoseconds;
1642 
1643                                 // Convert to nanoseconds.
1644                                 let nanos = datetime
1645                                     .seconds
1646                                     .checked_mul(1_000_000_000)
1647                                     .ok_or(ERRNO_OVERFLOW)?;
1648                                 nanos
1649                                     .checked_add(datetime.nanoseconds.into())
1650                                     .ok_or(ERRNO_OVERFLOW)?
1651                             } else {
1652                                 clock.timeout
1653                             };
1654 
1655                             monotonic_clock::subscribe(timeout, false)
1656                         }
1657 
1658                         CLOCKID_MONOTONIC => monotonic_clock::subscribe(clock.timeout, absolute),
1659 
1660                         _ => return Err(ERRNO_INVAL),
1661                     }
1662                 }
1663 
1664                 EVENTTYPE_FD_READ => {
1665                     let stream = state
1666                         .descriptors()
1667                         .get_read_stream(subscription.u.u.fd_read.file_descriptor)?;
1668                     streams::subscribe_to_input_stream(stream)
1669                 }
1670 
1671                 EVENTTYPE_FD_WRITE => {
1672                     let stream = state
1673                         .descriptors()
1674                         .get_write_stream(subscription.u.u.fd_write.file_descriptor)?;
1675                     streams::subscribe_to_output_stream(stream)
1676                 }
1677 
1678                 _ => return Err(ERRNO_INVAL),
1679             });
1680         }
1681 
1682         let vec = state.import_alloc.with_buffer(
1683             results,
1684             nsubscriptions
1685                 .checked_mul(size_of::<bool>())
1686                 .trapping_unwrap(),
1687             || poll::poll_oneoff(slice::from_raw_parts(pollables.pointer, pollables.length)),
1688         );
1689 
1690         assert_eq!(vec.len(), nsubscriptions);
1691         assert_eq!(vec.as_ptr(), results);
1692         forget(vec);
1693 
1694         drop(pollables);
1695 
1696         let ready = subscriptions
1697             .iter()
1698             .enumerate()
1699             .filter_map(|(i, s)| (*results.add(i) != 0).then_some(s));
1700 
1701         let mut count = 0;
1702 
1703         for subscription in ready {
1704             let error;
1705             let type_;
1706             let nbytes;
1707             let flags;
1708 
1709             match subscription.u.tag {
1710                 EVENTTYPE_CLOCK => {
1711                     error = ERRNO_SUCCESS;
1712                     type_ = wasi::EVENTTYPE_CLOCK;
1713                     nbytes = 0;
1714                     flags = 0;
1715                 }
1716 
1717                 EVENTTYPE_FD_READ => {
1718                     type_ = wasi::EVENTTYPE_FD_READ;
1719                     let ds = state.descriptors();
1720                     let desc = ds
1721                         .get(subscription.u.u.fd_read.file_descriptor)
1722                         .trapping_unwrap();
1723                     match desc {
1724                         Descriptor::Streams(streams) => match &streams.type_ {
1725                             StreamType::File(file) => match filesystem::stat(file.fd) {
1726                                 Ok(stat) => {
1727                                     error = ERRNO_SUCCESS;
1728                                     nbytes = stat.size.saturating_sub(file.position.get());
1729                                     flags = if nbytes == 0 {
1730                                         EVENTRWFLAGS_FD_READWRITE_HANGUP
1731                                     } else {
1732                                         0
1733                                     };
1734                                 }
1735                                 Err(e) => {
1736                                     error = e.into();
1737                                     nbytes = 1;
1738                                     flags = 0;
1739                                 }
1740                             },
1741                             StreamType::Socket(connection) => {
1742                                 unreachable!() // TODO
1743                                                /*
1744                                                match tcp::bytes_readable(*connection) {
1745                                                    Ok(result) => {
1746                                                        error = ERRNO_SUCCESS;
1747                                                        nbytes = result.0;
1748                                                        flags = if result.1 {
1749                                                            EVENTRWFLAGS_FD_READWRITE_HANGUP
1750                                                        } else {
1751                                                            0
1752                                                        };
1753                                                    }
1754                                                    Err(e) => {
1755                                                        error = e.into();
1756                                                        nbytes = 0;
1757                                                        flags = 0;
1758                                                    }
1759                                                }
1760                                                */
1761                             }
1762                             StreamType::Stdio => {
1763                                 error = ERRNO_SUCCESS;
1764                                 nbytes = 1;
1765                                 flags = 0;
1766                             }
1767                         },
1768                         _ => unreachable!(),
1769                     }
1770                 }
1771                 EVENTTYPE_FD_WRITE => {
1772                     type_ = wasi::EVENTTYPE_FD_WRITE;
1773                     let ds = state.descriptors();
1774                     let desc = ds
1775                         .get(subscription.u.u.fd_write.file_descriptor)
1776                         .trapping_unwrap();
1777                     match desc {
1778                         Descriptor::Streams(streams) => match streams.type_ {
1779                             StreamType::File(_) | StreamType::Stdio => {
1780                                 error = ERRNO_SUCCESS;
1781                                 nbytes = 1;
1782                                 flags = 0;
1783                             }
1784                             StreamType::Socket(connection) => {
1785                                 unreachable!() // TODO
1786                                                /*
1787                                                match tcp::bytes_writable(connection) {
1788                                                    Ok(result) => {
1789                                                        error = ERRNO_SUCCESS;
1790                                                        nbytes = result.0;
1791                                                        flags = if result.1 {
1792                                                            EVENTRWFLAGS_FD_READWRITE_HANGUP
1793                                                        } else {
1794                                                            0
1795                                                        };
1796                                                    }
1797                                                    Err(e) => {
1798                                                        error = e.into();
1799                                                        nbytes = 0;
1800                                                        flags = 0;
1801                                                    }
1802                                                }
1803                                                */
1804                             }
1805                         },
1806                         _ => unreachable!(),
1807                     }
1808                 }
1809 
1810                 _ => unreachable!(),
1811             }
1812 
1813             *out.add(count) = Event {
1814                 userdata: subscription.userdata,
1815                 error,
1816                 type_,
1817                 fd_readwrite: EventFdReadwrite { nbytes, flags },
1818             };
1819 
1820             count += 1;
1821         }
1822 
1823         *nevents = count;
1824 
1825         Ok(())
1826     })
1827 }
1828 
1829 /// Terminate the process normally. An exit code of 0 indicates successful
1830 /// termination of the program. The meanings of other values is dependent on
1831 /// the environment.
1832 #[no_mangle]
1833 pub unsafe extern "C" fn proc_exit(rval: Exitcode) -> ! {
1834     let status = if rval == 0 { Ok(()) } else { Err(()) };
1835     exit::exit(status); // does not return
1836     unreachable!("host exit implementation didn't exit!") // actually unreachable
1837 }
1838 
1839 /// Send a signal to the process of the calling thread.
1840 /// Note: This is similar to `raise` in POSIX.
1841 #[no_mangle]
1842 pub unsafe extern "C" fn proc_raise(sig: Signal) -> Errno {
1843     unreachable!()
1844 }
1845 
1846 /// Temporarily yield execution of the calling thread.
1847 /// Note: This is similar to `sched_yield` in POSIX.
1848 #[no_mangle]
1849 pub unsafe extern "C" fn sched_yield() -> Errno {
1850     // TODO: This is not yet covered in Preview2.
1851 
1852     ERRNO_SUCCESS
1853 }
1854 
1855 /// Write high-quality random data into a buffer.
1856 /// This function blocks when the implementation is unable to immediately
1857 /// provide sufficient high-quality random data.
1858 /// This function may execute slowly, so when large mounts of random data are
1859 /// required, it's advisable to use this function to seed a pseudo-random
1860 /// number generator, rather than to provide the random data directly.
1861 #[no_mangle]
1862 pub unsafe extern "C" fn random_get(buf: *mut u8, buf_len: Size) -> Errno {
1863     if matches!(
1864         get_allocation_state(),
1865         AllocationState::StackAllocated | AllocationState::StateAllocated
1866     ) {
1867         State::with(|state| {
1868             assert_eq!(buf_len as u32 as Size, buf_len);
1869             let result = state
1870                 .import_alloc
1871                 .with_buffer(buf, buf_len, || random::get_random_bytes(buf_len as u64));
1872             assert_eq!(result.as_ptr(), buf);
1873 
1874             // The returned buffer's memory was allocated in `buf`, so don't separately
1875             // free it.
1876             forget(result);
1877 
1878             Ok(())
1879         })
1880     } else {
1881         ERRNO_SUCCESS
1882     }
1883 }
1884 
1885 /// Accept a new incoming connection.
1886 /// Note: This is similar to `accept` in POSIX.
1887 #[no_mangle]
1888 pub unsafe extern "C" fn sock_accept(fd: Fd, flags: Fdflags, connection: *mut Fd) -> Errno {
1889     unreachable!()
1890 }
1891 
1892 /// Receive a message from a socket.
1893 /// Note: This is similar to `recv` in POSIX, though it also supports reading
1894 /// the data into multiple buffers in the manner of `readv`.
1895 #[no_mangle]
1896 pub unsafe extern "C" fn sock_recv(
1897     fd: Fd,
1898     ri_data_ptr: *const Iovec,
1899     ri_data_len: usize,
1900     ri_flags: Riflags,
1901     ro_datalen: *mut Size,
1902     ro_flags: *mut Roflags,
1903 ) -> Errno {
1904     unreachable!()
1905 }
1906 
1907 /// Send a message on a socket.
1908 /// Note: This is similar to `send` in POSIX, though it also supports writing
1909 /// the data from multiple buffers in the manner of `writev`.
1910 #[no_mangle]
1911 pub unsafe extern "C" fn sock_send(
1912     fd: Fd,
1913     si_data_ptr: *const Ciovec,
1914     si_data_len: usize,
1915     si_flags: Siflags,
1916     so_datalen: *mut Size,
1917 ) -> Errno {
1918     unreachable!()
1919 }
1920 
1921 /// Shut down socket send and receive channels.
1922 /// Note: This is similar to `shutdown` in POSIX.
1923 #[no_mangle]
1924 pub unsafe extern "C" fn sock_shutdown(fd: Fd, how: Sdflags) -> Errno {
1925     unreachable!()
1926 }
1927 
1928 fn datetime_to_timestamp(datetime: filesystem::Datetime) -> Timestamp {
1929     u64::from(datetime.nanoseconds).saturating_add(datetime.seconds.saturating_mul(1_000_000_000))
1930 }
1931 
1932 fn at_flags_from_lookupflags(flags: Lookupflags) -> filesystem::PathFlags {
1933     if flags & LOOKUPFLAGS_SYMLINK_FOLLOW == LOOKUPFLAGS_SYMLINK_FOLLOW {
1934         filesystem::PathFlags::SYMLINK_FOLLOW
1935     } else {
1936         filesystem::PathFlags::empty()
1937     }
1938 }
1939 
1940 fn o_flags_from_oflags(flags: Oflags) -> filesystem::OpenFlags {
1941     let mut o_flags = filesystem::OpenFlags::empty();
1942     if flags & OFLAGS_CREAT == OFLAGS_CREAT {
1943         o_flags |= filesystem::OpenFlags::CREATE;
1944     }
1945     if flags & OFLAGS_DIRECTORY == OFLAGS_DIRECTORY {
1946         o_flags |= filesystem::OpenFlags::DIRECTORY;
1947     }
1948     if flags & OFLAGS_EXCL == OFLAGS_EXCL {
1949         o_flags |= filesystem::OpenFlags::EXCLUSIVE;
1950     }
1951     if flags & OFLAGS_TRUNC == OFLAGS_TRUNC {
1952         o_flags |= filesystem::OpenFlags::TRUNCATE;
1953     }
1954     o_flags
1955 }
1956 
1957 fn descriptor_flags_from_flags(rights: Rights, fdflags: Fdflags) -> filesystem::DescriptorFlags {
1958     let mut flags = filesystem::DescriptorFlags::empty();
1959     if rights & wasi::RIGHTS_FD_READ == wasi::RIGHTS_FD_READ {
1960         flags |= filesystem::DescriptorFlags::READ;
1961     }
1962     if rights & wasi::RIGHTS_FD_WRITE == wasi::RIGHTS_FD_WRITE {
1963         flags |= filesystem::DescriptorFlags::WRITE;
1964     }
1965     if fdflags & wasi::FDFLAGS_SYNC == wasi::FDFLAGS_SYNC {
1966         flags |= filesystem::DescriptorFlags::FILE_INTEGRITY_SYNC;
1967     }
1968     if fdflags & wasi::FDFLAGS_DSYNC == wasi::FDFLAGS_DSYNC {
1969         flags |= filesystem::DescriptorFlags::DATA_INTEGRITY_SYNC;
1970     }
1971     if fdflags & wasi::FDFLAGS_RSYNC == wasi::FDFLAGS_RSYNC {
1972         flags |= filesystem::DescriptorFlags::REQUESTED_WRITE_SYNC;
1973     }
1974     flags
1975 }
1976 
1977 impl From<filesystem::ErrorCode> for Errno {
1978     #[inline(never)] // Disable inlining as this is bulky and relatively cold.
1979     fn from(err: filesystem::ErrorCode) -> Errno {
1980         match err {
1981             // Use a black box to prevent the optimizer from generating a
1982             // lookup table, which would require a static initializer.
1983             filesystem::ErrorCode::Access => black_box(ERRNO_ACCES),
1984             filesystem::ErrorCode::WouldBlock => ERRNO_AGAIN,
1985             filesystem::ErrorCode::Already => ERRNO_ALREADY,
1986             filesystem::ErrorCode::BadDescriptor => ERRNO_BADF,
1987             filesystem::ErrorCode::Busy => ERRNO_BUSY,
1988             filesystem::ErrorCode::Deadlock => ERRNO_DEADLK,
1989             filesystem::ErrorCode::Quota => ERRNO_DQUOT,
1990             filesystem::ErrorCode::Exist => ERRNO_EXIST,
1991             filesystem::ErrorCode::FileTooLarge => ERRNO_FBIG,
1992             filesystem::ErrorCode::IllegalByteSequence => ERRNO_ILSEQ,
1993             filesystem::ErrorCode::InProgress => ERRNO_INPROGRESS,
1994             filesystem::ErrorCode::Interrupted => ERRNO_INTR,
1995             filesystem::ErrorCode::Invalid => ERRNO_INVAL,
1996             filesystem::ErrorCode::Io => ERRNO_IO,
1997             filesystem::ErrorCode::IsDirectory => ERRNO_ISDIR,
1998             filesystem::ErrorCode::Loop => ERRNO_LOOP,
1999             filesystem::ErrorCode::TooManyLinks => ERRNO_MLINK,
2000             filesystem::ErrorCode::MessageSize => ERRNO_MSGSIZE,
2001             filesystem::ErrorCode::NameTooLong => ERRNO_NAMETOOLONG,
2002             filesystem::ErrorCode::NoDevice => ERRNO_NODEV,
2003             filesystem::ErrorCode::NoEntry => ERRNO_NOENT,
2004             filesystem::ErrorCode::NoLock => ERRNO_NOLCK,
2005             filesystem::ErrorCode::InsufficientMemory => ERRNO_NOMEM,
2006             filesystem::ErrorCode::InsufficientSpace => ERRNO_NOSPC,
2007             filesystem::ErrorCode::Unsupported => ERRNO_NOTSUP,
2008             filesystem::ErrorCode::NotDirectory => ERRNO_NOTDIR,
2009             filesystem::ErrorCode::NotEmpty => ERRNO_NOTEMPTY,
2010             filesystem::ErrorCode::NotRecoverable => ERRNO_NOTRECOVERABLE,
2011             filesystem::ErrorCode::NoTty => ERRNO_NOTTY,
2012             filesystem::ErrorCode::NoSuchDevice => ERRNO_NXIO,
2013             filesystem::ErrorCode::Overflow => ERRNO_OVERFLOW,
2014             filesystem::ErrorCode::NotPermitted => ERRNO_PERM,
2015             filesystem::ErrorCode::Pipe => ERRNO_PIPE,
2016             filesystem::ErrorCode::ReadOnly => ERRNO_ROFS,
2017             filesystem::ErrorCode::InvalidSeek => ERRNO_SPIPE,
2018             filesystem::ErrorCode::TextFileBusy => ERRNO_TXTBSY,
2019             filesystem::ErrorCode::CrossDevice => ERRNO_XDEV,
2020         }
2021     }
2022 }
2023 
2024 impl From<filesystem::DescriptorType> for wasi::Filetype {
2025     fn from(ty: filesystem::DescriptorType) -> wasi::Filetype {
2026         match ty {
2027             filesystem::DescriptorType::RegularFile => FILETYPE_REGULAR_FILE,
2028             filesystem::DescriptorType::Directory => FILETYPE_DIRECTORY,
2029             filesystem::DescriptorType::BlockDevice => FILETYPE_BLOCK_DEVICE,
2030             filesystem::DescriptorType::CharacterDevice => FILETYPE_CHARACTER_DEVICE,
2031             // preview1 never had a FIFO code.
2032             filesystem::DescriptorType::Fifo => FILETYPE_UNKNOWN,
2033             // TODO: Add a way to disginguish between FILETYPE_SOCKET_STREAM and
2034             // FILETYPE_SOCKET_DGRAM.
2035             filesystem::DescriptorType::Socket => unreachable!(),
2036             filesystem::DescriptorType::SymbolicLink => FILETYPE_SYMBOLIC_LINK,
2037             filesystem::DescriptorType::Unknown => FILETYPE_UNKNOWN,
2038         }
2039     }
2040 }
2041 
2042 #[repr(C)]
2043 pub struct File {
2044     /// The handle to the preview2 descriptor that this file is referencing.
2045     fd: filesystem::Descriptor,
2046 
2047     /// The descriptor type, as supplied by filesystem::get_type at opening
2048     descriptor_type: filesystem::DescriptorType,
2049 
2050     /// The current-position pointer.
2051     position: Cell<filesystem::Filesize>,
2052 
2053     /// In append mode, all writes append to the file.
2054     append: bool,
2055 
2056     /// In blocking mode, read and write calls dispatch to blocking_read and
2057     /// blocking_write on the underlying streams. When false, read and write
2058     /// dispatch to stream's plain read and write.
2059     blocking: bool,
2060 }
2061 
2062 impl File {
2063     fn is_dir(&self) -> bool {
2064         match self.descriptor_type {
2065             filesystem::DescriptorType::Directory => true,
2066             _ => false,
2067         }
2068     }
2069 }
2070 
2071 const PAGE_SIZE: usize = 65536;
2072 
2073 /// The maximum path length. WASI doesn't explicitly guarantee this, but all
2074 /// popular OS's have a `PATH_MAX` of at most 4096, so that's enough for this
2075 /// polyfill.
2076 const PATH_MAX: usize = 4096;
2077 
2078 /// Maximum number of bytes to cache for a `wasi::Dirent` plus its path name.
2079 const DIRENT_CACHE: usize = 256;
2080 
2081 /// A canary value to detect memory corruption within `State`.
2082 const MAGIC: u32 = u32::from_le_bytes(*b"ugh!");
2083 
2084 #[repr(C)] // used for now to keep magic1 and magic2 at the start and end
2085 struct State {
2086     /// A canary constant value located at the beginning of this structure to
2087     /// try to catch memory corruption coming from the bottom.
2088     magic1: u32,
2089 
2090     /// Used to coordinate allocations of `cabi_import_realloc`
2091     import_alloc: ImportAlloc,
2092 
2093     /// Storage of mapping from preview1 file descriptors to preview2 file
2094     /// descriptors.
2095     ///
2096     /// Do not use this member directly - use State::descriptors() to ensure
2097     /// lazy initialization happens.
2098     descriptors: RefCell<Option<Descriptors>>,
2099 
2100     /// Auxiliary storage to handle the `path_readlink` function.
2101     path_buf: UnsafeCell<MaybeUninit<[u8; PATH_MAX]>>,
2102 
2103     /// Long-lived bump allocated memory arena.
2104     ///
2105     /// This is used for the cabi_export_realloc to allocate data passed to the
2106     /// `run` entrypoint. Allocations in this arena are safe to use for
2107     /// the lifetime of the State struct. It may also be used for import allocations
2108     /// which need to be long-lived, by using `import_alloc.with_arena`.
2109     long_lived_arena: BumpArena,
2110 
2111     /// Arguments. Initialized lazily. Access with `State::get_args` to take care of
2112     /// initialization.
2113     args: Cell<Option<&'static [WasmStr]>>,
2114 
2115     /// Environment variables. Initialized lazily. Access with `State::get_environment`
2116     /// to take care of initialization.
2117     env_vars: Cell<Option<&'static [StrTuple]>>,
2118 
2119     /// Cache for the `fd_readdir` call for a final `wasi::Dirent` plus path
2120     /// name that didn't fit into the caller's buffer.
2121     dirent_cache: DirentCache,
2122 
2123     /// The string `..` for use by the directory iterator.
2124     dotdot: [UnsafeCell<u8>; 2],
2125 
2126     /// Another canary constant located at the end of the structure to catch
2127     /// memory corruption coming from the bottom.
2128     magic2: u32,
2129 }
2130 
2131 struct DirentCache {
2132     stream: Cell<Option<DirectoryEntryStream>>,
2133     for_fd: Cell<wasi::Fd>,
2134     cookie: Cell<wasi::Dircookie>,
2135     cached_dirent: Cell<wasi::Dirent>,
2136     path_data: UnsafeCell<MaybeUninit<[u8; DIRENT_CACHE]>>,
2137 }
2138 
2139 struct DirectoryEntryStream(filesystem::DirectoryEntryStream);
2140 
2141 impl Drop for DirectoryEntryStream {
2142     fn drop(&mut self) {
2143         filesystem::drop_directory_entry_stream(self.0);
2144     }
2145 }
2146 
2147 #[repr(C)]
2148 pub struct WasmStr {
2149     ptr: *const u8,
2150     len: usize,
2151 }
2152 
2153 #[repr(C)]
2154 pub struct WasmStrList {
2155     base: *const WasmStr,
2156     len: usize,
2157 }
2158 
2159 #[repr(C)]
2160 pub struct StrTuple {
2161     key: WasmStr,
2162     value: WasmStr,
2163 }
2164 
2165 #[derive(Copy, Clone)]
2166 #[repr(C)]
2167 pub struct StrTupleList {
2168     base: *const StrTuple,
2169     len: usize,
2170 }
2171 
2172 const fn bump_arena_size() -> usize {
2173     // The total size of the struct should be a page, so start there
2174     let mut start = PAGE_SIZE;
2175 
2176     // Remove the big chunks of the struct, the `path_buf` and `descriptors`
2177     // fields.
2178     start -= PATH_MAX;
2179     start -= size_of::<Descriptors>();
2180     start -= size_of::<DirentCache>();
2181 
2182     // Remove miscellaneous metadata also stored in state.
2183     start -= 16 * size_of::<usize>();
2184 
2185     // Everything else is the `command_data` allocation.
2186     start
2187 }
2188 
2189 // Statically assert that the `State` structure is the size of a wasm page. This
2190 // mostly guarantees that it's not larger than one page which is relied upon
2191 // below.
2192 const _: () = {
2193     let _size_assert: [(); PAGE_SIZE] = [(); size_of::<RefCell<State>>()];
2194 };
2195 
2196 #[allow(unused)]
2197 #[repr(i32)]
2198 enum AllocationState {
2199     StackUnallocated,
2200     StackAllocating,
2201     StackAllocated,
2202     StateAllocating,
2203     StateAllocated,
2204 }
2205 
2206 #[allow(improper_ctypes)]
2207 extern "C" {
2208     fn get_state_ptr() -> *const RefCell<State>;
2209     fn set_state_ptr(state: *const RefCell<State>);
2210     fn get_allocation_state() -> AllocationState;
2211     fn set_allocation_state(state: AllocationState);
2212     fn get_stderr_stream() -> Fd;
2213     fn set_stderr_stream(fd: Fd);
2214 }
2215 
2216 impl State {
2217     fn with(f: impl FnOnce(&State) -> Result<(), Errno>) -> Errno {
2218         let ptr = State::ptr();
2219         let ptr = ptr.try_borrow().unwrap_or_else(|_| unreachable!());
2220         assert_eq!(ptr.magic1, MAGIC);
2221         assert_eq!(ptr.magic2, MAGIC);
2222         let ret = f(&*ptr);
2223         match ret {
2224             Ok(()) => ERRNO_SUCCESS,
2225             Err(err) => err,
2226         }
2227     }
2228 
2229     fn with_mut(f: impl FnOnce(&mut State) -> Result<(), Errno>) -> Errno {
2230         let ptr = State::ptr();
2231         let mut ptr = ptr.try_borrow_mut().unwrap_or_else(|_| unreachable!());
2232         assert_eq!(ptr.magic1, MAGIC);
2233         assert_eq!(ptr.magic2, MAGIC);
2234         let ret = f(&mut *ptr);
2235         match ret {
2236             Ok(()) => ERRNO_SUCCESS,
2237             Err(err) => err,
2238         }
2239     }
2240 
2241     fn ptr() -> &'static RefCell<State> {
2242         unsafe {
2243             let mut ptr = get_state_ptr();
2244             if ptr.is_null() {
2245                 ptr = State::new();
2246                 set_state_ptr(ptr);
2247             }
2248             &*ptr
2249         }
2250     }
2251 
2252     #[cold]
2253     fn new() -> &'static RefCell<State> {
2254         #[link(wasm_import_module = "__main_module__")]
2255         extern "C" {
2256             fn cabi_realloc(
2257                 old_ptr: *mut u8,
2258                 old_len: usize,
2259                 align: usize,
2260                 new_len: usize,
2261             ) -> *mut u8;
2262         }
2263 
2264         assert!(matches!(
2265             unsafe { get_allocation_state() },
2266             AllocationState::StackAllocated
2267         ));
2268 
2269         unsafe { set_allocation_state(AllocationState::StateAllocating) };
2270 
2271         let ret = unsafe {
2272             cabi_realloc(
2273                 ptr::null_mut(),
2274                 0,
2275                 mem::align_of::<RefCell<State>>(),
2276                 mem::size_of::<RefCell<State>>(),
2277             ) as *mut RefCell<State>
2278         };
2279 
2280         unsafe { set_allocation_state(AllocationState::StateAllocated) };
2281 
2282         unsafe {
2283             ret.write(RefCell::new(State {
2284                 magic1: MAGIC,
2285                 magic2: MAGIC,
2286                 import_alloc: ImportAlloc::new(),
2287                 descriptors: RefCell::new(None),
2288                 path_buf: UnsafeCell::new(MaybeUninit::uninit()),
2289                 long_lived_arena: BumpArena::new(),
2290                 args: Cell::new(None),
2291                 env_vars: Cell::new(None),
2292                 dirent_cache: DirentCache {
2293                     stream: Cell::new(None),
2294                     for_fd: Cell::new(0),
2295                     cookie: Cell::new(wasi::DIRCOOKIE_START),
2296                     cached_dirent: Cell::new(wasi::Dirent {
2297                         d_next: 0,
2298                         d_ino: 0,
2299                         d_type: FILETYPE_UNKNOWN,
2300                         d_namlen: 0,
2301                     }),
2302                     path_data: UnsafeCell::new(MaybeUninit::uninit()),
2303                 },
2304                 dotdot: [UnsafeCell::new(b'.'), UnsafeCell::new(b'.')],
2305             }));
2306             &*ret
2307         }
2308     }
2309 
2310     /// Accessor for the descriptors member that ensures it is properly initialized
2311     fn descriptors<'a>(&'a self) -> impl Deref<Target = Descriptors> + 'a {
2312         let mut d = self
2313             .descriptors
2314             .try_borrow_mut()
2315             .unwrap_or_else(|_| unreachable!());
2316         if d.is_none() {
2317             *d = Some(Descriptors::new(&self.import_alloc, &self.long_lived_arena));
2318         }
2319         RefMut::map(d, |d| d.as_mut().unwrap_or_else(|| unreachable!()))
2320     }
2321 
2322     /// Mut accessor for the descriptors member that ensures it is properly initialized
2323     fn descriptors_mut<'a>(&'a mut self) -> impl DerefMut + Deref<Target = Descriptors> + 'a {
2324         let mut d = self
2325             .descriptors
2326             .try_borrow_mut()
2327             .unwrap_or_else(|_| unreachable!());
2328         if d.is_none() {
2329             *d = Some(Descriptors::new(&self.import_alloc, &self.long_lived_arena));
2330         }
2331         RefMut::map(d, |d| d.as_mut().unwrap_or_else(|| unreachable!()))
2332     }
2333 
2334     fn get_environment(&self) -> &[StrTuple] {
2335         if self.env_vars.get().is_none() {
2336             #[link(wasm_import_module = "environment")]
2337             extern "C" {
2338                 #[link_name = "get-environment"]
2339                 fn get_environment_import(rval: *mut StrTupleList);
2340             }
2341             let mut list = StrTupleList {
2342                 base: std::ptr::null(),
2343                 len: 0,
2344             };
2345             self.import_alloc
2346                 .with_arena(&self.long_lived_arena, || unsafe {
2347                     get_environment_import(&mut list as *mut _)
2348                 });
2349             self.env_vars.set(Some(unsafe {
2350                 /* allocation comes from long lived arena, so it is safe to
2351                  * cast this to a &'static slice: */
2352                 std::slice::from_raw_parts(list.base, list.len)
2353             }));
2354         }
2355         self.env_vars.get().trapping_unwrap()
2356     }
2357 
2358     fn get_args(&self) -> &[WasmStr] {
2359         if self.args.get().is_none() {
2360             #[link(wasm_import_module = "environment")]
2361             extern "C" {
2362                 #[link_name = "get-arguments"]
2363                 fn get_args_import(rval: *mut WasmStrList);
2364             }
2365             let mut list = WasmStrList {
2366                 base: std::ptr::null(),
2367                 len: 0,
2368             };
2369             self.import_alloc
2370                 .with_arena(&self.long_lived_arena, || unsafe {
2371                     get_args_import(&mut list as *mut _)
2372                 });
2373             self.args.set(Some(unsafe {
2374                 /* allocation comes from long lived arena, so it is safe to
2375                  * cast this to a &'static slice: */
2376                 std::slice::from_raw_parts(list.base, list.len)
2377             }));
2378         }
2379         self.args.get().trapping_unwrap()
2380     }
2381 }
2382