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