backhand/filesystem/reader.rs
1use std::sync::{Mutex, RwLock};
2
3use super::node::Nodes;
4use crate::compressor::{CompressionOptions, Compressor};
5use crate::data::DataSize;
6use crate::error::BackhandError;
7use crate::fragment::Fragment;
8use crate::id::Id;
9use crate::kinds::Kind;
10use crate::reader::BufReadSeek;
11use crate::squashfs::Cache;
12use crate::{Node, Squashfs, SquashfsFileReader};
13
14#[cfg(not(feature = "parallel"))]
15use crate::filesystem::reader_no_parallel::{SquashfsRawData, SquashfsReadFile};
16#[cfg(feature = "parallel")]
17use crate::filesystem::reader_parallel::{SquashfsRawData, SquashfsReadFile};
18
19/// Representation of SquashFS filesystem after read from image
20/// - Use [`Self::from_reader`] to read into `Self` from a `reader`
21///
22/// # Read direct into [`Self`]
23/// Usual workflow, reading from image into a default squashfs [`Self`]. See [InnerNode] for more
24/// details for `.nodes`.
25/// ```rust,no_run
26/// # use std::fs::File;
27/// # use std::io::BufReader;
28/// # use backhand::{
29/// # FilesystemReader, InnerNode, Squashfs, SquashfsBlockDevice, SquashfsCharacterDevice,
30/// # SquashfsDir, SquashfsSymlink,
31/// # };
32/// // Read into filesystem
33/// let file = BufReader::new(File::open("image.squashfs").unwrap());
34/// let filesystem = FilesystemReader::from_reader(file).unwrap();
35///
36/// // Iterate through nodes
37/// // (See src/bin/unsquashfs.rs for more examples on extraction)
38/// for node in filesystem.files() {
39/// // extract
40/// match &node.inner {
41/// InnerNode::File(_) => (),
42/// InnerNode::Symlink(_) => (),
43/// InnerNode::Dir(_) => (),
44/// InnerNode::CharacterDevice(_) => (),
45/// InnerNode::BlockDevice(_) => (),
46/// InnerNode::NamedPipe => (),
47/// InnerNode::Socket => (),
48/// }
49/// }
50/// ```
51///
52/// # Read from [`Squashfs`]
53/// Performance wise, you may want to read into a [`Squashfs`] first, if for instance you are
54/// optionally not extracting and only listing some Superblock fields.
55/// ```rust,no_run
56/// # use std::fs::File;
57/// # use std::io::BufReader;
58/// # use backhand::{
59/// # FilesystemReader, InnerNode, Squashfs, SquashfsBlockDevice, SquashfsCharacterDevice,
60/// # SquashfsDir, SquashfsSymlink,
61/// # };
62/// // Read into Squashfs
63/// let file = BufReader::new(File::open("image.squashfs").unwrap());
64/// let squashfs = Squashfs::from_reader_with_offset(file, 0).unwrap();
65///
66/// // Display the Superblock info
67/// let superblock = squashfs.superblock;
68/// println!("{superblock:#08x?}");
69///
70/// // Now read into filesystem
71/// let filesystem = squashfs.into_filesystem_reader().unwrap();
72/// ```
73/// [InnerNode]: [`crate::InnerNode`]
74pub struct FilesystemReader<'b> {
75 pub kind: Kind,
76 /// The size of a data block in bytes. Must be a power of two between 4096 (4k) and 1048576 (1 MiB).
77 pub block_size: u32,
78 /// The log2 of the block size. If the two fields do not agree, the archive is considered corrupted.
79 pub block_log: u16,
80 /// Compressor used for data
81 pub compressor: Compressor,
82 /// Optional Compressor used for data stored in image
83 pub compression_options: Option<CompressionOptions>,
84 /// Last modification time of the archive. Count seconds since 00:00, Jan 1st 1970 UTC (not counting leap seconds).
85 /// This is unsigned, so it expires in the year 2106 (as opposed to 2038).
86 pub mod_time: u32,
87 /// ID's stored for gui(s) and uid(s)
88 pub id_table: Vec<Id>,
89 /// Fragments Lookup Table
90 pub fragments: Option<Vec<Fragment>>,
91 /// All files and directories in filesystem
92 pub root: Nodes<SquashfsFileReader>,
93 /// File reader
94 pub(crate) reader: Mutex<Box<dyn BufReadSeek + 'b>>,
95 /// Cache used in the decompression
96 pub(crate) cache: RwLock<Cache>,
97 /// Superblock Flag to remove duplicate flags
98 pub(crate) no_duplicate_files: bool,
99}
100
101impl<'b> FilesystemReader<'b> {
102 /// Call [`Squashfs::from_reader`], then [`Squashfs::into_filesystem_reader`]
103 ///
104 /// With default kind: [`crate::kind::LE_V4_0`] and offset `0`.
105 pub fn from_reader<R>(reader: R) -> Result<Self, BackhandError>
106 where
107 R: BufReadSeek + 'b,
108 {
109 let squashfs = Squashfs::from_reader_with_offset(reader, 0)?;
110 squashfs.into_filesystem_reader()
111 }
112
113 /// Same as [`Self::from_reader`], but seek'ing to `offset` in `reader` before reading
114 pub fn from_reader_with_offset<R>(reader: R, offset: u64) -> Result<Self, BackhandError>
115 where
116 R: BufReadSeek + 'b,
117 {
118 let squashfs = Squashfs::from_reader_with_offset(reader, offset)?;
119 squashfs.into_filesystem_reader()
120 }
121
122 /// Same as [`Self::from_reader_with_offset`], but setting custom `kind`
123 pub fn from_reader_with_offset_and_kind<R>(
124 reader: R,
125 offset: u64,
126 kind: Kind,
127 ) -> Result<Self, BackhandError>
128 where
129 R: BufReadSeek + 'b,
130 {
131 let squashfs = Squashfs::from_reader_with_offset_and_kind(reader, offset, kind)?;
132 squashfs.into_filesystem_reader()
133 }
134
135 /// Return a file handler for this file
136 pub fn file<'a>(&'a self, file: &'a SquashfsFileReader) -> FilesystemReaderFile<'a, 'b> {
137 FilesystemReaderFile::new(self, file)
138 }
139
140 /// Iterator of all files, including the root
141 ///
142 /// # Example
143 /// Used when extracting a file from the image, for example using [`FilesystemReaderFile`]:
144 /// ```rust,no_run
145 /// # use std::fs::File;
146 /// # use std::io::BufReader;
147 /// # use backhand::{
148 /// # FilesystemReader, InnerNode, Squashfs, SquashfsBlockDevice, SquashfsCharacterDevice,
149 /// # SquashfsDir, SquashfsSymlink,
150 /// # };
151 /// # let file = BufReader::new(File::open("image.squashfs").unwrap());
152 /// # let filesystem = FilesystemReader::from_reader(file).unwrap();
153 /// // [snip: creating FilesystemReader]
154 ///
155 /// for node in filesystem.files() {
156 /// // extract
157 /// match &node.inner {
158 /// InnerNode::File(file) => {
159 /// let mut reader = filesystem
160 /// .file(&file)
161 /// .reader();
162 /// // Then, do something with the reader
163 /// },
164 /// _ => (),
165 /// }
166 /// }
167 /// ```
168 pub fn files(&self) -> impl Iterator<Item = &Node<SquashfsFileReader>> {
169 self.root.nodes.iter()
170 }
171}
172
173/// Filesystem handle for file
174#[derive(Copy, Clone)]
175pub struct FilesystemReaderFile<'a, 'b> {
176 pub(crate) system: &'a FilesystemReader<'b>,
177 pub(crate) file: &'a SquashfsFileReader,
178}
179
180impl<'a, 'b> FilesystemReaderFile<'a, 'b> {
181 pub fn new(system: &'a FilesystemReader<'b>, file: &'a SquashfsFileReader) -> Self {
182 Self { system, file }
183 }
184
185 /// Create [`SquashfsReadFile`] that impls [`std::io::Read`] from [`FilesystemReaderFile`].
186 /// This can be used to then call functions from [`std::io::Read`]
187 /// to de-compress and read the data from this file.
188 ///
189 /// [Read::read]: std::io::Read::read
190 /// [Vec::clear]: Vec::clear
191 pub fn reader(&self) -> SquashfsReadFile<'a, 'b> {
192 self.raw_data_reader().into_reader()
193 }
194
195 pub fn fragment(&self) -> Option<&'a Fragment> {
196 if self.file.frag_index() == 0xffffffff {
197 None
198 } else {
199 self.system.fragments.as_ref().map(|fragments| &fragments[self.file.frag_index()])
200 }
201 }
202
203 pub(crate) fn raw_data_reader(&self) -> SquashfsRawData<'a, 'b> {
204 SquashfsRawData::new(Self { system: self.system, file: self.file })
205 }
206}
207
208impl<'a> IntoIterator for FilesystemReaderFile<'a, '_> {
209 type IntoIter = BlockIterator<'a>;
210 type Item = <BlockIterator<'a> as Iterator>::Item;
211
212 fn into_iter(self) -> Self::IntoIter {
213 BlockIterator { blocks: self.file.block_sizes(), fragment: self.fragment() }
214 }
215}
216
217pub enum BlockFragment<'a> {
218 Block(&'a DataSize),
219 Fragment(&'a Fragment),
220}
221
222pub struct BlockIterator<'a> {
223 pub blocks: &'a [DataSize],
224 pub fragment: Option<&'a Fragment>,
225}
226
227impl<'a> Iterator for BlockIterator<'a> {
228 type Item = BlockFragment<'a>;
229
230 fn next(&mut self) -> Option<Self::Item> {
231 self.blocks
232 .split_first()
233 .map(|(first, rest)| {
234 self.blocks = rest;
235 BlockFragment::Block(first)
236 })
237 .or_else(|| self.fragment.take().map(BlockFragment::Fragment))
238 }
239}