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
21cfg_select! {
22 any(
23 all(target_os = "linux", not(target_env = "musl")),
24 target_os = "android",
25 target_os = "hurd",
26 ) => {
27 use libc::pread64;
30 }
31 _ => {
32 use libc::pread as pread64;
33 }
34}
35
36use crate::cmp;
37use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, Read};
38use crate::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
39#[cfg(all(target_os = "android", target_pointer_width = "64"))]
40use crate::sys::pal::weak::syscall;
41#[cfg(any(
42 all(target_os = "android", target_pointer_width = "32"),
43 all(target_vendor = "apple", not(all(target_os = "macos", target_arch = "aarch64")))
44))]
45use crate::sys::pal::weak::weak;
46use crate::sys::{AsInner, FromInner, IntoInner, cvt};
47
48#[derive(#[automatically_derived]
impl ::core::fmt::Debug for FileDesc {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_tuple_field1_finish(f, "FileDesc",
&&self.0)
}
}Debug)]
49pub struct FileDesc(OwnedFd);
50
51const READ_LIMIT: usize = if falsecfg!(target_vendor = "apple") {
60 libc::c_int::MAX as usize
61} else {
62 libc::ssize_t::MAX as usize
63};
64
65#[cfg(any(
66 target_os = "dragonfly",
67 target_os = "freebsd",
68 target_os = "netbsd",
69 target_os = "openbsd",
70 target_vendor = "apple",
71 target_os = "cygwin",
72))]
73const fn max_iov() -> usize {
74 libc::IOV_MAX as usize
75}
76
77#[cfg(any(
78 target_os = "android",
79 target_os = "emscripten",
80 target_os = "linux",
81 target_os = "nto",
82 target_os = "qnx",
83))]
84const fn max_iov() -> usize {
85 libc::UIO_MAXIOV as usize
86}
87
88#[cfg(not(any(
89 target_os = "android",
90 target_os = "dragonfly",
91 target_os = "emscripten",
92 target_os = "espidf",
93 target_os = "freebsd",
94 target_os = "linux",
95 target_os = "netbsd",
96 target_os = "nuttx",
97 target_os = "nto",
98 target_os = "qnx",
99 target_os = "openbsd",
100 target_os = "horizon",
101 target_os = "vita",
102 target_vendor = "apple",
103 target_os = "cygwin",
104)))]
105const fn max_iov() -> usize {
106 16 }
108
109impl FileDesc {
110 #[inline]
111 pub fn try_clone(&self) -> io::Result<Self> {
112 self.duplicate()
113 }
114
115 pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
116 let ret = cvt(unsafe {
117 libc::read(
118 self.as_raw_fd(),
119 buf.as_mut_ptr() as *mut libc::c_void,
120 cmp::min(buf.len(), READ_LIMIT),
121 )
122 })?;
123 Ok(ret as usize)
124 }
125
126 #[cfg(not(any(
127 target_os = "espidf",
128 target_os = "horizon",
129 target_os = "vita",
130 target_os = "nuttx"
131 )))]
132 pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
133 let ret = cvt(unsafe {
134 libc::readv(
135 self.as_raw_fd(),
136 bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec,
137 cmp::min(bufs.len(), max_iov()) as libc::c_int,
138 )
139 })?;
140 Ok(ret as usize)
141 }
142
143 #[cfg(any(
144 target_os = "espidf",
145 target_os = "horizon",
146 target_os = "vita",
147 target_os = "nuttx"
148 ))]
149 pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
150 io::default_read_vectored(|b| self.read(b), bufs)
151 }
152
153 #[inline]
154 pub fn is_read_vectored(&self) -> bool {
155 truecfg!(not(any(
156 target_os = "espidf",
157 target_os = "horizon",
158 target_os = "vita",
159 target_os = "nuttx",
160 target_os = "wasi",
161 )))
162 }
163
164 pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> {
165 let mut me = self;
166 (&mut me).read_to_end(buf)
167 }
168
169 pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
170 cvt(unsafe {
171 pread64(
172 self.as_raw_fd(),
173 buf.as_mut_ptr() as *mut libc::c_void,
174 cmp::min(buf.len(), READ_LIMIT),
175 offset as off64_t, )
177 })
178 .map(|n| n as usize)
179 }
180
181 pub fn read_buf(&self, mut cursor: BorrowedCursor<'_, u8>) -> io::Result<()> {
182 let ret = cvt(unsafe {
184 libc::read(
185 self.as_raw_fd(),
186 cursor.as_mut().as_mut_ptr().cast::<libc::c_void>(),
187 cmp::min(cursor.capacity(), READ_LIMIT),
188 )
189 })?;
190
191 unsafe {
193 cursor.advance(ret as usize);
194 }
195 Ok(())
196 }
197
198 pub fn read_buf_at(&self, mut cursor: BorrowedCursor<'_, u8>, offset: u64) -> io::Result<()> {
199 let ret = cvt(unsafe {
201 pread64(
202 self.as_raw_fd(),
203 cursor.as_mut().as_mut_ptr().cast::<libc::c_void>(),
204 cmp::min(cursor.capacity(), READ_LIMIT),
205 offset as off64_t, )
207 })?;
208
209 unsafe {
211 cursor.advance(ret as usize);
212 }
213 Ok(())
214 }
215
216 #[cfg(any(
217 target_os = "aix",
218 target_os = "dragonfly", target_os = "emscripten",
220 target_os = "freebsd",
221 target_os = "fuchsia",
222 target_os = "hurd",
223 target_os = "illumos",
224 target_os = "linux",
225 target_os = "netbsd",
226 target_os = "openbsd", all(target_os = "macos", target_arch = "aarch64"),
228 ))]
229 pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
230 let ret = cvt(unsafe {
231 libc::preadv(
232 self.as_raw_fd(),
233 bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec,
234 cmp::min(bufs.len(), max_iov()) as libc::c_int,
235 offset as _,
236 )
237 })?;
238 Ok(ret as usize)
239 }
240
241 #[cfg(not(any(
242 target_os = "aix",
243 target_os = "android",
244 target_os = "dragonfly",
245 target_os = "emscripten",
246 target_os = "freebsd",
247 target_os = "fuchsia",
248 target_os = "hurd",
249 target_os = "illumos",
250 target_os = "linux",
251 target_os = "netbsd",
252 target_os = "openbsd",
253 target_vendor = "apple",
254 )))]
255 pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
256 io::default_read_vectored(|b| self.read_at(b, offset), bufs)
257 }
258
259 #[cfg(all(target_os = "android", target_pointer_width = "64"))]
266 pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
267 syscall!(
268 fn preadv(
269 fd: libc::c_int,
270 iovec: *const libc::iovec,
271 n_iovec: libc::c_int,
272 offset: off64_t,
273 ) -> isize;
274 );
275
276 let ret = cvt(unsafe {
277 preadv(
278 self.as_raw_fd(),
279 bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec,
280 cmp::min(bufs.len(), max_iov()) as libc::c_int,
281 offset as _,
282 )
283 })?;
284 Ok(ret as usize)
285 }
286
287 #[cfg(all(target_os = "android", target_pointer_width = "32"))]
288 pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
289 weak!(
290 fn preadv64(
291 fd: libc::c_int,
292 iovec: *const libc::iovec,
293 n_iovec: libc::c_int,
294 offset: off64_t,
295 ) -> isize;
296 );
297
298 match preadv64.get() {
299 Some(preadv) => {
300 let ret = cvt(unsafe {
301 preadv(
302 self.as_raw_fd(),
303 bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec,
304 cmp::min(bufs.len(), max_iov()) as libc::c_int,
305 offset as _,
306 )
307 })?;
308 Ok(ret as usize)
309 }
310 None => io::default_read_vectored(|b| self.read_at(b, offset), bufs),
311 }
312 }
313
314 #[cfg(all(target_vendor = "apple", not(all(target_os = "macos", target_arch = "aarch64"))))]
325 pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
326 weak!(
327 fn preadv(
328 fd: libc::c_int,
329 iovec: *const libc::iovec,
330 n_iovec: libc::c_int,
331 offset: off64_t,
332 ) -> isize;
333 );
334
335 match preadv.get() {
336 Some(preadv) => {
337 let ret = cvt(unsafe {
338 preadv(
339 self.as_raw_fd(),
340 bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec,
341 cmp::min(bufs.len(), max_iov()) as libc::c_int,
342 offset as _,
343 )
344 })?;
345 Ok(ret as usize)
346 }
347 None => io::default_read_vectored(|b| self.read_at(b, offset), bufs),
348 }
349 }
350
351 pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
352 let ret = cvt(unsafe {
353 libc::write(
354 self.as_raw_fd(),
355 buf.as_ptr() as *const libc::c_void,
356 cmp::min(buf.len(), READ_LIMIT),
357 )
358 })?;
359 Ok(ret as usize)
360 }
361
362 #[cfg(not(any(
363 target_os = "espidf",
364 target_os = "horizon",
365 target_os = "vita",
366 target_os = "nuttx"
367 )))]
368 pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
369 let ret = cvt(unsafe {
370 libc::writev(
371 self.as_raw_fd(),
372 bufs.as_ptr() as *const libc::iovec,
373 cmp::min(bufs.len(), max_iov()) as libc::c_int,
374 )
375 })?;
376 Ok(ret as usize)
377 }
378
379 #[cfg(any(
380 target_os = "espidf",
381 target_os = "horizon",
382 target_os = "vita",
383 target_os = "nuttx"
384 ))]
385 pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
386 io::default_write_vectored(|b| self.write(b), bufs)
387 }
388
389 #[inline]
390 pub fn is_write_vectored(&self) -> bool {
391 truecfg!(not(any(
392 target_os = "espidf",
393 target_os = "horizon",
394 target_os = "vita",
395 target_os = "nuttx",
396 target_os = "wasi",
397 )))
398 }
399
400 pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
401 #[cfg(not(any(
402 all(target_os = "linux", not(target_env = "musl")),
403 target_os = "android",
404 target_os = "hurd"
405 )))]
406 use libc::pwrite as pwrite64;
407 #[cfg(any(
408 all(target_os = "linux", not(target_env = "musl")),
409 target_os = "android",
410 target_os = "hurd"
411 ))]
412 use libc::pwrite64;
413
414 unsafe {
415 cvt(pwrite64(
416 self.as_raw_fd(),
417 buf.as_ptr() as *const libc::c_void,
418 cmp::min(buf.len(), READ_LIMIT),
419 offset as off64_t,
420 ))
421 .map(|n| n as usize)
422 }
423 }
424
425 #[cfg(any(
426 target_os = "aix",
427 target_os = "dragonfly", target_os = "emscripten",
429 target_os = "freebsd",
430 target_os = "fuchsia",
431 target_os = "hurd",
432 target_os = "illumos",
433 target_os = "linux",
434 target_os = "netbsd",
435 target_os = "openbsd", all(target_os = "macos", target_arch = "aarch64"),
437 ))]
438 pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
439 let ret = cvt(unsafe {
440 libc::pwritev(
441 self.as_raw_fd(),
442 bufs.as_ptr() as *const libc::iovec,
443 cmp::min(bufs.len(), max_iov()) as libc::c_int,
444 offset as _,
445 )
446 })?;
447 Ok(ret as usize)
448 }
449
450 #[cfg(not(any(
451 target_os = "aix",
452 target_os = "android",
453 target_os = "dragonfly",
454 target_os = "emscripten",
455 target_os = "freebsd",
456 target_os = "fuchsia",
457 target_os = "hurd",
458 target_os = "illumos",
459 target_os = "linux",
460 target_os = "netbsd",
461 target_os = "openbsd",
462 target_vendor = "apple",
463 )))]
464 pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
465 io::default_write_vectored(|b| self.write_at(b, offset), bufs)
466 }
467
468 #[cfg(all(target_os = "android", target_pointer_width = "64"))]
475 pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
476 syscall!(
477 fn pwritev(
478 fd: libc::c_int,
479 iovec: *const libc::iovec,
480 n_iovec: libc::c_int,
481 offset: off64_t,
482 ) -> isize;
483 );
484
485 let ret = cvt(unsafe {
486 pwritev(
487 self.as_raw_fd(),
488 bufs.as_ptr() as *const libc::iovec,
489 cmp::min(bufs.len(), max_iov()) as libc::c_int,
490 offset as _,
491 )
492 })?;
493 Ok(ret as usize)
494 }
495
496 #[cfg(all(target_os = "android", target_pointer_width = "32"))]
497 pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
498 weak!(
499 fn pwritev64(
500 fd: libc::c_int,
501 iovec: *const libc::iovec,
502 n_iovec: libc::c_int,
503 offset: off64_t,
504 ) -> isize;
505 );
506
507 match pwritev64.get() {
508 Some(pwritev) => {
509 let ret = cvt(unsafe {
510 pwritev(
511 self.as_raw_fd(),
512 bufs.as_ptr() as *const libc::iovec,
513 cmp::min(bufs.len(), max_iov()) as libc::c_int,
514 offset as _,
515 )
516 })?;
517 Ok(ret as usize)
518 }
519 None => io::default_write_vectored(|b| self.write_at(b, offset), bufs),
520 }
521 }
522
523 #[cfg(all(target_vendor = "apple", not(all(target_os = "macos", target_arch = "aarch64")),))]
534 pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
535 weak!(
536 fn pwritev(
537 fd: libc::c_int,
538 iovec: *const libc::iovec,
539 n_iovec: libc::c_int,
540 offset: off64_t,
541 ) -> isize;
542 );
543
544 match pwritev.get() {
545 Some(pwritev) => {
546 let ret = cvt(unsafe {
547 pwritev(
548 self.as_raw_fd(),
549 bufs.as_ptr() as *const libc::iovec,
550 cmp::min(bufs.len(), max_iov()) as libc::c_int,
551 offset as _,
552 )
553 })?;
554 Ok(ret as usize)
555 }
556 None => io::default_write_vectored(|b| self.write_at(b, offset), bufs),
557 }
558 }
559
560 #[cfg(not(any(
561 target_env = "newlib",
562 target_os = "solaris",
563 target_os = "illumos",
564 target_os = "emscripten",
565 target_os = "fuchsia",
566 target_os = "l4re",
567 target_os = "linux",
568 target_os = "cygwin",
569 target_os = "haiku",
570 target_os = "redox",
571 target_os = "vxworks",
572 target_os = "nto",
573 target_os = "qnx",
574 target_os = "wasi",
575 )))]
576 pub fn set_cloexec(&self) -> io::Result<()> {
577 unsafe {
578 cvt(libc::ioctl(self.as_raw_fd(), libc::FIOCLEX))?;
579 Ok(())
580 }
581 }
582 #[cfg(any(
583 all(
584 target_env = "newlib",
585 not(any(target_os = "espidf", target_os = "horizon", target_os = "vita"))
586 ),
587 target_os = "solaris",
588 target_os = "illumos",
589 target_os = "emscripten",
590 target_os = "fuchsia",
591 target_os = "l4re",
592 target_os = "linux",
593 target_os = "cygwin",
594 target_os = "haiku",
595 target_os = "redox",
596 target_os = "vxworks",
597 target_os = "nto",
598 target_os = "qnx",
599 target_os = "wasi",
600 ))]
601 pub fn set_cloexec(&self) -> io::Result<()> {
602 unsafe {
603 let previous = cvt(libc::fcntl(self.as_raw_fd(), libc::F_GETFD))?;
604 let new = previous | libc::FD_CLOEXEC;
605 if new != previous {
606 cvt(libc::fcntl(self.as_raw_fd(), libc::F_SETFD, new))?;
607 }
608 Ok(())
609 }
610 }
611 #[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita"))]
612 pub fn set_cloexec(&self) -> io::Result<()> {
613 Ok(())
616 }
617
618 #[cfg(target_os = "linux")]
619 pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
620 unsafe {
621 let v = nonblocking as libc::c_int;
622 cvt(libc::ioctl(self.as_raw_fd(), libc::FIONBIO, &v))?;
623 Ok(())
624 }
625 }
626
627 #[cfg(not(target_os = "linux"))]
628 pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
629 unsafe {
630 let previous = cvt(libc::fcntl(self.as_raw_fd(), libc::F_GETFL))?;
631 let new = if nonblocking {
632 previous | libc::O_NONBLOCK
633 } else {
634 previous & !libc::O_NONBLOCK
635 };
636 if new != previous {
637 cvt(libc::fcntl(self.as_raw_fd(), libc::F_SETFL, new))?;
638 }
639 Ok(())
640 }
641 }
642
643 #[inline]
644 pub fn duplicate(&self) -> io::Result<FileDesc> {
645 Ok(Self(self.0.try_clone()?))
646 }
647}
648
649impl<'a> Read for &'a FileDesc {
650 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
651 (**self).read(buf)
652 }
653
654 fn read_buf(&mut self, cursor: BorrowedCursor<'_, u8>) -> io::Result<()> {
655 (**self).read_buf(cursor)
656 }
657
658 fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
659 (**self).read_vectored(bufs)
660 }
661
662 #[inline]
663 fn is_read_vectored(&self) -> bool {
664 (**self).is_read_vectored()
665 }
666}
667
668impl AsInner<OwnedFd> for FileDesc {
669 #[inline]
670 fn as_inner(&self) -> &OwnedFd {
671 &self.0
672 }
673}
674
675impl IntoInner<OwnedFd> for FileDesc {
676 fn into_inner(self) -> OwnedFd {
677 self.0
678 }
679}
680
681impl FromInner<OwnedFd> for FileDesc {
682 fn from_inner(owned_fd: OwnedFd) -> Self {
683 Self(owned_fd)
684 }
685}
686
687impl AsFd for FileDesc {
688 fn as_fd(&self) -> BorrowedFd<'_> {
689 self.0.as_fd()
690 }
691}
692
693impl AsRawFd for FileDesc {
694 #[inline]
695 fn as_raw_fd(&self) -> RawFd {
696 self.0.as_raw_fd()
697 }
698}
699
700impl IntoRawFd for FileDesc {
701 fn into_raw_fd(self) -> RawFd {
702 self.0.into_raw_fd()
703 }
704}
705
706impl FromRawFd for FileDesc {
707 unsafe fn from_raw_fd(raw_fd: RawFd) -> Self {
708 Self(unsafe { FromRawFd::from_raw_fd(raw_fd) })
709 }
710}