]> git.lizzy.rs Git - rust.git/blob - library/std/src/sys/unix/fd.rs
Add fallback for cfg(unix) targets that do not define libc::_SC_IOV_MAX.
[rust.git] / library / std / src / sys / unix / fd.rs
1 #![unstable(reason = "not public", issue = "none", feature = "fd")]
2
3 use crate::cmp;
4 use crate::io::{self, Initializer, IoSlice, IoSliceMut, Read};
5 use crate::mem;
6 #[cfg(not(any(target_os = "redox", target_env = "newlib")))]
7 use crate::sync::atomic::{AtomicUsize, Ordering};
8 use crate::sys::cvt;
9 use crate::sys_common::AsInner;
10
11 use libc::{c_int, c_void};
12
13 #[derive(Debug)]
14 pub struct FileDesc {
15     fd: c_int,
16 }
17
18 // The maximum read limit on most POSIX-like systems is `SSIZE_MAX`,
19 // with the man page quoting that if the count of bytes to read is
20 // greater than `SSIZE_MAX` the result is "unspecified".
21 //
22 // On macOS, however, apparently the 64-bit libc is either buggy or
23 // intentionally showing odd behavior by rejecting any read with a size
24 // larger than or equal to INT_MAX. To handle both of these the read
25 // size is capped on both platforms.
26 #[cfg(target_os = "macos")]
27 const READ_LIMIT: usize = c_int::MAX as usize - 1;
28 #[cfg(not(target_os = "macos"))]
29 const READ_LIMIT: usize = libc::ssize_t::MAX as usize;
30
31 #[cfg(not(any(target_os = "redox", target_env = "newlib")))]
32 fn max_iov() -> usize {
33     static LIM: AtomicUsize = AtomicUsize::new(0);
34
35     let mut lim = LIM.load(Ordering::Relaxed);
36     if lim == 0 {
37         let ret = unsafe { libc::sysconf(libc::_SC_IOV_MAX) };
38
39         // 16 is the minimum value required by POSIX.
40         lim = if ret > 0 { ret as usize } else { 16 };
41         LIM.store(lim, Ordering::Relaxed);
42     }
43
44     lim
45 }
46
47 #[cfg(any(target_os = "redox", target_env = "newlib"))]
48 fn max_iov() -> usize {
49     16 // The minimum value required by POSIX.
50 }
51
52 impl FileDesc {
53     pub fn new(fd: c_int) -> FileDesc {
54         FileDesc { fd }
55     }
56
57     pub fn raw(&self) -> c_int {
58         self.fd
59     }
60
61     /// Extracts the actual file descriptor without closing it.
62     pub fn into_raw(self) -> c_int {
63         let fd = self.fd;
64         mem::forget(self);
65         fd
66     }
67
68     pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
69         let ret = cvt(unsafe {
70             libc::read(self.fd, buf.as_mut_ptr() as *mut c_void, cmp::min(buf.len(), READ_LIMIT))
71         })?;
72         Ok(ret as usize)
73     }
74
75     pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
76         let ret = cvt(unsafe {
77             libc::readv(
78                 self.fd,
79                 bufs.as_ptr() as *const libc::iovec,
80                 cmp::min(bufs.len(), max_iov()) as c_int,
81             )
82         })?;
83         Ok(ret as usize)
84     }
85
86     #[inline]
87     pub fn is_read_vectored(&self) -> bool {
88         true
89     }
90
91     pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> {
92         let mut me = self;
93         (&mut me).read_to_end(buf)
94     }
95
96     pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
97         #[cfg(target_os = "android")]
98         use super::android::cvt_pread64;
99
100         #[cfg(not(target_os = "android"))]
101         unsafe fn cvt_pread64(
102             fd: c_int,
103             buf: *mut c_void,
104             count: usize,
105             offset: i64,
106         ) -> io::Result<isize> {
107             #[cfg(not(target_os = "linux"))]
108             use libc::pread as pread64;
109             #[cfg(target_os = "linux")]
110             use libc::pread64;
111             cvt(pread64(fd, buf, count, offset))
112         }
113
114         unsafe {
115             cvt_pread64(
116                 self.fd,
117                 buf.as_mut_ptr() as *mut c_void,
118                 cmp::min(buf.len(), READ_LIMIT),
119                 offset as i64,
120             )
121             .map(|n| n as usize)
122         }
123     }
124
125     pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
126         let ret = cvt(unsafe {
127             libc::write(self.fd, buf.as_ptr() as *const c_void, cmp::min(buf.len(), READ_LIMIT))
128         })?;
129         Ok(ret as usize)
130     }
131
132     pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
133         let ret = cvt(unsafe {
134             libc::writev(
135                 self.fd,
136                 bufs.as_ptr() as *const libc::iovec,
137                 cmp::min(bufs.len(), max_iov()) as c_int,
138             )
139         })?;
140         Ok(ret as usize)
141     }
142
143     #[inline]
144     pub fn is_write_vectored(&self) -> bool {
145         true
146     }
147
148     pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
149         #[cfg(target_os = "android")]
150         use super::android::cvt_pwrite64;
151
152         #[cfg(not(target_os = "android"))]
153         unsafe fn cvt_pwrite64(
154             fd: c_int,
155             buf: *const c_void,
156             count: usize,
157             offset: i64,
158         ) -> io::Result<isize> {
159             #[cfg(not(target_os = "linux"))]
160             use libc::pwrite as pwrite64;
161             #[cfg(target_os = "linux")]
162             use libc::pwrite64;
163             cvt(pwrite64(fd, buf, count, offset))
164         }
165
166         unsafe {
167             cvt_pwrite64(
168                 self.fd,
169                 buf.as_ptr() as *const c_void,
170                 cmp::min(buf.len(), READ_LIMIT),
171                 offset as i64,
172             )
173             .map(|n| n as usize)
174         }
175     }
176
177     #[cfg(target_os = "linux")]
178     pub fn get_cloexec(&self) -> io::Result<bool> {
179         unsafe { Ok((cvt(libc::fcntl(self.fd, libc::F_GETFD))? & libc::FD_CLOEXEC) != 0) }
180     }
181
182     #[cfg(not(any(
183         target_env = "newlib",
184         target_os = "solaris",
185         target_os = "illumos",
186         target_os = "emscripten",
187         target_os = "fuchsia",
188         target_os = "l4re",
189         target_os = "linux",
190         target_os = "haiku",
191         target_os = "redox"
192     )))]
193     pub fn set_cloexec(&self) -> io::Result<()> {
194         unsafe {
195             cvt(libc::ioctl(self.fd, libc::FIOCLEX))?;
196             Ok(())
197         }
198     }
199     #[cfg(any(
200         target_env = "newlib",
201         target_os = "solaris",
202         target_os = "illumos",
203         target_os = "emscripten",
204         target_os = "fuchsia",
205         target_os = "l4re",
206         target_os = "linux",
207         target_os = "haiku",
208         target_os = "redox"
209     ))]
210     pub fn set_cloexec(&self) -> io::Result<()> {
211         unsafe {
212             let previous = cvt(libc::fcntl(self.fd, libc::F_GETFD))?;
213             let new = previous | libc::FD_CLOEXEC;
214             if new != previous {
215                 cvt(libc::fcntl(self.fd, libc::F_SETFD, new))?;
216             }
217             Ok(())
218         }
219     }
220
221     #[cfg(target_os = "linux")]
222     pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
223         unsafe {
224             let v = nonblocking as c_int;
225             cvt(libc::ioctl(self.fd, libc::FIONBIO, &v))?;
226             Ok(())
227         }
228     }
229
230     #[cfg(not(target_os = "linux"))]
231     pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
232         unsafe {
233             let previous = cvt(libc::fcntl(self.fd, libc::F_GETFL))?;
234             let new = if nonblocking {
235                 previous | libc::O_NONBLOCK
236             } else {
237                 previous & !libc::O_NONBLOCK
238             };
239             if new != previous {
240                 cvt(libc::fcntl(self.fd, libc::F_SETFL, new))?;
241             }
242             Ok(())
243         }
244     }
245
246     pub fn duplicate(&self) -> io::Result<FileDesc> {
247         // We want to atomically duplicate this file descriptor and set the
248         // CLOEXEC flag, and currently that's done via F_DUPFD_CLOEXEC. This
249         // is a POSIX flag that was added to Linux in 2.6.24.
250         let fd = cvt(unsafe { libc::fcntl(self.raw(), libc::F_DUPFD_CLOEXEC, 0) })?;
251         Ok(FileDesc::new(fd))
252     }
253 }
254
255 impl<'a> Read for &'a FileDesc {
256     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
257         (**self).read(buf)
258     }
259
260     #[inline]
261     unsafe fn initializer(&self) -> Initializer {
262         Initializer::nop()
263     }
264 }
265
266 impl AsInner<c_int> for FileDesc {
267     fn as_inner(&self) -> &c_int {
268         &self.fd
269     }
270 }
271
272 impl Drop for FileDesc {
273     fn drop(&mut self) {
274         // Note that errors are ignored when closing a file descriptor. The
275         // reason for this is that if an error occurs we don't actually know if
276         // the file descriptor was closed or not, and if we retried (for
277         // something like EINTR), we might close another valid file descriptor
278         // opened after we closed ours.
279         let _ = unsafe { libc::close(self.fd) };
280     }
281 }
282
283 #[cfg(test)]
284 mod tests {
285     use super::{FileDesc, IoSlice};
286
287     #[test]
288     fn limit_vector_count() {
289         let stdout = FileDesc { fd: 1 };
290         let bufs = (0..1500).map(|_| IoSlice::new(&[])).collect::<Vec<_>>();
291
292         assert!(stdout.write_vectored(&bufs).is_ok());
293     }
294 }