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