diff --git a/crates/host_env/src/fileutils.rs b/crates/host_env/src/fileutils.rs index 1713370a942..a4922e7a2fe 100644 --- a/crates/host_env/src/fileutils.rs +++ b/crates/host_env/src/fileutils.rs @@ -2,22 +2,14 @@ #![allow(non_snake_case)] #[cfg(not(windows))] -pub use libc::stat as StatStruct; +pub use rustix::fs::Stat as StatStruct; #[cfg(windows)] pub use windows::{StatStruct, fstat}; #[cfg(not(windows))] pub fn fstat(fd: crate::crt_fd::Borrowed<'_>) -> std::io::Result { - let mut stat = core::mem::MaybeUninit::uninit(); - unsafe { - let ret = libc::fstat(fd.as_raw(), stat.as_mut_ptr()); - if ret == -1 { - Err(crate::os::errno_io_error()) - } else { - Ok(stat.assume_init()) - } - } + rustix::fs::fstat(fd).map_err(Into::into) } #[cfg(windows)] diff --git a/crates/host_env/src/io.rs b/crates/host_env/src/io.rs index 4ae1e4b3641..6df29bcd6bc 100644 --- a/crates/host_env/src/io.rs +++ b/crates/host_env/src/io.rs @@ -2,6 +2,9 @@ use core::ffi::CStr; use std::io; +#[cfg(any(unix, target_os = "wasi"))] +use rustix::{fs::FileType, io::Errno}; + #[cfg(any(unix, target_os = "wasi"))] use crate::fileutils; use crate::{crt_fd, os}; @@ -148,8 +151,8 @@ pub struct FileTargetInfo { #[cfg(any(unix, target_os = "wasi"))] pub fn inspect_file_target(fd: crt_fd::Borrowed<'_>) -> io::Result { let status = fileutils::fstat(fd)?; - if (status.st_mode & libc::S_IFMT) == libc::S_IFDIR { - return Err(io::Error::from_raw_os_error(libc::EISDIR)); + if FileType::from_raw_mode(status.st_mode).is_dir() { + return Err(io::Error::from(Errno::ISDIR)); } #[allow(clippy::useless_conversion, reason = "needed for 32-bit platforms")] let blksize = (status.st_blksize > 1).then(|| i64::from(status.st_blksize)); diff --git a/crates/host_env/src/posix.rs b/crates/host_env/src/posix.rs index e20accee715..50d3f52a674 100644 --- a/crates/host_env/src/posix.rs +++ b/crates/host_env/src/posix.rs @@ -304,50 +304,6 @@ pub fn fchown(fd: BorrowedFd<'_>, uid: Option, gid: Option) -> std::io .map_err(std::io::Error::from) } -#[cfg(not(windows))] -#[expect( - clippy::std_instead_of_core, - reason = "false positive: core::io::ErrorKind is unstable (core_io)" -)] -pub fn stat_path( - path: &OsStr, - dir_fd: Option, - follow_symlinks: bool, -) -> std::io::Result> { - use crate::os::ffi::OsStrExt; - - let path = match CString::new(path.as_bytes()) { - Ok(path) => path, - Err(_) => return Err(std::io::Error::from(std::io::ErrorKind::InvalidInput)), - }; - - let mut stat = core::mem::MaybeUninit::uninit(); - #[cfg(not(target_os = "redox"))] - if let Some(dir_fd) = dir_fd { - let flags = if follow_symlinks { - 0 - } else { - libc::AT_SYMLINK_NOFOLLOW - }; - let ret = unsafe { libc::fstatat(dir_fd, path.as_ptr(), stat.as_mut_ptr(), flags) }; - if ret < 0 { - return Err(std::io::Error::last_os_error()); - } - return Ok(Some(unsafe { stat.assume_init() })); - } - - let ret = if follow_symlinks { - unsafe { libc::stat(path.as_ptr(), stat.as_mut_ptr()) } - } else { - unsafe { libc::lstat(path.as_ptr(), stat.as_mut_ptr()) } - }; - if ret < 0 { - Err(std::io::Error::last_os_error()) - } else { - Ok(Some(unsafe { stat.assume_init() })) - } -} - #[cfg(not(windows))] pub fn stat_fd(fd: crate::crt_fd::Borrowed<'_>) -> std::io::Result { crate::fileutils::fstat(fd) diff --git a/crates/host_env/src/posix_unix_like.rs b/crates/host_env/src/posix_unix_like.rs index 9bb29c41c84..183feb5316b 100644 --- a/crates/host_env/src/posix_unix_like.rs +++ b/crates/host_env/src/posix_unix_like.rs @@ -2,11 +2,14 @@ use std::{io, path::Path}; -use rustix::{fd::AsFd, fs}; +use rustix::{ + fd::AsFd, + fs::{self, AtFlags}, +}; pub use rustix::fs::RawMode; -use crate::crt_fd; +use crate::{crt_fd, fileutils::StatStruct}; /// https://pubs.opengroup.org/onlinepubs/9799919799/functions/mkdir.html pub fn make_dir( @@ -45,3 +48,19 @@ pub fn replace( ) -> io::Result<()> { rename(from, from_fd, to, to_fd) } + +pub fn stat_path( + path: impl AsRef, + dir_fd: Option>, + follow_symlinks: bool, +) -> io::Result> { + let flags = if follow_symlinks { + AtFlags::empty() + } else { + AtFlags::SYMLINK_NOFOLLOW + }; + let dir_fd = dir_fd.as_ref().map_or(fs::CWD, AsFd::as_fd); + fs::statat(dir_fd, path.as_ref(), flags) + .map(Option::Some) + .map_err(Into::into) +} diff --git a/crates/host_env/src/posix_wasi.rs b/crates/host_env/src/posix_wasi.rs index 23c3be2fb91..791991e981d 100644 --- a/crates/host_env/src/posix_wasi.rs +++ b/crates/host_env/src/posix_wasi.rs @@ -12,39 +12,6 @@ pub fn remove_dir_at(dir_fd: i32, path: &CStr) -> io::Result<()> { Ok(()) } -pub fn stat_path( - path: &OsStr, - dir_fd: Option, - follow_symlinks: bool, -) -> io::Result> { - use crate::os::ffi::OsStrExt; - - let path = match CString::new(path.as_bytes()) { - Ok(path) => path, - Err(_) => return Err(io::Error::from(io::ErrorKind::InvalidInput)), - }; - - let mut stat = core::mem::MaybeUninit::uninit(); - if let Some(dir_fd) = dir_fd { - let flags = if follow_symlinks { - 0 - } else { - libc::AT_SYMLINK_NOFOLLOW - }; - unsafe { libc::fstatat(dir_fd, path.as_ptr(), stat.as_mut_ptr(), flags) } - .check_libc_neg()?; - return Ok(Some(unsafe { stat.assume_init() })); - } - - let ret = if follow_symlinks { - unsafe { libc::stat(path.as_ptr(), stat.as_mut_ptr()) } - } else { - unsafe { libc::lstat(path.as_ptr(), stat.as_mut_ptr()) } - }; - ret.check_libc_neg()?; - Ok(Some(unsafe { stat.assume_init() })) -} - pub fn stat_fd(fd: crate::crt_fd::Borrowed<'_>) -> io::Result { crate::fileutils::fstat(fd) } diff --git a/crates/vm/src/stdlib/os.rs b/crates/vm/src/stdlib/os.rs index a41e9990f12..a934c6d812f 100644 --- a/crates/vm/src/stdlib/os.rs +++ b/crates/vm/src/stdlib/os.rs @@ -1183,18 +1183,15 @@ pub(super) mod _os { pub st_gid: PyIntRef, pub st_size: PyIntRef, // Indices 7-9: integer seconds - #[cfg_attr(target_env = "musl", allow(deprecated))] #[pyarg(positional, default)] #[pystruct_sequence(unnamed)] - pub st_atime_int: libc::time_t, - #[cfg_attr(target_env = "musl", allow(deprecated))] + pub st_atime_int: i64, #[pyarg(positional, default)] #[pystruct_sequence(unnamed)] - pub st_mtime_int: libc::time_t, - #[cfg_attr(target_env = "musl", allow(deprecated))] + pub st_mtime_int: i64, #[pyarg(positional, default)] #[pystruct_sequence(unnamed)] - pub st_ctime_int: libc::time_t, + pub st_ctime_int: i64, // Float time attributes #[pyarg(any, default)] #[pystruct_sequence(skip)] @@ -1219,11 +1216,11 @@ pub(super) mod _os { #[cfg(not(windows))] #[pyarg(any, default)] #[pystruct_sequence(skip)] - pub st_blksize: i64, + pub st_blksize: u64, #[cfg(not(windows))] #[pyarg(any, default)] #[pystruct_sequence(skip)] - pub st_blocks: i64, + pub st_blocks: u64, #[cfg(windows)] #[pyarg(any, default)] #[pystruct_sequence(skip)] @@ -1237,19 +1234,12 @@ pub(super) mod _os { impl StatResultData { fn from_stat(stat: &StatStruct, vm: &VirtualMachine) -> Self { let (atime, mtime, ctime); - #[cfg(any(unix, windows))] - #[cfg(not(any(target_os = "netbsd", target_os = "wasi")))] + #[cfg(all(any(unix, windows), not(target_os = "wasi")))] { atime = (stat.st_atime, stat.st_atime_nsec); mtime = (stat.st_mtime, stat.st_mtime_nsec); ctime = (stat.st_ctime, stat.st_ctime_nsec); } - #[cfg(target_os = "netbsd")] - { - atime = (stat.st_atime, stat.st_atimensec); - mtime = (stat.st_mtime, stat.st_mtimensec); - ctime = (stat.st_ctime, stat.st_ctimensec); - } #[cfg(target_os = "wasi")] { atime = (stat.st_atim.tv_sec, stat.st_atim.tv_nsec); @@ -1274,12 +1264,18 @@ pub(super) mod _os { let st_ino = stat.st_ino; #[cfg(not(windows))] - #[allow(clippy::useless_conversion, reason = "needed for 32-bit platforms")] - let st_blksize = i64::from(stat.st_blksize); + #[allow( + clippy::useless_conversion, + reason = "signedness differs between platforms" + )] + let st_blksize = stat.st_blksize.try_into().unwrap_or(4096); #[cfg(not(windows))] - #[allow(clippy::useless_conversion, reason = "needed for 32-bit platforms")] - let st_blocks = i64::from(stat.st_blocks); + #[allow( + clippy::useless_conversion, + reason = "signedness differs between platforms" + )] + let st_blocks = stat.st_blocks.try_into().unwrap_or_default(); Self { st_mode: vm.ctx.new_pyref(stat.st_mode), @@ -1360,11 +1356,9 @@ pub(super) mod _os { follow_symlinks: FollowSymlinks, ) -> io::Result> { match file { - OsPathOrFd::Path(path) => host_posix::stat_path( - path.as_ref().as_os_str(), - dir_fd.raw_opt(), - follow_symlinks.0, - ), + OsPathOrFd::Path(path) => { + host_posix::stat_path(path, dir_fd.get_opt(), follow_symlinks.0) + } OsPathOrFd::Fd(fd) => host_posix::stat_fd(fd).map(Some), } }