]> git.lizzy.rs Git - rust.git/blob - library/std/src/sys/unix/fd.rs
Rollup merge of #88159 - spastorino:fix-unused-generic-param-test, r=oli-obk
[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     #[cfg(not(target_os = "espidf"))]
95     pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
96         let ret = cvt(unsafe {
97             libc::readv(
98                 self.fd,
99                 bufs.as_ptr() as *const libc::iovec,
100                 cmp::min(bufs.len(), max_iov()) as c_int,
101             )
102         })?;
103         Ok(ret as usize)
104     }
105
106     #[cfg(target_os = "espidf")]
107     pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
108         return crate::io::default_read_vectored(|b| self.read(b), bufs);
109     }
110
111     #[inline]
112     pub fn is_read_vectored(&self) -> bool {
113         cfg!(not(target_os = "espidf"))
114     }
115
116     pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> {
117         let mut me = self;
118         (&mut me).read_to_end(buf)
119     }
120
121     pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
122         #[cfg(target_os = "android")]
123         use super::android::cvt_pread64;
124
125         #[cfg(not(target_os = "android"))]
126         unsafe fn cvt_pread64(
127             fd: c_int,
128             buf: *mut c_void,
129             count: usize,
130             offset: i64,
131         ) -> io::Result<isize> {
132             #[cfg(not(target_os = "linux"))]
133             use libc::pread as pread64;
134             #[cfg(target_os = "linux")]
135             use libc::pread64;
136             cvt(pread64(fd, buf, count, offset))
137         }
138
139         unsafe {
140             cvt_pread64(
141                 self.fd,
142                 buf.as_mut_ptr() as *mut c_void,
143                 cmp::min(buf.len(), READ_LIMIT),
144                 offset as i64,
145             )
146             .map(|n| n as usize)
147         }
148     }
149
150     pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
151         let ret = cvt(unsafe {
152             libc::write(self.fd, buf.as_ptr() as *const c_void, cmp::min(buf.len(), READ_LIMIT))
153         })?;
154         Ok(ret as usize)
155     }
156
157     #[cfg(not(target_os = "espidf"))]
158     pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
159         let ret = cvt(unsafe {
160             libc::writev(
161                 self.fd,
162                 bufs.as_ptr() as *const libc::iovec,
163                 cmp::min(bufs.len(), max_iov()) as c_int,
164             )
165         })?;
166         Ok(ret as usize)
167     }
168
169     #[cfg(target_os = "espidf")]
170     pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
171         return crate::io::default_write_vectored(|b| self.write(b), bufs);
172     }
173
174     #[inline]
175     pub fn is_write_vectored(&self) -> bool {
176         cfg!(not(target_os = "espidf"))
177     }
178
179     pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
180         #[cfg(target_os = "android")]
181         use super::android::cvt_pwrite64;
182
183         #[cfg(not(target_os = "android"))]
184         unsafe fn cvt_pwrite64(
185             fd: c_int,
186             buf: *const c_void,
187             count: usize,
188             offset: i64,
189         ) -> io::Result<isize> {
190             #[cfg(not(target_os = "linux"))]
191             use libc::pwrite as pwrite64;
192             #[cfg(target_os = "linux")]
193             use libc::pwrite64;
194             cvt(pwrite64(fd, buf, count, offset))
195         }
196
197         unsafe {
198             cvt_pwrite64(
199                 self.fd,
200                 buf.as_ptr() as *const c_void,
201                 cmp::min(buf.len(), READ_LIMIT),
202                 offset as i64,
203             )
204             .map(|n| n as usize)
205         }
206     }
207
208     #[cfg(target_os = "linux")]
209     pub fn get_cloexec(&self) -> io::Result<bool> {
210         unsafe { Ok((cvt(libc::fcntl(self.fd, libc::F_GETFD))? & libc::FD_CLOEXEC) != 0) }
211     }
212
213     #[cfg(not(any(
214         target_env = "newlib",
215         target_os = "solaris",
216         target_os = "illumos",
217         target_os = "emscripten",
218         target_os = "fuchsia",
219         target_os = "l4re",
220         target_os = "linux",
221         target_os = "haiku",
222         target_os = "redox",
223         target_os = "vxworks"
224     )))]
225     pub fn set_cloexec(&self) -> io::Result<()> {
226         unsafe {
227             cvt(libc::ioctl(self.fd, libc::FIOCLEX))?;
228             Ok(())
229         }
230     }
231     #[cfg(any(
232         all(target_env = "newlib", not(target_os = "espidf")),
233         target_os = "solaris",
234         target_os = "illumos",
235         target_os = "emscripten",
236         target_os = "fuchsia",
237         target_os = "l4re",
238         target_os = "linux",
239         target_os = "haiku",
240         target_os = "redox",
241         target_os = "vxworks"
242     ))]
243     pub fn set_cloexec(&self) -> io::Result<()> {
244         unsafe {
245             let previous = cvt(libc::fcntl(self.fd, libc::F_GETFD))?;
246             let new = previous | libc::FD_CLOEXEC;
247             if new != previous {
248                 cvt(libc::fcntl(self.fd, libc::F_SETFD, new))?;
249             }
250             Ok(())
251         }
252     }
253     #[cfg(target_os = "espidf")]
254     pub fn set_cloexec(&self) -> io::Result<()> {
255         // FD_CLOEXEC is not supported in ESP-IDF but there's no need to,
256         // because ESP-IDF does not support spawning processes either.
257         Ok(())
258     }
259
260     #[cfg(target_os = "linux")]
261     pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
262         unsafe {
263             let v = nonblocking as c_int;
264             cvt(libc::ioctl(self.fd, libc::FIONBIO, &v))?;
265             Ok(())
266         }
267     }
268
269     #[cfg(not(target_os = "linux"))]
270     pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
271         unsafe {
272             let previous = cvt(libc::fcntl(self.fd, libc::F_GETFL))?;
273             let new = if nonblocking {
274                 previous | libc::O_NONBLOCK
275             } else {
276                 previous & !libc::O_NONBLOCK
277             };
278             if new != previous {
279                 cvt(libc::fcntl(self.fd, libc::F_SETFL, new))?;
280             }
281             Ok(())
282         }
283     }
284
285     pub fn duplicate(&self) -> io::Result<FileDesc> {
286         // We want to atomically duplicate this file descriptor and set the
287         // CLOEXEC flag, and currently that's done via F_DUPFD_CLOEXEC. This
288         // is a POSIX flag that was added to Linux in 2.6.24.
289         #[cfg(not(target_os = "espidf"))]
290         let cmd = libc::F_DUPFD_CLOEXEC;
291
292         // For ESP-IDF, F_DUPFD is used instead, because the CLOEXEC semantics
293         // will never be supported, as this is a bare metal framework with
294         // no capabilities for multi-process execution.  While F_DUPFD is also
295         // not supported yet, it might be (currently it returns ENOSYS).
296         #[cfg(target_os = "espidf")]
297         let cmd = libc::F_DUPFD;
298
299         let fd = cvt(unsafe { libc::fcntl(self.raw(), cmd, 0) })?;
300         Ok(FileDesc::new(fd))
301     }
302 }
303
304 impl<'a> Read for &'a FileDesc {
305     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
306         (**self).read(buf)
307     }
308
309     #[inline]
310     unsafe fn initializer(&self) -> Initializer {
311         Initializer::nop()
312     }
313 }
314
315 impl AsInner<c_int> for FileDesc {
316     fn as_inner(&self) -> &c_int {
317         &self.fd
318     }
319 }
320
321 impl Drop for FileDesc {
322     fn drop(&mut self) {
323         // Note that errors are ignored when closing a file descriptor. The
324         // reason for this is that if an error occurs we don't actually know if
325         // the file descriptor was closed or not, and if we retried (for
326         // something like EINTR), we might close another valid file descriptor
327         // opened after we closed ours.
328         let _ = unsafe { libc::close(self.fd) };
329     }
330 }