use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned};
use std::fmt::{Display, Formatter};
use snafu::{OptionExt, Snafu, ensure};
#[derive(Debug, Snafu)]
#[non_exhaustive]
pub enum ReadError {
#[snafu(display("invalid version"))]
InvalidVersion,
#[snafu(display("invalid format"))]
InvalidFormat,
#[snafu(display("too many blocks for inodes"))]
TooManyInodeBlocks,
#[snafu(display("source buffer is too short to read the header"))]
ReadHeaderFailed,
#[snafu(display("source buffer is too short to read the key seed"))]
ReadKeySeedFailed,
}
use zerocopy::byteorder::little_endian::{U16, U32, U64};
const VERSION: u64 = 1;
const FORMAT: u64 = 20130315;
pub(crate) const HEADER_SIZE: usize = 0x380;
#[derive(FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)]
#[repr(C)]
pub(crate) struct PfsHeaderRaw {
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, }
#[derive(FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)]
#[repr(C)]
pub(crate) struct FlagsRaw {
fmode: u8,
clean: u8,
ronly: u8,
rsv: u8,
}
pub(crate) struct PfsHeader {
raw_header: PfsHeaderRaw,
key_seed: [u8; 16],
}
impl PfsHeader {
pub(super) fn from_bytes(data: &[u8]) -> Result<Self, ReadError> {
let (raw_header, rest) =
PfsHeaderRaw::read_from_prefix(data).map_err(|_| ReadHeaderFailedSnafu.build())?;
ensure!(raw_header.version.get() == VERSION, InvalidVersionSnafu);
ensure!(raw_header.format.get() == FORMAT, InvalidFormatSnafu);
ensure!(
raw_header.ndinodeblock.get() <= (u32::MAX as u64),
TooManyInodeBlocksSnafu
);
let key_seed_offset = 0x370 - size_of::<PfsHeaderRaw>();
let key_seed: [u8; 16] = rest
.get(key_seed_offset..key_seed_offset + 16)
.context(ReadKeySeedFailedSnafu)?
.try_into()
.unwrap();
Ok(Self {
raw_header,
key_seed,
})
}
pub fn mode(&self) -> Mode {
self.raw_header.mode
}
pub fn block_size(&self) -> u32 {
self.raw_header.block_size.get()
}
pub fn inode_count(&self) -> usize {
self.raw_header.ndinode.get() as usize
}
pub fn inode_block_count(&self) -> u32 {
self.raw_header.ndinodeblock.get() as u32
}
pub fn super_root_inode(&self) -> usize {
self.raw_header.superroot_ino.get() as usize
}
pub fn key_seed(&self) -> &[u8; 16] {
&self.key_seed
}
}
#[derive(
Clone,
Copy,
Debug,
Default,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
FromBytes,
IntoBytes,
KnownLayout,
Immutable,
Unaligned,
)]
#[repr(C)]
pub struct Mode {
flags: U16,
}
impl Mode {
#[inline]
#[must_use]
pub const fn is_signed(&self) -> bool {
self.flags.get() & 0x1 != 0
}
#[inline]
#[must_use]
pub const fn is_64bits(&self) -> bool {
self.flags.get() & 0x2 != 0
}
#[inline]
#[must_use]
pub const fn is_encrypted(&self) -> bool {
self.flags.get() & 0x4 != 0
}
}
impl Display for Mode {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
write!(f, "{:x}", self.flags.get())?;
let mut op = false;
let mut first = true;
let mut flag = |name: &str| -> std::fmt::Result {
if !op {
f.write_str(" (")?;
op = true;
}
if !first {
f.write_str(", ")?;
}
f.write_str(name)?;
first = false;
Ok(())
};
if self.is_signed() {
flag("signed")?;
}
if self.is_64bits() {
flag("64-bits")?;
}
if self.is_encrypted() {
flag("encrypted")?;
}
if op {
f.write_str(")")?;
}
Ok(())
}
}