#[cfg(feature = "std")]
use super::io::Parsable;
use super::io::{self, Read, Write};
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
use core::{ffi::CStr, fmt::Debug};
use super::directory::RootDirectoryEntry;
use crate::types::{
BigEndian, Charset, DecDateTime, Endian, IsoStr, IsoStrA, IsoStrD, LittleEndian, U16LsbMsb,
U32, U32LsbMsb,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum VolumeError {
Io,
InvalidHeader,
PrimaryNotFound,
}
impl core::fmt::Display for VolumeError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::Io => write!(f, "I/O error"),
Self::InvalidHeader => write!(
f,
"invalid volume descriptor header (missing CD001 signature)"
),
Self::PrimaryNotFound => write!(f, "primary volume descriptor not found"),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for VolumeError {}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum VolumeDescriptorType {
BootRecord,
PrimaryVolumeDescriptor,
SupplementaryVolumeDescriptor,
VolumePartitionDescriptor,
VolumeSetTerminator,
Unknown(u8),
}
impl VolumeDescriptorType {
pub fn to_u8(self) -> u8 {
match self {
Self::BootRecord => 0x00,
Self::PrimaryVolumeDescriptor => 0x01,
Self::SupplementaryVolumeDescriptor => 0x02,
Self::VolumePartitionDescriptor => 0x03,
Self::VolumeSetTerminator => 0xFF,
Self::Unknown(value) => value,
}
}
pub fn from_u8(value: u8) -> Self {
match value {
0x00 => Self::BootRecord,
0x01 => Self::PrimaryVolumeDescriptor,
0x02 => Self::SupplementaryVolumeDescriptor,
0x03 => Self::VolumePartitionDescriptor,
0xFF => Self::VolumeSetTerminator,
value => Self::Unknown(value),
}
}
}
#[derive(Debug, Clone, Copy)]
pub enum VolumeDescriptor {
BootRecord(BootRecordVolumeDescriptor),
Primary(PrimaryVolumeDescriptor),
Supplementary(SupplementaryVolumeDescriptor),
End(VolumeDescriptorSetTerminator),
Unknown(UnknownVolumeDescriptor),
}
io_transform! {
#[cfg(feature = "std")]
impl Parsable for VolumeDescriptor {
async fn parse<R: Read>(reader: &mut R) -> io::Result<Self> {
let mut buf = [0u8; 2048];
reader.read_exact(&mut buf).await?;
let header = VolumeDescriptorHeader::from_bytes(&buf[0..7]);
if !header.is_valid() {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"Invalid volume descriptor header (missing CD001 signature)",
));
}
Ok(VolumeDescriptor::new(buf))
}
}
}
impl VolumeDescriptor {
pub fn as_bytes(&self) -> &[u8] {
match self {
VolumeDescriptor::BootRecord(entry) => bytemuck::bytes_of(entry),
VolumeDescriptor::Primary(entry) => bytemuck::bytes_of(entry),
VolumeDescriptor::Supplementary(entry) => bytemuck::bytes_of(entry),
VolumeDescriptor::End(entry) => bytemuck::bytes_of(entry),
VolumeDescriptor::Unknown(entry) => bytemuck::bytes_of(entry),
}
}
pub fn header(&self) -> VolumeDescriptorHeader {
match self {
VolumeDescriptor::BootRecord(entry) => entry.header,
VolumeDescriptor::Primary(entry) => entry.header,
VolumeDescriptor::Supplementary(entry) => entry.header,
VolumeDescriptor::End(entry) => entry.header,
VolumeDescriptor::Unknown(entry) => entry.header,
}
}
pub fn new(data: [u8; 2048]) -> Self {
let ty = VolumeDescriptorType::from_u8(data[0]);
match ty {
VolumeDescriptorType::BootRecord => VolumeDescriptor::BootRecord(bytemuck::cast(data)),
VolumeDescriptorType::PrimaryVolumeDescriptor => {
VolumeDescriptor::Primary(bytemuck::cast(data))
}
VolumeDescriptorType::SupplementaryVolumeDescriptor => {
VolumeDescriptor::Supplementary(bytemuck::cast(data))
}
VolumeDescriptorType::VolumeSetTerminator => {
VolumeDescriptor::End(bytemuck::cast(data))
}
_ => VolumeDescriptor::Unknown(bytemuck::cast(data)),
}
}
}
#[cfg(feature = "alloc")]
#[derive(Debug, Clone)]
pub struct VolumeDescriptorList {
pub descriptors: Vec<VolumeDescriptor>,
}
#[cfg(feature = "alloc")]
impl VolumeDescriptorList {
pub fn empty() -> Self {
Self {
descriptors: Vec::new(),
}
}
pub fn primary(&self) -> &PrimaryVolumeDescriptor {
self.descriptors
.iter()
.find_map(|d| match d {
VolumeDescriptor::Primary(d) => Some(d),
_ => None,
})
.expect("Primary volume descriptor not found")
}
pub fn primary_mut(&mut self) -> &mut PrimaryVolumeDescriptor {
self.descriptors
.iter_mut()
.find_map(|d| match d {
VolumeDescriptor::Primary(d) => Some(d),
_ => None,
})
.expect("Primary volume descriptor not found")
}
pub fn supplementary(&self) -> impl Iterator<Item = &SupplementaryVolumeDescriptor> {
self.descriptors.iter().filter_map(|d| match d {
VolumeDescriptor::Supplementary(d) => Some(d),
_ => None,
})
}
pub fn supplementary_mut(
&mut self,
) -> impl Iterator<Item = &mut SupplementaryVolumeDescriptor> {
self.descriptors.iter_mut().filter_map(|d| match d {
VolumeDescriptor::Supplementary(d) => Some(d),
_ => None,
})
}
pub fn boot_record(&self) -> Option<&BootRecordVolumeDescriptor> {
self.descriptors.iter().find_map(|d| match d {
VolumeDescriptor::BootRecord(d) => Some(d),
_ => None,
})
}
pub fn boot_record_mut(&mut self) -> Option<&mut BootRecordVolumeDescriptor> {
self.descriptors.iter_mut().find_map(|d| match d {
VolumeDescriptor::BootRecord(d) => Some(d),
_ => None,
})
}
pub fn push(&mut self, descriptor: VolumeDescriptor) {
self.descriptors.push(descriptor);
}
pub fn insert(&mut self, index: usize, descriptor: VolumeDescriptor) {
self.descriptors.insert(index, descriptor);
}
pub fn size_required(&self) -> usize {
(self.descriptors.len() + 1) * 2048
}
}
io_transform! {
#[cfg(feature = "alloc")]
impl VolumeDescriptorList {
pub async fn parse<T: Read>(reader: &mut T) -> Result<Self, io::Error> {
let mut descriptors = Vec::new();
let mut buffer = [0u8; 2048];
loop {
reader.read_exact(&mut buffer).await?;
let header = VolumeDescriptorHeader::from_bytes(&buffer[0..7]);
let ty = VolumeDescriptorType::from_u8(header.descriptor_type);
if let VolumeDescriptorType::VolumeSetTerminator = ty {
break;
}
if !header.is_valid() {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"Invalid volume descriptor header (missing CD001 signature)",
));
}
descriptors.push(VolumeDescriptor::new(buffer));
}
Ok(Self { descriptors })
}
pub async fn write<W: Write>(&self, writer: &mut W) -> io::Result<usize> {
let mut written = 0;
for descriptor in &self.descriptors {
writer.write_all(descriptor.as_bytes()).await?;
written += 2048;
}
writer.write_all(VolumeDescriptorSetTerminator::new().to_bytes()).await?;
written += 2048;
Ok(written)
}
}
}
#[repr(C)]
#[derive(Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
pub struct VolumeDescriptorHeader {
pub descriptor_type: u8,
pub standard_identifier: IsoStrA<5>,
pub version: u8,
}
impl Debug for VolumeDescriptorHeader {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("VolumeDescriptorHeader")
.field(
"descriptor_type",
&VolumeDescriptorType::from_u8(self.descriptor_type),
)
.field("standard_identifier", &self.standard_identifier)
.field("version", &self.version)
.finish()
}
}
impl VolumeDescriptorHeader {
const IDENTIFIER: IsoStrA<5> = IsoStrA::from_bytes_exact(*b"CD001");
pub fn new(ty: VolumeDescriptorType) -> Self {
Self {
descriptor_type: ty.to_u8(),
standard_identifier: Self::IDENTIFIER,
version: 1,
}
}
pub fn is_valid(&self) -> bool {
self.standard_identifier == Self::IDENTIFIER
}
pub fn from_bytes(bytes: &[u8]) -> &Self {
bytemuck::from_bytes(bytes)
}
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct UnknownVolumeDescriptor {
pub header: VolumeDescriptorHeader,
pub data: [u8; 2041],
}
impl Debug for UnknownVolumeDescriptor {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("UnknownVolumeDescriptor")
.field("header", &self.header)
.finish_non_exhaustive()
}
}
unsafe impl bytemuck::Zeroable for UnknownVolumeDescriptor {}
unsafe impl bytemuck::Pod for UnknownVolumeDescriptor {}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct PrimaryVolumeDescriptor {
pub header: VolumeDescriptorHeader,
pub unused0: u8,
pub system_identifier: IsoStrA<32>,
pub volume_identifier: IsoStrD<32>,
pub unused1: [u8; 8],
pub volume_space_size: U32LsbMsb,
pub unused2: [u8; 32],
pub volume_set_size: U16LsbMsb,
pub volume_sequence_number: U16LsbMsb,
pub logical_block_size: U16LsbMsb,
pub path_table_size: U32LsbMsb,
pub type_l_path_table: U32<LittleEndian>,
pub opt_type_l_path_table: U32<LittleEndian>,
pub type_m_path_table: U32<BigEndian>,
pub opt_type_m_path_table: U32<BigEndian>,
pub dir_record: RootDirectoryEntry,
pub volume_set_identifier: IsoStrD<128>,
pub publisher_identifier: IsoStrA<128>,
pub preparer_identifier: IsoStrA<128>,
pub application_identifier: IsoStrA<128>,
pub copyright_file_identifier: IsoStrD<37>,
pub abstract_file_identifier: IsoStrD<37>,
pub bibliographic_file_identifier: IsoStrD<37>,
pub creation_date: DecDateTime,
pub modification_date: DecDateTime,
pub expiration_date: DecDateTime,
pub effective_date: DecDateTime,
pub file_structure_version: u8,
pub unused3: u8,
pub app_data: [u8; 512],
pub reserved: [u8; 653],
}
impl Debug for PrimaryVolumeDescriptor {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("PrimaryVolumeDescriptor")
.field("header", &self.header)
.field("system_identifier", &self.system_identifier)
.field("volume_identifier", &self.volume_identifier)
.field("volume_space_size", &self.volume_space_size)
.field("volume_set_size", &self.volume_set_size)
.field("volume_sequence_number", &self.volume_sequence_number)
.field("logical_block_size", &self.logical_block_size)
.field("path_table_size", &self.path_table_size)
.field("type_l_path_table", &self.type_l_path_table)
.field("opt_type_l_path_table", &self.opt_type_l_path_table)
.field("type_m_path_table", &self.type_m_path_table)
.field("opt_type_m_path_table", &self.opt_type_m_path_table)
.field("dir_record", &self.dir_record)
.field("volume_set_identifier", &self.volume_set_identifier)
.field("publisher_identifier", &self.publisher_identifier)
.field("preparer_identifier", &self.preparer_identifier)
.field("application_identifier", &self.application_identifier)
.field("copyright_file_identifier", &self.copyright_file_identifier)
.field("abstract_file_identifier", &self.abstract_file_identifier)
.field(
"bibliographic_file_identifier",
&self.bibliographic_file_identifier,
)
.field("creation_date", &self.creation_date)
.field("modification_date", &self.modification_date)
.field("expiration_date", &self.expiration_date)
.field("effective_date", &self.effective_date)
.field("file_structure_version", &self.file_structure_version)
.finish_non_exhaustive()
}
}
impl PrimaryVolumeDescriptor {
pub fn new(name: &str, sectors: u32) -> Self {
Self {
header: VolumeDescriptorHeader {
descriptor_type: VolumeDescriptorType::PrimaryVolumeDescriptor.to_u8(),
standard_identifier: IsoStrA::from_str("CD001").unwrap(),
version: 1,
},
unused0: 0,
system_identifier: IsoStrA::empty(),
volume_identifier: IsoStrD::from_str_lossy(name).unwrap(),
unused1: [0; 8],
volume_space_size: U32LsbMsb::new(sectors),
unused2: [0; 32],
volume_set_size: U16LsbMsb::new(1),
volume_sequence_number: U16LsbMsb::new(1),
logical_block_size: U16LsbMsb::new(2048),
path_table_size: U32LsbMsb::new(0),
type_l_path_table: U32::<LittleEndian>::new(0),
opt_type_l_path_table: U32::<LittleEndian>::new(0),
type_m_path_table: U32::<BigEndian>::new(0),
opt_type_m_path_table: U32::<BigEndian>::new(0),
dir_record: RootDirectoryEntry::default(),
volume_set_identifier: IsoStrD::empty(),
publisher_identifier: IsoStrA::empty(),
preparer_identifier: IsoStrA::empty(),
application_identifier: IsoStrA::from_str("HADRIS-ISO").unwrap(),
copyright_file_identifier: IsoStrD::empty(),
abstract_file_identifier: IsoStrD::empty(),
bibliographic_file_identifier: IsoStrD::empty(),
creation_date: DecDateTime::now(),
modification_date: DecDateTime::now(),
expiration_date: DecDateTime::now(),
effective_date: DecDateTime::now(),
file_structure_version: 1,
unused3: 0,
app_data: [0; 512],
reserved: [0; 653],
}
}
}
unsafe impl bytemuck::Zeroable for PrimaryVolumeDescriptor {}
unsafe impl bytemuck::Pod for PrimaryVolumeDescriptor {}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct BootRecordVolumeDescriptor {
pub header: VolumeDescriptorHeader,
pub boot_system_identifier: [u8; 32],
pub unused0: [u8; 32],
pub catalog_ptr: U32<LittleEndian>,
pub unused1: [u8; 1973],
}
impl BootRecordVolumeDescriptor {
pub fn new(catalog_sector: u32) -> Self {
const BOOT_SYSTEM_IDENTIFIER: [u8; 32] = *b"EL TORITO SPECIFICATION\0\0\0\0\0\0\0\0\0";
Self {
header: VolumeDescriptorHeader::new(VolumeDescriptorType::BootRecord),
boot_system_identifier: BOOT_SYSTEM_IDENTIFIER,
unused0: [0; 32],
catalog_ptr: U32::<LittleEndian>::new(catalog_sector),
unused1: [0; 1973],
}
}
}
impl Debug for BootRecordVolumeDescriptor {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let system_identifier = CStr::from_bytes_until_nul(&self.boot_system_identifier);
f.debug_struct("BootRecordVolumeDescriptor")
.field("header", &self.header)
.field("boot_system_identifier", &system_identifier)
.field("catalog_ptr", &self.catalog_ptr)
.finish_non_exhaustive()
}
}
unsafe impl bytemuck::Zeroable for BootRecordVolumeDescriptor {}
unsafe impl bytemuck::Pod for BootRecordVolumeDescriptor {}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct SupplementaryVolumeDescriptor {
pub header: VolumeDescriptorHeader,
pub flags: u8,
pub system_identifier: IsoStrA<32>,
pub volume_identifier: IsoStrD<32>,
pub unused1: [u8; 8],
pub volume_space_size: U32LsbMsb,
pub escape_sequences: [u8; 32],
pub volume_set_size: U16LsbMsb,
pub volume_sequence_number: U16LsbMsb,
pub logical_block_size: U16LsbMsb,
pub path_table_size: U32LsbMsb,
pub type_l_path_table: U32<LittleEndian>,
pub opt_type_l_path_table: U32<LittleEndian>,
pub type_m_path_table: U32<BigEndian>,
pub opt_type_m_path_table: U32<BigEndian>,
pub dir_record: RootDirectoryEntry,
pub volume_set_identifier: IsoStrD<128>,
pub publisher_identifier: IsoStrA<128>,
pub preparer_identifier: IsoStrA<128>,
pub application_identifier: IsoStrA<128>,
pub copyright_file_identifier: IsoStrD<37>,
pub abstract_file_identifier: IsoStrD<37>,
pub bibliographic_file_identifier: IsoStrD<37>,
pub creation_date: DecDateTime,
pub modification_date: DecDateTime,
pub expiration_date: DecDateTime,
pub effective_date: DecDateTime,
pub file_structure_version: u8,
pub unused3: u8,
pub app_data: [u8; 512],
pub reserved: [u8; 653],
}
impl SupplementaryVolumeDescriptor {
pub fn utf16be_str<C: Charset, const N: usize>(s: &str) -> IsoStr<C, N> {
let mut bytes = [0u8; N];
let paired = N & !1; for i in (0..paired).step_by(2) {
bytes[i] = 0x00;
bytes[i + 1] = 0x20;
}
let mut pos = 0;
for c in s.encode_utf16() {
if pos + 2 > paired {
break;
}
let be = c.to_be_bytes();
bytes[pos] = be[0];
bytes[pos + 1] = be[1];
pos += 2;
}
IsoStr::from_bytes_exact(bytes)
}
pub fn utf16be_empty<C: Charset, const N: usize>() -> IsoStr<C, N> {
let mut bytes = [0u8; N];
let paired = N & !1;
for i in (0..paired).step_by(2) {
bytes[i] = 0x00;
bytes[i + 1] = 0x20;
}
IsoStr::from_bytes_exact(bytes)
}
pub fn new_svd(name: &str, sectors: u32, escape_sequences: [u8; 32]) -> Self {
Self {
header: VolumeDescriptorHeader {
descriptor_type: VolumeDescriptorType::SupplementaryVolumeDescriptor.to_u8(),
standard_identifier: IsoStrA::from_str("CD001").unwrap(),
version: 1,
},
flags: 0,
system_identifier: Self::utf16be_empty(),
volume_identifier: Self::utf16be_str(name),
unused1: [0; 8],
volume_space_size: U32LsbMsb::new(sectors),
escape_sequences,
volume_set_size: U16LsbMsb::new(1),
volume_sequence_number: U16LsbMsb::new(1),
logical_block_size: U16LsbMsb::new(2048),
path_table_size: U32LsbMsb::new(0),
type_l_path_table: U32::<LittleEndian>::new(0),
opt_type_l_path_table: U32::<LittleEndian>::new(0),
type_m_path_table: U32::<BigEndian>::new(0),
opt_type_m_path_table: U32::<BigEndian>::new(0),
dir_record: RootDirectoryEntry::default(),
volume_set_identifier: Self::utf16be_empty(),
publisher_identifier: Self::utf16be_empty(),
preparer_identifier: Self::utf16be_empty(),
application_identifier: Self::utf16be_str("HADRIS-ISO"),
copyright_file_identifier: Self::utf16be_empty(),
abstract_file_identifier: Self::utf16be_empty(),
bibliographic_file_identifier: Self::utf16be_empty(),
creation_date: DecDateTime::now(),
modification_date: DecDateTime::now(),
expiration_date: DecDateTime::now(),
effective_date: DecDateTime::now(),
file_structure_version: 1,
unused3: 0,
app_data: [0; 512],
reserved: [0; 653],
}
}
pub fn new_evd(name: &str, sectors: u32) -> Self {
Self {
header: VolumeDescriptorHeader {
descriptor_type: VolumeDescriptorType::SupplementaryVolumeDescriptor.to_u8(),
standard_identifier: IsoStrA::from_str("CD001").unwrap(),
version: 2,
},
flags: 0,
system_identifier: IsoStrA::empty(),
volume_identifier: IsoStrD::from_str_lossy(name).unwrap(),
unused1: [0; 8],
volume_space_size: U32LsbMsb::new(sectors),
escape_sequences: [b' '; 32],
volume_set_size: U16LsbMsb::new(1),
volume_sequence_number: U16LsbMsb::new(1),
logical_block_size: U16LsbMsb::new(2048),
path_table_size: U32LsbMsb::new(0),
type_l_path_table: U32::<LittleEndian>::new(0),
opt_type_l_path_table: U32::<LittleEndian>::new(0),
type_m_path_table: U32::<BigEndian>::new(0),
opt_type_m_path_table: U32::<BigEndian>::new(0),
dir_record: RootDirectoryEntry::default(),
volume_set_identifier: IsoStrD::empty(),
publisher_identifier: IsoStrA::empty(),
preparer_identifier: IsoStrA::empty(),
application_identifier: IsoStrA::from_str("HADRIS-ISO").unwrap(),
copyright_file_identifier: IsoStrD::empty(),
abstract_file_identifier: IsoStrD::empty(),
bibliographic_file_identifier: IsoStrD::empty(),
creation_date: DecDateTime::now(),
modification_date: DecDateTime::now(),
expiration_date: DecDateTime::now(),
effective_date: DecDateTime::now(),
file_structure_version: 2,
unused3: 0,
app_data: [0; 512],
reserved: [0; 653],
}
}
}
unsafe impl bytemuck::Zeroable for SupplementaryVolumeDescriptor {}
unsafe impl bytemuck::Pod for SupplementaryVolumeDescriptor {}
impl Debug for SupplementaryVolumeDescriptor {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("SupplementaryVolumeDescriptor")
.field("header", &self.header)
.field("flags", &self.flags)
.field("system_identifier", &self.system_identifier)
.field("volume_identifier", &self.volume_identifier)
.field("volume_space_size", &self.volume_space_size)
.field("escape_sequences", &self.escape_sequences)
.field("volume_set_size", &self.volume_set_size)
.field("volume_sequence_number", &self.volume_sequence_number)
.field("logical_block_size", &self.logical_block_size)
.field("path_table_size", &self.path_table_size)
.field("type_l_path_table", &self.type_l_path_table)
.field("opt_type_l_path_table", &self.opt_type_l_path_table)
.field("type_m_path_table", &self.type_m_path_table)
.field("opt_type_m_path_table", &self.opt_type_m_path_table)
.field("dir_record", &self.dir_record)
.field("volume_set_identifier", &self.volume_set_identifier)
.field("publisher_identifier", &self.publisher_identifier)
.field("preparer_identifier", &self.preparer_identifier)
.field("application_identifier", &self.application_identifier)
.field("copyright_file_identifier", &self.copyright_file_identifier)
.field("abstract_file_identifier", &self.abstract_file_identifier)
.field(
"bibliographic_file_identifier",
&self.bibliographic_file_identifier,
)
.field("creation_date", &self.creation_date)
.field("modification_date", &self.modification_date)
.field("expiration_date", &self.expiration_date)
.field("effective_date", &self.effective_date)
.field("file_structure_version", &self.file_structure_version)
.finish_non_exhaustive()
}
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct VolumeDescriptorSetTerminator {
header: VolumeDescriptorHeader,
padding: [u8; 2041],
}
impl Default for VolumeDescriptorSetTerminator {
fn default() -> Self {
Self::new()
}
}
impl VolumeDescriptorSetTerminator {
pub fn new() -> Self {
Self {
header: VolumeDescriptorHeader {
descriptor_type: VolumeDescriptorType::VolumeSetTerminator.to_u8(),
standard_identifier: VolumeDescriptorHeader::IDENTIFIER,
version: 1,
},
padding: [0; 2041],
}
}
pub fn to_bytes(&self) -> &[u8] {
bytemuck::bytes_of(self)
}
}
impl Debug for VolumeDescriptorSetTerminator {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("VolumeDescriptorSetTerminator")
.field("header", &self.header)
.finish_non_exhaustive()
}
}
unsafe impl bytemuck::Zeroable for VolumeDescriptorSetTerminator {}
unsafe impl bytemuck::Pod for VolumeDescriptorSetTerminator {}
#[cfg(test)]
mod tests {
use super::*;
static_assertions::assert_eq_size!(PrimaryVolumeDescriptor, [u8; 2048]);
static_assertions::assert_eq_size!(VolumeDescriptorSetTerminator, [u8; 2048]);
static_assertions::assert_eq_size!(BootRecordVolumeDescriptor, [u8; 2048]);
static_assertions::assert_eq_size!(UnknownVolumeDescriptor, [u8; 2048]);
static_assertions::assert_eq_align!(PrimaryVolumeDescriptor, u8);
static_assertions::assert_eq_align!(VolumeDescriptorSetTerminator, u8);
static_assertions::assert_eq_align!(BootRecordVolumeDescriptor, u8);
}