Skip to content

Commit 4839b81

Browse files
committed
fs: Use readdir() instead of readdir_r() on Linux
readdir() is preferred over readdir_r() on Linux and many other platforms because it more gracefully supports long file names. Both glibc and musl (and presumably all other Linux libc implementations) guarantee that readdir() is thread-safe as long as a single DIR* is not accessed concurrently, which is enough to make a readdir()-based implementation of ReadDir safe. This implementation is already used for some other OSes including Fuchsia, Redox, and Solaris. See #40021 for more details. Fixes #86649.
1 parent 2e2c86e commit 4839b81

File tree

2 files changed

+24
-15
lines changed

2 files changed

+24
-15
lines changed

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

+23-14
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,17 @@ use libc::c_char;
3434
use libc::dirfd;
3535
#[cfg(any(target_os = "linux", target_os = "emscripten"))]
3636
use libc::fstatat64;
37+
#[cfg(any(
38+
target_os = "solaris",
39+
target_os = "fuchsia",
40+
target_os = "redox",
41+
target_os = "illumos"
42+
))]
43+
use libc::readdir as readdir64;
44+
#[cfg(target_os = "linux")]
45+
use libc::readdir64;
46+
#[cfg(any(target_os = "emscripten", target_os = "l4re"))]
47+
use libc::readdir64_r;
3748
#[cfg(not(any(
3849
target_os = "linux",
3950
target_os = "emscripten",
@@ -60,9 +71,7 @@ use libc::{
6071
lstat as lstat64, off_t as off64_t, open as open64, stat as stat64,
6172
};
6273
#[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "l4re"))]
63-
use libc::{
64-
dirent64, fstat64, ftruncate64, lseek64, lstat64, off64_t, open64, readdir64_r, stat64,
65-
};
74+
use libc::{dirent64, fstat64, ftruncate64, lseek64, lstat64, off64_t, open64, stat64};
6675

6776
pub use crate::sys_common::fs::{remove_dir_all, try_exists};
6877

@@ -202,6 +211,7 @@ struct InnerReadDir {
202211
pub struct ReadDir {
203212
inner: Arc<InnerReadDir>,
204213
#[cfg(not(any(
214+
target_os = "linux",
205215
target_os = "solaris",
206216
target_os = "illumos",
207217
target_os = "fuchsia",
@@ -223,12 +233,13 @@ pub struct DirEntry {
223233
// array to store the name, b) its lifetime between readdir
224234
// calls is not guaranteed.
225235
#[cfg(any(
236+
target_os = "linux",
226237
target_os = "solaris",
227238
target_os = "illumos",
228239
target_os = "fuchsia",
229240
target_os = "redox"
230241
))]
231-
name: Box<[u8]>,
242+
name: Box<CStr>,
232243
}
233244

234245
#[derive(Clone, Debug)]
@@ -449,22 +460,21 @@ impl Iterator for ReadDir {
449460
type Item = io::Result<DirEntry>;
450461

451462
#[cfg(any(
463+
target_os = "linux",
452464
target_os = "solaris",
453465
target_os = "fuchsia",
454466
target_os = "redox",
455467
target_os = "illumos"
456468
))]
457469
fn next(&mut self) -> Option<io::Result<DirEntry>> {
458-
use crate::slice;
459-
460470
unsafe {
461471
loop {
462472
// Although readdir_r(3) would be a correct function to use here because
463473
// of the thread safety, on Illumos and Fuchsia the readdir(3C) function
464474
// is safe to use in threaded applications and it is generally preferred
465475
// over the readdir_r(3C) function.
466476
super::os::set_errno(0);
467-
let entry_ptr = libc::readdir(self.inner.dirp.0);
477+
let entry_ptr = readdir64(self.inner.dirp.0);
468478
if entry_ptr.is_null() {
469479
// null can mean either the end is reached or an error occurred.
470480
// So we had to clear errno beforehand to check for an error now.
@@ -475,14 +485,11 @@ impl Iterator for ReadDir {
475485
}
476486

477487
let name = (*entry_ptr).d_name.as_ptr();
478-
let namelen = libc::strlen(name) as usize;
479488

480489
let ret = DirEntry {
481490
entry: *entry_ptr,
482-
name: slice::from_raw_parts(name as *const u8, namelen as usize)
483-
.to_owned()
484-
.into_boxed_slice(),
485491
dir: Arc::clone(&self.inner),
492+
name: CStr::from_ptr(name).into(),
486493
};
487494
if ret.name_bytes() != b"." && ret.name_bytes() != b".." {
488495
return Some(Ok(ret));
@@ -492,6 +499,7 @@ impl Iterator for ReadDir {
492499
}
493500

494501
#[cfg(not(any(
502+
target_os = "linux",
495503
target_os = "solaris",
496504
target_os = "fuchsia",
497505
target_os = "redox",
@@ -547,7 +555,7 @@ impl DirEntry {
547555
#[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "android"))]
548556
pub fn metadata(&self) -> io::Result<FileAttr> {
549557
let fd = cvt(unsafe { dirfd(self.dir.dirp.0) })?;
550-
let name = self.entry.d_name.as_ptr();
558+
let name = self.name.as_ptr();
551559

552560
cfg_has_statx! {
553561
if let Some(ret) = unsafe { try_statx(
@@ -647,7 +655,6 @@ impl DirEntry {
647655
}
648656
#[cfg(any(
649657
target_os = "android",
650-
target_os = "linux",
651658
target_os = "emscripten",
652659
target_os = "l4re",
653660
target_os = "haiku",
@@ -658,13 +665,14 @@ impl DirEntry {
658665
unsafe { CStr::from_ptr(self.entry.d_name.as_ptr()).to_bytes() }
659666
}
660667
#[cfg(any(
668+
target_os = "linux",
661669
target_os = "solaris",
662670
target_os = "illumos",
663671
target_os = "fuchsia",
664672
target_os = "redox"
665673
))]
666674
fn name_bytes(&self) -> &[u8] {
667-
&*self.name
675+
self.name.to_bytes()
668676
}
669677

670678
pub fn file_name_os_str(&self) -> &OsStr {
@@ -1068,6 +1076,7 @@ pub fn readdir(p: &Path) -> io::Result<ReadDir> {
10681076
Ok(ReadDir {
10691077
inner: Arc::new(inner),
10701078
#[cfg(not(any(
1079+
target_os = "linux",
10711080
target_os = "solaris",
10721081
target_os = "illumos",
10731082
target_os = "fuchsia",

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ pub fn errno() -> i32 {
7575
}
7676

7777
/// Sets the platform-specific value of errno
78-
#[cfg(all(not(target_os = "linux"), not(target_os = "dragonfly"), not(target_os = "vxworks")))] // needed for readdir and syscall!
78+
#[cfg(all(not(target_os = "dragonfly"), not(target_os = "vxworks")))] // needed for readdir and syscall!
7979
#[allow(dead_code)] // but not all target cfgs actually end up using it
8080
pub fn set_errno(e: i32) {
8181
unsafe { *errno_location() = e as c_int }

0 commit comments

Comments
 (0)