Skip to content

Commit 253f64c

Browse files
authored
Rollup merge of #92778 - tavianator:linux-readdir-no-r, r=joshtriplett
fs: Use readdir() instead of readdir_r() on Linux and Android See #40021 for more details. Fixes #86649. Fixes #34668.
2 parents e2b2bfe + 3eeb3ca commit 253f64c

File tree

2 files changed

+58
-36
lines changed

2 files changed

+58
-36
lines changed

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

+57-35
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,20 @@ 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 = "android",
39+
target_os = "solaris",
40+
target_os = "fuchsia",
41+
target_os = "redox",
42+
target_os = "illumos"
43+
))]
44+
use libc::readdir as readdir64;
45+
#[cfg(target_os = "linux")]
46+
use libc::readdir64;
47+
#[cfg(any(target_os = "emscripten", target_os = "l4re"))]
48+
use libc::readdir64_r;
3749
#[cfg(not(any(
50+
target_os = "android",
3851
target_os = "linux",
3952
target_os = "emscripten",
4053
target_os = "solaris",
@@ -60,9 +73,7 @@ use libc::{
6073
lstat as lstat64, off_t as off64_t, open as open64, stat as stat64,
6174
};
6275
#[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-
};
76+
use libc::{dirent64, fstat64, ftruncate64, lseek64, lstat64, off64_t, open64, stat64};
6677

6778
pub use crate::sys_common::fs::try_exists;
6879

