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 #[no_sanitize(cfi)]
257 pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
258 super::weak::weak!(fn preadv64(libc::c_int, *const libc::iovec, libc::c_int, off64_t) -> isize);
259
260 match preadv64.get() {
261 Some(preadv) => {
262 let ret = cvt(unsafe {
263 preadv(
264 self.as_raw_fd(),
265 bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec,
266 cmp::min(bufs.len(), max_iov()) as libc::c_int,
267 offset as _,
268 )
269 })?;
270 Ok(ret as usize)
271 }
272 None => io::default_read_vectored(|b| self.read_at(b, offset), bufs),
273 }
274 }
275
276 #[cfg(target_vendor = "apple")]
286 pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
287 super::weak::weak!(fn preadv(libc::c_int, *const libc::iovec, libc::c_int, off64_t) -> isize);
288
289 match preadv.get() {
290 Some(preadv) => {
291 let ret = cvt(unsafe {
292 preadv(
293 self.as_raw_fd(),
294 bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec,
295 cmp::min(bufs.len(), max_iov()) as libc::c_int,
296 offset as _,
297 )
298 })?;
299 Ok(ret as usize)
300 }
301 None => io::default_read_vectored(|b| self.read_at(b, offset), bufs),
302 }
303 }
304
305 pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
306 let ret = cvt(unsafe {
307 libc::write(
308 self.as_raw_fd(),
309 buf.as_ptr() as *const libc::c_void,
310 cmp::min(buf.len(), READ_LIMIT),
311 )
312 })?;
313 Ok(ret as usize)
314 }
315
316 #[cfg(not(any(
317 target_os = "espidf",
318 target_os = "horizon",
319 target_os = "vita",
320 target_os = "nuttx"
321 )))]
322 pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
323 let ret = cvt(unsafe {
324 libc::writev(
325 self.as_raw_fd(),
326 bufs.as_ptr() as *const libc::iovec,
327 cmp::min(bufs.len(), max_iov()) as libc::c_int,
328 )
329 })?;
330 Ok(ret as usize)
331 }
332
333 #[cfg(any(
334 target_os = "espidf",
335 target_os = "horizon",
336 target_os = "vita",
337 target_os = "nuttx"
338 ))]
339 pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
340 io::default_write_vectored(|b| self.write(b), bufs)
341 }
342
343 #[inline]
344 pub fn is_write_vectored(&self) -> bool {
345 cfg!(not(any(
346 target_os = "espidf",
347 target_os = "horizon",
348 target_os = "vita",
349 target_os = "nuttx"
350 )))
351 }
352
353 #[cfg_attr(target_os = "vxworks", allow(unused_unsafe))]
354 pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
355 #[cfg(not(any(
356 all(target_os = "linux", not(target_env = "musl")),
357 target_os = "android",
358 target_os = "hurd"
359 )))]
360 use libc::pwrite as pwrite64;
361 #[cfg(any(
362 all(target_os = "linux", not(target_env = "musl")),
363 target_os = "android",
364 target_os = "hurd"
365 ))]
366 use libc::pwrite64;
367
368 unsafe {
369 cvt(pwrite64(
370 self.as_raw_fd(),
371 buf.as_ptr() as *const libc::c_void,
372 cmp::min(buf.len(), READ_LIMIT),
373 offset as off64_t,
374 ))
375 .map(|n| n as usize)
376 }
377 }
378
379 #[cfg(any(
380 target_os = "aix",
381 target_os = "dragonfly", target_os = "emscripten",
383 target_os = "freebsd",
384 target_os = "fuchsia",
385 target_os = "hurd",
386 target_os = "illumos",
387 target_os = "linux",
388 target_os = "netbsd",
389 target_os = "openbsd", ))]
391 pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
392 let ret = cvt(unsafe {
393 libc::pwritev(
394 self.as_raw_fd(),
395 bufs.as_ptr() as *const libc::iovec,
396 cmp::min(bufs.len(), max_iov()) as libc::c_int,
397 offset as _,
398 )
399 })?;
400 Ok(ret as usize)
401 }
402
403 #[cfg(not(any(
404 target_os = "aix",
405 target_os = "android",
406 target_os = "dragonfly",
407 target_os = "emscripten",
408 target_os = "freebsd",
409 target_os = "fuchsia",
410 target_os = "hurd",
411 target_os = "illumos",
412 target_os = "linux",
413 target_os = "netbsd",
414 target_os = "openbsd",
415 target_vendor = "apple",
416 )))]
417 pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
418 io::default_write_vectored(|b| self.write_at(b, offset), bufs)
419 }
420
421 #[cfg(all(target_os = "android", target_pointer_width = "64"))]
428 pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
429 super::weak::syscall! {
430 fn pwritev(
431 fd: libc::c_int,
432 iovec: *const libc::iovec,
433 n_iovec: libc::c_int,
434 offset: off64_t
435 ) -> isize
436 }
437
438 let ret = cvt(unsafe {
439 pwritev(
440 self.as_raw_fd(),
441 bufs.as_ptr() as *const libc::iovec,
442 cmp::min(bufs.len(), max_iov()) as libc::c_int,
443 offset as _,
444 )
445 })?;
446 Ok(ret as usize)
447 }
448
449 #[cfg(all(target_os = "android", target_pointer_width = "32"))]
450 pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
451 super::weak::weak!(fn pwritev64(libc::c_int, *const libc::iovec, libc::c_int, off64_t) -> isize);
452
453 match pwritev64.get() {
454 Some(pwritev) => {
455 let ret = cvt(unsafe {
456 pwritev(
457 self.as_raw_fd(),
458 bufs.as_ptr() as *const libc::iovec,
459 cmp::min(bufs.len(), max_iov()) as libc::c_int,
460 offset as _,
461 )
462 })?;
463 Ok(ret as usize)
464 }
465 None => io::default_write_vectored(|b| self.write_at(b, offset), bufs),
466 }
467 }
468
469 #[cfg(target_vendor = "apple")]
479 pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
480 super::weak::weak!(fn pwritev(libc::c_int, *const libc::iovec, libc::c_int, off64_t) -> isize);
481
482 match pwritev.get() {
483 Some(pwritev) => {
484 let ret = cvt(unsafe {
485 pwritev(
486 self.as_raw_fd(),
487 bufs.as_ptr() as *const libc::iovec,
488 cmp::min(bufs.len(), max_iov()) as libc::c_int,
489 offset as _,
490 )
491 })?;
492 Ok(ret as usize)
493 }
494 None => io::default_write_vectored(|b| self.write_at(b, offset), bufs),
495 }
496 }
497
498 #[cfg(not(any(
499 target_env = "newlib",
500 target_os = "solaris",
501 target_os = "illumos",
502 target_os = "emscripten",
503 target_os = "fuchsia",
504 target_os = "l4re",
505 target_os = "linux",
506 target_os = "haiku",
507 target_os = "redox",
508 target_os = "vxworks",
509 target_os = "nto",
510 )))]
511 pub fn set_cloexec(&self) -> io::Result<()> {
512 unsafe {
513 cvt(libc::ioctl(self.as_raw_fd(), libc::FIOCLEX))?;
514 Ok(())
515 }
516 }
517 #[cfg(any(
518 all(
519 target_env = "newlib",
520 not(any(target_os = "espidf", target_os = "horizon", target_os = "vita"))
521 ),
522 target_os = "solaris",
523 target_os = "illumos",
524 target_os = "emscripten",
525 target_os = "fuchsia",
526 target_os = "l4re",
527 target_os = "linux",
528 target_os = "haiku",
529 target_os = "redox",
530 target_os = "vxworks",
531 target_os = "nto",
532 ))]
533 pub fn set_cloexec(&self) -> io::Result<()> {
534 unsafe {
535 let previous = cvt(libc::fcntl(self.as_raw_fd(), libc::F_GETFD))?;
536 let new = previous | libc::FD_CLOEXEC;
537 if new != previous {
538 cvt(libc::fcntl(self.as_raw_fd(), libc::F_SETFD, new))?;
539 }
540 Ok(())
541 }
542 }
543 #[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita"))]
544 pub fn set_cloexec(&self) -> io::Result<()> {
545 Ok(())
548 }
549
550 #[cfg(target_os = "linux")]
551 pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
552 unsafe {
553 let v = nonblocking as libc::c_int;
554 cvt(libc::ioctl(self.as_raw_fd(), libc::FIONBIO, &v))?;
555 Ok(())
556 }
557 }
558
559 #[cfg(not(target_os = "linux"))]
560 pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
561 unsafe {
562 let previous = cvt(libc::fcntl(self.as_raw_fd(), libc::F_GETFL))?;
563 let new = if nonblocking {
564 previous | libc::O_NONBLOCK
565 } else {
566 previous & !libc::O_NONBLOCK
567 };
568 if new != previous {
569 cvt(libc::fcntl(self.as_raw_fd(), libc::F_SETFL, new))?;
570 }
571 Ok(())
572 }
573 }
574
575 #[inline]
576 pub fn duplicate(&self) -> io::Result<FileDesc> {
577 Ok(Self(self.0.try_clone()?))
578 }
579}
580
581impl<'a> Read for &'a FileDesc {
582 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
583 (**self).read(buf)
584 }
585
586 fn read_buf(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> {
587 (**self).read_buf(cursor)
588 }
589
590 fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
591 (**self).read_vectored(bufs)
592 }
593
594 #[inline]
595 fn is_read_vectored(&self) -> bool {
596 (**self).is_read_vectored()
597 }
598}
599
600impl AsInner<OwnedFd> for FileDesc {
601 #[inline]
602 fn as_inner(&self) -> &OwnedFd {
603 &self.0
604 }
605}
606
607impl IntoInner<OwnedFd> for FileDesc {
608 fn into_inner(self) -> OwnedFd {
609 self.0
610 }
611}
612
613impl FromInner<OwnedFd> for FileDesc {
614 fn from_inner(owned_fd: OwnedFd) -> Self {
615 Self(owned_fd)
616 }
617}
618
619impl AsFd for FileDesc {
620 fn as_fd(&self) -> BorrowedFd<'_> {
621 self.0.as_fd()
622 }
623}
624
625impl AsRawFd for FileDesc {
626 #[inline]
627 fn as_raw_fd(&self) -> RawFd {
628 self.0.as_raw_fd()
629 }
630}
631
632impl IntoRawFd for FileDesc {
633 fn into_raw_fd(self) -> RawFd {
634 self.0.into_raw_fd()
635 }
636}
637
638impl FromRawFd for FileDesc {
639 unsafe fn from_raw_fd(raw_fd: RawFd) -> Self {
640 Self(FromRawFd::from_raw_fd(raw_fd))
641 }
642}