1use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned};
2
3use std::fmt::{Display, Formatter};
4
5use snafu::{OptionExt, Snafu, ensure};
6
7#[derive(Debug, Snafu)]
9#[non_exhaustive]
10pub enum ReadError {
11 #[snafu(display("invalid version"))]
12 InvalidVersion,
13
14 #[snafu(display("invalid format"))]
15 InvalidFormat,
16
17 #[snafu(display("too many blocks for inodes"))]
18 TooManyInodeBlocks,
19
20 #[snafu(display("source buffer is too short to read the header"))]
21 ReadHeaderFailed,
22
23 #[snafu(display("source buffer is too short to read the key seed"))]
24 ReadKeySeedFailed,
25}
26
27use zerocopy::byteorder::little_endian::{U16, U32, U64};
28
29const VERSION: u64 = 1;
30const FORMAT: u64 = 20130315;
31
32pub(crate) const HEADER_SIZE: usize = 0x380;
34
35#[derive(FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)]
39#[repr(C)]
40pub(crate) struct PfsHeaderRaw {
41 version: U64, format: U64, id: U64, flags: FlagsRaw, mode: Mode, unknown: U16, block_size: U32, nbackup: U32, nblock: U64, ndinode: U64, ndblock: U64, ndinodeblock: U64, superroot_ino: U64, }
55
56#[derive(FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)]
57#[repr(C)]
58pub(crate) struct FlagsRaw {
59 fmode: u8,
60 clean: u8,
61 ronly: u8,
62 rsv: u8,
63}
64
65pub(crate) struct PfsHeader {
66 raw_header: PfsHeaderRaw,
67 key_seed: [u8; 16],
68}
69
70impl PfsHeader {
71 pub(super) fn from_bytes(data: &[u8]) -> Result<Self, ReadError> {
75 let (raw_header, rest) =
76 PfsHeaderRaw::read_from_prefix(data).map_err(|_| ReadHeaderFailedSnafu.build())?;
77
78 ensure!(raw_header.version.get() == VERSION, InvalidVersionSnafu);
80
81 ensure!(raw_header.format.get() == FORMAT, InvalidFormatSnafu);
83
84 ensure!(
86 raw_header.ndinodeblock.get() <= (u32::MAX as u64),
87 TooManyInodeBlocksSnafu
88 );
89
90 let key_seed_offset = 0x370 - size_of::<PfsHeaderRaw>();
92 let key_seed: [u8; 16] = rest
93 .get(key_seed_offset..key_seed_offset + 16)
94 .context(ReadKeySeedFailedSnafu)?
95 .try_into()
96 .unwrap();
97
98 Ok(Self {
99 raw_header,
100 key_seed,
101 })
102 }
103
104 pub fn mode(&self) -> Mode {
105 self.raw_header.mode
106 }
107
108 pub fn block_size(&self) -> u32 {
109 self.raw_header.block_size.get()
110 }
111
112 pub fn inode_count(&self) -> usize {
114 self.raw_header.ndinode.get() as usize
115 }
116
117 pub fn inode_block_count(&self) -> u32 {
119 self.raw_header.ndinodeblock.get() as u32
120 }
121
122 pub fn super_root_inode(&self) -> usize {
123 self.raw_header.superroot_ino.get() as usize
124 }
125
126 pub fn key_seed(&self) -> &[u8; 16] {
127 &self.key_seed
128 }
129}
130
131#[derive(
133 Clone,
134 Copy,
135 Debug,
136 Default,
137 PartialEq,
138 Eq,
139 PartialOrd,
140 Ord,
141 Hash,
142 FromBytes,
143 IntoBytes,
144 KnownLayout,
145 Immutable,
146 Unaligned,
147)]
148#[repr(C)]
149pub struct Mode {
150 flags: U16,
151}
152
153impl Mode {
154 #[inline]
156 #[must_use]
157 pub const fn is_signed(&self) -> bool {
158 self.flags.get() & 0x1 != 0
159 }
160
161 #[inline]
162 #[must_use]
163 pub const fn is_64bits(&self) -> bool {
164 self.flags.get() & 0x2 != 0
165 }
166
167 #[inline]
169 #[must_use]
170 pub const fn is_encrypted(&self) -> bool {
171 self.flags.get() & 0x4 != 0
172 }
173}
174
175impl Display for Mode {
176 fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
177 write!(f, "{:x}", self.flags.get())?;
178
179 let mut op = false;
180
181 let mut first = true;
182
183 let mut flag = |name: &str| -> std::fmt::Result {
184 if !op {
185 f.write_str(" (")?;
186 op = true;
187 }
188
189 if !first {
190 f.write_str(", ")?;
191 }
192
193 f.write_str(name)?;
194 first = false;
195
196 Ok(())
197 };
198
199 if self.is_signed() {
200 flag("signed")?;
201 }
202
203 if self.is_64bits() {
204 flag("64-bits")?;
205 }
206
207 if self.is_encrypted() {
208 flag("encrypted")?;
209 }
210
211 if op {
212 f.write_str(")")?;
213 }
214
215 Ok(())
216 }
217}