@@ -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.
520522pub fn remove_file < P : AsRef < Path > > ( p : P ) -> Result < ( ) > {
521523 _remove_file ( p. as_ref ( ) )
522524}
523525
524526fn _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
540573fn set_not_readonly ( p : & Path ) -> io:: Result < bool > {
@@ -908,4 +941,50 @@ mod tests {
908941 ) ;
909942 }
910943 }
944+
945+ #[ test]
946+ #[ cfg( windows) ]
947+ fn test_remove_symlink_dir ( ) {
948+ use super :: * ;
949+ use std:: fs;
950+ use std:: os:: windows:: fs:: symlink_dir;
951+
952+ let tmpdir = tempfile:: tempdir ( ) . unwrap ( ) ;
953+ let dir_path = tmpdir. path ( ) . join ( "testdir" ) ;
954+ let symlink_path = tmpdir. path ( ) . join ( "symlink" ) ;
955+
956+ fs:: create_dir ( & dir_path) . unwrap ( ) ;
957+
958+ symlink_dir ( & dir_path, & symlink_path) . expect ( "failed to create symlink" ) ;
959+
960+ assert ! ( symlink_path. exists( ) ) ;
961+
962+ assert ! ( remove_file( symlink_path. clone( ) ) . is_ok( ) ) ;
963+
964+ assert ! ( !symlink_path. exists( ) ) ;
965+ assert ! ( dir_path. exists( ) ) ;
966+ }
967+
968+ #[ test]
969+ #[ cfg( windows) ]
970+ fn test_remove_symlink_file ( ) {
971+ use super :: * ;
972+ use std:: fs;
973+ use std:: os:: windows:: fs:: symlink_file;
974+
975+ let tmpdir = tempfile:: tempdir ( ) . unwrap ( ) ;
976+ let file_path = tmpdir. path ( ) . join ( "testfile" ) ;
977+ let symlink_path = tmpdir. path ( ) . join ( "symlink" ) ;
978+
979+ fs:: write ( & file_path, b"test" ) . unwrap ( ) ;
980+
981+ symlink_file ( & file_path, & symlink_path) . expect ( "failed to create symlink" ) ;
982+
983+ assert ! ( symlink_path. exists( ) ) ;
984+
985+ assert ! ( remove_file( symlink_path. clone( ) ) . is_ok( ) ) ;
986+
987+ assert ! ( !symlink_path. exists( ) ) ;
988+ assert ! ( file_path. exists( ) ) ;
989+ }
911990}
0 commit comments