]> git.proxmox.com Git - rustc.git/blob - library/std/src/sys/unix/fd.rs
New upstream version 1.56.0~beta.4+dfsg1
[rustc.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 }