Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Windows: remove readonly files #134679

Merged
merged 3 commits into from
Feb 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions library/std/src/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion library/std/src/fs/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
26 changes: 24 additions & 2 deletions library/std/src/sys/pal/windows/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -1226,8 +1230,26 @@ 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 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.
Err(io::Error::from_raw_os_error(err.code as i32))
} else {
Ok(())
}
}

pub fn rename(old: &Path, new: &Path) -> io::Result<()> {
Expand Down
8 changes: 8 additions & 0 deletions library/std/tests/win_delete_self.rs
Original file line number Diff line number Diff line change
@@ -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());
}
Loading