use crate::Result;
use super::directory::DirRecord;
#[derive(Debug, Clone)]
pub enum VolumeDescriptor {
Primary(PrimaryVolumeDescriptor),
Supplementary(SupplementaryVolumeDescriptor),
Boot { catalog_lba: u32 },
Partition,
Terminator,
}
impl VolumeDescriptor {
pub fn probe(sector: &[u8]) -> Result<Self> {
if sector.len() < super::SECTOR_SIZE as usize {
return Err(crate::Error::InvalidImage(
"iso9660: short volume descriptor sector".into(),
));
}
let kind = sector[0];
if §or[1..6] != super::ISO_MAGIC {
return Err(crate::Error::InvalidImage(
"iso9660: CD001 magic missing in descriptor".into(),
));
}
match kind {
0 => {
let catalog_lba = u32::from_le_bytes(sector[0x47..0x4B].try_into().unwrap());
Ok(Self::Boot { catalog_lba })
}
1 => Ok(Self::Primary(PrimaryVolumeDescriptor::decode(sector)?)),
2 => Ok(Self::Supplementary(SupplementaryVolumeDescriptor::decode(
sector,
)?)),
3 => Ok(Self::Partition),
255 => Ok(Self::Terminator),
other => Err(crate::Error::InvalidImage(format!(
"iso9660: unknown volume descriptor type {other}"
))),
}
}
}
#[derive(Debug, Clone)]
pub struct PrimaryVolumeDescriptor {
pub system_id: String,
pub volume_id: String,
pub volume_space_size: u32,
pub logical_block_size: u16,
pub path_table_size: u32,
pub l_path_table_lba: u32,
pub m_path_table_lba: u32,
pub root: DirRecord,
}
impl PrimaryVolumeDescriptor {
fn decode(sector: &[u8]) -> Result<Self> {
let system_id = trim_strd(§or[8..40]);
let volume_id = trim_strd(§or[40..72]);
let volume_space_size = decode_both_endian_u32(§or[80..88], "volume_space_size")?;
let logical_block_size = decode_both_endian_u16(§or[128..132], "logical_block_size")?;
let path_table_size = decode_both_endian_u32(§or[132..140], "path_table_size")?;
let l_path_table_lba = u32::from_le_bytes(sector[140..144].try_into().unwrap());
let m_path_table_lba = u32::from_be_bytes(sector[148..152].try_into().unwrap());
let root = DirRecord::decode(§or[156..156 + 34])?;
Ok(Self {
system_id,
volume_id,
volume_space_size,
logical_block_size,
path_table_size,
l_path_table_lba,
m_path_table_lba,
root,
})
}
}
#[derive(Debug, Clone)]
pub struct SupplementaryVolumeDescriptor {
pub escape_sequences: [u8; 32],
pub volume_id: String,
pub volume_space_size: u32,
pub logical_block_size: u16,
pub path_table_size: u32,
pub l_path_table_lba: u32,
pub m_path_table_lba: u32,
pub root: DirRecord,
}
impl SupplementaryVolumeDescriptor {
fn decode(sector: &[u8]) -> Result<Self> {
let volume_id = super::joliet::ucs2_be_to_string(§or[40..72]);
let mut escape_sequences = [0u8; 32];
escape_sequences.copy_from_slice(§or[88..120]);
let volume_space_size = decode_both_endian_u32(§or[80..88], "volume_space_size")?;
let logical_block_size = decode_both_endian_u16(§or[128..132], "logical_block_size")?;
let path_table_size = decode_both_endian_u32(§or[132..140], "path_table_size")?;
let l_path_table_lba = u32::from_le_bytes(sector[140..144].try_into().unwrap());
let m_path_table_lba = u32::from_be_bytes(sector[148..152].try_into().unwrap());
let root = DirRecord::decode(§or[156..156 + 34])?;
Ok(Self {
escape_sequences,
volume_id,
volume_space_size,
logical_block_size,
path_table_size,
l_path_table_lba,
m_path_table_lba,
root,
})
}
pub fn is_joliet(&self) -> bool {
let seq = &self.escape_sequences;
seq.starts_with(b"%/@") || seq.starts_with(b"%/C") || seq.starts_with(b"%/E")
}
}
pub(crate) fn decode_both_endian_u32(buf: &[u8], label: &str) -> Result<u32> {
if buf.len() < 8 {
return Err(crate::Error::InvalidImage(format!(
"iso9660: short both-endian u32 for {label}"
)));
}
let le = u32::from_le_bytes(buf[0..4].try_into().unwrap());
let be = u32::from_be_bytes(buf[4..8].try_into().unwrap());
if le != be {
return Err(crate::Error::InvalidImage(format!(
"iso9660: both-endian {label} mismatch ({le} != {be})"
)));
}
Ok(le)
}
pub(crate) fn decode_both_endian_u16(buf: &[u8], label: &str) -> Result<u16> {
if buf.len() < 4 {
return Err(crate::Error::InvalidImage(format!(
"iso9660: short both-endian u16 for {label}"
)));
}
let le = u16::from_le_bytes(buf[0..2].try_into().unwrap());
let be = u16::from_be_bytes(buf[2..4].try_into().unwrap());
if le != be {
return Err(crate::Error::InvalidImage(format!(
"iso9660: both-endian {label} mismatch ({le} != {be})"
)));
}
Ok(le)
}
fn trim_strd(buf: &[u8]) -> String {
let end = buf
.iter()
.rposition(|&b| b != b' ' && b != 0)
.map(|p| p + 1)
.unwrap_or(0);
String::from_utf8_lossy(&buf[..end]).into_owned()
}