From a68fa86aad7be732afbb9ec0960a6d3fdfee0c96 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Fri, 26 Aug 2022 11:39:00 -0700 Subject: [PATCH] Make wasi-common-std-sync's dependency on system-interface private. (#4784) * Make wasi-common-std-sync's dependency on system-interface private. Change some `pub` functions which exposed system-interface types to be non-`pub`. And, change `from_sysif_fdflags` functions to `get_fd_flags` functions that take `impl AsFilelike` arguments instead of system-interface types. With these changes, system-interface is no longer exposed in the public API. * Add a public API for `is_read_write` too. Implementors using types implementing `AsFilelike` may want to use the same `is_read_write` logic, without explicitly depending on system-interface, so provide a function that provides that. --- crates/wasi-common/cap-std-sync/Cargo.toml | 1 + crates/wasi-common/cap-std-sync/src/file.rs | 24 +++++--- crates/wasi-common/cap-std-sync/src/net.rs | 64 ++++++++++++++++----- 3 files changed, 69 insertions(+), 20 deletions(-) diff --git a/crates/wasi-common/cap-std-sync/Cargo.toml b/crates/wasi-common/cap-std-sync/Cargo.toml index d93b534ffa..261f3be448 100644 --- a/crates/wasi-common/cap-std-sync/Cargo.toml +++ b/crates/wasi-common/cap-std-sync/Cargo.toml @@ -31,6 +31,7 @@ rustix = { version = "0.35.6", features = ["fs"] } [target.'cfg(windows)'.dependencies] once_cell = "1.12.0" io-extras = "0.15.0" +rustix = { version = "0.35.6", features = ["net"] } [target.'cfg(windows)'.dependencies.windows-sys] version = "0.36.0" diff --git a/crates/wasi-common/cap-std-sync/src/file.rs b/crates/wasi-common/cap-std-sync/src/file.rs index bd2ff40ed9..af710cd7ae 100644 --- a/crates/wasi-common/cap-std-sync/src/file.rs +++ b/crates/wasi-common/cap-std-sync/src/file.rs @@ -1,5 +1,6 @@ use cap_fs_ext::MetadataExt; use fs_set_times::{SetTimes, SystemTimeSpec}; +use io_lifetimes::AsFilelike; use is_terminal::IsTerminal; use std::any::Any; use std::convert::TryInto; @@ -48,8 +49,8 @@ impl WasiFile for File { Ok(filetype_from(&meta.file_type())) } async fn get_fdflags(&mut self) -> Result { - let fdflags = self.0.get_fd_flags()?; - Ok(from_sysif_fdflags(fdflags)) + let fdflags = get_fd_flags(&self.0)?; + Ok(fdflags) } async fn set_fdflags(&mut self, fdflags: FdFlags) -> Result<(), Error> { if fdflags.intersects( @@ -187,7 +188,10 @@ impl AsFd for File { self.0.as_fd() } } -pub fn convert_systimespec(t: Option) -> Option { + +pub(crate) fn convert_systimespec( + t: Option, +) -> Option { match t { Some(wasi_common::SystemTimeSpec::Absolute(t)) => { Some(SystemTimeSpec::Absolute(t.into_std())) @@ -197,7 +201,7 @@ pub fn convert_systimespec(t: Option) -> Option system_interface::fs::FdFlags { +pub(crate) fn to_sysif_fdflags(f: wasi_common::file::FdFlags) -> system_interface::fs::FdFlags { let mut out = system_interface::fs::FdFlags::empty(); if f.contains(wasi_common::file::FdFlags::APPEND) { out |= system_interface::fs::FdFlags::APPEND; @@ -216,7 +220,12 @@ pub fn to_sysif_fdflags(f: wasi_common::file::FdFlags) -> system_interface::fs:: } out } -pub fn from_sysif_fdflags(f: system_interface::fs::FdFlags) -> wasi_common::file::FdFlags { + +/// Return the file-descriptor flags for a given file-like object. +/// +/// This returns the flags needed to implement [`WasiFile::get_fdflags`]. +pub fn get_fd_flags(f: Filelike) -> io::Result { + let f = f.as_filelike().get_fd_flags()?; let mut out = wasi_common::file::FdFlags::empty(); if f.contains(system_interface::fs::FdFlags::APPEND) { out |= wasi_common::file::FdFlags::APPEND; @@ -233,9 +242,10 @@ pub fn from_sysif_fdflags(f: system_interface::fs::FdFlags) -> wasi_common::file if f.contains(system_interface::fs::FdFlags::SYNC) { out |= wasi_common::file::FdFlags::SYNC; } - out + Ok(out) } -pub fn convert_advice(advice: Advice) -> system_interface::fs::Advice { + +fn convert_advice(advice: Advice) -> system_interface::fs::Advice { match advice { Advice::Normal => system_interface::fs::Advice::Normal, Advice::Sequential => system_interface::fs::Advice::Sequential, diff --git a/crates/wasi-common/cap-std-sync/src/net.rs b/crates/wasi-common/cap-std-sync/src/net.rs index 7670d9e44b..223cb894fe 100644 --- a/crates/wasi-common/cap-std-sync/src/net.rs +++ b/crates/wasi-common/cap-std-sync/src/net.rs @@ -1,7 +1,5 @@ #[cfg(windows)] use io_extras::os::windows::{AsRawHandleOrSocket, RawHandleOrSocket}; -#[cfg(unix)] -use io_lifetimes::AsFilelike; use io_lifetimes::AsSocketlike; #[cfg(unix)] use io_lifetimes::{AsFd, BorrowedFd}; @@ -105,8 +103,8 @@ macro_rules! wasi_listen_write_impl { } #[cfg(unix)] async fn get_fdflags(&mut self) -> Result { - let fdflags = self.0.as_filelike().get_fd_flags()?; - Ok(from_sysif_fdflags(fdflags)) + let fdflags = get_fd_flags(&self.0)?; + Ok(fdflags) } async fn set_fdflags(&mut self, fdflags: FdFlags) -> Result<(), Error> { if fdflags == wasi_common::file::FdFlags::NONBLOCK { @@ -193,8 +191,8 @@ macro_rules! wasi_stream_write_impl { } #[cfg(unix)] async fn get_fdflags(&mut self) -> Result { - let fdflags = self.0.as_filelike().get_fd_flags()?; - Ok(from_sysif_fdflags(fdflags)) + let fdflags = get_fd_flags(&self.0)?; + Ok(fdflags) } async fn set_fdflags(&mut self, fdflags: FdFlags) -> Result<(), Error> { if fdflags == wasi_common::file::FdFlags::NONBLOCK { @@ -230,7 +228,7 @@ macro_rules! wasi_stream_write_impl { Ok(val) } async fn readable(&self) -> Result<(), Error> { - let (readable, _writeable) = self.0.is_read_write()?; + let (readable, _writeable) = is_read_write(&self.0)?; if readable { Ok(()) } else { @@ -238,7 +236,7 @@ macro_rules! wasi_stream_write_impl { } } async fn writable(&self) -> Result<(), Error> { - let (_readable, writeable) = self.0.is_read_write()?; + let (_readable, writeable) = is_read_write(&self.0)?; if writeable { Ok(()) } else { @@ -303,10 +301,50 @@ pub fn filetype_from(ft: &cap_std::fs::FileType) -> FileType { } } -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; +/// Return the file-descriptor flags for a given file-like object. +/// +/// This returns the flags needed to implement [`WasiFile::get_fdflags`]. +pub fn get_fd_flags( + f: Socketlike, +) -> io::Result { + // On Unix-family platforms, we can use the same system call that we'd use + // for files on sockets here. + #[cfg(not(windows))] + { + let mut out = wasi_common::file::FdFlags::empty(); + if f.get_fd_flags()? + .contains(system_interface::fs::FdFlags::NONBLOCK) + { + out |= wasi_common::file::FdFlags::NONBLOCK; + } + Ok(out) + } + + // On Windows, sockets are different, and there is no direct way to + // query for the non-blocking flag. We can get a sufficient approximation + // by testing whether a zero-length `recv` appears to block. + #[cfg(windows)] + match rustix::net::recv(f, &mut [], rustix::net::RecvFlags::empty()) { + Ok(_) => Ok(wasi_common::file::FdFlags::empty()), + Err(rustix::io::Errno::WOULDBLOCK) => Ok(wasi_common::file::FdFlags::NONBLOCK), + Err(e) => Err(e.into()), + } +} + +/// Return the file-descriptor flags for a given file-like object. +/// +/// This returns the flags needed to implement [`WasiFile::get_fdflags`]. +pub fn is_read_write(f: Socketlike) -> io::Result<(bool, bool)> { + // On Unix-family platforms, we have an `IsReadWrite` impl. + #[cfg(not(windows))] + { + f.is_read_write() + } + + // On Windows, we only have a `TcpStream` impl, so make a view first. + #[cfg(windows)] + { + f.as_socketlike_view::() + .is_read_write() } - out }