From 61b45c670bc6a9c0d7d35769ed58899f347a6371 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sat, 18 Jun 2022 20:00:21 -0700 Subject: [PATCH 1/7] Support setting file accessed/modified timestamps Add `struct FileTimes` to contain the relevant file timestamps, since most platforms require setting all of them at once. (This also allows for future platform-specific extensions such as setting creation time.) Add `File::set_file_time` to set the timestamps for a `File`. Implement the `sys` backends for UNIX, macOS (which needs to fall back to `futimes` before macOS 10.13 because it lacks `futimens`), Windows, and WASI. --- library/std/src/fs.rs | 72 +++++++++++++++++++++++++++ library/std/src/sys/unix/fs.rs | 64 ++++++++++++++++++++++++ library/std/src/sys/unsupported/fs.rs | 12 +++++ library/std/src/sys/wasi/fs.rs | 25 ++++++++++ library/std/src/sys/wasi/time.rs | 4 ++ library/std/src/sys/windows/c.rs | 8 ++- library/std/src/sys/windows/fs.rs | 24 +++++++++ library/std/src/sys/windows/time.rs | 7 +++ library/std/src/time.rs | 8 ++- 9 files changed, 222 insertions(+), 2 deletions(-) diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 55bd2c59406df..0500e826768af 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -184,6 +184,11 @@ pub struct DirEntry(fs_imp::DirEntry); #[stable(feature = "rust1", since = "1.0.0")] pub struct OpenOptions(fs_imp::OpenOptions); +/// Representation of the various timestamps on a file. +#[derive(Copy, Clone, Debug, Default)] +#[unstable(feature = "file_set_times", issue = "98245")] +pub struct FileTimes(fs_imp::FileTimes); + /// Representation of the various permissions on a file. /// /// This module only currently provides one bit of information, @@ -590,6 +595,49 @@ impl File { pub fn set_permissions(&self, perm: Permissions) -> io::Result<()> { self.inner.set_permissions(perm.0) } + + /// Changes the timestamps of the underlying file. + /// + /// # Platform-specific behavior + /// + /// This function currently corresponds to the `futimens` function on Unix (falling back to + /// `futimes` on macOS before 10.13) and the `SetFileTime` function on Windows. Note that this + /// [may change in the future][changes]. + /// + /// [changes]: io#platform-specific-behavior + /// + /// # Errors + /// + /// This function will return an error if the user lacks permission to change timestamps on the + /// underlying file. It may also return an error in other os-specific unspecified cases. + /// + /// This function may return an error if the operating system lacks support to change one or + /// more of the timestamps set in the `FileTimes` structure. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(file_set_times)] + /// + /// fn main() -> std::io::Result<()> { + /// use std::fs::{self, File, FileTimes}; + /// + /// let src = fs::metadata("src")?; + /// let dest = File::options().write(true).open("dest")?; + /// let times = FileTimes::new() + /// .set_accessed(src.accessed()?) + /// .set_modified(src.modified()?); + /// dest.set_times(times)?; + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "file_set_times", issue = "98245")] + #[doc(alias = "futimens")] + #[doc(alias = "futimes")] + #[doc(alias = "SetFileTime")] + pub fn set_times(&self, times: FileTimes) -> io::Result<()> { + self.inner.set_times(times.0) + } } // In addition to the `impl`s here, `File` also has `impl`s for @@ -1246,6 +1294,30 @@ impl FromInner for Metadata { } } +impl FileTimes { + /// Create a new `FileTimes` with no times set. + /// + /// Using the resulting `FileTimes` in [`File::set_times`] will not modify any timestamps. + #[unstable(feature = "file_set_times", issue = "98245")] + pub fn new() -> Self { + Self::default() + } + + /// Set the last access time of a file. + #[unstable(feature = "file_set_times", issue = "98245")] + pub fn set_accessed(mut self, t: SystemTime) -> Self { + self.0.set_accessed(t.into_inner()); + self + } + + /// Set the last modified time of a file. + #[unstable(feature = "file_set_times", issue = "98245")] + pub fn set_modified(mut self, t: SystemTime) -> Self { + self.0.set_modified(t.into_inner()); + self + } +} + impl Permissions { /// Returns `true` if these permissions describe a readonly (unwritable) file. /// diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs index 8b0bbd6a55c6b..db81b26e7e7a8 100644 --- a/library/std/src/sys/unix/fs.rs +++ b/library/std/src/sys/unix/fs.rs @@ -311,6 +311,9 @@ pub struct FilePermissions { mode: mode_t, } +#[derive(Copy, Clone)] +pub struct FileTimes([libc::timespec; 2]); + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub struct FileType { mode: mode_t, @@ -503,6 +506,43 @@ impl FilePermissions { } } +impl FileTimes { + pub fn set_accessed(&mut self, t: SystemTime) { + self.0[0] = t.t.to_timespec().expect("Invalid system time"); + } + + pub fn set_modified(&mut self, t: SystemTime) { + self.0[1] = t.t.to_timespec().expect("Invalid system time"); + } +} + +struct TimespecDebugAdapter<'a>(&'a libc::timespec); + +impl fmt::Debug for TimespecDebugAdapter<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("timespec") + .field("tv_sec", &self.0.tv_sec) + .field("tv_nsec", &self.0.tv_nsec) + .finish() + } +} + +impl fmt::Debug for FileTimes { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("FileTimes") + .field("accessed", &TimespecDebugAdapter(&self.0[0])) + .field("modified", &TimespecDebugAdapter(&self.0[1])) + .finish() + } +} + +impl Default for FileTimes { + fn default() -> Self { + let omit = libc::timespec { tv_sec: 0, tv_nsec: libc::UTIME_OMIT as _ }; + Self([omit; 2]) + } +} + impl FileType { pub fn is_dir(&self) -> bool { self.is(libc::S_IFDIR) @@ -1021,6 +1061,30 @@ impl File { cvt_r(|| unsafe { libc::fchmod(self.as_raw_fd(), perm.mode) })?; Ok(()) } + + pub fn set_times(&self, times: FileTimes) -> io::Result<()> { + cfg_if::cfg_if! { + // futimens requires macOS 10.13 + if #[cfg(target_os = "macos")] { + fn ts_to_tv(ts: &libc::timespec) -> libc::timeval { + libc::timeval { tv_sec: ts.tv_sec, tv_usec: (ts.tv_nsec / 1000) as _ } + } + cvt(unsafe { + weak!(fn futimens(c_int, *const libc::timespec) -> c_int); + futimens.get() + .map(|futimens| futimens(self.as_raw_fd(), times.0.as_ptr())) + .unwrap_or_else(|| { + let timevals = [ts_to_tv(×.0[0]), ts_to_tv(×.0[1])]; + libc::futimes(self.as_raw_fd(), timevals.as_ptr()) + }) + })?; + } else { + cvt(unsafe { libc::futimens(self.as_raw_fd(), times.0.as_ptr()) })?; + } + } + + Ok(()) + } } impl DirBuilder { diff --git a/library/std/src/sys/unsupported/fs.rs b/library/std/src/sys/unsupported/fs.rs index d1d2847cd33be..0e1a6257ed763 100644 --- a/library/std/src/sys/unsupported/fs.rs +++ b/library/std/src/sys/unsupported/fs.rs @@ -17,6 +17,9 @@ pub struct DirEntry(!); #[derive(Clone, Debug)] pub struct OpenOptions {} +#[derive(Copy, Clone, Debug, Default)] +pub struct FileTimes {} + pub struct FilePermissions(!); pub struct FileType(!); @@ -86,6 +89,11 @@ impl fmt::Debug for FilePermissions { } } +impl FileTimes { + pub fn set_accessed(&mut self, _t: SystemTime) {} + pub fn set_modified(&mut self, _t: SystemTime) {} +} + impl FileType { pub fn is_dir(&self) -> bool { self.0 @@ -237,6 +245,10 @@ impl File { pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> { self.0 } + + pub fn set_times(&self, _times: FileTimes) -> io::Result<()> { + self.0 + } } impl DirBuilder { diff --git a/library/std/src/sys/wasi/fs.rs b/library/std/src/sys/wasi/fs.rs index cd6815bfc2136..6614ae397b57f 100644 --- a/library/std/src/sys/wasi/fs.rs +++ b/library/std/src/sys/wasi/fs.rs @@ -63,6 +63,12 @@ pub struct FilePermissions { readonly: bool, } +#[derive(Copy, Clone, Debug, Default)] +pub struct FileTimes { + accessed: Option, + modified: Option, +} + #[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)] pub struct FileType { bits: wasi::Filetype, @@ -112,6 +118,16 @@ impl FilePermissions { } } +impl FileTimes { + pub fn set_accessed(&mut self, t: SystemTime) { + self.accessed = Some(t.to_wasi_timestamp_or_panic()); + } + + pub fn set_modified(&mut self, t: SystemTime) { + self.modified = Some(t.to_wasi_timestamp_or_panic()); + } +} + impl FileType { pub fn is_dir(&self) -> bool { self.bits == wasi::FILETYPE_DIRECTORY @@ -459,6 +475,15 @@ impl File { unsupported() } + pub fn set_times(&self, times: FileTimes) -> io::Result<()> { + self.fd.filestat_set_times( + times.accessed.unwrap_or(0), + times.modified.unwrap_or(0), + times.accessed.map_or(0, |_| wasi::FSTFLAGS_ATIM) + | times.modified.map_or(0, |_| wasi::FSTFLAGS_MTIM), + ) + } + pub fn read_link(&self, file: &Path) -> io::Result { read_link(&self.fd, file) } diff --git a/library/std/src/sys/wasi/time.rs b/library/std/src/sys/wasi/time.rs index 088585654b948..3d326e49106ca 100644 --- a/library/std/src/sys/wasi/time.rs +++ b/library/std/src/sys/wasi/time.rs @@ -47,6 +47,10 @@ impl SystemTime { SystemTime(Duration::from_nanos(ts)) } + pub fn to_wasi_timestamp_or_panic(&self) -> wasi::Timestamp { + self.0.as_nanos().try_into().expect("time does not fit in WASI timestamp") + } + pub fn sub_time(&self, other: &SystemTime) -> Result { self.0.checked_sub(other.0).ok_or_else(|| other.0 - self.0) } diff --git a/library/std/src/sys/windows/c.rs b/library/std/src/sys/windows/c.rs index 5469487df1eed..abb471213d251 100644 --- a/library/std/src/sys/windows/c.rs +++ b/library/std/src/sys/windows/c.rs @@ -607,7 +607,7 @@ pub struct SOCKADDR { } #[repr(C)] -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Debug, Default)] pub struct FILETIME { pub dwLowDateTime: DWORD, pub dwHighDateTime: DWORD, @@ -875,6 +875,12 @@ extern "system" { pub fn GetSystemDirectoryW(lpBuffer: LPWSTR, uSize: UINT) -> UINT; pub fn RemoveDirectoryW(lpPathName: LPCWSTR) -> BOOL; pub fn SetFileAttributesW(lpFileName: LPCWSTR, dwFileAttributes: DWORD) -> BOOL; + pub fn SetFileTime( + hFile: BorrowedHandle<'_>, + lpCreationTime: Option<&FILETIME>, + lpLastAccessTime: Option<&FILETIME>, + lpLastWriteTime: Option<&FILETIME>, + ) -> BOOL; pub fn SetLastError(dwErrCode: DWORD); pub fn GetCommandLineW() -> LPWSTR; pub fn GetTempPathW(nBufferLength: DWORD, lpBuffer: LPCWSTR) -> DWORD; diff --git a/library/std/src/sys/windows/fs.rs b/library/std/src/sys/windows/fs.rs index 157d8a044d3cd..e3809f3579dfd 100644 --- a/library/std/src/sys/windows/fs.rs +++ b/library/std/src/sys/windows/fs.rs @@ -82,6 +82,12 @@ pub struct FilePermissions { attrs: c::DWORD, } +#[derive(Copy, Clone, Debug, Default)] +pub struct FileTimes { + accessed: c::FILETIME, + modified: c::FILETIME, +} + #[derive(Debug)] pub struct DirBuilder; @@ -550,6 +556,14 @@ impl File { })?; Ok(()) } + + pub fn set_times(&self, times: FileTimes) -> io::Result<()> { + cvt(unsafe { + c::SetFileTime(self.as_handle(), None, Some(×.accessed), Some(×.modified)) + })?; + Ok(()) + } + /// Get only basic file information such as attributes and file times. fn basic_info(&self) -> io::Result { unsafe { @@ -895,6 +909,16 @@ impl FilePermissions { } } +impl FileTimes { + pub fn set_accessed(&mut self, t: SystemTime) { + self.accessed = t.into_inner(); + } + + pub fn set_modified(&mut self, t: SystemTime) { + self.modified = t.into_inner(); + } +} + impl FileType { fn new(attrs: c::DWORD, reparse_tag: c::DWORD) -> FileType { FileType { attributes: attrs, reparse_tag } diff --git a/library/std/src/sys/windows/time.rs b/library/std/src/sys/windows/time.rs index 8f46781c75340..b8209a8544585 100644 --- a/library/std/src/sys/windows/time.rs +++ b/library/std/src/sys/windows/time.rs @@ -2,6 +2,7 @@ use crate::cmp::Ordering; use crate::fmt; use crate::mem; use crate::sys::c; +use crate::sys_common::IntoInner; use crate::time::Duration; use core::hash::{Hash, Hasher}; @@ -136,6 +137,12 @@ impl From for SystemTime { } } +impl IntoInner for SystemTime { + fn into_inner(self) -> c::FILETIME { + self.t + } +} + impl Hash for SystemTime { fn hash(&self, state: &mut H) { self.intervals().hash(state) diff --git a/library/std/src/time.rs b/library/std/src/time.rs index b2014f462bd34..759a59e1f98d2 100644 --- a/library/std/src/time.rs +++ b/library/std/src/time.rs @@ -38,7 +38,7 @@ use crate::error::Error; use crate::fmt; use crate::ops::{Add, AddAssign, Sub, SubAssign}; use crate::sys::time; -use crate::sys_common::FromInner; +use crate::sys_common::{FromInner, IntoInner}; #[stable(feature = "time", since = "1.3.0")] pub use core::time::Duration; @@ -686,3 +686,9 @@ impl FromInner for SystemTime { SystemTime(time) } } + +impl IntoInner for SystemTime { + fn into_inner(self) -> time::SystemTime { + self.0 + } +} From 1ac8d5ece31dae9441f56a6915115633e30eeb69 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Mon, 20 Jun 2022 14:37:38 -0700 Subject: [PATCH 2/7] Add alias `File::set_modified` as shorthand --- library/std/src/fs.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 0500e826768af..4f321cdeeea00 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -638,6 +638,15 @@ impl File { pub fn set_times(&self, times: FileTimes) -> io::Result<()> { self.inner.set_times(times.0) } + + /// Changes the modification time of the underlying file. + /// + /// This is an alias for `set_times(FileTimes::new().set_modified(time))`. + #[unstable(feature = "file_set_times", issue = "98245")] + #[inline] + pub fn set_modified(&self, time: SystemTime) -> io::Result<()> { + self.set_times(FileTimes::new().set_modified(time)) + } } // In addition to the `impl`s here, `File` also has `impl`s for From 828b637c2a37c85f8d3734d57ad459868ea31584 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sat, 25 Jun 2022 12:04:13 -0700 Subject: [PATCH 3/7] Return an error if trying to set a file timestamp to 0 on Windows This would otherwise silently ignore the attempt, since 0 serves as a flag to not set a timestamp. --- library/std/src/sys/windows/fs.rs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/library/std/src/sys/windows/fs.rs b/library/std/src/sys/windows/fs.rs index e3809f3579dfd..39aabadcf15fc 100644 --- a/library/std/src/sys/windows/fs.rs +++ b/library/std/src/sys/windows/fs.rs @@ -84,8 +84,8 @@ pub struct FilePermissions { #[derive(Copy, Clone, Debug, Default)] pub struct FileTimes { - accessed: c::FILETIME, - modified: c::FILETIME, + accessed: Option, + modified: Option, } #[derive(Debug)] @@ -558,8 +558,15 @@ impl File { } pub fn set_times(&self, times: FileTimes) -> io::Result<()> { + let is_zero = |t: c::FILETIME| t.dwLowDateTime == 0 && t.dwHighDateTime == 0; + if times.accessed.map_or(false, is_zero) || times.modified.map_or(false, is_zero) { + return Err(io::const_io_error!( + io::ErrorKind::InvalidInput, + "Cannot set file timestamp to 0", + )); + } cvt(unsafe { - c::SetFileTime(self.as_handle(), None, Some(×.accessed), Some(×.modified)) + c::SetFileTime(self.as_handle(), None, times.accessed.as_ref(), times.modified.as_ref()) })?; Ok(()) } @@ -911,11 +918,11 @@ impl FilePermissions { impl FileTimes { pub fn set_accessed(&mut self, t: SystemTime) { - self.accessed = t.into_inner(); + self.accessed = Some(t.into_inner()); } pub fn set_modified(&mut self, t: SystemTime) { - self.modified = t.into_inner(); + self.modified = Some(t.into_inner()); } } From e387cff7a3eb442aa0a0b84a3ebb6f170e205ea2 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Mon, 27 Jun 2022 11:05:10 -0700 Subject: [PATCH 4/7] Also use fallback for futimens on Android futimens requires Android API level 19, and std still supports older API levels. --- library/std/src/sys/unix/fs.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs index db81b26e7e7a8..91deff71e4222 100644 --- a/library/std/src/sys/unix/fs.rs +++ b/library/std/src/sys/unix/fs.rs @@ -19,7 +19,7 @@ use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; target_os = "ios", ))] use crate::sys::weak::syscall; -#[cfg(target_os = "macos")] +#[cfg(any(target_os = "android", target_os = "macos"))] use crate::sys::weak::weak; use libc::{c_int, mode_t}; @@ -1064,8 +1064,8 @@ impl File { pub fn set_times(&self, times: FileTimes) -> io::Result<()> { cfg_if::cfg_if! { - // futimens requires macOS 10.13 - if #[cfg(target_os = "macos")] { + // futimens requires macOS 10.13, and Android API level 19 + if #[cfg(any(target_os = "android", target_os = "macos"))] { fn ts_to_tv(ts: &libc::timespec) -> libc::timeval { libc::timeval { tv_sec: ts.tv_sec, tv_usec: (ts.tv_nsec / 1000) as _ } } From 3da17293e78198575c8adbab40ec783f250e9fe3 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Mon, 11 Jul 2022 18:23:34 -0700 Subject: [PATCH 5/7] Don't fall back to futimes on Android; it needs a newer API level than futimens Just return `io::ErrorKind::Unsupported` instead. --- library/std/src/sys/unix/fs.rs | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs index 91deff71e4222..59f17a8659d4d 100644 --- a/library/std/src/sys/unix/fs.rs +++ b/library/std/src/sys/unix/fs.rs @@ -1066,17 +1066,28 @@ impl File { cfg_if::cfg_if! { // futimens requires macOS 10.13, and Android API level 19 if #[cfg(any(target_os = "android", target_os = "macos"))] { - fn ts_to_tv(ts: &libc::timespec) -> libc::timeval { - libc::timeval { tv_sec: ts.tv_sec, tv_usec: (ts.tv_nsec / 1000) as _ } - } cvt(unsafe { weak!(fn futimens(c_int, *const libc::timespec) -> c_int); - futimens.get() - .map(|futimens| futimens(self.as_raw_fd(), times.0.as_ptr())) - .unwrap_or_else(|| { + match futimens.get() { + Some(futimens) => futimens(self.as_raw_fd(), times.0.as_ptr()), + #[cfg(target_os = "macos")] + None => { + fn ts_to_tv(ts: &libc::timespec) -> libc::timeval { + libc::timeval { + tv_sec: ts.tv_sec, + tv_usec: (ts.tv_nsec / 1000) as _ + } + } let timevals = [ts_to_tv(×.0[0]), ts_to_tv(×.0[1])]; libc::futimes(self.as_raw_fd(), timevals.as_ptr()) - }) + } + // futimes requires even newer Android. + #[cfg(target_os = "android")] + None => return Err(io::const_io_error!( + io::ErrorKind::Unsupported, + "setting file times requires Android API level >= 19", + )), + } })?; } else { cvt(unsafe { libc::futimens(self.as_raw_fd(), times.0.as_ptr()) })?; From 11d9be63596c28d4f6318194e68e16cf0d172287 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Wed, 20 Jul 2022 21:14:24 -0700 Subject: [PATCH 6/7] Stub out `set_times` to return unsupported on Redox Redox doesn't appear to support `UTIME_OMIT`, so we can't set file times individually. --- library/std/src/sys/unix/fs.rs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs index 59f17a8659d4d..1a4c2f8caf40e 100644 --- a/library/std/src/sys/unix/fs.rs +++ b/library/std/src/sys/unix/fs.rs @@ -538,6 +538,11 @@ impl fmt::Debug for FileTimes { impl Default for FileTimes { fn default() -> Self { + // Redox doesn't appear to support `UTIME_OMIT`, so we stub it out here, and always return + // an error in `set_times`. + #[cfg(target_os = "redox")] + let omit = libc::timespec { tv_sec: 0, tv_nsec: 0 }; + #[cfg(not(target_os = "redox"))] let omit = libc::timespec { tv_sec: 0, tv_nsec: libc::UTIME_OMIT as _ }; Self([omit; 2]) } @@ -1064,8 +1069,14 @@ impl File { pub fn set_times(&self, times: FileTimes) -> io::Result<()> { cfg_if::cfg_if! { - // futimens requires macOS 10.13, and Android API level 19 - if #[cfg(any(target_os = "android", target_os = "macos"))] { + if #[cfg(target_os = "redox")] { + // Redox doesn't appear to support `UTIME_OMIT`. + return Err(io::const_io_error!( + io::ErrorKind::Unsupported, + "setting file times not supported", + )); + } else if #[cfg(any(target_os = "android", target_os = "macos"))] { + // futimens requires macOS 10.13, and Android API level 19 cvt(unsafe { weak!(fn futimens(c_int, *const libc::timespec) -> c_int); match futimens.get() { From f8061ddb03f6b07108b0d041742f4faf0b3d5339 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sat, 30 Jul 2022 13:28:17 -0700 Subject: [PATCH 7/7] Fix warnings in stubbed out set_times --- library/std/src/sys/unix/fs.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs index 1a4c2f8caf40e..0f3bb392a31d9 100644 --- a/library/std/src/sys/unix/fs.rs +++ b/library/std/src/sys/unix/fs.rs @@ -1071,10 +1071,11 @@ impl File { cfg_if::cfg_if! { if #[cfg(target_os = "redox")] { // Redox doesn't appear to support `UTIME_OMIT`. - return Err(io::const_io_error!( + drop(times); + Err(io::const_io_error!( io::ErrorKind::Unsupported, "setting file times not supported", - )); + )) } else if #[cfg(any(target_os = "android", target_os = "macos"))] { // futimens requires macOS 10.13, and Android API level 19 cvt(unsafe { @@ -1100,12 +1101,12 @@ impl File { )), } })?; + Ok(()) } else { cvt(unsafe { libc::futimens(self.as_raw_fd(), times.0.as_ptr()) })?; + Ok(()) } } - - Ok(()) } }