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