orbis_pfs/directory/
mod.rs1use snafu::{OptionExt, ResultExt, ensure};
2
3use self::dirent::Dirent;
4use crate::Pfs;
5use crate::file::File;
6use crate::inode::Inode;
7use std::collections::BTreeMap;
8use std::sync::Arc;
9
10pub mod dirent;
11
12#[derive(Debug, snafu::Snafu)]
14#[non_exhaustive]
15pub enum OpenError {
16 #[snafu(display("inode #{inode} is not valid"))]
17 InvalidInode { inode: usize },
18
19 #[snafu(display("cannot read block #{block}"))]
20 ReadBlock { block: u32, source: std::io::Error },
21
22 #[snafu(display("cannot read directory entry"))]
23 ReadDirEntry { source: dirent::ReadError },
24
25 #[snafu(display("dirent #{dirent} in block #{block} has invalid size"))]
26 DirentInvalidSize { block: u32, dirent: usize },
27
28 #[snafu(display("dirent #{dirent} in block #{block} has unknown type"))]
29 DirentUnknownType { block: u32, dirent: usize },
30}
31
32#[derive(Clone)]
57#[must_use]
58pub struct Directory<'a> {
59 pfs: Arc<Pfs<'a>>,
60 inode: usize,
61}
62
63impl<'a> std::fmt::Debug for Directory<'a> {
64 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
65 f.debug_struct("Directory")
66 .field("inode", &self.inode)
67 .field("mode", &self.mode())
68 .finish_non_exhaustive()
69 }
70}
71
72impl<'a> Directory<'a> {
73 pub(super) fn new(pfs: Arc<Pfs<'a>>, inode: usize) -> Self {
74 Self { pfs, inode }
75 }
76
77 #[must_use]
78 pub fn mode(&self) -> u16 {
79 self.inode_ref().mode()
80 }
81
82 #[must_use]
83 pub fn flags(&self) -> u32 {
84 self.inode_ref().flags().value()
85 }
86
87 #[must_use]
89 pub fn atime(&self) -> u64 {
90 self.inode_ref().atime()
91 }
92
93 #[must_use]
95 pub fn mtime(&self) -> u64 {
96 self.inode_ref().mtime()
97 }
98
99 #[must_use]
101 pub fn ctime(&self) -> u64 {
102 self.inode_ref().ctime()
103 }
104
105 #[must_use]
107 pub fn birthtime(&self) -> u64 {
108 self.inode_ref().birthtime()
109 }
110
111 #[must_use]
113 pub fn mtimensec(&self) -> u32 {
114 self.inode_ref().mtimensec()
115 }
116
117 #[must_use]
119 pub fn atimensec(&self) -> u32 {
120 self.inode_ref().atimensec()
121 }
122
123 #[must_use]
125 pub fn ctimensec(&self) -> u32 {
126 self.inode_ref().ctimensec()
127 }
128
129 #[must_use]
131 pub fn birthnsec(&self) -> u32 {
132 self.inode_ref().birthnsec()
133 }
134
135 #[must_use]
136 pub fn uid(&self) -> u32 {
137 self.inode_ref().uid()
138 }
139
140 #[must_use]
141 pub fn gid(&self) -> u32 {
142 self.inode_ref().gid()
143 }
144
145 pub fn open(&self) -> Result<DirEntries<'a>, OpenError> {
149 let blocks = self.pfs.block_map(self.inode);
150 let block_size = self.pfs.block_size;
151 let img = self.pfs.image();
152
153 let mut items: BTreeMap<Vec<u8>, DirEntry<'a>> = BTreeMap::new();
155 let mut block_data = vec![0; block_size as usize];
156
157 for &block_num in blocks {
158 let offset = (block_num as u64) * (block_size as u64);
160
161 img.read_exact_at(offset, &mut block_data)
162 .context(ReadBlockSnafu { block: block_num })?;
163
164 let mut next = block_data.as_slice();
166
167 for num in 0_usize.. {
168 let dirent = match Dirent::read(&mut next) {
170 Ok(v) => v,
171 Err(dirent::ReadError::TooSmall | dirent::ReadError::EndOfEntry) => {
172 break;
173 }
174 err => err.context(ReadDirEntrySnafu)?,
175 };
176
177 next = next
179 .get(dirent.padding_size()..)
180 .context(DirentInvalidSizeSnafu {
181 block: block_num,
182 dirent: num,
183 })?;
184
185 let inode = dirent.inode();
187 ensure!(inode < self.pfs.inode_count(), InvalidInodeSnafu { inode });
188
189 let entry = match dirent.ty() {
191 Dirent::FILE => DirEntry::File(File::new(self.pfs.clone(), inode)),
192 Dirent::DIRECTORY => {
193 DirEntry::Directory(Directory::new(self.pfs.clone(), inode))
194 }
195 Dirent::SELF | Dirent::PARENT => continue,
196 _ => {
197 return Err(DirentUnknownTypeSnafu {
198 block: block_num,
199 dirent: num,
200 }
201 .build());
202 }
203 };
204
205 items.insert(dirent.name().to_vec(), entry);
206 }
207 }
208
209 Ok(DirEntries { items })
210 }
211
212 fn inode_ref(&self) -> &Inode {
213 self.pfs.inode(self.inode)
214 }
215}
216
217#[derive(Debug)]
222#[must_use]
223pub struct DirEntries<'a> {
224 items: BTreeMap<Vec<u8>, DirEntry<'a>>,
225}
226
227impl<'a> DirEntries<'a> {
228 #[must_use]
230 pub fn len(&self) -> usize {
231 self.items.len()
232 }
233
234 #[must_use]
236 pub fn is_empty(&self) -> bool {
237 self.items.is_empty()
238 }
239
240 #[must_use]
242 pub fn get(&self, name: &[u8]) -> Option<&DirEntry<'a>> {
243 self.items.get(name)
244 }
245
246 pub fn remove(&mut self, name: &[u8]) -> Option<DirEntry<'a>> {
248 self.items.remove(name)
249 }
250
251 pub fn iter(&self) -> DirEntriesIter<'_, 'a> {
253 DirEntriesIter {
254 inner: self.items.iter(),
255 }
256 }
257
258 pub fn names(&self) -> impl Iterator<Item = &[u8]> {
260 self.items.keys().map(|k| k.as_slice())
261 }
262}
263
264impl<'a> IntoIterator for DirEntries<'a> {
265 type Item = (Vec<u8>, DirEntry<'a>);
266 type IntoIter = DirEntriesOwnedIter<'a>;
267
268 fn into_iter(self) -> Self::IntoIter {
269 DirEntriesOwnedIter {
270 inner: self.items.into_iter(),
271 }
272 }
273}
274
275impl<'b, 'a> IntoIterator for &'b DirEntries<'a> {
276 type Item = (&'b [u8], &'b DirEntry<'a>);
277 type IntoIter = DirEntriesIter<'b, 'a>;
278
279 fn into_iter(self) -> Self::IntoIter {
280 self.iter()
281 }
282}
283
284#[derive(Debug)]
286#[must_use = "iterators are lazy and do nothing unless consumed"]
287pub struct DirEntriesIter<'b, 'a> {
288 inner: std::collections::btree_map::Iter<'b, Vec<u8>, DirEntry<'a>>,
289}
290
291impl<'b, 'a> Iterator for DirEntriesIter<'b, 'a> {
292 type Item = (&'b [u8], &'b DirEntry<'a>);
293
294 fn next(&mut self) -> Option<Self::Item> {
295 self.inner.next().map(|(k, v)| (k.as_slice(), v))
296 }
297
298 fn size_hint(&self) -> (usize, Option<usize>) {
299 self.inner.size_hint()
300 }
301}
302
303impl ExactSizeIterator for DirEntriesIter<'_, '_> {}
304
305#[derive(Debug)]
307#[must_use = "iterators are lazy and do nothing unless consumed"]
308pub struct DirEntriesOwnedIter<'a> {
309 inner: std::collections::btree_map::IntoIter<Vec<u8>, DirEntry<'a>>,
310}
311
312impl<'a> Iterator for DirEntriesOwnedIter<'a> {
313 type Item = (Vec<u8>, DirEntry<'a>);
314
315 fn next(&mut self) -> Option<Self::Item> {
316 self.inner.next()
317 }
318
319 fn size_hint(&self) -> (usize, Option<usize>) {
320 self.inner.size_hint()
321 }
322}
323
324impl ExactSizeIterator for DirEntriesOwnedIter<'_> {}
325
326#[derive(Debug, Clone)]
328#[non_exhaustive]
329pub enum DirEntry<'a> {
330 Directory(Directory<'a>),
332 File(File<'a>),
334}