@@ -517,24 +517,57 @@ fn _remove_dir(p: &Path) -> Result<()> {
517
517
///
518
518
/// If the file is readonly, this will attempt to change the permissions to
519
519
/// 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.
520
522
pub fn remove_file < P : AsRef < Path > > ( p : P ) -> Result < ( ) > {
521
523
_remove_file ( p. as_ref ( ) )
522
524
}
523
525
524
526
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) ;
534
537
}
535
538
}
536
539
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
+ }
538
571
}
539
572
540
573
fn set_not_readonly ( p : & Path ) -> io:: Result < bool > {
@@ -908,4 +941,50 @@ mod tests {
908
941
) ;
909
942
}
910
943
}
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
+ }
911
990
}
0 commit comments