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