Rollup merge of #129800 - ChrisDenton:remove-dir-all2, r=Amanieu · patricklam/verify-rust-std@e41afdc (original) (raw)
`@@ -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 {
`
``
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 {
`
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 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 {
`
``
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 {
`
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 {
`