Skip to content

Commit b31f9cc

Browse files
committed
Auto merge of rust-lang#97178 - sunfishcode:ownedfd-and-dup, r=joshtriplett
Add a `BorrowedFd::try_clone_to_owned` and accompanying documentation Add a `BorrowedFd::try_clone_to_owned`, which returns a new `OwnedFd` sharing the underlying file description. And similar for `BorrowedHandle` and `BorrowedSocket` on WIndows. This is similar to the existing `OwnedFd::try_clone`, but it's named differently to reflect that it doesn't return `Result<Self, ...>`. I'm open to suggestions for better names. Also, extend the `unix::io` documentation to mention that `dup` is permitted on `BorrowedFd`. This was originally requsted [here](rust-lang#88564 (comment)). At the time I wasn't sure whether it was desirable, but it does have uses and it helps clarify the API. The documentation previously didn't rule out using `dup` on a `BorrowedFd`, but the API only offered convenient ways to do it from an `OwnedFd`. With this patch, the API allows one to do `try_clone` on any type where it's permitted.
2 parents ca98305 + ee49d65 commit b31f9cc

File tree

6 files changed

+81
-36
lines changed

6 files changed

+81
-36
lines changed

library/std/src/os/fd/owned.rs

+16-5
Original file line numberDiff line numberDiff line change
@@ -77,11 +77,20 @@ impl BorrowedFd<'_> {
7777
}
7878

