Skip to content

Commit b2c8c70

Browse files
committedMar 6, 2021
Update ned_fd.
1 parent d045009 commit b2c8c70

File tree

2 files changed

+160
-85
lines changed

2 files changed

+160
-85
lines changed
 

‎library/std/src/sys/unix/net.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ pub mod netc {
1717
#[cfg(target_os = "freertos")]
1818
extern "C" {
1919
#[link_name = "lwip_fcntl"]
20-
pub fn fcntl(s: c_int, cmd: c_int, val: c_int) -> c_int;
20+
pub fn fcntl(s: c_int, cmd: c_int, ...) -> c_int;
2121
#[link_name = "lwip_close"]
2222
pub fn close(s: c_int) -> ssize_t;
2323
#[link_name = "lwip_read"]

‎library/std/src/sys/unix/net_fd.rs

+159-84
Original file line numberDiff line numberDiff line change
@@ -3,37 +3,72 @@
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::{
87
cvt,
9-
net::netc::{self, c_int, c_void, ssize_t},
8+
net::netc::{self, c_int, c_void},
109
};
1110
use crate::sys_common::AsInner;
1211

1312
#[derive(Debug)]
13+
#[rustc_layout_scalar_valid_range_start(0)]
14+
// libstd/os/raw/mod.rs assures me that every libstd-supported platform has a
15+
// 32-bit c_int. Below is -2, in two's complement, but that only works out
16+
// because c_int is 32 bits.
17+
#[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)]
1418
pub struct NetFileDesc {
1519
fd: c_int,
1620
}
1721

18-
fn max_len() -> usize {
19-
// The maximum read limit on most posix-like systems is `SSIZE_MAX`,
20-
// with the man page quoting that if the count of bytes to read is
21-
// greater than `SSIZE_MAX` the result is "unspecified".
22-
//
23-
// On macOS, however, apparently the 64-bit libc is either buggy or
24-
// intentionally showing odd behavior by rejecting any read with a size
25-
// larger than or equal to INT_MAX. To handle both of these the read
26-
// size is capped on both platforms.
27-
if cfg!(target_os = "macos") {
28-
<c_int>::max_value() as usize - 1
29-
} else {
30-
<ssize_t>::max_value() as usize
31-
}
22+
// The maximum read limit on most POSIX-like systems is `SSIZE_MAX`,
23+
// with the man page quoting that if the count of bytes to read is
24+
// greater than `SSIZE_MAX` the result is "unspecified".
25+
//
26+
// On macOS, however, apparently the 64-bit libc is either buggy or
27+
// intentionally showing odd behavior by rejecting any read with a size
28+
// larger than or equal to INT_MAX. To handle both of these the read
29+
// size is capped on both platforms.
30+
#[cfg(target_os = "macos")]
31+
const READ_LIMIT: usize = c_int::MAX as usize - 1;
32+
#[cfg(not(target_os = "macos"))]
33+
const READ_LIMIT: usize = netc::ssize_t::MAX as usize;
34+
35+
#[cfg(any(
36+
target_os = "dragonfly",
37+
target_os = "freebsd",
38+
target_os = "ios",
39+
target_os = "macos",
40+
target_os = "netbsd",
41+
target_os = "openbsd",
42+
))]
43+
const fn max_iov() -> usize {
44+
netc::IOV_MAX as usize
45+
}
46+
47+
#[cfg(any(target_os = "android", target_os = "emscripten", target_os = "linux"))]
48+
const fn max_iov() -> usize {
49+
netc::UIO_MAXIOV as usize
50+
}
51+
52+
#[cfg(not(any(
53+
target_os = "android",
54+
target_os = "dragonfly",
55+
target_os = "emscripten",
56+
target_os = "freebsd",
57+
target_os = "ios",
58+
target_os = "linux",
59+
target_os = "macos",
60+
target_os = "netbsd",
61+
target_os = "openbsd",
62+
)))]
63+
const fn max_iov() -> usize {
64+
16 // The minimum value required by POSIX.
3265
}
3366

