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