1#![unstable(reason = "not public", issue = "none", feature = "fd")]
2
3#[cfg(test)]
4mod tests;
5
6use crate::cmp;
7use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, Read};
8use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
9use crate::sys::cvt;
10use crate::sys_common::{AsInner, FromInner, IntoInner};
11
12#[cfg(any(
13 target_os = "android",
14 target_os = "linux",
15 target_os = "emscripten",
16 target_os = "l4re",
17 target_os = "hurd",
18))]
19use libc::off64_t;
20#[cfg(not(any(
21 target_os = "linux",
22 target_os = "emscripten",
23 target_os = "l4re",
24 target_os = "android",
25 target_os = "hurd",
26)))]
27use libc::off_t as off64_t;
28
29#[derive(Debug)]
30pub struct FileDesc(OwnedFd);
31
32// The maximum read limit on most POSIX-like systems is `SSIZE_MAX`,
33// with the man page quoting that if the count of bytes to read is
34// greater than `SSIZE_MAX` the result is "unspecified".
35//
36// On macOS, however, apparently the 64-bit libc is either buggy or
37// intentionally showing odd behavior by rejecting any read with a size
38// larger than or equal to INT_MAX. To handle both of these the read
39// size is capped on both platforms.
40#[cfg(target_os = "macos")]
41const READ_LIMIT: usize = libc::c_int::MAX as usize - 1;
42#[cfg(not(target_os = "macos"))]
43const READ_LIMIT: usize = libc::ssize_t::MAX as usize;
44
45#[cfg(any(
46 target_os = "dragonfly",
47 target_os = "freebsd",
48 target_os = "netbsd",
49 target_os = "openbsd",
50 target_vendor = "apple",
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)))]
79const fn max_iov() -> usize {
80 16 // The minimum value required by POSIX.
81}
82
83impl FileDesc {
84 pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
85 let ret = cvt(unsafe {
86 libc::read(
87 self.as_raw_fd(),
88 buf.as_mut_ptr() as *mut libc::c_void,
89 cmp::min(buf.len(), READ_LIMIT),
90 )
91 })?;
92 Ok(ret as usize)
93 }
94
95 #[cfg(not(any(target_os = "espidf", target_os = "horizon", target_os = "vita")))]
96 pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
97 let ret = cvt(unsafe {
98 libc::readv(
99 self.as_raw_fd(),
100 bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec,
101 cmp::min(bufs.len(), max_iov()) as libc::c_int,
102 )
103 })?;
104 Ok(ret as usize)
105 }
106
107 #[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita"))]
108 pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
109 io::default_read_vectored(|b| self.read(b), bufs)
110 }
111
112 #[inline]
113 pub fn is_read_vectored(&self) -> bool {
114 cfg!(not(any(target_os = "espidf", target_os = "horizon", target_os = "vita")))
115 }
116
117 pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> {
118 let mut me = self;
119 (&mut me).read_to_end(buf)
120 }
121
122 pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
123 #[cfg(not(any(
124 all(target_os = "linux", not(target_env = "musl")),
125 target_os = "android",
126 target_os = "hurd"
127 )))]
128 use libc::pread as pread64;
129 #[cfg(any(
130 all(target_os = "linux", not(target_env = "musl")),
131 target_os = "android",
132 target_os = "hurd"
133 ))]
134 use libc::pread64;
135
136 unsafe {
137 cvt(pread64(
138 self.as_raw_fd(),
139 buf.as_mut_ptr() as *mut libc::c_void,
140 cmp::min(buf.len(), READ_LIMIT),
141 offset as off64_t,
142 ))
143 .map(|n| n as usize)
144 }
145 }
146
147 pub fn read_buf(&self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> {
148 let ret = cvt(unsafe {
149 libc::read(
150 self.as_raw_fd(),
151 cursor.as_mut().as_mut_ptr() as *mut libc::c_void,
152 cmp::min(cursor.capacity(), READ_LIMIT),
153 )
154 })?;
155
156 // Safety: `ret` bytes were written to the initialized portion of the buffer
157 unsafe {
158 cursor.advance_unchecked(ret as usize);
159 }
160 Ok(())
161 }
162
163 #[cfg(any(
164 target_os = "aix",
165 target_os = "dragonfly", // DragonFly 1.5
166 target_os = "emscripten",
167 target_os = "freebsd",
168 target_os = "fuchsia",
169 target_os = "hurd",
170 target_os = "illumos",
171 target_os = "linux",
172 target_os = "netbsd",
173 target_os = "openbsd", // OpenBSD 2.7
174 ))]
175 pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
176 let ret = cvt(unsafe {
177 libc::preadv(
178 self.as_raw_fd(),
179 bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec,
180 cmp::min(bufs.len(), max_iov()) as libc::c_int,
181 offset as _,
182 )
183 })?;
184 Ok(ret as usize)
185 }
186
187 #[cfg(not(any(
188 target_os = "aix",
189 target_os = "android",
190 target_os = "dragonfly",
191 target_os = "emscripten",
192 target_os = "freebsd",
193 target_os = "fuchsia",
194 target_os = "hurd",
195 target_os = "illumos",
196 target_os = "linux",
197 target_os = "netbsd",
198 target_os = "openbsd",
199 target_vendor = "apple",
200 )))]
201 pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
202 io::default_read_vectored(|b| self.read_at(b, offset), bufs)
203 }
204
205 // We support some old Android versions that do not have `preadv` in libc,
206 // so we use weak linkage and fallback to a direct syscall if not available.
207 //
208 // On 32-bit targets, we don't want to deal with weird ABI issues around
209 // passing 64-bits parameters to syscalls, so we fallback to the default
210 // implementation if `preadv` is not available.
211 #[cfg(all(target_os = "android", target_pointer_width = "64"))]
212 pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
213 super::weak::syscall! {
214 fn preadv(
215 fd: libc::c_int,
216 iovec: *const libc::iovec,
217 n_iovec: libc::c_int,
218 offset: off64_t
219 ) -> isize
220 }
221
222 let ret = cvt(unsafe {
223 preadv(
224 self.as_raw_fd(),
225 bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec,
226 cmp::min(bufs.len(), max_iov()) as libc::c_int,
227 offset as _,
228 )
229 })?;
230 Ok(ret as usize)
231 }
232
233 #[cfg(all(target_os = "android", target_pointer_width = "32"))]
234 pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
235 super::weak::weak!(fn preadv64(libc::c_int, *const libc::iovec, libc::c_int, off64_t) -> isize);
236
237 match preadv64.get() {
238 Some(preadv) => {
239 let ret = cvt(unsafe {
240 preadv(
241 self.as_raw_fd(),
242 bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec,
243 cmp::min(bufs.len(), max_iov()) as libc::c_int,
244 offset as _,
245 )
246 })?;
247 Ok(ret as usize)
248 }
249 None => io::default_read_vectored(|b| self.read_at(b, offset), bufs),
250 }
251 }
252
253 // We support old MacOS, iOS, watchOS, tvOS and visionOS. `preadv` was added in the following
254 // Apple OS versions:
255 // ios 14.0
256 // tvos 14.0
257 // macos 11.0
258 // watchos 7.0
259 //
260 // These versions may be newer than the minimum supported versions of OS's we support so we must
261 // use "weak" linking.
262 #[cfg(target_vendor = "apple")]
263 pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
264 super::weak::weak!(fn preadv(libc::c_int, *const libc::iovec, libc::c_int, off64_t) -> isize);
265
266 match preadv.get() {
267 Some(preadv) => {
268 let ret = cvt(unsafe {
269 preadv(
270 self.as_raw_fd(),
271 bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec,
272 cmp::min(bufs.len(), max_iov()) as libc::c_int,
273 offset as _,
274 )
275 })?;
276 Ok(ret as usize)
277 }
278 None => io::default_read_vectored(|b| self.read_at(b, offset), bufs),
279 }
280 }
281
282 pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
283 let ret = cvt(unsafe {
284 libc::write(
285 self.as_raw_fd(),
286 buf.as_ptr() as *const libc::c_void,
287 cmp::min(buf.len(), READ_LIMIT),
288 )
289 })?;
290 Ok(ret as usize)
291 }
292
293 #[cfg(not(any(target_os = "espidf", target_os = "horizon", target_os = "vita")))]
294 pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
295 let ret = cvt(unsafe {
296 libc::writev(
297 self.as_raw_fd(),
298 bufs.as_ptr() as *const libc::iovec,
299 cmp::min(bufs.len(), max_iov()) as libc::c_int,
300 )
301 })?;
302 Ok(ret as usize)
303 }
304
305 #[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita"))]
306 pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
307 io::default_write_vectored(|b| self.write(b), bufs)
308 }
309
310 #[inline]
311 pub fn is_write_vectored(&self) -> bool {
312 cfg!(not(any(target_os = "espidf", target_os = "horizon", target_os = "vita")))
313 }
314
315 pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
316 #[cfg(not(any(
317 all(target_os = "linux", not(target_env = "musl")),
318 target_os = "android",
319 target_os = "hurd"
320 )))]
321 use libc::pwrite as pwrite64;
322 #[cfg(any(
323 all(target_os = "linux", not(target_env = "musl")),
324 target_os = "android",
325 target_os = "hurd"
326 ))]
327 use libc::pwrite64;
328
329 unsafe {
330 cvt(pwrite64(
331 self.as_raw_fd(),
332 buf.as_ptr() as *const libc::c_void,
333 cmp::min(buf.len(), READ_LIMIT),
334 offset as off64_t,
335 ))
336 .map(|n| n as usize)
337 }
338 }
339
340 #[cfg(any(
341 target_os = "aix",
342 target_os = "dragonfly", // DragonFly 1.5
343 target_os = "emscripten",
344 target_os = "freebsd",
345 target_os = "fuchsia",
346 target_os = "hurd",
347 target_os = "illumos",
348 target_os = "linux",
349 target_os = "netbsd",
350 target_os = "openbsd", // OpenBSD 2.7
351 ))]
352 pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
353 let ret = cvt(unsafe {
354 libc::pwritev(
355 self.as_raw_fd(),
356 bufs.as_ptr() as *const libc::iovec,
357 cmp::min(bufs.len(), max_iov()) as libc::c_int,
358 offset as _,
359 )
360 })?;
361 Ok(ret as usize)
362 }
363
364 #[cfg(not(any(
365 target_os = "aix",
366 target_os = "android",
367 target_os = "dragonfly",
368 target_os = "emscripten",
369 target_os = "freebsd",
370 target_os = "fuchsia",
371 target_os = "hurd",
372 target_os = "illumos",
373 target_os = "linux",
374 target_os = "netbsd",
375 target_os = "openbsd",
376 target_vendor = "apple",
377 )))]
378 pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
379 io::default_write_vectored(|b| self.write_at(b, offset), bufs)
380 }
381
382 // We support some old Android versions that do not have `pwritev` in libc,
383 // so we use weak linkage and fallback to a direct syscall if not available.
384 //
385 // On 32-bit targets, we don't want to deal with weird ABI issues around
386 // passing 64-bits parameters to syscalls, so we fallback to the default
387 // implementation if `pwritev` is not available.
388 #[cfg(all(target_os = "android", target_pointer_width = "64"))]
389 pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
390 super::weak::syscall! {
391 fn pwritev(
392 fd: libc::c_int,
393 iovec: *const libc::iovec,
394 n_iovec: libc::c_int,
395 offset: off64_t
396 ) -> isize
397 }
398
399 let ret = cvt(unsafe {
400 pwritev(
401 self.as_raw_fd(),
402 bufs.as_ptr() as *const libc::iovec,
403 cmp::min(bufs.len(), max_iov()) as libc::c_int,
404 offset as _,
405 )
406 })?;
407 Ok(ret as usize)
408 }
409
410 #[cfg(all(target_os = "android", target_pointer_width = "32"))]
411 pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
412 super::weak::weak!(fn pwritev64(libc::c_int, *const libc::iovec, libc::c_int, off64_t) -> isize);
413
414 match pwritev64.get() {
415 Some(pwritev) => {
416 let ret = cvt(unsafe {
417 pwritev(
418 self.as_raw_fd(),
419 bufs.as_ptr() as *const libc::iovec,
420 cmp::min(bufs.len(), max_iov()) as libc::c_int,
421 offset as _,
422 )
423 })?;
424 Ok(ret as usize)
425 }
426 None => io::default_write_vectored(|b| self.write_at(b, offset), bufs),
427 }
428 }
429
430 // We support old MacOS, iOS, watchOS, tvOS and visionOS. `pwritev` was added in the following
431 // Apple OS versions:
432 // ios 14.0
433 // tvos 14.0
434 // macos 11.0
435 // watchos 7.0
436 //
437 // These versions may be newer than the minimum supported versions of OS's we support so we must
438 // use "weak" linking.
439 #[cfg(target_vendor = "apple")]
440 pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
441 super::weak::weak!(fn pwritev(libc::c_int, *const libc::iovec, libc::c_int, off64_t) -> isize);
442
443 match pwritev.get() {
444 Some(pwritev) => {
445 let ret = cvt(unsafe {
446 pwritev(
447 self.as_raw_fd(),
448 bufs.as_ptr() as *const libc::iovec,
449 cmp::min(bufs.len(), max_iov()) as libc::c_int,
450 offset as _,
451 )
452 })?;
453 Ok(ret as usize)
454 }
455 None => io::default_write_vectored(|b| self.write_at(b, offset), bufs),
456 }
457 }
458
459 #[cfg(not(any(
460 target_env = "newlib",
461 target_os = "solaris",
462 target_os = "illumos",
463 target_os = "emscripten",
464 target_os = "fuchsia",
465 target_os = "l4re",
466 target_os = "linux",
467 target_os = "haiku",
468 target_os = "redox",
469 target_os = "vxworks",
470 target_os = "nto",
471 )))]
472 pub fn set_cloexec(&self) -> io::Result<()> {
473 unsafe {
474 cvt(libc::ioctl(self.as_raw_fd(), libc::FIOCLEX))?;
475 Ok(())
476 }
477 }
478 #[cfg(any(
479 all(
480 target_env = "newlib",
481 not(any(target_os = "espidf", target_os = "horizon", target_os = "vita"))
482 ),
483 target_os = "solaris",
484 target_os = "illumos",
485 target_os = "emscripten",
486 target_os = "fuchsia",
487 target_os = "l4re",
488 target_os = "linux",
489 target_os = "haiku",
490 target_os = "redox",
491 target_os = "vxworks",
492 target_os = "nto",
493 ))]
494 pub fn set_cloexec(&self) -> io::Result<()> {
495 unsafe {
496 let previous = cvt(libc::fcntl(self.as_raw_fd(), libc::F_GETFD))?;
497 let new = previous | libc::FD_CLOEXEC;
498 if new != previous {
499 cvt(libc::fcntl(self.as_raw_fd(), libc::F_SETFD, new))?;
500 }
501 Ok(())
502 }
503 }
504 #[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita"))]
505 pub fn set_cloexec(&self) -> io::Result<()> {
506 // FD_CLOEXEC is not supported in ESP-IDF, Horizon OS and Vita but there's no need to,
507 // because none of them supports spawning processes.
508 Ok(())
509 }
510
511 #[cfg(target_os = "linux")]
512 pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
513 unsafe {
514 let v = nonblocking as libc::c_int;
515 cvt(libc::ioctl(self.as_raw_fd(), libc::FIONBIO, &v))?;
516 Ok(())
517 }
518 }
519
520 #[cfg(not(target_os = "linux"))]
521 pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
522 unsafe {
523 let previous = cvt(libc::fcntl(self.as_raw_fd(), libc::F_GETFL))?;
524 let new = if nonblocking {
525 previous | libc::O_NONBLOCK
526 } else {
527 previous & !libc::O_NONBLOCK
528 };
529 if new != previous {
530 cvt(libc::fcntl(self.as_raw_fd(), libc::F_SETFL, new))?;
531 }
532 Ok(())
533 }
534 }
535
536 #[inline]
537 pub fn duplicate(&self) -> io::Result<FileDesc> {
538 Ok(Self(self.0.try_clone()?))
539 }
540}
541
542impl<'a> Read for &'a FileDesc {
543 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
544 (**self).read(buf)
545 }
546
547 fn read_buf(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> {
548 (**self).read_buf(cursor)
549 }
550
551 fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
552 (**self).read_vectored(bufs)
553 }
554
555 #[inline]
556 fn is_read_vectored(&self) -> bool {
557 (**self).is_read_vectored()
558 }
559}
560
561impl AsInner<OwnedFd> for FileDesc {
562 #[inline]
563 fn as_inner(&self) -> &OwnedFd {
564 &self.0
565 }
566}
567
568impl IntoInner<OwnedFd> for FileDesc {
569 fn into_inner(self) -> OwnedFd {
570 self.0
571 }
572}
573
574impl FromInner<OwnedFd> for FileDesc {
575 fn from_inner(owned_fd: OwnedFd) -> Self {
576 Self(owned_fd)
577 }
578}
579
580impl AsFd for FileDesc {
581 fn as_fd(&self) -> BorrowedFd<'_> {
582 self.0.as_fd()
583 }
584}
585
586impl AsRawFd for FileDesc {
587 #[inline]
588 fn as_raw_fd(&self) -> RawFd {
589 self.0.as_raw_fd()
590 }
591}
592
593impl IntoRawFd for FileDesc {
594 fn into_raw_fd(self) -> RawFd {
595 self.0.into_raw_fd()
596 }
597}
598
599impl FromRawFd for FileDesc {
600 unsafe fn from_raw_fd(raw_fd: RawFd) -> Self {
601 Self(FromRawFd::from_raw_fd(raw_fd))
602 }
603}
604