Skip to content

Commit 293f408

Browse files
committed
Port try_clone to io-lifetimes.
Port std's `try_clone`, added in rust-lang/rust#88794, to io-lifetimes. This puts the actua clone logic under control of `feature = "close"`, as it has a similar circular dependency issue with rustix.
1 parent ffc0e79 commit 293f408

File tree

2 files changed

+192
-1
lines changed

2 files changed

+192
-1
lines changed

.github/workflows/main.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ jobs:
4747
with:
4848
toolchain: ${{ matrix.rust }}
4949
- run: cargo test --workspace --all-features
50+
- run: cargo test --workspace --no-default-features
5051

5152
test_use_std:
5253
name: Test with std's types and traits
@@ -74,3 +75,4 @@ jobs:
7475
with:
7576
toolchain: ${{ matrix.rust }}
7677
- run: cargo test --workspace --all-features
78+
- run: cargo test --workspace --no-default-features

src/types.rs

Lines changed: 190 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,27 @@ use std::{
1414
},
1515
};
1616
#[cfg(all(windows, feature = "close"))]
17-
use winapi::{um::handleapi::INVALID_HANDLE_VALUE, um::winsock2::INVALID_SOCKET};
17+
use winapi::{
18+
shared::minwindef::{BOOL, DWORD},
19+
shared::ntdef::HANDLE,
20+
um::handleapi::DuplicateHandle,
21+
um::handleapi::SetHandleInformation,
22+
um::handleapi::INVALID_HANDLE_VALUE,
23+
um::processthreadsapi::GetCurrentProcess,
24+
um::processthreadsapi::GetCurrentProcessId,
25+
um::winbase::HANDLE_FLAG_INHERIT,
26+
um::winnt::DUPLICATE_SAME_ACCESS,
27+
um::winsock2::WSADuplicateSocketW,
28+
um::winsock2::WSAGetLastError,
29+
um::winsock2::WSASocketW,
30+
um::winsock2::INVALID_SOCKET,
31+
um::winsock2::SOCKET_ERROR,
32+
um::winsock2::WSAEINVAL,
33+
um::winsock2::WSAEPROTOTYPE,
34+
um::winsock2::WSAPROTOCOL_INFOW,
35+
um::winsock2::WSA_FLAG_NO_HANDLE_INHERIT,
36+
um::winsock2::WSA_FLAG_OVERLAPPED,
37+
};
1838

1939
#[cfg(all(windows, not(feature = "winapi")))]
2040
const INVALID_HANDLE_VALUE: *mut core::ffi::c_void = !0 as _;
@@ -110,6 +130,42 @@ pub struct OwnedFd {
110130
fd: RawFd,
111131
}
112132

133+
#[cfg(any(unix, target_os = "wasi"))]
134+
impl OwnedFd {
135+
/// Creates a new `OwnedFd` instance that shares the same underlying file handle
136+
/// as the existing `OwnedFd` instance.
137+
pub fn try_clone(&self) -> std::io::Result<Self> {
138+
#[cfg(feature = "close")]
139+
{
140+
// We want to atomically duplicate this file descriptor and set the
141+
// CLOEXEC flag, and currently that's done via F_DUPFD_CLOEXEC. This
142+
// is a POSIX flag that was added to Linux in 2.6.24.
143+
#[cfg(not(target_os = "espidf"))]
144+
let cmd = libc::F_DUPFD_CLOEXEC;
145+
146+
// For ESP-IDF, F_DUPFD is used instead, because the CLOEXEC semantics
147+
// will never be supported, as this is a bare metal framework with
148+
// no capabilities for multi-process execution. While F_DUPFD is also
149+
// not supported yet, it might be (currently it returns ENOSYS).
150+
#[cfg(target_os = "espidf")]
151+
let cmd = libc::F_DUPFD;
152+
153+
let fd = match unsafe { libc::fcntl(self.as_raw_fd(), cmd, 0) } {
154+
-1 => return Err(std::io::Error::last_os_error()),
155+
fd => fd,
156+
};
157+
Ok(unsafe { Self::from_raw_fd(fd) })
158+
}
159+
160+
// If the `close` feature is disabled, we expect users to avoid cloning
161+
// `OwnedFd` instances, so that we don't have to call `fcntl`.
162+
#[cfg(not(feature = "close"))]
163+
{
164+
unreachable!("try_clone called without the \"close\" feature in io-lifetimes");
165+
}
166+
}
167+
}
168+
113169
/// An owned handle.
114170
///
115171
/// This closes the handle on drop.
@@ -133,6 +189,51 @@ pub struct OwnedHandle {
133189
handle: RawHandle,
134190
}
135191

