/// `DIR*` pubstruct Dir { /// The `libc` `DIR` pointer.
libc_dir: NonNull<c::DIR>,
/// Have we seen any errors in this iteration?
any_errors: bool,
}
impl Dir { /// Take ownership of `fd` and construct a `Dir` that reads entries from /// the given directory file descriptor. #[inline] pubfn new<Fd: Into<OwnedFd>>(fd: Fd) -> io::Result<Self> { Self::_new(fd.into())
}
#[inline] fn _new(fd: OwnedFd) -> io::Result<Self> { let raw = owned_fd(fd); unsafe { let libc_dir = c::fdopendir(raw);
/// Borrow `fd` and construct a `Dir` that reads entries from the given /// directory file descriptor. #[inline] pubfn read_from<Fd: AsFd>(fd: Fd) -> io::Result<Self> { Self::_read_from(fd.as_fd())
}
// Given an arbitrary `OwnedFd`, it's impossible to know whether the // user holds a `dup`'d copy which could continue to modify the // file description state, which would cause Undefined Behavior after // our call to `fdopendir`. To prevent this, we obtain an independent // `OwnedFd`. let flags = fcntl_getfl(fd)?; let fd_for_dir = match openat(fd, cstr!("."), flags | OFlags::CLOEXEC, Mode::empty()) {
Ok(fd) => fd, #[cfg(not(target_os = "wasi"))]
Err(io::Errno::NOENT) => { // If "." doesn't exist, it means the directory was removed. // We treat that as iterating through a directory with no // entries.
any_errors = true; crate::io::dup(fd)?
}
Err(err) => return Err(err),
};
let raw = owned_fd(fd_for_dir); unsafe { let libc_dir = c::fdopendir(raw);
/// `readdir(self)`, where `None` means the end of the directory. pubfn read(&mutself) -> Option<io::Result<DirEntry>> { // If we've seen errors, don't continue to try to read anything further. ifself.any_errors { return None;
}
set_errno(Errno(0)); let dirent_ptr = unsafe { libc_readdir(self.libc_dir.as_ptr()) }; if dirent_ptr.is_null() { let curr_errno = errno().0; if curr_errno == 0 { // We successfully reached the end of the stream.
None
} else { // `errno` is unknown or non-zero, so an error occurred. self.any_errors = true;
Some(Err(io::Errno(curr_errno)))
}
} else { // We successfully read an entry. unsafe { let dirent = &*dirent_ptr;
// We have our own copy of OpenBSD's dirent; check that the // layout minimally matches libc's. #[cfg(target_os = "openbsd")]
check_dirent_layout(dirent);
/// `Dir` implements `Send` but not `Sync`, because we use `readdir` which is /// not guaranteed to be thread-safe. Users can wrap this in a `Mutex` if they /// need `Sync`, which is effectively what'd need to do to implement `Sync` /// ourselves. unsafeimpl Send for Dir {}
impl Drop for Dir { #[inline] fn drop(&mutself) { unsafe { c::closedir(self.libc_dir.as_ptr()) };
}
}
impl Iterator for Dir { type Item = io::Result<DirEntry>;
impl DirEntry { /// Returns the file name of this directory entry. #[inline] pubfn file_name(&self) -> &CStr {
&self.name
}
/// Returns the type of this directory entry. #[cfg(not(any(
solarish,
target_os = "aix",
target_os = "haiku",
target_os = "nto",
target_os = "vita"
)))] #[inline] pubfn file_type(&self) -> FileType {
FileType::from_dirent_d_type(self.d_type)
}
/// Return the inode number of this directory entry. #[cfg(not(any(freebsdlike, netbsdlike, target_os = "vita")))] #[inline] pubfn ino(&self) -> u64 { self.d_ino as u64
}
/// Return the inode number of this directory entry. #[cfg(any(freebsdlike, netbsdlike))] #[inline] pubfn ino(&self) -> u64 { #[allow(clippy::useless_conversion)] self.d_fileno.into()
}
}
/// libc's OpenBSD `dirent` has a private field so we can't construct it /// directly, so we declare it ourselves to make all fields accessible. #[cfg(target_os = "openbsd")] #[repr(C)] #[derive(Debug)] struct libc_dirent {
d_fileno: c::ino_t,
d_off: c::off_t,
d_reclen: u16,
d_type: u8,
d_namlen: u8,
__d_padding: [u8; 4],
d_name: [c::c_char; 256],
}
/// We have our own copy of OpenBSD's dirent; check that the layout /// minimally matches libc's. #[cfg(target_os = "openbsd")] fn check_dirent_layout(dirent: &c::dirent) { usecrate::utils::as_ptr;
// Check that the basic layouts match. #[cfg(test)]
{
assert_eq_size!(libc_dirent, c::dirent);
assert_eq_size!(libc_dirent, c::dirent);
}
// Check that the field offsets match.
assert_eq!(
{ let z = libc_dirent {
d_fileno: 0_u64,
d_off: 0_i64,
d_reclen: 0_u16,
d_type: 0_u8,
d_namlen: 0_u8,
__d_padding: [0_u8; 4],
d_name: [0as c::c_char; 256],
}; let base = as_ptr(&z) as usize;
(
(as_ptr(&z.d_fileno) as usize) - base,
(as_ptr(&z.d_off) as usize) - base,
(as_ptr(&z.d_reclen) as usize) - base,
(as_ptr(&z.d_type) as usize) - base,
(as_ptr(&z.d_namlen) as usize) - base,
(as_ptr(&z.d_name) as usize) - base,
)
},
{ let z = dirent; let base = as_ptr(z) as usize;
(
(as_ptr(&z.d_fileno) as usize) - base,
(as_ptr(&z.d_off) as usize) - base,
(as_ptr(&z.d_reclen) as usize) - base,
(as_ptr(&z.d_type) as usize) - base,
(as_ptr(&z.d_namlen) as usize) - base,
(as_ptr(&z.d_name) as usize) - base,
)
}
);
}
#[test] fn dir_iterator_handles_io_errors() { // create a dir, keep the FD, then delete the dir let tmp = tempfile::tempdir().unwrap(); let fd = crate::fs::openat( crate::fs::CWD,
tmp.path(), crate::fs::OFlags::RDONLY | crate::fs::OFlags::CLOEXEC, crate::fs::Mode::empty(),
)
.unwrap();
// Reach inside the `Dir` and replace its directory with a file, which // will cause the subsequent `readdir` to fail. unsafe { let raw_fd = c::dirfd(dir.libc_dir.as_ptr()); letmut owned_fd: crate::fd::OwnedFd = crate::fd::FromRawFd::from_raw_fd(raw_fd); crate::io::dup2(&file_fd, &mut owned_fd).unwrap();
core::mem::forget(owned_fd);
}
// FreeBSD and macOS seem to read some directory entries before we call // `.next()`. #[cfg(any(apple, freebsdlike))]
{
dir.rewind();
}
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.