1#[derive(Clone, Copy, PartialEq, Eq, Debug)]
14pub struct Verb(u8);
15
16impl Verb {
17 pub const fn code(self) -> u8 {
22 self.0
23 }
24}
25
26pub const OPEN: Verb = Verb(3);
27pub const CLOSE: Verb = Verb(4);
28pub const READ: Verb = Verb(5);
29pub const OPENDIR: Verb = Verb(11);
30pub const READDIR: Verb = Verb(12);
31pub const REALPATH: Verb = Verb(16);
32
33pub const INIT: u8 = 1;
35pub const VERSION: u8 = 2;
36
37pub const STATUS: u8 = 101;
39pub const HANDLE: u8 = 102;
40pub const DATA: u8 = 103;
41pub const NAME: u8 = 104;
42pub const ATTRS: u8 = 105;
43
44pub const FXF_READ: u32 = 0x0000_0001;
46
47pub const STATUS_EOF: u32 = 1;
50
51pub const STATUS_OK: u32 = 0;
53
54pub const STATUS_NO_SUCH_FILE: u32 = 2;
59
60const A_SIZE: u32 = 0x0000_0001;
61const A_UIDGID: u32 = 0x0000_0002;
62const A_PERM: u32 = 0x0000_0004;
63const A_TIME: u32 = 0x0000_0008;
64const A_EXT: u32 = 0x8000_0000;
65
66const S_IFMT: u32 = 0o170000;
67const S_IFDIR: u32 = 0o040000;
68const S_IFLNK: u32 = 0o120000;
69
70#[derive(Default)]
72pub struct Enc(Vec<u8>);
73
74impl Enc {
75 pub fn new() -> Self {
76 Self(Vec::new())
77 }
78
79 pub fn u32(mut self, v: u32) -> Self {
80 self.0.extend_from_slice(&v.to_be_bytes());
81 self
82 }
83
84 pub fn u64(mut self, v: u64) -> Self {
85 self.0.extend_from_slice(&v.to_be_bytes());
86 self
87 }
88
89 pub fn str(mut self, v: &[u8]) -> Self {
90 self.0.extend_from_slice(&(v.len() as u32).to_be_bytes());
91 self.0.extend_from_slice(v);
92 self
93 }
94
95 pub fn done(self) -> Vec<u8> {
96 self.0
97 }
98}
99
100pub struct Dec<'a> {
103 b: &'a [u8],
104 i: usize,
105}
106
107impl<'a> Dec<'a> {
108 pub fn new(b: &'a [u8]) -> Self {
109 Self { b, i: 0 }
110 }
111
112 pub fn u32(&mut self) -> Option<u32> {
113 let end = self.i.checked_add(4)?;
114 let v = u32::from_be_bytes(self.b.get(self.i..end)?.try_into().ok()?);
115 self.i = end;
116 Some(v)
117 }
118
119 pub fn u64(&mut self) -> Option<u64> {
120 let end = self.i.checked_add(8)?;
121 let v = u64::from_be_bytes(self.b.get(self.i..end)?.try_into().ok()?);
122 self.i = end;
123 Some(v)
124 }
125
126 pub fn str(&mut self) -> Option<&'a [u8]> {
127 let n = self.u32()? as usize;
128 let end = self.i.checked_add(n)?;
129 let v = self.b.get(self.i..end)?;
130 self.i = end;
131 Some(v)
132 }
133}
134
135#[derive(Debug, Clone, Copy, Default)]
140pub struct Attrs {
141 pub size: Option<u64>,
142 pub uid: Option<u32>,
143 pub gid: Option<u32>,
144 pub permissions: Option<u32>,
145 pub atime: Option<u32>,
146 pub mtime: Option<u32>,
147}
148
149impl Attrs {
150 pub fn decode(d: &mut Dec<'_>) -> Option<Self> {
151 let flags = d.u32()?;
152 let mut a = Self::default();
153 if flags & A_SIZE != 0 {
154 a.size = Some(d.u64()?);
155 }
156 if flags & A_UIDGID != 0 {
157 a.uid = Some(d.u32()?);
158 a.gid = Some(d.u32()?);
159 }
160 if flags & A_PERM != 0 {
161 a.permissions = Some(d.u32()?);
162 }
163 if flags & A_TIME != 0 {
164 a.atime = Some(d.u32()?);
165 a.mtime = Some(d.u32()?);
166 }
167 if flags & A_EXT != 0 {
168 for _ in 0..d.u32()? {
169 d.str()?;
170 d.str()?;
171 }
172 }
173 Some(a)
174 }
175
176 pub fn is_dir(&self) -> bool {
177 self.permissions.is_some_and(|p| p & S_IFMT == S_IFDIR)
178 }
179
180 pub fn is_symlink(&self) -> bool {
181 self.permissions.is_some_and(|p| p & S_IFMT == S_IFLNK)
182 }
183}
184
185#[cfg(test)]
186mod tests {
187 use super::*;
188
189 #[test]
195 fn the_verbs_are_the_six_read_ones() {
196 let mut codes = [OPEN, CLOSE, READ, OPENDIR, READDIR, REALPATH].map(Verb::code);
197 codes.sort_unstable();
198 assert_eq!(codes, [3, 4, 5, 11, 12, 16]);
199
200 for writes in [6u8, 9, 10, 13, 14, 15, 18, 20] {
204 assert!(!codes.contains(&writes), "{writes} is a write");
205 }
206 }
207
208 #[test]
211 fn the_only_open_flag_is_read() {
212 assert_eq!(FXF_READ, 1);
213 }
214
215 #[test]
216 fn roundtrips_primitives() {
217 let bytes = Enc::new().u32(7).u64(1 << 40).str(b"hi").done();
218 let mut d = Dec::new(&bytes);
219 assert_eq!(d.u32(), Some(7));
220 assert_eq!(d.u64(), Some(1 << 40));
221 assert_eq!(d.str(), Some(&b"hi"[..]));
222 assert_eq!(d.u32(), None);
223 }
224
225 #[test]
226 fn truncated_input_returns_none_instead_of_panicking() {
227 let mut d = Dec::new(&[0, 0, 0, 9, 1, 2]);
228 assert_eq!(d.str(), None);
229 }
230
231 #[test]
232 fn decodes_attrs_and_classifies_a_directory() {
233 let bytes = Enc::new()
234 .u32(A_SIZE | A_UIDGID | A_PERM | A_TIME)
235 .u64(4096)
236 .u32(1000)
237 .u32(1000)
238 .u32(0o040755)
239 .u32(111)
240 .u32(222)
241 .done();
242 let a = Attrs::decode(&mut Dec::new(&bytes)).expect("attrs");
243 assert_eq!(a.size, Some(4096));
244 assert_eq!(a.uid, Some(1000));
245 assert_eq!(a.mtime, Some(222));
246 assert!(a.is_dir());
247 assert!(!a.is_symlink());
248 }
249
250 #[test]
251 fn skips_extended_attr_pairs() {
252 let bytes = Enc::new()
253 .u32(A_SIZE | A_EXT)
254 .u64(1)
255 .u32(1)
256 .str(b"k")
257 .str(b"v")
258 .done();
259 let a = Attrs::decode(&mut Dec::new(&bytes)).expect("attrs");
260 assert_eq!(a.size, Some(1));
261 }
262}