From 457e5287944a3080c4db8eff502c71c6c4936231 Mon Sep 17 00:00:00 2001 From: Chris Denton 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 { let path = maybe_verbatim(path)?; + Self::open_native(&path, opts) + } + + fn open_native(path: &[u16], opts: &OpenOptions) -> io::Result { let creation = opts.get_creation_mode()?; let handle = unsafe { c::CreateFileW( @@ -1226,8 +1230,24 @@ pub fn readdir(p: &Path) -> io::Result { 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 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 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 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()); +}