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
40 changes: 36 additions & 4 deletions library/std/src/sys/fs/hermit.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use alloc_crate::borrow::Cow;

use crate::ffi::{CStr, OsStr, OsString, c_char};
use crate::fs::TryLockError;
use crate::io::{self, BorrowedCursor, Error, ErrorKind, IoSlice, IoSliceMut, SeekFrom};
Expand Down Expand Up @@ -53,6 +55,37 @@ impl ReadDir {
}
}

/// Specialization trait used to construct a `ReadDir`
trait ReadDirFromPath<P> {
fn from_path(dir: Vec<u8>, path: P) -> Self;
}

impl<P: AsRef<Path>> ReadDirFromPath<P> for ReadDir {
default fn from_path(dir: Vec<u8>, path: P) -> Self {
let inner = InnerReadDir { root: path.as_ref().to_path_buf(), dir };
ReadDir::new(inner)
}
}

/// This constructs a `ReadDir` for all types that can be converted
/// into `PathBuf` without allocating
macro_rules! impl_read_dir_from_path {
($t:ty) => {
impl ReadDirFromPath<$t> for ReadDir {
fn from_path(dir: Vec<u8>, path: $t) -> Self {
let inner = InnerReadDir::new(path.into(), dir);
ReadDir::new(inner)
}
}
};
}

