Rollup merge of #104558 - thomcc:unalign-diriter, r=ChrisDenton
Don't assume `FILE_ID_BOTH_DIR_INFO` will be aligned Fixes #104530. See that issue for info. r? `@ChrisDenton`
This commit is contained in:
commit
379d3365fd
1 changed files with 23 additions and 10 deletions
|
@ -1,5 +1,6 @@
|
||||||
use crate::os::windows::prelude::*;
|
use crate::os::windows::prelude::*;
|
||||||
|
|
||||||
|
use crate::borrow::Cow;
|
||||||
use crate::ffi::OsString;
|
use crate::ffi::OsString;
|
||||||
use crate::fmt;
|
use crate::fmt;
|
||||||
use crate::io::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, SeekFrom};
|
use crate::io::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, SeekFrom};
|
||||||
|
@ -719,7 +720,7 @@ impl<'a> DirBuffIter<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<'a> Iterator for DirBuffIter<'a> {
|
impl<'a> Iterator for DirBuffIter<'a> {
|
||||||
type Item = (&'a [u16], bool);
|
type Item = (Cow<'a, [u16]>, bool);
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
use crate::mem::size_of;
|
use crate::mem::size_of;
|
||||||
let buffer = &self.buffer?[self.cursor..];
|
let buffer = &self.buffer?[self.cursor..];
|
||||||
|
@ -734,15 +735,19 @@ impl<'a> Iterator for DirBuffIter<'a> {
|
||||||
// `FileNameLength` bytes)
|
// `FileNameLength` bytes)
|
||||||
let (name, is_directory, next_entry) = unsafe {
|
let (name, is_directory, next_entry) = unsafe {
|
||||||
let info = buffer.as_ptr().cast::<c::FILE_ID_BOTH_DIR_INFO>();
|
let info = buffer.as_ptr().cast::<c::FILE_ID_BOTH_DIR_INFO>();
|
||||||
// Guaranteed to be aligned in documentation for
|
// While this is guaranteed to be aligned in documentation for
|
||||||
// https://docs.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-file_id_both_dir_info
|
// https://docs.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-file_id_both_dir_info
|
||||||
assert!(info.is_aligned());
|
// it does not seem that reality is so kind, and assuming this
|
||||||
let next_entry = (*info).NextEntryOffset as usize;
|
// caused crashes in some cases (https://github.com/rust-lang/rust/issues/104530)
|
||||||
let name = crate::slice::from_raw_parts(
|
// presumably, this can be blamed on buggy filesystem drivers, but who knows.
|
||||||
|
let next_entry = ptr::addr_of!((*info).NextEntryOffset).read_unaligned() as usize;
|
||||||
|
let length = ptr::addr_of!((*info).FileNameLength).read_unaligned() as usize;
|
||||||
|
let attrs = ptr::addr_of!((*info).FileAttributes).read_unaligned();
|
||||||
|
let name = from_maybe_unaligned(
|
||||||
ptr::addr_of!((*info).FileName).cast::<u16>(),
|
ptr::addr_of!((*info).FileName).cast::<u16>(),
|
||||||
(*info).FileNameLength as usize / size_of::<u16>(),
|
length / size_of::<u16>(),
|
||||||
);
|
);
|
||||||
let is_directory = ((*info).FileAttributes & c::FILE_ATTRIBUTE_DIRECTORY) != 0;
|
let is_directory = (attrs & c::FILE_ATTRIBUTE_DIRECTORY) != 0;
|
||||||
|
|
||||||
(name, is_directory, next_entry)
|
(name, is_directory, next_entry)
|
||||||
};
|
};
|
||||||
|
@ -755,13 +760,21 @@ impl<'a> Iterator for DirBuffIter<'a> {
|
||||||
|
|
||||||
// Skip `.` and `..` pseudo entries.
|
// Skip `.` and `..` pseudo entries.
|
||||||
const DOT: u16 = b'.' as u16;
|
const DOT: u16 = b'.' as u16;
|
||||||
match name {
|
match &name[..] {
|
||||||
[DOT] | [DOT, DOT] => self.next(),
|
[DOT] | [DOT, DOT] => self.next(),
|
||||||
_ => Some((name, is_directory)),
|
_ => Some((name, is_directory)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsafe fn from_maybe_unaligned<'a>(p: *const u16, len: usize) -> Cow<'a, [u16]> {
|
||||||
|
if p.is_aligned() {
|
||||||
|
Cow::Borrowed(crate::slice::from_raw_parts(p, len))
|
||||||
|
} else {
|
||||||
|
Cow::Owned((0..len).map(|i| p.add(i).read_unaligned()).collect())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Open a link relative to the parent directory, ensure no symlinks are followed.
|
/// 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> {
|
fn open_link_no_reparse(parent: &File, name: &[u16], access: u32) -> io::Result<File> {
|
||||||
// This is implemented using the lower level `NtCreateFile` function as
|
// This is implemented using the lower level `NtCreateFile` function as
|
||||||
|
@ -1117,13 +1130,13 @@ fn remove_dir_all_iterative(f: &File, delete: fn(&File) -> io::Result<()>) -> io
|
||||||
if is_directory {
|
if is_directory {
|
||||||
let child_dir = open_link_no_reparse(
|
let child_dir = open_link_no_reparse(
|
||||||
&dir,
|
&dir,
|
||||||
name,
|
&name,
|
||||||
c::SYNCHRONIZE | c::DELETE | c::FILE_LIST_DIRECTORY,
|
c::SYNCHRONIZE | c::DELETE | c::FILE_LIST_DIRECTORY,
|
||||||
)?;
|
)?;
|
||||||
dirlist.push(child_dir);
|
dirlist.push(child_dir);
|
||||||
} else {
|
} else {
|
||||||
for i in 1..=MAX_RETRIES {
|
for i in 1..=MAX_RETRIES {
|
||||||
let result = open_link_no_reparse(&dir, name, c::SYNCHRONIZE | c::DELETE);
|
let result = open_link_no_reparse(&dir, &name, c::SYNCHRONIZE | c::DELETE);
|
||||||
match result {
|
match result {
|
||||||
Ok(f) => delete(&f)?,
|
Ok(f) => delete(&f)?,
|
||||||
// Already deleted, so skip.
|
// Already deleted, so skip.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue