Skip to content

Commit 912db92

Browse files
authored
Work around a negative timestamps bug on macOS. (#1305)
* 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]: rust-lang/rust#108277 (comment) * Make st_mtime etc. signed. * Define our own `Stat` on NetBSD, fix types for Apple. * More stat layout checks. * powerp64 defines a stat64 but doesn't use it. * Make `st_size` signed on s390x.
1 parent 40733be commit 912db92

File tree

12 files changed

+393
-176
lines changed

12 files changed

+393
-176
lines changed

src/backend/libc/fs/syscalls.rs

Lines changed: 47 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -611,9 +611,15 @@ pub(crate) fn stat(path: &CStr) -> io::Result<Stat> {
611611
)
612612
)))]
613613
unsafe {
614+
#[cfg(test)]
615+
assert_eq_size!(Stat, c::stat);
616+
614617
let mut stat = MaybeUninit::<Stat>::uninit();
615-
ret(c::stat(c_str(path), stat.as_mut_ptr()))?;
616-
Ok(stat.assume_init())
618+
ret(c::stat(c_str(path), stat.as_mut_ptr().cast()))?;
619+
let stat = stat.assume_init();
620+
#[cfg(apple)]
621+
let stat = fix_negative_stat_nsecs(stat);
622+
Ok(stat)
617623
}
618624
}
619625

@@ -651,9 +657,15 @@ pub(crate) fn lstat(path: &CStr) -> io::Result<Stat> {
651657
)
652658
)))]
653659
unsafe {
660+
#[cfg(test)]
661+
assert_eq_size!(Stat, c::stat);
662+
654663
let mut stat = MaybeUninit::<Stat>::uninit();
655-
ret(c::lstat(c_str(path), stat.as_mut_ptr()))?;
656-
Ok(stat.assume_init())
664+
ret(c::lstat(c_str(path), stat.as_mut_ptr().cast()))?;
665+
let stat = stat.assume_init();
666+
#[cfg(apple)]
667+
let stat = fix_negative_stat_nsecs(stat);
668+
Ok(stat)
657669
}
658670
}
659671

@@ -687,14 +699,20 @@ pub(crate) fn statat(dirfd: BorrowedFd<'_>, path: &CStr, flags: AtFlags) -> io::
687699
)
688700
)))]
689701
unsafe {
702+
#[cfg(test)]
703+
assert_eq_size!(Stat, c::stat);
704+
690705
let mut stat = MaybeUninit::<Stat>::uninit();
691706
ret(c::fstatat(
692707
borrowed_fd(dirfd),
693708
c_str(path),
694-
stat.as_mut_ptr(),
709+
stat.as_mut_ptr().cast(),
695710
bitflags_bits!(flags),
696711
))?;
697-
Ok(stat.assume_init())
712+
let stat = stat.assume_init();
713+
#[cfg(apple)]
714+
let stat = fix_negative_stat_nsecs(stat);
715+
Ok(stat)
698716
}
699717
}
700718

@@ -1443,9 +1461,15 @@ pub(crate) fn fstat(fd: BorrowedFd<'_>) -> io::Result<Stat> {
14431461
)
14441462
)))]
14451463
unsafe {
1464+
#[cfg(test)]
1465+
assert_eq_size!(Stat, c::stat);
1466+
14461467
let mut stat = MaybeUninit::<Stat>::uninit();
1447-
ret(c::fstat(borrowed_fd(fd), stat.as_mut_ptr()))?;
1448-
Ok(stat.assume_init())
1468+
ret(c::fstat(borrowed_fd(fd), stat.as_mut_ptr().cast()))?;
1469+
let stat = stat.assume_init();
1470+
#[cfg(apple)]
1471+
let stat = fix_negative_stat_nsecs(stat);
1472+
Ok(stat)
14491473
}
14501474
}
14511475

