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_STATX StatX arg:fuse_statx_in;
65 FUSE_CANONICAL_PATH CanonicalPath;
66}
67
68pub struct Request {
70 header: fuse_in_header,
71 operation: FuseOperation,
72}
73
74impl Request {
75 pub fn new(mut reader: impl RequestReader) -> lx::Result<Self> {
77 let header: fuse_in_header = reader.read_type()?;
78 let operation = Self::read_operation(&header, reader);
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 return FuseOperation::Invalid;
140 }
141
142 match FuseOperation::read(header.opcode, reader) {
143 Ok(operation) => operation,
144 Err(e) => {
145 tracing::error!(
146 opcode = header.opcode,
147 unique = header.unique,
148 error = &e as &dyn std::error::Error,
149 "Invalid message payload",
150 );
151 FuseOperation::Error(e)
152 }
153 }
154 }
155}
156
157pub trait RequestReader: io::Read {
159 fn read_until(&mut self, byte: u8) -> lx::Result<Vec<u8>>;
166
167 fn remaining_len(&self) -> usize;
169
170 fn read_count(&mut self, count: usize) -> lx::Result<Box<[u8]>> {
172 let mut buffer = vec![0u8; count];
173 self.read_exact(&mut buffer)?;
174 Ok(buffer.into_boxed_slice())
175 }
176
177 fn read_all(&mut self) -> lx::Result<Box<[u8]>> {
179 self.read_count(self.remaining_len())
180 }
181
182 fn read_type<T: IntoBytes + FromBytes + Immutable + KnownLayout>(&mut self) -> lx::Result<T> {
184 let mut value: T = T::new_zeroed();
185 self.read_exact(value.as_mut_bytes())?;
186 Ok(value)
187 }
188
189 fn string(&mut self) -> lx::Result<lx::LxString> {
191 let buffer = self.read_until(b'\0')?;
192 Ok(lx::LxString::from_vec(buffer))
193 }
194
195 const NAME_MAX: usize = 255;
197
198 fn name(&mut self) -> lx::Result<lx::LxString> {
200 let name = self.string()?;
201 if name.is_empty() || name == "." || name == ".." || name.as_bytes().contains(&b'/') {
202 return Err(lx::Error::EINVAL);
203 }
204 if name.len() > Self::NAME_MAX {
205 return Err(lx::Error::ENAMETOOLONG);
206 }
207
208 Ok(name)
209 }
210}
211
212impl RequestReader for &[u8] {
213 fn read_until(&mut self, byte: u8) -> lx::Result<Vec<u8>> {
214 let length = self
215 .iter()
216 .position(|&c| c == byte)
217 .ok_or(lx::Error::EINVAL)?;
218
219 let result = Vec::from(&self[..length]);
220 *self = &self[length + 1..];
221 Ok(result)
222 }
223
224 fn remaining_len(&self) -> usize {
225 self.len()
226 }
227}
228
229#[cfg(test)]
230pub(crate) mod tests {
231 use super::*;
232
233 #[test]
234 fn parse_init() {
235 let request = Request::new(FUSE_INIT_REQUEST).unwrap();
236 check_header(&request, 1, FUSE_INIT, 0);
237 if let FuseOperation::Init { arg } = request.operation {
238 assert_eq!(arg.major, 7);
239 assert_eq!(arg.minor, 27);
240 assert_eq!(arg.max_readahead, 131072);
241 assert_eq!(arg.flags, 0x3FFFFB);
242 } else {
243 panic!("Incorrect operation {:?}", request.operation);
244 }
245 }
246
247 #[test]
248 fn parse_get_attr() {
249 let request = Request::new(FUSE_GETATTR_REQUEST).unwrap();
250 check_header(&request, 2, FUSE_GETATTR, 1);
251 if let FuseOperation::GetAttr { arg } = request.operation {
252 assert_eq!(arg.fh, 0);
253 assert_eq!(arg.getattr_flags, 0);
254 } else {
255 panic!("Incorrect operation {:?}", request.operation);
256 }
257 }
258
259 #[test]
260 fn parse_statx() {
261 let request = Request::new(FUSE_STATX_REQUEST).unwrap();
262 check_header(&request, 2, FUSE_STATX, 1);
263 if let FuseOperation::StatX { arg } = request.operation {
264 assert_eq!(arg.fh, 0);
265 assert_eq!(arg.getattr_flags, 0);
266 let mask = lx::StatExMask::new()
267 .with_file_type(true)
268 .with_mode(true)
269 .with_nlink(true)
270 .with_uid(true)
271 .with_gid(true)
272 .with_atime(true)
273 .with_mtime(true)
274 .with_ctime(true)
275 .with_ino(true)
276 .with_size(true)
277 .with_blocks(true)
278 .with_btime(true);
279 assert_eq!(arg.mask, mask.into_bits());
280 let flags = StatxFlags::new().with_dont_sync(true);
281 assert_eq!(arg.flags.into_bits(), flags.into_bits());
282 } else {
283 panic!("Incorrect operation {:?}", request.operation);
284 }
285 }
286
287 #[test]
288 fn parse_lookup() {
289 let request = Request::new(FUSE_LOOKUP_REQUEST).unwrap();
290 check_header(&request, 3, FUSE_LOOKUP, 1);
291 if let FuseOperation::Lookup { name } = request.operation {
292 assert_eq!(name, "hello");
293 } else {
294 panic!("Incorrect operation {:?}", request.operation);
295 }
296 }
297
298 #[test]
299 fn parse_open() {
300 let request = Request::new(FUSE_OPEN_REQUEST).unwrap();
301 check_header(&request, 4, FUSE_OPEN, 2);
302 if let FuseOperation::Open { arg } = request.operation {
303 assert_eq!(arg.flags, 0x8000);
304 } else {
305 panic!("Incorrect operation {:?}", request.operation);
306 }
307 }
308
309 #[test]
310 fn parse_read() {
311 let request = Request::new(FUSE_READ_REQUEST).unwrap();
312 check_header(&request, 5, FUSE_READ, 2);
313 if let FuseOperation::Read { arg } = request.operation {
314 assert_eq!(arg.fh, 1);
315 assert_eq!(arg.offset, 0);
316 assert_eq!(arg.size, 4096);
317 assert_eq!(arg.read_flags, 0);
318 assert_eq!(arg.lock_owner, 0);
319 assert_eq!(arg.flags, 0x8000);
320 } else {
321 panic!("Incorrect operation {:?}", request.operation);
322 }
323 }
324
325 #[test]
326 fn parse_flush() {
327 let request = Request::new(FUSE_FLUSH_REQUEST).unwrap();
328 check_header(&request, 7, FUSE_FLUSH, 2);
329 if let FuseOperation::Flush { arg } = request.operation {
330 assert_eq!(arg.fh, 1);
331 assert_eq!(arg.lock_owner, 13021892616250331871);
334 } else {
335 panic!("Incorrect operation {:?}", request.operation);
336 }
337 }
338
339 #[test]
340 fn parse_release() {
341 let request = Request::new(FUSE_RELEASE_REQUEST).unwrap();
342 check_header(&request, 8, FUSE_RELEASE, 2);
343 if let FuseOperation::Release { arg } = request.operation {
344 assert_eq!(arg.fh, 1);
345 assert_eq!(arg.flags, 0x8000);
346 assert_eq!(arg.release_flags, 0);
347 assert_eq!(arg.lock_owner, 0);
348 } else {
349 panic!("Incorrect operation {:?}", request.operation);
350 }
351 }
352
353 #[test]
354 fn parse_opendir() {
355 let request = Request::new(FUSE_OPENDIR_REQUEST).unwrap();
356 check_header(&request, 9, FUSE_OPENDIR, 1);
357 if let FuseOperation::OpenDir { arg } = request.operation {
358 assert_eq!(arg.flags, 0x18800);
359 } else {
360 panic!("Incorrect operation {:?}", request.operation);
361 }
362 }
363
364 #[test]
365 fn parse_readdir() {
366 let request = Request::new(FUSE_READDIR_REQUEST).unwrap();
367 check_header(&request, 11, FUSE_READDIR, 1);
368 if let FuseOperation::ReadDir { arg } = request.operation {
369 assert_eq!(arg.fh, 0);
370 assert_eq!(arg.offset, 3);
371 assert_eq!(arg.size, 4096);
372 assert_eq!(arg.read_flags, 0);
373 assert_eq!(arg.lock_owner, 0);
374 assert_eq!(arg.flags, 0x18800);
375 } else {
376 panic!("Incorrect operation {:?}", request.operation);
377 }
378 }
379
380 #[test]
381 fn parse_releasedir() {
382 let request = Request::new(FUSE_RELEASEDIR_REQUEST).unwrap();
383 check_header(&request, 12, FUSE_RELEASEDIR, 1);
384 if let FuseOperation::ReleaseDir { arg } = request.operation {
385 assert_eq!(arg.fh, 0);
386 assert_eq!(arg.flags, 0x18800);
387 assert_eq!(arg.release_flags, 0);
388 assert_eq!(arg.lock_owner, 0);
389 } else {
390 panic!("Incorrect operation {:?}", request.operation);
391 }
392 }
393
394 fn check_header(request: &Request, unique: u64, opcode: u32, ino: u64) {
395 assert_eq!(request.unique(), unique);
396 assert_eq!(request.opcode(), opcode);
397 assert_eq!(request.node_id(), ino);
398 assert_eq!(request.uid(), 0);
399 assert_eq!(request.gid(), 0);
400 assert_eq!(
401 request.pid(),
402 if opcode == FUSE_INIT || opcode == FUSE_RELEASE || opcode == FUSE_RELEASEDIR {
403 0
404 } else {
405 971
406 }
407 );
408 }
409
410 pub const FUSE_INIT_REQUEST: &[u8] = &[
411 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,
412 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,
413 ];
414
415 pub const FUSE_GETATTR_REQUEST: &[u8] = &[
416 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,
417 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,
418 ];
419
420 pub const FUSE_LOOKUP_REQUEST: &[u8] = &[
421 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,
422 0, 0, 203, 3, 0, 0, 0, 0, 0, 0, 104, 101, 108, 108, 111, 0,
423 ];
424
425 const FUSE_OPEN_REQUEST: &[u8] = &[
426 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,
427 0, 0, 203, 3, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0,
428 ];
429
430 const FUSE_READ_REQUEST: &[u8] = &[
431 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,
432 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,
433 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0,
434 ];
435
436 const FUSE_FLUSH_REQUEST: &[u8] = &[
437 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,
438 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,
439 226, 110, 87, 14, 183, 180,
440 ];
441
442 const FUSE_RELEASE_REQUEST: &[u8] = &[
443 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,
444 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,
445 0, 0, 0, 0,
446 ];
447
448 const FUSE_OPENDIR_REQUEST: &[u8] = &[
449 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,
450 0, 0, 203, 3, 0, 0, 0, 0, 0, 0, 0, 136, 1, 0, 0, 0, 0, 0,
451 ];
452
453 const FUSE_READDIR_REQUEST: &[u8] = &[
454 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,
455 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,
456 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 136, 1, 0, 0, 0, 0, 0,
457 ];
458
459 const FUSE_RELEASEDIR_REQUEST: &[u8] = &[
460 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,
461 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,
462 0, 0, 0, 0, 0,
463 ];
464
465 const FUSE_STATX_REQUEST: &[u8] = &[
466 64, 0, 0, 0, 52, 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,
467 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, 0, 64, 0,
468 0, 255, 15, 0, 0,
469 ];
470
471 #[test]
472 fn name_too_long_returns_enametoolong() {
473 let mut data = vec![b'a'; 256];
475 data.push(0); let mut reader: &[u8] = &data;
478 let result = reader.name();
479
480 assert_eq!(result, Err(lx::Error::ENAMETOOLONG));
481 }
482}