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