@@ -1850,17 +1874,17 @@ fn stat64_to_stat(s64: c::stat64) -> io::Result<Stat> {
18501874
st_size: s64.st_size.try_into().map_err(|_| io::Errno::OVERFLOW)?,
18511875
st_blksize: s64.st_blksize.try_into().map_err(|_| io::Errno::OVERFLOW)?,
18521876
st_blocks: s64.st_blocks.try_into().map_err(|_| io::Errno::OVERFLOW)?,
1853-
st_atime: bitcast!(i64::from(s64.st_atime)),
1877+
st_atime: i64::from(s64.st_atime),
18541878
st_atime_nsec: s64
18551879
.st_atime_nsec
18561880
.try_into()
18571881
.map_err(|_| io::Errno::OVERFLOW)?,
1858-
st_mtime: bitcast!(i64::from(s64.st_mtime)),
1882+
st_mtime: i64::from(s64.st_mtime),
18591883
st_mtime_nsec: s64
18601884
.st_mtime_nsec
18611885
.try_into()
18621886
.map_err(|_| io::Errno::OVERFLOW)?,
1863-
st_ctime: bitcast!(i64::from(s64.st_ctime)),
1887+
st_ctime: i64::from(s64.st_ctime),
18641888
st_ctime_nsec: s64
18651889
.st_ctime_nsec
18661890
.try_into()
@@ -2553,6 +2577,18 @@ pub(crate) fn fremovexattr(fd: BorrowedFd<'_>, name: &CStr) -> io::Result<()> {
25532577
}
25542578
}
25552579

2580+
/// See [`crate::timespec::fix_negative_nsec`] for details.
2581+
#[cfg(apple)]
2582+
fn fix_negative_stat_nsecs(mut stat: Stat) -> Stat {
2583+
stat.st_atime_nsec =
2584+
crate::timespec::fix_negative_nsecs(&mut stat.st_atime, stat.st_atime_nsec as _) as _;
2585+
stat.st_mtime_nsec =
2586+
crate::timespec::fix_negative_nsecs(&mut stat.st_mtime, stat.st_mtime_nsec as _) as _;
2587+
stat.st_ctime_nsec =
2588+
crate::timespec::fix_negative_nsecs(&mut stat.st_ctime, stat.st_ctime_nsec as _) as _;
2589+
stat
2590+
}
2591+
25562592
#[test]
25572593
fn test_sizes() {
25582594
#[cfg(linux_kernel)]

src/backend/libc/fs/types.rs

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -877,7 +877,7 @@ pub enum FlockOperation {
877877
///
878878
/// [`statat`]: crate::fs::statat
879879
/// [`fstat`]: crate::fs::fstat
880-
#[cfg(not(any(linux_like, target_os = "hurd")))]
880+
#[cfg(not(any(linux_like, target_os = "hurd", target_os = "netbsd")))]
881881
pub type Stat = c::stat;
882882

883883
/// `struct stat` for use with [`statat`] and [`fstat`].
@@ -921,6 +921,40 @@ pub struct Stat {
921921
pub st_ino: u64,
922922
}
923923

924+
/// `struct stat` for use with [`statat`] and [`fstat`].
925+
///
926+
/// [`statat`]: crate::fs::statat
927+
/// [`fstat`]: crate::fs::fstat
928+
// NetBSD's `st_mtime_nsec` is named `st_mtimensec` so we declare our own
929+
// `Stat` so that we can be consistent with other platforms.
930+
#[cfg(target_os = "netbsd")]
931+
#[derive(Debug, Copy, Clone)]
932+
#[allow(missing_docs)]
933+
#[repr(C)]
934+
pub struct Stat {
935+
pub st_dev: c::dev_t,
936+
pub st_mode: c::mode_t,
937+
pub st_ino: c::ino_t,
938+
pub st_nlink: c::nlink_t,
939+
pub st_uid: c::uid_t,
940+
pub st_gid: c::gid_t,
941+
pub st_rdev: c::dev_t,
942+
pub st_atime: c::time_t,
943+
pub st_atime_nsec: c::c_long,
944+
pub st_mtime: c::time_t,
945+
pub st_mtime_nsec: c::c_long,
946+
pub st_ctime: c::time_t,
947+
pub st_ctime_nsec: c::c_long,
948+
pub st_birthtime: c::time_t,
949+
pub st_birthtime_nsec: c::c_long,
950+
pub st_size: c::off_t,
951+
pub st_blocks: c::blkcnt_t,
952+
pub st_blksize: c::blksize_t,
953+
pub st_flags: u32,
954+
pub st_gen: u32,
955+
pub st_spare: [u32; 2],
956+
}
957+
924958
/// `struct statfs` for use with [`statfs`] and [`fstatfs`].
925959
///
926960
/// [`statfs`]: crate::fs::statfs

src/backend/libc/time/syscalls.rs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,10 @@ pub(crate) fn clock_gettime(id: ClockId) -> Timespec {
130130
as_libc_timespec_mut_ptr(&mut timespec),
131131
))
132132
.unwrap();
133-
timespec.assume_init()
133+
let timespec = timespec.assume_init();
134+
#[cfg(apple)]
135+
let timespec = fix_negative_timespec_nsecs(timespec);
136+
timespec
134137
}
135138
}
136139

