]> git.lizzy.rs Git - rust.git/blob - library/std/src/sys/unix/fd.rs
Rollup merge of #90704 - ijackson:exitstatus-comments, r=joshtriplett
[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::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
9 use crate::sys::cvt;
10 use crate::sys_common::{AsInner, FromInner, IntoInner};
11
12 use libc::{c_int, c_void};
13
14 #[derive(Debug)]
15 pub struct FileDesc(OwnedFd);
16
17 // The maximum read limit on most POSIX-like systems is `SSIZE_MAX`,
18 // with the man page quoting that if the count of bytes to read is
19 // greater than `SSIZE_MAX` the result is "unspecified".
20 //
21 // On macOS, however, apparently the 64-bit libc is either buggy or
22 // intentionally showing odd behavior by rejecting any read with a size
23 // larger than or equal to INT_MAX. To handle both of these the read
24 // size is capped on both platforms.
25 #[cfg(target_os = "macos")]
26 const READ_LIMIT: usize = c_int::MAX as usize - 1;
27 #[cfg(not(target_os = "macos"))]
28 const READ_LIMIT: usize = libc::ssize_t::MAX as usize;
29
30 #[cfg(any(
31     target_os = "dragonfly",
32     target_os = "freebsd",
33     target_os = "ios",
34     target_os = "macos",
35     target_os = "netbsd",
36     target_os = "openbsd",
37 ))]
38 const fn max_iov() -> usize {
39     libc::IOV_MAX as usize
40 }
41
42 #[cfg(any(target_os = "android", target_os = "emscripten", target_os = "linux"))]
43 const fn max_iov() -> usize {
44     libc::UIO_MAXIOV as usize
45 }
46
47 #[cfg(not(any(
48     target_os = "android",
49     target_os = "dragonfly",
50     target_os = "emscripten",
51     target_os = "freebsd",
52     target_os = "ios",
53     target_os = "linux",
54     target_os = "macos",
55     target_os = "netbsd",
56     target_os = "openbsd",
57 )))]
58 const fn max_iov() -> usize {
59     16 // The minimum value required by POSIX.
60 }
61
62 impl FileDesc {
63     pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
64         let ret = cvt(unsafe {
65             libc::read(
66                 self.as_raw_fd(),
67                 buf.as_mut_ptr() as *mut c_void,
68                 cmp::min(buf.len(), READ_LIMIT),
69             )
70         })?;
71         Ok(ret as usize)
72     }
73
74     #[cfg(not(target_os = "espidf"))]
75     pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
76         let ret = cvt(unsafe {
77             libc::readv(
78                 self.as_raw_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     #[cfg(target_os = "espidf")]
87     pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
88         return crate::io::default_read_vectored(|b| self.read(b), bufs);
89     }
90
91     #[inline]
92     pub fn is_read_vectored(&self) -> bool {
93         cfg!(not(target_os = "espidf"))
94     }
95
96     pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> {
97         let mut me = self;
98         (&mut me).read_to_end(buf)
99     }
100
101     pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
102         #[cfg(target_os = "android")]
103         use super::android::cvt_pread64;
104
105         #[cfg(not(target_os = "android"))]
106         unsafe fn cvt_pread64(
107             fd: c_int,
108             buf: *mut c_void,
109             count: usize,
110             offset: i64,
111         ) -> io::Result<isize> {
112             #[cfg(not(target_os = "linux"))]
113             use libc::pread as pread64;
114             #[cfg(target_os = "linux")]
115             use libc::pread64;
116             cvt(pread64(fd, buf, count, offset))
117         }
118
119         unsafe {
120             cvt_pread64(
121                 self.as_raw_fd(),
122                 buf.as_mut_ptr() as *mut c_void,
123                 cmp::min(buf.len(), READ_LIMIT),
124                 offset as i64,
125             )
126             .map(|n| n as usize)
127         }
128     }
129
130     pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
131         let ret = cvt(unsafe {
132             libc::write(
133                 self.as_raw_fd(),
134                 buf.as_ptr() as *const c_void,
135                 cmp::min(buf.len(), READ_LIMIT),
136             )
137         })?;
138         Ok(ret as usize)
139     }
140
141     #[cfg(not(target_os = "espidf"))]
142     pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
143         let ret = cvt(unsafe {
144             libc::writev(
145                 self.as_raw_fd(),
146                 bufs.as_ptr() as *const libc::iovec,
147                 cmp::min(bufs.len(), max_iov()) as c_int,
148             )
149         })?;
150         Ok(ret as usize)
151     }
152
153     #[cfg(target_os = "espidf")]
154     pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
155         return crate::io::default_write_vectored(|b| self.write(b), bufs);
156     }
157
158     #[inline]
159     pub fn is_write_vectored(&self) -> bool {
160         cfg!(not(target_os = "espidf"))
161     }
162
163     pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
164         #[cfg(target_os = "android")]
165         use super::android::cvt_pwrite64;
166
167         #[cfg(not(target_os = "android"))]
168         unsafe fn cvt_pwrite64(
169             fd: c_int,
170             buf: *const c_void,
171             count: usize,
172             offset: i64,
173         ) -> io::Result<isize> {
174             #[cfg(not(target_os = "linux"))]
175             use libc::pwrite as pwrite64;
176             #[cfg(target_os = "linux")]
177             use libc::pwrite64;
178             cvt(pwrite64(fd, buf, count, offset))
179         }
180
181         unsafe {
182             cvt_pwrite64(
183                 self.as_raw_fd(),
184                 buf.as_ptr() as *const c_void,
185                 cmp::min(buf.len(), READ_LIMIT),
186                 offset as i64,
187             )
188             .map(|n| n as usize)
189         }
190     }
191
192     #[cfg(target_os = "linux")]
193     pub fn get_cloexec(&self) -> io::Result<bool> {
194         unsafe { Ok((cvt(libc::fcntl(self.as_raw_fd(), libc::F_GETFD))? & libc::FD_CLOEXEC) != 0) }
195     }
196
197     #[cfg(not(any(
198         target_env = "newlib",
199         target_os = "solaris",
200         target_os = "illumos",
201         target_os = "emscripten",
202         target_os = "fuchsia",
203         target_os = "l4re",
204         target_os = "linux",
205         target_os = "haiku",
206         target_os = "redox",
207         target_os = "vxworks"
208     )))]
209     pub fn set_cloexec(&self) -> io::Result<()> {
210         unsafe {
211             cvt(libc::ioctl(self.as_raw_fd(), libc::FIOCLEX))?;
212             Ok(())
213         }
214     }
215     #[cfg(any(
216         all(target_env = "newlib", not(target_os = "espidf")),
217         target_os = "solaris",
218         target_os = "illumos",
219         target_os = "emscripten",
220         target_os = "fuchsia",
221         target_os = "l4re",
222         target_os = "linux",
223         target_os = "haiku",
224         target_os = "redox",
225         target_os = "vxworks"
226     ))]
227     pub fn set_cloexec(&self) -> io::Result<()> {
228         unsafe {
229             let previous = cvt(libc::fcntl(self.as_raw_fd(), libc::F_GETFD))?;
230             let new = previous | libc::FD_CLOEXEC;
231             if new != previous {
232                 cvt(libc::fcntl(self.as_raw_fd(), libc::F_SETFD, new))?;
233             }
234             Ok(())
235         }
236     }
237     #[cfg(target_os = "espidf")]
238     pub fn set_cloexec(&self) -> io::Result<()> {
239         // FD_CLOEXEC is not supported in ESP-IDF but there's no need to,
240         // because ESP-IDF does not support spawning processes either.
241         Ok(())
242     }
243
244     #[cfg(target_os = "linux")]
245     pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
246         unsafe {
247             let v = nonblocking as c_int;
248             cvt(libc::ioctl(self.as_raw_fd(), libc::FIONBIO, &v))?;
249             Ok(())
250         }
251     }
252
253     #[cfg(not(target_os = "linux"))]
254     pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
255         unsafe {
256             let previous = cvt(libc::fcntl(self.as_raw_fd(), libc::F_GETFL))?;
257             let new = if nonblocking {
258                 previous | libc::O_NONBLOCK
259             } else {
260                 previous & !libc::O_NONBLOCK
261             };
262             if new != previous {
263                 cvt(libc::fcntl(self.as_raw_fd(), libc::F_SETFL, new))?;
264             }
265             Ok(())
266         }
267     }
268
269     pub fn duplicate(&self) -> io::Result<FileDesc> {
270         // We want to atomically duplicate this file descriptor and set the
271         // CLOEXEC flag, and currently that's done via F_DUPFD_CLOEXEC. This
272         // is a POSIX flag that was added to Linux in 2.6.24.
273         #[cfg(not(target_os = "espidf"))]
274         let cmd = libc::F_DUPFD_CLOEXEC;
275
276         // For ESP-IDF, F_DUPFD is used instead, because the CLOEXEC semantics
277         // will never be supported, as this is a bare metal framework with
278         // no capabilities for multi-process execution.  While F_DUPFD is also
279         // not supported yet, it might be (currently it returns ENOSYS).
280         #[cfg(target_os = "espidf")]
281         let cmd = libc::F_DUPFD;
282
283         let fd = cvt(unsafe { libc::fcntl(self.as_raw_fd(), cmd, 0) })?;
284         Ok(unsafe { FileDesc::from_raw_fd(fd) })
285     }
286 }
287
288 impl<'a> Read for &'a FileDesc {
289     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
290         (**self).read(buf)
291     }
292
293     #[inline]
294     unsafe fn initializer(&self) -> Initializer {
295         Initializer::nop()
296     }
297 }
298
299 impl AsInner<OwnedFd> for FileDesc {
300     fn as_inner(&self) -> &OwnedFd {
301         &self.0
302     }
303 }
304
305 impl IntoInner<OwnedFd> for FileDesc {
306     fn into_inner(self) -> OwnedFd {
307         self.0
308     }
309 }
310
311 impl FromInner<OwnedFd> for FileDesc {
312     fn from_inner(owned_fd: OwnedFd) -> Self {
313         Self(owned_fd)
314     }
315 }
316
317 impl AsFd for FileDesc {
318     fn as_fd(&self) -> BorrowedFd<'_> {
319         self.0.as_fd()
320     }
321 }
322
323 impl AsRawFd for FileDesc {
324     fn as_raw_fd(&self) -> RawFd {
325         self.0.as_raw_fd()
326     }
327 }
328
329 impl IntoRawFd for FileDesc {
330     fn into_raw_fd(self) -> RawFd {
331         self.0.into_raw_fd()
332     }
333 }
334
335 impl FromRawFd for FileDesc {
336     unsafe fn from_raw_fd(raw_fd: RawFd) -> Self {
337         Self(FromRawFd::from_raw_fd(raw_fd))
338     }
339 }