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