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