//! SOLID-specific extensions to general I/O primitives //! //! Just like raw pointers, raw SOLID Sockets 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 raw 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. //! //! `BorrowedFd` values may be used in APIs which provide safe access to `dup` //! system calls, so types implementing `AsFd` or `From` should not //! assume they always have exclusive access to the underlying file //! description. //! //! Like boxes, `OwnedFd` values conceptually own the resource they point to, //! and free (close) it when they are dropped. //! //! [`BorrowedFd<'a>`]: crate::os::solid::io::BorrowedFd #![deny(unsafe_op_in_unsafe_fn)] #![unstable(feature = "solid_ext", issue = "none")] use crate::fmt; use crate::marker::PhantomData; use crate::mem::forget; use crate::net; use crate::sys; use crate::sys_common::{self, AsInner, FromInner, IntoInner}; /// Raw file descriptors. pub type RawFd = i32; /// A borrowed SOLID Sockets file descriptor. /// /// 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 file /// descriptor, 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 /// `SOLID_NET_INVALID_FD`. /// /// This type's `.to_owned()` implementation returns another `BorrowedFd` /// rather than an `OwnedFd`. It just makes a trivial copy of the raw /// socket, which is then borrowed under the same lifetime. #[derive(Copy, Clone)] #[repr(transparent)] #[rustc_layout_scalar_valid_range_start(0)] // This is -2, in two's complement. -1 is `SOLID_NET_INVALID_FD`. #[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)] #[rustc_nonnull_optimization_guaranteed] pub struct BorrowedFd<'socket> { fd: RawFd, _phantom: PhantomData<&'socket OwnedFd>, } /// An owned SOLID Sockets 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 socket is passed as /// an argument, it is not captured or consumed, and it never has the value /// `SOLID_NET_INVALID_FD`. #[repr(transparent)] #[rustc_layout_scalar_valid_range_start(0)] // This is -2, in two's complement. -1 is `SOLID_NET_INVALID_FD`. #[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)] #[rustc_nonnull_optimization_guaranteed] 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 /// `SOLID_NET_INVALID_FD`. #[inline] pub const unsafe fn borrow_raw(fd: RawFd) -> Self { assert!(fd != -1 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 } } } } impl OwnedFd { /// Creates a new `OwnedFd` instance that shares the same underlying file /// description as the existing `OwnedFd` instance. pub fn try_clone(&self) -> crate::io::Result { self.as_fd().try_clone_to_owned() } } impl BorrowedFd<'_> { /// Creates a new `OwnedFd` instance that shares the same underlying file /// description as the existing `BorrowedFd` instance. pub fn try_clone_to_owned(&self) -> crate::io::Result { let fd = sys::net::cvt(unsafe { sys::net::netc::dup(self.as_raw_fd()) })?; Ok(unsafe { OwnedFd::from_raw_fd(fd) }) } } impl AsRawFd for BorrowedFd<'_> { #[inline] fn as_raw_fd(&self) -> RawFd { self.fd } } impl AsRawFd for OwnedFd { #[inline] fn as_raw_fd(&self) -> RawFd { self.fd } } impl IntoRawFd for OwnedFd { #[inline] fn into_raw_fd(self) -> RawFd { let fd = self.fd; forget(self); fd } } 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, -1 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 } } } } impl Drop for OwnedFd { #[inline] fn drop(&mut self) { unsafe { sys::net::netc::close(self.fd) }; } } impl fmt::Debug for BorrowedFd<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("BorrowedFd").field("fd", &self.fd).finish() } } impl fmt::Debug for OwnedFd { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("OwnedFd").field("fd", &self.fd).finish() } } macro_rules! impl_is_terminal { ($($t:ty),*$(,)?) => {$( #[unstable(feature = "sealed", issue = "none")] impl crate::sealed::Sealed for $t {} #[stable(feature = "is_terminal", since = "1.70.0")] impl crate::io::IsTerminal for $t { #[inline] fn is_terminal(&self) -> bool { crate::sys::io::is_terminal(self) } } )*} } impl_is_terminal!(BorrowedFd<'_>, OwnedFd); /// A trait to borrow the SOLID Sockets file descriptor from an underlying /// object. pub trait AsFd { /// Borrows the file descriptor. fn as_fd(&self) -> BorrowedFd<'_>; } impl AsFd for &T { #[inline] fn as_fd(&self) -> BorrowedFd<'_> { T::as_fd(self) } } impl AsFd for &mut T { #[inline] fn as_fd(&self) -> BorrowedFd<'_> { T::as_fd(self) } } impl AsFd for BorrowedFd<'_> { #[inline] fn as_fd(&self) -> BorrowedFd<'_> { *self } } impl AsFd for OwnedFd { #[inline] fn as_fd(&self) -> BorrowedFd<'_> { // Safety: `OwnedFd` and `BorrowedFd` have the same validity // invariants, and the `BorrowedFd` is bounded by the lifetime // of `&self`. unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) } } } macro_rules! impl_owned_fd_traits { ($($t:ident)*) => {$( impl AsFd for net::$t { #[inline] fn as_fd(&self) -> BorrowedFd<'_> { self.as_inner().socket().as_fd() } } impl From for OwnedFd { #[inline] fn from(socket: net::$t) -> OwnedFd { socket.into_inner().into_socket().into_inner() } } impl From for net::$t { #[inline] fn from(owned_fd: OwnedFd) -> Self { Self::from_inner(FromInner::from_inner(FromInner::from_inner(owned_fd))) } } )*}; } impl_owned_fd_traits! { TcpStream TcpListener UdpSocket } /// This impl allows implementing traits that require `AsFd` on Arc. /// ``` /// # #[cfg(target_os = "solid_asp3")] mod group_cfg { /// # use std::os::solid::io::AsFd; /// use std::net::UdpSocket; /// use std::sync::Arc; /// /// trait MyTrait: AsFd {} /// impl MyTrait for Arc {} /// impl MyTrait for Box {} /// # } /// ``` impl AsFd for crate::sync::Arc { #[inline] fn as_fd(&self) -> BorrowedFd<'_> { (**self).as_fd() } } impl AsFd for crate::rc::Rc { #[inline] fn as_fd(&self) -> BorrowedFd<'_> { (**self).as_fd() } } impl AsFd for Box { #[inline] fn as_fd(&self) -> BorrowedFd<'_> { (**self).as_fd() } } /// A trait to extract the raw SOLID Sockets 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 is typically used to **consume ownership** of the /// specified file descriptor. When used in this way, the returned object /// will take responsibility for closing it when the object goes out of /// scope. /// /// However, consuming ownership is not strictly required. Use a /// [`From::from`] implementation for an API which strictly /// consumes ownership. /// /// # Safety /// /// The `fd` passed in must be an [owned file descriptor][io-safety]; /// in particular, it must be open. /// /// [io-safety]: io#io-safety 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 } } macro_rules! impl_as_raw_fd { ($($t:ident)*) => {$( #[stable(feature = "rust1", since = "1.0.0")] impl AsRawFd for net::$t { #[inline] fn as_raw_fd(&self) -> RawFd { self.as_inner().socket().as_raw_fd() } } )*}; } impl_as_raw_fd! { TcpStream TcpListener UdpSocket } macro_rules! impl_from_raw_fd { ($($t:ident)*) => {$( #[stable(feature = "from_raw_os", since = "1.1.0")] impl FromRawFd for net::$t { #[inline] unsafe fn from_raw_fd(fd: RawFd) -> net::$t { let socket = unsafe { sys::net::Socket::from_raw_fd(fd) }; net::$t::from_inner(sys_common::net::$t::from_inner(socket)) } } )*}; } impl_from_raw_fd! { TcpStream TcpListener UdpSocket } macro_rules! impl_into_raw_fd { ($($t:ident)*) => {$( #[stable(feature = "into_raw_os", since = "1.4.0")] impl IntoRawFd for net::$t { #[inline] fn into_raw_fd(self) -> RawFd { self.into_inner().into_socket().into_raw_fd() } } )*}; } impl_into_raw_fd! { TcpStream TcpListener UdpSocket }