@@ -14,8 +14,11 @@ use crate::sys::handle::Handle;
14
14
use crate :: sys:: path:: maybe_verbatim;
15
15
use crate :: sys:: time:: SystemTime ;
16
16
use crate :: sys:: { c, cvt, Align8 } ;
17
- use crate :: sys_common:: { ignore_notfound, AsInner , FromInner , IntoInner } ;
18
- use crate :: { fmt, ptr, slice, thread} ;
17
+ use crate :: sys_common:: { AsInner , FromInner , IntoInner } ;
18
+ use crate :: { fmt, ptr, slice} ;
19
+
20
+ mod remove_dir_all;
21
+ use remove_dir_all:: remove_dir_all_iterative;
19
22
20
23
pub struct File {
21
24
handle : Handle ,
@@ -646,6 +649,22 @@ impl File {
646
649
Ok ( info)
647
650
}
648
651
}
652
+
653
+ /// Deletes the file, consuming the file handle to ensure the delete occurs
654
+ /// as immediately as possible.
655
+ /// This attempts to use `posix_delete` but falls back to `win32_delete`
656
+ /// if that is not supported by the filesystem.
657
+ #[ allow( unused) ]
658
+ fn delete ( self ) -> Result < ( ) , WinError > {
659
+ // If POSIX delete is not supported for this filesystem then fallback to win32 delete.
660
+ match self . posix_delete ( ) {
661
+ Err ( WinError :: INVALID_PARAMETER )
662
+ | Err ( WinError :: NOT_SUPPORTED )
663
+ | Err ( WinError :: INVALID_FUNCTION ) => self . win32_delete ( ) ,
664
+ result => result,
665
+ }
666
+ }
667
+
649
668
/// Delete using POSIX semantics.
650
669
///
651
670
/// Files will be deleted as soon as the handle is closed. This is supported
@@ -654,21 +673,23 @@ impl File {
654
673
///
655
674
/// If the operation is not supported for this filesystem or OS version
656
675
/// then errors will be `ERROR_NOT_SUPPORTED` or `ERROR_INVALID_PARAMETER`.
657
- fn posix_delete ( & self ) -> io:: Result < ( ) > {
676
+ #[ allow( unused) ]
677
+ fn posix_delete ( & self ) -> Result < ( ) , WinError > {
658
678
let info = c:: FILE_DISPOSITION_INFO_EX {
659
679
Flags : c:: FILE_DISPOSITION_FLAG_DELETE
660
680
| c:: FILE_DISPOSITION_FLAG_POSIX_SEMANTICS
661
681
| c:: FILE_DISPOSITION_FLAG_IGNORE_READONLY_ATTRIBUTE ,
662
682
} ;
663
- api:: set_file_information_by_handle ( self . handle . as_raw_handle ( ) , & info) . io_result ( )
683
+ api:: set_file_information_by_handle ( self . handle . as_raw_handle ( ) , & info)
664
684
}
665
685
666
686
/// Delete a file using win32 semantics. The file won't actually be deleted
667
687
/// until all file handles are closed. However, marking a file for deletion
668
688
/// will prevent anyone from opening a new handle to the file.
669
- fn win32_delete ( & self ) -> io:: Result < ( ) > {
689
+ #[ allow( unused) ]
690
+ fn win32_delete ( & self ) -> Result < ( ) , WinError > {
670
691
let info = c:: FILE_DISPOSITION_INFO { DeleteFile : c:: TRUE as _ } ;
671
- api:: set_file_information_by_handle ( self . handle . as_raw_handle ( ) , & info) . io_result ( )
692
+ api:: set_file_information_by_handle ( self . handle . as_raw_handle ( ) , & info)
672
693
}
673
694
674
695
/// Fill the given buffer with as many directory entries as will fit.
@@ -684,21 +705,23 @@ impl File {
684
705
/// A symlink directory is simply an empty directory with some "reparse" metadata attached.
685
706
/// So if you open a link (not its target) and iterate the directory,
686
707
/// you will always iterate an empty directory regardless of the target.
687
- fn fill_dir_buff ( & self , buffer : & mut DirBuff , restart : bool ) -> io:: Result < bool > {
708
+ #[ allow( unused) ]
709
+ fn fill_dir_buff ( & self , buffer : & mut DirBuff , restart : bool ) -> Result < bool , WinError > {
688
710
let class =
689
711
if restart { c:: FileIdBothDirectoryRestartInfo } else { c:: FileIdBothDirectoryInfo } ;
690
712
691
713
unsafe {
692
- let result = cvt ( c:: GetFileInformationByHandleEx (
693
- self . handle . as_raw_handle ( ) ,
714
+ let result = c:: GetFileInformationByHandleEx (
715
+ self . as_raw_handle ( ) ,
694
716
class,
695
717
buffer. as_mut_ptr ( ) . cast ( ) ,
696
718
buffer. capacity ( ) as _ ,
697
- ) ) ;
698
- match result {
699
- Ok ( _) => Ok ( true ) ,
700
- Err ( e) if e. raw_os_error ( ) == Some ( c:: ERROR_NO_MORE_FILES as _ ) => Ok ( false ) ,
701
- Err ( e) => Err ( e) ,
719
+ ) ;
720
+ if result == 0 {
721
+ let err = api:: get_last_error ( ) ;
722
+ if err. code == c:: ERROR_NO_MORE_FILES { Ok ( false ) } else { Err ( err) }
723
+ } else {
724
+ Ok ( true )
702
725
}
703
726
}
704
727
}
@@ -804,62 +827,6 @@ unsafe fn from_maybe_unaligned<'a>(p: *const u16, len: usize) -> Cow<'a, [u16]>
804
827
}
805
828
}
806
829
807
- /// Open a link relative to the parent directory, ensure no symlinks are followed.
808
- fn open_link_no_reparse ( parent : & File , name : & [ u16 ] , access : u32 ) -> io:: Result < File > {
809
- // This is implemented using the lower level `NtCreateFile` function as
810
- // unfortunately opening a file relative to a parent is not supported by
811
- // win32 functions. It is however a fundamental feature of the NT kernel.
812
- //
813
- // See https://docs.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntcreatefile
814
- unsafe {
815
- let mut handle = ptr:: null_mut ( ) ;
816
- let mut io_status = c:: IO_STATUS_BLOCK :: PENDING ;
817
- let mut name_str = c:: UNICODE_STRING :: from_ref ( name) ;
818
- use crate :: sync:: atomic:: { AtomicU32 , Ordering } ;
819
- // The `OBJ_DONT_REPARSE` attribute ensures that we haven't been
820
- // tricked into following a symlink. However, it may not be available in
821
- // earlier versions of Windows.
822
- static ATTRIBUTES : AtomicU32 = AtomicU32 :: new ( c:: OBJ_DONT_REPARSE ) ;
823
- let object = c:: OBJECT_ATTRIBUTES {
824
- ObjectName : & mut name_str,
825
- RootDirectory : parent. as_raw_handle ( ) ,
826
- Attributes : ATTRIBUTES . load ( Ordering :: Relaxed ) ,
827
- ..c:: OBJECT_ATTRIBUTES :: default ( )
828
- } ;
829
- let status = c:: NtCreateFile (
830
- & mut handle,
831
- access,
832
- & object,
833
- & mut io_status,
834
- crate :: ptr:: null_mut ( ) ,
835
- 0 ,
836
- c:: FILE_SHARE_DELETE | c:: FILE_SHARE_READ | c:: FILE_SHARE_WRITE ,
837
- c:: FILE_OPEN ,
838
- // If `name` is a symlink then open the link rather than the target.
839
- c:: FILE_OPEN_REPARSE_POINT ,
840
- crate :: ptr:: null_mut ( ) ,
841
- 0 ,
842
- ) ;
843
- // Convert an NTSTATUS to the more familiar Win32 error codes (aka "DosError")
844
- if c:: nt_success ( status) {
845
- Ok ( File :: from_raw_handle ( handle) )
846
- } else if status == c:: STATUS_DELETE_PENDING {
847
- // We make a special exception for `STATUS_DELETE_PENDING` because
848
- // otherwise this will be mapped to `ERROR_ACCESS_DENIED` which is
849
- // very unhelpful.
850
- Err ( io:: Error :: from_raw_os_error ( c:: ERROR_DELETE_PENDING as i32 ) )
851
- } else if status == c:: STATUS_INVALID_PARAMETER
852
- && ATTRIBUTES . load ( Ordering :: Relaxed ) == c:: OBJ_DONT_REPARSE
853
- {
854
- // Try without `OBJ_DONT_REPARSE`. See above.
855
- ATTRIBUTES . store ( 0 , Ordering :: Relaxed ) ;
856
- open_link_no_reparse ( parent, name, access)
857
- } else {
858
- Err ( io:: Error :: from_raw_os_error ( c:: RtlNtStatusToDosError ( status) as _ ) )
859
- }
860
- }
861
- }
862
-
863
830
impl AsInner < Handle > for File {
864
831
#[ inline]
865
832
fn as_inner ( & self ) -> & Handle {
@@ -1142,114 +1109,22 @@ pub fn rmdir(p: &Path) -> io::Result<()> {
1142
1109
Ok ( ( ) )
1143
1110
}
1144
1111
1145
- /// Open a file or directory without following symlinks.
1146
- fn open_link ( path : & Path , access_mode : u32 ) -> io :: Result < File > {
1112
+ pub fn remove_dir_all ( path : & Path ) -> io :: Result < ( ) > {
1113
+ // Open a file or directory without following symlinks.
1147
1114
let mut opts = OpenOptions :: new ( ) ;
1148
- opts. access_mode ( access_mode ) ;
1115
+ opts. access_mode ( c :: FILE_LIST_DIRECTORY ) ;
1149
1116
// `FILE_FLAG_BACKUP_SEMANTICS` allows opening directories.
1150
1117
// `FILE_FLAG_OPEN_REPARSE_POINT` opens a link instead of its target.
1151
1118
opts. custom_flags ( c:: FILE_FLAG_BACKUP_SEMANTICS | c:: FILE_FLAG_OPEN_REPARSE_POINT ) ;
1152
- File :: open ( path, & opts)
1153
- }
1154
-
1155
- pub fn remove_dir_all ( path : & Path ) -> io:: Result < ( ) > {
1156
- let file = open_link ( path, c:: DELETE | c:: FILE_LIST_DIRECTORY ) ?;
1119
+ let file = File :: open ( path, & opts) ?;
1157
1120
1158
1121
// Test if the file is not a directory or a symlink to a directory.
1159
1122
if ( file. basic_info ( ) ?. FileAttributes & c:: FILE_ATTRIBUTE_DIRECTORY ) == 0 {
1160
1123
return Err ( io:: Error :: from_raw_os_error ( c:: ERROR_DIRECTORY as _ ) ) ;
1161
1124
}
1162
1125
1163
- match ignore_notfound ( remove_dir_all_iterative ( & file, File :: posix_delete) ) {
1164
- Err ( e) => {
1165
- if let Some ( code) = e. raw_os_error ( ) {
1166
- match code as u32 {
1167
- // If POSIX delete is not supported for this filesystem then fallback to win32 delete.
1168
- c:: ERROR_NOT_SUPPORTED
1169
- | c:: ERROR_INVALID_FUNCTION
1170
- | c:: ERROR_INVALID_PARAMETER => {
1171
- remove_dir_all_iterative ( & file, File :: win32_delete)
1172
- }
1173
- _ => Err ( e) ,
1174
- }
1175
- } else {
1176
- Err ( e)
1177
- }
1178
- }
1179
- ok => ok,
1180
- }
1181
- }
1182
-
1183
- fn remove_dir_all_iterative ( f : & File , delete : fn ( & File ) -> io:: Result < ( ) > ) -> io:: Result < ( ) > {
1184
- // When deleting files we may loop this many times when certain error conditions occur.
1185
- // This allows remove_dir_all to succeed when the error is temporary.
1186
- const MAX_RETRIES : u32 = 10 ;
1187
-
1188
- let mut buffer = DirBuff :: new ( ) ;
1189
- let mut dirlist = vec ! [ f. duplicate( ) ?] ;
1190
-
1191
- // FIXME: This is a hack so we can push to the dirlist vec after borrowing from it.
1192
- fn copy_handle ( f : & File ) -> mem:: ManuallyDrop < File > {
1193
- unsafe { mem:: ManuallyDrop :: new ( File :: from_raw_handle ( f. as_raw_handle ( ) ) ) }
1194
- }
1195
-
1196
- let mut restart = true ;
1197
- while let Some ( dir) = dirlist. last ( ) {
1198
- let dir = copy_handle ( dir) ;
1199
-
1200
- // Fill the buffer and iterate the entries.
1201
- let more_data = dir. fill_dir_buff ( & mut buffer, restart) ?;
1202
- restart = false ;
1203
- for ( name, is_directory) in buffer. iter ( ) {
1204
- if is_directory {
1205
- let child_dir = open_link_no_reparse (
1206
- & dir,
1207
- & name,
1208
- c:: SYNCHRONIZE | c:: DELETE | c:: FILE_LIST_DIRECTORY ,
1209
- ) ;
1210
- // On success, add the handle to the queue.
1211
- // If opening the directory fails we treat it the same as a file
1212
- if let Ok ( child_dir) = child_dir {
1213
- dirlist. push ( child_dir) ;
1214
- continue ;
1215
- }
1216
- }
1217
- for i in 1 ..=MAX_RETRIES {
1218
- let result = open_link_no_reparse ( & dir, & name, c:: SYNCHRONIZE | c:: DELETE ) ;
1219
- match result {
1220
- Ok ( f) => delete ( & f) ?,
1221
- // Already deleted, so skip.
1222
- Err ( e) if e. kind ( ) == io:: ErrorKind :: NotFound => break ,
1223
- // Retry a few times if the file is locked or a delete is already in progress.
1224
- Err ( e)
1225
- if i < MAX_RETRIES
1226
- && ( e. raw_os_error ( ) == Some ( c:: ERROR_DELETE_PENDING as _ )
1227
- || e. raw_os_error ( ) == Some ( c:: ERROR_SHARING_VIOLATION as _ ) ) => { }
1228
- // Otherwise return the error.
1229
- Err ( e) => return Err ( e) ,
1230
- }
1231
- thread:: yield_now ( ) ;
1232
- }
1233
- }
1234
- // If there were no more files then delete the directory.
1235
- if !more_data {
1236
- if let Some ( dir) = dirlist. pop ( ) {
1237
- // Retry deleting a few times in case we need to wait for a file to be deleted.
1238
- for i in 1 ..=MAX_RETRIES {
1239
- let result = delete ( & dir) ;
1240
- if let Err ( e) = result {
1241
- if i == MAX_RETRIES || e. kind ( ) != io:: ErrorKind :: DirectoryNotEmpty {
1242
- return Err ( e) ;
1243
- }
1244
- thread:: yield_now ( ) ;
1245
- } else {
1246
- break ;
1247
- }
1248
- }
1249
- }
1250
- }
1251
- }
1252
- Ok ( ( ) )
1126
+ // Remove the directory and all its contents.
1127
+ remove_dir_all_iterative ( file) . io_result ( )
1253
1128
}
1254
1129
1255
1130
pub fn readlink ( path : & Path ) -> io:: Result < PathBuf > {
0 commit comments