iso9660/
lib.rs

1// SPDX-License-Identifier: (MIT OR Apache-2.0)
2
3#![cfg_attr(feature = "nightly", feature(read_initializer, specialization))]
4
5extern crate time;
6#[macro_use]
7extern crate bitflags;
8extern crate nom;
9
10use std::result;
11
12pub use directory_entry::{
13    DirectoryEntry, ISODirectory, ISODirectoryIterator, ISOFile, ISOFileReader,
14};
15pub use error::ISOError;
16pub(crate) use fileref::FileRef;
17pub use fileref::ISO9660Reader;
18use parse::VolumeDescriptor;
19
20pub type Result<T> = result::Result<T, ISOError>;
21
22mod directory_entry;
23mod error;
24mod fileref;
25mod parse;
26
27pub struct ISO9660<T: ISO9660Reader> {
28    _file: FileRef<T>,
29    pub root: ISODirectory<T>,
30    primary: VolumeDescriptor,
31}
32
33macro_rules! primary_prop_str {
34    ($name:ident) => {
35        pub fn $name(&self) -> &str {
36            if let VolumeDescriptor::Primary { $name, .. } = &self.primary {
37                &$name
38            } else {
39                unreachable!()
40            }
41        }
42    };
43}
44
45impl<T: ISO9660Reader> ISO9660<T> {
46    pub fn new(mut reader: T) -> Result<ISO9660<T>> {
47        let mut buf: [u8; 2048] = [0; 2048];
48        let mut root = None;
49        let mut primary = None;
50
51        // Skip the "system area"
52        let mut lba = 16;
53
54        // Read volume descriptors
55        loop {
56            let count = reader.read_at(&mut buf, lba)?;
57
58            if count != 2048 {
59                return Err(ISOError::ReadSize(2048, count));
60            }
61
62            let descriptor = VolumeDescriptor::parse(&buf)?;
63            match &descriptor {
64                Some(VolumeDescriptor::Primary {
65                    logical_block_size,
66                    root_directory_entry,
67                    root_directory_entry_identifier,
68                    ..
69                }) => {
70                    if *logical_block_size != 2048 {
71                        // This is almost always the case, but technically
72                        // not guaranteed by the standard.
73                        // TODO: Implement this
74                        return Err(ISOError::InvalidFs("Block size not 2048"));
75                    }
76
77                    root = Some((
78                        root_directory_entry.clone(),
79                        root_directory_entry_identifier.clone(),
80                    ));
81                    primary = descriptor;
82                }
83                Some(VolumeDescriptor::VolumeDescriptorSetTerminator) => break,
84                _ => {}
85            }
86
87            lba += 1;
88        }
89
90        let file = FileRef::new(reader);
91        let file2 = file.clone();
92
93        let (root, primary) = match (root, primary) {
94            (Some(root), Some(primary)) => (root, primary),
95            _ => {
96                return Err(ISOError::InvalidFs("No primary volume descriptor"));
97            }
98        };
99
100        Ok(ISO9660 {
101            _file: file,
102            root: ISODirectory::new(root.0, root.1, file2),
103            primary,
104        })
105    }
106
107    pub fn open(&self, path: &str) -> Result<Option<DirectoryEntry<T>>> {
108        // TODO: avoid clone()
109        let mut entry = DirectoryEntry::Directory(self.root.clone());
110        for segment in path.split('/').filter(|x| !x.is_empty()) {
111            let parent = match entry {
112                DirectoryEntry::Directory(dir) => dir,
113                _ => return Ok(None),
114            };
115
116            entry = match parent.find(segment)? {
117                Some(entry) => entry,
118                None => return Ok(None),
119            };
120        }
121
122        Ok(Some(entry))
123    }
124
125    pub fn block_size(&self) -> u16 {
126        2048 // XXX
127    }
128
129    primary_prop_str!(volume_set_identifier);
130    primary_prop_str!(publisher_identifier);
131    primary_prop_str!(data_preparer_identifier);
132    primary_prop_str!(application_identifier);
133    primary_prop_str!(copyright_file_identifier);
134    primary_prop_str!(abstract_file_identifier);
135    primary_prop_str!(bibliographic_file_identifier);
136}