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 11 use super::raw::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; 12 use crate::io::close; 13 use core::fmt; 14 use core::marker::PhantomData; 15 use 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)] 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. 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)] 60 pub struct OwnedFd { 61 fd: RawFd, 62 } 63 64 impl 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"))] borrow_raw(fd: RawFd) -> Self73 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 86 impl 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"))] try_clone(&self) -> crate::io::Result<Self>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")] try_clone(&self) -> crate::io::Result<Self>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"))] 117 impl AsRawFd for BorrowedFd<'_> { 118 #[inline] as_raw_fd(&self) -> RawFd119 fn as_raw_fd(&self) -> RawFd { 120 self.fd 121 } 122 } 123 124 #[cfg_attr(staged_api, unstable(feature = "io_safety", issue = "87074"))] 125 impl AsRawFd for OwnedFd { 126 #[inline] as_raw_fd(&self) -> RawFd127 fn as_raw_fd(&self) -> RawFd { 128 self.fd 129 } 130 } 131 132 #[cfg_attr(staged_api, unstable(feature = "io_safety", issue = "87074"))] 133 impl IntoRawFd for OwnedFd { 134 #[inline] into_raw_fd(self) -> RawFd135 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"))] 143 impl 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] from_raw_fd(fd: RawFd) -> Self151 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"))] 162 impl Drop for OwnedFd { 163 #[inline] drop(&mut self)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"))] 177 impl fmt::Debug for BorrowedFd<'_> { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result178 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"))] 184 impl fmt::Debug for OwnedFd { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result185 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"))] 196 pub 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"))] as_fd(&self) -> BorrowedFd<'_>216 fn as_fd(&self) -> BorrowedFd<'_>; 217 } 218 219 #[cfg_attr(staged_api, unstable(feature = "io_safety", issue = "87074"))] 220 impl<T: AsFd> AsFd for &T { 221 #[inline] as_fd(&self) -> BorrowedFd<'_>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"))] 228 impl<T: AsFd> AsFd for &mut T { 229 #[inline] as_fd(&self) -> BorrowedFd<'_>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"))] 236 impl AsFd for BorrowedFd<'_> { 237 #[inline] as_fd(&self) -> BorrowedFd<'_>238 fn as_fd(&self) -> BorrowedFd<'_> { 239 *self 240 } 241 } 242 243 #[cfg_attr(staged_api, unstable(feature = "io_safety", issue = "87074"))] 244 impl AsFd for OwnedFd { 245 #[inline] as_fd(&self) -> BorrowedFd<'_>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