squashfs_async/
directory_table.rs1use std::collections::hash_map::DefaultHasher;
5use std::collections::HashMap;
6use std::hash::{Hash, Hasher};
7use std::io::SeekFrom;
8
9use deser::from_reader;
10use itertools::Itertools;
11use serde::Deserialize;
12use tokio::io::{AsyncReadExt, AsyncSeekExt};
13use tracing::*;
14
15use super::deser;
16use super::error::DirectoryTableError;
17use super::inodes::{DirectoryInode, InodeType};
18use super::metadata::MetadataBlock;
19use super::superblock::SuperBlock;
20
21#[derive(Debug, Deserialize)]
22struct Header {
23 entries: u32,
24 inode_table_offset: u32,
25 inode_number_base: u32,
26}
27from_reader!(Header, 12);
28
29#[derive(Debug, Deserialize)]
30struct EntryInternal {
31 inode_metadata_offset: u16,
32 inode_offset: i16,
33 r#type: InodeType,
34 name_size: u16,
35 #[serde(skip)]
36 name: String,
37}
38
39#[derive(Debug)]
41pub struct Entry {
42 _inode_metadata_offset: u32,
43 pub inode: u32,
44 pub r#type: InodeType,
45 pub name: String,
46}
47impl Entry {
48 pub fn is_dir(&self) -> bool {
49 self.r#type.is_dir()
50 }
51}
52impl std::fmt::Display for Entry {
53 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
54 write!(
55 f,
56 "{}{}",
57 self.name,
58 self.is_dir().then_some("/").unwrap_or_default()
59 )
60 }
61}
62impl Entry {
63 fn from(header: &Header, entry: EntryInternal) -> Self {
64 Self {
65 _inode_metadata_offset: header.inode_table_offset + entry.inode_metadata_offset as u32,
66 name: entry.name,
67 r#type: entry.r#type,
68 inode: (header.inode_number_base as i32 + entry.inode_offset as i32) as u32,
69 }
70 }
71}
72impl EntryInternal {
73 async fn from_reader(mut r: impl crate::AsyncRead) -> Result<Self, DirectoryTableError> {
74 let mut entry: Self = deser::bincode_deser_from(&mut r, 8)
75 .await
76 .map_err(|_| DirectoryTableError::InvalidEntry)?;
77 entry.name = deser::bincode_deser_string_from(r, entry.name_size as usize + 1)
78 .await
79 .map_err(|_| DirectoryTableError::InvalidEntry)?;
80 Ok(entry)
81 }
82}
83
84fn index_hash(s: &str) -> u64 {
85 let mut hasher = DefaultHasher::new();
86 s.hash(&mut hasher);
87 hasher.finish()
88}
89#[derive(Default, Debug)]
91pub struct DirectoryTable {
92 pub entries: Vec<Entry>,
93 index: HashMap<u64, Vec<usize>>,
95}
96impl DirectoryTable {
97 pub fn find(&self, name: &str) -> Option<&Entry> {
98 self.index
99 .get(&index_hash(name))
100 .into_iter()
101 .flatten()
102 .map(|i| &self.entries[*i])
103 .find(|e| e.name == name)
104 }
105 async fn from_reader(mut r: impl crate::AsyncRead) -> Result<Self, DirectoryTableError> {
106 let mut entries = vec![];
108 let mut header = [0; 12];
109 loop {
110 let header = match r.read_exact(&mut header).await {
112 Ok(_) => Header::from_reader(&header[..])
113 .await
114 .map_err(|_| DirectoryTableError::InvalidHeader)?,
115 Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => {
116 break;
117 }
118 Err(_) => return Err(DirectoryTableError::InvalidHeader),
119 };
120 debug!("Directory table header {:?}", header);
121 for _ in 0..header.entries + 1 {
123 let entry = EntryInternal::from_reader(&mut r).await?;
124 entries.push(Entry::from(&header, entry));
125 }
126 }
127 Ok(DirectoryTable {
128 index: entries
129 .iter()
130 .enumerate()
131 .map(|(i, e)| (index_hash(&e.name), i))
132 .into_group_map(),
133 entries,
134 })
135 }
136 #[allow(clippy::borrowed_box)]
137 pub async fn from_reader_directory(
138 directory: &Box<dyn DirectoryInode + Send + Sync>,
139 superblock: &SuperBlock,
140 mut r: impl crate::AsyncSeekBufRead,
141 ) -> Result<Self, DirectoryTableError> {
142 let loc = directory.table_location();
143 r.seek(SeekFrom::Start(
144 superblock.directory_table_start + loc.start,
145 ))
146 .await
147 .map_err(DirectoryTableError::ReadFailure)?;
148 let r = MetadataBlock::from_reader_flatten(
149 r,
150 superblock.fragment_table_start,
151 superblock.compression,
152 )
153 .await?;
154 let mut r = Box::pin(r);
155 let r2 = &mut r;
157 tokio::io::copy(&mut r2.take(loc.offset), &mut tokio::io::sink())
158 .await
159 .map_err(DirectoryTableError::ReadFailure)?;
160 let r = r.take(loc.file_size);
161 Self::from_reader(r).await
162 }
163}