aboutsummaryrefslogtreecommitdiff
path: root/vendor/rustix/src/backend/linux_raw/fs/dir.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/rustix/src/backend/linux_raw/fs/dir.rs')
-rw-r--r--vendor/rustix/src/backend/linux_raw/fs/dir.rs315
1 files changed, 0 insertions, 315 deletions
diff --git a/vendor/rustix/src/backend/linux_raw/fs/dir.rs b/vendor/rustix/src/backend/linux_raw/fs/dir.rs
deleted file mode 100644
index dbddd58..0000000
--- a/vendor/rustix/src/backend/linux_raw/fs/dir.rs
+++ /dev/null
@@ -1,315 +0,0 @@
-use crate::fd::{AsFd, BorrowedFd, OwnedFd};
-use crate::ffi::{CStr, CString};
-use crate::fs::{
- fcntl_getfl, fstat, fstatfs, fstatvfs, openat, FileType, Mode, OFlags, Stat, StatFs, StatVfs,
-};
-use crate::io;
-#[cfg(feature = "process")]
-use crate::process::fchdir;
-use crate::utils::as_ptr;
-use alloc::borrow::ToOwned;
-use alloc::vec::Vec;
-use core::fmt;
-use core::mem::size_of;
-use linux_raw_sys::general::{linux_dirent64, SEEK_SET};
-
-/// `DIR*`
-pub struct Dir {
- /// The `OwnedFd` that we read directory entries from.
- fd: OwnedFd,
-
- /// Have we seen any errors in this iteration?
- any_errors: bool,
-
- /// Should we rewind the stream on the next iteration?
- rewind: bool,
-
- /// The buffer for `linux_dirent64` entries.
- buf: Vec<u8>,
-
- /// Where we are in the buffer.
- pos: usize,
-}
-
-impl Dir {
- /// Take ownership of `fd` and construct a `Dir` that reads entries from
- /// the given directory file descriptor.
- #[inline]
- pub fn new<Fd: Into<OwnedFd>>(fd: Fd) -> io::Result<Self> {
- Self::_new(fd.into())
- }
-
- #[inline]
- fn _new(fd: OwnedFd) -> io::Result<Self> {
- Ok(Self {
- fd,
- any_errors: false,
- rewind: false,
- buf: Vec::new(),
- pos: 0,
- })
- }
-
- /// Borrow `fd` and construct a `Dir` that reads entries from the given
- /// directory file descriptor.
- #[inline]
- pub fn read_from<Fd: AsFd>(fd: Fd) -> io::Result<Self> {
- Self::_read_from(fd.as_fd())
- }
-
- #[inline]
- fn _read_from(fd: BorrowedFd<'_>) -> io::Result<Self> {
- let flags = fcntl_getfl(fd)?;
- let fd_for_dir = openat(fd, cstr!("."), flags | OFlags::CLOEXEC, Mode::empty())?;
-
- Ok(Self {
- fd: fd_for_dir,
- any_errors: false,
- rewind: false,
- buf: Vec::new(),
- pos: 0,
- })
- }
-
- /// `rewinddir(self)`
- #[inline]
- pub fn rewind(&mut self) {
- self.any_errors = false;
- self.rewind = true;
- self.pos = self.buf.len();
- }
-
- /// `readdir(self)`, where `None` means the end of the directory.
- pub fn read(&mut self) -> Option<io::Result<DirEntry>> {
- // If we've seen errors, don't continue to try to read anyting further.
- if self.any_errors {
- return None;
- }
-
- // If a rewind was requested, seek to the beginning.
- if self.rewind {
- self.rewind = false;
- match io::retry_on_intr(|| {
- crate::backend::fs::syscalls::_seek(self.fd.as_fd(), 0, SEEK_SET)
- }) {
- Ok(_) => (),
- Err(err) => {
- self.any_errors = true;
- return Some(Err(err));
- }
- }
- }
-
- // Compute linux_dirent64 field offsets.
- let z = linux_dirent64 {
- d_ino: 0_u64,
- d_off: 0_i64,
- d_type: 0_u8,
- d_reclen: 0_u16,
- d_name: Default::default(),
- };
- let base = as_ptr(&z) as usize;
- let offsetof_d_reclen = (as_ptr(&z.d_reclen) as usize) - base;
- let offsetof_d_name = (as_ptr(&z.d_name) as usize) - base;
- let offsetof_d_ino = (as_ptr(&z.d_ino) as usize) - base;
- let offsetof_d_type = (as_ptr(&z.d_type) as usize) - base;
-
- // Test if we need more entries, and if so, read more.
- if self.buf.len() - self.pos < size_of::<linux_dirent64>() {
- match self.read_more()? {
- Ok(()) => (),
- Err(err) => return Some(Err(err)),
- }
- }
-
- // We successfully read an entry. Extract the fields.
- let pos = self.pos;
-
- // Do an unaligned u16 load.
- let d_reclen = u16::from_ne_bytes([
- self.buf[pos + offsetof_d_reclen],
- self.buf[pos + offsetof_d_reclen + 1],
- ]);
- assert!(self.buf.len() - pos >= d_reclen as usize);
- self.pos += d_reclen as usize;
-
- // Read the NUL-terminated name from the `d_name` field. Without
- // `unsafe`, we need to scan for the NUL twice: once to obtain a size
- // for the slice, and then once within `CStr::from_bytes_with_nul`.
- let name_start = pos + offsetof_d_name;
- let name_len = self.buf[name_start..]
- .iter()
- .position(|x| *x == b'\0')
- .unwrap();
- let name = CStr::from_bytes_with_nul(&self.buf[name_start..][..=name_len]).unwrap();
- let name = name.to_owned();
- assert!(name.as_bytes().len() <= self.buf.len() - name_start);
-
- // Do an unaligned u64 load.
- let d_ino = u64::from_ne_bytes([
- self.buf[pos + offsetof_d_ino],
- self.buf[pos + offsetof_d_ino + 1],
- self.buf[pos + offsetof_d_ino + 2],
- self.buf[pos + offsetof_d_ino + 3],
- self.buf[pos + offsetof_d_ino + 4],
- self.buf[pos + offsetof_d_ino + 5],
- self.buf[pos + offsetof_d_ino + 6],
- self.buf[pos + offsetof_d_ino + 7],
- ]);
-
- let d_type = self.buf[pos + offsetof_d_type];
-
- // Check that our types correspond to the `linux_dirent64` types.
- let _ = linux_dirent64 {
- d_ino,
- d_off: 0,
- d_type,
- d_reclen,
- d_name: Default::default(),
- };
-
- Some(Ok(DirEntry {
- d_ino,
- d_type,
- name,
- }))
- }
-
- #[must_use]
- fn read_more(&mut self) -> Option<io::Result<()>> {
- // The first few times we're called, we allocate a relatively small
- // buffer, because many directories are small. If we're called more,
- // use progressively larger allocations, up to a fixed maximum.
- //
- // The specific sizes and policy here have not been tuned in detail yet
- // and may need to be adjusted. In doing so, we should be careful to
- // avoid unbounded buffer growth. This buffer only exists to share the
- // cost of a `getdents` call over many entries, so if it gets too big,
- // cache and heap usage will outweigh the benefit. And ultimately,
- // directories can contain more entries than we can allocate contiguous
- // memory for, so we'll always need to cap the size at some point.
- if self.buf.len() < 1024 * size_of::<linux_dirent64>() {
- self.buf.reserve(32 * size_of::<linux_dirent64>());
- }
- self.buf.resize(self.buf.capacity(), 0);
- let nread = match io::retry_on_intr(|| {
- crate::backend::fs::syscalls::getdents(self.fd.as_fd(), &mut self.buf)
- }) {
- Ok(nread) => nread,
- Err(io::Errno::NOENT) => {
- self.any_errors = true;
- return None;
- }
- Err(err) => {
- self.any_errors = true;
- return Some(Err(err));
- }
- };
- self.buf.resize(nread, 0);
- self.pos = 0;
- if nread == 0 {
- None
- } else {
- Some(Ok(()))
- }
- }
-
- /// `fstat(self)`
- #[inline]
- pub fn stat(&self) -> io::Result<Stat> {
- fstat(&self.fd)
- }
-
- /// `fstatfs(self)`
- #[inline]
- pub fn statfs(&self) -> io::Result<StatFs> {
- fstatfs(&self.fd)
- }
-
- /// `fstatvfs(self)`
- #[inline]
- pub fn statvfs(&self) -> io::Result<StatVfs> {
- fstatvfs(&self.fd)
- }
-
- /// `fchdir(self)`
- #[cfg(feature = "process")]
- #[cfg_attr(doc_cfg, doc(cfg(feature = "process")))]
- #[inline]
- pub fn chdir(&self) -> io::Result<()> {
- fchdir(&self.fd)
- }
-}
-
-impl Iterator for Dir {
- type Item = io::Result<DirEntry>;
-
- #[inline]
- fn next(&mut self) -> Option<Self::Item> {
- Self::read(self)
- }
-}
-
-impl fmt::Debug for Dir {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.debug_struct("Dir").field("fd", &self.fd).finish()
- }
-}
-
-/// `struct dirent`
-#[derive(Debug)]
-pub struct DirEntry {
- d_ino: u64,
- d_type: u8,
- name: CString,
-}
-
-impl DirEntry {
- /// Returns the file name of this directory entry.
- #[inline]
- pub fn file_name(&self) -> &CStr {
- &self.name
- }
-
- /// Returns the type of this directory entry.
- #[inline]
- pub fn file_type(&self) -> FileType {
- FileType::from_dirent_d_type(self.d_type)
- }
-
- /// Return the inode number of this directory entry.
- #[inline]
- pub fn ino(&self) -> u64 {
- self.d_ino
- }
-}
-
-#[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();
-
- let file_fd = crate::fs::openat(
- &fd,
- tmp.path().join("test.txt"),
- crate::fs::OFlags::WRONLY | crate::fs::OFlags::CREATE,
- crate::fs::Mode::RWXU,
- )
- .unwrap();
-
- let mut dir = Dir::read_from(&fd).unwrap();
-
- // Reach inside the `Dir` and replace its directory with a file, which
- // will cause the subsequent `getdents64` to fail.
- crate::io::dup2(&file_fd, &mut dir.fd).unwrap();
-
- assert!(matches!(dir.next(), Some(Err(_))));
- assert!(dir.next().is_none());
-}