Rollup merge of #88794 - sunfishcode:sunfishcode/try-clone, r=joshtriplett

Add a `try_clone()` function to `OwnedFd`.

As suggested in #88564. This adds a `try_clone()` to `OwnedFd` by
refactoring the code out of the existing `File`/`Socket` code.

r? ``@joshtriplett``
This commit is contained in:
Matthias Krüger 2022-01-25 05:51:09 +01:00 committed by GitHub
commit 687bb583c8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 151 additions and 91 deletions

View file

@ -8,6 +8,8 @@ use crate::fmt;
use crate::fs;
use crate::marker::PhantomData;
use crate::mem::forget;
#[cfg(not(target_os = "wasi"))]
use crate::sys::cvt;
use crate::sys_common::{AsInner, FromInner, IntoInner};
/// A borrowed file descriptor.
@ -67,6 +69,37 @@ impl BorrowedFd<'_> {
}
}
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<Self> {
// 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 cmd = libc::F_DUPFD_CLOEXEC;
// 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 cmd = libc::F_DUPFD;
let fd = cvt(unsafe { libc::fcntl(self.as_raw_fd(), cmd, 0) })?;
Ok(unsafe { Self::from_raw_fd(fd) })
}
#[cfg(target_os = "wasi")]
pub fn try_clone(&self) -> crate::io::Result<Self> {
Err(crate::io::Error::new_const(
crate::io::ErrorKind::Unsupported,
&"operation not supported on WASI yet",
))
}
}
#[unstable(feature = "io_safety", issue = "87074")]
impl AsRawFd for BorrowedFd<'_> {
#[inline]

View file

@ -6,9 +6,11 @@ use super::raw::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle};
use crate::convert::TryFrom;
use crate::fmt;
use crate::fs;
use crate::io;
use crate::marker::PhantomData;
use crate::mem::forget;
use crate::sys::c;
use crate::sys::cvt;
use crate::sys_common::{AsInner, FromInner, IntoInner};
/// A borrowed handle.
@ -144,6 +146,36 @@ impl TryFrom<HandleOrNull> for OwnedHandle {
}
}
impl OwnedHandle {
/// Creates a new `OwnedHandle` instance that shares the same underlying file handle
/// as the existing `OwnedHandle` instance.
pub fn try_clone(&self) -> crate::io::Result<Self> {
self.duplicate(0, false, c::DUPLICATE_SAME_ACCESS)
}
pub(crate) fn duplicate(
&self,
access: c::DWORD,
inherit: bool,
options: c::DWORD,
) -> io::Result<Self> {
let mut ret = 0 as c::HANDLE;
cvt(unsafe {
let cur_proc = c::GetCurrentProcess();
c::DuplicateHandle(
cur_proc,
self.as_raw_handle(),
cur_proc,
&mut ret,
access,
inherit as c::BOOL,
options,
)
})?;
unsafe { Ok(Self::from_raw_handle(ret)) }
}
}
impl TryFrom<HandleOrInvalid> for OwnedHandle {
type Error = ();

View file

@ -4,9 +4,13 @@
use super::raw::{AsRawSocket, FromRawSocket, IntoRawSocket, RawSocket};
use crate::fmt;
use crate::io;
use crate::marker::PhantomData;
use crate::mem;
use crate::mem::forget;
use crate::sys;
use crate::sys::c;
use crate::sys::cvt;
/// A borrowed socket.
///
@ -69,6 +73,77 @@ impl BorrowedSocket<'_> {
}
}
impl OwnedSocket {
/// Creates a new `OwnedSocket` instance that shares the same underlying socket
/// as the existing `OwnedSocket` instance.
pub fn try_clone(&self) -> io::Result<Self> {
let mut info = unsafe { mem::zeroed::<c::WSAPROTOCOL_INFO>() };
let result = unsafe {
c::WSADuplicateSocketW(self.as_raw_socket(), c::GetCurrentProcessId(), &mut info)
};
sys::net::cvt(result)?;
let socket = unsafe {
c::WSASocketW(
info.iAddressFamily,
info.iSocketType,
info.iProtocol,
&mut info,
0,
c::WSA_FLAG_OVERLAPPED | c::WSA_FLAG_NO_HANDLE_INHERIT,
)
};
if socket != c::INVALID_SOCKET {
unsafe { Ok(OwnedSocket::from_raw_socket(socket)) }
} else {
let error = unsafe { c::WSAGetLastError() };
if error != c::WSAEPROTOTYPE && error != c::WSAEINVAL {
return Err(io::Error::from_raw_os_error(error));
}
let socket = unsafe {
c::WSASocketW(
info.iAddressFamily,
info.iSocketType,
info.iProtocol,
&mut info,
0,
c::WSA_FLAG_OVERLAPPED,
)
};
if socket == c::INVALID_SOCKET {
return Err(last_error());
}
unsafe {
let socket = OwnedSocket::from_raw_socket(socket);
socket.set_no_inherit()?;
Ok(socket)
}
}
}
#[cfg(not(target_vendor = "uwp"))]
pub(crate) fn set_no_inherit(&self) -> io::Result<()> {
cvt(unsafe {
c::SetHandleInformation(self.as_raw_socket() as c::HANDLE, c::HANDLE_FLAG_INHERIT, 0)
})
.map(drop)
}
#[cfg(target_vendor = "uwp")]
pub(crate) fn set_no_inherit(&self) -> io::Result<()> {
Err(io::Error::new_const(io::ErrorKind::Unsupported, &"Unavailable on UWP"))
}
}
/// Returns the last error from the Windows socket interface.
fn last_error() -> io::Error {
io::Error::from_raw_os_error(unsafe { c::WSAGetLastError() })
}
impl AsRawSocket for BorrowedSocket<'_> {
#[inline]
fn as_raw_socket(&self) -> RawSocket {

View file

@ -259,22 +259,9 @@ impl FileDesc {
}
}
#[inline]
pub fn duplicate(&self) -> io::Result<FileDesc> {
// 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 cmd = libc::F_DUPFD_CLOEXEC;
// 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 cmd = libc::F_DUPFD;
let fd = cvt(unsafe { libc::fcntl(self.as_raw_fd(), cmd, 0) })?;
Ok(unsafe { FileDesc::from_raw_fd(fd) })
Ok(Self(self.0.try_clone()?))
}
}

