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::symlink::read_symlink_target;
9use crate::types::{BlockDevice, EntryType, FsFlags, FsType};
10
11pub struct AffsReader<'a, D: BlockDevice> {
44 device: &'a D,
45 boot: BootBlock,
47 root: RootBlock,
49 root_block: u32,
51 total_blocks: u32,
53}
54
55impl<'a, D: BlockDevice> AffsReader<'a, D> {
56 pub fn new(device: &'a D) -> Result<Self> {
58 Self::with_size(device, FLOPPY_DD_SECTORS)
59 }
60
61 pub fn new_hd(device: &'a D) -> Result<Self> {
63 Self::with_size(device, FLOPPY_HD_SECTORS)
64 }
65
66 pub fn with_size(device: &'a D, total_blocks: u32) -> Result<Self> {
68 let mut boot_buf = [0u8; BOOT_BLOCK_SIZE];
70 device
71 .read_block(0, array_ref_mut(&mut boot_buf, 0))
72 .map_err(|()| AffsError::BlockReadError)?;
73 device
74 .read_block(1, array_ref_mut(&mut boot_buf, BLOCK_SIZE))
75 .map_err(|()| AffsError::BlockReadError)?;
76
77 let boot = BootBlock::parse(&boot_buf)?;
78
79 let root_block = if boot.root_block != 0 {
81 boot.root_block
82 } else {
83 total_blocks / 2
84 };
85
86 if root_block >= total_blocks {
88 return Err(AffsError::BlockOutOfRange);
89 }
90
91 let mut root_buf = [0u8; BLOCK_SIZE];
93 device
94 .read_block(root_block, &mut root_buf)
95 .map_err(|()| AffsError::BlockReadError)?;
96
97 let root = RootBlock::parse(&root_buf)?;
98
99 Ok(Self {
100 device,
101 boot,
102 root,
103 root_block,
104 total_blocks,
105 })
106 }
107
108 #[inline]
110 pub const fn fs_type(&self) -> FsType {
111 self.boot.fs_type()
112 }
113
114 #[inline]
116 pub const fn fs_flags(&self) -> FsFlags {
117 self.boot.fs_flags()
118 }
119
120 #[inline]
122 pub const fn is_intl(&self) -> bool {
123 self.boot.fs_flags().intl
124 }
125
126 #[inline]
128 pub const fn root_block(&self) -> u32 {
129 self.root_block
130 }
131
132 #[inline]
134 pub const fn total_blocks(&self) -> u32 {
135 self.total_blocks
136 }
137
138 #[inline]
140 pub fn disk_name(&self) -> &[u8] {
141 self.root.name()
142 }
143
144 #[inline]
146 pub fn disk_name_str(&self) -> Option<&str> {
147 core::str::from_utf8(self.disk_name()).ok()
148 }
149
150 #[inline]
154 pub fn label(&self) -> &[u8] {
155 self.disk_name()
156 }
157
158 #[inline]
160 pub fn label_str(&self) -> Option<&str> {
161 self.disk_name_str()
162 }
163
164 #[inline]
166 pub fn creation_date(&self) -> crate::date::AmigaDate {
167 self.root.creation_date
168 }
169
170 #[inline]
172 pub fn last_modified(&self) -> crate::date::AmigaDate {
173 self.root.last_modified
174 }
175
176 #[inline]
181 pub fn mtime(&self) -> i64 {
182 self.root.last_modified.to_unix_timestamp()
183 }
184
185 #[inline]
187 pub const fn bitmap_valid(&self) -> bool {
188 self.root.bitmap_valid()
189 }
190
191 #[inline]
193 pub fn root_hash_table(&self) -> &[u32; HASH_TABLE_SIZE] {
194 &self.root.hash_table
195 }
196
197 #[inline]
199 pub const fn device(&self) -> &'a D {
200 self.device
201 }
202
203 pub fn read_root_dir(&self) -> DirIter<'_, D> {
205 DirIter::new(self.device, self.root.hash_table, self.is_intl())
206 }
207
208 pub fn read_dir(&self, block: u32) -> Result<DirIter<'_, D>> {
213 if block == self.root_block {
214 return Ok(self.read_root_dir());
215 }
216
217 let mut buf = [0u8; BLOCK_SIZE];
218 self.device
219 .read_block(block, &mut buf)
220 .map_err(|()| AffsError::BlockReadError)?;
221
222 let entry = EntryBlock::parse(&buf)?;
223
224 if !entry.is_dir() {
225 return Err(AffsError::NotADirectory);
226 }
227
228 Ok(DirIter::new(self.device, entry.hash_table, self.is_intl()))
229 }
230
231 pub fn find_entry(&self, dir_block: u32, name: &[u8]) -> Result<DirEntry> {
237 let dir = self.read_dir(dir_block)?;
238 dir.find(name)
239 }
240
241 pub fn find_path(&self, path: &[u8]) -> Result<DirEntry> {
245 let mut current_block = self.root_block;
246 let mut final_entry: Option<DirEntry> = None;
247
248 for component in path.split(|&b| b == b'/') {
249 if component.is_empty() {
250 continue;
251 }
252
253 let entry = self.find_entry(current_block, component)?;
254
255 if entry.is_dir() {
256 current_block = entry.block;
257 }
258
259 final_entry = Some(entry);
260 }
261
262 final_entry.ok_or(AffsError::EntryNotFound)
263 }
264
265 pub fn read_file(&self, block: u32) -> Result<FileReader<'_, D>> {
270 FileReader::new(self.device, self.fs_type(), block)
271 }
272
273 pub fn read_entry(&self, block: u32) -> Result<EntryBlock> {
275 let mut buf = [0u8; BLOCK_SIZE];
276 self.device
277 .read_block(block, &mut buf)
278 .map_err(|()| AffsError::BlockReadError)?;
279 EntryBlock::parse(&buf)
280 }
281
282 pub fn read_symlink(&self, block: u32, out: &mut [u8]) -> Result<usize> {
297 let mut buf = [0u8; BLOCK_SIZE];
298 self.device
299 .read_block(block, &mut buf)
300 .map_err(|()| AffsError::BlockReadError)?;
301
302 let entry = EntryBlock::parse(&buf)?;
304 if entry.entry_type() != Some(EntryType::SoftLink) {
305 return Err(AffsError::NotASymlink);
306 }
307
308 Ok(read_symlink_target(&buf, out))
309 }
310
311 pub fn read_symlink_entry(&self, entry: &DirEntry, out: &mut [u8]) -> Result<usize> {
315 if !entry.is_symlink() {
316 return Err(AffsError::NotASymlink);
317 }
318 self.read_symlink(entry.block, out)
319 }
320
321 pub fn root_entry(&self) -> DirEntry {
323 DirEntry::from_root(&self.root, self.root_block)
324 }
325}
326
327#[inline]
329fn array_ref_mut(slice: &mut [u8], offset: usize) -> &mut [u8; BLOCK_SIZE] {
330 (&mut slice[offset..offset + BLOCK_SIZE])
331 .try_into()
332 .expect("slice size mismatch")
333}
334
335impl crate::dir::DirEntry {
337 pub(crate) fn from_root(root: &RootBlock, block: u32) -> Self {
339 let mut name = [0u8; MAX_NAME_LEN];
340 let name_len = root.name_len.min(MAX_NAME_LEN as u8);
341 name[..name_len as usize].copy_from_slice(&root.disk_name[..name_len as usize]);
342
343 Self {
344 name,
345 name_len,
346 entry_type: crate::types::EntryType::Root,
347 block,
348 parent: 0,
349 size: 0,
350 access: crate::types::Access::new(0),
351 date: root.last_modified,
352 real_entry: 0,
353 comment: [0u8; MAX_COMMENT_LEN],
354 comment_len: 0,
355 }
356 }
357}
358
359#[cfg(test)]
360mod tests {
361 use super::*;
362
363 struct DummyDevice;
364
365 impl BlockDevice for DummyDevice {
366 fn read_block(&self, _block: u32, _buf: &mut [u8; 512]) -> core::result::Result<(), ()> {
367 Err(())
368 }
369 }
370
371 #[test]
372 fn test_reader_error_on_bad_device() {
373 let device = DummyDevice;
374 let result = AffsReader::new(&device);
375 assert!(result.is_err());
376 }
377}