Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions library/std/src/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2797,6 +2797,19 @@ impl AsInner<fs_imp::FilePermissions> for Permissions {
}
}

impl ReadDir {
/// Returns a handle for the directory this `ReadDir` is reading.
///
/// Whenever possible, this will directly reuse the underlying handle. However, this may
/// fall back to opening a new handle based on the path, which is subject to race conditions
/// if the file system has changed since the `ReadDir` was created.
// FIXME: before stabilization, ensure that we have a race-free implementation on Windows!
#[unstable(feature = "dirfd", issue = "120426")]
pub fn dir(&self) -> io::Result<Dir> {
self.0.dir().map(|d| Dir { inner: d })
}
}

#[stable(feature = "rust1", since = "1.0.0")]
impl Iterator for ReadDir {
type Item = io::Result<DirEntry>;
Expand Down
13 changes: 13 additions & 0 deletions library/std/src/fs/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3162,6 +3162,19 @@ fn test_dir_metadata() {
assert!(metadata.is_symlink());
}

#[test]
fn test_read_dir_dir() {
let tmpdir = tmpdir();
check!(fs::write(tmpdir.path().join("file.txt"), b"hello"));

let read_dir = check!(fs::read_dir(tmpdir.path()));
let dir = check!(read_dir.dir());
let mut f = check!(dir.open_file("file.txt"));
let mut data = vec![];
check!(f.read_to_end(&mut data));
assert_eq!(data, b"hello");
}

fn root_test_dir(what: &str) -> PathBuf {
crate::env::current_dir().unwrap().ancestors().last().unwrap().join(what)
}
Expand Down
8 changes: 8 additions & 0 deletions library/std/src/sys/fs/hermit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,14 @@ impl FileType {
}
}

impl ReadDir {
pub fn dir(&self) -> io::Result<Dir> {
let mut options = OpenOptions::new();
options.read(true);
Dir::open(&self.inner.root, &options)
}
}

impl fmt::Debug for ReadDir {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame.
Expand Down
8 changes: 8 additions & 0 deletions library/std/src/sys/fs/motor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,14 @@ pub fn readdir(path: &Path) -> io::Result<ReadDir> {
})
}

impl ReadDir {
pub fn dir(&self) -> io::Result<Dir> {
let mut options = OpenOptions::new();
options.read(true);
Dir::open(Path::new(&self.path), &options)
}
}

impl Iterator for ReadDir {
type Item = io::Result<DirEntry>;

Expand Down
8 changes: 8 additions & 0 deletions library/std/src/sys/fs/solid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,14 @@ pub fn readdir(p: &Path) -> io::Result<ReadDir> {
}
}

impl ReadDir {
pub fn dir(&self) -> io::Result<Dir> {
let mut options = OpenOptions::new();
options.read(true);
Dir::open(&self.inner.root, &options)
}
}

impl fmt::Debug for ReadDir {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame.
Expand Down
8 changes: 8 additions & 0 deletions library/std/src/sys/fs/uefi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,14 @@ impl FileType {
}
}

impl ReadDir {
pub fn dir(&self) -> io::Result<Dir> {
let mut options = OpenOptions::new();
options.read(true);
Dir::open(&self.0.path(), &options)
}
}

impl fmt::Debug for ReadDir {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut b = f.debug_struct("ReadDir");
Expand Down
33 changes: 33 additions & 0 deletions library/std/src/sys/fs/unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -796,6 +796,39 @@ impl fmt::Debug for FilePermissions {
}
}

