1#![unstable(reason = "not public", issue = "none", feature = "fd")]
2
3#[cfg(test)]
4mod tests;
5
6#[cfg(not(any(
7 target_os = "linux",
8 target_os = "l4re",
9 target_os = "android",
10 target_os = "hurd",
11)))]
12use libc::off_t as off64_t;
13#[cfg(any(
14 target_os = "android",
15 target_os = "linux",
16 target_os = "l4re",
17 target_os = "hurd",
18))]
19use libc::off64_t;
20
21use crate::cmp;
22use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, Read};
23use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
24use crate::sys::cvt;
25use crate::sys_common::{AsInner, FromInner, IntoInner};
26
27#[derive(Debug)]
28pub 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 Apple targets 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.
38const READ_LIMIT: usize = if cfg!(target_vendor = "apple") {
39 libc::c_int::MAX as usize - 1
40} else {
41 libc::ssize_t::MAX as usize
42};
43
44#[cfg(any(
45 target_os = "dragonfly",
46 target_os = "freebsd",
47 target_os = "netbsd",
48 target_os = "openbsd",
49 target_vendor = "apple",
50 target_os = "cygwin",
51))]
52const 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))]
62const 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 = "linux",
72 target_os = "netbsd",
73 target_os = "nto",
74 target_os = "openbsd",
75 target_os = "horizon",
76 target_os = "vita",
77 target_vendor = "apple",
78 target_os = "cygwin",
79)))]
80const fn max_iov() -> usize {
81 16 // The minimum value required by POSIX.
82}
83
84impl FileDesc {
85 #[inline]
86 pub fn try_clone(&self) -> io::Result<Self> {
87 self.duplicate()
88 }
89
90 pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
91 let ret = cvt(unsafe {
92 libc::read(
93 self.as_raw_fd(),
94 buf.as_mut_ptr() as *mut libc::c_void,
95 cmp::min(buf.len(), READ_LIMIT),
96 )
97 })?;
98 Ok(ret as usize)
99 }
100
101 #[cfg(not(any(
102 target_os = "espidf",
103 target_os = "horizon",
104 target_os = "vita",
105 target_os = "nuttx"
106 )))]
107 pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
108 let ret = cvt(unsafe {
109 libc::readv(
110 self.as_raw_fd(),
111 bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec,
112 cmp::min(bufs.len(), max_iov()) as libc::c_int,
113 )
114 })?;
115 Ok(ret as usize)
116 }
117
118 #[cfg(any(
119 target_os = "espidf",
120 target_os = "horizon",
121 target_os = "vita",
122 target_os = "nuttx"
123 ))]
124 pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
125 io::default_read_vectored(|b| self.read(b), bufs)
126 }
127
128 #[inline]
129 pub fn is_read_vectored(&self) -> bool {
130 cfg!(not(any(
131 target_os = "espidf",
132 target_os = "horizon",
133 target_os = "vita",
134 target_os = "nuttx"
135 )))
136 }
137
138 pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> {
139 let mut me = self;
140 (&mut me).read_to_end(buf)
141 }
142
143 #[cfg_attr(target_os = "vxworks", allow(unused_unsafe))]
144 pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
145 #[cfg(not(any(
146 all(target_os = "linux", not(target_env = "musl")),
147 target_os = "android",
148 target_os = "hurd"
149 )))]
150 use libc::pread as pread64;
151 #[cfg(any(
152 all(target_os = "linux", not(target_env = "musl")),
153 target_os = "android",
154 target_os = "hurd"
155 ))]
156 use libc::pread64;
157
158 unsafe {
159 cvt(pread64(
160 self.as_raw_fd(),
161 buf.as_mut_ptr() as *mut libc::c_void,
162 cmp::min(buf.len(), READ_LIMIT),
163 offset as off64_t,
164 ))
165 .map(|n| n as usize)
166 }
167 }
168
169 pub fn read_buf(&self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> {
170 let ret = cvt(unsafe {
171 libc::read(
172 self.as_raw_fd(),
173 cursor.as_mut().as_mut_ptr() as *mut libc::c_void,
174 cmp::min(cursor.capacity(), READ_LIMIT),
175 )
176 })?;
177
178 // Safety: `ret` bytes were written to the initialized portion of the buffer
179 unsafe {
180 cursor.advance_unchecked(ret as usize);
181 }
182 Ok(())
183 }
184
185 #[cfg(any(
186 target_os = "aix",
187 target_os = "dragonfly", // DragonFly 1.5
188 target_os = "emscripten",
189 target_os = "freebsd",
190 target_os = "fuchsia",
191 target_os = "hurd",
192 target_os = "illumos",
193 target_os = "linux",
194 target_os = "netbsd",
195 target_os = "openbsd", // OpenBSD 2.7
196 ))]
197 pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
198 let ret = cvt(unsafe {
199 libc::preadv(
200 self.as_raw_fd(),
201 bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec,
202 cmp::min(bufs.len(), max_iov()) as libc::c_int,
203 offset as _,
204 )
205 })?;
206 Ok(ret as usize)
207 }
208
209 #[cfg(not(any(
210 target_os = "aix",
211 target_os = "android",
212 target_os = "dragonfly",
213 target_os = "emscripten",
214 target_os = "freebsd",
215 target_os = "fuchsia",
216 target_os = "hurd",
217 target_os = "illumos",
218 target_os = "linux",
219 target_os = "netbsd",
220 target_os = "openbsd",
221 target_vendor = "apple",
222 )))]
223 pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
224 io::default_read_vectored(|b| self.read_at(b, offset), bufs)
225 }
226
227 // We support some old Android versions that do not have `preadv` in libc,
228 // so we use weak linkage and fallback to a direct syscall if not available.
229 //
230 // On 32-bit targets, we don't want to deal with weird ABI issues around
231 // passing 64-bits parameters to syscalls, so we fallback to the default
232 // implementation if `preadv` is not available.
233 #[cfg(all(target_os = "android", target_pointer_width = "64"))]
234 pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
235 super::weak::syscall! {
236 fn preadv(
237 fd: libc::c_int,
238 iovec: *const libc::iovec,
239 n_iovec: libc::c_int,
240 offset: off64_t
241 ) -> isize
242 }
243
244 let ret = cvt(unsafe {
245 preadv(
246 self.as_raw_fd(),
247 bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec,
248 cmp::min(bufs.len(), max_iov()) as libc::c_int,
249 offset as _,
250 )
251 })?;
252 Ok(ret as usize)
253 }
254
255 #[cfg(all(target_os = "android", target_pointer_width = "32"))]
256 // FIXME(#115199): Rust currently omits weak function definitions
257 // and its metadata from LLVM IR.
258 #[no_sanitize(cfi)]
259 pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
260 super::weak::weak!(fn preadv64(libc::c_int, *const libc::iovec, libc::c_int, off64_t) -> isize);
261
262 match preadv64.get() {
263 Some(preadv) => {
264 let ret = cvt(unsafe {
265 preadv(
266 self.as_raw_fd(),
267 bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec,
268 cmp::min(bufs.len(), max_iov()) as libc::c_int,
269 offset as _,
270 )
271 })?;
272 Ok(ret as usize)
273 }
274 None => io::default_read_vectored(|b| self.read_at(b, offset), bufs),
275 }
276 }
277
278 // We support old MacOS, iOS, watchOS, tvOS and visionOS. `preadv` was added in the following
279 // Apple OS versions:
280 // ios 14.0
281 // tvos 14.0
282 // macos 11.0
283 // watchos 7.0
284 //
285 // These versions may be newer than the minimum supported versions of OS's we support so we must
286 // use "weak" linking.
287 #[cfg(target_vendor = "apple")]
288 pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
289 super::weak::weak!(fn preadv(libc::c_int, *const libc::iovec, libc::c_int, off64_t) -> isize);
290
291 match preadv.get() {
292 Some(preadv) => {
293 let ret = cvt(unsafe {
294 preadv(
295 self.as_raw_fd(),
296 bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec,
297 cmp::min(bufs.len(), max_iov()) as libc::c_int,
298 offset as _,
299 )
300 })?;
301 Ok(ret as usize)
302 }
303 None => io::default_read_vectored(|b| self.read_at(b, offset), bufs),
304 }
305 }
306
307 pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
308 let ret = cvt(unsafe {
309 libc::write(
310 self.as_raw_fd(),
311 buf.as_ptr() as *const libc::c_void,
312 cmp::min(buf.len(), READ_LIMIT),
313 )
314 })?;
315 Ok(ret as usize)
316 }
317
318 #[cfg(not(any(
319 target_os = "espidf",
320 target_os = "horizon",
321 target_os = "vita",
322 target_os = "nuttx"
323 )))]
324 pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
325 let ret = cvt(unsafe {
326 libc::writev(
327 self.as_raw_fd(),
328 bufs.as_ptr() as *const libc::iovec,
329 cmp::min(bufs.len(), max_iov()) as libc::c_int,
330 )
331 })?;
332 Ok(ret as usize)
333 }
334
335 #[cfg(any(
336 target_os = "espidf",
337 target_os = "horizon",
338 target_os = "vita",
339 target_os = "nuttx"
340 ))]
341 pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
342 io::default_write_vectored(|b| self.write(b), bufs)
343 }
344
345 #[inline]
346 pub fn is_write_vectored(&self) -> bool {
347 cfg!(not(any(
348 target_os = "espidf",
349 target_os = "horizon",
350 target_os = "vita",
351 target_os = "nuttx"
352 )))
353 }
354
355 #[cfg_attr(target_os = "vxworks", allow(unused_unsafe))]
356 pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
357 #[cfg(not(any(
358 all(target_os = "linux", not(target_env = "musl")),
359 target_os = "android",
360 target_os = "hurd"
361 )))]
362 use libc::pwrite as pwrite64;
363 #[cfg(any(
364 all(target_os = "linux", not(target_env = "musl")),
365 target_os = "android",
366 target_os = "hurd"
367 ))]
368 use libc::pwrite64;
369
370 unsafe {
371 cvt(pwrite64(
372 self.as_raw_fd(),
373 buf.as_ptr() as *const libc::c_void,
374 cmp::min(buf.len(), READ_LIMIT),
375 offset as off64_t,
376 ))
377 .map(|n| n as usize)
378 }
379 }
380
381 #[cfg(any(
382 target_os = "aix",
383 target_os = "dragonfly", // DragonFly 1.5
384 target_os = "emscripten",
385 target_os = "freebsd",
386 target_os = "fuchsia",
387 target_os = "hurd",
388 target_os = "illumos",
389 target_os = "linux",
390 target_os = "netbsd",
391 target_os = "openbsd", // OpenBSD 2.7
392 ))]
393 pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
394 let ret = cvt(unsafe {
395 libc::pwritev(
396 self.as_raw_fd(),
397 bufs.as_ptr() as *const libc::iovec,
398 cmp::min(bufs.len(), max_iov()) as libc::c_int,
399 offset as _,
400 )
401 })?;
402 Ok(ret as usize)
403 }
404
405 #[cfg(not(any(
406 target_os = "aix",
407 target_os = "android",
408 target_os = "dragonfly",
409 target_os = "emscripten",
410 target_os = "freebsd",
411 target_os = "fuchsia",
412 target_os = "hurd",
413 target_os = "illumos",
414 target_os = "linux",
415 target_os = "netbsd",
416 target_os = "openbsd",
417 target_vendor = "apple",
418 )))]
419 pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
420 io::default_write_vectored(|b| self.write_at(b, offset), bufs)
421 }
422
423 // We support some old Android versions that do not have `pwritev` in libc,
424 // so we use weak linkage and fallback to a direct syscall if not available.
425 //
426 // On 32-bit targets, we don't want to deal with weird ABI issues around
427 // passing 64-bits parameters to syscalls, so we fallback to the default
428 // implementation if `pwritev` is not available.
429 #[cfg(all(target_os = "android", target_pointer_width = "64"))]
430 pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
431 super::weak::syscall! {
432 fn pwritev(
433 fd: libc::c_int,
434 iovec: *const libc::iovec,
435 n_iovec: libc::c_int,
436 offset: off64_t
437 ) -> isize
438 }
439
440 let ret = cvt(unsafe {
441 pwritev(
442 self.as_raw_fd(),
443 bufs.as_ptr() as *const libc::iovec,
444 cmp::min(bufs.len(), max_iov()) as libc::c_int,
445 offset as _,
446 )
447 })?;
448 Ok(ret as usize)
449 }
450
451 #[cfg(all(target_os = "android", target_pointer_width = "32"))]
452 pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
453 super::weak::weak!(fn pwritev64(libc::c_int, *const libc::iovec, libc::c_int, off64_t) -> isize);
454
455 match pwritev64.get() {
456 Some(pwritev) => {
457 let ret = cvt(unsafe {
458 pwritev(
459 self.as_raw_fd(),
460 bufs.as_ptr() as *const libc::iovec,
461 cmp::min(bufs.len(), max_iov()) as libc::c_int,
462 offset as _,
463 )
464 })?;
465 Ok(ret as usize)
466 }
467 None => io::default_write_vectored(|b| self.write_at(b, offset), bufs),
468 }
469 }
470
471 // We support old MacOS, iOS, watchOS, tvOS and visionOS. `pwritev` was added in the following
472 // Apple OS versions:
473 // ios 14.0
474 // tvos 14.0
475 // macos 11.0
476 // watchos 7.0
477 //
478 // These versions may be newer than the minimum supported versions of OS's we support so we must
479 // use "weak" linking.
480 #[cfg(target_vendor = "apple")]
481 pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
482 super::weak::weak!(fn pwritev(libc::c_int, *const libc::iovec, libc::c_int, off64_t) -> isize);
483
484 match pwritev.get() {
485 Some(pwritev) => {
486 let ret = cvt(unsafe {
487 pwritev(
488 self.as_raw_fd(),
489 bufs.as_ptr() as *const libc::iovec,
490 cmp::min(bufs.len(), max_iov()) as libc::c_int,
491 offset as _,
492 )
493 })?;
494 Ok(ret as usize)
495 }
496 None => io::default_write_vectored(|b| self.write_at(b, offset), bufs),
497 }
498 }
499
500 #[cfg(not(any(
501 target_env = "newlib",
502 target_os = "solaris",
503 target_os = "illumos",
504 target_os = "emscripten",
505 target_os = "fuchsia",
506 target_os = "l4re",
507 target_os = "linux",
508 target_os = "cygwin",
509 target_os = "haiku",
510 target_os = "redox",
511 target_os = "vxworks",
512 target_os = "nto",
513 )))]
514 pub fn set_cloexec(&self) -> io::Result<()> {
515 unsafe {
516 cvt(libc::ioctl(self.as_raw_fd(), libc::FIOCLEX))?;
517 Ok(())
518 }
519 }
520 #[cfg(any(
521 all(
522 target_env = "newlib",
523 not(any(target_os = "espidf", target_os = "horizon", target_os = "vita"))
524 ),
525 target_os = "solaris",
526 target_os = "illumos",
527 target_os = "emscripten",
528 target_os = "fuchsia",
529 target_os = "l4re",
530 target_os = "linux",
531 target_os = "cygwin",
532 target_os = "haiku",
533 target_os = "redox",
534 target_os = "vxworks",
535 target_os = "nto",
536 ))]
537 pub fn set_cloexec(&self) -> io::Result<()> {
538 unsafe {
539 let previous = cvt(libc::fcntl(self.as_raw_fd(), libc::F_GETFD))?;
540 let new = previous | libc::FD_CLOEXEC;
541 if new != previous {
542 cvt(libc::fcntl(self.as_raw_fd(), libc::F_SETFD, new))?;
543 }
544 Ok(())
545 }
546 }
547 #[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita"))]
548 pub fn set_cloexec(&self) -> io::Result<()> {
549 // FD_CLOEXEC is not supported in ESP-IDF, Horizon OS and Vita but there's no need to,
550 // because none of them supports spawning processes.
551 Ok(())
552 }
553
554 #[cfg(target_os = "linux")]
555 pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
556 unsafe {
557 let v = nonblocking as libc::c_int;
558 cvt(libc::ioctl(self.as_raw_fd(), libc::FIONBIO, &v))?;
559 Ok(())
560 }
561 }
562
563 #[cfg(not(target_os = "linux"))]
564 pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
565 unsafe {
566 let previous = cvt(libc::fcntl(self.as_raw_fd(), libc::F_GETFL))?;
567 let new = if nonblocking {
568 previous | libc::O_NONBLOCK
569 } else {
570 previous & !libc::O_NONBLOCK
571 };
572 if new != previous {
573 cvt(libc::fcntl(self.as_raw_fd(), libc::F_SETFL, new))?;
574 }
575 Ok(())
576 }
577 }
578
579 #[inline]
580 pub fn duplicate(&self) -> io::Result<FileDesc> {
581 Ok(Self(self.0.try_clone()?))
582 }
583}
584
585impl<'a> Read for &'a FileDesc {
586 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
587 (**self).read(buf)
588 }
589
590 fn read_buf(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> {
591 (**self).read_buf(cursor)
592 }
593
594 fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
595 (**self).read_vectored(bufs)
596 }
597
598 #[inline]
599 fn is_read_vectored(&self) -> bool {
600 (**self).is_read_vectored()
601 }
602}
603
604impl AsInner<OwnedFd> for FileDesc {
605 #[inline]
606 fn as_inner(&self) -> &OwnedFd {
607 &self.0
608 }
609}
610
611impl IntoInner<OwnedFd> for FileDesc {
612 fn into_inner(self) -> OwnedFd {
613 self.0
614 }
615}
616
617impl FromInner<OwnedFd> for FileDesc {
618 fn from_inner(owned_fd: OwnedFd) -> Self {
619 Self(owned_fd)
620 }
621}
622
623impl AsFd for FileDesc {
624 fn as_fd(&self) -> BorrowedFd<'_> {
625 self.0.as_fd()
626 }
627}
628
629impl AsRawFd for FileDesc {
630 #[inline]
631 fn as_raw_fd(&self) -> RawFd {
632 self.0.as_raw_fd()
633 }
634}
635
636impl IntoRawFd for FileDesc {
637 fn into_raw_fd(self) -> RawFd {
638 self.0.into_raw_fd()
639 }
640}
641
642impl FromRawFd for FileDesc {
643 unsafe fn from_raw_fd(raw_fd: RawFd) -> Self {
644 Self(FromRawFd::from_raw_fd(raw_fd))
645 }
646}
647