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