squashfs_async/inodes/
mod.rs1mod file;
5pub use file::FileInode;
6use file::FileInodeDeser;
7use file::{BasicFile, ExtendedFile};
8mod directory;
9use directory::{BasicDirectory, ExtendedDirectory};
10pub use directory::{DirectoryInode, DirectoryTableLocation};
11mod symlink;
12
13use std::collections::BTreeMap;
14use std::io::SeekFrom;
15
16use deser::from_reader;
17use serde::Deserialize;
18use serde_repr::Deserialize_repr;
19use tokio::io::{AsyncReadExt, AsyncSeekExt};
20use tracing::*;
21
22use super::deser;
23use super::error::InodeTableError;
24use super::metadata::MetadataBlock;
25use super::superblock::SuperBlock;
26
27#[derive(Debug, Copy, Clone, Deserialize)]
29pub struct InodeRef(u64);
30impl InodeRef {
31 fn block_start(&self) -> u64 {
32 self.0 >> 16
33 }
34 fn block_offset(&self) -> u64 {
35 self.0 & 0xFFFF
36 }
37}
38#[cfg(test)]
39mod test {
40 use super::*;
41 #[test]
42 fn inoderef_test() {
43 let iref = InodeRef(33489312);
44 assert_eq!(iref.block_start(), 511);
45 assert_eq!(iref.block_offset(), 416);
46 }
47}
48
49#[derive(Debug, Deserialize_repr)]
50#[repr(u16)]
51pub enum InodeType {
52 BasicDirectory = 1,
53 BasicFile,
54 BasicSymlink,
55 BasicBlockDevice,
56 BasicCharDevice,
57 BasicFifo,
58 BasicSocket,
59 ExtendedDirectory,
60 ExtendedFile,
61 ExtendedSymlink,
62 ExtendedBlockDevice,
63 ExtendedCharDevice,
64 ExtendedFifo,
65 ExtendedSocket,
66}
67impl InodeType {
68 pub(crate) fn is_dir(&self) -> bool {
69 matches!(self, Self::BasicDirectory | Self::ExtendedDirectory)
70 }
71}
72
73#[derive(Debug, Deserialize)]
74struct InodeHeader {
75 inode_type: InodeType,
76 _permissions: u16,
77 _uid_idx: u16,
78 _gid_idx: u16,
79 _modified_time: u32,
80 inode_number: u32,
81}
82from_reader!(InodeHeader, 16);
83
84#[derive(Default, Debug)]
86pub struct InodeTable {
87 pub directories: BTreeMap<u32, Box<dyn DirectoryInode + Send + Sync>>,
92 pub files: BTreeMap<u32, Box<dyn FileInode + Send + Sync>>,
93}
94impl std::fmt::Display for InodeTable {
95 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
96 write!(
97 f,
98 "Inode table with {} directories and {} files",
99 self.directories.len(),
100 self.files.len()
101 )
102 }
103}
104impl InodeTable {
105 pub fn ids(&self) -> impl Iterator<Item = u32> + '_ {
106 self.directories.keys().chain(self.files.keys()).copied()
107 }
108 async fn inode_table_bytes<'a>(
109 superblock: &'a SuperBlock,
110 mut r: impl crate::AsyncSeekBufRead + 'a,
111 inode_ref: Option<InodeRef>,
112 ) -> Result<impl crate::AsyncRead + 'a, InodeTableError> {
113 r.seek(SeekFrom::Start(
114 superblock.inode_table_start + inode_ref.map(|x| x.block_start()).unwrap_or_default(),
115 ))
116 .await
117 .map_err(InodeTableError::ReadFailure)?;
118 let r = MetadataBlock::from_reader_flatten(
119 r,
120 superblock.directory_table_start,
121 superblock.compression,
122 )
123 .await?;
124 let mut r = Box::pin(r);
125 if let Some(inode_ref) = inode_ref {
126 let r2 = &mut r;
127 tokio::io::copy(
128 &mut r2.take(inode_ref.block_offset()),
129 &mut tokio::io::sink(),
130 )
131 .await
132 .map_err(InodeTableError::ReadFailure)?;
133 }
134 Ok(r)
135 }
136 pub async fn read_root_inode(
137 inode_ref: InodeRef,
138 superblock: &SuperBlock,
139 mut r: impl crate::AsyncSeekBufRead,
140 ) -> Result<u32, InodeTableError> {
141 let mut r = Self::inode_table_bytes(superblock, &mut r, Some(inode_ref)).await?;
142 let header = InodeHeader::from_reader(&mut r)
143 .await
144 .map_err(|_| InodeTableError::InvalidHeader)?;
145 Ok(header.inode_number)
146 }
147 pub async fn from_reader(
148 superblock: &SuperBlock,
149 mut r: impl crate::AsyncSeekBufRead,
150 ) -> Result<Self, InodeTableError> {
151 debug!("Reading inode table");
152 let mut table = InodeTable::default();
153 let mut r = Self::inode_table_bytes(superblock, &mut r, None).await?;
154 loop {
155 let mut header = [0; 16];
156 let header = match r.read_exact(&mut header).await {
157 Ok(_) => InodeHeader::from_reader(&header[..])
158 .await
159 .map_err(|_| InodeTableError::InvalidHeader)?,
160 Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => {
161 break;
162 }
163 Err(_) => {
164 return Err(InodeTableError::InvalidHeader);
165 }
166 };
167 match header.inode_type {
168 InodeType::BasicFile => {
169 let file = BasicFile::from_reader(&mut r, superblock).await?;
170 table.files.insert(header.inode_number, Box::new(file));
171 }
172 InodeType::ExtendedFile => {
173 let file = ExtendedFile::from_reader(&mut r, superblock).await?;
174 table.files.insert(header.inode_number, Box::new(file));
175 }
176 InodeType::BasicDirectory => {
177 let dir = BasicDirectory::from_reader(&mut r)
178 .await
179 .map_err(|_| InodeTableError::InvalidEntry)?;
180 table.directories.insert(header.inode_number, Box::new(dir));
181 }
182 InodeType::ExtendedDirectory => {
183 let dir = ExtendedDirectory::from_reader(&mut r).await?;
184 table.directories.insert(header.inode_number, Box::new(dir));
185 }
186 InodeType::BasicSymlink => {
187 symlink::Symlink::from_reader(&mut r).await?;
188 }
189 _ => {
190 warn!("Skipping unsupposed inode of type {:?}", header.inode_type);
191 }
192 }
193 }
194 Ok(table)
195 }
196}