11#![ allow( nonstandard_style) ]
22
33use crate :: alloc:: { Layout , alloc, dealloc} ;
4- use crate :: borrow:: Cow ;
54use crate :: ffi:: { OsStr , OsString , c_void} ;
65use crate :: fs:: TryLockError ;
76use crate :: io:: { self , BorrowedCursor , Error , IoSlice , IoSliceMut , SeekFrom } ;
@@ -869,7 +868,7 @@ impl DirBuff {
869868 self . buffer . 0 . as_mut_ptr ( ) . cast ( )
870869 }
871870 /// Returns a `DirBuffIter`.
872- fn iter ( & self ) -> DirBuffIter < ' _ > {
871+ fn iter ( & mut self ) -> DirBuffIter < ' _ > {
873872 DirBuffIter :: new ( self )
874873 }
875874}
@@ -883,18 +882,44 @@ impl AsRef<[MaybeUninit<u8>]> for DirBuff {
883882///
884883/// Currently only returns file names (UTF-16 encoded).
885884struct DirBuffIter < ' a > {
886- buffer : Option < & ' a [ MaybeUninit < u8 > ] > ,
885+ buffer : Option < & ' a mut [ MaybeUninit < u8 > ] > ,
887886 cursor : usize ,
888887}
889888impl < ' a > DirBuffIter < ' a > {
890- fn new ( buffer : & ' a DirBuff ) -> Self {
891- Self { buffer : Some ( buffer. as_ref ( ) ) , cursor : 0 }
889+ fn new ( buffer : & ' a mut DirBuff ) -> Self {
890+ Self { buffer : Some ( & mut buffer. buffer . 0 ) , cursor : 0 }
892891 }
893892}
893+
894894impl < ' a > Iterator for DirBuffIter < ' a > {
895- type Item = ( Cow < ' a , [ u16 ] > , bool ) ;
895+ type Item = ( & ' a [ u16 ] , bool ) ;
896896 fn next ( & mut self ) -> Option < Self :: Item > {
897- let buffer = & self . buffer ?[ self . cursor ..] ;
897+ let buffer = self . buffer . as_mut ( ) ?;
898+
899+ // While the file information is guaranteed to be aligned in documentation for
900+ // https://docs.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-file_id_both_dir_info
901+ // it does not seem that reality is so kind.
902+ // In some cases crashes could be caused by security software that hooks system APIs
903+ // but doesn't uphold the documented alignment.
904+ // See https://github.com/rust-lang/rust/issues/104530
905+ debug_assert_eq ! ( self . cursor % 8 , 0 ) ;
906+ let buffer = if !self . cursor . is_multiple_of ( 8 ) {
907+ // Copy the data to the start of the buffer, which we've guarenteed to be aligned.
908+ // SAFETY: the FILE_ID_BOTH_DIR_INFO struct has been correctly initialized (if not aligned).
909+ unsafe {
910+ let info = buffer. as_ptr ( ) . byte_add ( self . cursor ) . cast :: < c:: FILE_ID_BOTH_DIR_INFO > ( ) ;
911+ let name_offset = mem:: offset_of!( c:: FILE_ID_BOTH_DIR_INFO , FileName ) ;
912+ let name_length = ( & raw const ( * info) . FileNameLength ) . read_unaligned ( ) as usize ;
913+ ptr:: copy (
914+ info. cast :: < u8 > ( ) ,
915+ buffer. as_mut_ptr ( ) . cast :: < u8 > ( ) ,
916+ name_offset + name_length,
917+ ) ;
918+ }
919+ & buffer[ ..]
920+ } else {
921+ & buffer[ self . cursor ..]
922+ } ;
898923
899924 // Get the name and next entry from the buffer.
900925 // SAFETY:
@@ -905,17 +930,16 @@ impl<'a> Iterator for DirBuffIter<'a> {
905930 // `FILE_ID_BOTH_DIR_INFO` and the trailing filename (for at least
906931 // `FileNameLength` bytes)
907932 let ( name, is_directory, next_entry) = unsafe {
908- let info = buffer. as_ptr ( ) . cast :: < c:: FILE_ID_BOTH_DIR_INFO > ( ) ;
909- // While this is guaranteed to be aligned in documentation for
910- // https://docs.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-file_id_both_dir_info
911- // it does not seem that reality is so kind, and assuming this
912- // caused crashes in some cases (https://github.com/rust-lang/rust/issues/104530)
913- // presumably, this can be blamed on buggy filesystem drivers, but who knows.
914- let next_entry = ( & raw const ( * info) . NextEntryOffset ) . read_unaligned ( ) as usize ;
915- let length = ( & raw const ( * info) . FileNameLength ) . read_unaligned ( ) as usize ;
916- let attrs = ( & raw const ( * info) . FileAttributes ) . read_unaligned ( ) ;
917- let name = from_maybe_unaligned (
918- ( & raw const ( * info) . FileName ) . cast :: < u16 > ( ) ,
933+ let info_ptr = buffer. as_ptr ( ) . cast :: < c:: FILE_ID_BOTH_DIR_INFO > ( ) ;
934+ // WARNING: use `info_ptr` to access the variable length FileName field.
935+ // This reference only spans the header and first item of the FileName array.
936+ let info = & * info_ptr;
937+
938+ let next_entry = info. NextEntryOffset as usize ;
939+ let length = info. FileNameLength as usize ;
940+ let attrs = info. FileAttributes ;
941+ let name = slice:: from_raw_parts (
942+ ( & raw const ( * info_ptr) . FileName ) . cast :: < u16 > ( ) ,
919943 length / size_of :: < u16 > ( ) ,
920944 ) ;
921945 let is_directory = ( attrs & c:: FILE_ATTRIBUTE_DIRECTORY ) != 0 ;
@@ -938,16 +962,6 @@ impl<'a> Iterator for DirBuffIter<'a> {
938962 }
939963}
940964
941- unsafe fn from_maybe_unaligned < ' a > ( p : * const u16 , len : usize ) -> Cow < ' a , [ u16 ] > {
942- unsafe {
943- if p. is_aligned ( ) {
944- Cow :: Borrowed ( crate :: slice:: from_raw_parts ( p, len) )
945- } else {
946- Cow :: Owned ( ( 0 ..len) . map ( |i| p. add ( i) . read_unaligned ( ) ) . collect ( ) )
947- }
948- }
949- }
950-
951965impl AsInner < Handle > for File {
952966 #[ inline]
953967 fn as_inner ( & self ) -> & Handle {
0 commit comments