Rollup merge of #74606 - cuviper:cloexec, r=sfackler
Remove Linux workarounds for missing CLOEXEC support Now that #74163 updated the minimum Linux kernel to 2.6.32, we can assume the availability of APIs that open file descriptors that are already set to close on exec, including the flags `O_CLOEXEC`, `SOCK_CLOEXEC`, and `F_DUPFD_CLOEXEC`. Closes #74519.
This commit is contained in:
commit
8909ac97d5
|
@ -3,7 +3,6 @@
|
|||
use crate::cmp;
|
||||
use crate::io::{self, Initializer, IoSlice, IoSliceMut, Read};
|
||||
use crate::mem;
|
||||
use crate::sync::atomic::{AtomicBool, Ordering};
|
||||
use crate::sys::cvt;
|
||||
use crate::sys_common::AsInner;
|
||||
|
||||
|
@ -224,50 +223,9 @@ impl FileDesc {
|
|||
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
|
||||
// flag, however, isn't supported on older Linux kernels (earlier than
|
||||
// 2.6.24).
|
||||
//
|
||||
// To detect this and ensure that CLOEXEC is still set, we
|
||||
// follow a strategy similar to musl [1] where if passing
|
||||
// F_DUPFD_CLOEXEC causes `fcntl` to return EINVAL it means it's not
|
||||
// supported (the third parameter, 0, is always valid), so we stop
|
||||
// trying that.
|
||||
//
|
||||
// Also note that Android doesn't have F_DUPFD_CLOEXEC, but get it to
|
||||
// resolve so we at least compile this.
|
||||
//
|
||||
// [1]: http://comments.gmane.org/gmane.linux.lib.musl.general/2963
|
||||
#[cfg(any(target_os = "android", target_os = "haiku"))]
|
||||
use libc::F_DUPFD as F_DUPFD_CLOEXEC;
|
||||
#[cfg(not(any(target_os = "android", target_os = "haiku")))]
|
||||
use libc::F_DUPFD_CLOEXEC;
|
||||
|
||||
let make_filedesc = |fd| {
|
||||
let fd = FileDesc::new(fd);
|
||||
fd.set_cloexec()?;
|
||||
Ok(fd)
|
||||
};
|
||||
static TRY_CLOEXEC: AtomicBool = AtomicBool::new(!cfg!(target_os = "android"));
|
||||
let fd = self.raw();
|
||||
if TRY_CLOEXEC.load(Ordering::Relaxed) {
|
||||
match cvt(unsafe { libc::fcntl(fd, F_DUPFD_CLOEXEC, 0) }) {
|
||||
// We *still* call the `set_cloexec` method as apparently some
|
||||
// linux kernel at some point stopped setting CLOEXEC even
|
||||
// though it reported doing so on F_DUPFD_CLOEXEC.
|
||||
Ok(fd) => {
|
||||
return Ok(if cfg!(target_os = "linux") {
|
||||
make_filedesc(fd)?
|
||||
} else {
|
||||
FileDesc::new(fd)
|
||||
});
|
||||
}
|
||||
Err(ref e) if e.raw_os_error() == Some(libc::EINVAL) => {
|
||||
TRY_CLOEXEC.store(false, Ordering::Relaxed);
|
||||
}
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
}
|
||||
cvt(unsafe { libc::fcntl(fd, libc::F_DUPFD, 0) }).and_then(make_filedesc)
|
||||
// is a POSIX flag that was added to Linux in 2.6.24.
|
||||
let fd = cvt(unsafe { libc::fcntl(self.raw(), libc::F_DUPFD_CLOEXEC, 0) })?;
|
||||
Ok(FileDesc::new(fd))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -708,56 +708,7 @@ impl File {
|
|||
// However, since this is a variadic function, C integer promotion rules mean that on
|
||||
// the ABI level, this still gets passed as `c_int` (aka `u32` on Unix platforms).
|
||||
let fd = cvt_r(|| unsafe { open64(path.as_ptr(), flags, opts.mode as c_int) })?;
|
||||
let fd = FileDesc::new(fd);
|
||||
|
||||
// Currently the standard library supports Linux 2.6.18 which did not
|
||||
// have the O_CLOEXEC flag (passed above). If we're running on an older
|
||||
// Linux kernel then the flag is just ignored by the OS. After we open
|
||||
// the first file, we check whether it has CLOEXEC set. If it doesn't,
|
||||
// we will explicitly ask for a CLOEXEC fd for every further file we
|
||||
// open, if it does, we will skip that step.
|
||||
//
|
||||
// The CLOEXEC flag, however, is supported on versions of macOS/BSD/etc
|
||||
// that we support, so we only do this on Linux currently.
|
||||
#[cfg(target_os = "linux")]
|
||||
fn ensure_cloexec(fd: &FileDesc) -> io::Result<()> {
|
||||
use crate::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
const OPEN_CLOEXEC_UNKNOWN: usize = 0;
|
||||
const OPEN_CLOEXEC_SUPPORTED: usize = 1;
|
||||
const OPEN_CLOEXEC_NOTSUPPORTED: usize = 2;
|
||||
static OPEN_CLOEXEC: AtomicUsize = AtomicUsize::new(OPEN_CLOEXEC_UNKNOWN);
|
||||
|
||||
let need_to_set;
|
||||
match OPEN_CLOEXEC.load(Ordering::Relaxed) {
|
||||
OPEN_CLOEXEC_UNKNOWN => {
|
||||
need_to_set = !fd.get_cloexec()?;
|
||||
OPEN_CLOEXEC.store(
|
||||
if need_to_set {
|
||||
OPEN_CLOEXEC_NOTSUPPORTED
|
||||
} else {
|
||||
OPEN_CLOEXEC_SUPPORTED
|
||||
},
|
||||
Ordering::Relaxed,
|
||||
);
|
||||
}
|
||||
OPEN_CLOEXEC_SUPPORTED => need_to_set = false,
|
||||
OPEN_CLOEXEC_NOTSUPPORTED => need_to_set = true,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
if need_to_set {
|
||||
fd.set_cloexec()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
fn ensure_cloexec(_: &FileDesc) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
ensure_cloexec(&fd)?;
|
||||
Ok(File(fd))
|
||||
Ok(File(FileDesc::new(fd)))
|
||||
}
|
||||
|
||||
pub fn file_attr(&self) -> io::Result<FileAttr> {
|
||||
|
|
|
@ -54,31 +54,26 @@ impl Socket {
|
|||
|
||||
pub fn new_raw(fam: c_int, ty: c_int) -> io::Result<Socket> {
|
||||
unsafe {
|
||||
// On linux we first attempt to pass the SOCK_CLOEXEC flag to
|
||||
// atomically create the socket and set it as CLOEXEC. Support for
|
||||
// this option, however, was added in 2.6.27, and we still support
|
||||
// 2.6.18 as a kernel, so if the returned error is EINVAL we
|
||||
// fallthrough to the fallback.
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
match cvt(libc::socket(fam, ty | libc::SOCK_CLOEXEC, 0)) {
|
||||
Ok(fd) => return Ok(Socket(FileDesc::new(fd))),
|
||||
Err(ref e) if e.raw_os_error() == Some(libc::EINVAL) => {}
|
||||
Err(e) => return Err(e),
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(target_os = "linux")] {
|
||||
// On Linux we pass the SOCK_CLOEXEC flag to atomically create
|
||||
// the socket and set it as CLOEXEC, added in 2.6.27.
|
||||
let fd = cvt(libc::socket(fam, ty | libc::SOCK_CLOEXEC, 0))?;
|
||||
Ok(Socket(FileDesc::new(fd)))
|
||||
} else {
|
||||
let fd = cvt(libc::socket(fam, ty, 0))?;
|
||||
let fd = FileDesc::new(fd);
|
||||
fd.set_cloexec()?;
|
||||
let socket = Socket(fd);
|
||||
|
||||
// macOS and iOS use `SO_NOSIGPIPE` as a `setsockopt`
|
||||
// flag to disable `SIGPIPE` emission on socket.
|
||||
#[cfg(target_vendor = "apple")]
|
||||
setsockopt(&socket, libc::SOL_SOCKET, libc::SO_NOSIGPIPE, 1)?;
|
||||
|
||||
Ok(socket)
|
||||
}
|
||||
}
|
||||
|
||||
let fd = cvt(libc::socket(fam, ty, 0))?;
|
||||
let fd = FileDesc::new(fd);
|
||||
fd.set_cloexec()?;
|
||||
let socket = Socket(fd);
|
||||
|
||||
// macOS and iOS use `SO_NOSIGPIPE` as a `setsockopt`
|
||||
// flag to disable `SIGPIPE` emission on socket.
|
||||
#[cfg(target_vendor = "apple")]
|
||||
setsockopt(&socket, libc::SOL_SOCKET, libc::SO_NOSIGPIPE, 1)?;
|
||||
|
||||
Ok(socket)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -86,24 +81,20 @@ impl Socket {
|
|||
unsafe {
|
||||
let mut fds = [0, 0];
|
||||
|
||||
// Like above, see if we can set cloexec atomically
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
match cvt(libc::socketpair(fam, ty | libc::SOCK_CLOEXEC, 0, fds.as_mut_ptr())) {
|
||||
Ok(_) => {
|
||||
return Ok((Socket(FileDesc::new(fds[0])), Socket(FileDesc::new(fds[1]))));
|
||||
}
|
||||
Err(ref e) if e.raw_os_error() == Some(libc::EINVAL) => {}
|
||||
Err(e) => return Err(e),
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(target_os = "linux")] {
|
||||
// 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]))))
|
||||
} else {
|
||||
cvt(libc::socketpair(fam, ty, 0, fds.as_mut_ptr()))?;
|
||||
let a = FileDesc::new(fds[0]);
|
||||
let b = FileDesc::new(fds[1]);
|
||||
a.set_cloexec()?;
|
||||
b.set_cloexec()?;
|
||||
Ok((Socket(a), Socket(b)))
|
||||
}
|
||||
}
|
||||
|
||||
cvt(libc::socketpair(fam, ty, 0, fds.as_mut_ptr()))?;
|
||||
let a = FileDesc::new(fds[0]);
|
||||
let b = FileDesc::new(fds[1]);
|
||||
a.set_cloexec()?;
|
||||
b.set_cloexec()?;
|
||||
Ok((Socket(a), Socket(b)))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -177,30 +168,20 @@ impl Socket {
|
|||
pub fn accept(&self, storage: *mut sockaddr, len: *mut socklen_t) -> io::Result<Socket> {
|
||||
// Unfortunately the only known way right now to accept a socket and
|
||||
// atomically set the CLOEXEC flag is to use the `accept4` syscall on
|
||||
// Linux. This was added in 2.6.28, however, and because we support
|
||||
// 2.6.18 we must detect this support dynamically.
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
syscall! {
|
||||
fn accept4(
|
||||
fd: c_int,
|
||||
addr: *mut sockaddr,
|
||||
addr_len: *mut socklen_t,
|
||||
flags: c_int
|
||||
) -> c_int
|
||||
}
|
||||
let res = cvt_r(|| unsafe { accept4(self.0.raw(), storage, len, libc::SOCK_CLOEXEC) });
|
||||
match res {
|
||||
Ok(fd) => return Ok(Socket(FileDesc::new(fd))),
|
||||
Err(ref e) if e.raw_os_error() == Some(libc::ENOSYS) => {}
|
||||
Err(e) => return Err(e),
|
||||
// Linux. This was added in 2.6.28, glibc 2.10 and musl 0.9.5.
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(target_os = "linux")] {
|
||||
let fd = cvt_r(|| unsafe {
|
||||
libc::accept4(self.0.raw(), storage, len, libc::SOCK_CLOEXEC)
|
||||
})?;
|
||||
Ok(Socket(FileDesc::new(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))
|
||||
}
|
||||
}
|
||||
|
||||
let fd = cvt_r(|| unsafe { libc::accept(self.0.raw(), storage, len) })?;
|
||||
let fd = FileDesc::new(fd);
|
||||
fd.set_cloexec()?;
|
||||
Ok(Socket(fd))
|
||||
}
|
||||
|
||||
pub fn duplicate(&self) -> io::Result<Socket> {
|
||||
|
|
|
@ -71,6 +71,7 @@ pub fn errno() -> i32 {
|
|||
|
||||
/// Sets the platform-specific value of errno
|
||||
#[cfg(all(not(target_os = "linux"), not(target_os = "dragonfly")))] // needed for readdir and syscall!
|
||||
#[allow(dead_code)] // but not all target cfgs actually end up using it
|
||||
pub fn set_errno(e: i32) {
|
||||
unsafe { *errno_location() = e as c_int }
|
||||
}
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
use crate::io::{self, IoSlice, IoSliceMut};
|
||||
use crate::mem;
|
||||
use crate::sync::atomic::{AtomicBool, Ordering};
|
||||
use crate::sys::fd::FileDesc;
|
||||
use crate::sys::{cvt, cvt_r};
|
||||
|
||||
use libc::c_int;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Anonymous pipes
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -13,46 +10,32 @@ use libc::c_int;
|
|||
pub struct AnonPipe(FileDesc);
|
||||
|
||||
pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> {
|
||||
syscall! { fn pipe2(fds: *mut c_int, flags: c_int) -> c_int }
|
||||
static INVALID: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
let mut fds = [0; 2];
|
||||
|
||||
// Unfortunately the only known way right now to create atomically set the
|
||||
// CLOEXEC flag is to use the `pipe2` syscall on Linux. This was added in
|
||||
// 2.6.27, however, and because we support 2.6.18 we must detect this
|
||||
// support dynamically.
|
||||
if cfg!(any(
|
||||
target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "linux",
|
||||
target_os = "netbsd",
|
||||
target_os = "openbsd",
|
||||
target_os = "redox"
|
||||
)) && !INVALID.load(Ordering::SeqCst)
|
||||
{
|
||||
// Note that despite calling a glibc function here we may still
|
||||
// get ENOSYS. Glibc has `pipe2` since 2.9 and doesn't try to
|
||||
// emulate on older kernels, so if you happen to be running on
|
||||
// an older kernel you may see `pipe2` as a symbol but still not
|
||||
// see the syscall.
|
||||
match cvt(unsafe { pipe2(fds.as_mut_ptr(), libc::O_CLOEXEC) }) {
|
||||
Ok(_) => {
|
||||
return Ok((AnonPipe(FileDesc::new(fds[0])), AnonPipe(FileDesc::new(fds[1]))));
|
||||
}
|
||||
Err(ref e) if e.raw_os_error() == Some(libc::ENOSYS) => {
|
||||
INVALID.store(true, Ordering::SeqCst);
|
||||
}
|
||||
Err(e) => return Err(e),
|
||||
// The only known way right now to create atomically set the CLOEXEC flag is
|
||||
// to use the `pipe2` syscall. This was added to Linux in 2.6.27, glibc 2.9
|
||||
// and musl 0.9.3, and some other targets also have it.
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(any(
|
||||
target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "linux",
|
||||
target_os = "netbsd",
|
||||
target_os = "openbsd",
|
||||
target_os = "redox"
|
||||
))] {
|
||||
cvt(unsafe { libc::pipe2(fds.as_mut_ptr(), libc::O_CLOEXEC) })?;
|
||||
Ok((AnonPipe(FileDesc::new(fds[0])), AnonPipe(FileDesc::new(fds[1]))))
|
||||
} else {
|
||||
cvt(unsafe { libc::pipe(fds.as_mut_ptr()) })?;
|
||||
|
||||
let fd0 = FileDesc::new(fds[0]);
|
||||
let fd1 = FileDesc::new(fds[1]);
|
||||
fd0.set_cloexec()?;
|
||||
fd1.set_cloexec()?;
|
||||
Ok((AnonPipe(fd0), AnonPipe(fd1)))
|
||||
}
|
||||
}
|
||||
cvt(unsafe { libc::pipe(fds.as_mut_ptr()) })?;
|
||||
|
||||
let fd0 = FileDesc::new(fds[0]);
|
||||
let fd1 = FileDesc::new(fds[1]);
|
||||
fd0.set_cloexec()?;
|
||||
fd1.set_cloexec()?;
|
||||
Ok((AnonPipe(fd0), AnonPipe(fd1)))
|
||||
}
|
||||
|
||||
impl AnonPipe {
|
||||
|
|
|
@ -16,6 +16,11 @@
|
|||
//! symbol, but that caused Debian to detect an unnecessarily strict versioned
|
||||
//! dependency on libc6 (#23628).
|
||||
|
||||
// There are a variety of `#[cfg]`s controlling which targets are involved in
|
||||
// each instance of `weak!` and `syscall!`. Rather than trying to unify all of
|
||||
// that, we'll just allow that some unix targets don't use this module at all.
|
||||
#![allow(dead_code, unused_macros)]
|
||||
|
||||
use crate::ffi::CStr;
|
||||
use crate::marker;
|
||||
use crate::mem;
|
||||
|
|
Loading…
Reference in New Issue