1 // The proxy world has no filesystem which most of this file is concerned with,
2 // so disable many warnings to avoid having to contort code too much for the
3 // proxy world.
4 #![cfg_attr(
5     feature = "proxy",
6     expect(
7         unused_mut,
8         unused_variables,
9         dead_code,
10         unused_imports,
11         unreachable_code,
12         reason = "stripped down in proxy build",
13     )
14 )]
15 
16 use crate::bindings::wasi::clocks::{monotonic_clock, wall_clock};
17 use crate::bindings::wasi::io::poll;
18 use crate::bindings::wasi::io::streams;
19 use crate::bindings::wasi::random::random;
20 use core::cell::OnceCell;
21 use core::cell::{Cell, RefCell, RefMut, UnsafeCell};
22 use core::cmp::min;
23 use core::ffi::c_void;
24 use core::hint::black_box;
25 use core::mem::{self, ManuallyDrop, MaybeUninit, align_of, forget, size_of};
26 use core::num::NonZeroUsize;
27 use core::ops::{Deref, DerefMut};
28 use core::ptr::{self, null_mut};
29 use core::slice;
30 use poll::Pollable;
31 use wasip1::*;
32 
33 #[cfg(not(feature = "proxy"))]
34 use crate::bindings::wasi::filesystem::types as filesystem;
35 
36 #[cfg(any(
37     all(feature = "command", feature = "reactor"),
38     all(feature = "reactor", feature = "proxy"),
39     all(feature = "command", feature = "proxy"),
40 ))]
41 compile_error!(
42     "only one of the `command`, `reactor` or `proxy` features may be selected at a time"
43 );
44 
45 #[macro_use]
46 mod macros;
47 
48 mod descriptors;
49 use crate::descriptors::{Descriptor, Descriptors, StreamType, Streams};
50 
51 pub mod bindings {
52     #[cfg(feature = "command")]
53     wit_bindgen_rust_macro::generate!({
54         path: "../wasi/src/p2/wit",
55         world: "wasi:cli/command",
56         raw_strings,
57         runtime_path: "crate::bindings::wit_bindgen_rt_shim",
58         // Automatically generated bindings for these functions will allocate
59         // Vecs, which in turn pulls in the panic machinery from std, which
60         // creates vtables that end up in the wasm elem section, which we
61         // can't support in these special core-wasm adapters.
62         // Instead, we manually define the bindings for these functions in
63         // terms of raw pointers.
64         skip: [
65             "run",
66             "get-environment",
67             "poll",
68             "[method]outgoing-datagram-stream.send",
69         ],
70         generate_all,
71         disable_custom_section_link_helpers: true,
72     });
73 
74     #[cfg(feature = "reactor")]
75     wit_bindgen_rust_macro::generate!({
76         path: "../wasi/src/p2/wit",
77         world: "wasi:cli/imports",
78         raw_strings,
79         runtime_path: "crate::bindings::wit_bindgen_rt_shim",
80         // Automatically generated bindings for these functions will allocate
81         // Vecs, which in turn pulls in the panic machinery from std, which
82         // creates vtables that end up in the wasm elem section, which we
83         // can't support in these special core-wasm adapters.
84         // Instead, we manually define the bindings for these functions in
85         // terms of raw pointers.
86         skip: [
87             "get-environment",
88             "poll",
89             "[method]outgoing-datagram-stream.send",
90         ],
91         generate_all,
92         disable_custom_section_link_helpers: true,
93     });
94 
95     #[cfg(feature = "proxy")]
96     wit_bindgen_rust_macro::generate!({
97         path: "../wasi-http/wit",
98         inline: r#"
99             package wasmtime:adapter;
100 
101             world adapter {
102                 import wasi:clocks/[email protected];
103                 import wasi:clocks/[email protected];
104                 import wasi:random/[email protected];
105                 import wasi:cli/[email protected];
106                 import wasi:cli/[email protected];
107                 import wasi:cli/[email protected];
108             }
109         "#,
110         world: "wasmtime:adapter/adapter",
111         raw_strings,
112         runtime_path: "crate::bindings::wit_bindgen_rt_shim",
113         skip: ["poll", "[method]outgoing-datagram-stream.send"],
114         generate_all,
115         disable_custom_section_link_helpers: true,
116     });
117 
118     pub mod wit_bindgen_rt_shim {
119         pub use bitflags;
120 
maybe_link_cabi_realloc()121         pub fn maybe_link_cabi_realloc() {}
122     }
123 }
124 
125 #[unsafe(export_name = "wasi:cli/run@0.2.6#run")]
126 #[cfg(feature = "command")]
run() -> u32127 pub extern "C" fn run() -> u32 {
128     #[link(wasm_import_module = "__main_module__")]
129     unsafe extern "C" {
130         safe fn _start();
131     }
132     _start();
133     0
134 }
135 
136 #[cfg(feature = "proxy")]
137 macro_rules! cfg_filesystem_available {
138     ($($t:tt)*) => {
139         wasip1::ERRNO_NOTSUP
140     };
141 }
142 #[cfg(not(feature = "proxy"))]
143 macro_rules! cfg_filesystem_available {
144     ($($t:tt)*) => ($($t)*);
145 }
146 
147 // The unwrap/expect methods in std pull panic when they fail, which pulls
148 // in unwinding machinery that we can't use in the adapter. Instead, use this
149 // extension trait to get postfixed upwrap on Option and Result.
150 trait TrappingUnwrap<T> {
trapping_unwrap(self) -> T151     fn trapping_unwrap(self) -> T;
152 }
153 
154 impl<T> TrappingUnwrap<T> for Option<T> {
trapping_unwrap(self) -> T155     fn trapping_unwrap(self) -> T {
156         match self {
157             Some(t) => t,
158             None => unreachable!(),
159         }
160     }
161 }
162 
163 impl<T, E> TrappingUnwrap<T> for Result<T, E> {
trapping_unwrap(self) -> T164     fn trapping_unwrap(self) -> T {
165         match self {
166             Ok(t) => t,
167             Err(_) => unreachable!(),
168         }
169     }
170 }
171 
172 /// Allocate a file descriptor which will generate an `ERRNO_BADF` if passed to
173 /// any WASI Preview 1 function implemented by this adapter.
174 ///
175 /// This is intended for use by `wasi-libc` during its incremental transition
176 /// from WASI Preview 1 to Preview 2.  It will use this function to reserve
177 /// descriptors for its own use, valid only for use with libc functions.
178 #[unsafe(no_mangle)]
adapter_open_badfd(fd: &mut u32) -> Errno179 pub unsafe extern "C" fn adapter_open_badfd(fd: &mut u32) -> Errno {
180     State::with(|state| {
181         *fd = state.descriptors_mut().open(Descriptor::Bad)?;
182         Ok(())
183     })
184 }
185 
186 /// Close a descriptor previously opened using `adapter_open_badfd`.
187 #[unsafe(no_mangle)]
adapter_close_badfd(fd: u32) -> Errno188 pub unsafe extern "C" fn adapter_close_badfd(fd: u32) -> Errno {
189     State::with(|state| state.descriptors_mut().close(fd))
190 }
191 
192 #[unsafe(no_mangle)]
reset_adapter_state()193 pub unsafe extern "C" fn reset_adapter_state() {
194     unsafe {
195         let state = get_state_ptr();
196         if !state.is_null() {
197             State::init(state)
198         }
199     }
200 }
201 
202 #[unsafe(no_mangle)]
cabi_import_realloc( old_ptr: *mut u8, old_size: usize, align: usize, new_size: usize, ) -> *mut u8203 pub unsafe extern "C" fn cabi_import_realloc(
204     old_ptr: *mut u8,
205     old_size: usize,
206     align: usize,
207     new_size: usize,
208 ) -> *mut u8 {
209     let mut ptr = null_mut::<u8>();
210     State::with(|state| {
211         let mut alloc = state.import_alloc.replace(ImportAlloc::None);
212         ptr = unsafe { alloc.alloc(old_ptr, old_size, align, new_size) };
213         state.import_alloc.set(alloc);
214         Ok(())
215     });
216     ptr
217 }
218 
219 /// Different ways that calling imports can allocate memory.
220 ///
221 /// This behavior is used to customize the behavior of `cabi_import_realloc`.
222 /// This is configured within `State` whenever an import is called that may
223 /// invoke `cabi_import_realloc`.
224 ///
225 /// The general idea behind these various behaviors of import allocation is
226 /// that we're limited for space in the adapter here to 1 page of memory but
227 /// that may not fit the total sum of arguments, environment variables, and
228 /// preopens. WASIp1 APIs all provide user-provided buffers as well for these
229 /// allocations so we technically don't need to store them in the adapter
230 /// itself. Instead what this does is it tries to copy strings and such directly
231 /// into their destination pointers where possible.
232 ///
233 /// The types requiring allocation in the WASIp2 APIs that the WASIp1 APIs call
234 /// are relatively simple. They all look like `list<T>` where `T` only has
235 /// indirections in the form of `String`. This means that we can apply a
236 /// "clever" hack where the alignment of an allocation is used to disambiguate
237 /// whether we're allocating a string or allocating the `list<T>` allocation.
238 /// This signal with alignment means that we can configure where everything
239 /// goes.
240 ///
241 /// For example consider `args_sizes_get` and `args_get`. When `args_sizes_get`
242 /// is called the `list<T>` allocation happens first with alignment 4. This
243 /// must be valid for the rest of the strings since the canonical ABI will fill
244 /// it in, so it's allocated from `State::temporary_data`. Next all other
245 /// arguments will be `string` type with alignment 1. These are also allocated
246 /// within `State::temporary_data` but they're "allocated on top of one
247 /// another" meaning that internal allocator state isn't updated after a string
248 /// is allocated. While these strings are discarded their sizes are all summed
249 /// up and returned from `args_sizes_get`.
250 ///
251 /// Later though when `args_get` is called it's a similar allocation strategy
252 /// except that strings are instead redirected to the allocation provided to
253 /// `args_get` itself. This enables strings to be directly allocated into their
254 /// destinations.
255 ///
256 /// Overall this means that we're limiting the maximum number of arguments plus
257 /// the size of the largest string, but otherwise we're not limiting the total
258 /// size of all arguments (or env vars, preopens, etc).
259 enum ImportAlloc {
260     /// A single allocation from the provided `BumpAlloc` is supported. After
261     /// the single allocation is performed all future allocations will fail.
262     OneAlloc(BumpAlloc),
263 
264     /// An allocator intended for `list<T>` where `T` has string types but no
265     /// other indirections. String allocations are discarded but counted for
266     /// size.
267     ///
268     /// This allocator will use `alloc` for all allocations. Any string-related
269     /// allocation, detected via an alignment of 1, is considered "temporary"
270     /// and doesn't affect the internal state of the allocator. The allocation
271     /// is assumed to not be used after the import call returns.
272     ///
273     /// The total sum of all string sizes, however, is accumulated within
274     /// `strings_size`.
275     CountAndDiscardStrings {
276         strings_size: usize,
277         alloc: BumpAlloc,
278     },
279 
280     /// An allocator intended for `list<T>` where `T` has string types but no
281     /// other indirections. String allocations go into `strings` and the
282     /// `list<..>` allocation goes into `pointers`.
283     ///
284     /// This allocator enables placing strings within a caller-supplied buffer
285     /// configured with `strings`. The `pointers` allocation is
286     /// `State::temporary_data`.
287     ///
288     /// This will additionally over-allocate strings with one extra byte to be
289     /// nul-terminated or `=`-terminated in the case of env vars.
290     SeparateStringsAndPointers {
291         strings: BumpAlloc,
292         pointers: BumpAlloc,
293     },
294 
295     /// An allocator specifically for getting the nth string allocation used
296     /// for preopens.
297     ///
298     /// This will allocate everything into `alloc`. All strings other than the
299     /// `nth` string, however, will be discarded (the allocator's state is reset
300     /// after the allocation). This means that the pointer returned for the
301     /// `nth` string will be retained in `alloc` while all others will be
302     /// discarded.
303     ///
304     /// The `cur` count starts at 0 and counts up per-string.
305     GetPreopenPath {
306         cur: u32,
307         nth: u32,
308         alloc: BumpAlloc,
309     },
310 
311     /// No import allocator is configured and if an allocation happens then
312     /// this will abort.
313     None,
314 }
315 
316 impl ImportAlloc {
317     /// To be used by cabi_import_realloc only!
alloc( &mut self, old_ptr: *mut u8, old_size: usize, align: usize, size: usize, ) -> *mut u8318     unsafe fn alloc(
319         &mut self,
320         old_ptr: *mut u8,
321         old_size: usize,
322         align: usize,
323         size: usize,
324     ) -> *mut u8 {
325         // This is ... a hack. This is a hack in subtle ways that is quite
326         // brittle and may break over time. There's only one case for the
327         // `realloc`-like-behavior in the canonical ABI and that's when the host
328         // is transferring a string to the guest and the host has a different
329         // string encoding. For example JS uses utf-16 (ish) and Rust/WASIp1 use
330         // utf-8. That means that when this adapter is used with a JS host
331         // realloc behavior may be triggered in which case `old_ptr` may not be
332         // null.
333         //
334         // In the case that `old_ptr` may not be null we come to the first
335         // brittle assumption: it's assumed that this is shrinking memory. In
336         // the canonical ABI overlarge allocations are made originally and then
337         // shrunk afterwards once encoding is finished. This means that the
338         // first allocation is too big and the `realloc` call is shrinking
339         // memory. This assumption may be violated in the future if the
340         // canonical ABI is updated to handle growing strings in addition to
341         // shrinking strings. (e.g. starting with an assume-ascii path and then
342         // falling back to an ok-needs-more-space path for larger unicode code
343         // points).
344         //
345         // This comes to the second brittle assumption, nothing happens here
346         // when a shrink happens. This is brittle for each of the cases below,
347         // enumerated here:
348         //
349         // * For `OneAlloc` this isn't the end of the world. That's already
350         //   asserting that only a single string is allocated. Returning the
351         //   original pointer keeps the pointer the same and the host will keep
352         //   track of the appropriate length. In this case the final length is
353         //   read out of the return value of a function, meaning that everything
354         //   actually works out here.
355         //
356         // * For `CountAndDiscardStrings` we're relying on the fact that
357         //   this is only used for `environ_sizes_get` and `args_sizes_get`. In
358         //   both situations we're actually going to return an "overlarge"
359         //   return value for the size of arguments and return values. By
360         //   assuming memory shrinks after the first allocation the return value
361         //   of `environ_sizes_get` and `args_sizes_get` will be the overlong
362         //   approximation for all strings. That means that the final exact size
363         //   won't be what's returned. This ends up being ok because technically
364         //   nothing about WASI says that those blocks have to be exact-sized.
365         //   In our case we're (ab)using that to force the caller to make an
366         //   overlarge return area which we'll allocate into. All-in-all we
367         //   don't track the shrink request and ignore the size.
368         //
369         // * For `SeparateStringsAndPointers` it's similar to the previous case
370         //   except the weird part is that the caller is providing the
371         //   argument/env space buffer to write into. It's over-large because of
372         //   the case of `CountAndDiscardStrings` above, but we'll exploit that
373         //   here and end up having space between all the arguments. Technically
374         //   WASI doesn't say all the strings have to be adjacent, so this
375         //   should work out in practice.
376         //
377         // * Finally for `GetPreopenPath` this works out only insofar that the
378         //   `State::temporary_alloc` space is used to store the path. The
379         //   WASI-provided buffer is precisely sized, not overly large, meaning
380         //   that we're forced to copy from `temporary_alloc` into the
381         //   destination buffer for this WASI call.
382         //
383         // Basically it's a case-by-case basis here that enables ignoring
384         // shrinking return calls here. Not robust.
385         if !old_ptr.is_null() {
386             assert!(old_size > size);
387             assert_eq!(align, 1);
388             return old_ptr;
389         }
390         match self {
391             ImportAlloc::OneAlloc(alloc) => unsafe {
392                 let ret = alloc.alloc(align, size);
393                 *self = ImportAlloc::None;
394                 ret
395             },
396             ImportAlloc::SeparateStringsAndPointers { strings, pointers } => unsafe {
397                 if align == 1 {
398                     strings.alloc(align, size + 1)
399                 } else {
400                     pointers.alloc(align, size)
401                 }
402             },
403             ImportAlloc::CountAndDiscardStrings {
404                 strings_size,
405                 alloc,
406             } => unsafe {
407                 if align == 1 {
408                     *strings_size += size;
409                     alloc.clone().alloc(align, size)
410                 } else {
411                     alloc.alloc(align, size)
412                 }
413             },
414             ImportAlloc::GetPreopenPath { cur, nth, alloc } => unsafe {
415                 if align == 1 {
416                     let real_alloc = *nth == *cur;
417                     *cur += 1;
418                     if real_alloc {
419                         alloc.alloc(align, size)
420                     } else {
421                         alloc.clone().alloc(align, size)
422                     }
423                 } else {
424                     alloc.alloc(align, size)
425                 }
426             },
427             ImportAlloc::None => {
428                 unreachable!("no allocator configured")
429             }
430         }
431     }
432 }
433 
434 /// Helper type to manage allocations from a `base`/`len` combo.
435 ///
436 /// This isn't really used much in an arena-style per se but it's used in
437 /// combination with the `ImportAlloc` flavors above.
438 #[derive(Clone)]
439 struct BumpAlloc {
440     base: *mut u8,
441     len: usize,
442 }
443 
444 impl BumpAlloc {
alloc(&mut self, align: usize, size: usize) -> *mut u8445     unsafe fn alloc(&mut self, align: usize, size: usize) -> *mut u8 {
446         unsafe {
447             self.align_to(align);
448         }
449         if size > self.len {
450             unreachable!("allocation size is too large")
451         }
452         self.len -= size;
453         let ret = self.base;
454         self.base = unsafe { ret.add(size) };
455         ret
456     }
457 
align_to(&mut self, align: usize)458     unsafe fn align_to(&mut self, align: usize) {
459         if !align.is_power_of_two() {
460             unreachable!("invalid alignment");
461         }
462         let align_offset = self.base.align_offset(align);
463         if align_offset > self.len {
464             unreachable!("failed to allocate")
465         }
466         self.len -= align_offset;
467         self.base = unsafe { self.base.add(align_offset) };
468     }
469 }
470 
471 #[cfg(not(feature = "proxy"))]
472 #[link(wasm_import_module = "wasi:cli/environment@0.2.6")]
473 unsafe extern "C" {
474     #[link_name = "get-arguments"]
wasi_cli_get_arguments(rval: *mut WasmStrList)475     fn wasi_cli_get_arguments(rval: *mut WasmStrList);
476     #[link_name = "get-environment"]
wasi_cli_get_environment(rval: *mut StrTupleList)477     fn wasi_cli_get_environment(rval: *mut StrTupleList);
478 }
479 
480 /// Read command-line argument data.
481 /// The size of the array should match that returned by `args_sizes_get`
482 #[unsafe(no_mangle)]
args_get(argv: *mut *mut u8, argv_buf: *mut u8) -> Errno483 pub unsafe extern "C" fn args_get(argv: *mut *mut u8, argv_buf: *mut u8) -> Errno {
484     State::with(|state| {
485         #[cfg(not(feature = "proxy"))]
486         {
487             let alloc = ImportAlloc::SeparateStringsAndPointers {
488                 strings: BumpAlloc {
489                     base: argv_buf,
490                     len: usize::MAX,
491                 },
492                 pointers: unsafe { state.temporary_alloc() },
493             };
494             let (list, _) = state.with_import_alloc(alloc, || unsafe {
495                 let mut list = WasmStrList {
496                     base: std::ptr::null(),
497                     len: 0,
498                 };
499                 wasi_cli_get_arguments(&mut list);
500                 list
501             });
502 
503             // Fill in `argv` by walking over the returned `list` and then
504             // additionally apply the nul-termination for each argument itself
505             // here.
506             unsafe {
507                 for i in 0..list.len {
508                     let s = list.base.add(i).read();
509                     *argv.add(i) = s.ptr.cast_mut();
510                     *s.ptr.add(s.len).cast_mut() = 0;
511                 }
512             }
513         }
514         Ok(())
515     })
516 }
517 
518 /// Return command-line argument data sizes.
519 #[unsafe(no_mangle)]
args_sizes_get(argc: &mut Size, argv_buf_size: &mut Size) -> Errno520 pub unsafe extern "C" fn args_sizes_get(argc: &mut Size, argv_buf_size: &mut Size) -> Errno {
521     State::with(|state| {
522         #[cfg(feature = "proxy")]
523         {
524             *argc = 0;
525             *argv_buf_size = 0;
526         }
527         #[cfg(not(feature = "proxy"))]
528         {
529             let alloc = ImportAlloc::CountAndDiscardStrings {
530                 strings_size: 0,
531                 alloc: unsafe { state.temporary_alloc() },
532             };
533             let (len, alloc) = state.with_import_alloc(alloc, || unsafe {
534                 let mut list = WasmStrList {
535                     base: std::ptr::null(),
536                     len: 0,
537                 };
538                 wasi_cli_get_arguments(&mut list);
539                 list.len
540             });
541             match alloc {
542                 ImportAlloc::CountAndDiscardStrings {
543                     strings_size,
544                     alloc: _,
545                 } => {
546                     *argc = len;
547                     // add in bytes needed for a 0-byte at the end of each
548                     // argument.
549                     *argv_buf_size = strings_size + len;
550                 }
551                 _ => unreachable!(),
552             }
553         }
554         Ok(())
555     })
556 }
557 
558 /// Read environment variable data.
559 /// The sizes of the buffers should match that returned by `environ_sizes_get`.
560 #[unsafe(no_mangle)]
environ_get(environ: *mut *const u8, environ_buf: *mut u8) -> Errno561 pub unsafe extern "C" fn environ_get(environ: *mut *const u8, environ_buf: *mut u8) -> Errno {
562     State::with(|state| {
563         #[cfg(not(feature = "proxy"))]
564         {
565             let alloc = ImportAlloc::SeparateStringsAndPointers {
566                 strings: BumpAlloc {
567                     base: environ_buf,
568                     len: usize::MAX,
569                 },
570                 pointers: unsafe { state.temporary_alloc() },
571             };
572             let (list, _) = state.with_import_alloc(alloc, || unsafe {
573                 let mut list = StrTupleList {
574                     base: std::ptr::null(),
575                     len: 0,
576                 };
577                 wasi_cli_get_environment(&mut list);
578                 list
579             });
580 
581             // Fill in `environ` by walking over the returned `list`. Strings
582             // are guaranteed to be allocated next to each other with one
583             // extra byte at the end, so also insert the `=` between keys and
584             // the `\0` at the end of the env var.
585             unsafe {
586                 for i in 0..list.len {
587                     let s = list.base.add(i).read();
588                     *environ.add(i) = s.key.ptr;
589                     *s.key.ptr.add(s.key.len).cast_mut() = b'=';
590                     *s.value.ptr.add(s.value.len).cast_mut() = 0;
591                 }
592             }
593         }
594 
595         Ok(())
596     })
597 }
598 
599 /// Return environment variable data sizes.
600 #[unsafe(no_mangle)]
environ_sizes_get( environc: &mut Size, environ_buf_size: &mut Size, ) -> Errno601 pub unsafe extern "C" fn environ_sizes_get(
602     environc: &mut Size,
603     environ_buf_size: &mut Size,
604 ) -> Errno {
605     if !matches!(
606         unsafe { get_allocation_state() },
607         AllocationState::StackAllocated | AllocationState::StateAllocated
608     ) {
609         *environc = 0;
610         *environ_buf_size = 0;
611         return ERRNO_SUCCESS;
612     }
613 
614     State::with(|state| {
615         #[cfg(feature = "proxy")]
616         {
617             *environc = 0;
618             *environ_buf_size = 0;
619         }
620         #[cfg(not(feature = "proxy"))]
621         {
622             let alloc = ImportAlloc::CountAndDiscardStrings {
623                 strings_size: 0,
624                 alloc: unsafe { state.temporary_alloc() },
625             };
626             let (len, alloc) = state.with_import_alloc(alloc, || unsafe {
627                 let mut list = StrTupleList {
628                     base: std::ptr::null(),
629                     len: 0,
630                 };
631                 wasi_cli_get_environment(&mut list);
632                 list.len
633             });
634             match alloc {
635                 ImportAlloc::CountAndDiscardStrings {
636                     strings_size,
637                     alloc: _,
638                 } => {
639                     *environc = len;
640                     // Account for `=` between keys and a 0-byte at the end of
641                     // each key.
642                     *environ_buf_size = strings_size + 2 * len;
643                 }
644                 _ => unreachable!(),
645             }
646         }
647 
648         Ok(())
649     })
650 }
651 
652 /// Return the resolution of a clock.
653 /// Implementations are required to provide a non-zero value for supported clocks. For unsupported clocks,
654 /// return `errno::inval`.
655 /// Note: This is similar to `clock_getres` in POSIX.
656 #[unsafe(no_mangle)]
clock_res_get(id: Clockid, resolution: &mut Timestamp) -> Errno657 pub extern "C" fn clock_res_get(id: Clockid, resolution: &mut Timestamp) -> Errno {
658     match id {
659         CLOCKID_MONOTONIC => {
660             *resolution = monotonic_clock::resolution();
661             ERRNO_SUCCESS
662         }
663         CLOCKID_REALTIME => {
664             let res = wall_clock::resolution();
665             *resolution = match Timestamp::from(res.seconds)
666                 .checked_mul(1_000_000_000)
667                 .and_then(|ns| ns.checked_add(res.nanoseconds.into()))
668             {
669                 Some(ns) => ns,
670                 None => return ERRNO_OVERFLOW,
671             };
672             ERRNO_SUCCESS
673         }
674         _ => ERRNO_BADF,
675     }
676 }
677 
678 /// Return the time value of a clock.
679 /// Note: This is similar to `clock_gettime` in POSIX.
680 #[unsafe(no_mangle)]
clock_time_get( id: Clockid, _precision: Timestamp, time: &mut Timestamp, ) -> Errno681 pub unsafe extern "C" fn clock_time_get(
682     id: Clockid,
683     _precision: Timestamp,
684     time: &mut Timestamp,
685 ) -> Errno {
686     match id {
687         CLOCKID_MONOTONIC => {
688             *time = monotonic_clock::now();
689             ERRNO_SUCCESS
690         }
691         CLOCKID_REALTIME => {
692             let res = wall_clock::now();
693             *time = match Timestamp::from(res.seconds)
694                 .checked_mul(1_000_000_000)
695                 .and_then(|ns| ns.checked_add(res.nanoseconds.into()))
696             {
697                 Some(ns) => ns,
698                 None => return ERRNO_OVERFLOW,
699             };
700             ERRNO_SUCCESS
701         }
702         _ => ERRNO_BADF,
703     }
704 }
705 
706 /// Provide file advisory information on a file descriptor.
707 /// Note: This is similar to `posix_fadvise` in POSIX.
708 #[unsafe(no_mangle)]
fd_advise( fd: Fd, offset: Filesize, len: Filesize, advice: Advice, ) -> Errno709 pub unsafe extern "C" fn fd_advise(
710     fd: Fd,
711     offset: Filesize,
712     len: Filesize,
713     advice: Advice,
714 ) -> Errno {
715     cfg_filesystem_available! {
716         let advice = match advice {
717             ADVICE_NORMAL => filesystem::Advice::Normal,
718             ADVICE_SEQUENTIAL => filesystem::Advice::Sequential,
719             ADVICE_RANDOM => filesystem::Advice::Random,
720             ADVICE_WILLNEED => filesystem::Advice::WillNeed,
721             ADVICE_DONTNEED => filesystem::Advice::DontNeed,
722             ADVICE_NOREUSE => filesystem::Advice::NoReuse,
723             _ => return ERRNO_INVAL,
724         };
725         State::with(|state| {
726             let ds = state.descriptors();
727             let file = ds.get_seekable_file(fd)?;
728             file.fd.advise(offset, len, advice)?;
729             Ok(())
730         })
731     }
732 }
733 
734 /// Force the allocation of space in a file.
735 /// Note: This is similar to `posix_fallocate` in POSIX.
736 #[unsafe(no_mangle)]
fd_allocate(fd: Fd, _offset: Filesize, _len: Filesize) -> Errno737 pub unsafe extern "C" fn fd_allocate(fd: Fd, _offset: Filesize, _len: Filesize) -> Errno {
738     cfg_filesystem_available! {
739         State::with(|state| {
740             let ds = state.descriptors();
741             // For not-files, fail with BADF
742             ds.get_file(fd)?;
743             // For all files, fail with NOTSUP, because this call does not exist in preview 2.
744             Err(wasip1::ERRNO_NOTSUP)
745         })
746     }
747 }
748 
749 /// Close a file descriptor.
750 /// Note: This is similar to `close` in POSIX.
751 #[unsafe(no_mangle)]
fd_close(fd: Fd) -> Errno752 pub unsafe extern "C" fn fd_close(fd: Fd) -> Errno {
753     State::with(|state| {
754         if let Descriptor::Bad = state.descriptors().get(fd)? {
755             return Err(wasip1::ERRNO_BADF);
756         }
757 
758         // If there's a dirent cache entry for this file descriptor then drop
759         // it since the descriptor is being closed and future calls to
760         // `fd_readdir` should return an error.
761         #[cfg(not(feature = "proxy"))]
762         if fd == state.dirent_cache.for_fd.get() {
763             drop(state.dirent_cache.stream.replace(None));
764         }
765 
766         state.descriptors_mut().close(fd)?;
767         Ok(())
768     })
769 }
770 
771 /// Synchronize the data of a file to disk.
772 /// Note: This is similar to `fdatasync` in POSIX.
773 #[unsafe(no_mangle)]
fd_datasync(fd: Fd) -> Errno774 pub unsafe extern "C" fn fd_datasync(fd: Fd) -> Errno {
775     cfg_filesystem_available! {
776         State::with(|state| {
777             let ds = state.descriptors();
778             let file = ds.get_file(fd)?;
779             file.fd.sync_data()?;
780             Ok(())
781         })
782     }
783 }
784 
785 /// Get the attributes of a file descriptor.
786 /// Note: This returns similar flags to `fsync(fd, F_GETFL)` in POSIX, as well as additional fields.
787 #[unsafe(no_mangle)]
fd_fdstat_get(fd: Fd, stat: *mut Fdstat) -> Errno788 pub unsafe extern "C" fn fd_fdstat_get(fd: Fd, stat: *mut Fdstat) -> Errno {
789     cfg_filesystem_available! {
790         State::with(|state| {
791             let ds = state.descriptors();
792             match ds.get(fd)? {
793                 Descriptor::Streams(Streams {
794                     type_: StreamType::File(file),
795                     ..
796                 }) => {
797                     let flags = file.fd.get_flags()?;
798                     let type_ = file.fd.get_type()?;
799                     match type_ {
800                         filesystem::DescriptorType::Directory => {
801                             // Hard-coded set of rights expected by many userlands:
802                             let fs_rights_base = wasip1::RIGHTS_PATH_CREATE_DIRECTORY
803                                 | wasip1::RIGHTS_PATH_CREATE_FILE
804                                 | wasip1::RIGHTS_PATH_LINK_SOURCE
805                                 | wasip1::RIGHTS_PATH_LINK_TARGET
806                                 | wasip1::RIGHTS_PATH_OPEN
807                                 | wasip1::RIGHTS_FD_READDIR
808                                 | wasip1::RIGHTS_PATH_READLINK
809                                 | wasip1::RIGHTS_PATH_RENAME_SOURCE
810                                 | wasip1::RIGHTS_PATH_RENAME_TARGET
811                                 | wasip1::RIGHTS_PATH_SYMLINK
812                                 | wasip1::RIGHTS_PATH_REMOVE_DIRECTORY
813                                 | wasip1::RIGHTS_PATH_UNLINK_FILE
814                                 | wasip1::RIGHTS_PATH_FILESTAT_GET
815                                 | wasip1::RIGHTS_PATH_FILESTAT_SET_TIMES
816                                 | wasip1::RIGHTS_FD_FILESTAT_GET
817                                 | wasip1::RIGHTS_FD_FILESTAT_SET_TIMES;
818 
819                             let fs_rights_inheriting = fs_rights_base
820                                 | wasip1::RIGHTS_FD_DATASYNC
821                                 | wasip1::RIGHTS_FD_READ
822                                 | wasip1::RIGHTS_FD_SEEK
823                                 | wasip1::RIGHTS_FD_FDSTAT_SET_FLAGS
824                                 | wasip1::RIGHTS_FD_SYNC
825                                 | wasip1::RIGHTS_FD_TELL
826                                 | wasip1::RIGHTS_FD_WRITE
827                                 | wasip1::RIGHTS_FD_ADVISE
828                                 | wasip1::RIGHTS_FD_ALLOCATE
829                                 | wasip1::RIGHTS_FD_FILESTAT_GET
830                                 | wasip1::RIGHTS_FD_FILESTAT_SET_SIZE
831                                 | wasip1::RIGHTS_FD_FILESTAT_SET_TIMES
832                                 | wasip1::RIGHTS_POLL_FD_READWRITE;
833 
834                             unsafe {
835                                 stat.write(Fdstat {
836                                     fs_filetype: wasip1::FILETYPE_DIRECTORY,
837                                     fs_flags: 0,
838                                     fs_rights_base,
839                                     fs_rights_inheriting,
840                                 });
841                             }
842                             Ok(())
843                         }
844                         _ => {
845                             let fs_filetype = type_.into();
846 
847                             let mut fs_flags = 0;
848                             let mut fs_rights_base = !0;
849                             if !flags.contains(filesystem::DescriptorFlags::READ) {
850                                 fs_rights_base &= !RIGHTS_FD_READ;
851                                 fs_rights_base &= !RIGHTS_FD_READDIR;
852                             }
853                             if !flags.contains(filesystem::DescriptorFlags::WRITE) {
854                                 fs_rights_base &= !RIGHTS_FD_WRITE;
855                             }
856                             if flags.contains(filesystem::DescriptorFlags::DATA_INTEGRITY_SYNC) {
857                                 fs_flags |= FDFLAGS_DSYNC;
858                             }
859                             if flags.contains(filesystem::DescriptorFlags::REQUESTED_WRITE_SYNC) {
860                                 fs_flags |= FDFLAGS_RSYNC;
861                             }
862                             if flags.contains(filesystem::DescriptorFlags::FILE_INTEGRITY_SYNC) {
863                                 fs_flags |= FDFLAGS_SYNC;
864                             }
865                             if file.append {
866                                 fs_flags |= FDFLAGS_APPEND;
867                             }
868                             if matches!(file.blocking_mode, BlockingMode::NonBlocking) {
869                                 fs_flags |= FDFLAGS_NONBLOCK;
870                             }
871                             let fs_rights_inheriting = fs_rights_base;
872 
873                             unsafe {
874                                 stat.write(Fdstat {
875                                     fs_filetype,
876                                     fs_flags,
877                                     fs_rights_base,
878                                     fs_rights_inheriting,
879                                 });
880                             }
881                             Ok(())
882                         }
883                     }
884                 }
885                 Descriptor::Streams(Streams {
886                     input,
887                     output,
888                     type_: StreamType::Stdio(stdio),
889                 }) => {
890                     let fs_flags = 0;
891                     let mut fs_rights_base = 0;
892                     if input.get().is_some() {
893                         fs_rights_base |= RIGHTS_FD_READ;
894                     }
895                     if output.get().is_some() {
896                         fs_rights_base |= RIGHTS_FD_WRITE;
897                     }
898                     let fs_rights_inheriting = fs_rights_base;
899                     unsafe {
900                         stat.write(Fdstat {
901                             fs_filetype: stdio.filetype(),
902                             fs_flags,
903                             fs_rights_base,
904                             fs_rights_inheriting,
905                         });
906                     }
907                     Ok(())
908                 }
909                 Descriptor::Closed(_) | Descriptor::Bad => Err(ERRNO_BADF),
910             }
911         })
912     }
913 }
914 
915 /// Adjust the flags associated with a file descriptor.
916 /// Note: This is similar to `fcntl(fd, F_SETFL, flags)` in POSIX.
917 #[unsafe(no_mangle)]
fd_fdstat_set_flags(fd: Fd, flags: Fdflags) -> Errno918 pub unsafe extern "C" fn fd_fdstat_set_flags(fd: Fd, flags: Fdflags) -> Errno {
919     // Only support changing the NONBLOCK or APPEND flags.
920     if flags & !(FDFLAGS_NONBLOCK | FDFLAGS_APPEND) != 0 {
921         return wasip1::ERRNO_INVAL;
922     }
923 
924     cfg_filesystem_available! {
925         State::with(|state| {
926             let mut ds = state.descriptors_mut();
927             let file = match ds.get_mut(fd)? {
928                 Descriptor::Streams(Streams {
929                     type_: StreamType::File(file),
930                     ..
931                 }) if !file.is_dir() => file,
932                 _ => Err(wasip1::ERRNO_BADF)?,
933             };
934             file.append = flags & FDFLAGS_APPEND == FDFLAGS_APPEND;
935             file.blocking_mode = if flags & FDFLAGS_NONBLOCK == FDFLAGS_NONBLOCK {
936                 BlockingMode::NonBlocking
937             } else {
938                 BlockingMode::Blocking
939             };
940             Ok(())
941         })
942     }
943 }
944 
945 /// Does not do anything if `fd` corresponds to a valid descriptor and returns [`wasip1::ERRNO_BADF`] otherwise.
946 #[unsafe(no_mangle)]
fd_fdstat_set_rights( fd: Fd, _fs_rights_base: Rights, _fs_rights_inheriting: Rights, ) -> Errno947 pub unsafe extern "C" fn fd_fdstat_set_rights(
948     fd: Fd,
949     _fs_rights_base: Rights,
950     _fs_rights_inheriting: Rights,
951 ) -> Errno {
952     State::with(|state| {
953         let ds = state.descriptors();
954         match ds.get(fd)? {
955             Descriptor::Streams(..) => Err(wasip1::ERRNO_NOTSUP),
956             Descriptor::Closed(..) | Descriptor::Bad => Err(wasip1::ERRNO_BADF),
957         }
958     })
959 }
960 
961 /// Return the attributes of an open file.
962 #[unsafe(no_mangle)]
fd_filestat_get(fd: Fd, buf: &mut Filestat) -> Errno963 pub unsafe extern "C" fn fd_filestat_get(fd: Fd, buf: &mut Filestat) -> Errno {
964     cfg_filesystem_available! {
965         State::with(|state| {
966             let ds = state.descriptors();
967             match ds.get(fd)? {
968                 Descriptor::Streams(Streams {
969                     type_: StreamType::File(file),
970                     ..
971                 }) => {
972                     let stat = file.fd.stat()?;
973                     let metadata_hash = file.fd.metadata_hash()?;
974                     let filetype = stat.type_.into();
975                     *buf = Filestat {
976                         dev: 1,
977                         ino: metadata_hash.lower,
978                         filetype,
979                         nlink: stat.link_count,
980                         size: stat.size,
981                         atim: datetime_to_timestamp(stat.data_access_timestamp),
982                         mtim: datetime_to_timestamp(stat.data_modification_timestamp),
983                         ctim: datetime_to_timestamp(stat.status_change_timestamp),
984                     };
985                     Ok(())
986                 }
987                 // Stdio is all zero fields, except for filetype character device
988                 Descriptor::Streams(Streams {
989                     type_: StreamType::Stdio(stdio),
990                     ..
991                 }) => {
992                     *buf = Filestat {
993                         dev: 0,
994                         ino: 0,
995                         filetype: stdio.filetype(),
996                         nlink: 0,
997                         size: 0,
998                         atim: 0,
999                         mtim: 0,
1000                         ctim: 0,
1001                     };
1002                     Ok(())
1003                 }
1004                 _ => Err(wasip1::ERRNO_BADF),
1005             }
1006         })
1007     }
1008 }
1009 
1010 /// Adjust the size of an open file. If this increases the file's size, the extra bytes are filled with zeros.
1011 /// Note: This is similar to `ftruncate` in POSIX.
1012 #[unsafe(no_mangle)]
fd_filestat_set_size(fd: Fd, size: Filesize) -> Errno1013 pub unsafe extern "C" fn fd_filestat_set_size(fd: Fd, size: Filesize) -> Errno {
1014     cfg_filesystem_available! {
1015         State::with(|state| {
1016             let ds = state.descriptors();
1017             let file = ds.get_file(fd)?;
1018             file.fd.set_size(size)?;
1019             Ok(())
1020         })
1021     }
1022 }
1023 
1024 #[cfg(not(feature = "proxy"))]
systimespec(set: bool, ts: Timestamp, now: bool) -> Result<filesystem::NewTimestamp, Errno>1025 fn systimespec(set: bool, ts: Timestamp, now: bool) -> Result<filesystem::NewTimestamp, Errno> {
1026     if set && now {
1027         Err(wasip1::ERRNO_INVAL)
1028     } else if set {
1029         Ok(filesystem::NewTimestamp::Timestamp(filesystem::Datetime {
1030             seconds: ts / 1_000_000_000,
1031             nanoseconds: (ts % 1_000_000_000) as _,
1032         }))
1033     } else if now {
1034         Ok(filesystem::NewTimestamp::Now)
1035     } else {
1036         Ok(filesystem::NewTimestamp::NoChange)
1037     }
1038 }
1039 
1040 /// Adjust the timestamps of an open file or directory.
1041 /// Note: This is similar to `futimens` in POSIX.
1042 #[unsafe(no_mangle)]
fd_filestat_set_times( fd: Fd, atim: Timestamp, mtim: Timestamp, fst_flags: Fstflags, ) -> Errno1043 pub unsafe extern "C" fn fd_filestat_set_times(
1044     fd: Fd,
1045     atim: Timestamp,
1046     mtim: Timestamp,
1047     fst_flags: Fstflags,
1048 ) -> Errno {
1049     cfg_filesystem_available! {
1050         State::with(|state| {
1051             let atim = systimespec(
1052                 fst_flags & FSTFLAGS_ATIM == FSTFLAGS_ATIM,
1053                 atim,
1054                 fst_flags & FSTFLAGS_ATIM_NOW == FSTFLAGS_ATIM_NOW,
1055             )?;
1056             let mtim = systimespec(
1057                 fst_flags & FSTFLAGS_MTIM == FSTFLAGS_MTIM,
1058                 mtim,
1059                 fst_flags & FSTFLAGS_MTIM_NOW == FSTFLAGS_MTIM_NOW,
1060             )?;
1061             let ds = state.descriptors();
1062             let file = ds.get_file(fd)?;
1063             file.fd.set_times(atim, mtim)?;
1064             Ok(())
1065         })
1066     }
1067 }
1068 
1069 /// Read from a file descriptor, without using and updating the file descriptor's offset.
1070 /// Note: This is similar to `preadv` in POSIX.
1071 #[unsafe(no_mangle)]
fd_pread( fd: Fd, mut iovs_ptr: *const Iovec, mut iovs_len: usize, offset: Filesize, nread: &mut Size, ) -> Errno1072 pub unsafe extern "C" fn fd_pread(
1073     fd: Fd,
1074     mut iovs_ptr: *const Iovec,
1075     mut iovs_len: usize,
1076     offset: Filesize,
1077     nread: &mut Size,
1078 ) -> Errno {
1079     cfg_filesystem_available! {
1080         let (ptr, len) = unsafe {
1081             // Skip leading non-empty buffers.
1082             while iovs_len > 1 && (*iovs_ptr).buf_len == 0 {
1083                 iovs_ptr = iovs_ptr.add(1);
1084                 iovs_len -= 1;
1085             }
1086             if iovs_len == 0 {
1087                 *nread = 0;
1088                 return ERRNO_SUCCESS;
1089             }
1090             ((*iovs_ptr).buf, (*iovs_ptr).buf_len)
1091         };
1092 
1093         State::with(|state| {
1094 
1095             let ds = state.descriptors();
1096             let file = ds.get_file(fd)?;
1097             let (data, end) = state
1098                 .with_one_import_alloc(ptr, len, || file.fd.read(len as u64, offset))?;
1099             assert_eq!(data.as_ptr(), ptr);
1100             assert!(data.len() <= len);
1101 
1102             let len = data.len();
1103             forget(data);
1104             if !end && len == 0 {
1105                 Err(ERRNO_INTR)
1106             } else {
1107                 *nread = len;
1108                 Ok(())
1109             }
1110         })
1111     }
1112 }
1113 
1114 /// Return a description of the given preopened file descriptor.
1115 #[unsafe(no_mangle)]
fd_prestat_get(fd: Fd, buf: *mut Prestat) -> Errno1116 pub unsafe extern "C" fn fd_prestat_get(fd: Fd, buf: *mut Prestat) -> Errno {
1117     if !matches!(
1118         unsafe { get_allocation_state() },
1119         AllocationState::StackAllocated | AllocationState::StateAllocated
1120     ) {
1121         return ERRNO_BADF;
1122     }
1123 
1124     // For the proxy adapter don't return `ERRNO_NOTSUP` through below, instead
1125     // always return `ERRNO_BADF` which is the indicator that prestats aren't
1126     // available.
1127     if cfg!(feature = "proxy") {
1128         return ERRNO_BADF;
1129     }
1130 
1131     cfg_filesystem_available! {
1132         State::with(|state| {
1133             let ds = state.descriptors();
1134             match ds.get(fd)? {
1135                 Descriptor::Streams(Streams {
1136                     type_: StreamType::File(File {
1137                         preopen_name_len: Some(len),
1138                         ..
1139                     }),
1140                     ..
1141                 }) => {
1142                     unsafe {
1143                         buf.write(Prestat {
1144                             tag: 0,
1145                             u: PrestatU {
1146                                 dir: PrestatDir {
1147                                     pr_name_len: len.get(),
1148                                 },
1149                             },
1150                         });
1151                     }
1152 
1153                     Ok(())
1154                 }
1155                 _ => Err(ERRNO_BADF),
1156             }
1157         })
1158     }
1159 }
1160 
1161 /// Return a description of the given preopened file descriptor.
1162 #[unsafe(no_mangle)]
fd_prestat_dir_name(fd: Fd, path: *mut u8, path_max_len: Size) -> Errno1163 pub unsafe extern "C" fn fd_prestat_dir_name(fd: Fd, path: *mut u8, path_max_len: Size) -> Errno {
1164     cfg_filesystem_available! {
1165         State::with(|state| {
1166             let ds = state.descriptors();
1167             let preopen_len = match ds.get(fd)? {
1168                 Descriptor::Streams(Streams {
1169                     type_: StreamType::File(File {
1170                         preopen_name_len: Some(len),
1171                         ..
1172                     }),
1173                     ..
1174                 }) => len.get(),
1175                 _ => return Err(ERRNO_BADF),
1176             };
1177             if preopen_len > path_max_len {
1178                 return Err(ERRNO_NAMETOOLONG)
1179             }
1180 
1181             unsafe {
1182                 ds.get_preopen_path(state, fd, path, path_max_len);
1183             }
1184             Ok(())
1185         })
1186     }
1187 }
1188 
1189 /// Write to a file descriptor, without using and updating the file descriptor's offset.
1190 /// Note: This is similar to `pwritev` in POSIX.
1191 #[unsafe(no_mangle)]
fd_pwrite( fd: Fd, mut iovs_ptr: *const Ciovec, mut iovs_len: usize, offset: Filesize, nwritten: &mut Size, ) -> Errno1192 pub unsafe extern "C" fn fd_pwrite(
1193     fd: Fd,
1194     mut iovs_ptr: *const Ciovec,
1195     mut iovs_len: usize,
1196     offset: Filesize,
1197     nwritten: &mut Size,
1198 ) -> Errno {
1199     cfg_filesystem_available! {
1200         let bytes = unsafe {
1201             // Skip leading non-empty buffers.
1202             while iovs_len > 1 && (*iovs_ptr).buf_len == 0 {
1203                 iovs_ptr = iovs_ptr.add(1);
1204                 iovs_len -= 1;
1205             }
1206             if iovs_len == 0 {
1207                 *nwritten = 0;
1208                 return ERRNO_SUCCESS;
1209             }
1210 
1211             let ptr = (*iovs_ptr).buf;
1212             let len = (*iovs_ptr).buf_len;
1213             slice::from_raw_parts(ptr, len)
1214         };
1215 
1216         State::with(|state| {
1217             let ds = state.descriptors();
1218             let file = ds.get_seekable_file(fd)?;
1219             let bytes = if file.append {
1220                 match file.fd.append_via_stream()?.blocking_write_and_flush(bytes) {
1221                     Ok(()) => bytes.len(),
1222                     Err(streams::StreamError::Closed) => 0,
1223                     Err(streams::StreamError::LastOperationFailed(e)) => {
1224                         return Err(stream_error_to_errno(e))
1225                     }
1226                 }
1227             } else {
1228                 file.fd.write(bytes, offset)? as usize
1229             };
1230             *nwritten = bytes;
1231             Ok(())
1232         })
1233     }
1234 }
1235 
1236 /// Read from a file descriptor.
1237 /// Note: This is similar to `readv` in POSIX.
1238 #[unsafe(no_mangle)]
fd_read( fd: Fd, mut iovs_ptr: *const Iovec, mut iovs_len: usize, nread: &mut Size, ) -> Errno1239 pub unsafe extern "C" fn fd_read(
1240     fd: Fd,
1241     mut iovs_ptr: *const Iovec,
1242     mut iovs_len: usize,
1243     nread: &mut Size,
1244 ) -> Errno {
1245     let (ptr, len) = unsafe {
1246         // Skip leading non-empty buffers.
1247         while iovs_len > 1 && (*iovs_ptr).buf_len == 0 {
1248             iovs_ptr = iovs_ptr.add(1);
1249             iovs_len -= 1;
1250         }
1251         if iovs_len == 0 {
1252             *nread = 0;
1253             return ERRNO_SUCCESS;
1254         }
1255 
1256         ((*iovs_ptr).buf, (*iovs_ptr).buf_len)
1257     };
1258 
1259     State::with(|state| {
1260         let ds = state.descriptors();
1261         match ds.get(fd)? {
1262             Descriptor::Streams(streams) => {
1263                 #[cfg(not(feature = "proxy"))]
1264                 let blocking_mode = if let StreamType::File(file) = &streams.type_ {
1265                     file.blocking_mode
1266                 } else {
1267                     BlockingMode::Blocking
1268                 };
1269                 #[cfg(feature = "proxy")]
1270                 let blocking_mode = BlockingMode::Blocking;
1271 
1272                 let read_len = u64::try_from(len).trapping_unwrap();
1273                 let wasi_stream = streams.get_read_stream()?;
1274                 let data = match state
1275                     .with_one_import_alloc(ptr, len, || blocking_mode.read(wasi_stream, read_len))
1276                 {
1277                     Ok(data) => data,
1278                     Err(streams::StreamError::Closed) => {
1279                         *nread = 0;
1280                         return Ok(());
1281                     }
1282                     Err(streams::StreamError::LastOperationFailed(e)) => {
1283                         Err(stream_error_to_errno(e))?
1284                     }
1285                 };
1286 
1287                 assert_eq!(data.as_ptr(), ptr);
1288                 assert!(data.len() <= len);
1289 
1290                 // If this is a file, keep the current-position pointer up to date.
1291                 #[cfg(not(feature = "proxy"))]
1292                 if let StreamType::File(file) = &streams.type_ {
1293                     file.position
1294                         .set(file.position.get() + data.len() as filesystem::Filesize);
1295                 }
1296 
1297                 let len = data.len();
1298                 *nread = len;
1299                 forget(data);
1300                 Ok(())
1301             }
1302             Descriptor::Closed(_) | Descriptor::Bad => Err(ERRNO_BADF),
1303         }
1304     })
1305 }
1306 
stream_error_to_errno(err: streams::Error) -> Errno1307 fn stream_error_to_errno(err: streams::Error) -> Errno {
1308     #[cfg(feature = "proxy")]
1309     return ERRNO_IO;
1310     #[cfg(not(feature = "proxy"))]
1311     match filesystem::filesystem_error_code(&err) {
1312         Some(code) => code.into(),
1313         None => ERRNO_IO,
1314     }
1315 }
1316 
1317 /// Read directory entries from a directory.
1318 /// When successful, the contents of the output buffer consist of a sequence of
1319 /// directory entries. Each directory entry consists of a `dirent` object,
1320 /// followed by `dirent::d_namlen` bytes holding the name of the directory
1321 /// entry.
1322 /// This function fills the output buffer as much as possible, potentially
1323 /// truncating the last directory entry. This allows the caller to grow its
1324 /// read buffer size in case it's too small to fit a single large directory
1325 /// entry, or skip the oversized directory entry.
1326 #[unsafe(no_mangle)]
1327 #[cfg(feature = "proxy")]
fd_readdir( fd: Fd, buf: *mut u8, buf_len: Size, cookie: Dircookie, bufused: *mut Size, ) -> Errno1328 pub unsafe extern "C" fn fd_readdir(
1329     fd: Fd,
1330     buf: *mut u8,
1331     buf_len: Size,
1332     cookie: Dircookie,
1333     bufused: *mut Size,
1334 ) -> Errno {
1335     wasip1::ERRNO_NOTSUP
1336 }
1337 
1338 #[unsafe(no_mangle)]
1339 #[cfg(not(feature = "proxy"))]
fd_readdir( fd: Fd, buf: *mut u8, buf_len: Size, cookie: Dircookie, bufused: &mut Size, ) -> Errno1340 pub unsafe extern "C" fn fd_readdir(
1341     fd: Fd,
1342     buf: *mut u8,
1343     buf_len: Size,
1344     cookie: Dircookie,
1345     bufused: &mut Size,
1346 ) -> Errno {
1347     let mut buf = unsafe { slice::from_raw_parts_mut(buf, buf_len) };
1348     return State::with(|state| {
1349         // First determine if there's an entry in the dirent cache to use. This
1350         // is done to optimize the use case where a large directory is being
1351         // used with a fixed-sized buffer to avoid re-invoking the `readdir`
1352         // function and continuing to use the same iterator.
1353         //
1354         // This is a bit tricky since the requested state in this function call
1355         // must match the prior state of the dirent stream, if any, so that's
1356         // all validated here as well.
1357         //
1358         // Note that for the duration of this function the `cookie` specifier is
1359         // the `n`th iteration of the `readdir` stream return value.
1360         let prev_stream = state.dirent_cache.stream.replace(None);
1361         let stream =
1362             if state.dirent_cache.for_fd.get() == fd && state.dirent_cache.cookie.get() == cookie {
1363                 prev_stream
1364             } else {
1365                 None
1366             };
1367 
1368         // Compute the inode of `.` so that the iterator can produce an entry
1369         // for it.
1370         let ds = state.descriptors();
1371         let dir = ds.get_dir(fd)?;
1372 
1373         let mut iter;
1374         match stream {
1375             // All our checks passed and a dirent cache was available with a
1376             // prior stream. Construct an iterator which will yield its first
1377             // entry from cache and is additionally resuming at the `cookie`
1378             // specified.
1379             Some(stream) => {
1380                 iter = DirectoryEntryIterator {
1381                     stream,
1382                     state,
1383                     cookie,
1384                     use_cache: true,
1385                     dir_descriptor: &dir.fd,
1386                 }
1387             }
1388 
1389             // Either a dirent stream wasn't previously available, a different
1390             // cookie was requested, or a brand new directory is now being read.
1391             // In these situations fall back to resuming reading the directory
1392             // from scratch, and the `cookie` value indicates how many items
1393             // need skipping.
1394             None => {
1395                 iter = DirectoryEntryIterator {
1396                     state,
1397                     cookie: wasip1::DIRCOOKIE_START,
1398                     use_cache: false,
1399                     stream: DirectoryEntryStream(dir.fd.read_directory()?),
1400                     dir_descriptor: &dir.fd,
1401                 };
1402 
1403                 // Skip to the entry that is requested by the `cookie`
1404                 // parameter.
1405                 for _ in wasip1::DIRCOOKIE_START..cookie {
1406                     match iter.next() {
1407                         Some(Ok(_)) => {}
1408                         Some(Err(e)) => return Err(e),
1409                         None => {
1410                             *bufused = 0;
1411                             return Ok(());
1412                         }
1413                     }
1414                 }
1415             }
1416         };
1417 
1418         while buf.len() > 0 {
1419             let (dirent, name) = match iter.next() {
1420                 Some(Ok(pair)) => pair,
1421                 Some(Err(e)) => return Err(e),
1422                 None => break,
1423             };
1424 
1425             // Copy a `dirent` describing this entry into the destination `buf`,
1426             // truncating it if it doesn't fit entirely.
1427             let bytes = unsafe {
1428                 slice::from_raw_parts(
1429                     (&dirent as *const wasip1::Dirent).cast::<u8>(),
1430                     size_of::<Dirent>(),
1431                 )
1432             };
1433             let dirent_bytes_to_copy = buf.len().min(bytes.len());
1434             unsafe {
1435                 ptr::copy_nonoverlapping(bytes.as_ptr(), buf.as_mut_ptr(), dirent_bytes_to_copy);
1436             }
1437             buf = &mut buf[dirent_bytes_to_copy..];
1438 
1439             // Copy the name bytes into the output `buf`, truncating it if it
1440             // doesn't fit.
1441             //
1442             // Note that this might be a 0-byte copy if the `dirent` was
1443             // truncated or fit entirely into the destination.
1444             let name_bytes_to_copy = buf.len().min(name.len());
1445             unsafe {
1446                 ptr::copy_nonoverlapping(
1447                     name.as_ptr().cast(),
1448                     buf.as_mut_ptr(),
1449                     name_bytes_to_copy,
1450                 );
1451             }
1452 
1453             buf = &mut buf[name_bytes_to_copy..];
1454 
1455             // If the buffer is empty then that means the value may be
1456             // truncated, so save the state of the iterator in our dirent cache
1457             // and return.
1458             //
1459             // Note that `cookie - 1` is stored here since `iter.cookie` stores
1460             // the address of the next item, and we're rewinding one item since
1461             // the current item is truncated and will want to resume from that
1462             // in the future.
1463             //
1464             // Additionally note that this caching step is skipped if the name
1465             // to store doesn't actually fit in the dirent cache's path storage.
1466             // In that case there's not much we can do and let the next call to
1467             // `fd_readdir` start from scratch.
1468             if buf.len() == 0 && name.len() <= DIRENT_CACHE {
1469                 let DirectoryEntryIterator { stream, cookie, .. } = iter;
1470                 state.dirent_cache.stream.set(Some(stream));
1471                 state.dirent_cache.for_fd.set(fd);
1472                 state.dirent_cache.cookie.set(cookie - 1);
1473                 state.dirent_cache.cached_dirent.set(dirent);
1474                 unsafe {
1475                     ptr::copy(
1476                         name.as_ptr().cast::<u8>(),
1477                         (*state.dirent_cache.path_data.get()).as_mut_ptr() as *mut u8,
1478                         name.len(),
1479                     );
1480                 }
1481                 break;
1482             }
1483         }
1484 
1485         *bufused = buf_len - buf.len();
1486         Ok(())
1487     });
1488 
1489     struct DirectoryEntryIterator<'a> {
1490         state: &'a State,
1491         use_cache: bool,
1492         cookie: Dircookie,
1493         stream: DirectoryEntryStream,
1494         dir_descriptor: &'a filesystem::Descriptor,
1495     }
1496 
1497     impl<'a> Iterator for DirectoryEntryIterator<'a> {
1498         // Note the usage of `UnsafeCell<u8>` here to indicate that the data can
1499         // alias the storage within `state`.
1500         type Item = Result<(wasip1::Dirent, &'a [UnsafeCell<u8>]), Errno>;
1501 
1502         fn next(&mut self) -> Option<Self::Item> {
1503             let current_cookie = self.cookie;
1504 
1505             self.cookie += 1;
1506 
1507             // Preview1 programs expect to see `.` and `..` in the traversal, but
1508             // Preview2 excludes them, so re-add them.
1509             match current_cookie {
1510                 0 => {
1511                     let metadata_hash = match self.dir_descriptor.metadata_hash() {
1512                         Ok(h) => h,
1513                         Err(e) => return Some(Err(e.into())),
1514                     };
1515                     let dirent = wasip1::Dirent {
1516                         d_next: self.cookie,
1517                         d_ino: metadata_hash.lower,
1518                         d_type: wasip1::FILETYPE_DIRECTORY,
1519                         d_namlen: 1,
1520                     };
1521                     return Some(Ok((dirent, &self.state.dotdot[..1])));
1522                 }
1523                 1 => {
1524                     let dirent = wasip1::Dirent {
1525                         d_next: self.cookie,
1526                         d_ino: 0,
1527                         d_type: wasip1::FILETYPE_DIRECTORY,
1528                         d_namlen: 2,
1529                     };
1530                     return Some(Ok((dirent, &self.state.dotdot[..])));
1531                 }
1532                 _ => {}
1533             }
1534 
1535             if self.use_cache {
1536                 self.use_cache = false;
1537                 return Some(unsafe {
1538                     let dirent = self.state.dirent_cache.cached_dirent.as_ptr().read();
1539                     let ptr = (*(*self.state.dirent_cache.path_data.get()).as_ptr())
1540                         .as_ptr()
1541                         .cast();
1542                     let buffer = slice::from_raw_parts(ptr, dirent.d_namlen as usize);
1543                     Ok((dirent, buffer))
1544                 });
1545             }
1546             let entry = self
1547                 .state
1548                 .with_one_temporary_alloc(|| self.stream.0.read_directory_entry());
1549             let entry = match entry {
1550                 Ok(Some(entry)) => entry,
1551                 Ok(None) => return None,
1552                 Err(e) => return Some(Err(e.into())),
1553             };
1554 
1555             let filesystem::DirectoryEntry { type_, name } = entry;
1556             let d_ino = self
1557                 .dir_descriptor
1558                 .metadata_hash_at(filesystem::PathFlags::empty(), &name)
1559                 .map(|h| h.lower)
1560                 .unwrap_or(0);
1561             let name = ManuallyDrop::new(name);
1562             let dirent = wasip1::Dirent {
1563                 d_next: self.cookie,
1564                 d_ino,
1565                 d_namlen: u32::try_from(name.len()).trapping_unwrap(),
1566                 d_type: type_.into(),
1567             };
1568             // Extend the lifetime of `name` to the `self.state` lifetime for
1569             // this iterator since the data for the name lives within state.
1570             let name = unsafe {
1571                 assert_eq!(name.as_ptr(), self.state.temporary_data.get().cast());
1572                 slice::from_raw_parts(name.as_ptr().cast(), name.len())
1573             };
1574             Some(Ok((dirent, name)))
1575         }
1576     }
1577 }
1578 
1579 /// Atomically replace a file descriptor by renumbering another file descriptor.
1580 /// Due to the strong focus on thread safety, this environment does not provide
1581 /// a mechanism to duplicate or renumber a file descriptor to an arbitrary
1582 /// number, like `dup2()`. This would be prone to race conditions, as an actual
1583 /// file descriptor with the same number could be allocated by a different
1584 /// thread at the same time.
1585 /// This function provides a way to atomically renumber file descriptors, which
1586 /// would disappear if `dup2()` were to be removed entirely.
1587 #[unsafe(no_mangle)]
fd_renumber(fd: Fd, to: Fd) -> Errno1588 pub unsafe extern "C" fn fd_renumber(fd: Fd, to: Fd) -> Errno {
1589     State::with(|state| state.descriptors_mut().renumber(fd, to))
1590 }
1591 
1592 /// Move the offset of a file descriptor.
1593 /// Note: This is similar to `lseek` in POSIX.
1594 #[unsafe(no_mangle)]
fd_seek( fd: Fd, offset: Filedelta, whence: Whence, newoffset: &mut Filesize, ) -> Errno1595 pub unsafe extern "C" fn fd_seek(
1596     fd: Fd,
1597     offset: Filedelta,
1598     whence: Whence,
1599     newoffset: &mut Filesize,
1600 ) -> Errno {
1601     cfg_filesystem_available! {
1602         State::with(|state| {
1603             let mut ds = state.descriptors_mut();
1604             let stream = ds.get_seekable_stream_mut(fd)?;
1605 
1606             // Seeking only works on files.
1607             if let StreamType::File(file) = &mut stream.type_ {
1608                 if let filesystem::DescriptorType::Directory = file.descriptor_type {
1609                     // This isn't really the "right" errno, but it is consistient with wasmtime's
1610                     // preview 1 tests.
1611                     return Err(ERRNO_BADF);
1612                 }
1613                 let from = match whence {
1614                     WHENCE_SET if offset >= 0 => offset,
1615                     WHENCE_CUR => match (file.position.get() as i64).checked_add(offset) {
1616                         Some(pos) if pos >= 0 => pos,
1617                         _ => return Err(ERRNO_INVAL),
1618                     },
1619                     WHENCE_END => match (file.fd.stat()?.size as i64).checked_add(offset) {
1620                         Some(pos) if pos >= 0 => pos,
1621                         _ => return Err(ERRNO_INVAL),
1622                     },
1623                     _ => return Err(ERRNO_INVAL),
1624                 };
1625                 drop(stream.input.take());
1626                 drop(stream.output.take());
1627                 file.position.set(from as filesystem::Filesize);
1628                 *newoffset = from as filesystem::Filesize;
1629                 Ok(())
1630             } else {
1631                 Err(ERRNO_SPIPE)
1632             }
1633         })
1634     }
1635 }
1636 
1637 /// Synchronize the data and metadata of a file to disk.
1638 /// Note: This is similar to `fsync` in POSIX.
1639 #[unsafe(no_mangle)]
fd_sync(fd: Fd) -> Errno1640 pub unsafe extern "C" fn fd_sync(fd: Fd) -> Errno {
1641     cfg_filesystem_available! {
1642         State::with(|state| {
1643             let ds = state.descriptors();
1644             let file = ds.get_file(fd)?;
1645             file.fd.sync()?;
1646             Ok(())
1647         })
1648     }
1649 }
1650 
1651 /// Return the current offset of a file descriptor.
1652 /// Note: This is similar to `lseek(fd, 0, SEEK_CUR)` in POSIX.
1653 #[unsafe(no_mangle)]
fd_tell(fd: Fd, offset: &mut Filesize) -> Errno1654 pub unsafe extern "C" fn fd_tell(fd: Fd, offset: &mut Filesize) -> Errno {
1655     cfg_filesystem_available! {
1656         State::with(|state| {
1657             let ds = state.descriptors();
1658             let file = ds.get_seekable_file(fd)?;
1659             *offset = file.position.get();
1660             Ok(())
1661         })
1662     }
1663 }
1664 
1665 /// Write to a file descriptor.
1666 /// Note: This is similar to `writev` in POSIX.
1667 #[unsafe(no_mangle)]
fd_write( fd: Fd, mut iovs_ptr: *const Ciovec, mut iovs_len: usize, nwritten: &mut Size, ) -> Errno1668 pub unsafe extern "C" fn fd_write(
1669     fd: Fd,
1670     mut iovs_ptr: *const Ciovec,
1671     mut iovs_len: usize,
1672     nwritten: &mut Size,
1673 ) -> Errno {
1674     if !matches!(
1675         unsafe { get_allocation_state() },
1676         AllocationState::StackAllocated | AllocationState::StateAllocated
1677     ) {
1678         *nwritten = 0;
1679         return ERRNO_IO;
1680     }
1681 
1682     let bytes = unsafe {
1683         // Skip leading empty buffers.
1684         while iovs_len > 1 && (*iovs_ptr).buf_len == 0 {
1685             iovs_ptr = iovs_ptr.add(1);
1686             iovs_len -= 1;
1687         }
1688         if iovs_len == 0 {
1689             *nwritten = 0;
1690             return ERRNO_SUCCESS;
1691         }
1692         let ptr = (*iovs_ptr).buf;
1693         let len = (*iovs_ptr).buf_len;
1694         slice::from_raw_parts(ptr, len)
1695     };
1696 
1697     State::with(|state| {
1698         let ds = state.descriptors();
1699         match ds.get(fd)? {
1700             Descriptor::Streams(streams) => {
1701                 let wasi_stream = streams.get_write_stream()?;
1702 
1703                 #[cfg(not(feature = "proxy"))]
1704                 let nbytes = if let StreamType::File(file) = &streams.type_ {
1705                     file.blocking_mode.write(wasi_stream, bytes)?
1706                 } else {
1707                     // Use blocking writes on non-file streams (stdout, stderr, as sockets
1708                     // aren't currently used).
1709                     BlockingMode::Blocking.write(wasi_stream, bytes)?
1710                 };
1711                 #[cfg(feature = "proxy")]
1712                 let nbytes = BlockingMode::Blocking.write(wasi_stream, bytes)?;
1713 
1714                 // If this is a file, keep the current-position pointer up
1715                 // to date. Note that for files that perform appending
1716                 // writes this function will always update the current
1717                 // position to the end of the file.
1718                 //
1719                 // NB: this isn't "atomic" as it doesn't necessarily account
1720                 // for concurrent writes, but there's not much that can be
1721                 // done about that.
1722                 #[cfg(not(feature = "proxy"))]
1723                 if let StreamType::File(file) = &streams.type_ {
1724                     if file.append {
1725                         file.position.set(file.fd.stat()?.size);
1726                     } else {
1727                         file.position.set(file.position.get() + nbytes as u64);
1728                     }
1729                 }
1730 
1731                 *nwritten = nbytes;
1732                 Ok(())
1733             }
1734             Descriptor::Closed(_) | Descriptor::Bad => Err(ERRNO_BADF),
1735         }
1736     })
1737 }
1738 
1739 /// Create a directory.
1740 /// Note: This is similar to `mkdirat` in POSIX.
1741 #[unsafe(no_mangle)]
path_create_directory( fd: Fd, path_ptr: *const u8, path_len: usize, ) -> Errno1742 pub unsafe extern "C" fn path_create_directory(
1743     fd: Fd,
1744     path_ptr: *const u8,
1745     path_len: usize,
1746 ) -> Errno {
1747     cfg_filesystem_available! {
1748         let path = unsafe { slice::from_raw_parts(path_ptr, path_len) };
1749 
1750         State::with(|state| {
1751             let ds = state.descriptors();
1752             let file = ds.get_dir(fd)?;
1753             file.fd.create_directory_at(path)?;
1754             Ok(())
1755         })
1756     }
1757 }
1758 
1759 /// Return the attributes of a file or directory.
1760 /// Note: This is similar to `stat` in POSIX.
1761 #[unsafe(no_mangle)]
path_filestat_get( fd: Fd, flags: Lookupflags, path_ptr: *const u8, path_len: usize, buf: &mut Filestat, ) -> Errno1762 pub unsafe extern "C" fn path_filestat_get(
1763     fd: Fd,
1764     flags: Lookupflags,
1765     path_ptr: *const u8,
1766     path_len: usize,
1767     buf: &mut Filestat,
1768 ) -> Errno {
1769     cfg_filesystem_available! {
1770         let path = unsafe { slice::from_raw_parts(path_ptr, path_len) };
1771         let at_flags = at_flags_from_lookupflags(flags);
1772 
1773         State::with(|state| {
1774             let ds = state.descriptors();
1775             let file = ds.get_dir(fd)?;
1776             let stat = file.fd.stat_at(at_flags, path)?;
1777             let metadata_hash = file.fd.metadata_hash_at(at_flags, path)?;
1778             let filetype = stat.type_.into();
1779             *buf = Filestat {
1780                 dev: 1,
1781                 ino: metadata_hash.lower,
1782                 filetype,
1783                 nlink: stat.link_count,
1784                 size: stat.size,
1785                 atim: datetime_to_timestamp(stat.data_access_timestamp),
1786                 mtim: datetime_to_timestamp(stat.data_modification_timestamp),
1787                 ctim: datetime_to_timestamp(stat.status_change_timestamp),
1788             };
1789             Ok(())
1790         })
1791     }
1792 }
1793 
1794 /// Adjust the timestamps of a file or directory.
1795 /// Note: This is similar to `utimensat` in POSIX.
1796 #[unsafe(no_mangle)]
path_filestat_set_times( fd: Fd, flags: Lookupflags, path_ptr: *const u8, path_len: usize, atim: Timestamp, mtim: Timestamp, fst_flags: Fstflags, ) -> Errno1797 pub unsafe extern "C" fn path_filestat_set_times(
1798     fd: Fd,
1799     flags: Lookupflags,
1800     path_ptr: *const u8,
1801     path_len: usize,
1802     atim: Timestamp,
1803     mtim: Timestamp,
1804     fst_flags: Fstflags,
1805 ) -> Errno {
1806     cfg_filesystem_available! {
1807         let path = unsafe { slice::from_raw_parts(path_ptr, path_len) };
1808         let at_flags = at_flags_from_lookupflags(flags);
1809 
1810         State::with(|state| {
1811             let atim = systimespec(
1812                 fst_flags & FSTFLAGS_ATIM == FSTFLAGS_ATIM,
1813                 atim,
1814                 fst_flags & FSTFLAGS_ATIM_NOW == FSTFLAGS_ATIM_NOW,
1815             )?;
1816             let mtim = systimespec(
1817                 fst_flags & FSTFLAGS_MTIM == FSTFLAGS_MTIM,
1818                 mtim,
1819                 fst_flags & FSTFLAGS_MTIM_NOW == FSTFLAGS_MTIM_NOW,
1820             )?;
1821 
1822             let ds = state.descriptors();
1823             let file = ds.get_dir(fd)?;
1824             file.fd.set_times_at(at_flags, path, atim, mtim)?;
1825             Ok(())
1826         })
1827     }
1828 }
1829 
1830 /// Create a hard link.
1831 /// Note: This is similar to `linkat` in POSIX.
1832 #[unsafe(no_mangle)]
path_link( old_fd: Fd, old_flags: Lookupflags, old_path_ptr: *const u8, old_path_len: usize, new_fd: Fd, new_path_ptr: *const u8, new_path_len: usize, ) -> Errno1833 pub unsafe extern "C" fn path_link(
1834     old_fd: Fd,
1835     old_flags: Lookupflags,
1836     old_path_ptr: *const u8,
1837     old_path_len: usize,
1838     new_fd: Fd,
1839     new_path_ptr: *const u8,
1840     new_path_len: usize,
1841 ) -> Errno {
1842     cfg_filesystem_available! {
1843         let old_path = unsafe { slice::from_raw_parts(old_path_ptr, old_path_len) };
1844         let new_path = unsafe { slice::from_raw_parts(new_path_ptr, new_path_len) };
1845         let at_flags = at_flags_from_lookupflags(old_flags);
1846 
1847         State::with(|state| {
1848             let ds = state.descriptors();
1849             let old = &ds.get_dir(old_fd)?.fd;
1850             let new = &ds.get_dir(new_fd)?.fd;
1851             old.link_at(at_flags, old_path, new, new_path)?;
1852             Ok(())
1853         })
1854     }
1855 }
1856 
1857 /// Open a file or directory.
1858 /// The returned file descriptor is not guaranteed to be the lowest-numbered
1859 /// file descriptor not currently open; it is randomized to prevent
1860 /// applications from depending on making assumptions about indexes, since this
1861 /// is error-prone in multi-threaded contexts. The returned file descriptor is
1862 /// guaranteed to be less than 2**31.
1863 /// Note: This is similar to `openat` in POSIX.
1864 #[unsafe(no_mangle)]
path_open( fd: Fd, dirflags: Lookupflags, path_ptr: *const u8, path_len: usize, oflags: Oflags, fs_rights_base: Rights, fs_rights_inheriting: Rights, fdflags: Fdflags, opened_fd: &mut Fd, ) -> Errno1865 pub unsafe extern "C" fn path_open(
1866     fd: Fd,
1867     dirflags: Lookupflags,
1868     path_ptr: *const u8,
1869     path_len: usize,
1870     oflags: Oflags,
1871     fs_rights_base: Rights,
1872     fs_rights_inheriting: Rights,
1873     fdflags: Fdflags,
1874     opened_fd: &mut Fd,
1875 ) -> Errno {
1876     cfg_filesystem_available! {
1877         let _ = fs_rights_inheriting;
1878 
1879         let path = unsafe { slice::from_raw_parts(path_ptr, path_len) };
1880         let at_flags = at_flags_from_lookupflags(dirflags);
1881         let o_flags = o_flags_from_oflags(oflags);
1882         let flags = descriptor_flags_from_flags(fs_rights_base, fdflags);
1883         let append = fdflags & wasip1::FDFLAGS_APPEND == wasip1::FDFLAGS_APPEND;
1884 
1885         #[cfg(feature = "proxy")]
1886         return wasip1::ERRNO_NOTSUP;
1887 
1888         #[cfg(not(feature = "proxy"))]
1889         State::with(|state| {
1890             let result = state
1891                 .descriptors()
1892                 .get_dir(fd)?
1893                 .fd
1894                 .open_at(at_flags, path, o_flags, flags)?;
1895             let descriptor_type = result.get_type()?;
1896             let desc = Descriptor::Streams(Streams {
1897                 input: OnceCell::new(),
1898                 output: OnceCell::new(),
1899                 type_: StreamType::File(File {
1900                     fd: result,
1901                     descriptor_type,
1902                     position: Cell::new(0),
1903                     append,
1904                     blocking_mode: if fdflags & wasip1::FDFLAGS_NONBLOCK == 0 {
1905                         BlockingMode::Blocking
1906                     } else {
1907                         BlockingMode::NonBlocking
1908                     },
1909                     preopen_name_len: None,
1910                 }),
1911             });
1912 
1913             let fd = state.descriptors_mut().open(desc)?;
1914             *opened_fd = fd;
1915             Ok(())
1916         })
1917     }
1918 }
1919 
1920 /// Read the contents of a symbolic link.
1921 /// Note: This is similar to `readlinkat` in POSIX.
1922 #[unsafe(no_mangle)]
path_readlink( fd: Fd, path_ptr: *const u8, path_len: usize, buf: *mut u8, buf_len: Size, bufused: &mut Size, ) -> Errno1923 pub unsafe extern "C" fn path_readlink(
1924     fd: Fd,
1925     path_ptr: *const u8,
1926     path_len: usize,
1927     buf: *mut u8,
1928     buf_len: Size,
1929     bufused: &mut Size,
1930 ) -> Errno {
1931     cfg_filesystem_available! {
1932         let path = unsafe { slice::from_raw_parts(path_ptr, path_len) };
1933 
1934         State::with(|state| {
1935             // If the user gave us a buffer shorter than `PATH_MAX`, it may not be
1936             // long enough to accept the actual path. `cabi_realloc` can't fail,
1937             // so instead we handle this case specially.
1938             let use_state_buf = buf_len < PATH_MAX;
1939 
1940             let ds = state.descriptors();
1941             let file = ds.get_dir(fd)?;
1942             let path = if use_state_buf {
1943                 state
1944                     .with_one_temporary_alloc( || {
1945                         file.fd.readlink_at(path)
1946                     })?
1947             } else {
1948                 state
1949                     .with_one_import_alloc(buf, buf_len, || file.fd.readlink_at(path))?
1950             };
1951 
1952             if use_state_buf {
1953                 // Preview1 follows POSIX in truncating the returned path if it
1954                 // doesn't fit.
1955                 let len = min(path.len(), buf_len);
1956                 unsafe {
1957                     ptr::copy_nonoverlapping(path.as_ptr().cast(), buf, len);
1958                 }
1959                 *bufused = len;
1960             } else {
1961                 *bufused = path.len();
1962             }
1963 
1964             // The returned string's memory was allocated in `buf`, so don't separately
1965             // free it.
1966             forget(path);
1967 
1968             Ok(())
1969         })
1970     }
1971 }
1972 
1973 /// Remove a directory.
1974 /// Return `errno::notempty` if the directory is not empty.
1975 /// Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX.
1976 #[unsafe(no_mangle)]
path_remove_directory( fd: Fd, path_ptr: *const u8, path_len: usize, ) -> Errno1977 pub unsafe extern "C" fn path_remove_directory(
1978     fd: Fd,
1979     path_ptr: *const u8,
1980     path_len: usize,
1981 ) -> Errno {
1982     cfg_filesystem_available! {
1983         let path = unsafe { slice::from_raw_parts(path_ptr, path_len) };
1984 
1985         State::with(|state| {
1986             let ds = state.descriptors();
1987             let file = ds.get_dir(fd)?;
1988             file.fd.remove_directory_at(path)?;
1989             Ok(())
1990         })
1991     }
1992 }
1993 
1994 /// Rename a file or directory.
1995 /// Note: This is similar to `renameat` in POSIX.
1996 #[unsafe(no_mangle)]
path_rename( old_fd: Fd, old_path_ptr: *const u8, old_path_len: usize, new_fd: Fd, new_path_ptr: *const u8, new_path_len: usize, ) -> Errno1997 pub unsafe extern "C" fn path_rename(
1998     old_fd: Fd,
1999     old_path_ptr: *const u8,
2000     old_path_len: usize,
2001     new_fd: Fd,
2002     new_path_ptr: *const u8,
2003     new_path_len: usize,
2004 ) -> Errno {
2005     cfg_filesystem_available! {
2006         let old_path = unsafe { slice::from_raw_parts(old_path_ptr, old_path_len) };
2007         let new_path = unsafe { slice::from_raw_parts(new_path_ptr, new_path_len) };
2008 
2009         State::with(|state| {
2010             let ds = state.descriptors();
2011             let old = &ds.get_dir(old_fd)?.fd;
2012             let new = &ds.get_dir(new_fd)?.fd;
2013             old.rename_at(old_path, new, new_path)?;
2014             Ok(())
2015         })
2016     }
2017 }
2018 
2019 /// Create a symbolic link.
2020 /// Note: This is similar to `symlinkat` in POSIX.
2021 #[unsafe(no_mangle)]
path_symlink( old_path_ptr: *const u8, old_path_len: usize, fd: Fd, new_path_ptr: *const u8, new_path_len: usize, ) -> Errno2022 pub unsafe extern "C" fn path_symlink(
2023     old_path_ptr: *const u8,
2024     old_path_len: usize,
2025     fd: Fd,
2026     new_path_ptr: *const u8,
2027     new_path_len: usize,
2028 ) -> Errno {
2029     cfg_filesystem_available! {
2030         let old_path = unsafe { slice::from_raw_parts(old_path_ptr, old_path_len) };
2031         let new_path = unsafe { slice::from_raw_parts(new_path_ptr, new_path_len) };
2032 
2033         State::with(|state| {
2034             let ds = state.descriptors();
2035             let file = ds.get_dir(fd)?;
2036             file.fd.symlink_at(old_path, new_path)?;
2037             Ok(())
2038         })
2039     }
2040 }
2041 
2042 /// Unlink a file.
2043 /// Return `errno::isdir` if the path refers to a directory.
2044 /// Note: This is similar to `unlinkat(fd, path, 0)` in POSIX.
2045 #[unsafe(no_mangle)]
path_unlink_file(fd: Fd, path_ptr: *const u8, path_len: usize) -> Errno2046 pub unsafe extern "C" fn path_unlink_file(fd: Fd, path_ptr: *const u8, path_len: usize) -> Errno {
2047     cfg_filesystem_available! {
2048         let path = unsafe { slice::from_raw_parts(path_ptr, path_len) };
2049 
2050         State::with(|state| {
2051             let ds = state.descriptors();
2052             let file = ds.get_dir(fd)?;
2053             file.fd.unlink_file_at(path)?;
2054             Ok(())
2055         })
2056     }
2057 }
2058 
2059 struct Pollables {
2060     pointer: *mut Pollable,
2061     index: usize,
2062     length: usize,
2063 }
2064 
2065 impl Pollables {
push(&mut self, pollable: Pollable)2066     unsafe fn push(&mut self, pollable: Pollable) {
2067         assert!(self.index < self.length);
2068         // Use `ptr::write` instead of `*... = pollable` because `ptr::write`
2069         // doesn't call drop on the old memory.
2070         unsafe {
2071             self.pointer.add(self.index).write(pollable);
2072         }
2073         self.index += 1;
2074     }
2075 }
2076 
2077 // We create new pollable handles for each `poll_oneoff` call, so drop them all
2078 // after the call.
2079 impl Drop for Pollables {
drop(&mut self)2080     fn drop(&mut self) {
2081         while self.index != 0 {
2082             self.index -= 1;
2083             unsafe {
2084                 core::ptr::drop_in_place(self.pointer.add(self.index));
2085             }
2086         }
2087     }
2088 }
2089 
2090 /// Concurrently poll for the occurrence of a set of events.
2091 #[unsafe(no_mangle)]
poll_oneoff( r#in: *const Subscription, out: *mut Event, nsubscriptions: Size, nevents: &mut Size, ) -> Errno2092 pub unsafe extern "C" fn poll_oneoff(
2093     r#in: *const Subscription,
2094     out: *mut Event,
2095     nsubscriptions: Size,
2096     nevents: &mut Size,
2097 ) -> Errno {
2098     *nevents = 0;
2099 
2100     let subscriptions = unsafe { slice::from_raw_parts(r#in, nsubscriptions) };
2101 
2102     // We're going to split the `nevents` buffer into two non-overlapping
2103     // buffers: one to store the pollable handles, and the other to store
2104     // the bool results.
2105     //
2106     // First, we assert that this is possible:
2107     assert!(align_of::<Event>() >= align_of::<Pollable>());
2108     assert!(align_of::<Pollable>() >= align_of::<u32>());
2109     assert!(
2110         nsubscriptions
2111             .checked_mul(size_of::<Event>())
2112             .trapping_unwrap()
2113             >= nsubscriptions
2114                 .checked_mul(size_of::<Pollable>())
2115                 .trapping_unwrap()
2116                 .checked_add(
2117                     nsubscriptions
2118                         .checked_mul(size_of::<u32>())
2119                         .trapping_unwrap()
2120                 )
2121                 .trapping_unwrap()
2122     );
2123     // Store the pollable handles at the beginning, and the bool results at the
2124     // end, so that we don't clobber the bool results when writing the events.
2125     let pollables = out as *mut c_void as *mut Pollable;
2126     let results = unsafe { out.add(nsubscriptions).cast::<u32>().sub(nsubscriptions) };
2127 
2128     // Indefinite sleeping is not supported in preview1.
2129     if nsubscriptions == 0 {
2130         return ERRNO_INVAL;
2131     }
2132 
2133     State::with(|state| {
2134         const EVENTTYPE_CLOCK: u8 = wasip1::EVENTTYPE_CLOCK.raw();
2135         const EVENTTYPE_FD_READ: u8 = wasip1::EVENTTYPE_FD_READ.raw();
2136         const EVENTTYPE_FD_WRITE: u8 = wasip1::EVENTTYPE_FD_WRITE.raw();
2137 
2138         let mut pollables = Pollables {
2139             pointer: pollables,
2140             index: 0,
2141             length: nsubscriptions,
2142         };
2143 
2144         for subscription in subscriptions {
2145             let pollable = match subscription.u.tag {
2146                 EVENTTYPE_CLOCK => {
2147                     let clock = unsafe { &subscription.u.u.clock };
2148                     let absolute = (clock.flags & SUBCLOCKFLAGS_SUBSCRIPTION_CLOCK_ABSTIME)
2149                         == SUBCLOCKFLAGS_SUBSCRIPTION_CLOCK_ABSTIME;
2150                     match clock.id {
2151                         CLOCKID_REALTIME => {
2152                             let timeout = if absolute {
2153                                 // Convert `clock.timeout` to `Datetime`.
2154                                 let mut datetime = wall_clock::Datetime {
2155                                     seconds: clock.timeout / 1_000_000_000,
2156                                     nanoseconds: (clock.timeout % 1_000_000_000) as _,
2157                                 };
2158 
2159                                 // Subtract `now`.
2160                                 let now = wall_clock::now();
2161                                 datetime.seconds -= now.seconds;
2162                                 if datetime.nanoseconds < now.nanoseconds {
2163                                     datetime.seconds -= 1;
2164                                     datetime.nanoseconds += 1_000_000_000;
2165                                 }
2166                                 datetime.nanoseconds -= now.nanoseconds;
2167 
2168                                 // Convert to nanoseconds.
2169                                 let nanos = datetime
2170                                     .seconds
2171                                     .checked_mul(1_000_000_000)
2172                                     .ok_or(ERRNO_OVERFLOW)?;
2173                                 nanos
2174                                     .checked_add(datetime.nanoseconds.into())
2175                                     .ok_or(ERRNO_OVERFLOW)?
2176                             } else {
2177                                 clock.timeout
2178                             };
2179 
2180                             monotonic_clock::subscribe_duration(timeout)
2181                         }
2182 
2183                         CLOCKID_MONOTONIC => {
2184                             if absolute {
2185                                 monotonic_clock::subscribe_instant(clock.timeout)
2186                             } else {
2187                                 monotonic_clock::subscribe_duration(clock.timeout)
2188                             }
2189                         }
2190 
2191                         _ => return Err(ERRNO_INVAL),
2192                     }
2193                 }
2194 
2195                 EVENTTYPE_FD_READ => state
2196                     .descriptors()
2197                     .get_read_stream(unsafe { subscription.u.u.fd_read.file_descriptor })
2198                     .map(|stream| stream.subscribe())?,
2199 
2200                 EVENTTYPE_FD_WRITE => state
2201                     .descriptors()
2202                     .get_write_stream(unsafe { subscription.u.u.fd_write.file_descriptor })
2203                     .map(|stream| stream.subscribe())?,
2204 
2205                 _ => return Err(ERRNO_INVAL),
2206             };
2207             unsafe {
2208                 pollables.push(pollable);
2209             }
2210         }
2211 
2212         #[link(wasm_import_module = "wasi:io/poll@0.2.6")]
2213         unsafe extern "C" {
2214             #[link_name = "poll"]
2215             fn poll_import(pollables: *const Pollable, len: usize, rval: *mut ReadyList);
2216         }
2217         let mut ready_list = ReadyList {
2218             base: std::ptr::null(),
2219             len: 0,
2220         };
2221 
2222         state.with_one_import_alloc(
2223             results.cast(),
2224             nsubscriptions
2225                 .checked_mul(size_of::<u32>())
2226                 .trapping_unwrap(),
2227             || unsafe {
2228                 poll_import(
2229                     pollables.pointer,
2230                     pollables.length,
2231                     &mut ready_list as *mut _,
2232                 );
2233             },
2234         );
2235 
2236         assert!(ready_list.len <= nsubscriptions);
2237         assert_eq!(ready_list.base, results as *const u32);
2238 
2239         drop(pollables);
2240 
2241         let ready = unsafe { std::slice::from_raw_parts(ready_list.base, ready_list.len) };
2242 
2243         let mut count = 0;
2244 
2245         for subscription in ready {
2246             let subscription = unsafe { *subscriptions.as_ptr().add(*subscription as usize) };
2247 
2248             let type_;
2249 
2250             let (error, nbytes, flags) = match subscription.u.tag {
2251                 EVENTTYPE_CLOCK => {
2252                     type_ = wasip1::EVENTTYPE_CLOCK;
2253                     (ERRNO_SUCCESS, 0, 0)
2254                 }
2255 
2256                 EVENTTYPE_FD_READ => {
2257                     type_ = wasip1::EVENTTYPE_FD_READ;
2258                     let ds = state.descriptors();
2259                     let desc = ds
2260                         .get(unsafe { subscription.u.u.fd_read.file_descriptor })
2261                         .trapping_unwrap();
2262                     match desc {
2263                         Descriptor::Streams(streams) => match &streams.type_ {
2264                             #[cfg(not(feature = "proxy"))]
2265                             StreamType::File(file) => match file.fd.stat() {
2266                                 Ok(stat) => {
2267                                     let nbytes = stat.size.saturating_sub(file.position.get());
2268                                     (
2269                                         ERRNO_SUCCESS,
2270                                         nbytes,
2271                                         if nbytes == 0 {
2272                                             EVENTRWFLAGS_FD_READWRITE_HANGUP
2273                                         } else {
2274                                             0
2275                                         },
2276                                     )
2277                                 }
2278                                 Err(e) => (e.into(), 1, 0),
2279                             },
2280                             StreamType::Stdio(_) => (ERRNO_SUCCESS, 1, 0),
2281                         },
2282                         _ => unreachable!(),
2283                     }
2284                 }
2285                 EVENTTYPE_FD_WRITE => {
2286                     type_ = wasip1::EVENTTYPE_FD_WRITE;
2287                     let ds = state.descriptors();
2288                     let desc = ds
2289                         .get(unsafe { subscription.u.u.fd_write.file_descriptor })
2290                         .trapping_unwrap();
2291                     match desc {
2292                         Descriptor::Streams(streams) => match &streams.type_ {
2293                             #[cfg(not(feature = "proxy"))]
2294                             StreamType::File(_) => (ERRNO_SUCCESS, 1, 0),
2295                             StreamType::Stdio(_) => (ERRNO_SUCCESS, 1, 0),
2296                         },
2297                         _ => unreachable!(),
2298                     }
2299                 }
2300 
2301                 _ => unreachable!(),
2302             };
2303 
2304             unsafe {
2305                 *out.add(count) = Event {
2306                     userdata: subscription.userdata,
2307                     error,
2308                     type_,
2309                     fd_readwrite: EventFdReadwrite { nbytes, flags },
2310                 };
2311             }
2312 
2313             count += 1;
2314         }
2315 
2316         *nevents = count;
2317 
2318         Ok(())
2319     })
2320 }
2321 
2322 /// Terminate the process normally. An exit code of 0 indicates successful
2323 /// termination of the program. The meanings of other values is dependent on
2324 /// the environment.
2325 #[unsafe(no_mangle)]
proc_exit(rval: Exitcode) -> !2326 pub unsafe extern "C" fn proc_exit(rval: Exitcode) -> ! {
2327     #[cfg(feature = "proxy")]
2328     {
2329         unreachable!("no other implementation available in proxy world");
2330     }
2331     #[cfg(not(feature = "proxy"))]
2332     {
2333         let status = if rval == 0 { Ok(()) } else { Err(()) };
2334         crate::bindings::wasi::cli::exit::exit(status); // does not return
2335         unreachable!("host exit implementation didn't exit!") // actually unreachable
2336     }
2337 }
2338 
2339 /// Send a signal to the process of the calling thread.
2340 /// Note: This is similar to `raise` in POSIX.
2341 #[unsafe(no_mangle)]
proc_raise(_sig: Signal) -> Errno2342 pub unsafe extern "C" fn proc_raise(_sig: Signal) -> Errno {
2343     unreachable!()
2344 }
2345 
2346 /// Temporarily yield execution of the calling thread.
2347 /// Note: This is similar to `sched_yield` in POSIX.
2348 #[unsafe(no_mangle)]
sched_yield() -> Errno2349 pub unsafe extern "C" fn sched_yield() -> Errno {
2350     // TODO: This is not yet covered in Preview2.
2351 
2352     ERRNO_SUCCESS
2353 }
2354 
2355 /// Write high-quality random data into a buffer.
2356 /// This function blocks when the implementation is unable to immediately
2357 /// provide sufficient high-quality random data.
2358 /// This function may execute slowly, so when large mounts of random data are
2359 /// required, it's advisable to use this function to seed a pseudo-random
2360 /// number generator, rather than to provide the random data directly.
2361 #[unsafe(no_mangle)]
random_get(buf: *mut u8, buf_len: Size) -> Errno2362 pub unsafe extern "C" fn random_get(buf: *mut u8, buf_len: Size) -> Errno {
2363     if matches!(
2364         unsafe { get_allocation_state() },
2365         AllocationState::StackAllocated | AllocationState::StateAllocated
2366     ) {
2367         State::with(|state| {
2368             assert_eq!(buf_len as u32 as Size, buf_len);
2369             let result = state
2370                 .with_one_import_alloc(buf, buf_len, || random::get_random_bytes(buf_len as u64));
2371             assert_eq!(result.as_ptr(), buf);
2372 
2373             // The returned buffer's memory was allocated in `buf`, so don't separately
2374             // free it.
2375             forget(result);
2376 
2377             Ok(())
2378         })
2379     } else {
2380         ERRNO_SUCCESS
2381     }
2382 }
2383 
2384 /// Accept a new incoming connection.
2385 /// Note: This is similar to `accept` in POSIX.
2386 #[unsafe(no_mangle)]
sock_accept(_fd: Fd, _flags: Fdflags, _connection: *mut Fd) -> Errno2387 pub unsafe extern "C" fn sock_accept(_fd: Fd, _flags: Fdflags, _connection: *mut Fd) -> Errno {
2388     unreachable!()
2389 }
2390 
2391 /// Receive a message from a socket.
2392 /// Note: This is similar to `recv` in POSIX, though it also supports reading
2393 /// the data into multiple buffers in the manner of `readv`.
2394 #[unsafe(no_mangle)]
sock_recv( _fd: Fd, _ri_data_ptr: *const Iovec, _ri_data_len: usize, _ri_flags: Riflags, _ro_datalen: *mut Size, _ro_flags: *mut Roflags, ) -> Errno2395 pub unsafe extern "C" fn sock_recv(
2396     _fd: Fd,
2397     _ri_data_ptr: *const Iovec,
2398     _ri_data_len: usize,
2399     _ri_flags: Riflags,
2400     _ro_datalen: *mut Size,
2401     _ro_flags: *mut Roflags,
2402 ) -> Errno {
2403     unreachable!()
2404 }
2405 
2406 /// Send a message on a socket.
2407 /// Note: This is similar to `send` in POSIX, though it also supports writing
2408 /// the data from multiple buffers in the manner of `writev`.
2409 #[unsafe(no_mangle)]
sock_send( _fd: Fd, _si_data_ptr: *const Ciovec, _si_data_len: usize, _si_flags: Siflags, _so_datalen: *mut Size, ) -> Errno2410 pub unsafe extern "C" fn sock_send(
2411     _fd: Fd,
2412     _si_data_ptr: *const Ciovec,
2413     _si_data_len: usize,
2414     _si_flags: Siflags,
2415     _so_datalen: *mut Size,
2416 ) -> Errno {
2417     unreachable!()
2418 }
2419 
2420 /// Shut down socket send and receive channels.
2421 /// Note: This is similar to `shutdown` in POSIX.
2422 #[unsafe(no_mangle)]
sock_shutdown(fd: Fd, _how: Sdflags) -> Errno2423 pub unsafe extern "C" fn sock_shutdown(fd: Fd, _how: Sdflags) -> Errno {
2424     State::with(|state| {
2425         state
2426             .descriptors_mut()
2427             .get_stream_with_error_mut(fd, wasip1::ERRNO_BADF)?;
2428         Err(wasip1::ERRNO_NOTSOCK)
2429     })
2430 }
2431 
2432 #[cfg(not(feature = "proxy"))]
datetime_to_timestamp(datetime: Option<filesystem::Datetime>) -> Timestamp2433 fn datetime_to_timestamp(datetime: Option<filesystem::Datetime>) -> Timestamp {
2434     match datetime {
2435         Some(datetime) => u64::from(datetime.nanoseconds)
2436             .saturating_add(datetime.seconds.saturating_mul(1_000_000_000)),
2437         None => 0,
2438     }
2439 }
2440 
2441 #[cfg(not(feature = "proxy"))]
at_flags_from_lookupflags(flags: Lookupflags) -> filesystem::PathFlags2442 fn at_flags_from_lookupflags(flags: Lookupflags) -> filesystem::PathFlags {
2443     if flags & LOOKUPFLAGS_SYMLINK_FOLLOW == LOOKUPFLAGS_SYMLINK_FOLLOW {
2444         filesystem::PathFlags::SYMLINK_FOLLOW
2445     } else {
2446         filesystem::PathFlags::empty()
2447     }
2448 }
2449 
2450 #[cfg(not(feature = "proxy"))]
o_flags_from_oflags(flags: Oflags) -> filesystem::OpenFlags2451 fn o_flags_from_oflags(flags: Oflags) -> filesystem::OpenFlags {
2452     let mut o_flags = filesystem::OpenFlags::empty();
2453     if flags & OFLAGS_CREAT == OFLAGS_CREAT {
2454         o_flags |= filesystem::OpenFlags::CREATE;
2455     }
2456     if flags & OFLAGS_DIRECTORY == OFLAGS_DIRECTORY {
2457         o_flags |= filesystem::OpenFlags::DIRECTORY;
2458     }
2459     if flags & OFLAGS_EXCL == OFLAGS_EXCL {
2460         o_flags |= filesystem::OpenFlags::EXCLUSIVE;
2461     }
2462     if flags & OFLAGS_TRUNC == OFLAGS_TRUNC {
2463         o_flags |= filesystem::OpenFlags::TRUNCATE;
2464     }
2465     o_flags
2466 }
2467 
2468 #[cfg(not(feature = "proxy"))]
descriptor_flags_from_flags(rights: Rights, fdflags: Fdflags) -> filesystem::DescriptorFlags2469 fn descriptor_flags_from_flags(rights: Rights, fdflags: Fdflags) -> filesystem::DescriptorFlags {
2470     let mut flags = filesystem::DescriptorFlags::empty();
2471     if rights & wasip1::RIGHTS_FD_READ == wasip1::RIGHTS_FD_READ {
2472         flags |= filesystem::DescriptorFlags::READ;
2473     }
2474     if rights & wasip1::RIGHTS_FD_WRITE == wasip1::RIGHTS_FD_WRITE {
2475         flags |= filesystem::DescriptorFlags::WRITE;
2476     }
2477     if fdflags & wasip1::FDFLAGS_SYNC == wasip1::FDFLAGS_SYNC {
2478         flags |= filesystem::DescriptorFlags::FILE_INTEGRITY_SYNC;
2479     }
2480     if fdflags & wasip1::FDFLAGS_DSYNC == wasip1::FDFLAGS_DSYNC {
2481         flags |= filesystem::DescriptorFlags::DATA_INTEGRITY_SYNC;
2482     }
2483     if fdflags & wasip1::FDFLAGS_RSYNC == wasip1::FDFLAGS_RSYNC {
2484         flags |= filesystem::DescriptorFlags::REQUESTED_WRITE_SYNC;
2485     }
2486     flags
2487 }
2488 
2489 #[cfg(not(feature = "proxy"))]
2490 impl From<filesystem::ErrorCode> for Errno {
2491     #[inline(never)] // Disable inlining as this is bulky and relatively cold.
from(err: filesystem::ErrorCode) -> Errno2492     fn from(err: filesystem::ErrorCode) -> Errno {
2493         match err {
2494             // Use a black box to prevent the optimizer from generating a
2495             // lookup table, which would require a static initializer.
2496             filesystem::ErrorCode::Access => black_box(ERRNO_ACCES),
2497             filesystem::ErrorCode::WouldBlock => ERRNO_AGAIN,
2498             filesystem::ErrorCode::Already => ERRNO_ALREADY,
2499             filesystem::ErrorCode::BadDescriptor => ERRNO_BADF,
2500             filesystem::ErrorCode::Busy => ERRNO_BUSY,
2501             filesystem::ErrorCode::Deadlock => ERRNO_DEADLK,
2502             filesystem::ErrorCode::Quota => ERRNO_DQUOT,
2503             filesystem::ErrorCode::Exist => ERRNO_EXIST,
2504             filesystem::ErrorCode::FileTooLarge => ERRNO_FBIG,
2505             filesystem::ErrorCode::IllegalByteSequence => ERRNO_ILSEQ,
2506             filesystem::ErrorCode::InProgress => ERRNO_INPROGRESS,
2507             filesystem::ErrorCode::Interrupted => ERRNO_INTR,
2508             filesystem::ErrorCode::Invalid => ERRNO_INVAL,
2509             filesystem::ErrorCode::Io => ERRNO_IO,
2510             filesystem::ErrorCode::IsDirectory => ERRNO_ISDIR,
2511             filesystem::ErrorCode::Loop => ERRNO_LOOP,
2512             filesystem::ErrorCode::TooManyLinks => ERRNO_MLINK,
2513             filesystem::ErrorCode::MessageSize => ERRNO_MSGSIZE,
2514             filesystem::ErrorCode::NameTooLong => ERRNO_NAMETOOLONG,
2515             filesystem::ErrorCode::NoDevice => ERRNO_NODEV,
2516             filesystem::ErrorCode::NoEntry => ERRNO_NOENT,
2517             filesystem::ErrorCode::NoLock => ERRNO_NOLCK,
2518             filesystem::ErrorCode::InsufficientMemory => ERRNO_NOMEM,
2519             filesystem::ErrorCode::InsufficientSpace => ERRNO_NOSPC,
2520             filesystem::ErrorCode::Unsupported => ERRNO_NOTSUP,
2521             filesystem::ErrorCode::NotDirectory => ERRNO_NOTDIR,
2522             filesystem::ErrorCode::NotEmpty => ERRNO_NOTEMPTY,
2523             filesystem::ErrorCode::NotRecoverable => ERRNO_NOTRECOVERABLE,
2524             filesystem::ErrorCode::NoTty => ERRNO_NOTTY,
2525             filesystem::ErrorCode::NoSuchDevice => ERRNO_NXIO,
2526             filesystem::ErrorCode::Overflow => ERRNO_OVERFLOW,
2527             filesystem::ErrorCode::NotPermitted => ERRNO_PERM,
2528             filesystem::ErrorCode::Pipe => ERRNO_PIPE,
2529             filesystem::ErrorCode::ReadOnly => ERRNO_ROFS,
2530             filesystem::ErrorCode::InvalidSeek => ERRNO_SPIPE,
2531             filesystem::ErrorCode::TextFileBusy => ERRNO_TXTBSY,
2532             filesystem::ErrorCode::CrossDevice => ERRNO_XDEV,
2533         }
2534     }
2535 }
2536 
2537 #[cfg(not(feature = "proxy"))]
2538 impl From<filesystem::DescriptorType> for wasip1::Filetype {
from(ty: filesystem::DescriptorType) -> wasip1::Filetype2539     fn from(ty: filesystem::DescriptorType) -> wasip1::Filetype {
2540         match ty {
2541             filesystem::DescriptorType::RegularFile => FILETYPE_REGULAR_FILE,
2542             filesystem::DescriptorType::Directory => FILETYPE_DIRECTORY,
2543             filesystem::DescriptorType::BlockDevice => FILETYPE_BLOCK_DEVICE,
2544             filesystem::DescriptorType::CharacterDevice => FILETYPE_CHARACTER_DEVICE,
2545             // preview1 never had a FIFO code.
2546             filesystem::DescriptorType::Fifo => FILETYPE_UNKNOWN,
2547             // TODO: Add a way to disginguish between FILETYPE_SOCKET_STREAM and
2548             // FILETYPE_SOCKET_DGRAM.
2549             filesystem::DescriptorType::Socket => unreachable!(),
2550             filesystem::DescriptorType::SymbolicLink => FILETYPE_SYMBOLIC_LINK,
2551             filesystem::DescriptorType::Unknown => FILETYPE_UNKNOWN,
2552         }
2553     }
2554 }
2555 
2556 #[derive(Clone, Copy)]
2557 pub enum BlockingMode {
2558     NonBlocking,
2559     Blocking,
2560 }
2561 
2562 impl BlockingMode {
2563     // note: these methods must take self, not &self, to avoid rustc creating a constant
2564     // out of a BlockingMode literal that it places in .romem, creating a data section and
2565     // breaking our fragile linking scheme
read( self, input_stream: &streams::InputStream, read_len: u64, ) -> Result<Vec<u8>, streams::StreamError>2566     fn read(
2567         self,
2568         input_stream: &streams::InputStream,
2569         read_len: u64,
2570     ) -> Result<Vec<u8>, streams::StreamError> {
2571         match self {
2572             BlockingMode::NonBlocking => input_stream.read(read_len),
2573             BlockingMode::Blocking => input_stream.blocking_read(read_len),
2574         }
2575     }
write( self, output_stream: &streams::OutputStream, mut bytes: &[u8], ) -> Result<usize, Errno>2576     fn write(
2577         self,
2578         output_stream: &streams::OutputStream,
2579         mut bytes: &[u8],
2580     ) -> Result<usize, Errno> {
2581         match self {
2582             BlockingMode::Blocking => {
2583                 let total = bytes.len();
2584                 loop {
2585                     let len = bytes.len().min(4096);
2586                     let (chunk, rest) = bytes.split_at(len);
2587                     bytes = rest;
2588                     match output_stream.blocking_write_and_flush(chunk) {
2589                         Ok(()) if bytes.is_empty() => break,
2590                         Ok(()) => {}
2591                         Err(streams::StreamError::Closed) => return Err(ERRNO_IO),
2592                         Err(streams::StreamError::LastOperationFailed(e)) => {
2593                             return Err(stream_error_to_errno(e));
2594                         }
2595                     }
2596                 }
2597                 Ok(total)
2598             }
2599 
2600             BlockingMode::NonBlocking => {
2601                 let permit = match output_stream.check_write() {
2602                     Ok(n) => n,
2603                     Err(streams::StreamError::Closed) => 0,
2604                     Err(streams::StreamError::LastOperationFailed(e)) => {
2605                         return Err(stream_error_to_errno(e));
2606                     }
2607                 };
2608 
2609                 let len = bytes.len().min(permit as usize);
2610 
2611                 match output_stream.write(&bytes[..len]) {
2612                     Ok(_) => {}
2613                     Err(streams::StreamError::Closed) => return Ok(0),
2614                     Err(streams::StreamError::LastOperationFailed(e)) => {
2615                         return Err(stream_error_to_errno(e));
2616                     }
2617                 }
2618 
2619                 match output_stream.blocking_flush() {
2620                     Ok(_) => {}
2621                     Err(streams::StreamError::Closed) => return Ok(0),
2622                     Err(streams::StreamError::LastOperationFailed(e)) => {
2623                         return Err(stream_error_to_errno(e));
2624                     }
2625                 }
2626 
2627                 Ok(len)
2628             }
2629         }
2630     }
2631 }
2632 
2633 #[repr(C)]
2634 #[cfg(not(feature = "proxy"))]
2635 pub struct File {
2636     /// The handle to the preview2 descriptor that this file is referencing.
2637     fd: filesystem::Descriptor,
2638 
2639     /// The descriptor type, as supplied by filesystem::get_type at opening
2640     descriptor_type: filesystem::DescriptorType,
2641 
2642     /// The current-position pointer.
2643     position: Cell<filesystem::Filesize>,
2644 
2645     /// In append mode, all writes append to the file.
2646     append: bool,
2647 
2648     /// In blocking mode, read and write calls dispatch to blocking_read and
2649     /// blocking_check_write on the underlying streams. When false, read and write
2650     /// dispatch to stream's plain read and check_write.
2651     blocking_mode: BlockingMode,
2652 
2653     /// TODO
2654     preopen_name_len: Option<NonZeroUsize>,
2655 }
2656 
2657 #[cfg(not(feature = "proxy"))]
2658 impl File {
is_dir(&self) -> bool2659     fn is_dir(&self) -> bool {
2660         match self.descriptor_type {
2661             filesystem::DescriptorType::Directory => true,
2662             _ => false,
2663         }
2664     }
2665 }
2666 
2667 const PAGE_SIZE: usize = 65536;
2668 
2669 /// The maximum path length. WASI doesn't explicitly guarantee this, but all
2670 /// popular OS's have a `PATH_MAX` of at most 4096, so that's enough for this
2671 /// polyfill.
2672 const PATH_MAX: usize = 4096;
2673 
2674 /// Maximum number of bytes to cache for a `wasip1::Dirent` plus its path name.
2675 const DIRENT_CACHE: usize = 256;
2676 
2677 /// A canary value to detect memory corruption within `State`.
2678 const MAGIC: u32 = u32::from_le_bytes(*b"ugh!");
2679 
2680 #[repr(C)] // used for now to keep magic1 and magic2 at the start and end
2681 struct State {
2682     /// A canary constant value located at the beginning of this structure to
2683     /// try to catch memory corruption coming from the bottom.
2684     magic1: u32,
2685 
2686     /// Used to coordinate allocations of `cabi_import_realloc`
2687     import_alloc: Cell<ImportAlloc>,
2688 
2689     /// Storage of mapping from preview1 file descriptors to preview2 file
2690     /// descriptors.
2691     ///
2692     /// Do not use this member directly - use State::descriptors() to ensure
2693     /// lazy initialization happens.
2694     descriptors: RefCell<Option<Descriptors>>,
2695 
2696     /// TODO
2697     temporary_data: UnsafeCell<MaybeUninit<[u8; temporary_data_size()]>>,
2698 
2699     /// Cache for the `fd_readdir` call for a final `wasip1::Dirent` plus path
2700     /// name that didn't fit into the caller's buffer.
2701     #[cfg(not(feature = "proxy"))]
2702     dirent_cache: DirentCache,
2703 
2704     /// The string `..` for use by the directory iterator.
2705     #[cfg(not(feature = "proxy"))]
2706     dotdot: [UnsafeCell<u8>; 2],
2707 
2708     /// Another canary constant located at the end of the structure to catch
2709     /// memory corruption coming from the bottom.
2710     magic2: u32,
2711 }
2712 
2713 #[cfg(not(feature = "proxy"))]
2714 struct DirentCache {
2715     stream: Cell<Option<DirectoryEntryStream>>,
2716     for_fd: Cell<wasip1::Fd>,
2717     cookie: Cell<wasip1::Dircookie>,
2718     cached_dirent: Cell<wasip1::Dirent>,
2719     path_data: UnsafeCell<MaybeUninit<[u8; DIRENT_CACHE]>>,
2720 }
2721 
2722 #[cfg(not(feature = "proxy"))]
2723 struct DirectoryEntryStream(filesystem::DirectoryEntryStream);
2724 
2725 #[repr(C)]
2726 pub struct WasmStr {
2727     ptr: *const u8,
2728     len: usize,
2729 }
2730 
2731 #[repr(C)]
2732 pub struct WasmStrList {
2733     base: *const WasmStr,
2734     len: usize,
2735 }
2736 
2737 #[repr(C)]
2738 pub struct StrTuple {
2739     key: WasmStr,
2740     value: WasmStr,
2741 }
2742 
2743 #[derive(Copy, Clone)]
2744 #[repr(C)]
2745 pub struct StrTupleList {
2746     base: *const StrTuple,
2747     len: usize,
2748 }
2749 
2750 #[derive(Copy, Clone)]
2751 #[repr(C)]
2752 pub struct ReadyList {
2753     base: *const u32,
2754     len: usize,
2755 }
2756 
temporary_data_size() -> usize2757 const fn temporary_data_size() -> usize {
2758     // The total size of the struct should be a page, so start there
2759     let mut start = PAGE_SIZE;
2760 
2761     // Remove big chunks of the struct for its various fields.
2762     start -= size_of::<Descriptors>();
2763     #[cfg(not(feature = "proxy"))]
2764     {
2765         start -= size_of::<DirentCache>();
2766     }
2767 
2768     // Remove miscellaneous metadata also stored in state.
2769     let misc = if cfg!(feature = "proxy") { 8 } else { 10 };
2770     start -= misc * size_of::<usize>();
2771 
2772     // Everything else is the `command_data` allocation.
2773     start
2774 }
2775 
2776 // Statically assert that the `State` structure is the size of a wasm page. This
2777 // mostly guarantees that it's not larger than one page which is relied upon
2778 // below.
2779 #[cfg(target_arch = "wasm32")]
2780 const _: () = {
2781     let _size_assert: [(); PAGE_SIZE] = [(); size_of::<State>()];
2782 };
2783 
2784 #[expect(unused, reason = "not used in all configurations")]
2785 #[repr(i32)]
2786 enum AllocationState {
2787     StackUnallocated,
2788     StackAllocating,
2789     StackAllocated,
2790     StateAllocating,
2791     StateAllocated,
2792 }
2793 
2794 #[expect(improper_ctypes, reason = "types behind pointers")]
2795 unsafe extern "C" {
get_state_ptr() -> *mut State2796     fn get_state_ptr() -> *mut State;
set_state_ptr(state: *mut State)2797     fn set_state_ptr(state: *mut State);
get_allocation_state() -> AllocationState2798     fn get_allocation_state() -> AllocationState;
set_allocation_state(state: AllocationState)2799     fn set_allocation_state(state: AllocationState);
2800 }
2801 
2802 impl State {
with(f: impl FnOnce(&State) -> Result<(), Errno>) -> Errno2803     fn with(f: impl FnOnce(&State) -> Result<(), Errno>) -> Errno {
2804         let state_ref = State::ptr();
2805         assert_eq!(state_ref.magic1, MAGIC);
2806         assert_eq!(state_ref.magic2, MAGIC);
2807         let ret = f(state_ref);
2808         match ret {
2809             Ok(()) => ERRNO_SUCCESS,
2810             Err(err) => err,
2811         }
2812     }
2813 
ptr() -> &'static State2814     fn ptr() -> &'static State {
2815         unsafe {
2816             let mut ptr = get_state_ptr();
2817             if ptr.is_null() {
2818                 ptr = State::new();
2819                 set_state_ptr(ptr);
2820             }
2821             &*ptr
2822         }
2823     }
2824 
2825     #[cold]
new() -> *mut State2826     fn new() -> *mut State {
2827         #[link(wasm_import_module = "__main_module__")]
2828         unsafe extern "C" {
2829             fn cabi_realloc(
2830                 old_ptr: *mut u8,
2831                 old_len: usize,
2832                 align: usize,
2833                 new_len: usize,
2834             ) -> *mut u8;
2835         }
2836 
2837         assert!(matches!(
2838             unsafe { get_allocation_state() },
2839             AllocationState::StackAllocated
2840         ));
2841 
2842         unsafe { set_allocation_state(AllocationState::StateAllocating) };
2843 
2844         let ret = unsafe {
2845             cabi_realloc(
2846                 ptr::null_mut(),
2847                 0,
2848                 mem::align_of::<UnsafeCell<State>>(),
2849                 mem::size_of::<UnsafeCell<State>>(),
2850             ) as *mut State
2851         };
2852 
2853         unsafe { set_allocation_state(AllocationState::StateAllocated) };
2854 
2855         unsafe {
2856             Self::init(ret);
2857         }
2858 
2859         ret
2860     }
2861 
2862     #[cold]
init(state: *mut State)2863     unsafe fn init(state: *mut State) {
2864         unsafe {
2865             state.write(State {
2866                 magic1: MAGIC,
2867                 magic2: MAGIC,
2868                 import_alloc: Cell::new(ImportAlloc::None),
2869                 descriptors: RefCell::new(None),
2870                 temporary_data: UnsafeCell::new(MaybeUninit::uninit()),
2871                 #[cfg(not(feature = "proxy"))]
2872                 dirent_cache: DirentCache {
2873                     stream: Cell::new(None),
2874                     for_fd: Cell::new(0),
2875                     cookie: Cell::new(wasip1::DIRCOOKIE_START),
2876                     cached_dirent: Cell::new(wasip1::Dirent {
2877                         d_next: 0,
2878                         d_ino: 0,
2879                         d_type: FILETYPE_UNKNOWN,
2880                         d_namlen: 0,
2881                     }),
2882                     path_data: UnsafeCell::new(MaybeUninit::uninit()),
2883                 },
2884                 #[cfg(not(feature = "proxy"))]
2885                 dotdot: [UnsafeCell::new(b'.'), UnsafeCell::new(b'.')],
2886             });
2887         }
2888     }
2889 
2890     /// Accessor for the descriptors member that ensures it is properly initialized
descriptors<'a>(&'a self) -> impl Deref<Target = Descriptors> + 'a2891     fn descriptors<'a>(&'a self) -> impl Deref<Target = Descriptors> + 'a {
2892         let mut d = self
2893             .descriptors
2894             .try_borrow_mut()
2895             .unwrap_or_else(|_| unreachable!());
2896         if d.is_none() {
2897             *d = Some(Descriptors::new(self));
2898         }
2899         RefMut::map(d, |d| d.as_mut().unwrap_or_else(|| unreachable!()))
2900     }
2901 
2902     /// Mut accessor for the descriptors member that ensures it is properly initialized
descriptors_mut<'a>(&'a self) -> impl DerefMut + Deref<Target = Descriptors> + 'a2903     fn descriptors_mut<'a>(&'a self) -> impl DerefMut + Deref<Target = Descriptors> + 'a {
2904         let mut d = self
2905             .descriptors
2906             .try_borrow_mut()
2907             .unwrap_or_else(|_| unreachable!());
2908         if d.is_none() {
2909             *d = Some(Descriptors::new(self));
2910         }
2911         RefMut::map(d, |d| d.as_mut().unwrap_or_else(|| unreachable!()))
2912     }
2913 
temporary_alloc(&self) -> BumpAlloc2914     unsafe fn temporary_alloc(&self) -> BumpAlloc {
2915         BumpAlloc {
2916             base: self.temporary_data.get().cast(),
2917             len: mem::size_of_val(&self.temporary_data),
2918         }
2919     }
2920 
2921     /// Configure that `cabi_import_realloc` will allocate once from
2922     /// `self.temporary_data` for the duration of the closure `f`.
2923     ///
2924     /// Panics if the import allocator is already configured.
with_one_temporary_alloc<T>(&self, f: impl FnOnce() -> T) -> T2925     fn with_one_temporary_alloc<T>(&self, f: impl FnOnce() -> T) -> T {
2926         let alloc = unsafe { self.temporary_alloc() };
2927         self.with_import_alloc(ImportAlloc::OneAlloc(alloc), f).0
2928     }
2929 
2930     /// Configure that `cabi_import_realloc` will allocate once from
2931     /// `base` with at most `len` bytes for the duration of `f`.
2932     ///
2933     /// Panics if the import allocator is already configured.
with_one_import_alloc<T>(&self, base: *mut u8, len: usize, f: impl FnOnce() -> T) -> T2934     fn with_one_import_alloc<T>(&self, base: *mut u8, len: usize, f: impl FnOnce() -> T) -> T {
2935         let alloc = BumpAlloc { base, len };
2936         self.with_import_alloc(ImportAlloc::OneAlloc(alloc), f).0
2937     }
2938 
2939     /// Configures the `alloc` specified to be the allocator for
2940     /// `cabi_import_realloc` for the duration of `f`.
2941     ///
2942     /// Panics if the import allocator is already configured.
with_import_alloc<T>(&self, alloc: ImportAlloc, f: impl FnOnce() -> T) -> (T, ImportAlloc)2943     fn with_import_alloc<T>(&self, alloc: ImportAlloc, f: impl FnOnce() -> T) -> (T, ImportAlloc) {
2944         match self.import_alloc.replace(alloc) {
2945             ImportAlloc::None => {}
2946             _ => unreachable!("import allocator already set"),
2947         }
2948         let r = f();
2949         let alloc = self.import_alloc.replace(ImportAlloc::None);
2950         (r, alloc)
2951     }
2952 }
2953