Skip to main content

orbis_pfs/directory/
dirent.rs

1use std::io::Read;
2
3use snafu::{Snafu, ensure};
4use zerocopy::{FromBytes, Immutable, KnownLayout, little_endian::U32};
5
6/// Errors when reading a directory entry.
7#[derive(Debug, Snafu)]
8#[non_exhaustive]
9pub enum ReadError {
10    #[snafu(display("i/o failed"))]
11    IoFailed { source: std::io::Error },
12
13    #[snafu(display("data too small"))]
14    TooSmall,
15
16    #[snafu(display("end of entry"))]
17    EndOfEntry,
18}
19
20impl From<std::io::Error> for ReadError {
21    fn from(v: std::io::Error) -> Self {
22        if v.kind() == std::io::ErrorKind::UnexpectedEof {
23            ReadError::TooSmall
24        } else {
25            ReadError::IoFailed { source: v }
26        }
27    }
28}
29
30/// Raw directory entry header (16 bytes).
31///
32/// https://www.psdevwiki.com/ps4/PFS#Dirents
33#[derive(FromBytes, KnownLayout, Immutable)]
34#[repr(C)]
35struct DirentRaw {
36    ino: U32,
37    ty: U32,
38    namelen: U32,
39    entsize: U32,
40}
41
42pub(crate) struct Dirent {
43    raw: DirentRaw,
44    name: Vec<u8>,
45}
46
47impl Dirent {
48    pub const FILE: u32 = 2;
49    pub const DIRECTORY: u32 = 3;
50    pub const SELF: u32 = 4;
51    pub const PARENT: u32 = 5;
52
53    pub fn read<F: Read>(from: &mut F) -> Result<Self, ReadError> {
54        // Read fixed header.
55        let mut header_buf = [0u8; size_of::<DirentRaw>()];
56        from.read_exact(&mut header_buf)?;
57
58        let raw =
59            DirentRaw::read_from_bytes(&header_buf).expect("header buffer is correctly sized");
60
61        ensure!(raw.entsize.get() != 0, EndOfEntrySnafu);
62
63        // Read name.
64        let mut name = vec![0u8; raw.namelen.get() as usize];
65        from.read_exact(&mut name)?;
66
67        Ok(Self { raw, name })
68    }
69
70    pub const fn inode(&self) -> usize {
71        self.raw.ino.get() as usize
72    }
73
74    pub const fn ty(&self) -> u32 {
75        self.raw.ty.get()
76    }
77
78    pub const fn name(&self) -> &[u8] {
79        self.name.as_slice()
80    }
81
82    /// Returns the padding size after the name
83    pub fn padding_size(&self) -> usize {
84        self.raw.entsize.get() as usize - size_of::<DirentRaw>() - self.name.len()
85    }
86}