syscall/
dirent.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
use core::{
    mem::size_of,
    ops::{Deref, DerefMut},
    slice,
};

use crate::{
    error::{Error, Result, EINVAL},
    ENAMETOOLONG,
};

#[derive(Clone, Copy, Debug, Default)]
#[repr(packed)]
pub struct DirentHeader {
    pub inode: u64,
    /// A filesystem-specific opaque value used to uniquely identify directory entries. This value,
    /// in the last returned entry from a SYS_GETDENTS invocation, shall be passed to the next
    /// call.
    pub next_opaque_id: u64,
    // This struct intentionally does not include a "next" offset field, unlike Linux, to easily
    // guarantee the iterator will be reasonably deterministic, even if the scheme is adversarial.
    pub record_len: u16,
    /// A `DirentKind`.
    ///
    /// May not be directly available (Unspecified), and if so needs to be looked using fstat.
    pub kind: u8,
}

impl Deref for DirentHeader {
    type Target = [u8];
    fn deref(&self) -> &[u8] {
        unsafe { slice::from_raw_parts(self as *const Self as *const u8, size_of::<Self>()) }
    }
}

impl DerefMut for DirentHeader {
    fn deref_mut(&mut self) -> &mut [u8] {
        unsafe { slice::from_raw_parts_mut(self as *mut Self as *mut u8, size_of::<Self>()) }
    }
}
#[derive(Clone, Copy, Debug, Default)]
#[repr(u8)]
pub enum DirentKind {
    #[default]
    Unspecified = 0,

    Regular = 1,
    Directory = 2,
    Symlink = 3,
    BlockDev = 4,
    CharDev = 5,
    Socket = 6,
}
impl DirentKind {
    // TODO: derive(FromPrimitive)
    pub fn try_from_raw(raw: u8) -> Option<Self> {
        Some(match raw {
            0 => Self::Unspecified,

            1 => Self::Regular,
            2 => Self::Directory,
            3 => Self::Symlink,
            4 => Self::BlockDev,
            5 => Self::CharDev,
            6 => Self::Socket,

            _ => return None,
        })
    }
}

pub struct DirentIter<'a>(&'a [u8]);

impl<'a> DirentIter<'a> {
    pub const fn new(buffer: &'a [u8]) -> Self {
        Self(buffer)
    }
}
#[derive(Debug)]
pub struct Invalid;

impl<'a> Iterator for DirentIter<'a> {
    type Item = Result<(&'a DirentHeader, &'a [u8]), Invalid>;

    fn next(&mut self) -> Option<Self::Item> {
        if self.0.len() < size_of::<DirentHeader>() {
            return None;
        }
        let header = unsafe { &*(self.0.as_ptr().cast::<DirentHeader>()) };
        if self.0.len() < usize::from(header.record_len) {
            return Some(Err(Invalid));
        }
        let (this, remaining) = self.0.split_at(usize::from(header.record_len));
        self.0 = remaining;

        let name_and_nul = &this[size_of::<DirentHeader>()..];
        let name = &name_and_nul[..name_and_nul.len() - 1];

        Some(Ok((header, name)))
    }
}

pub struct DirentBuf<B> {
    buffer: B,

    // Exists in order to allow future extensions to the DirentHeader struct.

    // TODO: Might add an upper bound to protect against cache miss DoS. The kernel currently
    // forbids any other value than size_of::<DirentHeader>().
    header_size: u16,

    written: usize,
}
/// Abstraction between &mut [u8] and the kernel's UserSliceWo.
pub trait Buffer<'a>: Sized + 'a {
    fn empty() -> Self;
    fn length(&self) -> usize;

    /// Split all of `self` into two disjoint contiguous subbuffers of lengths `index` and `length
    /// - index` respectively.
    ///
    /// Returns None if and only if `index > length`.
    fn split_at(self, index: usize) -> Option<[Self; 2]>;

    /// Copy from `src`, lengths must match exactly.
    ///
    /// Allowed to overwrite subsequent buffer space, for performance reasons. Can be changed in
    /// the future if too restrictive.
    fn copy_from_slice_exact(self, src: &[u8]) -> Result<()>;

    /// Write zeroes to this part of the buffer.
    ///
    /// Allowed to overwrite subsequent buffer space, for performance reasons. Can be changed in
    /// the future if too restrictive.
    fn zero_out(self) -> Result<()>;
}
impl<'a> Buffer<'a> for &'a mut [u8] {
    fn empty() -> Self {
        &mut []
    }
    fn length(&self) -> usize {
        self.len()
    }

    fn split_at(self, index: usize) -> Option<[Self; 2]> {
        self.split_at_mut_checked(index).map(|(a, b)| [a, b])
    }
    fn copy_from_slice_exact(self, src: &[u8]) -> Result<()> {
        self.copy_from_slice(src);
        Ok(())
    }
    fn zero_out(self) -> Result<()> {
        self.fill(0);
        Ok(())
    }
}

pub struct DirEntry<'name> {
    pub inode: u64,
    pub next_opaque_id: u64,
    pub name: &'name str,
    pub kind: DirentKind,
}

impl<'a, B: Buffer<'a>> DirentBuf<B> {
    pub fn new(buffer: B, header_size: u16) -> Option<Self> {
        if usize::from(header_size) < size_of::<DirentHeader>() {
            return None;
        }

        Some(Self {
            buffer,
            header_size,
            written: 0,
        })
    }
    pub fn entry(&mut self, entry: DirEntry<'_>) -> Result<()> {
        let name16 = u16::try_from(entry.name.len()).map_err(|_| Error::new(EINVAL))?;
        let record_len = self
            .header_size
            .checked_add(name16)
            // XXX: NUL byte. Unfortunately this is probably the only performant way to be
            // compatible with C.
            .and_then(|l| l.checked_add(1))
            .ok_or(Error::new(ENAMETOOLONG))?;

        let [this, remaining] = core::mem::replace(&mut self.buffer, B::empty())
            .split_at(usize::from(record_len))
            .ok_or(Error::new(EINVAL))?;

        let [this_header_variable, this_name_and_nul] = this
            .split_at(usize::from(self.header_size))
            .expect("already know header_size + ... >= header_size");

        let [this_name, this_name_nul] = this_name_and_nul
            .split_at(usize::from(name16))
            .expect("already know name.len() <= name.len() + 1");

        // Every write here is currently sequential, allowing the buffer trait to do optimizations
        // where subbuffer writes are out-of-bounds (but inside the total buffer).

        let [this_header, this_header_extra] = this_header_variable
            .split_at(size_of::<DirentHeader>())
            .expect("already checked header_size <= size_of Header");

        this_header.copy_from_slice_exact(&DirentHeader {
            record_len,
            next_opaque_id: entry.next_opaque_id,
            inode: entry.inode,
            kind: entry.kind as u8,
        })?;
        this_header_extra.zero_out()?;
        this_name.copy_from_slice_exact(entry.name.as_bytes())?;
        this_name_nul.copy_from_slice_exact(&[0])?;

        self.written += usize::from(record_len);
        self.buffer = remaining;

        Ok(())
    }
    pub fn finalize(self) -> usize {
        self.written
    }
}