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