1use core::fmt;
2use core::num::NonZeroUsize;
3use no_std_io2::io::Read;
4use std::fs::File;
5use std::io::BufReader;
6use std::path::{Path, PathBuf};
7use std::sync::{Arc, Mutex};
8
9use crate::error::BackhandError;
10use crate::v4::data::Added;
11use crate::v4::filesystem::normalize_squashfs_path;
12use crate::v4::inode::{BasicFile, ExtendedFile, InodeHeader};
13use crate::{DataSize, FilesystemReaderFile, Id};
14
15#[derive(Debug, PartialEq, Eq, Default, Clone, Copy)]
17pub struct NodeHeader {
18 pub permissions: u16,
19 pub uid: u32,
21 pub gid: u32,
23 pub mtime: u32,
24}
25
26impl NodeHeader {
27 pub fn new(permissions: u16, uid: u32, gid: u32, mtime: u32) -> Self {
28 Self { permissions, uid, gid, mtime }
29 }
30}
31
32impl NodeHeader {
33 pub fn from_inode(inode_header: InodeHeader, id_table: &[Id]) -> Result<Self, BackhandError> {
34 let uid = id_table.get(inode_header.uid as usize).ok_or(BackhandError::InvalidIdTable)?;
35 let gid = id_table.get(inode_header.gid as usize).ok_or(BackhandError::InvalidIdTable)?;
36 Ok(Self {
37 permissions: inode_header.permissions,
38 uid: uid.num,
39 gid: gid.num,
40 mtime: inode_header.mtime,
41 })
42 }
43}
44
45#[derive(Clone, Debug)]
47pub struct Node<T> {
48 pub fullpath: PathBuf,
49 pub header: NodeHeader,
50 pub inner: InnerNode<T>,
51}
52
53impl<T> PartialEq for Node<T> {
54 fn eq(&self, other: &Self) -> bool {
55 self.fullpath.eq(&other.fullpath)
56 }
57}
58impl<T> Eq for Node<T> {}
59impl<T> PartialOrd for Node<T> {
60 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
61 Some(self.cmp(other))
62 }
63}
64impl<T> Ord for Node<T> {
65 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
66 self.fullpath.cmp(&other.fullpath)
67 }
68}
69
70impl<T> Node<T> {
71 pub(crate) fn new(fullpath: PathBuf, header: NodeHeader, inner: InnerNode<T>) -> Self {
72 Self { fullpath, header, inner }
73 }
74
75 pub fn new_root(header: NodeHeader) -> Self {
76 let fullpath = PathBuf::from("/");
77 let inner = InnerNode::Dir(SquashfsDir::default());
78 Self { fullpath, header, inner }
79 }
80}
81
82#[derive(Debug, Clone, PartialEq, Eq)]
84pub enum InnerNode<T> {
85 File(T),
87 Symlink(SquashfsSymlink),
88 Dir(SquashfsDir),
89 CharacterDevice(SquashfsCharacterDevice),
90 BlockDevice(SquashfsBlockDevice),
91 NamedPipe,
92 Socket,
93}
94
95#[derive(Debug, PartialEq, Eq, Clone)]
97pub enum SquashfsFileReader {
98 Basic(BasicFile),
99 Extended(ExtendedFile),
100}
101
102impl SquashfsFileReader {
103 pub fn file_len(&self) -> usize {
104 match self {
105 SquashfsFileReader::Basic(basic) => basic.file_size as usize,
106 SquashfsFileReader::Extended(extended) => extended.file_size as usize,
107 }
108 }
109
110 pub fn frag_index(&self) -> usize {
111 match self {
112 SquashfsFileReader::Basic(basic) => basic.frag_index as usize,
113 SquashfsFileReader::Extended(extended) => extended.frag_index as usize,
114 }
115 }
116
117 pub fn block_sizes(&self) -> &[DataSize] {
118 match self {
119 SquashfsFileReader::Basic(basic) => &basic.block_sizes,
120 SquashfsFileReader::Extended(extended) => &extended.block_sizes,
121 }
122 }
123
124 pub fn blocks_start(&self) -> u64 {
125 match self {
126 SquashfsFileReader::Basic(basic) => basic.blocks_start as u64,
127 SquashfsFileReader::Extended(extended) => extended.blocks_start,
128 }
129 }
130
131 pub fn block_offset(&self) -> u32 {
132 match self {
133 SquashfsFileReader::Basic(basic) => basic.block_offset,
134 SquashfsFileReader::Extended(extended) => extended.block_offset,
135 }
136 }
137}
138
139#[derive(Debug, Clone, PartialEq, Eq)]
145pub struct LazyFile {
146 path: PathBuf,
147}
148
149impl LazyFile {
150 pub fn new(path: impl Into<PathBuf>) -> Self {
151 Self { path: path.into() }
152 }
153
154 pub fn path(&self) -> &Path {
156 &self.path
157 }
158
159 pub(crate) fn open(&self) -> Result<BufReader<File>, BackhandError> {
161 Ok(BufReader::new(File::open(&self.path)?))
162 }
163}
164
165pub enum SquashfsFileWriter<'a, 'b, 'c> {
167 UserDefined(Arc<Mutex<dyn Read + 'c>>),
168 LazyFile(LazyFile),
170 SquashfsFile(FilesystemReaderFile<'a, 'b>),
171 Consumed(usize, Added),
172}
173
174impl fmt::Debug for SquashfsFileWriter<'_, '_, '_> {
175 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
176 f.debug_struct("FileWriter").finish()
177 }
178}
179
180#[derive(Debug, PartialEq, Eq, Clone)]
182pub struct SquashfsSymlink {
183 pub link: PathBuf,
184}
185
186#[derive(Debug, PartialEq, Eq, Clone, Copy, Default)]
188pub struct SquashfsDir {}
189
190#[derive(Debug, PartialEq, Eq, Clone, Copy)]
192pub struct SquashfsCharacterDevice {
193 pub device_number: u32,
194}
195
196#[derive(Debug, PartialEq, Eq, Clone, Copy)]
198pub struct SquashfsBlockDevice {
199 pub device_number: u32,
200}
201
202#[derive(Debug, Clone)]
203pub struct Nodes<T> {
204 pub nodes: Vec<Node<T>>,
205}
206
207impl<T> Nodes<T> {
208 pub fn new_root(header: NodeHeader) -> Self {
209 Self { nodes: vec![Node::new_root(header)] }
210 }
211
212 pub fn root(&self) -> &Node<T> {
213 &self.nodes[0]
214 }
215
216 pub fn root_mut(&mut self) -> &mut Node<T> {
217 &mut self.nodes[0]
218 }
219
220 pub fn node_mut<S: AsRef<Path>>(&mut self, path: S) -> Option<&mut Node<T>> {
221 let find_path = normalize_squashfs_path(path.as_ref()).ok()?;
224 self.nodes
225 .binary_search_by(|node| node.fullpath.cmp(&find_path))
226 .ok()
227 .map(|found| &mut self.nodes[found])
228 }
229
230 pub fn insert(&mut self, node: Node<T>) -> Result<(), BackhandError> {
231 let path = &node.fullpath;
232 let parent = node.fullpath.parent().ok_or(BackhandError::InvalidFilePath)?;
233
234 let parent = self.node_mut(parent).ok_or(BackhandError::InvalidFilePath)?;
236 match &parent.inner {
237 InnerNode::Dir(_) => {}
238 _ => return Err(BackhandError::InvalidFilePath),
239 }
240
241 match self.nodes.binary_search_by(|node| node.fullpath.as_path().cmp(path)) {
242 Ok(_index) => Err(BackhandError::DuplicatedFileName),
244 Err(index) => {
246 self.nodes.insert(index, node);
247 Ok(())
248 }
249 }
250 }
251
252 fn inner_children_of(&self, node_index: usize) -> Option<&[Node<T>]> {
253 let parent = &self.nodes[node_index];
254 let children_start = node_index + 1;
255 let unbounded_children = self.nodes.get(children_start..)?;
256 let children_len = unbounded_children
257 .iter()
258 .enumerate()
259 .find(|(_, node)| !node.fullpath.starts_with(&parent.fullpath))
260 .map(|(index, _)| index)
261 .unwrap_or(unbounded_children.len());
262 Some(&unbounded_children[..children_len])
263 }
264
265 pub fn node(&self, node_index: NonZeroUsize) -> Option<&Node<T>> {
266 self.nodes.get(node_index.get() - 1)
267 }
268
269 pub fn children_of(
270 &self,
271 node_index: NonZeroUsize,
272 ) -> impl Iterator<Item = (NonZeroUsize, &Node<T>)> {
273 self.inner_children_of(node_index.get() - 1).unwrap_or(&[]).iter().enumerate().map(
274 move |(index, node)| (NonZeroUsize::new(node_index.get() + index + 1).unwrap(), node),
275 )
276 }
277}