diff --git a/crates/cargo-util/src/paths.rs b/crates/cargo-util/src/paths.rs index fe48dd6b1594..71c8edd46f90 100644 --- a/crates/cargo-util/src/paths.rs +++ b/crates/cargo-util/src/paths.rs @@ -517,24 +517,57 @@ fn _remove_dir(p: &Path) -> Result<()> { /// /// If the file is readonly, this will attempt to change the permissions to /// force the file to be deleted. +/// And if the file is a symlink to a directory, this will attempt to remove +/// the symlink itself. pub fn remove_file>(p: P) -> Result<()> { _remove_file(p.as_ref()) } fn _remove_file(p: &Path) -> Result<()> { - let mut err = match fs::remove_file(p) { - Ok(()) => return Ok(()), - Err(e) => e, - }; - - if err.kind() == io::ErrorKind::PermissionDenied && set_not_readonly(p).unwrap_or(false) { - match fs::remove_file(p) { - Ok(()) => return Ok(()), - Err(e) => err = e, + // For Windows, we need to check if the file is a symlink to a directory + // and remove the symlink itself by calling `remove_dir` instead of + // `remove_file`. + #[cfg(target_os = "windows")] + { + use std::os::windows::fs::FileTypeExt; + let metadata = symlink_metadata(p)?; + let file_type = metadata.file_type(); + if file_type.is_symlink_dir() { + return remove_symlink_dir_with_permission_check(p); } } - Err(err).with_context(|| format!("failed to remove file `{}`", p.display())) + remove_file_with_permission_check(p) +} + +#[cfg(target_os = "windows")] +fn remove_symlink_dir_with_permission_check(p: &Path) -> Result<()> { + remove_with_permission_check(fs::remove_dir, p) + .context(format!("failed to remove symlink dir `{}`", p.display())) +} + +fn remove_file_with_permission_check(p: &Path) -> Result<()> { + remove_with_permission_check(fs::remove_file, p) + .context(format!("failed to remove file `{}`", p.display())) +} + +fn remove_with_permission_check(remove_func: F, p: P) -> io::Result<()> +where + F: Fn(P) -> io::Result<()>, + P: AsRef + Clone, +{ + match remove_func(p.clone()) { + Ok(()) => Ok(()), + Err(e) => { + if e.kind() == io::ErrorKind::PermissionDenied + && set_not_readonly(p.as_ref()).unwrap_or(false) + { + remove_func(p) + } else { + Err(e) + } + } + } } fn set_not_readonly(p: &Path) -> io::Result {