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