#![warn(missing_docs)]
pub type Result<T> = std::result::Result<T, ISOError>;
mod directory_entry;
mod error;
mod fileref;
mod parse;
use fileref::FileRef;
use parse::volume_descriptor::VolumeDescriptor;
pub use directory_entry::{
DirectoryEntry, ExtraAttributes, ExtraMeta, ISODirectory, ISODirectoryIterator, ISOFile,
ISOFileReader, PosixAttributes, PosixFileMode, PosixTimestamp, SuspExtension, Symlink,
};
pub use error::ISOError;
pub use fileref::ISO9660Reader;
pub struct ISO9660<T: ISO9660Reader> {
_file: FileRef<T>,
root: ISODirectory<T>,
sup_root: Option<ISODirectory<T>>,
primary: VolumeDescriptor,
}
pub const BLOCK_SIZE: u16 = 2048;
pub type BlockBuffer = [u8; BLOCK_SIZE as usize];
pub trait BlockBufferCtor {
fn new() -> Self;
}
impl BlockBufferCtor for BlockBuffer {
#[inline(always)]
fn new() -> Self {
[0; BLOCK_SIZE as usize]
}
}
macro_rules! primary_prop_str {
($(#[$attr:meta])* $name:ident) => {
$(#[$attr])*
pub fn $name(&self) -> &str {
if let VolumeDescriptor::Primary(table) = &self.primary {
&table.$name
} else {
unreachable!()
}
}
};
}
impl<T: ISO9660Reader> ISO9660<T> {
pub fn new(mut reader: T) -> Result<ISO9660<T>> {
let blksize = usize::from(BLOCK_SIZE);
let mut buf = BlockBuffer::new();
let mut root = None;
let mut primary = None;
let mut sup_root = None;
let mut lba = 16;
loop {
let count = reader.read_at(&mut buf, lba)?;
if count != blksize {
return Err(ISOError::ReadSize(count));
}
let descriptor = VolumeDescriptor::parse(&buf)?;
match &descriptor {
Some(VolumeDescriptor::Primary(table)) => {
if usize::from(table.logical_block_size) != blksize {
return Err(ISOError::InvalidFs("Block size not 2048"));
}
root = Some((
table.root_directory_entry.clone(),
table.root_directory_entry_identifier.clone(),
));
primary = descriptor;
}
Some(VolumeDescriptor::Supplementary(table)) => {
if usize::from(table.logical_block_size) != blksize {
return Err(ISOError::InvalidFs("Block size not 2048"));
}
sup_root = Some((
table.root_directory_entry.clone(),
table.root_directory_entry_identifier.clone(),
));
}
Some(VolumeDescriptor::VolumeDescriptorSetTerminator) => break,
_ => {}
}
lba += 1;
}
let file = FileRef::new(reader);
let file2 = file.clone();
let file3 = file.clone();
let (root, primary) = match (root, primary) {
(Some(root), Some(primary)) => (root, primary),
_ => {
return Err(ISOError::InvalidFs("No primary volume descriptor"));
}
};
Ok(ISO9660 {
_file: file,
root: ISODirectory::new(root.0, ExtraMeta::default(), root.1, file2),
sup_root: sup_root.map(|sup_root| {
ISODirectory::new(sup_root.0, ExtraMeta::default(), sup_root.1, file3)
}),
primary,
})
}
pub fn open(&self, path: &str) -> Result<Option<DirectoryEntry<T>>> {
self.root().find_recursive(path)
}
pub fn is_rr(&self) -> bool {
match self.root.contents().next() {
Some(Ok(DirectoryEntry::Directory(dirent))) => dirent.is_rock_ridge(),
_ => false, }
}
pub fn root(&self) -> &ISODirectory<T> {
if self.is_rr() {
&self.root
} else {
match self.sup_root.as_ref() {
Some(sup_root) => sup_root,
None => &self.root,
}
}
}
pub fn root_at(&self, index: usize) -> Option<&ISODirectory<T>> {
match index {
0 => Some(&self.root),
1 => self.sup_root.as_ref(),
_ => unimplemented!(),
}
}
pub fn block_size(&self) -> u16 {
BLOCK_SIZE
}
primary_prop_str! {
volume_set_identifier
}
primary_prop_str! {
publisher_identifier
}
primary_prop_str! {
data_preparer_identifier
}
primary_prop_str! {
application_identifier
}
primary_prop_str! {
copyright_file_identifier
}
primary_prop_str! {
abstract_file_identifier
}
primary_prop_str! {
bibliographic_file_identifier
}
}