xref: /third_party/rust/crates/rustix/src/io/fd/owned.rs (revision b8a62b91)
1//! The following is derived from Rust's
2//! library/std/src/os/fd/owned.rs at revision
3//! fa68e73e9947be8ffc5b3b46d899e4953a44e7e9.
4//!
5//! Owned and borrowed Unix-like file descriptors.
6
7#![cfg_attr(staged_api, unstable(feature = "io_safety", issue = "87074"))]
8#![deny(unsafe_op_in_unsafe_fn)]
9#![allow(unsafe_code)]
10
11use super::raw::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
12use crate::io::close;
13use core::fmt;
14use core::marker::PhantomData;
15use core::mem::forget;
16
17/// A borrowed file descriptor.
18///
19/// This has a lifetime parameter to tie it to the lifetime of something that
20/// owns the file descriptor.
21///
22/// This uses `repr(transparent)` and has the representation of a host file
23/// descriptor, so it can be used in FFI in places where a file descriptor is
24/// passed as an argument, it is not captured or consumed, and it never has the
25/// value `-1`.
26///
27/// This type's `.to_owned()` implementation returns another `BorrowedFd`
28/// rather than an `OwnedFd`. It just makes a trivial copy of the raw file
29/// descriptor, which is then borrowed under the same lifetime.
30#[derive(Copy, Clone)]
31#[repr(transparent)]
32#[cfg_attr(rustc_attrs, rustc_layout_scalar_valid_range_start(0))]
33// libstd/os/raw/mod.rs assures me that every libstd-supported platform has a
34// 32-bit c_int. Below is -2, in two's complement, but that only works out
35// because c_int is 32 bits.
36#[cfg_attr(rustc_attrs, rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE))]
37#[cfg_attr(staged_api, unstable(feature = "io_safety", issue = "87074"))]
38#[cfg_attr(rustc_attrs, rustc_nonnull_optimization_guaranteed)]
39pub struct BorrowedFd<'fd> {
40    fd: RawFd,
41    _phantom: PhantomData<&'fd OwnedFd>,
42}
43
44/// An owned file descriptor.
45///
46/// This closes the file descriptor on drop.
47///
48/// This uses `repr(transparent)` and has the representation of a host file
49/// descriptor, so it can be used in FFI in places where a file descriptor is
50/// passed as a consumed argument or returned as an owned value, and it never
51/// has the value `-1`.
52#[repr(transparent)]
53#[cfg_attr(rustc_attrs, rustc_layout_scalar_valid_range_start(0))]
54// libstd/os/raw/mod.rs assures me that every libstd-supported platform has a
55// 32-bit c_int. Below is -2, in two's complement, but that only works out
56// because c_int is 32 bits.
57#[cfg_attr(rustc_attrs, rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE))]
58#[cfg_attr(staged_api, unstable(feature = "io_safety", issue = "87074"))]
59#[cfg_attr(rustc_attrs, rustc_nonnull_optimization_guaranteed)]
60pub struct OwnedFd {
61    fd: RawFd,
62}
63
64impl BorrowedFd<'_> {
65    /// Return a `BorrowedFd` holding the given raw file descriptor.
66    ///
67    /// # Safety
68    ///
69    /// The resource pointed to by `fd` must remain open for the duration of
70    /// the returned `BorrowedFd`, and it must not have the value `-1`.
71    #[inline]
72    #[cfg_attr(staged_api, unstable(feature = "io_safety", issue = "87074"))]
73    pub const unsafe fn borrow_raw(fd: RawFd) -> Self {
74        assert!(fd != u32::MAX as RawFd);
75        // 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)
76        #[allow(unused_unsafe)]
77        unsafe {
78            Self {
79                fd,
80                _phantom: PhantomData,
81            }
82        }
83    }
84}
85
86impl OwnedFd {
87    /// Creates a new `OwnedFd` instance that shares the same underlying file handle
88    /// as the existing `OwnedFd` instance.
89    #[cfg(not(target_arch = "wasm32"))]
90    pub fn try_clone(&self) -> crate::io::Result<Self> {
91        // We want to atomically duplicate this file descriptor and set the
92        // CLOEXEC flag, and currently that's done via F_DUPFD_CLOEXEC. This
93        // is a POSIX flag that was added to Linux in 2.6.24.
94        #[cfg(not(target_os = "espidf"))]
95        let fd = crate::io::fcntl_dupfd_cloexec(self, 0)?;
96
97        // For ESP-IDF, F_DUPFD is used instead, because the CLOEXEC semantics
98        // will never be supported, as this is a bare metal framework with
99        // no capabilities for multi-process execution. While F_DUPFD is also
100        // not supported yet, it might be (currently it returns ENOSYS).
101        #[cfg(target_os = "espidf")]
102        let fd = crate::io::fcntl_dupfd(self)?;
103
104        Ok(fd.into())
105    }
106
107    #[cfg(target_arch = "wasm32")]
108    pub fn try_clone(&self) -> crate::io::Result<Self> {
109        Err(crate::io::const_io_error!(
110            crate::io::ErrorKind::Unsupported,
111            "operation not supported on WASI yet",
112        ))
113    }
114}
115
116#[cfg_attr(staged_api, unstable(feature = "io_safety", issue = "87074"))]
117impl AsRawFd for BorrowedFd<'_> {
118    #[inline]
119    fn as_raw_fd(&self) -> RawFd {
120        self.fd
121    }
122}
123
124#[cfg_attr(staged_api, unstable(feature = "io_safety", issue = "87074"))]
125impl AsRawFd for OwnedFd {
126    #[inline]
127    fn as_raw_fd(&self) -> RawFd {
128        self.fd
129    }
130}
131
132#[cfg_attr(staged_api, unstable(feature = "io_safety", issue = "87074"))]
133impl IntoRawFd for OwnedFd {
134    #[inline]
135    fn into_raw_fd(self) -> RawFd {
136        let fd = self.fd;
137        forget(self);
138        fd
139    }
140}
141
142#[cfg_attr(staged_api, unstable(feature = "io_safety", issue = "87074"))]
143impl FromRawFd for OwnedFd {
144    /// Constructs a new instance of `Self` from the given raw file descriptor.
145    ///
146    /// # Safety
147    ///
148    /// The resource pointed to by `fd` must be open and suitable for assuming
149    /// ownership. The resource must not require any cleanup other than `close`.
150    #[inline]
151    unsafe fn from_raw_fd(fd: RawFd) -> Self {
152        assert_ne!(fd, u32::MAX as RawFd);
153        // 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)
154        #[allow(unused_unsafe)]
155        unsafe {
156            Self { fd }
157        }
158    }
159}
160
161#[cfg_attr(staged_api, unstable(feature = "io_safety", issue = "87074"))]
162impl Drop for OwnedFd {
163    #[inline]
164    fn drop(&mut self) {
165        unsafe {
166            // Errors are ignored when closing a file descriptor. The reason
167            // for this is that if an error occurs we don't actually know if
168            // the file descriptor was closed or not, and if we retried (for
169            // something like EINTR), we might close another valid file
170            // descriptor opened after we closed ours.
171            let _ = close(self.fd as _);
172        }
173    }
174}
175
176#[cfg_attr(staged_api, unstable(feature = "io_safety", issue = "87074"))]
177impl fmt::Debug for BorrowedFd<'_> {
178    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
179        f.debug_struct("BorrowedFd").field("fd", &self.fd).finish()
180    }
181}
182
183#[cfg_attr(staged_api, unstable(feature = "io_safety", issue = "87074"))]
184impl fmt::Debug for OwnedFd {
185    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
186        f.debug_struct("OwnedFd").field("fd", &self.fd).finish()
187    }
188}
189
190/// A trait to borrow the file descriptor from an underlying object.
191///
192/// This is only available on unix platforms and must be imported in order to
193/// call the method. Windows platforms have a corresponding `AsHandle` and
194/// `AsSocket` set of traits.
195#[cfg_attr(staged_api, unstable(feature = "io_safety", issue = "87074"))]
196pub trait AsFd {
197    /// Borrows the file descriptor.
198    ///
199    /// # Example
200    ///
201    /// ```rust,no_run
202    /// # #![feature(io_safety)]
203    /// use std::fs::File;
204    /// # use std::io;
205    /// # #[cfg(target_os = "wasi")]
206    /// # use std::os::wasi::io::{AsFd, BorrowedFd};
207    /// # #[cfg(unix)]
208    /// # use std::os::unix::io::{AsFd, BorrowedFd};
209    ///
210    /// let mut f = File::open("foo.txt")?;
211    /// # #[cfg(any(unix, target_os = "wasi"))]
212    /// let borrowed_fd: BorrowedFd<'_> = f.as_fd();
213    /// # Ok::<(), io::Error>(())
214    /// ```
215    #[cfg_attr(staged_api, unstable(feature = "io_safety", issue = "87074"))]
216    fn as_fd(&self) -> BorrowedFd<'_>;
217}
218
219#[cfg_attr(staged_api, unstable(feature = "io_safety", issue = "87074"))]
220impl<T: AsFd> AsFd for &T {
221    #[inline]
222    fn as_fd(&self) -> BorrowedFd<'_> {
223        T::as_fd(self)
224    }
225}
226
227#[cfg_attr(staged_api, unstable(feature = "io_safety", issue = "87074"))]
228impl<T: AsFd> AsFd for &mut T {
229    #[inline]
230    fn as_fd(&self) -> BorrowedFd<'_> {
231        T::as_fd(self)
232    }
233}
234
235#[cfg_attr(staged_api, unstable(feature = "io_safety", issue = "87074"))]
236impl AsFd for BorrowedFd<'_> {
237    #[inline]
238    fn as_fd(&self) -> BorrowedFd<'_> {
239        *self
240    }
241}
242
243#[cfg_attr(staged_api, unstable(feature = "io_safety", issue = "87074"))]
244impl AsFd for OwnedFd {
245    #[inline]
246    fn as_fd(&self) -> BorrowedFd<'_> {
247        // Safety: `OwnedFd` and `BorrowedFd` have the same validity
248        // invariants, and the `BorrowedFd` is bounded by the lifetime
249        // of `&self`.
250        unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
251    }
252}
253