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