1pub const INIT: u8 = 1;
4pub const VERSION: u8 = 2;
5pub const OPEN: u8 = 3;
6pub const CLOSE: u8 = 4;
7pub const READ: u8 = 5;
8pub const LSTAT: u8 = 7;
9pub const OPENDIR: u8 = 11;
10pub const READDIR: u8 = 12;
11pub const REALPATH: u8 = 16;
12pub const STATUS: u8 = 101;
13pub const HANDLE: u8 = 102;
14pub const DATA: u8 = 103;
15pub const NAME: u8 = 104;
16pub const ATTRS: u8 = 105;
17
18pub const FXF_READ: u32 = 0x0000_0001;
20
21pub const STATUS_EOF: u32 = 1;
24
25pub const STATUS_OK: u32 = 0;
27
28pub const STATUS_NO_SUCH_FILE: u32 = 2;
33
34const A_SIZE: u32 = 0x0000_0001;
35const A_UIDGID: u32 = 0x0000_0002;
36const A_PERM: u32 = 0x0000_0004;
37const A_TIME: u32 = 0x0000_0008;
38const A_EXT: u32 = 0x8000_0000;
39
40const S_IFMT: u32 = 0o170000;
41const S_IFDIR: u32 = 0o040000;
42const S_IFLNK: u32 = 0o120000;
43
44#[derive(Default)]
46pub struct Enc(Vec<u8>);
47
48impl Enc {
49 pub fn new() -> Self {
50 Self(Vec::new())
51 }
52
53 pub fn u32(mut self, v: u32) -> Self {
54 self.0.extend_from_slice(&v.to_be_bytes());
55 self
56 }
57
58 pub fn u64(mut self, v: u64) -> Self {
59 self.0.extend_from_slice(&v.to_be_bytes());
60 self
61 }
62
63 pub fn str(mut self, v: &[u8]) -> Self {
64 self.0.extend_from_slice(&(v.len() as u32).to_be_bytes());
65 self.0.extend_from_slice(v);
66 self
67 }
68
69 pub fn done(self) -> Vec<u8> {
70 self.0
71 }
72}
73
74pub struct Dec<'a> {
77 b: &'a [u8],
78 i: usize,
79}
80
81impl<'a> Dec<'a> {
82 pub fn new(b: &'a [u8]) -> Self {
83 Self { b, i: 0 }
84 }
85
86 pub fn u32(&mut self) -> Option<u32> {
87 let end = self.i.checked_add(4)?;
88 let v = u32::from_be_bytes(self.b.get(self.i..end)?.try_into().ok()?);
89 self.i = end;
90 Some(v)
91 }
92
93 pub fn u64(&mut self) -> Option<u64> {
94 let end = self.i.checked_add(8)?;
95 let v = u64::from_be_bytes(self.b.get(self.i..end)?.try_into().ok()?);
96 self.i = end;
97 Some(v)
98 }
99
100 pub fn str(&mut self) -> Option<&'a [u8]> {
101 let n = self.u32()? as usize;
102 let end = self.i.checked_add(n)?;
103 let v = self.b.get(self.i..end)?;
104 self.i = end;
105 Some(v)
106 }
107}
108
109#[derive(Debug, Clone, Copy, Default)]
114pub struct Attrs {
115 pub size: Option<u64>,
116 pub uid: Option<u32>,
117 pub gid: Option<u32>,
118 pub permissions: Option<u32>,
119 pub atime: Option<u32>,
120 pub mtime: Option<u32>,
121}
122
123impl Attrs {
124 pub fn decode(d: &mut Dec<'_>) -> Option<Self> {
125 let flags = d.u32()?;
126 let mut a = Self::default();
127 if flags & A_SIZE != 0 {
128 a.size = Some(d.u64()?);
129 }
130 if flags & A_UIDGID != 0 {
131 a.uid = Some(d.u32()?);
132 a.gid = Some(d.u32()?);
133 }
134 if flags & A_PERM != 0 {
135 a.permissions = Some(d.u32()?);
136 }
137 if flags & A_TIME != 0 {
138 a.atime = Some(d.u32()?);
139 a.mtime = Some(d.u32()?);
140 }
141 if flags & A_EXT != 0 {
142 for _ in 0..d.u32()? {
143 d.str()?;
144 d.str()?;
145 }
146 }
147 Some(a)
148 }
149
150 pub fn is_dir(&self) -> bool {
151 self.permissions.is_some_and(|p| p & S_IFMT == S_IFDIR)
152 }
153
154 pub fn is_symlink(&self) -> bool {
155 self.permissions.is_some_and(|p| p & S_IFMT == S_IFLNK)
156 }
157}
158
159#[cfg(test)]
160mod tests {
161 use super::*;
162
163 #[test]
164 fn roundtrips_primitives() {
165 let bytes = Enc::new().u32(7).u64(1 << 40).str(b"hi").done();
166 let mut d = Dec::new(&bytes);
167 assert_eq!(d.u32(), Some(7));
168 assert_eq!(d.u64(), Some(1 << 40));
169 assert_eq!(d.str(), Some(&b"hi"[..]));
170 assert_eq!(d.u32(), None);
171 }
172
173 #[test]
174 fn truncated_input_returns_none_instead_of_panicking() {
175 let mut d = Dec::new(&[0, 0, 0, 9, 1, 2]);
176 assert_eq!(d.str(), None);
177 }
178
179 #[test]
180 fn decodes_attrs_and_classifies_a_directory() {
181 let bytes = Enc::new()
182 .u32(A_SIZE | A_UIDGID | A_PERM | A_TIME)
183 .u64(4096)
184 .u32(1000)
185 .u32(1000)
186 .u32(0o040755)
187 .u32(111)
188 .u32(222)
189 .done();
190 let a = Attrs::decode(&mut Dec::new(&bytes)).expect("attrs");
191 assert_eq!(a.size, Some(4096));
192 assert_eq!(a.uid, Some(1000));
193 assert_eq!(a.mtime, Some(222));
194 assert!(a.is_dir());
195 assert!(!a.is_symlink());
196 }
197
198 #[test]
199 fn skips_extended_attr_pairs() {
200 let bytes = Enc::new()
201 .u32(A_SIZE | A_EXT)
202 .u64(1)
203 .u32(1)
204 .str(b"k")
205 .str(b"v")
206 .done();
207 let a = Attrs::decode(&mut Dec::new(&bytes)).expect("attrs");
208 assert_eq!(a.size, Some(1));
209 }
210}