impl_read_dir_from_path!(PathBuf);
impl_read_dir_from_path!(Box<Path>);
impl_read_dir_from_path!(Cow<'_, Path>);
impl_read_dir_from_path!(OsString);
impl_read_dir_from_path!(String);

pub struct DirEntry {
/// path to the entry
root: PathBuf,
Expand Down Expand Up @@ -512,12 +545,11 @@ impl FromRawFd for File {
}
}

pub fn readdir(path: &Path) -> io::Result<ReadDir> {
let fd_raw = run_path_with_cstr(path, &|path| {
pub fn readdir<P: AsRef<Path>>(path: P) -> io::Result<ReadDir> {
let fd_raw = run_path_with_cstr(path.as_ref(), &|path| {
cvt(unsafe { hermit_abi::open(path.as_ptr(), O_RDONLY | O_DIRECTORY, 0) })
})?;
let fd = unsafe { FileDesc::from_raw_fd(fd_raw as i32) };
let root = path.to_path_buf();

// read all director entries
let mut vec: Vec<u8> = Vec::new();
Expand Down Expand Up @@ -551,7 +583,7 @@ pub fn readdir(path: &Path) -> io::Result<ReadDir> {
}
}

Ok(ReadDir::new(InnerReadDir::new(root, vec)))
Ok(<ReadDir as ReadDirFromPath<P>>::from_path(vec, path))
}

pub fn unlink(path: &Path) -> io::Result<()> {
Expand Down
2 changes: 1 addition & 1 deletion library/std/src/sys/fs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ pub use imp::{
ReadDir,
};

pub fn read_dir(path: &Path) -> io::Result<ReadDir> {
pub fn read_dir<P: AsRef<Path>>(path: P) -> io::Result<ReadDir> {
Comment thread
bjorn3 marked this conversation as resolved.
// FIXME: use with_native_path on all platforms
imp::readdir(path)
}
Expand Down
51 changes: 45 additions & 6 deletions library/std/src/sys/fs/motor.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use alloc_crate::borrow::Cow;

use crate::ffi::OsString;
use crate::hash::Hash;
use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom};
Expand Down Expand Up @@ -377,12 +379,49 @@ impl Drop for ReadDir {
}
}

pub fn readdir(path: &Path) -> io::Result<ReadDir> {
let path = path.to_str().ok_or(io::Error::from(io::ErrorKind::InvalidFilename))?;
Ok(ReadDir {
rt_fd: moto_rt::fs::opendir(path).map_err(map_motor_error)?,
path: path.to_owned(),
})
/// Specialization trait used to construct a `ReadDir`
trait ReadDirFromPath<P> {
fn from_path(path: P) -> io::Result<ReadDir>;
}

impl<P: AsRef<Path>> ReadDirFromPath<P> for ReadDir {
default fn from_path(path: P) -> io::Result<ReadDir> {
let path = path.as_ref().to_str().ok_or(io::Error::from(io::ErrorKind::InvalidFilename))?;
Ok(ReadDir {
rt_fd: moto_rt::fs::opendir(path).map_err(map_motor_error)?,
path: path.to_owned(),
})
}
}

/// This constructs a `ReadDir` for all types that can be converted
/// into `String` without allocating
macro_rules! impl_read_dir_from_path {
($t:ty) => {
impl ReadDirFromPath<$t> for ReadDir {
fn from_path(path: $t) -> io::Result<ReadDir> {
let path_buf: PathBuf = path.into();
let path = path_buf.into_os_string().into_string();
match path {
Err(_) => return Err(io::Error::from(io::ErrorKind::InvalidFilename)),
Ok(path) => Ok(ReadDir {
rt_fd: moto_rt::fs::opendir(path).map_err(map_motor_error)?,
path,
}),
}
}
}
};
}

impl_read_dir_from_path!(PathBuf);
impl_read_dir_from_path!(Box<Path>);
impl_read_dir_from_path!(Cow<'_, Path>);
impl_read_dir_from_path!(OsString);
impl_read_dir_from_path!(String);

pub fn readdir<P: AsRef<Path>>(path: P) -> io::Result<ReadDir> {
<ReadDir as ReadDirFromPath<P>>::from_path(path)
}

impl Iterator for ReadDir {
Expand Down
40 changes: 36 additions & 4 deletions library/std/src/sys/fs/solid.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#![allow(dead_code)]

use alloc_crate::borrow::Cow;

use crate::ffi::{CStr, CString, OsStr, OsString};
use crate::fmt;
use crate::fs::TryLockError;
Expand Down Expand Up @@ -146,19 +148,49 @@ impl FileType {
}
}

pub fn readdir(p: &Path) -> io::Result<ReadDir> {
pub fn readdir<P: AsRef<Path>>(p: P) -> io::Result<ReadDir> {
unsafe {
let mut dir = MaybeUninit::uninit();
error::SolidError::err_if_negative(abi::SOLID_FS_OpenDir(
cstr(p)?.as_ptr(),
cstr(p.as_ref())?.as_ptr(),
dir.as_mut_ptr(),
))
.map_err(|e| e.as_io_error())?;
let inner = Arc::new(InnerReadDir { dirp: dir.assume_init(), root: p.to_owned() });
Ok(ReadDir { inner })
Ok(<ReadDir as ReadDirFromPath<P>>::from_path(dir.assume_init(), p))
}
}

/// Specialization trait used to construct a `ReadDir`
trait ReadDirFromPath<P> {
fn from_path(dirp: abi::S_DIR, path: P) -> Self;
}

impl<P: AsRef<Path>> ReadDirFromPath<P> for ReadDir {
default fn from_path(dirp: abi::S_DIR, path: P) -> Self {
let inner = InnerReadDir { dirp, root: path.as_ref().to_path_buf() };
ReadDir { inner: Arc::new(inner) }
}
}

/// This constructs a `ReadDir` for all types that can be converted
/// into `PathBuf` without allocating
macro_rules! impl_read_dir_from_path {
($t:ty) => {
impl ReadDirFromPath<$t> for ReadDir {
fn from_path(dirp: abi::S_DIR, path: $t) -> Self {
let inner = InnerReadDir { dirp, root: path.into() };
ReadDir { inner: Arc::new(inner) }
}
}
};
}

impl_read_dir_from_path!(PathBuf);
impl_read_dir_from_path!(Box<Path>);
impl_read_dir_from_path!(Cow<'_, Path>);
impl_read_dir_from_path!(OsString);
impl_read_dir_from_path!(String);

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
5 changes: 2 additions & 3 deletions library/std/src/sys/fs/uefi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -415,9 +415,8 @@ impl fmt::Debug for File {
}
}

pub fn readdir(p: &Path) -> io::Result<ReadDir> {
let path = crate::path::absolute(p)?;

@asder8215 asder8215 Apr 16, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to be unnecessary since internally uefi_fs::File::from_path already uses crate::path::absolute(p) on the given path reference.

View changes since the review

let f = uefi_fs::File::from_path(&path, file::MODE_READ, 0)?;
pub fn readdir<P: AsRef<Path>>(p: P) -> io::Result<ReadDir> {
let f = uefi_fs::File::from_path(p.as_ref(), file::MODE_READ, 0)?;
let file_info = f.file_info()?;
let file_attr = FileAttr::from_uefi(file_info);

Expand Down
40 changes: 35 additions & 5 deletions library/std/src/sys/fs/unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#[cfg(test)]
mod tests;

use alloc_crate::borrow::Cow;
#[cfg(all(target_os = "linux", target_env = "gnu"))]
use libc::c_char;
#[cfg(any(
Expand Down Expand Up @@ -272,6 +273,37 @@ impl ReadDir {
}
}

/// Specialization trait used to construct a `ReadDir`
trait ReadDirFromPath<P> {
fn from_path(dirp: DirStream, path: P) -> Self;
}

impl<P: AsRef<Path>> ReadDirFromPath<P> for ReadDir {
default fn from_path(dirp: DirStream, path: P) -> Self {
let inner = InnerReadDir { dirp, root: path.as_ref().to_path_buf() };
ReadDir::new(inner)
}
}

/// This constructs a `ReadDir` for all types that can be converted
/// into `PathBuf` without allocating
macro_rules! impl_read_dir_from_path {
($t:ty) => {
impl ReadDirFromPath<$t> for ReadDir {
fn from_path(dirp: DirStream, path: $t) -> Self {
let inner = InnerReadDir { dirp, root: path.into() };
ReadDir::new(inner)
}
}
};
}

impl_read_dir_from_path!(PathBuf);
impl_read_dir_from_path!(Box<Path>);
impl_read_dir_from_path!(Cow<'_, Path>);
impl_read_dir_from_path!(OsString);
impl_read_dir_from_path!(String);

struct DirStream(*mut libc::DIR);

// dir::Dir requires openat support
Expand Down Expand Up @@ -2077,14 +2109,12 @@ impl fmt::Debug for Mode {
}
}

pub fn readdir(path: &Path) -> io::Result<ReadDir> {
let ptr = run_path_with_cstr(path, &|p| unsafe { Ok(libc::opendir(p.as_ptr())) })?;
pub fn readdir<P: AsRef<Path>>(path: P) -> io::Result<ReadDir> {
let ptr = run_path_with_cstr(path.as_ref(), &|p| unsafe { Ok(libc::opendir(p.as_ptr())) })?;
if ptr.is_null() {
Err(Error::last_os_error())
} else {
let root = path.to_path_buf();
let inner = InnerReadDir { dirp: DirStream(ptr), root };
Ok(ReadDir::new(inner))
Ok(<ReadDir as ReadDirFromPath<P>>::from_path(DirStream(ptr), path))
}
}

Expand Down
2 changes: 1 addition & 1 deletion library/std/src/sys/fs/unsupported.rs
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ impl fmt::Debug for File {
}
}

pub fn readdir(_p: &Path) -> io::Result<ReadDir> {
pub fn readdir<P: AsRef<Path>>(_p: P) -> io::Result<ReadDir> {
unsupported()
}

Expand Down
2 changes: 1 addition & 1 deletion library/std/src/sys/fs/vexos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -477,7 +477,7 @@ impl Drop for File {
}
}

pub fn readdir(_p: &Path) -> io::Result<ReadDir> {
pub fn readdir<P: AsRef<Path>>(_p: P) -> io::Result<ReadDir> {
// While there *is* a userspace function for reading file directories,
// the necessary implementation cannot currently be done cleanly, as
// VEXos does not expose directory length to user programs.
Expand Down
60 changes: 50 additions & 10 deletions library/std/src/sys/fs/windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,47 @@ pub struct ReadDir {
first: Option<c::WIN32_FIND_DATAW>,
}

/// Specialization trait used to construct a `ReadDir`
trait ReadDirFromPath<P> {
fn from_path(
handle: Option<FindNextFileHandle>,
path: P,
first: Option<c::WIN32_FIND_DATAW>,
) -> Self;
}

impl<P: AsRef<Path>> ReadDirFromPath<P> for ReadDir {
default fn from_path(
handle: Option<FindNextFileHandle>,
path: P,
first: Option<c::WIN32_FIND_DATAW>,
) -> Self {
ReadDir { handle, root: Arc::new(path.as_ref().to_path_buf()), first }
}
}

/// This constructs a `ReadDir` for all types that can be converted
/// into `PathBuf` without allocating
macro_rules! impl_read_dir_from_path {
($t:ty) => {
impl ReadDirFromPath<$t> for ReadDir {
fn from_path(
handle: Option<FindNextFileHandle>,
path: $t,
first: Option<c::WIN32_FIND_DATAW>,
) -> Self {
ReadDir { handle, root: Arc::new(path.into()), first }
}
}
};
}

impl_read_dir_from_path!(PathBuf);
impl_read_dir_from_path!(Box<Path>);
impl_read_dir_from_path!(Cow<'_, Path>);
impl_read_dir_from_path!(OsString);
impl_read_dir_from_path!(String);

struct FindNextFileHandle(c::HANDLE);

unsafe impl Send for FindNextFileHandle {}
Expand Down Expand Up @@ -1222,17 +1263,16 @@ impl DirBuilder {
}
}

pub fn readdir(p: &Path) -> io::Result<ReadDir> {
pub fn readdir<P: AsRef<Path>>(p: P) -> io::Result<ReadDir> {
// We push a `*` to the end of the path which cause the empty path to be
// treated as the current directory. So, for consistency with other platforms,
// we explicitly error on the empty path.
if p.as_os_str().is_empty() {
if p.as_ref().as_os_str().is_empty() {
// Return an error code consistent with other ways of opening files.
// E.g. fs::metadata or File::open.
return Err(io::Error::from_raw_os_error(c::ERROR_PATH_NOT_FOUND as i32));
}
let root = p.to_path_buf();
let star = p.join("*");
let star = p.as_ref().join("*");
let path = maybe_verbatim(&star)?;

unsafe {
Expand All @@ -1254,11 +1294,11 @@ pub fn readdir(p: &Path) -> io::Result<ReadDir> {
);

if find_handle != c::INVALID_HANDLE_VALUE {
Ok(ReadDir {
handle: Some(FindNextFileHandle(find_handle)),
root: Arc::new(root),
first: Some(wfd),
})
Ok(<ReadDir as ReadDirFromPath<P>>::from_path(
Some(FindNextFileHandle(find_handle)),
p,
Some(wfd),
))
} else {
// The status `ERROR_FILE_NOT_FOUND` is returned by the `FindFirstFileExW` function
// if no matching files can be found, but not necessarily that the path to find the
Expand All @@ -1273,7 +1313,7 @@ pub fn readdir(p: &Path) -> io::Result<ReadDir> {
// See issue #120040: https://github.com/rust-lang/rust/issues/120040.
let last_error = api::get_last_error();
if last_error == WinError::FILE_NOT_FOUND {
return Ok(ReadDir { handle: None, root: Arc::new(root), first: None });
return Ok(<ReadDir as ReadDirFromPath<P>>::from_path(None, p, None));
}

// Just return the error constructed from the raw OS error if the above is not the case.
Expand Down
Loading