@@ -13,6 +13,7 @@ use crate::sys::handle::Handle;
13
13
use crate :: sys:: time:: SystemTime ;
14
14
use crate :: sys:: { c, cvt} ;
15
15
use crate :: sys_common:: { AsInner , FromInner , IntoInner } ;
16
+ use crate :: thread;
16
17
17
18
use super :: path:: maybe_verbatim;
18
19
use super :: to_u16s;
@@ -679,7 +680,7 @@ impl<'a> DirBuffIter<'a> {
679
680
}
680
681
}
681
682
impl < ' a > Iterator for DirBuffIter < ' a > {
682
- type Item = & ' a [ u16 ] ;
683
+ type Item = ( & ' a [ u16 ] , bool ) ;
683
684
fn next ( & mut self ) -> Option < Self :: Item > {
684
685
use crate :: mem:: size_of;
685
686
let buffer = & self . buffer ?[ self . cursor ..] ;
@@ -688,14 +689,16 @@ impl<'a> Iterator for DirBuffIter<'a> {
688
689
// SAFETY: The buffer contains a `FILE_ID_BOTH_DIR_INFO` struct but the
689
690
// last field (the file name) is unsized. So an offset has to be
690
691
// used to get the file name slice.
691
- let ( name, next_entry) = unsafe {
692
+ let ( name, is_directory , next_entry) = unsafe {
692
693
let info = buffer. as_ptr ( ) . cast :: < c:: FILE_ID_BOTH_DIR_INFO > ( ) ;
693
694
let next_entry = ( * info) . NextEntryOffset as usize ;
694
695
let name = crate :: slice:: from_raw_parts (
695
696
( * info) . FileName . as_ptr ( ) . cast :: < u16 > ( ) ,
696
697
( * info) . FileNameLength as usize / size_of :: < u16 > ( ) ,
697
698
) ;
698
- ( name, next_entry)
699
+ let is_directory = ( ( * info) . FileAttributes & c:: FILE_ATTRIBUTE_DIRECTORY ) != 0 ;
700
+
701
+ ( name, is_directory, next_entry)
699
702
} ;
700
703
701
704
if next_entry == 0 {
@@ -708,7 +711,7 @@ impl<'a> Iterator for DirBuffIter<'a> {
708
711
const DOT : u16 = b'.' as u16 ;
709
712
match name {
710
713
[ DOT ] | [ DOT , DOT ] => self . next ( ) ,
711
- _ => Some ( name) ,
714
+ _ => Some ( ( name, is_directory ) ) ,
712
715
}
713
716
}
714
717
}
@@ -993,89 +996,92 @@ pub fn remove_dir_all(path: &Path) -> io::Result<()> {
993
996
if ( file. basic_info ( ) ?. FileAttributes & c:: FILE_ATTRIBUTE_DIRECTORY ) == 0 {
994
997
return Err ( io:: Error :: from_raw_os_error ( c:: ERROR_DIRECTORY as _ ) ) ;
995
998
}
996
- let mut delete: fn ( & File ) -> io:: Result < ( ) > = File :: posix_delete;
997
- let result = match delete ( & file) {
998
- Err ( e) if e. kind ( ) == io:: ErrorKind :: DirectoryNotEmpty => {
999
- match remove_dir_all_recursive ( & file, delete) {
1000
- // Return unexpected errors.
1001
- Err ( e) if e. kind ( ) != io:: ErrorKind :: DirectoryNotEmpty => return Err ( e) ,
1002
- result => result,
1003
- }
1004
- }
1005
- // If POSIX delete is not supported for this filesystem then fallback to win32 delete.
1006
- Err ( e)
1007
- if e. raw_os_error ( ) == Some ( c:: ERROR_NOT_SUPPORTED as i32 )
1008
- || e. raw_os_error ( ) == Some ( c:: ERROR_INVALID_PARAMETER as i32 ) =>
1009
- {
1010
- delete = File :: win32_delete;
1011
- Err ( e)
1012
- }
1013
- result => result,
1014
- } ;
1015
- if result. is_ok ( ) {
1016
- Ok ( ( ) )
1017
- } else {
1018
- // This is a fallback to make sure the directory is actually deleted.
1019
- // Otherwise this function is prone to failing with `DirectoryNotEmpty`
1020
- // due to possible delays between marking a file for deletion and the
1021
- // file actually being deleted from the filesystem.
1022
- //
1023
- // So we retry a few times before giving up.
1024
- for _ in 0 ..5 {
1025
- match remove_dir_all_recursive ( & file, delete) {
1026
- Err ( e) if e. kind ( ) == io:: ErrorKind :: DirectoryNotEmpty => { }
1027
- result => return result,
999
+
1000
+ match remove_dir_all_iterative ( & file, File :: posix_delete) {
1001
+ Err ( e) => {
1002
+ if let Some ( code) = e. raw_os_error ( ) {
1003
+ match code as u32 {
1004
+ // If POSIX delete is not supported for this filesystem then fallback to win32 delete.
1005
+ c:: ERROR_NOT_SUPPORTED
1006
+ | c:: ERROR_INVALID_FUNCTION
1007
+ | c:: ERROR_INVALID_PARAMETER => {
1008
+ remove_dir_all_iterative ( & file, File :: win32_delete)
1009
+ }
1010
+ _ => Err ( e) ,
1011
+ }
1012
+ } else {
1013
+ Err ( e)
1028
1014
}
1029
1015
}
1030
- // Try one last time.
1031
- delete ( & file)
1016
+ ok => ok,
1032
1017
}
1033
1018
}
1034
1019
1035
- fn remove_dir_all_recursive ( f : & File , delete : fn ( & File ) -> io:: Result < ( ) > ) -> io:: Result < ( ) > {
1020
+ fn remove_dir_all_iterative ( f : & File , delete : fn ( & File ) -> io:: Result < ( ) > ) -> io:: Result < ( ) > {
1021
+ // When deleting files we may loop this many times when certain error conditions occur.
1022
+ // This allows remove_dir_all to succeed when the error is temporary.
1023
+ const MAX_RETRIES : u32 = 10 ;
1024
+
1036
1025
let mut buffer = DirBuff :: new ( ) ;
1037
- let mut restart = true ;
1038
- // Fill the buffer and iterate the entries.
1039
- while f. fill_dir_buff ( & mut buffer, restart) ? {
1040
- for name in buffer. iter ( ) {
1041
- // Open the file without following symlinks and try deleting it.
1042
- // We try opening will all needed permissions and if that is denied
1043
- // fallback to opening without `FILE_LIST_DIRECTORY` permission.
1044
- // Note `SYNCHRONIZE` permission is needed for synchronous access.
1045
- let mut result =
1046
- open_link_no_reparse ( & f, name, c:: SYNCHRONIZE | c:: DELETE | c:: FILE_LIST_DIRECTORY ) ;
1047
- if matches ! ( & result, Err ( e) if e. kind( ) == io:: ErrorKind :: PermissionDenied ) {
1048
- result = open_link_no_reparse ( & f, name, c:: SYNCHRONIZE | c:: DELETE ) ;
1026
+ let mut dirlist = vec ! [ f. duplicate( ) ?] ;
1027
+
1028
+ // FIXME: This is a hack so we can push to the dirlist vec after borrowing from it.
1029
+ fn copy_handle ( f : & File ) -> mem:: ManuallyDrop < File > {
1030
+ unsafe { mem:: ManuallyDrop :: new ( File :: from_raw_handle ( f. as_raw_handle ( ) ) ) }
1031
+ }
1032
+
1033
+ while let Some ( dir) = dirlist. last ( ) {
1034
+ let dir = copy_handle ( dir) ;
1035
+
1036
+ // Fill the buffer and iterate the entries.
1037
+ let more_data = dir. fill_dir_buff ( & mut buffer, false ) ?;
1038
+ for ( name, is_directory) in buffer. iter ( ) {
1039
+ if is_directory {
1040
+ let child_dir = open_link_no_reparse (
1041
+ & dir,
1042
+ name,
1043
+ c:: SYNCHRONIZE | c:: DELETE | c:: FILE_LIST_DIRECTORY ,
1044
+ ) ?;
1045
+ dirlist. push ( child_dir) ;
1046
+ } else {
1047
+ for i in 1 ..=MAX_RETRIES {
1048
+ let result = open_link_no_reparse ( & dir, name, c:: SYNCHRONIZE | c:: DELETE ) ;
1049
+ match result {
1050
+ Ok ( f) => delete ( & f) ?,
1051
+ // Already deleted, so skip.
1052
+ Err ( e) if e. kind ( ) == io:: ErrorKind :: NotFound => break ,
1053
+ // Retry a few times if the file is locked or a delete is already in progress.
1054
+ Err ( e)
1055
+ if i < MAX_RETRIES
1056
+ && ( e. raw_os_error ( ) == Some ( c:: ERROR_DELETE_PENDING as _ )
1057
+ || e. raw_os_error ( )
1058
+ == Some ( c:: ERROR_SHARING_VIOLATION as _ ) ) => { }
1059
+ // Otherwise return the error.
1060
+ Err ( e) => return Err ( e) ,
1061
+ }
1062
+ thread:: yield_now ( ) ;
1063
+ }
1049
1064
}
1050
- match result {
1051
- Ok ( file ) => match delete ( & file ) {
1052
- Err ( e ) if e . kind ( ) == io :: ErrorKind :: DirectoryNotEmpty => {
1053
- // Iterate the directory's files.
1054
- // Ignore `DirectoryNotEmpty` errors here. They will be
1055
- // caught when `remove_dir_all` tries to delete the top
1056
- // level directory. It can then decide if to retry or not.
1057
- match remove_dir_all_recursive ( & file , delete ) {
1058
- Err ( e ) if e. kind ( ) == io:: ErrorKind :: DirectoryNotEmpty => { }
1059
- result => result? ,
1065
+ }
1066
+ // If there were no more files then delete the directory.
1067
+ if !more_data {
1068
+ if let Some ( dir ) = dirlist . pop ( ) {
1069
+ // Retry deleting a few times in case we need to wait for a file to be deleted.
1070
+ for i in 1 ..= MAX_RETRIES {
1071
+ let result = delete ( & dir ) ;
1072
+ if let Err ( e ) = result {
1073
+ if i == MAX_RETRIES || e. kind ( ) != io:: ErrorKind :: DirectoryNotEmpty {
1074
+ return Err ( e ) ;
1060
1075
}
1076
+ thread:: yield_now ( ) ;
1077
+ } else {
1078
+ break ;
1061
1079
}
1062
- result => result?,
1063
- } ,
1064
- // Ignore error if a delete is already in progress or the file
1065
- // has already been deleted. It also ignores sharing violations
1066
- // (where a file is locked by another process) as these are
1067
- // usually temporary.
1068
- Err ( e)
1069
- if e. raw_os_error ( ) == Some ( c:: ERROR_DELETE_PENDING as _ )
1070
- || e. kind ( ) == io:: ErrorKind :: NotFound
1071
- || e. raw_os_error ( ) == Some ( c:: ERROR_SHARING_VIOLATION as _ ) => { }
1072
- Err ( e) => return Err ( e) ,
1080
+ }
1073
1081
}
1074
1082
}
1075
- // Continue reading directory entries without restarting from the beginning,
1076
- restart = false ;
1077
1083
}
1078
- delete ( & f )
1084
+ Ok ( ( ) )
1079
1085
}
1080
1086
1081
1087
pub fn readlink ( path : & Path ) -> io:: Result < PathBuf > {
0 commit comments