@@ -680,7 +680,7 @@ impl<'a> DirBuffIter<'a> {
680680 }
681681}
682682impl < ' a > Iterator for DirBuffIter < ' a > {
683- type Item = & ' a [ u16 ] ;
683+ type Item = ( & ' a [ u16 ] , bool ) ;
684684 fn next ( & mut self ) -> Option < Self :: Item > {
685685 use crate :: mem:: size_of;
686686 let buffer = & self . buffer ?[ self . cursor ..] ;
@@ -689,14 +689,16 @@ impl<'a> Iterator for DirBuffIter<'a> {
689689 // SAFETY: The buffer contains a `FILE_ID_BOTH_DIR_INFO` struct but the
690690 // last field (the file name) is unsized. So an offset has to be
691691 // used to get the file name slice.
692- let ( name, next_entry) = unsafe {
692+ let ( name, is_directory , next_entry) = unsafe {
693693 let info = buffer. as_ptr ( ) . cast :: < c:: FILE_ID_BOTH_DIR_INFO > ( ) ;
694694 let next_entry = ( * info) . NextEntryOffset as usize ;
695695 let name = crate :: slice:: from_raw_parts (
696696 ( * info) . FileName . as_ptr ( ) . cast :: < u16 > ( ) ,
697697 ( * info) . FileNameLength as usize / size_of :: < u16 > ( ) ,
698698 ) ;
699- ( name, next_entry)
699+ let is_directory = ( ( * info) . FileAttributes & c:: FILE_ATTRIBUTE_DIRECTORY ) != 0 ;
700+
701+ ( name, is_directory, next_entry)
700702 } ;
701703
702704 if next_entry == 0 {
@@ -709,7 +711,7 @@ impl<'a> Iterator for DirBuffIter<'a> {
709711 const DOT : u16 = b'.' as u16 ;
710712 match name {
711713 [ DOT ] | [ DOT , DOT ] => self . next ( ) ,
712- _ => Some ( name) ,
714+ _ => Some ( ( name, is_directory ) ) ,
713715 }
714716 }
715717}
@@ -994,89 +996,77 @@ pub fn remove_dir_all(path: &Path) -> io::Result<()> {
994996 if ( file. basic_info ( ) ?. FileAttributes & c:: FILE_ATTRIBUTE_DIRECTORY ) == 0 {
995997 return Err ( io:: Error :: from_raw_os_error ( c:: ERROR_DIRECTORY as _ ) ) ;
996998 }
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)
10291014 }
10301015 }
1031- // Try one last time.
1032- delete ( & file)
1016+ ok => ok,
10331017 }
10341018}
10351019
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 < ( ) > {
10371021 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) ,
10621058 }
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) ?;
10741066 }
10751067 }
1076- // Continue reading directory entries without restarting from the beginning,
1077- restart = false ;
10781068 }
1079- delete ( & f )
1069+ Ok ( ( ) )
10801070}
10811071
10821072pub fn readlink ( path : & Path ) -> io:: Result < PathBuf > {
0 commit comments