about summary refs log tree commit diff
path: root/library/std/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-09-03 05:44:18 +0000
committerbors <bors@rust-lang.org>2024-09-03 05:44:18 +0000
commitd6c8169c186ab16a3404cd0d0866674018e8a19e (patch)
tree712ddccb4dc768c899d4ae0c5040a55fc0da68fe /library/std/src
parent6199b69c53a8c275ca3cd59647ea0af5ca29aae2 (diff)
parent29bcf8062a95a4e48cc69fe963dd08e8106df270 (diff)
downloadrust-d6c8169c186ab16a3404cd0d0866674018e8a19e.tar.gz
rust-d6c8169c186ab16a3404cd0d0866674018e8a19e.zip
Auto merge of #129922 - matthiaskrgr:rollup-4vqx8ct, r=matthiaskrgr
Rollup of 8 pull requests

Successful merges:

 - #129152 (custom/external clippy support for bootstrapping)
 - #129311 (don't copy `.rustc-dev-contents` from CI rustc)
 - #129800 (Move the Windows remove_dir_all impl into a module and make it more race resistant)
 - #129860 (update `object` dependency to remove duplicate `wasmparser`)
 - #129885 (chore: remove repetitive words)
 - #129913 (Add missing read_buf stub for x86_64-unknown-l4re-uclibc)
 - #129916 (process.rs: remove "Basic usage" text where not useful)
 - #129917 (Fix parsing of beta version in dry-run mode)

