summaryrefslogtreecommitdiffstats
path: root/library/std/src/sys/wasi
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:02:58 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:02:58 +0000
commit698f8c2f01ea549d77d7dc3338a12e04c11057b9 (patch)
tree173a775858bd501c378080a10dca74132f05bc50 /library/std/src/sys/wasi
parentInitial commit. (diff)
downloadrustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.tar.xz
rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.zip
Adding upstream version 1.64.0+dfsg1.upstream/1.64.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'library/std/src/sys/wasi')
-rw-r--r--library/std/src/sys/wasi/args.rs62
-rw-r--r--library/std/src/sys/wasi/env.rs9
-rw-r--r--library/std/src/sys/wasi/fd.rs307
-rw-r--r--library/std/src/sys/wasi/fs.rs798
-rw-r--r--library/std/src/sys/wasi/io.rs73
-rw-r--r--library/std/src/sys/wasi/mod.rs100
-rw-r--r--library/std/src/sys/wasi/net.rs527
-rw-r--r--library/std/src/sys/wasi/os.rs243
-rw-r--r--library/std/src/sys/wasi/stdio.rs112
-rw-r--r--library/std/src/sys/wasi/thread.rs81
-rw-r--r--library/std/src/sys/wasi/time.rs65
11 files changed, 2377 insertions, 0 deletions
diff --git a/library/std/src/sys/wasi/args.rs b/library/std/src/sys/wasi/args.rs
new file mode 100644
index 000000000..c42c310e3
--- /dev/null
+++ b/library/std/src/sys/wasi/args.rs
@@ -0,0 +1,62 @@
+#![deny(unsafe_op_in_unsafe_fn)]
+
+use crate::ffi::{CStr, OsStr, OsString};
+use crate::fmt;
+use crate::os::wasi::ffi::OsStrExt;
+use crate::vec;
+
+pub struct Args {
+ iter: vec::IntoIter<OsString>,
+}
+
+impl !Send for Args {}
+impl !Sync for Args {}
+
+/// Returns the command line arguments
+pub fn args() -> Args {
+ Args { iter: maybe_args().unwrap_or(Vec::new()).into_iter() }
+}
+
+fn maybe_args() -> Option<Vec<OsString>> {
+ unsafe {
+ let (argc, buf_size) = wasi::args_sizes_get().ok()?;
+ let mut argv = Vec::with_capacity(argc);
+ let mut buf = Vec::with_capacity(buf_size);
+ wasi::args_get(argv.as_mut_ptr(), buf.as_mut_ptr()).ok()?;
+ argv.set_len(argc);
+ let mut ret = Vec::with_capacity(argc);
+ for ptr in argv {
+ let s = CStr::from_ptr(ptr.cast());
+ ret.push(OsStr::from_bytes(s.to_bytes()).to_owned());
+ }
+ Some(ret)
+ }
+}
+
+impl fmt::Debug for Args {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.iter.as_slice().fmt(f)
+ }
+}
+
+impl Iterator for Args {
+ type Item = OsString;
+ fn next(&mut self) -> Option<OsString> {
+ self.iter.next()
+ }
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ self.iter.size_hint()
+ }
+}
+
+impl ExactSizeIterator for Args {
+ fn len(&self) -> usize {
+ self.iter.len()
+ }
+}
+
+impl DoubleEndedIterator for Args {
+ fn next_back(&mut self) -> Option<OsString> {
+ self.iter.next_back()
+ }
+}
diff --git a/library/std/src/sys/wasi/env.rs b/library/std/src/sys/wasi/env.rs
new file mode 100644
index 000000000..730e356d7
--- /dev/null
+++ b/library/std/src/sys/wasi/env.rs
@@ -0,0 +1,9 @@
+pub mod os {
+ pub const FAMILY: &str = "";
+ pub const OS: &str = "";
+ pub const DLL_PREFIX: &str = "";
+ pub const DLL_SUFFIX: &str = ".wasm";
+ pub const DLL_EXTENSION: &str = "wasm";
+ pub const EXE_SUFFIX: &str = ".wasm";
+ pub const EXE_EXTENSION: &str = "wasm";
+}
diff --git a/library/std/src/sys/wasi/fd.rs b/library/std/src/sys/wasi/fd.rs
new file mode 100644
index 000000000..0b9c8e61d
--- /dev/null
+++ b/library/std/src/sys/wasi/fd.rs
@@ -0,0 +1,307 @@
+#![deny(unsafe_op_in_unsafe_fn)]
+#![allow(dead_code)]
+
+use super::err2io;
+use crate::io::{self, IoSlice, IoSliceMut, SeekFrom};
+use crate::mem;
+use crate::net::Shutdown;
+use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
+use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner};
+
+#[derive(Debug)]
+pub struct WasiFd {
+ fd: OwnedFd,
+}
+
+fn iovec<'a>(a: &'a mut [IoSliceMut<'_>]) -> &'a [wasi::Iovec] {
+ assert_eq!(mem::size_of::<IoSliceMut<'_>>(), mem::size_of::<wasi::Iovec>());
+ assert_eq!(mem::align_of::<IoSliceMut<'_>>(), mem::align_of::<wasi::Iovec>());
+ // SAFETY: `IoSliceMut` and `IoVec` have exactly the same memory layout
+ unsafe { mem::transmute(a) }
+}
+
+fn ciovec<'a>(a: &'a [IoSlice<'_>]) -> &'a [wasi::Ciovec] {
+ assert_eq!(mem::size_of::<IoSlice<'_>>(), mem::size_of::<wasi::Ciovec>());
+ assert_eq!(mem::align_of::<IoSlice<'_>>(), mem::align_of::<wasi::Ciovec>());
+ // SAFETY: `IoSlice` and `CIoVec` have exactly the same memory layout
+ unsafe { mem::transmute(a) }
+}
+
+impl WasiFd {
+ pub fn datasync(&self) -> io::Result<()> {
+ unsafe { wasi::fd_datasync(self.as_raw_fd() as wasi::Fd).map_err(err2io) }
+ }
+
+ pub fn pread(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
+ unsafe { wasi::fd_pread(self.as_raw_fd() as wasi::Fd, iovec(bufs), offset).map_err(err2io) }
+ }
+
+ pub fn pwrite(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
+ unsafe {
+ wasi::fd_pwrite(self.as_raw_fd() as wasi::Fd, ciovec(bufs), offset).map_err(err2io)
+ }
+ }
+
+ pub fn read(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
+ unsafe { wasi::fd_read(self.as_raw_fd() as wasi::Fd, iovec(bufs)).map_err(err2io) }
+ }
+
+ pub fn write(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
+ unsafe { wasi::fd_write(self.as_raw_fd() as wasi::Fd, ciovec(bufs)).map_err(err2io) }
+ }
+
+ pub fn seek(&self, pos: SeekFrom) -> io::Result<u64> {
+ let (whence, offset) = match pos {
+ SeekFrom::Start(pos) => (wasi::WHENCE_SET, pos as i64),
+ SeekFrom::End(pos) => (wasi::WHENCE_END, pos),
+ SeekFrom::Current(pos) => (wasi::WHENCE_CUR, pos),
+ };
+ unsafe { wasi::fd_seek(self.as_raw_fd() as wasi::Fd, offset, whence).map_err(err2io) }
+ }
+
+ pub fn tell(&self) -> io::Result<u64> {
+ unsafe { wasi::fd_tell(self.as_raw_fd() as wasi::Fd).map_err(err2io) }
+ }
+
+ // FIXME: __wasi_fd_fdstat_get
+
+ pub fn set_flags(&self, flags: wasi::Fdflags) -> io::Result<()> {
+ unsafe { wasi::fd_fdstat_set_flags(self.as_raw_fd() as wasi::Fd, flags).map_err(err2io) }
+ }
+
+ pub fn set_rights(&self, base: wasi::Rights, inheriting: wasi::Rights) -> io::Result<()> {
+ unsafe {
+ wasi::fd_fdstat_set_rights(self.as_raw_fd() as wasi::Fd, base, inheriting)
+ .map_err(err2io)
+ }
+ }
+
+ pub fn sync(&self) -> io::Result<()> {
+ unsafe { wasi::fd_sync(self.as_raw_fd() as wasi::Fd).map_err(err2io) }
+ }
+
+ pub fn advise(&self, offset: u64, len: u64, advice: wasi::Advice) -> io::Result<()> {
+ unsafe {
+ wasi::fd_advise(self.as_raw_fd() as wasi::Fd, offset, len, advice).map_err(err2io)
+ }
+ }
+
+ pub fn allocate(&self, offset: u64, len: u64) -> io::Result<()> {
+ unsafe { wasi::fd_allocate(self.as_raw_fd() as wasi::Fd, offset, len).map_err(err2io) }
+ }
+
+ pub fn create_directory(&self, path: &str) -> io::Result<()> {
+ unsafe { wasi::path_create_directory(self.as_raw_fd() as wasi::Fd, path).map_err(err2io) }
+ }
+
+ pub fn link(
+ &self,
+ old_flags: wasi::Lookupflags,
+ old_path: &str,
+ new_fd: &WasiFd,
+ new_path: &str,
+ ) -> io::Result<()> {
+ unsafe {
+ wasi::path_link(
+ self.as_raw_fd() as wasi::Fd,
+ old_flags,
+ old_path,
+ new_fd.as_raw_fd() as wasi::Fd,
+ new_path,
+ )
+ .map_err(err2io)
+ }
+ }
+
+ pub fn open(
+ &self,
+ dirflags: wasi::Lookupflags,
+ path: &str,
+ oflags: wasi::Oflags,
+ fs_rights_base: wasi::Rights,
+ fs_rights_inheriting: wasi::Rights,
+ fs_flags: wasi::Fdflags,
+ ) -> io::Result<WasiFd> {
+ unsafe {
+ wasi::path_open(
+ self.as_raw_fd() as wasi::Fd,
+ dirflags,
+ path,
+ oflags,
+ fs_rights_base,
+ fs_rights_inheriting,
+ fs_flags,
+ )
+ .map(|fd| WasiFd::from_raw_fd(fd as RawFd))
+ .map_err(err2io)
+ }
+ }
+
+ pub fn readdir(&self, buf: &mut [u8], cookie: wasi::Dircookie) -> io::Result<usize> {
+ unsafe {
+ wasi::fd_readdir(self.as_raw_fd() as wasi::Fd, buf.as_mut_ptr(), buf.len(), cookie)
+ .map_err(err2io)
+ }
+ }
+
+ pub fn readlink(&self, path: &str, buf: &mut [u8]) -> io::Result<usize> {
+ unsafe {
+ wasi::path_readlink(self.as_raw_fd() as wasi::Fd, path, buf.as_mut_ptr(), buf.len())
+ .map_err(err2io)
+ }
+ }
+
+ pub fn rename(&self, old_path: &str, new_fd: &WasiFd, new_path: &str) -> io::Result<()> {
+ unsafe {
+ wasi::path_rename(
+ self.as_raw_fd() as wasi::Fd,
+ old_path,
+ new_fd.as_raw_fd() as wasi::Fd,
+ new_path,
+ )
+ .map_err(err2io)
+ }
+ }
+
+ pub fn filestat_get(&self) -> io::Result<wasi::Filestat> {
+ unsafe { wasi::fd_filestat_get(self.as_raw_fd() as wasi::Fd).map_err(err2io) }
+ }
+
+ pub fn filestat_set_times(
+ &self,
+ atim: wasi::Timestamp,
+ mtim: wasi::Timestamp,
+ fstflags: wasi::Fstflags,
+ ) -> io::Result<()> {
+ unsafe {
+ wasi::fd_filestat_set_times(self.as_raw_fd() as wasi::Fd, atim, mtim, fstflags)
+ .map_err(err2io)
+ }
+ }
+
+ pub fn filestat_set_size(&self, size: u64) -> io::Result<()> {
+ unsafe { wasi::fd_filestat_set_size(self.as_raw_fd() as wasi::Fd, size).map_err(err2io) }
+ }
+
+ pub fn path_filestat_get(
+ &self,
+ flags: wasi::Lookupflags,
+ path: &str,
+ ) -> io::Result<wasi::Filestat> {
+ unsafe {
+ wasi::path_filestat_get(self.as_raw_fd() as wasi::Fd, flags, path).map_err(err2io)
+ }
+ }
+
+ pub fn path_filestat_set_times(
+ &self,
+ flags: wasi::Lookupflags,
+ path: &str,
+ atim: wasi::Timestamp,
+ mtim: wasi::Timestamp,
+ fstflags: wasi::Fstflags,
+ ) -> io::Result<()> {
+ unsafe {
+ wasi::path_filestat_set_times(
+ self.as_raw_fd() as wasi::Fd,
+ flags,
+ path,
+ atim,
+ mtim,
+ fstflags,
+ )
+ .map_err(err2io)
+ }
+ }
+
+ pub fn symlink(&self, old_path: &str, new_path: &str) -> io::Result<()> {
+ unsafe {
+ wasi::path_symlink(old_path, self.as_raw_fd() as wasi::Fd, new_path).map_err(err2io)
+ }
+ }
+
+ pub fn unlink_file(&self, path: &str) -> io::Result<()> {
+ unsafe { wasi::path_unlink_file(self.as_raw_fd() as wasi::Fd, path).map_err(err2io) }
+ }
+
+ pub fn remove_directory(&self, path: &str) -> io::Result<()> {
+ unsafe { wasi::path_remove_directory(self.as_raw_fd() as wasi::Fd, path).map_err(err2io) }
+ }
+
+ pub fn sock_accept(&self, flags: wasi::Fdflags) -> io::Result<wasi::Fd> {
+ unsafe { wasi::sock_accept(self.as_raw_fd() as wasi::Fd, flags).map_err(err2io) }
+ }
+
+ pub fn sock_recv(
+ &self,
+ ri_data: &mut [IoSliceMut<'_>],
+ ri_flags: wasi::Riflags,
+ ) -> io::Result<(usize, wasi::Roflags)> {
+ unsafe {
+ wasi::sock_recv(self.as_raw_fd() as wasi::Fd, iovec(ri_data), ri_flags).map_err(err2io)
+ }
+ }
+
+ pub fn sock_send(&self, si_data: &[IoSlice<'_>], si_flags: wasi::Siflags) -> io::Result<usize> {
+ unsafe {
+ wasi::sock_send(self.as_raw_fd() as wasi::Fd, ciovec(si_data), si_flags).map_err(err2io)
+ }
+ }
+
+ pub fn sock_shutdown(&self, how: Shutdown) -> io::Result<()> {
+ let how = match how {
+ Shutdown::Read => wasi::SDFLAGS_RD,
+ Shutdown::Write => wasi::SDFLAGS_WR,
+ Shutdown::Both => wasi::SDFLAGS_WR | wasi::SDFLAGS_RD,
+ };
+ unsafe { wasi::sock_shutdown(self.as_raw_fd() as wasi::Fd, how).map_err(err2io) }
+ }
+}
+
+impl AsInner<OwnedFd> for WasiFd {
+ fn as_inner(&self) -> &OwnedFd {
+ &self.fd
+ }
+}
+
+impl AsInnerMut<OwnedFd> for WasiFd {
+ fn as_inner_mut(&mut self) -> &mut OwnedFd {
+ &mut self.fd
+ }
+}
+
+impl IntoInner<OwnedFd> for WasiFd {
+ fn into_inner(self) -> OwnedFd {
+ self.fd
+ }
+}
+
+impl FromInner<OwnedFd> for WasiFd {
+ fn from_inner(owned_fd: OwnedFd) -> Self {
+ Self { fd: owned_fd }
+ }
+}
+
+impl AsFd for WasiFd {
+ fn as_fd(&self) -> BorrowedFd<'_> {
+ self.fd.as_fd()
+ }
+}
+
+impl AsRawFd for WasiFd {
+ fn as_raw_fd(&self) -> RawFd {
+ self.fd.as_raw_fd()
+ }
+}
+
+impl IntoRawFd for WasiFd {
+ fn into_raw_fd(self) -> RawFd {
+ self.fd.into_raw_fd()
+ }
+}
+
+impl FromRawFd for WasiFd {
+ unsafe fn from_raw_fd(raw_fd: RawFd) -> Self {
+ unsafe { Self { fd: FromRawFd::from_raw_fd(raw_fd) } }
+ }
+}
diff --git a/library/std/src/sys/wasi/fs.rs b/library/std/src/sys/wasi/fs.rs
new file mode 100644
index 000000000..6614ae397
--- /dev/null
+++ b/library/std/src/sys/wasi/fs.rs
@@ -0,0 +1,798 @@
+#![deny(unsafe_op_in_unsafe_fn)]
+
+use super::fd::WasiFd;
+use crate::ffi::{CStr, CString, OsStr, OsString};
+use crate::fmt;
+use crate::io::{self, IoSlice, IoSliceMut, ReadBuf, SeekFrom};
+use crate::iter;
+use crate::mem::{self, ManuallyDrop};
+use crate::os::raw::c_int;
+use crate::os::wasi::ffi::{OsStrExt, OsStringExt};
+use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd};
+use crate::path::{Path, PathBuf};
+use crate::ptr;
+use crate::sync::Arc;
+use crate::sys::time::SystemTime;
+use crate::sys::unsupported;
+use crate::sys_common::{AsInner, FromInner, IntoInner};
+
+pub use crate::sys_common::fs::try_exists;
+
+pub struct File {
+ fd: WasiFd,
+}
+
+#[derive(Clone)]
+pub struct FileAttr {
+ meta: wasi::Filestat,
+}
+
+pub struct ReadDir {
+ inner: Arc<ReadDirInner>,
+ cookie: Option<wasi::Dircookie>,
+ buf: Vec<u8>,
+ offset: usize,
+ cap: usize,
+}
+
+struct ReadDirInner {
+ root: PathBuf,
+ dir: File,
+}
+
+pub struct DirEntry {
+ meta: wasi::Dirent,
+ name: Vec<u8>,
+ inner: Arc<ReadDirInner>,
+}
+
+#[derive(Clone, Debug, Default)]
+pub struct OpenOptions {
+ read: bool,
+ write: bool,
+ append: bool,
+ dirflags: wasi::Lookupflags,
+ fdflags: wasi::Fdflags,
+ oflags: wasi::Oflags,
+ rights_base: Option<wasi::Rights>,
+ rights_inheriting: Option<wasi::Rights>,
+}
+
+#[derive(Clone, PartialEq, Eq, Debug)]
+pub struct FilePermissions {
+ readonly: bool,
+}
+
+#[derive(Copy, Clone, Debug, Default)]
+pub struct FileTimes {
+ accessed: Option<wasi::Timestamp>,
+ modified: Option<wasi::Timestamp>,
+}
+
+#[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)]
+pub struct FileType {
+ bits: wasi::Filetype,
+}
+
+#[derive(Debug)]
+pub struct DirBuilder {}
+
+impl FileAttr {
+ pub fn size(&self) -> u64 {
+ self.meta.size
+ }
+
+ pub fn perm(&self) -> FilePermissions {
+ // not currently implemented in wasi yet
+ FilePermissions { readonly: false }
+ }
+
+ pub fn file_type(&self) -> FileType {
+ FileType { bits: self.meta.filetype }
+ }
+
+ pub fn modified(&self) -> io::Result<SystemTime> {
+ Ok(SystemTime::from_wasi_timestamp(self.meta.mtim))
+ }
+
+ pub fn accessed(&self) -> io::Result<SystemTime> {
+ Ok(SystemTime::from_wasi_timestamp(self.meta.atim))
+ }
+
+ pub fn created(&self) -> io::Result<SystemTime> {
+ Ok(SystemTime::from_wasi_timestamp(self.meta.ctim))
+ }
+
+ pub fn as_wasi(&self) -> &wasi::Filestat {
+ &self.meta
+ }
+}
+
+impl FilePermissions {
+ pub fn readonly(&self) -> bool {
+ self.readonly
+ }
+
+ pub fn set_readonly(&mut self, readonly: bool) {
+ self.readonly = readonly;
+ }
+}
+
+impl FileTimes {
+ pub fn set_accessed(&mut self, t: SystemTime) {
+ self.accessed = Some(t.to_wasi_timestamp_or_panic());
+ }
+
+ pub fn set_modified(&mut self, t: SystemTime) {
+ self.modified = Some(t.to_wasi_timestamp_or_panic());
+ }
+}
+
+impl FileType {
+ pub fn is_dir(&self) -> bool {
+ self.bits == wasi::FILETYPE_DIRECTORY
+ }
+
+ pub fn is_file(&self) -> bool {
+ self.bits == wasi::FILETYPE_REGULAR_FILE
+ }
+
+ pub fn is_symlink(&self) -> bool {
+ self.bits == wasi::FILETYPE_SYMBOLIC_LINK
+ }
+
+ pub fn bits(&self) -> wasi::Filetype {
+ self.bits
+ }
+}
+
+impl ReadDir {
+ fn new(dir: File, root: PathBuf) -> ReadDir {
+ ReadDir {
+ cookie: Some(0),
+ buf: vec![0; 128],
+ offset: 0,
+ cap: 0,
+ inner: Arc::new(ReadDirInner { dir, root }),
+ }
+ }
+}
+
+impl fmt::Debug for ReadDir {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("ReadDir").finish_non_exhaustive()
+ }
+}
+
+impl Iterator for ReadDir {
+ type Item = io::Result<DirEntry>;
+
+ fn next(&mut self) -> Option<io::Result<DirEntry>> {
+ loop {
+ // If we've reached the capacity of our buffer then we need to read
+ // some more from the OS, otherwise we pick up at our old offset.
+ let offset = if self.offset == self.cap {
+ let cookie = self.cookie.take()?;
+ match self.inner.dir.fd.readdir(&mut self.buf, cookie) {
+ Ok(bytes) => self.cap = bytes,
+ Err(e) => return Some(Err(e)),
+ }
+ self.offset = 0;
+ self.cookie = Some(cookie);
+
+ // If we didn't actually read anything, this is in theory the
+ // end of the directory.
+ if self.cap == 0 {
+ self.cookie = None;
+ return None;
+ }
+
+ 0
+ } else {
+ self.offset
+ };
+ let data = &self.buf[offset..self.cap];
+
+ // If we're not able to read a directory entry then that means it
+ // must have been truncated at the end of the buffer, so reset our
+ // offset so we can go back and reread into the buffer, picking up
+ // where we last left off.
+ let dirent_size = mem::size_of::<wasi::Dirent>();
+ if data.len() < dirent_size {
+ assert!(self.cookie.is_some());
+ assert!(self.buf.len() >= dirent_size);
+ self.offset = self.cap;
+ continue;
+ }
+ let (dirent, data) = data.split_at(dirent_size);
+ let dirent = unsafe { ptr::read_unaligned(dirent.as_ptr() as *const wasi::Dirent) };
+
+ // If the file name was truncated, then we need to reinvoke
+ // `readdir` so we truncate our buffer to start over and reread this
+ // descriptor. Note that if our offset is 0 that means the file name
+ // is massive and we need a bigger buffer.
+ if data.len() < dirent.d_namlen as usize {
+ if offset == 0 {
+ let amt_to_add = self.buf.capacity();
+ self.buf.extend(iter::repeat(0).take(amt_to_add));
+ }
+ assert!(self.cookie.is_some());
+ self.offset = self.cap;
+ continue;
+ }
+ self.cookie = Some(dirent.d_next);
+ self.offset = offset + dirent_size + dirent.d_namlen as usize;
+
+ let name = &data[..(dirent.d_namlen as usize)];
+
+ // These names are skipped on all other platforms, so let's skip
+ // them here too
+ if name == b"." || name == b".." {
+ continue;
+ }
+
+ return Some(Ok(DirEntry {
+ meta: dirent,
+ name: name.to_vec(),
+ inner: self.inner.clone(),
+ }));
+ }
+ }
+}
+
+impl DirEntry {
+ pub fn path(&self) -> PathBuf {
+ let name = OsStr::from_bytes(&self.name);
+ self.inner.root.join(name)
+ }
+
+ pub fn file_name(&self) -> OsString {
+ OsString::from_vec(self.name.clone())
+ }
+
+ pub fn metadata(&self) -> io::Result<FileAttr> {
+ metadata_at(&self.inner.dir.fd, 0, OsStr::from_bytes(&self.name).as_ref())
+ }
+
+ pub fn file_type(&self) -> io::Result<FileType> {
+ Ok(FileType { bits: self.meta.d_type })
+ }
+
+ pub fn ino(&self) -> wasi::Inode {
+ self.meta.d_ino
+ }
+}
+
+impl OpenOptions {
+ pub fn new() -> OpenOptions {
+ let mut base = OpenOptions::default();
+ base.dirflags = wasi::LOOKUPFLAGS_SYMLINK_FOLLOW;
+ return base;
+ }
+
+ pub fn read(&mut self, read: bool) {
+ self.read = read;
+ }
+
+ pub fn write(&mut self, write: bool) {
+ self.write = write;
+ }
+
+ pub fn truncate(&mut self, truncate: bool) {
+ self.oflag(wasi::OFLAGS_TRUNC, truncate);
+ }
+
+ pub fn create(&mut self, create: bool) {
+ self.oflag(wasi::OFLAGS_CREAT, create);
+ }
+
+ pub fn create_new(&mut self, create_new: bool) {
+ self.oflag(wasi::OFLAGS_EXCL, create_new);
+ self.oflag(wasi::OFLAGS_CREAT, create_new);
+ }
+
+ pub fn directory(&mut self, directory: bool) {
+ self.oflag(wasi::OFLAGS_DIRECTORY, directory);
+ }
+
+ fn oflag(&mut self, bit: wasi::Oflags, set: bool) {
+ if set {
+ self.oflags |= bit;
+ } else {
+ self.oflags &= !bit;
+ }
+ }
+
+ pub fn append(&mut self, append: bool) {
+ self.append = append;
+ self.fdflag(wasi::FDFLAGS_APPEND, append);
+ }
+
+ pub fn dsync(&mut self, set: bool) {
+ self.fdflag(wasi::FDFLAGS_DSYNC, set);
+ }
+
+ pub fn nonblock(&mut self, set: bool) {
+ self.fdflag(wasi::FDFLAGS_NONBLOCK, set);
+ }
+
+ pub fn rsync(&mut self, set: bool) {
+ self.fdflag(wasi::FDFLAGS_RSYNC, set);
+ }
+
+ pub fn sync(&mut self, set: bool) {
+ self.fdflag(wasi::FDFLAGS_SYNC, set);
+ }
+
+ fn fdflag(&mut self, bit: wasi::Fdflags, set: bool) {
+ if set {
+ self.fdflags |= bit;
+ } else {
+ self.fdflags &= !bit;
+ }
+ }
+
+ pub fn fs_rights_base(&mut self, rights: wasi::Rights) {
+ self.rights_base = Some(rights);
+ }
+
+ pub fn fs_rights_inheriting(&mut self, rights: wasi::Rights) {
+ self.rights_inheriting = Some(rights);
+ }
+
+ fn rights_base(&self) -> wasi::Rights {
+ if let Some(rights) = self.rights_base {
+ return rights;
+ }
+
+ // If rights haven't otherwise been specified try to pick a reasonable
+ // set. This can always be overridden by users via extension traits, and
+ // implementations may give us fewer rights silently than we ask for. So
+ // given that, just look at `read` and `write` and bucket permissions
+ // based on that.
+ let mut base = 0;
+ if self.read {
+ base |= wasi::RIGHTS_FD_READ;
+ base |= wasi::RIGHTS_FD_READDIR;
+ }
+ if self.write || self.append {
+ base |= wasi::RIGHTS_FD_WRITE;
+ base |= wasi::RIGHTS_FD_DATASYNC;
+ base |= wasi::RIGHTS_FD_ALLOCATE;
+ base |= wasi::RIGHTS_FD_FILESTAT_SET_SIZE;
+ }
+
+ // FIXME: some of these should probably be read-only or write-only...
+ base |= wasi::RIGHTS_FD_ADVISE;
+ base |= wasi::RIGHTS_FD_FDSTAT_SET_FLAGS;
+ base |= wasi::RIGHTS_FD_FILESTAT_GET;
+ base |= wasi::RIGHTS_FD_FILESTAT_SET_TIMES;
+ base |= wasi::RIGHTS_FD_SEEK;
+ base |= wasi::RIGHTS_FD_SYNC;
+ base |= wasi::RIGHTS_FD_TELL;
+ base |= wasi::RIGHTS_PATH_CREATE_DIRECTORY;
+ base |= wasi::RIGHTS_PATH_CREATE_FILE;
+ base |= wasi::RIGHTS_PATH_FILESTAT_GET;
+ base |= wasi::RIGHTS_PATH_LINK_SOURCE;
+ base |= wasi::RIGHTS_PATH_LINK_TARGET;
+ base |= wasi::RIGHTS_PATH_OPEN;
+ base |= wasi::RIGHTS_PATH_READLINK;
+ base |= wasi::RIGHTS_PATH_REMOVE_DIRECTORY;
+ base |= wasi::RIGHTS_PATH_RENAME_SOURCE;
+ base |= wasi::RIGHTS_PATH_RENAME_TARGET;
+ base |= wasi::RIGHTS_PATH_SYMLINK;
+ base |= wasi::RIGHTS_PATH_UNLINK_FILE;
+ base |= wasi::RIGHTS_POLL_FD_READWRITE;
+
+ return base;
+ }
+
+ fn rights_inheriting(&self) -> wasi::Rights {
+ self.rights_inheriting.unwrap_or_else(|| self.rights_base())
+ }
+
+ pub fn lookup_flags(&mut self, flags: wasi::Lookupflags) {
+ self.dirflags = flags;
+ }
+}
+
+impl File {
+ pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
+ let (dir, file) = open_parent(path)?;
+ open_at(&dir, &file, opts)
+ }
+
+ pub fn open_at(&self, path: &Path, opts: &OpenOptions) -> io::Result<File> {
+ open_at(&self.fd, path, opts)
+ }
+
+ pub fn file_attr(&self) -> io::Result<FileAttr> {
+ self.fd.filestat_get().map(|meta| FileAttr { meta })
+ }
+
+ pub fn metadata_at(&self, flags: wasi::Lookupflags, path: &Path) -> io::Result<FileAttr> {
+ metadata_at(&self.fd, flags, path)
+ }
+
+ pub fn fsync(&self) -> io::Result<()> {
+ self.fd.sync()
+ }
+
+ pub fn datasync(&self) -> io::Result<()> {
+ self.fd.datasync()
+ }
+
+ pub fn truncate(&self, size: u64) -> io::Result<()> {
+ self.fd.filestat_set_size(size)
+ }
+
+ pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
+ self.read_vectored(&mut [IoSliceMut::new(buf)])
+ }
+
+ pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
+ self.fd.read(bufs)
+ }
+
+ #[inline]
+ pub fn is_read_vectored(&self) -> bool {
+ true
+ }
+
+ pub fn read_buf(&self, buf: &mut ReadBuf<'_>) -> io::Result<()> {
+ crate::io::default_read_buf(|buf| self.read(buf), buf)
+ }
+
+ pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
+ self.write_vectored(&[IoSlice::new(buf)])
+ }
+
+ pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
+ self.fd.write(bufs)
+ }
+
+ #[inline]
+ pub fn is_write_vectored(&self) -> bool {
+ true
+ }
+
+ pub fn flush(&self) -> io::Result<()> {
+ Ok(())
+ }
+
+ pub fn seek(&self, pos: SeekFrom) -> io::Result<u64> {
+ self.fd.seek(pos)
+ }
+
+ pub fn duplicate(&self) -> io::Result<File> {
+ // https://github.com/CraneStation/wasmtime/blob/master/docs/WASI-rationale.md#why-no-dup
+ unsupported()
+ }
+
+ pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> {
+ // Permissions haven't been fully figured out in wasi yet, so this is
+ // likely temporary
+ unsupported()
+ }
+
+ pub fn set_times(&self, times: FileTimes) -> io::Result<()> {
+ self.fd.filestat_set_times(
+ times.accessed.unwrap_or(0),
+ times.modified.unwrap_or(0),
+ times.accessed.map_or(0, |_| wasi::FSTFLAGS_ATIM)
+ | times.modified.map_or(0, |_| wasi::FSTFLAGS_MTIM),
+ )
+ }
+
+ pub fn read_link(&self, file: &Path) -> io::Result<PathBuf> {
+ read_link(&self.fd, file)
+ }
+}
+
+impl AsInner<WasiFd> for File {
+ fn as_inner(&self) -> &WasiFd {
+ &self.fd
+ }
+}
+
+impl IntoInner<WasiFd> for File {
+ fn into_inner(self) -> WasiFd {
+ self.fd
+ }
+}
+
+impl FromInner<WasiFd> for File {
+ fn from_inner(fd: WasiFd) -> File {
+ File { fd }
+ }
+}
+
+impl AsFd for File {
+ fn as_fd(&self) -> BorrowedFd<'_> {
+ self.fd.as_fd()
+ }
+}
+
+impl AsRawFd for File {
+ fn as_raw_fd(&self) -> RawFd {
+ self.fd.as_raw_fd()
+ }
+}
+
+impl IntoRawFd for File {
+ fn into_raw_fd(self) -> RawFd {
+ self.fd.into_raw_fd()
+ }
+}
+
+impl FromRawFd for File {
+ unsafe fn from_raw_fd(raw_fd: RawFd) -> Self {
+ unsafe { Self { fd: FromRawFd::from_raw_fd(raw_fd) } }
+ }
+}
+
+impl DirBuilder {
+ pub fn new() -> DirBuilder {
+ DirBuilder {}
+ }
+
+ pub fn mkdir(&self, p: &Path) -> io::Result<()> {
+ let (dir, file) = open_parent(p)?;
+ dir.create_directory(osstr2str(file.as_ref())?)
+ }
+}
+
+impl fmt::Debug for File {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("File").field("fd", &self.as_raw_fd()).finish()
+ }
+}
+
+pub fn readdir(p: &Path) -> io::Result<ReadDir> {
+ let mut opts = OpenOptions::new();
+ opts.directory(true);
+ opts.read(true);
+ let dir = File::open(p, &opts)?;
+ Ok(ReadDir::new(dir, p.to_path_buf()))
+}
+
+pub fn unlink(p: &Path) -> io::Result<()> {
+ let (dir, file) = open_parent(p)?;
+ dir.unlink_file(osstr2str(file.as_ref())?)
+}
+
+pub fn rename(old: &Path, new: &Path) -> io::Result<()> {
+ let (old, old_file) = open_parent(old)?;
+ let (new, new_file) = open_parent(new)?;
+ old.rename(osstr2str(old_file.as_ref())?, &new, osstr2str(new_file.as_ref())?)
+}
+
+pub fn set_perm(_p: &Path, _perm: FilePermissions) -> io::Result<()> {
+ // Permissions haven't been fully figured out in wasi yet, so this is
+ // likely temporary
+ unsupported()
+}
+
+pub fn rmdir(p: &Path) -> io::Result<()> {
+ let (dir, file) = open_parent(p)?;
+ dir.remove_directory(osstr2str(file.as_ref())?)
+}
+
+pub fn readlink(p: &Path) -> io::Result<PathBuf> {
+ let (dir, file) = open_parent(p)?;
+ read_link(&dir, &file)
+}
+
+fn read_link(fd: &WasiFd, file: &Path) -> io::Result<PathBuf> {
+ // Try to get a best effort initial capacity for the vector we're going to
+ // fill. Note that if it's not a symlink we don't use a file to avoid
+ // allocating gigabytes if you read_link a huge movie file by accident.
+ // Additionally we add 1 to the initial size so if it doesn't change until
+ // when we call `readlink` the returned length will be less than the
+ // capacity, guaranteeing that we got all the data.
+ let meta = metadata_at(fd, 0, file)?;
+ let initial_size = if meta.file_type().is_symlink() {
+ (meta.size() as usize).saturating_add(1)
+ } else {
+ 1 // this'll fail in just a moment
+ };
+
+ // Now that we have an initial guess of how big to make our buffer, call
+ // `readlink` in a loop until it fails or reports it filled fewer bytes than
+ // we asked for, indicating we got everything.
+ let file = osstr2str(file.as_ref())?;
+ let mut destination = vec![0u8; initial_size];
+ loop {
+ let len = fd.readlink(file, &mut destination)?;
+ if len < destination.len() {
+ destination.truncate(len);
+ destination.shrink_to_fit();
+ return Ok(PathBuf::from(OsString::from_vec(destination)));
+ }
+ let amt_to_add = destination.len();
+ destination.extend(iter::repeat(0).take(amt_to_add));
+ }
+}
+
+pub fn symlink(original: &Path, link: &Path) -> io::Result<()> {
+ let (link, link_file) = open_parent(link)?;
+ link.symlink(osstr2str(original.as_ref())?, osstr2str(link_file.as_ref())?)
+}
+
+pub fn link(original: &Path, link: &Path) -> io::Result<()> {
+ let (original, original_file) = open_parent(original)?;
+ let (link, link_file) = open_parent(link)?;
+ // Pass 0 as the flags argument, meaning don't follow symlinks.
+ original.link(0, osstr2str(original_file.as_ref())?, &link, osstr2str(link_file.as_ref())?)
+}
+
+pub fn stat(p: &Path) -> io::Result<FileAttr> {
+ let (dir, file) = open_parent(p)?;
+ metadata_at(&dir, wasi::LOOKUPFLAGS_SYMLINK_FOLLOW, &file)
+}
+
+pub fn lstat(p: &Path) -> io::Result<FileAttr> {
+ let (dir, file) = open_parent(p)?;
+ metadata_at(&dir, 0, &file)
+}
+
+fn metadata_at(fd: &WasiFd, flags: wasi::Lookupflags, path: &Path) -> io::Result<FileAttr> {
+ let meta = fd.path_filestat_get(flags, osstr2str(path.as_ref())?)?;
+ Ok(FileAttr { meta })
+}
+
+pub fn canonicalize(_p: &Path) -> io::Result<PathBuf> {
+ // This seems to not be in wasi's API yet, and we may need to end up
+ // emulating it ourselves. For now just return an error.
+ unsupported()
+}
+
+fn open_at(fd: &WasiFd, path: &Path, opts: &OpenOptions) -> io::Result<File> {
+ let fd = fd.open(
+ opts.dirflags,
+ osstr2str(path.as_ref())?,
+ opts.oflags,
+ opts.rights_base(),
+ opts.rights_inheriting(),
+ opts.fdflags,
+ )?;
+ Ok(File { fd })
+}
+
+/// Attempts to open a bare path `p`.
+///
+/// WASI has no fundamental capability to do this. All syscalls and operations
+/// are relative to already-open file descriptors. The C library, however,
+/// manages a map of pre-opened file descriptors to their path, and then the C
+/// library provides an API to look at this. In other words, when you want to
+/// open a path `p`, you have to find a previously opened file descriptor in a
+/// global table and then see if `p` is relative to that file descriptor.
+///
+/// This function, if successful, will return two items:
+///
+/// * The first is a `ManuallyDrop<WasiFd>`. This represents a pre-opened file
+/// descriptor which we don't have ownership of, but we can use. You shouldn't
+/// actually drop the `fd`.
+///
+/// * The second is a path that should be a part of `p` and represents a
+/// relative traversal from the file descriptor specified to the desired
+/// location `p`.
+///
+/// If successful you can use the returned file descriptor to perform
+/// file-descriptor-relative operations on the path returned as well. The
+/// `rights` argument indicates what operations are desired on the returned file
+/// descriptor, and if successful the returned file descriptor should have the
+/// appropriate rights for performing `rights` actions.
+///
+/// Note that this can fail if `p` doesn't look like it can be opened relative
+/// to any pre-opened file descriptor.
+fn open_parent(p: &Path) -> io::Result<(ManuallyDrop<WasiFd>, PathBuf)> {
+ let p = CString::new(p.as_os_str().as_bytes())?;
+ let mut buf = Vec::<u8>::with_capacity(512);
+ loop {
+ unsafe {
+ let mut relative_path = buf.as_ptr().cast();
+ let mut abs_prefix = ptr::null();
+ let fd = __wasilibc_find_relpath(
+ p.as_ptr(),
+ &mut abs_prefix,
+ &mut relative_path,
+ buf.capacity(),
+ );
+ if fd == -1 {
+ if io::Error::last_os_error().raw_os_error() == Some(libc::ENOMEM) {
+ // Trigger the internal buffer resizing logic of `Vec` by requiring
+ // more space than the current capacity.
+ let cap = buf.capacity();
+ buf.set_len(cap);
+ buf.reserve(1);
+ continue;
+ }
+ let msg = format!(
+ "failed to find a pre-opened file descriptor \
+ through which {:?} could be opened",
+ p
+ );
+ return Err(io::Error::new(io::ErrorKind::Uncategorized, msg));
+ }
+ let relative = CStr::from_ptr(relative_path).to_bytes().to_vec();
+
+ return Ok((
+ ManuallyDrop::new(WasiFd::from_raw_fd(fd as c_int)),
+ PathBuf::from(OsString::from_vec(relative)),
+ ));
+ }
+ }
+
+ extern "C" {
+ pub fn __wasilibc_find_relpath(
+ path: *const libc::c_char,
+ abs_prefix: *mut *const libc::c_char,
+ relative_path: *mut *const libc::c_char,
+ relative_path_len: libc::size_t,
+ ) -> libc::c_int;
+ }
+}
+
+pub fn osstr2str(f: &OsStr) -> io::Result<&str> {
+ f.to_str()
+ .ok_or_else(|| io::const_io_error!(io::ErrorKind::Uncategorized, "input must be utf-8"))
+}
+
+pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
+ use crate::fs::File;
+
+ let mut reader = File::open(from)?;
+ let mut writer = File::create(to)?;
+
+ io::copy(&mut reader, &mut writer)
+}
+
+pub fn remove_dir_all(path: &Path) -> io::Result<()> {
+ let (parent, path) = open_parent(path)?;
+ remove_dir_all_recursive(&parent, &path)
+}
+
+fn remove_dir_all_recursive(parent: &WasiFd, path: &Path) -> io::Result<()> {
+ // Open up a file descriptor for the directory itself. Note that we don't
+ // follow symlinks here and we specifically open directories.
+ //
+ // At the root invocation of this function this will correctly handle
+ // symlinks passed to the top-level `remove_dir_all`. At the recursive
+ // level this will double-check that after the `readdir` call deduced this
+ // was a directory it's still a directory by the time we open it up.
+ //
+ // If the opened file was actually a symlink then the symlink is deleted,
+ // not the directory recursively.
+ let mut opts = OpenOptions::new();
+ opts.lookup_flags(0);
+ opts.directory(true);
+ opts.read(true);
+ let fd = open_at(parent, path, &opts)?;
+ if fd.file_attr()?.file_type().is_symlink() {
+ return parent.unlink_file(osstr2str(path.as_ref())?);
+ }
+
+ // this "root" is only used by `DirEntry::path` which we don't use below so
+ // it's ok for this to be a bogus value
+ let dummy_root = PathBuf::new();
+
+ // Iterate over all the entries in this directory, and travel recursively if
+ // necessary
+ for entry in ReadDir::new(fd, dummy_root) {
+ let entry = entry?;
+ let path = crate::str::from_utf8(&entry.name).map_err(|_| {
+ io::const_io_error!(io::ErrorKind::Uncategorized, "invalid utf-8 file name found")
+ })?;
+
+ if entry.file_type()?.is_dir() {
+ remove_dir_all_recursive(&entry.inner.dir.fd, path.as_ref())?;
+ } else {
+ entry.inner.dir.fd.unlink_file(path)?;
+ }
+ }
+
+ // Once all this directory's contents are deleted it should be safe to
+ // delete the directory tiself.
+ parent.remove_directory(osstr2str(path.as_ref())?)
+}
diff --git a/library/std/src/sys/wasi/io.rs b/library/std/src/sys/wasi/io.rs
new file mode 100644
index 000000000..ee017d13a
--- /dev/null
+++ b/library/std/src/sys/wasi/io.rs
@@ -0,0 +1,73 @@
+#![deny(unsafe_op_in_unsafe_fn)]
+
+use crate::marker::PhantomData;
+use crate::slice;
+
+#[derive(Copy, Clone)]
+#[repr(transparent)]
+pub struct IoSlice<'a> {
+ vec: wasi::Ciovec,
+ _p: PhantomData<&'a [u8]>,
+}
+
+impl<'a> IoSlice<'a> {
+ #[inline]
+ pub fn new(buf: &'a [u8]) -> IoSlice<'a> {
+ IoSlice { vec: wasi::Ciovec { buf: buf.as_ptr(), buf_len: buf.len() }, _p: PhantomData }
+ }
+
+ #[inline]
+ pub fn advance(&mut self, n: usize) {
+ if self.vec.buf_len < n {
+ panic!("advancing IoSlice beyond its length");
+ }
+
+ unsafe {
+ self.vec.buf_len -= n;
+ self.vec.buf = self.vec.buf.add(n);
+ }
+ }
+
+ #[inline]
+ pub fn as_slice(&self) -> &[u8] {
+ unsafe { slice::from_raw_parts(self.vec.buf as *const u8, self.vec.buf_len) }
+ }
+}
+
+#[repr(transparent)]
+pub struct IoSliceMut<'a> {
+ vec: wasi::Iovec,
+ _p: PhantomData<&'a mut [u8]>,
+}
+
+impl<'a> IoSliceMut<'a> {
+ #[inline]
+ pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> {
+ IoSliceMut {
+ vec: wasi::Iovec { buf: buf.as_mut_ptr(), buf_len: buf.len() },
+ _p: PhantomData,
+ }
+ }
+
+ #[inline]
+ pub fn advance(&mut self, n: usize) {
+ if self.vec.buf_len < n {
+ panic!("advancing IoSlice beyond its length");
+ }
+
+ unsafe {
+ self.vec.buf_len -= n;
+ self.vec.buf = self.vec.buf.add(n);
+ }
+ }
+
+ #[inline]
+ pub fn as_slice(&self) -> &[u8] {
+ unsafe { slice::from_raw_parts(self.vec.buf as *const u8, self.vec.buf_len) }
+ }
+
+ #[inline]
+ pub fn as_mut_slice(&mut self) -> &mut [u8] {
+ unsafe { slice::from_raw_parts_mut(self.vec.buf as *mut u8, self.vec.buf_len) }
+ }
+}
diff --git a/library/std/src/sys/wasi/mod.rs b/library/std/src/sys/wasi/mod.rs
new file mode 100644
index 000000000..683a07a34
--- /dev/null
+++ b/library/std/src/sys/wasi/mod.rs
@@ -0,0 +1,100 @@
+//! System bindings for the wasm/web platform
+//!
+//! This module contains the facade (aka platform-specific) implementations of
+//! OS level functionality for wasm. Note that this wasm is *not* the emscripten
+//! wasm, so we have no runtime here.
+//!
+//! This is all super highly experimental and not actually intended for
+//! wide/production use yet, it's still all in the experimental category. This
+//! will likely change over time.
+//!
+//! Currently all functions here are basically stubs that immediately return
+//! errors. The hope is that with a portability lint we can turn actually just
+//! remove all this and just omit parts of the standard library if we're
+//! compiling for wasm. That way it's a compile time error for something that's
+//! guaranteed to be a runtime error!
+
+use crate::io as std_io;
+use crate::mem;
+
+#[path = "../unix/alloc.rs"]
+pub mod alloc;
+pub mod args;
+#[path = "../unix/cmath.rs"]
+pub mod cmath;
+pub mod env;
+pub mod fd;
+pub mod fs;
+pub mod io;
+#[path = "../unsupported/locks/mod.rs"]
+pub mod locks;
+pub mod net;
+pub mod os;
+#[path = "../unix/os_str.rs"]
+pub mod os_str;
+#[path = "../unix/path.rs"]
+pub mod path;
+#[path = "../unsupported/pipe.rs"]
+pub mod pipe;
+#[path = "../unsupported/process.rs"]
+pub mod process;
+pub mod stdio;
+pub mod thread;
+#[path = "../unsupported/thread_local_dtor.rs"]
+pub mod thread_local_dtor;
+#[path = "../unsupported/thread_local_key.rs"]
+pub mod thread_local_key;
+pub mod time;
+
+#[path = "../unsupported/common.rs"]
+#[deny(unsafe_op_in_unsafe_fn)]
+#[allow(unused)]
+mod common;
+pub use common::*;
+
+pub fn decode_error_kind(errno: i32) -> std_io::ErrorKind {
+ use std_io::ErrorKind::*;
+ if errno > u16::MAX as i32 || errno < 0 {
+ return Uncategorized;
+ }
+
+ match errno {
+ e if e == wasi::ERRNO_CONNREFUSED.raw().into() => ConnectionRefused,
+ e if e == wasi::ERRNO_CONNRESET.raw().into() => ConnectionReset,
+ e if e == wasi::ERRNO_PERM.raw().into() || e == wasi::ERRNO_ACCES.raw().into() => {
+ PermissionDenied
+ }
+ e if e == wasi::ERRNO_PIPE.raw().into() => BrokenPipe,
+ e if e == wasi::ERRNO_NOTCONN.raw().into() => NotConnected,
+ e if e == wasi::ERRNO_CONNABORTED.raw().into() => ConnectionAborted,
+ e if e == wasi::ERRNO_ADDRNOTAVAIL.raw().into() => AddrNotAvailable,
+ e if e == wasi::ERRNO_ADDRINUSE.raw().into() => AddrInUse,
+ e if e == wasi::ERRNO_NOENT.raw().into() => NotFound,
+ e if e == wasi::ERRNO_INTR.raw().into() => Interrupted,
+ e if e == wasi::ERRNO_INVAL.raw().into() => InvalidInput,
+ e if e == wasi::ERRNO_TIMEDOUT.raw().into() => TimedOut,
+ e if e == wasi::ERRNO_EXIST.raw().into() => AlreadyExists,
+ e if e == wasi::ERRNO_AGAIN.raw().into() => WouldBlock,
+ e if e == wasi::ERRNO_NOSYS.raw().into() => Unsupported,
+ e if e == wasi::ERRNO_NOMEM.raw().into() => OutOfMemory,
+ _ => Uncategorized,
+ }
+}
+
+pub fn abort_internal() -> ! {
+ unsafe { libc::abort() }
+}
+
+pub fn hashmap_random_keys() -> (u64, u64) {
+ let mut ret = (0u64, 0u64);
+ unsafe {
+ let base = &mut ret as *mut (u64, u64) as *mut u8;
+ let len = mem::size_of_val(&ret);
+ wasi::random_get(base, len).expect("random_get failure");
+ }
+ return ret;
+}
+
+fn err2io(err: wasi::Errno) -> std_io::Error {
+ std_io::Error::from_raw_os_error(err.raw().into())
+}
diff --git a/library/std/src/sys/wasi/net.rs b/library/std/src/sys/wasi/net.rs
new file mode 100644
index 000000000..590d268c3
--- /dev/null
+++ b/library/std/src/sys/wasi/net.rs
@@ -0,0 +1,527 @@
+#![deny(unsafe_op_in_unsafe_fn)]
+
+use super::err2io;
+use super::fd::WasiFd;
+use crate::fmt;
+use crate::io::{self, IoSlice, IoSliceMut};
+use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr};
+use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd};
+use crate::sys::unsupported;
+use crate::sys_common::{AsInner, FromInner, IntoInner};
+use crate::time::Duration;
+
+pub struct Socket(WasiFd);
+
+pub struct TcpStream {
+ inner: Socket,
+}
+
+impl AsInner<WasiFd> for Socket {
+ fn as_inner(&self) -> &WasiFd {
+ &self.0
+ }
+}
+
+impl IntoInner<WasiFd> for Socket {
+ fn into_inner(self) -> WasiFd {
+ self.0
+ }
+}
+
+impl FromInner<WasiFd> for Socket {
+ fn from_inner(inner: WasiFd) -> Socket {
+ Socket(inner)
+ }
+}
+
+impl AsFd for Socket {
+ fn as_fd(&self) -> BorrowedFd<'_> {
+ self.0.as_fd()
+ }
+}
+
+impl AsRawFd for Socket {
+ fn as_raw_fd(&self) -> RawFd {
+ self.0.as_raw_fd()
+ }
+}
+
+impl IntoRawFd for Socket {
+ fn into_raw_fd(self) -> RawFd {
+ self.0.into_raw_fd()
+ }
+}
+
+impl FromRawFd for Socket {
+ unsafe fn from_raw_fd(raw_fd: RawFd) -> Self {
+ unsafe { Self(FromRawFd::from_raw_fd(raw_fd)) }
+ }
+}
+
+impl TcpStream {
+ pub fn connect(_: io::Result<&SocketAddr>) -> io::Result<TcpStream> {
+ unsupported()
+ }
+
+ pub fn connect_timeout(_: &SocketAddr, _: Duration) -> io::Result<TcpStream> {
+ unsupported()
+ }
+
+ pub fn set_read_timeout(&self, _: Option<Duration>) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn set_write_timeout(&self, _: Option<Duration>) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
+ unsupported()
+ }
+
+ pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
+ unsupported()
+ }
+
+ pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> {
+ unsupported()
+ }
+
+ pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
+ self.read_vectored(&mut [IoSliceMut::new(buf)])
+ }
+
+ pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
+ self.socket().as_inner().read(bufs)
+ }
+
+ pub fn is_read_vectored(&self) -> bool {
+ true
+ }
+
+ pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
+ self.write_vectored(&[IoSlice::new(buf)])
+ }
+
+ pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
+ self.socket().as_inner().write(bufs)
+ }
+
+ pub fn is_write_vectored(&self) -> bool {
+ true
+ }
+
+ pub fn peer_addr(&self) -> io::Result<SocketAddr> {
+ unsupported()
+ }
+
+ pub fn socket_addr(&self) -> io::Result<SocketAddr> {
+ unsupported()
+ }
+
+ pub fn shutdown(&self, _: Shutdown) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn duplicate(&self) -> io::Result<TcpStream> {
+ unsupported()
+ }
+
+ pub fn set_linger(&self, _: Option<Duration>) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn linger(&self) -> io::Result<Option<Duration>> {
+ unsupported()
+ }
+
+ pub fn set_nodelay(&self, _: bool) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn nodelay(&self) -> io::Result<bool> {
+ unsupported()
+ }
+
+ pub fn set_ttl(&self, _: u32) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn ttl(&self) -> io::Result<u32> {
+ unsupported()
+ }
+
+ pub fn take_error(&self) -> io::Result<Option<io::Error>> {
+ unsupported()
+ }
+
+ pub fn set_nonblocking(&self, state: bool) -> io::Result<()> {
+ let fdstat = unsafe {
+ wasi::fd_fdstat_get(self.socket().as_inner().as_raw_fd() as wasi::Fd).map_err(err2io)?
+ };
+
+ let mut flags = fdstat.fs_flags;
+
+ if state {
+ flags |= wasi::FDFLAGS_NONBLOCK;
+ } else {
+ flags &= !wasi::FDFLAGS_NONBLOCK;
+ }
+
+ unsafe {
+ wasi::fd_fdstat_set_flags(self.socket().as_inner().as_raw_fd() as wasi::Fd, flags)
+ .map_err(err2io)
+ }
+ }
+
+ pub fn socket(&self) -> &Socket {
+ &self.inner
+ }
+
+ pub fn into_socket(self) -> Socket {
+ self.inner
+ }
+}
+
+impl FromInner<Socket> for TcpStream {
+ fn from_inner(socket: Socket) -> TcpStream {
+ TcpStream { inner: socket }
+ }
+}
+
+impl fmt::Debug for TcpStream {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("TcpStream").field("fd", &self.inner.as_raw_fd()).finish()
+ }
+}
+
+pub struct TcpListener {
+ inner: Socket,
+}
+
+impl TcpListener {
+ pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<TcpListener> {
+ unsupported()
+ }
+
+ pub fn socket_addr(&self) -> io::Result<SocketAddr> {
+ unsupported()
+ }
+
+ pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> {
+ let fd = unsafe {
+ wasi::sock_accept(self.as_inner().as_inner().as_raw_fd() as _, 0).map_err(err2io)?
+ };
+
+ Ok((
+ TcpStream::from_inner(unsafe { Socket::from_raw_fd(fd as _) }),
+ // WASI has no concept of SocketAddr yet
+ // return an unspecified IPv4Addr
+ SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), 0),
+ ))
+ }
+
+ pub fn duplicate(&self) -> io::Result<TcpListener> {
+ unsupported()
+ }
+
+ pub fn set_ttl(&self, _: u32) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn ttl(&self) -> io::Result<u32> {
+ unsupported()
+ }
+
+ pub fn set_only_v6(&self, _: bool) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn only_v6(&self) -> io::Result<bool> {
+ unsupported()
+ }
+
+ pub fn take_error(&self) -> io::Result<Option<io::Error>> {
+ unsupported()
+ }
+
+ pub fn set_nonblocking(&self, state: bool) -> io::Result<()> {
+ let fdstat = unsafe {
+ wasi::fd_fdstat_get(self.socket().as_inner().as_raw_fd() as wasi::Fd).map_err(err2io)?
+ };
+
+ let mut flags = fdstat.fs_flags;
+
+ if state {
+ flags |= wasi::FDFLAGS_NONBLOCK;
+ } else {
+ flags &= !wasi::FDFLAGS_NONBLOCK;
+ }
+
+ unsafe {
+ wasi::fd_fdstat_set_flags(self.socket().as_inner().as_raw_fd() as wasi::Fd, flags)
+ .map_err(err2io)
+ }
+ }
+
+ pub fn socket(&self) -> &Socket {
+ &self.inner
+ }
+
+ pub fn into_socket(self) -> Socket {
+ self.inner
+ }
+}
+
+impl AsInner<Socket> for TcpListener {
+ fn as_inner(&self) -> &Socket {
+ &self.inner
+ }
+}
+
+impl IntoInner<Socket> for TcpListener {
+ fn into_inner(self) -> Socket {
+ self.inner
+ }
+}
+
+impl FromInner<Socket> for TcpListener {
+ fn from_inner(inner: Socket) -> TcpListener {
+ TcpListener { inner }
+ }
+}
+
+impl fmt::Debug for TcpListener {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("TcpListener").field("fd", &self.inner.as_raw_fd()).finish()
+ }
+}
+
+pub struct UdpSocket {
+ inner: Socket,
+}
+
+impl UdpSocket {
+ pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<UdpSocket> {
+ unsupported()
+ }
+
+ pub fn peer_addr(&self) -> io::Result<SocketAddr> {
+ unsupported()
+ }
+
+ pub fn socket_addr(&self) -> io::Result<SocketAddr> {
+ unsupported()
+ }
+
+ pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
+ unsupported()
+ }
+
+ pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
+ unsupported()
+ }
+
+ pub fn send_to(&self, _: &[u8], _: &SocketAddr) -> io::Result<usize> {
+ unsupported()
+ }
+
+ pub fn duplicate(&self) -> io::Result<UdpSocket> {
+ unsupported()
+ }
+
+ pub fn set_read_timeout(&self, _: Option<Duration>) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn set_write_timeout(&self, _: Option<Duration>) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
+ unsupported()
+ }
+
+ pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
+ unsupported()
+ }
+
+ pub fn set_broadcast(&self, _: bool) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn broadcast(&self) -> io::Result<bool> {
+ unsupported()
+ }
+
+ pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn multicast_loop_v4(&self) -> io::Result<bool> {
+ unsupported()
+ }
+
+ pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn multicast_ttl_v4(&self) -> io::Result<u32> {
+ unsupported()
+ }
+
+ pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn multicast_loop_v6(&self) -> io::Result<bool> {
+ unsupported()
+ }
+
+ pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn set_ttl(&self, _: u32) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn ttl(&self) -> io::Result<u32> {
+ unsupported()
+ }
+
+ pub fn take_error(&self) -> io::Result<Option<io::Error>> {
+ unsupported()
+ }
+
+ pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn recv(&self, _: &mut [u8]) -> io::Result<usize> {
+ unsupported()
+ }
+
+ pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> {
+ unsupported()
+ }
+
+ pub fn send(&self, _: &[u8]) -> io::Result<usize> {
+ unsupported()
+ }
+
+ pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn socket(&self) -> &Socket {
+ &self.inner
+ }
+
+ pub fn into_socket(self) -> Socket {
+ self.inner
+ }
+}
+
+impl AsInner<Socket> for UdpSocket {
+ fn as_inner(&self) -> &Socket {
+ &self.inner
+ }
+}
+
+impl IntoInner<Socket> for UdpSocket {
+ fn into_inner(self) -> Socket {
+ self.inner
+ }
+}
+
+impl FromInner<Socket> for UdpSocket {
+ fn from_inner(inner: Socket) -> UdpSocket {
+ UdpSocket { inner }
+ }
+}
+
+impl fmt::Debug for UdpSocket {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("UdpSocket").field("fd", &self.inner.as_raw_fd()).finish()
+ }
+}
+
+pub struct LookupHost(!);
+
+impl LookupHost {
+ pub fn port(&self) -> u16 {
+ self.0
+ }
+}
+
+impl Iterator for LookupHost {
+ type Item = SocketAddr;
+ fn next(&mut self) -> Option<SocketAddr> {
+ self.0
+ }
+}
+
+impl<'a> TryFrom<&'a str> for LookupHost {
+ type Error = io::Error;
+
+ fn try_from(_v: &'a str) -> io::Result<LookupHost> {
+ unsupported()
+ }
+}
+
+impl<'a> TryFrom<(&'a str, u16)> for LookupHost {
+ type Error = io::Error;
+
+ fn try_from(_v: (&'a str, u16)) -> io::Result<LookupHost> {
+ unsupported()
+ }
+}
+
+#[allow(nonstandard_style)]
+pub mod netc {
+ pub const AF_INET: u8 = 0;
+ pub const AF_INET6: u8 = 1;
+ pub type sa_family_t = u8;
+
+ #[derive(Copy, Clone)]
+ pub struct in_addr {
+ pub s_addr: u32,
+ }
+
+ #[derive(Copy, Clone)]
+ pub struct sockaddr_in {
+ pub sin_family: sa_family_t,
+ pub sin_port: u16,
+ pub sin_addr: in_addr,
+ }
+
+ #[derive(Copy, Clone)]
+ pub struct in6_addr {
+ pub s6_addr: [u8; 16],
+ }
+
+ #[derive(Copy, Clone)]
+ pub struct sockaddr_in6 {
+ pub sin6_family: sa_family_t,
+ pub sin6_port: u16,
+ pub sin6_addr: in6_addr,
+ pub sin6_flowinfo: u32,
+ pub sin6_scope_id: u32,
+ }
+
+ #[derive(Copy, Clone)]
+ pub struct sockaddr {}
+}
diff --git a/library/std/src/sys/wasi/os.rs b/library/std/src/sys/wasi/os.rs
new file mode 100644
index 000000000..c5229a188
--- /dev/null
+++ b/library/std/src/sys/wasi/os.rs
@@ -0,0 +1,243 @@
+#![deny(unsafe_op_in_unsafe_fn)]
+
+use crate::any::Any;
+use crate::error::Error as StdError;
+use crate::ffi::{CStr, CString, OsStr, OsString};
+use crate::fmt;
+use crate::io;
+use crate::marker::PhantomData;
+use crate::os::wasi::prelude::*;
+use crate::path::{self, PathBuf};
+use crate::str;
+use crate::sys::memchr;
+use crate::sys::unsupported;
+use crate::vec;
+
+// Add a few symbols not in upstream `libc` just yet.
+mod libc {
+ pub use libc::*;
+
+ extern "C" {
+ pub fn getcwd(buf: *mut c_char, size: size_t) -> *mut c_char;
+ pub fn chdir(dir: *const c_char) -> c_int;
+ }
+}
+
+#[cfg(not(target_feature = "atomics"))]
+pub unsafe fn env_lock() -> impl Any {
+ // No need for a lock if we're single-threaded, but this function will need
+ // to get implemented for multi-threaded scenarios
+}
+
+pub fn errno() -> i32 {
+ extern "C" {
+ #[thread_local]
+ static errno: libc::c_int;
+ }
+
+ unsafe { errno as i32 }
+}
+
+pub fn error_string(errno: i32) -> String {
+ let mut buf = [0 as libc::c_char; 1024];
+
+ let p = buf.as_mut_ptr();
+ unsafe {
+ if libc::strerror_r(errno as libc::c_int, p, buf.len()) < 0 {
+ panic!("strerror_r failure");
+ }
+ str::from_utf8(CStr::from_ptr(p).to_bytes()).unwrap().to_owned()
+ }
+}
+
+pub fn getcwd() -> io::Result<PathBuf> {
+ let mut buf = Vec::with_capacity(512);
+ loop {
+ unsafe {
+ let ptr = buf.as_mut_ptr() as *mut libc::c_char;
+ if !libc::getcwd(ptr, buf.capacity()).is_null() {
+ let len = CStr::from_ptr(buf.as_ptr() as *const libc::c_char).to_bytes().len();
+ buf.set_len(len);
+ buf.shrink_to_fit();
+ return Ok(PathBuf::from(OsString::from_vec(buf)));
+ } else {
+ let error = io::Error::last_os_error();
+ if error.raw_os_error() != Some(libc::ERANGE) {
+ return Err(error);
+ }
+ }
+
+ // Trigger the internal buffer resizing logic of `Vec` by requiring
+ // more space than the current capacity.
+ let cap = buf.capacity();
+ buf.set_len(cap);
+ buf.reserve(1);
+ }
+ }
+}
+
+pub fn chdir(p: &path::Path) -> io::Result<()> {
+ let p: &OsStr = p.as_ref();
+ let p = CString::new(p.as_bytes())?;
+ unsafe {
+ match libc::chdir(p.as_ptr()) == (0 as libc::c_int) {
+ true => Ok(()),
+ false => Err(io::Error::last_os_error()),
+ }
+ }
+}
+
+pub struct SplitPaths<'a>(!, PhantomData<&'a ()>);
+
+pub fn split_paths(_unparsed: &OsStr) -> SplitPaths<'_> {
+ panic!("unsupported")
+}
+
+impl<'a> Iterator for SplitPaths<'a> {
+ type Item = PathBuf;
+ fn next(&mut self) -> Option<PathBuf> {
+ self.0
+ }
+}
+
+#[derive(Debug)]
+pub struct JoinPathsError;
+
+pub fn join_paths<I, T>(_paths: I) -> Result<OsString, JoinPathsError>
+where
+ I: Iterator<Item = T>,
+ T: AsRef<OsStr>,
+{
+ Err(JoinPathsError)
+}
+
+impl fmt::Display for JoinPathsError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ "not supported on wasm yet".fmt(f)
+ }
+}
+
+impl StdError for JoinPathsError {
+ #[allow(deprecated)]
+ fn description(&self) -> &str {
+ "not supported on wasm yet"
+ }
+}
+
+pub fn current_exe() -> io::Result<PathBuf> {
+ unsupported()
+}
+pub struct Env {
+ iter: vec::IntoIter<(OsString, OsString)>,
+}
+
+impl !Send for Env {}
+impl !Sync for Env {}
+
+impl Iterator for Env {
+ type Item = (OsString, OsString);
+ fn next(&mut self) -> Option<(OsString, OsString)> {
+ self.iter.next()
+ }
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ self.iter.size_hint()
+ }
+}
+
+pub fn env() -> Env {
+ unsafe {
+ let _guard = env_lock();
+ let mut environ = libc::environ;
+ let mut result = Vec::new();
+ if !environ.is_null() {
+ while !(*environ).is_null() {
+ if let Some(key_value) = parse(CStr::from_ptr(*environ).to_bytes()) {
+ result.push(key_value);
+ }
+ environ = environ.add(1);
+ }
+ }
+ return Env { iter: result.into_iter() };
+ }
+
+ // See src/libstd/sys/unix/os.rs, same as that
+ fn parse(input: &[u8]) -> Option<(OsString, OsString)> {
+ if input.is_empty() {
+ return None;
+ }
+ let pos = memchr::memchr(b'=', &input[1..]).map(|p| p + 1);
+ pos.map(|p| {
+ (
+ OsStringExt::from_vec(input[..p].to_vec()),
+ OsStringExt::from_vec(input[p + 1..].to_vec()),
+ )
+ })
+ }
+}
+
+pub fn getenv(k: &OsStr) -> Option<OsString> {
+ let k = CString::new(k.as_bytes()).ok()?;
+ unsafe {
+ let _guard = env_lock();
+ let s = libc::getenv(k.as_ptr()) as *const libc::c_char;
+ if s.is_null() {
+ None
+ } else {
+ Some(OsStringExt::from_vec(CStr::from_ptr(s).to_bytes().to_vec()))
+ }
+ }
+}
+
+pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
+ let k = CString::new(k.as_bytes())?;
+ let v = CString::new(v.as_bytes())?;
+
+ unsafe {
+ let _guard = env_lock();
+ cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop)
+ }
+}
+
+pub fn unsetenv(n: &OsStr) -> io::Result<()> {
+ let nbuf = CString::new(n.as_bytes())?;
+
+ unsafe {
+ let _guard = env_lock();
+ cvt(libc::unsetenv(nbuf.as_ptr())).map(drop)
+ }
+}
+
+pub fn temp_dir() -> PathBuf {
+ panic!("no filesystem on wasm")
+}
+
+pub fn home_dir() -> Option<PathBuf> {
+ None
+}
+
+pub fn exit(code: i32) -> ! {
+ unsafe { libc::exit(code) }
+}
+
+pub fn getpid() -> u32 {
+ panic!("unsupported");
+}
+
+#[doc(hidden)]
+pub trait IsMinusOne {
+ fn is_minus_one(&self) -> bool;
+}
+
+macro_rules! impl_is_minus_one {
+ ($($t:ident)*) => ($(impl IsMinusOne for $t {
+ fn is_minus_one(&self) -> bool {
+ *self == -1
+ }
+ })*)
+}
+
+impl_is_minus_one! { i8 i16 i32 i64 isize }
+
+fn cvt<T: IsMinusOne>(t: T) -> io::Result<T> {
+ if t.is_minus_one() { Err(io::Error::last_os_error()) } else { Ok(t) }
+}
diff --git a/library/std/src/sys/wasi/stdio.rs b/library/std/src/sys/wasi/stdio.rs
new file mode 100644
index 000000000..4cc0e4ed5
--- /dev/null
+++ b/library/std/src/sys/wasi/stdio.rs
@@ -0,0 +1,112 @@
+#![deny(unsafe_op_in_unsafe_fn)]
+
+use super::fd::WasiFd;
+use crate::io::{self, IoSlice, IoSliceMut};
+use crate::mem::ManuallyDrop;
+use crate::os::raw;
+use crate::os::wasi::io::{AsRawFd, FromRawFd};
+
+pub struct Stdin;
+pub struct Stdout;
+pub struct Stderr;
+
+impl Stdin {
+ pub const fn new() -> Stdin {
+ Stdin
+ }
+}
+
+impl AsRawFd for Stdin {
+ #[inline]
+ fn as_raw_fd(&self) -> raw::c_int {
+ 0
+ }
+}
+
+impl io::Read for Stdin {
+ fn read(&mut self, data: &mut [u8]) -> io::Result<usize> {
+ self.read_vectored(&mut [IoSliceMut::new(data)])
+ }
+
+ fn read_vectored(&mut self, data: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
+ ManuallyDrop::new(unsafe { WasiFd::from_raw_fd(self.as_raw_fd()) }).read(data)
+ }
+
+ #[inline]
+ fn is_read_vectored(&self) -> bool {
+ true
+ }
+}
+
+impl Stdout {
+ pub const fn new() -> Stdout {
+ Stdout
+ }
+}
+
+impl AsRawFd for Stdout {
+ #[inline]
+ fn as_raw_fd(&self) -> raw::c_int {
+ 1
+ }
+}
+
+impl io::Write for Stdout {
+ fn write(&mut self, data: &[u8]) -> io::Result<usize> {
+ self.write_vectored(&[IoSlice::new(data)])
+ }
+
+ fn write_vectored(&mut self, data: &[IoSlice<'_>]) -> io::Result<usize> {
+ ManuallyDrop::new(unsafe { WasiFd::from_raw_fd(self.as_raw_fd()) }).write(data)
+ }
+
+ #[inline]
+ fn is_write_vectored(&self) -> bool {
+ true
+ }
+ fn flush(&mut self) -> io::Result<()> {
+ Ok(())
+ }
+}
+
+impl Stderr {
+ pub const fn new() -> Stderr {
+ Stderr
+ }
+}
+
+impl AsRawFd for Stderr {
+ #[inline]
+ fn as_raw_fd(&self) -> raw::c_int {
+ 2
+ }
+}
+
+impl io::Write for Stderr {
+ fn write(&mut self, data: &[u8]) -> io::Result<usize> {
+ self.write_vectored(&[IoSlice::new(data)])
+ }
+
+ fn write_vectored(&mut self, data: &[IoSlice<'_>]) -> io::Result<usize> {
+ ManuallyDrop::new(unsafe { WasiFd::from_raw_fd(self.as_raw_fd()) }).write(data)
+ }
+
+ #[inline]
+ fn is_write_vectored(&self) -> bool {
+ true
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ Ok(())
+ }
+}
+
+pub const STDIN_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE;
+
+pub fn is_ebadf(err: &io::Error) -> bool {
+ err.raw_os_error() == Some(wasi::ERRNO_BADF.raw().into())
+}
+
+pub fn panic_output() -> Option<impl io::Write> {
+ Some(Stderr::new())
+}
diff --git a/library/std/src/sys/wasi/thread.rs b/library/std/src/sys/wasi/thread.rs
new file mode 100644
index 000000000..e7a6ab4be
--- /dev/null
+++ b/library/std/src/sys/wasi/thread.rs
@@ -0,0 +1,81 @@
+#![deny(unsafe_op_in_unsafe_fn)]
+
+use crate::ffi::CStr;
+use crate::io;
+use crate::mem;
+use crate::num::NonZeroUsize;
+use crate::sys::unsupported;
+use crate::time::Duration;
+
+pub struct Thread(!);
+
+pub const DEFAULT_MIN_STACK_SIZE: usize = 4096;
+
+impl Thread {
+ // unsafe: see thread::Builder::spawn_unchecked for safety requirements
+ pub unsafe fn new(_stack: usize, _p: Box<dyn FnOnce()>) -> io::Result<Thread> {
+ unsupported()
+ }
+
+ pub fn yield_now() {
+ let ret = unsafe { wasi::sched_yield() };
+ debug_assert_eq!(ret, Ok(()));
+ }
+
+ pub fn set_name(_name: &CStr) {
+ // nope
+ }
+
+ pub fn sleep(dur: Duration) {
+ let nanos = dur.as_nanos();
+ assert!(nanos <= u64::MAX as u128);
+
+ const USERDATA: wasi::Userdata = 0x0123_45678;
+
+ let clock = wasi::SubscriptionClock {
+ id: wasi::CLOCKID_MONOTONIC,
+ timeout: nanos as u64,
+ precision: 0,
+ flags: 0,
+ };
+
+ let in_ = wasi::Subscription {
+ userdata: USERDATA,
+ u: wasi::SubscriptionU { tag: 0, u: wasi::SubscriptionUU { clock } },
+ };
+ unsafe {
+ let mut event: wasi::Event = mem::zeroed();
+ let res = wasi::poll_oneoff(&in_, &mut event, 1);
+ match (res, event) {
+ (
+ Ok(1),
+ wasi::Event {
+ userdata: USERDATA,
+ error: wasi::ERRNO_SUCCESS,
+ type_: wasi::EVENTTYPE_CLOCK,
+ ..
+ },
+ ) => {}
+ _ => panic!("thread::sleep(): unexpected result of poll_oneoff"),
+ }
+ }
+ }
+
+ pub fn join(self) {
+ self.0
+ }
+}
+
+pub fn available_parallelism() -> io::Result<NonZeroUsize> {
+ unsupported()
+}
+
+pub mod guard {
+ pub type Guard = !;
+ pub unsafe fn current() -> Option<Guard> {
+ None
+ }
+ pub unsafe fn init() -> Option<Guard> {
+ None
+ }
+}
diff --git a/library/std/src/sys/wasi/time.rs b/library/std/src/sys/wasi/time.rs
new file mode 100644
index 000000000..3d326e491
--- /dev/null
+++ b/library/std/src/sys/wasi/time.rs
@@ -0,0 +1,65 @@
+#![deny(unsafe_op_in_unsafe_fn)]
+
+use crate::time::Duration;
+
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
+pub struct Instant(Duration);
+
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
+pub struct SystemTime(Duration);
+
+pub const UNIX_EPOCH: SystemTime = SystemTime(Duration::from_secs(0));
+
+fn current_time(clock: wasi::Clockid) -> Duration {
+ let ts = unsafe {
+ wasi::clock_time_get(
+ clock, 1, // precision... seems ignored though?
+ )
+ .unwrap()
+ };
+ Duration::new((ts / 1_000_000_000) as u64, (ts % 1_000_000_000) as u32)
+}
+
+impl Instant {
+ pub fn now() -> Instant {
+ Instant(current_time(wasi::CLOCKID_MONOTONIC))
+ }
+
+ pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
+ self.0.checked_sub(other.0)
+ }
+
+ pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> {
+ Some(Instant(self.0.checked_add(*other)?))
+ }
+
+ pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> {
+ Some(Instant(self.0.checked_sub(*other)?))
+ }
+}
+
+impl SystemTime {
+ pub fn now() -> SystemTime {
+ SystemTime(current_time(wasi::CLOCKID_REALTIME))
+ }
+
+ pub fn from_wasi_timestamp(ts: wasi::Timestamp) -> SystemTime {
+ SystemTime(Duration::from_nanos(ts))
+ }
+
+ pub fn to_wasi_timestamp_or_panic(&self) -> wasi::Timestamp {
+ self.0.as_nanos().try_into().expect("time does not fit in WASI timestamp")
+ }
+
+ pub fn sub_time(&self, other: &SystemTime) -> Result<Duration, Duration> {
+ self.0.checked_sub(other.0).ok_or_else(|| other.0 - self.0)
+ }
+
+ pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> {
+ Some(SystemTime(self.0.checked_add(*other)?))
+ }
+
+ pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> {
+ Some(SystemTime(self.0.checked_sub(*other)?))
+ }
+}