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