From 0df4e961c0e64a395e6e29f35ebbb217e507e527 Mon Sep 17 00:00:00 2001 From: Nathaniel McCallum Date: Thu, 10 Mar 2022 13:09:06 -0500 Subject: [PATCH] fix(wasi): enable all `WasiFile`s to be pollable (#3913) Currently, the use of the downcast method means that you have to use one of the hard-coded types. But Enarx needs to define its own `WasiFile` implementations. This works fine, except the resulting files cannot be used in poll because they aren't part of the hard-coded list. Replace this with an accessor method for the pollable type in `WasiFile`. Because we provide a default implementation of the method and manually implement it on all the hard-coded types, this is backwards compatible. Signed-off-by: Nathaniel McCallum --- Cargo.lock | 1 + crates/wasi-common/Cargo.toml | 1 + crates/wasi-common/cap-std-sync/src/file.rs | 9 +++ crates/wasi-common/cap-std-sync/src/net.rs | 18 +++++ .../cap-std-sync/src/sched/unix.rs | 52 +++----------- .../cap-std-sync/src/sched/windows.rs | 70 ++----------------- crates/wasi-common/cap-std-sync/src/stdio.rs | 18 +++++ crates/wasi-common/src/file.rs | 10 +++ crates/wasi-common/tokio/src/file.rs | 9 +++ crates/wasi-common/tokio/src/sched/windows.rs | 46 +----------- 10 files changed, 84 insertions(+), 150 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5e7257c120..61cedcc745 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3204,6 +3204,7 @@ dependencies = [ "bitflags", "cap-rand", "cap-std", + "io-extras", "rustix", "thiserror", "tracing", diff --git a/crates/wasi-common/Cargo.toml b/crates/wasi-common/Cargo.toml index 0b3a7d173d..9caa87c73b 100644 --- a/crates/wasi-common/Cargo.toml +++ b/crates/wasi-common/Cargo.toml @@ -30,6 +30,7 @@ bitflags = "1.2" rustix = "0.33.0" [target.'cfg(windows)'.dependencies] +io-extras = "0.13.2" winapi = "0.3" [badges] diff --git a/crates/wasi-common/cap-std-sync/src/file.rs b/crates/wasi-common/cap-std-sync/src/file.rs index 8fec43c684..bd2ff40ed9 100644 --- a/crates/wasi-common/cap-std-sync/src/file.rs +++ b/crates/wasi-common/cap-std-sync/src/file.rs @@ -26,6 +26,15 @@ impl WasiFile for File { fn as_any(&self) -> &dyn Any { self } + #[cfg(unix)] + fn pollable(&self) -> Option { + Some(self.0.as_fd()) + } + + #[cfg(windows)] + fn pollable(&self) -> Option { + Some(self.0.as_raw_handle_or_socket()) + } async fn datasync(&mut self) -> Result<(), Error> { self.0.sync_data()?; Ok(()) diff --git a/crates/wasi-common/cap-std-sync/src/net.rs b/crates/wasi-common/cap-std-sync/src/net.rs index 0fb82b5f02..ef815c9482 100644 --- a/crates/wasi-common/cap-std-sync/src/net.rs +++ b/crates/wasi-common/cap-std-sync/src/net.rs @@ -85,6 +85,15 @@ macro_rules! wasi_listen_write_impl { fn as_any(&self) -> &dyn Any { self } + #[cfg(unix)] + fn pollable(&self) -> Option { + Some(self.0.as_fd()) + } + + #[cfg(windows)] + fn pollable(&self) -> Option { + Some(self.0.as_raw_handle_or_socket()) + } async fn sock_accept(&mut self, fdflags: FdFlags) -> Result, Error> { let (stream, _) = self.0.accept()?; let mut stream = <$stream>::from_cap_std(stream); @@ -170,6 +179,15 @@ macro_rules! wasi_stream_write_impl { fn as_any(&self) -> &dyn Any { self } + #[cfg(unix)] + fn pollable(&self) -> Option { + Some(self.0.as_fd()) + } + + #[cfg(windows)] + fn pollable(&self) -> Option { + Some(self.0.as_raw_handle_or_socket()) + } async fn get_filetype(&mut self) -> Result { Ok(FileType::SocketStream) } 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 0e1114d3b6..52389b7373 100644 --- a/crates/wasi-common/cap-std-sync/src/sched/unix.rs +++ b/crates/wasi-common/cap-std-sync/src/sched/unix.rs @@ -1,15 +1,8 @@ use cap_std::time::Duration; -use io_lifetimes::{AsFd, BorrowedFd}; use rustix::io::{PollFd, PollFlags}; use std::convert::TryInto; -use wasi_common::{ - file::WasiFile, - sched::{ - subscription::{RwEventFlags, Subscription}, - Poll, - }, - Error, ErrorExt, -}; +use wasi_common::sched::subscription::{RwEventFlags, Subscription}; +use wasi_common::{sched::Poll, Error, ErrorExt}; pub async fn poll_oneoff<'a>(poll: &mut Poll<'a>) -> Result<(), Error> { if poll.is_empty() { @@ -19,16 +12,18 @@ pub async fn poll_oneoff<'a>(poll: &mut Poll<'a>) -> Result<(), Error> { for s in poll.rw_subscriptions() { match s { Subscription::Read(f) => { - let fd = wasi_file_fd(f.file).ok_or( - Error::invalid_argument().context("read subscription fd downcast failed"), - )?; + let fd = f + .file + .pollable() + .ok_or(Error::invalid_argument().context("file is not pollable"))?; pollfds.push(PollFd::from_borrowed_fd(fd, PollFlags::IN)); } Subscription::Write(f) => { - let fd = wasi_file_fd(f.file).ok_or( - Error::invalid_argument().context("write subscription fd downcast failed"), - )?; + let fd = f + .file + .pollable() + .ok_or(Error::invalid_argument().context("file is not pollable"))?; pollfds.push(PollFd::from_borrowed_fd(fd, PollFlags::OUT)); } Subscription::MonotonicClock { .. } => unreachable!(), @@ -85,30 +80,3 @@ pub async fn poll_oneoff<'a>(poll: &mut Poll<'a>) -> Result<(), Error> { } Ok(()) } - -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::() { - Some(a.downcast_ref::().unwrap().as_fd()) - } else if a.is::() { - Some(a.downcast_ref::().unwrap().as_fd()) - } else { - None - } -} 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 5a21c40498..c3f2d06810 100644 --- a/crates/wasi-common/cap-std-sync/src/sched/windows.rs +++ b/crates/wasi-common/cap-std-sync/src/sched/windows.rs @@ -9,30 +9,21 @@ // taken the time to improve it. See bug #2880. use anyhow::Context; -use io_extras::os::windows::{AsRawHandleOrSocket, RawHandleOrSocket}; use std::ops::Deref; use std::sync::mpsc::{self, Receiver, RecvTimeoutError, Sender, TryRecvError}; use std::sync::Mutex; use std::thread; use std::time::Duration; -use wasi_common::{ - file::WasiFile, - sched::{ - subscription::{RwEventFlags, Subscription}, - Poll, - }, - Error, ErrorExt, -}; +use wasi_common::sched::subscription::{RwEventFlags, Subscription}; +use wasi_common::{file::WasiFile, sched::Poll, Error, ErrorExt}; pub async fn poll_oneoff<'a>(poll: &mut Poll<'a>) -> Result<(), Error> { - poll_oneoff_(poll, wasi_file_is_stdin, wasi_file_raw_handle).await + poll_oneoff_(poll, wasi_file_is_stdin).await } -// For reuse by wasi-tokio, which has a different WasiFile -> RawHandle translator. 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, ) -> Result<(), Error> { if poll.is_empty() { return Ok(()); @@ -61,21 +52,17 @@ pub async fn poll_oneoff_<'a>( Subscription::Read(r) => { if file_is_stdin(r.file.deref()) { stdin_read_subs.push(r); - } else if file_to_handle(r.file.deref()).is_some() { + } else if r.file.pollable().is_some() { immediate_reads.push(r); } else { - return Err( - Error::invalid_argument().context("read subscription fd downcast failed") - ); + return Err(Error::invalid_argument().context("file is not pollable")); } } Subscription::Write(w) => { - if file_to_handle(w.file.deref()).is_some() { + if w.file.pollable().is_some() { immediate_writes.push(w); } else { - return Err( - Error::invalid_argument().context("write subscription fd downcast failed") - ); + return Err(Error::invalid_argument().context("file is not pollable")); } } Subscription::MonotonicClock { .. } => unreachable!(), @@ -139,49 +126,6 @@ pub fn wasi_file_is_stdin(f: &dyn WasiFile) -> bool { f.as_any().is::() } -pub fn wasi_file_raw_handle(f: &dyn WasiFile) -> Option { - let a = f.as_any(); - 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_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_raw_handle_or_socket(), - ) - } else { - None - } -} - enum PollState { Ready, NotReady, // Not ready, but did not wait diff --git a/crates/wasi-common/cap-std-sync/src/stdio.rs b/crates/wasi-common/cap-std-sync/src/stdio.rs index 24f8631529..a27b70700f 100644 --- a/crates/wasi-common/cap-std-sync/src/stdio.rs +++ b/crates/wasi-common/cap-std-sync/src/stdio.rs @@ -31,6 +31,15 @@ impl WasiFile for Stdin { fn as_any(&self) -> &dyn Any { self } + #[cfg(unix)] + fn pollable(&self) -> Option { + Some(self.0.as_fd()) + } + + #[cfg(windows)] + fn pollable(&self) -> Option { + Some(self.0.as_raw_handle_or_socket()) + } async fn get_filetype(&mut self) -> Result { if self.isatty() { Ok(FileType::CharacterDevice) @@ -98,6 +107,15 @@ macro_rules! wasi_file_write_impl { fn as_any(&self) -> &dyn Any { self } + #[cfg(unix)] + fn pollable(&self) -> Option { + Some(self.0.as_fd()) + } + + #[cfg(windows)] + fn pollable(&self) -> Option { + Some(self.0.as_raw_handle_or_socket()) + } async fn get_filetype(&mut self) -> Result { if self.isatty() { Ok(FileType::CharacterDevice) diff --git a/crates/wasi-common/src/file.rs b/crates/wasi-common/src/file.rs index a846ae6779..0c6c9b2a3e 100644 --- a/crates/wasi-common/src/file.rs +++ b/crates/wasi-common/src/file.rs @@ -7,6 +7,16 @@ pub trait WasiFile: Send + Sync { fn as_any(&self) -> &dyn Any; async fn get_filetype(&mut self) -> Result; + #[cfg(unix)] + fn pollable(&self) -> Option { + None + } + + #[cfg(windows)] + fn pollable(&self) -> Option { + None + } + fn isatty(&mut self) -> bool { false } diff --git a/crates/wasi-common/tokio/src/file.rs b/crates/wasi-common/tokio/src/file.rs index b80bd66e19..030e60e511 100644 --- a/crates/wasi-common/tokio/src/file.rs +++ b/crates/wasi-common/tokio/src/file.rs @@ -94,6 +94,15 @@ macro_rules! wasi_file_impl { fn as_any(&self) -> &dyn Any { self } + #[cfg(unix)] + fn pollable(&self) -> Option { + Some(self.0.as_fd()) + } + + #[cfg(windows)] + fn pollable(&self) -> Option { + Some(self.0.as_raw_handle_or_socket()) + } async fn datasync(&mut self) -> Result<(), Error> { block_on_dummy_executor(|| self.0.datasync()) } diff --git a/crates/wasi-common/tokio/src/sched/windows.rs b/crates/wasi-common/tokio/src/sched/windows.rs index 4d4ebab19f..54cf8615b6 100644 --- a/crates/wasi-common/tokio/src/sched/windows.rs +++ b/crates/wasi-common/tokio/src/sched/windows.rs @@ -1,5 +1,4 @@ use crate::block_on_dummy_executor; -use io_extras::os::windows::{AsRawHandleOrSocket, RawHandleOrSocket}; use wasi_cap_std_sync::sched::windows::poll_oneoff_; use wasi_common::{file::WasiFile, sched::Poll, Error}; @@ -8,52 +7,9 @@ pub async fn poll_oneoff<'a>(poll: &mut Poll<'a>) -> Result<(), Error> { // we use the blocking poll_oneoff implementation from the wasi-cap-std-crate. // We provide a function specific to this crate's WasiFile types for downcasting // to a RawHandle. - block_on_dummy_executor(move || poll_oneoff_(poll, wasi_file_is_stdin, wasi_file_raw_handle)) + block_on_dummy_executor(move || poll_oneoff_(poll, wasi_file_is_stdin)) } pub fn wasi_file_is_stdin(f: &dyn WasiFile) -> bool { f.as_any().is::() } - -fn wasi_file_raw_handle(f: &dyn WasiFile) -> Option { - let a = f.as_any(); - 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_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_raw_handle_or_socket(), - ) - } else { - None - } -}