1use no_std_io2::io::{Read, Seek};
4use std::collections::HashMap;
5use std::io::{BufRead, Cursor, SeekFrom, Write};
6
7use deku::prelude::*;
8use solana_nohash_hasher::IntMap;
9
10use super::export::Export;
11use super::fragment::Fragment;
12use super::inode::Inode;
13use super::metadata::METADATA_MAXSIZE;
14use super::squashfs::SuperBlock;
15use super::{fragment, metadata};
16use crate::error::BackhandError;
17use crate::kinds::Kind;
18
19#[derive(Debug)]
21pub(crate) struct SquashfsReaderWithOffset<R: BufReadSeek> {
22 io: R,
23 offset: u64,
25}
26
27impl<R: BufReadSeek> SquashfsReaderWithOffset<R> {
28 pub fn new(mut io: R, offset: u64) -> std::io::Result<Self> {
29 io.seek(SeekFrom::Start(offset))?;
30 Ok(Self { io, offset })
31 }
32}
33
34impl<R: BufReadSeek> BufRead for SquashfsReaderWithOffset<R> {
35 fn fill_buf(&mut self) -> std::io::Result<&[u8]> {
36 self.io.fill_buf()
37 }
38
39 fn consume(&mut self, amt: usize) {
40 self.io.consume(amt)
41 }
42}
43
44impl<R: BufReadSeek> Read for SquashfsReaderWithOffset<R> {
45 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
46 self.io.read(buf)
47 }
48}
49
50impl<R: BufReadSeek> Seek for SquashfsReaderWithOffset<R> {
51 fn seek(&mut self, pos: SeekFrom) -> std::io::Result<u64> {
52 let seek = match pos {
53 SeekFrom::Start(start) => SeekFrom::Start(self.offset + start),
54 seek => seek,
55 };
56 self.io.seek(seek).map(|x| x - self.offset)
57 }
58}
59
60pub trait BufReadSeek: BufRead + Seek + Send {}
62impl<T: BufRead + Seek + Send> BufReadSeek for T {}
63
64pub trait WriteSeek: Write + Seek {}
66impl<T: Write + Seek> WriteSeek for T {}
67
68impl<T: BufReadSeek> SquashFsReader for T {}
69
70pub trait SquashFsReader: BufReadSeek {
72 fn inodes(
74 &mut self,
75 superblock: &SuperBlock,
76 kind: &Kind,
77 ) -> Result<IntMap<u32, Inode>, BackhandError> {
78 self.seek(SeekFrom::Start(superblock.inode_table_start))?;
79
80 let mut next = vec![];
85
86 let mut metadata_offsets = vec![];
87 let mut ret_vec = HashMap::default();
88 let start = self.stream_position()?;
89
90 while self.stream_position()? < superblock.directory_table_start {
91 metadata_offsets.push(self.stream_position()? - start);
92 let mut bytes = metadata::read_block(self, superblock, kind)?;
94
95 let mut inode_bytes = next;
97 inode_bytes.append(&mut bytes);
98 let mut c_inode_bytes = Cursor::new(inode_bytes.clone());
99 let mut container = Reader::new(&mut c_inode_bytes);
100
101 let mut container_bits_read = container.bits_read;
103 loop {
104 match Inode::from_reader_with_ctx(
105 &mut container,
106 (
107 superblock.bytes_used,
108 superblock.block_size,
109 superblock.block_log,
110 kind.inner.type_endian,
111 kind.inner.bit_order.unwrap(),
112 ),
113 ) {
114 Ok(inode) => {
115 ret_vec.insert(inode.header.inode_number, inode);
116 container_bits_read = container.bits_read;
117 }
118 Err(e) => {
119 if matches!(e, DekuError::Incomplete(_)) {
120 next = inode_bytes.clone()[(container_bits_read / 8)..].to_vec();
122 break;
123 } else {
124 error!("Fatal error parsing inode: {:?}", e);
125 return Err(BackhandError::Deku(e));
126 }
127 }
128 }
129 }
130 }
131
132 if ret_vec.len() != superblock.inode_count.try_into().unwrap() {
133 error!("Parsed {} inodes, expected {}", ret_vec.len(), superblock.inode_count);
134 return Err(BackhandError::CorruptedOrInvalidSquashfs);
135 }
136
137 Ok(ret_vec)
138 }
139
140 fn root_inode(&mut self, superblock: &SuperBlock, kind: &Kind) -> Result<Inode, BackhandError> {
142 let root_inode_start = (superblock.root_inode >> 16) as usize;
143 let root_inode_offset = (superblock.root_inode & 0xffff) as usize;
144 info!("root_inode_start: 0x{root_inode_start:02x?}");
145 info!("root_inode_offset: 0x{root_inode_offset:02x?}");
146 if (root_inode_start as u64) > superblock.bytes_used {
147 error!("root_inode_offset > bytes_used");
148 return Err(BackhandError::CorruptedOrInvalidSquashfs);
149 }
150
151 let seek = superblock.inode_table_start + root_inode_start as u64;
153 self.seek(SeekFrom::Start(seek))?;
154 let mut bytes_01 = metadata::read_block(self, superblock, kind)?;
155
156 if root_inode_offset > bytes_01.len() {
158 error!("root_inode_offset > bytes.len()");
159 return Err(BackhandError::CorruptedOrInvalidSquashfs);
160 }
161 let mut cursor = Cursor::new(&bytes_01[root_inode_offset..]);
162 let mut new_bytes = Reader::new(&mut cursor);
163 if let Ok(inode) = Inode::from_reader_with_ctx(
164 &mut new_bytes,
165 (
166 superblock.bytes_used,
167 superblock.block_size,
168 superblock.block_log,
169 kind.inner.type_endian,
170 kind.inner.bit_order.unwrap(),
171 ),
172 ) {
173 info!("ROOT: {:?}", inode);
174 return Ok(inode);
175 }
176
177 let bytes_02 = metadata::read_block(self, superblock, kind)?;
179 bytes_01.write_all(&bytes_02)?;
180 if root_inode_offset > bytes_01.len() {
181 error!("root_inode_offset > bytes.len()");
182 return Err(BackhandError::CorruptedOrInvalidSquashfs);
183 }
184
185 let mut cursor = Cursor::new(&bytes_01[root_inode_offset..]);
186 let mut new_bytes = Reader::new(&mut cursor);
187 match Inode::from_reader_with_ctx(
188 &mut new_bytes,
189 (
190 superblock.bytes_used,
191 superblock.block_size,
192 superblock.block_log,
193 kind.inner.type_endian,
194 kind.inner.bit_order.unwrap(),
195 ),
196 ) {
197 Ok(inode) => Ok(inode),
198 Err(e) => Err(e.into()),
199 }
200 }
201
202 fn dir_blocks(
204 &mut self,
205 superblock: &SuperBlock,
206 end_ptr: u64,
207 kind: &Kind,
208 ) -> Result<(IntMap<u64, u64>, Vec<u8>), BackhandError> {
209 let seek = superblock.directory_table_start;
210 self.seek(SeekFrom::Start(seek))?;
211 let mut block_map = IntMap::default();
212 let mut dir_data = Vec::new();
213
214 while self.stream_position()? != end_ptr {
215 let metadata_start = self.stream_position()?;
216 let bytes = metadata::read_block(self, superblock, kind)?;
217 let compressed_offset = metadata_start - seek;
218 let decompressed_offset = dir_data.len() as u64;
219 block_map.insert(compressed_offset, decompressed_offset);
220 dir_data.extend(bytes);
221 }
222
223 Ok((block_map, dir_data))
224 }
225
226 fn fragments(
228 &mut self,
229 superblock: &SuperBlock,
230 kind: &Kind,
231 ) -> Result<Option<(u64, Vec<Fragment>)>, BackhandError> {
232 let (ptr, table) = self.fragment_lookup_table(
233 superblock,
234 superblock.fragment_table_start,
235 u64::from(superblock.fragments) * fragment::SIZE as u64,
236 kind,
237 )?;
238 trace!("{:02x?}", table);
239 Ok(Some((ptr, table)))
240 }
241
242 fn export(
244 &mut self,
245 _superblock: &SuperBlock,
246 _kind: &Kind,
247 ) -> Result<Option<(u64, Vec<Export>)>, BackhandError> {
248 Ok(None)
249 }
250
251 fn uid(&mut self, superblock: &SuperBlock, kind: &Kind) -> Result<Vec<u16>, BackhandError> {
253 let ptr = superblock.uid_start;
254 let count = superblock.no_uids as u64;
255 self.seek(SeekFrom::Start(ptr))?;
256
257 let mut buf = vec![0u8; count as usize * core::mem::size_of::<u16>()];
259 self.read_exact(&mut buf)?;
260
261 let mut cursor = Cursor::new(buf);
262 let mut deku_reader = Reader::new(&mut cursor);
263 let mut table = Vec::with_capacity(count as usize);
264 for _ in 0..count {
265 let v = u16::from_reader_with_ctx(
266 &mut deku_reader,
267 (kind.inner.type_endian, kind.inner.bit_order.unwrap()),
268 )?;
269 table.push(v);
270 }
271
272 Ok(table)
273 }
274
275 fn guid(&mut self, superblock: &SuperBlock, kind: &Kind) -> Result<Vec<u16>, BackhandError> {
277 let ptr = superblock.guid_start;
278 let count = superblock.no_guids as u64;
279 self.seek(SeekFrom::Start(ptr))?;
280
281 let mut buf = vec![0u8; count as usize * core::mem::size_of::<u16>()];
283 self.read_exact(&mut buf)?;
284
285 let mut cursor = Cursor::new(buf);
286 let mut deku_reader = Reader::new(&mut cursor);
287 let mut table = Vec::with_capacity(count as usize);
288 for _ in 0..count {
289 let v = u16::from_reader_with_ctx(
290 &mut deku_reader,
291 (kind.inner.type_endian, kind.inner.bit_order.unwrap()),
292 )?;
293 table.push(v);
294 }
295
296 Ok(table)
297 }
298
299 fn fragment_lookup_table(
301 &mut self,
302 superblock: &SuperBlock,
303 seek: u64,
304 size: u64,
305 kind: &Kind,
306 ) -> Result<(u64, Vec<Fragment>), BackhandError> {
307 trace!(
308 "fragment_lookup_table: seek=0x{:x}, size={}, fragments={}",
309 seek, size, superblock.fragments
310 );
311
312 let fragment_count = superblock.fragments as u64;
318 let fragment_bytes = fragment_count * fragment::SIZE as u64;
319 let metadata_block_count = fragment_bytes.div_ceil(METADATA_MAXSIZE as u64);
320
321 trace!(
322 "fragment_lookup_table: {} fragments need {} metadata blocks",
323 fragment_count, metadata_block_count
324 );
325
326 self.seek(SeekFrom::Start(seek))?;
328 let index_size = metadata_block_count * core::mem::size_of::<u64>() as u64;
329 let mut index_buf = vec![0u8; index_size as usize];
330 self.read_exact(&mut index_buf)?;
331
332 let mut index_ptrs = vec![];
334 let mut cursor = Cursor::new(&index_buf);
335 let mut reader = Reader::new(&mut cursor);
336
337 for i in 0..metadata_block_count {
338 let ptr = u64::from_reader_with_ctx(
339 &mut reader,
340 (kind.inner.type_endian, kind.inner.bit_order.unwrap()),
341 )?;
342 trace!("Fragment metadata block {}: pointer 0x{:x}", i, ptr);
343 index_ptrs.push(ptr);
344 }
345
346 let mut ret_vec = vec![];
348 let mut fragments_read = 0;
349
350 for (i, &ptr) in index_ptrs.iter().enumerate() {
351 if fragments_read >= fragment_count {
352 break;
353 }
354
355 let fragments_in_this_block = core::cmp::min(
356 fragment_count - fragments_read,
357 METADATA_MAXSIZE as u64 / fragment::SIZE as u64,
358 );
359
360 trace!(
361 "Reading {} fragments from metadata block {} at 0x{:x}",
362 fragments_in_this_block, i, ptr
363 );
364
365 self.seek(SeekFrom::Start(ptr))?;
366 let block_fragments = self.fragment_metadata_with_count(superblock, ptr, 1, kind)?;
367
368 let take_count =
370 core::cmp::min(block_fragments.len(), fragments_in_this_block as usize);
371 ret_vec.extend_from_slice(&block_fragments[..take_count]);
372 fragments_read += take_count as u64;
373 }
374
375 trace!("fragment_lookup_table: successfully read {} fragments", ret_vec.len());
376 Ok((seek, ret_vec))
377 }
378
379 fn fragment_metadata_with_count(
381 &mut self,
382 superblock: &SuperBlock,
383 seek: u64,
384 count: u64,
385 kind: &Kind,
386 ) -> Result<Vec<Fragment>, BackhandError> {
387 trace!("fragment_metadata_with_count: seek=0x{:02x}, count={}", seek, count);
388 self.seek(SeekFrom::Start(seek))?;
389
390 let mut all_bytes = vec![];
391 for i in 0..count {
392 let pos_before = self.stream_position()?;
393 let mut bytes = metadata::read_block(self, superblock, kind)?;
394 let pos_after = self.stream_position()?;
395 trace!(
396 "fragment metadata block {}: pos 0x{:x} -> 0x{:x}, read {} decompressed bytes, first 20: {:02x?}",
397 i,
398 pos_before,
399 pos_after,
400 bytes.len(),
401 &bytes[..core::cmp::min(20, bytes.len())]
402 );
403 all_bytes.append(&mut bytes);
404 }
405
406 trace!(
407 "fragment_metadata_with_count: total decompressed bytes: {}, content: {:02x?}",
408 all_bytes.len(),
409 &all_bytes[..core::cmp::min(50, all_bytes.len())]
410 );
411
412 let mut ret_vec = vec![];
413 let mut cursor = Cursor::new(&all_bytes);
415 let mut container = Reader::new(&mut cursor);
416 loop {
417 match Fragment::from_reader_with_ctx(
418 &mut container,
419 (kind.inner.type_endian, kind.inner.bit_order.unwrap()),
420 ) {
421 Ok(t) => {
422 trace!("Parsed fragment: {:?}", t);
423 ret_vec.push(t);
424 }
425 Err(e) => {
426 trace!("Failed to parse more fragments: {:?}", e);
427 break;
428 }
429 }
430 }
431
432 Ok(ret_vec)
433 }
434}