Skip to content

Commit 698f731

Browse files
committed
Fall back to old impl for remove_dir_all
The recursive directory removal implementation falls back to the old one if the necessary APIs are not available (`NtCreateFile`, `GetFileInformationByHandleEx`, `SetFileInformationByHandle`). The APIs are available on Vista/Server 2008. See notes on `fileextd.lib` above to extend the support to Windows XP/Server 2003. **This might cause security issues**, see rust-lang#93112
1 parent 02bd38f commit 698f731

File tree

2 files changed

+80
-49
lines changed

2 files changed

+80
-49
lines changed

library/std/src/sys/windows/c.rs

Lines changed: 49 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,7 @@ pub unsafe fn NtReadFile(
282282
byteoffset: Option<&LARGE_INTEGER>,
283283
key: Option<&ULONG>,
284284
) -> NTSTATUS {
285-
windows_sys::NtReadFile(
285+
ntdll::NtReadFile(
286286
filehandle.as_raw_handle(),
287287
event,
288288
apcroutine,
@@ -305,7 +305,7 @@ pub unsafe fn NtWriteFile(
305305
byteoffset: Option<&LARGE_INTEGER>,
306306
key: Option<&ULONG>,
307307
) -> NTSTATUS {
308-
windows_sys::NtWriteFile(
308+
ntdll::NtWriteFile(
309309
filehandle.as_raw_handle(),
310310
event,
311311
apcroutine,
@@ -565,6 +565,53 @@ pub const RtlGenRandom: unsafe fn(
565565
randombufferlength: u32,
566566
) -> BOOLEAN = SystemFunction036;
567567

568+
compat_fn_lazy! {
569+
pub static NTDLL: &CStr = c"ntdll" => { load: true, unicows: false };
570+
571+
pub fn NtCreateFile(
572+
filehandle: *mut HANDLE,
573+
desiredaccess: FILE_ACCESS_RIGHTS,
574+
objectattributes: *const OBJECT_ATTRIBUTES,
575+
iostatusblock: *mut IO_STATUS_BLOCK,
576+
allocationsize: *const i64,
577+
fileattributes: FILE_FLAGS_AND_ATTRIBUTES,
578+
shareaccess: FILE_SHARE_MODE,
579+
createdisposition: NTCREATEFILE_CREATE_DISPOSITION,
580+
createoptions: NTCREATEFILE_CREATE_OPTIONS,
581+
eabuffer: *const ::core::ffi::c_void,
582+
ealength: u32,
583+
) -> NTSTATUS;
584+
}
585+
586+
pub mod ntdll {
587+
use super::*;
588+
compat_fn_lazy! {
589+
pub static NTDLL: &CStr = c"ntdll" => { load: true, unicows: false };
590+
591+
pub fn NtReadFile(
592+
filehandle: HANDLE,
593+
event: HANDLE,
594+
apcroutine: PIO_APC_ROUTINE,
595+
apccontext: *const ::core::ffi::c_void,
596+
iostatusblock: *mut IO_STATUS_BLOCK,
597+
buffer: *mut ::core::ffi::c_void,
598+
length: u32,
599+
byteoffset: *const i64,
600+
key: *const u32,
601+
) -> NTSTATUS;
602+
pub fn NtWriteFile(
603+
filehandle: HANDLE,
604+
event: HANDLE,
605+
apcroutine: PIO_APC_ROUTINE,
606+
apccontext: *const ::core::ffi::c_void,
607+
iostatusblock: *mut IO_STATUS_BLOCK,
608+
buffer: *const ::core::ffi::c_void,
609+
length: u32,
610+
byteoffset: *const i64,
611+
key: *const u32,
612+
) -> NTSTATUS;
613+
}
614+
}
568615
compat_fn_with_fallback! {
569616
pub static NTDLL: &CStr = c"ntdll" => { load: true, unicows: false };
570617

@@ -592,53 +639,6 @@ compat_fn_with_fallback! {
592639
) -> NTSTATUS {
593640
panic!("keyed events not available")
594641
}
595-
596-
// These functions are available on UWP when lazily loaded. They will fail WACK if loaded statically.
597-
#[cfg(target_vendor = "uwp")]
598-
pub fn NtCreateFile(
599-
filehandle: *mut HANDLE,
600-
desiredaccess: FILE_ACCESS_RIGHTS,
601-
objectattributes: *const OBJECT_ATTRIBUTES,
602-
iostatusblock: *mut IO_STATUS_BLOCK,
603-
allocationsize: *const i64,
604-
fileattributes: FILE_FLAGS_AND_ATTRIBUTES,
605-
shareaccess: FILE_SHARE_MODE,
606-
createdisposition: NTCREATEFILE_CREATE_DISPOSITION,
607-
createoptions: NTCREATEFILE_CREATE_OPTIONS,
608-
eabuffer: *const ::core::ffi::c_void,
609-
ealength: u32
610-
) -> NTSTATUS {
611-
STATUS_NOT_IMPLEMENTED
612-
}
613-
#[cfg(target_vendor = "uwp")]
614-
pub fn NtReadFile(
615-
filehandle: BorrowedHandle<'_>,
616-
event: HANDLE,
617-
apcroutine: PIO_APC_ROUTINE,
618-
apccontext: *mut c_void,
619-
iostatusblock: &mut IO_STATUS_BLOCK,
620-
buffer: *mut crate::mem::MaybeUninit<u8>,
621-
length: ULONG,
622-
byteoffset: Option<&LARGE_INTEGER>,
623-
key: Option<&ULONG>
624-
) -> NTSTATUS {
625-
STATUS_NOT_IMPLEMENTED
626-
}
627-
#[cfg(target_vendor = "uwp")]
628-
pub fn NtWriteFile(
629-
filehandle: BorrowedHandle<'_>,
630-
event: HANDLE,
631-
apcroutine: PIO_APC_ROUTINE,
632-
apccontext: *mut c_void,
633-
iostatusblock: &mut IO_STATUS_BLOCK,
634-
buffer: *const u8,
635-
length: ULONG,
636-
byteoffset: Option<&LARGE_INTEGER>,
637-
key: Option<&ULONG>
638-
) -> NTSTATUS {
639-
STATUS_NOT_IMPLEMENTED
640-
}
641-
#[cfg(target_vendor = "uwp")]
642642
pub fn RtlNtStatusToDosError(Status: NTSTATUS) -> u32 {
643643
Status as u32
644644
}

library/std/src/sys/windows/fs.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1133,6 +1133,22 @@ fn open_link(path: &Path, access_mode: u32) -> io::Result<File> {
11331133
}
11341134

11351135
pub fn remove_dir_all(path: &Path) -> io::Result<()> {
1136+
// if the modern file/directory APIs are not available, we'll fall back to the old (unsafe, see
1137+
// https://github.com/rust-lang/rust/pull/93112) directory removal implementation
1138+
if c::NtCreateFile::option().is_none()
1139+
|| c::GetFileInformationByHandleEx::option().is_none()
1140+
|| c::SetFileInformationByHandle::option().is_none()
1141+
{
1142+
let filetype = lstat(path)?.file_type();
1143+
if filetype.is_symlink() {
1144+
// On Windows symlinks to files and directories are removed differently.
1145+
// rmdir only deletes dir symlinks and junctions, not file symlinks.
1146+
return rmdir(path);
1147+
} else {
1148+
return remove_dir_all_recursive_old(path);
1149+
}
1150+
}
1151+
11361152
let file = open_link(path, c::DELETE | c::FILE_LIST_DIRECTORY)?;
11371153

11381154
// Test if the file is not a directory or a symlink to a directory.
@@ -1232,6 +1248,21 @@ fn remove_dir_all_iterative(f: &File, delete: fn(&File) -> io::Result<()>) -> io
12321248
Ok(())
12331249
}
12341250

1251+
fn remove_dir_all_recursive_old(path: &Path) -> io::Result<()> {
1252+
for child in readdir(path)? {
1253+
let child = child?;
1254+
let child_type = child.file_type()?;
1255+
if child_type.is_dir() {
1256+
remove_dir_all_recursive_old(&child.path())?;
1257+
} else if child_type.is_symlink_dir() {
1258+
rmdir(&child.path())?;
1259+
} else {
1260+
unlink(&child.path())?;
1261+
}
1262+
}
1263+
rmdir(path)
1264+
}
1265+
12351266
pub fn readlink(path: &Path) -> io::Result<PathBuf> {
12361267
// Open the link with no access mode, instead of generic read.
12371268
// By default FILE_LIST_DIRECTORY is denied for the junction "C:\Documents and Settings", so

0 commit comments

Comments
 (0)