From 4e1100b8538543f7264b5740ea7dd26b9315c77f Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Mon, 7 Feb 2022 17:44:08 -0800 Subject: [PATCH 1/4] Add a `try_clone` method to `OwnedFd`. This corresponds to the `try_clone` function in sunfishcode/io-lifetimes#16 and rust-lang/rust#88794. --- src/io/owned_fd.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/io/owned_fd.rs b/src/io/owned_fd.rs index cc3f7604f..f40790557 100644 --- a/src/io/owned_fd.rs +++ b/src/io/owned_fd.rs @@ -27,6 +27,27 @@ pub struct OwnedFd { inner: ManuallyDrop, } +impl OwnedFd { + /// Creates a new `OwnedFd` instance that shares the same underlying file handle + /// as the existing `OwnedFd` instance. + pub fn try_clone(&self) -> std::io::Result { + // We want to atomically duplicate this file descriptor and set the + // CLOEXEC flag, and currently that's done via F_DUPFD_CLOEXEC. This + // is a POSIX flag that was added to Linux in 2.6.24. + #[cfg(not(target_os = "espidf"))] + let fd = crate::fs::fcntl_dupfd_cloexec(self, 0)?; + + // For ESP-IDF, F_DUPFD is used instead, because the CLOEXEC semantics + // will never be supported, as this is a bare metal framework with + // no capabilities for multi-process execution. While F_DUPFD is also + // not supported yet, it might be (currently it returns ENOSYS). + #[cfg(target_os = "espidf")] + let fd = crate::fs::fcntl_dupfd(self)?; + + Ok(fd) + } +} + #[cfg(not(windows))] impl AsFd for OwnedFd { #[inline] From 8cff5bb68448f7a74b38989e7b2a01dca5bce8c7 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Mon, 7 Feb 2022 17:59:53 -0800 Subject: [PATCH 2/4] Fix try_clone's error type. --- src/io/owned_fd.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/io/owned_fd.rs b/src/io/owned_fd.rs index f40790557..9bd403ae3 100644 --- a/src/io/owned_fd.rs +++ b/src/io/owned_fd.rs @@ -30,7 +30,7 @@ pub struct OwnedFd { impl OwnedFd { /// Creates a new `OwnedFd` instance that shares the same underlying file handle /// as the existing `OwnedFd` instance. - pub fn try_clone(&self) -> std::io::Result { + pub fn try_clone(&self) -> crate::io::Result { // We want to atomically duplicate this file descriptor and set the // CLOEXEC flag, and currently that's done via F_DUPFD_CLOEXEC. This // is a POSIX flag that was added to Linux in 2.6.24. From c2200115f4d9e478f62f227499848b5a28188285 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Tue, 8 Feb 2022 05:11:33 -0800 Subject: [PATCH 3/4] Fix compilation on WASI. --- src/io/owned_fd.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/io/owned_fd.rs b/src/io/owned_fd.rs index 9bd403ae3..5d3f4b5d2 100644 --- a/src/io/owned_fd.rs +++ b/src/io/owned_fd.rs @@ -30,6 +30,7 @@ pub struct OwnedFd { impl OwnedFd { /// Creates a new `OwnedFd` instance that shares the same underlying file handle /// as the existing `OwnedFd` instance. + #[cfg(not(target_os = "wasi"))] pub fn try_clone(&self) -> crate::io::Result { // We want to atomically duplicate this file descriptor and set the // CLOEXEC flag, and currently that's done via F_DUPFD_CLOEXEC. This @@ -46,6 +47,16 @@ impl OwnedFd { Ok(fd) } + + /// Creates a new `OwnedFd` instance that shares the same underlying file handle + /// as the existing `OwnedFd` instance. + #[cfg(target_os = "wasi")] + pub fn try_clone(&self) -> std::io::Result { + Err(std::io::Error::new( + std::io::ErrorKind::Unsupported, + "operation not supported on WASI yet", + )) + } } #[cfg(not(windows))] From cb77052ae879531f9d80510931bf3f87ff22170a Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Tue, 8 Feb 2022 07:12:01 -0800 Subject: [PATCH 4/4] Fix compilation on Windows. --- src/io/owned_fd.rs | 84 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 83 insertions(+), 1 deletion(-) diff --git a/src/io/owned_fd.rs b/src/io/owned_fd.rs index 5d3f4b5d2..89c875cc5 100644 --- a/src/io/owned_fd.rs +++ b/src/io/owned_fd.rs @@ -30,7 +30,7 @@ pub struct OwnedFd { impl OwnedFd { /// Creates a new `OwnedFd` instance that shares the same underlying file handle /// as the existing `OwnedFd` instance. - #[cfg(not(target_os = "wasi"))] + #[cfg(all(unix, not(target_os = "wasi")))] pub fn try_clone(&self) -> crate::io::Result { // We want to atomically duplicate this file descriptor and set the // CLOEXEC flag, and currently that's done via F_DUPFD_CLOEXEC. This @@ -57,6 +57,88 @@ impl OwnedFd { "operation not supported on WASI yet", )) } + + /// Creates a new `OwnedFd` instance that shares the same underlying file + /// handle as the existing `OwnedFd` instance. + #[cfg(target_os = "windows")] + pub fn try_clone(&self) -> std::io::Result { + use winapi::um::processthreadsapi::GetCurrentProcessId; + use winapi::um::winsock2::{ + WSADuplicateSocketW, WSAGetLastError, WSASocketW, INVALID_SOCKET, WSAEINVAL, + WSAEPROTOTYPE, WSAPROTOCOL_INFOW, WSA_FLAG_NO_HANDLE_INHERIT, WSA_FLAG_OVERLAPPED, + }; + + let mut info = unsafe { std::mem::zeroed::() }; + let result = + unsafe { WSADuplicateSocketW(self.as_raw_fd() as _, GetCurrentProcessId(), &mut info) }; + match result { + SOCKET_ERROR => return Err(std::io::Error::last_os_error()), + 0 => (), + _ => panic!(), + } + let socket = unsafe { + WSASocketW( + info.iAddressFamily, + info.iSocketType, + info.iProtocol, + &mut info, + 0, + WSA_FLAG_OVERLAPPED | WSA_FLAG_NO_HANDLE_INHERIT, + ) + }; + + if socket != INVALID_SOCKET { + unsafe { Ok(Self::from_raw_fd(socket as _)) } + } else { + let error = unsafe { WSAGetLastError() }; + + if error != WSAEPROTOTYPE && error != WSAEINVAL { + return Err(std::io::Error::from_raw_os_error(error)); + } + + let socket = unsafe { + WSASocketW( + info.iAddressFamily, + info.iSocketType, + info.iProtocol, + &mut info, + 0, + WSA_FLAG_OVERLAPPED, + ) + }; + + if socket == INVALID_SOCKET { + return Err(std::io::Error::last_os_error()); + } + + unsafe { + let socket = Self::from_raw_fd(socket as _); + socket.set_no_inherit()?; + Ok(socket) + } + } + } + + #[cfg(windows)] + #[cfg(not(target_vendor = "uwp"))] + fn set_no_inherit(&self) -> std::io::Result<()> { + use winapi::um::handleapi::SetHandleInformation; + use winapi::um::winbase::HANDLE_FLAG_INHERIT; + use winapi::um::winnt::HANDLE; + match unsafe { SetHandleInformation(self.as_raw_fd() as HANDLE, HANDLE_FLAG_INHERIT, 0) } { + 0 => return Err(std::io::Error::last_os_error()), + _ => Ok(()), + } + } + + #[cfg(windows)] + #[cfg(target_vendor = "uwp")] + fn set_no_inherit(&self) -> std::io::Result<()> { + Err(io::Error::new_const( + std::io::ErrorKind::Unsupported, + &"Unavailable on UWP", + )) + } } #[cfg(not(windows))]