Skip to main content

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