Skip to content

Commit 37dd7a0

Browse files
committed
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`.
1 parent 8ad7bc3 commit 37dd7a0

File tree

4 files changed

+58
-187
lines changed

4 files changed

+58
-187
lines changed

Diff for: src/libstd/sys/unix/fd.rs

+3-45
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
use crate::cmp;
44
use crate::io::{self, Initializer, IoSlice, IoSliceMut, Read};
55
use crate::mem;
6-
use crate::sync::atomic::{AtomicBool, Ordering};
76
use crate::sys::cvt;
87
use crate::sys_common::AsInner;
98

@@ -223,50 +222,9 @@ impl FileDesc {
223222
pub fn duplicate(&self) -> io::Result<FileDesc> {
224223
// We want to atomically duplicate this file descriptor and set the
225224
// CLOEXEC flag, and currently that's done via F_DUPFD_CLOEXEC. This
226-
// flag, however, isn't supported on older Linux kernels (earlier than
227-
// 2.6.24).
228-
//
229-
// To detect this and ensure that CLOEXEC is still set, we
230-
// follow a strategy similar to musl [1] where if passing
231-
// F_DUPFD_CLOEXEC causes `fcntl` to return EINVAL it means it's not
232-
// supported (the third parameter, 0, is always valid), so we stop
233-
// trying that.
234-
//
235-
// Also note that Android doesn't have F_DUPFD_CLOEXEC, but get it to
236-
// resolve so we at least compile this.
237-
//
238-
// [1]: http://comments.gmane.org/gmane.linux.lib.musl.general/2963
239-
#[cfg(any(target_os = "android", target_os = "haiku"))]
240-
use libc::F_DUPFD as F_DUPFD_CLOEXEC;
241-
#[cfg(not(any(target_os = "android", target_os = "haiku")))]
242-
use libc::F_DUPFD_CLOEXEC;
243-
244-
let make_filedesc = |fd| {
245-
let fd = FileDesc::new(fd);
246-
fd.set_cloexec()?;
247-
Ok(fd)
248-
};
249-
static TRY_CLOEXEC: AtomicBool = AtomicBool::new(!cfg!(target_os = "android"));
250-
let fd = self.raw();
251-
if TRY_CLOEXEC.load(Ordering::Relaxed) {
252-
match cvt(unsafe { libc::fcntl(fd, F_DUPFD_CLOEXEC, 0) }) {
253-
// We *still* call the `set_cloexec` method as apparently some
254-
// linux kernel at some point stopped setting CLOEXEC even
255-
// though it reported doing so on F_DUPFD_CLOEXEC.
256-
Ok(fd) => {
257-
return Ok(if cfg!(target_os = "linux") {
258-
make_filedesc(fd)?
259-
} else {
260-
FileDesc::new(fd)
261-
});
262-
}
263-
Err(ref e) if e.raw_os_error() == Some(libc::EINVAL) => {
264-
TRY_CLOEXEC.store(false, Ordering::Relaxed);
265-
}
266-
Err(e) => return Err(e),
267-
}
268-
}
269-
cvt(unsafe { libc::fcntl(fd, libc::F_DUPFD, 0) }).and_then(make_filedesc)
225+
// is a POSIX flag that was added to Linux in 2.6.24.
226+
let fd = cvt(unsafe { libc::fcntl(self.raw(), libc::F_DUPFD_CLOEXEC, 0) })?;
227+
Ok(FileDesc::new(fd))
270228
}
271229
}
272230

Diff for: src/libstd/sys/unix/fs.rs

+1-50
Original file line numberDiff line numberDiff line change
@@ -708,56 +708,7 @@ impl File {
708708
// However, since this is a variadic function, C integer promotion rules mean that on
709709
// the ABI level, this still gets passed as `c_int` (aka `u32` on Unix platforms).
710710
let fd = cvt_r(|| unsafe { open64(path.as_ptr(), flags, opts.mode as c_int) })?;
711-
let fd = FileDesc::new(fd);
712-
713-
// Currently the standard library supports Linux 2.6.18 which did not
714-
// have the O_CLOEXEC flag (passed above). If we're running on an older
715-
// Linux kernel then the flag is just ignored by the OS. After we open
716-
// the first file, we check whether it has CLOEXEC set. If it doesn't,
717-
// we will explicitly ask for a CLOEXEC fd for every further file we
718-
// open, if it does, we will skip that step.
719-
//
720-
// The CLOEXEC flag, however, is supported on versions of macOS/BSD/etc
721-
// that we support, so we only do this on Linux currently.
722-
#[cfg(target_os = "linux")]
723-
fn ensure_cloexec(fd: &FileDesc) -> io::Result<()> {
724-
use crate::sync::atomic::{AtomicUsize, Ordering};
725-
726-
const OPEN_CLOEXEC_UNKNOWN: usize = 0;
727-
const OPEN_CLOEXEC_SUPPORTED: usize = 1;
728-
const OPEN_CLOEXEC_NOTSUPPORTED: usize = 2;
729-
static OPEN_CLOEXEC: AtomicUsize = AtomicUsize::new(OPEN_CLOEXEC_UNKNOWN);
730-
731-
let need_to_set;
732-
match OPEN_CLOEXEC.load(Ordering::Relaxed) {
733-
OPEN_CLOEXEC_UNKNOWN => {
734-
need_to_set = !fd.get_cloexec()?;
735-
OPEN_CLOEXEC.store(
736-
if need_to_set {
737-
OPEN_CLOEXEC_NOTSUPPORTED
738-
} else {
739-
OPEN_CLOEXEC_SUPPORTED
740-
},
741-
Ordering::Relaxed,
742-
);
743-
}
744-
OPEN_CLOEXEC_SUPPORTED => need_to_set = false,
745-
OPEN_CLOEXEC_NOTSUPPORTED => need_to_set = true,
746-
_ => unreachable!(),
747-
}
748-
if need_to_set {
749-
fd.set_cloexec()?;
750-
}
751-
Ok(())
752-
}
753-
754-
#[cfg(not(target_os = "linux"))]
755-
fn ensure_cloexec(_: &FileDesc) -> io::Result<()> {
756-
Ok(())
757-
}
758-
759-
ensure_cloexec(&fd)?;
760-
Ok(File(fd))
711+
Ok(File(FileDesc::new(fd)))
761712
}
762713

763714
pub fn file_attr(&self) -> io::Result<FileAttr> {

Diff for: src/libstd/sys/unix/net.rs

+42-61
Original file line numberDiff line numberDiff line change
@@ -54,56 +54,47 @@ impl Socket {
5454

5555
pub fn new_raw(fam: c_int, ty: c_int) -> io::Result<Socket> {
5656
unsafe {
57-
// On linux we first attempt to pass the SOCK_CLOEXEC flag to
58-
// atomically create the socket and set it as CLOEXEC. Support for
59-
// this option, however, was added in 2.6.27, and we still support
60-
// 2.6.18 as a kernel, so if the returned error is EINVAL we
61-
// fallthrough to the fallback.
62-
#[cfg(target_os = "linux")]
63-
{
64-
match cvt(libc::socket(fam, ty | libc::SOCK_CLOEXEC, 0)) {
65-
Ok(fd) => return Ok(Socket(FileDesc::new(fd))),
66-
Err(ref e) if e.raw_os_error() == Some(libc::EINVAL) => {}
67-
Err(e) => return Err(e),
68-
}
69-
}
70-
71-
let fd = cvt(libc::socket(fam, ty, 0))?;
72-
let fd = FileDesc::new(fd);
73-
fd.set_cloexec()?;
74-
let socket = Socket(fd);
57+
cfg_if::cfg_if! {
58+
if #[cfg(target_os = "linux")] {
59+
// On Linux we pass the SOCK_CLOEXEC flag to atomically create
60+
// the socket and set it as CLOEXEC, added in 2.6.27.
61+
let fd = cvt(libc::socket(fam, ty | libc::SOCK_CLOEXEC, 0))?;
62+
Ok(Socket(FileDesc::new(fd)))
63+
} else {
64+
let fd = cvt(libc::socket(fam, ty, 0))?;
65+
let fd = FileDesc::new(fd);
66+
fd.set_cloexec()?;
67+
let socket = Socket(fd);
7568

76-
// macOS and iOS use `SO_NOSIGPIPE` as a `setsockopt`
77-
// flag to disable `SIGPIPE` emission on socket.
78-
#[cfg(target_vendor = "apple")]
79-
setsockopt(&socket, libc::SOL_SOCKET, libc::SO_NOSIGPIPE, 1)?;
69+
// macOS and iOS use `SO_NOSIGPIPE` as a `setsockopt`
70+
// flag to disable `SIGPIPE` emission on socket.
71+
#[cfg(target_vendor = "apple")]
72+
setsockopt(&socket, libc::SOL_SOCKET, libc::SO_NOSIGPIPE, 1)?;
8073

81-
Ok(socket)
74+
Ok(socket)
75+
}
76+
}
8277
}
8378
}
8479

8580
pub fn new_pair(fam: c_int, ty: c_int) -> io::Result<(Socket, Socket)> {
8681
unsafe {
8782
let mut fds = [0, 0];
8883

89-
// Like above, see if we can set cloexec atomically
90-
#[cfg(target_os = "linux")]
91-
{
92-
match cvt(libc::socketpair(fam, ty | libc::SOCK_CLOEXEC, 0, fds.as_mut_ptr())) {
93-
Ok(_) => {
94-
return Ok((Socket(FileDesc::new(fds[0])), Socket(FileDesc::new(fds[1]))));
95-
}
96-
Err(ref e) if e.raw_os_error() == Some(libc::EINVAL) => {}
97-
Err(e) => return Err(e),
84+
cfg_if::cfg_if! {
85+
if #[cfg(target_os = "linux")] {
86+
// Like above, set cloexec atomically
87+
cvt(libc::socketpair(fam, ty | libc::SOCK_CLOEXEC, 0, fds.as_mut_ptr()))?;
88+
Ok((Socket(FileDesc::new(fds[0])), Socket(FileDesc::new(fds[1]))))
89+
} else {
90+
cvt(libc::socketpair(fam, ty, 0, fds.as_mut_ptr()))?;
91+
let a = FileDesc::new(fds[0]);
92+
let b = FileDesc::new(fds[1]);
93+
a.set_cloexec()?;
94+
b.set_cloexec()?;
95+
Ok((Socket(a), Socket(b)))
9896
}
9997
}
100-
101-
cvt(libc::socketpair(fam, ty, 0, fds.as_mut_ptr()))?;
102-
let a = FileDesc::new(fds[0]);
103-
let b = FileDesc::new(fds[1]);
104-
a.set_cloexec()?;
105-
b.set_cloexec()?;
106-
Ok((Socket(a), Socket(b)))
10798
}
10899
}
109100

@@ -177,30 +168,20 @@ impl Socket {
177168
pub fn accept(&self, storage: *mut sockaddr, len: *mut socklen_t) -> io::Result<Socket> {
178169
// Unfortunately the only known way right now to accept a socket and
179170
// atomically set the CLOEXEC flag is to use the `accept4` syscall on
180-
// Linux. This was added in 2.6.28, however, and because we support
181-
// 2.6.18 we must detect this support dynamically.
182-
#[cfg(target_os = "linux")]
183-
{
184-
syscall! {
185-
fn accept4(
186-
fd: c_int,
187-
addr: *mut sockaddr,
188-
addr_len: *mut socklen_t,
189-
flags: c_int
190-
) -> c_int
191-
}
192-
let res = cvt_r(|| unsafe { accept4(self.0.raw(), storage, len, libc::SOCK_CLOEXEC) });
193-
match res {
194-
Ok(fd) => return Ok(Socket(FileDesc::new(fd))),
195-
Err(ref e) if e.raw_os_error() == Some(libc::ENOSYS) => {}
196-
Err(e) => return Err(e),
171+
// Linux. This was added in 2.6.28, glibc 2.10 and musl 0.9.5.
172+
cfg_if::cfg_if! {
173+
if #[cfg(target_os = "linux")] {
174+
let fd = cvt_r(|| unsafe {
175+
libc::accept4(self.0.raw(), storage, len, libc::SOCK_CLOEXEC)
176+
})?;
177+
Ok(Socket(FileDesc::new(fd)))
178+
} else {
179+
let fd = cvt_r(|| unsafe { libc::accept(self.0.raw(), storage, len) })?;
180+
let fd = FileDesc::new(fd);
181+
fd.set_cloexec()?;
182+
Ok(Socket(fd))
197183
}
198184
}
199-
200-
let fd = cvt_r(|| unsafe { libc::accept(self.0.raw(), storage, len) })?;
201-
let fd = FileDesc::new(fd);
202-
fd.set_cloexec()?;
203-
Ok(Socket(fd))
204185
}
205186

206187
pub fn duplicate(&self) -> io::Result<Socket> {

Diff for: src/libstd/sys/unix/pipe.rs

+12-31
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,39 @@
11
use crate::io::{self, IoSlice, IoSliceMut};
22
use crate::mem;
3-
use crate::sync::atomic::{AtomicBool, Ordering};
43
use crate::sys::fd::FileDesc;
54
use crate::sys::{cvt, cvt_r};
65

7-
use libc::c_int;
8-
96
////////////////////////////////////////////////////////////////////////////////
107
// Anonymous pipes
118
////////////////////////////////////////////////////////////////////////////////
129

1310
pub struct AnonPipe(FileDesc);
1411

1512
pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> {
16-
syscall! { fn pipe2(fds: *mut c_int, flags: c_int) -> c_int }
17-
static INVALID: AtomicBool = AtomicBool::new(false);
18-
1913
let mut fds = [0; 2];
2014

2115
// Unfortunately the only known way right now to create atomically set the
2216
// CLOEXEC flag is to use the `pipe2` syscall on Linux. This was added in
23-
// 2.6.27, however, and because we support 2.6.18 we must detect this
24-
// support dynamically.
17+
// 2.6.27, glibc 2.9 and musl 0.9.3.
2518
if cfg!(any(
2619
target_os = "dragonfly",
2720
target_os = "freebsd",
2821
target_os = "linux",
2922
target_os = "netbsd",
3023
target_os = "openbsd",
3124
target_os = "redox"
32-
)) && !INVALID.load(Ordering::SeqCst)
33-
{
34-
// Note that despite calling a glibc function here we may still
35-
// get ENOSYS. Glibc has `pipe2` since 2.9 and doesn't try to
36-
// emulate on older kernels, so if you happen to be running on
37-
// an older kernel you may see `pipe2` as a symbol but still not
38-
// see the syscall.
39-
match cvt(unsafe { pipe2(fds.as_mut_ptr(), libc::O_CLOEXEC) }) {
40-
Ok(_) => {
41-
return Ok((AnonPipe(FileDesc::new(fds[0])), AnonPipe(FileDesc::new(fds[1]))));
42-
}
43-
Err(ref e) if e.raw_os_error() == Some(libc::ENOSYS) => {
44-
INVALID.store(true, Ordering::SeqCst);
45-
}
46-
Err(e) => return Err(e),
47-
}
25+
)) {
26+
cvt(unsafe { libc::pipe2(fds.as_mut_ptr(), libc::O_CLOEXEC) })?;
27+
Ok((AnonPipe(FileDesc::new(fds[0])), AnonPipe(FileDesc::new(fds[1]))))
28+
} else {
29+
cvt(unsafe { libc::pipe(fds.as_mut_ptr()) })?;
30+
31+
let fd0 = FileDesc::new(fds[0]);
32+
let fd1 = FileDesc::new(fds[1]);
33+
fd0.set_cloexec()?;
34+
fd1.set_cloexec()?;
35+
Ok((AnonPipe(fd0), AnonPipe(fd1)))
4836
}
49-
cvt(unsafe { libc::pipe(fds.as_mut_ptr()) })?;
50-
51-
let fd0 = FileDesc::new(fds[0]);
52-
let fd1 = FileDesc::new(fds[1]);
53-
fd0.set_cloexec()?;
54-
fd1.set_cloexec()?;
55-
Ok((AnonPipe(fd0), AnonPipe(fd1)))
5637
}
5738

5839
impl AnonPipe {

0 commit comments

Comments
 (0)