From 6ccc6fcf36675ddcf4af944ed41e9a9192bd13f5 Mon Sep 17 00:00:00 2001 From: Noa Date: Thu, 15 May 2025 15:51:26 -0500 Subject: [PATCH] Rework crt_fd to be more aligned with io-safety --- common/src/crt_fd.rs | 402 ++++++++++++++++++++++++++++++++++------- vm/src/ospath.rs | 42 +++-- vm/src/stdlib/io.rs | 43 +++-- vm/src/stdlib/os.rs | 205 ++++++++++++--------- vm/src/stdlib/posix.rs | 52 +++--- 5 files changed, 528 insertions(+), 216 deletions(-) diff --git a/common/src/crt_fd.rs b/common/src/crt_fd.rs index 64d4df98a5..0bb7485899 100644 --- a/common/src/crt_fd.rs +++ b/common/src/crt_fd.rs @@ -1,112 +1,392 @@ //! A module implementing an io type backed by the C runtime's file descriptors, i.e. what's //! returned from libc::open, even on windows. -use std::{cmp, ffi, io}; +use std::{cmp, ffi, fmt, io}; +#[cfg(not(windows))] +use std::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; #[cfg(windows)] -use libc::commit as fsync; -#[cfg(windows)] -unsafe extern "C" { - #[link_name = "_chsize_s"] - fn ftruncate(fd: i32, len: i64) -> i32; +use std::os::windows::io::BorrowedHandle; + +mod c { + pub(super) use libc::*; + + #[cfg(windows)] + pub(super) use libc::commit as fsync; + #[cfg(windows)] + unsafe extern "C" { + #[link_name = "_chsize_s"] + pub(super) fn ftruncate(fd: i32, len: i64) -> i32; + } } -#[cfg(not(windows))] -use libc::{fsync, ftruncate}; // this is basically what CPython has for Py_off_t; windows uses long long // for offsets, other platforms just use off_t #[cfg(not(windows))] -pub type Offset = libc::off_t; +pub type Offset = c::off_t; #[cfg(windows)] -pub type Offset = libc::c_longlong; +pub type Offset = c::c_longlong; + +#[cfg(not(windows))] +pub type Raw = RawFd; +#[cfg(windows)] +pub type Raw = i32; #[inline] -fn cvt(ret: I, f: impl FnOnce(I) -> T) -> io::Result { +fn cvt(ret: I) -> io::Result { if ret < I::zero() { Err(crate::os::last_os_error()) } else { - Ok(f(ret)) + Ok(ret) } } +fn cvt_fd(ret: Raw) -> io::Result { + cvt(ret).map(|fd| unsafe { Owned::from_raw(fd) }) +} + const MAX_RW: usize = if cfg!(any(windows, target_vendor = "apple")) { i32::MAX as usize } else { isize::MAX as usize }; -#[derive(Copy, Clone, PartialEq, Eq)] -#[repr(transparent)] -pub struct Fd(pub i32); +#[cfg(not(windows))] +type OwnedInner = OwnedFd; +#[cfg(not(windows))] +type BorrowedInner<'fd> = BorrowedFd<'fd>; -impl Fd { - pub fn open(path: &ffi::CStr, flags: i32, mode: i32) -> io::Result { - cvt(unsafe { libc::open(path.as_ptr(), flags, mode) }, Fd) +#[cfg(windows)] +mod win { + use super::*; + use sealed::SealedFd; + use std::marker::PhantomData; + use std::mem::ManuallyDrop; + + #[repr(transparent)] + pub(super) struct OwnedInner(i32); + + impl OwnedInner { + #[inline] + pub unsafe fn from_raw_fd(fd: Raw) -> Self { + Self(fd) + } + #[inline] + pub fn as_raw_fd(&self) -> Raw { + self.0 + } + #[inline] + pub fn into_raw_fd(self) -> Raw { + let me = ManuallyDrop::new(self); + me.0 + } } - #[cfg(windows)] - pub fn wopen(path: &widestring::WideCStr, flags: i32, mode: i32) -> io::Result { - cvt( - unsafe { suppress_iph!(libc::wopen(path.as_ptr(), flags, mode)) }, - Fd, - ) + impl Drop for OwnedInner { + #[inline] + fn drop(&mut self) { + let _ = _close(self.0); + } } - #[cfg(all(any(unix, target_os = "wasi"), not(target_os = "redox")))] - pub fn openat(&self, path: &ffi::CStr, flags: i32, mode: i32) -> io::Result { - cvt( - unsafe { libc::openat(self.0, path.as_ptr(), flags, mode) }, - Fd, - ) + #[derive(Copy, Clone)] + #[repr(transparent)] + pub(super) struct BorrowedInner<'fd> { + fd: Raw, + _marker: PhantomData<&'fd Owned>, + } + + impl BorrowedInner<'_> { + #[inline] + pub const unsafe fn borrow_raw(fd: Raw) -> Self { + Self { + fd, + _marker: PhantomData, + } + } + #[inline] + pub fn as_raw_fd(&self) -> Raw { + self.fd + } + } +} + +#[cfg(windows)] +use self::win::{BorrowedInner, MaybeBorrowedInner, MaybeOwnedInner, OwnedInner}; + +#[repr(transparent)] +pub struct Owned { + inner: OwnedInner, +} + +impl fmt::Debug for Owned { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("crt_fd::Owned") + .field(&self.as_raw()) + .finish() } +} + +#[derive(Copy, Clone)] +#[repr(transparent)] +pub struct Borrowed<'fd> { + inner: BorrowedInner<'fd>, +} - pub fn fsync(&self) -> io::Result<()> { - cvt(unsafe { suppress_iph!(fsync(self.0)) }, drop) +impl<'fd> PartialEq for Borrowed<'fd> { + fn eq(&self, other: &Self) -> bool { + self.as_raw() == other.as_raw() } +} +impl<'fd> Eq for Borrowed<'fd> {} - pub fn close(&self) -> io::Result<()> { - cvt(unsafe { suppress_iph!(libc::close(self.0)) }, drop) +impl fmt::Debug for Borrowed<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("crt_fd::Borrowed") + .field(&self.as_raw()) + .finish() } +} - pub fn ftruncate(&self, len: Offset) -> io::Result<()> { - cvt(unsafe { suppress_iph!(ftruncate(self.0, len)) }, drop) +impl Owned { + /// Create a `crt_fd::Owned` from a raw file descriptor. + /// + /// # Safety + /// + /// `fd` must be a valid file descriptor. + #[inline] + pub unsafe fn from_raw(fd: Raw) -> Self { + let inner = unsafe { OwnedInner::from_raw_fd(fd) }; + Self { inner } } - #[cfg(windows)] - pub fn to_raw_handle(&self) -> io::Result { - unsafe extern "C" { - fn _get_osfhandle(fd: i32) -> libc::intptr_t; - } - let handle = unsafe { suppress_iph!(_get_osfhandle(self.0)) }; - if handle == -1 { - Err(io::Error::last_os_error()) + /// Create a `crt_fd::Owned` from a raw file descriptor. + /// + /// Returns an error if `fd` is -1. + /// + /// # Safety + /// + /// `fd` must be a valid file descriptor. + #[inline] + pub unsafe fn try_from_raw(fd: Raw) -> io::Result { + if fd == -1 { + Err(ebadf()) } else { - Ok(handle as _) + Ok(unsafe { Self::from_raw(fd) }) } } + + #[inline] + pub fn borrow(&self) -> Borrowed<'_> { + unsafe { Borrowed::borrow_raw(self.as_raw()) } + } + + #[inline] + pub fn as_raw(&self) -> Raw { + self.inner.as_raw_fd() + } + + #[inline] + pub fn into_raw(self) -> Raw { + self.inner.into_raw_fd() + } +} + +#[cfg(unix)] +impl From for OwnedFd { + fn from(fd: Owned) -> Self { + fd.inner + } +} + +#[cfg(unix)] +impl From for Owned { + fn from(fd: OwnedFd) -> Self { + Self { inner: fd } + } } -impl io::Write for Fd { - fn write(&mut self, buf: &[u8]) -> io::Result { - let count = cmp::min(buf.len(), MAX_RW); - cvt( - unsafe { suppress_iph!(libc::write(self.0, buf.as_ptr() as _, count as _)) }, - |i| i as usize, - ) +#[cfg(unix)] +impl AsFd for Owned { + fn as_fd(&self) -> BorrowedFd<'_> { + self.inner.as_fd() + } +} + +#[cfg(unix)] +impl AsRawFd for Owned { + fn as_raw_fd(&self) -> RawFd { + self.as_raw() + } +} + +#[cfg(unix)] +impl FromRawFd for Owned { + unsafe fn from_raw_fd(fd: RawFd) -> Self { + unsafe { Self::from_raw(fd) } + } +} + +#[cfg(unix)] +impl IntoRawFd for Owned { + fn into_raw_fd(self) -> RawFd { + self.into_raw() + } +} + +impl<'fd> Borrowed<'fd> { + /// Create a `crt_fd::Borrowed` from a raw file descriptor. + /// + /// # Safety + /// + /// `fd` must be a valid file descriptor. + #[inline] + pub const unsafe fn borrow_raw(fd: Raw) -> Self { + let inner = unsafe { BorrowedInner::borrow_raw(fd) }; + Self { inner } + } + + /// Create a `crt_fd::Borrowed` from a raw file descriptor. + /// + /// Returns an error if `fd` is -1. + /// + /// # Safety + /// + /// `fd` must be a valid file descriptor. + #[inline] + pub unsafe fn try_borrow_raw(fd: Raw) -> io::Result { + if fd == -1 { + Err(ebadf()) + } else { + Ok(unsafe { Self::borrow_raw(fd) }) + } } #[inline] - fn flush(&mut self) -> io::Result<()> { - Ok(()) + pub fn as_raw(self) -> Raw { + self.inner.as_raw_fd() + } +} + +#[cfg(unix)] +impl<'fd> From> for BorrowedFd<'fd> { + fn from(fd: Borrowed<'fd>) -> Self { + fd.inner + } +} + +#[cfg(unix)] +impl<'fd> From> for Borrowed<'fd> { + fn from(fd: BorrowedFd<'fd>) -> Self { + Self { inner: fd } + } +} + +#[cfg(unix)] +impl AsFd for Borrowed<'_> { + fn as_fd(&self) -> BorrowedFd<'_> { + self.inner.as_fd() } } -impl io::Read for Fd { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - let count = cmp::min(buf.len(), MAX_RW); - cvt( - unsafe { suppress_iph!(libc::read(self.0, buf.as_mut_ptr() as _, count as _)) }, - |i| i as usize, - ) +#[cfg(unix)] +impl AsRawFd for Borrowed<'_> { + fn as_raw_fd(&self) -> RawFd { + self.as_raw() } } + +#[inline] +fn ebadf() -> io::Error { + io::Error::from_raw_os_error(c::EBADF) +} + +pub fn open(path: &ffi::CStr, flags: i32, mode: i32) -> io::Result { + cvt_fd(unsafe { c::open(path.as_ptr(), flags, mode) }) +} + +#[cfg(windows)] +pub fn wopen(path: &widestring::WideCStr, flags: i32, mode: i32) -> io::Result { + cvt_fd(unsafe { suppress_iph!(c::wopen(path.as_ptr(), flags, mode)) }) +} + +#[cfg(all(any(unix, target_os = "wasi"), not(target_os = "redox")))] +pub fn openat(dir: Borrowed<'_>, path: &ffi::CStr, flags: i32, mode: i32) -> io::Result { + cvt_fd(unsafe { c::openat(dir.as_raw(), path.as_ptr(), flags, mode) }) +} + +pub fn fsync(fd: Borrowed<'_>) -> io::Result<()> { + cvt(unsafe { suppress_iph!(c::fsync(fd.as_raw())) })?; + Ok(()) +} + +fn _close(fd: Raw) -> io::Result<()> { + cvt(unsafe { suppress_iph!(c::close(fd)) })?; + Ok(()) +} + +pub fn close(fd: Owned) -> io::Result<()> { + _close(fd.into_raw()) +} + +pub fn ftruncate(fd: Borrowed<'_>, len: Offset) -> io::Result<()> { + cvt(unsafe { suppress_iph!(c::ftruncate(fd.as_raw(), len)) })?; + Ok(()) +} + +#[cfg(windows)] +pub fn as_handle(fd: Borrowed<'_>) -> io::Result> { + unsafe extern "C" { + fn _get_osfhandle(fd: Borrowed<'_>) -> c::intptr_t; + } + let handle = unsafe { suppress_iph!(_get_osfhandle(fd)) }; + if handle == -1 { + Err(io::Error::last_os_error()) + } else { + Ok(unsafe { BorrowedHandle::borrow_raw(handle as _) }) + } +} + +fn _write(fd: Raw, buf: &[u8]) -> io::Result { + let count = cmp::min(buf.len(), MAX_RW); + let n = cvt(unsafe { suppress_iph!(c::write(fd, buf.as_ptr() as _, count as _)) })?; + Ok(n as usize) +} + +fn _read(fd: Raw, buf: &mut [u8]) -> io::Result { + let count = cmp::min(buf.len(), MAX_RW); + let n = cvt(unsafe { suppress_iph!(libc::read(fd, buf.as_mut_ptr() as _, count as _)) })?; + Ok(n as usize) +} + +pub fn write(fd: Borrowed<'_>, buf: &[u8]) -> io::Result { + _write(fd.as_raw(), buf) +} + +pub fn read(fd: Borrowed<'_>, buf: &mut [u8]) -> io::Result { + _read(fd.as_raw(), buf) +} + +macro_rules! impl_rw { + ($t:ty) => { + impl io::Write for $t { + fn write(&mut self, buf: &[u8]) -> io::Result { + _write(self.as_raw(), buf) + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } + } + + impl io::Read for $t { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + _read(self.as_raw(), buf) + } + } + }; +} + +impl_rw!(Owned); +impl_rw!(Borrowed<'_>); diff --git a/vm/src/ospath.rs b/vm/src/ospath.rs index 26d1582825..fbd06237e0 100644 --- a/vm/src/ospath.rs +++ b/vm/src/ospath.rs @@ -1,7 +1,9 @@ +use rustpython_common::crt_fd; + use crate::{ PyObjectRef, PyResult, VirtualMachine, builtins::PyBaseExceptionRef, - convert::{ToPyException, TryFromObject}, + convert::{IntoPyException, ToPyException, ToPyObject, TryFromObject}, function::FsPath, object::AsObject, }; @@ -95,32 +97,36 @@ impl TryFromObject for OsPath { // path_t with allow_fd in CPython #[derive(Clone)] -pub(crate) enum OsPathOrFd { +pub(crate) enum OsPathOrFd<'fd> { Path(OsPath), - Fd(i32), + Fd(crt_fd::Borrowed<'fd>), } -impl TryFromObject for OsPathOrFd { +impl TryFromObject for OsPathOrFd<'_> { fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult { - let r = match obj.try_index_opt(vm) { - Some(int) => Self::Fd(int?.try_to_primitive(vm)?), - None => Self::Path(obj.try_into_value(vm)?), - }; - Ok(r) + match obj.try_index_opt(vm) { + Some(int) => { + let fd = int?.try_to_primitive(vm)?; + unsafe { crt_fd::Borrowed::try_borrow_raw(fd) } + .map(Self::Fd) + .map_err(|e| e.into_pyexception(vm)) + } + None => obj.try_into_value(vm).map(Self::Path), + } } } -impl From for OsPathOrFd { +impl From for OsPathOrFd<'_> { fn from(path: OsPath) -> Self { Self::Path(path) } } -impl OsPathOrFd { +impl OsPathOrFd<'_> { pub fn filename(&self, vm: &VirtualMachine) -> PyObjectRef { match self { OsPathOrFd::Path(path) => path.filename(vm), - OsPathOrFd::Fd(fd) => vm.ctx.new_int(*fd).into(), + OsPathOrFd::Fd(fd) => fd.to_pyobject(vm), } } } @@ -128,8 +134,8 @@ impl OsPathOrFd { // TODO: preserve the input `PyObjectRef` of filename and filename2 (Failing check `self.assertIs(err.filename, name, str(func)`) pub struct IOErrorBuilder<'a> { error: &'a std::io::Error, - filename: Option, - filename2: Option, + filename: Option>, + filename2: Option>, } impl<'a> IOErrorBuilder<'a> { @@ -140,12 +146,12 @@ impl<'a> IOErrorBuilder<'a> { filename2: None, } } - pub(crate) fn filename(mut self, filename: impl Into) -> Self { + pub(crate) fn filename(mut self, filename: impl Into>) -> Self { let filename = filename.into(); self.filename.replace(filename); self } - pub(crate) fn filename2(mut self, filename: impl Into) -> Self { + pub(crate) fn filename2(mut self, filename: impl Into>) -> Self { let filename = filename.into(); self.filename2.replace(filename); self @@ -153,10 +159,10 @@ impl<'a> IOErrorBuilder<'a> { pub(crate) fn with_filename( error: &'a std::io::Error, - filename: impl Into, + filename: impl Into>, vm: &VirtualMachine, ) -> PyBaseExceptionRef { - let zelf = Self { + let zelf = IOErrorBuilder { error, filename: Some(filename.into()), filename2: None, diff --git a/vm/src/stdlib/io.rs b/vm/src/stdlib/io.rs index 3e1979e3d0..0f10552bbc 100644 --- a/vm/src/stdlib/io.rs +++ b/vm/src/stdlib/io.rs @@ -3878,7 +3878,7 @@ mod _io { // check file descriptor validity #[cfg(unix)] if let Ok(crate::ospath::OsPathOrFd::Fd(fd)) = file.clone().try_into_value(vm) { - nix::fcntl::fcntl(fd, nix::fcntl::F_GETFD) + nix::fcntl::fcntl(fd.as_raw(), nix::fcntl::F_GETFD) .map_err(|_| crate::stdlib::os::errno_err(vm))?; } @@ -4039,8 +4039,8 @@ mod fileio { use crate::{ AsObject, Py, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject, VirtualMachine, builtins::{PyBaseExceptionRef, PyStr, PyStrRef}, - common::crt_fd::Fd, - convert::ToPyException, + common::crt_fd, + convert::{IntoPyException, ToPyException}, function::{ArgBytesLike, ArgMemoryBuffer, OptionalArg, OptionalOption}, ospath::{IOErrorBuilder, OsPath, OsPathOrFd}, stdlib::os, @@ -4210,7 +4210,7 @@ mod fileio { let (fd, filename) = if let Some(fd) = arg_fd { zelf.closefd.store(args.closefd); - (fd, OsPathOrFd::Fd(fd)) + (fd, None) } else { zelf.closefd.store(true); if !args.closefd { @@ -4228,28 +4228,27 @@ mod fileio { if fd < 0 { return Err(vm.new_value_error(format!("opener returned {fd}"))); } - ( - fd, - OsPathOrFd::try_from_object(vm, name.clone()).unwrap_or(OsPathOrFd::Fd(fd)), - ) + (fd, None) } else { let path = OsPath::try_from_object(vm, name.clone())?; #[cfg(any(unix, target_os = "wasi"))] - let fd = Fd::open(&path.clone().into_cstring(vm)?, flags, 0o666); + let fd = crt_fd::open(&path.clone().into_cstring(vm)?, flags, 0o666); #[cfg(windows)] - let fd = Fd::wopen(&path.to_wide_cstring(vm)?, flags, 0o666); + let fd = crt_fd::wopen(&path.to_wide_cstring(vm)?, flags, 0o666); let filename = OsPathOrFd::Path(path); match fd { - Ok(fd) => (fd.0, filename), + Ok(fd) => (fd.into_raw(), Some(filename)), Err(e) => return Err(IOErrorBuilder::with_filename(&e, filename, vm)), } } }; zelf.fd.store(fd); + let fd = unsafe { crt_fd::Borrowed::borrow_raw(fd) }; + let filename = filename.unwrap_or(OsPathOrFd::Fd(fd)); // TODO: _Py_set_inheritable - let fd_fstat = crate::common::fileutils::fstat(fd); + let fd_fstat = crate::common::fileutils::fstat(fd.as_raw()); #[cfg(windows)] { @@ -4279,7 +4278,7 @@ mod fileio { zelf.as_object().set_attr("name", name, vm)?; if mode.contains(Mode::APPENDING) { - let _ = os::lseek(fd as _, 0, libc::SEEK_END, vm); + let _ = os::lseek(fd, 0, libc::SEEK_END, vm); } Ok(()) @@ -4344,8 +4343,9 @@ mod fileio { } } - fn get_fd(&self, vm: &VirtualMachine) -> PyResult { - self.fileno(vm).map(Fd) + fn get_fd(&self, vm: &VirtualMachine) -> PyResult> { + self.fileno(vm) + .map(|fd| unsafe { crt_fd::Borrowed::borrow_raw(fd) }) } #[pymethod] @@ -4461,8 +4461,7 @@ mod fileio { } let fd = zelf.fd.swap(-1); if fd >= 0 { - Fd(fd) - .close() + crt_fd::close(unsafe { crt_fd::Owned::from_raw(fd) }) .map_err(|err| Self::io_error(zelf, err, vm))?; } res @@ -4470,7 +4469,7 @@ mod fileio { #[pymethod] fn seekable(&self, vm: &VirtualMachine) -> PyResult { - let fd = self.fileno(vm)?; + let fd = self.get_fd(vm)?; Ok(self.seekable.load().unwrap_or_else(|| { let seekable = os::lseek(fd, 0, libc::SEEK_CUR, vm).is_ok(); self.seekable.store(Some(seekable)); @@ -4486,7 +4485,7 @@ mod fileio { vm: &VirtualMachine, ) -> PyResult { let how = how.unwrap_or(0); - let fd = self.fileno(vm)?; + let fd = self.get_fd(vm)?; let offset = get_offset(offset, vm)?; os::lseek(fd, offset, how, vm) @@ -4494,18 +4493,18 @@ mod fileio { #[pymethod] fn tell(&self, vm: &VirtualMachine) -> PyResult { - let fd = self.fileno(vm)?; + let fd = self.get_fd(vm)?; os::lseek(fd, 0, libc::SEEK_CUR, vm) } #[pymethod] fn truncate(&self, len: OptionalOption, vm: &VirtualMachine) -> PyResult { - let fd = self.fileno(vm)?; + let fd = self.get_fd(vm)?; let len = match len.flatten() { Some(l) => get_offset(l, vm)?, None => os::lseek(fd, 0, libc::SEEK_CUR, vm)?, }; - os::ftruncate(fd, len, vm)?; + os::ftruncate(fd, len).map_err(|e| e.into_pyexception(vm))?; Ok(len) } diff --git a/vm/src/stdlib/os.rs b/vm/src/stdlib/os.rs index 08a5051fe7..a5e4e781a8 100644 --- a/vm/src/stdlib/os.rs +++ b/vm/src/stdlib/os.rs @@ -1,10 +1,10 @@ // cspell:disable use crate::{ - AsObject, Py, PyPayload, PyResult, VirtualMachine, + AsObject, Py, PyObjectRef, PyPayload, PyResult, TryFromObject, VirtualMachine, builtins::{PyBaseExceptionRef, PyModule, PySet}, - common::crt_fd::Fd, - convert::ToPyException, + common::crt_fd, + convert::{IntoPyException, ToPyException, ToPyObject}, function::{ArgumentError, FromArgs, FuncArgs}, }; use std::{ffi, fs, io, path::Path}; @@ -54,13 +54,13 @@ cfg_if::cfg_if! { const AT_FDCWD: i32 = -100; } } -const DEFAULT_DIR_FD: Fd = Fd(AT_FDCWD); +const DEFAULT_DIR_FD: crt_fd::Borrowed<'static> = unsafe { crt_fd::Borrowed::borrow_raw(AT_FDCWD) }; // XXX: AVAILABLE should be a bool, but we can't yet have it as a bool and just cast it to usize #[derive(Copy, Clone, PartialEq, Eq)] -pub struct DirFd(pub(crate) [Fd; AVAILABLE]); +pub struct DirFd<'fd, const AVAILABLE: usize>(pub(crate) [crt_fd::Borrowed<'fd>; AVAILABLE]); -impl Default for DirFd { +impl Default for DirFd<'_, AVAILABLE> { fn default() -> Self { Self([DEFAULT_DIR_FD; AVAILABLE]) } @@ -68,33 +68,30 @@ impl Default for DirFd { // not used on all platforms #[allow(unused)] -impl DirFd<1> { +impl<'fd> DirFd<'fd, 1> { #[inline(always)] - pub(crate) fn fd_opt(&self) -> Option { - self.get_opt().map(Fd) + pub(crate) fn get_opt(self) -> Option> { + let [fd] = self.0; + (fd != DEFAULT_DIR_FD).then_some(fd) } #[inline] - pub(crate) fn get_opt(&self) -> Option { - let fd = self.fd(); - if fd == DEFAULT_DIR_FD { - None - } else { - Some(fd.0) - } + pub(crate) fn raw_opt(self) -> Option { + self.get_opt().map(|fd| fd.as_raw()) } #[inline(always)] - pub(crate) fn fd(&self) -> Fd { - self.0[0] + pub(crate) fn get(self) -> crt_fd::Borrowed<'fd> { + let [fd] = self.0; + fd } } -impl FromArgs for DirFd { +impl FromArgs for DirFd<'_, AVAILABLE> { fn from_args(vm: &VirtualMachine, args: &mut FuncArgs) -> Result { let fd = match args.take_keyword("dir_fd") { - Some(o) if vm.is_none(&o) => DEFAULT_DIR_FD, - None => DEFAULT_DIR_FD, + Some(o) if vm.is_none(&o) => Ok(DEFAULT_DIR_FD), + None => Ok(DEFAULT_DIR_FD), Some(o) => { let fd = o.try_index_opt(vm).unwrap_or_else(|| { Err(vm.new_type_error(format!( @@ -103,14 +100,15 @@ impl FromArgs for DirFd { ))) })?; let fd = fd.try_to_primitive(vm)?; - Fd(fd) + unsafe { crt_fd::Borrowed::try_borrow_raw(fd) } } }; - if AVAILABLE == 0 && fd != DEFAULT_DIR_FD { + if AVAILABLE == 0 && fd.as_ref().is_ok_and(|&fd| fd != DEFAULT_DIR_FD) { return Err(vm .new_not_implemented_error("dir_fd unavailable on this platform".to_owned()) .into()); } + let fd = fd.map_err(|e| e.to_pyexception(vm))?; Ok(Self([fd; AVAILABLE])) } } @@ -125,6 +123,32 @@ fn bytes_as_os_str<'a>(b: &'a [u8], vm: &VirtualMachine) -> PyResult<&'a ffi::Os .map_err(|_| vm.new_unicode_decode_error("can't decode path for utf-8".to_owned())) } +impl TryFromObject for crt_fd::Owned { + fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult { + let fd = crt_fd::Raw::try_from_object(vm, obj)?; + unsafe { crt_fd::Owned::try_from_raw(fd) }.map_err(|e| e.into_pyexception(vm)) + } +} + +impl TryFromObject for crt_fd::Borrowed<'_> { + fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult { + let fd = crt_fd::Raw::try_from_object(vm, obj)?; + unsafe { crt_fd::Borrowed::try_borrow_raw(fd) }.map_err(|e| e.into_pyexception(vm)) + } +} + +impl ToPyObject for crt_fd::Owned { + fn to_pyobject(self, vm: &VirtualMachine) -> PyObjectRef { + self.into_raw().to_pyobject(vm) + } +} + +impl ToPyObject for crt_fd::Borrowed<'_> { + fn to_pyobject(self, vm: &VirtualMachine) -> PyObjectRef { + self.as_raw().to_pyobject(vm) + } +} + #[pymodule(sub)] pub(super) mod _os { use super::{DirFd, FollowSymlinks, SupportFunc, errno_err}; @@ -134,7 +158,7 @@ pub(super) mod _os { PyBytesRef, PyGenericAlias, PyIntRef, PyStrRef, PyTuple, PyTupleRef, PyTypeRef, }, common::{ - crt_fd::{Fd, Offset}, + crt_fd, fileutils::StatStruct, lock::{OnceCell, PyRwLock}, suppress_iph, @@ -153,7 +177,7 @@ pub(super) mod _os { use std::{ env, ffi, fs, fs::OpenOptions, - io::{self, Read, Write}, + io, path::PathBuf, time::{Duration, SystemTime}, }; @@ -180,30 +204,32 @@ pub(super) mod _os { pub(crate) const X_OK: u8 = 1 << 0; #[pyfunction] - fn close(fileno: i32, vm: &VirtualMachine) -> PyResult<()> { - Fd(fileno).close().map_err(|e| e.into_pyexception(vm)) + fn close(fileno: crt_fd::Owned) -> io::Result<()> { + crt_fd::close(fileno) } #[pyfunction] fn closerange(fd_low: i32, fd_high: i32) { for fileno in fd_low..fd_high { - let _ = Fd(fileno).close(); + if let Ok(fd) = unsafe { crt_fd::Owned::try_from_raw(fileno) } { + drop(fd); + } } } #[cfg(any(unix, windows, target_os = "wasi"))] #[derive(FromArgs)] - struct OpenArgs { + struct OpenArgs<'fd> { path: OsPath, flags: i32, #[pyarg(any, default)] mode: Option, #[pyarg(flatten)] - dir_fd: DirFd<{ OPEN_DIR_FD as usize }>, + dir_fd: DirFd<'fd, { OPEN_DIR_FD as usize }>, } #[pyfunction] - fn open(args: OpenArgs, vm: &VirtualMachine) -> PyResult { + fn open(args: OpenArgs<'_>, vm: &VirtualMachine) -> PyResult { os_open(args.path, args.flags, args.mode, args.dir_fd, vm) } @@ -212,9 +238,9 @@ pub(super) mod _os { name: OsPath, flags: i32, mode: Option, - dir_fd: DirFd<{ OPEN_DIR_FD as usize }>, + dir_fd: DirFd<'_, { OPEN_DIR_FD as usize }>, vm: &VirtualMachine, - ) -> PyResult { + ) -> PyResult { let mode = mode.unwrap_or(0o777); #[cfg(windows)] let fd = { @@ -229,51 +255,42 @@ pub(super) mod _os { #[cfg(not(target_os = "wasi"))] let flags = flags | libc::O_CLOEXEC; #[cfg(not(target_os = "redox"))] - if let Some(dir_fd) = dir_fd.fd_opt() { - dir_fd.openat(&name, flags, mode) + if let Some(dir_fd) = dir_fd.get_opt() { + crt_fd::openat(dir_fd, &name, flags, mode) } else { - Fd::open(&name, flags, mode) + crt_fd::open(&name, flags, mode) } #[cfg(target_os = "redox")] { let [] = dir_fd.0; - Fd::open(&name, flags, mode) + crt_fd::open(&name, flags, mode) } }; - fd.map(|fd| fd.0) - .map_err(|err| IOErrorBuilder::with_filename(&err, name, vm)) + fd.map_err(|err| IOErrorBuilder::with_filename(&err, name, vm)) } #[pyfunction] - fn fsync(fd: i32, vm: &VirtualMachine) -> PyResult<()> { - Fd(fd).fsync().map_err(|err| err.into_pyexception(vm)) + fn fsync(fd: crt_fd::Borrowed<'_>) -> io::Result<()> { + crt_fd::fsync(fd) } #[pyfunction] - fn read(fd: i32, n: usize, vm: &VirtualMachine) -> PyResult { + fn read(fd: crt_fd::Borrowed<'_>, n: usize, vm: &VirtualMachine) -> io::Result { let mut buffer = vec![0u8; n]; - let mut file = Fd(fd); - let n = file - .read(&mut buffer) - .map_err(|err| err.into_pyexception(vm))?; + let n = crt_fd::read(fd, &mut buffer)?; buffer.truncate(n); Ok(vm.ctx.new_bytes(buffer)) } #[pyfunction] - fn write(fd: i32, data: ArgBytesLike, vm: &VirtualMachine) -> PyResult { - let mut file = Fd(fd); - let written = data - .with_ref(|b| file.write(b)) - .map_err(|err| err.into_pyexception(vm))?; - - Ok(vm.ctx.new_int(written).into()) + fn write(fd: crt_fd::Borrowed<'_>, data: ArgBytesLike) -> io::Result { + data.with_ref(|b| crt_fd::write(fd, b)) } #[pyfunction] #[pyfunction(name = "unlink")] - fn remove(path: OsPath, dir_fd: DirFd<0>, vm: &VirtualMachine) -> PyResult<()> { + fn remove(path: OsPath, dir_fd: DirFd<'_, 0>, vm: &VirtualMachine) -> PyResult<()> { let [] = dir_fd.0; let is_junction = cfg!(windows) && fs::metadata(&path).is_ok_and(|meta| meta.file_type().is_dir()) @@ -291,13 +308,13 @@ pub(super) mod _os { fn mkdir( path: OsPath, mode: OptionalArg, - dir_fd: DirFd<{ MKDIR_DIR_FD as usize }>, + dir_fd: DirFd<'_, { MKDIR_DIR_FD as usize }>, vm: &VirtualMachine, ) -> PyResult<()> { let mode = mode.unwrap_or(0o777); let path = path.into_cstring(vm)?; #[cfg(not(target_os = "redox"))] - if let Some(fd) = dir_fd.get_opt() { + if let Some(fd) = dir_fd.raw_opt() { let res = unsafe { libc::mkdirat(fd, path.as_ptr(), mode as _) }; let res = if res < 0 { Err(errno_err(vm)) } else { Ok(()) }; return res; @@ -314,7 +331,7 @@ pub(super) mod _os { } #[pyfunction] - fn rmdir(path: OsPath, dir_fd: DirFd<0>, vm: &VirtualMachine) -> PyResult<()> { + fn rmdir(path: OsPath, dir_fd: DirFd<'_, 0>, vm: &VirtualMachine) -> PyResult<()> { let [] = dir_fd.0; fs::remove_dir(&path).map_err(|err| IOErrorBuilder::with_filename(&err, path, vm)) } @@ -322,7 +339,10 @@ pub(super) mod _os { const LISTDIR_FD: bool = cfg!(all(unix, not(target_os = "redox"))); #[pyfunction] - fn listdir(path: OptionalArg, vm: &VirtualMachine) -> PyResult> { + fn listdir( + path: OptionalArg>, + vm: &VirtualMachine, + ) -> PyResult> { let path = path.unwrap_or_else(|| OsPathOrFd::Path(OsPath::new_str("."))); let list = match path { OsPathOrFd::Path(path) => { @@ -350,7 +370,8 @@ pub(super) mod _os { #[cfg(all(unix, not(target_os = "redox")))] { use rustpython_common::os::ffi::OsStrExt; - let new_fd = nix::unistd::dup(fno).map_err(|e| e.into_pyexception(vm))?; + let new_fd = + nix::unistd::dup(fno.as_raw()).map_err(|e| e.into_pyexception(vm))?; let mut dir = nix::dir::Dir::from_fd(new_fd).map_err(|e| e.into_pyexception(vm))?; dir.iter() @@ -422,7 +443,7 @@ pub(super) mod _os { } #[pyfunction] - fn readlink(path: OsPath, dir_fd: DirFd<0>, vm: &VirtualMachine) -> PyResult { + fn readlink(path: OsPath, dir_fd: DirFd<'_, 0>, vm: &VirtualMachine) -> PyResult { let mode = path.mode; let [] = dir_fd.0; let path = @@ -507,7 +528,7 @@ pub(super) mod _os { #[pymethod] fn stat( &self, - dir_fd: DirFd<{ STAT_DIR_FD as usize }>, + dir_fd: DirFd<'_, { STAT_DIR_FD as usize }>, follow_symlinks: FollowSymlinks, vm: &VirtualMachine, ) -> PyResult { @@ -835,8 +856,8 @@ pub(super) mod _os { #[cfg(not(windows))] fn stat_inner( - file: OsPathOrFd, - dir_fd: DirFd<{ STAT_DIR_FD as usize }>, + file: OsPathOrFd<'_>, + dir_fd: DirFd<'_, { STAT_DIR_FD as usize }>, follow_symlinks: FollowSymlinks, ) -> io::Result> { let mut stat = std::mem::MaybeUninit::uninit(); @@ -850,7 +871,7 @@ pub(super) mod _os { }; #[cfg(not(target_os = "redox"))] - let fstatat_ret = dir_fd.get_opt().map(|dir_fd| { + let fstatat_ret = dir_fd.raw_opt().map(|dir_fd| { let flags = if follow_symlinks.0 { 0 } else { @@ -869,7 +890,7 @@ pub(super) mod _os { } }) } - OsPathOrFd::Fd(fd) => unsafe { libc::fstat(fd, stat.as_mut_ptr()) }, + OsPathOrFd::Fd(fd) => unsafe { libc::fstat(fd.as_raw(), stat.as_mut_ptr()) }, }; if ret < 0 { return Err(io::Error::last_os_error()); @@ -880,8 +901,8 @@ pub(super) mod _os { #[pyfunction] #[pyfunction(name = "fstat")] fn stat( - file: OsPathOrFd, - dir_fd: DirFd<{ STAT_DIR_FD as usize }>, + file: OsPathOrFd<'_>, + dir_fd: DirFd<'_, { STAT_DIR_FD as usize }>, follow_symlinks: FollowSymlinks, vm: &VirtualMachine, ) -> PyResult { @@ -893,8 +914,8 @@ pub(super) mod _os { #[pyfunction] fn lstat( - file: OsPathOrFd, - dir_fd: DirFd<{ STAT_DIR_FD as usize }>, + file: OsPathOrFd<'_>, + dir_fd: DirFd<'_, { STAT_DIR_FD as usize }>, vm: &VirtualMachine, ) -> PyResult { stat(file, dir_fd, FollowSymlinks(false), vm) @@ -984,16 +1005,22 @@ pub(super) mod _os { } #[pyfunction] - pub fn lseek(fd: i32, position: Offset, how: i32, vm: &VirtualMachine) -> PyResult { + pub fn lseek( + fd: crt_fd::Borrowed<'_>, + position: crt_fd::Offset, + how: i32, + vm: &VirtualMachine, + ) -> PyResult { #[cfg(not(windows))] - let res = unsafe { suppress_iph!(libc::lseek(fd, position, how)) }; + let res = unsafe { suppress_iph!(libc::lseek(fd.as_raw(), position, how)) }; #[cfg(windows)] let res = unsafe { + use std::os::windows::io::AsRawHandle; use windows_sys::Win32::Storage::FileSystem; - let handle = Fd(fd).to_raw_handle().map_err(|e| e.into_pyexception(vm))?; + let handle = crt_fd::as_handle(fd).map_err(|e| e.into_pyexception(vm))?; let mut distance_to_move: [i32; 2] = std::mem::transmute(position); let ret = FileSystem::SetFilePointer( - handle as _, + handle.as_raw_handle(), distance_to_move[0], &mut distance_to_move[1], how as _, @@ -1027,20 +1054,20 @@ pub(super) mod _os { } #[derive(FromArgs)] - struct UtimeArgs { + struct UtimeArgs<'fd> { path: OsPath, #[pyarg(any, default)] times: Option, #[pyarg(named, default)] ns: Option, #[pyarg(flatten)] - dir_fd: DirFd<{ UTIME_DIR_FD as usize }>, + dir_fd: DirFd<'fd, { UTIME_DIR_FD as usize }>, #[pyarg(flatten)] follow_symlinks: FollowSymlinks, } #[pyfunction] - fn utime(args: UtimeArgs, vm: &VirtualMachine) -> PyResult<()> { + fn utime(args: UtimeArgs<'_>, vm: &VirtualMachine) -> PyResult<()> { let parse_tup = |tup: &PyTuple| -> Option<(PyObjectRef, PyObjectRef)> { if tup.len() != 2 { None @@ -1100,7 +1127,7 @@ pub(super) mod _os { path: OsPath, acc: Duration, modif: Duration, - dir_fd: DirFd<{ UTIME_DIR_FD as usize }>, + dir_fd: DirFd<'_, { UTIME_DIR_FD as usize }>, _follow_symlinks: FollowSymlinks, vm: &VirtualMachine, ) -> PyResult<()> { @@ -1118,7 +1145,7 @@ pub(super) mod _os { let ret = unsafe { libc::utimensat( - dir_fd.fd().0, + dir_fd.get().as_raw(), path.as_ptr(), times.as_ptr(), if _follow_symlinks.0 { @@ -1265,22 +1292,22 @@ pub(super) mod _os { #[cfg(target_os = "linux")] #[derive(FromArgs)] - struct CopyFileRangeArgs { + struct CopyFileRangeArgs<'fd> { #[pyarg(positional)] - src: i32, + src: crt_fd::Borrowed<'fd>, #[pyarg(positional)] - dst: i32, + dst: crt_fd::Borrowed<'fd>, #[pyarg(positional)] count: i64, #[pyarg(any, default)] - offset_src: Option, + offset_src: Option, #[pyarg(any, default)] - offset_dst: Option, + offset_dst: Option, } #[cfg(target_os = "linux")] #[pyfunction] - fn copy_file_range(args: CopyFileRangeArgs, vm: &VirtualMachine) -> PyResult { + fn copy_file_range(args: CopyFileRangeArgs<'_>, vm: &VirtualMachine) -> PyResult { let p_offset_src = args.offset_src.as_ref().map_or_else(std::ptr::null, |x| x); let p_offset_dst = args.offset_dst.as_ref().map_or_else(std::ptr::null, |x| x); let count: usize = args @@ -1320,14 +1347,14 @@ pub(super) mod _os { } #[pyfunction] - pub fn ftruncate(fd: i32, length: Offset, vm: &VirtualMachine) -> PyResult<()> { - Fd(fd).ftruncate(length).map_err(|e| e.into_pyexception(vm)) + pub fn ftruncate(fd: crt_fd::Borrowed<'_>, length: crt_fd::Offset) -> io::Result<()> { + crt_fd::ftruncate(fd, length) } #[pyfunction] - fn truncate(path: PyObjectRef, length: Offset, vm: &VirtualMachine) -> PyResult<()> { - if let Ok(fd) = path.try_to_value(vm) { - return ftruncate(fd, length, vm); + fn truncate(path: PyObjectRef, length: crt_fd::Offset, vm: &VirtualMachine) -> PyResult<()> { + if let Ok(fd) = path.clone().try_into_value(vm) { + return ftruncate(fd, length).map_err(|e| e.into_pyexception(vm)); } #[cold] diff --git a/vm/src/stdlib/posix.rs b/vm/src/stdlib/posix.rs index d75629745c..0fd6406dce 100644 --- a/vm/src/stdlib/posix.rs +++ b/vm/src/stdlib/posix.rs @@ -332,22 +332,22 @@ pub mod module { } #[derive(FromArgs)] - pub(super) struct SymlinkArgs { + pub(super) struct SymlinkArgs<'fd> { src: OsPath, dst: OsPath, #[pyarg(flatten)] _target_is_directory: TargetIsDirectory, #[pyarg(flatten)] - dir_fd: DirFd<{ _os::SYMLINK_DIR_FD as usize }>, + dir_fd: DirFd<'fd, { _os::SYMLINK_DIR_FD as usize }>, } #[pyfunction] - pub(super) fn symlink(args: SymlinkArgs, vm: &VirtualMachine) -> PyResult<()> { + pub(super) fn symlink(args: SymlinkArgs<'_>, vm: &VirtualMachine) -> PyResult<()> { let src = args.src.into_cstring(vm)?; let dst = args.dst.into_cstring(vm)?; #[cfg(not(target_os = "redox"))] { - nix::unistd::symlinkat(&*src, args.dir_fd.get_opt(), &*dst) + nix::unistd::symlinkat(&*src, args.dir_fd.raw_opt(), &*dst) .map_err(|err| err.into_pyexception(vm)) } #[cfg(target_os = "redox")] @@ -380,10 +380,10 @@ pub mod module { #[cfg(not(target_os = "redox"))] #[pyfunction] fn chown( - path: OsPathOrFd, + path: OsPathOrFd<'_>, uid: isize, gid: isize, - dir_fd: DirFd<1>, + dir_fd: DirFd<'_, 1>, follow_symlinks: FollowSymlinks, vm: &VirtualMachine, ) -> PyResult<()> { @@ -409,12 +409,12 @@ pub mod module { nix::fcntl::AtFlags::AT_SYMLINK_NOFOLLOW }; - let dir_fd = dir_fd.get_opt(); + let dir_fd = dir_fd.raw_opt(); match path { OsPathOrFd::Path(ref p) => { nix::unistd::fchownat(dir_fd, p.path.as_os_str(), uid, gid, flag) } - OsPathOrFd::Fd(fd) => nix::unistd::fchown(fd, uid, gid), + OsPathOrFd::Fd(fd) => nix::unistd::fchown(fd.as_raw(), uid, gid), } .map_err(|err| { // Use `From for io::Error` when it is available @@ -438,9 +438,9 @@ pub mod module { #[cfg(not(target_os = "redox"))] #[pyfunction] - fn fchown(fd: i32, uid: isize, gid: isize, vm: &VirtualMachine) -> PyResult<()> { + fn fchown(fd: BorrowedFd<'_>, uid: isize, gid: isize, vm: &VirtualMachine) -> PyResult<()> { chown( - OsPathOrFd::Fd(fd), + OsPathOrFd::Fd(fd.into()), uid, gid, DirFd::default(), @@ -567,7 +567,7 @@ pub mod module { #[cfg(not(target_os = "redox"))] #[derive(FromArgs)] - struct MknodArgs { + struct MknodArgs<'fd> { #[pyarg(any)] path: OsPath, #[pyarg(any)] @@ -575,11 +575,11 @@ pub mod module { #[pyarg(any)] device: libc::dev_t, #[pyarg(flatten)] - dir_fd: DirFd<{ MKNOD_DIR_FD as usize }>, + dir_fd: DirFd<'fd, { MKNOD_DIR_FD as usize }>, } #[cfg(not(target_os = "redox"))] - impl MknodArgs { + impl MknodArgs<'_> { fn _mknod(self, vm: &VirtualMachine) -> PyResult { Ok(unsafe { libc::mknod( @@ -591,7 +591,7 @@ pub mod module { } #[cfg(not(target_vendor = "apple"))] fn mknod(self, vm: &VirtualMachine) -> PyResult<()> { - let ret = match self.dir_fd.get_opt() { + let ret = match self.dir_fd.raw_opt() { None => self._mknod(vm)?, Some(non_default_fd) => unsafe { libc::mknodat( @@ -614,7 +614,7 @@ pub mod module { #[cfg(not(target_os = "redox"))] #[pyfunction] - fn mknod(args: MknodArgs, vm: &VirtualMachine) -> PyResult<()> { + fn mknod(args: MknodArgs<'_>, vm: &VirtualMachine) -> PyResult<()> { args.mknod(vm) } @@ -893,7 +893,7 @@ pub mod module { fn _chmod( path: OsPath, - dir_fd: DirFd<0>, + dir_fd: DirFd<'_, 0>, mode: u32, follow_symlinks: FollowSymlinks, vm: &VirtualMachine, @@ -911,9 +911,9 @@ pub mod module { } #[cfg(not(target_os = "redox"))] - fn _fchmod(fd: RawFd, mode: u32, vm: &VirtualMachine) -> PyResult<()> { + fn _fchmod(fd: BorrowedFd<'_>, mode: u32, vm: &VirtualMachine) -> PyResult<()> { nix::sys::stat::fchmod( - fd, + fd.as_raw_fd(), nix::sys::stat::Mode::from_bits(mode as libc::mode_t).unwrap(), ) .map_err(|err| err.into_pyexception(vm)) @@ -922,8 +922,8 @@ pub mod module { #[cfg(not(target_os = "redox"))] #[pyfunction] fn chmod( - path: OsPathOrFd, - dir_fd: DirFd<0>, + path: OsPathOrFd<'_>, + dir_fd: DirFd<'_, 0>, mode: u32, follow_symlinks: FollowSymlinks, vm: &VirtualMachine, @@ -936,7 +936,7 @@ pub mod module { } _chmod(path, dir_fd, mode, follow_symlinks, vm) } - OsPathOrFd::Fd(fd) => _fchmod(fd, mode, vm), + OsPathOrFd::Fd(fd) => _fchmod(fd.into(), mode, vm), } } @@ -954,7 +954,7 @@ pub mod module { #[cfg(not(target_os = "redox"))] #[pyfunction] - fn fchmod(fd: RawFd, mode: u32, vm: &VirtualMachine) -> PyResult<()> { + fn fchmod(fd: BorrowedFd<'_>, mode: u32, vm: &VirtualMachine) -> PyResult<()> { _fchmod(fd, mode, vm) } @@ -1943,7 +1943,7 @@ pub mod module { #[cfg(unix)] #[pyfunction] fn pathconf( - path: OsPathOrFd, + path: OsPathOrFd<'_>, PathconfName(name): PathconfName, vm: &VirtualMachine, ) -> PyResult> { @@ -1956,7 +1956,7 @@ pub mod module { let path = path.clone().into_cstring(vm)?; unsafe { libc::pathconf(path.as_ptr(), name) } } - OsPathOrFd::Fd(fd) => unsafe { libc::fpathconf(*fd, name) }, + OsPathOrFd::Fd(fd) => unsafe { libc::fpathconf(fd.as_raw(), name) }, }; if raw == -1 { @@ -1976,11 +1976,11 @@ pub mod module { #[pyfunction] fn fpathconf( - fd: i32, + fd: BorrowedFd<'_>, name: PathconfName, vm: &VirtualMachine, ) -> PyResult> { - pathconf(OsPathOrFd::Fd(fd), name, vm) + pathconf(OsPathOrFd::Fd(fd.into()), name, vm) } #[pyattr]