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