1use crate::header::Mode;
31
32use self::directory::Directory;
33use self::header::PfsHeader;
34use self::inode::Inode;
35use aes::Aes128;
36use aes::cipher::KeyInit;
37use snafu::{OptionExt, ResultExt, Snafu, ensure};
38use std::sync::Arc;
39use xts_mode::Xts128;
40
41pub mod directory;
42pub mod file;
43pub mod header;
44pub mod image;
45pub mod inode;
46pub mod pfsc;
47
48#[derive(Debug, Snafu)]
54#[non_exhaustive]
55pub enum OpenError {
56 #[snafu(display("invalid block size"))]
57 InvalidBlockSize,
58
59 #[snafu(display("cannot parse inode"))]
60 ParseInodeFailed { source: inode::FromRawError },
61
62 #[snafu(display("cannot read block #{block}"))]
63 ReadBlockFailed { block: u32, source: std::io::Error },
64
65 #[snafu(display("invalid super-root"))]
66 InvalidSuperRoot,
67
68 #[snafu(display("cannot load block map for inode #{inode}"))]
69 LoadBlockMapFailed {
70 inode: usize,
71 source: inode::LoadBlocksError,
72 },
73}
74
75#[derive(Debug, Snafu)]
77#[snafu(module)]
78#[non_exhaustive]
79pub enum OpenSliceError {
80 #[snafu(display("cannot parse header"))]
81 ReadHeaderFailed { source: header::ReadError },
82
83 #[snafu(display("block size too small for encryption"))]
84 EncryptionBlockSizeTooSmall,
85
86 #[snafu(display("encryption required but no EKPFS is provided"))]
87 EmptyEkpfs,
88
89 #[snafu(transparent)]
90 Open { source: OpenError },
91}
92
93#[derive(Debug, Snafu)]
95#[snafu(module)]
96#[non_exhaustive]
97pub enum OpenImageError {
98 #[snafu(display("cannot read header from image"))]
99 ReadHeaderIoFailed { source: std::io::Error },
100
101 #[snafu(display("cannot parse header"))]
102 ReadHeaderFailed { source: header::ReadError },
103
104 #[snafu(display("unsupported mode: {mode}"))]
105 UnsupportedMode { mode: Mode },
106
107 #[snafu(transparent)]
108 Open { source: OpenError },
109}
110
111#[must_use]
117pub struct Pfs<'a> {
118 image: Box<dyn image::Image + 'a>,
119 inodes: Vec<Inode>,
120 block_maps: Vec<Vec<u32>>,
123 root: usize,
124 block_size: u32,
125 data: Option<&'a [u8]>,
128}
129
130unsafe impl Send for Pfs<'_> {}
136unsafe impl Sync for Pfs<'_> {}
137
138impl<'a> std::fmt::Debug for Pfs<'a> {
139 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
140 f.debug_struct("Pfs")
141 .field("inode_count", &self.inodes.len())
142 .field("root", &self.root)
143 .field("block_size", &self.block_size)
144 .field("slice_backed", &self.data.is_some())
145 .finish_non_exhaustive()
146 }
147}
148
149impl<'a> Pfs<'a> {
150 #[must_use]
154 pub fn inode_count(&self) -> usize {
155 self.inodes.len()
156 }
157
158 pub fn root(self: &Arc<Self>) -> Directory<'a> {
178 Directory::new(self.clone(), self.root)
179 }
180
181 #[must_use]
183 pub fn block_size(&self) -> u32 {
184 self.block_size
185 }
186
187 pub(crate) fn image(&self) -> &dyn image::Image {
190 &*self.image
191 }
192
193 pub(crate) fn inode(&self, index: usize) -> &Inode {
194 &self.inodes[index]
195 }
196
197 pub(crate) fn block_map(&self, inode: usize) -> &[u32] {
198 &self.block_maps[inode]
199 }
200}
201
202pub fn open_slice<'a>(
237 data: &'a [u8],
238 ekpfs: Option<&[u8]>,
239) -> Result<Arc<Pfs<'a>>, OpenSliceError> {
240 let header = PfsHeader::from_bytes(data).context(open_slice_error::ReadHeaderFailedSnafu)?;
242
243 let (image, backing_data): (Box<dyn image::Image + 'a>, Option<&'a [u8]>) =
245 if header.mode().is_encrypted() {
246 ensure!(
247 (header.block_size() as usize) >= image::XTS_BLOCK_SIZE,
248 open_slice_error::EncryptionBlockSizeTooSmallSnafu
249 );
250
251 let ekpfs_bytes = ekpfs.context(open_slice_error::EmptyEkpfsSnafu)?;
252
253 let key_seed = header.key_seed();
254 let (data_key, tweak_key) = image::get_xts_keys(ekpfs_bytes, key_seed);
255 let cipher_1 = Aes128::new((&data_key).into());
256 let cipher_2 = Aes128::new((&tweak_key).into());
257
258 let enc = image::EncryptedSlice::new(
259 data,
260 Xts128::<Aes128>::new(cipher_1, cipher_2),
261 (header.block_size() as usize) / image::XTS_BLOCK_SIZE,
262 );
263
264 (Box::new(enc), None)
265 } else {
266 (Box::new(image::UnencryptedSlice::new(data)), Some(data))
267 };
268
269 Ok(open_inner(image, &header, backing_data)?)
270}
271
272pub fn open_image<'a>(image: impl image::Image + 'a) -> Result<Arc<Pfs<'a>>, OpenImageError> {
302 let mut header_buf = [0u8; header::HEADER_SIZE];
304
305 image
306 .read_exact_at(0, &mut header_buf)
307 .context(open_image_error::ReadHeaderIoFailedSnafu)?;
308
309 let header =
310 PfsHeader::from_bytes(&header_buf).context(open_image_error::ReadHeaderFailedSnafu)?;
311
312 ensure!(
313 !header.mode().is_encrypted(),
314 open_image_error::UnsupportedModeSnafu {
315 mode: header.mode()
316 }
317 );
318
319 Ok(open_inner(Box::new(image), &header, None)?)
320}
321
322fn open_inner<'a>(
327 image: Box<dyn image::Image + 'a>,
328 header: &PfsHeader,
329 data: Option<&'a [u8]>,
330) -> Result<Arc<Pfs<'a>>, OpenError> {
331 let mode = header.mode();
332 let block_size = header.block_size();
333 let inode_count = header.inode_count();
334 let inode_block_count = header.inode_block_count();
335 let super_root = header.super_root_inode();
336
337 ensure!(
338 block_size > 0 && block_size.is_power_of_two(),
339 InvalidBlockSizeSnafu
340 );
341
342 let mut inodes: Vec<Inode> = Vec::with_capacity(inode_count);
344 let mut block_buf = vec![0; block_size as usize];
345
346 for block_num in 0..inode_block_count {
347 let offset = (block_size as u64) + (block_num as u64) * (block_size as u64);
348
349 image
350 .read_exact_at(offset, &mut block_buf)
351 .context(ReadBlockFailedSnafu { block: block_num })?;
352
353 if parse_inodes_from_block(&block_buf, mode, &mut inodes, inode_count)? {
354 break;
355 }
356 }
357
358 ensure!(super_root < inodes.len(), InvalidSuperRootSnafu);
359
360 let block_maps = precompute_block_maps(&inodes, image.as_ref(), block_size)?;
362
363 Ok(Arc::new(Pfs {
364 image,
365 inodes,
366 block_maps,
367 root: super_root,
368 block_size,
369 data,
370 }))
371}
372
373fn precompute_block_maps(
375 inodes: &[Inode],
376 image: &dyn image::Image,
377 block_size: u32,
378) -> Result<Vec<Vec<u32>>, OpenError> {
379 let mut maps = Vec::with_capacity(inodes.len());
380
381 for (i, inode) in inodes.iter().enumerate() {
382 let block_map = inode
383 .load_block_map(image, block_size)
384 .context(LoadBlockMapFailedSnafu { inode: i })?;
385 maps.push(block_map);
386 }
387
388 Ok(maps)
389}
390
391fn parse_inodes_from_block(
396 block_data: &[u8],
397 mode: Mode,
398 inodes: &mut Vec<Inode>,
399 inode_count: usize,
400) -> Result<bool, OpenError> {
401 let reader = if mode.is_signed() {
402 Inode::from_raw32_signed
403 } else {
404 Inode::from_raw32_unsigned
405 };
406
407 let mut src = block_data;
408
409 while inodes.len() < inode_count {
410 let inode = match reader(inodes.len(), &mut src) {
411 Ok(v) => v,
412 Err(inode::FromRawError::TooSmall) => {
413 return Ok(false);
414 }
415 err => err.context(ParseInodeFailedSnafu)?,
416 };
417
418 inodes.push(inode);
419 }
420
421 Ok(true)
422}