@@ -493,24 +493,53 @@ fn _remove_dir(p: &Path) -> Result<()> {
493
493
///
494
494
/// If the file is readonly, this will attempt to change the permissions to
495
495
/// force the file to be deleted.
496
+ /// And if the file is a symlink to a directory, this will attempt to remove
497
+ /// the symlink itself.
496
498
pub fn remove_file < P : AsRef < Path > > ( p : P ) -> Result < ( ) > {
497
499
_remove_file ( p. as_ref ( ) )
498
500
}
499
501
500
502
fn _remove_file ( p : & Path ) -> Result < ( ) > {
501
- let mut err = match fs:: remove_file ( p) {
502
- Ok ( ( ) ) => return Ok ( ( ) ) ,
503
- Err ( e) => e,
504
- } ;
505
-
506
- if err. kind ( ) == io:: ErrorKind :: PermissionDenied && set_not_readonly ( p) . unwrap_or ( false ) {
507
- match fs:: remove_file ( p) {
508
- Ok ( ( ) ) => return Ok ( ( ) ) ,
509
- Err ( e) => err = e,
503
+ #[ cfg( target_os = "windows" ) ]
504
+ {
505
+ let metadata = symlink_metadata ( p) ?;
506
+ let file_type = metadata. file_type ( ) ;
507
+ if file_type. is_symlink ( ) && metadata. is_dir ( ) {
508
+ return remove_symlink_dir_with_permission_check ( p) ;
510
509
}
511
510
}
512
511
513
- Err ( err) . with_context ( || format ! ( "failed to remove file `{}`" , p. display( ) ) )
512
+ remove_file_with_permission_check ( p)
513
+ }
514
+
515
+ #[ cfg( target_os = "windows" ) ]
516
+ fn remove_symlink_dir_with_permission_check ( p : & Path ) -> Result < ( ) > {
517
+ remove_with_permission_check ( fs:: remove_dir, p)
518
+ . context ( format ! ( "failed to remove symlink dir `{}`" , p. display( ) ) )
519
+ }
520
+
521
+ fn remove_file_with_permission_check ( p : & Path ) -> Result < ( ) > {
522
+ remove_with_permission_check ( fs:: remove_file, p)
523
+ . context ( format ! ( "failed to remove file `{}`" , p. display( ) ) )
524
+ }
525
+
526
+ fn remove_with_permission_check < F , P > ( remove_func : F , p : P ) -> io:: Result < ( ) >
527
+ where
528
+ F : Fn ( P ) -> io:: Result < ( ) > ,
529
+ P : AsRef < Path > + Clone ,
530
+ {
531
+ match remove_func ( p. clone ( ) ) {
532
+ Ok ( ( ) ) => Ok ( ( ) ) ,
533
+ Err ( e) => {
534
+ if e. kind ( ) == io:: ErrorKind :: PermissionDenied
535
+ && set_not_readonly ( p. as_ref ( ) ) . unwrap_or ( false )
536
+ {
537
+ remove_func ( p)
538
+ } else {
539
+ Err ( e)
540
+ }
541
+ }
542
+ }
514
543
}
515
544
516
545
fn set_not_readonly ( p : & Path ) -> io:: Result < bool > {
@@ -858,4 +887,27 @@ mod tests {
858
887
) ;
859
888
}
860
889
}
890
+
891
+ #[ test]
892
+ #[ cfg( windows) ]
893
+ fn test_remove_symlink_dir ( ) {
894
+ use super :: * ;
895
+ use std:: fs;
896
+ use std:: os:: windows:: fs:: symlink_dir;
897
+
898
+ let tmpdir = tempfile:: tempdir ( ) . unwrap ( ) ;
899
+ let dir_path = tmpdir. path ( ) . join ( "testdir" ) ;
900
+ let symlink_path = tmpdir. path ( ) . join ( "symlink" ) ;
901
+
902
+ fs:: create_dir ( & dir_path) . unwrap ( ) ;
903
+
904
+ symlink_dir ( & dir_path, & symlink_path) . expect ( "failed to create symlink" ) ;
905
+
906
+ assert ! ( symlink_path. exists( ) ) ;
907
+
908
+ remove_dir ( symlink_path. clone ( ) ) . unwrap ( ) ;
909
+
910
+ assert ! ( !symlink_path. exists( ) ) ;
911
+ assert ! ( dir_path. exists( ) ) ;
912
+ }
861
913
}
0 commit comments