cap_primitives/rustix/fs/
read_dir_inner.rs1use crate::fs::{
2 open_dir_for_reading, open_dir_for_reading_unchecked, open_entry_impl, read_dir_unchecked,
3 remove_dir_unchecked, remove_file_unchecked, stat_unchecked, DirEntryInner, FollowSymlinks,
4 Metadata, OpenOptions, ReadDir,
5};
6use io_extras::os::rustix::{AsRawFd, FromRawFd, RawFd};
7use io_lifetimes::AsFd;
8use rustix::fd::OwnedFd;
9use rustix::fs::Dir;
10use std::ffi::OsStr;
11use std::mem::ManuallyDrop;
12#[cfg(unix)]
13use std::os::unix::ffi::OsStrExt;
14#[cfg(target_os = "wasi")]
15use std::os::wasi::ffi::OsStrExt;
16use std::path::{Component, Path};
17use std::sync::{Arc, Mutex};
18use std::{fmt, fs, io};
19
20pub(crate) struct ReadDirInner {
21 raw_fd: RawFd,
22
23 rustix: Arc<Mutex<(Dir, OwnedFd)>>,
27}
28
29impl ReadDirInner {
30 pub(crate) fn new(start: &fs::File, path: &Path, follow: FollowSymlinks) -> io::Result<Self> {
31 let fd = open_dir_for_reading(start, path, follow)?;
32 let dir = Dir::read_from(fd.as_fd())?;
33 Ok(Self {
34 raw_fd: fd.as_fd().as_raw_fd(),
35 rustix: Arc::new(Mutex::new((dir, OwnedFd::from(fd)))),
36 })
37 }
38
39 pub(crate) fn read_base_dir(start: &fs::File) -> io::Result<Self> {
40 let fd =
45 open_dir_for_reading_unchecked(start, Component::CurDir.as_ref(), FollowSymlinks::No)?;
46 let dir = Dir::read_from(fd.as_fd())?;
47 Ok(Self {
48 raw_fd: fd.as_fd().as_raw_fd(),
49 rustix: Arc::new(Mutex::new((dir, fd.into()))),
50 })
51 }
52
53 pub(crate) fn new_unchecked(
54 start: &fs::File,
55 path: &Path,
56 follow: FollowSymlinks,
57 ) -> io::Result<Self> {
58 let fd = open_dir_for_reading_unchecked(start, path, follow)?;
59 let dir = Dir::read_from(fd.as_fd())?;
60 Ok(Self {
61 raw_fd: fd.as_fd().as_raw_fd(),
62 rustix: Arc::new(Mutex::new((dir, fd.into()))),
63 })
64 }
65
66 pub(super) fn open(&self, file_name: &OsStr, options: &OpenOptions) -> io::Result<fs::File> {
67 open_entry_impl(&self.as_file_view(), file_name, options)
68 }
69
70 pub(super) fn metadata(&self, file_name: &OsStr) -> io::Result<Metadata> {
71 stat_unchecked(&self.as_file_view(), file_name.as_ref(), FollowSymlinks::No)
72 }
73
74 pub(super) fn remove_file(&self, file_name: &OsStr) -> io::Result<()> {
75 remove_file_unchecked(&self.as_file_view(), file_name.as_ref())
76 }
77
78 pub(super) fn remove_dir(&self, file_name: &OsStr) -> io::Result<()> {
79 remove_dir_unchecked(&self.as_file_view(), file_name.as_ref())
80 }
81
82 pub(super) fn self_metadata(&self) -> io::Result<Metadata> {
83 Metadata::from_file(&self.as_file_view())
84 }
85
86 pub(super) fn read_dir(
87 &self,
88 file_name: &OsStr,
89 follow: FollowSymlinks,
90 ) -> io::Result<ReadDir> {
91 read_dir_unchecked(&self.as_file_view(), file_name.as_ref(), follow)
92 }
93
94 #[allow(unsafe_code)]
95 fn as_file_view(&self) -> ManuallyDrop<fs::File> {
96 ManuallyDrop::new(unsafe { fs::File::from_raw_fd(self.raw_fd) })
99 }
100}
101
102impl Iterator for ReadDirInner {
103 type Item = io::Result<DirEntryInner>;
104
105 fn next(&mut self) -> Option<Self::Item> {
106 loop {
107 let entry = self.rustix.lock().unwrap().0.read()?;
108 let entry = match entry {
109 Ok(entry) => entry,
110 Err(e) => return Some(Err(e.into())),
111 };
112 let file_name = entry.file_name().to_bytes();
113 if file_name != Component::CurDir.as_os_str().as_bytes()
114 && file_name != Component::ParentDir.as_os_str().as_bytes()
115 {
116 let clone = Arc::clone(&self.rustix);
117 return Some(Ok(DirEntryInner {
118 rustix: entry,
119 read_dir: Self {
120 raw_fd: self.raw_fd,
121 rustix: clone,
122 },
123 }));
124 }
125 }
126 }
127}
128
129impl fmt::Debug for ReadDirInner {
130 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
132 let mut b = f.debug_struct("ReadDir");
133 b.field("raw_fd", &self.raw_fd);
134 b.finish()
135 }
136}