usecrate::errno::Errno; usecrate::fcntl::{self, OFlag}; usecrate::sys; usecrate::{Error, NixPath, Result}; use cfg_if::cfg_if; use std::ffi; use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd}; use std::ptr;
#[cfg(target_os = "linux")] use libc::{dirent64 as dirent, readdir64_r as readdir_r};
#[cfg(not(target_os = "linux"))] use libc::{dirent, readdir_r};
/// An open directory. /// /// This is a lower-level interface than `std::fs::ReadDir`. Notable differences: /// * can be opened from a file descriptor (as returned by `openat`, perhaps before knowing /// if the path represents a file or directory). /// * implements `AsRawFd`, so it can be passed to `fstat`, `openat`, etc. /// The file descriptor continues to be owned by the `Dir`, so callers must not keep a `RawFd` /// after the `Dir` is dropped. /// * can be iterated through multiple times without closing and reopening the file /// descriptor. Each iteration rewinds when finished. /// * returns entries for `.` (current directory) and `..` (parent directory). /// * returns entries' names as a `CStr` (no allocation or conversion beyond whatever libc /// does). #[derive(Debug, Eq, Hash, PartialEq)] pubstruct Dir(ptr::NonNull<libc::DIR>);
impl Dir { /// Opens the given path as with `fcntl::open`. pubfn open<P: ?Sized + NixPath>(
path: &P,
oflag: OFlag,
mode: sys::stat::Mode,
) -> Result<Self> { let fd = fcntl::open(path, oflag, mode)?;
Dir::from_fd(fd)
}
/// Opens the given path as with `fcntl::openat`. pubfn openat<P: ?Sized + NixPath>(
dirfd: Option<RawFd>,
path: &P,
oflag: OFlag,
mode: sys::stat::Mode,
) -> Result<Self> { let fd = fcntl::openat(dirfd, path, oflag, mode)?;
Dir::from_fd(fd)
}
/// Converts from a descriptor-based object, closing the descriptor on success or failure. #[inline] pubfn from<F: IntoRawFd>(fd: F) -> Result<Self> {
Dir::from_fd(fd.into_raw_fd())
}
/// Converts from a file descriptor, closing it on failure. #[doc(alias("fdopendir"))] pubfn from_fd(fd: RawFd) -> Result<Self> { let d = ptr::NonNull::new(unsafe { libc::fdopendir(fd) }).ok_or_else(
|| { let e = Error::last(); unsafe { libc::close(fd) };
e
},
)?;
Ok(Dir(d))
}
/// Returns an iterator of `Result<Entry>` which rewinds when finished. pubfn iter(&mutself) -> Iter {
Iter(self)
}
}
// `Dir` is not `Sync`. With the current implementation, it could be, but according to // https://www.gnu.org/software/libc/manual/html_node/Reading_002fClosing-Directory.html, // future versions of POSIX are likely to obsolete `readdir_r` and specify that it's unsafe to // call `readdir` simultaneously from multiple threads. // // `Dir` is safe to pass from one thread to another, as it's not reference-counted. unsafeimpl Send for Dir {}
impl AsRawFd for Dir { fn as_raw_fd(&self) -> RawFd { unsafe { libc::dirfd(self.0.as_ptr()) }
}
}
impl Drop for Dir { fn drop(&mutself) { let e = Errno::result(unsafe { libc::closedir(self.0.as_ptr()) }); if !std::thread::panicking() && e == Err(Errno::EBADF) {
panic!("Closing an invalid file descriptor!");
};
}
}
// The pass by mut is technically needless only because the inner NonNull is // Copy. But philosophically we're mutating the Dir, so we pass by mut. #[allow(clippy::needless_pass_by_ref_mut)] fn next(dir: &mut Dir) -> Option<Result<Entry>> { unsafe { // Note: POSIX specifies that portable applications should dynamically allocate a // buffer with room for a `d_name` field of size `pathconf(..., _PC_NAME_MAX)` plus 1 // for the NUL byte. It doesn't look like the std library does this; it just uses // fixed-sized buffers (and libc's dirent seems to be sized so this is appropriate). // Probably fine here too then. letmut ent = std::mem::MaybeUninit::<dirent>::uninit(); letmut result = ptr::null_mut(); iflet Err(e) = Errno::result(readdir_r(
dir.0.as_ptr(),
ent.as_mut_ptr(),
&mut result,
)) { return Some(Err(e));
} if result.is_null() { return None;
}
assert_eq!(result, ent.as_mut_ptr());
Some(Ok(Entry(ent.assume_init())))
}
}
/// Return type of [`Dir::iter`]. #[derive(Debug, Eq, Hash, PartialEq)] pubstruct Iter<'d>(&'d mut Dir);
impl<'d> Iterator for Iter<'d> { type Item = Result<Entry>;
/// The file descriptor continues to be owned by the `OwningIter`, /// so callers must not keep a `RawFd` after the `OwningIter` is dropped. impl AsRawFd for OwningIter { fn as_raw_fd(&self) -> RawFd { self.0.as_raw_fd()
}
}
impl IntoIterator for Dir { type Item = Result<Entry>; type IntoIter = OwningIter;
/// Creates a owning iterator, that is, one that takes ownership of the /// `Dir`. The `Dir` cannot be used after calling this. This can be useful /// when you have a function that both creates a `Dir` instance and returns /// an `Iterator`. /// /// Example: /// /// ``` /// use nix::{dir::Dir, fcntl::OFlag, sys::stat::Mode}; /// use std::{iter::Iterator, string::String}; /// /// fn ls_upper(dirname: &str) -> impl Iterator<Item=String> { /// let d = Dir::open(dirname, OFlag::O_DIRECTORY, Mode::S_IXUSR).unwrap(); /// d.into_iter().map(|x| x.unwrap().file_name().as_ref().to_string_lossy().to_ascii_uppercase()) /// } /// ``` fn into_iter(self) -> Self::IntoIter {
OwningIter(self)
}
}
/// A directory entry, similar to `std::fs::DirEntry`. /// /// Note that unlike the std version, this may represent the `.` or `..` entries. #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] #[repr(transparent)] pubstruct Entry(dirent);
/// Type of file referenced by a directory entry #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] pubenumType { /// FIFO (Named pipe)
Fifo, /// Character device
CharacterDevice, /// Directory
Directory, /// Block device
BlockDevice, /// Regular file
File, /// Symbolic link
Symlink, /// Unix-domain socket
Socket,
}
impl Entry { /// Returns the inode number (`d_ino`) of the underlying `dirent`. #[allow(clippy::useless_conversion)] // Not useless on all OSes // The cast is not unnecessary on all platforms. #[allow(clippy::unnecessary_cast)] pubfn ino(&self) -> u64 {
cfg_if! { if#[cfg(any(target_os = "aix",
target_os = "emscripten",
target_os = "fuchsia",
target_os = "haiku",
target_os = "hurd",
solarish,
linux_android,
apple_targets))] { self.0.d_ino as u64
} else {
u64::from(self.0.d_fileno)
}
}
}
/// Returns the bare file name of this directory entry without any other leading path component. pubfn file_name(&self) -> &ffi::CStr { unsafe { ffi::CStr::from_ptr(self.0.d_name.as_ptr()) }
}
/// Returns the type of this directory entry, if known. /// /// See platform `readdir(3)` or `dirent(5)` manpage for when the file type is known; /// notably, some Linux filesystems don't implement this. The caller should use `stat` or /// `fstat` if this returns `None`. pubfn file_type(&self) -> Option<Type> { #[cfg(not(any(solarish, target_os = "aix", target_os = "haiku")))] matchself.0.d_type {
libc::DT_FIFO => Some(Type::Fifo),
libc::DT_CHR => Some(Type::CharacterDevice),
libc::DT_DIR => Some(Type::Directory),
libc::DT_BLK => Some(Type::BlockDevice),
libc::DT_REG => Some(Type::File),
libc::DT_LNK => Some(Type::Symlink),
libc::DT_SOCK => Some(Type::Socket), /* libc::DT_UNKNOWN | */ _ => None,
}
// illumos, Solaris, and Haiku systems do not have the d_type member at all: #[cfg(any(solarish, target_os = "aix", target_os = "haiku"))]
None
}
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.17 Sekunden
(vorverarbeitet am 2026-06-18)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.