use std::io::{Read, Seek, SeekFrom};
use crate::partition::PartitionEntry;
use crate::Error;
#[derive(Debug, Clone)]
pub struct EbrEntry {
pub ebr_offset: u64,
pub ebr_lba: u64,
pub logical: PartitionEntry,
pub logical_lba_start: u64,
pub slack: [u8; 32],
pub has_slack: bool,
}
#[derive(Debug, Clone)]
pub struct EbrChain {
pub entries: Vec<EbrEntry>,
pub had_cycle: bool,
pub depth_exceeded: bool,
}
const MAX_DEPTH: usize = 64;
pub fn walk_ebr_chain<R: Read + Seek>(
reader: &mut R,
ext_start_lba: u64,
sector_size: u64,
) -> Result<EbrChain, Error> {
let mut entries = Vec::new();
let mut had_cycle = false;
let mut depth_exceeded = false;
let mut visited = std::collections::HashSet::new();
let mut next_ebr_lba = ext_start_lba;
loop {
if entries.len() >= MAX_DEPTH {
depth_exceeded = true;
break;
}
if !visited.insert(next_ebr_lba) {
had_cycle = true;
break;
}
if next_ebr_lba < ext_start_lba {
break;
}
let Some(ebr_byte_offset) = next_ebr_lba.checked_mul(sector_size) else {
break; };
reader.seek(SeekFrom::Start(ebr_byte_offset))?;
let mut sector = [0u8; 512];
if reader.read_exact(&mut sector).is_err() {
break; }
if sector[510] != 0x55 || sector[511] != 0xAA {
break;
}
let logical_raw: &[u8; 16] = sector[446..462].try_into().unwrap();
let next_raw: &[u8; 16] = sector[462..478].try_into().unwrap();
let slack_bytes: [u8; 32] = sector[478..510].try_into().unwrap();
let logical = PartitionEntry::from_bytes(logical_raw);
let next_entry = PartitionEntry::from_bytes(next_raw);
let logical_lba_start = next_ebr_lba.saturating_add(logical.lba_start as u64);
let has_slack = slack_bytes.iter().any(|&b| b != 0);
entries.push(EbrEntry {
ebr_offset: ebr_byte_offset,
ebr_lba: next_ebr_lba,
logical,
logical_lba_start,
slack: slack_bytes,
has_slack,
});
if next_entry.lba_start == 0 {
break;
}
let Some(next_lba) = ext_start_lba.checked_add(next_entry.lba_start as u64) else {
break; };
next_ebr_lba = next_lba;
}
Ok(EbrChain {
entries,
had_cycle,
depth_exceeded,
})
}