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