1b8a62b91Sopenharmony_ciuse super::super::c; 2b8a62b91Sopenharmony_ciuse super::super::conv::owned_fd; 3b8a62b91Sopenharmony_ci#[cfg(not(any(target_os = "haiku", target_os = "illumos", target_os = "solaris")))] 4b8a62b91Sopenharmony_ciuse super::types::FileType; 5b8a62b91Sopenharmony_ciuse crate::fd::{AsFd, BorrowedFd}; 6b8a62b91Sopenharmony_ciuse crate::ffi::CStr; 7b8a62b91Sopenharmony_ci#[cfg(target_os = "wasi")] 8b8a62b91Sopenharmony_ciuse crate::ffi::CString; 9b8a62b91Sopenharmony_ciuse crate::fs::{fcntl_getfl, fstat, openat, Mode, OFlags, Stat}; 10b8a62b91Sopenharmony_ci#[cfg(not(any( 11b8a62b91Sopenharmony_ci target_os = "haiku", 12b8a62b91Sopenharmony_ci target_os = "illumos", 13b8a62b91Sopenharmony_ci target_os = "netbsd", 14b8a62b91Sopenharmony_ci target_os = "redox", 15b8a62b91Sopenharmony_ci target_os = "solaris", 16b8a62b91Sopenharmony_ci target_os = "wasi", 17b8a62b91Sopenharmony_ci)))] 18b8a62b91Sopenharmony_ciuse crate::fs::{fstatfs, StatFs}; 19b8a62b91Sopenharmony_ci#[cfg(not(any( 20b8a62b91Sopenharmony_ci target_os = "haiku", 21b8a62b91Sopenharmony_ci target_os = "illumos", 22b8a62b91Sopenharmony_ci target_os = "redox", 23b8a62b91Sopenharmony_ci target_os = "solaris", 24b8a62b91Sopenharmony_ci target_os = "wasi", 25b8a62b91Sopenharmony_ci)))] 26b8a62b91Sopenharmony_ciuse crate::fs::{fstatvfs, StatVfs}; 27b8a62b91Sopenharmony_ciuse crate::io; 28b8a62b91Sopenharmony_ci#[cfg(not(any(target_os = "fuchsia", target_os = "wasi")))] 29b8a62b91Sopenharmony_ciuse crate::process::fchdir; 30b8a62b91Sopenharmony_ci#[cfg(target_os = "wasi")] 31b8a62b91Sopenharmony_ciuse alloc::borrow::ToOwned; 32b8a62b91Sopenharmony_ci#[cfg(not(any( 33b8a62b91Sopenharmony_ci target_os = "android", 34b8a62b91Sopenharmony_ci target_os = "emscripten", 35b8a62b91Sopenharmony_ci target_os = "l4re", 36b8a62b91Sopenharmony_ci target_os = "linux", 37b8a62b91Sopenharmony_ci target_os = "openbsd", 38b8a62b91Sopenharmony_ci)))] 39b8a62b91Sopenharmony_ciuse c::dirent as libc_dirent; 40b8a62b91Sopenharmony_ci#[cfg(not(any( 41b8a62b91Sopenharmony_ci target_os = "android", 42b8a62b91Sopenharmony_ci target_os = "emscripten", 43b8a62b91Sopenharmony_ci target_os = "l4re", 44b8a62b91Sopenharmony_ci target_os = "linux", 45b8a62b91Sopenharmony_ci)))] 46b8a62b91Sopenharmony_ciuse c::readdir as libc_readdir; 47b8a62b91Sopenharmony_ci#[cfg(any( 48b8a62b91Sopenharmony_ci target_os = "android", 49b8a62b91Sopenharmony_ci target_os = "emscripten", 50b8a62b91Sopenharmony_ci target_os = "l4re", 51b8a62b91Sopenharmony_ci target_os = "linux", 52b8a62b91Sopenharmony_ci))] 53b8a62b91Sopenharmony_ciuse c::{dirent64 as libc_dirent, readdir64 as libc_readdir}; 54b8a62b91Sopenharmony_ciuse core::fmt; 55b8a62b91Sopenharmony_ciuse core::mem::zeroed; 56b8a62b91Sopenharmony_ciuse core::ptr::NonNull; 57b8a62b91Sopenharmony_ciuse libc_errno::{errno, set_errno, Errno}; 58b8a62b91Sopenharmony_ci 59b8a62b91Sopenharmony_ci/// `DIR*` 60b8a62b91Sopenharmony_ci#[repr(transparent)] 61b8a62b91Sopenharmony_cipub struct Dir(NonNull<c::DIR>); 62b8a62b91Sopenharmony_ci 63b8a62b91Sopenharmony_ciimpl Dir { 64b8a62b91Sopenharmony_ci /// Construct a `Dir` that reads entries from the given directory 65b8a62b91Sopenharmony_ci /// file descriptor. 66b8a62b91Sopenharmony_ci #[inline] 67b8a62b91Sopenharmony_ci pub fn read_from<Fd: AsFd>(fd: Fd) -> io::Result<Self> { 68b8a62b91Sopenharmony_ci Self::_read_from(fd.as_fd()) 69b8a62b91Sopenharmony_ci } 70b8a62b91Sopenharmony_ci 71b8a62b91Sopenharmony_ci #[inline] 72b8a62b91Sopenharmony_ci fn _read_from(fd: BorrowedFd<'_>) -> io::Result<Self> { 73b8a62b91Sopenharmony_ci // Given an arbitrary `OwnedFd`, it's impossible to know whether the 74b8a62b91Sopenharmony_ci // user holds a `dup`'d copy which could continue to modify the 75b8a62b91Sopenharmony_ci // file description state, which would cause Undefined Behavior after 76b8a62b91Sopenharmony_ci // our call to `fdopendir`. To prevent this, we obtain an independent 77b8a62b91Sopenharmony_ci // `OwnedFd`. 78b8a62b91Sopenharmony_ci let flags = fcntl_getfl(fd)?; 79b8a62b91Sopenharmony_ci let fd_for_dir = openat(fd, cstr!("."), flags | OFlags::CLOEXEC, Mode::empty())?; 80b8a62b91Sopenharmony_ci 81b8a62b91Sopenharmony_ci let raw = owned_fd(fd_for_dir); 82b8a62b91Sopenharmony_ci unsafe { 83b8a62b91Sopenharmony_ci let libc_dir = c::fdopendir(raw); 84b8a62b91Sopenharmony_ci 85b8a62b91Sopenharmony_ci if let Some(libc_dir) = NonNull::new(libc_dir) { 86b8a62b91Sopenharmony_ci Ok(Self(libc_dir)) 87b8a62b91Sopenharmony_ci } else { 88b8a62b91Sopenharmony_ci let err = io::Errno::last_os_error(); 89b8a62b91Sopenharmony_ci let _ = c::close(raw); 90b8a62b91Sopenharmony_ci Err(err) 91b8a62b91Sopenharmony_ci } 92b8a62b91Sopenharmony_ci } 93b8a62b91Sopenharmony_ci } 94b8a62b91Sopenharmony_ci 95b8a62b91Sopenharmony_ci /// `rewinddir(self)` 96b8a62b91Sopenharmony_ci #[inline] 97b8a62b91Sopenharmony_ci pub fn rewind(&mut self) { 98b8a62b91Sopenharmony_ci unsafe { c::rewinddir(self.0.as_ptr()) } 99b8a62b91Sopenharmony_ci } 100b8a62b91Sopenharmony_ci 101b8a62b91Sopenharmony_ci /// `readdir(self)`, where `None` means the end of the directory. 102b8a62b91Sopenharmony_ci pub fn read(&mut self) -> Option<io::Result<DirEntry>> { 103b8a62b91Sopenharmony_ci set_errno(Errno(0)); 104b8a62b91Sopenharmony_ci let dirent_ptr = unsafe { libc_readdir(self.0.as_ptr()) }; 105b8a62b91Sopenharmony_ci if dirent_ptr.is_null() { 106b8a62b91Sopenharmony_ci let curr_errno = errno().0; 107b8a62b91Sopenharmony_ci if curr_errno == 0 { 108b8a62b91Sopenharmony_ci // We successfully reached the end of the stream. 109b8a62b91Sopenharmony_ci None 110b8a62b91Sopenharmony_ci } else { 111b8a62b91Sopenharmony_ci // `errno` is unknown or non-zero, so an error occurred. 112b8a62b91Sopenharmony_ci Some(Err(io::Errno(curr_errno))) 113b8a62b91Sopenharmony_ci } 114b8a62b91Sopenharmony_ci } else { 115b8a62b91Sopenharmony_ci // We successfully read an entry. 116b8a62b91Sopenharmony_ci unsafe { 117b8a62b91Sopenharmony_ci // We have our own copy of OpenBSD's dirent; check that the 118b8a62b91Sopenharmony_ci // layout minimally matches libc's. 119b8a62b91Sopenharmony_ci #[cfg(target_os = "openbsd")] 120b8a62b91Sopenharmony_ci check_dirent_layout(&*dirent_ptr); 121b8a62b91Sopenharmony_ci 122b8a62b91Sopenharmony_ci let result = DirEntry { 123b8a62b91Sopenharmony_ci dirent: read_dirent(&*dirent_ptr.cast()), 124b8a62b91Sopenharmony_ci 125b8a62b91Sopenharmony_ci #[cfg(target_os = "wasi")] 126b8a62b91Sopenharmony_ci name: CStr::from_ptr((*dirent_ptr).d_name.as_ptr()).to_owned(), 127b8a62b91Sopenharmony_ci }; 128b8a62b91Sopenharmony_ci 129b8a62b91Sopenharmony_ci Some(Ok(result)) 130b8a62b91Sopenharmony_ci } 131b8a62b91Sopenharmony_ci } 132b8a62b91Sopenharmony_ci } 133b8a62b91Sopenharmony_ci 134b8a62b91Sopenharmony_ci /// `fstat(self)` 135b8a62b91Sopenharmony_ci #[inline] 136b8a62b91Sopenharmony_ci pub fn stat(&self) -> io::Result<Stat> { 137b8a62b91Sopenharmony_ci fstat(unsafe { BorrowedFd::borrow_raw(c::dirfd(self.0.as_ptr())) }) 138b8a62b91Sopenharmony_ci } 139b8a62b91Sopenharmony_ci 140b8a62b91Sopenharmony_ci /// `fstatfs(self)` 141b8a62b91Sopenharmony_ci #[cfg(not(any( 142b8a62b91Sopenharmony_ci target_os = "haiku", 143b8a62b91Sopenharmony_ci target_os = "illumos", 144b8a62b91Sopenharmony_ci target_os = "netbsd", 145b8a62b91Sopenharmony_ci target_os = "redox", 146b8a62b91Sopenharmony_ci target_os = "solaris", 147b8a62b91Sopenharmony_ci target_os = "wasi", 148b8a62b91Sopenharmony_ci )))] 149b8a62b91Sopenharmony_ci #[inline] 150b8a62b91Sopenharmony_ci pub fn statfs(&self) -> io::Result<StatFs> { 151b8a62b91Sopenharmony_ci fstatfs(unsafe { BorrowedFd::borrow_raw(c::dirfd(self.0.as_ptr())) }) 152b8a62b91Sopenharmony_ci } 153b8a62b91Sopenharmony_ci 154b8a62b91Sopenharmony_ci /// `fstatvfs(self)` 155b8a62b91Sopenharmony_ci #[cfg(not(any( 156b8a62b91Sopenharmony_ci target_os = "haiku", 157b8a62b91Sopenharmony_ci target_os = "illumos", 158b8a62b91Sopenharmony_ci target_os = "redox", 159b8a62b91Sopenharmony_ci target_os = "solaris", 160b8a62b91Sopenharmony_ci target_os = "wasi", 161b8a62b91Sopenharmony_ci )))] 162b8a62b91Sopenharmony_ci #[inline] 163b8a62b91Sopenharmony_ci pub fn statvfs(&self) -> io::Result<StatVfs> { 164b8a62b91Sopenharmony_ci fstatvfs(unsafe { BorrowedFd::borrow_raw(c::dirfd(self.0.as_ptr())) }) 165b8a62b91Sopenharmony_ci } 166b8a62b91Sopenharmony_ci 167b8a62b91Sopenharmony_ci /// `fchdir(self)` 168b8a62b91Sopenharmony_ci #[cfg(not(any(target_os = "fuchsia", target_os = "wasi")))] 169b8a62b91Sopenharmony_ci #[inline] 170b8a62b91Sopenharmony_ci pub fn chdir(&self) -> io::Result<()> { 171b8a62b91Sopenharmony_ci fchdir(unsafe { BorrowedFd::borrow_raw(c::dirfd(self.0.as_ptr())) }) 172b8a62b91Sopenharmony_ci } 173b8a62b91Sopenharmony_ci} 174b8a62b91Sopenharmony_ci 175b8a62b91Sopenharmony_ci// A `dirent` pointer returned from `readdir` may not point to a full `dirent` 176b8a62b91Sopenharmony_ci// struct, as the name is NUL-terminated and memory may not be allocated for 177b8a62b91Sopenharmony_ci// the full extent of the struct. Copy the fields one at a time. 178b8a62b91Sopenharmony_ciunsafe fn read_dirent(input: &libc_dirent) -> libc_dirent { 179b8a62b91Sopenharmony_ci #[cfg(not(any( 180b8a62b91Sopenharmony_ci target_os = "aix", 181b8a62b91Sopenharmony_ci target_os = "haiku", 182b8a62b91Sopenharmony_ci target_os = "illumos", 183b8a62b91Sopenharmony_ci target_os = "solaris" 184b8a62b91Sopenharmony_ci )))] 185b8a62b91Sopenharmony_ci let d_type = input.d_type; 186b8a62b91Sopenharmony_ci 187b8a62b91Sopenharmony_ci #[cfg(not(any( 188b8a62b91Sopenharmony_ci target_os = "aix", 189b8a62b91Sopenharmony_ci target_os = "dragonfly", 190b8a62b91Sopenharmony_ci target_os = "freebsd", 191b8a62b91Sopenharmony_ci target_os = "haiku", 192b8a62b91Sopenharmony_ci target_os = "ios", 193b8a62b91Sopenharmony_ci target_os = "macos", 194b8a62b91Sopenharmony_ci target_os = "netbsd", 195b8a62b91Sopenharmony_ci target_os = "wasi", 196b8a62b91Sopenharmony_ci )))] 197b8a62b91Sopenharmony_ci let d_off = input.d_off; 198b8a62b91Sopenharmony_ci 199b8a62b91Sopenharmony_ci #[cfg(target_os = "aix")] 200b8a62b91Sopenharmony_ci let d_offset = input.d_offset; 201b8a62b91Sopenharmony_ci 202b8a62b91Sopenharmony_ci #[cfg(not(any( 203b8a62b91Sopenharmony_ci target_os = "dragonfly", 204b8a62b91Sopenharmony_ci target_os = "freebsd", 205b8a62b91Sopenharmony_ci target_os = "netbsd", 206b8a62b91Sopenharmony_ci target_os = "openbsd", 207b8a62b91Sopenharmony_ci )))] 208b8a62b91Sopenharmony_ci let d_ino = input.d_ino; 209b8a62b91Sopenharmony_ci 210b8a62b91Sopenharmony_ci #[cfg(any( 211b8a62b91Sopenharmony_ci target_os = "dragonfly", 212b8a62b91Sopenharmony_ci target_os = "freebsd", 213b8a62b91Sopenharmony_ci target_os = "netbsd", 214b8a62b91Sopenharmony_ci target_os = "openbsd" 215b8a62b91Sopenharmony_ci ))] 216b8a62b91Sopenharmony_ci let d_fileno = input.d_fileno; 217b8a62b91Sopenharmony_ci 218b8a62b91Sopenharmony_ci #[cfg(not(any(target_os = "dragonfly", target_os = "wasi")))] 219b8a62b91Sopenharmony_ci let d_reclen = input.d_reclen; 220b8a62b91Sopenharmony_ci 221b8a62b91Sopenharmony_ci #[cfg(any( 222b8a62b91Sopenharmony_ci target_os = "dragonfly", 223b8a62b91Sopenharmony_ci target_os = "freebsd", 224b8a62b91Sopenharmony_ci target_os = "netbsd", 225b8a62b91Sopenharmony_ci target_os = "openbsd", 226b8a62b91Sopenharmony_ci target_os = "ios", 227b8a62b91Sopenharmony_ci target_os = "macos", 228b8a62b91Sopenharmony_ci ))] 229b8a62b91Sopenharmony_ci let d_namlen = input.d_namlen; 230b8a62b91Sopenharmony_ci 231b8a62b91Sopenharmony_ci #[cfg(any(target_os = "ios", target_os = "macos"))] 232b8a62b91Sopenharmony_ci let d_seekoff = input.d_seekoff; 233b8a62b91Sopenharmony_ci 234b8a62b91Sopenharmony_ci #[cfg(target_os = "haiku")] 235b8a62b91Sopenharmony_ci let d_dev = input.d_dev; 236b8a62b91Sopenharmony_ci #[cfg(target_os = "haiku")] 237b8a62b91Sopenharmony_ci let d_pdev = input.d_pdev; 238b8a62b91Sopenharmony_ci #[cfg(target_os = "haiku")] 239b8a62b91Sopenharmony_ci let d_pino = input.d_pino; 240b8a62b91Sopenharmony_ci 241b8a62b91Sopenharmony_ci // Construct the input. Rust will give us an error if any OS has a input 242b8a62b91Sopenharmony_ci // with a field that we missed here. And we can avoid blindly copying the 243b8a62b91Sopenharmony_ci // whole `d_name` field, which may not be entirely allocated. 244b8a62b91Sopenharmony_ci #[cfg_attr(target_os = "wasi", allow(unused_mut))] 245b8a62b91Sopenharmony_ci #[cfg(not(any(target_os = "freebsd", target_os = "dragonfly")))] 246b8a62b91Sopenharmony_ci let mut dirent = libc_dirent { 247b8a62b91Sopenharmony_ci #[cfg(not(any( 248b8a62b91Sopenharmony_ci target_os = "aix", 249b8a62b91Sopenharmony_ci target_os = "haiku", 250b8a62b91Sopenharmony_ci target_os = "illumos", 251b8a62b91Sopenharmony_ci target_os = "solaris" 252b8a62b91Sopenharmony_ci )))] 253b8a62b91Sopenharmony_ci d_type, 254b8a62b91Sopenharmony_ci #[cfg(not(any( 255b8a62b91Sopenharmony_ci target_os = "aix", 256b8a62b91Sopenharmony_ci target_os = "freebsd", // Until FreeBSD 12 257b8a62b91Sopenharmony_ci target_os = "haiku", 258b8a62b91Sopenharmony_ci target_os = "ios", 259b8a62b91Sopenharmony_ci target_os = "macos", 260b8a62b91Sopenharmony_ci target_os = "netbsd", 261b8a62b91Sopenharmony_ci target_os = "wasi", 262b8a62b91Sopenharmony_ci )))] 263b8a62b91Sopenharmony_ci d_off, 264b8a62b91Sopenharmony_ci #[cfg(target_os = "aix")] 265b8a62b91Sopenharmony_ci d_offset, 266b8a62b91Sopenharmony_ci #[cfg(not(any(target_os = "freebsd", target_os = "netbsd", target_os = "openbsd")))] 267b8a62b91Sopenharmony_ci d_ino, 268b8a62b91Sopenharmony_ci #[cfg(any(target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))] 269b8a62b91Sopenharmony_ci d_fileno, 270b8a62b91Sopenharmony_ci #[cfg(not(target_os = "wasi"))] 271b8a62b91Sopenharmony_ci d_reclen, 272b8a62b91Sopenharmony_ci #[cfg(any( 273b8a62b91Sopenharmony_ci target_os = "aix", 274b8a62b91Sopenharmony_ci target_os = "freebsd", 275b8a62b91Sopenharmony_ci target_os = "ios", 276b8a62b91Sopenharmony_ci target_os = "macos", 277b8a62b91Sopenharmony_ci target_os = "netbsd", 278b8a62b91Sopenharmony_ci target_os = "openbsd", 279b8a62b91Sopenharmony_ci ))] 280b8a62b91Sopenharmony_ci d_namlen, 281b8a62b91Sopenharmony_ci #[cfg(any(target_os = "ios", target_os = "macos"))] 282b8a62b91Sopenharmony_ci d_seekoff, 283b8a62b91Sopenharmony_ci // The `d_name` field is NUL-terminated, and we need to be careful not 284b8a62b91Sopenharmony_ci // to read bytes past the NUL, even though they're within the nominal 285b8a62b91Sopenharmony_ci // extent of the `struct dirent`, because they may not be allocated. So 286b8a62b91Sopenharmony_ci // don't read it from `dirent_ptr`. 287b8a62b91Sopenharmony_ci // 288b8a62b91Sopenharmony_ci // In theory this could use `MaybeUninit::uninit().assume_init()`, but 289b8a62b91Sopenharmony_ci // that [invokes undefined behavior]. 290b8a62b91Sopenharmony_ci // 291b8a62b91Sopenharmony_ci // [invokes undefined behavior]: https://doc.rust-lang.org/stable/core/mem/union.MaybeUninit.html#initialization-invariant 292b8a62b91Sopenharmony_ci d_name: zeroed(), 293b8a62b91Sopenharmony_ci #[cfg(target_os = "openbsd")] 294b8a62b91Sopenharmony_ci __d_padding: zeroed(), 295b8a62b91Sopenharmony_ci #[cfg(target_os = "haiku")] 296b8a62b91Sopenharmony_ci d_dev, 297b8a62b91Sopenharmony_ci #[cfg(target_os = "haiku")] 298b8a62b91Sopenharmony_ci d_pdev, 299b8a62b91Sopenharmony_ci #[cfg(target_os = "haiku")] 300b8a62b91Sopenharmony_ci d_pino, 301b8a62b91Sopenharmony_ci }; 302b8a62b91Sopenharmony_ci /* 303b8a62b91Sopenharmony_ci pub d_ino: ino_t, 304b8a62b91Sopenharmony_ci pub d_pino: i64, 305b8a62b91Sopenharmony_ci pub d_reclen: ::c_ushort, 306b8a62b91Sopenharmony_ci pub d_name: [::c_char; 1024], // Max length is _POSIX_PATH_MAX 307b8a62b91Sopenharmony_ci // */ 308b8a62b91Sopenharmony_ci 309b8a62b91Sopenharmony_ci // On dragonfly and FreeBSD 12, `dirent` has some non-public padding fields 310b8a62b91Sopenharmony_ci // so we can't directly initialize it. 311b8a62b91Sopenharmony_ci #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] 312b8a62b91Sopenharmony_ci let mut dirent = { 313b8a62b91Sopenharmony_ci let mut dirent: libc_dirent = zeroed(); 314b8a62b91Sopenharmony_ci dirent.d_fileno = d_fileno; 315b8a62b91Sopenharmony_ci dirent.d_namlen = d_namlen; 316b8a62b91Sopenharmony_ci dirent.d_type = d_type; 317b8a62b91Sopenharmony_ci #[cfg(target_os = "freebsd")] 318b8a62b91Sopenharmony_ci { 319b8a62b91Sopenharmony_ci dirent.d_reclen = d_reclen; 320b8a62b91Sopenharmony_ci } 321b8a62b91Sopenharmony_ci dirent 322b8a62b91Sopenharmony_ci }; 323b8a62b91Sopenharmony_ci 324b8a62b91Sopenharmony_ci // Copy from d_name, reading up to and including the first NUL. 325b8a62b91Sopenharmony_ci #[cfg(not(target_os = "wasi"))] 326b8a62b91Sopenharmony_ci { 327b8a62b91Sopenharmony_ci let name_len = CStr::from_ptr(input.d_name.as_ptr()) 328b8a62b91Sopenharmony_ci .to_bytes_with_nul() 329b8a62b91Sopenharmony_ci .len(); 330b8a62b91Sopenharmony_ci dirent.d_name[..name_len].copy_from_slice(&input.d_name[..name_len]); 331b8a62b91Sopenharmony_ci } 332b8a62b91Sopenharmony_ci 333b8a62b91Sopenharmony_ci dirent 334b8a62b91Sopenharmony_ci} 335b8a62b91Sopenharmony_ci 336b8a62b91Sopenharmony_ci/// `Dir` implements `Send` but not `Sync`, because we use `readdir` which is 337b8a62b91Sopenharmony_ci/// not guaranteed to be thread-safe. Users can wrap this in a `Mutex` if they 338b8a62b91Sopenharmony_ci/// need `Sync`, which is effectively what'd need to do to implement `Sync` 339b8a62b91Sopenharmony_ci/// ourselves. 340b8a62b91Sopenharmony_ciunsafe impl Send for Dir {} 341b8a62b91Sopenharmony_ci 342b8a62b91Sopenharmony_ciimpl Drop for Dir { 343b8a62b91Sopenharmony_ci #[inline] 344b8a62b91Sopenharmony_ci fn drop(&mut self) { 345b8a62b91Sopenharmony_ci unsafe { c::closedir(self.0.as_ptr()) }; 346b8a62b91Sopenharmony_ci } 347b8a62b91Sopenharmony_ci} 348b8a62b91Sopenharmony_ci 349b8a62b91Sopenharmony_ciimpl Iterator for Dir { 350b8a62b91Sopenharmony_ci type Item = io::Result<DirEntry>; 351b8a62b91Sopenharmony_ci 352b8a62b91Sopenharmony_ci #[inline] 353b8a62b91Sopenharmony_ci fn next(&mut self) -> Option<Self::Item> { 354b8a62b91Sopenharmony_ci Self::read(self) 355b8a62b91Sopenharmony_ci } 356b8a62b91Sopenharmony_ci} 357b8a62b91Sopenharmony_ci 358b8a62b91Sopenharmony_ciimpl fmt::Debug for Dir { 359b8a62b91Sopenharmony_ci fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 360b8a62b91Sopenharmony_ci f.debug_struct("Dir") 361b8a62b91Sopenharmony_ci .field("fd", unsafe { &c::dirfd(self.0.as_ptr()) }) 362b8a62b91Sopenharmony_ci .finish() 363b8a62b91Sopenharmony_ci } 364b8a62b91Sopenharmony_ci} 365b8a62b91Sopenharmony_ci 366b8a62b91Sopenharmony_ci/// `struct dirent` 367b8a62b91Sopenharmony_ci#[derive(Debug)] 368b8a62b91Sopenharmony_cipub struct DirEntry { 369b8a62b91Sopenharmony_ci dirent: libc_dirent, 370b8a62b91Sopenharmony_ci 371b8a62b91Sopenharmony_ci #[cfg(target_os = "wasi")] 372b8a62b91Sopenharmony_ci name: CString, 373b8a62b91Sopenharmony_ci} 374b8a62b91Sopenharmony_ci 375b8a62b91Sopenharmony_ciimpl DirEntry { 376b8a62b91Sopenharmony_ci /// Returns the file name of this directory entry. 377b8a62b91Sopenharmony_ci #[inline] 378b8a62b91Sopenharmony_ci pub fn file_name(&self) -> &CStr { 379b8a62b91Sopenharmony_ci #[cfg(not(target_os = "wasi"))] 380b8a62b91Sopenharmony_ci unsafe { 381b8a62b91Sopenharmony_ci CStr::from_ptr(self.dirent.d_name.as_ptr()) 382b8a62b91Sopenharmony_ci } 383b8a62b91Sopenharmony_ci 384b8a62b91Sopenharmony_ci #[cfg(target_os = "wasi")] 385b8a62b91Sopenharmony_ci &self.name 386b8a62b91Sopenharmony_ci } 387b8a62b91Sopenharmony_ci 388b8a62b91Sopenharmony_ci /// Returns the type of this directory entry. 389b8a62b91Sopenharmony_ci #[cfg(not(any( 390b8a62b91Sopenharmony_ci target_os = "aix", 391b8a62b91Sopenharmony_ci target_os = "haiku", 392b8a62b91Sopenharmony_ci target_os = "illumos", 393b8a62b91Sopenharmony_ci target_os = "solaris" 394b8a62b91Sopenharmony_ci )))] 395b8a62b91Sopenharmony_ci #[inline] 396b8a62b91Sopenharmony_ci pub fn file_type(&self) -> FileType { 397b8a62b91Sopenharmony_ci FileType::from_dirent_d_type(self.dirent.d_type) 398b8a62b91Sopenharmony_ci } 399b8a62b91Sopenharmony_ci 400b8a62b91Sopenharmony_ci /// Return the inode number of this directory entry. 401b8a62b91Sopenharmony_ci #[cfg(not(any( 402b8a62b91Sopenharmony_ci target_os = "dragonfly", 403b8a62b91Sopenharmony_ci target_os = "freebsd", 404b8a62b91Sopenharmony_ci target_os = "netbsd", 405b8a62b91Sopenharmony_ci target_os = "openbsd", 406b8a62b91Sopenharmony_ci )))] 407b8a62b91Sopenharmony_ci #[inline] 408b8a62b91Sopenharmony_ci pub fn ino(&self) -> u64 { 409b8a62b91Sopenharmony_ci self.dirent.d_ino as u64 410b8a62b91Sopenharmony_ci } 411b8a62b91Sopenharmony_ci 412b8a62b91Sopenharmony_ci /// Return the inode number of this directory entry. 413b8a62b91Sopenharmony_ci #[cfg(any( 414b8a62b91Sopenharmony_ci target_os = "dragonfly", 415b8a62b91Sopenharmony_ci target_os = "freebsd", 416b8a62b91Sopenharmony_ci target_os = "netbsd", 417b8a62b91Sopenharmony_ci target_os = "openbsd", 418b8a62b91Sopenharmony_ci ))] 419b8a62b91Sopenharmony_ci #[inline] 420b8a62b91Sopenharmony_ci pub fn ino(&self) -> u64 { 421b8a62b91Sopenharmony_ci #[allow(clippy::useless_conversion)] 422b8a62b91Sopenharmony_ci self.dirent.d_fileno.into() 423b8a62b91Sopenharmony_ci } 424b8a62b91Sopenharmony_ci} 425b8a62b91Sopenharmony_ci 426b8a62b91Sopenharmony_ci/// libc's OpenBSD `dirent` has a private field so we can't construct it 427b8a62b91Sopenharmony_ci/// directly, so we declare it ourselves to make all fields accessible. 428b8a62b91Sopenharmony_ci#[cfg(target_os = "openbsd")] 429b8a62b91Sopenharmony_ci#[repr(C)] 430b8a62b91Sopenharmony_ci#[derive(Debug)] 431b8a62b91Sopenharmony_cistruct libc_dirent { 432b8a62b91Sopenharmony_ci d_fileno: c::ino_t, 433b8a62b91Sopenharmony_ci d_off: c::off_t, 434b8a62b91Sopenharmony_ci d_reclen: u16, 435b8a62b91Sopenharmony_ci d_type: u8, 436b8a62b91Sopenharmony_ci d_namlen: u8, 437b8a62b91Sopenharmony_ci __d_padding: [u8; 4], 438b8a62b91Sopenharmony_ci d_name: [c::c_char; 256], 439b8a62b91Sopenharmony_ci} 440b8a62b91Sopenharmony_ci 441b8a62b91Sopenharmony_ci/// We have our own copy of OpenBSD's dirent; check that the layout 442b8a62b91Sopenharmony_ci/// minimally matches libc's. 443b8a62b91Sopenharmony_ci#[cfg(target_os = "openbsd")] 444b8a62b91Sopenharmony_cifn check_dirent_layout(dirent: &c::dirent) { 445b8a62b91Sopenharmony_ci use crate::utils::as_ptr; 446b8a62b91Sopenharmony_ci use core::mem::{align_of, size_of}; 447b8a62b91Sopenharmony_ci 448b8a62b91Sopenharmony_ci // Check that the basic layouts match. 449b8a62b91Sopenharmony_ci assert_eq!(size_of::<libc_dirent>(), size_of::<c::dirent>()); 450b8a62b91Sopenharmony_ci assert_eq!(align_of::<libc_dirent>(), align_of::<c::dirent>()); 451b8a62b91Sopenharmony_ci 452b8a62b91Sopenharmony_ci // Check that the field offsets match. 453b8a62b91Sopenharmony_ci assert_eq!( 454b8a62b91Sopenharmony_ci { 455b8a62b91Sopenharmony_ci let z = libc_dirent { 456b8a62b91Sopenharmony_ci d_fileno: 0_u64, 457b8a62b91Sopenharmony_ci d_off: 0_i64, 458b8a62b91Sopenharmony_ci d_reclen: 0_u16, 459b8a62b91Sopenharmony_ci d_type: 0_u8, 460b8a62b91Sopenharmony_ci d_namlen: 0_u8, 461b8a62b91Sopenharmony_ci __d_padding: [0_u8; 4], 462b8a62b91Sopenharmony_ci d_name: [0 as c::c_char; 256], 463b8a62b91Sopenharmony_ci }; 464b8a62b91Sopenharmony_ci let base = as_ptr(&z) as usize; 465b8a62b91Sopenharmony_ci ( 466b8a62b91Sopenharmony_ci (as_ptr(&z.d_fileno) as usize) - base, 467b8a62b91Sopenharmony_ci (as_ptr(&z.d_off) as usize) - base, 468b8a62b91Sopenharmony_ci (as_ptr(&z.d_reclen) as usize) - base, 469b8a62b91Sopenharmony_ci (as_ptr(&z.d_type) as usize) - base, 470b8a62b91Sopenharmony_ci (as_ptr(&z.d_namlen) as usize) - base, 471b8a62b91Sopenharmony_ci (as_ptr(&z.d_name) as usize) - base, 472b8a62b91Sopenharmony_ci ) 473b8a62b91Sopenharmony_ci }, 474b8a62b91Sopenharmony_ci { 475b8a62b91Sopenharmony_ci let z = dirent; 476b8a62b91Sopenharmony_ci let base = as_ptr(z) as usize; 477b8a62b91Sopenharmony_ci ( 478b8a62b91Sopenharmony_ci (as_ptr(&z.d_fileno) as usize) - base, 479b8a62b91Sopenharmony_ci (as_ptr(&z.d_off) as usize) - base, 480b8a62b91Sopenharmony_ci (as_ptr(&z.d_reclen) as usize) - base, 481b8a62b91Sopenharmony_ci (as_ptr(&z.d_type) as usize) - base, 482b8a62b91Sopenharmony_ci (as_ptr(&z.d_namlen) as usize) - base, 483b8a62b91Sopenharmony_ci (as_ptr(&z.d_name) as usize) - base, 484b8a62b91Sopenharmony_ci ) 485b8a62b91Sopenharmony_ci } 486b8a62b91Sopenharmony_ci ); 487b8a62b91Sopenharmony_ci} 488