1#[macro_use]
5mod macros;
6
7use crate::protocol::*;
8use std::io;
9use zerocopy::FromBytes;
10use zerocopy::Immutable;
11use zerocopy::IntoBytes;
12use zerocopy::KnownLayout;
13
14fuse_operations! {
16 FUSE_LOOKUP Lookup name:name;
17 FUSE_FORGET Forget arg:fuse_forget_in;
18 FUSE_GETATTR GetAttr arg:fuse_getattr_in;
19 FUSE_SETATTR SetAttr arg:fuse_setattr_in;
20 FUSE_READLINK ReadLink;
21 FUSE_SYMLINK Symlink name:name target:str;
22 FUSE_MKNOD MkNod arg:fuse_mknod_in name:name;
23 FUSE_MKDIR MkDir arg:fuse_mkdir_in name:name;
24 FUSE_UNLINK Unlink name:name;
25 FUSE_RMDIR RmDir name:name;
26 FUSE_RENAME Rename arg:fuse_rename_in name:name new_name:name;
27 FUSE_LINK Link arg:fuse_link_in name:name;
28 FUSE_OPEN Open arg:fuse_open_in;
29 FUSE_READ Read arg:fuse_read_in;
30 FUSE_WRITE Write arg:fuse_write_in data:[u8; arg.size];
31 FUSE_STATFS StatFs;
32 FUSE_RELEASE Release arg:fuse_release_in;
33 FUSE_FSYNC FSync arg:fuse_fsync_in;
34 FUSE_SETXATTR SetXAttr arg:fuse_setxattr_in name:str value:[u8; arg.size];
35 FUSE_GETXATTR GetXAttr arg:fuse_getxattr_in name:str;
36 FUSE_LISTXATTR ListXAttr arg:fuse_getxattr_in;
37 FUSE_REMOVEXATTR RemoveXAttr name:str;
38 FUSE_FLUSH Flush arg:fuse_flush_in;
39 FUSE_INIT Init arg:fuse_init_in;
40 FUSE_OPENDIR OpenDir arg:fuse_open_in;
41 FUSE_READDIR ReadDir arg:fuse_read_in;
42 FUSE_RELEASEDIR ReleaseDir arg:fuse_release_in;
43 FUSE_FSYNCDIR FSyncDir arg:fuse_fsync_in;
44 FUSE_GETLK GetLock arg:fuse_lk_in;
45 FUSE_SETLK SetLock arg:fuse_lk_in;
46 FUSE_SETLKW SetLockSleep arg:fuse_lk_in;
47 FUSE_ACCESS Access arg:fuse_access_in;
48 FUSE_CREATE Create arg:fuse_create_in name:str;
49 FUSE_INTERRUPT Interrupt arg:fuse_interrupt_in;
50 FUSE_BMAP BMap arg:fuse_bmap_in;
51 FUSE_DESTROY Destroy;
52 FUSE_IOCTL Ioctl arg:fuse_ioctl_in data:[u8; arg.in_size];
53 FUSE_POLL Poll arg:fuse_poll_in;
54 FUSE_NOTIFY_REPLY NotifyReply arg:fuse_notify_retrieve_in data:[u8];
55 FUSE_BATCH_FORGET BatchForget arg:fuse_batch_forget_in nodes:[u8];
56 FUSE_FALLOCATE FAllocate arg:fuse_fallocate_in;
57 FUSE_READDIRPLUS ReadDirPlus arg:fuse_read_in;
58 FUSE_RENAME2 Rename2 arg:fuse_rename2_in name:name new_name:name;
59 FUSE_LSEEK LSeek arg:fuse_lseek_in;
60 FUSE_COPY_FILE_RANGE CopyFileRange arg:fuse_copy_file_range_in;
61 FUSE_SETUPMAPPING SetupMapping arg:fuse_setupmapping_in;
62 FUSE_REMOVEMAPPING RemoveMapping arg:fuse_removemapping_in mappings:[u8];
63 FUSE_SYNCFS SyncFs _arg:fuse_syncfs_in;
64 FUSE_CANONICAL_PATH CanonicalPath;
65}
66
67pub struct Request {
69 header: fuse_in_header,
70 operation: FuseOperation,
71}
72
73impl Request {
74 pub fn new(mut reader: impl RequestReader) -> lx::Result<Self> {
76 let header: fuse_in_header = reader.read_type()?;
77 let operation = Self::read_operation(&header, reader);
78
79 Ok(Self { header, operation })
80 }
81
82 pub fn opcode(&self) -> u32 {
84 self.header.opcode
85 }
86
87 pub fn unique(&self) -> u64 {
89 self.header.unique
90 }
91
92 pub fn node_id(&self) -> u64 {
94 self.header.nodeid
95 }
96
97 pub fn uid(&self) -> lx::uid_t {
99 self.header.uid
100 }
101
102 pub fn gid(&self) -> lx::gid_t {
104 self.header.gid
105 }
106
107 pub fn pid(&self) -> u32 {
109 self.header.pid
110 }
111
112 pub fn operation(&self) -> &FuseOperation {
114 &self.operation
115 }
116
117 pub fn log(&self) {
119 tracing::trace!(
120 unique = self.unique(),
121 node_id = self.node_id(),
122 uid = self.uid(),
123 gid = self.gid(),
124 pid = self.pid(),
125 operation = ?self.operation,
126 "Request",
127 );
128 }
129
130 fn read_operation(header: &fuse_in_header, reader: impl RequestReader) -> FuseOperation {
131 if header.len as usize > reader.remaining_len() + size_of_val(header) {
132 tracing::error!(
133 opcode = header.opcode,
134 unique = header.unique,
135 header_len = header.len,
136 len = reader.remaining_len() + size_of_val(header),
137 "Invalid message length",
138 );
139
140 return FuseOperation::Invalid;
141 }
142
143 match FuseOperation::read(header.opcode, reader) {
144 Ok(operation) => operation,
145 Err(e) => {
146 tracing::error!(
147 opcode = header.opcode,
148 unique = header.unique,
149 error = &e as &dyn std::error::Error,
150 "Invalid message payload",
151 );
152
153 FuseOperation::Invalid
154 }
155 }
156 }
157}
158
159pub trait RequestReader: io::Read {
161 fn read_until(&mut self, byte: u8) -> lx::Result<Vec<u8>>;
168
169 fn remaining_len(&self) -> usize;
171
172 fn read_count(&mut self, count: usize) -> lx::Result<Box<[u8]>> {
174 let mut buffer = vec![0u8; count];
175 self.read_exact(&mut buffer)?;
176 Ok(buffer.into_boxed_slice())
177 }
178
179 fn read_all(&mut self) -> lx::Result<Box<[u8]>> {
181 self.read_count(self.remaining_len())
182 }
183
184 fn read_type<T: IntoBytes + FromBytes + Immutable + KnownLayout>(&mut self) -> lx::Result<T> {
186 let mut value: T = T::new_zeroed();
187 self.read_exact(value.as_mut_bytes())?;
188 Ok(value)
189 }
190
191 fn string(&mut self) -> lx::Result<lx::LxString> {
193 let buffer = self.read_until(b'\0')?;
194 Ok(lx::LxString::from_vec(buffer))
195 }
196
197 fn name(&mut self) -> lx::Result<lx::LxString> {
199 let name = self.string()?;
200 if name.is_empty() || name == "." || name == ".." || name.as_bytes().contains(&b'/') {
201 return Err(lx::Error::EINVAL);
202 }
203
204 Ok(name)
205 }
206}
207
208impl RequestReader for &[u8] {
209 fn read_until(&mut self, byte: u8) -> lx::Result<Vec<u8>> {
210 let length = self
211 .iter()
212 .position(|&c| c == byte)
213 .ok_or(lx::Error::EINVAL)?;
214
215 let result = Vec::from(&self[..length]);
216 *self = &self[length + 1..];
217 Ok(result)
218 }
219
220 fn remaining_len(&self) -> usize {
221 self.len()
222 }
223}
224
225#[cfg(test)]
226pub(crate) mod tests {
227 use super::*;
228
229 #[test]
230 fn parse_init() {
231 let request = Request::new(FUSE_INIT_REQUEST).unwrap();
232 check_header(&request, 1, FUSE_INIT, 0);
233 if let FuseOperation::Init { arg } = request.operation {
234 assert_eq!(arg.major, 7);
235 assert_eq!(arg.minor, 27);
236 assert_eq!(arg.max_readahead, 131072);
237 assert_eq!(arg.flags, 0x3FFFFB);
238 } else {
239 panic!("Incorrect operation {:?}", request.operation);
240 }
241 }
242
243 #[test]
244 fn parse_get_attr() {
245 let request = Request::new(FUSE_GETATTR_REQUEST).unwrap();
246 check_header(&request, 2, FUSE_GETATTR, 1);
247 if let FuseOperation::GetAttr { arg } = request.operation {
248 assert_eq!(arg.fh, 0);
249 assert_eq!(arg.getattr_flags, 0);
250 } else {
251 panic!("Incorrect operation {:?}", request.operation);
252 }
253 }
254
255 #[test]
256 fn parse_lookup() {
257 let request = Request::new(FUSE_LOOKUP_REQUEST).unwrap();
258 check_header(&request, 3, FUSE_LOOKUP, 1);
259 if let FuseOperation::Lookup { name } = request.operation {
260 assert_eq!(name, "hello");
261 } else {
262 panic!("Incorrect operation {:?}", request.operation);
263 }
264 }
265
266 #[test]
267 fn parse_open() {
268 let request = Request::new(FUSE_OPEN_REQUEST).unwrap();
269 check_header(&request, 4, FUSE_OPEN, 2);
270 if let FuseOperation::Open { arg } = request.operation {
271 assert_eq!(arg.flags, 0x8000);
272 } else {
273 panic!("Incorrect operation {:?}", request.operation);
274 }
275 }
276
277 #[test]
278 fn parse_read() {
279 let request = Request::new(FUSE_READ_REQUEST).unwrap();
280 check_header(&request, 5, FUSE_READ, 2);
281 if let FuseOperation::Read { arg } = request.operation {
282 assert_eq!(arg.fh, 1);
283 assert_eq!(arg.offset, 0);
284 assert_eq!(arg.size, 4096);
285 assert_eq!(arg.read_flags, 0);
286 assert_eq!(arg.lock_owner, 0);
287 assert_eq!(arg.flags, 0x8000);
288 } else {
289 panic!("Incorrect operation {:?}", request.operation);
290 }
291 }
292
293 #[test]
294 fn parse_flush() {
295 let request = Request::new(FUSE_FLUSH_REQUEST).unwrap();
296 check_header(&request, 7, FUSE_FLUSH, 2);
297 if let FuseOperation::Flush { arg } = request.operation {
298 assert_eq!(arg.fh, 1);
299 assert_eq!(arg.lock_owner, 13021892616250331871);
302 } else {
303 panic!("Incorrect operation {:?}", request.operation);
304 }
305 }
306
307 #[test]
308 fn parse_release() {
309 let request = Request::new(FUSE_RELEASE_REQUEST).unwrap();
310 check_header(&request, 8, FUSE_RELEASE, 2);
311 if let FuseOperation::Release { arg } = request.operation {
312 assert_eq!(arg.fh, 1);
313 assert_eq!(arg.flags, 0x8000);
314 assert_eq!(arg.release_flags, 0);
315 assert_eq!(arg.lock_owner, 0);
316 } else {
317 panic!("Incorrect operation {:?}", request.operation);
318 }
319 }
320
321 #[test]
322 fn parse_opendir() {
323 let request = Request::new(FUSE_OPENDIR_REQUEST).unwrap();
324 check_header(&request, 9, FUSE_OPENDIR, 1);
325 if let FuseOperation::OpenDir { arg } = request.operation {
326 assert_eq!(arg.flags, 0x18800);
327 } else {
328 panic!("Incorrect operation {:?}", request.operation);
329 }
330 }
331
332 #[test]
333 fn parse_readdir() {
334 let request = Request::new(FUSE_READDIR_REQUEST).unwrap();
335 check_header(&request, 11, FUSE_READDIR, 1);
336 if let FuseOperation::ReadDir { arg } = request.operation {
337 assert_eq!(arg.fh, 0);
338 assert_eq!(arg.offset, 3);
339 assert_eq!(arg.size, 4096);
340 assert_eq!(arg.read_flags, 0);
341 assert_eq!(arg.lock_owner, 0);
342 assert_eq!(arg.flags, 0x18800);
343 } else {
344 panic!("Incorrect operation {:?}", request.operation);
345 }
346 }
347
348 #[test]
349 fn parse_releasedir() {
350 let request = Request::new(FUSE_RELEASEDIR_REQUEST).unwrap();
351 check_header(&request, 12, FUSE_RELEASEDIR, 1);
352 if let FuseOperation::ReleaseDir { arg } = request.operation {
353 assert_eq!(arg.fh, 0);
354 assert_eq!(arg.flags, 0x18800);
355 assert_eq!(arg.release_flags, 0);
356 assert_eq!(arg.lock_owner, 0);
357 } else {
358 panic!("Incorrect operation {:?}", request.operation);
359 }
360 }
361
362 fn check_header(request: &Request, unique: u64, opcode: u32, ino: u64) {
363 assert_eq!(request.unique(), unique);
364 assert_eq!(request.opcode(), opcode);
365 assert_eq!(request.node_id(), ino);
366 assert_eq!(request.uid(), 0);
367 assert_eq!(request.gid(), 0);
368 assert_eq!(
369 request.pid(),
370 if opcode == FUSE_INIT || opcode == FUSE_RELEASE || opcode == FUSE_RELEASEDIR {
371 0
372 } else {
373 971
374 }
375 );
376 }
377
378 pub const FUSE_INIT_REQUEST: &[u8] = &[
379 56, 0, 0, 0, 26, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
380 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 27, 0, 0, 0, 0, 0, 2, 0, 251, 255, 63, 0,
381 ];
382
383 pub const FUSE_GETATTR_REQUEST: &[u8] = &[
384 56, 0, 0, 0, 3, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
385 0, 0, 203, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
386 ];
387
388 pub const FUSE_LOOKUP_REQUEST: &[u8] = &[
389 46, 0, 0, 0, 1, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
390 0, 0, 203, 3, 0, 0, 0, 0, 0, 0, 104, 101, 108, 108, 111, 0,
391 ];
392
393 const FUSE_OPEN_REQUEST: &[u8] = &[
394 48, 0, 0, 0, 14, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
395 0, 0, 203, 3, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0,
396 ];
397
398 const FUSE_READ_REQUEST: &[u8] = &[
399 80, 0, 0, 0, 15, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
400 0, 0, 203, 3, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0,
401 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0,
402 ];
403
404 const FUSE_FLUSH_REQUEST: &[u8] = &[
405 64, 0, 0, 0, 25, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
406 0, 0, 203, 3, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 223, 18,
407 226, 110, 87, 14, 183, 180,
408 ];
409
410 const FUSE_RELEASE_REQUEST: &[u8] = &[
411 64, 0, 0, 0, 18, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
412 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
413 0, 0, 0, 0,
414 ];
415
416 const FUSE_OPENDIR_REQUEST: &[u8] = &[
417 48, 0, 0, 0, 27, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
418 0, 0, 203, 3, 0, 0, 0, 0, 0, 0, 0, 136, 1, 0, 0, 0, 0, 0,
419 ];
420
421 const FUSE_READDIR_REQUEST: &[u8] = &[
422 80, 0, 0, 0, 28, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
423 0, 0, 0, 203, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 16,
424 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 136, 1, 0, 0, 0, 0, 0,
425 ];
426
427 const FUSE_RELEASEDIR_REQUEST: &[u8] = &[
428 64, 0, 0, 0, 29, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
429 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 136, 1, 0, 0, 0, 0, 0, 0, 0, 0,
430 0, 0, 0, 0, 0,
431 ];
432}