diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-19 09:26:03 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-19 09:26:03 +0000 |
commit | 9918693037dce8aa4bb6f08741b6812923486c18 (patch) | |
tree | 21d2b40bec7e6a7ea664acee056eb3d08e15a1cf /library/std/src/os/solid/io.rs | |
parent | Releasing progress-linux version 1.75.0+dfsg1-5~progress7.99u1. (diff) | |
download | rustc-9918693037dce8aa4bb6f08741b6812923486c18.tar.xz rustc-9918693037dce8aa4bb6f08741b6812923486c18.zip |
Merging upstream version 1.76.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'library/std/src/os/solid/io.rs')
-rw-r--r-- | library/std/src/os/solid/io.rs | 300 |
1 files changed, 297 insertions, 3 deletions
diff --git a/library/std/src/os/solid/io.rs b/library/std/src/os/solid/io.rs index f82034663..19b4fe220 100644 --- a/library/std/src/os/solid/io.rs +++ b/library/std/src/os/solid/io.rs @@ -1,8 +1,55 @@ //! 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<OwnedFd>` 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}; @@ -10,6 +57,253 @@ 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> { + 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<OwnedFd> { + 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<T: AsFd> AsFd for &T { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + T::as_fd(self) + } +} + +impl<T: AsFd> 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<net::$t> for OwnedFd { + #[inline] + fn from(socket: net::$t) -> OwnedFd { + socket.into_inner().into_socket().into_inner() + } + } + + impl From<OwnedFd> 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<UdpSocket> {} +/// impl MyTrait for Box<UdpSocket> {} +/// # } +/// ``` +impl<T: AsFd> AsFd for crate::sync::Arc<T> { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + (**self).as_fd() + } +} + +impl<T: AsFd> AsFd for crate::rc::Rc<T> { + #[inline] + fn as_fd(&self) -> BorrowedFd<'_> { + (**self).as_fd() + } +} + +impl<T: AsFd> AsFd for Box<T> { + #[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 { @@ -84,7 +378,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() } } )*}; @@ -97,7 +391,7 @@ 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); + let socket = unsafe { sys::net::Socket::from_raw_fd(fd) }; net::$t::from_inner(sys_common::net::$t::from_inner(socket)) } } @@ -111,7 +405,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_raw_fd() } } )*}; |