diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 2c04481c04ec0..bdb172907ffed 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -587,6 +587,12 @@ impl File { } } +// In addition to the `impl`s here, `File` also has `impl`s for +// `AsFd`/`From`/`Into` and +// `AsRawFd`/`IntoRawFd`/`FromRawFd`, on Unix and WASI, and +// `AsHandle`/`From`/`Into` and +// `AsRawHandle`/`IntoRawHandle`/`FromRawHandle` on Windows. + impl AsInner for File { fn as_inner(&self) -> &fs_imp::File { &self.inner diff --git a/library/std/src/net/tcp.rs b/library/std/src/net/tcp.rs index 325acf0b97931..7f964e5c5a917 100644 --- a/library/std/src/net/tcp.rs +++ b/library/std/src/net/tcp.rs @@ -546,6 +546,12 @@ impl TcpStream { } } +// In addition to the `impl`s here, `TcpStream` also has `impl`s for +// `AsFd`/`From`/`Into` and +// `AsRawFd`/`IntoRawFd`/`FromRawFd`, on Unix and WASI, and +// `AsSocket`/`From`/`Into` and +// `AsRawSocket`/`IntoRawSocket`/`FromRawSocket` on Windows. + #[stable(feature = "rust1", since = "1.0.0")] impl Read for TcpStream { fn read(&mut self, buf: &mut [u8]) -> io::Result { @@ -908,6 +914,12 @@ impl TcpListener { } } +// In addition to the `impl`s here, `TcpListener` also has `impl`s for +// `AsFd`/`From`/`Into` and +// `AsRawFd`/`IntoRawFd`/`FromRawFd`, on Unix and WASI, and +// `AsSocket`/`From`/`Into` and +// `AsRawSocket`/`IntoRawSocket`/`FromRawSocket` on Windows. + #[stable(feature = "rust1", since = "1.0.0")] impl<'a> Iterator for Incoming<'a> { type Item = io::Result; diff --git a/library/std/src/net/udp.rs b/library/std/src/net/udp.rs index d2088a12b2c7d..871505843af23 100644 --- a/library/std/src/net/udp.rs +++ b/library/std/src/net/udp.rs @@ -779,6 +779,12 @@ impl UdpSocket { } } +// In addition to the `impl`s here, `UdpSocket` also has `impl`s for +// `AsFd`/`From`/`Into` and +// `AsRawFd`/`IntoRawFd`/`FromRawFd`, on Unix and WASI, and +// `AsSocket`/`From`/`Into` and +// `AsRawSocket`/`IntoRawSocket`/`FromRawSocket` on Windows. + impl AsInner for UdpSocket { fn as_inner(&self) -> &net_imp::UdpSocket { &self.0 diff --git a/library/std/src/net/udp/tests.rs b/library/std/src/net/udp/tests.rs index fbed3d32d451a..a51113dd9e749 100644 --- a/library/std/src/net/udp/tests.rs +++ b/library/std/src/net/udp/tests.rs @@ -2,7 +2,6 @@ use crate::io::ErrorKind; use crate::net::test::{next_test_ip4, next_test_ip6}; use crate::net::*; use crate::sync::mpsc::channel; -use crate::sys_common::AsInner; use crate::thread; use crate::time::{Duration, Instant}; @@ -173,7 +172,7 @@ fn debug() { let socket_addr = next_test_ip4(); let udpsock = t!(UdpSocket::bind(&socket_addr)); - let udpsock_inner = udpsock.0.socket().as_inner(); + let udpsock_inner = udpsock.0.socket().as_raw(); let compare = format!("UdpSocket {{ addr: {:?}, {}: {:?} }}", socket_addr, name, udpsock_inner); assert_eq!(format!("{:?}", udpsock), compare); } diff --git a/library/std/src/os/fd/mod.rs b/library/std/src/os/fd/mod.rs new file mode 100644 index 0000000000000..df11dc21aa7a6 --- /dev/null +++ b/library/std/src/os/fd/mod.rs @@ -0,0 +1,13 @@ +//! Owned and borrowed Unix-like file descriptors. + +#![unstable(feature = "io_safety", issue = "87074")] +#![deny(unsafe_op_in_unsafe_fn)] + +// `RawFd`, `AsRawFd`, etc. +pub mod raw; + +// `OwnedFd`, `AsFd`, etc. +pub mod owned; + +// Implementations for `AsRawFd` etc. for network types. +mod net; diff --git a/library/std/src/os/unix/net/raw_fd.rs b/library/std/src/os/fd/net.rs similarity index 68% rename from library/std/src/os/unix/net/raw_fd.rs rename to library/std/src/os/fd/net.rs index b3f1284410124..843f45f7f5f98 100644 --- a/library/std/src/os/unix/net/raw_fd.rs +++ b/library/std/src/os/fd/net.rs @@ -1,4 +1,5 @@ -use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; +use crate::os::fd::owned::OwnedFd; +use crate::os::fd::raw::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; use crate::sys_common::{self, AsInner, FromInner, IntoInner}; use crate::{net, sys}; @@ -8,7 +9,7 @@ macro_rules! impl_as_raw_fd { impl AsRawFd for net::$t { #[inline] fn as_raw_fd(&self) -> RawFd { - *self.as_inner().socket().as_inner() + self.as_inner().socket().as_raw_fd() } } )*}; @@ -21,8 +22,10 @@ macro_rules! impl_from_raw_fd { impl FromRawFd for net::$t { #[inline] unsafe fn from_raw_fd(fd: RawFd) -> net::$t { - let socket = sys::net::Socket::from_inner(fd); - net::$t::from_inner(sys_common::net::$t::from_inner(socket)) + unsafe { + let socket = sys::net::Socket::from_inner(FromInner::from_inner(OwnedFd::from_raw_fd(fd))); + net::$t::from_inner(sys_common::net::$t::from_inner(socket)) + } } } )*}; @@ -35,7 +38,7 @@ macro_rules! impl_into_raw_fd { impl IntoRawFd for net::$t { #[inline] fn into_raw_fd(self) -> RawFd { - self.into_inner().into_socket().into_inner() + self.into_inner().into_socket().into_inner().into_inner().into_raw_fd() } } )*}; diff --git a/library/std/src/os/fd/owned.rs b/library/std/src/os/fd/owned.rs new file mode 100644 index 0000000000000..52d7d4690d39f --- /dev/null +++ b/library/std/src/os/fd/owned.rs @@ -0,0 +1,289 @@ +//! Owned and borrowed Unix-like file descriptors. + +#![unstable(feature = "io_safety", issue = "87074")] +#![deny(unsafe_op_in_unsafe_fn)] + +use super::raw::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; +use crate::fmt; +use crate::fs; +use crate::marker::PhantomData; +use crate::mem::forget; +use crate::sys_common::{AsInner, FromInner, IntoInner}; + +/// A borrowed file descriptor. +/// +/// This has a lifetime parameter to tie it to the lifetime of something that +/// owns the file descriptor. +/// +/// This uses `repr(transparent)` and has the representation of a host file +/// descriptor, so it can be used in FFI in places where a file descriptor is +/// passed as an argument, it is not captured or consumed, and it never has the +/// value `-1`. +#[derive(Copy, Clone)] +#[repr(transparent)] +#[rustc_layout_scalar_valid_range_start(0)] +// libstd/os/raw/mod.rs assures me that every libstd-supported platform has a +// 32-bit c_int. Below is -2, in two's complement, but that only works out +// because c_int is 32 bits. +#[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)] +#[unstable(feature = "io_safety", issue = "87074")] +pub struct BorrowedFd<'fd> { + fd: RawFd, + _phantom: PhantomData<&'fd OwnedFd>, +} + +/// An owned file descriptor. +/// +/// This closes the file descriptor on drop. +/// +/// This uses `repr(transparent)` and has the representation of a host file +/// descriptor, so it can be used in FFI in places where a file descriptor is +/// passed as a consumed argument or returned as an owned value, and it never +/// has the value `-1`. +#[repr(transparent)] +#[rustc_layout_scalar_valid_range_start(0)] +// libstd/os/raw/mod.rs assures me that every libstd-supported platform has a +// 32-bit c_int. Below is -2, in two's complement, but that only works out +// because c_int is 32 bits. +#[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)] +#[unstable(feature = "io_safety", issue = "87074")] +pub struct OwnedFd { + fd: RawFd, +} + +impl BorrowedFd<'_> { + /// Return a `BorrowedFd` holding the given raw file descriptor. + /// + /// # Safety + /// + /// The resource pointed to by `fd` must remain open for the duration of + /// the returned `BorrowedFd`, and it must not have the value `-1`. + #[inline] + #[unstable(feature = "io_safety", issue = "87074")] + pub unsafe fn borrow_raw_fd(fd: RawFd) -> Self { + assert_ne!(fd, u32::MAX as RawFd); + // SAFETY: we just asserted that the value is in the valid range and isn't `-1` (the only value bigger than `0xFF_FF_FF_FE` unsigned) + unsafe { Self { fd, _phantom: PhantomData } } + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl AsRawFd for BorrowedFd<'_> { + #[inline] + fn as_raw_fd(&self) -> RawFd { + self.fd + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl AsRawFd for OwnedFd { + #[inline] + fn as_raw_fd(&self) -> RawFd { + self.fd + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl IntoRawFd for OwnedFd { + #[inline] + fn into_raw_fd(self) -> RawFd { + let fd = self.fd; + forget(self); + fd + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl FromRawFd for OwnedFd { + /// Constructs a new instance of `Self` from the given raw file descriptor. + /// + /// # Safety + /// + /// The resource pointed to by `fd` must be open and suitable for assuming + /// ownership. The resource must not require any cleanup other than `close`. + #[inline] + unsafe fn from_raw_fd(fd: RawFd) -> Self { + assert_ne!(fd, u32::MAX as RawFd); + // SAFETY: we just asserted that the value is in the valid range and isn't `-1` (the only value bigger than `0xFF_FF_FF_FE` unsigned) + unsafe { Self { fd } } + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl Drop for OwnedFd { + #[inline] + fn drop(&mut self) { + unsafe { + // Note that errors are ignored when closing a file descriptor. The + // reason for this is that if an error occurs we don't actually know if + // the file descriptor was closed or not, and if we retried (for + // something like EINTR), we might close another valid file descriptor + // opened after we closed ours. + let _ = libc::close(self.fd); + } + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl fmt::Debug for BorrowedFd<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("BorrowedFd").field("fd", &self.fd).finish() + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl fmt::Debug for OwnedFd { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("OwnedFd").field("fd", &self.fd).finish() + } +} + +/// A trait to borrow the file descriptor from an underlying object. +/// +/// This is only available on unix platforms and must be imported in order to +/// call the method. Windows platforms have a corresponding `AsHandle` and +/// `AsSocket` set of traits. +#[unstable(feature = "io_safety", issue = "87074")] +pub trait AsFd { + /// Borrows the file descriptor. + /// + /// # Example + /// + /// ```rust,no_run + /// # #![feature(io_safety)] + /// use std::fs::File; + /// # use std::io; + /// # #[cfg(target_os = "wasi")] + /// # use std::os::wasi::io::{AsFd, BorrowedFd}; + /// # #[cfg(unix)] + /// # use std::os::unix::io::{AsFd, BorrowedFd}; + /// + /// let mut f = File::open("foo.txt")?; + /// # #[cfg(any(unix, target_os = "wasi"))] + /// let borrowed_fd: BorrowedFd<'_> = f.as_fd(); + /// # Ok::<(), io::Error>(()) + /// ``` + #[unstable(feature = "io_safety", issue = "87074")] + fn as_fd(&self) -> BorrowedFd<'_>; +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl AsFd for BorrowedFd<'_> { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + *self + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl AsFd for OwnedFd { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + // Safety: `OwnedFd` and `BorrowedFd` have the same validity + // invariants, and the `BorrowdFd` is bounded by the lifetime + // of `&self`. + unsafe { BorrowedFd::borrow_raw_fd(self.as_raw_fd()) } + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl AsFd for fs::File { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + self.as_inner().as_fd() + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl From for OwnedFd { + #[inline] + fn from(file: fs::File) -> OwnedFd { + file.into_inner().into_inner().into_inner() + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl From for fs::File { + #[inline] + fn from(owned_fd: OwnedFd) -> Self { + Self::from_inner(FromInner::from_inner(FromInner::from_inner(owned_fd))) + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl AsFd for crate::net::TcpStream { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + self.as_inner().socket().as_fd() + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl From for OwnedFd { + #[inline] + fn from(tcp_stream: crate::net::TcpStream) -> OwnedFd { + tcp_stream.into_inner().into_socket().into_inner().into_inner().into() + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl From for crate::net::TcpStream { + #[inline] + fn from(owned_fd: OwnedFd) -> Self { + Self::from_inner(FromInner::from_inner(FromInner::from_inner(FromInner::from_inner( + owned_fd, + )))) + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl AsFd for crate::net::TcpListener { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + self.as_inner().socket().as_fd() + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl From for OwnedFd { + #[inline] + fn from(tcp_listener: crate::net::TcpListener) -> OwnedFd { + tcp_listener.into_inner().into_socket().into_inner().into_inner().into() + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl From for crate::net::TcpListener { + #[inline] + fn from(owned_fd: OwnedFd) -> Self { + Self::from_inner(FromInner::from_inner(FromInner::from_inner(FromInner::from_inner( + owned_fd, + )))) + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl AsFd for crate::net::UdpSocket { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + self.as_inner().socket().as_fd() + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl From for OwnedFd { + #[inline] + fn from(udp_socket: crate::net::UdpSocket) -> OwnedFd { + udp_socket.into_inner().into_socket().into_inner().into_inner().into() + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl From for crate::net::UdpSocket { + #[inline] + fn from(owned_fd: OwnedFd) -> Self { + Self::from_inner(FromInner::from_inner(FromInner::from_inner(FromInner::from_inner( + owned_fd, + )))) + } +} diff --git a/library/std/src/os/unix/io.rs b/library/std/src/os/fd/raw.rs similarity index 82% rename from library/std/src/os/unix/io.rs rename to library/std/src/os/fd/raw.rs index 07c30bfa9ed15..f874cf0b42d74 100644 --- a/library/std/src/os/unix/io.rs +++ b/library/std/src/os/fd/raw.rs @@ -1,23 +1,25 @@ -//! Unix-specific extensions to general I/O primitives. +//! Raw Unix-like file descriptors. #![stable(feature = "rust1", since = "1.0.0")] use crate::fs; use crate::io; use crate::os::raw; -use crate::sys; -use crate::sys_common::{AsInner, FromInner, IntoInner}; +#[cfg(unix)] +use crate::os::unix::io::OwnedFd; +#[cfg(target_os = "wasi")] +use crate::os::wasi::io::OwnedFd; +use crate::sys_common::{AsInner, IntoInner}; /// Raw file descriptors. #[stable(feature = "rust1", since = "1.0.0")] pub type RawFd = raw::c_int; -/// A trait to extract the raw unix file descriptor from an underlying -/// object. +/// A trait to extract the raw file descriptor from an underlying object. /// -/// This is only available on unix platforms and must be imported in order -/// to call the method. Windows platforms have a corresponding `AsRawHandle` -/// and `AsRawSocket` set of traits. +/// This is only available on unix and WASI platforms and must be imported in +/// order to call the method. Windows platforms have a corresponding +/// `AsRawHandle` and `AsRawSocket` set of traits. #[stable(feature = "rust1", since = "1.0.0")] pub trait AsRawFd { /// Extracts the raw file descriptor. @@ -31,10 +33,14 @@ pub trait AsRawFd { /// ```no_run /// use std::fs::File; /// # use std::io; + /// #[cfg(unix)] /// use std::os::unix::io::{AsRawFd, RawFd}; + /// #[cfg(target_os = "wasi")] + /// use std::os::wasi::io::{AsRawFd, RawFd}; /// /// let mut f = File::open("foo.txt")?; /// // Note that `raw_fd` is only valid as long as `f` exists. + /// #[cfg(any(unix, target_os = "wasi"))] /// let raw_fd: RawFd = f.as_raw_fd(); /// # Ok::<(), io::Error>(()) /// ``` @@ -64,12 +70,17 @@ pub trait FromRawFd { /// ```no_run /// use std::fs::File; /// # use std::io; + /// #[cfg(unix)] /// use std::os::unix::io::{FromRawFd, IntoRawFd, RawFd}; + /// #[cfg(target_os = "wasi")] + /// use std::os::wasi::io::{FromRawFd, IntoRawFd, RawFd}; /// /// let f = File::open("foo.txt")?; + /// # #[cfg(any(unix, target_os = "wasi"))] /// let raw_fd: RawFd = f.into_raw_fd(); /// // SAFETY: no other functions should call `from_raw_fd`, so there /// // is only one owner for the file descriptor. + /// # #[cfg(any(unix, target_os = "wasi"))] /// let f = unsafe { File::from_raw_fd(raw_fd) }; /// # Ok::<(), io::Error>(()) /// ``` @@ -92,9 +103,13 @@ pub trait IntoRawFd { /// ```no_run /// use std::fs::File; /// # use std::io; + /// #[cfg(unix)] /// use std::os::unix::io::{IntoRawFd, RawFd}; + /// #[cfg(target_os = "wasi")] + /// use std::os::wasi::io::{IntoRawFd, RawFd}; /// /// let f = File::open("foo.txt")?; + /// #[cfg(any(unix, target_os = "wasi"))] /// let raw_fd: RawFd = f.into_raw_fd(); /// # Ok::<(), io::Error>(()) /// ``` @@ -128,21 +143,21 @@ impl FromRawFd for RawFd { impl AsRawFd for fs::File { #[inline] fn as_raw_fd(&self) -> RawFd { - self.as_inner().fd().raw() + self.as_inner().as_raw_fd() } } #[stable(feature = "from_raw_os", since = "1.1.0")] impl FromRawFd for fs::File { #[inline] unsafe fn from_raw_fd(fd: RawFd) -> fs::File { - fs::File::from_inner(sys::fs::File::from_inner(fd)) + unsafe { fs::File::from(OwnedFd::from_raw_fd(fd)) } } } #[stable(feature = "into_raw_os", since = "1.4.0")] impl IntoRawFd for fs::File { #[inline] fn into_raw_fd(self) -> RawFd { - self.into_inner().into_fd().into_raw() + self.into_inner().into_inner().into_raw_fd() } } diff --git a/library/std/src/os/linux/process.rs b/library/std/src/os/linux/process.rs index 6daff0f003c8f..e3e7143c851ef 100644 --- a/library/std/src/os/linux/process.rs +++ b/library/std/src/os/linux/process.rs @@ -3,7 +3,7 @@ #![unstable(feature = "linux_pidfd", issue = "82971")] use crate::io::Result; -use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; +use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; use crate::process; use crate::sealed::Sealed; #[cfg(not(doc))] @@ -69,19 +69,37 @@ impl IntoInner for PidFd { impl AsRawFd for PidFd { fn as_raw_fd(&self) -> RawFd { - self.as_inner().raw() + self.as_inner().as_raw_fd() } } impl FromRawFd for PidFd { unsafe fn from_raw_fd(fd: RawFd) -> Self { - Self::from_inner(FileDesc::new(fd)) + Self::from_inner(FileDesc::from_raw_fd(fd)) } } impl IntoRawFd for PidFd { fn into_raw_fd(self) -> RawFd { - self.into_inner().into_raw() + self.into_inner().into_raw_fd() + } +} + +impl AsFd for PidFd { + fn as_fd(&self) -> BorrowedFd<'_> { + self.as_inner().as_fd() + } +} + +impl From for PidFd { + fn from(fd: OwnedFd) -> Self { + Self::from_inner(FileDesc::from_inner(fd)) + } +} + +impl From for OwnedFd { + fn from(pid_fd: PidFd) -> Self { + pid_fd.into_inner().into_inner() } } diff --git a/library/std/src/os/mod.rs b/library/std/src/os/mod.rs index 4c9814919cdfa..79e6967300767 100644 --- a/library/std/src/os/mod.rs +++ b/library/std/src/os/mod.rs @@ -121,3 +121,6 @@ mod imp { #[cfg(not(doc))] #[stable(feature = "os", since = "1.0.0")] pub use imp::*; + +#[cfg(any(unix, target_os = "wasi", doc))] +mod fd; diff --git a/library/std/src/os/unix/io/fd.rs b/library/std/src/os/unix/io/fd.rs new file mode 100644 index 0000000000000..7795db7abc01d --- /dev/null +++ b/library/std/src/os/unix/io/fd.rs @@ -0,0 +1,9 @@ +//! Owned and borrowed file descriptors. + +#![unstable(feature = "io_safety", issue = "87074")] + +// Tests for this module +#[cfg(test)] +mod tests; + +pub use crate::os::fd::owned::*; diff --git a/library/std/src/os/unix/io/fd/tests.rs b/library/std/src/os/unix/io/fd/tests.rs new file mode 100644 index 0000000000000..84d2a7a1a91b4 --- /dev/null +++ b/library/std/src/os/unix/io/fd/tests.rs @@ -0,0 +1,11 @@ +use crate::mem::size_of; +use crate::os::unix::io::RawFd; + +#[test] +fn test_raw_fd_layout() { + // `OwnedFd` and `BorrowedFd` use `rustc_layout_scalar_valid_range_start` + // and `rustc_layout_scalar_valid_range_end`, with values that depend on + // the bit width of `RawFd`. If this ever changes, those values will need + // to be updated. + assert_eq!(size_of::(), 4); +} diff --git a/library/std/src/os/unix/io/mod.rs b/library/std/src/os/unix/io/mod.rs new file mode 100644 index 0000000000000..0fd9591b0165a --- /dev/null +++ b/library/std/src/os/unix/io/mod.rs @@ -0,0 +1,57 @@ +//! Unix-specific extensions to general I/O primitives. +//! +//! Just like raw pointers, raw file descriptors point to resources with +//! dynamic lifetimes, and they can dangle if they outlive their resources +//! or be forged if they're created from invalid values. +//! +//! This module provides three types for representing file descriptors, +//! with different ownership properties: raw, borrowed, and owned, which are +//! analogous to types used for representing pointers: +//! +//! | Type | Analogous to | +//! | ------------------ | ------------ | +//! | [`RawFd`] | `*const _` | +//! | [`BorrowedFd<'a>`] | `&'a _` | +//! | [`OwnedFd`] | `Box<_>` | +//! +//! Like raw pointers, `RawFd` values are primitive values. And in new code, +//! they should be considered unsafe to do I/O on (analogous to dereferencing +//! them). Rust did not always provide this guidance, so existing code in the +//! Rust ecosystem often doesn't mark `RawFd` usage as unsafe. Once the +//! `io_safety` feature is stable, libraries will be encouraged to migrate, +//! either by adding `unsafe` to APIs that dereference `RawFd` values, or by +//! using to `BorrowedFd` or `OwnedFd` instead. +//! +//! Like references, `BorrowedFd` values are tied to a lifetime, to ensure +//! that they don't outlive the resource they point to. These are safe to +//! use. `BorrowedFd` values may be used in APIs which provide safe access to +//! any system call except for: +//! - `close`, because that would end the dynamic lifetime of the resource +//! without ending the lifetime of the file descriptor. +//! - `dup2`/`dup3`, in the second argument, because this argument is +//! closed and assigned a new resource, which may break the assumptions +//! other code using that file descriptor. +//! This list doesn't include `mmap`, since `mmap` does do a proper borrow of +//! its file descriptor argument. That said, `mmap` is unsafe for other +//! reasons: it operates on raw pointers, and it can have undefined behavior if +//! the underlying storage is mutated. Mutations may come from other processes, +//! or from the same process if the API provides `BorrowedFd` access, since as +//! mentioned earlier, `BorrowedFd` values may be used in APIs which provide +//! safe access to any system call. Consequently, code using `mmap` and +//! presenting a safe API must take full responsibility for ensuring that safe +//! Rust code cannot evoke undefined behavior through it. +//! +//! Like boxes, `OwnedFd` values conceptually own the resource they point to, +//! and free (close) it when they are dropped. +//! +//! [`BorrowedFd<'a>`]: crate::os::unix::io::BorrowedFd + +#![stable(feature = "rust1", since = "1.0.0")] + +mod fd; +mod raw; + +#[unstable(feature = "io_safety", issue = "87074")] +pub use fd::*; +#[stable(feature = "rust1", since = "1.0.0")] +pub use raw::*; diff --git a/library/std/src/os/unix/io/raw.rs b/library/std/src/os/unix/io/raw.rs new file mode 100644 index 0000000000000..6317e31747119 --- /dev/null +++ b/library/std/src/os/unix/io/raw.rs @@ -0,0 +1,5 @@ +//! Unix-specific extensions to general I/O primitives. + +#![stable(feature = "rust1", since = "1.0.0")] + +pub use crate::os::fd::raw::*; diff --git a/library/std/src/os/unix/mod.rs b/library/std/src/os/unix/mod.rs index 6c73d4b21dd3d..17a0259572446 100644 --- a/library/std/src/os/unix/mod.rs +++ b/library/std/src/os/unix/mod.rs @@ -108,7 +108,7 @@ pub mod prelude { pub use super::fs::{FileTypeExt, MetadataExt, OpenOptionsExt, PermissionsExt}; #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] - pub use super::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; + pub use super::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] pub use super::process::{CommandExt, ExitStatusExt}; diff --git a/library/std/src/os/unix/net/datagram.rs b/library/std/src/os/unix/net/datagram.rs index 9e39f70f68e69..f11eec18cc521 100644 --- a/library/std/src/os/unix/net/datagram.rs +++ b/library/std/src/os/unix/net/datagram.rs @@ -21,7 +21,7 @@ use super::{sockaddr_un, SocketAddr}; ))] use crate::io::{IoSlice, IoSliceMut}; use crate::net::Shutdown; -use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; +use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; use crate::path::Path; use crate::sys::cvt; use crate::sys::net::Socket; @@ -106,7 +106,7 @@ impl UnixDatagram { let socket = UnixDatagram::unbound()?; let (addr, len) = sockaddr_un(path.as_ref())?; - cvt(libc::bind(*socket.0.as_inner(), &addr as *const _ as *const _, len as _))?; + cvt(libc::bind(socket.as_raw_fd(), &addr as *const _ as *const _, len as _))?; Ok(socket) } @@ -187,7 +187,7 @@ impl UnixDatagram { unsafe { let (addr, len) = sockaddr_un(path.as_ref())?; - cvt(libc::connect(*self.0.as_inner(), &addr as *const _ as *const _, len))?; + cvt(libc::connect(self.as_raw_fd(), &addr as *const _ as *const _, len))?; } Ok(()) } @@ -229,7 +229,7 @@ impl UnixDatagram { /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn local_addr(&self) -> io::Result { - SocketAddr::new(|addr, len| unsafe { libc::getsockname(*self.0.as_inner(), addr, len) }) + SocketAddr::new(|addr, len| unsafe { libc::getsockname(self.as_raw_fd(), addr, len) }) } /// Returns the address of this socket's peer. @@ -253,7 +253,7 @@ impl UnixDatagram { /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn peer_addr(&self) -> io::Result { - SocketAddr::new(|addr, len| unsafe { libc::getpeername(*self.0.as_inner(), addr, len) }) + SocketAddr::new(|addr, len| unsafe { libc::getpeername(self.as_raw_fd(), addr, len) }) } fn recv_from_flags( @@ -264,7 +264,7 @@ impl UnixDatagram { let mut count = 0; let addr = SocketAddr::new(|addr, len| unsafe { count = libc::recvfrom( - *self.0.as_inner(), + self.as_raw_fd(), buf.as_mut_ptr() as *mut _, buf.len(), flags, @@ -462,7 +462,7 @@ impl UnixDatagram { let (addr, len) = sockaddr_un(path.as_ref())?; let count = cvt(libc::sendto( - *self.0.as_inner(), + self.as_raw_fd(), buf.as_ptr() as *const _, buf.len(), MSG_NOSIGNAL, @@ -881,7 +881,7 @@ impl UnixDatagram { impl AsRawFd for UnixDatagram { #[inline] fn as_raw_fd(&self) -> RawFd { - *self.0.as_inner() + self.0.as_inner().as_raw_fd() } } @@ -889,7 +889,7 @@ impl AsRawFd for UnixDatagram { impl FromRawFd for UnixDatagram { #[inline] unsafe fn from_raw_fd(fd: RawFd) -> UnixDatagram { - UnixDatagram(Socket::from_inner(fd)) + UnixDatagram(Socket::from_inner(FromInner::from_inner(OwnedFd::from_raw_fd(fd)))) } } @@ -897,6 +897,30 @@ impl FromRawFd for UnixDatagram { impl IntoRawFd for UnixDatagram { #[inline] fn into_raw_fd(self) -> RawFd { - self.0.into_inner() + self.0.into_inner().into_inner().into_raw_fd() + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl AsFd for UnixDatagram { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_inner().as_fd() + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl From for OwnedFd { + #[inline] + fn from(unix_datagram: UnixDatagram) -> OwnedFd { + unsafe { OwnedFd::from_raw_fd(unix_datagram.into_raw_fd()) } + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl From for UnixDatagram { + #[inline] + fn from(owned: OwnedFd) -> Self { + unsafe { Self::from_raw_fd(owned.into_raw_fd()) } } } diff --git a/library/std/src/os/unix/net/listener.rs b/library/std/src/os/unix/net/listener.rs index bdd08fe8380fa..9066c71794fad 100644 --- a/library/std/src/os/unix/net/listener.rs +++ b/library/std/src/os/unix/net/listener.rs @@ -1,5 +1,5 @@ use super::{sockaddr_un, SocketAddr, UnixStream}; -use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; +use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; use crate::path::Path; use crate::sys::cvt; use crate::sys::net::Socket; @@ -74,8 +74,8 @@ impl UnixListener { let inner = Socket::new_raw(libc::AF_UNIX, libc::SOCK_STREAM)?; let (addr, len) = sockaddr_un(path.as_ref())?; - cvt(libc::bind(*inner.as_inner(), &addr as *const _ as *const _, len as _))?; - cvt(libc::listen(*inner.as_inner(), 128))?; + cvt(libc::bind(inner.as_inner().as_raw_fd(), &addr as *const _ as *const _, len as _))?; + cvt(libc::listen(inner.as_inner().as_raw_fd(), 128))?; Ok(UnixListener(inner)) } @@ -150,7 +150,7 @@ impl UnixListener { /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn local_addr(&self) -> io::Result { - SocketAddr::new(|addr, len| unsafe { libc::getsockname(*self.0.as_inner(), addr, len) }) + SocketAddr::new(|addr, len| unsafe { libc::getsockname(self.as_raw_fd(), addr, len) }) } /// Moves the socket into or out of nonblocking mode. @@ -242,7 +242,7 @@ impl UnixListener { impl AsRawFd for UnixListener { #[inline] fn as_raw_fd(&self) -> RawFd { - *self.0.as_inner() + self.0.as_inner().as_raw_fd() } } @@ -250,7 +250,7 @@ impl AsRawFd for UnixListener { impl FromRawFd for UnixListener { #[inline] unsafe fn from_raw_fd(fd: RawFd) -> UnixListener { - UnixListener(Socket::from_inner(fd)) + UnixListener(Socket::from_inner(FromInner::from_inner(OwnedFd::from_raw_fd(fd)))) } } @@ -258,7 +258,7 @@ impl FromRawFd for UnixListener { impl IntoRawFd for UnixListener { #[inline] fn into_raw_fd(self) -> RawFd { - self.0.into_inner() + self.0.into_inner().into_inner().into_raw_fd() } } diff --git a/library/std/src/os/unix/net/mod.rs b/library/std/src/os/unix/net/mod.rs index 3088ffb5e5c01..d462bd4b5f75e 100644 --- a/library/std/src/os/unix/net/mod.rs +++ b/library/std/src/os/unix/net/mod.rs @@ -25,7 +25,6 @@ mod addr; mod ancillary; mod datagram; mod listener; -mod raw_fd; mod stream; #[cfg(all(test, not(target_os = "emscripten")))] mod tests; @@ -48,7 +47,5 @@ pub use self::ancillary::*; pub use self::datagram::*; #[stable(feature = "unix_socket", since = "1.10.0")] pub use self::listener::*; -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::raw_fd::*; #[stable(feature = "unix_socket", since = "1.10.0")] pub use self::stream::*; diff --git a/library/std/src/os/unix/net/stream.rs b/library/std/src/os/unix/net/stream.rs index fba084375e5f8..4119de3c03cbe 100644 --- a/library/std/src/os/unix/net/stream.rs +++ b/library/std/src/os/unix/net/stream.rs @@ -13,7 +13,7 @@ use super::{sockaddr_un, SocketAddr}; use crate::fmt; use crate::io::{self, Initializer, IoSlice, IoSliceMut}; use crate::net::Shutdown; -use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; +use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; #[cfg(any( target_os = "android", target_os = "linux", @@ -28,7 +28,7 @@ use crate::os::unix::ucred; use crate::path::Path; use crate::sys::cvt; use crate::sys::net::Socket; -use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::sys_common::{AsInner, FromInner}; use crate::time::Duration; #[unstable(feature = "peer_credentials_unix_socket", issue = "42839", reason = "unstable")] @@ -101,7 +101,7 @@ impl UnixStream { let inner = Socket::new_raw(libc::AF_UNIX, libc::SOCK_STREAM)?; let (addr, len) = sockaddr_un(path.as_ref())?; - cvt(libc::connect(*inner.as_inner(), &addr as *const _ as *const _, len))?; + cvt(libc::connect(inner.as_raw_fd(), &addr as *const _ as *const _, len))?; Ok(UnixStream(inner)) } } @@ -167,7 +167,7 @@ impl UnixStream { /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn local_addr(&self) -> io::Result { - SocketAddr::new(|addr, len| unsafe { libc::getsockname(*self.0.as_inner(), addr, len) }) + SocketAddr::new(|addr, len| unsafe { libc::getsockname(self.as_raw_fd(), addr, len) }) } /// Returns the socket address of the remote half of this connection. @@ -185,7 +185,7 @@ impl UnixStream { /// ``` #[stable(feature = "unix_socket", since = "1.10.0")] pub fn peer_addr(&self) -> io::Result { - SocketAddr::new(|addr, len| unsafe { libc::getpeername(*self.0.as_inner(), addr, len) }) + SocketAddr::new(|addr, len| unsafe { libc::getpeername(self.as_raw_fd(), addr, len) }) } /// Gets the peer credentials for this Unix domain socket. @@ -659,7 +659,7 @@ impl<'a> io::Write for &'a UnixStream { impl AsRawFd for UnixStream { #[inline] fn as_raw_fd(&self) -> RawFd { - *self.0.as_inner() + self.0.as_raw_fd() } } @@ -667,7 +667,7 @@ impl AsRawFd for UnixStream { impl FromRawFd for UnixStream { #[inline] unsafe fn from_raw_fd(fd: RawFd) -> UnixStream { - UnixStream(Socket::from_inner(fd)) + UnixStream(Socket::from_inner(FromInner::from_inner(OwnedFd::from_raw_fd(fd)))) } } @@ -675,6 +675,30 @@ impl FromRawFd for UnixStream { impl IntoRawFd for UnixStream { #[inline] fn into_raw_fd(self) -> RawFd { - self.0.into_inner() + self.0.into_raw_fd() + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl AsFd for UnixStream { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl From for OwnedFd { + #[inline] + fn from(unix_stream: UnixStream) -> OwnedFd { + unsafe { OwnedFd::from_raw_fd(unix_stream.into_raw_fd()) } + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl From for UnixStream { + #[inline] + fn from(owned: OwnedFd) -> Self { + unsafe { Self::from_raw_fd(owned.into_raw_fd()) } } } diff --git a/library/std/src/os/unix/process.rs b/library/std/src/os/unix/process.rs index 615290d270307..650dcbabbae8c 100644 --- a/library/std/src/os/unix/process.rs +++ b/library/std/src/os/unix/process.rs @@ -4,7 +4,7 @@ use crate::ffi::OsStr; use crate::io; -use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; +use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; use crate::process; use crate::sealed::Sealed; use crate::sys; @@ -321,7 +321,17 @@ impl ExitStatusExt for process::ExitStatusError { impl FromRawFd for process::Stdio { #[inline] unsafe fn from_raw_fd(fd: RawFd) -> process::Stdio { - let fd = sys::fd::FileDesc::new(fd); + let fd = sys::fd::FileDesc::from_raw_fd(fd); + let io = sys::process::Stdio::Fd(fd); + process::Stdio::from_inner(io) + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl From for process::Stdio { + #[inline] + fn from(fd: OwnedFd) -> process::Stdio { + let fd = sys::fd::FileDesc::from_inner(fd); let io = sys::process::Stdio::Fd(fd); process::Stdio::from_inner(io) } @@ -331,7 +341,7 @@ impl FromRawFd for process::Stdio { impl AsRawFd for process::ChildStdin { #[inline] fn as_raw_fd(&self) -> RawFd { - self.as_inner().fd().raw() + self.as_inner().as_raw_fd() } } @@ -339,7 +349,7 @@ impl AsRawFd for process::ChildStdin { impl AsRawFd for process::ChildStdout { #[inline] fn as_raw_fd(&self) -> RawFd { - self.as_inner().fd().raw() + self.as_inner().as_raw_fd() } } @@ -347,7 +357,7 @@ impl AsRawFd for process::ChildStdout { impl AsRawFd for process::ChildStderr { #[inline] fn as_raw_fd(&self) -> RawFd { - self.as_inner().fd().raw() + self.as_inner().as_raw_fd() } } @@ -355,7 +365,7 @@ impl AsRawFd for process::ChildStderr { impl IntoRawFd for process::ChildStdin { #[inline] fn into_raw_fd(self) -> RawFd { - self.into_inner().into_fd().into_raw() + self.into_inner().into_inner().into_raw_fd() } } @@ -363,7 +373,7 @@ impl IntoRawFd for process::ChildStdin { impl IntoRawFd for process::ChildStdout { #[inline] fn into_raw_fd(self) -> RawFd { - self.into_inner().into_fd().into_raw() + self.into_inner().into_inner().into_raw_fd() } } @@ -371,7 +381,55 @@ impl IntoRawFd for process::ChildStdout { impl IntoRawFd for process::ChildStderr { #[inline] fn into_raw_fd(self) -> RawFd { - self.into_inner().into_fd().into_raw() + self.into_inner().into_inner().into_raw_fd() + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl AsFd for crate::process::ChildStdin { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + self.as_inner().as_fd() + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl From for OwnedFd { + #[inline] + fn from(child_stdin: crate::process::ChildStdin) -> OwnedFd { + child_stdin.into_inner().into_inner().into_inner() + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl AsFd for crate::process::ChildStdout { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + self.as_inner().as_fd() + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl From for OwnedFd { + #[inline] + fn from(child_stdout: crate::process::ChildStdout) -> OwnedFd { + child_stdout.into_inner().into_inner().into_inner() + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl AsFd for crate::process::ChildStderr { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + self.as_inner().as_fd() + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl From for OwnedFd { + #[inline] + fn from(child_stderr: crate::process::ChildStderr) -> OwnedFd { + child_stderr.into_inner().into_inner().into_inner() } } diff --git a/library/std/src/os/wasi/fs.rs b/library/std/src/os/wasi/fs.rs index bd30d6ae3f333..3df27563e21e9 100644 --- a/library/std/src/os/wasi/fs.rs +++ b/library/std/src/os/wasi/fs.rs @@ -228,35 +228,35 @@ pub trait FileExt { impl FileExt for fs::File { fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result { - self.as_inner().fd().pread(bufs, offset) + self.as_inner().as_inner().pread(bufs, offset) } fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result { - self.as_inner().fd().pwrite(bufs, offset) + self.as_inner().as_inner().pwrite(bufs, offset) } fn tell(&self) -> io::Result { - self.as_inner().fd().tell() + self.as_inner().as_inner().tell() } fn fdstat_set_flags(&self, flags: u16) -> io::Result<()> { - self.as_inner().fd().set_flags(flags) + self.as_inner().as_inner().set_flags(flags) } fn fdstat_set_rights(&self, rights: u64, inheriting: u64) -> io::Result<()> { - self.as_inner().fd().set_rights(rights, inheriting) + self.as_inner().as_inner().set_rights(rights, inheriting) } fn advise(&self, offset: u64, len: u64, advice: u8) -> io::Result<()> { - self.as_inner().fd().advise(offset, len, advice) + self.as_inner().as_inner().advise(offset, len, advice) } fn allocate(&self, offset: u64, len: u64) -> io::Result<()> { - self.as_inner().fd().allocate(offset, len) + self.as_inner().as_inner().allocate(offset, len) } fn create_directory>(&self, dir: P) -> io::Result<()> { - self.as_inner().fd().create_directory(osstr2str(dir.as_ref().as_ref())?) + self.as_inner().as_inner().create_directory(osstr2str(dir.as_ref().as_ref())?) } fn read_link>(&self, path: P) -> io::Result { @@ -269,11 +269,11 @@ impl FileExt for fs::File { } fn remove_file>(&self, path: P) -> io::Result<()> { - self.as_inner().fd().unlink_file(osstr2str(path.as_ref().as_ref())?) + self.as_inner().as_inner().unlink_file(osstr2str(path.as_ref().as_ref())?) } fn remove_directory>(&self, path: P) -> io::Result<()> { - self.as_inner().fd().remove_directory(osstr2str(path.as_ref().as_ref())?) + self.as_inner().as_inner().remove_directory(osstr2str(path.as_ref().as_ref())?) } } @@ -486,10 +486,10 @@ pub fn link, U: AsRef>( new_fd: &File, new_path: U, ) -> io::Result<()> { - old_fd.as_inner().fd().link( + old_fd.as_inner().as_inner().link( old_flags, osstr2str(old_path.as_ref().as_ref())?, - new_fd.as_inner().fd(), + new_fd.as_inner().as_inner(), osstr2str(new_path.as_ref().as_ref())?, ) } @@ -503,9 +503,9 @@ pub fn rename, U: AsRef>( new_fd: &File, new_path: U, ) -> io::Result<()> { - old_fd.as_inner().fd().rename( + old_fd.as_inner().as_inner().rename( osstr2str(old_path.as_ref().as_ref())?, - new_fd.as_inner().fd(), + new_fd.as_inner().as_inner(), osstr2str(new_path.as_ref().as_ref())?, ) } @@ -519,7 +519,7 @@ pub fn symlink, U: AsRef>( new_path: U, ) -> io::Result<()> { fd.as_inner() - .fd() + .as_inner() .symlink(osstr2str(old_path.as_ref().as_ref())?, osstr2str(new_path.as_ref().as_ref())?) } diff --git a/library/std/src/os/wasi/io.rs b/library/std/src/os/wasi/io.rs deleted file mode 100644 index b6bc74da8e7b5..0000000000000 --- a/library/std/src/os/wasi/io.rs +++ /dev/null @@ -1,208 +0,0 @@ -//! WASI-specific extensions to general I/O primitives - -#![deny(unsafe_op_in_unsafe_fn)] -#![unstable(feature = "wasi_ext", issue = "71213")] - -use crate::fs; -use crate::io; -use crate::net; -use crate::os::raw; -use crate::sys; -use crate::sys_common::{AsInner, FromInner, IntoInner}; - -/// Raw file descriptors. -/// -/// This has type `c_int` to ease compatibility with code that also compiles on -/// Unix configurations, however unlike Unix and POSIX, in WASI negative file -/// descriptors are valid. Only `-1` is reserved for indicating errors. Code -/// intending to be portable across Unix platforms and WASI should avoid -/// assuming that negative file descriptors are invalid. -pub type RawFd = raw::c_int; - -/// A trait to extract the raw WASI file descriptor from an underlying -/// object. -pub trait AsRawFd { - /// Extracts the raw file descriptor. - /// - /// This method does **not** pass ownership of the raw file descriptor - /// to the caller. The descriptor is only guaranteed to be valid while - /// the original object has not yet been destroyed. - fn as_raw_fd(&self) -> RawFd; -} - -/// A trait to express the ability to construct an object from a raw file -/// descriptor. -pub trait FromRawFd { - /// Constructs a new instance of `Self` from the given raw file - /// descriptor. - /// - /// This function **consumes ownership** of the specified file - /// descriptor. The returned object will take responsibility for closing - /// it when the object goes out of scope. - /// - /// This function is also unsafe as the primitives currently returned - /// have the contract that they are the sole owner of the file - /// descriptor they are wrapping. Usage of this function could - /// accidentally allow violating this contract which can cause memory - /// unsafety in code that relies on it being true. - unsafe fn from_raw_fd(fd: RawFd) -> Self; -} - -/// A trait to express the ability to consume an object and acquire ownership of -/// its raw file descriptor. -pub trait IntoRawFd { - /// Consumes this object, returning the raw underlying file descriptor. - /// - /// This function **transfers ownership** of the underlying file descriptor - /// to the caller. Callers are then the unique owners of the file descriptor - /// and must close the descriptor once it's no longer needed. - fn into_raw_fd(self) -> RawFd; -} - -#[stable(feature = "raw_fd_reflexive_traits", since = "1.48.0")] -impl AsRawFd for RawFd { - #[inline] - fn as_raw_fd(&self) -> RawFd { - *self - } -} -#[stable(feature = "raw_fd_reflexive_traits", since = "1.48.0")] -impl IntoRawFd for RawFd { - #[inline] - fn into_raw_fd(self) -> RawFd { - self - } -} -#[stable(feature = "raw_fd_reflexive_traits", since = "1.48.0")] -impl FromRawFd for RawFd { - #[inline] - unsafe fn from_raw_fd(fd: RawFd) -> RawFd { - fd - } -} - -impl AsRawFd for net::TcpStream { - #[inline] - fn as_raw_fd(&self) -> RawFd { - self.as_inner().fd().as_raw() - } -} - -impl FromRawFd for net::TcpStream { - #[inline] - unsafe fn from_raw_fd(fd: RawFd) -> net::TcpStream { - net::TcpStream::from_inner(sys::net::TcpStream::from_inner(fd)) - } -} - -impl IntoRawFd for net::TcpStream { - #[inline] - fn into_raw_fd(self) -> RawFd { - self.into_inner().into_fd().into_raw() - } -} - -impl AsRawFd for net::TcpListener { - #[inline] - fn as_raw_fd(&self) -> RawFd { - self.as_inner().fd().as_raw() - } -} - -impl FromRawFd for net::TcpListener { - #[inline] - unsafe fn from_raw_fd(fd: RawFd) -> net::TcpListener { - net::TcpListener::from_inner(sys::net::TcpListener::from_inner(fd)) - } -} - -impl IntoRawFd for net::TcpListener { - #[inline] - fn into_raw_fd(self) -> RawFd { - self.into_inner().into_fd().into_raw() - } -} - -impl AsRawFd for net::UdpSocket { - #[inline] - fn as_raw_fd(&self) -> RawFd { - self.as_inner().fd().as_raw() - } -} - -impl FromRawFd for net::UdpSocket { - #[inline] - unsafe fn from_raw_fd(fd: RawFd) -> net::UdpSocket { - net::UdpSocket::from_inner(sys::net::UdpSocket::from_inner(fd)) - } -} - -impl IntoRawFd for net::UdpSocket { - #[inline] - fn into_raw_fd(self) -> RawFd { - self.into_inner().into_fd().into_raw() - } -} - -impl AsRawFd for fs::File { - #[inline] - fn as_raw_fd(&self) -> RawFd { - self.as_inner().fd().as_raw() - } -} - -impl FromRawFd for fs::File { - #[inline] - unsafe fn from_raw_fd(fd: RawFd) -> fs::File { - fs::File::from_inner(sys::fs::File::from_inner(fd)) - } -} - -impl IntoRawFd for fs::File { - #[inline] - fn into_raw_fd(self) -> RawFd { - self.into_inner().into_fd().into_raw() - } -} - -impl AsRawFd for io::Stdin { - #[inline] - fn as_raw_fd(&self) -> RawFd { - libc::STDIN_FILENO - } -} - -impl AsRawFd for io::Stdout { - #[inline] - fn as_raw_fd(&self) -> RawFd { - libc::STDOUT_FILENO - } -} - -impl AsRawFd for io::Stderr { - #[inline] - fn as_raw_fd(&self) -> RawFd { - libc::STDERR_FILENO - } -} - -impl<'a> AsRawFd for io::StdinLock<'a> { - #[inline] - fn as_raw_fd(&self) -> RawFd { - libc::STDIN_FILENO - } -} - -impl<'a> AsRawFd for io::StdoutLock<'a> { - #[inline] - fn as_raw_fd(&self) -> RawFd { - libc::STDOUT_FILENO - } -} - -impl<'a> AsRawFd for io::StderrLock<'a> { - #[inline] - fn as_raw_fd(&self) -> RawFd { - libc::STDERR_FILENO - } -} diff --git a/library/std/src/os/wasi/io/fd.rs b/library/std/src/os/wasi/io/fd.rs new file mode 100644 index 0000000000000..930aca887e3c4 --- /dev/null +++ b/library/std/src/os/wasi/io/fd.rs @@ -0,0 +1,9 @@ +//! Owned and borrowed file descriptors. + +#![unstable(feature = "wasi_ext", issue = "71213")] + +// Tests for this module +#[cfg(test)] +mod tests; + +pub use crate::os::fd::owned::*; diff --git a/library/std/src/os/wasi/io/fd/tests.rs b/library/std/src/os/wasi/io/fd/tests.rs new file mode 100644 index 0000000000000..418274752b0ad --- /dev/null +++ b/library/std/src/os/wasi/io/fd/tests.rs @@ -0,0 +1,11 @@ +use crate::mem::size_of; +use crate::os::wasi::io::RawFd; + +#[test] +fn test_raw_fd_layout() { + // `OwnedFd` and `BorrowedFd` use `rustc_layout_scalar_valid_range_start` + // and `rustc_layout_scalar_valid_range_end`, with values that depend on + // the bit width of `RawFd`. If this ever changes, those values will need + // to be updated. + assert_eq!(size_of::(), 4); +} diff --git a/library/std/src/os/wasi/io/mod.rs b/library/std/src/os/wasi/io/mod.rs new file mode 100644 index 0000000000000..6c884e2eaf471 --- /dev/null +++ b/library/std/src/os/wasi/io/mod.rs @@ -0,0 +1,12 @@ +//! WASI-specific extensions to general I/O primitives. + +#![deny(unsafe_op_in_unsafe_fn)] +#![unstable(feature = "wasi_ext", issue = "71213")] + +mod fd; +mod raw; + +#[unstable(feature = "wasi_ext", issue = "71213")] +pub use fd::*; +#[unstable(feature = "wasi_ext", issue = "71213")] +pub use raw::*; diff --git a/library/std/src/os/wasi/io/raw.rs b/library/std/src/os/wasi/io/raw.rs new file mode 100644 index 0000000000000..0e0c5824e3404 --- /dev/null +++ b/library/std/src/os/wasi/io/raw.rs @@ -0,0 +1,5 @@ +//! WASI-specific extensions to general I/O primitives. + +#![unstable(feature = "wasi_ext", issue = "71213")] + +pub use crate::os::fd::raw::*; diff --git a/library/std/src/os/wasi/mod.rs b/library/std/src/os/wasi/mod.rs index 44b7c32e95617..d767c149dc5d0 100644 --- a/library/std/src/os/wasi/mod.rs +++ b/library/std/src/os/wasi/mod.rs @@ -32,6 +32,7 @@ pub mod ffi; pub mod fs; pub mod io; +pub mod net; /// A prelude for conveniently writing platform-specific code. /// @@ -49,5 +50,5 @@ pub mod prelude { pub use super::fs::{DirEntryExt, FileExt, MetadataExt, OpenOptionsExt}; #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] - pub use super::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; + pub use super::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; } diff --git a/library/std/src/os/wasi/net/mod.rs b/library/std/src/os/wasi/net/mod.rs new file mode 100644 index 0000000000000..e6bcf87887f03 --- /dev/null +++ b/library/std/src/os/wasi/net/mod.rs @@ -0,0 +1,3 @@ +//! WASI-specific networking functionality + +#![unstable(feature = "wasi_ext", issue = "71213")] diff --git a/library/std/src/os/windows/io/handle.rs b/library/std/src/os/windows/io/handle.rs new file mode 100644 index 0000000000000..72a17adb3b470 --- /dev/null +++ b/library/std/src/os/windows/io/handle.rs @@ -0,0 +1,386 @@ +//! Owned and borrowed OS handles. + +#![unstable(feature = "io_safety", issue = "87074")] + +use super::raw::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle}; +use crate::convert::TryFrom; +use crate::ffi::c_void; +use crate::fmt; +use crate::fs; +use crate::marker::PhantomData; +use crate::mem::forget; +use crate::ptr::NonNull; +use crate::sys::c; +use crate::sys_common::{AsInner, FromInner, IntoInner}; + +/// A borrowed handle. +/// +/// This has a lifetime parameter to tie it to the lifetime of something that +/// owns the handle. +/// +/// This uses `repr(transparent)` and has the representation of a host handle, +/// so it can be used in FFI in places where a handle is passed as an argument, +/// it is not captured or consumed, and it is never null. +/// +/// Note that it *may* have the value `INVALID_HANDLE_VALUE` (-1), which is +/// sometimes a valid handle value. See [here] for the full story. +/// +/// [here]: https://devblogs.microsoft.com/oldnewthing/20040302-00/?p=40443 +#[derive(Copy, Clone)] +#[repr(transparent)] +#[unstable(feature = "io_safety", issue = "87074")] +pub struct BorrowedHandle<'handle> { + handle: NonNull, + _phantom: PhantomData<&'handle OwnedHandle>, +} + +/// An owned handle. +/// +/// This closes the handle on drop. +/// +/// This uses `repr(transparent)` and has the representation of a host handle, +/// so it can be used in FFI in places where a handle is passed as a consumed +/// argument or returned as an owned value, and is never null. +/// +/// Note that it *may* have the value `INVALID_HANDLE_VALUE` (-1), which is +/// sometimes a valid handle value. See [here] for the full story. For APIs +/// like `CreateFileW` which report errors with `INVALID_HANDLE_VALUE` instead +/// of null, use [`HandleOrInvalid`] instead of `Option`. +/// +/// `OwnedHandle` uses [`CloseHandle`] to close its handle on drop. As such, +/// it must not be used with handles to open registry keys which need to be +/// closed with [`RegCloseKey`] instead. +/// +/// [`CloseHandle`]: https://docs.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-closehandle +/// [`RegCloseKey`]: https://docs.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regclosekey +/// +/// [here]: https://devblogs.microsoft.com/oldnewthing/20040302-00/?p=40443 +#[repr(transparent)] +#[unstable(feature = "io_safety", issue = "87074")] +pub struct OwnedHandle { + handle: NonNull, +} + +/// FFI type for handles in return values or out parameters, where `INVALID_HANDLE_VALUE` is used +/// as a sentry value to indicate errors, such as in the return value of `CreateFileW`. This uses +/// `repr(transparent)` and has the representation of a host handle, so that it can be used in such +/// FFI declarations. +/// +/// The only thing you can usefully do with a `HandleOrInvalid` is to convert it into an +/// `OwnedHandle` using its [`TryFrom`] implementation; this conversion takes care of the check for +/// `INVALID_HANDLE_VALUE`. This ensures that such FFI calls cannot start using the handle without +/// checking for `INVALID_HANDLE_VALUE` first. +/// +/// If this holds a valid handle, it will close the handle on drop. +#[repr(transparent)] +#[unstable(feature = "io_safety", issue = "87074")] +#[derive(Debug)] +pub struct HandleOrInvalid(Option); + +// The Windows [`HANDLE`] type may be transferred across and shared between +// thread boundaries (despite containing a `*mut void`, which in general isn't +// `Send` or `Sync`). +// +// [`HANDLE`]: std::os::windows::raw::HANDLE +unsafe impl Send for OwnedHandle {} +unsafe impl Send for HandleOrInvalid {} +unsafe impl Send for BorrowedHandle<'_> {} +unsafe impl Sync for OwnedHandle {} +unsafe impl Sync for HandleOrInvalid {} +unsafe impl Sync for BorrowedHandle<'_> {} + +impl BorrowedHandle<'_> { + /// Return a `BorrowedHandle` holding the given raw handle. + /// + /// # Safety + /// + /// The resource pointed to by `handle` must be a valid open handle, it + /// must remain open for the duration of the returned `BorrowedHandle`, and + /// it must not be null. + /// + /// Note that it *may* have the value `INVALID_HANDLE_VALUE` (-1), which is + /// sometimes a valid handle value. See [here] for the full story. + /// + /// [here]: https://devblogs.microsoft.com/oldnewthing/20040302-00/?p=40443 + #[inline] + #[unstable(feature = "io_safety", issue = "87074")] + pub unsafe fn borrow_raw_handle(handle: RawHandle) -> Self { + assert!(!handle.is_null()); + Self { handle: NonNull::new_unchecked(handle), _phantom: PhantomData } + } +} + +impl TryFrom for OwnedHandle { + type Error = (); + + #[inline] + fn try_from(handle_or_invalid: HandleOrInvalid) -> Result { + // In theory, we ought to be able to assume that the pointer here is + // never null, use `OwnedHandle` rather than `Option`, and + // obviate the the panic path here. Unfortunately, Win32 documentation + // doesn't explicitly guarantee this anywhere. + // + // APIs like [`CreateFileW`] itself have `HANDLE` arguments where a + // null handle indicates an absent value, which wouldn't work if null + // were a valid handle value, so it seems very unlikely that it could + // ever return null. But who knows? + // + // [`CreateFileW`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew + let owned_handle = handle_or_invalid.0.expect("A `HandleOrInvalid` was null!"); + if owned_handle.handle.as_ptr() == c::INVALID_HANDLE_VALUE { + Err(()) + } else { + Ok(owned_handle) + } + } +} + +impl AsRawHandle for BorrowedHandle<'_> { + #[inline] + fn as_raw_handle(&self) -> RawHandle { + self.handle.as_ptr() + } +} + +impl AsRawHandle for OwnedHandle { + #[inline] + fn as_raw_handle(&self) -> RawHandle { + self.handle.as_ptr() + } +} + +impl IntoRawHandle for OwnedHandle { + #[inline] + fn into_raw_handle(self) -> RawHandle { + let handle = self.handle.as_ptr(); + forget(self); + handle + } +} + +impl FromRawHandle for OwnedHandle { + /// Constructs a new instance of `Self` from the given raw handle. + /// + /// Use `HandleOrInvalid` instead of `Option` for APIs that + /// use `INVALID_HANDLE_VALUE` to indicate failure. + /// + /// # Safety + /// + /// The resource pointed to by `handle` must be open and suitable for + /// assuming ownership. The resource must not require any cleanup other + /// than `CloseHandle`. + /// + /// In particular, it must not be used with handles to open registry + /// keys which need to be closed with [`RegCloseKey`] instead. + /// + /// Note that it *may* have the value `INVALID_HANDLE_VALUE` (-1), which is + /// sometimes a valid handle value. See [here] for the full story. + /// + /// [`RegCloseKey`]: https://docs.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regclosekey + /// [here]: https://devblogs.microsoft.com/oldnewthing/20040302-00/?p=40443 + #[inline] + unsafe fn from_raw_handle(handle: RawHandle) -> Self { + assert!(!handle.is_null()); + Self { handle: NonNull::new_unchecked(handle) } + } +} + +impl FromRawHandle for HandleOrInvalid { + /// Constructs a new instance of `Self` from the given `RawHandle` returned + /// from a Windows API that uses `INVALID_HANDLE_VALUE` to indicate + /// failure, such as `CreateFileW`. + /// + /// Use `Option` instead of `HandleOrInvalid` for APIs that + /// use null to indicate failure. + /// + /// # Safety + /// + /// The resource pointed to by `handle` must be either open and otherwise + /// unowned, or equal to `INVALID_HANDLE_VALUE` (-1). It must not be null. + /// Note that not all Windows APIs use `INVALID_HANDLE_VALUE` for errors; + /// see [here] for the full story. + /// + /// [here]: https://devblogs.microsoft.com/oldnewthing/20040302-00/?p=40443 + #[inline] + unsafe fn from_raw_handle(handle: RawHandle) -> Self { + // We require non-null here to catch errors earlier. + Self(Some(OwnedHandle::from_raw_handle(handle))) + } +} + +impl Drop for OwnedHandle { + #[inline] + fn drop(&mut self) { + unsafe { + let _ = c::CloseHandle(self.handle.as_ptr()); + } + } +} + +impl fmt::Debug for BorrowedHandle<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("BorrowedHandle").field("handle", &self.handle).finish() + } +} + +impl fmt::Debug for OwnedHandle { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("OwnedHandle").field("handle", &self.handle).finish() + } +} + +/// A trait to borrow the handle from an underlying object. +#[unstable(feature = "io_safety", issue = "87074")] +pub trait AsHandle { + /// Borrows the handle. + /// + /// # Example + /// + /// ```rust,no_run + /// # #![feature(io_safety)] + /// use std::fs::File; + /// # use std::io; + /// use std::os::windows::io::{AsHandle, BorrowedHandle}; + /// + /// let mut f = File::open("foo.txt")?; + /// let borrowed_handle: BorrowedHandle<'_> = f.as_handle(); + /// # Ok::<(), io::Error>(()) + /// ``` + fn as_handle(&self) -> BorrowedHandle<'_>; +} + +impl AsHandle for BorrowedHandle<'_> { + #[inline] + fn as_handle(&self) -> BorrowedHandle<'_> { + *self + } +} + +impl AsHandle for OwnedHandle { + #[inline] + fn as_handle(&self) -> BorrowedHandle<'_> { + // Safety: `OwnedHandle` and `BorrowedHandle` have the same validity + // invariants, and the `BorrowdHandle` is bounded by the lifetime + // of `&self`. + unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) } + } +} + +impl AsHandle for fs::File { + #[inline] + fn as_handle(&self) -> BorrowedHandle<'_> { + self.as_inner().as_handle() + } +} + +impl From for OwnedHandle { + #[inline] + fn from(file: fs::File) -> OwnedHandle { + file.into_inner().into_inner().into_inner().into() + } +} + +impl From for fs::File { + #[inline] + fn from(owned: OwnedHandle) -> Self { + Self::from_inner(FromInner::from_inner(FromInner::from_inner(owned))) + } +} + +impl AsHandle for crate::io::Stdin { + #[inline] + fn as_handle(&self) -> BorrowedHandle<'_> { + unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) } + } +} + +impl<'a> AsHandle for crate::io::StdinLock<'a> { + #[inline] + fn as_handle(&self) -> BorrowedHandle<'_> { + unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) } + } +} + +impl AsHandle for crate::io::Stdout { + #[inline] + fn as_handle(&self) -> BorrowedHandle<'_> { + unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) } + } +} + +impl<'a> AsHandle for crate::io::StdoutLock<'a> { + #[inline] + fn as_handle(&self) -> BorrowedHandle<'_> { + unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) } + } +} + +impl AsHandle for crate::io::Stderr { + #[inline] + fn as_handle(&self) -> BorrowedHandle<'_> { + unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) } + } +} + +impl<'a> AsHandle for crate::io::StderrLock<'a> { + #[inline] + fn as_handle(&self) -> BorrowedHandle<'_> { + unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) } + } +} + +impl AsHandle for crate::process::ChildStdin { + #[inline] + fn as_handle(&self) -> BorrowedHandle<'_> { + unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) } + } +} + +impl From for OwnedHandle { + #[inline] + fn from(child_stdin: crate::process::ChildStdin) -> OwnedHandle { + unsafe { OwnedHandle::from_raw_handle(child_stdin.into_raw_handle()) } + } +} + +impl AsHandle for crate::process::ChildStdout { + #[inline] + fn as_handle(&self) -> BorrowedHandle<'_> { + unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) } + } +} + +impl From for OwnedHandle { + #[inline] + fn from(child_stdout: crate::process::ChildStdout) -> OwnedHandle { + unsafe { OwnedHandle::from_raw_handle(child_stdout.into_raw_handle()) } + } +} + +impl AsHandle for crate::process::ChildStderr { + #[inline] + fn as_handle(&self) -> BorrowedHandle<'_> { + unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) } + } +} + +impl From for OwnedHandle { + #[inline] + fn from(child_stderr: crate::process::ChildStderr) -> OwnedHandle { + unsafe { OwnedHandle::from_raw_handle(child_stderr.into_raw_handle()) } + } +} + +impl AsHandle for crate::thread::JoinHandle { + #[inline] + fn as_handle(&self) -> BorrowedHandle<'_> { + unsafe { BorrowedHandle::borrow_raw_handle(self.as_raw_handle()) } + } +} + +impl From> for OwnedHandle { + #[inline] + fn from(join_handle: crate::thread::JoinHandle) -> OwnedHandle { + join_handle.into_inner().into_handle().into_inner() + } +} diff --git a/library/std/src/os/windows/io/mod.rs b/library/std/src/os/windows/io/mod.rs new file mode 100644 index 0000000000000..2f6f076954809 --- /dev/null +++ b/library/std/src/os/windows/io/mod.rs @@ -0,0 +1,56 @@ +//! Windows-specific extensions to general I/O primitives. +//! +//! Just like raw pointers, raw Windows handles and sockets point to resources +//! with dynamic lifetimes, and they can dangle if they outlive their resources +//! or be forged if they're created from invalid values. +//! +//! This module provides three types for representing raw handles and sockets +//! with different ownership properties: raw, borrowed, and owned, which are +//! analogous to types used for representing pointers: +//! +//! | Type | Analogous to | +//! | ---------------------- | ------------ | +//! | [`RawHandle`] | `*const _` | +//! | [`RawSocket`] | `*const _` | +//! | | | +//! | [`BorrowedHandle<'a>`] | `&'a _` | +//! | [`BorrowedSocket<'a>`] | `&'a _` | +//! | | | +//! | [`OwnedHandle`] | `Box<_>` | +//! | [`OwnedSocket`] | `Box<_>` | +//! +//! Like raw pointers, `RawHandle` and `RawSocket` values are primitive values. +//! And in new code, they should be considered unsafe to do I/O on (analogous +//! to dereferencing them). Rust did not always provide this guidance, so +//! existing code in the Rust ecosystem often doesn't mark `RawHandle` and +//! `RawSocket` usage as unsafe. Once the `io_safety` feature is stable, +//! libraries will be encouraged to migrate, either by adding `unsafe` to APIs +//! that dereference `RawHandle` and `RawSocket` values, or by using to +//! `BorrowedHandle`, `BorrowedSocket`, `OwnedHandle`, or `OwnedSocket`. +//! +//! Like references, `BorrowedHandle` and `BorrowedSocket` values are tied to a +//! lifetime, to ensure that they don't outlive the resource they point to. +//! These are safe to use. `BorrowedHandle` and `BorrowedSocket` values may be +//! used in APIs which provide safe access to any system call except for +//! `CloseHandle`, `closesocket`, or any other call that would end the +//! dynamic lifetime of the resource without ending the lifetime of the +//! handle or socket. +//! +//! Like boxes, `OwnedHandle` and `OwnedSocket` values conceptually own the +//! resource they point to, and free (close) it when they are dropped. +//! +//! [`BorrowedHandle<'a>`]: crate::os::windows::io::BorrowedHandle +//! [`BorrowedSocket<'a>`]: crate::os::windows::io::BorrowedSocket + +#![stable(feature = "rust1", since = "1.0.0")] + +mod handle; +mod raw; +mod socket; + +#[unstable(feature = "io_safety", issue = "87074")] +pub use handle::*; +#[stable(feature = "rust1", since = "1.0.0")] +pub use raw::*; +#[unstable(feature = "io_safety", issue = "87074")] +pub use socket::*; diff --git a/library/std/src/os/windows/io.rs b/library/std/src/os/windows/io/raw.rs similarity index 88% rename from library/std/src/os/windows/io.rs rename to library/std/src/os/windows/io/raw.rs index 31b5d015ed0c3..c7f122048a198 100644 --- a/library/std/src/os/windows/io.rs +++ b/library/std/src/os/windows/io/raw.rs @@ -5,6 +5,7 @@ use crate::fs; use crate::io; use crate::net; +use crate::os::windows::io::{OwnedHandle, OwnedSocket}; use crate::os::windows::raw; use crate::sys; use crate::sys::c; @@ -61,7 +62,7 @@ pub trait IntoRawHandle { impl AsRawHandle for fs::File { #[inline] fn as_raw_handle(&self) -> RawHandle { - self.as_inner().handle().raw() as RawHandle + self.as_inner().as_raw_handle() as RawHandle } } @@ -112,7 +113,9 @@ impl FromRawHandle for fs::File { #[inline] unsafe fn from_raw_handle(handle: RawHandle) -> fs::File { let handle = handle as c::HANDLE; - fs::File::from_inner(sys::fs::File::from_inner(handle)) + fs::File::from_inner(sys::fs::File::from_inner(FromInner::from_inner( + OwnedHandle::from_raw_handle(handle), + ))) } } @@ -120,7 +123,7 @@ impl FromRawHandle for fs::File { impl IntoRawHandle for fs::File { #[inline] fn into_raw_handle(self) -> RawHandle { - self.into_inner().into_handle().into_raw() as *mut _ + self.into_inner().into_raw_handle() as *mut _ } } @@ -166,21 +169,21 @@ pub trait IntoRawSocket { impl AsRawSocket for net::TcpStream { #[inline] fn as_raw_socket(&self) -> RawSocket { - *self.as_inner().socket().as_inner() + self.as_inner().socket().as_raw_socket() } } #[stable(feature = "rust1", since = "1.0.0")] impl AsRawSocket for net::TcpListener { #[inline] fn as_raw_socket(&self) -> RawSocket { - *self.as_inner().socket().as_inner() + self.as_inner().socket().as_raw_socket() } } #[stable(feature = "rust1", since = "1.0.0")] impl AsRawSocket for net::UdpSocket { #[inline] fn as_raw_socket(&self) -> RawSocket { - *self.as_inner().socket().as_inner() + self.as_inner().socket().as_raw_socket() } } @@ -188,7 +191,7 @@ impl AsRawSocket for net::UdpSocket { impl FromRawSocket for net::TcpStream { #[inline] unsafe fn from_raw_socket(sock: RawSocket) -> net::TcpStream { - let sock = sys::net::Socket::from_inner(sock); + let sock = sys::net::Socket::from_inner(OwnedSocket::from_raw_socket(sock)); net::TcpStream::from_inner(sys_common::net::TcpStream::from_inner(sock)) } } @@ -196,7 +199,7 @@ impl FromRawSocket for net::TcpStream { impl FromRawSocket for net::TcpListener { #[inline] unsafe fn from_raw_socket(sock: RawSocket) -> net::TcpListener { - let sock = sys::net::Socket::from_inner(sock); + let sock = sys::net::Socket::from_inner(OwnedSocket::from_raw_socket(sock)); net::TcpListener::from_inner(sys_common::net::TcpListener::from_inner(sock)) } } @@ -204,7 +207,7 @@ impl FromRawSocket for net::TcpListener { impl FromRawSocket for net::UdpSocket { #[inline] unsafe fn from_raw_socket(sock: RawSocket) -> net::UdpSocket { - let sock = sys::net::Socket::from_inner(sock); + let sock = sys::net::Socket::from_inner(OwnedSocket::from_raw_socket(sock)); net::UdpSocket::from_inner(sys_common::net::UdpSocket::from_inner(sock)) } } @@ -213,7 +216,7 @@ impl FromRawSocket for net::UdpSocket { impl IntoRawSocket for net::TcpStream { #[inline] fn into_raw_socket(self) -> RawSocket { - self.into_inner().into_socket().into_inner() + self.into_inner().into_socket().into_inner().into_raw_socket() } } @@ -221,7 +224,7 @@ impl IntoRawSocket for net::TcpStream { impl IntoRawSocket for net::TcpListener { #[inline] fn into_raw_socket(self) -> RawSocket { - self.into_inner().into_socket().into_inner() + self.into_inner().into_socket().into_inner().into_raw_socket() } } @@ -229,6 +232,6 @@ impl IntoRawSocket for net::TcpListener { impl IntoRawSocket for net::UdpSocket { #[inline] fn into_raw_socket(self) -> RawSocket { - self.into_inner().into_socket().into_inner() + self.into_inner().into_socket().into_inner().into_raw_socket() } } diff --git a/library/std/src/os/windows/io/socket.rs b/library/std/src/os/windows/io/socket.rs new file mode 100644 index 0000000000000..23db66df09f7a --- /dev/null +++ b/library/std/src/os/windows/io/socket.rs @@ -0,0 +1,216 @@ +//! Owned and borrowed OS sockets. + +#![unstable(feature = "io_safety", issue = "87074")] + +use super::raw::{AsRawSocket, FromRawSocket, IntoRawSocket, RawSocket}; +use crate::fmt; +use crate::marker::PhantomData; +use crate::mem::forget; +use crate::sys::c; + +/// A borrowed socket. +/// +/// This has a lifetime parameter to tie it to the lifetime of something that +/// owns the socket. +/// +/// This uses `repr(transparent)` and has the representation of a host socket, +/// so it can be used in FFI in places where a socket is passed as an argument, +/// it is not captured or consumed, and it never has the value +/// `INVALID_SOCKET`. +#[derive(Copy, Clone)] +#[repr(transparent)] +#[rustc_layout_scalar_valid_range_start(0)] +// This is -2, in two's complement. -1 is `INVALID_SOCKET`. +#[cfg_attr(target_pointer_width = "32", rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE))] +#[cfg_attr( + target_pointer_width = "64", + rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FF_FF_FF_FF_FE) +)] +#[unstable(feature = "io_safety", issue = "87074")] +pub struct BorrowedSocket<'socket> { + socket: RawSocket, + _phantom: PhantomData<&'socket OwnedSocket>, +} + +/// An owned socket. +/// +/// This closes the socket on drop. +/// +/// This uses `repr(transparent)` and has the representation of a host socket, +/// so it can be used in FFI in places where a socket is passed as a consumed +/// argument or returned as an owned value, and it never has the value +/// `INVALID_SOCKET`. +#[repr(transparent)] +#[rustc_layout_scalar_valid_range_start(0)] +// This is -2, in two's complement. -1 is `INVALID_SOCKET`. +#[cfg_attr(target_pointer_width = "32", rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE))] +#[cfg_attr( + target_pointer_width = "64", + rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FF_FF_FF_FF_FE) +)] +#[unstable(feature = "io_safety", issue = "87074")] +pub struct OwnedSocket { + socket: RawSocket, +} + +impl BorrowedSocket<'_> { + /// Return a `BorrowedSocket` holding the given raw socket. + /// + /// # Safety + /// + /// The resource pointed to by `raw` must remain open for the duration of + /// the returned `BorrowedSocket`, and it must not have the value + /// `INVALID_SOCKET`. + #[inline] + #[unstable(feature = "io_safety", issue = "87074")] + pub unsafe fn borrow_raw_socket(socket: RawSocket) -> Self { + debug_assert_ne!(socket, c::INVALID_SOCKET as RawSocket); + Self { socket, _phantom: PhantomData } + } +} + +impl AsRawSocket for BorrowedSocket<'_> { + #[inline] + fn as_raw_socket(&self) -> RawSocket { + self.socket + } +} + +impl AsRawSocket for OwnedSocket { + #[inline] + fn as_raw_socket(&self) -> RawSocket { + self.socket + } +} + +impl IntoRawSocket for OwnedSocket { + #[inline] + fn into_raw_socket(self) -> RawSocket { + let socket = self.socket; + forget(self); + socket + } +} + +impl FromRawSocket for OwnedSocket { + /// Constructs a new instance of `Self` from the given raw socket. + /// + /// # Safety + /// + /// The resource pointed to by `socket` must be open and suitable for + /// assuming ownership. The resource must not require cleanup other than + /// `closesocket`. + #[inline] + unsafe fn from_raw_socket(socket: RawSocket) -> Self { + debug_assert_ne!(socket, c::INVALID_SOCKET as RawSocket); + Self { socket } + } +} + +impl Drop for OwnedSocket { + #[inline] + fn drop(&mut self) { + unsafe { + let _ = c::closesocket(self.socket); + } + } +} + +impl fmt::Debug for BorrowedSocket<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("BorrowedSocket").field("socket", &self.socket).finish() + } +} + +impl fmt::Debug for OwnedSocket { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("OwnedSocket").field("socket", &self.socket).finish() + } +} + +/// A trait to borrow the socket from an underlying object. +#[unstable(feature = "io_safety", issue = "87074")] +pub trait AsSocket { + /// Borrows the socket. + fn as_socket(&self) -> BorrowedSocket<'_>; +} + +impl AsSocket for BorrowedSocket<'_> { + #[inline] + fn as_socket(&self) -> BorrowedSocket<'_> { + *self + } +} + +impl AsSocket for OwnedSocket { + #[inline] + fn as_socket(&self) -> BorrowedSocket<'_> { + // Safety: `OwnedSocket` and `BorrowedSocket` have the same validity + // invariants, and the `BorrowdSocket` is bounded by the lifetime + // of `&self`. + unsafe { BorrowedSocket::borrow_raw_socket(self.as_raw_socket()) } + } +} + +impl AsSocket for crate::net::TcpStream { + #[inline] + fn as_socket(&self) -> BorrowedSocket<'_> { + unsafe { BorrowedSocket::borrow_raw_socket(self.as_raw_socket()) } + } +} + +impl From for OwnedSocket { + #[inline] + fn from(tcp_stream: crate::net::TcpStream) -> OwnedSocket { + unsafe { OwnedSocket::from_raw_socket(tcp_stream.into_raw_socket()) } + } +} + +impl From for crate::net::TcpStream { + #[inline] + fn from(owned: OwnedSocket) -> Self { + unsafe { Self::from_raw_socket(owned.into_raw_socket()) } + } +} + +impl AsSocket for crate::net::TcpListener { + #[inline] + fn as_socket(&self) -> BorrowedSocket<'_> { + unsafe { BorrowedSocket::borrow_raw_socket(self.as_raw_socket()) } + } +} + +impl From for OwnedSocket { + #[inline] + fn from(tcp_listener: crate::net::TcpListener) -> OwnedSocket { + unsafe { OwnedSocket::from_raw_socket(tcp_listener.into_raw_socket()) } + } +} + +impl From for crate::net::TcpListener { + #[inline] + fn from(owned: OwnedSocket) -> Self { + unsafe { Self::from_raw_socket(owned.into_raw_socket()) } + } +} + +impl AsSocket for crate::net::UdpSocket { + #[inline] + fn as_socket(&self) -> BorrowedSocket<'_> { + unsafe { BorrowedSocket::borrow_raw_socket(self.as_raw_socket()) } + } +} + +impl From for OwnedSocket { + #[inline] + fn from(udp_socket: crate::net::UdpSocket) -> OwnedSocket { + unsafe { OwnedSocket::from_raw_socket(udp_socket.into_raw_socket()) } + } +} + +impl From for crate::net::UdpSocket { + #[inline] + fn from(owned: OwnedSocket) -> Self { + unsafe { Self::from_raw_socket(owned.into_raw_socket()) } + } +} diff --git a/library/std/src/os/windows/mod.rs b/library/std/src/os/windows/mod.rs index 52ac508f9f707..969054dd3b3dc 100644 --- a/library/std/src/os/windows/mod.rs +++ b/library/std/src/os/windows/mod.rs @@ -32,8 +32,11 @@ pub mod prelude { pub use super::fs::{MetadataExt, OpenOptionsExt}; #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] - pub use super::io::{AsRawHandle, AsRawSocket, RawHandle, RawSocket}; + pub use super::io::{ + AsHandle, AsSocket, BorrowedHandle, BorrowedSocket, FromRawHandle, FromRawSocket, + HandleOrInvalid, IntoRawHandle, IntoRawSocket, OwnedHandle, OwnedSocket, + }; #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] - pub use super::io::{FromRawHandle, FromRawSocket, IntoRawHandle, IntoRawSocket}; + pub use super::io::{AsRawHandle, AsRawSocket, RawHandle, RawSocket}; } diff --git a/library/std/src/os/windows/process.rs b/library/std/src/os/windows/process.rs index 9e7ccd015b658..b246599dfc0a9 100644 --- a/library/std/src/os/windows/process.rs +++ b/library/std/src/os/windows/process.rs @@ -3,7 +3,9 @@ #![stable(feature = "process_extensions", since = "1.2.0")] use crate::ffi::OsStr; -use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle}; +use crate::os::windows::io::{ + AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle, IntoRawHandle, OwnedHandle, RawHandle, +}; use crate::process; use crate::sealed::Sealed; use crate::sys; @@ -12,7 +14,16 @@ use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; #[stable(feature = "process_extensions", since = "1.2.0")] impl FromRawHandle for process::Stdio { unsafe fn from_raw_handle(handle: RawHandle) -> process::Stdio { - let handle = sys::handle::Handle::new(handle as *mut _); + let handle = sys::handle::Handle::from_raw_handle(handle as *mut _); + let io = sys::process::Stdio::Handle(handle); + process::Stdio::from_inner(io) + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl From for process::Stdio { + fn from(handle: OwnedHandle) -> process::Stdio { + let handle = sys::handle::Handle::from_inner(handle); let io = sys::process::Stdio::Handle(handle); process::Stdio::from_inner(io) } @@ -22,14 +33,29 @@ impl FromRawHandle for process::Stdio { impl AsRawHandle for process::Child { #[inline] fn as_raw_handle(&self) -> RawHandle { - self.as_inner().handle().raw() as *mut _ + self.as_inner().handle().as_raw_handle() as *mut _ + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl AsHandle for process::Child { + #[inline] + fn as_handle(&self) -> BorrowedHandle<'_> { + self.as_inner().handle().as_handle() } } #[stable(feature = "into_raw_os", since = "1.4.0")] impl IntoRawHandle for process::Child { fn into_raw_handle(self) -> RawHandle { - self.into_inner().into_handle().into_raw() as *mut _ + self.into_inner().into_handle().into_raw_handle() as *mut _ + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl From for OwnedHandle { + fn from(child: process::Child) -> OwnedHandle { + child.into_inner().into_handle().into_inner() } } @@ -37,7 +63,7 @@ impl IntoRawHandle for process::Child { impl AsRawHandle for process::ChildStdin { #[inline] fn as_raw_handle(&self) -> RawHandle { - self.as_inner().handle().raw() as *mut _ + self.as_inner().handle().as_raw_handle() as *mut _ } } @@ -45,7 +71,7 @@ impl AsRawHandle for process::ChildStdin { impl AsRawHandle for process::ChildStdout { #[inline] fn as_raw_handle(&self) -> RawHandle { - self.as_inner().handle().raw() as *mut _ + self.as_inner().handle().as_raw_handle() as *mut _ } } @@ -53,28 +79,28 @@ impl AsRawHandle for process::ChildStdout { impl AsRawHandle for process::ChildStderr { #[inline] fn as_raw_handle(&self) -> RawHandle { - self.as_inner().handle().raw() as *mut _ + self.as_inner().handle().as_raw_handle() as *mut _ } } #[stable(feature = "into_raw_os", since = "1.4.0")] impl IntoRawHandle for process::ChildStdin { fn into_raw_handle(self) -> RawHandle { - self.into_inner().into_handle().into_raw() as *mut _ + self.into_inner().into_handle().into_raw_handle() as *mut _ } } #[stable(feature = "into_raw_os", since = "1.4.0")] impl IntoRawHandle for process::ChildStdout { fn into_raw_handle(self) -> RawHandle { - self.into_inner().into_handle().into_raw() as *mut _ + self.into_inner().into_handle().into_raw_handle() as *mut _ } } #[stable(feature = "into_raw_os", since = "1.4.0")] impl IntoRawHandle for process::ChildStderr { fn into_raw_handle(self) -> RawHandle { - self.into_inner().into_handle().into_raw() as *mut _ + self.into_inner().into_handle().into_raw_handle() as *mut _ } } diff --git a/library/std/src/os/windows/thread.rs b/library/std/src/os/windows/thread.rs index 6bd02054f7150..fb1bf66ceed35 100644 --- a/library/std/src/os/windows/thread.rs +++ b/library/std/src/os/windows/thread.rs @@ -10,7 +10,7 @@ use crate::thread; impl AsRawHandle for thread::JoinHandle { #[inline] fn as_raw_handle(&self) -> RawHandle { - self.as_inner().handle().raw() as *mut _ + self.as_inner().handle().as_raw_handle() as *mut _ } } @@ -18,6 +18,6 @@ impl AsRawHandle for thread::JoinHandle { impl IntoRawHandle for thread::JoinHandle { #[inline] fn into_raw_handle(self) -> RawHandle { - self.into_inner().into_handle().into_raw() as *mut _ + self.into_inner().into_handle().into_raw_handle() as *mut _ } } diff --git a/library/std/src/process.rs b/library/std/src/process.rs index f5ce5210f8154..c9b21fcf9c6d2 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -258,6 +258,12 @@ pub struct ChildStdin { inner: AnonPipe, } +// In addition to the `impl`s here, `ChildStdin` also has `impl`s for +// `AsFd`/`From`/`Into` and +// `AsRawFd`/`IntoRawFd`/`FromRawFd`, on Unix and WASI, and +// `AsHandle`/`From`/`Into` and +// `AsRawHandle`/`IntoRawHandle`/`FromRawHandle` on Windows. + #[stable(feature = "process", since = "1.0.0")] impl Write for ChildStdin { fn write(&mut self, buf: &[u8]) -> io::Result { @@ -335,6 +341,12 @@ pub struct ChildStdout { inner: AnonPipe, } +// In addition to the `impl`s here, `ChildStdout` also has `impl`s for +// `AsFd`/`From`/`Into` and +// `AsRawFd`/`IntoRawFd`/`FromRawFd`, on Unix and WASI, and +// `AsHandle`/`From`/`Into` and +// `AsRawHandle`/`IntoRawHandle`/`FromRawHandle` on Windows. + #[stable(feature = "process", since = "1.0.0")] impl Read for ChildStdout { fn read(&mut self, buf: &mut [u8]) -> io::Result { @@ -396,6 +408,12 @@ pub struct ChildStderr { inner: AnonPipe, } +// In addition to the `impl`s here, `ChildStderr` also has `impl`s for +// `AsFd`/`From`/`Into` and +// `AsRawFd`/`IntoRawFd`/`FromRawFd`, on Unix and WASI, and +// `AsHandle`/`From`/`Into` and +// `AsRawHandle`/`IntoRawHandle`/`FromRawHandle` on Windows. + #[stable(feature = "process", since = "1.0.0")] impl Read for ChildStderr { fn read(&mut self, buf: &mut [u8]) -> io::Result { diff --git a/library/std/src/sys/unix/fd.rs b/library/std/src/sys/unix/fd.rs index 28e32681e15b3..0956726084e02 100644 --- a/library/std/src/sys/unix/fd.rs +++ b/library/std/src/sys/unix/fd.rs @@ -5,21 +5,14 @@ mod tests; use crate::cmp; use crate::io::{self, Initializer, IoSlice, IoSliceMut, Read}; -use crate::mem; +use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; use crate::sys::cvt; -use crate::sys_common::AsInner; +use crate::sys_common::{AsInner, FromInner, IntoInner}; use libc::{c_int, c_void}; #[derive(Debug)] -#[rustc_layout_scalar_valid_range_start(0)] -// libstd/os/raw/mod.rs assures me that every libstd-supported platform has a -// 32-bit c_int. Below is -2, in two's complement, but that only works out -// because c_int is 32 bits. -#[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)] -pub struct FileDesc { - fd: c_int, -} +pub struct FileDesc(OwnedFd); // The maximum read limit on most POSIX-like systems is `SSIZE_MAX`, // with the man page quoting that if the count of bytes to read is @@ -67,26 +60,13 @@ const fn max_iov() -> usize { } impl FileDesc { - pub fn new(fd: c_int) -> FileDesc { - assert_ne!(fd, -1i32); - // SAFETY: we just asserted that the value is in the valid range and isn't `-1` (the only value bigger than `0xFF_FF_FF_FE` unsigned) - unsafe { FileDesc { fd } } - } - - pub fn raw(&self) -> c_int { - self.fd - } - - /// Extracts the actual file descriptor without closing it. - pub fn into_raw(self) -> c_int { - let fd = self.fd; - mem::forget(self); - fd - } - pub fn read(&self, buf: &mut [u8]) -> io::Result { let ret = cvt(unsafe { - libc::read(self.fd, buf.as_mut_ptr() as *mut c_void, cmp::min(buf.len(), READ_LIMIT)) + libc::read( + self.as_raw_fd(), + buf.as_mut_ptr() as *mut c_void, + cmp::min(buf.len(), READ_LIMIT), + ) })?; Ok(ret as usize) } @@ -95,7 +75,7 @@ impl FileDesc { pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { let ret = cvt(unsafe { libc::readv( - self.fd, + self.as_raw_fd(), bufs.as_ptr() as *const libc::iovec, cmp::min(bufs.len(), max_iov()) as c_int, ) @@ -138,7 +118,7 @@ impl FileDesc { unsafe { cvt_pread64( - self.fd, + self.as_raw_fd(), buf.as_mut_ptr() as *mut c_void, cmp::min(buf.len(), READ_LIMIT), offset as i64, @@ -149,7 +129,11 @@ impl FileDesc { pub fn write(&self, buf: &[u8]) -> io::Result { let ret = cvt(unsafe { - libc::write(self.fd, buf.as_ptr() as *const c_void, cmp::min(buf.len(), READ_LIMIT)) + libc::write( + self.as_raw_fd(), + buf.as_ptr() as *const c_void, + cmp::min(buf.len(), READ_LIMIT), + ) })?; Ok(ret as usize) } @@ -158,7 +142,7 @@ impl FileDesc { pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { let ret = cvt(unsafe { libc::writev( - self.fd, + self.as_raw_fd(), bufs.as_ptr() as *const libc::iovec, cmp::min(bufs.len(), max_iov()) as c_int, ) @@ -196,7 +180,7 @@ impl FileDesc { unsafe { cvt_pwrite64( - self.fd, + self.as_raw_fd(), buf.as_ptr() as *const c_void, cmp::min(buf.len(), READ_LIMIT), offset as i64, @@ -207,7 +191,7 @@ impl FileDesc { #[cfg(target_os = "linux")] pub fn get_cloexec(&self) -> io::Result { - unsafe { Ok((cvt(libc::fcntl(self.fd, libc::F_GETFD))? & libc::FD_CLOEXEC) != 0) } + unsafe { Ok((cvt(libc::fcntl(self.as_raw_fd(), libc::F_GETFD))? & libc::FD_CLOEXEC) != 0) } } #[cfg(not(any( @@ -224,7 +208,7 @@ impl FileDesc { )))] pub fn set_cloexec(&self) -> io::Result<()> { unsafe { - cvt(libc::ioctl(self.fd, libc::FIOCLEX))?; + cvt(libc::ioctl(self.as_raw_fd(), libc::FIOCLEX))?; Ok(()) } } @@ -242,10 +226,10 @@ impl FileDesc { ))] pub fn set_cloexec(&self) -> io::Result<()> { unsafe { - let previous = cvt(libc::fcntl(self.fd, libc::F_GETFD))?; + let previous = cvt(libc::fcntl(self.as_raw_fd(), libc::F_GETFD))?; let new = previous | libc::FD_CLOEXEC; if new != previous { - cvt(libc::fcntl(self.fd, libc::F_SETFD, new))?; + cvt(libc::fcntl(self.as_raw_fd(), libc::F_SETFD, new))?; } Ok(()) } @@ -261,7 +245,7 @@ impl FileDesc { pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { unsafe { let v = nonblocking as c_int; - cvt(libc::ioctl(self.fd, libc::FIONBIO, &v))?; + cvt(libc::ioctl(self.as_raw_fd(), libc::FIONBIO, &v))?; Ok(()) } } @@ -269,14 +253,14 @@ impl FileDesc { #[cfg(not(target_os = "linux"))] pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { unsafe { - let previous = cvt(libc::fcntl(self.fd, libc::F_GETFL))?; + let previous = cvt(libc::fcntl(self.as_raw_fd(), libc::F_GETFL))?; let new = if nonblocking { previous | libc::O_NONBLOCK } else { previous & !libc::O_NONBLOCK }; if new != previous { - cvt(libc::fcntl(self.fd, libc::F_SETFL, new))?; + cvt(libc::fcntl(self.as_raw_fd(), libc::F_SETFL, new))?; } Ok(()) } @@ -296,8 +280,8 @@ impl FileDesc { #[cfg(target_os = "espidf")] let cmd = libc::F_DUPFD; - let fd = cvt(unsafe { libc::fcntl(self.raw(), cmd, 0) })?; - Ok(FileDesc::new(fd)) + let fd = cvt(unsafe { libc::fcntl(self.as_raw_fd(), cmd, 0) })?; + Ok(unsafe { FileDesc::from_raw_fd(fd) }) } } @@ -312,19 +296,44 @@ impl<'a> Read for &'a FileDesc { } } -impl AsInner for FileDesc { - fn as_inner(&self) -> &c_int { - &self.fd +impl AsInner for FileDesc { + fn as_inner(&self) -> &OwnedFd { + &self.0 + } +} + +impl IntoInner for FileDesc { + fn into_inner(self) -> OwnedFd { + self.0 + } +} + +impl FromInner for FileDesc { + fn from_inner(owned_fd: OwnedFd) -> Self { + Self(owned_fd) + } +} + +impl AsFd for FileDesc { + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } +} + +impl AsRawFd for FileDesc { + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} + +impl IntoRawFd for FileDesc { + fn into_raw_fd(self) -> RawFd { + self.0.into_raw_fd() } } -impl Drop for FileDesc { - fn drop(&mut self) { - // Note that errors are ignored when closing a file descriptor. The - // reason for this is that if an error occurs we don't actually know if - // the file descriptor was closed or not, and if we retried (for - // something like EINTR), we might close another valid file descriptor - // opened after we closed ours. - let _ = unsafe { libc::close(self.fd) }; +impl FromRawFd for FileDesc { + unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { + Self(FromRawFd::from_raw_fd(raw_fd)) } } diff --git a/library/std/src/sys/unix/fd/tests.rs b/library/std/src/sys/unix/fd/tests.rs index c9520485c3c7c..5d17e46786c79 100644 --- a/library/std/src/sys/unix/fd/tests.rs +++ b/library/std/src/sys/unix/fd/tests.rs @@ -1,9 +1,10 @@ use super::{FileDesc, IoSlice}; +use crate::os::unix::io::FromRawFd; use core::mem::ManuallyDrop; #[test] fn limit_vector_count() { - let stdout = ManuallyDrop::new(unsafe { FileDesc { fd: 1 } }); + let stdout = ManuallyDrop::new(unsafe { FileDesc::from_raw_fd(1) }); let bufs = (0..1500).map(|_| IoSlice::new(&[])).collect::>(); assert!(stdout.write_vectored(&bufs).is_ok()); } diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs index fd4defd72eb47..6075eb5c7c58a 100644 --- a/library/std/src/sys/unix/fs.rs +++ b/library/std/src/sys/unix/fs.rs @@ -4,13 +4,14 @@ use crate::ffi::{CStr, CString, OsStr, OsString}; use crate::fmt; use crate::io::{self, Error, IoSlice, IoSliceMut, SeekFrom}; use crate::mem; +use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd}; use crate::path::{Path, PathBuf}; use crate::ptr; use crate::sync::Arc; use crate::sys::fd::FileDesc; use crate::sys::time::SystemTime; use crate::sys::{cvt, cvt_r}; -use crate::sys_common::{AsInner, FromInner}; +use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; #[cfg(any( all(target_os = "linux", target_env = "gnu"), @@ -764,11 +765,11 @@ impl File { // However, since this is a variadic function, C integer promotion rules mean that on // the ABI level, this still gets passed as `c_int` (aka `u32` on Unix platforms). let fd = cvt_r(|| unsafe { open64(path.as_ptr(), flags, opts.mode as c_int) })?; - Ok(File(FileDesc::new(fd))) + Ok(File(unsafe { FileDesc::from_raw_fd(fd) })) } pub fn file_attr(&self) -> io::Result { - let fd = self.0.raw(); + let fd = self.as_raw_fd(); cfg_has_statx! { if let Some(ret) = unsafe { try_statx( @@ -787,7 +788,7 @@ impl File { } pub fn fsync(&self) -> io::Result<()> { - cvt_r(|| unsafe { os_fsync(self.0.raw()) })?; + cvt_r(|| unsafe { os_fsync(self.as_raw_fd()) })?; return Ok(()); #[cfg(any(target_os = "macos", target_os = "ios"))] @@ -801,7 +802,7 @@ impl File { } pub fn datasync(&self) -> io::Result<()> { - cvt_r(|| unsafe { os_datasync(self.0.raw()) })?; + cvt_r(|| unsafe { os_datasync(self.as_raw_fd()) })?; return Ok(()); #[cfg(any(target_os = "macos", target_os = "ios"))] @@ -834,14 +835,14 @@ impl File { pub fn truncate(&self, size: u64) -> io::Result<()> { #[cfg(target_os = "android")] - return crate::sys::android::ftruncate64(self.0.raw(), size); + return crate::sys::android::ftruncate64(self.as_raw_fd(), size); #[cfg(not(target_os = "android"))] { use crate::convert::TryInto; let size: off64_t = size.try_into().map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?; - cvt_r(|| unsafe { ftruncate64(self.0.raw(), size) }).map(drop) + cvt_r(|| unsafe { ftruncate64(self.as_raw_fd(), size) }).map(drop) } } @@ -891,7 +892,7 @@ impl File { SeekFrom::End(off) => (libc::SEEK_END, off), SeekFrom::Current(off) => (libc::SEEK_CUR, off), }; - let n = cvt(unsafe { lseek64(self.0.raw(), pos, whence) })?; + let n = cvt(unsafe { lseek64(self.as_raw_fd(), pos, whence) })?; Ok(n as u64) } @@ -899,16 +900,8 @@ impl File { self.0.duplicate().map(File) } - pub fn fd(&self) -> &FileDesc { - &self.0 - } - - pub fn into_fd(self) -> FileDesc { - self.0 - } - pub fn set_permissions(&self, perm: FilePermissions) -> io::Result<()> { - cvt_r(|| unsafe { libc::fchmod(self.0.raw(), perm.mode) })?; + cvt_r(|| unsafe { libc::fchmod(self.as_raw_fd(), perm.mode) })?; Ok(()) } } @@ -933,9 +926,51 @@ fn cstr(path: &Path) -> io::Result { Ok(CString::new(path.as_os_str().as_bytes())?) } -impl FromInner for File { - fn from_inner(fd: c_int) -> File { - File(FileDesc::new(fd)) +impl AsInner for File { + fn as_inner(&self) -> &FileDesc { + &self.0 + } +} + +impl AsInnerMut for File { + fn as_inner_mut(&mut self) -> &mut FileDesc { + &mut self.0 + } +} + +impl IntoInner for File { + fn into_inner(self) -> FileDesc { + self.0 + } +} + +impl FromInner for File { + fn from_inner(file_desc: FileDesc) -> Self { + Self(file_desc) + } +} + +impl AsFd for File { + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } +} + +impl AsRawFd for File { + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} + +impl IntoRawFd for File { + fn into_raw_fd(self) -> RawFd { + self.0.into_raw_fd() + } +} + +impl FromRawFd for File { + unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { + Self(FromRawFd::from_raw_fd(raw_fd)) } } @@ -1009,7 +1044,7 @@ impl fmt::Debug for File { None } - let fd = self.0.raw(); + let fd = self.as_raw_fd(); let mut b = f.debug_struct("File"); b.field("fd", &fd); if let Some(path) = get_path(fd) { diff --git a/library/std/src/sys/unix/net.rs b/library/std/src/sys/unix/net.rs index 3f614fde08aca..c2f5da1dbbb11 100644 --- a/library/std/src/sys/unix/net.rs +++ b/library/std/src/sys/unix/net.rs @@ -3,6 +3,7 @@ use crate::ffi::CStr; use crate::io::{self, IoSlice, IoSliceMut}; use crate::mem; use crate::net::{Shutdown, SocketAddr}; +use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; use crate::str; use crate::sys::fd::FileDesc; use crate::sys_common::net::{getsockopt, setsockopt, sockaddr_to_addr}; @@ -74,10 +75,10 @@ impl Socket { // flag to atomically create the socket and set it as // CLOEXEC. On Linux this was added in 2.6.27. let fd = cvt(libc::socket(fam, ty | libc::SOCK_CLOEXEC, 0))?; - Ok(Socket(FileDesc::new(fd))) + Ok(Socket(FileDesc::from_raw_fd(fd))) } else { let fd = cvt(libc::socket(fam, ty, 0))?; - let fd = FileDesc::new(fd); + let fd = FileDesc::from_raw_fd(fd); fd.set_cloexec()?; let socket = Socket(fd); @@ -109,11 +110,11 @@ impl Socket { ))] { // Like above, set cloexec atomically cvt(libc::socketpair(fam, ty | libc::SOCK_CLOEXEC, 0, fds.as_mut_ptr()))?; - Ok((Socket(FileDesc::new(fds[0])), Socket(FileDesc::new(fds[1])))) + Ok((Socket(FileDesc::from_raw_fd(fds[0])), Socket(FileDesc::from_raw_fd(fds[1])))) } else { cvt(libc::socketpair(fam, ty, 0, fds.as_mut_ptr()))?; - let a = FileDesc::new(fds[0]); - let b = FileDesc::new(fds[1]); + let a = FileDesc::from_raw_fd(fds[0]); + let b = FileDesc::from_raw_fd(fds[1]); a.set_cloexec()?; b.set_cloexec()?; Ok((Socket(a), Socket(b))) @@ -131,7 +132,7 @@ impl Socket { self.set_nonblocking(true)?; let r = unsafe { let (addrp, len) = addr.into_inner(); - cvt(libc::connect(self.0.raw(), addrp, len)) + cvt(libc::connect(self.as_raw_fd(), addrp, len)) }; self.set_nonblocking(false)?; @@ -142,7 +143,7 @@ impl Socket { Err(e) => return Err(e), } - let mut pollfd = libc::pollfd { fd: self.0.raw(), events: libc::POLLOUT, revents: 0 }; + let mut pollfd = libc::pollfd { fd: self.as_raw_fd(), events: libc::POLLOUT, revents: 0 }; if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 { return Err(io::Error::new_const( @@ -212,15 +213,17 @@ impl Socket { target_os = "netbsd", target_os = "openbsd", ))] { - let fd = cvt_r(|| unsafe { - libc::accept4(self.0.raw(), storage, len, libc::SOCK_CLOEXEC) - })?; - Ok(Socket(FileDesc::new(fd))) + unsafe { + let fd = cvt_r(|| libc::accept4(self.as_raw_fd(), storage, len, libc::SOCK_CLOEXEC))?; + Ok(Socket(FileDesc::from_raw_fd(fd))) + } } else { - let fd = cvt_r(|| unsafe { libc::accept(self.0.raw(), storage, len) })?; - let fd = FileDesc::new(fd); - fd.set_cloexec()?; - Ok(Socket(fd)) + unsafe { + let fd = cvt_r(|| libc::accept(self.as_raw_fd(), storage, len))?; + let fd = FileDesc::from_raw_fd(fd); + fd.set_cloexec()?; + Ok(Socket(fd)) + } } } } @@ -231,7 +234,7 @@ impl Socket { fn recv_with_flags(&self, buf: &mut [u8], flags: c_int) -> io::Result { let ret = cvt(unsafe { - libc::recv(self.0.raw(), buf.as_mut_ptr() as *mut c_void, buf.len(), flags) + libc::recv(self.as_raw_fd(), buf.as_mut_ptr() as *mut c_void, buf.len(), flags) })?; Ok(ret as usize) } @@ -263,7 +266,7 @@ impl Socket { let n = cvt(unsafe { libc::recvfrom( - self.0.raw(), + self.as_raw_fd(), buf.as_mut_ptr() as *mut c_void, buf.len(), flags, @@ -288,7 +291,7 @@ impl Socket { target_os = "openbsd", ))] pub fn recv_msg(&self, msg: &mut libc::msghdr) -> io::Result { - let n = cvt(unsafe { libc::recvmsg(self.0.raw(), msg, libc::MSG_CMSG_CLOEXEC) })?; + let n = cvt(unsafe { libc::recvmsg(self.as_raw_fd(), msg, libc::MSG_CMSG_CLOEXEC) })?; Ok(n as usize) } @@ -319,7 +322,7 @@ impl Socket { target_os = "openbsd", ))] pub fn send_msg(&self, msg: &mut libc::msghdr) -> io::Result { - let n = cvt(unsafe { libc::sendmsg(self.0.raw(), msg, 0) })?; + let n = cvt(unsafe { libc::sendmsg(self.as_raw_fd(), msg, 0) })?; Ok(n as usize) } @@ -369,7 +372,7 @@ impl Socket { Shutdown::Read => libc::SHUT_RD, Shutdown::Both => libc::SHUT_RDWR, }; - cvt(unsafe { libc::shutdown(self.0.raw(), how) })?; + cvt(unsafe { libc::shutdown(self.as_raw_fd(), how) })?; Ok(()) } @@ -396,7 +399,7 @@ impl Socket { #[cfg(not(any(target_os = "solaris", target_os = "illumos")))] pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { let mut nonblocking = nonblocking as libc::c_int; - cvt(unsafe { libc::ioctl(*self.as_inner(), libc::FIONBIO, &mut nonblocking) }).map(drop) + cvt(unsafe { libc::ioctl(self.as_raw_fd(), libc::FIONBIO, &mut nonblocking) }).map(drop) } #[cfg(any(target_os = "solaris", target_os = "illumos"))] @@ -410,23 +413,52 @@ impl Socket { let raw: c_int = getsockopt(self, libc::SOL_SOCKET, libc::SO_ERROR)?; if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) } } + + // This is used by sys_common code to abstract over Windows and Unix. + pub fn as_raw(&self) -> RawFd { + self.as_raw_fd() + } +} + +impl AsInner for Socket { + fn as_inner(&self) -> &FileDesc { + &self.0 + } +} + +impl IntoInner for Socket { + fn into_inner(self) -> FileDesc { + self.0 + } +} + +impl FromInner for Socket { + fn from_inner(file_desc: FileDesc) -> Self { + Self(file_desc) + } +} + +impl AsFd for Socket { + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } } -impl AsInner for Socket { - fn as_inner(&self) -> &c_int { - self.0.as_inner() +impl AsRawFd for Socket { + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() } } -impl FromInner for Socket { - fn from_inner(fd: c_int) -> Socket { - Socket(FileDesc::new(fd)) +impl IntoRawFd for Socket { + fn into_raw_fd(self) -> RawFd { + self.0.into_raw_fd() } } -impl IntoInner for Socket { - fn into_inner(self) -> c_int { - self.0.into_raw() +impl FromRawFd for Socket { + unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { + Self(FromRawFd::from_raw_fd(raw_fd)) } } diff --git a/library/std/src/sys/unix/pipe.rs b/library/std/src/sys/unix/pipe.rs index 7ae37bdda70bd..a56c275c94207 100644 --- a/library/std/src/sys/unix/pipe.rs +++ b/library/std/src/sys/unix/pipe.rs @@ -1,7 +1,9 @@ use crate::io::{self, IoSlice, IoSliceMut}; use crate::mem; +use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; use crate::sys::fd::FileDesc; use crate::sys::{cvt, cvt_r}; +use crate::sys_common::IntoInner; //////////////////////////////////////////////////////////////////////////////// // Anonymous pipes @@ -24,16 +26,20 @@ pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> { target_os = "openbsd", target_os = "redox" ))] { - cvt(unsafe { libc::pipe2(fds.as_mut_ptr(), libc::O_CLOEXEC) })?; - Ok((AnonPipe(FileDesc::new(fds[0])), AnonPipe(FileDesc::new(fds[1])))) + unsafe { + cvt(libc::pipe2(fds.as_mut_ptr(), libc::O_CLOEXEC))?; + Ok((AnonPipe(FileDesc::from_raw_fd(fds[0])), AnonPipe(FileDesc::from_raw_fd(fds[1])))) + } } else { - cvt(unsafe { libc::pipe(fds.as_mut_ptr()) })?; - - let fd0 = FileDesc::new(fds[0]); - let fd1 = FileDesc::new(fds[1]); - fd0.set_cloexec()?; - fd1.set_cloexec()?; - Ok((AnonPipe(fd0), AnonPipe(fd1))) + unsafe { + cvt(libc::pipe(fds.as_mut_ptr()))?; + + let fd0 = FileDesc::from_raw_fd(fds[0]); + let fd1 = FileDesc::from_raw_fd(fds[1]); + fd0.set_cloexec()?; + fd1.set_cloexec()?; + Ok((AnonPipe(fd0), AnonPipe(fd1))) + } } } } @@ -64,11 +70,10 @@ impl AnonPipe { pub fn is_write_vectored(&self) -> bool { self.0.is_write_vectored() } +} - pub fn fd(&self) -> &FileDesc { - &self.0 - } - pub fn into_fd(self) -> FileDesc { +impl IntoInner for AnonPipe { + fn into_inner(self) -> FileDesc { self.0 } } @@ -76,15 +81,15 @@ impl AnonPipe { pub fn read2(p1: AnonPipe, v1: &mut Vec, p2: AnonPipe, v2: &mut Vec) -> io::Result<()> { // Set both pipes into nonblocking mode as we're gonna be reading from both // in the `select` loop below, and we wouldn't want one to block the other! - let p1 = p1.into_fd(); - let p2 = p2.into_fd(); + let p1 = p1.into_inner(); + let p2 = p2.into_inner(); p1.set_nonblocking(true)?; p2.set_nonblocking(true)?; let mut fds: [libc::pollfd; 2] = unsafe { mem::zeroed() }; - fds[0].fd = p1.raw(); + fds[0].fd = p1.as_raw_fd(); fds[0].events = libc::POLLIN; - fds[1].fd = p2.raw(); + fds[1].fd = p2.as_raw_fd(); fds[1].events = libc::POLLIN; loop { // wait for either pipe to become readable using `poll` @@ -120,3 +125,27 @@ pub fn read2(p1: AnonPipe, v1: &mut Vec, p2: AnonPipe, v2: &mut Vec) -> } } } + +impl AsRawFd for AnonPipe { + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} + +impl AsFd for AnonPipe { + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } +} + +impl IntoRawFd for AnonPipe { + fn into_raw_fd(self) -> RawFd { + self.0.into_raw_fd() + } +} + +impl FromRawFd for AnonPipe { + unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { + Self(FromRawFd::from_raw_fd(raw_fd)) + } +} diff --git a/library/std/src/sys/unix/process/process_common.rs b/library/std/src/sys/unix/process/process_common.rs index a1972380a9fdb..7b261a302c33f 100644 --- a/library/std/src/sys/unix/process/process_common.rs +++ b/library/std/src/sys/unix/process/process_common.rs @@ -13,6 +13,7 @@ use crate::sys::fd::FileDesc; use crate::sys::fs::File; use crate::sys::pipe::{self, AnonPipe}; use crate::sys_common::process::{CommandEnv, CommandEnvs}; +use crate::sys_common::IntoInner; #[cfg(not(target_os = "fuchsia"))] use crate::sys::fs::OpenOptions; @@ -388,17 +389,17 @@ impl Stdio { // stderr. No matter which we dup first, the second will get // overwritten prematurely. Stdio::Fd(ref fd) => { - if fd.raw() >= 0 && fd.raw() <= libc::STDERR_FILENO { + if fd.as_raw_fd() >= 0 && fd.as_raw_fd() <= libc::STDERR_FILENO { Ok((ChildStdio::Owned(fd.duplicate()?), None)) } else { - Ok((ChildStdio::Explicit(fd.raw()), None)) + Ok((ChildStdio::Explicit(fd.as_raw_fd()), None)) } } Stdio::MakePipe => { let (reader, writer) = pipe::anon_pipe()?; let (ours, theirs) = if readable { (writer, reader) } else { (reader, writer) }; - Ok((ChildStdio::Owned(theirs.into_fd()), Some(ours))) + Ok((ChildStdio::Owned(theirs.into_inner()), Some(ours))) } #[cfg(not(target_os = "fuchsia"))] @@ -408,7 +409,7 @@ impl Stdio { opts.write(!readable); let path = unsafe { CStr::from_ptr(DEV_NULL.as_ptr() as *const _) }; let fd = File::open_c(&path, &opts)?; - Ok((ChildStdio::Owned(fd.into_fd()), None)) + Ok((ChildStdio::Owned(fd.into_inner()), None)) } #[cfg(target_os = "fuchsia")] @@ -419,13 +420,13 @@ impl Stdio { impl From for Stdio { fn from(pipe: AnonPipe) -> Stdio { - Stdio::Fd(pipe.into_fd()) + Stdio::Fd(pipe.into_inner()) } } impl From for Stdio { fn from(file: File) -> Stdio { - Stdio::Fd(file.into_fd()) + Stdio::Fd(file.into_inner()) } } @@ -434,7 +435,7 @@ impl ChildStdio { match *self { ChildStdio::Inherit => None, ChildStdio::Explicit(fd) => Some(fd), - ChildStdio::Owned(ref fd) => Some(fd.raw()), + ChildStdio::Owned(ref fd) => Some(fd.as_raw_fd()), #[cfg(target_os = "fuchsia")] ChildStdio::Null => None, diff --git a/library/std/src/sys/unix/process/process_unix.rs b/library/std/src/sys/unix/process/process_unix.rs index 4b210d6af1303..12edf04a4e2e9 100644 --- a/library/std/src/sys/unix/process/process_unix.rs +++ b/library/std/src/sys/unix/process/process_unix.rs @@ -97,7 +97,9 @@ impl Command { drop(env_lock); drop(output); - let mut p = Process::new(pid, pidfd); + // Safety: We obtained the pidfd from calling `clone3` with + // `CLONE_PIDFD` so it's valid an otherwise unowned. + let mut p = unsafe { Process::new(pid, pidfd) }; let mut bytes = [0; 8]; // loop to handle EINTR @@ -446,7 +448,8 @@ impl Command { None => None, }; - let mut p = Process::new(0, -1); + // Safety: -1 indicates we don't have a pidfd. + let mut p = unsafe { Process::new(0, -1) }; struct PosixSpawnFileActions<'a>(&'a mut MaybeUninit); @@ -545,14 +548,17 @@ pub struct Process { impl Process { #[cfg(target_os = "linux")] - fn new(pid: pid_t, pidfd: pid_t) -> Self { + unsafe fn new(pid: pid_t, pidfd: pid_t) -> Self { + use crate::os::unix::io::FromRawFd; use crate::sys_common::FromInner; - let pidfd = (pidfd >= 0).then(|| PidFd::from_inner(sys::fd::FileDesc::new(pidfd))); + // Safety: If `pidfd` is nonnegative, we assume it's valid and otherwise unowned. + let pidfd = (pidfd >= 0) + .then(|| PidFd::from_inner(unsafe { sys::fd::FileDesc::from_raw_fd(pidfd) })); Process { pid, status: None, pidfd } } #[cfg(not(target_os = "linux"))] - fn new(pid: pid_t, _pidfd: pid_t) -> Self { + unsafe fn new(pid: pid_t, _pidfd: pid_t) -> Self { Process { pid, status: None } } diff --git a/library/std/src/sys/unix/stdio.rs b/library/std/src/sys/unix/stdio.rs index a05fe8165cff2..b359987595d30 100644 --- a/library/std/src/sys/unix/stdio.rs +++ b/library/std/src/sys/unix/stdio.rs @@ -1,5 +1,6 @@ use crate::io::{self, IoSlice, IoSliceMut}; use crate::mem::ManuallyDrop; +use crate::os::unix::io::{AsFd, BorrowedFd, FromRawFd}; use crate::sys::fd::FileDesc; pub struct Stdin(()); @@ -14,11 +15,11 @@ impl Stdin { impl io::Read for Stdin { fn read(&mut self, buf: &mut [u8]) -> io::Result { - ManuallyDrop::new(FileDesc::new(libc::STDIN_FILENO)).read(buf) + unsafe { ManuallyDrop::new(FileDesc::from_raw_fd(libc::STDIN_FILENO)).read(buf) } } fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - ManuallyDrop::new(FileDesc::new(libc::STDIN_FILENO)).read_vectored(bufs) + unsafe { ManuallyDrop::new(FileDesc::from_raw_fd(libc::STDIN_FILENO)).read_vectored(bufs) } } #[inline] @@ -35,11 +36,13 @@ impl Stdout { impl io::Write for Stdout { fn write(&mut self, buf: &[u8]) -> io::Result { - ManuallyDrop::new(FileDesc::new(libc::STDOUT_FILENO)).write(buf) + unsafe { ManuallyDrop::new(FileDesc::from_raw_fd(libc::STDOUT_FILENO)).write(buf) } } fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - ManuallyDrop::new(FileDesc::new(libc::STDOUT_FILENO)).write_vectored(bufs) + unsafe { + ManuallyDrop::new(FileDesc::from_raw_fd(libc::STDOUT_FILENO)).write_vectored(bufs) + } } #[inline] @@ -60,11 +63,13 @@ impl Stderr { impl io::Write for Stderr { fn write(&mut self, buf: &[u8]) -> io::Result { - ManuallyDrop::new(FileDesc::new(libc::STDERR_FILENO)).write(buf) + unsafe { ManuallyDrop::new(FileDesc::from_raw_fd(libc::STDERR_FILENO)).write(buf) } } fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - ManuallyDrop::new(FileDesc::new(libc::STDERR_FILENO)).write_vectored(bufs) + unsafe { + ManuallyDrop::new(FileDesc::from_raw_fd(libc::STDERR_FILENO)).write_vectored(bufs) + } } #[inline] @@ -86,3 +91,51 @@ pub const STDIN_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE; pub fn panic_output() -> Option { Some(Stderr::new()) } + +#[unstable(feature = "io_safety", issue = "87074")] +impl AsFd for io::Stdin { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + unsafe { BorrowedFd::borrow_raw_fd(libc::STDIN_FILENO) } + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl<'a> AsFd for io::StdinLock<'a> { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + unsafe { BorrowedFd::borrow_raw_fd(libc::STDIN_FILENO) } + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl AsFd for io::Stdout { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + unsafe { BorrowedFd::borrow_raw_fd(libc::STDOUT_FILENO) } + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl<'a> AsFd for io::StdoutLock<'a> { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + unsafe { BorrowedFd::borrow_raw_fd(libc::STDOUT_FILENO) } + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl AsFd for io::Stderr { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + unsafe { BorrowedFd::borrow_raw_fd(libc::STDERR_FILENO) } + } +} + +#[unstable(feature = "io_safety", issue = "87074")] +impl<'a> AsFd for io::StderrLock<'a> { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + unsafe { BorrowedFd::borrow_raw_fd(libc::STDERR_FILENO) } + } +} diff --git a/library/std/src/sys/wasi/fd.rs b/library/std/src/sys/wasi/fd.rs index 1f6ea8d6e8df3..e4f4456611cd0 100644 --- a/library/std/src/sys/wasi/fd.rs +++ b/library/std/src/sys/wasi/fd.rs @@ -5,11 +5,12 @@ use super::err2io; use crate::io::{self, IoSlice, IoSliceMut, SeekFrom}; use crate::mem; use crate::net::Shutdown; -use crate::os::raw::c_int; +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: c_int, + fd: OwnedFd, } fn iovec<'a>(a: &'a mut [IoSliceMut<'_>]) -> &'a [wasi::Iovec] { @@ -27,38 +28,26 @@ fn ciovec<'a>(a: &'a [IoSlice<'_>]) -> &'a [wasi::Ciovec] { } impl WasiFd { - pub unsafe fn from_raw(fd: c_int) -> WasiFd { - WasiFd { fd } - } - - pub fn into_raw(self) -> c_int { - let ret = self.fd; - mem::forget(self); - ret - } - - pub fn as_raw(&self) -> c_int { - self.fd - } - pub fn datasync(&self) -> io::Result<()> { - unsafe { wasi::fd_datasync(self.fd as wasi::Fd).map_err(err2io) } + 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 { - unsafe { wasi::fd_pread(self.fd as wasi::Fd, iovec(bufs), offset).map_err(err2io) } + 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 { - unsafe { wasi::fd_pwrite(self.fd as wasi::Fd, ciovec(bufs), offset).map_err(err2io) } + 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 { - unsafe { wasi::fd_read(self.fd as wasi::Fd, iovec(bufs)).map_err(err2io) } + unsafe { wasi::fd_read(self.as_raw_fd() as wasi::Fd, iovec(bufs)).map_err(err2io) } } pub fn write(&self, bufs: &[IoSlice<'_>]) -> io::Result { - unsafe { wasi::fd_write(self.fd as wasi::Fd, ciovec(bufs)).map_err(err2io) } + unsafe { wasi::fd_write(self.as_raw_fd() as wasi::Fd, ciovec(bufs)).map_err(err2io) } } pub fn seek(&self, pos: SeekFrom) -> io::Result { @@ -67,37 +56,42 @@ impl WasiFd { SeekFrom::End(pos) => (wasi::WHENCE_END, pos), SeekFrom::Current(pos) => (wasi::WHENCE_CUR, pos), }; - unsafe { wasi::fd_seek(self.fd as wasi::Fd, offset, whence).map_err(err2io) } + unsafe { wasi::fd_seek(self.as_raw_fd() as wasi::Fd, offset, whence).map_err(err2io) } } pub fn tell(&self) -> io::Result { - unsafe { wasi::fd_tell(self.fd as wasi::Fd).map_err(err2io) } + 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.fd as wasi::Fd, flags).map_err(err2io) } + 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.fd as wasi::Fd, base, inheriting).map_err(err2io) } + 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.fd as wasi::Fd).map_err(err2io) } + 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.fd as wasi::Fd, offset, len, advice).map_err(err2io) } + 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.fd as wasi::Fd, offset, len).map_err(err2io) } + 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.fd as wasi::Fd, path).map_err(err2io) } + unsafe { wasi::path_create_directory(self.as_raw_fd() as wasi::Fd, path).map_err(err2io) } } pub fn link( @@ -109,10 +103,10 @@ impl WasiFd { ) -> io::Result<()> { unsafe { wasi::path_link( - self.fd as wasi::Fd, + self.as_raw_fd() as wasi::Fd, old_flags, old_path, - new_fd.fd as wasi::Fd, + new_fd.as_raw_fd() as wasi::Fd, new_path, ) .map_err(err2io) @@ -130,7 +124,7 @@ impl WasiFd { ) -> io::Result { unsafe { wasi::path_open( - self.fd as wasi::Fd, + self.as_raw_fd() as wasi::Fd, dirflags, path, oflags, @@ -138,34 +132,39 @@ impl WasiFd { fs_rights_inheriting, fs_flags, ) - .map(|fd| WasiFd::from_raw(fd as c_int)) + .map(|fd| WasiFd::from_raw_fd(fd as RawFd)) .map_err(err2io) } } pub fn readdir(&self, buf: &mut [u8], cookie: wasi::Dircookie) -> io::Result { unsafe { - wasi::fd_readdir(self.fd as wasi::Fd, buf.as_mut_ptr(), buf.len(), cookie) + 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 { unsafe { - wasi::path_readlink(self.fd as wasi::Fd, path, buf.as_mut_ptr(), buf.len()) + 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.fd as wasi::Fd, old_path, new_fd.fd as wasi::Fd, new_path) - .map_err(err2io) + 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 { - unsafe { wasi::fd_filestat_get(self.fd as wasi::Fd).map_err(err2io) } + unsafe { wasi::fd_filestat_get(self.as_raw_fd() as wasi::Fd).map_err(err2io) } } pub fn filestat_set_times( @@ -175,12 +174,13 @@ impl WasiFd { fstflags: wasi::Fstflags, ) -> io::Result<()> { unsafe { - wasi::fd_filestat_set_times(self.fd as wasi::Fd, atim, mtim, fstflags).map_err(err2io) + 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.fd as wasi::Fd, size).map_err(err2io) } + unsafe { wasi::fd_filestat_set_size(self.as_raw_fd() as wasi::Fd, size).map_err(err2io) } } pub fn path_filestat_get( @@ -188,7 +188,9 @@ impl WasiFd { flags: wasi::Lookupflags, path: &str, ) -> io::Result { - unsafe { wasi::path_filestat_get(self.fd as wasi::Fd, flags, path).map_err(err2io) } + unsafe { + wasi::path_filestat_get(self.as_raw_fd() as wasi::Fd, flags, path).map_err(err2io) + } } pub fn path_filestat_set_times( @@ -200,21 +202,30 @@ impl WasiFd { fstflags: wasi::Fstflags, ) -> io::Result<()> { unsafe { - wasi::path_filestat_set_times(self.fd as wasi::Fd, flags, path, atim, mtim, fstflags) - .map_err(err2io) + 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.fd as wasi::Fd, new_path).map_err(err2io) } + 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.fd as wasi::Fd, path).map_err(err2io) } + 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.fd as wasi::Fd, path).map_err(err2io) } + unsafe { wasi::path_remove_directory(self.as_raw_fd() as wasi::Fd, path).map_err(err2io) } } pub fn sock_recv( @@ -222,11 +233,15 @@ impl WasiFd { ri_data: &mut [IoSliceMut<'_>], ri_flags: wasi::Riflags, ) -> io::Result<(usize, wasi::Roflags)> { - unsafe { wasi::sock_recv(self.fd as wasi::Fd, iovec(ri_data), ri_flags).map_err(err2io) } + 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 { - unsafe { wasi::sock_send(self.fd as wasi::Fd, ciovec(si_data), si_flags).map_err(err2io) } + 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<()> { @@ -235,14 +250,54 @@ impl WasiFd { Shutdown::Write => wasi::SDFLAGS_WR, Shutdown::Both => wasi::SDFLAGS_WR | wasi::SDFLAGS_RD, }; - unsafe { wasi::sock_shutdown(self.fd as wasi::Fd, how).map_err(err2io) } + unsafe { wasi::sock_shutdown(self.as_raw_fd() as wasi::Fd, how).map_err(err2io) } + } +} + +impl AsInner for WasiFd { + fn as_inner(&self) -> &OwnedFd { + &self.fd + } +} + +impl AsInnerMut for WasiFd { + fn as_inner_mut(&mut self) -> &mut OwnedFd { + &mut self.fd + } +} + +impl IntoInner for WasiFd { + fn into_inner(self) -> OwnedFd { + self.fd + } +} + +impl FromInner 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 Drop for WasiFd { - fn drop(&mut self) { - // FIXME: can we handle the return code here even though we can't on - // unix? - let _ = unsafe { wasi::fd_close(self.fd as wasi::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 index 55c9c652a8bbb..984dda8dc0b4e 100644 --- a/library/std/src/sys/wasi/fs.rs +++ b/library/std/src/sys/wasi/fs.rs @@ -8,12 +8,13 @@ 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::FromInner; +use crate::sys_common::{AsInner, FromInner, IntoInner}; pub use crate::sys_common::fs::{remove_dir_all, try_exists}; @@ -442,22 +443,50 @@ impl File { unsupported() } - pub fn fd(&self) -> &WasiFd { + pub fn read_link(&self, file: &Path) -> io::Result { + read_link(&self.fd, file) + } +} + +impl AsInner for File { + fn as_inner(&self) -> &WasiFd { &self.fd } +} - pub fn into_fd(self) -> WasiFd { +impl IntoInner for File { + fn into_inner(self) -> WasiFd { self.fd } +} - pub fn read_link(&self, file: &Path) -> io::Result { - read_link(&self.fd, file) +impl FromInner 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 FromInner for File { - fn from_inner(fd: c_int) -> File { - unsafe { File { fd: WasiFd::from_raw(fd) } } +impl FromRawFd for File { + unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { + unsafe { Self { fd: FromRawFd::from_raw_fd(raw_fd) } } } } @@ -474,7 +503,7 @@ impl DirBuilder { impl fmt::Debug for File { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("File").field("fd", &self.fd.as_raw()).finish() + f.debug_struct("File").field("fd", &self.as_raw_fd()).finish() } } @@ -654,7 +683,7 @@ fn open_parent(p: &Path) -> io::Result<(ManuallyDrop, PathBuf)> { let relative = CStr::from_ptr(relative_path).to_bytes().to_vec(); return Ok(( - ManuallyDrop::new(WasiFd::from_raw(fd as c_int)), + ManuallyDrop::new(WasiFd::from_raw_fd(fd as c_int)), PathBuf::from(OsString::from_vec(relative)), )); } diff --git a/library/std/src/sys/wasi/net.rs b/library/std/src/sys/wasi/net.rs index 50b7352933e57..c7c4a9f6efdfb 100644 --- a/library/std/src/sys/wasi/net.rs +++ b/library/std/src/sys/wasi/net.rs @@ -5,13 +5,57 @@ use crate::convert::TryFrom; use crate::fmt; use crate::io::{self, IoSlice, IoSliceMut}; use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; -use crate::os::raw::c_int; +use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; use crate::sys::unsupported; -use crate::sys_common::FromInner; +use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::time::Duration; +pub struct Socket(WasiFd); + pub struct TcpStream { - fd: WasiFd, + inner: Socket, +} + +impl AsInner for Socket { + fn as_inner(&self) -> &WasiFd { + &self.0 + } +} + +impl IntoInner for Socket { + fn into_inner(self) -> WasiFd { + self.0 + } +} + +impl FromInner 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 { @@ -107,29 +151,29 @@ impl TcpStream { unsupported() } - pub fn fd(&self) -> &WasiFd { - &self.fd + pub fn socket(&self) -> &Socket { + &self.inner } - pub fn into_fd(self) -> WasiFd { - self.fd + pub fn into_socket(self) -> Socket { + self.inner } } -impl FromInner for TcpStream { - fn from_inner(fd: c_int) -> TcpStream { - unsafe { TcpStream { fd: WasiFd::from_raw(fd) } } +impl FromInner 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.fd.as_raw()).finish() + f.debug_struct("TcpStream").field("fd", &self.inner.as_raw_fd()).finish() } } pub struct TcpListener { - fd: WasiFd, + inner: Socket, } impl TcpListener { @@ -173,29 +217,41 @@ impl TcpListener { unsupported() } - pub fn fd(&self) -> &WasiFd { - &self.fd + pub fn socket(&self) -> &Socket { + &self.inner } - pub fn into_fd(self) -> WasiFd { - self.fd + pub fn into_socket(self) -> Socket { + self.inner } } -impl FromInner for TcpListener { - fn from_inner(fd: c_int) -> TcpListener { - unsafe { TcpListener { fd: WasiFd::from_raw(fd) } } +impl AsInner for TcpListener { + fn as_inner(&self) -> &Socket { + &self.inner + } +} + +impl IntoInner for TcpListener { + fn into_inner(self) -> Socket { + self.inner + } +} + +impl FromInner 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.fd.as_raw()).finish() + f.debug_struct("TcpListener").field("fd", &self.inner.as_raw_fd()).finish() } } pub struct UdpSocket { - fd: WasiFd, + inner: Socket, } impl UdpSocket { @@ -323,24 +379,36 @@ impl UdpSocket { unsupported() } - pub fn fd(&self) -> &WasiFd { - &self.fd + pub fn socket(&self) -> &Socket { + &self.inner + } + + pub fn into_socket(self) -> Socket { + self.inner } +} + +impl AsInner for UdpSocket { + fn as_inner(&self) -> &Socket { + &self.inner + } +} - pub fn into_fd(self) -> WasiFd { - self.fd +impl IntoInner for UdpSocket { + fn into_inner(self) -> Socket { + self.inner } } -impl FromInner for UdpSocket { - fn from_inner(fd: c_int) -> UdpSocket { - unsafe { UdpSocket { fd: WasiFd::from_raw(fd) } } +impl FromInner 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.fd.as_raw()).finish() + f.debug_struct("UdpSocket").field("fd", &self.inner.as_raw_fd()).finish() } } diff --git a/library/std/src/sys/wasi/stdio.rs b/library/std/src/sys/wasi/stdio.rs index 8782f333a1fa4..2c8f394cd47b0 100644 --- a/library/std/src/sys/wasi/stdio.rs +++ b/library/std/src/sys/wasi/stdio.rs @@ -4,6 +4,7 @@ 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; @@ -13,9 +14,11 @@ impl Stdin { pub const fn new() -> Stdin { Stdin } +} +impl AsRawFd for Stdin { #[inline] - pub fn as_raw_fd(&self) -> raw::c_int { + fn as_raw_fd(&self) -> raw::c_int { 0 } } @@ -26,7 +29,7 @@ impl io::Read for Stdin { } fn read_vectored(&mut self, data: &mut [IoSliceMut<'_>]) -> io::Result { - ManuallyDrop::new(unsafe { WasiFd::from_raw(self.as_raw_fd()) }).read(data) + ManuallyDrop::new(unsafe { WasiFd::from_raw_fd(self.as_raw_fd()) }).read(data) } #[inline] @@ -39,9 +42,11 @@ impl Stdout { pub const fn new() -> Stdout { Stdout } +} +impl AsRawFd for Stdout { #[inline] - pub fn as_raw_fd(&self) -> raw::c_int { + fn as_raw_fd(&self) -> raw::c_int { 1 } } @@ -52,7 +57,7 @@ impl io::Write for Stdout { } fn write_vectored(&mut self, data: &[IoSlice<'_>]) -> io::Result { - ManuallyDrop::new(unsafe { WasiFd::from_raw(self.as_raw_fd()) }).write(data) + ManuallyDrop::new(unsafe { WasiFd::from_raw_fd(self.as_raw_fd()) }).write(data) } #[inline] @@ -68,9 +73,11 @@ impl Stderr { pub const fn new() -> Stderr { Stderr } +} +impl AsRawFd for Stderr { #[inline] - pub fn as_raw_fd(&self) -> raw::c_int { + fn as_raw_fd(&self) -> raw::c_int { 2 } } @@ -81,7 +88,7 @@ impl io::Write for Stderr { } fn write_vectored(&mut self, data: &[IoSlice<'_>]) -> io::Result { - ManuallyDrop::new(unsafe { WasiFd::from_raw(self.as_raw_fd()) }).write(data) + ManuallyDrop::new(unsafe { WasiFd::from_raw_fd(self.as_raw_fd()) }).write(data) } #[inline] diff --git a/library/std/src/sys/windows/fs.rs b/library/std/src/sys/windows/fs.rs index c677adae6888e..0c1a50e231cd4 100644 --- a/library/std/src/sys/windows/fs.rs +++ b/library/std/src/sys/windows/fs.rs @@ -4,6 +4,7 @@ use crate::ffi::OsString; use crate::fmt; use crate::io::{self, Error, IoSlice, IoSliceMut, SeekFrom}; use crate::mem; +use crate::os::windows::io::{AsHandle, BorrowedHandle}; use crate::path::{Path, PathBuf}; use crate::ptr; use crate::slice; @@ -11,7 +12,7 @@ use crate::sync::Arc; use crate::sys::handle::Handle; use crate::sys::time::SystemTime; use crate::sys::{c, cvt}; -use crate::sys_common::FromInner; +use crate::sys_common::{AsInner, FromInner, IntoInner}; use super::to_u16s; @@ -295,12 +296,12 @@ impl File { if handle == c::INVALID_HANDLE_VALUE { Err(Error::last_os_error()) } else { - Ok(File { handle: Handle::new(handle) }) + unsafe { Ok(File { handle: Handle::from_raw_handle(handle) }) } } } pub fn fsync(&self) -> io::Result<()> { - cvt(unsafe { c::FlushFileBuffers(self.handle.raw()) })?; + cvt(unsafe { c::FlushFileBuffers(self.handle.as_raw_handle()) })?; Ok(()) } @@ -313,7 +314,7 @@ impl File { let size = mem::size_of_val(&info); cvt(unsafe { c::SetFileInformationByHandle( - self.handle.raw(), + self.handle.as_raw_handle(), c::FileEndOfFileInfo, &mut info as *mut _ as *mut _, size as c::DWORD, @@ -326,7 +327,7 @@ impl File { pub fn file_attr(&self) -> io::Result { unsafe { let mut info: c::BY_HANDLE_FILE_INFORMATION = mem::zeroed(); - cvt(c::GetFileInformationByHandle(self.handle.raw(), &mut info))?; + cvt(c::GetFileInformationByHandle(self.handle.as_raw_handle(), &mut info))?; let mut reparse_tag = 0; if info.dwFileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 { let mut b = [0; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; @@ -449,7 +450,7 @@ impl File { }; let pos = pos as c::LARGE_INTEGER; let mut newpos = 0; - cvt(unsafe { c::SetFilePointerEx(self.handle.raw(), pos, &mut newpos, whence) })?; + cvt(unsafe { c::SetFilePointerEx(self.handle.as_raw_handle(), pos, &mut newpos, whence) })?; Ok(newpos as u64) } @@ -457,14 +458,6 @@ impl File { Ok(File { handle: self.handle.duplicate(0, false, c::DUPLICATE_SAME_ACCESS)? }) } - pub fn handle(&self) -> &Handle { - &self.handle - } - - pub fn into_handle(self) -> Handle { - self.handle - } - fn reparse_point<'a>( &self, space: &'a mut [u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE], @@ -473,7 +466,7 @@ impl File { let mut bytes = 0; cvt({ c::DeviceIoControl( - self.handle.raw(), + self.handle.as_raw_handle(), c::FSCTL_GET_REPARSE_POINT, ptr::null_mut(), 0, @@ -541,7 +534,7 @@ impl File { let size = mem::size_of_val(&info); cvt(unsafe { c::SetFileInformationByHandle( - self.handle.raw(), + self.handle.as_raw_handle(), c::FileBasicInfo, &mut info as *mut _ as *mut _, size as c::DWORD, @@ -551,9 +544,45 @@ impl File { } } -impl FromInner for File { - fn from_inner(handle: c::HANDLE) -> File { - File { handle: Handle::new(handle) } +impl AsInner for File { + fn as_inner(&self) -> &Handle { + &self.handle + } +} + +impl IntoInner for File { + fn into_inner(self) -> Handle { + self.handle + } +} + +impl FromInner for File { + fn from_inner(handle: Handle) -> File { + File { handle: handle } + } +} + +impl AsHandle for File { + fn as_handle(&self) -> BorrowedHandle<'_> { + self.as_inner().as_handle() + } +} + +impl AsRawHandle for File { + fn as_raw_handle(&self) -> RawHandle { + self.as_inner().as_raw_handle() + } +} + +impl IntoRawHandle for File { + fn into_raw_handle(self) -> RawHandle { + self.into_inner().into_raw_handle() + } +} + +impl FromRawHandle for File { + unsafe fn from_raw_handle(raw_handle: RawHandle) -> Self { + Self { handle: FromInner::from_inner(FromRawHandle::from_raw_handle(raw_handle)) } } } @@ -561,7 +590,7 @@ impl fmt::Debug for File { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // FIXME(#24570): add more info here (e.g., mode) let mut b = f.debug_struct("File"); - b.field("handle", &self.handle.raw()); + b.field("handle", &self.handle.as_raw_handle()); if let Ok(path) = get_path(&self) { b.field("path", &path); } @@ -838,7 +867,7 @@ pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> { fn get_path(f: &File) -> io::Result { super::fill_utf16_buf( |buf, sz| unsafe { - c::GetFinalPathNameByHandleW(f.handle.raw(), buf, sz, c::VOLUME_NAME_DOS) + c::GetFinalPathNameByHandleW(f.handle.as_raw_handle(), buf, sz, c::VOLUME_NAME_DOS) }, |buf| PathBuf::from(OsString::from_wide(buf)), ) @@ -909,7 +938,7 @@ fn symlink_junction_inner(original: &Path, junction: &Path) -> io::Result<()> { opts.write(true); opts.custom_flags(c::FILE_FLAG_OPEN_REPARSE_POINT | c::FILE_FLAG_BACKUP_SEMANTICS); let f = File::open(junction, &opts)?; - let h = f.handle().raw(); + let h = f.as_inner().as_raw_handle(); unsafe { let mut data = [0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; diff --git a/library/std/src/sys/windows/handle.rs b/library/std/src/sys/windows/handle.rs index 0d4baa3b340df..21d86b002264a 100644 --- a/library/std/src/sys/windows/handle.rs +++ b/library/std/src/sys/windows/handle.rs @@ -3,76 +3,87 @@ use crate::cmp; use crate::io::{self, ErrorKind, IoSlice, IoSliceMut, Read}; use crate::mem; -use crate::ops::Deref; +use crate::os::windows::io::{ + AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle, IntoRawHandle, OwnedHandle, RawHandle, +}; use crate::ptr; use crate::sys::c; use crate::sys::cvt; +use crate::sys_common::{AsInner, FromInner, IntoInner}; /// An owned container for `HANDLE` object, closing them on Drop. /// /// All methods are inherited through a `Deref` impl to `RawHandle` -pub struct Handle(RawHandle); - -/// A wrapper type for `HANDLE` objects to give them proper Send/Sync inference -/// as well as Rust-y methods. -/// -/// This does **not** drop the handle when it goes out of scope, use `Handle` -/// instead for that. -#[derive(Copy, Clone)] -pub struct RawHandle(c::HANDLE); - -unsafe impl Send for RawHandle {} -unsafe impl Sync for RawHandle {} +pub struct Handle(OwnedHandle); impl Handle { - pub fn new(handle: c::HANDLE) -> Handle { - Handle(RawHandle::new(handle)) - } - pub fn new_event(manual: bool, init: bool) -> io::Result { unsafe { let event = c::CreateEventW(ptr::null_mut(), manual as c::BOOL, init as c::BOOL, ptr::null()); - if event.is_null() { Err(io::Error::last_os_error()) } else { Ok(Handle::new(event)) } + if event.is_null() { + Err(io::Error::last_os_error()) + } else { + Ok(Handle::from_raw_handle(event)) + } } } +} - pub fn into_raw(self) -> c::HANDLE { - let ret = self.raw(); - mem::forget(self); - ret +impl AsInner for Handle { + fn as_inner(&self) -> &OwnedHandle { + &self.0 } } -impl Deref for Handle { - type Target = RawHandle; - fn deref(&self) -> &RawHandle { - &self.0 +impl IntoInner for Handle { + fn into_inner(self) -> OwnedHandle { + self.0 } } -impl Drop for Handle { - fn drop(&mut self) { - unsafe { - let _ = c::CloseHandle(self.raw()); - } +impl FromInner for Handle { + fn from_inner(file_desc: OwnedHandle) -> Self { + Self(file_desc) } } -impl RawHandle { - pub fn new(handle: c::HANDLE) -> RawHandle { - RawHandle(handle) +impl AsHandle for Handle { + fn as_handle(&self) -> BorrowedHandle<'_> { + self.0.as_handle() } +} - pub fn raw(&self) -> c::HANDLE { - self.0 +impl AsRawHandle for Handle { + fn as_raw_handle(&self) -> RawHandle { + self.0.as_raw_handle() } +} +impl IntoRawHandle for Handle { + fn into_raw_handle(self) -> RawHandle { + self.0.into_raw_handle() + } +} + +impl FromRawHandle for Handle { + unsafe fn from_raw_handle(raw_handle: RawHandle) -> Self { + Self(FromRawHandle::from_raw_handle(raw_handle)) + } +} + +impl Handle { pub fn read(&self, buf: &mut [u8]) -> io::Result { let mut read = 0; let len = cmp::min(buf.len(), ::MAX as usize) as c::DWORD; let res = cvt(unsafe { - c::ReadFile(self.0, buf.as_mut_ptr() as c::LPVOID, len, &mut read, ptr::null_mut()) + c::ReadFile( + self.as_raw_handle(), + buf.as_mut_ptr() as c::LPVOID, + len, + &mut read, + ptr::null_mut(), + ) }); match res { @@ -104,7 +115,13 @@ impl RawHandle { let mut overlapped: c::OVERLAPPED = mem::zeroed(); overlapped.Offset = offset as u32; overlapped.OffsetHigh = (offset >> 32) as u32; - cvt(c::ReadFile(self.0, buf.as_mut_ptr() as c::LPVOID, len, &mut read, &mut overlapped)) + cvt(c::ReadFile( + self.as_raw_handle(), + buf.as_mut_ptr() as c::LPVOID, + len, + &mut read, + &mut overlapped, + )) }; match res { Ok(_) => Ok(read as usize), @@ -120,7 +137,13 @@ impl RawHandle { ) -> io::Result> { let len = cmp::min(buf.len(), ::MAX as usize) as c::DWORD; let mut amt = 0; - let res = cvt(c::ReadFile(self.0, buf.as_ptr() as c::LPVOID, len, &mut amt, overlapped)); + let res = cvt(c::ReadFile( + self.as_raw_handle(), + buf.as_ptr() as c::LPVOID, + len, + &mut amt, + overlapped, + )); match res { Ok(_) => Ok(Some(amt as usize)), Err(e) => { @@ -143,7 +166,8 @@ impl RawHandle { unsafe { let mut bytes = 0; let wait = if wait { c::TRUE } else { c::FALSE }; - let res = cvt(c::GetOverlappedResult(self.raw(), overlapped, &mut bytes, wait)); + let res = + cvt(c::GetOverlappedResult(self.as_raw_handle(), overlapped, &mut bytes, wait)); match res { Ok(_) => Ok(bytes as usize), Err(e) => { @@ -160,14 +184,20 @@ impl RawHandle { } pub fn cancel_io(&self) -> io::Result<()> { - unsafe { cvt(c::CancelIo(self.raw())).map(drop) } + unsafe { cvt(c::CancelIo(self.as_raw_handle())).map(drop) } } pub fn write(&self, buf: &[u8]) -> io::Result { let mut amt = 0; let len = cmp::min(buf.len(), ::MAX as usize) as c::DWORD; cvt(unsafe { - c::WriteFile(self.0, buf.as_ptr() as c::LPVOID, len, &mut amt, ptr::null_mut()) + c::WriteFile( + self.as_raw_handle(), + buf.as_ptr() as c::LPVOID, + len, + &mut amt, + ptr::null_mut(), + ) })?; Ok(amt as usize) } @@ -189,7 +219,7 @@ impl RawHandle { overlapped.Offset = offset as u32; overlapped.OffsetHigh = (offset >> 32) as u32; cvt(c::WriteFile( - self.0, + self.as_raw_handle(), buf.as_ptr() as c::LPVOID, len, &mut written, @@ -210,7 +240,7 @@ impl RawHandle { let cur_proc = c::GetCurrentProcess(); c::DuplicateHandle( cur_proc, - self.0, + self.as_raw_handle(), cur_proc, &mut ret, access, @@ -218,11 +248,11 @@ impl RawHandle { options, ) })?; - Ok(Handle::new(ret)) + unsafe { Ok(Handle::from_raw_handle(ret)) } } } -impl<'a> Read for &'a RawHandle { +impl<'a> Read for &'a Handle { fn read(&mut self, buf: &mut [u8]) -> io::Result { (**self).read(buf) } diff --git a/library/std/src/sys/windows/net.rs b/library/std/src/sys/windows/net.rs index 9cea5c5e63a2d..55aacb38c6f78 100644 --- a/library/std/src/sys/windows/net.rs +++ b/library/std/src/sys/windows/net.rs @@ -4,6 +4,9 @@ use crate::cmp; use crate::io::{self, IoSlice, IoSliceMut, Read}; use crate::mem; use crate::net::{Shutdown, SocketAddr}; +use crate::os::windows::io::{ + AsRawSocket, AsSocket, BorrowedSocket, FromRawSocket, IntoRawSocket, OwnedSocket, RawSocket, +}; use crate::ptr; use crate::sync::Once; use crate::sys; @@ -24,7 +27,7 @@ pub mod netc { pub use crate::sys::c::*; } -pub struct Socket(c::SOCKET); +pub struct Socket(OwnedSocket); static INIT: Once = Once::new(); @@ -109,7 +112,7 @@ impl Socket { }; if socket != c::INVALID_SOCKET { - Ok(Self(socket)) + unsafe { Ok(Self::from_raw_socket(socket)) } } else { let error = unsafe { c::WSAGetLastError() }; @@ -124,9 +127,11 @@ impl Socket { return Err(last_error()); } - let socket = Self(socket); - socket.set_no_inherit()?; - Ok(socket) + unsafe { + let socket = Self::from_raw_socket(socket); + socket.set_no_inherit()?; + Ok(socket) + } } } @@ -134,7 +139,7 @@ impl Socket { self.set_nonblocking(true)?; let result = { let (addrp, len) = addr.into_inner(); - let result = unsafe { c::connect(self.0, addrp, len) }; + let result = unsafe { c::connect(self.as_raw_socket(), addrp, len) }; cvt(result).map(drop) }; self.set_nonblocking(false)?; @@ -160,7 +165,7 @@ impl Socket { let fds = { let mut fds = unsafe { mem::zeroed::() }; fds.fd_count = 1; - fds.fd_array[0] = self.0; + fds.fd_array[0] = self.as_raw_socket(); fds }; @@ -194,17 +199,19 @@ impl Socket { } pub fn accept(&self, storage: *mut c::SOCKADDR, len: *mut c_int) -> io::Result { - let socket = unsafe { c::accept(self.0, storage, len) }; + let socket = unsafe { c::accept(self.as_raw_socket(), storage, len) }; match socket { c::INVALID_SOCKET => Err(last_error()), - _ => Ok(Self(socket)), + _ => unsafe { Ok(Self::from_raw_socket(socket)) }, } } pub fn duplicate(&self) -> io::Result { let mut info = unsafe { mem::zeroed::() }; - let result = unsafe { c::WSADuplicateSocketW(self.0, c::GetCurrentProcessId(), &mut info) }; + let result = unsafe { + c::WSADuplicateSocketW(self.as_raw_socket(), c::GetCurrentProcessId(), &mut info) + }; cvt(result)?; let socket = unsafe { c::WSASocketW( @@ -218,7 +225,7 @@ impl Socket { }; if socket != c::INVALID_SOCKET { - Ok(Self(socket)) + unsafe { Ok(Self::from_inner(OwnedSocket::from_raw_socket(socket))) } } else { let error = unsafe { c::WSAGetLastError() }; @@ -241,9 +248,11 @@ impl Socket { return Err(last_error()); } - let socket = Self(socket); - socket.set_no_inherit()?; - Ok(socket) + unsafe { + let socket = Self::from_inner(OwnedSocket::from_raw_socket(socket)); + socket.set_no_inherit()?; + Ok(socket) + } } } @@ -251,7 +260,8 @@ impl Socket { // On unix when a socket is shut down all further reads return 0, so we // do the same on windows to map a shut down socket to returning EOF. let length = cmp::min(buf.len(), i32::MAX as usize) as i32; - let result = unsafe { c::recv(self.0, buf.as_mut_ptr() as *mut _, length, flags) }; + let result = + unsafe { c::recv(self.as_raw_socket(), buf.as_mut_ptr() as *mut _, length, flags) }; match result { c::SOCKET_ERROR => { @@ -279,7 +289,7 @@ impl Socket { let mut flags = 0; let result = unsafe { c::WSARecv( - self.0, + self.as_raw_socket(), bufs.as_mut_ptr() as *mut c::WSABUF, length, &mut nread, @@ -325,7 +335,7 @@ impl Socket { // do the same on windows to map a shut down socket to returning EOF. let result = unsafe { c::recvfrom( - self.0, + self.as_raw_socket(), buf.as_mut_ptr() as *mut _, length, flags, @@ -361,7 +371,7 @@ impl Socket { let mut nwritten = 0; let result = unsafe { c::WSASend( - self.0, + self.as_raw_socket(), bufs.as_ptr() as *const c::WSABUF as *mut _, length, &mut nwritten, @@ -408,8 +418,10 @@ impl Socket { #[cfg(not(target_vendor = "uwp"))] fn set_no_inherit(&self) -> io::Result<()> { - sys::cvt(unsafe { c::SetHandleInformation(self.0 as c::HANDLE, c::HANDLE_FLAG_INHERIT, 0) }) - .map(drop) + sys::cvt(unsafe { + c::SetHandleInformation(self.as_raw_socket() as c::HANDLE, c::HANDLE_FLAG_INHERIT, 0) + }) + .map(drop) } #[cfg(target_vendor = "uwp")] @@ -423,13 +435,14 @@ impl Socket { Shutdown::Read => c::SD_RECEIVE, Shutdown::Both => c::SD_BOTH, }; - let result = unsafe { c::shutdown(self.0, how) }; + let result = unsafe { c::shutdown(self.as_raw_socket(), how) }; cvt(result).map(drop) } pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { let mut nonblocking = nonblocking as c_ulong; - let result = unsafe { c::ioctlsocket(self.0, c::FIONBIO as c_int, &mut nonblocking) }; + let result = + unsafe { c::ioctlsocket(self.as_raw_socket(), c::FIONBIO as c_int, &mut nonblocking) }; cvt(result).map(drop) } @@ -446,6 +459,11 @@ impl Socket { let raw: c_int = net::getsockopt(self, c::SOL_SOCKET, c::SO_ERROR)?; if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) } } + + // This is used by sys_common code to abstract over Windows and Unix. + pub fn as_raw(&self) -> RawSocket { + self.as_inner().as_raw_socket() + } } #[unstable(reason = "not public", issue = "none", feature = "fd_read")] @@ -455,28 +473,44 @@ impl<'a> Read for &'a Socket { } } -impl Drop for Socket { - fn drop(&mut self) { - let _ = unsafe { c::closesocket(self.0) }; +impl AsInner for Socket { + fn as_inner(&self) -> &OwnedSocket { + &self.0 } } -impl AsInner for Socket { - fn as_inner(&self) -> &c::SOCKET { - &self.0 +impl FromInner for Socket { + fn from_inner(sock: OwnedSocket) -> Socket { + Socket(sock) } } -impl FromInner for Socket { - fn from_inner(sock: c::SOCKET) -> Socket { - Socket(sock) +impl IntoInner for Socket { + fn into_inner(self) -> OwnedSocket { + self.0 + } +} + +impl AsSocket for Socket { + fn as_socket(&self) -> BorrowedSocket<'_> { + self.0.as_socket() + } +} + +impl AsRawSocket for Socket { + fn as_raw_socket(&self) -> RawSocket { + self.0.as_raw_socket() + } +} + +impl IntoRawSocket for Socket { + fn into_raw_socket(self) -> RawSocket { + self.0.into_raw_socket() } } -impl IntoInner for Socket { - fn into_inner(self) -> c::SOCKET { - let ret = self.0; - mem::forget(self); - ret +impl FromRawSocket for Socket { + unsafe fn from_raw_socket(raw_socket: RawSocket) -> Self { + Self(FromRawSocket::from_raw_socket(raw_socket)) } } diff --git a/library/std/src/sys/windows/os.rs b/library/std/src/sys/windows/os.rs index 8db97ba50a81f..883690c483167 100644 --- a/library/std/src/sys/windows/os.rs +++ b/library/std/src/sys/windows/os.rs @@ -288,7 +288,7 @@ fn home_dir_crt() -> Option { if c::OpenProcessToken(me, c::TOKEN_READ, &mut token) == 0 { return None; } - let _handle = Handle::new(token); + let _handle = Handle::from_raw_handle(token); super::fill_utf16_buf( |buf, mut sz| { match c::GetUserProfileDirectoryW(token, buf, &mut sz) { diff --git a/library/std/src/sys/windows/pipe.rs b/library/std/src/sys/windows/pipe.rs index 104a8db46596e..63d3d6c5ed42f 100644 --- a/library/std/src/sys/windows/pipe.rs +++ b/library/std/src/sys/windows/pipe.rs @@ -12,6 +12,7 @@ use crate::sys::c; use crate::sys::fs::{File, OpenOptions}; use crate::sys::handle::Handle; use crate::sys::hashmap_random_keys; +use crate::sys_common::IntoInner; //////////////////////////////////////////////////////////////////////////////// // Anonymous pipes @@ -21,6 +22,12 @@ pub struct AnonPipe { inner: Handle, } +impl IntoInner for AnonPipe { + fn into_inner(self) -> Handle { + self.inner + } +} + pub struct Pipes { pub ours: AnonPipe, pub theirs: AnonPipe, @@ -123,7 +130,7 @@ pub fn anon_pipe(ours_readable: bool, their_handle_inheritable: bool) -> io::Res } return Err(err); } - ours = Handle::new(handle); + ours = Handle::from_raw_handle(handle); break; } @@ -146,11 +153,11 @@ pub fn anon_pipe(ours_readable: bool, their_handle_inheritable: bool) -> io::Res }; opts.security_attributes(&mut sa); let theirs = File::open(Path::new(&name), &opts)?; - let theirs = AnonPipe { inner: theirs.into_handle() }; + let theirs = AnonPipe { inner: theirs.into_inner() }; Ok(Pipes { ours: AnonPipe { inner: ours }, - theirs: AnonPipe { inner: theirs.into_handle() }, + theirs: AnonPipe { inner: theirs.into_inner() }, }) } } @@ -207,7 +214,7 @@ pub fn read2(p1: AnonPipe, v1: &mut Vec, p2: AnonPipe, v2: &mut Vec) -> let mut p1 = AsyncPipe::new(p1, v1)?; let mut p2 = AsyncPipe::new(p2, v2)?; - let objs = [p1.event.raw(), p2.event.raw()]; + let objs = [p1.event.as_raw_handle(), p2.event.as_raw_handle()]; // In a loop we wait for either pipe's scheduled read operation to complete. // If the operation completes with 0 bytes, that means EOF was reached, in @@ -262,7 +269,7 @@ impl<'a> AsyncPipe<'a> { // I/O operation is successfully scheduled (what we want). let event = Handle::new_event(true, true)?; let mut overlapped: Box = unsafe { Box::new(mem::zeroed()) }; - overlapped.hEvent = event.raw(); + overlapped.hEvent = event.as_raw_handle(); Ok(AsyncPipe { pipe, overlapped, event, dst, state: State::NotReading }) } diff --git a/library/std/src/sys/windows/process.rs b/library/std/src/sys/windows/process.rs index ae193b82e91bb..ccff90629a371 100644 --- a/library/std/src/sys/windows/process.rs +++ b/library/std/src/sys/windows/process.rs @@ -15,6 +15,7 @@ use crate::io::{self, Error, ErrorKind}; use crate::mem; use crate::num::NonZeroI32; use crate::os::windows::ffi::OsStrExt; +use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle}; use crate::path::Path; use crate::ptr; use crate::sys::c; @@ -26,7 +27,7 @@ use crate::sys::pipe::{self, AnonPipe}; use crate::sys::stdio; use crate::sys_common::mutex::StaticMutex; use crate::sys_common::process::{CommandEnv, CommandEnvs}; -use crate::sys_common::AsInner; +use crate::sys_common::{AsInner, IntoInner}; use libc::{c_void, EXIT_FAILURE, EXIT_SUCCESS}; @@ -316,9 +317,9 @@ impl Command { let stdin = stdin.to_handle(c::STD_INPUT_HANDLE, &mut pipes.stdin)?; let stdout = stdout.to_handle(c::STD_OUTPUT_HANDLE, &mut pipes.stdout)?; let stderr = stderr.to_handle(c::STD_ERROR_HANDLE, &mut pipes.stderr)?; - si.hStdInput = stdin.raw(); - si.hStdOutput = stdout.raw(); - si.hStdError = stderr.raw(); + si.hStdInput = stdin.as_raw_handle(); + si.hStdOutput = stdout.as_raw_handle(); + si.hStdError = stderr.as_raw_handle(); unsafe { cvt(c::CreateProcessW( @@ -338,9 +339,11 @@ impl Command { // We close the thread handle because we don't care about keeping // the thread id valid, and we aren't keeping the thread handle // around to be able to close it later. - drop(Handle::new(pi.hThread)); + unsafe { + drop(Handle::from_raw_handle(pi.hThread)); - Ok((Process { handle: Handle::new(pi.hProcess) }, pipes)) + Ok((Process { handle: Handle::from_raw_handle(pi.hProcess) }, pipes)) + } } } @@ -365,13 +368,13 @@ impl Stdio { // should still be unavailable so propagate the // INVALID_HANDLE_VALUE. Stdio::Inherit => match stdio::get_handle(stdio_id) { - Ok(io) => { - let io = Handle::new(io); + Ok(io) => unsafe { + let io = Handle::from_raw_handle(io); let ret = io.duplicate(0, true, c::DUPLICATE_SAME_ACCESS); - io.into_raw(); + io.into_raw_handle(); ret - } - Err(..) => Ok(Handle::new(c::INVALID_HANDLE_VALUE)), + }, + Err(..) => unsafe { Ok(Handle::from_raw_handle(c::INVALID_HANDLE_VALUE)) }, }, Stdio::MakePipe => { @@ -397,7 +400,7 @@ impl Stdio { opts.read(stdio_id == c::STD_INPUT_HANDLE); opts.write(stdio_id != c::STD_INPUT_HANDLE); opts.security_attributes(&mut sa); - File::open(Path::new("NUL"), &opts).map(|file| file.into_handle()) + File::open(Path::new("NUL"), &opts).map(|file| file.into_inner()) } } } @@ -411,7 +414,7 @@ impl From for Stdio { impl From for Stdio { fn from(file: File) -> Stdio { - Stdio::Handle(file.into_handle()) + Stdio::Handle(file.into_inner()) } } @@ -430,29 +433,29 @@ pub struct Process { impl Process { pub fn kill(&mut self) -> io::Result<()> { - cvt(unsafe { c::TerminateProcess(self.handle.raw(), 1) })?; + cvt(unsafe { c::TerminateProcess(self.handle.as_raw_handle(), 1) })?; Ok(()) } pub fn id(&self) -> u32 { - unsafe { c::GetProcessId(self.handle.raw()) as u32 } + unsafe { c::GetProcessId(self.handle.as_raw_handle()) as u32 } } pub fn wait(&mut self) -> io::Result { unsafe { - let res = c::WaitForSingleObject(self.handle.raw(), c::INFINITE); + let res = c::WaitForSingleObject(self.handle.as_raw_handle(), c::INFINITE); if res != c::WAIT_OBJECT_0 { return Err(Error::last_os_error()); } let mut status = 0; - cvt(c::GetExitCodeProcess(self.handle.raw(), &mut status))?; + cvt(c::GetExitCodeProcess(self.handle.as_raw_handle(), &mut status))?; Ok(ExitStatus(status)) } } pub fn try_wait(&mut self) -> io::Result> { unsafe { - match c::WaitForSingleObject(self.handle.raw(), 0) { + match c::WaitForSingleObject(self.handle.as_raw_handle(), 0) { c::WAIT_OBJECT_0 => {} c::WAIT_TIMEOUT => { return Ok(None); @@ -460,7 +463,7 @@ impl Process { _ => return Err(io::Error::last_os_error()), } let mut status = 0; - cvt(c::GetExitCodeProcess(self.handle.raw(), &mut status))?; + cvt(c::GetExitCodeProcess(self.handle.as_raw_handle(), &mut status))?; Ok(Some(ExitStatus(status))) } } diff --git a/library/std/src/sys/windows/stdio.rs b/library/std/src/sys/windows/stdio.rs index 2973951fe9004..1cf0e9f0cf1ea 100644 --- a/library/std/src/sys/windows/stdio.rs +++ b/library/std/src/sys/windows/stdio.rs @@ -3,6 +3,7 @@ use crate::char::decode_utf16; use crate::cmp; use crate::io; +use crate::os::windows::io::{FromRawHandle, IntoRawHandle}; use crate::ptr; use crate::str; use crate::sys::c; @@ -53,10 +54,12 @@ fn is_console(handle: c::HANDLE) -> bool { fn write(handle_id: c::DWORD, data: &[u8]) -> io::Result { let handle = get_handle(handle_id)?; if !is_console(handle) { - let handle = Handle::new(handle); - let ret = handle.write(data); - handle.into_raw(); // Don't close the handle - return ret; + unsafe { + let handle = Handle::from_raw_handle(handle); + let ret = handle.write(data); + handle.into_raw_handle(); // Don't close the handle + return ret; + } } // As the console is meant for presenting text, we assume bytes of `data` come from a string @@ -140,10 +143,12 @@ impl io::Read for Stdin { fn read(&mut self, buf: &mut [u8]) -> io::Result { let handle = get_handle(c::STD_INPUT_HANDLE)?; if !is_console(handle) { - let handle = Handle::new(handle); - let ret = handle.read(buf); - handle.into_raw(); // Don't close the handle - return ret; + unsafe { + let handle = Handle::from_raw_handle(handle); + let ret = handle.read(buf); + handle.into_raw_handle(); // Don't close the handle + return ret; + } } if buf.len() == 0 { diff --git a/library/std/src/sys/windows/thread.rs b/library/std/src/sys/windows/thread.rs index ef7a9733fd880..a5293133b3ab0 100644 --- a/library/std/src/sys/windows/thread.rs +++ b/library/std/src/sys/windows/thread.rs @@ -1,6 +1,7 @@ use crate::ffi::CStr; use crate::io; use crate::num::NonZeroUsize; +use crate::os::windows::io::{AsRawHandle, FromRawHandle}; use crate::ptr; use crate::sys::c; use crate::sys::handle::Handle; @@ -45,7 +46,7 @@ impl Thread { drop(Box::from_raw(p)); Err(io::Error::last_os_error()) } else { - Ok(Thread { handle: Handle::new(ret) }) + Ok(Thread { handle: Handle::from_raw_handle(ret) }) }; extern "system" fn thread_start(main: *mut c_void) -> c::DWORD { @@ -71,7 +72,7 @@ impl Thread { } pub fn join(self) { - let rc = unsafe { c::WaitForSingleObject(self.handle.raw(), c::INFINITE) }; + let rc = unsafe { c::WaitForSingleObject(self.handle.as_raw_handle(), c::INFINITE) }; if rc == c::WAIT_FAILED { panic!("failed to join on thread: {}", io::Error::last_os_error()); } diff --git a/library/std/src/sys_common/net.rs b/library/std/src/sys_common/net.rs index d5f29c4a43970..0ffa5c01dd33b 100644 --- a/library/std/src/sys_common/net.rs +++ b/library/std/src/sys_common/net.rs @@ -61,13 +61,7 @@ cfg_if::cfg_if! { pub fn setsockopt(sock: &Socket, opt: c_int, val: c_int, payload: T) -> io::Result<()> { unsafe { let payload = &payload as *const T as *const c_void; - cvt(c::setsockopt( - *sock.as_inner(), - opt, - val, - payload, - mem::size_of::() as c::socklen_t, - ))?; + cvt(c::setsockopt(sock.as_raw(), opt, val, payload, mem::size_of::() as c::socklen_t))?; Ok(()) } } @@ -76,7 +70,7 @@ pub fn getsockopt(sock: &Socket, opt: c_int, val: c_int) -> io::Result< unsafe { let mut slot: T = mem::zeroed(); let mut len = mem::size_of::() as c::socklen_t; - cvt(c::getsockopt(*sock.as_inner(), opt, val, &mut slot as *mut _ as *mut _, &mut len))?; + cvt(c::getsockopt(sock.as_raw(), opt, val, &mut slot as *mut _ as *mut _, &mut len))?; assert_eq!(len as usize, mem::size_of::()); Ok(slot) } @@ -217,7 +211,7 @@ impl TcpStream { let sock = Socket::new(addr, c::SOCK_STREAM)?; let (addrp, len) = addr.into_inner(); - cvt_r(|| unsafe { c::connect(*sock.as_inner(), addrp, len) })?; + cvt_r(|| unsafe { c::connect(sock.as_raw(), addrp, len) })?; Ok(TcpStream { inner: sock }) } @@ -273,7 +267,7 @@ impl TcpStream { pub fn write(&self, buf: &[u8]) -> io::Result { let len = cmp::min(buf.len(), ::MAX as usize) as wrlen_t; let ret = cvt(unsafe { - c::send(*self.inner.as_inner(), buf.as_ptr() as *const c_void, len, MSG_NOSIGNAL) + c::send(self.inner.as_raw(), buf.as_ptr() as *const c_void, len, MSG_NOSIGNAL) })?; Ok(ret as usize) } @@ -288,11 +282,11 @@ impl TcpStream { } pub fn peer_addr(&self) -> io::Result { - sockname(|buf, len| unsafe { c::getpeername(*self.inner.as_inner(), buf, len) }) + sockname(|buf, len| unsafe { c::getpeername(self.inner.as_raw(), buf, len) }) } pub fn socket_addr(&self) -> io::Result { - sockname(|buf, len| unsafe { c::getsockname(*self.inner.as_inner(), buf, len) }) + sockname(|buf, len| unsafe { c::getsockname(self.inner.as_raw(), buf, len) }) } pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { @@ -348,7 +342,7 @@ impl fmt::Debug for TcpStream { } let name = if cfg!(windows) { "socket" } else { "fd" }; - res.field(name, &self.inner.as_inner()).finish() + res.field(name, &self.inner.as_raw()).finish() } } @@ -380,10 +374,10 @@ impl TcpListener { // Bind our new socket let (addrp, len) = addr.into_inner(); - cvt(unsafe { c::bind(*sock.as_inner(), addrp, len as _) })?; + cvt(unsafe { c::bind(sock.as_raw(), addrp, len as _) })?; // Start listening - cvt(unsafe { c::listen(*sock.as_inner(), 128) })?; + cvt(unsafe { c::listen(sock.as_raw(), 128) })?; Ok(TcpListener { inner: sock }) } @@ -396,7 +390,7 @@ impl TcpListener { } pub fn socket_addr(&self) -> io::Result { - sockname(|buf, len| unsafe { c::getsockname(*self.inner.as_inner(), buf, len) }) + sockname(|buf, len| unsafe { c::getsockname(self.inner.as_raw(), buf, len) }) } pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { @@ -453,7 +447,7 @@ impl fmt::Debug for TcpListener { } let name = if cfg!(windows) { "socket" } else { "fd" }; - res.field(name, &self.inner.as_inner()).finish() + res.field(name, &self.inner.as_raw()).finish() } } @@ -473,7 +467,7 @@ impl UdpSocket { let sock = Socket::new(addr, c::SOCK_DGRAM)?; let (addrp, len) = addr.into_inner(); - cvt(unsafe { c::bind(*sock.as_inner(), addrp, len as _) })?; + cvt(unsafe { c::bind(sock.as_raw(), addrp, len as _) })?; Ok(UdpSocket { inner: sock }) } @@ -486,11 +480,11 @@ impl UdpSocket { } pub fn peer_addr(&self) -> io::Result { - sockname(|buf, len| unsafe { c::getpeername(*self.inner.as_inner(), buf, len) }) + sockname(|buf, len| unsafe { c::getpeername(self.inner.as_raw(), buf, len) }) } pub fn socket_addr(&self) -> io::Result { - sockname(|buf, len| unsafe { c::getsockname(*self.inner.as_inner(), buf, len) }) + sockname(|buf, len| unsafe { c::getsockname(self.inner.as_raw(), buf, len) }) } pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { @@ -506,7 +500,7 @@ impl UdpSocket { let (dstp, dstlen) = dst.into_inner(); let ret = cvt(unsafe { c::sendto( - *self.inner.as_inner(), + self.inner.as_raw(), buf.as_ptr() as *const c_void, len, MSG_NOSIGNAL, @@ -643,14 +637,14 @@ impl UdpSocket { pub fn send(&self, buf: &[u8]) -> io::Result { let len = cmp::min(buf.len(), ::MAX as usize) as wrlen_t; let ret = cvt(unsafe { - c::send(*self.inner.as_inner(), buf.as_ptr() as *const c_void, len, MSG_NOSIGNAL) + c::send(self.inner.as_raw(), buf.as_ptr() as *const c_void, len, MSG_NOSIGNAL) })?; Ok(ret as usize) } pub fn connect(&self, addr: io::Result<&SocketAddr>) -> io::Result<()> { let (addrp, len) = addr?.into_inner(); - cvt_r(|| unsafe { c::connect(*self.inner.as_inner(), addrp, len) }).map(drop) + cvt_r(|| unsafe { c::connect(self.inner.as_raw(), addrp, len) }).map(drop) } } @@ -669,6 +663,6 @@ impl fmt::Debug for UdpSocket { } let name = if cfg!(windows) { "socket" } else { "fd" }; - res.field(name, &self.inner.as_inner()).finish() + res.field(name, &self.inner.as_raw()).finish() } }