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