From 1e6b444df7f1242ad9b0cc58620f101f34514734 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Tue, 7 Oct 2025 13:07:23 -0700 Subject: [PATCH 1/2] library: fs: Factor out a `file_time_to_timespec` function in preparation for reusing it --- library/std/src/sys/fs/unix.rs | 49 ++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs index 33a1e7ff5e40e..bcd5ea6494447 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -1604,24 +1604,6 @@ impl File { } pub fn set_times(&self, times: FileTimes) -> io::Result<()> { - #[cfg(not(any( - target_os = "redox", - target_os = "espidf", - target_os = "horizon", - target_os = "nuttx", - )))] - let to_timespec = |time: Option| match time { - Some(time) if let Some(ts) = time.t.to_timespec() => Ok(ts), - Some(time) if time > crate::sys::time::UNIX_EPOCH => Err(io::const_error!( - io::ErrorKind::InvalidInput, - "timestamp is too large to set as a file time", - )), - Some(_) => Err(io::const_error!( - io::ErrorKind::InvalidInput, - "timestamp is too small to set as a file time", - )), - None => Ok(libc::timespec { tv_sec: 0, tv_nsec: libc::UTIME_OMIT as _ }), - }; cfg_select! { any(target_os = "redox", target_os = "espidf", target_os = "horizon", target_os = "nuttx") => { // Redox doesn't appear to support `UTIME_OMIT`. @@ -1639,17 +1621,17 @@ impl File { let mut attrlist: libc::attrlist = unsafe { mem::zeroed() }; attrlist.bitmapcount = libc::ATTR_BIT_MAP_COUNT; if times.created.is_some() { - buf[num_times].write(to_timespec(times.created)?); + buf[num_times].write(file_time_to_timespec(times.created)?); num_times += 1; attrlist.commonattr |= libc::ATTR_CMN_CRTIME; } if times.modified.is_some() { - buf[num_times].write(to_timespec(times.modified)?); + buf[num_times].write(file_time_to_timespec(times.modified)?); num_times += 1; attrlist.commonattr |= libc::ATTR_CMN_MODTIME; } if times.accessed.is_some() { - buf[num_times].write(to_timespec(times.accessed)?); + buf[num_times].write(file_time_to_timespec(times.accessed)?); num_times += 1; attrlist.commonattr |= libc::ATTR_CMN_ACCTIME; } @@ -1663,7 +1645,7 @@ impl File { Ok(()) } target_os = "android" => { - let times = [to_timespec(times.accessed)?, to_timespec(times.modified)?]; + let times = [file_time_to_timespec(times.accessed)?, file_time_to_timespec(times.modified)?]; // futimens requires Android API level 19 cvt(unsafe { weak!( @@ -1697,7 +1679,7 @@ impl File { return Ok(()); } } - let times = [to_timespec(times.accessed)?, to_timespec(times.modified)?]; + let times = [file_time_to_timespec(times.accessed)?, file_time_to_timespec(times.modified)?]; cvt(unsafe { libc::futimens(self.as_raw_fd(), times.as_ptr()) })?; Ok(()) } @@ -1705,6 +1687,27 @@ impl File { } } +#[cfg(not(any( + target_os = "redox", + target_os = "espidf", + target_os = "horizon", + target_os = "nuttx", +)))] +fn file_time_to_timespec(time: Option) -> io::Result { + match time { + Some(time) if let Some(ts) = time.t.to_timespec() => Ok(ts), + Some(time) if time > crate::sys::time::UNIX_EPOCH => Err(io::const_error!( + io::ErrorKind::InvalidInput, + "timestamp is too large to set as a file time", + )), + Some(_) => Err(io::const_error!( + io::ErrorKind::InvalidInput, + "timestamp is too small to set as a file time", + )), + None => Ok(libc::timespec { tv_sec: 0, tv_nsec: libc::UTIME_OMIT as _ }), + } +}; + impl DirBuilder { pub fn new() -> DirBuilder { DirBuilder { mode: 0o777 } From 3bfda044d45009d8c2356e45e3771b751fd0ddab Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Tue, 7 Oct 2025 13:18:38 -0700 Subject: [PATCH 2/2] library: fs: Factor out the Apple file time to attrlist code for reuse --- library/std/src/sys/fs/unix.rs | 75 +++++++++++++++++++++++----------- 1 file changed, 52 insertions(+), 23 deletions(-) diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs index bcd5ea6494447..e45762131782f 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -1616,30 +1616,12 @@ impl File { )) } target_vendor = "apple" => { - let mut buf = [mem::MaybeUninit::::uninit(); 3]; - let mut num_times = 0; - let mut attrlist: libc::attrlist = unsafe { mem::zeroed() }; - attrlist.bitmapcount = libc::ATTR_BIT_MAP_COUNT; - if times.created.is_some() { - buf[num_times].write(file_time_to_timespec(times.created)?); - num_times += 1; - attrlist.commonattr |= libc::ATTR_CMN_CRTIME; - } - if times.modified.is_some() { - buf[num_times].write(file_time_to_timespec(times.modified)?); - num_times += 1; - attrlist.commonattr |= libc::ATTR_CMN_MODTIME; - } - if times.accessed.is_some() { - buf[num_times].write(file_time_to_timespec(times.accessed)?); - num_times += 1; - attrlist.commonattr |= libc::ATTR_CMN_ACCTIME; - } + let ta = TimesAttrlist::from_times(×)?; cvt(unsafe { libc::fsetattrlist( self.as_raw_fd(), - (&raw const attrlist).cast::().cast_mut(), - buf.as_ptr().cast::().cast_mut(), - num_times * size_of::(), + ta.attrlist(), + ta.times_buf(), + ta.times_buf_size(), 0 ) })?; Ok(()) @@ -1706,7 +1688,54 @@ fn file_time_to_timespec(time: Option) -> io::Result )), None => Ok(libc::timespec { tv_sec: 0, tv_nsec: libc::UTIME_OMIT as _ }), } -}; +} + +#[cfg(target_vendor = "apple")] +struct TimesAttrlist { + buf: [mem::MaybeUninit; 3], + attrlist: libc::attrlist, + num_times: usize, +} + +#[cfg(target_vendor = "apple")] +impl TimesAttrlist { + fn from_times(times: &FileTimes) -> io::Result { + let mut this = Self { + buf: [mem::MaybeUninit::::uninit(); 3], + attrlist: unsafe { mem::zeroed() }, + num_times: 0, + }; + this.attrlist.bitmapcount = libc::ATTR_BIT_MAP_COUNT; + if times.created.is_some() { + this.buf[this.num_times].write(file_time_to_timespec(times.created)?); + this.num_times += 1; + this.attrlist.commonattr |= libc::ATTR_CMN_CRTIME; + } + if times.modified.is_some() { + this.buf[this.num_times].write(file_time_to_timespec(times.modified)?); + this.num_times += 1; + this.attrlist.commonattr |= libc::ATTR_CMN_MODTIME; + } + if times.accessed.is_some() { + this.buf[this.num_times].write(file_time_to_timespec(times.accessed)?); + this.num_times += 1; + this.attrlist.commonattr |= libc::ATTR_CMN_ACCTIME; + } + Ok(this) + } + + fn attrlist(&self) -> *mut libc::c_void { + (&raw const self.attrlist).cast::().cast_mut() + } + + fn times_buf(&self) -> *mut libc::c_void { + self.buf.as_ptr().cast::().cast_mut() + } + + fn times_buf_size(&self) -> usize { + self.num_times * size_of::() + } +} impl DirBuilder { pub fn new() -> DirBuilder {