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