Skip to main content

strat9_syscall/
dirent.rs

1use core::fmt;
2pub use strat9_abi::data::DirentHeader;
3
4#[derive(Debug, Clone, Copy)]
5/// Decoded directory entry with an inline fixed-size name buffer.
6pub struct Dirent {
7    pub ino: u64,
8    pub type_: u8,
9    pub name_len: u16,
10    pub name: [u8; 256],
11}
12
13impl Dirent {
14    /// Create a `Dirent` from raw inode/type/name fields.
15    pub fn new(ino: u64, type_: u8, name: &[u8]) -> Self {
16        let mut d = Self {
17            ino,
18            type_,
19            name_len: 0,
20            name: [0; 256],
21        };
22        let len = core::cmp::min(name.len(), 255);
23        d.name[..len].copy_from_slice(&name[..len]);
24        d.name_len = len as u16;
25        d
26    }
27
28    /// Return the valid file name bytes (without trailing NUL).
29    pub fn name(&self) -> &[u8] {
30        &self.name[..self.name_len as usize]
31    }
32}
33
34impl fmt::Display for Dirent {
35    /// Implements fmt.
36    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37        write!(
38            f,
39            "{}",
40            core::str::from_utf8(self.name()).unwrap_or("<invalid>")
41        )
42    }
43}
44
45/// Iterator over packed kernel `getdents` records.
46pub struct DirentIter<'a> {
47    buf: &'a [u8],
48    offset: usize,
49}
50
51impl<'a> DirentIter<'a> {
52    /// Create an iterator over the first `valid_len` bytes of `buf`.
53    pub fn new(buf: &'a [u8], valid_len: usize) -> Self {
54        let valid_len = core::cmp::min(valid_len, buf.len());
55        Self {
56            buf: &buf[..valid_len],
57            offset: 0,
58        }
59    }
60}
61
62impl<'a> Iterator for DirentIter<'a> {
63    type Item = Dirent;
64
65    /// Implements next.
66    fn next(&mut self) -> Option<Dirent> {
67        if self.offset + DirentHeader::SIZE > self.buf.len() {
68            return None;
69        }
70        let hdr = &self.buf[self.offset..self.offset + DirentHeader::SIZE];
71        let ino = u64::from_le_bytes(hdr[0..8].try_into().ok()?);
72        let file_type = hdr[8];
73        let name_len = u16::from_le_bytes(hdr[9..11].try_into().ok()?);
74        let _padding = hdr[11];
75        let name_start = self.offset + DirentHeader::SIZE;
76        let name_end = name_start + name_len as usize;
77        if name_end + 1 > self.buf.len() {
78            return None;
79        }
80        let mut d = Dirent::new(ino, file_type, &self.buf[name_start..name_end]);
81        d.name_len = core::cmp::min(name_len, 255);
82        self.offset = name_end + 1; // skip NUL
83        Some(d)
84    }
85}