use crate::descriptor::{DescriptorTag, LongAllocationDescriptor};
use crate::time::UdfTimestamp;
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct FileEntry {
pub tag: DescriptorTag,
pub icb_tag: IcbTag,
pub uid: u32,
pub gid: u32,
pub permissions: u32,
pub file_link_count: u16,
pub record_format: u8,
pub record_display_attributes: u8,
pub record_length: u32,
pub information_length: u64,
pub logical_blocks_recorded: u64,
pub access_time: UdfTimestamp,
pub modification_time: UdfTimestamp,
pub attribute_time: UdfTimestamp,
pub checkpoint: u32,
pub extended_attribute_icb: LongAllocationDescriptor,
pub implementation_identifier: [u8; 32],
pub unique_id: u64,
pub extended_attributes_length: u32,
pub allocation_descriptors_length: u32,
}
unsafe impl bytemuck::Zeroable for FileEntry {}
unsafe impl bytemuck::Pod for FileEntry {}
impl FileEntry {
pub const BASE_SIZE: usize = 176;
pub fn size(&self) -> u64 {
self.information_length
}
pub fn file_type(&self) -> FileType {
self.icb_tag.file_type()
}
pub fn is_directory(&self) -> bool {
self.file_type() == FileType::Directory
}
pub fn is_file(&self) -> bool {
self.file_type() == FileType::RegularFile
}
pub fn allocation_type(&self) -> AllocationType {
AllocationType::from_bits((self.icb_tag.flags & 0x07) as u8)
}
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct ExtendedFileEntry {
pub tag: DescriptorTag,
pub icb_tag: IcbTag,
pub uid: u32,
pub gid: u32,
pub permissions: u32,
pub file_link_count: u16,
pub record_format: u8,
pub record_display_attributes: u8,
pub record_length: u32,
pub information_length: u64,
pub object_size: u64,
pub logical_blocks_recorded: u64,
pub access_time: UdfTimestamp,
pub modification_time: UdfTimestamp,
pub creation_time: UdfTimestamp,
pub attribute_time: UdfTimestamp,
pub checkpoint: u32,
reserved: u32,
pub extended_attribute_icb: LongAllocationDescriptor,
pub stream_directory_icb: LongAllocationDescriptor,
pub implementation_identifier: [u8; 32],
pub unique_id: u64,
pub extended_attributes_length: u32,
pub allocation_descriptors_length: u32,
}
unsafe impl bytemuck::Zeroable for ExtendedFileEntry {}
unsafe impl bytemuck::Pod for ExtendedFileEntry {}
impl ExtendedFileEntry {
pub const BASE_SIZE: usize = 216;
pub fn size(&self) -> u64 {
self.information_length
}
pub fn file_type(&self) -> FileType {
self.icb_tag.file_type()
}
pub fn is_directory(&self) -> bool {
self.file_type() == FileType::Directory
}
pub fn is_file(&self) -> bool {
self.file_type() == FileType::RegularFile
}
pub fn allocation_type(&self) -> AllocationType {
AllocationType::from_bits((self.icb_tag.flags & 0x07) as u8)
}
}
#[repr(C)]
#[derive(Debug, Clone, Copy, Default, bytemuck::Zeroable, bytemuck::Pod)]
pub struct IcbTag {
pub prior_recorded_num_direct_entries: u32,
pub strategy_type: u16,
pub strategy_parameters: [u8; 2],
pub max_num_entries: u16,
reserved: u8,
pub file_type: u8,
pub parent_icb_location: [u8; 6],
pub flags: u16,
}
impl IcbTag {
pub fn file_type(&self) -> FileType {
FileType::from_u8(self.file_type)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum FileType {
Unspecified = 0,
UnallocatedSpaceEntry = 1,
PartitionIntegrityEntry = 2,
IndirectEntry = 3,
Directory = 4,
RegularFile = 5,
BlockDevice = 6,
CharacterDevice = 7,
ExtendedAttribute = 8,
Fifo = 9,
Socket = 10,
TerminalEntry = 11,
SymbolicLink = 12,
StreamDirectory = 13,
Unknown = 255,
}
impl FileType {
fn from_u8(value: u8) -> Self {
match value {
0 => Self::Unspecified,
1 => Self::UnallocatedSpaceEntry,
2 => Self::PartitionIntegrityEntry,
3 => Self::IndirectEntry,
4 => Self::Directory,
5 => Self::RegularFile,
6 => Self::BlockDevice,
7 => Self::CharacterDevice,
8 => Self::ExtendedAttribute,
9 => Self::Fifo,
10 => Self::Socket,
11 => Self::TerminalEntry,
12 => Self::SymbolicLink,
13 => Self::StreamDirectory,
_ => Self::Unknown,
}
}
}
impl core::fmt::Display for FileType {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::Unspecified => write!(f, "unspecified"),
Self::UnallocatedSpaceEntry => write!(f, "unallocated space entry"),
Self::PartitionIntegrityEntry => write!(f, "partition integrity entry"),
Self::IndirectEntry => write!(f, "indirect entry"),
Self::Directory => write!(f, "directory"),
Self::RegularFile => write!(f, "regular file"),
Self::BlockDevice => write!(f, "block device"),
Self::CharacterDevice => write!(f, "character device"),
Self::ExtendedAttribute => write!(f, "extended attribute"),
Self::Fifo => write!(f, "fifo"),
Self::Socket => write!(f, "socket"),
Self::TerminalEntry => write!(f, "terminal entry"),
Self::SymbolicLink => write!(f, "symlink"),
Self::StreamDirectory => write!(f, "stream directory"),
Self::Unknown => write!(f, "unknown"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AllocationType {
Short,
Long,
Extended,
Embedded,
}
impl AllocationType {
pub(crate) fn from_bits(bits: u8) -> Self {
match bits & 0x07 {
0 => Self::Short,
1 => Self::Long,
2 => Self::Extended,
3 => Self::Embedded,
_ => Self::Short, }
}
}
#[allow(dead_code)]
pub struct UdfFile {
size: u64,
_icb: LongAllocationDescriptor,
_allocation_type: AllocationType,
}
#[allow(dead_code)]
impl UdfFile {
pub(crate) fn new(
size: u64,
icb: LongAllocationDescriptor,
allocation_type: AllocationType,
) -> Self {
Self {
size,
_icb: icb,
_allocation_type: allocation_type,
}
}
pub fn size(&self) -> u64 {
self.size
}
}
#[cfg(test)]
mod tests {
use super::*;
static_assertions::const_assert_eq!(size_of::<FileEntry>(), 176);
static_assertions::const_assert_eq!(size_of::<ExtendedFileEntry>(), 216);
static_assertions::const_assert_eq!(size_of::<IcbTag>(), 20);
#[test]
fn test_file_type_roundtrip() {
for ft in [
FileType::Directory,
FileType::RegularFile,
FileType::SymbolicLink,
] {
let value = ft as u8;
assert_eq!(FileType::from_u8(value), ft);
}
}
}