View file

@ -460,7 +460,7 @@ impl File {
}
pub fn duplicate(&self) -> io::Result<File> {
Ok(File { handle: self.handle.duplicate(0, false, c::DUPLICATE_SAME_ACCESS)? })
Ok(Self { handle: self.handle.try_clone()? })
}
fn reparse_point<'a>(

View file

@ -262,26 +262,17 @@ impl Handle {
Ok(written as usize)
}
pub fn try_clone(&self) -> io::Result<Self> {
Ok(Self(self.0.try_clone()?))
}
pub fn duplicate(
&self,
access: c::DWORD,
inherit: bool,
options: c::DWORD,
) -> io::Result<Handle> {
let mut ret = 0 as c::HANDLE;
cvt(unsafe {
let cur_proc = c::GetCurrentProcess();
c::DuplicateHandle(
cur_proc,
self.as_raw_handle(),
cur_proc,
&mut ret,
access,
inherit as c::BOOL,
options,
)
})?;
unsafe { Ok(Handle::from_raw_handle(ret)) }
) -> io::Result<Self> {
Ok(Self(self.0.duplicate(access, inherit, options)?))
}
}

View file

@ -134,7 +134,7 @@ impl Socket {
unsafe {
let socket = Self::from_raw_socket(socket);
socket.set_no_inherit()?;
socket.0.set_no_inherit()?;
Ok(socket)
}
}
@ -213,52 +213,7 @@ impl Socket {
}
pub fn duplicate(&self) -> io::Result<Socket> {
let mut info = unsafe { mem::zeroed::<c::WSAPROTOCOL_INFO>() };
let result = unsafe {
c::WSADuplicateSocketW(self.as_raw_socket(), c::GetCurrentProcessId(), &mut info)
};
cvt(result)?;
let socket = unsafe {
c::WSASocketW(
info.iAddressFamily,
info.iSocketType,
info.iProtocol,
&mut info,
0,
c::WSA_FLAG_OVERLAPPED | c::WSA_FLAG_NO_HANDLE_INHERIT,
)
};
if socket != c::INVALID_SOCKET {
unsafe { Ok(Self::from_inner(OwnedSocket::from_raw_socket(socket))) }
} else {
let error = unsafe { c::WSAGetLastError() };
if error != c::WSAEPROTOTYPE && error != c::WSAEINVAL {
return Err(io::Error::from_raw_os_error(error));
}
let socket = unsafe {
c::WSASocketW(
info.iAddressFamily,
info.iSocketType,
info.iProtocol,
&mut info,
0,
c::WSA_FLAG_OVERLAPPED,
)
};
if socket == c::INVALID_SOCKET {
return Err(last_error());
}
unsafe {
let socket = Self::from_inner(OwnedSocket::from_raw_socket(socket));
socket.set_no_inherit()?;
Ok(socket)
}
}
Ok(Self(self.0.try_clone()?))
}
fn recv_with_flags(&self, buf: &mut [u8], flags: c_int) -> io::Result<usize> {
@ -421,19 +376,6 @@ impl Socket {
}
}
#[cfg(not(target_vendor = "uwp"))]
fn set_no_inherit(&self) -> io::Result<()> {
sys::cvt(unsafe {
c::SetHandleInformation(self.as_raw_socket() as c::HANDLE, c::HANDLE_FLAG_INHERIT, 0)
})
.map(drop)
}
#[cfg(target_vendor = "uwp")]
fn set_no_inherit(&self) -> io::Result<()> {
Err(io::Error::new_const(io::ErrorKind::Unsupported, &"Unavailable on UWP"))
}
pub fn shutdown(&self, how: Shutdown) -> io::Result<()> {
let how = match how {
Shutdown::Write => c::SD_SEND,