3467
impl NetFileDesc {
3568
pub fn new(fd: c_int) -> NetFileDesc {
36-
NetFileDesc { fd }
69+
assert_ne!(fd, -1i32);
70+
// SAFETY: we just asserted that the value is in the valid range and isn't `-1` (the only value bigger than `0xFF_FF_FF_FE` unsigned)
71+
unsafe { NetFileDesc { fd } }
3772
}
3873

3974
pub fn raw(&self) -> c_int {
@@ -49,7 +84,7 @@ impl NetFileDesc {
4984

5085
pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
5186
let ret = cvt(unsafe {
52-
netc::read(self.fd, buf.as_mut_ptr() as *mut c_void, cmp::min(buf.len(), max_len()))
87+
netc::read(self.fd, buf.as_mut_ptr() as *mut c_void, cmp::min(buf.len(), READ_LIMIT))
5388
})?;
5489
Ok(ret as usize)
5590
}
@@ -59,7 +94,7 @@ impl NetFileDesc {
5994
netc::readv(
6095
self.fd,
6196
bufs.as_ptr() as *const netc::iovec,
62-
cmp::min(bufs.len(), c_int::max_value() as usize) as c_int,
97+
cmp::min(bufs.len(), max_iov()) as c_int,
6398
)
6499
})?;
65100
Ok(ret as usize)
@@ -75,9 +110,38 @@ impl NetFileDesc {
75110
(&mut me).read_to_end(buf)
76111
}
77112

113+
pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
114+
#[cfg(target_os = "android")]
115+
use super::android::cvt_pread64;
116+
117+
#[cfg(not(target_os = "android"))]
118+
unsafe fn cvt_pread64(
119+
fd: c_int,
120+
buf: *mut c_void,
121+
count: usize,
122+
offset: i64,
123+
) -> io::Result<isize> {
124+
#[cfg(not(target_os = "linux"))]
125+
use netc::pread as pread64;
126+
#[cfg(target_os = "linux")]
127+
use netc::pread64;
128+
cvt(pread64(fd, buf, count, offset))
129+
}
130+
131+
unsafe {
132+
cvt_pread64(
133+
self.fd,
134+
buf.as_mut_ptr() as *mut c_void,
135+
cmp::min(buf.len(), READ_LIMIT),
136+
offset as i64,
137+
)
138+
.map(|n| n as usize)
139+
}
140+
}
141+
78142
pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
79143
let ret = cvt(unsafe {
80-
netc::write(self.fd, buf.as_ptr() as *const c_void, cmp::min(buf.len(), max_len()))
144+
netc::write(self.fd, buf.as_ptr() as *const c_void, cmp::min(buf.len(), READ_LIMIT))
81145
})?;
82146
Ok(ret as usize)
83147
}
@@ -87,7 +151,7 @@ impl NetFileDesc {
87151
netc::writev(
88152
self.fd,
89153
bufs.as_ptr() as *const netc::iovec,
90-
cmp::min(bufs.len(), c_int::max_value() as usize) as c_int,
154+
cmp::min(bufs.len(), max_iov()) as c_int,
91155
)
92156
})?;
93157
Ok(ret as usize)
@@ -98,10 +162,45 @@ impl NetFileDesc {
98162
true
99163
}
100164

165+
pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
166+
#[cfg(target_os = "android")]
167+
use super::android::cvt_pwrite64;
168+
169+
#[cfg(not(target_os = "android"))]
170+
unsafe fn cvt_pwrite64(
171+
fd: c_int,
172+
buf: *const c_void,
173+
count: usize,
174+
offset: i64,
175+
) -> io::Result<isize> {
176+
#[cfg(not(target_os = "linux"))]
177+
use netc::pwrite as pwrite64;
178+
#[cfg(target_os = "linux")]
179+
use netc::pwrite64;
180+
cvt(pwrite64(fd, buf, count, offset))
181+
}
182+
183+
unsafe {
184+
cvt_pwrite64(
185+
self.fd,
186+
buf.as_ptr() as *const c_void,
187+
cmp::min(buf.len(), READ_LIMIT),
188+
offset as i64,
189+
)
190+
.map(|n| n as usize)
191+
}
192+
}
193+
101194
#[cfg(target_os = "linux")]
102195
pub fn get_cloexec(&self) -> io::Result<bool> {
103196
unsafe { Ok((cvt(netc::fcntl(self.fd, netc::F_GETFD))? & netc::FD_CLOEXEC) != 0) }
104197
}
198+
// Setting `FD_CLOEXEC` is not supported on FreeRTOS
199+
// since there is no `exec` functionality.
200+
#[cfg(target_os = "freertos")]
201+
pub fn set_cloexec(&self) -> io::Result<()> {
202+
Ok(())
203+
}
105204

