Skip to content

Commit 0d08bbc

Browse files
authored
Rollup merge of rust-lang#93459 - tavianator:dirent-copy-only-reclen, r=cuviper
fs: Don't copy d_name from struct dirent The dirent returned from readdir() is only guaranteed to be valid for d_reclen bytes on common platforms. Since we copy the name separately anyway, we can copy everything except d_name into DirEntry::entry. Fixes rust-lang#93384.
2 parents 605ffd6 + d0c8b29 commit 0d08bbc

File tree

2 files changed

+26
-2
lines changed

2 files changed

+26
-2
lines changed

library/std/src/fs/tests.rs

+16
Original file line numberDiff line numberDiff line change
@@ -1504,3 +1504,19 @@ fn create_dir_long_paths() {
15041504
let path = Path::new("");
15051505
assert_eq!(path.canonicalize().unwrap_err().kind(), crate::io::ErrorKind::NotFound);
15061506
}
1507+
1508+
/// Ensure ReadDir works on large directories.
1509+
/// Regression test for https://github.com/rust-lang/rust/issues/93384.
1510+
#[test]
1511+
fn read_large_dir() {
1512+
let tmpdir = tmpdir();
1513+
1514+
let count = 32 * 1024;
1515+
for i in 0..count {
1516+
check!(fs::File::create(tmpdir.join(&i.to_string())));
1517+
}
1518+
1519+
for entry in fs::read_dir(tmpdir.path()).unwrap() {
1520+
entry.unwrap();
1521+
}
1522+
}

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

+10-2
Original file line numberDiff line numberDiff line change
@@ -489,10 +489,18 @@ impl Iterator for ReadDir {
489489
};
490490
}
491491

492+
// Only d_reclen bytes of *entry_ptr are valid, so we can't just copy the
493+
// whole thing (#93384). Instead, copy everything except the name.
494+
let entry_bytes = entry_ptr as *const u8;
495+
let entry_name = ptr::addr_of!((*entry_ptr).d_name) as *const u8;
496+
let name_offset = entry_name.offset_from(entry_bytes) as usize;
497+
let mut entry: dirent64 = mem::zeroed();
498+
ptr::copy_nonoverlapping(entry_bytes, &mut entry as *mut _ as *mut u8, name_offset);
499+
492500
let ret = DirEntry {
493-
entry: *entry_ptr,
501+
entry,
494502
// d_name is guaranteed to be null-terminated.
495-
name: CStr::from_ptr((*entry_ptr).d_name.as_ptr()).to_owned(),
503+
name: CStr::from_ptr(entry_name as *const _).to_owned(),
496504
dir: Arc::clone(&self.inner),
497505
};
498506
if ret.name_bytes() != b"." && ret.name_bytes() != b".." {

0 commit comments

Comments
 (0)