From e24dc76446afd00981c64dfc439dc9547bc5b9df Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Tue, 31 Oct 2023 11:01:45 -0700 Subject: [PATCH 1/8] Work around a negative timestamps bug on macOS. As described [here], macOS can sometimes return `timespec` values with negative `tv_nsecs` values. Adjust `timespec` values as needed to ensure that `tv_nsec` is in 0..1_000_000_000. [here]: https://github.com/rust-lang/rust/issues/108277#issuecomment-1787057158 --- src/backend/libc/fs/syscalls.rs | 29 ++++++++++++++--- src/backend/libc/time/syscalls.rs | 18 +++++++++-- src/timespec.rs | 35 +++++++++++++++++++++ tests/fs/main.rs | 1 + tests/fs/negative_timestamp.rs | 52 +++++++++++++++++++++++++++++++ 5 files changed, 128 insertions(+), 7 deletions(-) create mode 100644 tests/fs/negative_timestamp.rs diff --git a/src/backend/libc/fs/syscalls.rs b/src/backend/libc/fs/syscalls.rs index 84abe6260..619568813 100644 --- a/src/backend/libc/fs/syscalls.rs +++ b/src/backend/libc/fs/syscalls.rs @@ -613,7 +613,10 @@ pub(crate) fn stat(path: &CStr) -> io::Result { unsafe { let mut stat = MaybeUninit::::uninit(); ret(c::stat(c_str(path), stat.as_mut_ptr()))?; - Ok(stat.assume_init()) + let stat = stat.assume_init(); + #[cfg(apple)] + let stat = fix_negative_stat_nsecs(stat); + Ok(stat) } } @@ -653,7 +656,10 @@ pub(crate) fn lstat(path: &CStr) -> io::Result { unsafe { let mut stat = MaybeUninit::::uninit(); ret(c::lstat(c_str(path), stat.as_mut_ptr()))?; - Ok(stat.assume_init()) + let stat = stat.assume_init(); + #[cfg(apple)] + let stat = fix_negative_stat_nsecs(stat); + Ok(stat) } } @@ -694,7 +700,10 @@ pub(crate) fn statat(dirfd: BorrowedFd<'_>, path: &CStr, flags: AtFlags) -> io:: stat.as_mut_ptr(), bitflags_bits!(flags), ))?; - Ok(stat.assume_init()) + let stat = stat.assume_init(); + #[cfg(apple)] + let stat = fix_negative_stat_nsecs(stat); + Ok(stat) } } @@ -1445,7 +1454,10 @@ pub(crate) fn fstat(fd: BorrowedFd<'_>) -> io::Result { unsafe { let mut stat = MaybeUninit::::uninit(); ret(c::fstat(borrowed_fd(fd), stat.as_mut_ptr()))?; - Ok(stat.assume_init()) + let stat = stat.assume_init(); + #[cfg(apple)] + let stat = fix_negative_stat_nsecs(stat); + Ok(stat) } } @@ -2553,6 +2565,15 @@ pub(crate) fn fremovexattr(fd: BorrowedFd<'_>, name: &CStr) -> io::Result<()> { } } +/// See [`crate::timespec::fix_negative_nsec`] for details. +#[cfg(apple)] +fn fix_negative_stat_nsecs(mut stat: Stat) -> Stat { + crate::timespec::fix_negative_nsecs(&mut stat.st_atime, &mut stat.st_atime_nsec); + crate::timespec::fix_negative_nsecs(&mut stat.st_mtime, &mut stat.st_mtime_nsec); + crate::timespec::fix_negative_nsecs(&mut stat.st_ctime, &mut stat.st_ctime_nsec); + stat +} + #[test] fn test_sizes() { #[cfg(linux_kernel)] diff --git a/src/backend/libc/time/syscalls.rs b/src/backend/libc/time/syscalls.rs index ed1e745c8..12cc47584 100644 --- a/src/backend/libc/time/syscalls.rs +++ b/src/backend/libc/time/syscalls.rs @@ -130,7 +130,10 @@ pub(crate) fn clock_gettime(id: ClockId) -> Timespec { as_libc_timespec_mut_ptr(&mut timespec), )) .unwrap(); - timespec.assume_init() + let timespec = timespec.assume_init(); + #[cfg(apple)] + let timespec = fix_negative_timespec_nsecs(timespec); + timespec } } @@ -220,8 +223,10 @@ pub(crate) fn clock_gettime_dynamic(id: DynamicClockId<'_>) -> io::Result) -> io::Result { }, }) } + +/// See [`crate::timespec::fix_negative_nsecs`] for details. +#[cfg(apple)] +fn fix_negative_timespec_nsecs(mut ts: Timespec) -> Timespec { + crate::timespec::fix_negative_nsecs(&mut ts.tv_sec, &mut ts.tv_nsec); + ts +} diff --git a/src/timespec.rs b/src/timespec.rs index 080e85cbe..594f853f4 100644 --- a/src/timespec.rs +++ b/src/timespec.rs @@ -109,6 +109,41 @@ pub(crate) fn option_as_libc_timespec_ptr(timespec: Option<&Timespec>) -> *const } } +/// As described [here], Apple platforms may return a negative nanoseconds +/// value in some cases; adjust it so that nanoseconds is always in +/// `0..1_000_000_000`. +/// +/// [here]: https://github.com/rust-lang/rust/issues/108277#issuecomment-1787057158 +#[cfg(apple)] +#[inline] +pub(crate) fn fix_negative_nsecs(secs: &mut i64, nsecs: &mut i64) { + if *nsecs < 0 { + adjust(secs, nsecs); + } + + #[cold] + fn adjust(secs: &mut i64, nsecs: &mut i64) { + assert!(*nsecs >= -1_000_000_000); + assert!(*secs < 0); + assert!(*secs > i64::MIN); + *nsecs += 1_000_000_000; + *secs -= 1; + } +} + +#[cfg(apple)] +#[test] +fn test_negative_timestamps() { + let mut secs = -59; + let mut nsecs = -900_000_000; + fix_negative_nsecs(&mut secs, &mut nsecs); + assert_eq!(secs, -60); + assert_eq!(nsecs, 100_000_000); + fix_negative_nsecs(&mut secs, &mut nsecs); + assert_eq!(secs, -60); + assert_eq!(nsecs, 100_000_000); +} + #[test] fn test_sizes() { assert_eq_size!(Secs, u64); diff --git a/tests/fs/main.rs b/tests/fs/main.rs index 54e41cb51..e2f810824 100644 --- a/tests/fs/main.rs +++ b/tests/fs/main.rs @@ -31,6 +31,7 @@ mod long_paths; mod makedev; mod mkdirat; mod mknodat; +mod negative_timestamp; #[cfg(linux_kernel)] mod openat; #[cfg(linux_kernel)] diff --git a/tests/fs/negative_timestamp.rs b/tests/fs/negative_timestamp.rs new file mode 100644 index 000000000..84d74c1db --- /dev/null +++ b/tests/fs/negative_timestamp.rs @@ -0,0 +1,52 @@ +#[test] +fn negative_file_timetamp() { + use rustix::fs::{ + fstat, futimens, lstat, open, stat, statat, AtFlags, Mode, OFlags, Timespec, Timestamps, + CWD, + }; + + let tmp = tempfile::tempdir().unwrap(); + + let file = open( + tmp.path().join("foo"), + OFlags::CREATE | OFlags::WRONLY, + Mode::RWXU, + ) + .unwrap(); + + let stamps = Timestamps { + last_modification: Timespec { + tv_sec: -20, + tv_nsec: 12, + }, + last_access: Timespec { + tv_sec: -23, + tv_nsec: 14, + }, + }; + futimens(&file, &stamps).unwrap(); + + let st = fstat(file).unwrap(); + assert_eq!(st.st_mtime, -20); + assert_eq!(st.st_mtime_nsec, 12); + assert_eq!(st.st_atime, -23); + assert_eq!(st.st_atime_nsec, 14); + + let st = stat(tmp.path().join("foo")).unwrap(); + assert_eq!(st.st_mtime, -20); + assert_eq!(st.st_mtime_nsec, 12); + assert_eq!(st.st_atime, -23); + assert_eq!(st.st_atime_nsec, 14); + + let st = lstat(tmp.path().join("foo")).unwrap(); + assert_eq!(st.st_mtime, -20); + assert_eq!(st.st_mtime_nsec, 12); + assert_eq!(st.st_atime, -23); + assert_eq!(st.st_atime_nsec, 14); + + let st = statat(CWD, tmp.path().join("foo"), AtFlags::empty()).unwrap(); + assert_eq!(st.st_mtime, -20); + assert_eq!(st.st_mtime_nsec, 12); + assert_eq!(st.st_atime, -23); + assert_eq!(st.st_atime_nsec, 14); +} From cf785f70fa5eee8184ec7d064858a96532bff6cc Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Fri, 31 Jan 2025 06:24:27 -0800 Subject: [PATCH 2/8] Make st_mtime etc. signed. --- src/backend/libc/fs/syscalls.rs | 6 +++--- src/backend/linux_raw/fs/syscalls.rs | 18 +++++++++--------- src/backend/linux_raw/fs/types.rs | 18 +++++++++--------- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/backend/libc/fs/syscalls.rs b/src/backend/libc/fs/syscalls.rs index 619568813..0d873e34e 100644 --- a/src/backend/libc/fs/syscalls.rs +++ b/src/backend/libc/fs/syscalls.rs @@ -1862,17 +1862,17 @@ fn stat64_to_stat(s64: c::stat64) -> io::Result { st_size: s64.st_size.try_into().map_err(|_| io::Errno::OVERFLOW)?, st_blksize: s64.st_blksize.try_into().map_err(|_| io::Errno::OVERFLOW)?, st_blocks: s64.st_blocks.try_into().map_err(|_| io::Errno::OVERFLOW)?, - st_atime: bitcast!(i64::from(s64.st_atime)), + st_atime: i64::from(s64.st_atime), st_atime_nsec: s64 .st_atime_nsec .try_into() .map_err(|_| io::Errno::OVERFLOW)?, - st_mtime: bitcast!(i64::from(s64.st_mtime)), + st_mtime: i64::from(s64.st_mtime), st_mtime_nsec: s64 .st_mtime_nsec .try_into() .map_err(|_| io::Errno::OVERFLOW)?, - st_ctime: bitcast!(i64::from(s64.st_ctime)), + st_ctime: i64::from(s64.st_ctime), st_ctime_nsec: s64 .st_ctime_nsec .try_into() diff --git a/src/backend/linux_raw/fs/syscalls.rs b/src/backend/linux_raw/fs/syscalls.rs index 67a0a236e..d098ed5ad 100644 --- a/src/backend/linux_raw/fs/syscalls.rs +++ b/src/backend/linux_raw/fs/syscalls.rs @@ -730,11 +730,11 @@ fn statx_to_stat(x: crate::fs::Statx) -> io::Result { st_size: x.stx_size.try_into().map_err(|_| io::Errno::OVERFLOW)?, st_blksize: x.stx_blksize.into(), st_blocks: x.stx_blocks.into(), - st_atime: bitcast!(i64::from(x.stx_atime.tv_sec)), + st_atime: i64::from(x.stx_atime.tv_sec), st_atime_nsec: x.stx_atime.tv_nsec.into(), - st_mtime: bitcast!(i64::from(x.stx_mtime.tv_sec)), + st_mtime: i64::from(x.stx_mtime.tv_sec), st_mtime_nsec: x.stx_mtime.tv_nsec.into(), - st_ctime: bitcast!(i64::from(x.stx_ctime.tv_sec)), + st_ctime: i64::from(x.stx_ctime.tv_sec), st_ctime_nsec: x.stx_ctime.tv_nsec.into(), st_ino: x.stx_ino.into(), }) @@ -754,17 +754,17 @@ fn stat_to_stat(s64: linux_raw_sys::general::stat64) -> io::Result { st_size: s64.st_size.try_into().map_err(|_| io::Errno::OVERFLOW)?, st_blksize: s64.st_blksize.try_into().map_err(|_| io::Errno::OVERFLOW)?, st_blocks: s64.st_blocks.try_into().map_err(|_| io::Errno::OVERFLOW)?, - st_atime: bitcast!(i64::from(s64.st_atime)), + st_atime: i64::from(s64.st_atime), st_atime_nsec: s64 .st_atime_nsec .try_into() .map_err(|_| io::Errno::OVERFLOW)?, - st_mtime: bitcast!(i64::from(s64.st_mtime)), + st_mtime: i64::from(s64.st_mtime), st_mtime_nsec: s64 .st_mtime_nsec .try_into() .map_err(|_| io::Errno::OVERFLOW)?, - st_ctime: bitcast!(i64::from(s64.st_ctime)), + st_ctime: i64::from(s64.st_ctime), st_ctime_nsec: s64 .st_ctime_nsec .try_into() @@ -786,17 +786,17 @@ fn stat_to_stat(s: linux_raw_sys::general::stat) -> io::Result { st_size: s.st_size.try_into().map_err(|_| io::Errno::OVERFLOW)?, st_blksize: s.st_blksize.try_into().map_err(|_| io::Errno::OVERFLOW)?, st_blocks: s.st_blocks.try_into().map_err(|_| io::Errno::OVERFLOW)?, - st_atime: bitcast!(i64::from(s.st_atime)), + st_atime: i64::from(s.st_atime), st_atime_nsec: s .st_atime_nsec .try_into() .map_err(|_| io::Errno::OVERFLOW)?, - st_mtime: bitcast!(i64::from(s.st_mtime)), + st_mtime: i64::from(s.st_mtime), st_mtime_nsec: s .st_mtime_nsec .try_into() .map_err(|_| io::Errno::OVERFLOW)?, - st_ctime: bitcast!(i64::from(s.st_ctime)), + st_ctime: i64::from(s.st_ctime), st_ctime_nsec: s .st_ctime_nsec .try_into() diff --git a/src/backend/linux_raw/fs/types.rs b/src/backend/linux_raw/fs/types.rs index 7c621bedd..b5a418f59 100644 --- a/src/backend/linux_raw/fs/types.rs +++ b/src/backend/linux_raw/fs/types.rs @@ -636,11 +636,11 @@ pub struct Stat { pub st_size: ffi::c_long, pub st_blksize: ffi::c_long, pub st_blocks: ffi::c_long, - pub st_atime: ffi::c_ulong, + pub st_atime: ffi::c_long, pub st_atime_nsec: ffi::c_ulong, - pub st_mtime: ffi::c_ulong, + pub st_mtime: ffi::c_long, pub st_mtime_nsec: ffi::c_ulong, - pub st_ctime: ffi::c_ulong, + pub st_ctime: ffi::c_long, pub st_ctime_nsec: ffi::c_ulong, pub(crate) __unused: [ffi::c_long; 3], } @@ -715,11 +715,11 @@ pub struct Stat { pub st_size: ffi::c_long, pub st_blksize: ffi::c_ulong, pub st_blocks: ffi::c_ulong, - pub st_atime: ffi::c_ulong, + pub st_atime: ffi::c_long, pub st_atime_nsec: ffi::c_ulong, - pub st_mtime: ffi::c_ulong, + pub st_mtime: ffi::c_long, pub st_mtime_nsec: ffi::c_ulong, - pub st_ctime: ffi::c_ulong, + pub st_ctime: ffi::c_long, pub st_ctime_nsec: ffi::c_ulong, pub(crate) __unused4: ffi::c_ulong, pub(crate) __unused5: ffi::c_ulong, @@ -740,11 +740,11 @@ pub struct Stat { pub(crate) __pad1: ffi::c_uint, pub st_rdev: ffi::c_ulong, pub st_size: ffi::c_ulong, - pub st_atime: ffi::c_ulong, + pub st_atime: ffi::c_long, pub st_atime_nsec: ffi::c_ulong, - pub st_mtime: ffi::c_ulong, + pub st_mtime: ffi::c_long, pub st_mtime_nsec: ffi::c_ulong, - pub st_ctime: ffi::c_ulong, + pub st_ctime: ffi::c_long, pub st_ctime_nsec: ffi::c_ulong, pub st_blksize: ffi::c_ulong, pub st_blocks: ffi::c_long, From 526ed6e2270690fd633749d14f9211487bf72097 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Fri, 31 Jan 2025 07:36:29 -0800 Subject: [PATCH 3/8] Define our own `Stat` on NetBSD, fix types for Apple. --- src/backend/libc/fs/syscalls.rs | 17 ++--- src/backend/libc/fs/types.rs | 36 ++++++++++- src/backend/libc/time/syscalls.rs | 2 +- src/fs/constants.rs | 100 ++++++++++++++++++++++++++++-- src/timespec.rs | 21 ++++--- tests/fs/futimens.rs | 6 -- tests/fs/utimensat.rs | 12 ---- tests/fs/y2038.rs | 54 ---------------- 8 files changed, 151 insertions(+), 97 deletions(-) diff --git a/src/backend/libc/fs/syscalls.rs b/src/backend/libc/fs/syscalls.rs index 0d873e34e..378d7782e 100644 --- a/src/backend/libc/fs/syscalls.rs +++ b/src/backend/libc/fs/syscalls.rs @@ -612,7 +612,7 @@ pub(crate) fn stat(path: &CStr) -> io::Result { )))] unsafe { let mut stat = MaybeUninit::::uninit(); - ret(c::stat(c_str(path), stat.as_mut_ptr()))?; + ret(c::stat(c_str(path), stat.as_mut_ptr().cast()))?; let stat = stat.assume_init(); #[cfg(apple)] let stat = fix_negative_stat_nsecs(stat); @@ -655,7 +655,7 @@ pub(crate) fn lstat(path: &CStr) -> io::Result { )))] unsafe { let mut stat = MaybeUninit::::uninit(); - ret(c::lstat(c_str(path), stat.as_mut_ptr()))?; + ret(c::lstat(c_str(path), stat.as_mut_ptr().cast()))?; let stat = stat.assume_init(); #[cfg(apple)] let stat = fix_negative_stat_nsecs(stat); @@ -697,7 +697,7 @@ pub(crate) fn statat(dirfd: BorrowedFd<'_>, path: &CStr, flags: AtFlags) -> io:: ret(c::fstatat( borrowed_fd(dirfd), c_str(path), - stat.as_mut_ptr(), + stat.as_mut_ptr().cast(), bitflags_bits!(flags), ))?; let stat = stat.assume_init(); @@ -1453,7 +1453,7 @@ pub(crate) fn fstat(fd: BorrowedFd<'_>) -> io::Result { )))] unsafe { let mut stat = MaybeUninit::::uninit(); - ret(c::fstat(borrowed_fd(fd), stat.as_mut_ptr()))?; + ret(c::fstat(borrowed_fd(fd), stat.as_mut_ptr().cast()))?; let stat = stat.assume_init(); #[cfg(apple)] let stat = fix_negative_stat_nsecs(stat); @@ -2568,9 +2568,12 @@ pub(crate) fn fremovexattr(fd: BorrowedFd<'_>, name: &CStr) -> io::Result<()> { /// See [`crate::timespec::fix_negative_nsec`] for details. #[cfg(apple)] fn fix_negative_stat_nsecs(mut stat: Stat) -> Stat { - crate::timespec::fix_negative_nsecs(&mut stat.st_atime, &mut stat.st_atime_nsec); - crate::timespec::fix_negative_nsecs(&mut stat.st_mtime, &mut stat.st_mtime_nsec); - crate::timespec::fix_negative_nsecs(&mut stat.st_ctime, &mut stat.st_ctime_nsec); + stat.st_atime_nsec = + crate::timespec::fix_negative_nsecs(&mut stat.st_atime, stat.st_atime_nsec as _) as _; + stat.st_mtime_nsec = + crate::timespec::fix_negative_nsecs(&mut stat.st_mtime, stat.st_mtime_nsec as _) as _; + stat.st_ctime_nsec = + crate::timespec::fix_negative_nsecs(&mut stat.st_ctime, stat.st_ctime_nsec as _) as _; stat } diff --git a/src/backend/libc/fs/types.rs b/src/backend/libc/fs/types.rs index 0da1717b3..c92250475 100644 --- a/src/backend/libc/fs/types.rs +++ b/src/backend/libc/fs/types.rs @@ -877,7 +877,7 @@ pub enum FlockOperation { /// /// [`statat`]: crate::fs::statat /// [`fstat`]: crate::fs::fstat -#[cfg(not(any(linux_like, target_os = "hurd")))] +#[cfg(not(any(linux_like, target_os = "hurd", target_os = "netbsd")))] pub type Stat = c::stat; /// `struct stat` for use with [`statat`] and [`fstat`]. @@ -921,6 +921,40 @@ pub struct Stat { pub st_ino: u64, } +/// `struct stat` for use with [`statat`] and [`fstat`]. +/// +/// [`statat`]: crate::fs::statat +/// [`fstat`]: crate::fs::fstat +// NetBSD's `st_mtime_nsec` is named `st_mtimensec` so we declare our own +// `Stat` so that we can be consistent with other platforms. +#[cfg(target_os = "netbsd")] +#[derive(Debug, Copy, Clone)] +#[allow(missing_docs)] +#[repr(C)] +pub struct Stat { + pub st_dev: c::dev_t, + pub st_mode: c::mode_t, + pub st_ino: c::ino_t, + pub st_nlink: c::nlink_t, + pub st_uid: c::uid_t, + pub st_gid: c::gid_t, + pub st_rdev: c::dev_t, + pub st_atime: c::time_t, + pub st_atime_nsec: c::c_long, + pub st_mtime: c::time_t, + pub st_mtime_nsec: c::c_long, + pub st_ctime: c::time_t, + pub st_ctime_nsec: c::c_long, + pub st_birthtime: c::time_t, + pub st_birthtime_nsec: c::c_long, + pub st_size: c::off_t, + pub st_blocks: c::blkcnt_t, + pub st_blksize: c::blksize_t, + pub st_flags: u32, + pub st_gen: u32, + pub st_spare: [u32; 2], +} + /// `struct statfs` for use with [`statfs`] and [`fstatfs`]. /// /// [`statfs`]: crate::fs::statfs diff --git a/src/backend/libc/time/syscalls.rs b/src/backend/libc/time/syscalls.rs index 12cc47584..06fe4a823 100644 --- a/src/backend/libc/time/syscalls.rs +++ b/src/backend/libc/time/syscalls.rs @@ -485,6 +485,6 @@ fn timerfd_gettime_old(fd: BorrowedFd<'_>) -> io::Result { /// See [`crate::timespec::fix_negative_nsecs`] for details. #[cfg(apple)] fn fix_negative_timespec_nsecs(mut ts: Timespec) -> Timespec { - crate::timespec::fix_negative_nsecs(&mut ts.tv_sec, &mut ts.tv_nsec); + ts.tv_nsec = crate::timespec::fix_negative_nsecs(&mut ts.tv_sec, ts.tv_nsec as _) as _; ts } diff --git a/src/fs/constants.rs b/src/fs/constants.rs index fced70632..7528932ba 100644 --- a/src/fs/constants.rs +++ b/src/fs/constants.rs @@ -8,6 +8,7 @@ pub use backend::fs::types::*; #[cfg(test)] #[allow(unused_imports)] +#[allow(unsafe_code)] mod tests { use super::*; use crate::backend::c; @@ -39,12 +40,19 @@ mod tests { ))] assert_eq_size!(u16, linux_raw_sys::general::__kernel_mode_t); - #[cfg(all( - linux_raw, - target_pointer_width = "64", - not(target_arch = "mips64"), - not(target_arch = "mips64r6") - ))] + // Ensure that seconds fields are 64-bit. + let some_stat: Stat = unsafe { core::mem::zeroed() }; + assert_eq!(some_stat.st_atime, 0_i64); + assert_eq!(some_stat.st_mtime, 0_i64); + assert_eq!(some_stat.st_ctime, 0_i64); + + // Check that the layout of `Stat` matches `libc::stat`. + #[cfg(not(any( + all(linux_kernel, target_pointer_width = "64", libc), + target_os = "hurd", + target_os = "emscripten", + target_os = "l4re", + )))] { check_renamed_type!(Stat, stat); check_renamed_struct_field!(Stat, stat, st_dev); @@ -79,11 +87,20 @@ mod tests { check_renamed_struct_field!(Stat, stat, __pad2); check_renamed_struct_field!(Stat, stat, st_blocks); check_renamed_struct_field!(Stat, stat, st_atime); + #[cfg(not(target_os = "netbsd"))] check_renamed_struct_field!(Stat, stat, st_atime_nsec); + #[cfg(target_os = "netbsd")] + check_renamed_struct_renamed_field!(Stat, stat, st_atime_nsec, st_atimensec); check_renamed_struct_field!(Stat, stat, st_mtime); + #[cfg(not(target_os = "netbsd"))] check_renamed_struct_field!(Stat, stat, st_mtime_nsec); + #[cfg(target_os = "netbsd")] + check_renamed_struct_renamed_field!(Stat, stat, st_mtime_nsec, st_mtimensec); check_renamed_struct_field!(Stat, stat, st_ctime); + #[cfg(not(target_os = "netbsd"))] check_renamed_struct_field!(Stat, stat, st_ctime_nsec); + #[cfg(target_os = "netbsd")] + check_renamed_struct_renamed_field!(Stat, stat, st_ctime_nsec, st_ctimensec); #[cfg(all( linux_raw, not(any( @@ -109,6 +126,77 @@ mod tests { check_renamed_struct_field!(Stat, stat, __unused6); } + // Check that the layout of `Stat` matches `libc::stat64`. + #[cfg(any( + all(linux_kernel, target_pointer_width = "64", libc), + target_os = "hurd", + target_os = "emscripten", + target_os = "l4re", + ))] + { + check_renamed_type!(Stat, stat64); + check_renamed_struct_field!(Stat, stat64, st_dev); + check_renamed_struct_field!(Stat, stat64, st_ino); + check_renamed_struct_field!(Stat, stat64, st_nlink); + check_renamed_struct_field!(Stat, stat64, st_mode); + check_renamed_struct_field!(Stat, stat64, st_uid); + check_renamed_struct_field!(Stat, stat64, st_gid); + #[cfg(all( + linux_raw, + not(any( + target_arch = "aarch64", + target_arch = "powerpc64", + target_arch = "riscv64", + target_arch = "s390x" + )) + ))] + check_renamed_struct_field!(Stat, stat64, __pad0); + check_renamed_struct_field!(Stat, stat64, st_rdev); + #[cfg(all(linux_raw, not(any(target_arch = "powerpc64", target_arch = "x86_64"))))] + check_renamed_struct_field!(Stat, stat64, __pad1); + check_renamed_struct_field!(Stat, stat64, st_size); + check_renamed_struct_field!(Stat, stat64, st_blksize); + #[cfg(all( + linux_raw, + not(any( + target_arch = "powerpc64", + target_arch = "s390x", + target_arch = "x86_64" + )) + ))] + check_renamed_struct_field!(Stat, stat64, __pad2); + check_renamed_struct_field!(Stat, stat64, st_blocks); + check_renamed_struct_field!(Stat, stat64, st_atime); + check_renamed_struct_field!(Stat, stat64, st_atime_nsec); + check_renamed_struct_field!(Stat, stat64, st_mtime); + check_renamed_struct_field!(Stat, stat64, st_mtime_nsec); + check_renamed_struct_field!(Stat, stat64, st_ctime); + check_renamed_struct_field!(Stat, stat64, st_ctime_nsec); + #[cfg(all( + linux_raw, + not(any( + target_arch = "aarch64", + target_arch = "powerpc64", + target_arch = "riscv64" + )) + ))] + check_renamed_struct_field!(Stat, stat64, __unused); + #[cfg(all(linux_raw, not(any(target_arch = "s390x", target_arch = "x86_64"))))] + check_renamed_struct_field!(Stat, stat64, __unused4); + #[cfg(all(linux_raw, not(any(target_arch = "s390x", target_arch = "x86_64"))))] + check_renamed_struct_field!(Stat, stat64, __unused5); + #[cfg(all( + linux_raw, + not(any( + target_arch = "aarch64", + target_arch = "riscv64", + target_arch = "s390x", + target_arch = "x86_64" + )) + ))] + check_renamed_struct_field!(Stat, stat64, __unused6); + } + #[cfg(not(any( solarish, target_os = "haiku", diff --git a/src/timespec.rs b/src/timespec.rs index 594f853f4..961159b2c 100644 --- a/src/timespec.rs +++ b/src/timespec.rs @@ -116,19 +116,20 @@ pub(crate) fn option_as_libc_timespec_ptr(timespec: Option<&Timespec>) -> *const /// [here]: https://github.com/rust-lang/rust/issues/108277#issuecomment-1787057158 #[cfg(apple)] #[inline] -pub(crate) fn fix_negative_nsecs(secs: &mut i64, nsecs: &mut i64) { - if *nsecs < 0 { - adjust(secs, nsecs); - } - +pub(crate) fn fix_negative_nsecs(secs: &mut i64, mut nsecs: i32) -> i32 { #[cold] - fn adjust(secs: &mut i64, nsecs: &mut i64) { - assert!(*nsecs >= -1_000_000_000); + fn adjust(secs: &mut i64, nsecs: i32) -> i32 { + assert!(nsecs >= -1_000_000_000); assert!(*secs < 0); assert!(*secs > i64::MIN); - *nsecs += 1_000_000_000; *secs -= 1; + nsecs + 1_000_000_000 + } + + if nsecs < 0 { + nsecs = adjust(secs, nsecs); } + nsecs } #[cfg(apple)] @@ -136,10 +137,10 @@ pub(crate) fn fix_negative_nsecs(secs: &mut i64, nsecs: &mut i64) { fn test_negative_timestamps() { let mut secs = -59; let mut nsecs = -900_000_000; - fix_negative_nsecs(&mut secs, &mut nsecs); + nsecs = fix_negative_nsecs(&mut secs, nsecs); assert_eq!(secs, -60); assert_eq!(nsecs, 100_000_000); - fix_negative_nsecs(&mut secs, &mut nsecs); + nsecs = fix_negative_nsecs(&mut secs, nsecs); assert_eq!(secs, -60); assert_eq!(nsecs, 100_000_000); } diff --git a/tests/fs/futimens.rs b/tests/fs/futimens.rs index 616638f13..963b57f39 100644 --- a/tests/fs/futimens.rs +++ b/tests/fs/futimens.rs @@ -29,14 +29,8 @@ fn test_futimens() { let after = fstat(&file).unwrap(); assert_eq!(times.last_modification.tv_sec as u64, after.st_mtime as u64); - #[cfg(not(target_os = "netbsd"))] assert_eq!( times.last_modification.tv_nsec as u64, after.st_mtime_nsec as u64 ); - #[cfg(target_os = "netbsd")] - assert_eq!( - times.last_modification.tv_nsec as u64, - after.st_mtimensec as u64 - ); } diff --git a/tests/fs/utimensat.rs b/tests/fs/utimensat.rs index ec65f466d..2da161286 100644 --- a/tests/fs/utimensat.rs +++ b/tests/fs/utimensat.rs @@ -35,27 +35,15 @@ fn test_utimensat() { let after = statat(&dir, "file", AtFlags::empty()).unwrap(); assert_eq!(times.last_modification.tv_sec as u64, after.st_mtime as u64); - #[cfg(not(target_os = "netbsd"))] assert_eq!( times.last_modification.tv_nsec as u64, after.st_mtime_nsec as u64 ); - #[cfg(target_os = "netbsd")] - assert_eq!( - times.last_modification.tv_nsec as u64, - after.st_mtimensec as u64 - ); assert!(times.last_access.tv_sec as u64 >= after.st_atime as u64); - #[cfg(not(target_os = "netbsd"))] assert!( times.last_access.tv_sec as u64 > after.st_atime as u64 || times.last_access.tv_nsec as u64 >= after.st_atime_nsec as u64 ); - #[cfg(target_os = "netbsd")] - assert!( - times.last_access.tv_sec as u64 > after.st_atime as u64 - || times.last_access.tv_nsec as u64 >= after.st_atimensec as u64 - ); } #[cfg(not(any(target_os = "redox", target_os = "wasi")))] diff --git a/tests/fs/y2038.rs b/tests/fs/y2038.rs index eba3a16d5..6b49a823c 100644 --- a/tests/fs/y2038.rs +++ b/tests/fs/y2038.rs @@ -48,23 +48,14 @@ fn test_y2038_with_utimensat() { assert_eq!(TryInto::::try_into(stat.st_mtime).unwrap(), m_sec); - #[cfg(not(target_os = "netbsd"))] assert_eq!(stat.st_mtime_nsec as u32, m_nsec); - #[cfg(target_os = "netbsd")] - assert_eq!(stat.st_mtimensec as u32, m_nsec); assert!(TryInto::::try_into(stat.st_atime).unwrap() >= a_sec); - #[cfg(not(target_os = "netbsd"))] assert!( TryInto::::try_into(stat.st_atime).unwrap() > a_sec || stat.st_atime_nsec as u32 >= a_nsec ); - #[cfg(target_os = "netbsd")] - assert!( - TryInto::::try_into(stat.st_atime).unwrap() > a_sec - || stat.st_atimensec as u32 >= a_nsec - ); // Now test the same thing, but with `fstat`. let file = openat(&dir, "file", OFlags::RDONLY, Mode::empty()).unwrap(); @@ -72,23 +63,14 @@ fn test_y2038_with_utimensat() { assert_eq!(TryInto::::try_into(stat.st_mtime).unwrap(), m_sec); - #[cfg(not(target_os = "netbsd"))] assert_eq!(stat.st_mtime_nsec as u32, m_nsec); - #[cfg(target_os = "netbsd")] - assert_eq!(stat.st_mtimensec as u32, m_nsec); assert!(TryInto::::try_into(stat.st_atime).unwrap() >= a_sec); - #[cfg(not(target_os = "netbsd"))] assert!( TryInto::::try_into(stat.st_atime).unwrap() > a_sec || stat.st_atime_nsec as u32 >= a_nsec ); - #[cfg(target_os = "netbsd")] - assert!( - TryInto::::try_into(stat.st_atime).unwrap() > a_sec - || stat.st_atimensec as u32 >= a_nsec - ); } /// Test that we can set a file timestamp to a date past the year 2038 with @@ -141,23 +123,14 @@ fn test_y2038_with_futimens() { assert_eq!(TryInto::::try_into(stat.st_mtime).unwrap(), m_sec); - #[cfg(not(target_os = "netbsd"))] assert_eq!(stat.st_mtime_nsec as u32, m_nsec); - #[cfg(target_os = "netbsd")] - assert_eq!(stat.st_mtimensec as u32, m_nsec); assert!(TryInto::::try_into(stat.st_atime).unwrap() >= a_sec); - #[cfg(not(target_os = "netbsd"))] assert!( TryInto::::try_into(stat.st_atime).unwrap() > a_sec || stat.st_atime_nsec as u32 >= a_nsec ); - #[cfg(target_os = "netbsd")] - assert!( - TryInto::::try_into(stat.st_atime).unwrap() > a_sec - || stat.st_atimensec as u32 >= a_nsec - ); // Now test the same thing, but with `fstat`. let file = openat(&dir, "file", OFlags::RDONLY, Mode::empty()).unwrap(); @@ -165,23 +138,14 @@ fn test_y2038_with_futimens() { assert_eq!(TryInto::::try_into(stat.st_mtime).unwrap(), m_sec); - #[cfg(not(target_os = "netbsd"))] assert_eq!(stat.st_mtime_nsec as u32, m_nsec); - #[cfg(target_os = "netbsd")] - assert_eq!(stat.st_mtimensec as u32, m_nsec); assert!(TryInto::::try_into(stat.st_atime).unwrap() >= a_sec); - #[cfg(not(target_os = "netbsd"))] assert!( TryInto::::try_into(stat.st_atime).unwrap() > a_sec || stat.st_atime_nsec as u32 >= a_nsec ); - #[cfg(target_os = "netbsd")] - assert!( - TryInto::::try_into(stat.st_atime).unwrap() > a_sec - || stat.st_atimensec as u32 >= a_nsec - ); } /// Like `test_y2038_with_futimens`, but using `stat` instead of `statat`. @@ -235,23 +199,14 @@ fn test_y2038_with_futimens_and_stat() { assert_eq!(TryInto::::try_into(stat.st_mtime).unwrap(), m_sec); - #[cfg(not(target_os = "netbsd"))] assert_eq!(stat.st_mtime_nsec as u32, m_nsec); - #[cfg(target_os = "netbsd")] - assert_eq!(stat.st_mtimensec as u32, m_nsec); assert!(TryInto::::try_into(stat.st_atime).unwrap() >= a_sec); - #[cfg(not(target_os = "netbsd"))] assert!( TryInto::::try_into(stat.st_atime).unwrap() > a_sec || stat.st_atime_nsec as u32 >= a_nsec ); - #[cfg(target_os = "netbsd")] - assert!( - TryInto::::try_into(stat.st_atime).unwrap() > a_sec - || stat.st_atimensec as u32 >= a_nsec - ); // Now test the same thing, but with `fstat`. let file = open(tmp.path().join("file"), OFlags::RDONLY, Mode::empty()).unwrap(); @@ -259,21 +214,12 @@ fn test_y2038_with_futimens_and_stat() { assert_eq!(TryInto::::try_into(stat.st_mtime).unwrap(), m_sec); - #[cfg(not(target_os = "netbsd"))] assert_eq!(stat.st_mtime_nsec as u32, m_nsec); - #[cfg(target_os = "netbsd")] - assert_eq!(stat.st_mtimensec as u32, m_nsec); assert!(TryInto::::try_into(stat.st_atime).unwrap() >= a_sec); - #[cfg(not(target_os = "netbsd"))] assert!( TryInto::::try_into(stat.st_atime).unwrap() > a_sec || stat.st_atime_nsec as u32 >= a_nsec ); - #[cfg(target_os = "netbsd")] - assert!( - TryInto::::try_into(stat.st_atime).unwrap() > a_sec - || stat.st_atimensec as u32 >= a_nsec - ); } From 81b9ea279b48be06eb66f302f57e110c61af7950 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Fri, 31 Jan 2025 07:50:52 -0800 Subject: [PATCH 4/8] Fix more configuratitons. --- src/fs/constants.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/fs/constants.rs b/src/fs/constants.rs index 7528932ba..49116f165 100644 --- a/src/fs/constants.rs +++ b/src/fs/constants.rs @@ -48,7 +48,8 @@ mod tests { // Check that the layout of `Stat` matches `libc::stat`. #[cfg(not(any( - all(linux_kernel, target_pointer_width = "64", libc), + linux_raw, + all(linux_kernel, libc), target_os = "hurd", target_os = "emscripten", target_os = "l4re", From 9a6cd2073fc3ecfe4d557536650c436a63f71444 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Fri, 31 Jan 2025 08:40:41 -0800 Subject: [PATCH 5/8] More stat layout checks. --- src/backend/libc/fs/syscalls.rs | 12 ++ src/backend/linux_raw/fs/types.rs | 7 +- src/fs/constants.rs | 324 +++++++++++++++++------------- 3 files changed, 194 insertions(+), 149 deletions(-) diff --git a/src/backend/libc/fs/syscalls.rs b/src/backend/libc/fs/syscalls.rs index 378d7782e..2eda05519 100644 --- a/src/backend/libc/fs/syscalls.rs +++ b/src/backend/libc/fs/syscalls.rs @@ -611,6 +611,9 @@ pub(crate) fn stat(path: &CStr) -> io::Result { ) )))] unsafe { + #[cfg(test)] + assert_eq_size!(Stat, c::stat); + let mut stat = MaybeUninit::::uninit(); ret(c::stat(c_str(path), stat.as_mut_ptr().cast()))?; let stat = stat.assume_init(); @@ -654,6 +657,9 @@ pub(crate) fn lstat(path: &CStr) -> io::Result { ) )))] unsafe { + #[cfg(test)] + assert_eq_size!(Stat, c::stat); + let mut stat = MaybeUninit::::uninit(); ret(c::lstat(c_str(path), stat.as_mut_ptr().cast()))?; let stat = stat.assume_init(); @@ -693,6 +699,9 @@ pub(crate) fn statat(dirfd: BorrowedFd<'_>, path: &CStr, flags: AtFlags) -> io:: ) )))] unsafe { + #[cfg(test)] + assert_eq_size!(Stat, c::stat); + let mut stat = MaybeUninit::::uninit(); ret(c::fstatat( borrowed_fd(dirfd), @@ -1452,6 +1461,9 @@ pub(crate) fn fstat(fd: BorrowedFd<'_>) -> io::Result { ) )))] unsafe { + #[cfg(test)] + assert_eq_size!(Stat, c::stat); + let mut stat = MaybeUninit::::uninit(); ret(c::fstat(borrowed_fd(fd), stat.as_mut_ptr().cast()))?; let stat = stat.assume_init(); diff --git a/src/backend/linux_raw/fs/types.rs b/src/backend/linux_raw/fs/types.rs index b5a418f59..cb546fb8d 100644 --- a/src/backend/linux_raw/fs/types.rs +++ b/src/backend/linux_raw/fs/types.rs @@ -584,15 +584,14 @@ pub enum FlockOperation { /// /// [`statat`]: crate::fs::statat /// [`fstat`]: crate::fs::fstat -// On 32-bit, and mips64, Linux's `struct stat64` has a 32-bit `st_mtime` and -// friends, so we use our own struct, populated from `statx` where possible, to -// avoid the y2038 bug. +// On 32-bit with `struct stat64` and mips64 with `struct stat`, Linux's +// `st_mtime` and friends are 32-bit, so we use our own struct, populated from +// `statx` where possible, to avoid the y2038 bug. #[cfg(any( target_pointer_width = "32", target_arch = "mips64", target_arch = "mips64r6" ))] -#[repr(C)] #[derive(Debug, Copy, Clone)] #[allow(missing_docs)] #[non_exhaustive] diff --git a/src/fs/constants.rs b/src/fs/constants.rs index 49116f165..f4826ba98 100644 --- a/src/fs/constants.rs +++ b/src/fs/constants.rs @@ -46,156 +46,190 @@ mod tests { assert_eq!(some_stat.st_mtime, 0_i64); assert_eq!(some_stat.st_ctime, 0_i64); - // Check that the layout of `Stat` matches `libc::stat`. + // Ensure that file offsets are 64-bit. + assert_eq!(some_stat.st_size, 0_i64); + + // Check that various fields match expected types. + assert_eq!(some_stat.st_mode, 0 as RawMode); + assert_eq!(some_stat.st_dev, 0 as Dev); + assert_eq!(some_stat.st_rdev, 0 as Dev); + + // `Stat` should match `c::stat` or `c::stat64` unless we need y2038 + // fixes and are using a different layout. #[cfg(not(any( - linux_raw, - all(linux_kernel, libc), - target_os = "hurd", - target_os = "emscripten", - target_os = "l4re", + all(libc, linux_kernel, target_pointer_width = "32"), + all( + linux_raw, + any( + target_pointer_width = "32", + target_arch = "mips64", + target_arch = "mips64r6" + ) + ) )))] { - check_renamed_type!(Stat, stat); - check_renamed_struct_field!(Stat, stat, st_dev); - check_renamed_struct_field!(Stat, stat, st_ino); - check_renamed_struct_field!(Stat, stat, st_nlink); - check_renamed_struct_field!(Stat, stat, st_mode); - check_renamed_struct_field!(Stat, stat, st_uid); - check_renamed_struct_field!(Stat, stat, st_gid); - #[cfg(all( - linux_raw, - not(any( - target_arch = "aarch64", - target_arch = "powerpc64", - target_arch = "riscv64", - target_arch = "s390x" - )) - ))] - check_renamed_struct_field!(Stat, stat, __pad0); - check_renamed_struct_field!(Stat, stat, st_rdev); - #[cfg(all(linux_raw, not(any(target_arch = "powerpc64", target_arch = "x86_64"))))] - check_renamed_struct_field!(Stat, stat, __pad1); - check_renamed_struct_field!(Stat, stat, st_size); - check_renamed_struct_field!(Stat, stat, st_blksize); - #[cfg(all( - linux_raw, - not(any( - target_arch = "powerpc64", - target_arch = "s390x", - target_arch = "x86_64" - )) - ))] - check_renamed_struct_field!(Stat, stat, __pad2); - check_renamed_struct_field!(Stat, stat, st_blocks); - check_renamed_struct_field!(Stat, stat, st_atime); - #[cfg(not(target_os = "netbsd"))] - check_renamed_struct_field!(Stat, stat, st_atime_nsec); - #[cfg(target_os = "netbsd")] - check_renamed_struct_renamed_field!(Stat, stat, st_atime_nsec, st_atimensec); - check_renamed_struct_field!(Stat, stat, st_mtime); - #[cfg(not(target_os = "netbsd"))] - check_renamed_struct_field!(Stat, stat, st_mtime_nsec); - #[cfg(target_os = "netbsd")] - check_renamed_struct_renamed_field!(Stat, stat, st_mtime_nsec, st_mtimensec); - check_renamed_struct_field!(Stat, stat, st_ctime); - #[cfg(not(target_os = "netbsd"))] - check_renamed_struct_field!(Stat, stat, st_ctime_nsec); - #[cfg(target_os = "netbsd")] - check_renamed_struct_renamed_field!(Stat, stat, st_ctime_nsec, st_ctimensec); - #[cfg(all( - linux_raw, - not(any( - target_arch = "aarch64", - target_arch = "powerpc64", - target_arch = "riscv64" - )) - ))] - check_renamed_struct_field!(Stat, stat, __unused); - #[cfg(all(linux_raw, not(any(target_arch = "s390x", target_arch = "x86_64"))))] - check_renamed_struct_field!(Stat, stat, __unused4); - #[cfg(all(linux_raw, not(any(target_arch = "s390x", target_arch = "x86_64"))))] - check_renamed_struct_field!(Stat, stat, __unused5); - #[cfg(all( - linux_raw, - not(any( - target_arch = "aarch64", - target_arch = "riscv64", - target_arch = "s390x", - target_arch = "x86_64" - )) - ))] - check_renamed_struct_field!(Stat, stat, __unused6); - } + // Check that `Stat` matches `c::stat`. + #[cfg(not(any( + all(linux_raw, target_arch = "powerpc64"), + all( + libc, + any( + all(linux_kernel, target_pointer_width = "64"), + target_os = "hurd", + target_os = "emscripten", + target_os = "l4re", + ) + ) + )))] + { + check_renamed_type!(Stat, stat); + check_renamed_struct_field!(Stat, stat, st_dev); + check_renamed_struct_field!(Stat, stat, st_ino); + check_renamed_struct_field!(Stat, stat, st_nlink); + check_renamed_struct_field!(Stat, stat, st_mode); + check_renamed_struct_field!(Stat, stat, st_uid); + check_renamed_struct_field!(Stat, stat, st_gid); + #[cfg(all( + linux_raw, + not(any( + target_arch = "aarch64", + target_arch = "powerpc64", + target_arch = "riscv64", + target_arch = "s390x" + )) + ))] + check_renamed_struct_field!(Stat, stat, __pad0); + check_renamed_struct_field!(Stat, stat, st_rdev); + #[cfg(all(linux_raw, not(any(target_arch = "powerpc64", target_arch = "x86_64"))))] + check_renamed_struct_field!(Stat, stat, __pad1); + check_renamed_struct_field!(Stat, stat, st_size); + check_renamed_struct_field!(Stat, stat, st_blksize); + #[cfg(all( + linux_raw, + not(any( + target_arch = "powerpc64", + target_arch = "s390x", + target_arch = "x86_64" + )) + ))] + check_renamed_struct_field!(Stat, stat, __pad2); + check_renamed_struct_field!(Stat, stat, st_blocks); + check_renamed_struct_field!(Stat, stat, st_atime); + #[cfg(not(target_os = "netbsd"))] + check_renamed_struct_field!(Stat, stat, st_atime_nsec); + #[cfg(target_os = "netbsd")] + check_renamed_struct_renamed_field!(Stat, stat, st_atime_nsec, st_atimensec); + check_renamed_struct_field!(Stat, stat, st_mtime); + #[cfg(not(target_os = "netbsd"))] + check_renamed_struct_field!(Stat, stat, st_mtime_nsec); + #[cfg(target_os = "netbsd")] + check_renamed_struct_renamed_field!(Stat, stat, st_mtime_nsec, st_mtimensec); + check_renamed_struct_field!(Stat, stat, st_ctime); + #[cfg(not(target_os = "netbsd"))] + check_renamed_struct_field!(Stat, stat, st_ctime_nsec); + #[cfg(target_os = "netbsd")] + check_renamed_struct_renamed_field!(Stat, stat, st_ctime_nsec, st_ctimensec); + #[cfg(all( + linux_raw, + not(any( + target_arch = "aarch64", + target_arch = "powerpc64", + target_arch = "riscv64" + )) + ))] + check_renamed_struct_field!(Stat, stat, __unused); + #[cfg(all(linux_raw, not(any(target_arch = "s390x", target_arch = "x86_64"))))] + check_renamed_struct_field!(Stat, stat, __unused4); + #[cfg(all(linux_raw, not(any(target_arch = "s390x", target_arch = "x86_64"))))] + check_renamed_struct_field!(Stat, stat, __unused5); + #[cfg(all( + linux_raw, + not(any( + target_arch = "aarch64", + target_arch = "riscv64", + target_arch = "s390x", + target_arch = "x86_64" + )) + ))] + check_renamed_struct_field!(Stat, stat, __unused6); + } - // Check that the layout of `Stat` matches `libc::stat64`. - #[cfg(any( - all(linux_kernel, target_pointer_width = "64", libc), - target_os = "hurd", - target_os = "emscripten", - target_os = "l4re", - ))] - { - check_renamed_type!(Stat, stat64); - check_renamed_struct_field!(Stat, stat64, st_dev); - check_renamed_struct_field!(Stat, stat64, st_ino); - check_renamed_struct_field!(Stat, stat64, st_nlink); - check_renamed_struct_field!(Stat, stat64, st_mode); - check_renamed_struct_field!(Stat, stat64, st_uid); - check_renamed_struct_field!(Stat, stat64, st_gid); - #[cfg(all( - linux_raw, - not(any( - target_arch = "aarch64", - target_arch = "powerpc64", - target_arch = "riscv64", - target_arch = "s390x" - )) - ))] - check_renamed_struct_field!(Stat, stat64, __pad0); - check_renamed_struct_field!(Stat, stat64, st_rdev); - #[cfg(all(linux_raw, not(any(target_arch = "powerpc64", target_arch = "x86_64"))))] - check_renamed_struct_field!(Stat, stat64, __pad1); - check_renamed_struct_field!(Stat, stat64, st_size); - check_renamed_struct_field!(Stat, stat64, st_blksize); - #[cfg(all( - linux_raw, - not(any( - target_arch = "powerpc64", - target_arch = "s390x", - target_arch = "x86_64" - )) - ))] - check_renamed_struct_field!(Stat, stat64, __pad2); - check_renamed_struct_field!(Stat, stat64, st_blocks); - check_renamed_struct_field!(Stat, stat64, st_atime); - check_renamed_struct_field!(Stat, stat64, st_atime_nsec); - check_renamed_struct_field!(Stat, stat64, st_mtime); - check_renamed_struct_field!(Stat, stat64, st_mtime_nsec); - check_renamed_struct_field!(Stat, stat64, st_ctime); - check_renamed_struct_field!(Stat, stat64, st_ctime_nsec); - #[cfg(all( - linux_raw, - not(any( - target_arch = "aarch64", - target_arch = "powerpc64", - target_arch = "riscv64" - )) - ))] - check_renamed_struct_field!(Stat, stat64, __unused); - #[cfg(all(linux_raw, not(any(target_arch = "s390x", target_arch = "x86_64"))))] - check_renamed_struct_field!(Stat, stat64, __unused4); - #[cfg(all(linux_raw, not(any(target_arch = "s390x", target_arch = "x86_64"))))] - check_renamed_struct_field!(Stat, stat64, __unused5); - #[cfg(all( - linux_raw, - not(any( - target_arch = "aarch64", - target_arch = "riscv64", - target_arch = "s390x", - target_arch = "x86_64" - )) + // Check that `Stat` matches `c::stat64`. + #[cfg(any( + all(linux_raw, target_arch = "powerpc64"), + all( + libc, + any( + all(linux_kernel, target_pointer_width = "64"), + target_os = "hurd", + target_os = "emscripten", + target_os = "l4re", + ) + ) ))] - check_renamed_struct_field!(Stat, stat64, __unused6); + { + check_renamed_type!(Stat, stat64); + check_renamed_struct_field!(Stat, stat64, st_dev); + check_renamed_struct_field!(Stat, stat64, st_ino); + check_renamed_struct_field!(Stat, stat64, st_nlink); + check_renamed_struct_field!(Stat, stat64, st_mode); + check_renamed_struct_field!(Stat, stat64, st_uid); + check_renamed_struct_field!(Stat, stat64, st_gid); + #[cfg(all( + linux_raw, + not(any( + target_arch = "aarch64", + target_arch = "powerpc64", + target_arch = "riscv64", + target_arch = "s390x" + )) + ))] + check_renamed_struct_field!(Stat, stat64, __pad0); + check_renamed_struct_field!(Stat, stat64, st_rdev); + #[cfg(all(linux_raw, not(any(target_arch = "powerpc64", target_arch = "x86_64"))))] + check_renamed_struct_field!(Stat, stat64, __pad1); + check_renamed_struct_field!(Stat, stat64, st_size); + check_renamed_struct_field!(Stat, stat64, st_blksize); + #[cfg(all( + linux_raw, + not(any( + target_arch = "powerpc64", + target_arch = "s390x", + target_arch = "x86_64" + )) + ))] + check_renamed_struct_field!(Stat, stat64, __pad2); + check_renamed_struct_field!(Stat, stat64, st_blocks); + check_renamed_struct_field!(Stat, stat64, st_atime); + check_renamed_struct_field!(Stat, stat64, st_atime_nsec); + check_renamed_struct_field!(Stat, stat64, st_mtime); + check_renamed_struct_field!(Stat, stat64, st_mtime_nsec); + check_renamed_struct_field!(Stat, stat64, st_ctime); + check_renamed_struct_field!(Stat, stat64, st_ctime_nsec); + #[cfg(all( + linux_raw, + not(any( + target_arch = "aarch64", + target_arch = "powerpc64", + target_arch = "riscv64" + )) + ))] + check_renamed_struct_field!(Stat, stat64, __unused); + #[cfg(all(linux_raw, not(any(target_arch = "s390x", target_arch = "x86_64"))))] + check_renamed_struct_field!(Stat, stat64, __unused4); + #[cfg(all(linux_raw, not(any(target_arch = "s390x", target_arch = "x86_64"))))] + check_renamed_struct_field!(Stat, stat64, __unused5); + #[cfg(all( + linux_raw, + not(any( + target_arch = "aarch64", + target_arch = "riscv64", + target_arch = "s390x", + target_arch = "x86_64" + )) + ))] + check_renamed_struct_field!(Stat, stat64, __unused6); + } } #[cfg(not(any( From fef9af3cb2cff40153fc282fcbc1c30d67123e0a Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Fri, 31 Jan 2025 09:00:00 -0800 Subject: [PATCH 6/8] powerp64 defines a stat64 but doesn't use it. --- src/backend/linux_raw/fs/types.rs | 2 +- src/fs/constants.rs | 34 +++++++++++++------------------ 2 files changed, 15 insertions(+), 21 deletions(-) diff --git a/src/backend/linux_raw/fs/types.rs b/src/backend/linux_raw/fs/types.rs index cb546fb8d..62abb8e6f 100644 --- a/src/backend/linux_raw/fs/types.rs +++ b/src/backend/linux_raw/fs/types.rs @@ -697,7 +697,7 @@ pub struct Stat { pub(crate) __unused4: ffi::c_uint, pub(crate) __unused5: ffi::c_uint, } -// This follows `stat64`. +// This follows `stat`. powerpc64 defines a `stat64` but it's not used. #[repr(C)] #[derive(Debug, Copy, Clone)] #[allow(missing_docs)] diff --git a/src/fs/constants.rs b/src/fs/constants.rs index f4826ba98..6496db79b 100644 --- a/src/fs/constants.rs +++ b/src/fs/constants.rs @@ -69,16 +69,13 @@ mod tests { )))] { // Check that `Stat` matches `c::stat`. - #[cfg(not(any( - all(linux_raw, target_arch = "powerpc64"), - all( - libc, - any( - all(linux_kernel, target_pointer_width = "64"), - target_os = "hurd", - target_os = "emscripten", - target_os = "l4re", - ) + #[cfg(not(all( + libc, + any( + all(linux_kernel, target_pointer_width = "64"), + target_os = "hurd", + target_os = "emscripten", + target_os = "l4re", ) )))] { @@ -155,16 +152,13 @@ mod tests { } // Check that `Stat` matches `c::stat64`. - #[cfg(any( - all(linux_raw, target_arch = "powerpc64"), - all( - libc, - any( - all(linux_kernel, target_pointer_width = "64"), - target_os = "hurd", - target_os = "emscripten", - target_os = "l4re", - ) + #[cfg(all( + libc, + any( + all(linux_kernel, target_pointer_width = "64"), + target_os = "hurd", + target_os = "emscripten", + target_os = "l4re", ) ))] { From ca993fb39f61a58eff51503637d04d9a8d926ed0 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Fri, 31 Jan 2025 09:09:13 -0800 Subject: [PATCH 7/8] Make `st_size` signed on s390x. --- src/backend/linux_raw/fs/types.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend/linux_raw/fs/types.rs b/src/backend/linux_raw/fs/types.rs index 62abb8e6f..629ca4a22 100644 --- a/src/backend/linux_raw/fs/types.rs +++ b/src/backend/linux_raw/fs/types.rs @@ -738,7 +738,7 @@ pub struct Stat { pub st_gid: ffi::c_uint, pub(crate) __pad1: ffi::c_uint, pub st_rdev: ffi::c_ulong, - pub st_size: ffi::c_ulong, + pub st_size: ffi::c_long, // Linux has `c_ulong` but we make it signed. pub st_atime: ffi::c_long, pub st_atime_nsec: ffi::c_ulong, pub st_mtime: ffi::c_long, From bd42b869b7ae37921328ca6f4ac3e0a2d94eaf4c Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Fri, 31 Jan 2025 09:36:46 -0800 Subject: [PATCH 8/8] Add more asserts. --- src/fs/constants.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/fs/constants.rs b/src/fs/constants.rs index 6496db79b..09dd8e46e 100644 --- a/src/fs/constants.rs +++ b/src/fs/constants.rs @@ -53,6 +53,8 @@ mod tests { assert_eq!(some_stat.st_mode, 0 as RawMode); assert_eq!(some_stat.st_dev, 0 as Dev); assert_eq!(some_stat.st_rdev, 0 as Dev); + assert_eq!(some_stat.st_uid, 0 as crate::ugid::RawUid); + assert_eq!(some_stat.st_gid, 0 as crate::ugid::RawGid); // `Stat` should match `c::stat` or `c::stat64` unless we need y2038 // fixes and are using a different layout.