Skip to content

Commit 8909ac9

Browse files
authored
Rollup merge of rust-lang#74606 - cuviper:cloexec, r=sfackler
Remove Linux workarounds for missing CLOEXEC support Now that rust-lang#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 rust-lang#74519.
2 parents bea2eed + ae06e13 commit 8909ac9

File tree

6 files changed

+74
-195
lines changed

6 files changed

+74
-195
lines changed

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

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

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> {

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> {

src/libstd/sys/unix/os.rs

+1
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ pub fn errno() -> i32 {
7171

7272
/// Sets the platform-specific value of errno
7373
#[cfg(all(not(target_os = "linux"), not(target_os = "dragonfly")))] // needed for readdir and syscall!
74+
#[allow(dead_code)] // but not all target cfgs actually end up using it
7475
pub fn set_errno(e: i32) {
7576
unsafe { *errno_location() = e as c_int }
7677
}

src/libstd/sys/unix/pipe.rs

+22-39
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,41 @@
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

21-
// Unfortunately the only known way right now to create atomically set the
22-
// 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.
25-
if cfg!(any(
26-
target_os = "dragonfly",
27-
target_os = "freebsd",
28-
target_os = "linux",
29-
target_os = "netbsd",
30-
target_os = "openbsd",
31-
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),
15+
// The only known way right now to create atomically set the CLOEXEC flag is
16+
// to use the `pipe2` syscall. This was added to Linux in 2.6.27, glibc 2.9
17+
// and musl 0.9.3, and some other targets also have it.
18+
cfg_if::cfg_if! {
19+
if #[cfg(any(
20+
target_os = "dragonfly",
21+
target_os = "freebsd",
22+
target_os = "linux",
23+
target_os = "netbsd",
24+
target_os = "openbsd",
25+
target_os = "redox"
26+
))] {
27+
cvt(unsafe { libc::pipe2(fds.as_mut_ptr(), libc::O_CLOEXEC) })?;
28+
Ok((AnonPipe(FileDesc::new(fds[0])), AnonPipe(FileDesc::new(fds[1]))))
29+
} else {
30+
cvt(unsafe { libc::pipe(fds.as_mut_ptr()) })?;
31+
32+
let fd0 = FileDesc::new(fds[0]);
33+
let fd1 = FileDesc::new(fds[1]);
34+
fd0.set_cloexec()?;
35+
fd1.set_cloexec()?;
36+
Ok((AnonPipe(fd0), AnonPipe(fd1)))
4737
}
4838
}
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)))
5639
}
5740

5841
impl AnonPipe {

src/libstd/sys/unix/weak.rs

+5
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@
1616
//! symbol, but that caused Debian to detect an unnecessarily strict versioned
1717
//! dependency on libc6 (#23628).
1818
19+
// There are a variety of `#[cfg]`s controlling which targets are involved in
20+
// each instance of `weak!` and `syscall!`. Rather than trying to unify all of
21+
// that, we'll just allow that some unix targets don't use this module at all.
22+
#![allow(dead_code, unused_macros)]
23+
1924
use crate::ffi::CStr;
2025
use crate::marker;
2126
use crate::mem;

0 commit comments

Comments
 (0)