#
std/os/fd/
owned.rs

    1//! Owned and borrowed Unix-like file descriptors.
    2
    3#![stable(feature = "io_safety", since = "1.63.0")]
    4#![deny(unsafe_op_in_unsafe_fn)]
    5
    6use super::raw::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
    7#[cfg(not(target_os = "trusty"))]
    8use crate::fs;
    9use crate::marker::PhantomData;
    10use crate::mem::ManuallyDrop;
    11#[cfg(not(any(
    12    target_arch = "wasm32",
    13    target_env = "sgx",
    14    target_os = "hermit",
    15    target_os = "trusty"
    16)))]
    17use crate::sys::cvt;
    18#[cfg(not(target_os = "trusty"))]
    19use crate::sys_common::{AsInner, FromInner, IntoInner};
    20use crate::{fmt, io};
    21
    22type ValidRawFd = core::num::niche_types::NotAllOnes<RawFd>;
    23
    24/// A borrowed file descriptor.
    25///
    26/// This has a lifetime parameter to tie it to the lifetime of something that owns the file
    27/// descriptor. For the duration of that lifetime, it is guaranteed that nobody will close the file
    28/// descriptor.
    29///
    30/// This uses `repr(transparent)` and has the representation of a host file
    31/// descriptor, so it can be used in FFI in places where a file descriptor is
    32/// passed as an argument, it is not captured or consumed, and it never has the
    33/// value `-1`.
    34///
    35/// This type does not have a [`ToOwned`][crate::borrow::ToOwned]
    36/// implementation. Calling `.to_owned()` on a variable of this type will call
    37/// it on `&BorrowedFd` and use `Clone::clone()` like `ToOwned` does for all
    38/// types implementing `Clone`. The result will be descriptor borrowed under
    39/// the same lifetime.
    40///
    41/// To obtain an [`OwnedFd`], you can use [`BorrowedFd::try_clone_to_owned`]
    42/// instead, but this is not supported on all platforms.
    43#[derive(Copy, Clone)]
    44#[repr(transparent)]
    45#[rustc_nonnull_optimization_guaranteed]
    46#[stable(feature = "io_safety", since = "1.63.0")]
    47pub struct BorrowedFd<'fd> {
    48    fd: ValidRawFd,
    49    _phantom: PhantomData<&'fd OwnedFd>,
    50}
    51
    52/// An owned file descriptor.
    53///
    54/// This closes the file descriptor on drop. It is guaranteed that nobody else will close the file
    55/// descriptor.
    56///
    57/// This uses `repr(transparent)` and has the representation of a host file
    58/// descriptor, so it can be used in FFI in places where a file descriptor is
    59/// passed as a consumed argument or returned as an owned value, and it never
    60/// has the value `-1`.
    61///
    62/// You can use [`AsFd::as_fd`] to obtain a [`BorrowedFd`].
    63#[repr(transparent)]
    64#[rustc_nonnull_optimization_guaranteed]
    65#[stable(feature = "io_safety", since = "1.63.0")]
    66pub struct OwnedFd {
    67    fd: ValidRawFd,
    68}
    69
    70impl BorrowedFd<'_> {
    71    /// Returns a `BorrowedFd` holding the given raw file descriptor.
    72    ///
    73    /// # Safety
    74    ///
    75    /// The resource pointed to by `fd` must remain open for the duration of
    76    /// the returned `BorrowedFd`, and it must not have the value `-1`.
    77    #[inline]
    78    #[track_caller]
    79    #[rustc_const_stable(feature = "io_safety", since = "1.63.0")]
    80    #[stable(feature = "io_safety", since = "1.63.0")]
    81    pub const unsafe fn borrow_raw(fd: RawFd) -> Self {
    82        Self { fd: ValidRawFd::new(fd).expect("fd != -1"), _phantom: PhantomData }
    83    }
    84}
    85
    86impl OwnedFd {
    87    /// Creates a new `OwnedFd` instance that shares the same underlying file
    88    /// description as the existing `OwnedFd` instance.
    89    #[stable(feature = "io_safety", since = "1.63.0")]
    90    pub fn try_clone(&self) -> crate::io::Result<Self> {
    91        self.as_fd().try_clone_to_owned()
    92    }
    93}
    94
    95impl BorrowedFd<'_> {
    96    /// Creates a new `OwnedFd` instance that shares the same underlying file
    97    /// description as the existing `BorrowedFd` instance.
    98    #[cfg(not(any(target_arch = "wasm32", target_os = "hermit", target_os = "trusty")))]
    99    #[stable(feature = "io_safety", since = "1.63.0")]
    100    pub fn try_clone_to_owned(&self) -> crate::io::Result<OwnedFd> {
    101        // We want to atomically duplicate this file descriptor and set the
    102        // CLOEXEC flag, and currently that's done via F_DUPFD_CLOEXEC. This
    103        // is a POSIX flag that was added to Linux in 2.6.24.
    104        #[cfg(not(any(target_os = "espidf", target_os = "vita")))]
    105        let cmd = libc::F_DUPFD_CLOEXEC;
    106
    107        // For ESP-IDF, F_DUPFD is used instead, because the CLOEXEC semantics
    108        // will never be supported, as this is a bare metal framework with
    109        // no capabilities for multi-process execution. While F_DUPFD is also
    110        // not supported yet, it might be (currently it returns ENOSYS).
    111        #[cfg(any(target_os = "espidf", target_os = "vita"))]
    112        let cmd = libc::F_DUPFD;
    113
    114        // Avoid using file descriptors below 3 as they are used for stdio
    115        let fd = cvt(unsafe { libc::fcntl(self.as_raw_fd(), cmd, 3) })?;
    116        Ok(unsafe { OwnedFd::from_raw_fd(fd) })
    117    }
    118
    119    /// Creates a new `OwnedFd` instance that shares the same underlying file
    120    /// description as the existing `BorrowedFd` instance.
    121    #[cfg(any(target_arch = "wasm32", target_os = "hermit", target_os = "trusty"))]
    122    #[stable(feature = "io_safety", since = "1.63.0")]
    123    pub fn try_clone_to_owned(&self) -> crate::io::Result<OwnedFd> {
    124        Err(crate::io::Error::UNSUPPORTED_PLATFORM)
    125    }
    126}
    127
    128#[stable(feature = "io_safety", since = "1.63.0")]
    129impl AsRawFd for BorrowedFd<'_> {
    130    #[inline]
    131    fn as_raw_fd(&self) -> RawFd {
    132        self.fd.as_inner()
    133    }
    134}
    135
    136#[stable(feature = "io_safety", since = "1.63.0")]
    137impl AsRawFd for OwnedFd {
    138    #[inline]
    139    fn as_raw_fd(&self) -> RawFd {
    140        self.fd.as_inner()
    141    }
    142}
    143
    144#[stable(feature = "io_safety", since = "1.63.0")]
    145impl IntoRawFd for OwnedFd {
    146    #[inline]
    147    fn into_raw_fd(self) -> RawFd {
    148        ManuallyDrop::new(self).fd.as_inner()
    149    }
    150}
    151
    152#[stable(feature = "io_safety", since = "1.63.0")]
    153impl FromRawFd for OwnedFd {
    154    /// Constructs a new instance of `Self` from the given raw file descriptor.
    155    ///
    156    /// # Safety
    157    ///
    158    /// The resource pointed to by `fd` must be open and suitable for assuming
    159    /// [ownership][io-safety]. The resource must not require any cleanup other than `close`.
    160    ///
    161    /// [io-safety]: io#io-safety
    162    #[inline]
    163    #[track_caller]
    164    unsafe fn from_raw_fd(fd: RawFd) -> Self {
    165        Self { fd: ValidRawFd::new(fd).expect("fd != -1") }
    166    }
    167}
    168
    169#[stable(feature = "io_safety", since = "1.63.0")]
    170impl Drop for OwnedFd {
    171    #[inline]
    172    fn drop(&mut self) {
    173        unsafe {
    174            // Note that errors are ignored when closing a file descriptor. According to POSIX 2024,
    175            // we can and indeed should retry `close` on `EINTR`
    176            // (https://pubs.opengroup.org/onlinepubs/9799919799.2024edition/functions/close.html),
    177            // but it is not clear yet how well widely-used implementations are conforming with this
    178            // mandate since older versions of POSIX left the state of the FD after an `EINTR`
    179            // unspecified. Ignoring errors is "fine" because some of the major Unices (in
    180            // particular, Linux) do make sure to always close the FD, even when `close()` is
    181            // interrupted, and the scenario is rare to begin with. If we retried on a
    182            // not-POSIX-compliant implementation, the consequences could be really bad since we may
    183            // close the wrong FD. Helpful link to an epic discussion by POSIX workgroup that led to
    184            // the latest POSIX wording: http://austingroupbugs.net/view.php?id=529
    185            #[cfg(not(target_os = "hermit"))]
    186            {
    187                #[cfg(unix)]
    188                crate::sys::fs::debug_assert_fd_is_open(self.fd.as_inner());
    189
    190                let _ = libc::close(self.fd.as_inner());
    191            }
    192            #[cfg(target_os = "hermit")]
    193            let _ = hermit_abi::close(self.fd.as_inner());
    194        }
    195    }
    196}
    197
    198#[stable(feature = "io_safety", since = "1.63.0")]
    199impl fmt::Debug for BorrowedFd<'_> {
    200    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
    201        f.debug_struct("BorrowedFd").field("fd", &self.fd).finish()
    202    }
    203}
    204
    205#[stable(feature = "io_safety", since = "1.63.0")]
    206impl fmt::Debug for OwnedFd {
    207    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
    208        f.debug_struct("OwnedFd").field("fd", &self.fd).finish()
    209    }
    210}
    211
    212macro_rules! impl_is_terminal {
    213    ($($t:ty),*$(,)?) => {$(
    214        #[unstable(feature = "sealed", issue = "none")]
    215        impl crate::sealed::Sealed for $t {}
    216
    217        #[stable(feature = "is_terminal", since = "1.70.0")]
    218        impl crate::io::IsTerminal for $t {
    219            #[inline]
    220            fn is_terminal(&self) -> bool {
    221                crate::sys::io::is_terminal(self)
    222            }
    223        }
    224    )*}
    225}
    226
    227impl_is_terminal!(BorrowedFd<'_>, OwnedFd);
    228
    229/// A trait to borrow the file descriptor from an underlying object.
    230///
    231/// This is only available on unix platforms and must be imported in order to
    232/// call the method. Windows platforms have a corresponding `AsHandle` and
    233/// `AsSocket` set of traits.
    234#[stable(feature = "io_safety", since = "1.63.0")]
    235pub trait AsFd {
    236    /// Borrows the file descriptor.
    237    ///
    238    /// # Example
    239    ///
    240    /// ```rust,no_run
    241    /// use std::fs::File;
    242    /// # use std::io;
    243    /// # #[cfg(any(unix, target_os = "wasi"))]
    244    /// # use std::os::fd::{AsFd, BorrowedFd};
    245    ///
    246    /// let mut f = File::open("foo.txt")?;
    247    /// # #[cfg(any(unix, target_os = "wasi"))]
    248    /// let borrowed_fd: BorrowedFd<'_> = f.as_fd();
    249    /// # Ok::<(), io::Error>(())
    250    /// ```
    251    #[stable(feature = "io_safety", since = "1.63.0")]
    252    fn as_fd(&self) -> BorrowedFd<'_>;
    253}
    254
    255#[stable(feature = "io_safety", since = "1.63.0")]
    256impl<T: AsFd + ?Sized> AsFd for &T {
    257    #[inline]
    258    fn as_fd(&self) -> BorrowedFd<'_> {
    259        T::as_fd(self)
    260    }
    261}
    262
    263#[stable(feature = "io_safety", since = "1.63.0")]
    264impl<T: AsFd + ?Sized> AsFd for &mut T {
    265    #[inline]
    266    fn as_fd(&self) -> BorrowedFd<'_> {
    267        T::as_fd(self)
    268    }
    269}
    270
    271#[stable(feature = "io_safety", since = "1.63.0")]
    272impl AsFd for BorrowedFd<'_> {
    273    #[inline]
    274    fn as_fd(&self) -> BorrowedFd<'_> {
    275        *self
    276    }
    277}
    278
    279#[stable(feature = "io_safety", since = "1.63.0")]
    280impl AsFd for OwnedFd {
    281    #[inline]
    282    fn as_fd(&self) -> BorrowedFd<'_> {
    283        // Safety: `OwnedFd` and `BorrowedFd` have the same validity
    284        // invariants, and the `BorrowedFd` is bounded by the lifetime
    285        // of `&self`.
    286        unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
    287    }
    288}
    289
    290#[stable(feature = "io_safety", since = "1.63.0")]
    291#[cfg(not(target_os = "trusty"))]
    292impl AsFd for fs::File {
    293    #[inline]
    294    fn as_fd(&self) -> BorrowedFd<'_> {
    295        self.as_inner().as_fd()
    296    }
    297}
    298
    299#[stable(feature = "io_safety", since = "1.63.0")]
    300#[cfg(not(target_os = "trusty"))]
    301impl From<fs::File> for OwnedFd {
    302    /// Takes ownership of a [`File`](fs::File)'s underlying file descriptor.
    303    #[inline]
    304    fn from(file: fs::File) -> OwnedFd {
    305        file.into_inner().into_inner().into_inner()
    306    }
    307}
    308
    309#[stable(feature = "io_safety", since = "1.63.0")]
    310#[cfg(not(target_os = "trusty"))]
    311impl From<OwnedFd> for fs::File {
    312    /// Returns a [`File`](fs::File) that takes ownership of the given
    313    /// file descriptor.
    314    #[inline]
    315    fn from(owned_fd: OwnedFd) -> Self {
    316        Self::from_inner(FromInner::from_inner(FromInner::from_inner(owned_fd)))
    317    }
    318}
    319
    320#[stable(feature = "io_safety", since = "1.63.0")]
    321#[cfg(not(target_os = "trusty"))]
    322impl AsFd for crate::net::TcpStream {
    323    #[inline]
    324    fn as_fd(&self) -> BorrowedFd<'_> {
    325        self.as_inner().socket().as_fd()
    326    }
    327}
    328
    329#[stable(feature = "io_safety", since = "1.63.0")]
    330#[cfg(not(target_os = "trusty"))]
    331impl From<crate::net::TcpStream> for OwnedFd {
    332    /// Takes ownership of a [`TcpStream`](crate::net::TcpStream)'s socket file descriptor.
    333    #[inline]
    334    fn from(tcp_stream: crate::net::TcpStream) -> OwnedFd {
    335        tcp_stream.into_inner().into_socket().into_inner().into_inner().into()
    336    }
    337}
    338
    339#[stable(feature = "io_safety", since = "1.63.0")]
    340#[cfg(not(target_os = "trusty"))]
    341impl From<OwnedFd> for crate::net::TcpStream {
    342    #[inline]
    343    fn from(owned_fd: OwnedFd) -> Self {
    344        Self::from_inner(FromInner::from_inner(FromInner::from_inner(FromInner::from_inner(
    345            owned_fd,
    346        ))))
    347    }
    348}
    349
    350#[stable(feature = "io_safety", since = "1.63.0")]
    351#[cfg(not(target_os = "trusty"))]
    352impl AsFd for crate::net::TcpListener {
    353    #[inline]
    354    fn as_fd(&self) -> BorrowedFd<'_> {
    355        self.as_inner().socket().as_fd()
    356    }
    357}
    358
    359#[stable(feature = "io_safety", since = "1.63.0")]
    360#[cfg(not(target_os = "trusty"))]
    361impl From<crate::net::TcpListener> for OwnedFd {
    362    /// Takes ownership of a [`TcpListener`](crate::net::TcpListener)'s socket file descriptor.
    363    #[inline]
    364    fn from(tcp_listener: crate::net::TcpListener) -> OwnedFd {
    365        tcp_listener.into_inner().into_socket().into_inner().into_inner().into()
    366    }
    367}
    368
    369#[stable(feature = "io_safety", since = "1.63.0")]
    370#[cfg(not(target_os = "trusty"))]
    371impl From<OwnedFd> for crate::net::TcpListener {
    372    #[inline]
    373    fn from(owned_fd: OwnedFd) -> Self {
    374        Self::from_inner(FromInner::from_inner(FromInner::from_inner(FromInner::from_inner(
    375            owned_fd,
    376        ))))
    377    }
    378}
    379
    380#[stable(feature = "io_safety", since = "1.63.0")]
    381#[cfg(not(target_os = "trusty"))]
    382impl AsFd for crate::net::UdpSocket {
    383    #[inline]
    384    fn as_fd(&self) -> BorrowedFd<'_> {
    385        self.as_inner().socket().as_fd()
    386    }
    387}
    388
    389#[stable(feature = "io_safety", since = "1.63.0")]
    390#[cfg(not(target_os = "trusty"))]
    391impl From<crate::net::UdpSocket> for OwnedFd {
    392    /// Takes ownership of a [`UdpSocket`](crate::net::UdpSocket)'s file descriptor.
    393    #[inline]
    394    fn from(udp_socket: crate::net::UdpSocket) -> OwnedFd {
    395        udp_socket.into_inner().into_socket().into_inner().into_inner().into()
    396    }
    397}
    398
    399#[stable(feature = "io_safety", since = "1.63.0")]
    400#[cfg(not(target_os = "trusty"))]
    401impl From<OwnedFd> for crate::net::UdpSocket {
    402    #[inline]
    403    fn from(owned_fd: OwnedFd) -> Self {
    404        Self::from_inner(FromInner::from_inner(FromInner::from_inner(FromInner::from_inner(
    405            owned_fd,
    406        ))))
    407    }
    408}
    409
    410#[stable(feature = "asfd_ptrs", since = "1.64.0")]
    411/// This impl allows implementing traits that require `AsFd` on Arc.
    412/// ```
    413/// # #[cfg(any(unix, target_os = "wasi"))] mod group_cfg {
    414/// # #[cfg(target_os = "wasi")]
    415/// # use std::os::wasi::io::AsFd;
    416/// # #[cfg(unix)]
    417/// # use std::os::unix::io::AsFd;
    418/// use std::net::UdpSocket;
    419/// use std::sync::Arc;
    420///
    421/// trait MyTrait: AsFd {}
    422/// impl MyTrait for Arc<UdpSocket> {}
    423/// impl MyTrait for Box<UdpSocket> {}
    424/// # }
    425/// ```
    426impl<T: AsFd + ?Sized> AsFd for crate::sync::Arc<T> {
    427    #[inline]
    428    fn as_fd(&self) -> BorrowedFd<'_> {
    429        (**self).as_fd()
    430    }
    431}
    432
    433#[stable(feature = "asfd_rc", since = "1.69.0")]
    434impl<T: AsFd + ?Sized> AsFd for crate::rc::Rc<T> {
    435    #[inline]
    436    fn as_fd(&self) -> BorrowedFd<'_> {
    437        (**self).as_fd()
    438    }
    439}
    440
    441#[unstable(feature = "unique_rc_arc", issue = "112566")]
    442impl<T: AsFd + ?Sized> AsFd for crate::rc::UniqueRc<T> {
    443    #[inline]
    444    fn as_fd(&self) -> BorrowedFd<'_> {
    445        (**self).as_fd()
    446    }
    447}
    448
    449#[stable(feature = "asfd_ptrs", since = "1.64.0")]
    450impl<T: AsFd + ?Sized> AsFd for Box<T> {
    451    #[inline]
    452    fn as_fd(&self) -> BorrowedFd<'_> {
    453        (**self).as_fd()
    454    }
    455}
    456
    457#[stable(feature = "io_safety", since = "1.63.0")]
    458impl AsFd for io::Stdin {
    459    #[inline]
    460    fn as_fd(&self) -> BorrowedFd<'_> {
    461        unsafe { BorrowedFd::borrow_raw(0) }
    462    }
    463}
    464
    465#[stable(feature = "io_safety", since = "1.63.0")]
    466impl<'a> AsFd for io::StdinLock<'a> {
    467    #[inline]
    468    fn as_fd(&self) -> BorrowedFd<'_> {
    469        // SAFETY: user code should not close stdin out from under the standard library
    470        unsafe { BorrowedFd::borrow_raw(0) }
    471    }
    472}
    473
    474#[stable(feature = "io_safety", since = "1.63.0")]
    475impl AsFd for io::Stdout {
    476    #[inline]
    477    fn as_fd(&self) -> BorrowedFd<'_> {
    478        unsafe { BorrowedFd::borrow_raw(1) }
    479    }
    480}
    481
    482#[stable(feature = "io_safety", since = "1.63.0")]
    483impl<'a> AsFd for io::StdoutLock<'a> {
    484    #[inline]
    485    fn as_fd(&self) -> BorrowedFd<'_> {
    486        // SAFETY: user code should not close stdout out from under the standard library
    487        unsafe { BorrowedFd::borrow_raw(1) }
    488    }
    489}
    490
    491#[stable(feature = "io_safety", since = "1.63.0")]
    492impl AsFd for io::Stderr {
    493    #[inline]
    494    fn as_fd(&self) -> BorrowedFd<'_> {
    495        unsafe { BorrowedFd::borrow_raw(2) }
    496    }
    497}
    498
    499#[stable(feature = "io_safety", since = "1.63.0")]
    500impl<'a> AsFd for io::StderrLock<'a> {
    501    #[inline]
    502    fn as_fd(&self) -> BorrowedFd<'_> {
    503        // SAFETY: user code should not close stderr out from under the standard library
    504        unsafe { BorrowedFd::borrow_raw(2) }
    505    }
    506}
    507
    508#[stable(feature = "anonymous_pipe", since = "1.87.0")]
    509#[cfg(not(target_os = "trusty"))]
    510impl AsFd for io::PipeReader {
    511    fn as_fd(&self) -> BorrowedFd<'_> {
    512        self.0.as_fd()
    513    }
    514}
    515
    516#[stable(feature = "anonymous_pipe", since = "1.87.0")]
    517#[cfg(not(target_os = "trusty"))]
    518impl From<io::PipeReader> for OwnedFd {
    519    fn from(pipe: io::PipeReader) -> Self {
    520        pipe.0.into_inner()
    521    }
    522}
    523
    524#[stable(feature = "anonymous_pipe", since = "1.87.0")]
    525#[cfg(not(target_os = "trusty"))]
    526impl AsFd for io::PipeWriter {
    527    fn as_fd(&self) -> BorrowedFd<'_> {
    528        self.0.as_fd()
    529    }
    530}
    531
    532#[stable(feature = "anonymous_pipe", since = "1.87.0")]
    533#[cfg(not(target_os = "trusty"))]
    534impl From<io::PipeWriter> for OwnedFd {
    535    fn from(pipe: io::PipeWriter) -> Self {
    536        pipe.0.into_inner()
    537    }
    538}
    539
    540#[stable(feature = "anonymous_pipe", since = "1.87.0")]
    541#[cfg(not(target_os = "trusty"))]
    542impl From<OwnedFd> for io::PipeReader {
    543    fn from(owned_fd: OwnedFd) -> Self {
    544        Self(FromInner::from_inner(owned_fd))
    545    }
    546}
    547
    548#[stable(feature = "anonymous_pipe", since = "1.87.0")]
    549#[cfg(not(target_os = "trusty"))]
    550impl From<OwnedFd> for io::PipeWriter {
    551    fn from(owned_fd: OwnedFd) -> Self {
    552        Self(FromInner::from_inner(owned_fd))
    553    }
    554}