106205
#[cfg(not(any(
107206
target_env = "newlib",
@@ -112,27 +211,26 @@ impl NetFileDesc {
112211
target_os = "l4re",
113212
target_os = "linux",
114213
target_os = "haiku",
115-
target_os = "redox"
214+
target_os = "redox",
215+
target_os = "vxworks"
116216
)))]
117217
pub fn set_cloexec(&self) -> io::Result<()> {
118218
unsafe {
119219
cvt(netc::ioctl(self.fd, netc::FIOCLEX))?;
120220
Ok(())
121221
}
122222
}
123-
#[cfg(all(
124-
any(
125-
target_env = "newlib",
126-
target_os = "solaris",
127-
target_os = "illumos",
128-
target_os = "emscripten",
129-
target_os = "fuchsia",
130-
target_os = "l4re",
131-
target_os = "linux",
132-
target_os = "haiku",
133-
target_os = "redox"
134-
),
135-
not(target_os = "freertos")
223+
#[cfg(any(
224+
all(target_env = "newlib", not(target_os = "freertos")),
225+
target_os = "solaris",
226+
target_os = "illumos",
227+
target_os = "emscripten",
228+
target_os = "fuchsia",
229+
target_os = "l4re",
230+
target_os = "linux",
231+
target_os = "haiku",
232+
target_os = "redox",
233+
target_os = "vxworks"
136234
))]
137235
pub fn set_cloexec(&self) -> io::Result<()> {
138236
unsafe {
@@ -145,60 +243,37 @@ impl NetFileDesc {
145243
}
146244
}
147245

148-
// Setting `FD_CLOEXEC` is not supported on FreeRTOS
149-
// since there is no `exec` functionality.
150-
#[cfg(target_os = "freertos")]
151-
pub fn set_cloexec(&self) -> io::Result<()> {
152-
Ok(())
246+
#[cfg(target_os = "linux")]
247+
pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
248+
unsafe {
249+
let v = nonblocking as c_int;
250+
cvt(netc::ioctl(self.fd, netc::FIONBIO, &v))?;
251+
Ok(())
252+
}
253+
}
254+
255+
#[cfg(not(target_os = "linux"))]
256+
pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
257+
unsafe {
258+
let previous = cvt(netc::fcntl(self.fd, netc::F_GETFL))?;
259+
let new = if nonblocking {
260+
previous | netc::O_NONBLOCK
261+
} else {
262+
previous & !netc::O_NONBLOCK
263+
};
264+
if new != previous {
265+
cvt(netc::fcntl(self.fd, netc::F_SETFL, new))?;
266+
}
267+
Ok(())
268+
}
153269
}
154270

155271
pub fn duplicate(&self) -> io::Result<NetFileDesc> {
156272
// We want to atomically duplicate this file descriptor and set the
157273
// CLOEXEC flag, and currently that's done via F_DUPFD_CLOEXEC. This
158-
// flag, however, isn't supported on older Linux kernels (earlier than
159-
// 2.6.24).
160-
//
161-
// To detect this and ensure that CLOEXEC is still set, we
162-
// follow a strategy similar to musl [1] where if passing
163-
// F_DUPFD_CLOEXEC causes `fcntl` to return EINVAL it means it's not
164-
// supported (the third parameter, 0, is always valid), so we stop
165-
// trying that.
166-
//
167-
// Also note that Android doesn't have F_DUPFD_CLOEXEC, but get it to
168-
// resolve so we at least compile this.
169-
//
170-
// [1]: http://comments.gmane.org/gmane.linux.lib.musl.general/2963
171-
#[cfg(any(target_os = "android", target_os = "haiku"))]
172-
use netc::F_DUPFD as F_DUPFD_CLOEXEC;
173-
#[cfg(not(any(target_os = "android", target_os = "haiku")))]
174-
use netc::F_DUPFD_CLOEXEC;
175-
176-
let make_filedesc = |fd| {
177-
let fd = NetFileDesc::new(fd);
178-
fd.set_cloexec()?;
179-
Ok(fd)
180-
};
181-
static TRY_CLOEXEC: AtomicBool = AtomicBool::new(!cfg!(target_os = "android"));
182-
let fd = self.raw();
183-
if TRY_CLOEXEC.load(Ordering::Relaxed) {
184-
match cvt(unsafe { netc::fcntl(fd, F_DUPFD_CLOEXEC, 0) }) {
185-
// We *still* call the `set_cloexec` method as apparently some
186-
// linux kernel at some point stopped setting CLOEXEC even
187-
// though it reported doing so on F_DUPFD_CLOEXEC.
188-
Ok(fd) => {
189-
return Ok(if cfg!(target_os = "linux") {
190-
make_filedesc(fd)?
191-
} else {
192-
NetFileDesc::new(fd)
193-
});
194-
}
195-
Err(ref e) if e.raw_os_error() == Some(netc::EINVAL) => {
196-
TRY_CLOEXEC.store(false, Ordering::Relaxed);
197-
}
198-
Err(e) => return Err(e),
199-
}
200-
}
201-
cvt(unsafe { netc::fcntl(fd, netc::F_DUPFD, 0) }).and_then(make_filedesc)
274+
// is a POSIX flag that was added to Linux in 2.6.24.
275+
let fd = cvt(unsafe { netc::fcntl(self.raw(), netc::F_DUPFD_CLOEXEC, 0) })?;
276+
Ok(NetFileDesc::new(fd))
202277
}
203278
}
204279

0 commit comments

Comments
 (0)
Please sign in to comment.