From 61b45c670bc6a9c0d7d35769ed58899f347a6371 Mon Sep 17 00:00:00 2001
From: Josh Triplett <josh@joshtriplett.org>
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 55bd2c59406..0500e826768 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<fs_imp::FileAttr> 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 8b0bbd6a55c..db81b26e7e7 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(&times.0[0]), ts_to_tv(&times.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 d1d2847cd33..0e1a6257ed7 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 cd6815bfc21..6614ae397b5 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<wasi::Timestamp>,
+    modified: Option<wasi::Timestamp>,
+}
+
 #[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<PathBuf> {
         read_link(&self.fd, file)
     }
diff --git a/library/std/src/sys/wasi/time.rs b/library/std/src/sys/wasi/time.rs
index 088585654b9..3d326e49106 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<Duration, Duration> {
         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 5469487df1e..abb471213d2 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 157d8a044d3..e3809f3579d 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(&times.accessed), Some(&times.modified))
+        })?;
+        Ok(())
+    }
+
     /// Get only basic file information such as attributes and file times.
     fn basic_info(&self) -> io::Result<c::FILE_BASIC_INFO> {
         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 8f46781c753..b8209a85445 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<c::FILETIME> for SystemTime {
     }
 }
 
+impl IntoInner<c::FILETIME> for SystemTime {
+    fn into_inner(self) -> c::FILETIME {
+        self.t
+    }
+}
+
 impl Hash for SystemTime {
     fn hash<H: Hasher>(&self, state: &mut H) {
         self.intervals().hash(state)
diff --git a/library/std/src/time.rs b/library/std/src/time.rs
index b2014f462bd..759a59e1f98 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<time::SystemTime> for SystemTime {
         SystemTime(time)
     }
 }
+
+impl IntoInner<time::SystemTime> for SystemTime {
+    fn into_inner(self) -> time::SystemTime {
+        self.0
+    }
+}

From 1ac8d5ece31dae9441f56a6915115633e30eeb69 Mon Sep 17 00:00:00 2001
From: Josh Triplett <josh@joshtriplett.org>
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 0500e826768..4f321cdeeea 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 <josh@joshtriplett.org>
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 e3809f3579d..39aabadcf15 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<c::FILETIME>,
+    modified: Option<c::FILETIME>,
 }
 
 #[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(&times.accessed), Some(&times.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 <josh@joshtriplett.org>
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 db81b26e7e7..91deff71e42 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 <josh@joshtriplett.org>
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 91deff71e42..59f17a8659d 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(&times.0[0]), ts_to_tv(&times.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 <josh@joshtriplett.org>
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 59f17a8659d..1a4c2f8caf4 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 <josh@joshtriplett.org>
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 1a4c2f8caf4..0f3bb392a31 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(())
     }
 }