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