Skip to main content

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