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 | use super::raw::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; |
7 | use crate::fmt; |
8 | use crate::fs; |
9 | use crate::io; |
10 | use crate::marker::PhantomData; |
11 | use crate::mem::forget; |
12 | #[cfg (not(any(target_arch = "wasm32" , target_env = "sgx" , target_os = "hermit" )))] |
13 | use crate::sys::cvt; |
14 | use crate::sys_common::{AsInner, FromInner, IntoInner}; |
15 | |
16 | /// A borrowed file descriptor. |
17 | /// |
18 | /// This has a lifetime parameter to tie it to the lifetime of something that owns the file |
19 | /// descriptor. For the duration of that lifetime, it is guaranteed that nobody will close the file |
20 | /// descriptor. |
21 | /// |
22 | /// This uses `repr(transparent)` and has the representation of a host file |
23 | /// descriptor, so it can be used in FFI in places where a file descriptor is |
24 | /// passed as an argument, it is not captured or consumed, and it never has the |
25 | /// value `-1`. |
26 | /// |
27 | /// This type's `.to_owned()` implementation returns another `BorrowedFd` |
28 | /// rather than an `OwnedFd`. It just makes a trivial copy of the raw file |
29 | /// descriptor, which is then borrowed under the same lifetime. |
30 | #[derive (Copy, Clone)] |
31 | #[repr (transparent)] |
32 | #[rustc_layout_scalar_valid_range_start (0)] |
33 | // libstd/os/raw/mod.rs assures me that every libstd-supported platform has a |
34 | // 32-bit c_int. Below is -2, in two's complement, but that only works out |
35 | // because c_int is 32 bits. |
36 | #[rustc_layout_scalar_valid_range_end (0xFF_FF_FF_FE)] |
37 | #[rustc_nonnull_optimization_guaranteed ] |
38 | #[stable (feature = "io_safety" , since = "1.63.0" )] |
39 | pub struct BorrowedFd<'fd> { |
40 | fd: RawFd, |
41 | _phantom: PhantomData<&'fd OwnedFd>, |
42 | } |
43 | |
44 | /// An owned file descriptor. |
45 | /// |
46 | /// This closes the file descriptor on drop. It is guaranteed that nobody else will close the file |
47 | /// descriptor. |
48 | /// |
49 | /// This uses `repr(transparent)` and has the representation of a host file |
50 | /// descriptor, so it can be used in FFI in places where a file descriptor is |
51 | /// passed as a consumed argument or returned as an owned value, and it never |
52 | /// has the value `-1`. |
53 | #[repr (transparent)] |
54 | #[rustc_layout_scalar_valid_range_start (0)] |
55 | // libstd/os/raw/mod.rs assures me that every libstd-supported platform has a |
56 | // 32-bit c_int. Below is -2, in two's complement, but that only works out |
57 | // because c_int is 32 bits. |
58 | #[rustc_layout_scalar_valid_range_end (0xFF_FF_FF_FE)] |
59 | #[rustc_nonnull_optimization_guaranteed ] |
60 | #[stable (feature = "io_safety" , since = "1.63.0" )] |
61 | pub struct OwnedFd { |
62 | fd: RawFd, |
63 | } |
64 | |
65 | impl BorrowedFd<'_> { |
66 | /// Return a `BorrowedFd` holding the given raw file descriptor. |
67 | /// |
68 | /// # Safety |
69 | /// |
70 | /// The resource pointed to by `fd` must remain open for the duration of |
71 | /// the returned `BorrowedFd`, and it must not have the value `-1`. |
72 | #[inline ] |
73 | #[rustc_const_stable (feature = "io_safety" , since = "1.63.0" )] |
74 | #[stable (feature = "io_safety" , since = "1.63.0" )] |
75 | pub const unsafe fn borrow_raw(fd: RawFd) -> Self { |
76 | assert!(fd != u32::MAX as RawFd); |
77 | // 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) |
78 | unsafe { Self { fd, _phantom: PhantomData } } |
79 | } |
80 | } |
81 | |
82 | impl OwnedFd { |
83 | /// Creates a new `OwnedFd` instance that shares the same underlying file |
84 | /// description as the existing `OwnedFd` instance. |
85 | #[stable (feature = "io_safety" , since = "1.63.0" )] |
86 | pub fn try_clone(&self) -> crate::io::Result<Self> { |
87 | self.as_fd().try_clone_to_owned() |
88 | } |
89 | } |
90 | |
91 | impl BorrowedFd<'_> { |
92 | /// Creates a new `OwnedFd` instance that shares the same underlying file |
93 | /// description as the existing `BorrowedFd` instance. |
94 | #[cfg (not(any(target_arch = "wasm32" , target_os = "hermit" )))] |
95 | #[stable (feature = "io_safety" , since = "1.63.0" )] |
96 | pub fn try_clone_to_owned(&self) -> crate::io::Result<OwnedFd> { |
97 | // We want to atomically duplicate this file descriptor and set the |
98 | // CLOEXEC flag, and currently that's done via F_DUPFD_CLOEXEC. This |
99 | // is a POSIX flag that was added to Linux in 2.6.24. |
100 | #[cfg (not(any(target_os = "espidf" , target_os = "vita" )))] |
101 | let cmd = libc::F_DUPFD_CLOEXEC; |
102 | |
103 | // For ESP-IDF, F_DUPFD is used instead, because the CLOEXEC semantics |
104 | // will never be supported, as this is a bare metal framework with |
105 | // no capabilities for multi-process execution. While F_DUPFD is also |
106 | // not supported yet, it might be (currently it returns ENOSYS). |
107 | #[cfg (any(target_os = "espidf" , target_os = "vita" ))] |
108 | let cmd = libc::F_DUPFD; |
109 | |
110 | // Avoid using file descriptors below 3 as they are used for stdio |
111 | let fd = cvt(unsafe { libc::fcntl(self.as_raw_fd(), cmd, 3) })?; |
112 | Ok(unsafe { OwnedFd::from_raw_fd(fd) }) |
113 | } |
114 | |
115 | /// Creates a new `OwnedFd` instance that shares the same underlying file |
116 | /// description as the existing `BorrowedFd` instance. |
117 | #[cfg (any(target_arch = "wasm32" , target_os = "hermit" ))] |
118 | #[stable (feature = "io_safety" , since = "1.63.0" )] |
119 | pub fn try_clone_to_owned(&self) -> crate::io::Result<OwnedFd> { |
120 | Err(crate::io::Error::UNSUPPORTED_PLATFORM) |
121 | } |
122 | } |
123 | |
124 | #[stable (feature = "io_safety" , since = "1.63.0" )] |
125 | impl AsRawFd for BorrowedFd<'_> { |
126 | #[inline ] |
127 | fn as_raw_fd(&self) -> RawFd { |
128 | self.fd |
129 | } |
130 | } |
131 | |
132 | #[stable (feature = "io_safety" , since = "1.63.0" )] |
133 | impl AsRawFd for OwnedFd { |
134 | #[inline ] |
135 | fn as_raw_fd(&self) -> RawFd { |
136 | self.fd |
137 | } |
138 | } |
139 | |
140 | #[stable (feature = "io_safety" , since = "1.63.0" )] |
141 | impl IntoRawFd for OwnedFd { |
142 | #[inline ] |
143 | fn into_raw_fd(self) -> RawFd { |
144 | let fd: i32 = self.fd; |
145 | forget(self); |
146 | fd |
147 | } |
148 | } |
149 | |
150 | #[stable (feature = "io_safety" , since = "1.63.0" )] |
151 | impl FromRawFd for OwnedFd { |
152 | /// Constructs a new instance of `Self` from the given raw file descriptor. |
153 | /// |
154 | /// # Safety |
155 | /// |
156 | /// The resource pointed to by `fd` must be open and suitable for assuming |
157 | /// [ownership][io-safety]. The resource must not require any cleanup other than `close`. |
158 | /// |
159 | /// [io-safety]: io#io-safety |
160 | #[inline ] |
161 | unsafe fn from_raw_fd(fd: RawFd) -> Self { |
162 | assert_ne!(fd, u32::MAX as RawFd); |
163 | // 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) |
164 | unsafe { Self { fd } } |
165 | } |
166 | } |
167 | |
168 | #[stable (feature = "io_safety" , since = "1.63.0" )] |
169 | impl Drop for OwnedFd { |
170 | #[inline ] |
171 | fn drop(&mut self) { |
172 | unsafe { |
173 | // Note that errors are ignored when closing a file descriptor. The |
174 | // reason for this is that if an error occurs we don't actually know if |
175 | // the file descriptor was closed or not, and if we retried (for |
176 | // something like EINTR), we might close another valid file descriptor |
177 | // opened after we closed ours. |
178 | #[cfg (not(target_os = "hermit" ))] |
179 | let _ = libc::close(self.fd); |
180 | #[cfg (target_os = "hermit" )] |
181 | let _ = hermit_abi::close(self.fd); |
182 | } |
183 | } |
184 | } |
185 | |
186 | #[stable (feature = "io_safety" , since = "1.63.0" )] |
187 | impl fmt::Debug for BorrowedFd<'_> { |
188 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
189 | f.debug_struct("BorrowedFd" ).field(name:"fd" , &self.fd).finish() |
190 | } |
191 | } |
192 | |
193 | #[stable (feature = "io_safety" , since = "1.63.0" )] |
194 | impl fmt::Debug for OwnedFd { |
195 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
196 | f.debug_struct("OwnedFd" ).field(name:"fd" , &self.fd).finish() |
197 | } |
198 | } |
199 | |
200 | macro_rules! impl_is_terminal { |
201 | ($($t:ty),*$(,)?) => {$( |
202 | #[unstable(feature = "sealed" , issue = "none" )] |
203 | impl crate::sealed::Sealed for $t {} |
204 | |
205 | #[stable(feature = "is_terminal" , since = "1.70.0" )] |
206 | impl crate::io::IsTerminal for $t { |
207 | #[inline] |
208 | fn is_terminal(&self) -> bool { |
209 | crate::sys::io::is_terminal(self) |
210 | } |
211 | } |
212 | )*} |
213 | } |
214 | |
215 | impl_is_terminal!(BorrowedFd<'_>, OwnedFd); |
216 | |
217 | /// A trait to borrow the file descriptor from an underlying object. |
218 | /// |
219 | /// This is only available on unix platforms and must be imported in order to |
220 | /// call the method. Windows platforms have a corresponding `AsHandle` and |
221 | /// `AsSocket` set of traits. |
222 | #[stable (feature = "io_safety" , since = "1.63.0" )] |
223 | pub trait AsFd { |
224 | /// Borrows the file descriptor. |
225 | /// |
226 | /// # Example |
227 | /// |
228 | /// ```rust,no_run |
229 | /// use std::fs::File; |
230 | /// # use std::io; |
231 | /// # #[cfg (any(unix, target_os = "wasi" ))] |
232 | /// # use std::os::fd::{AsFd, BorrowedFd}; |
233 | /// |
234 | /// let mut f = File::open("foo.txt" )?; |
235 | /// # #[cfg (any(unix, target_os = "wasi" ))] |
236 | /// let borrowed_fd: BorrowedFd<'_> = f.as_fd(); |
237 | /// # Ok::<(), io::Error>(()) |
238 | /// ``` |
239 | #[stable (feature = "io_safety" , since = "1.63.0" )] |
240 | fn as_fd(&self) -> BorrowedFd<'_>; |
241 | } |
242 | |
243 | #[stable (feature = "io_safety" , since = "1.63.0" )] |
244 | impl<T: AsFd + ?Sized> AsFd for &T { |
245 | #[inline ] |
246 | fn as_fd(&self) -> BorrowedFd<'_> { |
247 | T::as_fd(self) |
248 | } |
249 | } |
250 | |
251 | #[stable (feature = "io_safety" , since = "1.63.0" )] |
252 | impl<T: AsFd + ?Sized> AsFd for &mut T { |
253 | #[inline ] |
254 | fn as_fd(&self) -> BorrowedFd<'_> { |
255 | T::as_fd(self) |
256 | } |
257 | } |
258 | |
259 | #[stable (feature = "io_safety" , since = "1.63.0" )] |
260 | impl AsFd for BorrowedFd<'_> { |
261 | #[inline ] |
262 | fn as_fd(&self) -> BorrowedFd<'_> { |
263 | *self |
264 | } |
265 | } |
266 | |
267 | #[stable (feature = "io_safety" , since = "1.63.0" )] |
268 | impl AsFd for OwnedFd { |
269 | #[inline ] |
270 | fn as_fd(&self) -> BorrowedFd<'_> { |
271 | // Safety: `OwnedFd` and `BorrowedFd` have the same validity |
272 | // invariants, and the `BorrowedFd` is bounded by the lifetime |
273 | // of `&self`. |
274 | unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) } |
275 | } |
276 | } |
277 | |
278 | #[stable (feature = "io_safety" , since = "1.63.0" )] |
279 | impl AsFd for fs::File { |
280 | #[inline ] |
281 | fn as_fd(&self) -> BorrowedFd<'_> { |
282 | self.as_inner().as_fd() |
283 | } |
284 | } |
285 | |
286 | #[stable (feature = "io_safety" , since = "1.63.0" )] |
287 | impl From<fs::File> for OwnedFd { |
288 | /// Takes ownership of a [`File`](fs::File)'s underlying file descriptor. |
289 | #[inline ] |
290 | fn from(file: fs::File) -> OwnedFd { |
291 | file.into_inner().into_inner().into_inner() |
292 | } |
293 | } |
294 | |
295 | #[stable (feature = "io_safety" , since = "1.63.0" )] |
296 | impl From<OwnedFd> for fs::File { |
297 | /// Returns a [`File`](fs::File) that takes ownership of the given |
298 | /// file descriptor. |
299 | #[inline ] |
300 | fn from(owned_fd: OwnedFd) -> Self { |
301 | Self::from_inner(FromInner::from_inner(FromInner::from_inner(owned_fd))) |
302 | } |
303 | } |
304 | |
305 | #[stable (feature = "io_safety" , since = "1.63.0" )] |
306 | impl AsFd for crate::net::TcpStream { |
307 | #[inline ] |
308 | fn as_fd(&self) -> BorrowedFd<'_> { |
309 | self.as_inner().socket().as_fd() |
310 | } |
311 | } |
312 | |
313 | #[stable (feature = "io_safety" , since = "1.63.0" )] |
314 | impl From<crate::net::TcpStream> for OwnedFd { |
315 | /// Takes ownership of a [`TcpStream`](crate::net::TcpStream)'s socket file descriptor. |
316 | #[inline ] |
317 | fn from(tcp_stream: crate::net::TcpStream) -> OwnedFd { |
318 | tcp_stream.into_inner().into_socket().into_inner().into_inner().into() |
319 | } |
320 | } |
321 | |
322 | #[stable (feature = "io_safety" , since = "1.63.0" )] |
323 | impl From<OwnedFd> for crate::net::TcpStream { |
324 | #[inline ] |
325 | fn from(owned_fd: OwnedFd) -> Self { |
326 | Self::from_inner(FromInner::from_inner(FromInner::from_inner(FromInner::from_inner( |
327 | owned_fd, |
328 | )))) |
329 | } |
330 | } |
331 | |
332 | #[stable (feature = "io_safety" , since = "1.63.0" )] |
333 | impl AsFd for crate::net::TcpListener { |
334 | #[inline ] |
335 | fn as_fd(&self) -> BorrowedFd<'_> { |
336 | self.as_inner().socket().as_fd() |
337 | } |
338 | } |
339 | |
340 | #[stable (feature = "io_safety" , since = "1.63.0" )] |
341 | impl From<crate::net::TcpListener> for OwnedFd { |
342 | /// Takes ownership of a [`TcpListener`](crate::net::TcpListener)'s socket file descriptor. |
343 | #[inline ] |
344 | fn from(tcp_listener: crate::net::TcpListener) -> OwnedFd { |
345 | tcp_listener.into_inner().into_socket().into_inner().into_inner().into() |
346 | } |
347 | } |
348 | |
349 | #[stable (feature = "io_safety" , since = "1.63.0" )] |
350 | impl From<OwnedFd> for crate::net::TcpListener { |
351 | #[inline ] |
352 | fn from(owned_fd: OwnedFd) -> Self { |
353 | Self::from_inner(FromInner::from_inner(FromInner::from_inner(FromInner::from_inner( |
354 | owned_fd, |
355 | )))) |
356 | } |
357 | } |
358 | |
359 | #[stable (feature = "io_safety" , since = "1.63.0" )] |
360 | impl AsFd for crate::net::UdpSocket { |
361 | #[inline ] |
362 | fn as_fd(&self) -> BorrowedFd<'_> { |
363 | self.as_inner().socket().as_fd() |
364 | } |
365 | } |
366 | |
367 | #[stable (feature = "io_safety" , since = "1.63.0" )] |
368 | impl From<crate::net::UdpSocket> for OwnedFd { |
369 | /// Takes ownership of a [`UdpSocket`](crate::net::UdpSocket)'s file descriptor. |
370 | #[inline ] |
371 | fn from(udp_socket: crate::net::UdpSocket) -> OwnedFd { |
372 | udp_socket.into_inner().into_socket().into_inner().into_inner().into() |
373 | } |
374 | } |
375 | |
376 | #[stable (feature = "io_safety" , since = "1.63.0" )] |
377 | impl From<OwnedFd> for crate::net::UdpSocket { |
378 | #[inline ] |
379 | fn from(owned_fd: OwnedFd) -> Self { |
380 | Self::from_inner(FromInner::from_inner(FromInner::from_inner(FromInner::from_inner( |
381 | owned_fd, |
382 | )))) |
383 | } |
384 | } |
385 | |
386 | #[stable (feature = "asfd_ptrs" , since = "1.64.0" )] |
387 | /// This impl allows implementing traits that require `AsFd` on Arc. |
388 | /// ``` |
389 | /// # #[cfg (any(unix, target_os = "wasi" ))] mod group_cfg { |
390 | /// # #[cfg (target_os = "wasi" )] |
391 | /// # use std::os::wasi::io::AsFd; |
392 | /// # #[cfg (unix)] |
393 | /// # use std::os::unix::io::AsFd; |
394 | /// use std::net::UdpSocket; |
395 | /// use std::sync::Arc; |
396 | /// |
397 | /// trait MyTrait: AsFd {} |
398 | /// impl MyTrait for Arc<UdpSocket> {} |
399 | /// impl MyTrait for Box<UdpSocket> {} |
400 | /// # } |
401 | /// ``` |
402 | impl<T: AsFd + ?Sized> AsFd for crate::sync::Arc<T> { |
403 | #[inline ] |
404 | fn as_fd(&self) -> BorrowedFd<'_> { |
405 | (**self).as_fd() |
406 | } |
407 | } |
408 | |
409 | #[stable (feature = "asfd_rc" , since = "1.69.0" )] |
410 | impl<T: AsFd + ?Sized> AsFd for crate::rc::Rc<T> { |
411 | #[inline ] |
412 | fn as_fd(&self) -> BorrowedFd<'_> { |
413 | (**self).as_fd() |
414 | } |
415 | } |
416 | |
417 | #[stable (feature = "asfd_ptrs" , since = "1.64.0" )] |
418 | impl<T: AsFd + ?Sized> AsFd for Box<T> { |
419 | #[inline ] |
420 | fn as_fd(&self) -> BorrowedFd<'_> { |
421 | (**self).as_fd() |
422 | } |
423 | } |
424 | |
425 | #[stable (feature = "io_safety" , since = "1.63.0" )] |
426 | impl AsFd for io::Stdin { |
427 | #[inline ] |
428 | fn as_fd(&self) -> BorrowedFd<'_> { |
429 | unsafe { BorrowedFd::borrow_raw(fd:0) } |
430 | } |
431 | } |
432 | |
433 | #[stable (feature = "io_safety" , since = "1.63.0" )] |
434 | impl<'a> AsFd for io::StdinLock<'a> { |
435 | #[inline ] |
436 | fn as_fd(&self) -> BorrowedFd<'_> { |
437 | // SAFETY: user code should not close stdin out from under the standard library |
438 | unsafe { BorrowedFd::borrow_raw(fd:0) } |
439 | } |
440 | } |
441 | |
442 | #[stable (feature = "io_safety" , since = "1.63.0" )] |
443 | impl AsFd for io::Stdout { |
444 | #[inline ] |
445 | fn as_fd(&self) -> BorrowedFd<'_> { |
446 | unsafe { BorrowedFd::borrow_raw(fd:1) } |
447 | } |
448 | } |
449 | |
450 | #[stable (feature = "io_safety" , since = "1.63.0" )] |
451 | impl<'a> AsFd for io::StdoutLock<'a> { |
452 | #[inline ] |
453 | fn as_fd(&self) -> BorrowedFd<'_> { |
454 | // SAFETY: user code should not close stdout out from under the standard library |
455 | unsafe { BorrowedFd::borrow_raw(fd:1) } |
456 | } |
457 | } |
458 | |
459 | #[stable (feature = "io_safety" , since = "1.63.0" )] |
460 | impl AsFd for io::Stderr { |
461 | #[inline ] |
462 | fn as_fd(&self) -> BorrowedFd<'_> { |
463 | unsafe { BorrowedFd::borrow_raw(fd:2) } |
464 | } |
465 | } |
466 | |
467 | #[stable (feature = "io_safety" , since = "1.63.0" )] |
468 | impl<'a> AsFd for io::StderrLock<'a> { |
469 | #[inline ] |
470 | fn as_fd(&self) -> BorrowedFd<'_> { |
471 | // SAFETY: user code should not close stderr out from under the standard library |
472 | unsafe { BorrowedFd::borrow_raw(fd:2) } |
473 | } |
474 | } |
475 | |