@@ -14,8 +14,11 @@ use crate::sys::handle::Handle;
1414use crate :: sys:: path:: maybe_verbatim;
1515use crate :: sys:: time:: SystemTime ;
1616use 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;
1922
2023pub struct File {
2124 handle : Handle ,
@@ -646,6 +649,22 @@ impl File {
646649 Ok ( info)
647650 }
648651 }
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+
649668 /// Delete using POSIX semantics.
650669 ///
651670 /// Files will be deleted as soon as the handle is closed. This is supported
@@ -654,21 +673,23 @@ impl File {
654673 ///
655674 /// If the operation is not supported for this filesystem or OS version
656675 /// 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 > {
658678 let info = c:: FILE_DISPOSITION_INFO_EX {
659679 Flags : c:: FILE_DISPOSITION_FLAG_DELETE
660680 | c:: FILE_DISPOSITION_FLAG_POSIX_SEMANTICS
661681 | c:: FILE_DISPOSITION_FLAG_IGNORE_READONLY_ATTRIBUTE ,
662682 } ;
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)
664684 }
665685
666686 /// Delete a file using win32 semantics. The file won't actually be deleted
667687 /// until all file handles are closed. However, marking a file for deletion
668688 /// 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 > {
670691 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)
672693 }
673694
674695 /// Fill the given buffer with as many directory entries as will fit.
@@ -684,21 +705,23 @@ impl File {
684705 /// A symlink directory is simply an empty directory with some "reparse" metadata attached.
685706 /// So if you open a link (not its target) and iterate the directory,
686707 /// 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 > {
688710 let class =
689711 if restart { c:: FileIdBothDirectoryRestartInfo } else { c:: FileIdBothDirectoryInfo } ;
690712
691713 unsafe {
692- let result = cvt ( c:: GetFileInformationByHandleEx (
693- self . handle . as_raw_handle ( ) ,
714+ let result = c:: GetFileInformationByHandleEx (
715+ self . as_raw_handle ( ) ,
694716 class,
695717 buffer. as_mut_ptr ( ) . cast ( ) ,
696718 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 )
702725 }
703726 }
704727 }
@@ -804,62 +827,6 @@ unsafe fn from_maybe_unaligned<'a>(p: *const u16, len: usize) -> Cow<'a, [u16]>
804827 }
805828}
806829
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-
863830impl AsInner < Handle > for File {
864831 #[ inline]
865832 fn as_inner ( & self ) -> & Handle {
@@ -1142,114 +1109,22 @@ pub fn rmdir(p: &Path) -> io::Result<()> {
11421109 Ok ( ( ) )
11431110}
11441111
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.
11471114 let mut opts = OpenOptions :: new ( ) ;
1148- opts. access_mode ( access_mode ) ;
1115+ opts. access_mode ( c :: FILE_LIST_DIRECTORY ) ;
11491116 // `FILE_FLAG_BACKUP_SEMANTICS` allows opening directories.
11501117 // `FILE_FLAG_OPEN_REPARSE_POINT` opens a link instead of its target.
11511118 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) ?;
11571120
11581121 // Test if the file is not a directory or a symlink to a directory.
11591122 if ( file. basic_info ( ) ?. FileAttributes & c:: FILE_ATTRIBUTE_DIRECTORY ) == 0 {
11601123 return Err ( io:: Error :: from_raw_os_error ( c:: ERROR_DIRECTORY as _ ) ) ;
11611124 }
11621125
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 ( )
12531128}
12541129
12551130pub fn readlink ( path : & Path ) -> io:: Result < PathBuf > {
0 commit comments