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