192+
#[cfg(windows)]
193+
impl OwnedHandle {
194+
/// Creates a new `OwnedHandle` instance that shares the same underlying file handle
195+
/// as the existing `OwnedHandle` instance.
196+
pub fn try_clone(&self) -> std::io::Result<OwnedHandle> {
197+
#[cfg(feature = "close")]
198+
{
199+
self.duplicate(0, false, DUPLICATE_SAME_ACCESS)
200+
}
201+
202+
// If the `close` feature is disabled, we expect users to avoid cloning
203+
// `OwnedHandle` instances, so that we don't have to call `fcntl`.
204+
#[cfg(not(feature = "close"))]
205+
{
206+
unreachable!("try_clone called without the \"close\" feature in io-lifetimes");
207+
}
208+
}
209+
210+
#[cfg(feature = "close")]
211+
pub(crate) fn duplicate(
212+
&self,
213+
access: DWORD,
214+
inherit: bool,
215+
options: DWORD,
216+
) -> std::io::Result<Self> {
217+
let mut ret = 0 as HANDLE;
218+
match unsafe {
219+
let cur_proc = GetCurrentProcess();
220+
DuplicateHandle(
221+
cur_proc,
222+
self.as_raw_handle(),
223+
cur_proc,
224+
&mut ret,
225+
access,
226+
inherit as BOOL,
227+
options,
228+
)
229+
} {
230+
0 => return Err(std::io::Error::last_os_error()),
231+
_ => (),
232+
}
233+
unsafe { Ok(Self::from_raw_handle(ret)) }
234+
}
235+
}
236+
136237
/// An owned socket.
137238
///
138239
/// This closes the socket on drop.
@@ -157,6 +258,94 @@ pub struct OwnedSocket {
157258
socket: RawSocket,
158259
}
159260

261+
#[cfg(windows)]
262+
impl OwnedSocket {
263+
/// Creates a new `OwnedSocket` instance that shares the same underlying socket
264+
/// as the existing `OwnedSocket` instance.
265+
pub fn try_clone(&self) -> std::io::Result<Self> {
266+
#[cfg(feature = "close")]
267+
{
268+
let mut info = unsafe { std::mem::zeroed::<WSAPROTOCOL_INFOW>() };
269+
let result = unsafe {
270+
WSADuplicateSocketW(self.as_raw_socket() as _, GetCurrentProcessId(), &mut info)
271+
};
272+
match result {
273+
SOCKET_ERROR => return Err(std::io::Error::last_os_error()),
274+
0 => (),
275+
_ => panic!(),
276+
}
277+
let socket = unsafe {
278+
WSASocketW(
279+
info.iAddressFamily,
280+
info.iSocketType,
281+
info.iProtocol,
282+
&mut info,
283+
0,
284+
WSA_FLAG_OVERLAPPED | WSA_FLAG_NO_HANDLE_INHERIT,
285+
)
286+
};
287+
288+
if socket != INVALID_SOCKET {
289+
unsafe { Ok(OwnedSocket::from_raw_socket(socket as _)) }
290+
} else {
291+
let error = unsafe { WSAGetLastError() };
292+
293+
if error != WSAEPROTOTYPE && error != WSAEINVAL {
294+
return Err(std::io::Error::from_raw_os_error(error));
295+
}
296+
297+
let socket = unsafe {
298+
WSASocketW(
299+
info.iAddressFamily,
300+
info.iSocketType,
301+
info.iProtocol,
302+
&mut info,
303+
0,
304+
WSA_FLAG_OVERLAPPED,
305+
)
306+
};
307+
308+
if socket == INVALID_SOCKET {
309+
return Err(std::io::Error::last_os_error());
310+
}
311+
312+
unsafe {
313+
let socket = OwnedSocket::from_raw_socket(socket as _);
314+
socket.set_no_inherit()?;
315+
Ok(socket)
316+
}
317+
}
318+
}
319+
320+
// If the `close` feature is disabled, we expect users to avoid cloning
321+
// `OwnedSocket` instances, so that we don't have to call `fcntl`.
322+
#[cfg(not(feature = "close"))]
323+
{
324+
unreachable!("try_clone called without the \"close\" feature in io-lifetimes");
325+
}
326+
}
327+
328+
#[cfg(feature = "close")]
329+
#[cfg(not(target_vendor = "uwp"))]
330+
fn set_no_inherit(&self) -> std::io::Result<()> {
331+
match unsafe {
332+
SetHandleInformation(self.as_raw_socket() as HANDLE, HANDLE_FLAG_INHERIT, 0)
333+
} {
334+
0 => return Err(std::io::Error::last_os_error()),
335+
_ => Ok(()),
336+
}
337+
}
338+
339+
#[cfg(feature = "close")]
340+
#[cfg(target_vendor = "uwp")]
341+
fn set_no_inherit(&self) -> std::io::Result<()> {
342+
Err(io::Error::new_const(
343+
std::io::ErrorKind::Unsupported,
344+
&"Unavailable on UWP",
345+
))
346+
}
347+
}
348+
160349
/// FFI type for handles in return values or out parameters, where `INVALID_HANDLE_VALUE` is used
161350
/// as a sentry value to indicate errors, such as in the return value of `CreateFileW`. This uses
162351
/// `repr(transparent)` and has the representation of a host handle, so that it can be used in such

0 commit comments

Comments
 (0)