use super::{
CharSpec, DescriptorTag, EntityIdentifier, ExtentDescriptor, LongAllocationDescriptor,
TagIdentifier,
};
use crate::error::Result;
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct LogicalVolumeDescriptor {
pub tag: DescriptorTag,
pub vds_number: u32,
pub descriptor_char_set: CharSpec,
pub logical_volume_identifier: [u8; 128],
pub logical_block_size: u32,
pub domain_identifier: EntityIdentifier,
pub logical_volume_contents_use: [u8; 16],
pub map_table_length: u32,
pub num_partition_maps: u32,
pub implementation_identifier: EntityIdentifier,
pub implementation_use: [u8; 128],
pub integrity_sequence_extent: ExtentDescriptor,
pub partition_maps: [u8; 72],
}
unsafe impl bytemuck::Zeroable for LogicalVolumeDescriptor {}
unsafe impl bytemuck::Pod for LogicalVolumeDescriptor {}
impl LogicalVolumeDescriptor {
#[cfg(feature = "alloc")]
pub(crate) fn into_native(mut self) -> Self {
self.tag = DescriptorTag::from_disk_bytes(bytemuck::bytes_of(&self.tag))
.expect("DescriptorTag has its fixed on-disk size");
self.vds_number = self.vds_number.to_le();
self.logical_block_size = self.logical_block_size.to_le();
self.map_table_length = self.map_table_length.to_le();
self.num_partition_maps = self.num_partition_maps.to_le();
self.integrity_sequence_extent = self.integrity_sequence_extent.into_native();
self
}
pub fn validate(&self, location: u32) -> Result<()> {
self.tag
.validate(TagIdentifier::LogicalVolumeDescriptor, location)
}
pub fn file_set_location(&self) -> LongAllocationDescriptor {
(*bytemuck::from_bytes::<LongAllocationDescriptor>(&self.logical_volume_contents_use))
.into_native()
}
#[cfg(feature = "alloc")]
pub fn volume_id(&self) -> alloc::string::String {
super::primary::decode_dstring(&self.logical_volume_identifier)
}
pub fn is_udf_domain(&self) -> bool {
self.domain_identifier.is(b"*OSTA UDF Compliant")
}
pub fn type1_partition_maps(&self) -> impl Iterator<Item = Type1PartitionMap> + '_ {
let maps = &self.partition_maps;
let mut offset = 0usize;
let mut remaining = self.num_partition_maps as usize;
core::iter::from_fn(move || {
while remaining > 0 {
remaining -= 1;
if offset + 2 > maps.len() {
return None;
}
let map_type = maps[offset];
let map_len = maps[offset + 1] as usize;
if map_len < 2 || offset + map_len > maps.len() {
return None;
}
let entry = &maps[offset..offset + map_len];
offset += map_len;
if map_type == 1 && map_len >= core::mem::size_of::<Type1PartitionMap>() {
let mut map: Type1PartitionMap =
*bytemuck::from_bytes(&entry[..core::mem::size_of::<Type1PartitionMap>()]);
map.volume_sequence_number = map.volume_sequence_number.to_le();
map.partition_number = map.partition_number.to_le();
return Some(map);
}
}
None
})
}
}
#[repr(C)]
#[derive(Debug, Clone, Copy, bytemuck::Zeroable, bytemuck::Pod)]
pub struct Type1PartitionMap {
pub partition_map_type: u8,
pub partition_map_length: u8,
pub volume_sequence_number: u16,
pub partition_number: u16,
}
#[cfg(test)]
mod tests {
use super::*;
static_assertions::const_assert_eq!(size_of::<LogicalVolumeDescriptor>(), 512);
static_assertions::const_assert_eq!(size_of::<Type1PartitionMap>(), 6);
#[test]
fn type1_partition_maps_parses_embedded_table() {
let mut lvd = LogicalVolumeDescriptor {
tag: bytemuck::Zeroable::zeroed(),
vds_number: 0,
descriptor_char_set: CharSpec::default(),
logical_volume_identifier: [0; 128],
logical_block_size: 2048,
domain_identifier: EntityIdentifier::EMPTY,
logical_volume_contents_use: [0; 16],
map_table_length: 6,
num_partition_maps: 1,
implementation_identifier: EntityIdentifier::EMPTY,
implementation_use: [0; 128],
integrity_sequence_extent: ExtentDescriptor::default(),
partition_maps: [0; 72],
};
let map = Type1PartitionMap {
partition_map_type: 1,
partition_map_length: 6,
volume_sequence_number: 1,
partition_number: 0,
};
lvd.partition_maps[..6].copy_from_slice(bytemuck::bytes_of(&map));
let mut parsed = lvd.type1_partition_maps();
let first = parsed.next().expect("type 1 map");
assert!(parsed.next().is_none());
assert_eq!(first.partition_map_type, 1);
assert_eq!(first.partition_map_length, 6);
assert_eq!(first.volume_sequence_number, 1);
assert_eq!(first.partition_number, 0);
}
}