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