Skip to main content

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