Skip to content

Commit 40ff7be

Browse files
committed
fix: remove symlink on Windows
Signed-off-by: hi-rustin <[email protected]>
1 parent 0e1b115 commit 40ff7be

File tree

1 file changed

+45
-12
lines changed

1 file changed

+45
-12
lines changed

crates/cargo-util/src/paths.rs

Lines changed: 45 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -517,24 +517,57 @@ fn _remove_dir(p: &Path) -> Result<()> {
517517
///
518518
/// If the file is readonly, this will attempt to change the permissions to
519519
/// force the file to be deleted.
520+
/// On Windows, if the file is a symlink to a directory, this will attempt to remove
521+
/// the symlink itself.
520522
pub fn remove_file<P: AsRef<Path>>(p: P) -> Result<()> {
521523
_remove_file(p.as_ref())
522524
}
523525

524526
fn _remove_file(p: &Path) -> Result<()> {
525-
let mut err = match fs::remove_file(p) {
526-
Ok(()) => return Ok(()),
527-
Err(e) => e,
528-
};
529-
530-
if err.kind() == io::ErrorKind::PermissionDenied && set_not_readonly(p).unwrap_or(false) {
531-
match fs::remove_file(p) {
532-
Ok(()) => return Ok(()),
533-
Err(e) => err = e,
527+
// For Windows, we need to check if the file is a symlink to a directory
528+
// and remove the symlink itself by calling `remove_dir` instead of
529+
// `remove_file`.
530+
#[cfg(target_os = "windows")]
531+
{
532+
use std::os::windows::fs::FileTypeExt;
533+
let metadata = symlink_metadata(p)?;
534+
let file_type = metadata.file_type();
535+
if file_type.is_symlink_dir() {
536+
return remove_symlink_dir_with_permission_check(p);
534537
}
535538
}
536539

537-
Err(err).with_context(|| format!("failed to remove file `{}`", p.display()))
540+
remove_file_with_permission_check(p)
541+
}
542+
543+
#[cfg(target_os = "windows")]
544+
fn remove_symlink_dir_with_permission_check(p: &Path) -> Result<()> {
545+
remove_with_permission_check(fs::remove_dir, p)
546+
.with_context(|| format!("failed to remove symlink dir `{}`", p.display()))
547+
}
548+
549+
fn remove_file_with_permission_check(p: &Path) -> Result<()> {
550+
remove_with_permission_check(fs::remove_file, p)
551+
.with_context(|| format!("failed to remove file `{}`", p.display()))
552+
}
553+
554+
fn remove_with_permission_check<F, P>(remove_func: F, p: P) -> io::Result<()>
555+
where
556+
F: Fn(P) -> io::Result<()>,
557+
P: AsRef<Path> + Clone,
558+
{
559+
match remove_func(p.clone()) {
560+
Ok(()) => Ok(()),
561+
Err(e) => {
562+
if e.kind() == io::ErrorKind::PermissionDenied
563+
&& set_not_readonly(p.as_ref()).unwrap_or(false)
564+
{
565+
remove_func(p)
566+
} else {
567+
Err(e)
568+
}
569+
}
570+
}
538571
}
539572

540573
fn set_not_readonly(p: &Path) -> io::Result<bool> {
@@ -926,9 +959,9 @@ mod tests {
926959

927960
assert!(symlink_path.exists());
928961

929-
assert!(remove_file(symlink_path.clone()).is_err());
962+
assert!(remove_file(symlink_path.clone()).is_ok());
930963

931-
assert!(symlink_path.exists());
964+
assert!(!symlink_path.exists());
932965
assert!(dir_path.exists());
933966
}
934967

0 commit comments

Comments
 (0)