From 457e5287944a3080c4db8eff502c71c6c4936231 Mon Sep 17 00:00:00 2001
From: Chris Denton <chris@chrisdenton.dev>
Date: Mon, 23 Dec 2024 07:08:00 +0000
Subject: [PATCH 1/3] Windows: remove readonly files

---
 library/std/src/fs/tests.rs           |  2 +-
 library/std/src/sys/pal/windows/fs.rs | 24 ++++++++++++++++++++++--
 2 files changed, 23 insertions(+), 3 deletions(-)

diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs
index 28f16da1ed8d2..605c5f53cd5e8 100644
--- a/library/std/src/fs/tests.rs
+++ b/library/std/src/fs/tests.rs
@@ -1384,7 +1384,7 @@ fn file_try_clone() {
 }
 
 #[test]
-#[cfg(not(windows))]
+#[cfg(not(target_vendor = "win7"))]
 fn unlink_readonly() {
     let tmpdir = tmpdir();
     let path = tmpdir.join("file");
diff --git a/library/std/src/sys/pal/windows/fs.rs b/library/std/src/sys/pal/windows/fs.rs
index f8493c21ad444..750cf7faeaeab 100644
--- a/library/std/src/sys/pal/windows/fs.rs
+++ b/library/std/src/sys/pal/windows/fs.rs
@@ -296,6 +296,10 @@ impl OpenOptions {
 impl File {
     pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
         let path = maybe_verbatim(path)?;
+        Self::open_native(&path, opts)
+    }
+
+    fn open_native(path: &[u16], opts: &OpenOptions) -> io::Result<File> {
         let creation = opts.get_creation_mode()?;
         let handle = unsafe {
             c::CreateFileW(
@@ -1226,8 +1230,24 @@ pub fn readdir(p: &Path) -> io::Result<ReadDir> {
 
 pub fn unlink(p: &Path) -> io::Result<()> {
     let p_u16s = maybe_verbatim(p)?;
-    cvt(unsafe { c::DeleteFileW(p_u16s.as_ptr()) })?;
-    Ok(())
+    if unsafe { c::DeleteFileW(p_u16s.as_ptr()) } == 0 {
+        let err = api::get_last_error();
+        // if `DeleteFileW` fails with ERROR_ACCESS_DENIED then try to remove
+        // the file while ignoring the readonly attribute.
+        // This is accomplished by calling the `posix_delete` function on an open file handle.
+        if err == WinError::ACCESS_DENIED {
+            let mut opts = OpenOptions::new();
+            opts.access_mode(c::DELETE);
+            opts.custom_flags(c::FILE_FLAG_OPEN_REPARSE_POINT);
+            if File::open_native(&p_u16s, &opts).map(|f| f.posix_delete()).is_ok() {
+                return Ok(());
+            }
+        }
+        // return the original error if any of the above fails.
+        Err(io::Error::from_raw_os_error(err.code as i32))
+    } else {
+        Ok(())
+    }
 }
 
 pub fn rename(old: &Path, new: &Path) -> io::Result<()> {

From 50522fad483ad86291db1871b396f9d54f0de6e7 Mon Sep 17 00:00:00 2001
From: Chris Denton <chris@chrisdenton.dev>
Date: Thu, 26 Dec 2024 10:42:06 +0000
Subject: [PATCH 2/3] Update platform information for remove_file

---
 library/std/src/fs.rs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs
index 1f8aac48a9a8b..75d1fcc166a1b 100644
--- a/library/std/src/fs.rs
+++ b/library/std/src/fs.rs
@@ -2284,8 +2284,8 @@ impl AsInner<fs_imp::DirEntry> for DirEntry {
 ///
 /// # Platform-specific behavior
 ///
-/// This function currently corresponds to the `unlink` function on Unix
-/// and the `DeleteFile` function on Windows.
+/// This function currently corresponds to the `unlink` function on Unix.
+/// On Windows, `DeleteFile` is used or `CreateFileW` and `SetInformationByHandle` for readonly files.
 /// Note that, this [may change in the future][changes].
 ///
 /// [changes]: io#platform-specific-behavior

From 962ebf0a48436445cf300efc0fa552044f4ca19d Mon Sep 17 00:00:00 2001
From: Chris Denton <chris@chrisdenton.dev>
Date: Sun, 26 Jan 2025 06:15:58 +0000
Subject: [PATCH 3/3] Windows: Test that deleting a running binary fails

---
 library/std/src/sys/pal/windows/fs.rs | 6 ++++--
 library/std/tests/win_delete_self.rs  | 8 ++++++++
 2 files changed, 12 insertions(+), 2 deletions(-)
 create mode 100644 library/std/tests/win_delete_self.rs

diff --git a/library/std/src/sys/pal/windows/fs.rs b/library/std/src/sys/pal/windows/fs.rs
index 750cf7faeaeab..bdb55643bb101 100644
--- a/library/std/src/sys/pal/windows/fs.rs
+++ b/library/std/src/sys/pal/windows/fs.rs
@@ -1239,8 +1239,10 @@ pub fn unlink(p: &Path) -> io::Result<()> {
             let mut opts = OpenOptions::new();
             opts.access_mode(c::DELETE);
             opts.custom_flags(c::FILE_FLAG_OPEN_REPARSE_POINT);
-            if File::open_native(&p_u16s, &opts).map(|f| f.posix_delete()).is_ok() {
-                return Ok(());
+            if let Ok(f) = File::open_native(&p_u16s, &opts) {
+                if f.posix_delete().is_ok() {
+                    return Ok(());
+                }
             }
         }
         // return the original error if any of the above fails.
diff --git a/library/std/tests/win_delete_self.rs b/library/std/tests/win_delete_self.rs
new file mode 100644
index 0000000000000..1c3ce4d710c38
--- /dev/null
+++ b/library/std/tests/win_delete_self.rs
@@ -0,0 +1,8 @@
+#![cfg(windows)]
+
+/// Attempting to delete a running binary should return an error on Windows.
+#[test]
+fn win_delete_self() {
+    let path = std::env::current_exe().unwrap();
+    assert!(std::fs::remove_file(path).is_err());
+}