@@ -2082,6 +2082,16 @@ mod remove_dir_impl {
2082
2082
}
2083
2083
}
2084
2084
2085
+ fn is_enoent ( result : & io:: Result < ( ) > ) -> bool {
2086
+ if let Err ( err) = result
2087
+ && matches ! ( err. raw_os_error( ) , Some ( libc:: ENOENT ) )
2088
+ {
2089
+ true
2090
+ } else {
2091
+ false
2092
+ }
2093
+ }
2094
+
2085
2095
fn remove_dir_all_recursive ( parent_fd : Option < RawFd > , path : & CStr ) -> io:: Result < ( ) > {
2086
2096
// try opening as directory
2087
2097
let fd = match openat_nofollow_dironly ( parent_fd, & path) {
@@ -2097,36 +2107,57 @@ mod remove_dir_impl {
2097
2107
None => Err ( err) ,
2098
2108
} ;
2099
2109
}
2110
+ Err ( err) if matches ! ( err. raw_os_error( ) , Some ( libc:: ENOENT ) ) => {
2111
+ // the file has already been removed by something else,
2112
+ // do nothing.
2113
+ return Ok ( ( ) ) ;
2114
+ }
2100
2115
result => result?,
2101
2116
} ;
2102
2117
2103
- // open the directory passing ownership of the fd
2104
- let ( dir, fd) = fdreaddir ( fd) ?;
2105
- for child in dir {
2106
- let child = child?;
2107
- let child_name = child. name_cstr ( ) ;
2108
- match is_dir ( & child) {
2109
- Some ( true ) => {
2110
- remove_dir_all_recursive ( Some ( fd) , child_name) ?;
2111
- }
2112
- Some ( false ) => {
2113
- cvt ( unsafe { unlinkat ( fd, child_name. as_ptr ( ) , 0 ) } ) ?;
2114
- }
2115
- None => {
2116
- // POSIX specifies that calling unlink()/unlinkat(..., 0) on a directory can succeed
2117
- // if the process has the appropriate privileges. This however can causing orphaned
2118
- // directories requiring an fsck e.g. on Solaris and Illumos. So we try recursing
2119
- // into it first instead of trying to unlink() it.
2120
- remove_dir_all_recursive ( Some ( fd) , child_name) ?;
2118
+ let result: io:: Result < ( ) > = try {
2119
+ // open the directory passing ownership of the fd
2120
+ let ( dir, fd) = fdreaddir ( fd) ?;
2121
+ for child in dir {
2122
+ let child = child?;
2123
+ let child_name = child. name_cstr ( ) ;
2124
+ // we need an inner try block, because if one of these
2125
+ // directories has already been deleted, then we need to
2126
+ // continue the loop, not return ok.
2127
+ let result: io:: Result < ( ) > = try {
2128
+ match is_dir ( & child) {
2129
+ Some ( true ) => {
2130
+ remove_dir_all_recursive ( Some ( fd) , child_name) ?;
2131
+ }
2132
+ Some ( false ) => {
2133
+ cvt ( unsafe { unlinkat ( fd, child_name. as_ptr ( ) , 0 ) } ) ?;
2134
+ }
2135
+ None => {
2136
+ // POSIX specifies that calling unlink()/unlinkat(..., 0) on a directory can succeed
2137
+ // if the process has the appropriate privileges. This however can causing orphaned
2138
+ // directories requiring an fsck e.g. on Solaris and Illumos. So we try recursing
2139
+ // into it first instead of trying to unlink() it.
2140
+ remove_dir_all_recursive ( Some ( fd) , child_name) ?;
2141
+ }
2142
+ }
2143
+ } ;
2144
+ if result. is_err ( ) && !is_enoent ( & result) {
2145
+ return result;
2121
2146
}
2122
2147
}
2123
- }
2124
2148
2125
- // unlink the directory after removing its contents
2126
- cvt ( unsafe {
2127
- unlinkat ( parent_fd. unwrap_or ( libc:: AT_FDCWD ) , path. as_ptr ( ) , libc:: AT_REMOVEDIR )
2128
- } ) ?;
2129
- Ok ( ( ) )
2149
+ // unlink the directory after removing its contents
2150
+ cvt ( unsafe {
2151
+ unlinkat ( parent_fd. unwrap_or ( libc:: AT_FDCWD ) , path. as_ptr ( ) , libc:: AT_REMOVEDIR )
2152
+ } ) ?;
2153
+ } ;
2154
+ if is_enoent ( & result) {
2155
+ // the file has already been removed by something else,
2156
+ // do nothing.
2157
+ Ok ( ( ) )
2158
+ } else {
2159
+ result
2160
+ }
2130
2161
}
2131
2162
2132
2163
fn remove_dir_all_modern ( p : & Path ) -> io:: Result < ( ) > {
0 commit comments