ext4_view/iters/
read_dir.rs1use crate::Ext4;
10use crate::block_index::FsBlockIndex;
11use crate::checksum::Checksum;
12use crate::dir_block::DirBlock;
13use crate::dir_entry::DirEntry;
14use crate::error::{CorruptKind, Ext4Error};
15use crate::inode::{Inode, InodeFlags, InodeIndex};
16use crate::iters::file_blocks::FileBlocks;
17use crate::path::PathBuf;
18use alloc::rc::Rc;
19use alloc::vec;
20use alloc::vec::Vec;
21use core::fmt::{self, Debug, Formatter};
22
23pub struct ReadDir {
25 fs: Ext4,
26
27 path: Rc<PathBuf>,
33
34 file_blocks: FileBlocks,
36
37 block_index: Option<FsBlockIndex>,
40
41 is_first_block: bool,
43
44 block: Vec<u8>,
46
47 offset_within_block: usize,
49
50 is_done: bool,
53
54 has_htree: bool,
58
59 checksum_base: Checksum,
62
63 inode: InodeIndex,
65}
66
67impl ReadDir {
68 pub(crate) fn new(
69 fs: Ext4,
70 inode: &Inode,
71 path: PathBuf,
72 ) -> Result<Self, Ext4Error> {
73 let has_htree = inode.flags.contains(InodeFlags::DIRECTORY_HTREE);
74
75 if inode.flags.contains(InodeFlags::DIRECTORY_ENCRYPTED) {
76 return Err(Ext4Error::Encrypted);
77 }
78
79 Ok(Self {
80 fs: fs.clone(),
81 path: Rc::new(path),
82 file_blocks: FileBlocks::new(fs.clone(), inode)?,
83 block_index: None,
84 is_first_block: true,
85 block: vec![0; fs.0.superblock.block_size.to_usize()],
86 offset_within_block: 0,
87 is_done: false,
88 has_htree,
89 checksum_base: inode.checksum_base.clone(),
90 inode: inode.index,
91 })
92 }
93
94 fn next_impl(&mut self) -> Result<Option<DirEntry>, Ext4Error> {
95 let block_index = if let Some(block_index) = self.block_index {
97 block_index
98 } else {
99 match self.file_blocks.next() {
100 Some(Ok(block_index)) => {
101 self.block_index = Some(block_index);
102 self.offset_within_block = 0;
103
104 block_index
105 }
106 Some(Err(err)) => return Err(err),
107 None => {
108 self.is_done = true;
109 return Ok(None);
110 }
111 }
112 };
113
114 let block_size = self.fs.0.superblock.block_size;
117 if self.offset_within_block >= block_size {
118 self.is_first_block = false;
119 self.block_index = None;
120 return Ok(None);
121 }
122
123 if self.offset_within_block == 0 {
125 DirBlock {
126 fs: &self.fs,
127 dir_inode: self.inode,
128 block_index,
129 is_first: self.is_first_block,
130 has_htree: self.has_htree,
131 checksum_base: self.checksum_base.clone(),
132 }
133 .read(&mut self.block)?;
134 }
135
136 let (entry, entry_size) = DirEntry::from_bytes(
137 self.fs.clone(),
138 &self.block[self.offset_within_block..],
139 self.inode,
140 self.path.clone(),
141 )?;
142
143 self.offset_within_block = self
144 .offset_within_block
145 .checked_add(entry_size)
146 .ok_or(CorruptKind::DirEntry(self.inode))?;
147
148 Ok(entry)
149 }
150}
151
152impl Debug for ReadDir {
153 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
154 write!(f, r#"ReadDir("{:?}")"#, self.path)
157 }
158}
159
160impl_result_iter!(ReadDir, DirEntry);
169
170#[cfg(feature = "std")]
171#[cfg(test)]
172mod tests {
173 use super::*;
174 use crate::test_util::load_test_disk1;
175
176 #[test]
177 fn test_read_dir() {
178 let fs = load_test_disk1();
179 let root_inode = fs.read_root_inode().unwrap();
180 let root_path = crate::PathBuf::new("/");
181
182 let entries: Vec<_> = ReadDir::new(fs, &root_inode, root_path)
184 .unwrap()
185 .map(|e| e.unwrap())
186 .collect();
187
188 assert!(entries.iter().any(|e| e.file_name() == "."));
190 assert!(entries.iter().any(|e| e.file_name() == ".."));
191 assert!(entries.iter().any(|e| e.file_name() == "empty_file"));
192 assert!(entries.iter().any(|e| e.file_name() == "empty_dir"));
193
194 assert!(!entries.iter().any(|e| e.file_name() == "does_not_exist"));
196 }
197}