Skip to main content

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 alloc::fmt;
5use core::cmp;
6use std::{ffi, io};
7
8#[cfg(unix)]
9use std::os::fd::AsFd;
10#[cfg(not(windows))]
11use std::os::fd::{AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
12#[cfg(windows)]
13use std::os::windows::io::BorrowedHandle;
14
15mod c {
16    pub(super) use libc::*;
17
18    #[cfg(windows)]
19    pub(super) use libc::commit as fsync;
20    #[cfg(windows)]
21    unsafe extern "C" {
22        #[link_name = "_chsize_s"]
23        pub(super) fn ftruncate(fd: i32, len: i64) -> i32;
24    }
25}
26
27// this is basically what CPython has for Py_off_t; windows uses long long
28// for offsets, other platforms just use off_t
29#[cfg(not(windows))]
30pub type Offset = c::off_t;
31#[cfg(windows)]
32pub type Offset = c::c_longlong;
33
34#[cfg(not(windows))]
35pub type Raw = RawFd;
36#[cfg(windows)]
37pub type Raw = i32;
38
39#[inline]
40fn cvt<I: num_traits::PrimInt>(ret: I) -> io::Result<I> {
41    if ret < I::zero() {
42        // CRT functions set errno, not GetLastError(), so use errno_io_error
43        Err(crate::os::errno_io_error())
44    } else {
45        Ok(ret)
46    }
47}
48
49fn cvt_fd(ret: Raw) -> io::Result<Owned> {
50    cvt(ret).map(|fd| unsafe { Owned::from_raw(fd) })
51}
52
53const MAX_RW: usize = if cfg!(any(windows, target_vendor = "apple")) {
54    i32::MAX as usize
55} else {
56    isize::MAX as usize
57};
58
59#[cfg(not(windows))]
60type OwnedInner = OwnedFd;
61#[cfg(not(windows))]
62type BorrowedInner<'fd> = BorrowedFd<'fd>;
63
64#[cfg(windows)]
65mod win {
66    use super::*;
67    use core::marker::PhantomData;
68    use core::mem::ManuallyDrop;
69
70    #[repr(transparent)]
71    pub(super) struct OwnedInner(i32);
72
73    impl OwnedInner {
74        #[inline]
75        pub unsafe fn from_raw_fd(fd: Raw) -> Self {
76            Self(fd)
77        }
78        #[inline]
79        pub fn as_raw_fd(&self) -> Raw {
80            self.0
81        }
82        #[inline]
83        pub fn into_raw_fd(self) -> Raw {
84            let me = ManuallyDrop::new(self);
85            me.0
86        }
87    }
88
89    impl Drop for OwnedInner {
90        #[inline]
91        fn drop(&mut self) {
92            let _ = _close(self.0);
93        }
94    }
95
96    #[derive(Copy, Clone)]
97    #[repr(transparent)]
98    pub(super) struct BorrowedInner<'fd> {
99        fd: Raw,
100        _marker: PhantomData<&'fd Owned>,
101    }
102
103    impl BorrowedInner<'_> {
104        #[inline]
105        pub const unsafe fn borrow_raw(fd: Raw) -> Self {
106            Self {
107                fd,
108                _marker: PhantomData,
109            }
110        }
111        #[inline]
112        pub fn as_raw_fd(&self) -> Raw {
113            self.fd
114        }
115    }
116}
117
118#[cfg(windows)]
119use self::win::{BorrowedInner, OwnedInner};
120
121#[repr(transparent)]
122pub struct Owned {
123    inner: OwnedInner,
124}
125
126impl fmt::Debug for Owned {
127    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
128        f.debug_tuple("crt_fd::Owned")
129            .field(&self.as_raw())
130            .finish()
131    }
132}
133
134#[derive(Copy, Clone)]
135#[repr(transparent)]
136pub struct Borrowed<'fd> {
137    inner: BorrowedInner<'fd>,
138}
139
140impl<'fd> PartialEq for Borrowed<'fd> {
141    fn eq(&self, other: &Self) -> bool {
142        self.as_raw() == other.as_raw()
143    }
144}
145impl<'fd> Eq for Borrowed<'fd> {}
146
147impl fmt::Debug for Borrowed<'_> {
148    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
149        f.debug_tuple("crt_fd::Borrowed")
150            .field(&self.as_raw())
151            .finish()
152    }
153}
154
155impl Owned {
156    /// Create a `crt_fd::Owned` from a raw file descriptor.
157    ///
158    /// # Safety
159    ///
160    /// `fd` must be a valid file descriptor.
161    #[inline]
162    pub unsafe fn from_raw(fd: Raw) -> Self {
163        let inner = unsafe { OwnedInner::from_raw_fd(fd) };
164        Self { inner }
165    }
166
167    /// Create a `crt_fd::Owned` from a raw file descriptor.
168    ///
169    /// Returns an error if `fd` is -1.
170    ///
171    /// # Safety
172    ///
173    /// `fd` must be a valid file descriptor.
174    #[inline]
175    pub unsafe fn try_from_raw(fd: Raw) -> io::Result<Self> {
176        if fd == -1 {
177            Err(ebadf())
178        } else {
179            Ok(unsafe { Self::from_raw(fd) })
180        }
181    }
182
183    #[inline]
184    pub fn borrow(&self) -> Borrowed<'_> {
185        unsafe { Borrowed::borrow_raw(self.as_raw()) }
186    }
187
188    #[inline]
189    pub fn as_raw(&self) -> Raw {
190        self.inner.as_raw_fd()
191    }
192
193    #[inline]
194    pub fn into_raw(self) -> Raw {
195        self.inner.into_raw_fd()
196    }
197
198    pub fn leak<'fd>(self) -> Borrowed<'fd> {
199        unsafe { Borrowed::borrow_raw(self.into_raw()) }
200    }
201}
202
203#[cfg(unix)]
204impl From<Owned> for OwnedFd {
205    fn from(fd: Owned) -> Self {
206        fd.inner
207    }
208}
209
210#[cfg(unix)]
211impl From<OwnedFd> for Owned {
212    fn from(fd: OwnedFd) -> Self {
213        Self { inner: fd }
214    }
215}
216
217#[cfg(unix)]
218impl AsFd for Owned {
219    fn as_fd(&self) -> BorrowedFd<'_> {
220        self.inner.as_fd()
221    }
222}
223
224#[cfg(unix)]
225impl AsRawFd for Owned {
226    fn as_raw_fd(&self) -> RawFd {
227        self.as_raw()
228    }
229}
230
231#[cfg(unix)]
232impl FromRawFd for Owned {
233    unsafe fn from_raw_fd(fd: RawFd) -> Self {
234        unsafe { Self::from_raw(fd) }
235    }
236}
237
238#[cfg(unix)]
239impl IntoRawFd for Owned {
240    fn into_raw_fd(self) -> RawFd {
241        self.into_raw()
242    }
243}
244
245impl<'fd> Borrowed<'fd> {
246    /// Create a `crt_fd::Borrowed` from a raw file descriptor.
247    ///
248    /// # Safety
249    ///
250    /// `fd` must be a valid file descriptor.
251    #[inline]
252    pub const unsafe fn borrow_raw(fd: Raw) -> Self {
253        let inner = unsafe { BorrowedInner::borrow_raw(fd) };
254        Self { inner }
255    }
256
257    /// Create a `crt_fd::Borrowed` from a raw file descriptor.
258    ///
259    /// Returns an error if `fd` is -1.
260    ///
261    /// # Safety
262    ///
263    /// `fd` must be a valid file descriptor.
264    #[inline]
265    pub unsafe fn try_borrow_raw(fd: Raw) -> io::Result<Self> {
266        if fd == -1 {
267            Err(ebadf())
268        } else {
269            Ok(unsafe { Self::borrow_raw(fd) })
270        }
271    }
272
273    #[inline]
274    pub fn as_raw(self) -> Raw {
275        self.inner.as_raw_fd()
276    }
277}
278
279#[cfg(unix)]
280impl<'fd> From<Borrowed<'fd>> for BorrowedFd<'fd> {
281    fn from(fd: Borrowed<'fd>) -> Self {
282        fd.inner
283    }
284}
285
286#[cfg(unix)]
287impl<'fd> From<BorrowedFd<'fd>> for Borrowed<'fd> {
288    fn from(fd: BorrowedFd<'fd>) -> Self {
289        Self { inner: fd }
290    }
291}
292
293#[cfg(unix)]
294impl AsFd for Borrowed<'_> {
295    fn as_fd(&self) -> BorrowedFd<'_> {
296        self.inner.as_fd()
297    }
298}
299
300#[cfg(unix)]
301impl AsRawFd for Borrowed<'_> {
302    fn as_raw_fd(&self) -> RawFd {
303        self.as_raw()
304    }
305}
306
307#[inline]
308fn ebadf() -> io::Error {
309    io::Error::from_raw_os_error(c::EBADF)
310}
311
312pub fn open(path: &ffi::CStr, flags: i32, mode: i32) -> io::Result<Owned> {
313    cvt_fd(unsafe { c::open(path.as_ptr(), flags, mode) })
314}
315
316#[cfg(windows)]
317pub fn wopen(path: &widestring::WideCStr, flags: i32, mode: i32) -> io::Result<Owned> {
318    cvt_fd(unsafe { suppress_iph!(c::wopen(path.as_ptr(), flags, mode)) })
319}
320
321#[cfg(all(any(unix, target_os = "wasi"), not(target_os = "redox")))]
322pub fn openat(dir: Borrowed<'_>, path: &ffi::CStr, flags: i32, mode: i32) -> io::Result<Owned> {
323    cvt_fd(unsafe { c::openat(dir.as_raw(), path.as_ptr(), flags, mode) })
324}
325
326pub fn fsync(fd: Borrowed<'_>) -> io::Result<()> {
327    cvt(unsafe { suppress_iph!(c::fsync(fd.as_raw())) })?;
328    Ok(())
329}
330
331fn _close(fd: Raw) -> io::Result<()> {
332    cvt(unsafe { suppress_iph!(c::close(fd)) })?;
333    Ok(())
334}
335
336pub fn close(fd: Owned) -> io::Result<()> {
337    _close(fd.into_raw())
338}
339
340pub fn ftruncate(fd: Borrowed<'_>, len: Offset) -> io::Result<()> {
341    let ret = unsafe { suppress_iph!(c::ftruncate(fd.as_raw(), len)) };
342    // On Windows, _chsize_s returns 0 on success, or a positive error code (errno value) on failure.
343    // On other platforms, ftruncate returns 0 on success, or -1 on failure with errno set.
344    #[cfg(windows)]
345    {
346        if ret != 0 {
347            // _chsize_s returns errno directly, convert to Windows error code
348            let winerror = crate::os::errno_to_winerror(ret);
349            return Err(io::Error::from_raw_os_error(winerror));
350        }
351    }
352    #[cfg(not(windows))]
353    {
354        cvt(ret)?;
355    }
356    Ok(())
357}
358
359#[cfg(windows)]
360pub fn as_handle(fd: Borrowed<'_>) -> io::Result<BorrowedHandle<'_>> {
361    use windows_sys::Win32::Foundation::{HANDLE, INVALID_HANDLE_VALUE};
362    unsafe extern "C" {
363        fn _get_osfhandle(fd: Borrowed<'_>) -> c::intptr_t;
364    }
365    let handle = unsafe { suppress_iph!(_get_osfhandle(fd)) };
366    if handle as HANDLE == INVALID_HANDLE_VALUE {
367        // _get_osfhandle is a CRT function that sets errno, not GetLastError()
368        Err(crate::os::errno_io_error())
369    } else {
370        Ok(unsafe { BorrowedHandle::borrow_raw(handle as _) })
371    }
372}
373
374fn _write(fd: Raw, buf: &[u8]) -> io::Result<usize> {
375    let count = cmp::min(buf.len(), MAX_RW);
376    let n = cvt(unsafe { suppress_iph!(c::write(fd, buf.as_ptr() as _, count as _)) })?;
377    Ok(n as usize)
378}
379
380fn _read(fd: Raw, buf: &mut [u8]) -> io::Result<usize> {
381    let count = cmp::min(buf.len(), MAX_RW);
382    let n = cvt(unsafe { suppress_iph!(libc::read(fd, buf.as_mut_ptr() as _, count as _)) })?;
383    Ok(n as usize)
384}
385
386pub fn write(fd: Borrowed<'_>, buf: &[u8]) -> io::Result<usize> {
387    _write(fd.as_raw(), buf)
388}
389
390pub fn read(fd: Borrowed<'_>, buf: &mut [u8]) -> io::Result<usize> {
391    _read(fd.as_raw(), buf)
392}
393
394macro_rules! impl_rw {
395    ($t:ty) => {
396        impl io::Write for $t {
397            fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
398                _write(self.as_raw(), buf)
399            }
400
401            #[inline]
402            fn flush(&mut self) -> io::Result<()> {
403                Ok(())
404            }
405        }
406
407        impl io::Read for $t {
408            fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
409                _read(self.as_raw(), buf)
410            }
411        }
412    };
413}
414
415impl_rw!(Owned);
416impl_rw!(Borrowed<'_>);