mod anchor;
mod fileset;
mod logical;
mod partition;
mod primary;
mod tag;
pub use anchor::AnchorVolumeDescriptorPointer;
pub use fileset::FileSetDescriptor;
pub use logical::LogicalVolumeDescriptor;
pub use partition::{PartitionContents, PartitionDescriptor};
pub use primary::PrimaryVolumeDescriptor;
pub use tag::{DescriptorTag, TagIdentifier};
use super::super::{Read, Seek, SeekFrom};
use crate::error::{UdfError, UdfResult};
#[repr(C)]
#[derive(Debug, Clone, Copy, Default, bytemuck::Zeroable, bytemuck::Pod)]
pub struct ExtentDescriptor {
pub length: u32,
pub location: u32,
}
impl ExtentDescriptor {
pub fn is_empty(&self) -> bool {
self.length == 0
}
}
#[repr(C)]
#[derive(Debug, Clone, Copy, Default)]
pub struct LongAllocationDescriptor {
pub extent_length: u32,
pub logical_block_num: u32,
pub partition_ref_num: u16,
pub impl_use: [u8; 6],
}
unsafe impl bytemuck::Zeroable for LongAllocationDescriptor {}
unsafe impl bytemuck::Pod for LongAllocationDescriptor {}
impl LongAllocationDescriptor {
pub fn length(&self) -> u32 {
self.extent_length & 0x3FFFFFFF
}
pub fn extent_type(&self) -> ExtentType {
ExtentType::from_bits((self.extent_length >> 30) as u8)
}
pub fn is_recorded(&self) -> bool {
matches!(self.extent_type(), ExtentType::RecordedAllocated)
}
pub fn extent_location(&self) -> LbAddr {
LbAddr {
logical_block_num: self.logical_block_num,
partition_ref_num: self.partition_ref_num,
}
}
}
#[repr(C)]
#[derive(Debug, Clone, Copy, Default, bytemuck::Zeroable, bytemuck::Pod)]
pub struct ShortAllocationDescriptor {
pub extent_length: u32,
pub extent_position: u32,
}
impl ShortAllocationDescriptor {
pub fn length(&self) -> u32 {
self.extent_length & 0x3FFFFFFF
}
pub fn extent_type(&self) -> ExtentType {
ExtentType::from_bits((self.extent_length >> 30) as u8)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ExtentType {
RecordedAllocated,
AllocatedNotRecorded,
NotAllocatedNotRecorded,
NextExtent,
}
impl ExtentType {
fn from_bits(bits: u8) -> Self {
match bits & 0x03 {
0 => Self::RecordedAllocated,
1 => Self::AllocatedNotRecorded,
2 => Self::NotAllocatedNotRecorded,
3 => Self::NextExtent,
_ => unreachable!(),
}
}
}
#[repr(C)]
#[derive(Debug, Clone, Copy, Default)]
pub struct LbAddr {
pub logical_block_num: u32,
pub partition_ref_num: u16,
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct EntityIdentifier {
pub flags: u8,
pub identifier: [u8; 23],
pub suffix: [u8; 8],
}
impl EntityIdentifier {
pub const EMPTY: Self = Self {
flags: 0,
identifier: [0; 23],
suffix: [0; 8],
};
#[cfg(feature = "alloc")]
pub fn as_str(&self) -> alloc::string::String {
let end = self.identifier.iter().position(|&b| b == 0).unwrap_or(23);
alloc::string::String::from_utf8_lossy(&self.identifier[..end]).into_owned()
}
pub fn is(&self, id: &[u8]) -> bool {
let end = self.identifier.iter().position(|&b| b == 0).unwrap_or(23);
&self.identifier[..end] == id
}
}
impl Default for EntityIdentifier {
fn default() -> Self {
Self::EMPTY
}
}
unsafe impl bytemuck::Zeroable for EntityIdentifier {}
unsafe impl bytemuck::Pod for EntityIdentifier {}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct CharSpec {
pub char_set_type: u8,
pub char_set_info: [u8; 63],
}
impl CharSpec {
pub const OSTA_COMPRESSED_UNICODE: Self = Self {
char_set_type: 0,
char_set_info: [0; 63],
};
}
impl Default for CharSpec {
fn default() -> Self {
Self::OSTA_COMPRESSED_UNICODE
}
}
unsafe impl bytemuck::Zeroable for CharSpec {}
unsafe impl bytemuck::Pod for CharSpec {}
pub mod vrs {
pub const BEA01: &[u8; 5] = b"BEA01";
pub const NSR02: &[u8; 5] = b"NSR02";
pub const NSR03: &[u8; 5] = b"NSR03";
pub const TEA01: &[u8; 5] = b"TEA01";
pub const CD001: &[u8; 5] = b"CD001";
}
io_transform! {
pub async fn parse_vrs<R: Read + Seek>(reader: &mut R) -> UdfResult<VrsType> {
reader.seek(SeekFrom::Start(16 * 2048)).await?;
let mut buffer = [0u8; 2048];
let mut found_bea = false;
let mut found_nsr = None;
for _ in 0..16 {
reader.read_exact(&mut buffer).await?;
if buffer[0] != 0 || buffer[6] != 1 {
continue;
}
let id = &buffer[1..6];
match id {
b"BEA01" => found_bea = true,
b"NSR02" if found_bea => found_nsr = Some(VrsType::Nsr02),
b"NSR03" if found_bea => found_nsr = Some(VrsType::Nsr03),
b"TEA01" if found_nsr.is_some() => return Ok(found_nsr.unwrap()),
b"CD001" => continue, _ => continue,
}
}
match found_nsr {
Some(nsr) => Ok(nsr),
None => Err(UdfError::InvalidVrs),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum VrsType {
Nsr02,
Nsr03,
}
#[cfg(test)]
mod tests {
use super::*;
static_assertions::const_assert_eq!(size_of::<ExtentDescriptor>(), 8);
static_assertions::const_assert_eq!(size_of::<LongAllocationDescriptor>(), 16);
static_assertions::const_assert_eq!(size_of::<ShortAllocationDescriptor>(), 8);
static_assertions::const_assert_eq!(size_of::<EntityIdentifier>(), 32);
static_assertions::const_assert_eq!(size_of::<CharSpec>(), 64);
}