Skip to main content

backhand/v3/filesystem/
node.rs

1use core::fmt;
2use core::num::NonZeroUsize;
3use no_std_io2::io::Read;
4use std::path::{Path, PathBuf};
5use std::sync::{Arc, Mutex};
6
7use super::super::data::Added;
8use super::super::data::DataSize;
9use super::super::id::Id;
10use super::super::inode::{BasicFile, ExtendedFile, InodeHeader};
11use super::normalize_squashfs_path;
12use super::reader::FilesystemReaderFile;
13use crate::error::BackhandError;
14
15/// File information for Node
16#[derive(Debug, PartialEq, Eq, Default, Clone, Copy)]
17pub struct NodeHeader {
18    pub permissions: u16,
19    /// actual value
20    pub uid: u32,
21    /// actual value
22    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 = if (inode_header.uid as usize) < id_table.len() {
35            id_table[inode_header.uid as usize].num
36        } else {
37            // Fallback to root uid for out-of-bounds values
38            0
39        };
40        // Handle case where gid might be out of bounds (v3 quirk)
41        let gid = if (inode_header.gid as usize) < id_table.len() {
42            id_table[inode_header.gid as usize].num
43        } else {
44            // Fallback to root gid for out-of-bounds values
45            0
46        };
47        Ok(Self { permissions: inode_header.permissions, uid, gid, mtime: inode_header.mtime })
48    }
49}
50
51/// Filesystem Node
52#[derive(Clone, Debug)]
53pub struct Node<T> {
54    pub fullpath: PathBuf,
55    pub header: NodeHeader,
56    pub inner: InnerNode<T>,
57}
58
59impl<T> PartialEq for Node<T> {
60    fn eq(&self, other: &Self) -> bool {
61        self.fullpath.eq(&other.fullpath)
62    }
63}
64impl<T> Eq for Node<T> {}
65impl<T> PartialOrd for Node<T> {
66    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
67        Some(self.cmp(other))
68    }
69}
70impl<T> Ord for Node<T> {
71    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
72        self.fullpath.cmp(&other.fullpath)
73    }
74}
75
76impl<T> Node<T> {
77    pub fn new_root(header: NodeHeader) -> Self {
78        let fullpath = PathBuf::from("/");
79        let inner = InnerNode::Dir(SquashfsDir::default());
80        Self { fullpath, header, inner }
81    }
82}
83
84/// Filesystem node
85#[derive(Debug, Clone, PartialEq, Eq)]
86pub enum InnerNode<T> {
87    /// Either [`SquashfsFileReader`] or [`SquashfsFileWriter`]
88    File(T),
89    Symlink(SquashfsSymlink),
90    Dir(SquashfsDir),
91    CharacterDevice(SquashfsCharacterDevice),
92    BlockDevice(SquashfsBlockDevice),
93    NamedPipe,
94    Socket,
95}
96
97/// Unread file for filesystem
98#[derive(Debug, PartialEq, Eq, Clone)]
99pub enum SquashfsFileReader {
100    Basic(BasicFile),
101    Extended(ExtendedFile),
102}
103
104impl SquashfsFileReader {
105    pub fn file_len(&self) -> usize {
106        match self {
107            SquashfsFileReader::Basic(basic) => basic.file_size as usize,
108            SquashfsFileReader::Extended(extended) => extended.file_size as usize,
109        }
110    }
111
112    pub fn frag_index(&self) -> usize {
113        match self {
114            SquashfsFileReader::Basic(basic) => basic.frag as usize,
115            SquashfsFileReader::Extended(extended) => extended.frag_index as usize,
116        }
117    }
118
119    pub fn block_sizes(&self) -> &[DataSize] {
120        match self {
121            SquashfsFileReader::Basic(basic) => &basic.block_sizes,
122            SquashfsFileReader::Extended(extended) => &extended.block_sizes,
123        }
124    }
125
126    pub fn blocks_start(&self) -> u64 {
127        match self {
128            SquashfsFileReader::Basic(basic) => basic.blocks_start,
129            SquashfsFileReader::Extended(extended) => extended.blocks_start,
130        }
131    }
132
133    pub fn block_offset(&self) -> u32 {
134        match self {
135            SquashfsFileReader::Basic(basic) => basic.block_offset,
136            SquashfsFileReader::Extended(extended) => extended.block_offset,
137        }
138    }
139}
140
141/// Read file from other SquashfsFile or an user file
142pub enum SquashfsFileWriter<'a, 'b, 'c> {
143    UserDefined(Arc<Mutex<dyn Read + 'c>>),
144    SquashfsFile(FilesystemReaderFile<'a, 'b>),
145    Consumed(usize, Added),
146}
147
148impl fmt::Debug for SquashfsFileWriter<'_, '_, '_> {
149    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
150        f.debug_struct("FileWriter").finish()
151    }
152}
153
154/// Symlink for filesystem
155#[derive(Debug, PartialEq, Eq, Clone)]
156pub struct SquashfsSymlink {
157    pub link: PathBuf,
158}
159
160/// Directory for filesystem
161#[derive(Debug, PartialEq, Eq, Clone, Copy, Default)]
162pub struct SquashfsDir {}
163
164/// Character Device for filesystem
165#[derive(Debug, PartialEq, Eq, Clone, Copy)]
166pub struct SquashfsCharacterDevice {
167    pub device_number: u32,
168}
169
170/// Block Device for filesystem
171#[derive(Debug, PartialEq, Eq, Clone, Copy)]
172pub struct SquashfsBlockDevice {
173    pub device_number: u32,
174}
175
176#[derive(Debug, Clone)]
177pub struct Nodes<T> {
178    pub nodes: Vec<Node<T>>,
179}
180
181impl<T> Nodes<T> {
182    pub fn new_root(header: NodeHeader) -> Self {
183        Self { nodes: vec![Node::new_root(header)] }
184    }
185
186    pub fn root(&self) -> &Node<T> {
187        &self.nodes[0]
188    }
189
190    pub fn root_mut(&mut self) -> &mut Node<T> {
191        &mut self.nodes[0]
192    }
193
194    pub fn node_mut<S: AsRef<Path>>(&mut self, path: S) -> Option<&mut Node<T>> {
195        //the search path root prefix is optional, so remove it if present to
196        //not affect the search
197        let find_path = normalize_squashfs_path(path.as_ref()).ok()?;
198        self.nodes
199            .binary_search_by(|node| node.fullpath.cmp(&find_path))
200            .ok()
201            .map(|found| &mut self.nodes[found])
202    }
203
204    pub fn insert(&mut self, node: Node<T>) -> Result<(), BackhandError> {
205        let path = &node.fullpath;
206        let parent = node.fullpath.parent().ok_or(BackhandError::InvalidFilePath)?;
207
208        //check if the parent exists and is a dir
209        let parent = self.node_mut(parent).ok_or(BackhandError::InvalidFilePath)?;
210        match &parent.inner {
211            InnerNode::Dir(_) => {}
212            _ => return Err(BackhandError::InvalidFilePath),
213        }
214
215        match self.nodes.binary_search_by(|node| node.fullpath.as_path().cmp(path)) {
216            //file with this fullpath already exists
217            Ok(_index) => Err(BackhandError::DuplicatedFileName),
218            //file don't exists, insert it at this location
219            Err(index) => {
220                self.nodes.insert(index, node);
221                Ok(())
222            }
223        }
224    }
225
226    fn inner_children_of(&self, node_index: usize) -> Option<&[Node<T>]> {
227        let parent = &self.nodes[node_index];
228        let children_start = node_index + 1;
229        let unbounded_children = self.nodes.get(children_start..)?;
230        let children_len = unbounded_children
231            .iter()
232            .enumerate()
233            .find(|(_, node)| !node.fullpath.starts_with(&parent.fullpath))
234            .map(|(index, _)| index)
235            .unwrap_or(unbounded_children.len());
236        Some(&unbounded_children[..children_len])
237    }
238
239    pub fn node(&self, node_index: NonZeroUsize) -> Option<&Node<T>> {
240        self.nodes.get(node_index.get() - 1)
241    }
242
243    pub fn children_of(
244        &self,
245        node_index: NonZeroUsize,
246    ) -> impl Iterator<Item = (NonZeroUsize, &Node<T>)> {
247        self.inner_children_of(node_index.get() - 1).unwrap_or(&[]).iter().enumerate().map(
248            move |(index, node)| (NonZeroUsize::new(node_index.get() + index + 1).unwrap(), node),
249        )
250    }
251}