nix/dir.rs
1//! List directory contents
2
3use crate::errno::Errno;
4use crate::fcntl::{self, OFlag};
5use crate::sys;
6use crate::{NixPath, Result};
7use cfg_if::cfg_if;
8use std::ffi::{CStr, CString};
9use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
10use std::ptr;
11
12/// An open directory.
13///
14/// This is a lower-level interface than [`std::fs::ReadDir`]. Notable differences:
15/// * can be opened from a file descriptor (as returned by [`openat`][openat],
16/// perhaps before knowing if the path represents a file or directory).
17/// * implements [`AsFd`][AsFd], so it can be passed to [`fstat`][fstat],
18/// [`openat`][openat], etc. The file descriptor continues to be owned by the
19/// `Dir`, so callers must not keep a `RawFd` after the `Dir` is dropped.
20/// * can be iterated through multiple times without closing and reopening the file
21/// descriptor. Each iteration rewinds when finished.
22/// * returns entries for `.` (current directory) and `..` (parent directory).
23/// * returns entries' names as a [`CStr`] (no allocation or conversion beyond whatever libc
24/// does).
25///
26/// [AsFd]: std::os::fd::AsFd
27/// [fstat]: crate::sys::stat::fstat
28/// [openat]: crate::fcntl::openat
29/// [cstr]: std::ffi::CStr
30///
31/// # Examples
32///
33/// Traverse the current directory, and print entries' names:
34///
35/// ```
36/// use nix::dir::Dir;
37/// use nix::fcntl::OFlag;
38/// use nix::sys::stat::Mode;
39///
40/// let mut cwd = Dir::open(".", OFlag::O_RDONLY | OFlag::O_CLOEXEC, Mode::empty()).unwrap();
41/// for res_entry in cwd.iter() {
42/// let entry = res_entry.unwrap();
43/// println!("File name: {}", entry.file_name().to_string_lossy());
44/// }
45/// ```
46#[derive(Debug, Eq, Hash, PartialEq)]
47pub struct Dir(ptr::NonNull<libc::DIR>);
48
49impl Dir {
50 /// Opens the given path as with `fcntl::open`.
51 pub fn open<P: ?Sized + NixPath>(
52 path: &P,
53 oflag: OFlag,
54 mode: sys::stat::Mode,
55 ) -> Result<Self> {
56 let fd = fcntl::open(path, oflag, mode)?;
57 Dir::from_fd(fd)
58 }
59
60 /// Opens the given path as with `fcntl::openat`.
61 pub fn openat<Fd: std::os::fd::AsFd, P: ?Sized + NixPath>(
62 dirfd: Fd,
63 path: &P,
64 oflag: OFlag,
65 mode: sys::stat::Mode,
66 ) -> Result<Self> {
67 let fd = fcntl::openat(dirfd, path, oflag, mode)?;
68 Dir::from_fd(fd)
69 }
70
71 /// Converts from a descriptor-based object, closing the descriptor on success or failure.
72 ///
73 /// # Safety
74 ///
75 /// It is only safe if `fd` is an owned file descriptor.
76 #[inline]
77 #[deprecated(
78 since = "0.30.0",
79 note = "Deprecate this since it is not I/O-safe, use from_fd instead."
80 )]
81 pub unsafe fn from<F: IntoRawFd>(fd: F) -> Result<Self> {
82 use std::os::fd::FromRawFd;
83 use std::os::fd::OwnedFd;
84
85 // SAFETY:
86 //
87 // This is indeed unsafe is `fd` it not an owned fd.
88 let owned_fd = unsafe { OwnedFd::from_raw_fd(fd.into_raw_fd()) };
89 Dir::from_fd(owned_fd)
90 }
91
92 /// Converts from a file descriptor, closing it on failure.
93 ///
94 /// # Examples
95 ///
96 /// `ENOTDIR` would be returned if `fd` does not refer to a directory:
97 ///
98 /// ```should_panic
99 /// use std::os::fd::OwnedFd;
100 /// use nix::dir::Dir;
101 ///
102 /// let temp_file = tempfile::tempfile().unwrap();
103 /// let temp_file_fd: OwnedFd = temp_file.into();
104 /// let never = Dir::from_fd(temp_file_fd).unwrap();
105 /// ```
106 #[doc(alias("fdopendir"))]
107 pub fn from_fd(fd: std::os::fd::OwnedFd) -> Result<Self> {
108 // take the ownership as the constructed `Dir` is now the owner
109 let raw_fd = fd.into_raw_fd();
110 let d = ptr::NonNull::new(unsafe { libc::fdopendir(raw_fd) })
111 .ok_or(Errno::last())?;
112 Ok(Dir(d))
113 }
114
115 /// Returns an iterator of `Result<Entry>` which rewinds when finished.
116 pub fn iter(&mut self) -> Iter<'_> {
117 Iter(self)
118 }
119}
120
121// `Dir` is not `Sync` because it's unsafe to call `readdir` simultaneously from multiple threads.
122//
123// `Dir` is safe to pass from one thread to another, as it's not reference-counted.
124unsafe impl Send for Dir {}
125
126impl std::os::fd::AsFd for Dir {
127 fn as_fd(&self) -> std::os::fd::BorrowedFd<'_> {
128 let raw_fd = self.as_raw_fd();
129
130 // SAFETY:
131 //
132 // `raw_fd` should be open and valid for the lifetime of the returned
133 // `BorrowedFd` as it is extracted from `&self`.
134 unsafe { std::os::fd::BorrowedFd::borrow_raw(raw_fd) }
135 }
136}
137
138impl AsRawFd for Dir {
139 fn as_raw_fd(&self) -> RawFd {
140 unsafe { libc::dirfd(self.0.as_ptr()) }
141 }
142}
143
144impl Drop for Dir {
145 fn drop(&mut self) {
146 let e = Errno::result(unsafe { libc::closedir(self.0.as_ptr()) });
147 if !std::thread::panicking() && e == Err(Errno::EBADF) {
148 panic!("Closing an invalid file descriptor!");
149 };
150 }
151}
152
153// The pass by mut is technically needless only because the inner NonNull is
154// Copy. But we are actually mutating the Dir, so we pass by mut.
155#[allow(clippy::needless_pass_by_ref_mut)]
156fn readdir(dir: &mut Dir) -> Option<Result<Entry>> {
157 Errno::clear();
158 unsafe {
159 let de = libc::readdir(dir.0.as_ptr());
160 if de.is_null() {
161 if Errno::last_raw() == 0 {
162 // EOF
163 None
164 } else {
165 Some(Err(Errno::last()))
166 }
167 } else {
168 Some(Ok(Entry::from_raw(&*de)))
169 }
170 }
171}
172
173/// Return type of [`Dir::iter`].
174#[derive(Debug, Eq, Hash, PartialEq)]
175pub struct Iter<'d>(&'d mut Dir);
176
177impl Iterator for Iter<'_> {
178 type Item = Result<Entry>;
179
180 fn next(&mut self) -> Option<Self::Item> {
181 readdir(self.0)
182 }
183}
184
185impl Drop for Iter<'_> {
186 fn drop(&mut self) {
187 unsafe { libc::rewinddir((self.0).0.as_ptr()) }
188 }
189}
190
191/// The return type of [Dir::into_iter]
192#[derive(Debug, Eq, Hash, PartialEq)]
193pub struct OwningIter(Dir);
194
195impl Iterator for OwningIter {
196 type Item = Result<Entry>;
197
198 fn next(&mut self) -> Option<Self::Item> {
199 readdir(&mut self.0)
200 }
201}
202
203/// The file descriptor continues to be owned by the `OwningIter`,
204/// so callers must not keep a `RawFd` after the `OwningIter` is dropped.
205impl AsRawFd for OwningIter {
206 fn as_raw_fd(&self) -> RawFd {
207 self.0.as_raw_fd()
208 }
209}
210
211impl IntoIterator for Dir {
212 type Item = Result<Entry>;
213 type IntoIter = OwningIter;
214
215 /// Creates a owning iterator, that is, one that takes ownership of the
216 /// `Dir`. The `Dir` cannot be used after calling this. This can be useful
217 /// when you have a function that both creates a `Dir` instance and returns
218 /// an `Iterator`.
219 ///
220 /// Example:
221 ///
222 /// ```
223 /// use nix::{dir::Dir, fcntl::OFlag, sys::stat::Mode};
224 /// use std::{iter::Iterator, string::String};
225 ///
226 /// fn ls_upper(dirname: &str) -> impl Iterator<Item=String> {
227 /// let d = Dir::open(dirname, OFlag::O_DIRECTORY, Mode::S_IXUSR).unwrap();
228 /// d.into_iter().map(|x| x.unwrap().file_name().as_ref().to_string_lossy().to_ascii_uppercase())
229 /// }
230 /// ```
231 fn into_iter(self) -> Self::IntoIter {
232 OwningIter(self)
233 }
234}
235
236/// A directory entry, similar to `std::fs::DirEntry`.
237///
238/// Note that unlike the std version, this may represent the `.` or `..` entries.
239#[derive(Clone, Debug, Eq, Hash, PartialEq)]
240pub struct Entry {
241 ino: u64,
242 type_: Option<Type>,
243 // On some platforms libc::dirent is a "flexible-length structure", where there may be
244 // data beyond the end of the struct definition. So it isn't possible to copy it and subsequently
245 // dereference the copy's d_name field. Nor is it possible for Entry to wrap a &libc::dirent.
246 // At least, not if it is to work with the Iterator trait. Because the Entry would need to
247 // maintain a mutable reference to the Dir, which would conflict with the iterator's mutable
248 // reference to the same Dir. So we're forced to copy the name here.
249 name: CString,
250}
251
252/// Type of file referenced by a directory entry
253#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
254pub enum Type {
255 /// FIFO (Named pipe)
256 Fifo,
257 /// Character device
258 CharacterDevice,
259 /// Directory
260 Directory,
261 /// Block device
262 BlockDevice,
263 /// Regular file
264 File,
265 /// Symbolic link
266 Symlink,
267 /// Unix-domain socket
268 Socket,
269}
270
271impl Entry {
272 /// Returns the inode number (`d_ino`) of the underlying `dirent`.
273 pub fn ino(&self) -> u64 {
274 self.ino
275 }
276
277 /// Returns the bare file name of this directory entry without any other leading path component.
278 pub fn file_name(&self) -> &CStr {
279 self.name.as_c_str()
280 }
281
282 /// Returns the type of this directory entry, if known.
283 ///
284 /// See platform `readdir(3)` or `dirent(5)` manpage for when the file type is known;
285 /// notably, some Linux filesystems don't implement this. The caller should use `stat` or
286 /// `fstat` if this returns `None`.
287 pub fn file_type(&self) -> Option<Type> {
288 self.type_
289 }
290
291 #[allow(clippy::useless_conversion)] // Not useless on all OSes
292 // The cast is not unnecessary on all platforms.
293 #[allow(clippy::unnecessary_cast)]
294 fn from_raw(de: &libc::dirent) -> Self {
295 cfg_if! {
296 if #[cfg(any(target_os = "aix",
297 target_os = "emscripten",
298 target_os = "fuchsia",
299 target_os = "haiku",
300 target_os = "hurd",
301 target_os = "cygwin",
302 solarish,
303 linux_android,
304 apple_targets))] {
305 let ino = de.d_ino as u64;
306 } else {
307 let ino = u64::from(de.d_fileno);
308 }
309 }
310 cfg_if! {
311 if #[cfg(not(any(solarish, target_os = "aix", target_os = "haiku")))] {
312 let type_ = match de.d_type {
313 libc::DT_FIFO => Some(Type::Fifo),
314 libc::DT_CHR => Some(Type::CharacterDevice),
315 libc::DT_DIR => Some(Type::Directory),
316 libc::DT_BLK => Some(Type::BlockDevice),
317 libc::DT_REG => Some(Type::File),
318 libc::DT_LNK => Some(Type::Symlink),
319 libc::DT_SOCK => Some(Type::Socket),
320 /* libc::DT_UNKNOWN | */ _ => None,
321 };
322 } else {
323 // illumos, Solaris, and Haiku systems do not have the d_type member at all:
324 #[cfg(any(solarish, target_os = "aix", target_os = "haiku"))]
325 let type_ = None;
326 }
327 }
328 // Annoyingly, since libc::dirent is open-ended, the easiest way to read the name field in
329 // Rust is to treat it like a pointer.
330 // Safety: safe because we knod that de.d_name is in valid memory.
331 let name = unsafe { CStr::from_ptr(de.d_name.as_ptr()) }.to_owned();
332
333 Entry { ino, type_, name }
334 }
335}