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