7979
impl OwnedFd {
80-
/// Creates a new `OwnedFd` instance that shares the same underlying file handle
81-
/// as the existing `OwnedFd` instance.
82-
#[cfg(not(target_arch = "wasm32"))]
80+
/// Creates a new `OwnedFd` instance that shares the same underlying file
81+
/// description as the existing `OwnedFd` instance.
8382
#[stable(feature = "io_safety", since = "1.63.0")]
8483
pub fn try_clone(&self) -> crate::io::Result<Self> {
84+
self.as_fd().try_clone_to_owned()
85+
}
86+
}
87+
88+
impl BorrowedFd<'_> {
89+
/// Creates a new `OwnedFd` instance that shares the same underlying file
90+
/// description as the existing `BorrowedFd` instance.
91+
#[cfg(not(target_arch = "wasm32"))]
92+
#[stable(feature = "io_safety", since = "1.63.0")]
93+
pub fn try_clone_to_owned(&self) -> crate::io::Result<OwnedFd> {
8594
// We want to atomically duplicate this file descriptor and set the
8695
// CLOEXEC flag, and currently that's done via F_DUPFD_CLOEXEC. This
8796
// is a POSIX flag that was added to Linux in 2.6.24.
@@ -96,12 +105,14 @@ impl OwnedFd {
96105
let cmd = libc::F_DUPFD;
97106

98107
let fd = cvt(unsafe { libc::fcntl(self.as_raw_fd(), cmd, 0) })?;
99-
Ok(unsafe { Self::from_raw_fd(fd) })
108+
Ok(unsafe { OwnedFd::from_raw_fd(fd) })
100109
}
101110

111+
/// Creates a new `OwnedFd` instance that shares the same underlying file
112+
/// description as the existing `BorrowedFd` instance.
102113
#[cfg(target_arch = "wasm32")]
103114
#[stable(feature = "io_safety", since = "1.63.0")]
104-
pub fn try_clone(&self) -> crate::io::Result<Self> {
115+
pub fn try_clone_to_owned(&self) -> crate::io::Result<OwnedFd> {
105116
Err(crate::io::const_io_error!(
106117
crate::io::ErrorKind::Unsupported,
107118
"operation not supported on WASI yet",

library/std/src/os/unix/io/mod.rs

+19-9
Original file line numberDiff line numberDiff line change
@@ -26,20 +26,30 @@
2626
//! that they don't outlive the resource they point to. These are safe to
2727
//! use. `BorrowedFd` values may be used in APIs which provide safe access to
2828
//! any system call except for:
29+
//!
2930
//! - `close`, because that would end the dynamic lifetime of the resource
3031
//! without ending the lifetime of the file descriptor.
32+
//!
3133
//! - `dup2`/`dup3`, in the second argument, because this argument is
3234
//! closed and assigned a new resource, which may break the assumptions
3335
//! other code using that file descriptor.
34-
//! This list doesn't include `mmap`, since `mmap` does do a proper borrow of
35-
//! its file descriptor argument. That said, `mmap` is unsafe for other
36-
//! reasons: it operates on raw pointers, and it can have undefined behavior if
37-
//! the underlying storage is mutated. Mutations may come from other processes,
38-
//! or from the same process if the API provides `BorrowedFd` access, since as
39-
//! mentioned earlier, `BorrowedFd` values may be used in APIs which provide
40-
//! safe access to any system call. Consequently, code using `mmap` and
41-
//! presenting a safe API must take full responsibility for ensuring that safe
42-
//! Rust code cannot evoke undefined behavior through it.
36+
//!
37+
//! `BorrowedFd` values may be used in APIs which provide safe access to `dup`
38+
//! system calls, so types implementing `AsFd` or `From<OwnedFd>` should not
39+
//! assume they always have exclusive access to the underlying file
40+
//! description.
41+
//!
42+
//! `BorrowedFd` values may also be used with `mmap`, since `mmap` uses the
43+
//! provided file descriptor in a manner similar to `dup` and does not require
44+
//! the `BorrowedFd` passed to it to live for the lifetime of the resulting
45+
//! mapping. That said, `mmap` is unsafe for other reasons: it operates on raw
46+
//! pointers, and it can have undefined behavior if the underlying storage is
47+
//! mutated. Mutations may come from other processes, or from the same process
48+
//! if the API provides `BorrowedFd` access, since as mentioned earlier,
49+
//! `BorrowedFd` values may be used in APIs which provide safe access to any
50+
//! system call. Consequently, code using `mmap` and presenting a safe API must
51+
//! take full responsibility for ensuring that safe Rust code cannot evoke
52+
//! undefined behavior through it.
4353
//!
4454
//! Like boxes, `OwnedFd` values conceptually own the resource they point to,
4555
//! and free (close) it when they are dropped.

library/std/src/os/windows/io/handle.rs

+14-5
Original file line numberDiff line numberDiff line change
@@ -177,10 +177,19 @@ impl TryFrom<HandleOrNull> for OwnedHandle {
177177
}
178178

179179
impl OwnedHandle {
180-
/// Creates a new `OwnedHandle` instance that shares the same underlying file handle
181-
/// as the existing `OwnedHandle` instance.
180+
/// Creates a new `OwnedHandle` instance that shares the same underlying
181+
/// object as the existing `OwnedHandle` instance.
182182
#[stable(feature = "io_safety", since = "1.63.0")]
183183
pub fn try_clone(&self) -> crate::io::Result<Self> {
184+
self.as_handle().try_clone_to_owned()
185+
}
186+
}
187+
188+
impl BorrowedHandle<'_> {
189+
/// Creates a new `OwnedHandle` instance that shares the same underlying
190+
/// object as the existing `BorrowedHandle` instance.
191+
#[stable(feature = "io_safety", since = "1.63.0")]
192+
pub fn try_clone_to_owned(&self) -> crate::io::Result<OwnedHandle> {
184193
self.duplicate(0, false, c::DUPLICATE_SAME_ACCESS)
185194
}
186195

@@ -189,15 +198,15 @@ impl OwnedHandle {
189198
access: c::DWORD,
190199
inherit: bool,
191200
options: c::DWORD,
192-
) -> io::Result<Self> {
201+
) -> io::Result<OwnedHandle> {
193202
let handle = self.as_raw_handle();
194203

195204
// `Stdin`, `Stdout`, and `Stderr` can all hold null handles, such as
196205
// in a process with a detached console. `DuplicateHandle` would fail
197206
// if we passed it a null handle, but we can treat null as a valid
198207
// handle which doesn't do any I/O, and allow it to be duplicated.
199208
if handle.is_null() {
200-
return unsafe { Ok(Self::from_raw_handle(handle)) };
209+
return unsafe { Ok(OwnedHandle::from_raw_handle(handle)) };
201210
}
202211

203212
let mut ret = ptr::null_mut();
@@ -213,7 +222,7 @@ impl OwnedHandle {
213222
options,
214223
)
215224
})?;
216-
unsafe { Ok(Self::from_raw_handle(ret)) }
225+
unsafe { Ok(OwnedHandle::from_raw_handle(ret)) }
217226
}
218227
}
219228

library/std/src/os/windows/io/mod.rs

+6
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@
3636
//! dynamic lifetime of the resource without ending the lifetime of the
3737
//! handle or socket.
3838
//!
39+
//! `BorrowedHandle` and `BorrowedSocket` values may be used in APIs which
40+
//! provide safe access to `DuplicateHandle` and `WSADuplicateSocketW` and
41+
//! related functions, so types implementing `AsHandle`, `AsSocket`,
42+
//! `From<OwnedHandle>`, or `From<OwnedSocket>` should not assume they always
43+
//! have exclusive access to the underlying object.
44+
//!
3945
//! Like boxes, `OwnedHandle` and `OwnedSocket` values conceptually own the
4046
//! resource they point to, and free (close) it when they are dropped.
4147
//!

library/std/src/os/windows/io/socket.rs

+25-16
Original file line numberDiff line numberDiff line change
@@ -82,10 +82,33 @@ impl BorrowedSocket<'_> {
8282
}
8383

8484
impl OwnedSocket {
85-
/// Creates a new `OwnedSocket` instance that shares the same underlying socket
86-
/// as the existing `OwnedSocket` instance.
85+
/// Creates a new `OwnedSocket` instance that shares the same underlying
86+
/// object as the existing `OwnedSocket` instance.
8787
#[stable(feature = "io_safety", since = "1.63.0")]
8888
pub fn try_clone(&self) -> io::Result<Self> {
89+
self.as_socket().try_clone_to_owned()
90+
}
91+
92+
// FIXME(strict_provenance_magic): we defined RawSocket to be a u64 ;-;
93+
#[cfg(not(target_vendor = "uwp"))]
94+
pub(crate) fn set_no_inherit(&self) -> io::Result<()> {
95+
cvt(unsafe {
96+
c::SetHandleInformation(self.as_raw_socket() as c::HANDLE, c::HANDLE_FLAG_INHERIT, 0)
97+
})
98+
.map(drop)
99+
}
100+
101+
#[cfg(target_vendor = "uwp")]
102+
pub(crate) fn set_no_inherit(&self) -> io::Result<()> {
103+
Err(io::const_io_error!(io::ErrorKind::Unsupported, "Unavailable on UWP"))
104+
}
105+
}
106+
107+
impl BorrowedSocket<'_> {
108+
/// Creates a new `OwnedSocket` instance that shares the same underlying
109+
/// object as the existing `BorrowedSocket` instance.
110+
#[stable(feature = "io_safety", since = "1.63.0")]
111+
pub fn try_clone_to_owned(&self) -> io::Result<OwnedSocket> {
89112
let mut info = unsafe { mem::zeroed::<c::WSAPROTOCOL_INFO>() };
90113
let result = unsafe {
91114
c::WSADuplicateSocketW(self.as_raw_socket(), c::GetCurrentProcessId(), &mut info)
@@ -133,20 +156,6 @@ impl OwnedSocket {
133156
}
134157
}
135158
}
136-
137-
// FIXME(strict_provenance_magic): we defined RawSocket to be a u64 ;-;
138-
#[cfg(not(target_vendor = "uwp"))]
139-
pub(crate) fn set_no_inherit(&self) -> io::Result<()> {
140-
cvt(unsafe {
141-
c::SetHandleInformation(self.as_raw_socket() as c::HANDLE, c::HANDLE_FLAG_INHERIT, 0)
142-
})
143-
.map(drop)
144-
}
145-
146-
#[cfg(target_vendor = "uwp")]
147-
pub(crate) fn set_no_inherit(&self) -> io::Result<()> {
148-
Err(io::const_io_error!(io::ErrorKind::Unsupported, "Unavailable on UWP"))
149-
}
150159
}
151160

152161
/// Returns the last error from the Windows socket interface.

library/std/src/sys/windows/handle.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ impl Handle {
218218
inherit: bool,
219219
options: c::DWORD,
220220
) -> io::Result<Self> {
221-
Ok(Self(self.0.duplicate(access, inherit, options)?))
221+
Ok(Self(self.0.as_handle().duplicate(access, inherit, options)?))
222222
}
223223

224224
/// Performs a synchronous read.

0 commit comments

Comments
 (0)