Skip to content

Commit e1563f8

Browse files
authored
Add a try_clone method to OwnedFd. (#218)
This corresponds to the `try_clone` function in sunfishcode/io-lifetimes#16 and rust-lang/rust#88794.
1 parent 32b50d5 commit e1563f8

File tree

1 file changed

+114
-0
lines changed

1 file changed

+114
-0
lines changed

Diff for: src/io/owned_fd.rs

+114
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,120 @@ pub struct OwnedFd {
2727
inner: ManuallyDrop<crate::imp::fd::OwnedFd>,
2828
}
2929

30+
impl OwnedFd {
31+
/// Creates a new `OwnedFd` instance that shares the same underlying file handle
32+
/// as the existing `OwnedFd` instance.
33+
#[cfg(all(unix, not(target_os = "wasi")))]
34+
pub fn try_clone(&self) -> crate::io::Result<Self> {
35+
// We want to atomically duplicate this file descriptor and set the
36+
// CLOEXEC flag, and currently that's done via F_DUPFD_CLOEXEC. This
37+
// is a POSIX flag that was added to Linux in 2.6.24.
38+
#[cfg(not(target_os = "espidf"))]
39+
let fd = crate::fs::fcntl_dupfd_cloexec(self, 0)?;
40+
41+
// For ESP-IDF, F_DUPFD is used instead, because the CLOEXEC semantics
42+
// will never be supported, as this is a bare metal framework with
43+
// no capabilities for multi-process execution. While F_DUPFD is also
44+
// not supported yet, it might be (currently it returns ENOSYS).
45+
#[cfg(target_os = "espidf")]
46+
let fd = crate::fs::fcntl_dupfd(self)?;
47+
48+
Ok(fd)
49+
}
50+
51+
/// Creates a new `OwnedFd` instance that shares the same underlying file handle
52+
/// as the existing `OwnedFd` instance.
53+
#[cfg(target_os = "wasi")]
54+
pub fn try_clone(&self) -> std::io::Result<Self> {
55+
Err(std::io::Error::new(
56+
std::io::ErrorKind::Unsupported,
57+
"operation not supported on WASI yet",
58+
))
59+
}
60+
61+
/// Creates a new `OwnedFd` instance that shares the same underlying file
62+
/// handle as the existing `OwnedFd` instance.
63+
#[cfg(target_os = "windows")]
64+
pub fn try_clone(&self) -> std::io::Result<Self> {
65+
use winapi::um::processthreadsapi::GetCurrentProcessId;
66+
use winapi::um::winsock2::{
67+
WSADuplicateSocketW, WSAGetLastError, WSASocketW, INVALID_SOCKET, WSAEINVAL,
68+
WSAEPROTOTYPE, WSAPROTOCOL_INFOW, WSA_FLAG_NO_HANDLE_INHERIT, WSA_FLAG_OVERLAPPED,
69+
};
70+
71+
let mut info = unsafe { std::mem::zeroed::<WSAPROTOCOL_INFOW>() };
72+
let result =
73+
unsafe { WSADuplicateSocketW(self.as_raw_fd() as _, GetCurrentProcessId(), &mut info) };
74+
match result {
75+
SOCKET_ERROR => return Err(std::io::Error::last_os_error()),
76+
0 => (),
77+
_ => panic!(),
78+
}
79+
let socket = unsafe {
80+
WSASocketW(
81+
info.iAddressFamily,
82+
info.iSocketType,
83+
info.iProtocol,
84+
&mut info,
85+
0,
86+
WSA_FLAG_OVERLAPPED | WSA_FLAG_NO_HANDLE_INHERIT,
87+
)
88+
};
89+
90+
if socket != INVALID_SOCKET {
91+
unsafe { Ok(Self::from_raw_fd(socket as _)) }
92+
} else {
93+
let error = unsafe { WSAGetLastError() };
94+
95+
if error != WSAEPROTOTYPE && error != WSAEINVAL {
96+
return Err(std::io::Error::from_raw_os_error(error));
97+
}
98+
99+
let socket = unsafe {
100+
WSASocketW(
101+
info.iAddressFamily,
102+
info.iSocketType,
103+
info.iProtocol,
104+
&mut info,
105+
0,
106+
WSA_FLAG_OVERLAPPED,
107+
)
108+
};
109+
110+
if socket == INVALID_SOCKET {
111+
return Err(std::io::Error::last_os_error());
112+
}
113+
114+
unsafe {
115+
let socket = Self::from_raw_fd(socket as _);
116+
socket.set_no_inherit()?;
117+
Ok(socket)
118+
}
119+
}
120+
}
121+
122+
#[cfg(windows)]
123+
#[cfg(not(target_vendor = "uwp"))]
124+
fn set_no_inherit(&self) -> std::io::Result<()> {
125+
use winapi::um::handleapi::SetHandleInformation;
126+
use winapi::um::winbase::HANDLE_FLAG_INHERIT;
127+
use winapi::um::winnt::HANDLE;
128+
match unsafe { SetHandleInformation(self.as_raw_fd() as HANDLE, HANDLE_FLAG_INHERIT, 0) } {
129+
0 => return Err(std::io::Error::last_os_error()),
130+
_ => Ok(()),
131+
}
132+
}
133+
134+
#[cfg(windows)]
135+
#[cfg(target_vendor = "uwp")]
136+
fn set_no_inherit(&self) -> std::io::Result<()> {
137+
Err(io::Error::new_const(
138+
std::io::ErrorKind::Unsupported,
139+
&"Unavailable on UWP",
140+
))
141+
}
142+
}
143+
30144
#[cfg(not(windows))]
31145
impl AsFd for OwnedFd {
32146
#[inline]

0 commit comments

Comments
 (0)