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