wasmer_wasi/syscalls/
mod.rs

1#![allow(unused, clippy::too_many_arguments, clippy::cognitive_complexity)]
2
3pub mod types {
4    pub use wasmer_wasi_types::types::*;
5    pub use wasmer_wasi_types::wasi;
6}
7
8#[cfg(any(
9    target_os = "freebsd",
10    target_os = "linux",
11    target_os = "android",
12    target_vendor = "apple"
13))]
14pub mod unix;
15#[cfg(any(target_arch = "wasm32"))]
16pub mod wasm32;
17#[cfg(any(target_os = "windows"))]
18pub mod windows;
19
20pub mod legacy;
21//pub mod wasi;
22#[cfg(feature = "wasix")]
23pub mod wasix32;
24#[cfg(feature = "wasix")]
25pub mod wasix64;
26
27use self::types::{
28    wasi::{
29        Addressfamily, Advice, Bid, BusDataFormat, BusErrno, BusHandles, Cid, Clockid, Dircookie,
30        Dirent, Errno, Event, EventEnum, EventFdReadwrite, Eventrwflags, Eventtype, Fd as WasiFd,
31        Fdflags, Fdstat, Filesize, Filestat, Filetype, Fstflags, Linkcount, OptionFd, Pid, Prestat,
32        Rights, Snapshot0Clockid, Sockoption, Sockstatus, Socktype, StdioMode as WasiStdioMode,
33        Streamsecurity, Subscription, SubscriptionEnum, SubscriptionFsReadwrite, Tid, Timestamp,
34        Tty, Whence,
35    },
36    *,
37};
38use crate::state::{bus_error_into_wasi_err, wasi_error_into_bus_err, InodeHttpSocketType};
39use crate::utils::map_io_err;
40use crate::WasiBusProcessId;
41use crate::{
42    mem_error_to_wasi,
43    state::{
44        self, fs_error_into_wasi_err, iterate_poll_events, net_error_into_wasi_err, poll,
45        virtual_file_type_to_wasi_file_type, Inode, InodeSocket, InodeSocketKind, InodeVal, Kind,
46        PollEvent, PollEventBuilder, WasiPipe, WasiState, MAX_SYMLINKS,
47    },
48    Fd, WasiEnv, WasiError, WasiThread, WasiThreadId,
49};
50use bytes::Bytes;
51use std::borrow::{Borrow, Cow};
52use std::convert::{Infallible, TryInto};
53use std::io::{self, Read, Seek, Write};
54use std::mem::transmute;
55use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
56use std::ops::{Deref, DerefMut};
57use std::sync::atomic::AtomicU64;
58use std::sync::{atomic::Ordering, Mutex};
59use std::sync::{mpsc, Arc};
60use std::time::Duration;
61use tracing::{debug, error, trace, warn};
62use wasmer::{
63    AsStoreMut, Extern, FunctionEnv, FunctionEnvMut, Instance, Memory, Memory32, Memory64,
64    MemorySize, MemoryView, Module, RuntimeError, Value, WasmPtr, WasmSlice,
65};
66use wasmer_vbus::{FileDescriptor, StdioMode};
67use wasmer_vfs::{FsError, VirtualFile};
68use wasmer_vnet::{SocketHttpRequest, StreamSecurity};
69
70#[cfg(any(
71    target_os = "freebsd",
72    target_os = "linux",
73    target_os = "android",
74    target_vendor = "apple"
75))]
76pub use unix::*;
77
78#[cfg(any(target_os = "windows"))]
79pub use windows::*;
80
81#[cfg(any(target_arch = "wasm32"))]
82pub use wasm32::*;
83
84fn to_offset<M: MemorySize>(offset: usize) -> Result<M::Offset, Errno> {
85    let ret: M::Offset = offset.try_into().map_err(|_| Errno::Inval)?;
86    Ok(ret)
87}
88
89fn from_offset<M: MemorySize>(offset: M::Offset) -> Result<usize, Errno> {
90    let ret: usize = offset.try_into().map_err(|_| Errno::Inval)?;
91    Ok(ret)
92}
93
94fn write_bytes_inner<T: Write, M: MemorySize>(
95    mut write_loc: T,
96    memory: &MemoryView,
97    iovs_arr_cell: WasmSlice<__wasi_ciovec_t<M>>,
98) -> Result<usize, Errno> {
99    let mut bytes_written = 0usize;
100    for iov in iovs_arr_cell.iter() {
101        let iov_inner = iov.read().map_err(mem_error_to_wasi)?;
102        let bytes = WasmPtr::<u8, M>::new(iov_inner.buf)
103            .slice(memory, iov_inner.buf_len)
104            .map_err(mem_error_to_wasi)?;
105        let bytes = bytes.read_to_vec().map_err(mem_error_to_wasi)?;
106        write_loc.write_all(&bytes).map_err(map_io_err)?;
107
108        bytes_written += from_offset::<M>(iov_inner.buf_len)?;
109    }
110    Ok(bytes_written)
111}
112
113pub(crate) fn write_bytes<T: Write, M: MemorySize>(
114    mut write_loc: T,
115    memory: &MemoryView,
116    iovs_arr: WasmSlice<__wasi_ciovec_t<M>>,
117) -> Result<usize, Errno> {
118    let result = write_bytes_inner::<_, M>(&mut write_loc, memory, iovs_arr);
119    write_loc.flush();
120    result
121}
122
123pub(crate) fn read_bytes<T: Read, M: MemorySize>(
124    mut reader: T,
125    memory: &MemoryView,
126    iovs_arr: WasmSlice<__wasi_iovec_t<M>>,
127) -> Result<usize, Errno> {
128    let mut bytes_read = 0usize;
129
130    // We allocate the raw_bytes first once instead of
131    // N times in the loop.
132    let mut raw_bytes: Vec<u8> = vec![0; 1024];
133
134    for iov in iovs_arr.iter() {
135        let iov_inner = iov.read().map_err(mem_error_to_wasi)?;
136        raw_bytes.clear();
137        let to_read = from_offset::<M>(iov_inner.buf_len)?;
138        raw_bytes.resize(to_read, 0);
139        let has_read = reader.read(&mut raw_bytes).map_err(map_io_err)?;
140
141        let buf = WasmPtr::<u8, M>::new(iov_inner.buf)
142            .slice(memory, iov_inner.buf_len)
143            .map_err(mem_error_to_wasi)?;
144        buf.write_slice(&raw_bytes).map_err(mem_error_to_wasi)?;
145        bytes_read += has_read;
146        if has_read != to_read {
147            return Ok(bytes_read);
148        }
149    }
150    Ok(bytes_read)
151}
152
153fn __sock_actor<T, F>(
154    ctx: &FunctionEnvMut<'_, WasiEnv>,
155    sock: WasiFd,
156    rights: Rights,
157    actor: F,
158) -> Result<T, Errno>
159where
160    F: FnOnce(&crate::state::InodeSocket) -> Result<T, Errno>,
161{
162    let env = ctx.data();
163    let (_, state, inodes) = env.get_memory_and_wasi_state_and_inodes(&ctx, 0);
164
165    let fd_entry = state.fs.get_fd(sock)?;
166    let ret = {
167        if !rights.is_empty() && !fd_entry.rights.contains(rights) {
168            return Err(Errno::Access);
169        }
170
171        let inode_idx = fd_entry.inode;
172        let inode = &inodes.arena[inode_idx];
173
174        let mut guard = inode.read();
175        let deref = guard.deref();
176        match deref {
177            Kind::Socket { socket } => actor(socket)?,
178            _ => {
179                return Err(Errno::Notsock);
180            }
181        }
182    };
183
184    Ok(ret)
185}
186
187fn __sock_actor_mut<T, F>(
188    ctx: &FunctionEnvMut<'_, WasiEnv>,
189    sock: WasiFd,
190    rights: Rights,
191    actor: F,
192) -> Result<T, Errno>
193where
194    F: FnOnce(&mut crate::state::InodeSocket) -> Result<T, Errno>,
195{
196    let env = ctx.data();
197    let (_, state, inodes) = env.get_memory_and_wasi_state_and_inodes(&ctx, 0);
198
199    let fd_entry = state.fs.get_fd(sock)?;
200    let ret = {
201        if !rights.is_empty() && !fd_entry.rights.contains(rights) {
202            return Err(Errno::Access);
203        }
204
205        let inode_idx = fd_entry.inode;
206        let inode = &inodes.arena[inode_idx];
207
208        let mut guard = inode.write();
209        let deref_mut = guard.deref_mut();
210        match deref_mut {
211            Kind::Socket { socket } => actor(socket)?,
212            _ => {
213                return Err(Errno::Notsock);
214            }
215        }
216    };
217
218    Ok(ret)
219}
220
221fn __sock_upgrade<F>(
222    ctx: &FunctionEnvMut<'_, WasiEnv>,
223    sock: WasiFd,
224    rights: Rights,
225    actor: F,
226) -> Result<(), Errno>
227where
228    F: FnOnce(&mut crate::state::InodeSocket) -> Result<Option<crate::state::InodeSocket>, Errno>,
229{
230    let env = ctx.data();
231    let (_, state, inodes) = env.get_memory_and_wasi_state_and_inodes(&ctx, 0);
232
233    let fd_entry = state.fs.get_fd(sock)?;
234    if !rights.is_empty() && !fd_entry.rights.contains(rights) {
235        return Err(Errno::Access);
236    }
237
238    let inode_idx = fd_entry.inode;
239    let inode = &inodes.arena[inode_idx];
240
241    let mut guard = inode.write();
242    let deref_mut = guard.deref_mut();
243    match deref_mut {
244        Kind::Socket { socket } => {
245            let new_socket = actor(socket)?;
246
247            if let Some(mut new_socket) = new_socket {
248                std::mem::swap(socket, &mut new_socket);
249            }
250        }
251        _ => {
252            return Err(Errno::Notsock);
253        }
254    }
255
256    Ok(())
257}
258
259#[must_use]
260fn write_buffer_array<M: MemorySize>(
261    memory: &MemoryView,
262    from: &[Vec<u8>],
263    ptr_buffer: WasmPtr<WasmPtr<u8, M>, M>,
264    buffer: WasmPtr<u8, M>,
265) -> Errno {
266    let ptrs = wasi_try_mem!(ptr_buffer.slice(memory, wasi_try!(to_offset::<M>(from.len()))));
267
268    let mut current_buffer_offset = 0usize;
269    for ((i, sub_buffer), ptr) in from.iter().enumerate().zip(ptrs.iter()) {
270        trace!("ptr: {:?}, subbuffer: {:?}", ptr, sub_buffer);
271        let mut buf_offset = buffer.offset();
272        buf_offset += wasi_try!(to_offset::<M>(current_buffer_offset));
273        let new_ptr = WasmPtr::new(buf_offset);
274        wasi_try_mem!(ptr.write(new_ptr));
275
276        let data =
277            wasi_try_mem!(new_ptr.slice(memory, wasi_try!(to_offset::<M>(sub_buffer.len()))));
278        wasi_try_mem!(data.write_slice(sub_buffer));
279        wasi_try_mem!(wasi_try_mem!(
280            new_ptr.add_offset(wasi_try!(to_offset::<M>(sub_buffer.len())))
281        )
282        .write(memory, 0));
283
284        current_buffer_offset += sub_buffer.len() + 1;
285    }
286
287    Errno::Success
288}
289
290fn get_current_time_in_nanos() -> Result<Timestamp, Errno> {
291    let now = std::time::SystemTime::now();
292    let duration = now
293        .duration_since(std::time::SystemTime::UNIX_EPOCH)
294        .map_err(|_| Errno::Io)?;
295    Ok(duration.as_nanos() as Timestamp)
296}
297
298/// ### `args_get()`
299/// Read command-line argument data.
300/// The sizes of the buffers should match that returned by [`args_sizes_get()`](#args_sizes_get).
301/// Inputs:
302/// - `char **argv`
303///     A pointer to a buffer to write the argument pointers.
304/// - `char *argv_buf`
305///     A pointer to a buffer to write the argument string data.
306///
307pub fn args_get<M: MemorySize>(
308    mut ctx: FunctionEnvMut<'_, WasiEnv>,
309    argv: WasmPtr<WasmPtr<u8, M>, M>,
310    argv_buf: WasmPtr<u8, M>,
311) -> Errno {
312    debug!("wasi::args_get");
313    let env = ctx.data();
314    let (memory, mut state) = env.get_memory_and_wasi_state(&ctx, 0);
315
316    let result = write_buffer_array(&memory, &state.args, argv, argv_buf);
317
318    debug!(
319        "=> args:\n{}",
320        state
321            .args
322            .iter()
323            .enumerate()
324            .map(|(i, v)| format!("{:>20}: {}", i, ::std::str::from_utf8(v).unwrap()))
325            .collect::<Vec<String>>()
326            .join("\n")
327    );
328
329    result
330}
331
332/// ### `args_sizes_get()`
333/// Return command-line argument data sizes.
334/// Outputs:
335/// - `size_t *argc`
336///     The number of arguments.
337/// - `size_t *argv_buf_size`
338///     The size of the argument string data.
339pub fn args_sizes_get<M: MemorySize>(
340    mut ctx: FunctionEnvMut<'_, WasiEnv>,
341    argc: WasmPtr<M::Offset, M>,
342    argv_buf_size: WasmPtr<M::Offset, M>,
343) -> Errno {
344    debug!("wasi::args_sizes_get");
345    let env = ctx.data();
346    let (memory, mut state) = env.get_memory_and_wasi_state(&ctx, 0);
347
348    let argc = argc.deref(&memory);
349    let argv_buf_size = argv_buf_size.deref(&memory);
350
351    let argc_val: M::Offset = wasi_try!(state.args.len().try_into().map_err(|_| Errno::Overflow));
352    let argv_buf_size_val: usize = state.args.iter().map(|v| v.len() + 1).sum();
353    let argv_buf_size_val: M::Offset =
354        wasi_try!(argv_buf_size_val.try_into().map_err(|_| Errno::Overflow));
355    wasi_try_mem!(argc.write(argc_val));
356    wasi_try_mem!(argv_buf_size.write(argv_buf_size_val));
357
358    debug!("=> argc={}, argv_buf_size={}", argc_val, argv_buf_size_val);
359
360    Errno::Success
361}
362
363/// ### `clock_res_get()`
364/// Get the resolution of the specified clock
365/// Input:
366/// - `Clockid clock_id`
367///     The ID of the clock to get the resolution of
368/// Output:
369/// - `Timestamp *resolution`
370///     The resolution of the clock in nanoseconds
371pub fn clock_res_get<M: MemorySize>(
372    mut ctx: FunctionEnvMut<'_, WasiEnv>,
373    clock_id: Snapshot0Clockid,
374    resolution: WasmPtr<Timestamp, M>,
375) -> Errno {
376    trace!("wasi::clock_res_get");
377    let env = ctx.data();
378    let memory = env.memory_view(&ctx);
379
380    let out_addr = resolution.deref(&memory);
381    let t_out = wasi_try!(platform_clock_res_get(clock_id, out_addr));
382    wasi_try_mem!(resolution.write(&memory, t_out as Timestamp));
383    Errno::Success
384}
385
386/// ### `clock_time_get()`
387/// Get the time of the specified clock
388/// Inputs:
389/// - `Clockid clock_id`
390///     The ID of the clock to query
391/// - `Timestamp precision`
392///     The maximum amount of error the reading may have
393/// Output:
394/// - `Timestamp *time`
395///     The value of the clock in nanoseconds
396pub fn clock_time_get<M: MemorySize>(
397    ctx: FunctionEnvMut<'_, WasiEnv>,
398    clock_id: Snapshot0Clockid,
399    precision: Timestamp,
400    time: WasmPtr<Timestamp, M>,
401) -> Errno {
402    debug!(
403        "wasi::clock_time_get clock_id: {}, precision: {}",
404        clock_id as u8, precision
405    );
406    let env = ctx.data();
407    let memory = env.memory_view(&ctx);
408
409    let t_out = wasi_try!(platform_clock_time_get(clock_id, precision));
410    wasi_try_mem!(time.write(&memory, t_out as Timestamp));
411
412    let result = Errno::Success;
413    trace!(
414        "time: {} => {}",
415        wasi_try_mem!(time.deref(&memory).read()),
416        result
417    );
418    result
419}
420
421/// ### `environ_get()`
422/// Read environment variable data.
423/// The sizes of the buffers should match that returned by [`environ_sizes_get()`](#environ_sizes_get).
424/// Inputs:
425/// - `char **environ`
426///     A pointer to a buffer to write the environment variable pointers.
427/// - `char *environ_buf`
428///     A pointer to a buffer to write the environment variable string data.
429pub fn environ_get<M: MemorySize>(
430    ctx: FunctionEnvMut<'_, WasiEnv>,
431    environ: WasmPtr<WasmPtr<u8, M>, M>,
432    environ_buf: WasmPtr<u8, M>,
433) -> Errno {
434    debug!(
435        "wasi::environ_get. Environ: {:?}, environ_buf: {:?}",
436        environ, environ_buf
437    );
438    let env = ctx.data();
439    let (memory, mut state) = env.get_memory_and_wasi_state(&ctx, 0);
440    trace!(" -> State envs: {:?}", state.envs);
441
442    write_buffer_array(&memory, &state.envs, environ, environ_buf)
443}
444
445/// ### `environ_sizes_get()`
446/// Return command-line argument data sizes.
447/// Outputs:
448/// - `size_t *environ_count`
449///     The number of environment variables.
450/// - `size_t *environ_buf_size`
451///     The size of the environment variable string data.
452pub fn environ_sizes_get<M: MemorySize>(
453    ctx: FunctionEnvMut<'_, WasiEnv>,
454    environ_count: WasmPtr<M::Offset, M>,
455    environ_buf_size: WasmPtr<M::Offset, M>,
456) -> Errno {
457    trace!("wasi::environ_sizes_get");
458    let env = ctx.data();
459    let (memory, mut state) = env.get_memory_and_wasi_state(&ctx, 0);
460
461    let environ_count = environ_count.deref(&memory);
462    let environ_buf_size = environ_buf_size.deref(&memory);
463
464    let env_var_count: M::Offset =
465        wasi_try!(state.envs.len().try_into().map_err(|_| Errno::Overflow));
466    let env_buf_size: usize = state.envs.iter().map(|v| v.len() + 1).sum();
467    let env_buf_size: M::Offset = wasi_try!(env_buf_size.try_into().map_err(|_| Errno::Overflow));
468    wasi_try_mem!(environ_count.write(env_var_count));
469    wasi_try_mem!(environ_buf_size.write(env_buf_size));
470
471    trace!(
472        "env_var_count: {}, env_buf_size: {}",
473        env_var_count,
474        env_buf_size
475    );
476
477    Errno::Success
478}
479
480/// ### `fd_advise()`
481/// Advise the system about how a file will be used
482/// Inputs:
483/// - `Fd fd`
484///     The file descriptor the advice applies to
485/// - `Filesize offset`
486///     The offset from which the advice applies
487/// - `Filesize len`
488///     The length from the offset to which the advice applies
489/// - `__wasi_advice_t advice`
490///     The advice to give
491pub fn fd_advise(
492    ctx: FunctionEnvMut<'_, WasiEnv>,
493    fd: WasiFd,
494    offset: Filesize,
495    len: Filesize,
496    advice: Advice,
497) -> Errno {
498    debug!("wasi::fd_advise: fd={}", fd);
499
500    // this is used for our own benefit, so just returning success is a valid
501    // implementation for now
502    Errno::Success
503}
504
505/// ### `fd_allocate`
506/// Allocate extra space for a file descriptor
507/// Inputs:
508/// - `Fd fd`
509///     The file descriptor to allocate for
510/// - `Filesize offset`
511///     The offset from the start marking the beginning of the allocation
512/// - `Filesize len`
513///     The length from the offset marking the end of the allocation
514pub fn fd_allocate(
515    ctx: FunctionEnvMut<'_, WasiEnv>,
516    fd: WasiFd,
517    offset: Filesize,
518    len: Filesize,
519) -> Errno {
520    debug!("wasi::fd_allocate");
521    let env = ctx.data();
522    let (_, mut state, inodes) = env.get_memory_and_wasi_state_and_inodes(&ctx, 0);
523    let fd_entry = wasi_try!(state.fs.get_fd(fd));
524    let inode = fd_entry.inode;
525
526    if !fd_entry.rights.contains(Rights::FD_ALLOCATE) {
527        return Errno::Access;
528    }
529    let new_size = wasi_try!(offset.checked_add(len).ok_or(Errno::Inval));
530    {
531        let mut guard = inodes.arena[inode].write();
532        let deref_mut = guard.deref_mut();
533        match deref_mut {
534            Kind::File { handle, .. } => {
535                if let Some(handle) = handle {
536                    wasi_try!(handle.set_len(new_size).map_err(fs_error_into_wasi_err));
537                } else {
538                    return Errno::Badf;
539                }
540            }
541            Kind::Socket { .. } => return Errno::Badf,
542            Kind::Pipe { .. } => return Errno::Badf,
543            Kind::Buffer { buffer } => {
544                buffer.resize(new_size as usize, 0);
545            }
546            Kind::Symlink { .. } => return Errno::Badf,
547            Kind::EventNotifications { .. } => return Errno::Badf,
548            Kind::Dir { .. } | Kind::Root { .. } => return Errno::Isdir,
549        }
550    }
551    inodes.arena[inode].stat.write().unwrap().st_size = new_size;
552    debug!("New file size: {}", new_size);
553
554    Errno::Success
555}
556
557/// ### `fd_close()`
558/// Close an open file descriptor
559/// Inputs:
560/// - `Fd fd`
561///     A file descriptor mapping to an open file to close
562/// Errors:
563/// - `Errno::Isdir`
564///     If `fd` is a directory
565/// - `Errno::Badf`
566///     If `fd` is invalid or not open
567pub fn fd_close(ctx: FunctionEnvMut<'_, WasiEnv>, fd: WasiFd) -> Errno {
568    debug!("wasi::fd_close: fd={}", fd);
569    let env = ctx.data();
570    let (_, mut state, inodes) = env.get_memory_and_wasi_state_and_inodes(&ctx, 0);
571
572    let fd_entry = wasi_try!(state.fs.get_fd(fd));
573
574    wasi_try!(state.fs.close_fd(inodes.deref(), fd));
575
576    Errno::Success
577}
578
579/// ### `fd_datasync()`
580/// Synchronize the file data to disk
581/// Inputs:
582/// - `Fd fd`
583///     The file descriptor to sync
584pub fn fd_datasync(ctx: FunctionEnvMut<'_, WasiEnv>, fd: WasiFd) -> Errno {
585    debug!("wasi::fd_datasync");
586    let env = ctx.data();
587    let (_, mut state, inodes) = env.get_memory_and_wasi_state_and_inodes(&ctx, 0);
588    let fd_entry = wasi_try!(state.fs.get_fd(fd));
589    if !fd_entry.rights.contains(Rights::FD_DATASYNC) {
590        return Errno::Access;
591    }
592
593    if let Err(e) = state.fs.flush(inodes.deref(), fd) {
594        e
595    } else {
596        Errno::Success
597    }
598}
599
600/// ### `fd_fdstat_get()`
601/// Get metadata of a file descriptor
602/// Input:
603/// - `Fd fd`
604///     The file descriptor whose metadata will be accessed
605/// Output:
606/// - `Fdstat *buf`
607///     The location where the metadata will be written
608pub fn fd_fdstat_get<M: MemorySize>(
609    ctx: FunctionEnvMut<'_, WasiEnv>,
610    fd: WasiFd,
611    buf_ptr: WasmPtr<Fdstat, M>,
612) -> Errno {
613    debug!(
614        "wasi::fd_fdstat_get: fd={}, buf_ptr={}",
615        fd,
616        buf_ptr.offset()
617    );
618    let env = ctx.data();
619    let (memory, mut state, inodes) = env.get_memory_and_wasi_state_and_inodes(&ctx, 0);
620    let stat = wasi_try!(state.fs.fdstat(inodes.deref(), fd));
621
622    let buf = buf_ptr.deref(&memory);
623
624    wasi_try_mem!(buf.write(stat));
625
626    Errno::Success
627}
628
629/// ### `fd_fdstat_set_flags()`
630/// Set file descriptor flags for a file descriptor
631/// Inputs:
632/// - `Fd fd`
633///     The file descriptor to apply the new flags to
634/// - `Fdflags flags`
635///     The flags to apply to `fd`
636pub fn fd_fdstat_set_flags(ctx: FunctionEnvMut<'_, WasiEnv>, fd: WasiFd, flags: Fdflags) -> Errno {
637    debug!("wasi::fd_fdstat_set_flags");
638    let env = ctx.data();
639    let (_, mut state) = env.get_memory_and_wasi_state(&ctx, 0);
640    let mut fd_map = state.fs.fd_map.write().unwrap();
641    let fd_entry = wasi_try!(fd_map.get_mut(&fd).ok_or(Errno::Badf));
642
643    if !fd_entry.rights.contains(Rights::FD_FDSTAT_SET_FLAGS) {
644        return Errno::Access;
645    }
646
647    fd_entry.flags = flags;
648    Errno::Success
649}
650
651/// ### `fd_fdstat_set_rights()`
652/// Set the rights of a file descriptor.  This can only be used to remove rights
653/// Inputs:
654/// - `Fd fd`
655///     The file descriptor to apply the new rights to
656/// - `Rights fs_rights_base`
657///     The rights to apply to `fd`
658/// - `Rights fs_rights_inheriting`
659///     The inheriting rights to apply to `fd`
660pub fn fd_fdstat_set_rights(
661    ctx: FunctionEnvMut<'_, WasiEnv>,
662    fd: WasiFd,
663    fs_rights_base: Rights,
664    fs_rights_inheriting: Rights,
665) -> Errno {
666    debug!("wasi::fd_fdstat_set_rights");
667    let env = ctx.data();
668    let (_, mut state) = env.get_memory_and_wasi_state(&ctx, 0);
669    let mut fd_map = state.fs.fd_map.write().unwrap();
670    let fd_entry = wasi_try!(fd_map.get_mut(&fd).ok_or(Errno::Badf));
671
672    // ensure new rights are a subset of current rights
673    if fd_entry.rights | fs_rights_base != fd_entry.rights
674        || fd_entry.rights_inheriting | fs_rights_inheriting != fd_entry.rights_inheriting
675    {
676        return Errno::Notcapable;
677    }
678
679    fd_entry.rights = fs_rights_base;
680    fd_entry.rights_inheriting = fs_rights_inheriting;
681
682    Errno::Success
683}
684
685/// ### `fd_filestat_get()`
686/// Get the metadata of an open file
687/// Input:
688/// - `Fd fd`
689///     The open file descriptor whose metadata will be read
690/// Output:
691/// - `Filestat *buf`
692///     Where the metadata from `fd` will be written
693pub fn fd_filestat_get<M: MemorySize>(
694    ctx: FunctionEnvMut<'_, WasiEnv>,
695    fd: WasiFd,
696    buf: WasmPtr<Filestat, M>,
697) -> Errno {
698    debug!("wasi::fd_filestat_get");
699    let env = ctx.data();
700    let (memory, mut state, inodes) = env.get_memory_and_wasi_state_and_inodes(&ctx, 0);
701    let fd_entry = wasi_try!(state.fs.get_fd(fd));
702    if !fd_entry.rights.contains(Rights::FD_FILESTAT_GET) {
703        return Errno::Access;
704    }
705
706    let stat = wasi_try!(state.fs.filestat_fd(inodes.deref(), fd));
707
708    let buf = buf.deref(&memory);
709    wasi_try_mem!(buf.write(stat));
710
711    Errno::Success
712}
713
714/// ### `fd_filestat_set_size()`
715/// Change the size of an open file, zeroing out any new bytes
716/// Inputs:
717/// - `Fd fd`
718///     File descriptor to adjust
719/// - `Filesize st_size`
720///     New size that `fd` will be set to
721pub fn fd_filestat_set_size(
722    ctx: FunctionEnvMut<'_, WasiEnv>,
723    fd: WasiFd,
724    st_size: Filesize,
725) -> Errno {
726    debug!("wasi::fd_filestat_set_size");
727    let env = ctx.data();
728    let (_, mut state, inodes) = env.get_memory_and_wasi_state_and_inodes(&ctx, 0);
729    let fd_entry = wasi_try!(state.fs.get_fd(fd));
730    let inode = fd_entry.inode;
731
732    if !fd_entry.rights.contains(Rights::FD_FILESTAT_SET_SIZE) {
733        return Errno::Access;
734    }
735
736    {
737        let mut guard = inodes.arena[inode].write();
738        let deref_mut = guard.deref_mut();
739        match deref_mut {
740            Kind::File { handle, .. } => {
741                if let Some(handle) = handle {
742                    wasi_try!(handle.set_len(st_size).map_err(fs_error_into_wasi_err));
743                } else {
744                    return Errno::Badf;
745                }
746            }
747            Kind::Buffer { buffer } => {
748                buffer.resize(st_size as usize, 0);
749            }
750            Kind::Socket { .. } => return Errno::Badf,
751            Kind::Pipe { .. } => return Errno::Badf,
752            Kind::Symlink { .. } => return Errno::Badf,
753            Kind::EventNotifications { .. } => return Errno::Badf,
754            Kind::Dir { .. } | Kind::Root { .. } => return Errno::Isdir,
755        }
756    }
757    inodes.arena[inode].stat.write().unwrap().st_size = st_size;
758
759    Errno::Success
760}
761
762/// ### `fd_filestat_set_times()`
763/// Set timestamp metadata on a file
764/// Inputs:
765/// - `Timestamp st_atim`
766///     Last accessed time
767/// - `Timestamp st_mtim`
768///     Last modified time
769/// - `Fstflags fst_flags`
770///     Bit-vector for controlling which times get set
771pub fn fd_filestat_set_times(
772    ctx: FunctionEnvMut<'_, WasiEnv>,
773    fd: WasiFd,
774    st_atim: Timestamp,
775    st_mtim: Timestamp,
776    fst_flags: Fstflags,
777) -> Errno {
778    debug!("wasi::fd_filestat_set_times");
779    let env = ctx.data();
780    let (_, mut state, inodes) = env.get_memory_and_wasi_state_and_inodes(&ctx, 0);
781    let fd_entry = wasi_try!(state.fs.get_fd(fd));
782
783    if !fd_entry.rights.contains(Rights::FD_FILESTAT_SET_TIMES) {
784        return Errno::Access;
785    }
786
787    if (fst_flags.contains(Fstflags::SET_ATIM) && fst_flags.contains(Fstflags::SET_ATIM_NOW))
788        || (fst_flags.contains(Fstflags::SET_MTIM) && fst_flags.contains(Fstflags::SET_MTIM_NOW))
789    {
790        return Errno::Inval;
791    }
792
793    let inode_idx = fd_entry.inode;
794    let inode = &inodes.arena[inode_idx];
795
796    if fst_flags.contains(Fstflags::SET_ATIM) || fst_flags.contains(Fstflags::SET_ATIM_NOW) {
797        let time_to_set = if fst_flags.contains(Fstflags::SET_ATIM) {
798            st_atim
799        } else {
800            wasi_try!(get_current_time_in_nanos())
801        };
802        inode.stat.write().unwrap().st_atim = time_to_set;
803    }
804
805    if fst_flags.contains(Fstflags::SET_MTIM) || fst_flags.contains(Fstflags::SET_MTIM_NOW) {
806        let time_to_set = if fst_flags.contains(Fstflags::SET_MTIM) {
807            st_mtim
808        } else {
809            wasi_try!(get_current_time_in_nanos())
810        };
811        inode.stat.write().unwrap().st_mtim = time_to_set;
812    }
813
814    Errno::Success
815}
816
817/// ### `fd_pread()`
818/// Read from the file at the given offset without updating the file cursor.
819/// This acts like a stateless version of Seek + Read
820/// Inputs:
821/// - `Fd fd`
822///     The file descriptor to read the data with
823/// - `const __wasi_iovec_t* iovs'
824///     Vectors where the data will be stored
825/// - `size_t iovs_len`
826///     The number of vectors to store the data into
827/// - `Filesize offset`
828///     The file cursor to use: the starting position from which data will be read
829/// Output:
830/// - `size_t nread`
831///     The number of bytes read
832pub fn fd_pread<M: MemorySize>(
833    ctx: FunctionEnvMut<'_, WasiEnv>,
834    fd: WasiFd,
835    iovs: WasmPtr<__wasi_iovec_t<M>, M>,
836    iovs_len: M::Offset,
837    offset: Filesize,
838    nread: WasmPtr<M::Offset, M>,
839) -> Result<Errno, WasiError> {
840    trace!("wasi::fd_pread: fd={}, offset={}", fd, offset);
841    let env = ctx.data();
842    let (memory, mut state, inodes) = env.get_memory_and_wasi_state_and_inodes(&ctx, 0);
843
844    let iovs = wasi_try_mem_ok!(iovs.slice(&memory, iovs_len));
845    let nread_ref = nread.deref(&memory);
846
847    let fd_entry = wasi_try_ok!(state.fs.get_fd(fd));
848    let bytes_read = match fd {
849        __WASI_STDIN_FILENO => {
850            let mut guard = wasi_try_ok!(
851                inodes
852                    .stdin_mut(&state.fs.fd_map)
853                    .map_err(fs_error_into_wasi_err),
854                env
855            );
856            if let Some(ref mut stdin) = guard.deref_mut() {
857                wasi_try_ok!(read_bytes(stdin, &memory, iovs), env)
858            } else {
859                return Ok(Errno::Badf);
860            }
861        }
862        __WASI_STDOUT_FILENO => return Ok(Errno::Inval),
863        __WASI_STDERR_FILENO => return Ok(Errno::Inval),
864        _ => {
865            let inode = fd_entry.inode;
866
867            if !fd_entry.rights.contains(Rights::FD_READ | Rights::FD_SEEK) {
868                debug!(
869                    "Invalid rights on {:X}: expected READ and SEEK",
870                    fd_entry.rights
871                );
872                return Ok(Errno::Access);
873            }
874            let mut guard = inodes.arena[inode].write();
875            let deref_mut = guard.deref_mut();
876            match deref_mut {
877                Kind::File { handle, .. } => {
878                    if let Some(h) = handle {
879                        wasi_try_ok!(
880                            h.seek(std::io::SeekFrom::Start(offset as u64))
881                                .map_err(map_io_err),
882                            env
883                        );
884                        wasi_try_ok!(read_bytes(h, &memory, iovs), env)
885                    } else {
886                        return Ok(Errno::Inval);
887                    }
888                }
889                Kind::Socket { socket } => {
890                    wasi_try_ok!(socket.recv(&memory, iovs), env)
891                }
892                Kind::Pipe { pipe } => {
893                    wasi_try_ok!(pipe.recv(&memory, iovs), env)
894                }
895                Kind::EventNotifications { .. } => return Ok(Errno::Inval),
896                Kind::Dir { .. } | Kind::Root { .. } => return Ok(Errno::Isdir),
897                Kind::Symlink { .. } => unimplemented!("Symlinks in wasi::fd_pread"),
898                Kind::Buffer { buffer } => {
899                    wasi_try_ok!(read_bytes(&buffer[(offset as usize)..], &memory, iovs), env)
900                }
901            }
902        }
903    };
904
905    let bytes_read: M::Offset = wasi_try_ok!(bytes_read.try_into().map_err(|_| Errno::Overflow));
906    wasi_try_mem_ok!(nread_ref.write(bytes_read));
907    debug!("Success: {} bytes read", bytes_read);
908    Ok(Errno::Success)
909}
910
911/// ### `fd_prestat_get()`
912/// Get metadata about a preopened file descriptor
913/// Input:
914/// - `Fd fd`
915///     The preopened file descriptor to query
916/// Output:
917/// - `__wasi_prestat *buf`
918///     Where the metadata will be written
919pub fn fd_prestat_get<M: MemorySize>(
920    ctx: FunctionEnvMut<'_, WasiEnv>,
921    fd: WasiFd,
922    buf: WasmPtr<Prestat, M>,
923) -> Errno {
924    trace!("wasi::fd_prestat_get: fd={}", fd);
925    let env = ctx.data();
926    let (memory, mut state, inodes) = env.get_memory_and_wasi_state_and_inodes(&ctx, 0);
927
928    let prestat_ptr = buf.deref(&memory);
929    wasi_try_mem!(
930        prestat_ptr.write(wasi_try!(state.fs.prestat_fd(inodes.deref(), fd).map_err(
931            |code| {
932                debug!("fd_prestat_get failed (fd={}) - errno={}", fd, code);
933                code
934            }
935        )))
936    );
937
938    Errno::Success
939}
940
941pub fn fd_prestat_dir_name<M: MemorySize>(
942    ctx: FunctionEnvMut<'_, WasiEnv>,
943    fd: WasiFd,
944    path: WasmPtr<u8, M>,
945    path_len: M::Offset,
946) -> Errno {
947    trace!(
948        "wasi::fd_prestat_dir_name: fd={}, path_len={}",
949        fd,
950        path_len
951    );
952    let env = ctx.data();
953    let (memory, mut state, inodes) = env.get_memory_and_wasi_state_and_inodes(&ctx, 0);
954    let path_chars = wasi_try_mem!(path.slice(&memory, path_len));
955
956    let real_inode = wasi_try!(state.fs.get_fd_inode(fd));
957    let inode_val = &inodes.arena[real_inode];
958
959    // check inode-val.is_preopened?
960
961    trace!("=> inode: {:?}", inode_val);
962    let guard = inode_val.read();
963    let deref = guard.deref();
964    match deref {
965        Kind::Dir { .. } | Kind::Root { .. } => {
966            let path_len: u64 = path_len.into();
967            if (inode_val.name.len() as u64) <= path_len {
968                wasi_try_mem!(path_chars
969                    .subslice(0..inode_val.name.len() as u64)
970                    .write_slice(inode_val.name.as_bytes()));
971
972                trace!("=> result: \"{}\"", inode_val.name);
973
974                Errno::Success
975            } else {
976                Errno::Overflow
977            }
978        }
979        Kind::Symlink { .. }
980        | Kind::Buffer { .. }
981        | Kind::File { .. }
982        | Kind::Socket { .. }
983        | Kind::Pipe { .. }
984        | Kind::EventNotifications { .. } => Errno::Notdir,
985    }
986}
987
988/// ### `fd_pwrite()`
989/// Write to a file without adjusting its offset
990/// Inputs:
991/// - `Fd`
992///     File descriptor (opened with writing) to write to
993/// - `const __wasi_ciovec_t *iovs`
994///     List of vectors to read data from
995/// - `u32 iovs_len`
996///     Length of data in `iovs`
997/// - `Filesize offset`
998///     The offset to write at
999/// Output:
1000/// - `u32 *nwritten`
1001///     Number of bytes written
1002pub fn fd_pwrite<M: MemorySize>(
1003    ctx: FunctionEnvMut<'_, WasiEnv>,
1004    fd: WasiFd,
1005    iovs: WasmPtr<__wasi_ciovec_t<M>, M>,
1006    iovs_len: M::Offset,
1007    offset: Filesize,
1008    nwritten: WasmPtr<M::Offset, M>,
1009) -> Result<Errno, WasiError> {
1010    trace!("wasi::fd_pwrite");
1011    // TODO: refactor, this is just copied from `fd_write`...
1012    let env = ctx.data();
1013    let (memory, mut state, inodes) = env.get_memory_and_wasi_state_and_inodes(&ctx, 0);
1014    let iovs_arr = wasi_try_mem_ok!(iovs.slice(&memory, iovs_len));
1015    let nwritten_ref = nwritten.deref(&memory);
1016
1017    let fd_entry = wasi_try_ok!(state.fs.get_fd(fd));
1018    let bytes_written = match fd {
1019        __WASI_STDIN_FILENO => return Ok(Errno::Inval),
1020        __WASI_STDOUT_FILENO => {
1021            let mut guard = wasi_try_ok!(
1022                inodes
1023                    .stdout_mut(&state.fs.fd_map)
1024                    .map_err(fs_error_into_wasi_err),
1025                env
1026            );
1027            if let Some(ref mut stdout) = guard.deref_mut() {
1028                wasi_try_ok!(write_bytes(stdout, &memory, iovs_arr), env)
1029            } else {
1030                return Ok(Errno::Badf);
1031            }
1032        }
1033        __WASI_STDERR_FILENO => {
1034            let mut guard = wasi_try_ok!(
1035                inodes
1036                    .stderr_mut(&state.fs.fd_map)
1037                    .map_err(fs_error_into_wasi_err),
1038                env
1039            );
1040            if let Some(ref mut stderr) = guard.deref_mut() {
1041                wasi_try_ok!(write_bytes(stderr, &memory, iovs_arr), env)
1042            } else {
1043                return Ok(Errno::Badf);
1044            }
1045        }
1046        _ => {
1047            if !fd_entry.rights.contains(Rights::FD_WRITE | Rights::FD_SEEK) {
1048                return Ok(Errno::Access);
1049            }
1050
1051            let inode_idx = fd_entry.inode;
1052            let inode = &inodes.arena[inode_idx];
1053
1054            let mut guard = inode.write();
1055            let deref_mut = guard.deref_mut();
1056            match deref_mut {
1057                Kind::File { handle, .. } => {
1058                    if let Some(handle) = handle {
1059                        wasi_try_ok!(
1060                            handle
1061                                .seek(std::io::SeekFrom::Start(offset as u64))
1062                                .map_err(map_io_err),
1063                            env
1064                        );
1065                        wasi_try_ok!(write_bytes(handle, &memory, iovs_arr), env)
1066                    } else {
1067                        return Ok(Errno::Inval);
1068                    }
1069                }
1070                Kind::Socket { socket } => {
1071                    wasi_try_ok!(socket.send(&memory, iovs_arr), env)
1072                }
1073                Kind::Pipe { pipe } => {
1074                    wasi_try_ok!(pipe.send(&memory, iovs_arr), env)
1075                }
1076                Kind::Dir { .. } | Kind::Root { .. } => {
1077                    // TODO: verify
1078                    return Ok(Errno::Isdir);
1079                }
1080                Kind::EventNotifications { .. } => return Ok(Errno::Inval),
1081                Kind::Symlink { .. } => unimplemented!("Symlinks in wasi::fd_pwrite"),
1082                Kind::Buffer { buffer } => {
1083                    wasi_try_ok!(
1084                        write_bytes(&mut buffer[(offset as usize)..], &memory, iovs_arr),
1085                        env
1086                    )
1087                }
1088            }
1089        }
1090    };
1091
1092    let bytes_written: M::Offset =
1093        wasi_try_ok!(bytes_written.try_into().map_err(|_| Errno::Overflow));
1094    wasi_try_mem_ok!(nwritten_ref.write(bytes_written));
1095
1096    Ok(Errno::Success)
1097}
1098
1099/// ### `fd_read()`
1100/// Read data from file descriptor
1101/// Inputs:
1102/// - `Fd fd`
1103///     File descriptor from which data will be read
1104/// - `const __wasi_iovec_t *iovs`
1105///     Vectors where data will be stored
1106/// - `u32 iovs_len`
1107///     Length of data in `iovs`
1108/// Output:
1109/// - `u32 *nread`
1110///     Number of bytes read
1111///
1112pub fn fd_read<M: MemorySize>(
1113    ctx: FunctionEnvMut<'_, WasiEnv>,
1114    fd: WasiFd,
1115    iovs: WasmPtr<__wasi_iovec_t<M>, M>,
1116    iovs_len: M::Offset,
1117    nread: WasmPtr<M::Offset, M>,
1118) -> Result<Errno, WasiError> {
1119    trace!("wasi::fd_read: fd={}", fd);
1120    let env = ctx.data();
1121    let (memory, mut state, inodes) = env.get_memory_and_wasi_state_and_inodes(&ctx, 0);
1122    //let iovs_len = if iovs_len > M::Offset::from(1u32) { M::Offset::from(1u32) } else { iovs_len };
1123    let iovs_arr = wasi_try_mem_ok!(iovs.slice(&memory, iovs_len));
1124    let nread_ref = nread.deref(&memory);
1125
1126    let fd_entry = wasi_try_ok!(state.fs.get_fd(fd));
1127    let bytes_read = match fd {
1128        __WASI_STDIN_FILENO => {
1129            let mut guard = wasi_try_ok!(
1130                inodes
1131                    .stdin_mut(&state.fs.fd_map)
1132                    .map_err(fs_error_into_wasi_err),
1133                env
1134            );
1135            if let Some(ref mut stdin) = guard.deref_mut() {
1136                wasi_try_ok!(read_bytes(stdin, &memory, iovs_arr), env)
1137            } else {
1138                return Ok(Errno::Badf);
1139            }
1140        }
1141        __WASI_STDOUT_FILENO | __WASI_STDERR_FILENO => return Ok(Errno::Inval),
1142        _ => {
1143            if !fd_entry.rights.contains(Rights::FD_READ) {
1144                // TODO: figure out the error to return when lacking rights
1145                return Ok(Errno::Access);
1146            }
1147
1148            let is_non_blocking = fd_entry.flags.contains(Fdflags::NONBLOCK);
1149            let offset = fd_entry.offset as usize;
1150            let inode_idx = fd_entry.inode;
1151            let inode = &inodes.arena[inode_idx];
1152
1153            let bytes_read = {
1154                let mut guard = inode.write();
1155                let deref_mut = guard.deref_mut();
1156                match deref_mut {
1157                    Kind::File { handle, .. } => {
1158                        if let Some(handle) = handle {
1159                            wasi_try_ok!(
1160                                handle
1161                                    .seek(std::io::SeekFrom::Start(offset as u64))
1162                                    .map_err(map_io_err),
1163                                env
1164                            );
1165                            wasi_try_ok!(read_bytes(handle, &memory, iovs_arr), env)
1166                        } else {
1167                            return Ok(Errno::Inval);
1168                        }
1169                    }
1170                    Kind::Socket { socket } => {
1171                        wasi_try_ok!(socket.recv(&memory, iovs_arr), env)
1172                    }
1173                    Kind::Pipe { pipe } => {
1174                        wasi_try_ok!(pipe.recv(&memory, iovs_arr), env)
1175                    }
1176                    Kind::Dir { .. } | Kind::Root { .. } => {
1177                        // TODO: verify
1178                        return Ok(Errno::Isdir);
1179                    }
1180                    Kind::EventNotifications {
1181                        counter,
1182                        is_semaphore,
1183                        wakers,
1184                    } => {
1185                        let counter = Arc::clone(counter);
1186                        let is_semaphore: bool = *is_semaphore;
1187                        let wakers = Arc::clone(wakers);
1188                        drop(guard);
1189                        drop(inodes);
1190
1191                        let (tx, rx) = mpsc::channel();
1192                        {
1193                            let mut guard = wakers.lock().unwrap();
1194                            guard.push_front(tx);
1195                        }
1196
1197                        let ret;
1198                        loop {
1199                            let val = counter.load(Ordering::Acquire);
1200                            if val > 0 {
1201                                let new_val = if is_semaphore { val - 1 } else { 0 };
1202                                if counter
1203                                    .compare_exchange(
1204                                        val,
1205                                        new_val,
1206                                        Ordering::AcqRel,
1207                                        Ordering::Acquire,
1208                                    )
1209                                    .is_ok()
1210                                {
1211                                    let reader = val.to_ne_bytes();
1212                                    ret = wasi_try_ok!(
1213                                        read_bytes(&reader[..], &memory, iovs_arr),
1214                                        env
1215                                    );
1216                                    break;
1217                                } else {
1218                                    continue;
1219                                }
1220                            }
1221
1222                            // If its none blocking then exit
1223                            if is_non_blocking {
1224                                return Ok(Errno::Again);
1225                            }
1226
1227                            // Yield for a fixed period of time and then check again
1228                            env.yield_now()?;
1229                            if rx.recv_timeout(Duration::from_millis(5)).is_err() {
1230                                env.sleep(Duration::from_millis(5))?;
1231                            }
1232                        }
1233                        ret
1234                    }
1235                    Kind::Symlink { .. } => unimplemented!("Symlinks in wasi::fd_read"),
1236                    Kind::Buffer { buffer } => {
1237                        wasi_try_ok!(read_bytes(&buffer[offset..], &memory, iovs_arr), env)
1238                    }
1239                }
1240            };
1241
1242            // reborrow
1243            let mut fd_map = state.fs.fd_map.write().unwrap();
1244            let fd_entry = wasi_try_ok!(fd_map.get_mut(&fd).ok_or(Errno::Badf));
1245            fd_entry.offset += bytes_read as u64;
1246
1247            bytes_read
1248        }
1249    };
1250    let bytes_read: M::Offset = wasi_try_ok!(bytes_read.try_into().map_err(|_| Errno::Overflow));
1251    wasi_try_mem_ok!(nread_ref.write(bytes_read));
1252
1253    Ok(Errno::Success)
1254}
1255
1256/// ### `fd_readdir()`
1257/// Read data from directory specified by file descriptor
1258/// Inputs:
1259/// - `Fd fd`
1260///     File descriptor from which directory data will be read
1261/// - `void *buf`
1262///     Buffer where directory entries are stored
1263/// - `u32 buf_len`
1264///     Length of data in `buf`
1265/// - `Dircookie cookie`
1266///     Where the directory reading should start from
1267/// Output:
1268/// - `u32 *bufused`
1269///     The Number of bytes stored in `buf`; if less than `buf_len` then entire
1270///     directory has been read
1271pub fn fd_readdir<M: MemorySize>(
1272    ctx: FunctionEnvMut<'_, WasiEnv>,
1273    fd: WasiFd,
1274    buf: WasmPtr<u8, M>,
1275    buf_len: M::Offset,
1276    cookie: Dircookie,
1277    bufused: WasmPtr<M::Offset, M>,
1278) -> Errno {
1279    trace!("wasi::fd_readdir");
1280    let env = ctx.data();
1281    let (memory, mut state, inodes) = env.get_memory_and_wasi_state_and_inodes(&ctx, 0);
1282    // TODO: figure out how this is supposed to work;
1283    // is it supposed to pack the buffer full every time until it can't? or do one at a time?
1284
1285    let buf_arr = wasi_try_mem!(buf.slice(&memory, buf_len));
1286    let bufused_ref = bufused.deref(&memory);
1287    let working_dir = wasi_try!(state.fs.get_fd(fd));
1288    let mut cur_cookie = cookie;
1289    let mut buf_idx = 0usize;
1290
1291    let entries: Vec<(String, Filetype, u64)> = {
1292        let guard = inodes.arena[working_dir.inode].read();
1293        let deref = guard.deref();
1294        match deref {
1295            Kind::Dir { path, entries, .. } => {
1296                debug!("Reading dir {:?}", path);
1297                // TODO: refactor this code
1298                // we need to support multiple calls,
1299                // simple and obviously correct implementation for now:
1300                // maintain consistent order via lexacographic sorting
1301                let fs_info = wasi_try!(wasi_try!(state.fs_read_dir(path))
1302                    .collect::<Result<Vec<_>, _>>()
1303                    .map_err(fs_error_into_wasi_err));
1304                let mut entry_vec = wasi_try!(fs_info
1305                    .into_iter()
1306                    .map(|entry| {
1307                        let filename = entry.file_name().to_string_lossy().to_string();
1308                        debug!("Getting file: {:?}", filename);
1309                        let filetype = virtual_file_type_to_wasi_file_type(
1310                            entry.file_type().map_err(fs_error_into_wasi_err)?,
1311                        );
1312                        Ok((
1313                            filename, filetype, 0, // TODO: inode
1314                        ))
1315                    })
1316                    .collect::<Result<Vec<(String, Filetype, u64)>, _>>());
1317                entry_vec.extend(
1318                    entries
1319                        .iter()
1320                        .filter(|(_, inode)| inodes.arena[**inode].is_preopened)
1321                        .map(|(name, inode)| {
1322                            let entry = &inodes.arena[*inode];
1323                            let stat = entry.stat.read().unwrap();
1324                            (entry.name.to_string(), stat.st_filetype, stat.st_ino)
1325                        }),
1326                );
1327                // adding . and .. special folders
1328                // TODO: inode
1329                entry_vec.push((".".to_string(), Filetype::Directory, 0));
1330                entry_vec.push(("..".to_string(), Filetype::Directory, 0));
1331                entry_vec.sort_by(|a, b| a.0.cmp(&b.0));
1332                entry_vec
1333            }
1334            Kind::Root { entries } => {
1335                debug!("Reading root");
1336                let sorted_entries = {
1337                    let mut entry_vec: Vec<(String, Inode)> =
1338                        entries.iter().map(|(a, b)| (a.clone(), *b)).collect();
1339                    entry_vec.sort_by(|a, b| a.0.cmp(&b.0));
1340                    entry_vec
1341                };
1342                sorted_entries
1343                    .into_iter()
1344                    .map(|(name, inode)| {
1345                        let entry = &inodes.arena[inode];
1346                        let stat = entry.stat.read().unwrap();
1347                        (format!("/{}", entry.name), stat.st_filetype, stat.st_ino)
1348                    })
1349                    .collect()
1350            }
1351            Kind::File { .. }
1352            | Kind::Symlink { .. }
1353            | Kind::Buffer { .. }
1354            | Kind::Socket { .. }
1355            | Kind::Pipe { .. }
1356            | Kind::EventNotifications { .. } => return Errno::Notdir,
1357        }
1358    };
1359
1360    for (entry_path_str, wasi_file_type, ino) in entries.iter().skip(cookie as usize) {
1361        cur_cookie += 1;
1362        let namlen = entry_path_str.len();
1363        debug!("Returning dirent for {}", entry_path_str);
1364        let dirent = Dirent {
1365            d_next: cur_cookie,
1366            d_ino: *ino,
1367            d_namlen: namlen as u32,
1368            d_type: *wasi_file_type,
1369        };
1370        let dirent_bytes = dirent_to_le_bytes(&dirent);
1371        let buf_len: u64 = buf_len.into();
1372        let upper_limit = std::cmp::min(
1373            (buf_len - buf_idx as u64) as usize,
1374            std::mem::size_of::<Dirent>(),
1375        );
1376        for (i, b) in dirent_bytes.iter().enumerate().take(upper_limit) {
1377            wasi_try_mem!(buf_arr.index((i + buf_idx) as u64).write(*b));
1378        }
1379        buf_idx += upper_limit;
1380        if upper_limit != std::mem::size_of::<Dirent>() {
1381            break;
1382        }
1383        let upper_limit = std::cmp::min((buf_len - buf_idx as u64) as usize, namlen);
1384        for (i, b) in entry_path_str.bytes().take(upper_limit).enumerate() {
1385            wasi_try_mem!(buf_arr.index((i + buf_idx) as u64).write(b));
1386        }
1387        buf_idx += upper_limit;
1388        if upper_limit != namlen {
1389            break;
1390        }
1391    }
1392
1393    let buf_idx: M::Offset = wasi_try!(buf_idx.try_into().map_err(|_| Errno::Overflow));
1394    wasi_try_mem!(bufused_ref.write(buf_idx));
1395    Errno::Success
1396}
1397
1398/// ### `fd_renumber()`
1399/// Atomically copy file descriptor
1400/// Inputs:
1401/// - `Fd from`
1402///     File descriptor to copy
1403/// - `Fd to`
1404///     Location to copy file descriptor to
1405pub fn fd_renumber(ctx: FunctionEnvMut<'_, WasiEnv>, from: WasiFd, to: WasiFd) -> Errno {
1406    debug!("wasi::fd_renumber: from={}, to={}", from, to);
1407    let env = ctx.data();
1408    let (_, mut state) = env.get_memory_and_wasi_state(&ctx, 0);
1409
1410    let mut fd_map = state.fs.fd_map.write().unwrap();
1411    let fd_entry = wasi_try!(fd_map.get_mut(&from).ok_or(Errno::Badf));
1412
1413    let new_fd_entry = Fd {
1414        // TODO: verify this is correct
1415        rights: fd_entry.rights_inheriting,
1416        ..*fd_entry
1417    };
1418
1419    fd_map.insert(to, new_fd_entry);
1420    fd_map.remove(&from);
1421    Errno::Success
1422}
1423
1424/// ### `fd_dup()`
1425/// Duplicates the file handle
1426/// Inputs:
1427/// - `Fd fd`
1428///   File handle to be cloned
1429/// Outputs:
1430/// - `Fd fd`
1431///   The new file handle that is a duplicate of the original
1432pub fn fd_dup<M: MemorySize>(
1433    ctx: FunctionEnvMut<'_, WasiEnv>,
1434    fd: WasiFd,
1435    ret_fd: WasmPtr<WasiFd, M>,
1436) -> Errno {
1437    debug!("wasi::fd_dup");
1438
1439    let env = ctx.data();
1440    let (memory, state) = env.get_memory_and_wasi_state(&ctx, 0);
1441    let fd = wasi_try!(state.fs.clone_fd(fd));
1442
1443    wasi_try_mem!(ret_fd.write(&memory, fd));
1444
1445    Errno::Success
1446}
1447
1448/// ### `fd_event()`
1449/// Creates a file handle for event notifications
1450pub fn fd_event<M: MemorySize>(
1451    ctx: FunctionEnvMut<'_, WasiEnv>,
1452    initial_val: u64,
1453    flags: EventFdFlags,
1454    ret_fd: WasmPtr<WasiFd, M>,
1455) -> Errno {
1456    debug!("wasi::fd_event");
1457
1458    let env = ctx.data();
1459    let (memory, state, mut inodes) = env.get_memory_and_wasi_state_and_inodes_mut(&ctx, 0);
1460
1461    let kind = Kind::EventNotifications {
1462        counter: Arc::new(AtomicU64::new(initial_val)),
1463        is_semaphore: flags & EVENT_FD_FLAGS_SEMAPHORE != 0,
1464        wakers: Default::default(),
1465    };
1466
1467    let inode = state.fs.create_inode_with_default_stat(
1468        inodes.deref_mut(),
1469        kind,
1470        false,
1471        "event".to_string(),
1472    );
1473    let rights = Rights::FD_READ | Rights::FD_WRITE | Rights::POLL_FD_READWRITE;
1474    let fd = wasi_try!(state
1475        .fs
1476        .create_fd(rights, rights, Fdflags::empty(), 0, inode));
1477
1478    wasi_try_mem!(ret_fd.write(&memory, fd));
1479
1480    Errno::Success
1481}
1482
1483/// ### `fd_seek()`
1484/// Update file descriptor offset
1485/// Inputs:
1486/// - `Fd fd`
1487///     File descriptor to mutate
1488/// - `FileDelta offset`
1489///     Number of bytes to adjust offset by
1490/// - `Whence whence`
1491///     What the offset is relative to
1492/// Output:
1493/// - `Filesize *fd`
1494///     The new offset relative to the start of the file
1495pub fn fd_seek<M: MemorySize>(
1496    ctx: FunctionEnvMut<'_, WasiEnv>,
1497    fd: WasiFd,
1498    offset: FileDelta,
1499    whence: Whence,
1500    newoffset: WasmPtr<Filesize, M>,
1501) -> Result<Errno, WasiError> {
1502    trace!("wasi::fd_seek: fd={}, offset={}", fd, offset);
1503    let env = ctx.data();
1504    let (memory, mut state, inodes) = env.get_memory_and_wasi_state_and_inodes(&ctx, 0);
1505    let new_offset_ref = newoffset.deref(&memory);
1506    let fd_entry = wasi_try_ok!(state.fs.get_fd(fd));
1507
1508    if !fd_entry.rights.contains(Rights::FD_SEEK) {
1509        return Ok(Errno::Access);
1510    }
1511
1512    // TODO: handle case if fd is a dir?
1513    match whence {
1514        Whence::Cur => {
1515            let mut fd_map = state.fs.fd_map.write().unwrap();
1516            let fd_entry = wasi_try_ok!(fd_map.get_mut(&fd).ok_or(Errno::Badf));
1517            fd_entry.offset = (fd_entry.offset as i64 + offset) as u64
1518        }
1519        Whence::End => {
1520            use std::io::SeekFrom;
1521            let inode_idx = fd_entry.inode;
1522            let mut guard = inodes.arena[inode_idx].write();
1523            let deref_mut = guard.deref_mut();
1524            match deref_mut {
1525                Kind::File { ref mut handle, .. } => {
1526                    if let Some(handle) = handle {
1527                        let end =
1528                            wasi_try_ok!(handle.seek(SeekFrom::End(0)).map_err(map_io_err), env);
1529
1530                        // TODO: handle case if fd_entry.offset uses 64 bits of a u64
1531                        drop(guard);
1532                        let mut fd_map = state.fs.fd_map.write().unwrap();
1533                        let fd_entry = wasi_try_ok!(fd_map.get_mut(&fd).ok_or(Errno::Badf));
1534                        fd_entry.offset = (end as i64 + offset) as u64;
1535                    } else {
1536                        return Ok(Errno::Inval);
1537                    }
1538                }
1539                Kind::Symlink { .. } => {
1540                    unimplemented!("wasi::fd_seek not implemented for symlinks")
1541                }
1542                Kind::Dir { .. }
1543                | Kind::Root { .. }
1544                | Kind::Socket { .. }
1545                | Kind::Pipe { .. }
1546                | Kind::EventNotifications { .. } => {
1547                    // TODO: check this
1548                    return Ok(Errno::Inval);
1549                }
1550                Kind::Buffer { .. } => {
1551                    // seeking buffers probably makes sense
1552                    // TODO: implement this
1553                    return Ok(Errno::Inval);
1554                }
1555            }
1556        }
1557        Whence::Set => {
1558            let mut fd_map = state.fs.fd_map.write().unwrap();
1559            let fd_entry = wasi_try_ok!(fd_map.get_mut(&fd).ok_or(Errno::Badf));
1560            fd_entry.offset = offset as u64
1561        }
1562        _ => return Ok(Errno::Inval),
1563    }
1564    // reborrow
1565    let fd_entry = wasi_try_ok!(state.fs.get_fd(fd));
1566    wasi_try_mem_ok!(new_offset_ref.write(fd_entry.offset));
1567
1568    Ok(Errno::Success)
1569}
1570
1571/// ### `fd_sync()`
1572/// Synchronize file and metadata to disk (TODO: expand upon what this means in our system)
1573/// Inputs:
1574/// - `Fd fd`
1575///     The file descriptor to sync
1576/// Errors:
1577/// TODO: figure out which errors this should return
1578/// - `Errno::Perm`
1579/// - `Errno::Notcapable`
1580pub fn fd_sync(ctx: FunctionEnvMut<'_, WasiEnv>, fd: WasiFd) -> Errno {
1581    debug!("wasi::fd_sync");
1582    debug!("=> fd={}", fd);
1583    let env = ctx.data();
1584    let (_, mut state, inodes) = env.get_memory_and_wasi_state_and_inodes(&ctx, 0);
1585    let fd_entry = wasi_try!(state.fs.get_fd(fd));
1586    if !fd_entry.rights.contains(Rights::FD_SYNC) {
1587        return Errno::Access;
1588    }
1589    let inode = fd_entry.inode;
1590
1591    // TODO: implement this for more than files
1592    {
1593        let mut guard = inodes.arena[inode].write();
1594        let deref_mut = guard.deref_mut();
1595        match deref_mut {
1596            Kind::File { handle, .. } => {
1597                if let Some(h) = handle {
1598                    wasi_try!(h.sync_to_disk().map_err(fs_error_into_wasi_err));
1599                } else {
1600                    return Errno::Inval;
1601                }
1602            }
1603            Kind::Root { .. } | Kind::Dir { .. } => return Errno::Isdir,
1604            Kind::Buffer { .. }
1605            | Kind::Symlink { .. }
1606            | Kind::Socket { .. }
1607            | Kind::Pipe { .. }
1608            | Kind::EventNotifications { .. } => return Errno::Inval,
1609        }
1610    }
1611
1612    Errno::Success
1613}
1614
1615/// ### `fd_tell()`
1616/// Get the offset of the file descriptor
1617/// Inputs:
1618/// - `Fd fd`
1619///     The file descriptor to access
1620/// Output:
1621/// - `Filesize *offset`
1622///     The offset of `fd` relative to the start of the file
1623pub fn fd_tell<M: MemorySize>(
1624    ctx: FunctionEnvMut<'_, WasiEnv>,
1625    fd: WasiFd,
1626    offset: WasmPtr<Filesize, M>,
1627) -> Errno {
1628    debug!("wasi::fd_tell");
1629    let env = ctx.data();
1630    let (memory, mut state) = env.get_memory_and_wasi_state(&ctx, 0);
1631    let offset_ref = offset.deref(&memory);
1632
1633    let fd_entry = wasi_try!(state.fs.get_fd(fd));
1634
1635    if !fd_entry.rights.contains(Rights::FD_TELL) {
1636        return Errno::Access;
1637    }
1638
1639    wasi_try_mem!(offset_ref.write(fd_entry.offset));
1640
1641    Errno::Success
1642}
1643
1644/// ### `fd_write()`
1645/// Write data to the file descriptor
1646/// Inputs:
1647/// - `Fd`
1648///     File descriptor (opened with writing) to write to
1649/// - `const __wasi_ciovec_t *iovs`
1650///     List of vectors to read data from
1651/// - `u32 iovs_len`
1652///     Length of data in `iovs`
1653/// Output:
1654/// - `u32 *nwritten`
1655///     Number of bytes written
1656/// Errors:
1657///
1658pub fn fd_write<M: MemorySize>(
1659    ctx: FunctionEnvMut<'_, WasiEnv>,
1660    fd: WasiFd,
1661    iovs: WasmPtr<__wasi_ciovec_t<M>, M>,
1662    iovs_len: M::Offset,
1663    nwritten: WasmPtr<M::Offset, M>,
1664) -> Result<Errno, WasiError> {
1665    trace!("wasi::fd_write: fd={}", fd);
1666    let env = ctx.data();
1667    let (memory, mut state, inodes) = env.get_memory_and_wasi_state_and_inodes(&ctx, 0);
1668    let iovs_arr = wasi_try_mem_ok!(iovs.slice(&memory, iovs_len));
1669    let nwritten_ref = nwritten.deref(&memory);
1670
1671    let fd_entry = wasi_try_ok!(state.fs.get_fd(fd));
1672    let bytes_written = match fd {
1673        __WASI_STDIN_FILENO => return Ok(Errno::Inval),
1674        __WASI_STDOUT_FILENO => {
1675            let mut guard = wasi_try_ok!(
1676                inodes
1677                    .stdout_mut(&state.fs.fd_map)
1678                    .map_err(fs_error_into_wasi_err),
1679                env
1680            );
1681            if let Some(ref mut stdout) = guard.deref_mut() {
1682                wasi_try_ok!(write_bytes(stdout, &memory, iovs_arr), env)
1683            } else {
1684                return Ok(Errno::Badf);
1685            }
1686        }
1687        __WASI_STDERR_FILENO => {
1688            let mut guard = wasi_try_ok!(
1689                inodes
1690                    .stderr_mut(&state.fs.fd_map)
1691                    .map_err(fs_error_into_wasi_err),
1692                env
1693            );
1694            if let Some(ref mut stderr) = guard.deref_mut() {
1695                wasi_try_ok!(write_bytes(stderr, &memory, iovs_arr), env)
1696            } else {
1697                return Ok(Errno::Badf);
1698            }
1699        }
1700        _ => {
1701            if !fd_entry.rights.contains(Rights::FD_WRITE) {
1702                return Ok(Errno::Access);
1703            }
1704
1705            let offset = fd_entry.offset as usize;
1706            let inode_idx = fd_entry.inode;
1707            let inode = &inodes.arena[inode_idx];
1708
1709            let bytes_written = {
1710                let mut guard = inode.write();
1711                let deref_mut = guard.deref_mut();
1712                match deref_mut {
1713                    Kind::File { handle, .. } => {
1714                        if let Some(handle) = handle {
1715                            wasi_try_ok!(
1716                                handle
1717                                    .seek(std::io::SeekFrom::Start(offset as u64))
1718                                    .map_err(map_io_err),
1719                                env
1720                            );
1721                            wasi_try_ok!(write_bytes(handle, &memory, iovs_arr), env)
1722                        } else {
1723                            return Ok(Errno::Inval);
1724                        }
1725                    }
1726                    Kind::Socket { socket } => {
1727                        wasi_try_ok!(socket.send(&memory, iovs_arr), env)
1728                    }
1729                    Kind::Pipe { pipe } => {
1730                        wasi_try_ok!(pipe.send(&memory, iovs_arr), env)
1731                    }
1732                    Kind::Dir { .. } | Kind::Root { .. } => {
1733                        // TODO: verify
1734                        return Ok(Errno::Isdir);
1735                    }
1736                    Kind::EventNotifications {
1737                        counter, wakers, ..
1738                    } => {
1739                        let mut val = 0u64.to_ne_bytes();
1740                        let written = wasi_try_ok!(write_bytes(&mut val[..], &memory, iovs_arr));
1741                        if written != val.len() {
1742                            return Ok(Errno::Inval);
1743                        }
1744                        let val = u64::from_ne_bytes(val);
1745
1746                        counter.fetch_add(val, Ordering::AcqRel);
1747                        {
1748                            let mut guard = wakers.lock().unwrap();
1749                            while let Some(wake) = guard.pop_back() {
1750                                if wake.send(()).is_ok() {
1751                                    break;
1752                                }
1753                            }
1754                        }
1755
1756                        written
1757                    }
1758                    Kind::Symlink { .. } => unimplemented!("Symlinks in wasi::fd_write"),
1759                    Kind::Buffer { buffer } => {
1760                        wasi_try_ok!(write_bytes(&mut buffer[offset..], &memory, iovs_arr), env)
1761                    }
1762                }
1763            };
1764
1765            // reborrow
1766            {
1767                let mut fd_map = state.fs.fd_map.write().unwrap();
1768                let fd_entry = wasi_try_ok!(fd_map.get_mut(&fd).ok_or(Errno::Badf));
1769                fd_entry.offset += bytes_written as u64;
1770            }
1771            wasi_try_ok!(state.fs.filestat_resync_size(inodes.deref(), fd), env);
1772
1773            bytes_written
1774        }
1775    };
1776
1777    let bytes_written: M::Offset =
1778        wasi_try_ok!(bytes_written.try_into().map_err(|_| Errno::Overflow));
1779    wasi_try_mem_ok!(nwritten_ref.write(bytes_written));
1780
1781    Ok(Errno::Success)
1782}
1783
1784/// ### `fd_pipe()`
1785/// Creates ta pipe that feeds data between two file handles
1786/// Output:
1787/// - `Fd`
1788///     First file handle that represents one end of the pipe
1789/// - `Fd`
1790///     Second file handle that represents the other end of the pipe
1791pub fn fd_pipe<M: MemorySize>(
1792    ctx: FunctionEnvMut<'_, WasiEnv>,
1793    ro_fd1: WasmPtr<WasiFd, M>,
1794    ro_fd2: WasmPtr<WasiFd, M>,
1795) -> Errno {
1796    trace!("wasi::fd_pipe");
1797
1798    let env = ctx.data();
1799    let (memory, state, mut inodes) = env.get_memory_and_wasi_state_and_inodes_mut(&ctx, 0);
1800
1801    let (pipe1, pipe2) = WasiPipe::new();
1802
1803    let inode1 = state.fs.create_inode_with_default_stat(
1804        inodes.deref_mut(),
1805        Kind::Pipe { pipe: pipe1 },
1806        false,
1807        "pipe".to_string(),
1808    );
1809    let inode2 = state.fs.create_inode_with_default_stat(
1810        inodes.deref_mut(),
1811        Kind::Pipe { pipe: pipe2 },
1812        false,
1813        "pipe".to_string(),
1814    );
1815
1816    let rights = Rights::all_socket();
1817    let fd1 = wasi_try!(state
1818        .fs
1819        .create_fd(rights, rights, Fdflags::empty(), 0, inode1));
1820    let fd2 = wasi_try!(state
1821        .fs
1822        .create_fd(rights, rights, Fdflags::empty(), 0, inode2));
1823
1824    wasi_try_mem!(ro_fd1.write(&memory, fd1));
1825    wasi_try_mem!(ro_fd2.write(&memory, fd2));
1826
1827    Errno::Success
1828}
1829
1830/// ### `path_create_directory()`
1831/// Create directory at a path
1832/// Inputs:
1833/// - `Fd fd`
1834///     The directory that the path is relative to
1835/// - `const char *path`
1836///     String containing path data
1837/// - `u32 path_len`
1838///     The length of `path`
1839/// Errors:
1840/// Required Rights:
1841/// - Rights::PATH_CREATE_DIRECTORY
1842///     This right must be set on the directory that the file is created in (TODO: verify that this is true)
1843pub fn path_create_directory<M: MemorySize>(
1844    ctx: FunctionEnvMut<'_, WasiEnv>,
1845    fd: WasiFd,
1846    path: WasmPtr<u8, M>,
1847    path_len: M::Offset,
1848) -> Errno {
1849    debug!("wasi::path_create_directory");
1850    let env = ctx.data();
1851    let (memory, state, mut inodes) = env.get_memory_and_wasi_state_and_inodes_mut(&ctx, 0);
1852
1853    let working_dir = wasi_try!(state.fs.get_fd(fd));
1854    {
1855        let guard = inodes.arena[working_dir.inode].read();
1856        if let Kind::Root { .. } = guard.deref() {
1857            return Errno::Access;
1858        }
1859    }
1860    if !working_dir.rights.contains(Rights::PATH_CREATE_DIRECTORY) {
1861        return Errno::Access;
1862    }
1863    let path_string = unsafe { get_input_str!(&memory, path, path_len) };
1864    debug!("=> fd: {}, path: {}", fd, &path_string);
1865
1866    let path = std::path::PathBuf::from(&path_string);
1867    let path_vec = wasi_try!(path
1868        .components()
1869        .map(|comp| {
1870            comp.as_os_str()
1871                .to_str()
1872                .map(|inner_str| inner_str.to_string())
1873                .ok_or(Errno::Inval)
1874        })
1875        .collect::<Result<Vec<String>, Errno>>());
1876    if path_vec.is_empty() {
1877        return Errno::Inval;
1878    }
1879
1880    debug!("Looking at components {:?}", &path_vec);
1881
1882    let mut cur_dir_inode = working_dir.inode;
1883    for comp in &path_vec {
1884        debug!("Creating dir {}", comp);
1885        let mut guard = inodes.arena[cur_dir_inode].write();
1886        let deref_mut = guard.deref_mut();
1887        match deref_mut {
1888            Kind::Dir {
1889                ref mut entries,
1890                path,
1891                parent,
1892            } => {
1893                match comp.borrow() {
1894                    ".." => {
1895                        if let Some(p) = parent {
1896                            cur_dir_inode = *p;
1897                            continue;
1898                        }
1899                    }
1900                    "." => continue,
1901                    _ => (),
1902                }
1903                if let Some(child) = entries.get(comp) {
1904                    cur_dir_inode = *child;
1905                } else {
1906                    let mut adjusted_path = path.clone();
1907                    drop(guard);
1908
1909                    // TODO: double check this doesn't risk breaking the sandbox
1910                    adjusted_path.push(comp);
1911                    if let Ok(adjusted_path_stat) = path_filestat_get_internal(
1912                        &memory,
1913                        state,
1914                        inodes.deref_mut(),
1915                        fd,
1916                        0,
1917                        &adjusted_path.to_string_lossy(),
1918                    ) {
1919                        if adjusted_path_stat.st_filetype != Filetype::Directory {
1920                            return Errno::Notdir;
1921                        }
1922                    } else {
1923                        wasi_try!(state.fs_create_dir(&adjusted_path));
1924                    }
1925                    let kind = Kind::Dir {
1926                        parent: Some(cur_dir_inode),
1927                        path: adjusted_path,
1928                        entries: Default::default(),
1929                    };
1930                    let new_inode = wasi_try!(state.fs.create_inode(
1931                        inodes.deref_mut(),
1932                        kind,
1933                        false,
1934                        comp.to_string()
1935                    ));
1936
1937                    // reborrow to insert
1938                    {
1939                        let mut guard = inodes.arena[cur_dir_inode].write();
1940                        if let Kind::Dir {
1941                            ref mut entries, ..
1942                        } = guard.deref_mut()
1943                        {
1944                            entries.insert(comp.to_string(), new_inode);
1945                        }
1946                    }
1947                    cur_dir_inode = new_inode;
1948                }
1949            }
1950            Kind::Root { .. } => return Errno::Access,
1951            _ => return Errno::Notdir,
1952        }
1953    }
1954
1955    Errno::Success
1956}
1957
1958/// ### `path_filestat_get()`
1959/// Access metadata about a file or directory
1960/// Inputs:
1961/// - `Fd fd`
1962///     The directory that `path` is relative to
1963/// - `LookupFlags flags`
1964///     Flags to control how `path` is understood
1965/// - `const char *path`
1966///     String containing the file path
1967/// - `u32 path_len`
1968///     The length of the `path` string
1969/// Output:
1970/// - `__wasi_file_stat_t *buf`
1971///     The location where the metadata will be stored
1972pub fn path_filestat_get<M: MemorySize>(
1973    ctx: FunctionEnvMut<'_, WasiEnv>,
1974    fd: WasiFd,
1975    flags: LookupFlags,
1976    path: WasmPtr<u8, M>,
1977    path_len: M::Offset,
1978    buf: WasmPtr<Filestat, M>,
1979) -> Errno {
1980    debug!("wasi::path_filestat_get (fd={})", fd);
1981    let env = ctx.data();
1982    let (memory, mut state, mut inodes) = env.get_memory_and_wasi_state_and_inodes_mut(&ctx, 0);
1983
1984    let path_string = unsafe { get_input_str!(&memory, path, path_len) };
1985
1986    let stat = wasi_try!(path_filestat_get_internal(
1987        &memory,
1988        state,
1989        inodes.deref_mut(),
1990        fd,
1991        flags,
1992        &path_string
1993    ));
1994
1995    wasi_try_mem!(buf.deref(&memory).write(stat));
1996
1997    Errno::Success
1998}
1999
2000/// ### `path_filestat_get()`
2001/// Access metadata about a file or directory
2002/// Inputs:
2003/// - `Fd fd`
2004///     The directory that `path` is relative to
2005/// - `LookupFlags flags`
2006///     Flags to control how `path` is understood
2007/// - `const char *path`
2008///     String containing the file path
2009/// - `u32 path_len`
2010///     The length of the `path` string
2011/// Output:
2012/// - `__wasi_file_stat_t *buf`
2013///     The location where the metadata will be stored
2014pub fn path_filestat_get_internal(
2015    memory: &MemoryView,
2016    state: &WasiState,
2017    inodes: &mut crate::WasiInodes,
2018    fd: WasiFd,
2019    flags: LookupFlags,
2020    path_string: &str,
2021) -> Result<Filestat, Errno> {
2022    let root_dir = state.fs.get_fd(fd)?;
2023
2024    if !root_dir.rights.contains(Rights::PATH_FILESTAT_GET) {
2025        return Err(Errno::Access);
2026    }
2027    debug!("=> base_fd: {}, path: {}", fd, path_string);
2028
2029    let file_inode = state.fs.get_inode_at_path(
2030        inodes,
2031        fd,
2032        path_string,
2033        flags & __WASI_LOOKUP_SYMLINK_FOLLOW != 0,
2034    )?;
2035    if inodes.arena[file_inode].is_preopened {
2036        Ok(*inodes.arena[file_inode].stat.read().unwrap().deref())
2037    } else {
2038        let guard = inodes.arena[file_inode].read();
2039        state.fs.get_stat_for_kind(inodes.deref(), guard.deref())
2040    }
2041}
2042
2043/// ### `path_filestat_set_times()`
2044/// Update time metadata on a file or directory
2045/// Inputs:
2046/// - `Fd fd`
2047///     The directory relative to which the path is resolved
2048/// - `LookupFlags flags`
2049///     Flags to control how the path is understood
2050/// - `const char *path`
2051///     String containing the file path
2052/// - `u32 path_len`
2053///     The length of the `path` string
2054/// - `Timestamp st_atim`
2055///     The timestamp that the last accessed time attribute is set to
2056/// -  `Timestamp st_mtim`
2057///     The timestamp that the last modified time attribute is set to
2058/// - `Fstflags fst_flags`
2059///     A bitmask controlling which attributes are set
2060pub fn path_filestat_set_times<M: MemorySize>(
2061    ctx: FunctionEnvMut<'_, WasiEnv>,
2062    fd: WasiFd,
2063    flags: LookupFlags,
2064    path: WasmPtr<u8, M>,
2065    path_len: M::Offset,
2066    st_atim: Timestamp,
2067    st_mtim: Timestamp,
2068    fst_flags: Fstflags,
2069) -> Errno {
2070    debug!("wasi::path_filestat_set_times");
2071    let env = ctx.data();
2072    let (memory, mut state, mut inodes) = env.get_memory_and_wasi_state_and_inodes_mut(&ctx, 0);
2073    let fd_entry = wasi_try!(state.fs.get_fd(fd));
2074    let fd_inode = fd_entry.inode;
2075    if !fd_entry.rights.contains(Rights::PATH_FILESTAT_SET_TIMES) {
2076        return Errno::Access;
2077    }
2078    if (fst_flags.contains(Fstflags::SET_ATIM) && fst_flags.contains(Fstflags::SET_ATIM_NOW))
2079        || (fst_flags.contains(Fstflags::SET_MTIM) && fst_flags.contains(Fstflags::SET_MTIM_NOW))
2080    {
2081        return Errno::Inval;
2082    }
2083
2084    let path_string = unsafe { get_input_str!(&memory, path, path_len) };
2085    debug!("=> base_fd: {}, path: {}", fd, &path_string);
2086
2087    let file_inode = wasi_try!(state.fs.get_inode_at_path(
2088        inodes.deref_mut(),
2089        fd,
2090        &path_string,
2091        flags & __WASI_LOOKUP_SYMLINK_FOLLOW != 0,
2092    ));
2093    let stat = {
2094        let guard = inodes.arena[file_inode].read();
2095        wasi_try!(state.fs.get_stat_for_kind(inodes.deref(), guard.deref()))
2096    };
2097
2098    let inode = &inodes.arena[fd_inode];
2099
2100    if fst_flags.contains(Fstflags::SET_ATIM) || fst_flags.contains(Fstflags::SET_ATIM_NOW) {
2101        let time_to_set = if fst_flags.contains(Fstflags::SET_ATIM) {
2102            st_atim
2103        } else {
2104            wasi_try!(get_current_time_in_nanos())
2105        };
2106        inode.stat.write().unwrap().st_atim = time_to_set;
2107    }
2108    if fst_flags.contains(Fstflags::SET_MTIM) || fst_flags.contains(Fstflags::SET_MTIM_NOW) {
2109        let time_to_set = if fst_flags.contains(Fstflags::SET_MTIM) {
2110            st_mtim
2111        } else {
2112            wasi_try!(get_current_time_in_nanos())
2113        };
2114        inode.stat.write().unwrap().st_mtim = time_to_set;
2115    }
2116
2117    Errno::Success
2118}
2119
2120/// ### `path_link()`
2121/// Create a hard link
2122/// Inputs:
2123/// - `Fd old_fd`
2124///     The directory relative to which the `old_path` is
2125/// - `LookupFlags old_flags`
2126///     Flags to control how `old_path` is understood
2127/// - `const char *old_path`
2128///     String containing the old file path
2129/// - `u32 old_path_len`
2130///     Length of the `old_path` string
2131/// - `Fd new_fd`
2132///     The directory relative to which the `new_path` is
2133/// - `const char *new_path`
2134///     String containing the new file path
2135/// - `u32 old_path_len`
2136///     Length of the `new_path` string
2137pub fn path_link<M: MemorySize>(
2138    ctx: FunctionEnvMut<'_, WasiEnv>,
2139    old_fd: WasiFd,
2140    old_flags: LookupFlags,
2141    old_path: WasmPtr<u8, M>,
2142    old_path_len: M::Offset,
2143    new_fd: WasiFd,
2144    new_path: WasmPtr<u8, M>,
2145    new_path_len: M::Offset,
2146) -> Errno {
2147    debug!("wasi::path_link");
2148    if old_flags & __WASI_LOOKUP_SYMLINK_FOLLOW != 0 {
2149        debug!("  - will follow symlinks when opening path");
2150    }
2151    let env = ctx.data();
2152    let (memory, mut state, mut inodes) = env.get_memory_and_wasi_state_and_inodes_mut(&ctx, 0);
2153    let old_path_str = unsafe { get_input_str!(&memory, old_path, old_path_len) };
2154    let new_path_str = unsafe { get_input_str!(&memory, new_path, new_path_len) };
2155    let source_fd = wasi_try!(state.fs.get_fd(old_fd));
2156    let target_fd = wasi_try!(state.fs.get_fd(new_fd));
2157    debug!(
2158        "=> source_fd: {}, source_path: {}, target_fd: {}, target_path: {}",
2159        old_fd, &old_path_str, new_fd, new_path_str
2160    );
2161
2162    if !source_fd.rights.contains(Rights::PATH_LINK_SOURCE)
2163        || !target_fd.rights.contains(Rights::PATH_LINK_TARGET)
2164    {
2165        return Errno::Access;
2166    }
2167
2168    let source_inode = wasi_try!(state.fs.get_inode_at_path(
2169        inodes.deref_mut(),
2170        old_fd,
2171        &old_path_str,
2172        old_flags & __WASI_LOOKUP_SYMLINK_FOLLOW != 0,
2173    ));
2174    let target_path_arg = std::path::PathBuf::from(&new_path_str);
2175    let (target_parent_inode, new_entry_name) = wasi_try!(state.fs.get_parent_inode_at_path(
2176        inodes.deref_mut(),
2177        new_fd,
2178        &target_path_arg,
2179        false
2180    ));
2181
2182    if inodes.arena[source_inode].stat.write().unwrap().st_nlink == Linkcount::max_value() {
2183        return Errno::Mlink;
2184    }
2185    {
2186        let mut guard = inodes.arena[target_parent_inode].write();
2187        let deref_mut = guard.deref_mut();
2188        match deref_mut {
2189            Kind::Dir { entries, .. } => {
2190                if entries.contains_key(&new_entry_name) {
2191                    return Errno::Exist;
2192                }
2193                entries.insert(new_entry_name, source_inode);
2194            }
2195            Kind::Root { .. } => return Errno::Inval,
2196            Kind::File { .. }
2197            | Kind::Symlink { .. }
2198            | Kind::Buffer { .. }
2199            | Kind::Socket { .. }
2200            | Kind::Pipe { .. }
2201            | Kind::EventNotifications { .. } => return Errno::Notdir,
2202        }
2203    }
2204    inodes.arena[source_inode].stat.write().unwrap().st_nlink += 1;
2205
2206    Errno::Success
2207}
2208
2209/// ### `path_open()`
2210/// Open file located at the given path
2211/// Inputs:
2212/// - `Fd dirfd`
2213///     The fd corresponding to the directory that the file is in
2214/// - `LookupFlags dirflags`
2215///     Flags specifying how the path will be resolved
2216/// - `char *path`
2217///     The path of the file or directory to open
2218/// - `u32 path_len`
2219///     The length of the `path` string
2220/// - `Oflags o_flags`
2221///     How the file will be opened
2222/// - `Rights fs_rights_base`
2223///     The rights of the created file descriptor
2224/// - `Rights fs_rightsinheriting`
2225///     The rights of file descriptors derived from the created file descriptor
2226/// - `Fdflags fs_flags`
2227///     The flags of the file descriptor
2228/// Output:
2229/// - `Fd* fd`
2230///     The new file descriptor
2231/// Possible Errors:
2232/// - `Errno::Access`, `Errno::Badf`, `Errno::Fault`, `Errno::Fbig?`, `Errno::Inval`, `Errno::Io`, `Errno::Loop`, `Errno::Mfile`, `Errno::Nametoolong?`, `Errno::Nfile`, `Errno::Noent`, `Errno::Notdir`, `Errno::Rofs`, and `Errno::Notcapable`
2233pub fn path_open<M: MemorySize>(
2234    ctx: FunctionEnvMut<'_, WasiEnv>,
2235    dirfd: WasiFd,
2236    dirflags: LookupFlags,
2237    path: WasmPtr<u8, M>,
2238    path_len: M::Offset,
2239    o_flags: Oflags,
2240    fs_rights_base: Rights,
2241    fs_rights_inheriting: Rights,
2242    fs_flags: Fdflags,
2243    fd: WasmPtr<WasiFd, M>,
2244) -> Errno {
2245    debug!("wasi::path_open");
2246    if dirflags & __WASI_LOOKUP_SYMLINK_FOLLOW != 0 {
2247        debug!("  - will follow symlinks when opening path");
2248    }
2249    let env = ctx.data();
2250    let (memory, mut state, mut inodes) = env.get_memory_and_wasi_state_and_inodes_mut(&ctx, 0);
2251    /* TODO: find actual upper bound on name size (also this is a path, not a name :think-fish:) */
2252    let path_len64: u64 = path_len.into();
2253    if path_len64 > 1024u64 * 1024u64 {
2254        return Errno::Nametoolong;
2255    }
2256
2257    let fd_ref = fd.deref(&memory);
2258
2259    // o_flags:
2260    // - __WASI_O_CREAT (create if it does not exist)
2261    // - __WASI_O_DIRECTORY (fail if not dir)
2262    // - __WASI_O_EXCL (fail if file exists)
2263    // - __WASI_O_TRUNC (truncate size to 0)
2264
2265    let working_dir = wasi_try!(state.fs.get_fd(dirfd));
2266    let working_dir_rights_inheriting = working_dir.rights_inheriting;
2267
2268    // ASSUMPTION: open rights apply recursively
2269    if !working_dir.rights.contains(Rights::PATH_OPEN) {
2270        return Errno::Access;
2271    }
2272
2273    let path_string = unsafe { get_input_str!(&memory, path, path_len) };
2274
2275    debug!("=> path_open(): fd: {}, path: {}", dirfd, &path_string);
2276
2277    let path_arg = std::path::PathBuf::from(&path_string);
2278    let maybe_inode = state.fs.get_inode_at_path(
2279        inodes.deref_mut(),
2280        dirfd,
2281        &path_string,
2282        dirflags & __WASI_LOOKUP_SYMLINK_FOLLOW != 0,
2283    );
2284
2285    let mut open_flags = 0;
2286    // TODO: traverse rights of dirs properly
2287    // COMMENTED OUT: WASI isn't giving appropriate rights here when opening
2288    //              TODO: look into this; file a bug report if this is a bug
2289    //
2290    // Maximum rights: should be the working dir rights
2291    // Minimum rights: whatever rights are provided
2292    let adjusted_rights = /*fs_rights_base &*/ working_dir_rights_inheriting;
2293    let mut open_options = state.fs_new_open_options();
2294
2295    let target_rights = match maybe_inode {
2296        Ok(_) => {
2297            let write_permission = adjusted_rights.contains(Rights::FD_WRITE);
2298
2299            // append, truncate, and create all require the permission to write
2300            let (append_permission, truncate_permission, create_permission) = if write_permission {
2301                (
2302                    fs_flags.contains(Fdflags::APPEND),
2303                    o_flags.contains(Oflags::TRUNC),
2304                    o_flags.contains(Oflags::CREATE),
2305                )
2306            } else {
2307                (false, false, false)
2308            };
2309
2310            wasmer_vfs::OpenOptionsConfig {
2311                read: fs_rights_base.contains(Rights::FD_READ),
2312                write: write_permission,
2313                create_new: create_permission && o_flags.contains(Oflags::EXCL),
2314                create: create_permission,
2315                append: append_permission,
2316                truncate: truncate_permission,
2317            }
2318        }
2319        Err(_) => wasmer_vfs::OpenOptionsConfig {
2320            append: fs_flags.contains(Fdflags::APPEND),
2321            write: fs_rights_base.contains(Rights::FD_WRITE),
2322            read: fs_rights_base.contains(Rights::FD_READ),
2323            create_new: o_flags.contains(Oflags::CREATE) && o_flags.contains(Oflags::EXCL),
2324            create: o_flags.contains(Oflags::CREATE),
2325            truncate: o_flags.contains(Oflags::TRUNC),
2326        },
2327    };
2328
2329    let parent_rights = wasmer_vfs::OpenOptionsConfig {
2330        read: working_dir.rights.contains(Rights::FD_READ),
2331        write: working_dir.rights.contains(Rights::FD_WRITE),
2332        // The parent is a directory, which is why these options
2333        // aren't inherited from the parent (append / truncate doesn't work on directories)
2334        create_new: true,
2335        create: true,
2336        append: true,
2337        truncate: true,
2338    };
2339
2340    let minimum_rights = target_rights.minimum_rights(&parent_rights);
2341
2342    open_options.options(minimum_rights.clone());
2343
2344    let inode = if let Ok(inode) = maybe_inode {
2345        // Happy path, we found the file we're trying to open
2346        let mut guard = inodes.arena[inode].write();
2347        let deref_mut = guard.deref_mut();
2348        match deref_mut {
2349            Kind::File {
2350                ref mut handle,
2351                path,
2352                fd,
2353            } => {
2354                if let Some(special_fd) = fd {
2355                    // short circuit if we're dealing with a special file
2356                    assert!(handle.is_some());
2357                    wasi_try_mem!(fd_ref.write(*special_fd));
2358                    return Errno::Success;
2359                }
2360                if o_flags.contains(Oflags::DIRECTORY) {
2361                    return Errno::Notdir;
2362                }
2363                if o_flags.contains(Oflags::EXCL) {
2364                    return Errno::Exist;
2365                }
2366
2367                let open_options = open_options
2368                    .write(minimum_rights.write)
2369                    .create(minimum_rights.create)
2370                    .append(minimum_rights.append)
2371                    .truncate(minimum_rights.truncate);
2372
2373                if minimum_rights.read {
2374                    open_flags |= Fd::READ;
2375                }
2376                if minimum_rights.write {
2377                    open_flags |= Fd::WRITE;
2378                }
2379                if minimum_rights.create {
2380                    open_flags |= Fd::CREATE;
2381                }
2382                if minimum_rights.truncate {
2383                    open_flags |= Fd::TRUNCATE;
2384                }
2385
2386                *handle = Some(wasi_try!(open_options
2387                    .open(&path)
2388                    .map_err(fs_error_into_wasi_err)));
2389            }
2390            Kind::Buffer { .. } => unimplemented!("wasi::path_open for Buffer type files"),
2391            Kind::Root { .. } => {
2392                if !o_flags.contains(Oflags::DIRECTORY) {
2393                    return Errno::Notcapable;
2394                }
2395            }
2396            Kind::Dir { .. }
2397            | Kind::Socket { .. }
2398            | Kind::Pipe { .. }
2399            | Kind::EventNotifications { .. } => {}
2400            Kind::Symlink {
2401                base_po_dir,
2402                path_to_symlink,
2403                relative_path,
2404            } => {
2405                // I think this should return an error (because symlinks should be resolved away by the path traversal)
2406                // TODO: investigate this
2407                unimplemented!("SYMLINKS IN PATH_OPEN");
2408            }
2409        }
2410        inode
2411    } else {
2412        // less-happy path, we have to try to create the file
2413        debug!("Maybe creating file");
2414        if o_flags.contains(Oflags::CREATE) {
2415            if o_flags.contains(Oflags::DIRECTORY) {
2416                return Errno::Notdir;
2417            }
2418            debug!("Creating file");
2419            // strip end file name
2420
2421            let (parent_inode, new_entity_name) = wasi_try!(state.fs.get_parent_inode_at_path(
2422                inodes.deref_mut(),
2423                dirfd,
2424                &path_arg,
2425                dirflags & __WASI_LOOKUP_SYMLINK_FOLLOW != 0
2426            ));
2427            let new_file_host_path = {
2428                let guard = inodes.arena[parent_inode].read();
2429                let deref = guard.deref();
2430                match deref {
2431                    Kind::Dir { path, .. } => {
2432                        let mut new_path = path.clone();
2433                        new_path.push(&new_entity_name);
2434                        new_path
2435                    }
2436                    Kind::Root { .. } => {
2437                        let mut new_path = std::path::PathBuf::new();
2438                        new_path.push(&new_entity_name);
2439                        new_path
2440                    }
2441                    _ => return Errno::Inval,
2442                }
2443            };
2444            // once we got the data we need from the parent, we lookup the host file
2445            // todo: extra check that opening with write access is okay
2446            let handle = {
2447                let open_options = open_options
2448                    .read(minimum_rights.read)
2449                    .append(minimum_rights.append)
2450                    .write(minimum_rights.write)
2451                    .create_new(minimum_rights.create_new);
2452
2453                if minimum_rights.read {
2454                    open_flags |= Fd::READ;
2455                }
2456                if minimum_rights.write {
2457                    open_flags |= Fd::WRITE;
2458                }
2459                if minimum_rights.create_new {
2460                    open_flags |= Fd::CREATE;
2461                }
2462                if minimum_rights.truncate {
2463                    open_flags |= Fd::TRUNCATE;
2464                }
2465
2466                Some(wasi_try!(open_options.open(&new_file_host_path).map_err(
2467                    |e| {
2468                        debug!("Error opening file {}", e);
2469                        fs_error_into_wasi_err(e)
2470                    }
2471                )))
2472            };
2473
2474            let new_inode = {
2475                let kind = Kind::File {
2476                    handle,
2477                    path: new_file_host_path,
2478                    fd: None,
2479                };
2480                wasi_try!(state.fs.create_inode(
2481                    inodes.deref_mut(),
2482                    kind,
2483                    false,
2484                    new_entity_name.clone()
2485                ))
2486            };
2487
2488            {
2489                let mut guard = inodes.arena[parent_inode].write();
2490                if let Kind::Dir {
2491                    ref mut entries, ..
2492                } = guard.deref_mut()
2493                {
2494                    entries.insert(new_entity_name, new_inode);
2495                }
2496            }
2497
2498            new_inode
2499        } else {
2500            return maybe_inode.unwrap_err();
2501        }
2502    };
2503
2504    {
2505        debug!("inode {:?} value {:#?} found!", inode, inodes.arena[inode]);
2506    }
2507
2508    // TODO: check and reduce these
2509    // TODO: ensure a mutable fd to root can never be opened
2510    let out_fd = wasi_try!(state.fs.create_fd(
2511        adjusted_rights,
2512        fs_rights_inheriting,
2513        fs_flags,
2514        open_flags,
2515        inode
2516    ));
2517
2518    wasi_try_mem!(fd_ref.write(out_fd));
2519    debug!("wasi::path_open returning fd {}", out_fd);
2520
2521    Errno::Success
2522}
2523
2524/// ### `path_readlink()`
2525/// Read the value of a symlink
2526/// Inputs:
2527/// - `Fd dir_fd`
2528///     The base directory from which `path` is understood
2529/// - `const char *path`
2530///     Pointer to UTF-8 bytes that make up the path to the symlink
2531/// - `u32 path_len`
2532///     The number of bytes to read from `path`
2533/// - `u32 buf_len`
2534///     Space available pointed to by `buf`
2535/// Outputs:
2536/// - `char *buf`
2537///     Pointer to characters containing the path that the symlink points to
2538/// - `u32 buf_used`
2539///     The number of bytes written to `buf`
2540pub fn path_readlink<M: MemorySize>(
2541    ctx: FunctionEnvMut<'_, WasiEnv>,
2542    dir_fd: WasiFd,
2543    path: WasmPtr<u8, M>,
2544    path_len: M::Offset,
2545    buf: WasmPtr<u8, M>,
2546    buf_len: M::Offset,
2547    buf_used: WasmPtr<M::Offset, M>,
2548) -> Errno {
2549    debug!("wasi::path_readlink");
2550    let env = ctx.data();
2551    let (memory, mut state, mut inodes) = env.get_memory_and_wasi_state_and_inodes_mut(&ctx, 0);
2552
2553    let base_dir = wasi_try!(state.fs.get_fd(dir_fd));
2554    if !base_dir.rights.contains(Rights::PATH_READLINK) {
2555        return Errno::Access;
2556    }
2557    let path_str = unsafe { get_input_str!(&memory, path, path_len) };
2558    let inode = wasi_try!(state
2559        .fs
2560        .get_inode_at_path(inodes.deref_mut(), dir_fd, &path_str, false));
2561
2562    {
2563        let guard = inodes.arena[inode].read();
2564        if let Kind::Symlink { relative_path, .. } = guard.deref() {
2565            let rel_path_str = relative_path.to_string_lossy();
2566            debug!("Result => {:?}", rel_path_str);
2567            let buf_len: u64 = buf_len.into();
2568            let bytes = rel_path_str.bytes();
2569            if bytes.len() as u64 >= buf_len {
2570                return Errno::Overflow;
2571            }
2572            let bytes: Vec<_> = bytes.collect();
2573
2574            let out = wasi_try_mem!(buf.slice(&memory, wasi_try!(to_offset::<M>(bytes.len()))));
2575            wasi_try_mem!(out.write_slice(&bytes));
2576            // should we null terminate this?
2577
2578            let bytes_len: M::Offset =
2579                wasi_try!(bytes.len().try_into().map_err(|_| Errno::Overflow));
2580            wasi_try_mem!(buf_used.deref(&memory).write(bytes_len));
2581        } else {
2582            return Errno::Inval;
2583        }
2584    }
2585
2586    Errno::Success
2587}
2588
2589/// Returns Errno::Notemtpy if directory is not empty
2590pub fn path_remove_directory<M: MemorySize>(
2591    ctx: FunctionEnvMut<'_, WasiEnv>,
2592    fd: WasiFd,
2593    path: WasmPtr<u8, M>,
2594    path_len: M::Offset,
2595) -> Errno {
2596    // TODO check if fd is a dir, ensure it's within sandbox, etc.
2597    debug!("wasi::path_remove_directory");
2598    let env = ctx.data();
2599    let (memory, mut state, mut inodes) = env.get_memory_and_wasi_state_and_inodes_mut(&ctx, 0);
2600
2601    let base_dir = wasi_try!(state.fs.get_fd(fd));
2602    let path_str = unsafe { get_input_str!(&memory, path, path_len) };
2603
2604    let inode = wasi_try!(state
2605        .fs
2606        .get_inode_at_path(inodes.deref_mut(), fd, &path_str, false));
2607    let (parent_inode, childs_name) = wasi_try!(state.fs.get_parent_inode_at_path(
2608        inodes.deref_mut(),
2609        fd,
2610        std::path::Path::new(&path_str),
2611        false
2612    ));
2613
2614    let host_path_to_remove = {
2615        let guard = inodes.arena[inode].read();
2616        let deref = guard.deref();
2617        match deref {
2618            Kind::Dir { entries, path, .. } => {
2619                if !entries.is_empty() || wasi_try!(state.fs_read_dir(path)).count() != 0 {
2620                    return Errno::Notempty;
2621                }
2622                path.clone()
2623            }
2624            Kind::Root { .. } => return Errno::Access,
2625            _ => return Errno::Notdir,
2626        }
2627    };
2628
2629    {
2630        let mut guard = inodes.arena[parent_inode].write();
2631        let deref_mut = guard.deref_mut();
2632        match deref_mut {
2633            Kind::Dir {
2634                ref mut entries, ..
2635            } => {
2636                let removed_inode = wasi_try!(entries.remove(&childs_name).ok_or(Errno::Inval));
2637                // TODO: make this a debug assert in the future
2638                assert!(inode == removed_inode);
2639            }
2640            Kind::Root { .. } => return Errno::Access,
2641            _ => unreachable!(
2642                "Internal logic error in wasi::path_remove_directory, parent is not a directory"
2643            ),
2644        }
2645    }
2646
2647    if let Err(err) = state.fs_remove_dir(host_path_to_remove) {
2648        // reinsert to prevent FS from being in bad state
2649        let mut guard = inodes.arena[parent_inode].write();
2650        if let Kind::Dir {
2651            ref mut entries, ..
2652        } = guard.deref_mut()
2653        {
2654            entries.insert(childs_name, inode);
2655        }
2656        return err;
2657    }
2658
2659    Errno::Success
2660}
2661
2662/// ### `path_rename()`
2663/// Rename a file or directory
2664/// Inputs:
2665/// - `Fd old_fd`
2666///     The base directory for `old_path`
2667/// - `const char* old_path`
2668///     Pointer to UTF8 bytes, the file to be renamed
2669/// - `u32 old_path_len`
2670///     The number of bytes to read from `old_path`
2671/// - `Fd new_fd`
2672///     The base directory for `new_path`
2673/// - `const char* new_path`
2674///     Pointer to UTF8 bytes, the new file name
2675/// - `u32 new_path_len`
2676///     The number of bytes to read from `new_path`
2677pub fn path_rename<M: MemorySize>(
2678    ctx: FunctionEnvMut<'_, WasiEnv>,
2679    old_fd: WasiFd,
2680    old_path: WasmPtr<u8, M>,
2681    old_path_len: M::Offset,
2682    new_fd: WasiFd,
2683    new_path: WasmPtr<u8, M>,
2684    new_path_len: M::Offset,
2685) -> Errno {
2686    debug!(
2687        "wasi::path_rename: old_fd = {}, new_fd = {}",
2688        old_fd, new_fd
2689    );
2690    let env = ctx.data();
2691    let (memory, mut state, mut inodes) = env.get_memory_and_wasi_state_and_inodes_mut(&ctx, 0);
2692    let source_str = unsafe { get_input_str!(&memory, old_path, old_path_len) };
2693    let source_path = std::path::Path::new(&source_str);
2694    let target_str = unsafe { get_input_str!(&memory, new_path, new_path_len) };
2695    let target_path = std::path::Path::new(&target_str);
2696    debug!("=> rename from {} to {}", &source_str, &target_str);
2697
2698    {
2699        let source_fd = wasi_try!(state.fs.get_fd(old_fd));
2700        if !source_fd.rights.contains(Rights::PATH_RENAME_SOURCE) {
2701            return Errno::Access;
2702        }
2703        let target_fd = wasi_try!(state.fs.get_fd(new_fd));
2704        if !target_fd.rights.contains(Rights::PATH_RENAME_TARGET) {
2705            return Errno::Access;
2706        }
2707    }
2708
2709    // this is to be sure the source file is fetch from filesystem if needed
2710    wasi_try!(state.fs.get_inode_at_path(
2711        inodes.deref_mut(),
2712        old_fd,
2713        source_path.to_str().as_ref().unwrap(),
2714        true
2715    ));
2716    // Create the destination inode if the file exists.
2717    let _ = state.fs.get_inode_at_path(
2718        inodes.deref_mut(),
2719        new_fd,
2720        target_path.to_str().as_ref().unwrap(),
2721        true,
2722    );
2723    let (source_parent_inode, source_entry_name) =
2724        wasi_try!(state
2725            .fs
2726            .get_parent_inode_at_path(inodes.deref_mut(), old_fd, source_path, true));
2727    let (target_parent_inode, target_entry_name) =
2728        wasi_try!(state
2729            .fs
2730            .get_parent_inode_at_path(inodes.deref_mut(), new_fd, target_path, true));
2731    let mut need_create = true;
2732    let host_adjusted_target_path = {
2733        let guard = inodes.arena[target_parent_inode].read();
2734        let deref = guard.deref();
2735        match deref {
2736            Kind::Dir { entries, path, .. } => {
2737                if entries.contains_key(&target_entry_name) {
2738                    need_create = false;
2739                }
2740                let mut out_path = path.clone();
2741                out_path.push(std::path::Path::new(&target_entry_name));
2742                out_path
2743            }
2744            Kind::Root { .. } => return Errno::Notcapable,
2745            Kind::Socket { .. } | Kind::Pipe { .. } | Kind::EventNotifications { .. } => {
2746                return Errno::Inval
2747            }
2748            Kind::Symlink { .. } | Kind::File { .. } | Kind::Buffer { .. } => {
2749                unreachable!("Fatal internal logic error: parent of inode is not a directory")
2750            }
2751        }
2752    };
2753
2754    let source_entry = {
2755        let mut guard = inodes.arena[source_parent_inode].write();
2756        let deref_mut = guard.deref_mut();
2757        match deref_mut {
2758            Kind::Dir { entries, .. } => {
2759                wasi_try!(entries.remove(&source_entry_name).ok_or(Errno::Noent))
2760            }
2761            Kind::Root { .. } => return Errno::Notcapable,
2762            Kind::Socket { .. } | Kind::Pipe { .. } | Kind::EventNotifications { .. } => {
2763                return Errno::Inval
2764            }
2765            Kind::Symlink { .. } | Kind::File { .. } | Kind::Buffer { .. } => {
2766                unreachable!("Fatal internal logic error: parent of inode is not a directory")
2767            }
2768        }
2769    };
2770
2771    {
2772        let mut guard = inodes.arena[source_entry].write();
2773        let deref_mut = guard.deref_mut();
2774        match deref_mut {
2775            Kind::File {
2776                handle, ref path, ..
2777            } => {
2778                // TODO: investigate why handle is not always there, it probably should be.
2779                // My best guess is the fact that a handle means currently open and a path
2780                // just means reference to host file on disk. But ideally those concepts
2781                // could just be unified even if there's a `Box<dyn VirtualFile>` which just
2782                // implements the logic of "I'm not actually a file, I'll try to be as needed".
2783                let result = if let Some(h) = handle {
2784                    drop(guard);
2785                    state.fs_rename(&source_path, &host_adjusted_target_path)
2786                } else {
2787                    let path_clone = path.clone();
2788                    drop(guard);
2789                    let out = state.fs_rename(&path_clone, &host_adjusted_target_path);
2790                    {
2791                        let mut guard = inodes.arena[source_entry].write();
2792                        if let Kind::File { ref mut path, .. } = guard.deref_mut() {
2793                            *path = host_adjusted_target_path;
2794                        } else {
2795                            unreachable!()
2796                        }
2797                    }
2798                    out
2799                };
2800                // if the above operation failed we have to revert the previous change and then fail
2801                if let Err(e) = result {
2802                    let mut guard = inodes.arena[source_parent_inode].write();
2803                    if let Kind::Dir { entries, .. } = guard.deref_mut() {
2804                        entries.insert(source_entry_name, source_entry);
2805                        return e;
2806                    }
2807                }
2808            }
2809            Kind::Dir { ref path, .. } => {
2810                let cloned_path = path.clone();
2811                if let Err(e) = state.fs_rename(cloned_path, &host_adjusted_target_path) {
2812                    return e;
2813                }
2814                {
2815                    drop(guard);
2816                    let mut guard = inodes.arena[source_entry].write();
2817                    if let Kind::Dir { path, .. } = guard.deref_mut() {
2818                        *path = host_adjusted_target_path;
2819                    }
2820                }
2821            }
2822            Kind::Buffer { .. } => {}
2823            Kind::Symlink { .. } => {}
2824            Kind::Socket { .. } => {}
2825            Kind::Pipe { .. } => {}
2826            Kind::EventNotifications { .. } => {}
2827            Kind::Root { .. } => unreachable!("The root can not be moved"),
2828        }
2829    }
2830
2831    if need_create {
2832        let mut guard = inodes.arena[target_parent_inode].write();
2833        if let Kind::Dir { entries, .. } = guard.deref_mut() {
2834            let result = entries.insert(target_entry_name, source_entry);
2835            assert!(
2836                result.is_none(),
2837                "Fatal error: race condition on filesystem detected or internal logic error"
2838            );
2839        }
2840    }
2841
2842    Errno::Success
2843}
2844
2845/// ### `path_symlink()`
2846/// Create a symlink
2847/// Inputs:
2848/// - `const char *old_path`
2849///     Array of UTF-8 bytes representing the source path
2850/// - `u32 old_path_len`
2851///     The number of bytes to read from `old_path`
2852/// - `Fd fd`
2853///     The base directory from which the paths are understood
2854/// - `const char *new_path`
2855///     Array of UTF-8 bytes representing the target path
2856/// - `u32 new_path_len`
2857///     The number of bytes to read from `new_path`
2858pub fn path_symlink<M: MemorySize>(
2859    ctx: FunctionEnvMut<'_, WasiEnv>,
2860    old_path: WasmPtr<u8, M>,
2861    old_path_len: M::Offset,
2862    fd: WasiFd,
2863    new_path: WasmPtr<u8, M>,
2864    new_path_len: M::Offset,
2865) -> Errno {
2866    debug!("wasi::path_symlink");
2867    let env = ctx.data();
2868    let (memory, mut state, mut inodes) = env.get_memory_and_wasi_state_and_inodes_mut(&ctx, 0);
2869    let old_path_str = unsafe { get_input_str!(&memory, old_path, old_path_len) };
2870    let new_path_str = unsafe { get_input_str!(&memory, new_path, new_path_len) };
2871    let base_fd = wasi_try!(state.fs.get_fd(fd));
2872    if !base_fd.rights.contains(Rights::PATH_SYMLINK) {
2873        return Errno::Access;
2874    }
2875
2876    // get the depth of the parent + 1 (UNDER INVESTIGATION HMMMMMMMM THINK FISH ^ THINK FISH)
2877    let old_path_path = std::path::Path::new(&old_path_str);
2878    let (source_inode, _) =
2879        wasi_try!(state
2880            .fs
2881            .get_parent_inode_at_path(inodes.deref_mut(), fd, old_path_path, true));
2882    let depth = state
2883        .fs
2884        .path_depth_from_fd(inodes.deref(), fd, source_inode);
2885
2886    // depth == -1 means folder is not relative. See issue #3233.
2887    let depth = match depth {
2888        Ok(depth) => depth as i32 - 1,
2889        Err(_) => -1,
2890    };
2891
2892    let new_path_path = std::path::Path::new(&new_path_str);
2893    let (target_parent_inode, entry_name) =
2894        wasi_try!(state
2895            .fs
2896            .get_parent_inode_at_path(inodes.deref_mut(), fd, new_path_path, true));
2897
2898    // short circuit if anything is wrong, before we create an inode
2899    {
2900        let guard = inodes.arena[target_parent_inode].read();
2901        let deref = guard.deref();
2902        match deref {
2903            Kind::Dir { entries, .. } => {
2904                if entries.contains_key(&entry_name) {
2905                    return Errno::Exist;
2906                }
2907            }
2908            Kind::Root { .. } => return Errno::Notcapable,
2909            Kind::Socket { .. } | Kind::Pipe { .. } | Kind::EventNotifications { .. } => {
2910                return Errno::Inval
2911            }
2912            Kind::File { .. } | Kind::Symlink { .. } | Kind::Buffer { .. } => {
2913                unreachable!("get_parent_inode_at_path returned something other than a Dir or Root")
2914            }
2915        }
2916    }
2917
2918    let mut source_path = std::path::Path::new(&old_path_str);
2919    let mut relative_path = std::path::PathBuf::new();
2920    for _ in 0..depth {
2921        relative_path.push("..");
2922    }
2923    relative_path.push(source_path);
2924    debug!(
2925        "Symlinking {} to {}",
2926        new_path_str,
2927        relative_path.to_string_lossy()
2928    );
2929
2930    let kind = Kind::Symlink {
2931        base_po_dir: fd,
2932        path_to_symlink: std::path::PathBuf::from(new_path_str),
2933        relative_path,
2934    };
2935    let new_inode = state.fs.create_inode_with_default_stat(
2936        inodes.deref_mut(),
2937        kind,
2938        false,
2939        entry_name.clone(),
2940    );
2941
2942    {
2943        let mut guard = inodes.arena[target_parent_inode].write();
2944        if let Kind::Dir {
2945            ref mut entries, ..
2946        } = guard.deref_mut()
2947        {
2948            entries.insert(entry_name, new_inode);
2949        }
2950    }
2951
2952    Errno::Success
2953}
2954
2955/// ### `path_unlink_file()`
2956/// Unlink a file, deleting if the number of hardlinks is 1
2957/// Inputs:
2958/// - `Fd fd`
2959///     The base file descriptor from which the path is understood
2960/// - `const char *path`
2961///     Array of UTF-8 bytes representing the path
2962/// - `u32 path_len`
2963///     The number of bytes in the `path` array
2964pub fn path_unlink_file<M: MemorySize>(
2965    ctx: FunctionEnvMut<'_, WasiEnv>,
2966    fd: WasiFd,
2967    path: WasmPtr<u8, M>,
2968    path_len: M::Offset,
2969) -> Errno {
2970    debug!("wasi::path_unlink_file");
2971    let env = ctx.data();
2972    let (memory, mut state, mut inodes) = env.get_memory_and_wasi_state_and_inodes_mut(&ctx, 0);
2973
2974    let base_dir = wasi_try!(state.fs.get_fd(fd));
2975    if !base_dir.rights.contains(Rights::PATH_UNLINK_FILE) {
2976        return Errno::Access;
2977    }
2978    let path_str = unsafe { get_input_str!(&memory, path, path_len) };
2979    debug!("Requested file: {}", path_str);
2980
2981    let inode = wasi_try!(state
2982        .fs
2983        .get_inode_at_path(inodes.deref_mut(), fd, &path_str, false));
2984    let (parent_inode, childs_name) = wasi_try!(state.fs.get_parent_inode_at_path(
2985        inodes.deref_mut(),
2986        fd,
2987        std::path::Path::new(&path_str),
2988        false
2989    ));
2990
2991    let removed_inode = {
2992        let mut guard = inodes.arena[parent_inode].write();
2993        let deref_mut = guard.deref_mut();
2994        match deref_mut {
2995            Kind::Dir {
2996                ref mut entries, ..
2997            } => {
2998                let removed_inode = wasi_try!(entries.remove(&childs_name).ok_or(Errno::Inval));
2999                // TODO: make this a debug assert in the future
3000                assert!(inode == removed_inode);
3001                debug_assert!(inodes.arena[inode].stat.read().unwrap().st_nlink > 0);
3002                removed_inode
3003            }
3004            Kind::Root { .. } => return Errno::Access,
3005            _ => unreachable!(
3006                "Internal logic error in wasi::path_unlink_file, parent is not a directory"
3007            ),
3008        }
3009    };
3010
3011    let st_nlink = {
3012        let mut guard = inodes.arena[removed_inode].stat.write().unwrap();
3013        guard.st_nlink -= 1;
3014        guard.st_nlink
3015    };
3016    if st_nlink == 0 {
3017        {
3018            let mut guard = inodes.arena[removed_inode].write();
3019            let deref_mut = guard.deref_mut();
3020            match deref_mut {
3021                Kind::File { handle, path, .. } => {
3022                    if let Some(h) = handle {
3023                        wasi_try!(h.unlink().map_err(fs_error_into_wasi_err));
3024                    } else {
3025                        // File is closed
3026                        // problem with the abstraction, we can't call unlink because there's no handle
3027                        // drop mutable borrow on `path`
3028                        let path = path.clone();
3029                        wasi_try!(state.fs_remove_file(path));
3030                    }
3031                }
3032                Kind::Dir { .. } | Kind::Root { .. } => return Errno::Isdir,
3033                Kind::Symlink { .. } => {
3034                    // TODO: actually delete real symlinks and do nothing for virtual symlinks
3035                }
3036                _ => unimplemented!("wasi::path_unlink_file for Buffer"),
3037            }
3038        }
3039        // TODO: test this on Windows and actually make it portable
3040        // make the file an orphan fd if the fd is still open
3041        let fd_is_orphaned = {
3042            let guard = inodes.arena[removed_inode].read();
3043            if let Kind::File { handle, .. } = guard.deref() {
3044                handle.is_some()
3045            } else {
3046                false
3047            }
3048        };
3049        let removed_inode_val = unsafe { state.fs.remove_inode(inodes.deref_mut(), removed_inode) };
3050        assert!(
3051            removed_inode_val.is_some(),
3052            "Inode could not be removed because it doesn't exist"
3053        );
3054
3055        if fd_is_orphaned {
3056            inodes
3057                .orphan_fds
3058                .insert(removed_inode, removed_inode_val.unwrap());
3059        }
3060    }
3061
3062    Errno::Success
3063}
3064
3065/// ### `poll_oneoff()`
3066/// Concurrently poll for a set of events
3067/// Inputs:
3068/// - `const __wasi_subscription_t *in`
3069///     The events to subscribe to
3070/// - `Event *out`
3071///     The events that have occured
3072/// - `u32 nsubscriptions`
3073///     The number of subscriptions and the number of events
3074/// Output:
3075/// - `u32 nevents`
3076///     The number of events seen
3077pub fn poll_oneoff<M: MemorySize>(
3078    ctx: FunctionEnvMut<'_, WasiEnv>,
3079    in_: WasmPtr<Subscription, M>,
3080    out_: WasmPtr<Event, M>,
3081    nsubscriptions: M::Offset,
3082    nevents: WasmPtr<M::Offset, M>,
3083) -> Result<Errno, WasiError> {
3084    trace!("wasi::poll_oneoff");
3085    trace!("  => nsubscriptions = {}", nsubscriptions);
3086    let env = ctx.data();
3087    let (memory, mut state, inodes) = env.get_memory_and_wasi_state_and_inodes(&ctx, 0);
3088
3089    let subscription_array = wasi_try_mem_ok!(in_.slice(&memory, nsubscriptions));
3090    let event_array = wasi_try_mem_ok!(out_.slice(&memory, nsubscriptions));
3091    let mut events_seen: u32 = 0;
3092    let out_ptr = nevents.deref(&memory);
3093
3094    let mut fd_guards = vec![];
3095    let mut clock_subs = vec![];
3096    let mut in_events = vec![];
3097    let mut time_to_sleep = Duration::from_millis(5);
3098
3099    for sub in subscription_array.iter() {
3100        let s: Subscription = wasi_try_mem_ok!(sub.read());
3101        let mut peb = PollEventBuilder::new();
3102
3103        let fd = match s.data {
3104            SubscriptionEnum::Read(SubscriptionFsReadwrite { file_descriptor }) => {
3105                match file_descriptor {
3106                    __WASI_STDIN_FILENO | __WASI_STDOUT_FILENO | __WASI_STDERR_FILENO => (),
3107                    _ => {
3108                        let fd_entry = wasi_try_ok!(state.fs.get_fd(file_descriptor), env);
3109                        if !fd_entry.rights.contains(Rights::FD_READ) {
3110                            return Ok(Errno::Access);
3111                        }
3112                    }
3113                }
3114                in_events.push(peb.add(PollEvent::PollIn).build());
3115                Some(file_descriptor)
3116            }
3117            SubscriptionEnum::Write(SubscriptionFsReadwrite { file_descriptor }) => {
3118                match file_descriptor {
3119                    __WASI_STDIN_FILENO | __WASI_STDOUT_FILENO | __WASI_STDERR_FILENO => (),
3120                    _ => {
3121                        let fd_entry = wasi_try_ok!(state.fs.get_fd(file_descriptor), env);
3122                        if !fd_entry.rights.contains(Rights::FD_WRITE) {
3123                            return Ok(Errno::Access);
3124                        }
3125                    }
3126                }
3127                in_events.push(peb.add(PollEvent::PollOut).build());
3128                Some(file_descriptor)
3129            }
3130            SubscriptionEnum::Clock(clock_info) => {
3131                if matches!(clock_info.clock_id, Clockid::Realtime | Clockid::Monotonic) {
3132                    // this is a hack
3133                    // TODO: do this properly
3134                    time_to_sleep = Duration::from_nanos(clock_info.timeout);
3135                    clock_subs.push((clock_info, s.userdata));
3136                    None
3137                } else {
3138                    unimplemented!("Polling not implemented for clocks yet");
3139                }
3140            }
3141        };
3142
3143        if let Some(fd) = fd {
3144            let wasi_file_ref = match fd {
3145                __WASI_STDERR_FILENO => {
3146                    wasi_try_ok!(
3147                        inodes
3148                            .stderr(&state.fs.fd_map)
3149                            .map_err(fs_error_into_wasi_err),
3150                        env
3151                    )
3152                }
3153                __WASI_STDIN_FILENO => {
3154                    wasi_try_ok!(
3155                        inodes
3156                            .stdin(&state.fs.fd_map)
3157                            .map_err(fs_error_into_wasi_err),
3158                        env
3159                    )
3160                }
3161                __WASI_STDOUT_FILENO => {
3162                    wasi_try_ok!(
3163                        inodes
3164                            .stdout(&state.fs.fd_map)
3165                            .map_err(fs_error_into_wasi_err),
3166                        env
3167                    )
3168                }
3169                _ => {
3170                    let fd_entry = wasi_try_ok!(state.fs.get_fd(fd), env);
3171                    let inode = fd_entry.inode;
3172                    if !fd_entry.rights.contains(Rights::POLL_FD_READWRITE) {
3173                        return Ok(Errno::Access);
3174                    }
3175
3176                    {
3177                        let guard = inodes.arena[inode].read();
3178                        let deref = guard.deref();
3179                        match deref {
3180                            Kind::File { handle, .. } => {
3181                                if let Some(h) = handle {
3182                                    crate::state::InodeValFileReadGuard { guard }
3183                                } else {
3184                                    return Ok(Errno::Badf);
3185                                }
3186                            }
3187                            Kind::Socket { .. }
3188                            | Kind::Pipe { .. }
3189                            | Kind::EventNotifications { .. } => {
3190                                return Ok(Errno::Badf);
3191                            }
3192                            Kind::Dir { .. }
3193                            | Kind::Root { .. }
3194                            | Kind::Buffer { .. }
3195                            | Kind::Symlink { .. } => {
3196                                unimplemented!("polling read on non-files not yet supported")
3197                            }
3198                        }
3199                    }
3200                }
3201            };
3202            fd_guards.push(wasi_file_ref);
3203        }
3204    }
3205
3206    #[allow(clippy::significant_drop_in_scrutinee)]
3207    let fds = {
3208        let mut f = vec![];
3209        for fd in fd_guards.iter() {
3210            f.push(wasi_try_ok!(fd.as_ref().ok_or(Errno::Badf)).deref());
3211        }
3212        f
3213    };
3214
3215    let mut seen_events = vec![Default::default(); in_events.len()];
3216
3217    let start = platform_clock_time_get(Snapshot0Clockid::Monotonic, 1_000_000).unwrap() as u128;
3218    let mut triggered = 0;
3219    while triggered == 0 {
3220        let now = platform_clock_time_get(Snapshot0Clockid::Monotonic, 1_000_000).unwrap() as u128;
3221        let delta = match now.checked_sub(start) {
3222            Some(a) => Duration::from_nanos(a as u64),
3223            None => Duration::ZERO,
3224        };
3225        match poll(
3226            fds.as_slice(),
3227            in_events.as_slice(),
3228            seen_events.as_mut_slice(),
3229            Duration::from_millis(1),
3230        ) {
3231            Ok(0) => {
3232                env.yield_now()?;
3233            }
3234            Ok(a) => {
3235                triggered = a;
3236            }
3237            Err(FsError::WouldBlock) => {
3238                env.sleep(Duration::from_millis(1))?;
3239            }
3240            Err(err) => {
3241                return Ok(fs_error_into_wasi_err(err));
3242            }
3243        };
3244        if delta > time_to_sleep {
3245            break;
3246        }
3247    }
3248
3249    for (i, seen_event) in seen_events.into_iter().enumerate() {
3250        let mut flags = Eventrwflags::empty();
3251        let mut error = Errno::Again;
3252        let mut bytes_available = 0;
3253        let event_iter = iterate_poll_events(seen_event);
3254        for event in event_iter {
3255            match event {
3256                PollEvent::PollError => error = Errno::Io,
3257                PollEvent::PollHangUp => flags = Eventrwflags::FD_READWRITE_HANGUP,
3258                PollEvent::PollInvalid => error = Errno::Inval,
3259                PollEvent::PollIn => {
3260                    bytes_available = wasi_try_ok!(
3261                        fds[i]
3262                            .bytes_available_read()
3263                            .map_err(fs_error_into_wasi_err),
3264                        env
3265                    )
3266                    .unwrap_or(0usize);
3267                    error = Errno::Success;
3268                }
3269                PollEvent::PollOut => {
3270                    bytes_available = wasi_try_ok!(
3271                        fds[i]
3272                            .bytes_available_write()
3273                            .map_err(fs_error_into_wasi_err),
3274                        env
3275                    )
3276                    .unwrap_or(0usize);
3277                    error = Errno::Success;
3278                }
3279            }
3280        }
3281        let event = Event {
3282            userdata: wasi_try_mem_ok!(subscription_array.index(i as u64).read()).userdata,
3283            error,
3284            data: match wasi_try_mem_ok!(subscription_array.index(i as u64).read()).data {
3285                SubscriptionEnum::Read(d) => EventEnum::FdRead(EventFdReadwrite {
3286                    nbytes: bytes_available as u64,
3287                    flags,
3288                }),
3289                SubscriptionEnum::Write(d) => EventEnum::FdWrite(EventFdReadwrite {
3290                    nbytes: bytes_available as u64,
3291                    flags,
3292                }),
3293                SubscriptionEnum::Clock(_) => EventEnum::Clock,
3294            },
3295        };
3296        wasi_try_mem_ok!(event_array.index(events_seen as u64).write(event));
3297        events_seen += 1;
3298    }
3299    if triggered == 0 {
3300        for (clock_info, userdata) in clock_subs {
3301            let event = Event {
3302                userdata,
3303                error: Errno::Success,
3304                data: EventEnum::Clock,
3305            };
3306            wasi_try_mem_ok!(event_array.index(events_seen as u64).write(event));
3307            events_seen += 1;
3308        }
3309    }
3310    let events_seen: M::Offset = wasi_try_ok!(events_seen.try_into().map_err(|_| Errno::Overflow));
3311    wasi_try_mem_ok!(out_ptr.write(events_seen));
3312    Ok(Errno::Success)
3313}
3314
3315/// ### `proc_exit()`
3316/// Terminate the process normally. An exit code of 0 indicates successful
3317/// termination of the program. The meanings of other values is dependent on
3318/// the environment.
3319/// Inputs:
3320/// - `__wasi_exitcode_t`
3321///   Exit code to return to the operating system
3322pub fn proc_exit(
3323    ctx: FunctionEnvMut<'_, WasiEnv>,
3324    code: __wasi_exitcode_t,
3325) -> Result<(), WasiError> {
3326    debug!("wasi::proc_exit, {}", code);
3327    Err(WasiError::Exit(code))
3328}
3329
3330/// ### `proc_raise()`
3331/// Send a signal to the process of the calling thread.
3332/// Note: This is similar to `raise` in POSIX.
3333/// Inputs:
3334/// - `Signal`
3335///   Signal to be raised for this process
3336pub fn proc_raise(ctx: FunctionEnvMut<'_, WasiEnv>, sig: Signal) -> Errno {
3337    debug!("wasi::proc_raise");
3338    unimplemented!("wasi::proc_raise")
3339}
3340
3341/// ### `sched_yield()`
3342/// Yields execution of the thread
3343pub fn sched_yield(ctx: FunctionEnvMut<'_, WasiEnv>) -> Result<Errno, WasiError> {
3344    trace!("wasi::sched_yield");
3345    let env = ctx.data();
3346    env.yield_now()?;
3347    Ok(Errno::Success)
3348}
3349
3350/// ### `random_get()`
3351/// Fill buffer with high-quality random data.  This function may be slow and block
3352/// Inputs:
3353/// - `void *buf`
3354///     A pointer to a buffer where the random bytes will be written
3355/// - `size_t buf_len`
3356///     The number of bytes that will be written
3357pub fn random_get<M: MemorySize>(
3358    ctx: FunctionEnvMut<'_, WasiEnv>,
3359    buf: WasmPtr<u8, M>,
3360    buf_len: M::Offset,
3361) -> Errno {
3362    trace!("wasi::random_get buf_len: {}", buf_len);
3363    let env = ctx.data();
3364    let memory = env.memory_view(&ctx);
3365    let buf_len64: u64 = buf_len.into();
3366    let mut u8_buffer = vec![0; buf_len64 as usize];
3367    let res = getrandom::getrandom(&mut u8_buffer);
3368    match res {
3369        Ok(()) => {
3370            let buf = wasi_try_mem!(buf.slice(&memory, buf_len));
3371            wasi_try_mem!(buf.write_slice(&u8_buffer));
3372            Errno::Success
3373        }
3374        Err(_) => Errno::Io,
3375    }
3376}
3377
3378/// ### `tty_get()`
3379/// Retrieves the current state of the TTY
3380pub fn tty_get<M: MemorySize>(
3381    ctx: FunctionEnvMut<'_, WasiEnv>,
3382    tty_state: WasmPtr<Tty, M>,
3383) -> Errno {
3384    debug!("wasi::tty_stdin");
3385    let env = ctx.data();
3386
3387    let state = env.runtime.tty_get();
3388    let state = Tty {
3389        cols: state.cols,
3390        rows: state.rows,
3391        width: state.width,
3392        height: state.height,
3393        stdin_tty: state.stdin_tty,
3394        stdout_tty: state.stdout_tty,
3395        stderr_tty: state.stderr_tty,
3396        echo: state.echo,
3397        line_buffered: state.line_buffered,
3398    };
3399
3400    let memory = env.memory_view(&ctx);
3401    wasi_try_mem!(tty_state.write(&memory, state));
3402
3403    Errno::Success
3404}
3405
3406/// ### `tty_set()`
3407/// Updates the properties of the rect
3408pub fn tty_set<M: MemorySize>(
3409    ctx: FunctionEnvMut<'_, WasiEnv>,
3410    tty_state: WasmPtr<Tty, M>,
3411) -> Errno {
3412    debug!("wasi::tty_set");
3413
3414    let env = ctx.data();
3415    let memory = env.memory_view(&ctx);
3416    let state = wasi_try_mem!(tty_state.read(&memory));
3417    let state = super::runtime::WasiTtyState {
3418        cols: state.cols,
3419        rows: state.rows,
3420        width: state.width,
3421        height: state.height,
3422        stdin_tty: state.stdin_tty,
3423        stdout_tty: state.stdout_tty,
3424        stderr_tty: state.stderr_tty,
3425        echo: state.echo,
3426        line_buffered: state.line_buffered,
3427    };
3428
3429    env.runtime.tty_set(state);
3430
3431    Errno::Success
3432}
3433
3434/// ### `getcwd()`
3435/// Returns the current working directory
3436/// If the path exceeds the size of the buffer then this function
3437/// will fill the path_len with the needed size and return EOVERFLOW
3438pub fn getcwd<M: MemorySize>(
3439    ctx: FunctionEnvMut<'_, WasiEnv>,
3440    path: WasmPtr<u8, M>,
3441    path_len: WasmPtr<M::Offset, M>,
3442) -> Errno {
3443    debug!("wasi::getcwd");
3444    let env = ctx.data();
3445    let (memory, mut state, mut inodes) = env.get_memory_and_wasi_state_and_inodes_mut(&ctx, 0);
3446
3447    let (_, cur_dir) = wasi_try!(state
3448        .fs
3449        .get_current_dir(inodes.deref_mut(), crate::VIRTUAL_ROOT_FD,));
3450
3451    let max_path_len = wasi_try_mem!(path_len.read(&memory));
3452    let path_slice = wasi_try_mem!(path.slice(&memory, max_path_len));
3453    let max_path_len: u64 = max_path_len.into();
3454
3455    let cur_dir = cur_dir.as_bytes();
3456    wasi_try_mem!(path_len.write(&memory, wasi_try!(to_offset::<M>(cur_dir.len()))));
3457    if cur_dir.len() as u64 >= max_path_len {
3458        return Errno::Overflow;
3459    }
3460
3461    let cur_dir = {
3462        let mut u8_buffer = vec![0; max_path_len as usize];
3463        let cur_dir_len = cur_dir.len();
3464        if (cur_dir_len as u64) < max_path_len {
3465            u8_buffer[..cur_dir_len].clone_from_slice(cur_dir);
3466            u8_buffer[cur_dir_len] = 0;
3467        } else {
3468            return Errno::Overflow;
3469        }
3470        u8_buffer
3471    };
3472
3473    wasi_try_mem!(path_slice.write_slice(&cur_dir[..]));
3474    Errno::Success
3475}
3476
3477/// ### `chdir()`
3478/// Sets the current working directory
3479pub fn chdir<M: MemorySize>(
3480    ctx: FunctionEnvMut<'_, WasiEnv>,
3481    path: WasmPtr<u8, M>,
3482    path_len: M::Offset,
3483) -> Errno {
3484    debug!("wasi::chdir");
3485    let env = ctx.data();
3486    let (memory, mut state) = env.get_memory_and_wasi_state(&ctx, 0);
3487    let path = unsafe { get_input_str!(&memory, path, path_len) };
3488
3489    state.fs.set_current_dir(path.as_str());
3490    Errno::Success
3491}
3492
3493/// ### `thread_spawn()`
3494/// Creates a new thread by spawning that shares the same
3495/// memory address space, file handles and main event loops.
3496/// The function referenced by the fork call must be
3497/// exported by the web assembly process.
3498///
3499/// ## Parameters
3500///
3501/// * `name` - Name of the function that will be invoked as a new thread
3502/// * `user_data` - User data that will be supplied to the function when its called
3503/// * `reactor` - Indicates if the function will operate as a reactor or
3504///   as a normal thread. Reactors will be repeatable called
3505///   whenever IO work is available to be processed.
3506///
3507/// ## Return
3508///
3509/// Returns the thread index of the newly created thread
3510/// (indices always start from zero)
3511pub fn thread_spawn<M: MemorySize>(
3512    ctx: FunctionEnvMut<'_, WasiEnv>,
3513    method: WasmPtr<u8, M>,
3514    method_len: M::Offset,
3515    user_data: u64,
3516    reactor: Bool,
3517    ret_tid: WasmPtr<Tid, M>,
3518) -> Errno {
3519    debug!("wasi::thread_spawn");
3520    let env = ctx.data();
3521    let memory = env.memory_view(&ctx);
3522    let method = unsafe { get_input_str!(&memory, method, method_len) };
3523
3524    // Load the callback function
3525    if method.as_str() != "_thread_start" {
3526        return Errno::Notcapable;
3527    };
3528    /*
3529    let funct = unsafe {
3530        if env.thread_start_ref().is_none() {
3531            return Errno::Addrnotavail;
3532        }
3533        env.thread_start_ref_unchecked()
3534    };
3535    */
3536
3537    let reactor = match reactor {
3538        Bool::False => false,
3539        Bool::True => true,
3540        _ => return Errno::Inval,
3541    };
3542
3543    // Create the sub-thread
3544    let mut sub_env = env.clone();
3545    let mut sub_thread = env.new_thread();
3546    sub_env.id = sub_thread.id;
3547
3548    let child = {
3549        let id = sub_thread.id;
3550        wasi_try!(env
3551            .runtime
3552            .thread_spawn(Box::new(move || {
3553                /*
3554                if let Some(funct) = sub_env.thread_start_ref() {
3555                    if let Err(err) = funct.call(user_data) {
3556                        warn!("thread failed: {}", err);
3557                        std::mem::forget(sub_thread);
3558                        return;
3559                    }
3560                } else {
3561                    warn!("failed to start thread: missing callback '__wasix_thread_start'");
3562                    std::mem::forget(sub_thread);
3563                    return;
3564                }
3565                */
3566
3567                let thread = {
3568                    let mut guard = sub_env.state.threading.lock().unwrap();
3569                    let thread = guard.threads.remove(&id);
3570                    drop(guard);
3571                    thread
3572                };
3573
3574                if let Some(thread) = thread {
3575                    let mut thread_guard = thread.exit.lock().unwrap();
3576                    thread_guard.take();
3577                }
3578                drop(sub_thread);
3579            }))
3580            .map_err(|err| {
3581                let err: Errno = err.into();
3582                err
3583            }));
3584        id
3585    };
3586    let child: Tid = child.into();
3587
3588    wasi_try_mem!(ret_tid.write(&memory, child));
3589    Errno::Success
3590}
3591
3592/// ### `thread_sleep()`
3593/// Sends the current thread to sleep for a period of time
3594///
3595/// ## Parameters
3596///
3597/// * `duration` - Amount of time that the thread should sleep
3598pub fn thread_sleep(
3599    ctx: FunctionEnvMut<'_, WasiEnv>,
3600    duration: Timestamp,
3601) -> Result<Errno, WasiError> {
3602    debug!("wasi::thread_sleep");
3603
3604    let env = ctx.data();
3605    let duration = Duration::from_nanos(duration as u64);
3606    env.sleep(duration)?;
3607    Ok(Errno::Success)
3608}
3609
3610/// ### `thread_id()`
3611/// Returns the index of the current thread
3612/// (threads indices are sequencial from zero)
3613pub fn thread_id<M: MemorySize>(
3614    ctx: FunctionEnvMut<'_, WasiEnv>,
3615    ret_tid: WasmPtr<Tid, M>,
3616) -> Errno {
3617    debug!("wasi::thread_id");
3618
3619    let env = ctx.data();
3620    let tid: Tid = env.id.into();
3621    let memory = env.memory_view(&ctx);
3622    wasi_try_mem!(ret_tid.write(&memory, tid));
3623    Errno::Success
3624}
3625
3626/// ### `thread_join()`
3627/// Joins this thread with another thread, blocking this
3628/// one until the other finishes
3629///
3630/// ## Parameters
3631///
3632/// * `tid` - Handle of the thread to wait on
3633pub fn thread_join(ctx: FunctionEnvMut<'_, WasiEnv>, tid: Tid) -> Result<Errno, WasiError> {
3634    debug!("wasi::thread_join");
3635
3636    let env = ctx.data();
3637    let tid: WasiThreadId = tid.into();
3638    let other_thread = {
3639        let guard = env.state.threading.lock().unwrap();
3640        guard.threads.get(&tid).cloned()
3641    };
3642    if let Some(other_thread) = other_thread {
3643        loop {
3644            if other_thread.join(Duration::from_millis(5)) {
3645                break;
3646            }
3647            env.yield_now()?;
3648        }
3649        Ok(Errno::Success)
3650    } else {
3651        Ok(Errno::Success)
3652    }
3653}
3654
3655/// ### `thread_parallelism()`
3656/// Returns the available parallelism which is normally the
3657/// number of available cores that can run concurrently
3658pub fn thread_parallelism<M: MemorySize>(
3659    ctx: FunctionEnvMut<'_, WasiEnv>,
3660    ret_parallelism: WasmPtr<M::Offset, M>,
3661) -> Errno {
3662    debug!("wasi::thread_parallelism");
3663
3664    let env = ctx.data();
3665    let parallelism = wasi_try!(env.runtime().thread_parallelism().map_err(|err| {
3666        let err: Errno = err.into();
3667        err
3668    }));
3669    let parallelism: M::Offset = wasi_try!(parallelism.try_into().map_err(|_| Errno::Overflow));
3670    let memory = env.memory_view(&ctx);
3671    wasi_try_mem!(ret_parallelism.write(&memory, parallelism));
3672    Errno::Success
3673}
3674
3675/// ### `getpid()`
3676/// Returns the handle of the current process
3677pub fn getpid<M: MemorySize>(ctx: FunctionEnvMut<'_, WasiEnv>, ret_pid: WasmPtr<Pid, M>) -> Errno {
3678    debug!("wasi::getpid");
3679
3680    let env = ctx.data();
3681    let pid = env.runtime().getpid();
3682    if let Some(pid) = pid {
3683        let memory = env.memory_view(&ctx);
3684        wasi_try_mem!(ret_pid.write(&memory, pid as Pid));
3685        Errno::Success
3686    } else {
3687        Errno::Notsup
3688    }
3689}
3690
3691/// ### `thread_exit()`
3692/// Terminates the current running thread, if this is the last thread then
3693/// the process will also exit with the specified exit code. An exit code
3694/// of 0 indicates successful termination of the thread. The meanings of
3695/// other values is dependent on the environment.
3696///
3697/// ## Parameters
3698///
3699/// * `rval` - The exit code returned by the process.
3700pub fn thread_exit(
3701    ctx: FunctionEnvMut<'_, WasiEnv>,
3702    exitcode: __wasi_exitcode_t,
3703) -> Result<Errno, WasiError> {
3704    debug!("wasi::thread_exit");
3705    Err(WasiError::Exit(exitcode))
3706}
3707
3708/// Spawns a new process within the context of this machine
3709///
3710/// ## Parameters
3711///
3712/// * `name` - Name of the process to be spawned
3713/// * `chroot` - Indicates if the process will chroot or not
3714/// * `args` - List of the arguments to pass the process
3715///   (entries are separated by line feeds)
3716/// * `preopen` - List of the preopens for this process
3717///   (entries are separated by line feeds)
3718/// * `stdin` - How will stdin be handled
3719/// * `stdout` - How will stdout be handled
3720/// * `stderr` - How will stderr be handled
3721/// * `working_dir` - Working directory where this process should run
3722///   (passing '.' will use the current directory)
3723///
3724/// ## Return
3725///
3726/// Returns a bus process id that can be used to invoke calls
3727pub fn process_spawn<M: MemorySize>(
3728    ctx: FunctionEnvMut<'_, WasiEnv>,
3729    name: WasmPtr<u8, M>,
3730    name_len: M::Offset,
3731    chroot: Bool,
3732    args: WasmPtr<u8, M>,
3733    args_len: M::Offset,
3734    preopen: WasmPtr<u8, M>,
3735    preopen_len: M::Offset,
3736    stdin: WasiStdioMode,
3737    stdout: WasiStdioMode,
3738    stderr: WasiStdioMode,
3739    working_dir: WasmPtr<u8, M>,
3740    working_dir_len: M::Offset,
3741    ret_handles: WasmPtr<BusHandles, M>,
3742) -> BusErrno {
3743    let env = ctx.data();
3744    let bus = env.runtime.bus();
3745    let memory = env.memory_view(&ctx);
3746    let name = unsafe { get_input_str_bus!(&memory, name, name_len) };
3747    let args = unsafe { get_input_str_bus!(&memory, args, args_len) };
3748    let preopen = unsafe { get_input_str_bus!(&memory, preopen, preopen_len) };
3749    let working_dir = unsafe { get_input_str_bus!(&memory, working_dir, working_dir_len) };
3750    let chroot = chroot == Bool::True;
3751    debug!("wasi::process_spawn (name={})", name);
3752
3753    let args: Vec<_> = args.split(&['\n', '\r']).map(|a| a.to_string()).collect();
3754
3755    let preopen: Vec<_> = preopen
3756        .split(&['\n', '\r'])
3757        .map(|a| a.to_string())
3758        .collect();
3759
3760    let conv_stdio_mode = |mode: WasiStdioMode| match mode {
3761        WasiStdioMode::Piped => StdioMode::Piped,
3762        WasiStdioMode::Inherit => StdioMode::Inherit,
3763        WasiStdioMode::Log => StdioMode::Log,
3764        /*__WASI_STDIO_MODE_NULL |*/ _ => StdioMode::Null,
3765    };
3766
3767    let process = wasi_try_bus!(bus
3768        .new_spawn()
3769        .chroot(chroot)
3770        .args(args)
3771        .preopen(preopen)
3772        .stdin_mode(conv_stdio_mode(stdin))
3773        .stdout_mode(conv_stdio_mode(stdout))
3774        .stderr_mode(conv_stdio_mode(stderr))
3775        .working_dir(working_dir)
3776        .spawn(name.as_str())
3777        .map_err(bus_error_into_wasi_err));
3778
3779    let conv_stdio_fd = |a: Option<FileDescriptor>| match a {
3780        Some(fd) => OptionFd {
3781            tag: OptionTag::Some,
3782            fd: fd.into(),
3783        },
3784        None => OptionFd {
3785            tag: OptionTag::None,
3786            fd: 0,
3787        },
3788    };
3789
3790    // Convert the stdio
3791    let stdin = conv_stdio_fd(process.inst.stdin_fd());
3792    let stdout = conv_stdio_fd(process.inst.stdout_fd());
3793    let stderr = conv_stdio_fd(process.inst.stderr_fd());
3794
3795    // Add the process to the environment state
3796    let bid = {
3797        let mut guard = env.state.threading.lock().unwrap();
3798        guard.process_seed += 1;
3799        let bid = guard.process_seed;
3800        guard.processes.insert(bid.into(), process);
3801        bid
3802    };
3803
3804    let handles = BusHandles {
3805        bid,
3806        stdin,
3807        stdout,
3808        stderr,
3809    };
3810
3811    wasi_try_mem_bus!(ret_handles.write(&memory, handles));
3812
3813    BusErrno::Success
3814}
3815
3816/// Spawns a new bus process for a particular web WebAssembly
3817/// binary that is referenced by its process name.
3818///
3819/// ## Parameters
3820///
3821/// * `name` - Name of the process to be spawned
3822/// * `reuse` - Indicates if the existing processes should be reused
3823///   if they are already running
3824///
3825/// ## Return
3826///
3827/// Returns a bus process id that can be used to invoke calls
3828pub fn bus_open_local<M: MemorySize>(
3829    ctx: FunctionEnvMut<'_, WasiEnv>,
3830    name: WasmPtr<u8, M>,
3831    name_len: M::Offset,
3832    reuse: Bool,
3833    ret_bid: WasmPtr<Bid, M>,
3834) -> BusErrno {
3835    let env = ctx.data();
3836    let bus = env.runtime.bus();
3837    let memory = env.memory_view(&ctx);
3838    let name = unsafe { get_input_str_bus!(&memory, name, name_len) };
3839    let reuse = reuse == Bool::True;
3840    debug!("wasi::bus_open_local (name={}, reuse={})", name, reuse);
3841
3842    bus_open_local_internal(ctx, name, reuse, None, None, ret_bid)
3843}
3844
3845/// Spawns a new bus process for a particular web WebAssembly
3846/// binary that is referenced by its process name on a remote instance.
3847///
3848/// ## Parameters
3849///
3850/// * `name` - Name of the process to be spawned
3851/// * `reuse` - Indicates if the existing processes should be reused
3852///   if they are already running
3853/// * `instance` - Instance identifier where this process will be spawned
3854/// * `token` - Acceess token used to authenticate with the instance
3855///
3856/// ## Return
3857///
3858/// Returns a bus process id that can be used to invoke calls
3859pub fn bus_open_remote<M: MemorySize>(
3860    ctx: FunctionEnvMut<'_, WasiEnv>,
3861    name: WasmPtr<u8, M>,
3862    name_len: M::Offset,
3863    reuse: Bool,
3864    instance: WasmPtr<u8, M>,
3865    instance_len: M::Offset,
3866    token: WasmPtr<u8, M>,
3867    token_len: M::Offset,
3868    ret_bid: WasmPtr<Bid, M>,
3869) -> BusErrno {
3870    let env = ctx.data();
3871    let bus = env.runtime.bus();
3872    let memory = env.memory_view(&ctx);
3873    let name = unsafe { get_input_str_bus!(&memory, name, name_len) };
3874    let instance = unsafe { get_input_str_bus!(&memory, instance, instance_len) };
3875    let token = unsafe { get_input_str_bus!(&memory, token, token_len) };
3876    let reuse = reuse == Bool::True;
3877    debug!(
3878        "wasi::bus_open_remote (name={}, reuse={}, instance={})",
3879        name, reuse, instance
3880    );
3881
3882    bus_open_local_internal(ctx, name, reuse, Some(instance), Some(token), ret_bid)
3883}
3884
3885fn bus_open_local_internal<M: MemorySize>(
3886    ctx: FunctionEnvMut<'_, WasiEnv>,
3887    name: String,
3888    reuse: bool,
3889    instance: Option<String>,
3890    token: Option<String>,
3891    ret_bid: WasmPtr<Bid, M>,
3892) -> BusErrno {
3893    let env = ctx.data();
3894    let bus = env.runtime.bus();
3895    let memory = env.memory_view(&ctx);
3896    let name: Cow<'static, str> = name.into();
3897
3898    // Check if it already exists
3899    if reuse {
3900        let guard = env.state.threading.lock().unwrap();
3901        if let Some(bid) = guard.process_reuse.get(&name) {
3902            if guard.processes.contains_key(bid) {
3903                wasi_try_mem_bus!(ret_bid.write(&memory, (*bid).into()));
3904                return BusErrno::Success;
3905            }
3906        }
3907    }
3908
3909    let mut process = bus.new_spawn();
3910    process
3911        .reuse(reuse)
3912        .stdin_mode(StdioMode::Null)
3913        .stdout_mode(StdioMode::Null)
3914        .stderr_mode(StdioMode::Log);
3915
3916    if let Some(instance) = instance {
3917        process.remote_instance(instance);
3918    }
3919
3920    if let Some(token) = token {
3921        process.access_token(token);
3922    }
3923
3924    let process = wasi_try_bus!(process
3925        .spawn(name.as_ref())
3926        .map_err(bus_error_into_wasi_err));
3927
3928    // Add the process to the environment state
3929    let bid = {
3930        let mut guard = env.state.threading.lock().unwrap();
3931        guard.process_seed += 1;
3932        let bid: WasiBusProcessId = guard.process_seed.into();
3933        guard.processes.insert(bid, process);
3934        guard.process_reuse.insert(name, bid);
3935        bid
3936    };
3937
3938    wasi_try_mem_bus!(ret_bid.write(&memory, bid.into()));
3939
3940    BusErrno::Success
3941}
3942
3943/// Closes a bus process and releases all associated resources
3944///
3945/// ## Parameters
3946///
3947/// * `bid` - Handle of the bus process handle to be closed
3948pub fn bus_close(ctx: FunctionEnvMut<'_, WasiEnv>, bid: Bid) -> BusErrno {
3949    trace!("wasi::bus_close (bid={})", bid);
3950    let bid: WasiBusProcessId = bid.into();
3951
3952    let env = ctx.data();
3953    let mut guard = env.state.threading.lock().unwrap();
3954    guard.processes.remove(&bid);
3955
3956    BusErrno::Unsupported
3957}
3958
3959/// Invokes a call within a running bus process.
3960///
3961/// ## Parameters
3962///
3963/// * `bid` - Handle of the bus process to invoke the call within
3964/// * `keep_alive` - Causes the call handle to remain open even when A
3965///   reply is received. It is then the  callers responsibility
3966///   to invoke 'bus_drop' when they are finished with the call
3967/// * `topic` - Topic that describes the type of call to made
3968/// * `format` - Format of the data pushed onto the bus
3969/// * `buf` - The buffer where data to be transmitted is stored
3970pub fn bus_call<M: MemorySize>(
3971    ctx: FunctionEnvMut<'_, WasiEnv>,
3972    bid: Bid,
3973    keep_alive: Bool,
3974    topic: WasmPtr<u8, M>,
3975    topic_len: M::Offset,
3976    format: BusDataFormat,
3977    buf: WasmPtr<u8, M>,
3978    buf_len: M::Offset,
3979    ret_cid: WasmPtr<Cid, M>,
3980) -> BusErrno {
3981    let env = ctx.data();
3982    let bus = env.runtime.bus();
3983    let memory = env.memory_view(&ctx);
3984    let topic = unsafe { get_input_str_bus!(&memory, topic, topic_len) };
3985    let keep_alive = keep_alive == Bool::True;
3986    trace!(
3987        "wasi::bus_call (bid={}, topic={}, buf_len={})",
3988        bid,
3989        topic,
3990        buf_len
3991    );
3992
3993    BusErrno::Unsupported
3994}
3995
3996/// Invokes a call within the context of another call
3997///
3998/// ## Parameters
3999///
4000/// * `parent` - Parent bus call that this is related to
4001/// * `keep_alive` - Causes the call handle to remain open even when A
4002///   reply is received. It is then the  callers responsibility
4003///   to invoke 'bus_drop' when they are finished with the call
4004/// * `topic` - Topic that describes the type of call to made
4005/// * `format` - Format of the data pushed onto the bus
4006/// * `buf` - The buffer where data to be transmitted is stored
4007pub fn bus_subcall<M: MemorySize>(
4008    ctx: FunctionEnvMut<'_, WasiEnv>,
4009    parent: Cid,
4010    keep_alive: Bool,
4011    topic: WasmPtr<u8, M>,
4012    topic_len: M::Offset,
4013    format: BusDataFormat,
4014    buf: WasmPtr<u8, M>,
4015    buf_len: M::Offset,
4016    ret_cid: WasmPtr<Cid, M>,
4017) -> BusErrno {
4018    let env = ctx.data();
4019    let bus = env.runtime.bus();
4020    let memory = env.memory_view(&ctx);
4021    let topic = unsafe { get_input_str_bus!(&memory, topic, topic_len) };
4022    let keep_alive = keep_alive == Bool::True;
4023    trace!(
4024        "wasi::bus_subcall (parent={}, topic={}, buf_len={})",
4025        parent,
4026        topic,
4027        buf_len
4028    );
4029
4030    BusErrno::Unsupported
4031}
4032
4033/// Polls for any outstanding events from a particular
4034/// bus process by its handle
4035///
4036/// ## Parameters
4037///
4038/// * `timeout` - Timeout before the poll returns, if one passed 0
4039///   as the timeout then this call is non blocking.
4040/// * `events` - An events buffer that will hold any received bus events
4041/// * `malloc` - Name of the function that will be invoked to allocate memory
4042///   Function signature fn(u64) -> u64
4043///
4044/// ## Return
4045///
4046/// Returns the number of events that have occured
4047pub fn bus_poll<M: MemorySize>(
4048    ctx: FunctionEnvMut<'_, WasiEnv>,
4049    timeout: Timestamp,
4050    events: WasmPtr<u8, M>,
4051    nevents: M::Offset,
4052    malloc: WasmPtr<u8, M>,
4053    malloc_len: M::Offset,
4054    ret_nevents: WasmPtr<M::Offset, M>,
4055) -> BusErrno {
4056    let env = ctx.data();
4057    let bus = env.runtime.bus();
4058    let memory = env.memory_view(&ctx);
4059    let malloc = unsafe { get_input_str_bus!(&memory, malloc, malloc_len) };
4060    trace!("wasi::bus_poll (timeout={}, malloc={})", timeout, malloc);
4061
4062    BusErrno::Unsupported
4063}
4064
4065/// Replies to a call that was made to this process
4066/// from another process; where 'cid' is the call context.
4067/// This will may also drop the handle and release any
4068/// associated resources (if keepalive is not set)
4069///
4070/// ## Parameters
4071///
4072/// * `cid` - Handle of the call to send a reply on
4073/// * `format` - Format of the data pushed onto the bus
4074/// * `buf` - The buffer where data to be transmitted is stored
4075pub fn call_reply<M: MemorySize>(
4076    ctx: FunctionEnvMut<'_, WasiEnv>,
4077    cid: Cid,
4078    format: BusDataFormat,
4079    buf: WasmPtr<u8, M>,
4080    buf_len: M::Offset,
4081) -> BusErrno {
4082    let env = ctx.data();
4083    let bus = env.runtime.bus();
4084    trace!(
4085        "wasi::call_reply (cid={}, format={}, data_len={})",
4086        cid,
4087        format,
4088        buf_len
4089    );
4090
4091    BusErrno::Unsupported
4092}
4093
4094/// Causes a fault on a particular call that was made
4095/// to this process from another process; where 'bid'
4096/// is the callering process context.
4097///
4098/// ## Parameters
4099///
4100/// * `cid` - Handle of the call to raise a fault on
4101/// * `fault` - Fault to be raised on the bus
4102pub fn call_fault(ctx: FunctionEnvMut<'_, WasiEnv>, cid: Cid, fault: BusErrno) -> BusErrno {
4103    let env = ctx.data();
4104    let bus = env.runtime.bus();
4105    debug!("wasi::call_fault (cid={}, fault={})", cid, fault);
4106
4107    BusErrno::Unsupported
4108}
4109
4110/// Closes a bus call based on its bus call handle
4111///
4112/// ## Parameters
4113///
4114/// * `cid` - Handle of the bus call handle to be dropped
4115pub fn call_close(ctx: FunctionEnvMut<'_, WasiEnv>, cid: Cid) -> BusErrno {
4116    let env = ctx.data();
4117    let bus = env.runtime.bus();
4118    trace!("wasi::call_close (cid={})", cid);
4119
4120    BusErrno::Unsupported
4121}
4122
4123/// ### `ws_connect()`
4124/// Connects to a websocket at a particular network URL
4125///
4126/// ## Parameters
4127///
4128/// * `url` - URL of the web socket destination to connect to
4129///
4130/// ## Return
4131///
4132/// Returns a socket handle which is used to send and receive data
4133pub fn ws_connect<M: MemorySize>(
4134    ctx: FunctionEnvMut<'_, WasiEnv>,
4135    url: WasmPtr<u8, M>,
4136    url_len: M::Offset,
4137    ret_sock: WasmPtr<WasiFd, M>,
4138) -> Errno {
4139    debug!("wasi::ws_connect");
4140    let env = ctx.data();
4141    let memory = env.memory_view(&ctx);
4142    let url = unsafe { get_input_str!(&memory, url, url_len) };
4143
4144    let socket = wasi_try!(env
4145        .net()
4146        .ws_connect(url.as_str())
4147        .map_err(net_error_into_wasi_err));
4148
4149    let (memory, state, mut inodes) = env.get_memory_and_wasi_state_and_inodes_mut(&ctx, 0);
4150
4151    let kind = Kind::Socket {
4152        socket: InodeSocket::new(InodeSocketKind::WebSocket(socket)),
4153    };
4154
4155    let inode = state.fs.create_inode_with_default_stat(
4156        inodes.deref_mut(),
4157        kind,
4158        false,
4159        "socket".to_string(),
4160    );
4161    let rights = Rights::all_socket();
4162    let fd = wasi_try!(state
4163        .fs
4164        .create_fd(rights, rights, Fdflags::empty(), 0, inode));
4165
4166    wasi_try_mem!(ret_sock.write(&memory, fd));
4167
4168    Errno::Success
4169}
4170
4171/// ### `http_request()`
4172/// Makes a HTTP request to a remote web resource and
4173/// returns a socket handles that are used to send and receive data
4174///
4175/// ## Parameters
4176///
4177/// * `url` - URL of the HTTP resource to connect to
4178/// * `method` - HTTP method to be invoked
4179/// * `headers` - HTTP headers to attach to the request
4180///   (headers seperated by lines)
4181/// * `gzip` - Should the request body be compressed
4182///
4183/// ## Return
4184///
4185/// The body of the response can be streamed from the returned
4186/// file handle
4187pub fn http_request<M: MemorySize>(
4188    ctx: FunctionEnvMut<'_, WasiEnv>,
4189    url: WasmPtr<u8, M>,
4190    url_len: M::Offset,
4191    method: WasmPtr<u8, M>,
4192    method_len: M::Offset,
4193    headers: WasmPtr<u8, M>,
4194    headers_len: M::Offset,
4195    gzip: Bool,
4196    ret_handles: WasmPtr<HttpHandles, M>,
4197) -> Errno {
4198    debug!("wasi::http_request");
4199    let env = ctx.data();
4200    let memory = env.memory_view(&ctx);
4201    let url = unsafe { get_input_str!(&memory, url, url_len) };
4202    let method = unsafe { get_input_str!(&memory, method, method_len) };
4203    let headers = unsafe { get_input_str!(&memory, headers, headers_len) };
4204
4205    let gzip = match gzip {
4206        Bool::False => false,
4207        Bool::True => true,
4208        _ => return Errno::Inval,
4209    };
4210
4211    let socket = wasi_try!(env
4212        .net()
4213        .http_request(url.as_str(), method.as_str(), headers.as_str(), gzip)
4214        .map_err(net_error_into_wasi_err));
4215    let socket_req = SocketHttpRequest {
4216        request: socket.request,
4217        response: None,
4218        headers: None,
4219        status: socket.status.clone(),
4220    };
4221    let socket_res = SocketHttpRequest {
4222        request: None,
4223        response: socket.response,
4224        headers: None,
4225        status: socket.status.clone(),
4226    };
4227    let socket_hdr = SocketHttpRequest {
4228        request: None,
4229        response: None,
4230        headers: socket.headers,
4231        status: socket.status,
4232    };
4233
4234    let (memory, state, mut inodes) = env.get_memory_and_wasi_state_and_inodes_mut(&ctx, 0);
4235
4236    let kind_req = Kind::Socket {
4237        socket: InodeSocket::new(InodeSocketKind::HttpRequest(
4238            Mutex::new(socket_req),
4239            InodeHttpSocketType::Request,
4240        )),
4241    };
4242    let kind_res = Kind::Socket {
4243        socket: InodeSocket::new(InodeSocketKind::HttpRequest(
4244            Mutex::new(socket_res),
4245            InodeHttpSocketType::Response,
4246        )),
4247    };
4248    let kind_hdr = Kind::Socket {
4249        socket: InodeSocket::new(InodeSocketKind::HttpRequest(
4250            Mutex::new(socket_hdr),
4251            InodeHttpSocketType::Headers,
4252        )),
4253    };
4254
4255    let inode_req = state.fs.create_inode_with_default_stat(
4256        inodes.deref_mut(),
4257        kind_req,
4258        false,
4259        "http_request".to_string(),
4260    );
4261    let inode_res = state.fs.create_inode_with_default_stat(
4262        inodes.deref_mut(),
4263        kind_res,
4264        false,
4265        "http_response".to_string(),
4266    );
4267    let inode_hdr = state.fs.create_inode_with_default_stat(
4268        inodes.deref_mut(),
4269        kind_hdr,
4270        false,
4271        "http_headers".to_string(),
4272    );
4273    let rights = Rights::all_socket();
4274
4275    let handles = HttpHandles {
4276        req: wasi_try!(state
4277            .fs
4278            .create_fd(rights, rights, Fdflags::empty(), 0, inode_req)),
4279        res: wasi_try!(state
4280            .fs
4281            .create_fd(rights, rights, Fdflags::empty(), 0, inode_res)),
4282        hdr: wasi_try!(state
4283            .fs
4284            .create_fd(rights, rights, Fdflags::empty(), 0, inode_hdr)),
4285    };
4286
4287    wasi_try_mem!(ret_handles.write(&memory, handles));
4288
4289    Errno::Success
4290}
4291
4292/// ### `http_status()`
4293/// Retrieves the status of a HTTP request
4294///
4295/// ## Parameters
4296///
4297/// * `fd` - Handle of the HTTP request
4298/// * `status` - Pointer to a buffer that will be filled with the current
4299///   status of this HTTP request
4300pub fn http_status<M: MemorySize>(
4301    ctx: FunctionEnvMut<'_, WasiEnv>,
4302    sock: WasiFd,
4303    status: WasmPtr<HttpStatus, M>,
4304) -> Errno {
4305    debug!("wasi::http_status");
4306
4307    let env = ctx.data();
4308    let memory = env.memory_view(&ctx);
4309    let ref_status = status.deref(&memory);
4310
4311    let http_status = wasi_try!(__sock_actor(&ctx, sock, Rights::empty(), |socket| {
4312        socket.http_status()
4313    }));
4314
4315    // Write everything else and return the status to the caller
4316    let status = HttpStatus {
4317        ok: Bool::True,
4318        redirect: match http_status.redirected {
4319            true => Bool::True,
4320            false => Bool::False,
4321        },
4322        size: wasi_try!(Ok(http_status.size)),
4323        status: http_status.status,
4324    };
4325
4326    wasi_try_mem!(ref_status.write(status));
4327
4328    Errno::Success
4329}
4330
4331/// ### `port_bridge()`
4332/// Securely connects to a particular remote network
4333///
4334/// ## Parameters
4335///
4336/// * `network` - Fully qualified identifier for the network
4337/// * `token` - Access token used to authenticate with the network
4338/// * `security` - Level of encryption to encapsulate the network connection with
4339pub fn port_bridge<M: MemorySize>(
4340    ctx: FunctionEnvMut<'_, WasiEnv>,
4341    network: WasmPtr<u8, M>,
4342    network_len: M::Offset,
4343    token: WasmPtr<u8, M>,
4344    token_len: M::Offset,
4345    security: Streamsecurity,
4346) -> Errno {
4347    debug!("wasi::port_bridge");
4348    let env = ctx.data();
4349    let memory = env.memory_view(&ctx);
4350    let network = unsafe { get_input_str!(&memory, network, network_len) };
4351    let token = unsafe { get_input_str!(&memory, token, token_len) };
4352    let security = match security {
4353        Streamsecurity::Unencrypted => StreamSecurity::Unencrypted,
4354        Streamsecurity::AnyEncryption => StreamSecurity::AnyEncyption,
4355        Streamsecurity::ClassicEncryption => StreamSecurity::ClassicEncryption,
4356        Streamsecurity::DoubleEncryption => StreamSecurity::DoubleEncryption,
4357        _ => return Errno::Inval,
4358    };
4359
4360    wasi_try!(env
4361        .net()
4362        .bridge(network.as_str(), token.as_str(), security)
4363        .map_err(net_error_into_wasi_err));
4364    Errno::Success
4365}
4366
4367/// ### `port_unbridge()`
4368/// Disconnects from a remote network
4369pub fn port_unbridge(ctx: FunctionEnvMut<'_, WasiEnv>) -> Errno {
4370    debug!("wasi::port_unbridge");
4371    let env = ctx.data();
4372    wasi_try!(env.net().unbridge().map_err(net_error_into_wasi_err));
4373    Errno::Success
4374}
4375
4376/// ### `port_dhcp_acquire()`
4377/// Acquires a set of IP addresses using DHCP
4378pub fn port_dhcp_acquire(ctx: FunctionEnvMut<'_, WasiEnv>) -> Errno {
4379    debug!("wasi::port_dhcp_acquire");
4380    let env = ctx.data();
4381    wasi_try!(env.net().dhcp_acquire().map_err(net_error_into_wasi_err));
4382    Errno::Success
4383}
4384
4385/// ### `port_addr_add()`
4386/// Adds another static address to the local port
4387///
4388/// ## Parameters
4389///
4390/// * `addr` - Address to be added
4391pub fn port_addr_add<M: MemorySize>(
4392    ctx: FunctionEnvMut<'_, WasiEnv>,
4393    ip: WasmPtr<__wasi_cidr_t, M>,
4394) -> Errno {
4395    debug!("wasi::port_addr_add");
4396    let env = ctx.data();
4397    let memory = env.memory_view(&ctx);
4398    let cidr = wasi_try!(super::state::read_cidr(&memory, ip));
4399    wasi_try!(env
4400        .net()
4401        .ip_add(cidr.ip, cidr.prefix)
4402        .map_err(net_error_into_wasi_err));
4403    Errno::Success
4404}
4405
4406/// ### `port_addr_remove()`
4407/// Removes an address from the local port
4408///
4409/// ## Parameters
4410///
4411/// * `addr` - Address to be removed
4412pub fn port_addr_remove<M: MemorySize>(
4413    ctx: FunctionEnvMut<'_, WasiEnv>,
4414    ip: WasmPtr<__wasi_addr_t, M>,
4415) -> Errno {
4416    debug!("wasi::port_addr_remove");
4417    let env = ctx.data();
4418    let memory = env.memory_view(&ctx);
4419    let ip = wasi_try!(super::state::read_ip(&memory, ip));
4420    wasi_try!(env.net().ip_remove(ip).map_err(net_error_into_wasi_err));
4421    Errno::Success
4422}
4423
4424/// ### `port_addr_clear()`
4425/// Clears all the addresses on the local port
4426pub fn port_addr_clear(ctx: FunctionEnvMut<'_, WasiEnv>) -> Errno {
4427    debug!("wasi::port_addr_clear");
4428    let env = ctx.data();
4429    wasi_try!(env.net().ip_clear().map_err(net_error_into_wasi_err));
4430    Errno::Success
4431}
4432
4433/// ### `port_mac()`
4434/// Returns the MAC address of the local port
4435pub fn port_mac<M: MemorySize>(
4436    ctx: FunctionEnvMut<'_, WasiEnv>,
4437    ret_mac: WasmPtr<__wasi_hardwareaddress_t, M>,
4438) -> Errno {
4439    debug!("wasi::port_mac");
4440    let env = ctx.data();
4441    let memory = env.memory_view(&ctx);
4442    let mac = wasi_try!(env.net().mac().map_err(net_error_into_wasi_err));
4443    let mac = __wasi_hardwareaddress_t { octs: mac };
4444    wasi_try_mem!(ret_mac.write(&memory, mac));
4445    Errno::Success
4446}
4447
4448/// ### `port_ip_list()`
4449/// Returns a list of all the addresses owned by the local port
4450/// This function fills the output buffer as much as possible.
4451/// If the buffer is not big enough then the naddrs address will be
4452/// filled with the buffer size needed and the EOVERFLOW will be returned
4453///
4454/// ## Parameters
4455///
4456/// * `addrs` - The buffer where addresses will be stored
4457///
4458/// ## Return
4459///
4460/// The number of addresses returned.
4461pub fn port_addr_list<M: MemorySize>(
4462    ctx: FunctionEnvMut<'_, WasiEnv>,
4463    addrs: WasmPtr<__wasi_cidr_t, M>,
4464    naddrs: WasmPtr<M::Offset, M>,
4465) -> Errno {
4466    debug!("wasi::port_addr_list");
4467    let env = ctx.data();
4468    let memory = env.memory_view(&ctx);
4469    let max_addrs = wasi_try_mem!(naddrs.read(&memory));
4470    let max_addrs: u64 = wasi_try!(max_addrs.try_into().map_err(|_| Errno::Overflow));
4471    let ref_addrs =
4472        wasi_try_mem!(addrs.slice(&memory, wasi_try!(to_offset::<M>(max_addrs as usize))));
4473
4474    let addrs = wasi_try!(env.net().ip_list().map_err(net_error_into_wasi_err));
4475
4476    let addrs_len: M::Offset = wasi_try!(addrs.len().try_into().map_err(|_| Errno::Overflow));
4477    wasi_try_mem!(naddrs.write(&memory, addrs_len));
4478    if addrs.len() as u64 > max_addrs {
4479        return Errno::Overflow;
4480    }
4481
4482    for n in 0..addrs.len() {
4483        let nip = ref_addrs.index(n as u64);
4484        super::state::write_cidr(&memory, nip.as_ptr::<M>(), *addrs.get(n).unwrap());
4485    }
4486
4487    Errno::Success
4488}
4489
4490/// ### `port_gateway_set()`
4491/// Adds a default gateway to the port
4492///
4493/// ## Parameters
4494///
4495/// * `addr` - Address of the default gateway
4496pub fn port_gateway_set<M: MemorySize>(
4497    ctx: FunctionEnvMut<'_, WasiEnv>,
4498    ip: WasmPtr<__wasi_addr_t, M>,
4499) -> Errno {
4500    debug!("wasi::port_gateway_set");
4501    let env = ctx.data();
4502    let memory = env.memory_view(&ctx);
4503    let ip = wasi_try!(super::state::read_ip(&memory, ip));
4504
4505    wasi_try!(env.net().gateway_set(ip).map_err(net_error_into_wasi_err));
4506    Errno::Success
4507}
4508
4509/// ### `port_route_add()`
4510/// Adds a new route to the local port
4511pub fn port_route_add<M: MemorySize>(
4512    ctx: FunctionEnvMut<'_, WasiEnv>,
4513    cidr: WasmPtr<__wasi_cidr_t, M>,
4514    via_router: WasmPtr<__wasi_addr_t, M>,
4515    preferred_until: WasmPtr<OptionTimestamp, M>,
4516    expires_at: WasmPtr<OptionTimestamp, M>,
4517) -> Errno {
4518    debug!("wasi::port_route_add");
4519    let env = ctx.data();
4520    let memory = env.memory_view(&ctx);
4521    let cidr = wasi_try!(super::state::read_cidr(&memory, cidr));
4522    let via_router = wasi_try!(super::state::read_ip(&memory, via_router));
4523    let preferred_until = wasi_try_mem!(preferred_until.read(&memory));
4524    let preferred_until = match preferred_until.tag {
4525        OptionTag::None => None,
4526        OptionTag::Some => Some(Duration::from_nanos(preferred_until.u)),
4527        _ => return Errno::Inval,
4528    };
4529    let expires_at = wasi_try_mem!(expires_at.read(&memory));
4530    let expires_at = match expires_at.tag {
4531        OptionTag::None => None,
4532        OptionTag::Some => Some(Duration::from_nanos(expires_at.u)),
4533        _ => return Errno::Inval,
4534    };
4535
4536    wasi_try!(env
4537        .net()
4538        .route_add(cidr, via_router, preferred_until, expires_at)
4539        .map_err(net_error_into_wasi_err));
4540    Errno::Success
4541}
4542
4543/// ### `port_route_remove()`
4544/// Removes an existing route from the local port
4545pub fn port_route_remove<M: MemorySize>(
4546    ctx: FunctionEnvMut<'_, WasiEnv>,
4547    ip: WasmPtr<__wasi_addr_t, M>,
4548) -> Errno {
4549    debug!("wasi::port_route_remove");
4550    let env = ctx.data();
4551    let memory = env.memory_view(&ctx);
4552    let ip = wasi_try!(super::state::read_ip(&memory, ip));
4553    wasi_try!(env.net().route_remove(ip).map_err(net_error_into_wasi_err));
4554    Errno::Success
4555}
4556
4557/// ### `port_route_clear()`
4558/// Clears all the routes in the local port
4559pub fn port_route_clear(ctx: FunctionEnvMut<'_, WasiEnv>) -> Errno {
4560    debug!("wasi::port_route_clear");
4561    let env = ctx.data();
4562    wasi_try!(env.net().route_clear().map_err(net_error_into_wasi_err));
4563    Errno::Success
4564}
4565
4566/// ### `port_route_list()`
4567/// Returns a list of all the routes owned by the local port
4568/// This function fills the output buffer as much as possible.
4569/// If the buffer is too small this will return EOVERFLOW and
4570/// fill nroutes with the size of the buffer needed.
4571///
4572/// ## Parameters
4573///
4574/// * `routes` - The buffer where routes will be stored
4575pub fn port_route_list<M: MemorySize>(
4576    ctx: FunctionEnvMut<'_, WasiEnv>,
4577    routes: WasmPtr<Route, M>,
4578    nroutes: WasmPtr<M::Offset, M>,
4579) -> Errno {
4580    debug!("wasi::port_route_list");
4581    let env = ctx.data();
4582    let memory = env.memory_view(&ctx);
4583    let nroutes = nroutes.deref(&memory);
4584    let max_routes: usize = wasi_try!(wasi_try_mem!(nroutes.read())
4585        .try_into()
4586        .map_err(|_| Errno::Inval));
4587    let ref_routes = wasi_try_mem!(routes.slice(&memory, wasi_try!(to_offset::<M>(max_routes))));
4588
4589    let routes = wasi_try!(env.net().route_list().map_err(net_error_into_wasi_err));
4590
4591    let routes_len: M::Offset = wasi_try!(routes.len().try_into().map_err(|_| Errno::Inval));
4592    wasi_try_mem!(nroutes.write(routes_len));
4593    if routes.len() > max_routes {
4594        return Errno::Overflow;
4595    }
4596
4597    for n in 0..routes.len() {
4598        let nroute = ref_routes.index(n as u64);
4599        super::state::write_route(
4600            &memory,
4601            nroute.as_ptr::<M>(),
4602            routes.get(n).unwrap().clone(),
4603        );
4604    }
4605
4606    Errno::Success
4607}
4608
4609/// ### `sock_shutdown()`
4610/// Shut down socket send and receive channels.
4611/// Note: This is similar to `shutdown` in POSIX.
4612///
4613/// ## Parameters
4614///
4615/// * `how` - Which channels on the socket to shut down.
4616pub fn sock_shutdown(ctx: FunctionEnvMut<'_, WasiEnv>, sock: WasiFd, how: SdFlags) -> Errno {
4617    debug!("wasi::sock_shutdown");
4618
4619    let both = __WASI_SHUT_RD | __WASI_SHUT_WR;
4620    let how = match how {
4621        __WASI_SHUT_RD => std::net::Shutdown::Read,
4622        __WASI_SHUT_WR => std::net::Shutdown::Write,
4623        a if a == both => std::net::Shutdown::Both,
4624        _ => return Errno::Inval,
4625    };
4626
4627    wasi_try!(__sock_actor_mut(
4628        &ctx,
4629        sock,
4630        Rights::SOCK_SHUTDOWN,
4631        |socket| { socket.shutdown(how) }
4632    ));
4633
4634    Errno::Success
4635}
4636
4637/// ### `sock_status()`
4638/// Returns the current status of a socket
4639pub fn sock_status<M: MemorySize>(
4640    ctx: FunctionEnvMut<'_, WasiEnv>,
4641    sock: WasiFd,
4642    ret_status: WasmPtr<Sockstatus, M>,
4643) -> Errno {
4644    debug!("wasi::sock_status");
4645
4646    let status = wasi_try!(__sock_actor(&ctx, sock, Rights::empty(), |socket| {
4647        socket.status()
4648    }));
4649
4650    use super::state::WasiSocketStatus;
4651    let status = match status {
4652        WasiSocketStatus::Opening => Sockstatus::Opening,
4653        WasiSocketStatus::Opened => Sockstatus::Opened,
4654        WasiSocketStatus::Closed => Sockstatus::Closed,
4655        WasiSocketStatus::Failed => Sockstatus::Failed,
4656    };
4657
4658    let env = ctx.data();
4659    let memory = env.memory_view(&ctx);
4660    wasi_try_mem!(ret_status.write(&memory, status));
4661
4662    Errno::Success
4663}
4664
4665/// ### `sock_addr_local()`
4666/// Returns the local address to which the socket is bound.
4667///
4668/// Note: This is similar to `getsockname` in POSIX
4669///
4670/// When successful, the contents of the output buffer consist of an IP address,
4671/// either IP4 or IP6.
4672///
4673/// ## Parameters
4674///
4675/// * `fd` - Socket that the address is bound to
4676pub fn sock_addr_local<M: MemorySize>(
4677    ctx: FunctionEnvMut<'_, WasiEnv>,
4678    sock: WasiFd,
4679    ret_addr: WasmPtr<__wasi_addr_port_t, M>,
4680) -> Errno {
4681    debug!("wasi::sock_addr_local");
4682
4683    let addr = wasi_try!(__sock_actor(&ctx, sock, Rights::empty(), |socket| {
4684        socket.addr_local()
4685    }));
4686    let memory = ctx.data().memory_view(&ctx);
4687    wasi_try!(super::state::write_ip_port(
4688        &memory,
4689        ret_addr,
4690        addr.ip(),
4691        addr.port()
4692    ));
4693    Errno::Success
4694}
4695
4696/// ### `sock_addr_peer()`
4697/// Returns the remote address to which the socket is connected to.
4698///
4699/// Note: This is similar to `getpeername` in POSIX
4700///
4701/// When successful, the contents of the output buffer consist of an IP address,
4702/// either IP4 or IP6.
4703///
4704/// ## Parameters
4705///
4706/// * `fd` - Socket that the address is bound to
4707pub fn sock_addr_peer<M: MemorySize>(
4708    ctx: FunctionEnvMut<'_, WasiEnv>,
4709    sock: WasiFd,
4710    ro_addr: WasmPtr<__wasi_addr_port_t, M>,
4711) -> Errno {
4712    debug!("wasi::sock_addr_peer");
4713
4714    let env = ctx.data();
4715    let addr = wasi_try!(__sock_actor(&ctx, sock, Rights::empty(), |socket| {
4716        socket.addr_peer()
4717    }));
4718    let memory = env.memory_view(&ctx);
4719    wasi_try!(super::state::write_ip_port(
4720        &memory,
4721        ro_addr,
4722        addr.ip(),
4723        addr.port()
4724    ));
4725    Errno::Success
4726}
4727
4728/// ### `sock_open()`
4729/// Create an endpoint for communication.
4730///
4731/// creates an endpoint for communication and returns a file descriptor
4732/// tor that refers to that endpoint. The file descriptor returned by a successful
4733/// call will be the lowest-numbered file descriptor not currently open
4734/// for the process.
4735///
4736/// Note: This is similar to `socket` in POSIX using PF_INET
4737///
4738/// ## Parameters
4739///
4740/// * `af` - Address family
4741/// * `socktype` - Socket type, either datagram or stream
4742/// * `sock_proto` - Socket protocol
4743///
4744/// ## Return
4745///
4746/// The file descriptor of the socket that has been opened.
4747pub fn sock_open<M: MemorySize>(
4748    ctx: FunctionEnvMut<'_, WasiEnv>,
4749    af: Addressfamily,
4750    ty: Socktype,
4751    pt: SockProto,
4752    ro_sock: WasmPtr<WasiFd, M>,
4753) -> Errno {
4754    debug!("wasi::sock_open");
4755
4756    let env = ctx.data();
4757    let (memory, state, mut inodes) = env.get_memory_and_wasi_state_and_inodes_mut(&ctx, 0);
4758
4759    let kind = match ty {
4760        Socktype::Stream | Socktype::Dgram => Kind::Socket {
4761            socket: InodeSocket::new(InodeSocketKind::PreSocket {
4762                family: af,
4763                ty,
4764                pt,
4765                addr: None,
4766                only_v6: false,
4767                reuse_port: false,
4768                reuse_addr: false,
4769                send_buf_size: None,
4770                recv_buf_size: None,
4771                send_timeout: None,
4772                recv_timeout: None,
4773                connect_timeout: None,
4774                accept_timeout: None,
4775            }),
4776        },
4777        _ => return Errno::Notsup,
4778    };
4779
4780    let inode = state.fs.create_inode_with_default_stat(
4781        inodes.deref_mut(),
4782        kind,
4783        false,
4784        "socket".to_string(),
4785    );
4786    let rights = Rights::all_socket();
4787    let fd = wasi_try!(state
4788        .fs
4789        .create_fd(rights, rights, Fdflags::empty(), 0, inode));
4790
4791    wasi_try_mem!(ro_sock.write(&memory, fd));
4792
4793    Errno::Success
4794}
4795
4796/// ### `sock_set_opt_flag()`
4797/// Sets a particular socket setting
4798/// Note: This is similar to `setsockopt` in POSIX for SO_REUSEADDR
4799///
4800/// ## Parameters
4801///
4802/// * `fd` - Socket descriptor
4803/// * `sockopt` - Socket option to be set
4804/// * `flag` - Value to set the option to
4805pub fn sock_set_opt_flag(
4806    ctx: FunctionEnvMut<'_, WasiEnv>,
4807    sock: WasiFd,
4808    opt: Sockoption,
4809    flag: Bool,
4810) -> Errno {
4811    debug!("wasi::sock_set_opt_flag(ty={})", opt);
4812
4813    let flag = match flag {
4814        Bool::False => false,
4815        Bool::True => true,
4816        _ => return Errno::Inval,
4817    };
4818
4819    let option: super::state::WasiSocketOption = opt.into();
4820    wasi_try!(__sock_actor_mut(&ctx, sock, Rights::empty(), |socket| {
4821        socket.set_opt_flag(option, flag)
4822    }));
4823    Errno::Success
4824}
4825
4826/// ### `sock_get_opt_flag()`
4827/// Retrieve status of particular socket seting
4828/// Note: This is similar to `getsockopt` in POSIX for SO_REUSEADDR
4829///
4830/// ## Parameters
4831///
4832/// * `fd` - Socket descriptor
4833/// * `sockopt` - Socket option to be retrieved
4834pub fn sock_get_opt_flag<M: MemorySize>(
4835    ctx: FunctionEnvMut<'_, WasiEnv>,
4836    sock: WasiFd,
4837    opt: Sockoption,
4838    ret_flag: WasmPtr<Bool, M>,
4839) -> Errno {
4840    debug!("wasi::sock_get_opt_flag(ty={})", opt);
4841    let env = ctx.data();
4842    let memory = env.memory_view(&ctx);
4843
4844    let option: super::state::WasiSocketOption = opt.into();
4845    let flag = wasi_try!(__sock_actor(&ctx, sock, Rights::empty(), |socket| {
4846        socket.get_opt_flag(option)
4847    }));
4848    let flag = match flag {
4849        false => Bool::False,
4850        true => Bool::True,
4851    };
4852
4853    wasi_try_mem!(ret_flag.write(&memory, flag));
4854
4855    Errno::Success
4856}
4857
4858/// ### `sock_set_opt_time()`
4859/// Sets one of the times the socket
4860///
4861/// ## Parameters
4862///
4863/// * `fd` - Socket descriptor
4864/// * `sockopt` - Socket option to be set
4865/// * `time` - Value to set the time to
4866pub fn sock_set_opt_time<M: MemorySize>(
4867    ctx: FunctionEnvMut<'_, WasiEnv>,
4868    sock: WasiFd,
4869    opt: Sockoption,
4870    time: WasmPtr<OptionTimestamp, M>,
4871) -> Errno {
4872    debug!("wasi::sock_set_opt_time(ty={})", opt);
4873
4874    let env = ctx.data();
4875    let memory = env.memory_view(&ctx);
4876    let time = wasi_try_mem!(time.read(&memory));
4877    let time = match time.tag {
4878        OptionTag::None => None,
4879        OptionTag::Some => Some(Duration::from_nanos(time.u)),
4880        _ => return Errno::Inval,
4881    };
4882
4883    let ty = match opt {
4884        Sockoption::RecvTimeout => wasmer_vnet::TimeType::ReadTimeout,
4885        Sockoption::SendTimeout => wasmer_vnet::TimeType::WriteTimeout,
4886        Sockoption::ConnectTimeout => wasmer_vnet::TimeType::ConnectTimeout,
4887        Sockoption::AcceptTimeout => wasmer_vnet::TimeType::AcceptTimeout,
4888        Sockoption::Linger => wasmer_vnet::TimeType::Linger,
4889        _ => return Errno::Inval,
4890    };
4891
4892    let option: super::state::WasiSocketOption = opt.into();
4893    wasi_try!(__sock_actor_mut(&ctx, sock, Rights::empty(), |socket| {
4894        socket.set_opt_time(ty, time)
4895    }));
4896    Errno::Success
4897}
4898
4899/// ### `sock_get_opt_time()`
4900/// Retrieve one of the times on the socket
4901///
4902/// ## Parameters
4903///
4904/// * `fd` - Socket descriptor
4905/// * `sockopt` - Socket option to be retrieved
4906pub fn sock_get_opt_time<M: MemorySize>(
4907    ctx: FunctionEnvMut<'_, WasiEnv>,
4908    sock: WasiFd,
4909    opt: Sockoption,
4910    ret_time: WasmPtr<OptionTimestamp, M>,
4911) -> Errno {
4912    debug!("wasi::sock_get_opt_time(ty={})", opt);
4913    let env = ctx.data();
4914    let memory = env.memory_view(&ctx);
4915
4916    let ty = match opt {
4917        Sockoption::RecvTimeout => wasmer_vnet::TimeType::ReadTimeout,
4918        Sockoption::SendTimeout => wasmer_vnet::TimeType::WriteTimeout,
4919        Sockoption::ConnectTimeout => wasmer_vnet::TimeType::ConnectTimeout,
4920        Sockoption::AcceptTimeout => wasmer_vnet::TimeType::AcceptTimeout,
4921        Sockoption::Linger => wasmer_vnet::TimeType::Linger,
4922        _ => return Errno::Inval,
4923    };
4924
4925    let time = wasi_try!(__sock_actor(&ctx, sock, Rights::empty(), |socket| {
4926        socket.opt_time(ty)
4927    }));
4928    let time = match time {
4929        None => OptionTimestamp {
4930            tag: OptionTag::None,
4931            u: 0,
4932        },
4933        Some(timeout) => OptionTimestamp {
4934            tag: OptionTag::Some,
4935            u: timeout.as_nanos() as Timestamp,
4936        },
4937    };
4938
4939    wasi_try_mem!(ret_time.write(&memory, time));
4940
4941    Errno::Success
4942}
4943
4944/// ### `sock_set_opt_size()
4945/// Set size of particular option for this socket
4946/// Note: This is similar to `setsockopt` in POSIX for SO_RCVBUF
4947///
4948/// ## Parameters
4949///
4950/// * `fd` - Socket descriptor
4951/// * `opt` - Socket option to be set
4952/// * `size` - Buffer size
4953pub fn sock_set_opt_size(
4954    ctx: FunctionEnvMut<'_, WasiEnv>,
4955    sock: WasiFd,
4956    opt: Sockoption,
4957    size: Filesize,
4958) -> Errno {
4959    debug!("wasi::sock_set_opt_size(ty={})", opt);
4960
4961    let ty = match opt {
4962        Sockoption::RecvTimeout => wasmer_vnet::TimeType::ReadTimeout,
4963        Sockoption::SendTimeout => wasmer_vnet::TimeType::WriteTimeout,
4964        Sockoption::ConnectTimeout => wasmer_vnet::TimeType::ConnectTimeout,
4965        Sockoption::AcceptTimeout => wasmer_vnet::TimeType::AcceptTimeout,
4966        Sockoption::Linger => wasmer_vnet::TimeType::Linger,
4967        _ => return Errno::Inval,
4968    };
4969
4970    let option: super::state::WasiSocketOption = opt.into();
4971    wasi_try!(__sock_actor_mut(&ctx, sock, Rights::empty(), |socket| {
4972        match opt {
4973            Sockoption::RecvBufSize => socket.set_recv_buf_size(size as usize),
4974            Sockoption::SendBufSize => socket.set_send_buf_size(size as usize),
4975            Sockoption::Ttl => socket.set_ttl(size as u32),
4976            Sockoption::MulticastTtlV4 => socket.set_multicast_ttl_v4(size as u32),
4977            _ => Err(Errno::Inval),
4978        }
4979    }));
4980    Errno::Success
4981}
4982
4983/// ### `sock_get_opt_size()`
4984/// Retrieve the size of particular option for this socket
4985/// Note: This is similar to `getsockopt` in POSIX for SO_RCVBUF
4986///
4987/// ## Parameters
4988///
4989/// * `fd` - Socket descriptor
4990/// * `sockopt` - Socket option to be retrieved
4991pub fn sock_get_opt_size<M: MemorySize>(
4992    ctx: FunctionEnvMut<'_, WasiEnv>,
4993    sock: WasiFd,
4994    opt: Sockoption,
4995    ret_size: WasmPtr<Filesize, M>,
4996) -> Errno {
4997    debug!("wasi::sock_get_opt_size(ty={})", opt);
4998    let env = ctx.data();
4999    let memory = env.memory_view(&ctx);
5000
5001    let size = wasi_try!(__sock_actor(&ctx, sock, Rights::empty(), |socket| {
5002        match opt {
5003            Sockoption::RecvBufSize => socket.recv_buf_size().map(|a| a as Filesize),
5004            Sockoption::SendBufSize => socket.send_buf_size().map(|a| a as Filesize),
5005            Sockoption::Ttl => socket.ttl().map(|a| a as Filesize),
5006            Sockoption::MulticastTtlV4 => socket.multicast_ttl_v4().map(|a| a as Filesize),
5007            _ => Err(Errno::Inval),
5008        }
5009    }));
5010    wasi_try_mem!(ret_size.write(&memory, size));
5011
5012    Errno::Success
5013}
5014
5015/// ### `sock_join_multicast_v4()`
5016/// Joins a particular multicast IPv4 group
5017///
5018/// ## Parameters
5019///
5020/// * `fd` - Socket descriptor
5021/// * `multiaddr` - Multicast group to joined
5022/// * `interface` - Interface that will join
5023pub fn sock_join_multicast_v4<M: MemorySize>(
5024    ctx: FunctionEnvMut<'_, WasiEnv>,
5025    sock: WasiFd,
5026    multiaddr: WasmPtr<__wasi_addr_ip4_t, M>,
5027    iface: WasmPtr<__wasi_addr_ip4_t, M>,
5028) -> Errno {
5029    debug!("wasi::sock_join_multicast_v4");
5030
5031    let env = ctx.data();
5032    let memory = env.memory_view(&ctx);
5033    let multiaddr = wasi_try!(super::state::read_ip_v4(&memory, multiaddr));
5034    let iface = wasi_try!(super::state::read_ip_v4(&memory, iface));
5035    wasi_try!(__sock_actor_mut(&ctx, sock, Rights::empty(), |socket| {
5036        socket.join_multicast_v4(multiaddr, iface)
5037    }));
5038    Errno::Success
5039}
5040
5041/// ### `sock_leave_multicast_v4()`
5042/// Leaves a particular multicast IPv4 group
5043///
5044/// ## Parameters
5045///
5046/// * `fd` - Socket descriptor
5047/// * `multiaddr` - Multicast group to leave
5048/// * `interface` - Interface that will left
5049pub fn sock_leave_multicast_v4<M: MemorySize>(
5050    ctx: FunctionEnvMut<'_, WasiEnv>,
5051    sock: WasiFd,
5052    multiaddr: WasmPtr<__wasi_addr_ip4_t, M>,
5053    iface: WasmPtr<__wasi_addr_ip4_t, M>,
5054) -> Errno {
5055    debug!("wasi::sock_leave_multicast_v4");
5056
5057    let env = ctx.data();
5058    let memory = env.memory_view(&ctx);
5059    let multiaddr = wasi_try!(super::state::read_ip_v4(&memory, multiaddr));
5060    let iface = wasi_try!(super::state::read_ip_v4(&memory, iface));
5061    wasi_try!(__sock_actor_mut(&ctx, sock, Rights::empty(), |socket| {
5062        socket.leave_multicast_v4(multiaddr, iface)
5063    }));
5064    Errno::Success
5065}
5066
5067/// ### `sock_join_multicast_v6()`
5068/// Joins a particular multicast IPv6 group
5069///
5070/// ## Parameters
5071///
5072/// * `fd` - Socket descriptor
5073/// * `multiaddr` - Multicast group to joined
5074/// * `interface` - Interface that will join
5075pub fn sock_join_multicast_v6<M: MemorySize>(
5076    ctx: FunctionEnvMut<'_, WasiEnv>,
5077    sock: WasiFd,
5078    multiaddr: WasmPtr<__wasi_addr_ip6_t, M>,
5079    iface: u32,
5080) -> Errno {
5081    debug!("wasi::sock_join_multicast_v6");
5082
5083    let env = ctx.data();
5084    let memory = env.memory_view(&ctx);
5085    let multiaddr = wasi_try!(super::state::read_ip_v6(&memory, multiaddr));
5086    wasi_try!(__sock_actor_mut(&ctx, sock, Rights::empty(), |socket| {
5087        socket.join_multicast_v6(multiaddr, iface)
5088    }));
5089    Errno::Success
5090}
5091
5092/// ### `sock_leave_multicast_v6()`
5093/// Leaves a particular multicast IPv6 group
5094///
5095/// ## Parameters
5096///
5097/// * `fd` - Socket descriptor
5098/// * `multiaddr` - Multicast group to leave
5099/// * `interface` - Interface that will left
5100pub fn sock_leave_multicast_v6<M: MemorySize>(
5101    ctx: FunctionEnvMut<'_, WasiEnv>,
5102    sock: WasiFd,
5103    multiaddr: WasmPtr<__wasi_addr_ip6_t, M>,
5104    iface: u32,
5105) -> Errno {
5106    debug!("wasi::sock_leave_multicast_v6");
5107
5108    let env = ctx.data();
5109    let memory = env.memory_view(&ctx);
5110    let multiaddr = wasi_try!(super::state::read_ip_v6(&memory, multiaddr));
5111    wasi_try!(__sock_actor_mut(&ctx, sock, Rights::empty(), |socket| {
5112        socket.leave_multicast_v6(multiaddr, iface)
5113    }));
5114    Errno::Success
5115}
5116
5117/// ### `sock_bind()`
5118/// Bind a socket
5119/// Note: This is similar to `bind` in POSIX using PF_INET
5120///
5121/// ## Parameters
5122///
5123/// * `fd` - File descriptor of the socket to be bind
5124/// * `addr` - Address to bind the socket to
5125pub fn sock_bind<M: MemorySize>(
5126    ctx: FunctionEnvMut<'_, WasiEnv>,
5127    sock: WasiFd,
5128    addr: WasmPtr<__wasi_addr_port_t, M>,
5129) -> Errno {
5130    debug!("wasi::sock_bind");
5131
5132    let env = ctx.data();
5133    let memory = env.memory_view(&ctx);
5134    let addr = wasi_try!(super::state::read_ip_port(&memory, addr));
5135    let addr = SocketAddr::new(addr.0, addr.1);
5136    wasi_try!(__sock_upgrade(&ctx, sock, Rights::SOCK_BIND, |socket| {
5137        socket.bind(env.net(), addr)
5138    }));
5139    Errno::Success
5140}
5141
5142/// ### `sock_listen()`
5143/// Listen for connections on a socket
5144///
5145/// Polling the socket handle will wait until a connection
5146/// attempt is made
5147///
5148/// Note: This is similar to `listen`
5149///
5150/// ## Parameters
5151///
5152/// * `fd` - File descriptor of the socket to be bind
5153/// * `backlog` - Maximum size of the queue for pending connections
5154pub fn sock_listen<M: MemorySize>(
5155    ctx: FunctionEnvMut<'_, WasiEnv>,
5156    sock: WasiFd,
5157    backlog: M::Offset,
5158) -> Errno {
5159    debug!("wasi::sock_listen");
5160
5161    let env = ctx.data();
5162    let backlog: usize = wasi_try!(backlog.try_into().map_err(|_| Errno::Inval));
5163    wasi_try!(__sock_upgrade(&ctx, sock, Rights::SOCK_BIND, |socket| {
5164        socket.listen(env.net(), backlog)
5165    }));
5166    Errno::Success
5167}
5168
5169/// ### `sock_accept()`
5170/// Accept a new incoming connection.
5171/// Note: This is similar to `accept` in POSIX.
5172///
5173/// ## Parameters
5174///
5175/// * `fd` - The listening socket.
5176/// * `flags` - The desired values of the file descriptor flags.
5177///
5178/// ## Return
5179///
5180/// New socket connection
5181pub fn sock_accept<M: MemorySize>(
5182    ctx: FunctionEnvMut<'_, WasiEnv>,
5183    sock: WasiFd,
5184    fd_flags: Fdflags,
5185    ro_fd: WasmPtr<WasiFd, M>,
5186    ro_addr: WasmPtr<__wasi_addr_port_t, M>,
5187) -> Result<Errno, WasiError> {
5188    debug!("wasi::sock_accept");
5189
5190    let env = ctx.data();
5191    let (child, addr) = {
5192        let mut ret;
5193        let (_, state) = env.get_memory_and_wasi_state(&ctx, 0);
5194        loop {
5195            wasi_try_ok!(
5196                match __sock_actor(&ctx, sock, Rights::SOCK_ACCEPT, |socket| socket
5197                    .accept_timeout(fd_flags, Duration::from_millis(5)))
5198                {
5199                    Ok(a) => {
5200                        ret = a;
5201                        break;
5202                    }
5203                    Err(Errno::Timedout) => {
5204                        env.yield_now()?;
5205                        continue;
5206                    }
5207                    Err(Errno::Again) => {
5208                        env.sleep(Duration::from_millis(5))?;
5209                        continue;
5210                    }
5211                    Err(err) => Err(err),
5212                }
5213            );
5214        }
5215        ret
5216    };
5217
5218    let (memory, state, mut inodes) = env.get_memory_and_wasi_state_and_inodes_mut(&ctx, 0);
5219
5220    let kind = Kind::Socket {
5221        socket: InodeSocket::new(InodeSocketKind::TcpStream(child)),
5222    };
5223    let inode = state.fs.create_inode_with_default_stat(
5224        inodes.deref_mut(),
5225        kind,
5226        false,
5227        "socket".to_string(),
5228    );
5229
5230    let rights = Rights::all_socket();
5231    let fd = wasi_try_ok!(state
5232        .fs
5233        .create_fd(rights, rights, Fdflags::empty(), 0, inode));
5234
5235    wasi_try_mem_ok!(ro_fd.write(&memory, fd));
5236    wasi_try_ok!(super::state::write_ip_port(
5237        &memory,
5238        ro_addr,
5239        addr.ip(),
5240        addr.port()
5241    ));
5242
5243    Ok(Errno::Success)
5244}
5245
5246/// ### `sock_connect()`
5247/// Initiate a connection on a socket to the specified address
5248///
5249/// Polling the socket handle will wait for data to arrive or for
5250/// the socket status to change which can be queried via 'sock_status'
5251///
5252/// Note: This is similar to `connect` in POSIX
5253///
5254/// ## Parameters
5255///
5256/// * `fd` - Socket descriptor
5257/// * `addr` - Address of the socket to connect to
5258pub fn sock_connect<M: MemorySize>(
5259    ctx: FunctionEnvMut<'_, WasiEnv>,
5260    sock: WasiFd,
5261    addr: WasmPtr<__wasi_addr_port_t, M>,
5262) -> Errno {
5263    debug!("wasi::sock_connect");
5264
5265    let env = ctx.data();
5266    let memory = env.memory_view(&ctx);
5267    let addr = wasi_try!(super::state::read_ip_port(&memory, addr));
5268    let addr = SocketAddr::new(addr.0, addr.1);
5269    wasi_try!(__sock_upgrade(&ctx, sock, Rights::SOCK_CONNECT, |socket| {
5270        socket.connect(env.net(), addr)
5271    }));
5272    Errno::Success
5273}
5274
5275/// ### `sock_recv()`
5276/// Receive a message from a socket.
5277/// Note: This is similar to `recv` in POSIX, though it also supports reading
5278/// the data into multiple buffers in the manner of `readv`.
5279///
5280/// ## Parameters
5281///
5282/// * `ri_data` - List of scatter/gather vectors to which to store data.
5283/// * `ri_flags` - Message flags.
5284///
5285/// ## Return
5286///
5287/// Number of bytes stored in ri_data and message flags.
5288pub fn sock_recv<M: MemorySize>(
5289    ctx: FunctionEnvMut<'_, WasiEnv>,
5290    sock: WasiFd,
5291    ri_data: WasmPtr<__wasi_iovec_t<M>, M>,
5292    ri_data_len: M::Offset,
5293    _ri_flags: RiFlags,
5294    ro_data_len: WasmPtr<M::Offset, M>,
5295    ro_flags: WasmPtr<RoFlags, M>,
5296) -> Result<Errno, WasiError> {
5297    debug!("wasi::sock_recv");
5298
5299    let env = ctx.data();
5300    let memory = env.memory_view(&ctx);
5301    let iovs_arr = wasi_try_mem_ok!(ri_data.slice(&memory, ri_data_len));
5302
5303    let bytes_read = wasi_try_ok!(__sock_actor_mut(&ctx, sock, Rights::SOCK_RECV, |socket| {
5304        socket.recv(&memory, iovs_arr)
5305    }));
5306    let bytes_read: M::Offset = wasi_try_ok!(bytes_read.try_into().map_err(|_| Errno::Overflow));
5307
5308    wasi_try_mem_ok!(ro_flags.write(&memory, 0));
5309    wasi_try_mem_ok!(ro_data_len.write(&memory, bytes_read));
5310
5311    Ok(Errno::Success)
5312}
5313
5314/// ### `sock_recv_from()`
5315/// Receive a message and its peer address from a socket.
5316/// Note: This is similar to `recvfrom` in POSIX, though it also supports reading
5317/// the data into multiple buffers in the manner of `readv`.
5318///
5319/// ## Parameters
5320///
5321/// * `ri_data` - List of scatter/gather vectors to which to store data.
5322/// * `ri_flags` - Message flags.
5323///
5324/// ## Return
5325///
5326/// Number of bytes stored in ri_data and message flags.
5327pub fn sock_recv_from<M: MemorySize>(
5328    ctx: FunctionEnvMut<'_, WasiEnv>,
5329    sock: WasiFd,
5330    ri_data: WasmPtr<__wasi_iovec_t<M>, M>,
5331    ri_data_len: M::Offset,
5332    _ri_flags: RiFlags,
5333    ro_data_len: WasmPtr<M::Offset, M>,
5334    ro_flags: WasmPtr<RoFlags, M>,
5335    ro_addr: WasmPtr<__wasi_addr_port_t, M>,
5336) -> Result<Errno, WasiError> {
5337    debug!("wasi::sock_recv_from");
5338
5339    let env = ctx.data();
5340    let memory = env.memory_view(&ctx);
5341    let iovs_arr = wasi_try_mem_ok!(ri_data.slice(&memory, ri_data_len));
5342
5343    let bytes_read = wasi_try_ok!(__sock_actor_mut(
5344        &ctx,
5345        sock,
5346        Rights::SOCK_RECV_FROM,
5347        |socket| { socket.recv_from(&memory, iovs_arr, ro_addr) }
5348    ));
5349    let bytes_read: M::Offset = wasi_try_ok!(bytes_read.try_into().map_err(|_| Errno::Overflow));
5350
5351    wasi_try_mem_ok!(ro_flags.write(&memory, 0));
5352    wasi_try_mem_ok!(ro_data_len.write(&memory, bytes_read));
5353
5354    Ok(Errno::Success)
5355}
5356
5357/// ### `sock_send()`
5358/// Send a message on a socket.
5359/// Note: This is similar to `send` in POSIX, though it also supports writing
5360/// the data from multiple buffers in the manner of `writev`.
5361///
5362/// ## Parameters
5363///
5364/// * `si_data` - List of scatter/gather vectors to which to retrieve data
5365/// * `si_flags` - Message flags.
5366///
5367/// ## Return
5368///
5369/// Number of bytes transmitted.
5370pub fn sock_send<M: MemorySize>(
5371    ctx: FunctionEnvMut<'_, WasiEnv>,
5372    sock: WasiFd,
5373    si_data: WasmPtr<__wasi_ciovec_t<M>, M>,
5374    si_data_len: M::Offset,
5375    _si_flags: SiFlags,
5376    ret_data_len: WasmPtr<M::Offset, M>,
5377) -> Result<Errno, WasiError> {
5378    debug!("wasi::sock_send");
5379    let env = ctx.data();
5380
5381    let memory = env.memory_view(&ctx);
5382    let iovs_arr = wasi_try_mem_ok!(si_data.slice(&memory, si_data_len));
5383
5384    let bytes_written = wasi_try_ok!(__sock_actor_mut(&ctx, sock, Rights::SOCK_SEND, |socket| {
5385        socket.send(&memory, iovs_arr)
5386    }));
5387
5388    let bytes_written: M::Offset =
5389        wasi_try_ok!(bytes_written.try_into().map_err(|_| Errno::Overflow));
5390    wasi_try_mem_ok!(ret_data_len.write(&memory, bytes_written));
5391
5392    Ok(Errno::Success)
5393}
5394
5395/// ### `sock_send_to()`
5396/// Send a message on a socket to a specific address.
5397/// Note: This is similar to `sendto` in POSIX, though it also supports writing
5398/// the data from multiple buffers in the manner of `writev`.
5399///
5400/// ## Parameters
5401///
5402/// * `si_data` - List of scatter/gather vectors to which to retrieve data
5403/// * `si_flags` - Message flags.
5404/// * `addr` - Address of the socket to send message to
5405///
5406/// ## Return
5407///
5408/// Number of bytes transmitted.
5409pub fn sock_send_to<M: MemorySize>(
5410    ctx: FunctionEnvMut<'_, WasiEnv>,
5411    sock: WasiFd,
5412    si_data: WasmPtr<__wasi_ciovec_t<M>, M>,
5413    si_data_len: M::Offset,
5414    _si_flags: SiFlags,
5415    addr: WasmPtr<__wasi_addr_port_t, M>,
5416    ret_data_len: WasmPtr<M::Offset, M>,
5417) -> Result<Errno, WasiError> {
5418    debug!("wasi::sock_send_to");
5419    let env = ctx.data();
5420
5421    let memory = env.memory_view(&ctx);
5422    let iovs_arr = wasi_try_mem_ok!(si_data.slice(&memory, si_data_len));
5423
5424    let bytes_written = wasi_try_ok!(__sock_actor_mut(
5425        &ctx,
5426        sock,
5427        Rights::SOCK_SEND_TO,
5428        |socket| { socket.send_to::<M>(&memory, iovs_arr, addr) }
5429    ));
5430
5431    let bytes_written: M::Offset =
5432        wasi_try_ok!(bytes_written.try_into().map_err(|_| Errno::Overflow));
5433    wasi_try_mem_ok!(ret_data_len.write(&memory, bytes_written as M::Offset));
5434
5435    Ok(Errno::Success)
5436}
5437
5438/// ### `sock_send_file()`
5439/// Sends the entire contents of a file down a socket
5440///
5441/// ## Parameters
5442///
5443/// * `in_fd` - Open file that has the data to be transmitted
5444/// * `offset` - Offset into the file to start reading at
5445/// * `count` - Number of bytes to be sent
5446///
5447/// ## Return
5448///
5449/// Number of bytes transmitted.
5450pub unsafe fn sock_send_file<M: MemorySize>(
5451    mut ctx: FunctionEnvMut<'_, WasiEnv>,
5452    sock: WasiFd,
5453    in_fd: WasiFd,
5454    offset: Filesize,
5455    mut count: Filesize,
5456    ret_sent: WasmPtr<Filesize, M>,
5457) -> Result<Errno, WasiError> {
5458    debug!("wasi::send_file");
5459    let env = ctx.data();
5460    let (memory, mut state, inodes) = env.get_memory_and_wasi_state_and_inodes(&ctx, 0);
5461
5462    // Set the offset of the file
5463    {
5464        let mut fd_map = state.fs.fd_map.write().unwrap();
5465        let fd_entry = wasi_try_ok!(fd_map.get_mut(&in_fd).ok_or(Errno::Badf));
5466        fd_entry.offset = offset as u64;
5467    }
5468
5469    // Enter a loop that will process all the data
5470    let mut total_written: Filesize = 0;
5471    while (count > 0) {
5472        let mut buf = [0; 4096];
5473        let sub_count = count.min(4096);
5474        count -= sub_count;
5475
5476        let fd_entry = wasi_try_ok!(state.fs.get_fd(in_fd));
5477        let bytes_read = match in_fd {
5478            __WASI_STDIN_FILENO => {
5479                let mut guard = wasi_try_ok!(
5480                    inodes
5481                        .stdin_mut(&state.fs.fd_map)
5482                        .map_err(fs_error_into_wasi_err),
5483                    env
5484                );
5485                if let Some(ref mut stdin) = guard.deref_mut() {
5486                    wasi_try_ok!(stdin.read(&mut buf).map_err(map_io_err))
5487                } else {
5488                    return Ok(Errno::Badf);
5489                }
5490            }
5491            __WASI_STDOUT_FILENO | __WASI_STDERR_FILENO => return Ok(Errno::Inval),
5492            _ => {
5493                if !fd_entry.rights.contains(Rights::FD_READ) {
5494                    // TODO: figure out the error to return when lacking rights
5495                    return Ok(Errno::Access);
5496                }
5497
5498                let offset = fd_entry.offset as usize;
5499                let inode_idx = fd_entry.inode;
5500                let inode = &inodes.arena[inode_idx];
5501
5502                let bytes_read = {
5503                    let mut guard = inode.write();
5504                    let deref_mut = guard.deref_mut();
5505                    match deref_mut {
5506                        Kind::File { handle, .. } => {
5507                            if let Some(handle) = handle {
5508                                wasi_try_ok!(
5509                                    handle
5510                                        .seek(std::io::SeekFrom::Start(offset as u64))
5511                                        .map_err(map_io_err),
5512                                    env
5513                                );
5514                                wasi_try_ok!(handle.read(&mut buf).map_err(map_io_err))
5515                            } else {
5516                                return Ok(Errno::Inval);
5517                            }
5518                        }
5519                        Kind::Socket { socket } => {
5520                            wasi_try_ok!(socket.read(&mut buf).map_err(map_io_err))
5521                        }
5522                        Kind::Pipe { pipe } => {
5523                            wasi_try_ok!(pipe.read(&mut buf).map_err(map_io_err))
5524                        }
5525                        Kind::Dir { .. } | Kind::Root { .. } => {
5526                            return Ok(Errno::Isdir);
5527                        }
5528                        Kind::EventNotifications { .. } => {
5529                            return Ok(Errno::Inval);
5530                        }
5531                        Kind::Symlink { .. } => unimplemented!("Symlinks in wasi::fd_read"),
5532                        Kind::Buffer { buffer } => {
5533                            let mut buf_read = &buffer[offset..];
5534                            wasi_try_ok!(buf_read.read(&mut buf).map_err(map_io_err))
5535                        }
5536                    }
5537                };
5538
5539                // reborrow
5540                let mut fd_map = state.fs.fd_map.write().unwrap();
5541                let fd_entry = wasi_try_ok!(fd_map.get_mut(&in_fd).ok_or(Errno::Badf));
5542                fd_entry.offset += bytes_read as u64;
5543
5544                bytes_read
5545            }
5546        };
5547
5548        // Write it down to the socket
5549        let bytes_written =
5550            wasi_try_ok!(__sock_actor_mut(&ctx, sock, Rights::SOCK_SEND, |socket| {
5551                let buf = (&buf[..]).to_vec();
5552                socket.send_bytes::<M>(Bytes::from(buf))
5553            }));
5554        total_written += bytes_written as u64;
5555    }
5556
5557    wasi_try_mem_ok!(ret_sent.write(&memory, total_written as Filesize));
5558
5559    Ok(Errno::Success)
5560}
5561
5562/// ### `resolve()`
5563/// Resolves a hostname and a port to one or more IP addresses.
5564///
5565/// Note: This is similar to `getaddrinfo` in POSIX
5566///
5567/// When successful, the contents of the output buffer consist of a sequence of
5568/// IPv4 and/or IPv6 addresses. Each address entry consists of a addr_t object.
5569/// This function fills the output buffer as much as possible.
5570///
5571/// ## Parameters
5572///
5573/// * `host` - Host to resolve
5574/// * `port` - Port hint (zero if no hint is supplied)
5575/// * `addrs` - The buffer where addresses will be stored
5576///
5577/// ## Return
5578///
5579/// The number of IP addresses returned during the DNS resolution.
5580pub fn resolve<M: MemorySize>(
5581    ctx: FunctionEnvMut<'_, WasiEnv>,
5582    host: WasmPtr<u8, M>,
5583    host_len: M::Offset,
5584    port: u16,
5585    addrs: WasmPtr<__wasi_addr_t, M>,
5586    naddrs: M::Offset,
5587    ret_naddrs: WasmPtr<M::Offset, M>,
5588) -> Errno {
5589    debug!("wasi::resolve");
5590
5591    let naddrs: usize = wasi_try!(naddrs.try_into().map_err(|_| Errno::Inval));
5592    let env = ctx.data();
5593    let memory = env.memory_view(&ctx);
5594    let host_str = unsafe { get_input_str!(&memory, host, host_len) };
5595    let addrs = wasi_try_mem!(addrs.slice(&memory, wasi_try!(to_offset::<M>(naddrs))));
5596
5597    let port = if port > 0 { Some(port) } else { None };
5598
5599    let found_ips = wasi_try!(env
5600        .net()
5601        .resolve(host_str.as_str(), port, None)
5602        .map_err(net_error_into_wasi_err));
5603
5604    let mut idx = 0;
5605    for found_ip in found_ips.iter().take(naddrs) {
5606        super::state::write_ip(&memory, addrs.index(idx).as_ptr::<M>(), *found_ip);
5607        idx += 1;
5608    }
5609
5610    let idx: M::Offset = wasi_try!(idx.try_into().map_err(|_| Errno::Overflow));
5611    wasi_try_mem!(ret_naddrs.write(&memory, idx));
5612
5613    Errno::Success
5614}