r? `@ghost`
`@rustbot` modify labels: rollup
Diffstat (limited to 'library/std/src')
-rw-r--r--library/std/src/process.rs36
-rw-r--r--library/std/src/sync/reentrant_lock.rs2
-rw-r--r--library/std/src/sys/pal/unix/l4re.rs4
-rw-r--r--library/std/src/sys/pal/windows/api.rs5
-rw-r--r--library/std/src/sys/pal/windows/c/bindings.txt5
-rw-r--r--library/std/src/sys/pal/windows/c/windows_sys.rs5
-rw-r--r--library/std/src/sys/pal/windows/fs.rs211
-rw-r--r--library/std/src/sys/pal/windows/fs/remove_dir_all.rs196
8 files changed, 258 insertions, 206 deletions
diff --git a/library/std/src/process.rs b/library/std/src/process.rs
index bbea27ebc10..a155855029e 100644
--- a/library/std/src/process.rs
+++ b/library/std/src/process.rs
@@ -617,8 +617,6 @@ impl Command {
     ///
     /// # Examples
     ///
-    /// Basic usage:
-    ///
     /// ```no_run
     /// use std::process::Command;
     ///
@@ -699,8 +697,6 @@ impl Command {
     ///
     /// # Examples
     ///
-    /// Basic usage:
-    ///
     /// ```no_run
     /// use std::process::Command;
     ///
@@ -748,8 +744,6 @@ impl Command {
     ///
     /// # Examples
     ///
-    /// Basic usage:
-    ///
     /// ```no_run
     /// use std::process::Command;
     ///
@@ -786,8 +780,6 @@ impl Command {
     ///
     /// # Examples
     ///
-    /// Basic usage:
-    ///
     /// ```no_run
     /// use std::process::Command;
     ///
@@ -822,8 +814,6 @@ impl Command {
     ///
     /// # Examples
     ///
-    /// Basic usage:
-    ///
     /// ```no_run
     /// use std::process::{Command, Stdio};
     /// use std::env;
@@ -870,8 +860,6 @@ impl Command {
     ///
     /// # Examples
     ///
-    /// Basic usage:
-    ///
     /// ```no_run
     /// use std::process::Command;
     ///
@@ -900,8 +888,6 @@ impl Command {
     ///
     /// # Examples
     ///
-    /// Basic usage:
-    ///
     /// ```no_run
     /// use std::process::Command;
     ///
@@ -928,8 +914,6 @@ impl Command {
     ///
     /// # Examples
     ///
-    /// Basic usage:
-    ///
     /// ```no_run
     /// use std::process::Command;
     ///
@@ -959,8 +943,6 @@ impl Command {
     ///
     /// # Examples
     ///
-    /// Basic usage:
-    ///
     /// ```no_run
     /// use std::process::{Command, Stdio};
     ///
@@ -988,8 +970,6 @@ impl Command {
     ///
     /// # Examples
     ///
-    /// Basic usage:
-    ///
     /// ```no_run
     /// use std::process::{Command, Stdio};
     ///
@@ -1017,8 +997,6 @@ impl Command {
     ///
     /// # Examples
     ///
-    /// Basic usage:
-    ///
     /// ```no_run
     /// use std::process::{Command, Stdio};
     ///
@@ -1039,8 +1017,6 @@ impl Command {
     ///
     /// # Examples
     ///
-    /// Basic usage:
-    ///
     /// ```no_run
     /// use std::process::Command;
     ///
@@ -2105,8 +2081,6 @@ impl Child {
     ///
     /// # Examples
     ///
-    /// Basic usage:
-    ///
     /// ```no_run
     /// use std::process::Command;
     ///
@@ -2129,8 +2103,6 @@ impl Child {
     ///
     /// # Examples
     ///
-    /// Basic usage:
-    ///
     /// ```no_run
     /// use std::process::Command;
     ///
@@ -2158,8 +2130,6 @@ impl Child {
     ///
     /// # Examples
     ///
-    /// Basic usage:
-    ///
     /// ```no_run
     /// use std::process::Command;
     ///
@@ -2194,8 +2164,6 @@ impl Child {
     ///
     /// # Examples
     ///
-    /// Basic usage:
-    ///
     /// ```no_run
     /// use std::process::Command;
     ///
@@ -2398,15 +2366,11 @@ pub fn abort() -> ! {
 ///
 /// # Examples
 ///
-/// Basic usage:
-///
 /// ```no_run
 /// use std::process;
 ///
 /// println!("My pid is {}", process::id());
 /// ```
-///
-///
 #[must_use]
 #[stable(feature = "getpid", since = "1.26.0")]
 pub fn id() -> u32 {
diff --git a/library/std/src/sync/reentrant_lock.rs b/library/std/src/sync/reentrant_lock.rs
index 84a0b36db17..0b23681e907 100644
--- a/library/std/src/sync/reentrant_lock.rs
+++ b/library/std/src/sync/reentrant_lock.rs
@@ -136,7 +136,7 @@ cfg_if!(
             // match do we read out the actual TID.
             // Note also that we can use relaxed atomic operations here, because
             // we only ever read from the tid if `tls_addr` matches the current
-            // TLS address. In that case, either the the tid has been set by
+            // TLS address. In that case, either the tid has been set by
             // the current thread, or by a thread that has terminated before
             // the current thread was created. In either case, no further
             // synchronization is needed (as per <https://github.com/rust-lang/miri/issues/3450>)
diff --git a/library/std/src/sys/pal/unix/l4re.rs b/library/std/src/sys/pal/unix/l4re.rs
index fe9559f2a56..52d39dcfb16 100644
--- a/library/std/src/sys/pal/unix/l4re.rs
+++ b/library/std/src/sys/pal/unix/l4re.rs
@@ -54,6 +54,10 @@ pub mod net {
             unimpl!();
         }
 
+        pub fn read_buf(&self, _: BorrowedCursor<'_>) -> io::Result<()> {
+            unimpl!();
+        }
+
         pub fn read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
             unimpl!();
         }
diff --git a/library/std/src/sys/pal/windows/api.rs b/library/std/src/sys/pal/windows/api.rs
index 00c816a6c09..9e336ff2d47 100644
--- a/library/std/src/sys/pal/windows/api.rs
+++ b/library/std/src/sys/pal/windows/api.rs
@@ -254,7 +254,7 @@ pub struct WinError {
     pub code: u32,
 }
 impl WinError {
-    const fn new(code: u32) -> Self {
+    pub const fn new(code: u32) -> Self {
         Self { code }
     }
 }
@@ -272,8 +272,11 @@ impl WinError {
     // tidy-alphabetical-start
     pub const ACCESS_DENIED: Self = Self::new(c::ERROR_ACCESS_DENIED);
     pub const ALREADY_EXISTS: Self = Self::new(c::ERROR_ALREADY_EXISTS);
+    pub const BAD_NET_NAME: Self = Self::new(c::ERROR_BAD_NET_NAME);
+    pub const BAD_NETPATH: Self = Self::new(c::ERROR_BAD_NETPATH);
     pub const CANT_ACCESS_FILE: Self = Self::new(c::ERROR_CANT_ACCESS_FILE);
     pub const DELETE_PENDING: Self = Self::new(c::ERROR_DELETE_PENDING);
+    pub const DIR_NOT_EMPTY: Self = Self::new(c::ERROR_DIR_NOT_EMPTY);
     pub const DIRECTORY: Self = Self::new(c::ERROR_DIRECTORY);
     pub const FILE_NOT_FOUND: Self = Self::new(c::ERROR_FILE_NOT_FOUND);
     pub const INSUFFICIENT_BUFFER: Self = Self::new(c::ERROR_INSUFFICIENT_BUFFER);
diff --git a/library/std/src/sys/pal/windows/c/bindings.txt b/library/std/src/sys/pal/windows/c/bindings.txt
index afacc370c34..9c2e4500da0 100644
--- a/library/std/src/sys/pal/windows/c/bindings.txt
+++ b/library/std/src/sys/pal/windows/c/bindings.txt
@@ -34,6 +34,7 @@ Windows.Wdk.Storage.FileSystem.FILE_WRITE_THROUGH
 Windows.Wdk.Storage.FileSystem.NtCreateFile
 Windows.Wdk.Storage.FileSystem.NTCREATEFILE_CREATE_DISPOSITION
 Windows.Wdk.Storage.FileSystem.NTCREATEFILE_CREATE_OPTIONS
+Windows.Wdk.Storage.FileSystem.NtOpenFile
 Windows.Wdk.Storage.FileSystem.NtReadFile
 Windows.Wdk.Storage.FileSystem.NtWriteFile
 Windows.Wdk.Storage.FileSystem.SYMLINK_FLAG_RELATIVE
@@ -1931,10 +1932,14 @@ Windows.Win32.Foundation.RtlNtStatusToDosError
 Windows.Win32.Foundation.SetHandleInformation
 Windows.Win32.Foundation.SetLastError
 Windows.Win32.Foundation.STATUS_DELETE_PENDING
+Windows.Win32.Foundation.STATUS_DIRECTORY_NOT_EMPTY
 Windows.Win32.Foundation.STATUS_END_OF_FILE
+Windows.Win32.Foundation.STATUS_FILE_DELETED
+Windows.Win32.Foundation.STATUS_INVALID_HANDLE
 Windows.Win32.Foundation.STATUS_INVALID_PARAMETER
 Windows.Win32.Foundation.STATUS_NOT_IMPLEMENTED
 Windows.Win32.Foundation.STATUS_PENDING
+Windows.Win32.Foundation.STATUS_SHARING_VIOLATION
 Windows.Win32.Foundation.STATUS_SUCCESS
 Windows.Win32.Foundation.TRUE
 Windows.Win32.Foundation.UNICODE_STRING
diff --git a/library/std/src/sys/pal/windows/c/windows_sys.rs b/library/std/src/sys/pal/windows/c/windows_sys.rs
index 529c96a0e1e..ab5f8919d7a 100644
--- a/library/std/src/sys/pal/windows/c/windows_sys.rs
+++ b/library/std/src/sys/pal/windows/c/windows_sys.rs
@@ -105,6 +105,7 @@ windows_targets::link!("kernel32.dll" "system" fn WideCharToMultiByte(codepage :
 windows_targets::link!("kernel32.dll" "system" fn WriteConsoleW(hconsoleoutput : HANDLE, lpbuffer : PCWSTR, nnumberofcharstowrite : u32, lpnumberofcharswritten : *mut u32, lpreserved : *const core::ffi::c_void) -> BOOL);
 windows_targets::link!("kernel32.dll" "system" fn WriteFileEx(hfile : HANDLE, lpbuffer : *const u8, nnumberofbytestowrite : u32, lpoverlapped : *mut OVERLAPPED, lpcompletionroutine : LPOVERLAPPED_COMPLETION_ROUTINE) -> BOOL);
 windows_targets::link!("ntdll.dll" "system" fn NtCreateFile(filehandle : *mut HANDLE, desiredaccess : FILE_ACCESS_RIGHTS, objectattributes : *const OBJECT_ATTRIBUTES, iostatusblock : *mut IO_STATUS_BLOCK, allocationsize : *const i64, fileattributes : FILE_FLAGS_AND_ATTRIBUTES, shareaccess : FILE_SHARE_MODE, createdisposition : NTCREATEFILE_CREATE_DISPOSITION, createoptions : NTCREATEFILE_CREATE_OPTIONS, eabuffer : *const core::ffi::c_void, ealength : u32) -> NTSTATUS);
+windows_targets::link!("ntdll.dll" "system" fn NtOpenFile(filehandle : *mut HANDLE, desiredaccess : u32, objectattributes : *const OBJECT_ATTRIBUTES, iostatusblock : *mut IO_STATUS_BLOCK, shareaccess : u32, openoptions : u32) -> NTSTATUS);
 windows_targets::link!("ntdll.dll" "system" fn NtReadFile(filehandle : HANDLE, event : HANDLE, apcroutine : PIO_APC_ROUTINE, apccontext : *const core::ffi::c_void, iostatusblock : *mut IO_STATUS_BLOCK, buffer : *mut core::ffi::c_void, length : u32, byteoffset : *const i64, key : *const u32) -> NTSTATUS);
 windows_targets::link!("ntdll.dll" "system" fn NtWriteFile(filehandle : HANDLE, event : HANDLE, apcroutine : PIO_APC_ROUTINE, apccontext : *const core::ffi::c_void, iostatusblock : *mut IO_STATUS_BLOCK, buffer : *const core::ffi::c_void, length : u32, byteoffset : *const i64, key : *const u32) -> NTSTATUS);
 windows_targets::link!("ntdll.dll" "system" fn RtlNtStatusToDosError(status : NTSTATUS) -> u32);
@@ -2982,10 +2983,14 @@ pub struct STARTUPINFOW {
 }
 pub type STARTUPINFOW_FLAGS = u32;
 pub const STATUS_DELETE_PENDING: NTSTATUS = 0xC0000056_u32 as _;
+pub const STATUS_DIRECTORY_NOT_EMPTY: NTSTATUS = 0xC0000101_u32 as _;
 pub const STATUS_END_OF_FILE: NTSTATUS = 0xC0000011_u32 as _;
+pub const STATUS_FILE_DELETED: NTSTATUS = 0xC0000123_u32 as _;
+pub const STATUS_INVALID_HANDLE: NTSTATUS = 0xC0000008_u32 as _;
 pub const STATUS_INVALID_PARAMETER: NTSTATUS = 0xC000000D_u32 as _;
 pub const STATUS_NOT_IMPLEMENTED: NTSTATUS = 0xC0000002_u32 as _;
 pub const STATUS_PENDING: NTSTATUS = 0x103_u32 as _;
+pub const STATUS_SHARING_VIOLATION: NTSTATUS = 0xC0000043_u32 as _;
 pub const STATUS_SUCCESS: NTSTATUS = 0x0_u32 as _;
 pub const STD_ERROR_HANDLE: STD_HANDLE = 4294967284u32;
 pub type STD_HANDLE = u32;
diff --git a/library/std/src/sys/pal/windows/fs.rs b/library/std/src/sys/pal/windows/fs.rs
index 2134152ea93..5b360640c4e 100644
--- a/library/std/src/sys/pal/windows/fs.rs
+++ b/library/std/src/sys/pal/windows/fs.rs
@@ -14,8 +14,11 @@ use crate::sys::handle::Handle;
 use crate::sys::path::maybe_verbatim;
 use crate::sys::time::SystemTime;
 use crate::sys::{c, cvt, Align8};
-use crate::sys_common::{ignore_notfound, AsInner, FromInner, IntoInner};
-use crate::{fmt, ptr, slice, thread};
+use crate::sys_common::{AsInner, FromInner, IntoInner};
+use crate::{fmt, ptr, slice};
+
+mod remove_dir_all;
+use remove_dir_all::remove_dir_all_iterative;
 
 pub struct File {
     handle: Handle,
@@ -646,6 +649,22 @@ impl File {
             Ok(info)
         }
     }
+
+    /// Deletes the file, consuming the file handle to ensure the delete occurs
+    /// as immediately as possible.
+    /// This attempts to use `posix_delete` but falls back to `win32_delete`
+    /// if that is not supported by the filesystem.
+    #[allow(unused)]
+    fn delete(self) -> Result<(), WinError> {
+        // If POSIX delete is not supported for this filesystem then fallback to win32 delete.
+        match self.posix_delete() {
+            Err(WinError::INVALID_PARAMETER)
+            | Err(WinError::NOT_SUPPORTED)
+            | Err(WinError::INVALID_FUNCTION) => self.win32_delete(),
+            result => result,
+        }
+    }
+
     /// Delete using POSIX semantics.
     ///
     /// Files will be deleted as soon as the handle is closed. This is supported
@@ -654,21 +673,23 @@ impl File {
     ///
     /// If the operation is not supported for this filesystem or OS version
     /// then errors will be `ERROR_NOT_SUPPORTED` or `ERROR_INVALID_PARAMETER`.
-    fn posix_delete(&self) -> io::Result<()> {
+    #[allow(unused)]
+    fn posix_delete(&self) -> Result<(), WinError> {
         let info = c::FILE_DISPOSITION_INFO_EX {
             Flags: c::FILE_DISPOSITION_FLAG_DELETE
                 | c::FILE_DISPOSITION_FLAG_POSIX_SEMANTICS
                 | c::FILE_DISPOSITION_FLAG_IGNORE_READONLY_ATTRIBUTE,
         };
-        api::set_file_information_by_handle(self.handle.as_raw_handle(), &info).io_result()
+        api::set_file_information_by_handle(self.handle.as_raw_handle(), &info)
     }
 
     /// Delete a file using win32 semantics. The file won't actually be deleted
     /// until all file handles are closed. However, marking a file for deletion
     /// will prevent anyone from opening a new handle to the file.
-    fn win32_delete(&self) -> io::Result<()> {
+    #[allow(unused)]
+    fn win32_delete(&self) -> Result<(), WinError> {
         let info = c::FILE_DISPOSITION_INFO { DeleteFile: c::TRUE as _ };
-        api::set_file_information_by_handle(self.handle.as_raw_handle(), &info).io_result()
+        api::set_file_information_by_handle(self.handle.as_raw_handle(), &info)
     }
 
     /// Fill the given buffer with as many directory entries as will fit.
@@ -684,21 +705,23 @@ impl File {
     /// A symlink directory is simply an empty directory with some "reparse" metadata attached.
     /// So if you open a link (not its target) and iterate the directory,
     /// you will always iterate an empty directory regardless of the target.
-    fn fill_dir_buff(&self, buffer: &mut DirBuff, restart: bool) -> io::Result<bool> {
+    #[allow(unused)]
+    fn fill_dir_buff(&self, buffer: &mut DirBuff, restart: bool) -> Result<bool, WinError> {
         let class =
             if restart { c::FileIdBothDirectoryRestartInfo } else { c::FileIdBothDirectoryInfo };
 
         unsafe {
-            let result = cvt(c::GetFileInformationByHandleEx(
-                self.handle.as_raw_handle(),
+            let result = c::GetFileInformationByHandleEx(
+                self.as_raw_handle(),
                 class,
                 buffer.as_mut_ptr().cast(),
                 buffer.capacity() as _,
-            ));
-            match result {
-                Ok(_) => Ok(true),
-                Err(e) if e.raw_os_error() == Some(c::ERROR_NO_MORE_FILES as _) => Ok(false),
-                Err(e) => Err(e),
+            );
+            if result == 0 {
+                let err = api::get_last_error();
+                if err.code == c::ERROR_NO_MORE_FILES { Ok(false) } else { Err(err) }
+            } else {
+                Ok(true)
             }
         }
     }
@@ -804,62 +827,6 @@ unsafe fn from_maybe_unaligned<'a>(p: *const u16, len: usize) -> Cow<'a, [u16]>
     }
 }
 
-/// Open a link relative to the parent directory, ensure no symlinks are followed.
-fn open_link_no_reparse(parent: &File, name: &[u16], access: u32) -> io::Result<File> {
-    // This is implemented using the lower level `NtCreateFile` function as
-    // unfortunately opening a file relative to a parent is not supported by
-    // win32 functions. It is however a fundamental feature of the NT kernel.
-    //
-    // See https://docs.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntcreatefile
-    unsafe {
-        let mut handle = ptr::null_mut();
-        let mut io_status = c::IO_STATUS_BLOCK::PENDING;
-        let mut name_str = c::UNICODE_STRING::from_ref(name);
-        use crate::sync::atomic::{AtomicU32, Ordering};
-        // The `OBJ_DONT_REPARSE` attribute ensures that we haven't been
-        // tricked into following a symlink. However, it may not be available in
-        // earlier versions of Windows.
-        static ATTRIBUTES: AtomicU32 = AtomicU32::new(c::OBJ_DONT_REPARSE);
-        let object = c::OBJECT_ATTRIBUTES {
-            ObjectName: &mut name_str,
-            RootDirectory: parent.as_raw_handle(),
-            Attributes: ATTRIBUTES.load(Ordering::Relaxed),
-            ..c::OBJECT_ATTRIBUTES::default()
-        };
-        let status = c::NtCreateFile(
-            &mut handle,
-            access,
-            &object,
-            &mut io_status,
-            crate::ptr::null_mut(),
-            0,
-            c::FILE_SHARE_DELETE | c::FILE_SHARE_READ | c::FILE_SHARE_WRITE,
-            c::FILE_OPEN,
-            // If `name` is a symlink then open the link rather than the target.
-            c::FILE_OPEN_REPARSE_POINT,
-            crate::ptr::null_mut(),
-            0,
-        );
-        // Convert an NTSTATUS to the more familiar Win32 error codes (aka "DosError")
-        if c::nt_success(status) {
-            Ok(File::from_raw_handle(handle))
-        } else if status == c::STATUS_DELETE_PENDING {
-            // We make a special exception for `STATUS_DELETE_PENDING` because
-            // otherwise this will be mapped to `ERROR_ACCESS_DENIED` which is
-            // very unhelpful.
-            Err(io::Error::from_raw_os_error(c::ERROR_DELETE_PENDING as i32))
-        } else if status == c::STATUS_INVALID_PARAMETER
-            && ATTRIBUTES.load(Ordering::Relaxed) == c::OBJ_DONT_REPARSE
-        {
-            // Try without `OBJ_DONT_REPARSE`. See above.
-            ATTRIBUTES.store(0, Ordering::Relaxed);
-            open_link_no_reparse(parent, name, access)
-        } else {
-            Err(io::Error::from_raw_os_error(c::RtlNtStatusToDosError(status) as _))
-        }
-    }
-}
-
 impl AsInner<Handle> for File {
     #[inline]
     fn as_inner(&self) -> &Handle {
@@ -1142,114 +1109,22 @@ pub fn rmdir(p: &Path) -> io::Result<()> {
     Ok(())
 }
 
-/// Open a file or directory without following symlinks.
-fn open_link(path: &Path, access_mode: u32) -> io::Result<File> {
+pub fn remove_dir_all(path: &Path) -> io::Result<()> {
+    // Open a file or directory without following symlinks.
     let mut opts = OpenOptions::new();
-    opts.access_mode(access_mode);
+    opts.access_mode(c::FILE_LIST_DIRECTORY);
     // `FILE_FLAG_BACKUP_SEMANTICS` allows opening directories.
     // `FILE_FLAG_OPEN_REPARSE_POINT` opens a link instead of its target.
     opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS | c::FILE_FLAG_OPEN_REPARSE_POINT);
-    File::open(path, &opts)
-}
-
-pub fn remove_dir_all(path: &Path) -> io::Result<()> {
-    let file = open_link(path, c::DELETE | c::FILE_LIST_DIRECTORY)?;
+    let file = File::open(path, &opts)?;
 
     // Test if the file is not a directory or a symlink to a directory.
     if (file.basic_info()?.FileAttributes & c::FILE_ATTRIBUTE_DIRECTORY) == 0 {
         return Err(io::Error::from_raw_os_error(c::ERROR_DIRECTORY as _));
     }
 
-    match ignore_notfound(remove_dir_all_iterative(&file, File::posix_delete)) {
-        Err(e) => {
-            if let Some(code) = e.raw_os_error() {
-                match code as u32 {
-                    // If POSIX delete is not supported for this filesystem then fallback to win32 delete.
-                    c::ERROR_NOT_SUPPORTED
-                    | c::ERROR_INVALID_FUNCTION
-                    | c::ERROR_INVALID_PARAMETER => {
-                        remove_dir_all_iterative(&file, File::win32_delete)
-                    }
-                    _ => Err(e),
-                }
-            } else {
-                Err(e)
-            }
-        }
-        ok => ok,
-    }
-}
-
-fn remove_dir_all_iterative(f: &File, delete: fn(&File) -> io::Result<()>) -> io::Result<()> {
-    // When deleting files we may loop this many times when certain error conditions occur.
-    // This allows remove_dir_all to succeed when the error is temporary.
-    const MAX_RETRIES: u32 = 10;
-
-    let mut buffer = DirBuff::new();
-    let mut dirlist = vec![f.duplicate()?];
-
-    // FIXME: This is a hack so we can push to the dirlist vec after borrowing from it.
-    fn copy_handle(f: &File) -> mem::ManuallyDrop<File> {
-        unsafe { mem::ManuallyDrop::new(File::from_raw_handle(f.as_raw_handle())) }
-    }
-
-    let mut restart = true;
-    while let Some(dir) = dirlist.last() {
-        let dir = copy_handle(dir);
-
-        // Fill the buffer and iterate the entries.
-        let more_data = dir.fill_dir_buff(&mut buffer, restart)?;
-        restart = false;
-        for (name, is_directory) in buffer.iter() {
-            if is_directory {
-                let child_dir = open_link_no_reparse(
-                    &dir,
-                    &name,
-                    c::SYNCHRONIZE | c::DELETE | c::FILE_LIST_DIRECTORY,
-                );
-                // On success, add the handle to the queue.
-                // If opening the directory fails we treat it the same as a file
-                if let Ok(child_dir) = child_dir {
-                    dirlist.push(child_dir);
-                    continue;
-                }
-            }
-            for i in 1..=MAX_RETRIES {
-                let result = open_link_no_reparse(&dir, &name, c::SYNCHRONIZE | c::DELETE);
-                match result {
-                    Ok(f) => delete(&f)?,
-                    // Already deleted, so skip.
-                    Err(e) if e.kind() == io::ErrorKind::NotFound => break,
-                    // Retry a few times if the file is locked or a delete is already in progress.
-                    Err(e)
-                        if i < MAX_RETRIES
-                            && (e.raw_os_error() == Some(c::ERROR_DELETE_PENDING as _)
-                                || e.raw_os_error() == Some(c::ERROR_SHARING_VIOLATION as _)) => {}
-                    // Otherwise return the error.
-                    Err(e) => return Err(e),
-                }
-                thread::yield_now();
-            }
-        }
-        // If there were no more files then delete the directory.
-        if !more_data {
-            if let Some(dir) = dirlist.pop() {
-                // Retry deleting a few times in case we need to wait for a file to be deleted.
-                for i in 1..=MAX_RETRIES {
-                    let result = delete(&dir);
-                    if let Err(e) = result {
-                        if i == MAX_RETRIES || e.kind() != io::ErrorKind::DirectoryNotEmpty {
-                            return Err(e);
-                        }
-                        thread::yield_now();
-                    } else {
-                        break;
-                    }
-                }
-            }
-        }
-    }
-    Ok(())
+    // Remove the directory and all its contents.
+    remove_dir_all_iterative(file).io_result()
 }
 
 pub fn readlink(path: &Path) -> io::Result<PathBuf> {
diff --git a/library/std/src/sys/pal/windows/fs/remove_dir_all.rs b/library/std/src/sys/pal/windows/fs/remove_dir_all.rs
new file mode 100644
index 00000000000..e7234ed8e5f
--- /dev/null
+++ b/library/std/src/sys/pal/windows/fs/remove_dir_all.rs
@@ -0,0 +1,196 @@
+//! The Windows implementation of std::fs::remove_dir_all.
+//!
+//! This needs to address two issues:
+//!
+//! - It must not be possible to trick this into deleting files outside of
+//!   the parent directory (see CVE-2022-21658).
+//! - It should not fail if many threads or processes call `remove_dir_all`
+//!   on the same path.
+//!
+//! The first is handled by using the low-level `NtOpenFile` API to open a file
+//! relative to a parent directory.
+//!
+//! The second is trickier. Deleting a file works by setting its "disposition"
+//! to delete. However, it isn't actually deleted until the file is closed.
+//! During the gap between these two events, the file is in a kind of limbo
+//! state where it still exists in the filesystem but anything trying to open
+//! it fails with an error.
+//!
+//! The mitigations we use here are:
+//!
+//! - When attempting to open the file, we treat ERROR_DELETE_PENDING as a
+//!   successful delete.
+//! - If the file still hasn't been removed from the filesystem by the time we
+//!   attempt to delete the parent directory, we try to wait for it to finish.
+//!   We can't wait indefinitely though so after some number of spins, we give
+//!   up and return an error.
+//!
+//! In short, we can't guarantee this will always succeed in the event of a
+//! race but we do make a best effort such that it *should* do so.
+
+use core::ptr;
+use core::sync::atomic::{AtomicU32, Ordering};
+
+use super::{AsRawHandle, DirBuff, File, FromRawHandle};
+use crate::sys::c;
+use crate::sys::pal::windows::api::WinError;
+use crate::thread;
+
+// The maximum number of times to spin when waiting for deletes to complete.
+const MAX_RETRIES: usize = 50;
+
+/// A wrapper around a raw NtOpenFile call.
+///
+/// This isn't completely safe because `OBJECT_ATTRIBUTES` contains raw pointers.
+unsafe fn nt_open_file(
+    access: u32,
+    object_attribute: &c::OBJECT_ATTRIBUTES,
+    share: u32,
+    options: u32,
+) -> Result<File, WinError> {
+    unsafe {
+        let mut handle = ptr::null_mut();
+        let mut io_status = c::IO_STATUS_BLOCK::PENDING;
+        let status =
+            c::NtOpenFile(&mut handle, access, object_attribute, &mut io_status, share, options);
+        if c::nt_success(status) {
+            Ok(File::from_raw_handle(handle))
+        } else {
+            // Convert an NTSTATUS to the more familiar Win32 error code (aka "DosError")
+            let win_error = if status == c::STATUS_DELETE_PENDING {
+                // We make a special exception for `STATUS_DELETE_PENDING` because
+                // otherwise this will be mapped to `ERROR_ACCESS_DENIED` which is
+                // very unhelpful because that can also mean a permission error.
+                WinError::DELETE_PENDING
+            } else {
+                WinError::new(c::RtlNtStatusToDosError(status))
+            };
+            Err(win_error)
+        }
+    }
+}
+
+/// Open the file `path` in the directory `parent`, requesting the given `access` rights.
+fn open_link_no_reparse(
+    parent: &File,
+    path: &[u16],
+    access: u32,
+) -> Result<Option<File>, WinError> {
+    // This is implemented using the lower level `NtOpenFile` function as
+    // unfortunately opening a file relative to a parent is not supported by
+    // win32 functions.
+    //
+    // See https://learn.microsoft.com/windows/win32/api/winternl/nf-winternl-ntopenfile
+
+    // The `OBJ_DONT_REPARSE` attribute ensures that we haven't been
+    // tricked into following a symlink. However, it may not be available in
+    // earlier versions of Windows.
+    static ATTRIBUTES: AtomicU32 = AtomicU32::new(c::OBJ_DONT_REPARSE);
+
+    let result = unsafe {
+        let mut path_str = c::UNICODE_STRING::from_ref(path);
+        let mut object = c::OBJECT_ATTRIBUTES {
+            ObjectName: &mut path_str,
+            RootDirectory: parent.as_raw_handle(),
+            Attributes: ATTRIBUTES.load(Ordering::Relaxed),
+            ..c::OBJECT_ATTRIBUTES::default()
+        };
+        let share = c::FILE_SHARE_DELETE | c::FILE_SHARE_READ | c::FILE_SHARE_WRITE;
+        let options = c::FILE_OPEN_REPARSE_POINT;
+        let result = nt_open_file(access, &object, share, options);
+
+        // Retry without OBJ_DONT_REPARSE if it's not supported.
+        if matches!(result, Err(WinError::INVALID_PARAMETER))
+            && ATTRIBUTES.load(Ordering::Relaxed) == c::OBJ_DONT_REPARSE
+        {
+            ATTRIBUTES.store(0, Ordering::Relaxed);
+            object.Attributes = 0;
+            nt_open_file(access, &object, share, options)
+        } else {
+            result
+        }
+    };
+
+    // Ignore not found errors
+    match result {
+        Ok(f) => Ok(Some(f)),
+        Err(
+            WinError::FILE_NOT_FOUND
+            | WinError::PATH_NOT_FOUND
+            | WinError::BAD_NETPATH
+            | WinError::BAD_NET_NAME
+            // `DELETE_PENDING` means something else is already trying to delete it
+            // so we assume that will eventually succeed.
+            | WinError::DELETE_PENDING,
+        ) => Ok(None),
+        Err(e) => Err(e),
+    }
+}
+
+fn open_dir(parent: &File, name: &[u16]) -> Result<Option<File>, WinError> {
+    open_link_no_reparse(parent, name, c::SYNCHRONIZE | c::FILE_LIST_DIRECTORY)
+}
+
+fn delete(parent: &File, name: &[u16]) -> Result<(), WinError> {
+    // Note that the `delete` function consumes the opened file to ensure it's
+    // dropped immediately. See module comments for why this is important.
+    match open_link_no_reparse(parent, name, c::SYNCHRONIZE | c::DELETE) {
+        Ok(Some(f)) => f.delete(),
+        Ok(None) => Ok(()),
+        Err(e) => Err(e),
+    }
+}
+
+/// A simple retry loop that keeps running `f` while it fails with the given
+/// error code or until `MAX_RETRIES` is reached.
+fn retry<T: PartialEq>(
+    mut f: impl FnMut() -> Result<T, WinError>,
+    ignore: WinError,
+) -> Result<T, WinError> {
+    let mut i = MAX_RETRIES;
+    loop {
+        i -= 1;
+        if i == 0 {
+            return f();
+        } else {
+            let result = f();
+            if result != Err(ignore) {
+                return result;
+            }
+        }
+        thread::yield_now();
+    }
+}
+
+pub fn remove_dir_all_iterative(dir: File) -> Result<(), WinError> {
+    let mut buffer = DirBuff::new();
+    let mut dirlist = vec![dir];
+
+    let mut restart = true;
+    'outer: while let Some(dir) = dirlist.pop() {
+        let more_data = dir.fill_dir_buff(&mut buffer, restart)?;
+        for (name, is_directory) in buffer.iter() {
+            if is_directory {
+                let Some(subdir) = open_dir(&dir, &name)? else { continue };
+                dirlist.push(dir);
+                dirlist.push(subdir);
+                continue 'outer;
+            } else {
+                // Attempt to delete, retrying on sharing violation errors as these
+                // can often be very temporary. E.g. if something takes just a
+                // bit longer than expected to release a file handle.
+                retry(|| delete(&dir, &name), WinError::SHARING_VIOLATION)?;
+            }
+        }
+        if more_data {
+            dirlist.push(dir);
+            restart = false;
+        } else {
+            // Attempt to delete, retrying on not empty errors because we may
+            // need to wait some time for files to be removed from the filesystem.
+            retry(|| delete(&dir, &[]), WinError::DIR_NOT_EMPTY)?;
+            restart = true;
+        }
+    }
+    Ok(())
+}