1use crate::block::{BootBlock, EntryBlock, RootBlock};
4use crate::constants::*;
5use crate::dir::{DirEntry, DirIter};
6use crate::error::{AffsError, Result};
7use crate::file::FileReader;
8use crate::types::{BlockDevice, FsFlags, FsType};
9
10pub struct AffsReader<'a, D: BlockDevice> {
43 device: &'a D,
44 boot: BootBlock,
46 root: RootBlock,
48 root_block: u32,
50 total_blocks: u32,
52}
53
54impl<'a, D: BlockDevice> AffsReader<'a, D> {
55 pub fn new(device: &'a D) -> Result<Self> {
57 Self::with_size(device, FLOPPY_DD_SECTORS)
58 }
59
60 pub fn new_hd(device: &'a D) -> Result<Self> {
62 Self::with_size(device, FLOPPY_HD_SECTORS)
63 }
64
65 pub fn with_size(device: &'a D, total_blocks: u32) -> Result<Self> {
67 let mut boot_buf = [0u8; BOOT_BLOCK_SIZE];
69 device
70 .read_block(0, array_ref_mut(&mut boot_buf, 0))
71 .map_err(|()| AffsError::BlockReadError)?;
72 device
73 .read_block(1, array_ref_mut(&mut boot_buf, BLOCK_SIZE))
74 .map_err(|()| AffsError::BlockReadError)?;
75
76 let boot = BootBlock::parse(&boot_buf)?;
77
78 let root_block = if boot.root_block != 0 {
80 boot.root_block
81 } else {
82 total_blocks / 2
83 };
84
85 if root_block >= total_blocks {
87 return Err(AffsError::BlockOutOfRange);
88 }
89
90 let mut root_buf = [0u8; BLOCK_SIZE];
92 device
93 .read_block(root_block, &mut root_buf)
94 .map_err(|()| AffsError::BlockReadError)?;
95
96 let root = RootBlock::parse(&root_buf)?;
97
98 Ok(Self {
99 device,
100 boot,
101 root,
102 root_block,
103 total_blocks,
104 })
105 }
106
107 #[inline]
109 pub const fn fs_type(&self) -> FsType {
110 self.boot.fs_type()
111 }
112
113 #[inline]
115 pub const fn fs_flags(&self) -> FsFlags {
116 self.boot.fs_flags()
117 }
118
119 #[inline]
121 pub const fn is_intl(&self) -> bool {
122 self.boot.fs_flags().intl
123 }
124
125 #[inline]
127 pub const fn root_block(&self) -> u32 {
128 self.root_block
129 }
130
131 #[inline]
133 pub const fn total_blocks(&self) -> u32 {
134 self.total_blocks
135 }
136
137 #[inline]
139 pub fn disk_name(&self) -> &[u8] {
140 self.root.name()
141 }
142
143 #[inline]
145 pub fn disk_name_str(&self) -> Option<&str> {
146 core::str::from_utf8(self.disk_name()).ok()
147 }
148
149 #[inline]
151 pub const fn bitmap_valid(&self) -> bool {
152 self.root.bitmap_valid()
153 }
154
155 #[inline]
157 pub fn root_hash_table(&self) -> &[u32; HASH_TABLE_SIZE] {
158 &self.root.hash_table
159 }
160
161 #[inline]
163 pub const fn device(&self) -> &'a D {
164 self.device
165 }
166
167 pub fn read_root_dir(&self) -> DirIter<'_, D> {
169 DirIter::new(self.device, self.root.hash_table, self.is_intl())
170 }
171
172 pub fn read_dir(&self, block: u32) -> Result<DirIter<'_, D>> {
177 if block == self.root_block {
178 return Ok(self.read_root_dir());
179 }
180
181 let mut buf = [0u8; BLOCK_SIZE];
182 self.device
183 .read_block(block, &mut buf)
184 .map_err(|()| AffsError::BlockReadError)?;
185
186 let entry = EntryBlock::parse(&buf)?;
187
188 if !entry.is_dir() {
189 return Err(AffsError::NotADirectory);
190 }
191
192 Ok(DirIter::new(self.device, entry.hash_table, self.is_intl()))
193 }
194
195 pub fn find_entry(&self, dir_block: u32, name: &[u8]) -> Result<DirEntry> {
201 let dir = self.read_dir(dir_block)?;
202 dir.find(name)
203 }
204
205 pub fn find_path(&self, path: &[u8]) -> Result<DirEntry> {
209 let mut current_block = self.root_block;
210 let mut final_entry: Option<DirEntry> = None;
211
212 for component in path.split(|&b| b == b'/') {
213 if component.is_empty() {
214 continue;
215 }
216
217 let entry = self.find_entry(current_block, component)?;
218
219 if entry.is_dir() {
220 current_block = entry.block;
221 }
222
223 final_entry = Some(entry);
224 }
225
226 final_entry.ok_or(AffsError::EntryNotFound)
227 }
228
229 pub fn read_file(&self, block: u32) -> Result<FileReader<'_, D>> {
234 FileReader::new(self.device, self.fs_type(), block)
235 }
236
237 pub fn read_entry(&self, block: u32) -> Result<EntryBlock> {
239 let mut buf = [0u8; BLOCK_SIZE];
240 self.device
241 .read_block(block, &mut buf)
242 .map_err(|()| AffsError::BlockReadError)?;
243 EntryBlock::parse(&buf)
244 }
245
246 pub fn root_entry(&self) -> DirEntry {
248 DirEntry::from_root(&self.root, self.root_block)
249 }
250}
251
252#[inline]
254fn array_ref_mut(slice: &mut [u8], offset: usize) -> &mut [u8; BLOCK_SIZE] {
255 (&mut slice[offset..offset + BLOCK_SIZE])
256 .try_into()
257 .expect("slice size mismatch")
258}
259
260impl crate::dir::DirEntry {
262 pub(crate) fn from_root(root: &RootBlock, block: u32) -> Self {
264 let mut name = [0u8; MAX_NAME_LEN];
265 let name_len = root.name_len.min(MAX_NAME_LEN as u8);
266 name[..name_len as usize].copy_from_slice(&root.disk_name[..name_len as usize]);
267
268 Self {
269 name,
270 name_len,
271 entry_type: crate::types::EntryType::Root,
272 block,
273 parent: 0,
274 size: 0,
275 access: crate::types::Access::new(0),
276 date: root.last_modified,
277 real_entry: 0,
278 comment: [0u8; MAX_COMMENT_LEN],
279 comment_len: 0,
280 }
281 }
282}
283
284#[cfg(test)]
285mod tests {
286 use super::*;
287
288 struct DummyDevice;
289
290 impl BlockDevice for DummyDevice {
291 fn read_block(&self, _block: u32, _buf: &mut [u8; 512]) -> core::result::Result<(), ()> {
292 Err(())
293 }
294 }
295
296 #[test]
297 fn test_reader_error_on_bad_device() {
298 let device = DummyDevice;
299 let result = AffsReader::new(&device);
300 assert!(result.is_err());
301 }
302}