@@ -220,8 +223,10 @@ pub(crate) fn clock_gettime_dynamic(id: DynamicClockId<'_>) -> io::Result<Timesp
220223
id as c::clockid_t,
221224
as_libc_timespec_mut_ptr(&mut timespec),
222225
))?;
223-
224-
Ok(timespec.assume_init())
226+
let timespec = timespec.assume_init();
227+
#[cfg(apple)]
228+
let timespec = fix_negative_timespec_nsecs(timespec);
229+
Ok(timespec)
225230
}
226231
}
227232

@@ -476,3 +481,10 @@ fn timerfd_gettime_old(fd: BorrowedFd<'_>) -> io::Result<Itimerspec> {
476481
},
477482
})
478483
}
484+
485+
/// See [`crate::timespec::fix_negative_nsecs`] for details.
486+
#[cfg(apple)]
487+
fn fix_negative_timespec_nsecs(mut ts: Timespec) -> Timespec {
488+
ts.tv_nsec = crate::timespec::fix_negative_nsecs(&mut ts.tv_sec, ts.tv_nsec as _) as _;
489+
ts
490+
}

src/backend/linux_raw/fs/syscalls.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -730,11 +730,11 @@ fn statx_to_stat(x: crate::fs::Statx) -> io::Result<Stat> {
730730
st_size: x.stx_size.try_into().map_err(|_| io::Errno::OVERFLOW)?,
731731
st_blksize: x.stx_blksize.into(),
732732
st_blocks: x.stx_blocks.into(),
733-
st_atime: bitcast!(i64::from(x.stx_atime.tv_sec)),
733+
st_atime: i64::from(x.stx_atime.tv_sec),
734734
st_atime_nsec: x.stx_atime.tv_nsec.into(),
735-
st_mtime: bitcast!(i64::from(x.stx_mtime.tv_sec)),
735+
st_mtime: i64::from(x.stx_mtime.tv_sec),
736736
st_mtime_nsec: x.stx_mtime.tv_nsec.into(),
737-
st_ctime: bitcast!(i64::from(x.stx_ctime.tv_sec)),
737+
st_ctime: i64::from(x.stx_ctime.tv_sec),
738738
st_ctime_nsec: x.stx_ctime.tv_nsec.into(),
739739
st_ino: x.stx_ino.into(),
740740
})
@@ -754,17 +754,17 @@ fn stat_to_stat(s64: linux_raw_sys::general::stat64) -> io::Result<Stat> {
754754
st_size: s64.st_size.try_into().map_err(|_| io::Errno::OVERFLOW)?,
755755
st_blksize: s64.st_blksize.try_into().map_err(|_| io::Errno::OVERFLOW)?,
756756
st_blocks: s64.st_blocks.try_into().map_err(|_| io::Errno::OVERFLOW)?,
757-
st_atime: bitcast!(i64::from(s64.st_atime)),
757+
st_atime: i64::from(s64.st_atime),
758758
st_atime_nsec: s64
759759
.st_atime_nsec
760760
.try_into()
761761
.map_err(|_| io::Errno::OVERFLOW)?,
762-
st_mtime: bitcast!(i64::from(s64.st_mtime)),
762+
st_mtime: i64::from(s64.st_mtime),
763763
st_mtime_nsec: s64
764764
.st_mtime_nsec
765765
.try_into()
766766
.map_err(|_| io::Errno::OVERFLOW)?,
767-
st_ctime: bitcast!(i64::from(s64.st_ctime)),
767+
st_ctime: i64::from(s64.st_ctime),
768768
st_ctime_nsec: s64
769769
.st_ctime_nsec
770770
.try_into()
@@ -786,17 +786,17 @@ fn stat_to_stat(s: linux_raw_sys::general::stat) -> io::Result<Stat> {
786786
st_size: s.st_size.try_into().map_err(|_| io::Errno::OVERFLOW)?,
787787
st_blksize: s.st_blksize.try_into().map_err(|_| io::Errno::OVERFLOW)?,
788788
st_blocks: s.st_blocks.try_into().map_err(|_| io::Errno::OVERFLOW)?,
789-
st_atime: bitcast!(i64::from(s.st_atime)),
789+
st_atime: i64::from(s.st_atime),
790790
st_atime_nsec: s
791791
.st_atime_nsec
792792
.try_into()
793793
.map_err(|_| io::Errno::OVERFLOW)?,
794-
st_mtime: bitcast!(i64::from(s.st_mtime)),
794+
st_mtime: i64::from(s.st_mtime),
795795
st_mtime_nsec: s
796796
.st_mtime_nsec
797797
.try_into()
798798
.map_err(|_| io::Errno::OVERFLOW)?,
799-
st_ctime: bitcast!(i64::from(s.st_ctime)),
799+
st_ctime: i64::from(s.st_ctime),
800800
st_ctime_nsec: s
801801
.st_ctime_nsec
802802
.try_into()

src/backend/linux_raw/fs/types.rs

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -584,15 +584,14 @@ pub enum FlockOperation {
584584
///
585585
/// [`statat`]: crate::fs::statat
586586
/// [`fstat`]: crate::fs::fstat
587-
// On 32-bit, and mips64, Linux's `struct stat64` has a 32-bit `st_mtime` and
588-
// friends, so we use our own struct, populated from `statx` where possible, to
589-
// avoid the y2038 bug.
587+
// On 32-bit with `struct stat64` and mips64 with `struct stat`, Linux's
588+
// `st_mtime` and friends are 32-bit, so we use our own struct, populated from
589+
// `statx` where possible, to avoid the y2038 bug.
590590
#[cfg(any(
591591
target_pointer_width = "32",
592592
target_arch = "mips64",
593593
target_arch = "mips64r6"
594594
))]
595-
#[repr(C)]
596595
#[derive(Debug, Copy, Clone)]
597596
#[allow(missing_docs)]
598597
#[non_exhaustive]
@@ -636,11 +635,11 @@ pub struct Stat {
636635
pub st_size: ffi::c_long,
637636
pub st_blksize: ffi::c_long,
638637
pub st_blocks: ffi::c_long,
639-
pub st_atime: ffi::c_ulong,
638+
pub st_atime: ffi::c_long,
640639
pub st_atime_nsec: ffi::c_ulong,
641-
pub st_mtime: ffi::c_ulong,
640+
pub st_mtime: ffi::c_long,
642641
pub st_mtime_nsec: ffi::c_ulong,
643-
pub st_ctime: ffi::c_ulong,
642+
pub st_ctime: ffi::c_long,
644643
pub st_ctime_nsec: ffi::c_ulong,
645644
pub(crate) __unused: [ffi::c_long; 3],
646645
}
@@ -698,7 +697,7 @@ pub struct Stat {
698697
pub(crate) __unused4: ffi::c_uint,
699698
pub(crate) __unused5: ffi::c_uint,
700699
}
701-
// This follows `stat64`.
700+
// This follows `stat`. powerpc64 defines a `stat64` but it's not used.
702701
#[repr(C)]
703702
#[derive(Debug, Copy, Clone)]
704703
#[allow(missing_docs)]
@@ -715,11 +714,11 @@ pub struct Stat {
715714
pub st_size: ffi::c_long,
716715
pub st_blksize: ffi::c_ulong,
717716
pub st_blocks: ffi::c_ulong,
718-
pub st_atime: ffi::c_ulong,
717+
pub st_atime: ffi::c_long,
719718
pub st_atime_nsec: ffi::c_ulong,
720-
pub st_mtime: ffi::c_ulong,
719+
pub st_mtime: ffi::c_long,
721720
pub st_mtime_nsec: ffi::c_ulong,
722-
pub st_ctime: ffi::c_ulong,
721+
pub st_ctime: ffi::c_long,
723722
pub st_ctime_nsec: ffi::c_ulong,
724723
pub(crate) __unused4: ffi::c_ulong,
725724
pub(crate) __unused5: ffi::c_ulong,
@@ -739,12 +738,12 @@ pub struct Stat {
739738
pub st_gid: ffi::c_uint,
740739
pub(crate) __pad1: ffi::c_uint,
741740
pub st_rdev: ffi::c_ulong,
742-
pub st_size: ffi::c_ulong,
743-
pub st_atime: ffi::c_ulong,
741+
pub st_size: ffi::c_long, // Linux has `c_ulong` but we make it signed.
742+
pub st_atime: ffi::c_long,
744743
pub st_atime_nsec: ffi::c_ulong,
745-
pub st_mtime: ffi::c_ulong,
744+
pub st_mtime: ffi::c_long,
746745
pub st_mtime_nsec: ffi::c_ulong,
747-
pub st_ctime: ffi::c_ulong,
746+
pub st_ctime: ffi::c_long,
748747
pub st_ctime_nsec: ffi::c_ulong,
749748
pub st_blksize: ffi::c_ulong,
750749
pub st_blocks: ffi::c_long,

0 commit comments

Comments
 (0)