• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use crate::fd::{AsFd, BorrowedFd, OwnedFd};
2 use crate::ffi::{CStr, CString};
3 use crate::fs::{
4     fcntl_getfl, fstat, fstatfs, fstatvfs, openat, FileType, Mode, OFlags, Stat, StatFs, StatVfs,
5 };
6 use crate::io;
7 use crate::process::fchdir;
8 use crate::utils::as_ptr;
9 use alloc::borrow::ToOwned;
10 use alloc::vec::Vec;
11 use core::fmt;
12 use core::mem::size_of;
13 use linux_raw_sys::general::{linux_dirent64, SEEK_SET};
14 
15 /// `DIR*`
16 pub 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 
25 impl Dir {
26     /// Construct a `Dir` that reads entries from the given directory
27     /// file descriptor.
28     #[inline]
read_from<Fd: AsFd>(fd: Fd) -> io::Result<Self>29     pub fn read_from<Fd: AsFd>(fd: Fd) -> io::Result<Self> {
30         Self::_read_from(fd.as_fd())
31     }
32 
33     #[inline]
_read_from(fd: BorrowedFd<'_>) -> io::Result<Self>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]
rewind(&mut self)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.
read(&mut self) -> Option<io::Result<DirEntry>>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 
read_more(&mut self) -> Option<io::Result<()>>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]
stat(&self) -> io::Result<Stat>161     pub fn stat(&self) -> io::Result<Stat> {
162         fstat(&self.fd)
163     }
164 
165     /// `fstatfs(self)`
166     #[inline]
statfs(&self) -> io::Result<StatFs>167     pub fn statfs(&self) -> io::Result<StatFs> {
168         fstatfs(&self.fd)
169     }
170 
171     /// `fstatvfs(self)`
172     #[inline]
statvfs(&self) -> io::Result<StatVfs>173     pub fn statvfs(&self) -> io::Result<StatVfs> {
174         fstatvfs(&self.fd)
175     }
176 
177     /// `fchdir(self)`
178     #[inline]
chdir(&self) -> io::Result<()>179     pub fn chdir(&self) -> io::Result<()> {
180         fchdir(&self.fd)
181     }
182 }
183 
184 impl Iterator for Dir {
185     type Item = io::Result<DirEntry>;
186 
187     #[inline]
next(&mut self) -> Option<Self::Item>188     fn next(&mut self) -> Option<Self::Item> {
189         Self::read(self)
190     }
191 }
192 
193 impl fmt::Debug for Dir {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result194     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)]
201 pub struct DirEntry {
202     d_ino: u64,
203     d_type: u8,
204     name: CString,
205 }
206 
207 impl DirEntry {
208     /// Returns the file name of this directory entry.
209     #[inline]
file_name(&self) -> &CStr210     pub fn file_name(&self) -> &CStr {
211         &self.name
212     }
213 
214     /// Returns the type of this directory entry.
215     #[inline]
file_type(&self) -> FileType216     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]
ino(&self) -> u64222     pub fn ino(&self) -> u64 {
223         self.d_ino
224     }
225 }
226