summaryrefslogtreecommitdiff
path: root/vendor/rustix/src/fs/raw_dir.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/rustix/src/fs/raw_dir.rs')
-rw-r--r--vendor/rustix/src/fs/raw_dir.rs237
1 files changed, 237 insertions, 0 deletions
diff --git a/vendor/rustix/src/fs/raw_dir.rs b/vendor/rustix/src/fs/raw_dir.rs
new file mode 100644
index 0000000..93686b1
--- /dev/null
+++ b/vendor/rustix/src/fs/raw_dir.rs
@@ -0,0 +1,237 @@
+//! `RawDir` and `RawDirEntry`.
+
+use core::fmt;
+use core::mem::{align_of, MaybeUninit};
+use linux_raw_sys::general::linux_dirent64;
+
+use crate::backend::fs::syscalls::getdents_uninit;
+use crate::fd::AsFd;
+use crate::ffi::CStr;
+use crate::fs::FileType;
+use crate::io;
+
+/// A directory iterator implemented with getdents.
+///
+/// Note: This implementation does not handle growing the buffer. If this
+/// functionality is necessary, you'll need to drop the current iterator,
+/// resize the buffer, and then re-create the iterator. The iterator is
+/// guaranteed to continue where it left off provided the file descriptor isn't
+/// changed. See the example in [`RawDir::new`].
+pub struct RawDir<'buf, Fd: AsFd> {
+ fd: Fd,
+ buf: &'buf mut [MaybeUninit<u8>],
+ initialized: usize,
+ offset: usize,
+}
+
+impl<'buf, Fd: AsFd> RawDir<'buf, Fd> {
+ /// Create a new iterator from the given file descriptor and buffer.
+ ///
+ /// Note: the buffer size may be trimmed to accommodate alignment
+ /// requirements.
+ ///
+ /// # Examples
+ ///
+ /// ## Simple but non-portable
+ ///
+ /// These examples are non-portable, because file systems may not have a
+ /// maximum file name length. If you can make assumptions that bound
+ /// this length, then these examples may suffice.
+ ///
+ /// Using the heap:
+ ///
+ /// ```
+ /// # use std::mem::MaybeUninit;
+ /// # use rustix::fs::{CWD, Mode, OFlags, openat, RawDir};
+ /// # use rustix::cstr;
+ ///
+ /// let fd = openat(
+ /// CWD,
+ /// cstr!("."),
+ /// OFlags::RDONLY | OFlags::DIRECTORY | OFlags::CLOEXEC,
+ /// Mode::empty(),
+ /// )
+ /// .unwrap();
+ ///
+ /// let mut buf = Vec::with_capacity(8192);
+ /// let mut iter = RawDir::new(fd, buf.spare_capacity_mut());
+ /// while let Some(entry) = iter.next() {
+ /// let entry = entry.unwrap();
+ /// dbg!(&entry);
+ /// }
+ /// ```
+ ///
+ /// Using the stack:
+ ///
+ /// ```
+ /// # use std::mem::MaybeUninit;
+ /// # use rustix::fs::{CWD, Mode, OFlags, openat, RawDir};
+ /// # use rustix::cstr;
+ ///
+ /// let fd = openat(
+ /// CWD,
+ /// cstr!("."),
+ /// OFlags::RDONLY | OFlags::DIRECTORY | OFlags::CLOEXEC,
+ /// Mode::empty(),
+ /// )
+ /// .unwrap();
+ ///
+ /// let mut buf = [MaybeUninit::uninit(); 2048];
+ /// let mut iter = RawDir::new(fd, &mut buf);
+ /// while let Some(entry) = iter.next() {
+ /// let entry = entry.unwrap();
+ /// dbg!(&entry);
+ /// }
+ /// ```
+ ///
+ /// ## Portable
+ ///
+ /// Heap allocated growing buffer for supporting directory entries with
+ /// arbitrarily large file names:
+ ///
+ /// ```notrust
+ /// # // The `notrust` above can be removed when we can depend on Rust 1.65.
+ /// # use std::mem::MaybeUninit;
+ /// # use rustix::fs::{CWD, Mode, OFlags, openat, RawDir};
+ /// # use rustix::io::Errno;
+ /// # use rustix::cstr;
+ ///
+ /// let fd = openat(
+ /// CWD,
+ /// cstr!("."),
+ /// OFlags::RDONLY | OFlags::DIRECTORY | OFlags::CLOEXEC,
+ /// Mode::empty(),
+ /// )
+ /// .unwrap();
+ ///
+ /// let mut buf = Vec::with_capacity(8192);
+ /// 'read: loop {
+ /// 'resize: {
+ /// let mut iter = RawDir::new(&fd, buf.spare_capacity_mut());
+ /// while let Some(entry) = iter.next() {
+ /// let entry = match entry {
+ /// Err(Errno::INVAL) => break 'resize,
+ /// r => r.unwrap(),
+ /// };
+ /// dbg!(&entry);
+ /// }
+ /// break 'read;
+ /// }
+ ///
+ /// let new_capacity = buf.capacity() * 2;
+ /// buf.reserve(new_capacity);
+ /// }
+ /// ```
+ pub fn new(fd: Fd, buf: &'buf mut [MaybeUninit<u8>]) -> Self {
+ Self {
+ fd,
+ buf: {
+ let offset = buf.as_ptr().align_offset(align_of::<linux_dirent64>());
+ if offset < buf.len() {
+ &mut buf[offset..]
+ } else {
+ &mut []
+ }
+ },
+ initialized: 0,
+ offset: 0,
+ }
+ }
+}
+
+/// A raw directory entry, similar to [`std::fs::DirEntry`].
+///
+/// Unlike the std version, this may represent the `.` or `..` entries.
+pub struct RawDirEntry<'a> {
+ file_name: &'a CStr,
+ file_type: u8,
+ inode_number: u64,
+ next_entry_cookie: i64,
+}
+
+impl<'a> fmt::Debug for RawDirEntry<'a> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let mut f = f.debug_struct("RawDirEntry");
+ f.field("file_name", &self.file_name());
+ f.field("file_type", &self.file_type());
+ f.field("ino", &self.ino());
+ f.field("next_entry_cookie", &self.next_entry_cookie());
+ f.finish()
+ }
+}
+
+impl<'a> RawDirEntry<'a> {
+ /// Returns the file name of this directory entry.
+ #[inline]
+ pub fn file_name(&self) -> &CStr {
+ self.file_name
+ }
+
+ /// Returns the type of this directory entry.
+ #[inline]
+ pub fn file_type(&self) -> FileType {
+ FileType::from_dirent_d_type(self.file_type)
+ }
+
+ /// Returns the inode number of this directory entry.
+ #[inline]
+ #[doc(alias = "inode_number")]
+ pub fn ino(&self) -> u64 {
+ self.inode_number
+ }
+
+ /// Returns the seek cookie to the next directory entry.
+ #[inline]
+ #[doc(alias = "off")]
+ pub fn next_entry_cookie(&self) -> u64 {
+ self.next_entry_cookie as u64
+ }
+}
+
+impl<'buf, Fd: AsFd> RawDir<'buf, Fd> {
+ /// Identical to [`Iterator::next`] except that [`Iterator::Item`] borrows
+ /// from self.
+ ///
+ /// Note: this interface will be broken to implement a stdlib iterator API
+ /// with GAT support once one becomes available.
+ #[allow(unsafe_code)]
+ #[allow(clippy::should_implement_trait)]
+ pub fn next(&mut self) -> Option<io::Result<RawDirEntry<'_>>> {
+ if self.is_buffer_empty() {
+ match getdents_uninit(self.fd.as_fd(), self.buf) {
+ Ok(0) => return None,
+ Ok(bytes_read) => {
+ self.initialized = bytes_read;
+ self.offset = 0;
+ }
+ Err(e) => return Some(Err(e)),
+ }
+ }
+
+ let dirent_ptr = self.buf[self.offset..].as_ptr();
+ // SAFETY:
+ // - This data is initialized by the check above.
+ // - Assumption: the kernel will not give us partial structs.
+ // - Assumption: the kernel uses proper alignment between structs.
+ // - The starting pointer is aligned (performed in RawDir::new)
+ let dirent = unsafe { &*dirent_ptr.cast::<linux_dirent64>() };
+
+ self.offset += usize::from(dirent.d_reclen);
+
+ Some(Ok(RawDirEntry {
+ file_type: dirent.d_type,
+ inode_number: dirent.d_ino.into(),
+ next_entry_cookie: dirent.d_off.into(),
+ // SAFETY: The kernel guarantees a NUL-terminated string.
+ file_name: unsafe { CStr::from_ptr(dirent.d_name.as_ptr().cast()) },
+ }))
+ }
+
+ /// Returns true if the internal buffer is empty and will be refilled when
+ /// calling [`next`].
+ ///
+ /// [`next`]: Self::next
+ pub fn is_buffer_empty(&self) -> bool {
+ self.offset >= self.initialized
+ }
+}