std/sys/fd/
unix.rs

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