Skip to main content

hadris_iso/read/
boot.rs

1use super::super::boot::{BaseBootCatalog, BootSectionHeaderEntry};
2use super::super::io::LogicalSector;
3use hadris_common::types::endian::Endian;
4
5use super::super::io::{self, IsoCursor, Read, Seek, SeekFrom};
6use super::IsoImage;
7use bytemuck::Zeroable;
8use spin::Mutex;
9
10#[derive(Debug, Clone)]
11/// Represents BootInfo.
12pub struct BootInfo {
13    /// The Base Boot Catalog
14    /// This is used to provide basic information about booting
15    pub(crate) catalog: BaseBootCatalog,
16    /// The Start Sector of the Boot Catalog
17    /// This is used to construct an iterator for querying further sections
18    pub(crate) catalog_ptr: LogicalSector,
19}
20
21impl BootInfo {
22    /// Performs the `sections` operation.
23    pub fn sections<'a, R: Read + Seek>(
24        &'a self,
25        image: &'a IsoImage<R>,
26    ) -> BootSectionIter<'a, R> {
27        // Boot catalog structure:
28        // - Validation Entry (32 bytes) at sector start
29        // - Default/Initial Entry (32 bytes)
30        // - Section Headers and Entries follow (32 bytes each)
31        // We start reading section headers after the first 64 bytes
32        let catalog_byte_offset = (self.catalog_ptr.0 as u64) * 2048 + 64;
33        BootSectionIter {
34            data: &image.data,
35            current_seek: catalog_byte_offset,
36            has_more: true,
37        }
38    }
39
40    /// Returns the default boot entry information.
41    ///
42    /// Returns `None` if the default entry is not valid (malformed boot catalog).
43    pub fn default_entry(&self) -> Option<BootEntryInfo> {
44        let entry = &self.catalog.default_entry;
45        if !entry.is_valid() {
46            return None;
47        }
48        Some(BootEntryInfo {
49            bootable: entry.boot_indicator == 0x88,
50            media_type: entry.boot_media_type,
51            load_segment: entry.load_segment.get(),
52            sector_count: entry.sector_count.get(),
53            load_rba: entry.load_rba.get(),
54        })
55    }
56}
57
58#[derive(Debug, Clone)]
59/// Represents BootEntryInfo.
60pub struct BootEntryInfo {
61    /// Whether the catalog marks this entry bootable.
62    pub bootable: bool,
63    /// El-Torito media/emulation type byte.
64    pub media_type: u8,
65    /// Segment where the boot image is loaded.
66    pub load_segment: u16,
67    /// Number of virtual sectors to load.
68    pub sector_count: u16,
69    /// Starting logical block of the boot image.
70    pub load_rba: u32,
71}
72
73io_transform! {
74
75/// Represents BootSectionIter.
76pub struct BootSectionIter<'data, DATA: Read + Seek> {
77    data: &'data Mutex<IsoCursor<DATA>>,
78    current_seek: u64,
79    has_more: bool,
80}
81
82impl<DATA: Read + Seek> BootSectionIter<'_, DATA> {
83    /// Reads the next boot section header.
84    pub async fn next_section(&mut self) -> io::Result<Option<BootSectionHeaderEntry>> {
85        let mut data = self.data.lock();
86        if !self.has_more {
87            return Ok(None);
88        }
89        data
90            .seek(SeekFrom::Start(self.current_seek))
91            .await
92            .map_err(io::Error::erase)?;
93        let mut header = BootSectionHeaderEntry::zeroed();
94
95        // Limit iterations to prevent infinite loop on malformed data
96        const MAX_ENTRIES_PER_SECTOR: usize = 64; // 2048 / 32 = 64 entries max per sector
97        for _ in 0..MAX_ENTRIES_PER_SECTOR {
98            data.read_exact(bytemuck::bytes_of_mut(&mut header)).await?;
99            self.current_seek += 32; // Each entry is 32 bytes
100
101            match header.header_type {
102                0x00 => {
103                    // Terminator entry - no more sections
104                    self.has_more = false;
105                    return Ok(None);
106                }
107                0x90 => {
108                    // Section header with more sections following
109                    self.has_more = true;
110                    return Ok(Some(header));
111                }
112                0x91 => {
113                    // Final section header
114                    self.has_more = false;
115                    return Ok(Some(header));
116                }
117                // Skip past boot entries (not section headers)
118                _ => continue,
119            }
120        }
121
122        // Exceeded max iterations - malformed boot catalog
123        self.has_more = false;
124        Ok(None)
125    }
126}
127
128} // io_transform!
129
130sync_only! {
131impl<DATA: Read + Seek> Iterator for BootSectionIter<'_, DATA> {
132    type Item = io::Result<BootSectionHeaderEntry>;
133
134    fn next(&mut self) -> Option<Self::Item> {
135        self.next_section().transpose()
136    }
137}
138} // sync_only!