]> git.proxmox.com Git - rustc.git/blame - vendor/rustix-0.37.11/src/backend/linux_raw/fs/dir.rs
New upstream version 1.72.1+dfsg1
[rustc.git] / vendor / rustix-0.37.11 / src / backend / linux_raw / fs / dir.rs
CommitLineData
fe692bf9
FG
1use crate::fd::{AsFd, BorrowedFd, OwnedFd};
2use crate::ffi::{CStr, CString};
3use crate::fs::{
4 fcntl_getfl, fstat, fstatfs, fstatvfs, openat, FileType, Mode, OFlags, Stat, StatFs, StatVfs,
5};
6use crate::io;
7use crate::process::fchdir;
8use crate::utils::as_ptr;
9use alloc::borrow::ToOwned;
10use alloc::vec::Vec;
11use core::fmt;
12use core::mem::size_of;
13use linux_raw_sys::general::{linux_dirent64, SEEK_SET};
14
15/// `DIR*`
16pub struct Dir {
17 /// The `OwnedFd` that we read directory entries from.
18 fd: OwnedFd,
19
20 buf: Vec<u8>,
21 pos: usize,
22 next: Option<u64>,
23}
24
25impl Dir {
26 /// Construct a `Dir` that reads entries from the given directory
27 /// file descriptor.
28 #[inline]
29 pub fn read_from<Fd: AsFd>(fd: Fd) -> io::Result<Self> {
30 Self::_read_from(fd.as_fd())
31 }
32
33 #[inline]
34 fn _read_from(fd: BorrowedFd<'_>) -> io::Result<Self> {
35 let flags = fcntl_getfl(fd)?;
36 let fd_for_dir = openat(fd, cstr!("."), flags | OFlags::CLOEXEC, Mode::empty())?;
37
38 Ok(Self {
39 fd: fd_for_dir,
40 buf: Vec::new(),
41 pos: 0,
42 next: None,
43 })
44 }
45
46 /// `rewinddir(self)`
47 #[inline]
48 pub fn rewind(&mut self) {
49 self.pos = self.buf.len();
50 self.next = Some(0);
51 }
52
53 /// `readdir(self)`, where `None` means the end of the directory.
54 pub fn read(&mut self) -> Option<io::Result<DirEntry>> {
55 if let Some(next) = self.next.take() {
56 match crate::backend::fs::syscalls::_seek(self.fd.as_fd(), next as i64, SEEK_SET) {
57 Ok(_) => (),
58 Err(err) => return Some(Err(err)),
59 }
60 }
61
62 // Compute linux_dirent64 field offsets.
63 let z = linux_dirent64 {
64 d_ino: 0_u64,
65 d_off: 0_i64,
66 d_type: 0_u8,
67 d_reclen: 0_u16,
68 d_name: Default::default(),
69 };
70 let base = as_ptr(&z) as usize;
71 let offsetof_d_reclen = (as_ptr(&z.d_reclen) as usize) - base;
72 let offsetof_d_name = (as_ptr(&z.d_name) as usize) - base;
73 let offsetof_d_ino = (as_ptr(&z.d_ino) as usize) - base;
74 let offsetof_d_type = (as_ptr(&z.d_type) as usize) - base;
75
76 // Test if we need more entries, and if so, read more.
77 if self.buf.len() - self.pos < size_of::<linux_dirent64>() {
78 match self.read_more()? {
79 Ok(()) => (),
80 Err(e) => return Some(Err(e)),
81 }
82 }
83
84 // We successfully read an entry. Extract the fields.
85 let pos = self.pos;
86
87 // Do an unaligned u16 load.
88 let d_reclen = u16::from_ne_bytes([
89 self.buf[pos + offsetof_d_reclen],
90 self.buf[pos + offsetof_d_reclen + 1],
91 ]);
92 assert!(self.buf.len() - pos >= d_reclen as usize);
93 self.pos += d_reclen as usize;
94
95 // Read the NUL-terminated name from the `d_name` field. Without
96 // `unsafe`, we need to scan for the NUL twice: once to obtain a size
97 // for the slice, and then once within `CStr::from_bytes_with_nul`.
98 let name_start = pos + offsetof_d_name;
99 let name_len = self.buf[name_start..]
100 .iter()
101 .position(|x| *x == b'\0')
102 .unwrap();
103 let name =
104 CStr::from_bytes_with_nul(&self.buf[name_start..name_start + name_len + 1]).unwrap();
105 let name = name.to_owned();
106 assert!(name.as_bytes().len() <= self.buf.len() - name_start);
107
108 // Do an unaligned u64 load.
109 let d_ino = u64::from_ne_bytes([
110 self.buf[pos + offsetof_d_ino],
111 self.buf[pos + offsetof_d_ino + 1],
112 self.buf[pos + offsetof_d_ino + 2],
113 self.buf[pos + offsetof_d_ino + 3],
114 self.buf[pos + offsetof_d_ino + 4],
115 self.buf[pos + offsetof_d_ino + 5],
116 self.buf[pos + offsetof_d_ino + 6],
117 self.buf[pos + offsetof_d_ino + 7],
118 ]);
119
120 let d_type = self.buf[pos + offsetof_d_type];
121
122 // Check that our types correspond to the `linux_dirent64` types.
123 let _ = linux_dirent64 {
124 d_ino,
125 d_off: 0,
126 d_type,
127 d_reclen,
128 d_name: Default::default(),
129 };
130
131 Some(Ok(DirEntry {
132 d_ino,
133 d_type,
134 name,
135 }))
136 }
137
138 fn read_more(&mut self) -> Option<io::Result<()>> {
139 let og_len = self.buf.len();
140 // Capacity increment currently chosen by wild guess.
141 self.buf
142 .resize(self.buf.capacity() + 32 * size_of::<linux_dirent64>(), 0);
143 let nread = match crate::backend::fs::syscalls::getdents(self.fd.as_fd(), &mut self.buf) {
144 Ok(nread) => nread,
145 Err(err) => {
146 self.buf.resize(og_len, 0);
147 return Some(Err(err));
148 }
149 };
150 self.buf.resize(nread, 0);
151 self.pos = 0;
152 if nread == 0 {
153 None
154 } else {
155 Some(Ok(()))
156 }
157 }
158
159 /// `fstat(self)`
160 #[inline]
161 pub fn stat(&self) -> io::Result<Stat> {
162 fstat(&self.fd)
163 }
164
165 /// `fstatfs(self)`
166 #[inline]
167 pub fn statfs(&self) -> io::Result<StatFs> {
168 fstatfs(&self.fd)
169 }
170
171 /// `fstatvfs(self)`
172 #[inline]
173 pub fn statvfs(&self) -> io::Result<StatVfs> {
174 fstatvfs(&self.fd)
175 }
176
177 /// `fchdir(self)`
178 #[inline]
179 pub fn chdir(&self) -> io::Result<()> {
180 fchdir(&self.fd)
181 }
182}
183
184impl Iterator for Dir {
185 type Item = io::Result<DirEntry>;
186
187 #[inline]
188 fn next(&mut self) -> Option<Self::Item> {
189 Self::read(self)
190 }
191}
192
193impl fmt::Debug for Dir {
194 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
195 f.debug_struct("Dir").field("fd", &self.fd).finish()
196 }
197}
198
199/// `struct dirent`
200#[derive(Debug)]
201pub struct DirEntry {
202 d_ino: u64,
203 d_type: u8,
204 name: CString,
205}
206
207impl DirEntry {
208 /// Returns the file name of this directory entry.
209 #[inline]
210 pub fn file_name(&self) -> &CStr {
211 &self.name
212 }
213
214 /// Returns the type of this directory entry.
215 #[inline]
216 pub fn file_type(&self) -> FileType {
217 FileType::from_dirent_d_type(self.d_type)
218 }
219
220 /// Return the inode number of this directory entry.
221 #[inline]
222 pub fn ino(&self) -> u64 {
223 self.d_ino
224 }
225}