rustpython_common/
crt_fd.rs

1//! A module implementing an io type backed by the C runtime's file descriptors, i.e. what's
2//! returned from libc::open, even on windows.
3
4use std::{cmp, ffi, io};
5
6#[cfg(windows)]
7use libc::commit as fsync;
8#[cfg(windows)]
9extern "C" {
10    #[link_name = "_chsize_s"]
11    fn ftruncate(fd: i32, len: i64) -> i32;
12}
13#[cfg(not(windows))]
14use libc::{fsync, ftruncate};
15
16// this is basically what CPython has for Py_off_t; windows uses long long
17// for offsets, other platforms just use off_t
18#[cfg(not(windows))]
19pub type Offset = libc::off_t;
20#[cfg(windows)]
21pub type Offset = libc::c_longlong;
22
23#[inline]
24fn cvt<T, I: num_traits::PrimInt>(ret: I, f: impl FnOnce(I) -> T) -> io::Result<T> {
25    if ret < I::zero() {
26        Err(crate::os::last_os_error())
27    } else {
28        Ok(f(ret))
29    }
30}
31
32const MAX_RW: usize = if cfg!(any(windows, target_vendor = "apple")) {
33    i32::MAX as usize
34} else {
35    isize::MAX as usize
36};
37
38#[derive(Copy, Clone, PartialEq, Eq)]
39#[repr(transparent)]
40pub struct Fd(pub i32);
41
42impl Fd {
43    pub fn open(path: &ffi::CStr, flags: i32, mode: i32) -> io::Result<Self> {
44        cvt(unsafe { libc::open(path.as_ptr(), flags, mode) }, Fd)
45    }
46
47    #[cfg(windows)]
48    pub fn wopen(path: &widestring::WideCStr, flags: i32, mode: i32) -> io::Result<Self> {
49        cvt(
50            unsafe { suppress_iph!(libc::wopen(path.as_ptr(), flags, mode)) },
51            Fd,
52        )
53    }
54
55    #[cfg(all(any(unix, target_os = "wasi"), not(target_os = "redox")))]
56    pub fn openat(&self, path: &ffi::CStr, flags: i32, mode: i32) -> io::Result<Self> {
57        cvt(
58            unsafe { libc::openat(self.0, path.as_ptr(), flags, mode) },
59            Fd,
60        )
61    }
62
63    pub fn fsync(&self) -> io::Result<()> {
64        cvt(unsafe { suppress_iph!(fsync(self.0)) }, drop)
65    }
66
67    pub fn close(&self) -> io::Result<()> {
68        cvt(unsafe { suppress_iph!(libc::close(self.0)) }, drop)
69    }
70
71    pub fn ftruncate(&self, len: Offset) -> io::Result<()> {
72        cvt(unsafe { suppress_iph!(ftruncate(self.0, len)) }, drop)
73    }
74
75    #[cfg(windows)]
76    pub fn to_raw_handle(&self) -> io::Result<std::os::windows::io::RawHandle> {
77        extern "C" {
78            fn _get_osfhandle(fd: i32) -> libc::intptr_t;
79        }
80        let handle = unsafe { suppress_iph!(_get_osfhandle(self.0)) };
81        if handle == -1 {
82            Err(io::Error::last_os_error())
83        } else {
84            Ok(handle as _)
85        }
86    }
87}
88
89impl io::Write for Fd {
90    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
91        let count = cmp::min(buf.len(), MAX_RW);
92        cvt(
93            unsafe { suppress_iph!(libc::write(self.0, buf.as_ptr() as _, count as _)) },
94            |i| i as usize,
95        )
96    }
97
98    #[inline]
99    fn flush(&mut self) -> io::Result<()> {
100        Ok(())
101    }
102}
103
104impl io::Read for Fd {
105    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
106        let count = cmp::min(buf.len(), MAX_RW);
107        cvt(
108            unsafe { suppress_iph!(libc::read(self.0, buf.as_mut_ptr() as _, count as _)) },
109            |i| i as usize,
110        )
111    }
112}