@@ -13,6 +13,7 @@ use crate::sys::handle::Handle;
1313use crate :: sys:: time:: SystemTime ;
1414use crate :: sys:: { c, cvt} ;
1515use crate :: sys_common:: { AsInner , FromInner , IntoInner } ;
16+ use crate :: thread;
1617
1718use super :: path:: maybe_verbatim;
1819use super :: to_u16s;
@@ -679,7 +680,7 @@ impl<'a> DirBuffIter<'a> {
679680 }
680681}
681682impl < ' a > Iterator for DirBuffIter < ' a > {
682- type Item = & ' a [ u16 ] ;
683+ type Item = ( & ' a [ u16 ] , bool ) ;
683684 fn next ( & mut self ) -> Option < Self :: Item > {
684685 use crate :: mem:: size_of;
685686 let buffer = & self . buffer ?[ self . cursor ..] ;
@@ -688,14 +689,16 @@ impl<'a> Iterator for DirBuffIter<'a> {
688689 // SAFETY: The buffer contains a `FILE_ID_BOTH_DIR_INFO` struct but the
689690 // last field (the file name) is unsized. So an offset has to be
690691 // used to get the file name slice.
691- let ( name, next_entry) = unsafe {
692+ let ( name, is_directory , next_entry) = unsafe {
692693 let info = buffer. as_ptr ( ) . cast :: < c:: FILE_ID_BOTH_DIR_INFO > ( ) ;
693694 let next_entry = ( * info) . NextEntryOffset as usize ;
694695 let name = crate :: slice:: from_raw_parts (
695696 ( * info) . FileName . as_ptr ( ) . cast :: < u16 > ( ) ,
696697 ( * info) . FileNameLength as usize / size_of :: < u16 > ( ) ,
697698 ) ;
698- ( name, next_entry)
699+ let is_directory = ( ( * info) . FileAttributes & c:: FILE_ATTRIBUTE_DIRECTORY ) != 0 ;
700+
701+ ( name, is_directory, next_entry)
699702 } ;
700703
701704 if next_entry == 0 {
@@ -708,7 +711,7 @@ impl<'a> Iterator for DirBuffIter<'a> {
708711 const DOT : u16 = b'.' as u16 ;
709712 match name {
710713 [ DOT ] | [ DOT , DOT ] => self . next ( ) ,
711- _ => Some ( name) ,
714+ _ => Some ( ( name, is_directory ) ) ,
712715 }
713716 }
714717}
@@ -993,89 +996,92 @@ pub fn remove_dir_all(path: &Path) -> io::Result<()> {
993996 if ( file. basic_info ( ) ?. FileAttributes & c:: FILE_ATTRIBUTE_DIRECTORY ) == 0 {
994997 return Err ( io:: Error :: from_raw_os_error ( c:: ERROR_DIRECTORY as _ ) ) ;
995998 }
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)
10281014 }
10291015 }
1030- // Try one last time.
1031- delete ( & file)
1016+ ok => ok,
10321017 }
10331018}
10341019
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+
10361025 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+ }
10491064 }
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 ) ;
10601075 }
1076+ thread:: yield_now ( ) ;
1077+ } else {
1078+ break ;
10611079 }
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+ }
10731081 }
10741082 }
1075- // Continue reading directory entries without restarting from the beginning,
1076- restart = false ;
10771083 }
1078- delete ( & f )
1084+ Ok ( ( ) )
10791085}
10801086
10811087pub fn readlink ( path : & Path ) -> io:: Result < PathBuf > {
0 commit comments