@@ -202,6 +213,8 @@ struct InnerReadDir {
202213
pub struct ReadDir {
203214
inner: Arc<InnerReadDir>,
204215
#[cfg(not(any(
216+
target_os = "android",
217+
target_os = "linux",
205218
target_os = "solaris",
206219
target_os = "illumos",
207220
target_os = "fuchsia",
@@ -218,11 +231,12 @@ unsafe impl Sync for Dir {}
218231
pub struct DirEntry {
219232
entry: dirent64,
220233
dir: Arc<InnerReadDir>,
221-
// We need to store an owned copy of the entry name
222-
// on Solaris and Fuchsia because a) it uses a zero-length
223-
// array to store the name, b) its lifetime between readdir
224-
// calls is not guaranteed.
234+
// We need to store an owned copy of the entry name on platforms that use
235+
// readdir() (not readdir_r()), because a) struct dirent may use a flexible
236+
// array to store the name, b) it lives only until the next readdir() call.
225237
#[cfg(any(
238+
target_os = "android",
239+
target_os = "linux",
226240
target_os = "solaris",
227241
target_os = "illumos",
228242
target_os = "fuchsia",
@@ -449,6 +463,8 @@ impl Iterator for ReadDir {
449463
type Item = io::Result<DirEntry>;
450464

451465
#[cfg(any(
466+
target_os = "android",
467+
target_os = "linux",
452468
target_os = "solaris",
453469
target_os = "fuchsia",
454470
target_os = "redox",
@@ -457,12 +473,13 @@ impl Iterator for ReadDir {
457473
fn next(&mut self) -> Option<io::Result<DirEntry>> {
458474
unsafe {
459475
loop {
460-
// Although readdir_r(3) would be a correct function to use here because
461-
// of the thread safety, on Illumos and Fuchsia the readdir(3C) function
462-
// is safe to use in threaded applications and it is generally preferred
463-
// over the readdir_r(3C) function.
476+
// As of POSIX.1-2017, readdir() is not required to be thread safe; only
477+
// readdir_r() is. However, readdir_r() cannot correctly handle platforms
478+
// with unlimited or variable NAME_MAX. Many modern platforms guarantee
479+
// thread safety for readdir() as long an individual DIR* is not accessed
480+
// concurrently, which is sufficient for Rust.
464481
super::os::set_errno(0);
465-
let entry_ptr = libc::readdir(self.inner.dirp.0);
482+
let entry_ptr = readdir64(self.inner.dirp.0);
466483
if entry_ptr.is_null() {
467484
// null can mean either the end is reached or an error occurred.
468485
// So we had to clear errno beforehand to check for an error now.
@@ -486,6 +503,8 @@ impl Iterator for ReadDir {
486503
}
487504

488505
#[cfg(not(any(
506+
target_os = "android",
507+
target_os = "linux",
489508
target_os = "solaris",
490509
target_os = "fuchsia",
491510
target_os = "redox",
@@ -531,17 +550,17 @@ impl Drop for Dir {
531550

532551
impl DirEntry {
533552
pub fn path(&self) -> PathBuf {
534-
self.dir.root.join(OsStr::from_bytes(self.name_bytes()))
553+
self.dir.root.join(self.file_name_os_str())
535554
}
536555

537556
pub fn file_name(&self) -> OsString {
538-
OsStr::from_bytes(self.name_bytes()).to_os_string()
557+
self.file_name_os_str().to_os_string()
539558
}
540559

541560
#[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "android"))]
542561
pub fn metadata(&self) -> io::Result<FileAttr> {
543562
let fd = cvt(unsafe { dirfd(self.dir.dirp.0) })?;
544-
let name = self.entry.d_name.as_ptr();
563+
let name = self.name_cstr().as_ptr();
545564

546565
cfg_has_statx! {
547566
if let Some(ret) = unsafe { try_statx(
@@ -639,29 +658,21 @@ impl DirEntry {
639658
)
640659
}
641660
}
642-
#[cfg(any(
643-
target_os = "android",
644-
target_os = "linux",
645-
target_os = "emscripten",
646-
target_os = "l4re",
647-
target_os = "haiku",
648-
target_os = "vxworks",
649-
target_os = "espidf"
650-
))]
651-
fn name_bytes(&self) -> &[u8] {
652-
unsafe { CStr::from_ptr(self.entry.d_name.as_ptr()).to_bytes() }
653-
}
654-
#[cfg(any(
655-
target_os = "solaris",
656-
target_os = "illumos",
657-
target_os = "fuchsia",
658-
target_os = "redox"
659-
))]
661+
#[cfg(not(any(
662+
target_os = "macos",
663+
target_os = "ios",
664+
target_os = "netbsd",
665+
target_os = "openbsd",
666+
target_os = "freebsd",
667+
target_os = "dragonfly"
668+
)))]
660669
fn name_bytes(&self) -> &[u8] {
661-
self.name.as_bytes()
670+
self.name_cstr().to_bytes()
662671
}
663672

664673
#[cfg(not(any(
674+
target_os = "android",
675+
target_os = "linux",
665676
target_os = "solaris",
666677
target_os = "illumos",
667678
target_os = "fuchsia",
@@ -670,7 +681,14 @@ impl DirEntry {
670681
fn name_cstr(&self) -> &CStr {
671682
unsafe { CStr::from_ptr(self.entry.d_name.as_ptr()) }
672683
}
673-
#[cfg(any(target_os = "solaris", target_os = "illumos", target_os = "fuchsia"))]
684+
#[cfg(any(
685+
target_os = "android",
686+
target_os = "linux",
687+
target_os = "solaris",
688+
target_os = "illumos",
689+
target_os = "fuchsia",
690+
target_os = "redox"
691+
))]
674692
fn name_cstr(&self) -> &CStr {
675693
&self.name
676694
}
@@ -1076,6 +1094,8 @@ pub fn readdir(p: &Path) -> io::Result<ReadDir> {
10761094
Ok(ReadDir {
10771095
inner: Arc::new(inner),
10781096
#[cfg(not(any(
1097+
target_os = "android",
1098+
target_os = "linux",
10791099
target_os = "solaris",
10801100
target_os = "illumos",
10811101
target_os = "fuchsia",
@@ -1615,6 +1635,8 @@ mod remove_dir_impl {
16151635
ReadDir {
16161636
inner: Arc::new(InnerReadDir { dirp, root: dummy_root }),
16171637
#[cfg(not(any(
1638+
target_os = "android",
1639+
target_os = "linux",
16181640
target_os = "solaris",
16191641
target_os = "illumos",
16201642
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)