]> git.proxmox.com Git - rustc.git/blob - library/std/src/sys/unix/fd.rs
New upstream version 1.69.0+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, 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(
57 target_os = "android",
58 target_os = "emscripten",
59 target_os = "linux",
60 target_os = "nto",
61 ))]
62 const fn max_iov() -> usize {
63 libc::UIO_MAXIOV as usize
64 }
65
66 #[cfg(not(any(
67 target_os = "android",
68 target_os = "dragonfly",
69 target_os = "emscripten",
70 target_os = "freebsd",
71 target_os = "ios",
72 target_os = "linux",
73 target_os = "macos",
74 target_os = "netbsd",
75 target_os = "nto",
76 target_os = "openbsd",
77 target_os = "horizon",
78 target_os = "watchos",
79 )))]
80 const fn max_iov() -> usize {
81 16 // The minimum value required by POSIX.
82 }
83
84 impl FileDesc {
85 pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
86 let ret = cvt(unsafe {
87 libc::read(
88 self.as_raw_fd(),
89 buf.as_mut_ptr() as *mut libc::c_void,
90 cmp::min(buf.len(), READ_LIMIT),
91 )
92 })?;
93 Ok(ret as usize)
94 }
95
96 #[cfg(not(any(target_os = "espidf", target_os = "horizon")))]
97 pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
98 let ret = cvt(unsafe {
99 libc::readv(
100 self.as_raw_fd(),
101 bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec,
102 cmp::min(bufs.len(), max_iov()) as libc::c_int,
103 )
104 })?;
105 Ok(ret as usize)
106 }
107
108 #[cfg(any(target_os = "espidf", target_os = "horizon"))]
109 pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
110 io::default_read_vectored(|b| self.read(b), bufs)
111 }
112
113 #[inline]
114 pub fn is_read_vectored(&self) -> bool {
115 cfg!(not(any(target_os = "espidf", target_os = "horizon")))
116 }
117
118 pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> {
119 let mut me = self;
120 (&mut me).read_to_end(buf)
121 }
122
123 pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
124 #[cfg(not(any(target_os = "linux", target_os = "android")))]
125 use libc::pread as pread64;
126 #[cfg(any(target_os = "linux", target_os = "android"))]
127 use libc::pread64;
128
129 unsafe {
130 cvt(pread64(
131 self.as_raw_fd(),
132 buf.as_mut_ptr() as *mut libc::c_void,
133 cmp::min(buf.len(), READ_LIMIT),
134 offset as off64_t,
135 ))
136 .map(|n| n as usize)
137 }
138 }
139
140 pub fn read_buf(&self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> {
141 let ret = cvt(unsafe {
142 libc::read(
143 self.as_raw_fd(),
144 cursor.as_mut().as_mut_ptr() as *mut libc::c_void,
145 cmp::min(cursor.capacity(), READ_LIMIT),
146 )
147 })?;
148
149 // Safety: `ret` bytes were written to the initialized portion of the buffer
150 unsafe {
151 cursor.advance(ret as usize);
152 }
153 Ok(())
154 }
155
156 #[cfg(any(
157 target_os = "emscripten",
158 target_os = "freebsd",
159 target_os = "fuchsia",
160 target_os = "illumos",
161 target_os = "linux",
162 target_os = "netbsd",
163 ))]
164 pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
165 let ret = cvt(unsafe {
166 libc::preadv(
167 self.as_raw_fd(),
168 bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec,
169 cmp::min(bufs.len(), max_iov()) as libc::c_int,
170 offset as _,
171 )
172 })?;
173 Ok(ret as usize)
174 }
175
176 #[cfg(not(any(
177 target_os = "android",
178 target_os = "emscripten",
179 target_os = "freebsd",
180 target_os = "fuchsia",
181 target_os = "illumos",
182 target_os = "ios",
183 target_os = "linux",
184 target_os = "macos",
185 target_os = "netbsd",
186 )))]
187 pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
188 io::default_read_vectored(|b| self.read_at(b, offset), bufs)
189 }
190
191 // We support some old Android versions that do not have `preadv` in libc,
192 // so we use weak linkage and fallback to a direct syscall if not available.
193 //
194 // On 32-bit targets, we don't want to deal with weird ABI issues around
195 // passing 64-bits parameters to syscalls, so we fallback to the default
196 // implementation if `preadv` is not available.
197 #[cfg(all(target_os = "android", target_pointer_width = "64"))]
198 pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
199 super::weak::syscall! {
200 fn preadv(
201 fd: libc::c_int,
202 iovec: *const libc::iovec,
203 n_iovec: libc::c_int,
204 offset: off64_t
205 ) -> isize
206 }
207
208 let ret = cvt(unsafe {
209 preadv(
210 self.as_raw_fd(),
211 bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec,
212 cmp::min(bufs.len(), max_iov()) as libc::c_int,
213 offset as _,
214 )
215 })?;
216 Ok(ret as usize)
217 }
218
219 // We support old MacOS and iOS versions that do not have `preadv`. There is
220 // no `syscall` possible in these platform.
221 #[cfg(any(
222 all(target_os = "android", target_pointer_width = "32"),
223 target_os = "ios",
224 target_os = "macos",
225 ))]
226 pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
227 super::weak::weak!(fn preadv64(libc::c_int, *const libc::iovec, libc::c_int, off64_t) -> isize);
228
229 match preadv64.get() {
230 Some(preadv) => {
231 let ret = cvt(unsafe {
232 preadv(
233 self.as_raw_fd(),
234 bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec,
235 cmp::min(bufs.len(), max_iov()) as libc::c_int,
236 offset as _,
237 )
238 })?;
239 Ok(ret as usize)
240 }
241 None => io::default_read_vectored(|b| self.read_at(b, offset), bufs),
242 }
243 }
244
245 pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
246 let ret = cvt(unsafe {
247 libc::write(
248 self.as_raw_fd(),
249 buf.as_ptr() as *const libc::c_void,
250 cmp::min(buf.len(), READ_LIMIT),
251 )
252 })?;
253 Ok(ret as usize)
254 }
255
256 #[cfg(not(any(target_os = "espidf", target_os = "horizon")))]
257 pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
258 let ret = cvt(unsafe {
259 libc::writev(
260 self.as_raw_fd(),
261 bufs.as_ptr() as *const libc::iovec,
262 cmp::min(bufs.len(), max_iov()) as libc::c_int,
263 )
264 })?;
265 Ok(ret as usize)
266 }
267
268 #[cfg(any(target_os = "espidf", target_os = "horizon"))]
269 pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
270 io::default_write_vectored(|b| self.write(b), bufs)
271 }
272
273 #[inline]
274 pub fn is_write_vectored(&self) -> bool {
275 cfg!(not(any(target_os = "espidf", target_os = "horizon")))
276 }
277
278 pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
279 #[cfg(not(any(target_os = "linux", target_os = "android")))]
280 use libc::pwrite as pwrite64;
281 #[cfg(any(target_os = "linux", target_os = "android"))]
282 use libc::pwrite64;
283
284 unsafe {
285 cvt(pwrite64(
286 self.as_raw_fd(),
287 buf.as_ptr() as *const libc::c_void,
288 cmp::min(buf.len(), READ_LIMIT),
289 offset as off64_t,
290 ))
291 .map(|n| n as usize)
292 }
293 }
294
295 #[cfg(any(
296 target_os = "emscripten",
297 target_os = "freebsd",
298 target_os = "fuchsia",
299 target_os = "illumos",
300 target_os = "linux",
301 target_os = "netbsd",
302 ))]
303 pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
304 let ret = cvt(unsafe {
305 libc::pwritev(
306 self.as_raw_fd(),
307 bufs.as_ptr() as *const libc::iovec,
308 cmp::min(bufs.len(), max_iov()) as libc::c_int,
309 offset as _,
310 )
311 })?;
312 Ok(ret as usize)
313 }
314
315 #[cfg(not(any(
316 target_os = "android",
317 target_os = "emscripten",
318 target_os = "freebsd",
319 target_os = "fuchsia",
320 target_os = "illumos",
321 target_os = "ios",
322 target_os = "linux",
323 target_os = "macos",
324 target_os = "netbsd",
325 )))]
326 pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
327 io::default_write_vectored(|b| self.write_at(b, offset), bufs)
328 }
329
330 // We support some old Android versions that do not have `pwritev` in libc,
331 // so we use weak linkage and fallback to a direct syscall if not available.
332 //
333 // On 32-bit targets, we don't want to deal with weird ABI issues around
334 // passing 64-bits parameters to syscalls, so we fallback to the default
335 // implementation if `pwritev` is not available.
336 #[cfg(all(target_os = "android", target_pointer_width = "64"))]
337 pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
338 super::weak::syscall! {
339 fn pwritev(
340 fd: libc::c_int,
341 iovec: *const libc::iovec,
342 n_iovec: libc::c_int,
343 offset: off64_t
344 ) -> isize
345 }
346
347 let ret = cvt(unsafe {
348 pwritev(
349 self.as_raw_fd(),
350 bufs.as_ptr() as *const libc::iovec,
351 cmp::min(bufs.len(), max_iov()) as libc::c_int,
352 offset as _,
353 )
354 })?;
355 Ok(ret as usize)
356 }
357
358 // We support old MacOS and iOS versions that do not have `pwritev`. There is
359 // no `syscall` possible in these platform.
360 #[cfg(any(
361 all(target_os = "android", target_pointer_width = "32"),
362 target_os = "ios",
363 target_os = "macos",
364 ))]
365 pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
366 super::weak::weak!(fn pwritev64(libc::c_int, *const libc::iovec, libc::c_int, off64_t) -> isize);
367
368 match pwritev64.get() {
369 Some(pwritev) => {
370 let ret = cvt(unsafe {
371 pwritev(
372 self.as_raw_fd(),
373 bufs.as_ptr() as *const libc::iovec,
374 cmp::min(bufs.len(), max_iov()) as libc::c_int,
375 offset as _,
376 )
377 })?;
378 Ok(ret as usize)
379 }
380 None => io::default_write_vectored(|b| self.write_at(b, offset), bufs),
381 }
382 }
383
384 #[cfg(not(any(
385 target_env = "newlib",
386 target_os = "solaris",
387 target_os = "illumos",
388 target_os = "emscripten",
389 target_os = "fuchsia",
390 target_os = "l4re",
391 target_os = "linux",
392 target_os = "haiku",
393 target_os = "redox",
394 target_os = "vxworks",
395 target_os = "nto",
396 )))]
397 pub fn set_cloexec(&self) -> io::Result<()> {
398 unsafe {
399 cvt(libc::ioctl(self.as_raw_fd(), libc::FIOCLEX))?;
400 Ok(())
401 }
402 }
403 #[cfg(any(
404 all(target_env = "newlib", not(any(target_os = "espidf", target_os = "horizon"))),
405 target_os = "solaris",
406 target_os = "illumos",
407 target_os = "emscripten",
408 target_os = "fuchsia",
409 target_os = "l4re",
410 target_os = "linux",
411 target_os = "haiku",
412 target_os = "redox",
413 target_os = "vxworks",
414 target_os = "nto",
415 ))]
416 pub fn set_cloexec(&self) -> io::Result<()> {
417 unsafe {
418 let previous = cvt(libc::fcntl(self.as_raw_fd(), libc::F_GETFD))?;
419 let new = previous | libc::FD_CLOEXEC;
420 if new != previous {
421 cvt(libc::fcntl(self.as_raw_fd(), libc::F_SETFD, new))?;
422 }
423 Ok(())
424 }
425 }
426 #[cfg(any(target_os = "espidf", target_os = "horizon"))]
427 pub fn set_cloexec(&self) -> io::Result<()> {
428 // FD_CLOEXEC is not supported in ESP-IDF and Horizon OS but there's no need to,
429 // because neither supports spawning processes.
430 Ok(())
431 }
432
433 #[cfg(target_os = "linux")]
434 pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
435 unsafe {
436 let v = nonblocking as libc::c_int;
437 cvt(libc::ioctl(self.as_raw_fd(), libc::FIONBIO, &v))?;
438 Ok(())
439 }
440 }
441
442 #[cfg(not(target_os = "linux"))]
443 pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
444 unsafe {
445 let previous = cvt(libc::fcntl(self.as_raw_fd(), libc::F_GETFL))?;
446 let new = if nonblocking {
447 previous | libc::O_NONBLOCK
448 } else {
449 previous & !libc::O_NONBLOCK
450 };
451 if new != previous {
452 cvt(libc::fcntl(self.as_raw_fd(), libc::F_SETFL, new))?;
453 }
454 Ok(())
455 }
456 }
457
458 #[inline]
459 pub fn duplicate(&self) -> io::Result<FileDesc> {
460 Ok(Self(self.0.try_clone()?))
461 }
462 }
463
464 impl<'a> Read for &'a FileDesc {
465 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
466 (**self).read(buf)
467 }
468
469 fn read_buf(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> {
470 (**self).read_buf(cursor)
471 }
472 }
473
474 impl AsInner<OwnedFd> for FileDesc {
475 fn as_inner(&self) -> &OwnedFd {
476 &self.0
477 }
478 }
479
480 impl IntoInner<OwnedFd> for FileDesc {
481 fn into_inner(self) -> OwnedFd {
482 self.0
483 }
484 }
485
486 impl FromInner<OwnedFd> for FileDesc {
487 fn from_inner(owned_fd: OwnedFd) -> Self {
488 Self(owned_fd)
489 }
490 }
491
492 impl AsFd for FileDesc {
493 fn as_fd(&self) -> BorrowedFd<'_> {
494 self.0.as_fd()
495 }
496 }
497
498 impl AsRawFd for FileDesc {
499 fn as_raw_fd(&self) -> RawFd {
500 self.0.as_raw_fd()
501 }
502 }
503
504 impl IntoRawFd for FileDesc {
505 fn into_raw_fd(self) -> RawFd {
506 self.0.into_raw_fd()
507 }
508 }
509
510 impl FromRawFd for FileDesc {
511 unsafe fn from_raw_fd(raw_fd: RawFd) -> Self {
512 Self(FromRawFd::from_raw_fd(raw_fd))
513 }
514 }