#![cfg_attr(not(feature = "std"), no_std)]
#![deny(unsafe_code)]
pub mod descriptors;
pub mod extensions;
pub mod helpers;
pub mod iter;
pub mod types;
use alloc::vec;
const DISK_SECTOR_SIZE: usize = 2048;
const PRIMARY_VOLUME_DESCRIPTOR_POSITION: usize = 0x8000;
const FLAG_DIRECTORY: u8 = 1 << 1;
extern crate alloc;
use alloc::{borrow::ToOwned, boxed::Box, string::String, vec::Vec};
use core::marker::{Send, Sync};
use bitflags::bitflags;
bitflags! {
pub struct ISOInternalFlags: u32 {
const HasJoliet = (1 << 0);
}
}
#[repr(C, packed(1))]
#[derive(Clone, Copy, Debug, Default, FromBytes, Immutable, IntoBytes)]
pub struct ISODateTime {
pub year: u8,
pub month: u8,
pub day: u8,
pub hour: u8,
pub minute: u8,
pub second: u8,
pub gmt_offset: u8,
}
#[repr(C, packed(1))]
#[derive(Debug, Default, Clone, FromBytes, Immutable, IntoBytes)]
pub struct ISODirectoryRecord {
pub(crate) length: u8,
pub(crate) xar_length: u8,
pub lba: types::LSB_MSB<u32>,
pub data_length: types::LSB_MSB<u32>,
pub datetime: ISODateTime,
pub flags: u8,
pub(crate) unit_size: u8,
pub(crate) interleave_gap_size: u8,
pub(crate) volume_seq_number: types::LSB_MSB<u16>,
pub(crate) file_identifier_length: u8, }
#[derive(Debug, Default, Clone)]
pub struct ISODirectoryEntry {
pub record: ISODirectoryRecord,
pub name: String,
}
impl ISODirectoryEntry {
pub const fn is_folder(&self) -> bool {
(self.record.flags & FLAG_DIRECTORY) != 0
}
pub const fn is_file(&self) -> bool {
!self.is_folder()
}
pub fn lsb_position(&self) -> u32 {
self.record.lba.get()
}
pub fn file_size(&self) -> u32 {
self.record.data_length.get()
}
}
pub mod io;
pub use io::Read;
use zerocopy::{FromBytes, Immutable, IntoBytes};
use crate::{
descriptors::DescriptorType,
iter::{DescriptorIterator, DirectoryIter},
};
pub struct ISO9660 {
root_directory: ISODirectoryRecord,
flags: ISOInternalFlags,
device: Box<dyn Read + Send + Sync>,
}
impl ISO9660 {
pub fn from_device(mut device: impl Read + Send + Sync + 'static) -> Option<ISO9660> {
let mut flags = ISOInternalFlags::empty();
let pvd_desc = DescriptorIterator::new(&mut device)
.find(|x| x.header.desc_type == DescriptorType::PrimaryVolume)?;
let mut main_descriptor = pvd_desc.try_as_pvd()?;
let svd: Option<descriptors::Descriptor> = DescriptorIterator::new(&mut device)
.find(|x| x.header.desc_type == DescriptorType::SupplementaryVolume);
if let Some(ref svd) = svd {
main_descriptor = svd.try_as_svd()?;
flags |= ISOInternalFlags::HasJoliet;
}
let root_dir = ISODirectoryRecord::read_from_prefix(&main_descriptor.directory_entry)
.ok()?
.0;
Some(ISO9660 {
root_directory: root_dir,
flags: flags,
device: Box::new(device),
})
}
pub fn descriptors(&mut self) -> DescriptorIterator<'_> {
DescriptorIterator::new(self.device.as_mut())
}
fn read_rock_ridge_name(
&mut self,
byte_offset: usize,
main_part_size: usize,
extension_size: usize,
) -> Option<String> {
let mut address = byte_offset + main_part_size;
if address % 2 != 0 {
address += 1;
}
let mut extension_data: Vec<u8> = vec![0; extension_size];
self.device.read(address, &mut extension_data);
let rock_ridge_data = extensions::rock_ridge::parse(&extension_data);
for i in rock_ridge_data {
if let extensions::rock_ridge::Entity::Name { name } = i {
return Some(name.to_owned());
}
}
None
}
fn read_joliet_name(&mut self, byte_offset: usize, len: usize) -> Option<String> {
let mut address = byte_offset;
if (address % 2 != 0) && len != 1 {
address += 1;
}
let mut ucs2_name: Vec<u8> = vec![0; len];
self.device.read(address, &mut ucs2_name);
extensions::joliet::parse_name(&ucs2_name)
}
pub fn read_directory(&mut self, start_lba: usize) -> DirectoryIter<'_> {
let byte_offset = start_lba * DISK_SECTOR_SIZE;
DirectoryIter::new(self, byte_offset)
}
pub fn read_file(
&mut self,
directory_entry: &ISODirectoryEntry,
offset: usize,
data: &mut [u8],
) -> Option<usize> {
if (directory_entry.record.flags & FLAG_DIRECTORY) != 0 {
return None;
}
let position = directory_entry.lsb_position() as usize;
let data_length = directory_entry.file_size() as usize;
let read_length = data_length.min(data.len());
self.device.read(
(position * DISK_SECTOR_SIZE) + offset,
&mut data[0..read_length],
);
Some(read_length)
}
#[inline]
pub fn read_root(&mut self) -> DirectoryIter<'_> {
self.read_directory(self.root_directory.lba.get() as usize)
}
#[inline]
pub fn root(&self) -> &ISODirectoryRecord {
&self.root_directory
}
}