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