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`.
This commit is contained in:
parent
8ad7bc3f42
commit
37dd7a023b
4 changed files with 58 additions and 187 deletions
|
@ -3,7 +3,6 @@
|
||||||
use crate::cmp;
|
use crate::cmp;
|
||||||
use crate::io::{self, Initializer, IoSlice, IoSliceMut, Read};
|
use crate::io::{self, Initializer, IoSlice, IoSliceMut, Read};
|
||||||
use crate::mem;
|
use crate::mem;
|
||||||
use crate::sync::atomic::{AtomicBool, Ordering};
|
|
||||||
use crate::sys::cvt;
|
use crate::sys::cvt;
|
||||||
use crate::sys_common::AsInner;
|
use crate::sys_common::AsInner;
|
||||||
|
|
||||||
|
@ -223,50 +222,9 @@ impl FileDesc {
|
||||||
pub fn duplicate(&self) -> io::Result<FileDesc> {
|
pub fn duplicate(&self) -> io::Result<FileDesc> {
|
||||||
// We want to atomically duplicate this file descriptor and set the
|
// We want to atomically duplicate this file descriptor and set the
|
||||||
// CLOEXEC flag, and currently that's done via F_DUPFD_CLOEXEC. This
|
// CLOEXEC flag, and currently that's done via F_DUPFD_CLOEXEC. This
|
||||||
// flag, however, isn't supported on older Linux kernels (earlier than
|
// is a POSIX flag that was added to Linux in 2.6.24.
|
||||||
// 2.6.24).
|
let fd = cvt(unsafe { libc::fcntl(self.raw(), libc::F_DUPFD_CLOEXEC, 0) })?;
|
||||||
//
|
Ok(FileDesc::new(fd))
|
||||||
// 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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -708,56 +708,7 @@ impl File {
|
||||||
// However, since this is a variadic function, C integer promotion rules mean that on
|
// 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).
|
// 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 = cvt_r(|| unsafe { open64(path.as_ptr(), flags, opts.mode as c_int) })?;
|
||||||
let fd = FileDesc::new(fd);
|
Ok(File(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))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn file_attr(&self) -> io::Result<FileAttr> {
|
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> {
|
pub fn new_raw(fam: c_int, ty: c_int) -> io::Result<Socket> {
|
||||||
unsafe {
|
unsafe {
|
||||||
// On linux we first attempt to pass the SOCK_CLOEXEC flag to
|
cfg_if::cfg_if! {
|
||||||
// atomically create the socket and set it as CLOEXEC. Support for
|
if #[cfg(target_os = "linux")] {
|
||||||
// this option, however, was added in 2.6.27, and we still support
|
// On Linux we pass the SOCK_CLOEXEC flag to atomically create
|
||||||
// 2.6.18 as a kernel, so if the returned error is EINVAL we
|
// the socket and set it as CLOEXEC, added in 2.6.27.
|
||||||
// fallthrough to the fallback.
|
let fd = cvt(libc::socket(fam, ty | libc::SOCK_CLOEXEC, 0))?;
|
||||||
#[cfg(target_os = "linux")]
|
Ok(Socket(FileDesc::new(fd)))
|
||||||
{
|
} else {
|
||||||
match cvt(libc::socket(fam, ty | libc::SOCK_CLOEXEC, 0)) {
|
let fd = cvt(libc::socket(fam, ty, 0))?;
|
||||||
Ok(fd) => return Ok(Socket(FileDesc::new(fd))),
|
let fd = FileDesc::new(fd);
|
||||||
Err(ref e) if e.raw_os_error() == Some(libc::EINVAL) => {}
|
fd.set_cloexec()?;
|
||||||
Err(e) => return Err(e),
|
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 {
|
unsafe {
|
||||||
let mut fds = [0, 0];
|
let mut fds = [0, 0];
|
||||||
|
|
||||||
// Like above, see if we can set cloexec atomically
|
cfg_if::cfg_if! {
|
||||||
#[cfg(target_os = "linux")]
|
if #[cfg(target_os = "linux")] {
|
||||||
{
|
// Like above, set cloexec atomically
|
||||||
match cvt(libc::socketpair(fam, ty | libc::SOCK_CLOEXEC, 0, fds.as_mut_ptr())) {
|
cvt(libc::socketpair(fam, ty | libc::SOCK_CLOEXEC, 0, fds.as_mut_ptr()))?;
|
||||||
Ok(_) => {
|
Ok((Socket(FileDesc::new(fds[0])), Socket(FileDesc::new(fds[1]))))
|
||||||
return Ok((Socket(FileDesc::new(fds[0])), Socket(FileDesc::new(fds[1]))));
|
} else {
|
||||||
}
|
cvt(libc::socketpair(fam, ty, 0, fds.as_mut_ptr()))?;
|
||||||
Err(ref e) if e.raw_os_error() == Some(libc::EINVAL) => {}
|
let a = FileDesc::new(fds[0]);
|
||||||
Err(e) => return Err(e),
|
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> {
|
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
|
// Unfortunately the only known way right now to accept a socket and
|
||||||
// atomically set the CLOEXEC flag is to use the `accept4` syscall on
|
// 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
|
// Linux. This was added in 2.6.28, glibc 2.10 and musl 0.9.5.
|
||||||
// 2.6.18 we must detect this support dynamically.
|
cfg_if::cfg_if! {
|
||||||
#[cfg(target_os = "linux")]
|
if #[cfg(target_os = "linux")] {
|
||||||
{
|
let fd = cvt_r(|| unsafe {
|
||||||
syscall! {
|
libc::accept4(self.0.raw(), storage, len, libc::SOCK_CLOEXEC)
|
||||||
fn accept4(
|
})?;
|
||||||
fd: c_int,
|
Ok(Socket(FileDesc::new(fd)))
|
||||||
addr: *mut sockaddr,
|
} else {
|
||||||
addr_len: *mut socklen_t,
|
let fd = cvt_r(|| unsafe { libc::accept(self.0.raw(), storage, len) })?;
|
||||||
flags: c_int
|
let fd = FileDesc::new(fd);
|
||||||
) -> c_int
|
fd.set_cloexec()?;
|
||||||
}
|
Ok(Socket(fd))
|
||||||
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),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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> {
|
pub fn duplicate(&self) -> io::Result<Socket> {
|
||||||
|
|
|
@ -1,11 +1,8 @@
|
||||||
use crate::io::{self, IoSlice, IoSliceMut};
|
use crate::io::{self, IoSlice, IoSliceMut};
|
||||||
use crate::mem;
|
use crate::mem;
|
||||||
use crate::sync::atomic::{AtomicBool, Ordering};
|
|
||||||
use crate::sys::fd::FileDesc;
|
use crate::sys::fd::FileDesc;
|
||||||
use crate::sys::{cvt, cvt_r};
|
use crate::sys::{cvt, cvt_r};
|
||||||
|
|
||||||
use libc::c_int;
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// Anonymous pipes
|
// Anonymous pipes
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -13,15 +10,11 @@ use libc::c_int;
|
||||||
pub struct AnonPipe(FileDesc);
|
pub struct AnonPipe(FileDesc);
|
||||||
|
|
||||||
pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> {
|
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];
|
let mut fds = [0; 2];
|
||||||
|
|
||||||
// Unfortunately the only known way right now to create atomically set the
|
// 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
|
// 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
|
// 2.6.27, glibc 2.9 and musl 0.9.3.
|
||||||
// support dynamically.
|
|
||||||
if cfg!(any(
|
if cfg!(any(
|
||||||
target_os = "dragonfly",
|
target_os = "dragonfly",
|
||||||
target_os = "freebsd",
|
target_os = "freebsd",
|
||||||
|
@ -29,30 +22,18 @@ pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> {
|
||||||
target_os = "netbsd",
|
target_os = "netbsd",
|
||||||
target_os = "openbsd",
|
target_os = "openbsd",
|
||||||
target_os = "redox"
|
target_os = "redox"
|
||||||
)) && !INVALID.load(Ordering::SeqCst)
|
)) {
|
||||||
{
|
cvt(unsafe { libc::pipe2(fds.as_mut_ptr(), libc::O_CLOEXEC) })?;
|
||||||
// Note that despite calling a glibc function here we may still
|
Ok((AnonPipe(FileDesc::new(fds[0])), AnonPipe(FileDesc::new(fds[1]))))
|
||||||
// get ENOSYS. Glibc has `pipe2` since 2.9 and doesn't try to
|
} else {
|
||||||
// emulate on older kernels, so if you happen to be running on
|
cvt(unsafe { libc::pipe(fds.as_mut_ptr()) })?;
|
||||||
// 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),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cvt(unsafe { libc::pipe(fds.as_mut_ptr()) })?;
|
|
||||||
|
|
||||||
let fd0 = FileDesc::new(fds[0]);
|
let fd0 = FileDesc::new(fds[0]);
|
||||||
let fd1 = FileDesc::new(fds[1]);
|
let fd1 = FileDesc::new(fds[1]);
|
||||||
fd0.set_cloexec()?;
|
fd0.set_cloexec()?;
|
||||||
fd1.set_cloexec()?;
|
fd1.set_cloexec()?;
|
||||||
Ok((AnonPipe(fd0), AnonPipe(fd1)))
|
Ok((AnonPipe(fd0), AnonPipe(fd1)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AnonPipe {
|
impl AnonPipe {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue