iso9660/directory/
iterator.rs

1//! Directory iteration
2//!
3//! Iterator for reading directory entries sequentially.
4
5use crate::error::{Iso9660Error, Result};
6use crate::types::{FileEntry, SECTOR_SIZE};
7use crate::directory::record::DirectoryRecord;
8use crate::utils::string;
9use gpt_disk_io::BlockIo;
10use gpt_disk_types::Lba;
11use alloc::string::String;
12use alloc::boxed::Box;
13
14/// Directory iterator
15pub struct DirectoryIterator<'a, B: BlockIo> {
16    block_io: &'a mut B,
17    extent_lba: u32,
18    extent_len: u32,
19    offset: usize,
20    current_sector: Box<[u8; SECTOR_SIZE]>,
21    current_sector_lba: Option<u64>,
22}
23
24impl<'a, B: BlockIo> DirectoryIterator<'a, B> {
25    /// Create new directory iterator
26    pub fn new(block_io: &'a mut B, extent_lba: u32, extent_len: u32) -> Self {
27        Self {
28            block_io,
29            extent_lba,
30            extent_len,
31            offset: 0,
32            current_sector: Box::new([0u8; SECTOR_SIZE]),
33            current_sector_lba: None,
34        }
35    }
36}
37
38impl<'a, B: BlockIo> Iterator for DirectoryIterator<'a, B> {
39    type Item = Result<FileEntry>;
40    
41    fn next(&mut self) -> Option<Self::Item> {
42        loop {
43            // Check if we've read all directory data
44            if self.offset >= self.extent_len as usize {
45                return None;
46            }
47            
48            // Calculate current LBA and offset within sector
49            let sector_offset = self.offset / SECTOR_SIZE;
50            let lba = self.extent_lba as u64 + sector_offset as u64;
51            let offset_in_sector = self.offset % SECTOR_SIZE;
52            
53            // Read sector if needed
54            if self.current_sector_lba != Some(lba) {
55                if self.block_io.read_blocks(Lba(lba), self.current_sector.as_mut()).is_err() {
56                    return Some(Err(Iso9660Error::IoError));
57                }
58                self.current_sector_lba = Some(lba);
59            }
60            
61            // Get remaining data in current sector
62            let sector_data = &self.current_sector[offset_in_sector..];
63            
64            // Check for zero-length record (skip to next sector)
65            if sector_data.is_empty() || sector_data[0] == 0 {
66                // Move to next sector
67                let next_sector_offset = (sector_offset + 1) * SECTOR_SIZE;
68                self.offset = next_sector_offset;
69                continue;
70            }
71            
72            // Parse directory record
73            let record = match DirectoryRecord::parse(sector_data) {
74                Ok(r) => r,
75                Err(e) => return Some(Err(e)),
76            };
77            
78            // Advance offset by record length
79            self.offset += record.length as usize;
80            
81            // Convert file identifier to string
82            let file_id = record.file_identifier();
83            
84            // Handle special directory entries
85            let name = if file_id.len() == 1 && file_id[0] == 0 {
86                String::from(".")
87            } else if file_id.len() == 1 && file_id[0] == 1 {
88                String::from("..")
89            } else {
90                match string::dchars_to_str(file_id) {
91                    Ok(s) => {
92                        // Strip version suffix (e.g., ";1")
93                        let stripped = string::strip_version(s);
94                        String::from(stripped)
95                    }
96                    Err(_) => {
97                        // If not valid UTF-8, use lossy conversion
98                        let s = String::from_utf8_lossy(file_id);
99                        let stripped = string::strip_version(&s);
100                        String::from(stripped)
101                    }
102                }
103            };
104            
105            // Build FileEntry
106            let entry = FileEntry {
107                name,
108                size: record.get_data_length() as u64,
109                extent_lba: record.get_extent_lba(),
110                data_length: record.get_data_length(),
111                flags: record.get_flags(),
112                file_unit_size: record.file_unit_size,
113                interleave_gap: record.interleave_gap,
114            };
115            
116            return Some(Ok(entry));
117        }
118    }
119}