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