impl ReadDir {
pub fn dir(&self) -> io::Result<Dir> {
let mut options = OpenOptions::new();
options.read(true);

cfg_select! {
// Some targets don't have `dirfd`. Open `Dir` based on path.
any(
target_os = "redox",
target_os = "espidf",
target_os = "horizon",
target_os = "vita",
target_os = "nto",
target_os = "qnx",
target_os = "vxworks",
target_os = "l4re",
) => Dir::open(&self.inner.root, &options),
// Use `dirfd` where possible.
_ => {
let fd = unsafe { libc::dirfd(self.inner.dirp.0) };
// Make this FD into a directory handle. We don't actually drop it,
// so having an `OwnedFd` is fine.
let dir_handle =
mem::ManuallyDrop::new(dir::Dir(unsafe { OwnedFd::from_raw_fd(fd) }));
// We don't want to expose `fd` or even the underlying file description
// as the directory stream has state attached to it. So we open a completely
// new file description based on this one.
dir_handle.open_dir(Path::new("."), &options)
}
}
}
}

impl fmt::Debug for ReadDir {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame.
Expand Down
2 changes: 1 addition & 1 deletion library/std/src/sys/fs/unix/dir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ impl Dir {
pub fn self_metadata(&self) -> io::Result<FileAttr> {
// Reuse the implementation for files, which should work for all FDs.
let fd = self.0.as_raw_fd();
let f = core::mem::ManuallyDrop::new(File(
let f = mem::ManuallyDrop::new(File(
// SAFETY: we borrowed `self` so the FD will not be closed while this function runs.
unsafe { FileDesc::from_raw_fd(fd) },
));
Expand Down
6 changes: 6 additions & 0 deletions library/std/src/sys/fs/unsupported.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,12 @@ impl fmt::Debug for FileType {
}
}

impl ReadDir {
pub fn dir(&self) -> io::Result<Dir> {
self.0
}
}

impl fmt::Debug for ReadDir {
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0
Expand Down
6 changes: 6 additions & 0 deletions library/std/src/sys/fs/vexos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,12 @@ impl FileType {
}
}

impl ReadDir {
pub fn dir(&self) -> io::Result<Dir> {
self.0
}
}

impl fmt::Debug for ReadDir {
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0
Expand Down
9 changes: 9 additions & 0 deletions library/std/src/sys/fs/windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,15 @@ impl fmt::Debug for c::FILETIME {
#[derive(Debug)]
pub struct DirBuilder;

impl ReadDir {
pub fn dir(&self) -> io::Result<Dir> {
let mut options = OpenOptions::new();
options.read(true);
// FIXME: use race-free handle-based approach instead.
Dir::open(&self.root, &options)
}
}

impl fmt::Debug for ReadDir {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame.
Expand Down
15 changes: 7 additions & 8 deletions src/tools/miri/src/shims/unix/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1205,14 +1205,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {

match result {
Ok(read_dir) => {
// Also open the same directory as a directory handle, so we have
// an underlying FD.
// FIXME(https://github.com/rust-lang/miri/issues/5326): This is racy! We can't even
// verify whether there was a race. We just trust that the directory did not change
// in between above and here. One day, the standard library will support converting
// between `Dir` and `ReadDir` (one of the two directions would suffice for our
// needs), then we'll use that.
let Ok(dir) = fs::Dir::open(&name) else {
// Create an underlying FD as well, in case someone calls `dirfd` later.
// (We could do this lazily but that does not seem worth it.)
#[cfg(not(bootstrap))]
let dir = read_dir.dir();
#[cfg(bootstrap)]
let dir = fs::Dir::open(&name);
let Ok(dir) = dir else {
throw_unsup_format!(
"cannot `opendir` this directory: failed to create directory handle"
);
Expand Down
2 changes: 2 additions & 0 deletions src/tools/miri/tests/pass-dep/libc/libc-fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1373,6 +1373,8 @@ fn test_dirfd() {
assert!(!dir.is_null());

let dirfd = unsafe { libc::dirfd(dir) };
let dirfd2 = unsafe { libc::dirfd(dir) };
assert_eq!(dirfd, dirfd2); // make sure we always return the same one

let mut stat = MaybeUninit::<libc::stat>::uninit();
errno_check(unsafe { libc::fstat(dirfd, stat.as_mut_ptr()) });
Expand Down
Loading