about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--library/std/src/fs/tests.rs16
-rw-r--r--library/std/src/sys/unix/fs.rs12
2 files changed, 26 insertions, 2 deletions
diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs
index a62c01ef29b..16b8bf68242 100644
--- a/library/std/src/fs/tests.rs
+++ b/library/std/src/fs/tests.rs
@@ -1504,3 +1504,19 @@ fn create_dir_long_paths() {
     let path = Path::new("");
     assert_eq!(path.canonicalize().unwrap_err().kind(), crate::io::ErrorKind::NotFound);
 }
+
+/// Ensure ReadDir works on large directories.
+/// Regression test for https://github.com/rust-lang/rust/issues/93384.
+#[test]
+fn read_large_dir() {
+    let tmpdir = tmpdir();
+
+    let count = 32 * 1024;
+    for i in 0..count {
+        check!(fs::File::create(tmpdir.join(&i.to_string())));
+    }
+
+    for entry in fs::read_dir(tmpdir.path()).unwrap() {
+        entry.unwrap();
+    }
+}
diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs
index 5b2199c2b7f..65e000d9215 100644
--- a/library/std/src/sys/unix/fs.rs
+++ b/library/std/src/sys/unix/fs.rs
@@ -489,10 +489,18 @@ impl Iterator for ReadDir {
                     };
                 }
 
+                // Only d_reclen bytes of *entry_ptr are valid, so we can't just copy the
+                // whole thing (#93384).  Instead, copy everything except the name.
+                let entry_bytes = entry_ptr as *const u8;
+                let entry_name = ptr::addr_of!((*entry_ptr).d_name) as *const u8;
+                let name_offset = entry_name.offset_from(entry_bytes) as usize;
+                let mut entry: dirent64 = mem::zeroed();
+                ptr::copy_nonoverlapping(entry_bytes, &mut entry as *mut _ as *mut u8, name_offset);
+
                 let ret = DirEntry {
-                    entry: *entry_ptr,
+                    entry,
                     // d_name is guaranteed to be null-terminated.
-                    name: CStr::from_ptr((*entry_ptr).d_name.as_ptr()).to_owned(),
+                    name: CStr::from_ptr(entry_name as *const _).to_owned(),
                     dir: Arc::clone(&self.inner),
                 };
                 if ret.name_bytes() != b"." && ret.name_bytes() != b".." {