From 0f85778fdfd5327bd6ec1578331e0ffbff3030fd Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Fri, 21 Jan 2022 14:42:43 +0100 Subject: [PATCH] Implement `sock_accept` Signed-off-by: Harald Hoyer --- Cargo.lock | 2 + crates/wasi-common/WASI | 2 +- crates/wasi-common/cap-std-sync/Cargo.toml | 1 + crates/wasi-common/cap-std-sync/src/file.rs | 14 + crates/wasi-common/cap-std-sync/src/lib.rs | 21 +- crates/wasi-common/cap-std-sync/src/net.rs | 370 ++++++++++++++++++ .../cap-std-sync/src/sched/unix.rs | 12 + .../cap-std-sync/src/sched/windows.rs | 31 +- crates/wasi-common/cap-std-sync/src/stdio.rs | 23 ++ crates/wasi-common/src/error.rs | 3 + crates/wasi-common/src/file.rs | 1 + crates/wasi-common/src/pipe.rs | 8 + crates/wasi-common/src/snapshots/preview_1.rs | 24 ++ crates/wasi-common/tokio/Cargo.toml | 1 + crates/wasi-common/tokio/src/file.rs | 69 +++- crates/wasi-common/tokio/src/lib.rs | 21 + crates/wasi-common/tokio/src/net.rs | 6 + crates/wasi-common/tokio/src/sched/windows.rs | 29 +- 18 files changed, 608 insertions(+), 30 deletions(-) create mode 100644 crates/wasi-common/cap-std-sync/src/net.rs create mode 100644 crates/wasi-common/tokio/src/net.rs diff --git a/Cargo.lock b/Cargo.lock index 488e4f7b3aa7..7cbce8960687 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3135,6 +3135,7 @@ dependencies = [ "cap-std", "cap-time-ext", "fs-set-times", + "io-extras", "io-lifetimes", "lazy_static", "rustix", @@ -3196,6 +3197,7 @@ dependencies = [ "anyhow", "cap-std", "cap-tempfile", + "io-extras", "io-lifetimes", "lazy_static", "rustix", diff --git a/crates/wasi-common/WASI b/crates/wasi-common/WASI index 5ab83a68d4eb..0ba0c5e2e376 160000 --- a/crates/wasi-common/WASI +++ b/crates/wasi-common/WASI @@ -1 +1 @@ -Subproject commit 5ab83a68d4eb4f218a898ed03b963b7393caaedc +Subproject commit 0ba0c5e2e37625ca5a6d3e4255a998dfaa3efc52 diff --git a/crates/wasi-common/cap-std-sync/Cargo.toml b/crates/wasi-common/cap-std-sync/Cargo.toml index ac85ee8a7962..c8c18dad60c6 100644 --- a/crates/wasi-common/cap-std-sync/Cargo.toml +++ b/crates/wasi-common/cap-std-sync/Cargo.toml @@ -31,6 +31,7 @@ rustix = "0.31.0" winapi = "0.3" lazy_static = "1.4" atty = "0.2.14" +io-extras = "0.12.0" [dev-dependencies] tempfile = "3.1.0" diff --git a/crates/wasi-common/cap-std-sync/src/file.rs b/crates/wasi-common/cap-std-sync/src/file.rs index b789c94e2b69..07bc0bcbf04b 100644 --- a/crates/wasi-common/cap-std-sync/src/file.rs +++ b/crates/wasi-common/cap-std-sync/src/file.rs @@ -25,6 +25,9 @@ impl WasiFile for File { fn as_any(&self) -> &dyn Any { self } + async fn sock_accept(&mut self, _fdflags: FdFlags) -> Result, Error> { + Err(Error::badf()) + } async fn datasync(&self) -> Result<(), Error> { self.0.sync_data()?; Ok(()) @@ -171,8 +174,19 @@ impl AsHandle for File { } } +#[cfg(windows)] +use io_extras::os::windows::{AsRawHandleOrSocket, RawHandleOrSocket}; +#[cfg(windows)] +impl AsRawHandleOrSocket for File { + #[inline] + fn as_raw_handle_or_socket(&self) -> RawHandleOrSocket { + self.0.as_raw_handle_or_socket() + } +} + #[cfg(unix)] use io_lifetimes::{AsFd, BorrowedFd}; + #[cfg(unix)] impl AsFd for File { fn as_fd(&self) -> BorrowedFd<'_> { diff --git a/crates/wasi-common/cap-std-sync/src/lib.rs b/crates/wasi-common/cap-std-sync/src/lib.rs index 72875ad4921d..9d83dbcf65ae 100644 --- a/crates/wasi-common/cap-std-sync/src/lib.rs +++ b/crates/wasi-common/cap-std-sync/src/lib.rs @@ -36,17 +36,20 @@ pub mod clocks; pub mod dir; pub mod file; +pub mod net; pub mod sched; pub mod stdio; pub use cap_std::ambient_authority; pub use cap_std::fs::Dir; +pub use cap_std::net::TcpListener; pub use clocks::clocks_ctx; pub use sched::sched_ctx; +use crate::net::Listener; use cap_rand::RngCore; use std::path::Path; -use wasi_common::{table::Table, Error, WasiCtx, WasiFile}; +use wasi_common::{file::FileCaps, table::Table, Error, WasiCtx, WasiFile}; pub struct WasiCtxBuilder(WasiCtx); @@ -120,6 +123,22 @@ impl WasiCtxBuilder { self.0.push_preopened_dir(dir, guest_path)?; Ok(self) } + pub fn preopened_listener( + mut self, + fd: u32, + listener: impl Into, + ) -> Result { + let listener: Listener = listener.into(); + let file: Box = listener.into(); + + let caps = FileCaps::FDSTAT_SET_FLAGS + | FileCaps::FILESTAT_GET + | FileCaps::READ + | FileCaps::POLL_READWRITE; + + self.0.insert_file(fd, file, caps); + Ok(self) + } pub fn build(self) -> WasiCtx { self.0 } diff --git a/crates/wasi-common/cap-std-sync/src/net.rs b/crates/wasi-common/cap-std-sync/src/net.rs new file mode 100644 index 000000000000..6f2ae19479a2 --- /dev/null +++ b/crates/wasi-common/cap-std-sync/src/net.rs @@ -0,0 +1,370 @@ +#[cfg(windows)] +use io_extras::os::windows::{AsRawHandleOrSocket, RawHandleOrSocket}; +use io_lifetimes::AsSocketlike; +#[cfg(unix)] +use io_lifetimes::{AsFd, BorrowedFd}; +#[cfg(windows)] +use io_lifetimes::{AsSocket, BorrowedSocket}; +use std::any::Any; +use std::convert::TryInto; +use std::io; +use system_interface::fs::FileIoExt; +use system_interface::io::IsReadWrite; +use system_interface::io::ReadReady; +use wasi_common::{ + file::{Advice, FdFlags, FileType, Filestat, WasiFile}, + Error, ErrorExt, +}; + +pub enum Listener { + TcpListener(cap_std::net::TcpListener), + #[cfg(unix)] + UnixListener(cap_std::os::unix::net::UnixListener), +} + +impl From for Listener { + fn from(listener: cap_std::net::TcpListener) -> Self { + Self::TcpListener(listener) + } +} + +#[cfg(unix)] +impl From for Listener { + fn from(listener: cap_std::os::unix::net::UnixListener) -> Self { + Self::UnixListener(listener) + } +} + +#[cfg(unix)] +impl From for Box { + fn from(listener: Listener) -> Self { + match listener { + Listener::TcpListener(l) => Box::new(crate::net::TcpListener::from_cap_std(l)), + Listener::UnixListener(l) => Box::new(crate::net::UnixListener::from_cap_std(l)), + } + } +} + +#[cfg(windows)] +impl From for Box { + fn from(listener: Listener) -> Self { + match listener { + Listener::TcpListener(l) => Box::new(crate::net::TcpListener::from_cap_std(l)), + } + } +} + +macro_rules! wasi_listen_write_impl { + ($ty:ty, $stream:ty) => { + #[async_trait::async_trait] + impl WasiFile for $ty { + fn as_any(&self) -> &dyn Any { + self + } + async fn sock_accept(&mut self, fdflags: FdFlags) -> Result, Error> { + let (stream, _) = self.0.accept()?; + let mut stream = <$stream>::from_cap_std(stream); + stream.set_fdflags(fdflags).await?; + Ok(Box::new(stream)) + } + async fn datasync(&self) -> Result<(), Error> { + Err(Error::badf()) + } + async fn sync(&self) -> Result<(), Error> { + Err(Error::badf()) + } + async fn get_filetype(&self) -> Result { + Ok(FileType::SocketStream) + } + async fn get_fdflags(&self) -> Result { + Err(Error::badf()) + } + async fn set_fdflags(&mut self, fdflags: FdFlags) -> Result<(), Error> { + if fdflags == wasi_common::file::FdFlags::NONBLOCK { + self.0.set_nonblocking(true)?; + } else if fdflags.is_empty() { + self.0.set_nonblocking(false)?; + } else { + return Err( + Error::invalid_argument().context("cannot set anything else than NONBLOCK") + ); + } + Ok(()) + } + async fn get_filestat(&self) -> Result { + Err(Error::badf()) + } + async fn set_filestat_size(&self, _size: u64) -> Result<(), Error> { + Err(Error::badf()) + } + async fn advise(&self, _offset: u64, _len: u64, _advice: Advice) -> Result<(), Error> { + Err(Error::badf()) + } + async fn allocate(&self, _offset: u64, _len: u64) -> Result<(), Error> { + Err(Error::badf()) + } + async fn set_times( + &self, + _atime: Option, + _mtime: Option, + ) -> Result<(), Error> { + Err(Error::badf()) + } + async fn read_vectored<'a>( + &self, + _bufs: &mut [io::IoSliceMut<'a>], + ) -> Result { + Err(Error::badf()) + } + async fn read_vectored_at<'a>( + &self, + _bufs: &mut [io::IoSliceMut<'a>], + _offset: u64, + ) -> Result { + Err(Error::badf()) + } + async fn write_vectored<'a>(&self, _bufs: &[io::IoSlice<'a>]) -> Result { + Err(Error::badf()) + } + async fn write_vectored_at<'a>( + &self, + _bufs: &[io::IoSlice<'a>], + _offset: u64, + ) -> Result { + Err(Error::badf()) + } + async fn seek(&self, _pos: std::io::SeekFrom) -> Result { + Err(Error::badf()) + } + async fn peek(&self, _buf: &mut [u8]) -> Result { + Err(Error::badf()) + } + async fn num_ready_bytes(&self) -> Result { + Ok(1) + } + fn isatty(&self) -> bool { + false + } + async fn readable(&self) -> Result<(), Error> { + Err(Error::badf()) + } + async fn writable(&self) -> Result<(), Error> { + Err(Error::badf()) + } + } + + #[cfg(windows)] + impl AsSocket for $ty { + #[inline] + fn as_socket(&self) -> BorrowedSocket<'_> { + self.0.as_socket() + } + } + + #[cfg(windows)] + impl AsRawHandleOrSocket for $ty { + #[inline] + fn as_raw_handle_or_socket(&self) -> RawHandleOrSocket { + self.0.as_raw_handle_or_socket() + } + } + + #[cfg(unix)] + impl AsFd for $ty { + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } + } + }; +} + +pub struct TcpListener(cap_std::net::TcpListener); + +impl TcpListener { + pub fn from_cap_std(cap_std: cap_std::net::TcpListener) -> Self { + TcpListener(cap_std) + } +} +wasi_listen_write_impl!(TcpListener, TcpStream); + +pub struct UnixListener(cap_std::os::unix::net::UnixListener); + +impl UnixListener { + pub fn from_cap_std(cap_std: cap_std::os::unix::net::UnixListener) -> Self { + UnixListener(cap_std) + } +} +wasi_listen_write_impl!(UnixListener, UnixStream); + +macro_rules! wasi_stream_write_impl { + ($ty:ty, $std_ty:ty) => { + #[async_trait::async_trait] + impl WasiFile for $ty { + fn as_any(&self) -> &dyn Any { + self + } + async fn sock_accept(&mut self, _fdflags: FdFlags) -> Result, Error> { + Err(Error::badf()) + } + async fn datasync(&self) -> Result<(), Error> { + Err(Error::badf()) + } + async fn sync(&self) -> Result<(), Error> { + Err(Error::badf()) + } + async fn get_filetype(&self) -> Result { + Ok(FileType::SocketStream) + } + async fn get_fdflags(&self) -> Result { + Err(Error::badf()) + } + async fn set_fdflags(&mut self, fdflags: FdFlags) -> Result<(), Error> { + if fdflags == wasi_common::file::FdFlags::NONBLOCK { + self.0.set_nonblocking(true)?; + } else if fdflags.is_empty() { + self.0.set_nonblocking(false)?; + } else { + return Err( + Error::invalid_argument().context("cannot set anything else than NONBLOCK") + ); + } + Ok(()) + } + async fn get_filestat(&self) -> Result { + Err(Error::badf()) + } + async fn set_filestat_size(&self, _size: u64) -> Result<(), Error> { + Err(Error::badf()) + } + async fn advise(&self, _offset: u64, _len: u64, _advice: Advice) -> Result<(), Error> { + Err(Error::badf()) + } + async fn allocate(&self, _offset: u64, _len: u64) -> Result<(), Error> { + Err(Error::badf()) + } + async fn set_times( + &self, + _atime: Option, + _mtime: Option, + ) -> Result<(), Error> { + Err(Error::badf()) + } + async fn read_vectored<'a>( + &self, + bufs: &mut [io::IoSliceMut<'a>], + ) -> Result { + use std::io::Read; + let n = Read::read_vectored(&mut *self.as_socketlike_view::<$std_ty>(), bufs)?; + Ok(n.try_into()?) + } + async fn read_vectored_at<'a>( + &self, + _bufs: &mut [io::IoSliceMut<'a>], + _offset: u64, + ) -> Result { + Err(Error::badf()) + } + async fn write_vectored<'a>(&self, bufs: &[io::IoSlice<'a>]) -> Result { + use std::io::Write; + let n = Write::write_vectored(&mut *self.as_socketlike_view::<$std_ty>(), bufs)?; + Ok(n.try_into()?) + } + async fn write_vectored_at<'a>( + &self, + _bufs: &[io::IoSlice<'a>], + _offset: u64, + ) -> Result { + Err(Error::badf()) + } + async fn seek(&self, _pos: std::io::SeekFrom) -> Result { + Err(Error::badf()) + } + async fn peek(&self, buf: &mut [u8]) -> Result { + let n = self.0.peek(buf)?; + Ok(n.try_into()?) + } + async fn num_ready_bytes(&self) -> Result { + let val = self.as_socketlike_view::<$std_ty>().num_ready_bytes()?; + Ok(val) + } + fn isatty(&self) -> bool { + false + } + async fn readable(&self) -> Result<(), Error> { + let (readable, _writeable) = self.0.is_read_write()?; + if readable { + Ok(()) + } else { + Err(Error::io()) + } + } + async fn writable(&self) -> Result<(), Error> { + let (_readable, writeable) = self.0.is_read_write()?; + if writeable { + Ok(()) + } else { + Err(Error::io()) + } + } + } + #[cfg(unix)] + impl AsFd for $ty { + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } + } + + #[cfg(windows)] + impl AsSocket for $ty { + /// Borrows the socket. + fn as_socket(&self) -> BorrowedSocket<'_> { + self.0.as_socket() + } + } + + #[cfg(windows)] + impl AsRawHandleOrSocket for TcpStream { + #[inline] + fn as_raw_handle_or_socket(&self) -> RawHandleOrSocket { + self.0.as_raw_handle_or_socket() + } + } + }; +} + +pub struct TcpStream(cap_std::net::TcpStream); + +impl TcpStream { + pub fn from_cap_std(socket: cap_std::net::TcpStream) -> Self { + TcpStream(socket) + } +} + +wasi_stream_write_impl!(TcpStream, std::net::TcpStream); + +pub struct UnixStream(cap_std::os::unix::net::UnixStream); + +impl UnixStream { + pub fn from_cap_std(socket: cap_std::os::unix::net::UnixStream) -> Self { + UnixStream(socket) + } +} + +wasi_stream_write_impl!(UnixStream, std::os::unix::net::UnixStream); + +pub fn filetype_from(ft: &cap_std::fs::FileType) -> FileType { + use cap_fs_ext::FileTypeExt; + if ft.is_block_device() { + FileType::SocketDgram + } else { + FileType::SocketStream + } +} + +pub fn from_sysif_fdflags(f: system_interface::fs::FdFlags) -> wasi_common::file::FdFlags { + let mut out = wasi_common::file::FdFlags::empty(); + if f.contains(system_interface::fs::FdFlags::NONBLOCK) { + out |= wasi_common::file::FdFlags::NONBLOCK; + } + out +} diff --git a/crates/wasi-common/cap-std-sync/src/sched/unix.rs b/crates/wasi-common/cap-std-sync/src/sched/unix.rs index 767f1704f8af..0e1114d3b673 100644 --- a/crates/wasi-common/cap-std-sync/src/sched/unix.rs +++ b/crates/wasi-common/cap-std-sync/src/sched/unix.rs @@ -90,6 +90,18 @@ fn wasi_file_fd(f: &dyn WasiFile) -> Option> { let a = f.as_any(); if a.is::() { Some(a.downcast_ref::().unwrap().as_fd()) + } else if a.is::() { + Some(a.downcast_ref::().unwrap().as_fd()) + } else if a.is::() { + Some(a.downcast_ref::().unwrap().as_fd()) + } else if a.is::() { + Some(a.downcast_ref::().unwrap().as_fd()) + } else if a.is::() { + Some( + a.downcast_ref::() + .unwrap() + .as_fd(), + ) } else if a.is::() { Some(a.downcast_ref::().unwrap().as_fd()) } else if a.is::() { diff --git a/crates/wasi-common/cap-std-sync/src/sched/windows.rs b/crates/wasi-common/cap-std-sync/src/sched/windows.rs index e4755093c5f1..b0d913bb3e55 100644 --- a/crates/wasi-common/cap-std-sync/src/sched/windows.rs +++ b/crates/wasi-common/cap-std-sync/src/sched/windows.rs @@ -9,9 +9,8 @@ // taken the time to improve it. See bug #2880. use anyhow::Context; -use io_lifetimes::AsHandle; +use io_extras::os::windows::{AsRawHandleOrSocket, RawHandleOrSocket}; use std::ops::Deref; -use std::os::windows::io::{AsRawHandle, RawHandle}; use std::sync::mpsc::{self, Receiver, RecvTimeoutError, Sender, TryRecvError}; use std::sync::Mutex; use std::thread; @@ -33,7 +32,7 @@ pub async fn poll_oneoff<'a>(poll: &mut Poll<'a>) -> Result<(), Error> { pub async fn poll_oneoff_<'a>( poll: &mut Poll<'a>, file_is_stdin: impl Fn(&dyn WasiFile) -> bool, - file_to_handle: impl Fn(&dyn WasiFile) -> Option, + file_to_handle: impl Fn(&dyn WasiFile) -> Option, ) -> Result<(), Error> { if poll.is_empty() { return Ok(()); @@ -140,35 +139,43 @@ pub fn wasi_file_is_stdin(f: &dyn WasiFile) -> bool { f.as_any().is::() } -pub fn wasi_file_raw_handle(f: &dyn WasiFile) -> Option { +pub fn wasi_file_raw_handle(f: &dyn WasiFile) -> Option { let a = f.as_any(); if a.is::() { Some( a.downcast_ref::() .unwrap() - .as_handle() - .as_raw_handle(), + .as_raw_handle_or_socket(), + ) + } else if a.is::() { + Some( + a.downcast_ref::() + .unwrap() + .as_raw_handle_or_socket(), + ) + } else if a.is::() { + Some( + a.downcast_ref::() + .unwrap() + .as_raw_handle_or_socket(), ) } else if a.is::() { Some( a.downcast_ref::() .unwrap() - .as_handle() - .as_raw_handle(), + .as_raw_handle_or_socket(), ) } else if a.is::() { Some( a.downcast_ref::() .unwrap() - .as_handle() - .as_raw_handle(), + .as_raw_handle_or_socket(), ) } else if a.is::() { Some( a.downcast_ref::() .unwrap() - .as_handle() - .as_raw_handle(), + .as_raw_handle_or_socket(), ) } else { None diff --git a/crates/wasi-common/cap-std-sync/src/stdio.rs b/crates/wasi-common/cap-std-sync/src/stdio.rs index db0f5f6ea338..b47cc8a715e1 100644 --- a/crates/wasi-common/cap-std-sync/src/stdio.rs +++ b/crates/wasi-common/cap-std-sync/src/stdio.rs @@ -8,6 +8,8 @@ use std::io; use std::io::{Read, Write}; use system_interface::io::ReadReady; +#[cfg(windows)] +use io_extras::os::windows::{AsRawHandleOrSocket, RawHandleOrSocket}; #[cfg(unix)] use io_lifetimes::{AsFd, BorrowedFd}; #[cfg(windows)] @@ -124,6 +126,10 @@ impl WasiFile for Stdin { async fn writable(&self) -> Result<(), Error> { Err(Error::badf()) } + + async fn sock_accept(&mut self, _fdflags: FdFlags) -> Result, Error> { + Err(Error::badf()) + } } #[cfg(windows)] impl AsHandle for Stdin { @@ -131,6 +137,13 @@ impl AsHandle for Stdin { self.0.as_handle() } } +#[cfg(windows)] +impl AsRawHandleOrSocket for Stdin { + #[inline] + fn as_raw_handle_or_socket(&self) -> RawHandleOrSocket { + self.0.as_raw_handle_or_socket() + } +} #[cfg(unix)] impl AsFd for Stdin { fn as_fd(&self) -> BorrowedFd<'_> { @@ -244,6 +257,9 @@ macro_rules! wasi_file_write_impl { async fn writable(&self) -> Result<(), Error> { Err(Error::badf()) } + async fn sock_accept(&mut self, _fdflags: FdFlags) -> Result, Error> { + Err(Error::badf()) + } } #[cfg(windows)] impl AsHandle for $ty { @@ -257,6 +273,13 @@ macro_rules! wasi_file_write_impl { self.0.as_fd() } } + #[cfg(windows)] + impl AsRawHandleOrSocket for $ty { + #[inline] + fn as_raw_handle_or_socket(&self) -> RawHandleOrSocket { + self.0.as_raw_handle_or_socket() + } + } }; } diff --git a/crates/wasi-common/src/error.rs b/crates/wasi-common/src/error.rs index 20277554fc2e..e843a11766a9 100644 --- a/crates/wasi-common/src/error.rs +++ b/crates/wasi-common/src/error.rs @@ -30,6 +30,9 @@ pub use anyhow::{Context, Error}; /// the crate. Not all values are represented presently. #[derive(Debug, thiserror::Error)] pub enum ErrorKind { + /// Errno::WouldBlk: Would block + #[error("WouldBlk: Would block")] + WouldBlk, /// Errno::Noent: No such file or directory #[error("Noent: No such file or directory")] Noent, diff --git a/crates/wasi-common/src/file.rs b/crates/wasi-common/src/file.rs index f43d212ff833..8ee7b9297cb5 100644 --- a/crates/wasi-common/src/file.rs +++ b/crates/wasi-common/src/file.rs @@ -5,6 +5,7 @@ use std::any::Any; #[wiggle::async_trait] pub trait WasiFile: Send + Sync { fn as_any(&self) -> &dyn Any; + async fn sock_accept(&mut self, fdflags: FdFlags) -> Result, Error>; async fn datasync(&self) -> Result<(), Error>; // write op async fn sync(&self) -> Result<(), Error>; // file op async fn get_filetype(&self) -> Result; // file op diff --git a/crates/wasi-common/src/pipe.rs b/crates/wasi-common/src/pipe.rs index 6e8c5d9f7169..adddc1ddb8d9 100644 --- a/crates/wasi-common/src/pipe.rs +++ b/crates/wasi-common/src/pipe.rs @@ -189,6 +189,10 @@ impl WasiFile for ReadPipe { async fn writable(&self) -> Result<(), Error> { Err(Error::badf()) } + + async fn sock_accept(&mut self, fdflags: FdFlags) -> Result, Error> { + Err(Error::badf()) + } } /// A virtual pipe write end. @@ -348,4 +352,8 @@ impl WasiFile for WritePipe { async fn writable(&self) -> Result<(), Error> { Err(Error::badf()) } + + async fn sock_accept(&mut self, fdflags: FdFlags) -> Result, Error> { + Err(Error::badf()) + } } diff --git a/crates/wasi-common/src/snapshots/preview_1.rs b/crates/wasi-common/src/snapshots/preview_1.rs index 5491a8d90092..8a5dcbb415d2 100644 --- a/crates/wasi-common/src/snapshots/preview_1.rs +++ b/crates/wasi-common/src/snapshots/preview_1.rs @@ -70,6 +70,7 @@ impl From for types::Errno { fn from(e: ErrorKind) -> types::Errno { use types::Errno; match e { + ErrorKind::WouldBlk => Errno::Again, ErrorKind::Noent => Errno::Noent, ErrorKind::TooBig => Errno::TooBig, ErrorKind::Badf => Errno::Badf, @@ -114,6 +115,7 @@ impl TryFrom for types::Errno { fn raw_error_code(err: &std::io::Error) -> Option { use rustix::io::Error; match Error::from_io_error(err) { + Some(Error::AGAIN) => Some(types::Errno::Again), Some(Error::PIPE) => Some(types::Errno::Pipe), Some(Error::PERM) => Some(types::Errno::Perm), Some(Error::NOENT) => Some(types::Errno::Noent), @@ -147,6 +149,7 @@ impl TryFrom for types::Errno { fn raw_error_code(err: &std::io::Error) -> Option { use winapi::shared::winerror; match err.raw_os_error().map(|code| code as u32) { + Some(winerror::WSAEWOULDBLOCK) => Some(types::Errno::Again), Some(winerror::ERROR_BAD_ENVIRONMENT) => Some(types::Errno::TooBig), Some(winerror::ERROR_FILE_NOT_FOUND) => Some(types::Errno::Noent), Some(winerror::ERROR_PATH_NOT_FOUND) => Some(types::Errno::Noent), @@ -1148,6 +1151,27 @@ impl wasi_snapshot_preview1::WasiSnapshotPreview1 for WasiCtx { Ok(()) } + async fn sock_accept( + &mut self, + fd: types::Fd, + flags: types::Fdflags, + ) -> Result { + let table = self.table(); + let f = table + .get_file_mut(u32::from(fd))? + .get_cap_mut(FileCaps::READ)?; + + let file = f.sock_accept(FdFlags::from(flags)).await?; + let file_caps = FileCaps::READ + | FileCaps::WRITE + | FileCaps::FDSTAT_SET_FLAGS + | FileCaps::POLL_READWRITE + | FileCaps::FILESTAT_GET; + + let fd = table.push(Box::new(FileEntry::new(file_caps, file)))?; + Ok(types::Fd::from(fd)) + } + async fn sock_recv<'a>( &mut self, _fd: types::Fd, diff --git a/crates/wasi-common/tokio/Cargo.toml b/crates/wasi-common/tokio/Cargo.toml index 85fd40299b70..595f7c0dce56 100644 --- a/crates/wasi-common/tokio/Cargo.toml +++ b/crates/wasi-common/tokio/Cargo.toml @@ -25,6 +25,7 @@ rustix = "0.31.0" [target.'cfg(windows)'.dependencies] winapi = "0.3" lazy_static = "1.4" +io-extras = "0.12.0" [dev-dependencies] tempfile = "3.1.0" diff --git a/crates/wasi-common/tokio/src/file.rs b/crates/wasi-common/tokio/src/file.rs index 812ec2a5529a..ba368a012e16 100644 --- a/crates/wasi-common/tokio/src/file.rs +++ b/crates/wasi-common/tokio/src/file.rs @@ -1,8 +1,8 @@ use crate::block_on_dummy_executor; +#[cfg(windows)] +use io_extras::os::windows::{AsRawHandleOrSocket, RawHandleOrSocket}; #[cfg(not(windows))] use io_lifetimes::AsFd; -#[cfg(windows)] -use io_lifetimes::{AsHandle, BorrowedHandle}; use std::any::Any; use std::io; use wasi_common::{ @@ -21,6 +21,54 @@ impl File { } } +pub struct TcpListener(wasi_cap_std_sync::net::TcpListener); + +impl TcpListener { + pub(crate) fn from_inner(listener: wasi_cap_std_sync::net::TcpListener) -> Self { + TcpListener(listener) + } + pub fn from_cap_std(listener: cap_std::net::TcpListener) -> Self { + Self::from_inner(wasi_cap_std_sync::net::TcpListener::from_cap_std(listener)) + } +} + +pub struct TcpStream(wasi_cap_std_sync::net::TcpStream); + +impl TcpStream { + pub(crate) fn from_inner(stream: wasi_cap_std_sync::net::TcpStream) -> Self { + TcpStream(stream) + } + pub fn from_cap_std(stream: cap_std::net::TcpStream) -> Self { + Self::from_inner(wasi_cap_std_sync::net::TcpStream::from_cap_std(stream)) + } +} + +#[cfg(unix)] +pub struct UnixListener(wasi_cap_std_sync::net::UnixListener); + +#[cfg(unix)] +impl UnixListener { + pub(crate) fn from_inner(listener: wasi_cap_std_sync::net::UnixListener) -> Self { + UnixListener(listener) + } + pub fn from_cap_std(listener: cap_std::os::unix::net::UnixListener) -> Self { + Self::from_inner(wasi_cap_std_sync::net::UnixListener::from_cap_std(listener)) + } +} + +#[cfg(unix)] +pub struct UnixStream(wasi_cap_std_sync::net::UnixStream); + +#[cfg(unix)] +impl UnixStream { + fn from_inner(stream: wasi_cap_std_sync::net::UnixStream) -> Self { + UnixStream(stream) + } + pub fn from_cap_std(stream: cap_std::os::unix::net::UnixStream) -> Self { + Self::from_inner(wasi_cap_std_sync::net::UnixStream::from_cap_std(stream)) + } +} + pub struct Stdin(wasi_cap_std_sync::stdio::Stdin); pub fn stdin() -> Stdin { @@ -175,17 +223,28 @@ macro_rules! wasi_file_impl { use wasi_common::ErrorExt; Err(Error::badf()) } + + async fn sock_accept(&mut self, fdflags: FdFlags) -> Result, Error> { + block_on_dummy_executor(|| self.0.sock_accept(fdflags)) + } } #[cfg(windows)] - impl AsHandle for $ty { - fn as_handle(&self) -> BorrowedHandle<'_> { - self.0.as_handle() + impl AsRawHandleOrSocket for $ty { + #[inline] + fn as_raw_handle_or_socket(&self) -> RawHandleOrSocket { + self.0.as_raw_handle_or_socket() } } }; } wasi_file_impl!(File); +wasi_file_impl!(TcpListener); +wasi_file_impl!(TcpStream); +#[cfg(unix)] +wasi_file_impl!(UnixListener); +#[cfg(unix)] +wasi_file_impl!(UnixStream); wasi_file_impl!(Stdin); wasi_file_impl!(Stdout); wasi_file_impl!(Stderr); diff --git a/crates/wasi-common/tokio/src/lib.rs b/crates/wasi-common/tokio/src/lib.rs index e9e4b979e29d..8f7ed8643bf8 100644 --- a/crates/wasi-common/tokio/src/lib.rs +++ b/crates/wasi-common/tokio/src/lib.rs @@ -2,6 +2,7 @@ mod dir; mod file; +pub mod net; pub mod sched; pub mod stdio; @@ -12,6 +13,9 @@ use wasi_common::{Error, Table, WasiCtx, WasiFile}; pub use dir::Dir; pub use file::File; +pub use net::*; +use wasi_cap_std_sync::net::Listener; +use wasi_common::file::FileCaps; use crate::sched::sched_ctx; @@ -91,6 +95,23 @@ impl WasiCtxBuilder { self.0.push_preopened_dir(dir, guest_path)?; Ok(self) } + pub fn preopened_listener( + mut self, + fd: u32, + listener: impl Into, + ) -> Result { + let listener: Listener = listener.into(); + let file: Box = listener.into(); + + let caps = FileCaps::FDSTAT_SET_FLAGS + | FileCaps::FILESTAT_GET + | FileCaps::READ + | FileCaps::POLL_READWRITE; + + self.0.insert_file(fd, file, caps); + Ok(self) + } + pub fn build(self) -> WasiCtx { self.0 } diff --git a/crates/wasi-common/tokio/src/net.rs b/crates/wasi-common/tokio/src/net.rs new file mode 100644 index 000000000000..03e390a83bcd --- /dev/null +++ b/crates/wasi-common/tokio/src/net.rs @@ -0,0 +1,6 @@ +pub use crate::file::TcpListener; +pub use crate::file::TcpStream; +#[cfg(unix)] +pub use crate::file::UnixListener; +#[cfg(unix)] +pub use crate::file::UnixStream; diff --git a/crates/wasi-common/tokio/src/sched/windows.rs b/crates/wasi-common/tokio/src/sched/windows.rs index b6849c62167a..4d4ebab19fd2 100644 --- a/crates/wasi-common/tokio/src/sched/windows.rs +++ b/crates/wasi-common/tokio/src/sched/windows.rs @@ -1,6 +1,5 @@ use crate::block_on_dummy_executor; -use io_lifetimes::AsHandle; -use std::os::windows::io::{AsRawHandle, RawHandle}; +use io_extras::os::windows::{AsRawHandleOrSocket, RawHandleOrSocket}; use wasi_cap_std_sync::sched::windows::poll_oneoff_; use wasi_common::{file::WasiFile, sched::Poll, Error}; @@ -16,35 +15,43 @@ pub fn wasi_file_is_stdin(f: &dyn WasiFile) -> bool { f.as_any().is::() } -fn wasi_file_raw_handle(f: &dyn WasiFile) -> Option { +fn wasi_file_raw_handle(f: &dyn WasiFile) -> Option { let a = f.as_any(); if a.is::() { Some( a.downcast_ref::() .unwrap() - .as_handle() - .as_raw_handle(), + .as_raw_handle_or_socket(), + ) + } else if a.is::() { + Some( + a.downcast_ref::() + .unwrap() + .as_raw_handle_or_socket(), + ) + } else if a.is::() { + Some( + a.downcast_ref::() + .unwrap() + .as_raw_handle_or_socket(), ) } else if a.is::() { Some( a.downcast_ref::() .unwrap() - .as_handle() - .as_raw_handle(), + .as_raw_handle_or_socket(), ) } else if a.is::() { Some( a.downcast_ref::() .unwrap() - .as_handle() - .as_raw_handle(), + .as_raw_handle_or_socket(), ) } else if a.is::() { Some( a.downcast_ref::() .unwrap() - .as_handle() - .as_raw_handle(), + .as_raw_handle_or_socket(), ) } else { None