1
Fork 0

I/O safety.

Introduce `OwnedFd` and `BorrowedFd`, and the `AsFd` trait, and
implementations of `AsFd`, `From<OwnedFd>` and `From<T> for OwnedFd`
for relevant types, along with Windows counterparts for handles and
sockets.

Tracking issue:
 - <https://github.com/rust-lang/rust/issues/87074>

RFC:
 - <https://github.com/rust-lang/rfcs/blob/master/text/3128-io-safety.md>
This commit is contained in:
Dan Gohman 2021-06-30 21:44:30 -07:00
parent 2451f42c1d
commit d15418586c
43 changed files with 2366 additions and 562 deletions

View file

@ -3,6 +3,7 @@ use crate::ffi::CStr;
use crate::io::{self, IoSlice, IoSliceMut};
use crate::mem;
use crate::net::{Shutdown, SocketAddr};
use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd};
use crate::str;
use crate::sys::fd::FileDesc;
use crate::sys_common::net::{getsockopt, setsockopt, sockaddr_to_addr};
@ -74,10 +75,10 @@ impl Socket {
// flag to atomically create the socket and set it as
// CLOEXEC. On Linux this was added in 2.6.27.
let fd = cvt(libc::socket(fam, ty | libc::SOCK_CLOEXEC, 0))?;
Ok(Socket(FileDesc::new(fd)))
Ok(Socket(FileDesc::from_raw_fd(fd)))
} else {
let fd = cvt(libc::socket(fam, ty, 0))?;
let fd = FileDesc::new(fd);
let fd = FileDesc::from_raw_fd(fd);
fd.set_cloexec()?;
let socket = Socket(fd);
@ -109,11 +110,11 @@ impl Socket {
))] {
// Like above, set cloexec atomically
cvt(libc::socketpair(fam, ty | libc::SOCK_CLOEXEC, 0, fds.as_mut_ptr()))?;
Ok((Socket(FileDesc::new(fds[0])), Socket(FileDesc::new(fds[1]))))
Ok((Socket(FileDesc::from_raw_fd(fds[0])), Socket(FileDesc::from_raw_fd(fds[1]))))
} else {
cvt(libc::socketpair(fam, ty, 0, fds.as_mut_ptr()))?;
let a = FileDesc::new(fds[0]);
let b = FileDesc::new(fds[1]);
let a = FileDesc::from_raw_fd(fds[0]);
let b = FileDesc::from_raw_fd(fds[1]);
a.set_cloexec()?;
b.set_cloexec()?;
Ok((Socket(a), Socket(b)))
@ -131,7 +132,7 @@ impl Socket {
self.set_nonblocking(true)?;
let r = unsafe {
let (addrp, len) = addr.into_inner();
cvt(libc::connect(self.0.raw(), addrp, len))
cvt(libc::connect(self.as_raw_fd(), addrp, len))
};
self.set_nonblocking(false)?;
@ -142,7 +143,7 @@ impl Socket {
Err(e) => return Err(e),
}
let mut pollfd = libc::pollfd { fd: self.0.raw(), events: libc::POLLOUT, revents: 0 };
let mut pollfd = libc::pollfd { fd: self.as_raw_fd(), events: libc::POLLOUT, revents: 0 };
if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 {
return Err(io::Error::new_const(
@ -212,15 +213,17 @@ impl Socket {
target_os = "netbsd",
target_os = "openbsd",
))] {
let fd = cvt_r(|| unsafe {
libc::accept4(self.0.raw(), storage, len, libc::SOCK_CLOEXEC)
})?;
Ok(Socket(FileDesc::new(fd)))
unsafe {
let fd = cvt_r(|| libc::accept4(self.as_raw_fd(), storage, len, libc::SOCK_CLOEXEC))?;
Ok(Socket(FileDesc::from_raw_fd(fd)))
}
} else {
let fd = cvt_r(|| unsafe { libc::accept(self.0.raw(), storage, len) })?;
let fd = FileDesc::new(fd);
fd.set_cloexec()?;
Ok(Socket(fd))
unsafe {
let fd = cvt_r(|| libc::accept(self.as_raw_fd(), storage, len))?;
let fd = FileDesc::from_raw_fd(fd);
fd.set_cloexec()?;
Ok(Socket(fd))
}
}
}
}
@ -231,7 +234,7 @@ impl Socket {
fn recv_with_flags(&self, buf: &mut [u8], flags: c_int) -> io::Result<usize> {
let ret = cvt(unsafe {
libc::recv(self.0.raw(), buf.as_mut_ptr() as *mut c_void, buf.len(), flags)
libc::recv(self.as_raw_fd(), buf.as_mut_ptr() as *mut c_void, buf.len(), flags)
})?;
Ok(ret as usize)
}
@ -263,7 +266,7 @@ impl Socket {
let n = cvt(unsafe {
libc::recvfrom(
self.0.raw(),
self.as_raw_fd(),
buf.as_mut_ptr() as *mut c_void,
buf.len(),
flags,
@ -288,7 +291,7 @@ impl Socket {
target_os = "openbsd",
))]
pub fn recv_msg(&self, msg: &mut libc::msghdr) -> io::Result<usize> {
let n = cvt(unsafe { libc::recvmsg(self.0.raw(), msg, libc::MSG_CMSG_CLOEXEC) })?;
let n = cvt(unsafe { libc::recvmsg(self.as_raw_fd(), msg, libc::MSG_CMSG_CLOEXEC) })?;
Ok(n as usize)
}
@ -319,7 +322,7 @@ impl Socket {
target_os = "openbsd",
))]
pub fn send_msg(&self, msg: &mut libc::msghdr) -> io::Result<usize> {
let n = cvt(unsafe { libc::sendmsg(self.0.raw(), msg, 0) })?;
let n = cvt(unsafe { libc::sendmsg(self.as_raw_fd(), msg, 0) })?;
Ok(n as usize)
}
@ -369,7 +372,7 @@ impl Socket {
Shutdown::Read => libc::SHUT_RD,
Shutdown::Both => libc::SHUT_RDWR,
};
cvt(unsafe { libc::shutdown(self.0.raw(), how) })?;
cvt(unsafe { libc::shutdown(self.as_raw_fd(), how) })?;
Ok(())
}
@ -396,7 +399,7 @@ impl Socket {
#[cfg(not(any(target_os = "solaris", target_os = "illumos")))]
pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
let mut nonblocking = nonblocking as libc::c_int;
cvt(unsafe { libc::ioctl(*self.as_inner(), libc::FIONBIO, &mut nonblocking) }).map(drop)
cvt(unsafe { libc::ioctl(self.as_raw_fd(), libc::FIONBIO, &mut nonblocking) }).map(drop)
}
#[cfg(any(target_os = "solaris", target_os = "illumos"))]
@ -410,23 +413,52 @@ impl Socket {
let raw: c_int = getsockopt(self, libc::SOL_SOCKET, libc::SO_ERROR)?;
if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) }
}
}
impl AsInner<c_int> for Socket {
fn as_inner(&self) -> &c_int {
self.0.as_inner()
// This is used by sys_common code to abstract over Windows and Unix.
pub fn as_raw(&self) -> RawFd {
self.as_raw_fd()
}
}
impl FromInner<c_int> for Socket {
fn from_inner(fd: c_int) -> Socket {
Socket(FileDesc::new(fd))
impl AsInner<FileDesc> for Socket {
fn as_inner(&self) -> &FileDesc {
&self.0
}
}
impl IntoInner<c_int> for Socket {
fn into_inner(self) -> c_int {
self.0.into_raw()
impl IntoInner<FileDesc> for Socket {
fn into_inner(self) -> FileDesc {
self.0
}
}
impl FromInner<FileDesc> for Socket {
fn from_inner(file_desc: FileDesc) -> Self {
Self(file_desc)
}
}
impl AsFd for Socket {
fn as_fd(&self) -> BorrowedFd<'_> {
self.0.as_fd()
}
}
impl AsRawFd for Socket {
fn as_raw_fd(&self) -> RawFd {
self.0.as_raw_fd()
}
}
impl IntoRawFd for Socket {
fn into_raw_fd(self) -> RawFd {
self.0.into_raw_fd()
}
}
impl FromRawFd for Socket {
unsafe fn from_raw_fd(raw_fd: RawFd) -> Self {
Self(FromRawFd::from_raw_fd(raw_fd))
}
}