1#![allow(
9 clippy::cast_sign_loss,
10 clippy::cast_possible_wrap,
11 clippy::cast_possible_truncation,
12 clippy::ptr_as_ptr,
13 clippy::borrow_as_ptr,
14 clippy::significant_drop_tightening,
15 clippy::needless_pass_by_ref_mut
16)]
17
18use crate::error::{FsError, Result};
19use crate::fuse::{
20 FATTR_ATIME, FATTR_GID, FATTR_MODE, FATTR_MTIME, FATTR_SIZE, FATTR_UID,
21 FUSE_KERNEL_MINOR_VERSION, FUSE_KERNEL_VERSION, FuseAccessIn, FuseAttr, FuseAttrOut,
22 FuseCreateIn, FuseDirent, FuseEntryOut, FuseFallocateIn, FuseFlushIn, FuseForgetIn,
23 FuseFsyncIn, FuseGetxattrIn, FuseGetxattrOut, FuseInHeader, FuseInitIn, FuseInitOut,
24 FuseLinkIn, FuseLseekIn, FuseLseekOut, FuseMkdirIn, FuseMknodIn, FuseOpcode, FuseOpenIn,
25 FuseOpenOut, FuseOutHeader, FuseReadIn, FuseReleaseIn, FuseRenameIn, FuseSetattrIn,
26 FuseSetxattrIn, FuseStatfsOut, FuseWriteIn, FuseWriteOut,
27};
28use crate::passthrough::PassthroughFs;
29
30use std::ffi::OsStr;
31use std::mem::size_of;
32use std::os::unix::ffi::OsStrExt;
33use std::path::Path;
34use std::sync::Arc;
35
36#[derive(Debug, Clone)]
42pub struct DispatcherConfig {
43 pub entry_timeout: u64,
45 pub attr_timeout: u64,
47}
48
49impl Default for DispatcherConfig {
50 fn default() -> Self {
51 Self {
52 entry_timeout: 1,
53 attr_timeout: 1,
54 }
55 }
56}
57
58#[derive(Debug, Clone, Copy)]
64pub struct RequestContext {
65 pub unique: u64,
67 pub nodeid: u64,
69 pub uid: u32,
71 pub gid: u32,
73 pub pid: u32,
75}
76
77impl From<&FuseInHeader> for RequestContext {
78 fn from(header: &FuseInHeader) -> Self {
79 Self {
80 unique: header.unique,
81 nodeid: header.nodeid,
82 uid: header.uid,
83 gid: header.gid,
84 pid: header.pid,
85 }
86 }
87}
88
89pub struct ResponseBuilder {
95 buffer: Vec<u8>,
96}
97
98impl ResponseBuilder {
99 #[must_use]
101 pub fn new() -> Self {
102 Self {
103 buffer: Vec::with_capacity(4096),
104 }
105 }
106
107 pub fn write_error(&mut self, unique: u64, errno: i32) {
109 self.buffer.clear();
110 let header = FuseOutHeader::error(unique, errno);
111 self.write_struct(&header);
112 }
113
114 pub fn write_empty(&mut self, unique: u64) {
116 self.buffer.clear();
117 let header = FuseOutHeader::success(unique, FuseOutHeader::SIZE as u32);
118 self.write_struct(&header);
119 }
120
121 pub fn write_data<T: Copy>(&mut self, unique: u64, data: &T) {
123 self.buffer.clear();
124 let len = (FuseOutHeader::SIZE + size_of::<T>()) as u32;
125 let header = FuseOutHeader::success(unique, len);
126 self.write_struct(&header);
127 self.write_struct(data);
128 }
129
130 pub fn write_bytes(&mut self, unique: u64, data: &[u8]) {
132 self.buffer.clear();
133 let len = (FuseOutHeader::SIZE + data.len()) as u32;
134 let header = FuseOutHeader::success(unique, len);
135 self.write_struct(&header);
136 self.buffer.extend_from_slice(data);
137 }
138
139 #[must_use]
141 pub fn finish(self) -> Vec<u8> {
142 self.buffer
143 }
144
145 #[must_use]
147 pub fn as_bytes(&self) -> &[u8] {
148 &self.buffer
149 }
150
151 fn write_struct<T: Copy>(&mut self, value: &T) {
152 let bytes = unsafe {
153 std::slice::from_raw_parts(std::ptr::from_ref::<T>(value) as *const u8, size_of::<T>())
154 };
155 self.buffer.extend_from_slice(bytes);
156 }
157}
158
159impl Default for ResponseBuilder {
160 fn default() -> Self {
161 Self::new()
162 }
163}
164
165pub struct FuseDispatcher {
189 fs: Arc<PassthroughFs>,
191 config: DispatcherConfig,
193 initialized: std::sync::atomic::AtomicBool,
195}
196
197impl FuseDispatcher {
198 #[must_use]
200 pub fn new(fs: Arc<PassthroughFs>, config: DispatcherConfig) -> Self {
201 Self {
202 fs,
203 config,
204 initialized: std::sync::atomic::AtomicBool::new(false),
205 }
206 }
207
208 pub fn dispatch(&self, request: &[u8]) -> Result<Vec<u8>> {
214 if request.len() < FuseInHeader::SIZE {
216 return Err(FsError::Fuse("request too small".to_string()));
217 }
218
219 let header = unsafe { &*(request.as_ptr() as *const FuseInHeader) };
220 let body = &request[FuseInHeader::SIZE..];
221
222 let opcode = FuseOpcode::from_u32(header.opcode)
224 .ok_or_else(|| FsError::Fuse(format!("unknown opcode: {}", header.opcode)))?;
225
226 let ctx = RequestContext::from(header);
227 let mut response = ResponseBuilder::new();
228
229 match opcode {
231 FuseOpcode::Init => self.handle_init(&ctx, body, &mut response),
232 FuseOpcode::Destroy => self.handle_destroy(&ctx, &mut response),
233 FuseOpcode::Lookup => self.handle_lookup(&ctx, body, &mut response),
234 FuseOpcode::Forget => self.handle_forget(&ctx, body, &mut response),
235 FuseOpcode::Getattr => self.handle_getattr(&ctx, body, &mut response),
236 FuseOpcode::Setattr => self.handle_setattr(&ctx, body, &mut response),
237 FuseOpcode::Readlink => self.handle_readlink(&ctx, &mut response),
238 FuseOpcode::Mknod => self.handle_mknod(&ctx, body, &mut response),
239 FuseOpcode::Mkdir => self.handle_mkdir(&ctx, body, &mut response),
240 FuseOpcode::Unlink => self.handle_unlink(&ctx, body, &mut response),
241 FuseOpcode::Rmdir => self.handle_rmdir(&ctx, body, &mut response),
242 FuseOpcode::Symlink => self.handle_symlink(&ctx, body, &mut response),
243 FuseOpcode::Rename => self.handle_rename(&ctx, body, &mut response),
244 FuseOpcode::Link => self.handle_link(&ctx, body, &mut response),
245 FuseOpcode::Open => self.handle_open(&ctx, body, &mut response),
246 FuseOpcode::Read => self.handle_read(&ctx, body, &mut response),
247 FuseOpcode::Write => self.handle_write(&ctx, body, &mut response),
248 FuseOpcode::Statfs => self.handle_statfs(&ctx, &mut response),
249 FuseOpcode::Release => self.handle_release(&ctx, body, &mut response),
250 FuseOpcode::Fsync => self.handle_fsync(&ctx, body, &mut response),
251 FuseOpcode::Flush => self.handle_flush(&ctx, body, &mut response),
252 FuseOpcode::Opendir => self.handle_opendir(&ctx, body, &mut response),
253 FuseOpcode::Readdir => self.handle_readdir(&ctx, body, &mut response),
254 FuseOpcode::Releasedir => self.handle_releasedir(&ctx, body, &mut response),
255 FuseOpcode::Fsyncdir => self.handle_fsyncdir(&ctx, body, &mut response),
256 FuseOpcode::Access => self.handle_access(&ctx, body, &mut response),
257 FuseOpcode::Create => self.handle_create(&ctx, body, &mut response),
258 FuseOpcode::Getxattr => self.handle_getxattr(&ctx, body, &mut response),
259 FuseOpcode::Setxattr => self.handle_setxattr(&ctx, body, &mut response),
260 FuseOpcode::Removexattr => self.handle_removexattr(&ctx, body, &mut response),
261 FuseOpcode::Lseek => self.handle_lseek(&ctx, body, &mut response),
262 FuseOpcode::Fallocate => self.handle_fallocate(&ctx, body, &mut response),
263 _ => {
264 response.write_error(ctx.unique, libc::ENOSYS);
266 }
267 }
268
269 Ok(response.finish())
270 }
271
272 fn handle_init(&self, ctx: &RequestContext, body: &[u8], response: &mut ResponseBuilder) {
277 if body.len() < size_of::<FuseInitIn>() {
278 response.write_error(ctx.unique, libc::EINVAL);
279 return;
280 }
281
282 let init_in = unsafe { &*(body.as_ptr() as *const FuseInitIn) };
283
284 if init_in.major < FUSE_KERNEL_VERSION {
286 response.write_error(ctx.unique, libc::EPROTO);
287 return;
288 }
289
290 let init_out = FuseInitOut {
291 major: FUSE_KERNEL_VERSION,
292 minor: FUSE_KERNEL_MINOR_VERSION,
293 max_readahead: init_in.max_readahead,
294 ..FuseInitOut::default()
295 };
296
297 self.initialized
298 .store(true, std::sync::atomic::Ordering::SeqCst);
299 response.write_data(ctx.unique, &init_out);
300 }
301
302 fn handle_destroy(&self, ctx: &RequestContext, response: &mut ResponseBuilder) {
303 self.initialized
304 .store(false, std::sync::atomic::Ordering::SeqCst);
305 response.write_empty(ctx.unique);
306 }
307
308 fn handle_lookup(&self, ctx: &RequestContext, body: &[u8], response: &mut ResponseBuilder) {
313 let name = self.parse_name(body);
315
316 match self.fs.lookup(ctx.nodeid, name) {
317 Ok((inode, attr)) => {
318 let entry = self.make_entry_out(inode, &attr);
319 response.write_data(ctx.unique, &entry);
320 }
321 Err(e) => response.write_error(ctx.unique, e.to_errno()),
322 }
323 }
324
325 fn handle_forget(&self, ctx: &RequestContext, body: &[u8], response: &mut ResponseBuilder) {
326 if body.len() >= size_of::<FuseForgetIn>() {
327 let forget_in = unsafe { &*(body.as_ptr() as *const FuseForgetIn) };
328 self.fs.forget(ctx.nodeid, forget_in.nlookup);
329 }
330 let _ = response;
332 }
333
334 fn handle_getattr(&self, ctx: &RequestContext, _body: &[u8], response: &mut ResponseBuilder) {
339 match self.fs.getattr(ctx.nodeid) {
340 Ok(attr) => {
341 let attr_out = FuseAttrOut {
342 attr_valid: self.config.attr_timeout,
343 attr_valid_nsec: 0,
344 dummy: 0,
345 attr,
346 };
347 response.write_data(ctx.unique, &attr_out);
348 }
349 Err(e) => response.write_error(ctx.unique, e.to_errno()),
350 }
351 }
352
353 fn handle_setattr(&self, ctx: &RequestContext, body: &[u8], response: &mut ResponseBuilder) {
354 if body.len() < size_of::<FuseSetattrIn>() {
355 response.write_error(ctx.unique, libc::EINVAL);
356 return;
357 }
358
359 let setattr_in = unsafe { &*(body.as_ptr() as *const FuseSetattrIn) };
360
361 let mode = if setattr_in.valid & FATTR_MODE != 0 {
362 Some(setattr_in.mode)
363 } else {
364 None
365 };
366
367 let uid = if setattr_in.valid & FATTR_UID != 0 {
368 Some(setattr_in.uid)
369 } else {
370 None
371 };
372
373 let gid = if setattr_in.valid & FATTR_GID != 0 {
374 Some(setattr_in.gid)
375 } else {
376 None
377 };
378
379 let size = if setattr_in.valid & FATTR_SIZE != 0 {
380 Some(setattr_in.size)
381 } else {
382 None
383 };
384
385 let atime = if setattr_in.valid & FATTR_ATIME != 0 {
386 Some((setattr_in.atime as i64, setattr_in.atimensec))
387 } else {
388 None
389 };
390
391 let mtime = if setattr_in.valid & FATTR_MTIME != 0 {
392 Some((setattr_in.mtime as i64, setattr_in.mtimensec))
393 } else {
394 None
395 };
396
397 match self
398 .fs
399 .setattr(ctx.nodeid, mode, uid, gid, size, atime, mtime)
400 {
401 Ok(attr) => {
402 let attr_out = FuseAttrOut {
403 attr_valid: self.config.attr_timeout,
404 attr_valid_nsec: 0,
405 dummy: 0,
406 attr,
407 };
408 response.write_data(ctx.unique, &attr_out);
409 }
410 Err(e) => response.write_error(ctx.unique, e.to_errno()),
411 }
412 }
413
414 fn handle_readlink(&self, ctx: &RequestContext, response: &mut ResponseBuilder) {
415 match self.fs.readlink(ctx.nodeid) {
416 Ok(target) => {
417 response.write_bytes(ctx.unique, target.as_os_str().as_bytes());
418 }
419 Err(e) => response.write_error(ctx.unique, e.to_errno()),
420 }
421 }
422
423 fn handle_mknod(&self, ctx: &RequestContext, body: &[u8], response: &mut ResponseBuilder) {
428 if body.len() < size_of::<FuseMknodIn>() {
429 response.write_error(ctx.unique, libc::EINVAL);
430 return;
431 }
432
433 let mknod_in = unsafe { &*(body.as_ptr() as *const FuseMknodIn) };
434 let name = self.parse_name(&body[size_of::<FuseMknodIn>()..]);
435
436 match self
437 .fs
438 .mknod(ctx.nodeid, name, mknod_in.mode, u64::from(mknod_in.rdev))
439 {
440 Ok((inode, attr)) => {
441 let entry = self.make_entry_out(inode, &attr);
442 response.write_data(ctx.unique, &entry);
443 }
444 Err(e) => response.write_error(ctx.unique, e.to_errno()),
445 }
446 }
447
448 fn handle_mkdir(&self, ctx: &RequestContext, body: &[u8], response: &mut ResponseBuilder) {
449 if body.len() < size_of::<FuseMkdirIn>() {
450 response.write_error(ctx.unique, libc::EINVAL);
451 return;
452 }
453
454 let mkdir_in = unsafe { &*(body.as_ptr() as *const FuseMkdirIn) };
455 let name = self.parse_name(&body[size_of::<FuseMkdirIn>()..]);
456
457 match self.fs.mkdir(ctx.nodeid, name, mkdir_in.mode) {
458 Ok((inode, attr)) => {
459 let entry = self.make_entry_out(inode, &attr);
460 response.write_data(ctx.unique, &entry);
461 }
462 Err(e) => response.write_error(ctx.unique, e.to_errno()),
463 }
464 }
465
466 fn handle_symlink(&self, ctx: &RequestContext, body: &[u8], response: &mut ResponseBuilder) {
467 let parts: Vec<&[u8]> = body.splitn(2, |&b| b == 0).collect();
469 if parts.len() < 2 {
470 response.write_error(ctx.unique, libc::EINVAL);
471 return;
472 }
473
474 let name = OsStr::from_bytes(parts[0]);
475 let link_target = Path::new(OsStr::from_bytes(
476 parts[1].split(|&b| b == 0).next().unwrap_or(&[]),
477 ));
478
479 match self.fs.symlink(ctx.nodeid, name, link_target) {
480 Ok((inode, attr)) => {
481 let entry = self.make_entry_out(inode, &attr);
482 response.write_data(ctx.unique, &entry);
483 }
484 Err(e) => response.write_error(ctx.unique, e.to_errno()),
485 }
486 }
487
488 fn handle_link(&self, ctx: &RequestContext, body: &[u8], response: &mut ResponseBuilder) {
489 if body.len() < size_of::<FuseLinkIn>() {
490 response.write_error(ctx.unique, libc::EINVAL);
491 return;
492 }
493
494 let link_in = unsafe { &*(body.as_ptr() as *const FuseLinkIn) };
495 let name = self.parse_name(&body[size_of::<FuseLinkIn>()..]);
496
497 match self.fs.link(link_in.oldnodeid, ctx.nodeid, name) {
498 Ok((inode, attr)) => {
499 let entry = self.make_entry_out(inode, &attr);
500 response.write_data(ctx.unique, &entry);
501 }
502 Err(e) => response.write_error(ctx.unique, e.to_errno()),
503 }
504 }
505
506 fn handle_create(&self, ctx: &RequestContext, body: &[u8], response: &mut ResponseBuilder) {
507 if body.len() < size_of::<FuseCreateIn>() {
508 response.write_error(ctx.unique, libc::EINVAL);
509 return;
510 }
511
512 let create_in = unsafe { &*(body.as_ptr() as *const FuseCreateIn) };
513 let name = self.parse_name(&body[size_of::<FuseCreateIn>()..]);
514
515 match self
516 .fs
517 .create(ctx.nodeid, name, create_in.mode, create_in.flags)
518 {
519 Ok((inode, attr, handle)) => {
520 let entry = self.make_entry_out(inode, &attr);
521 let open_out = FuseOpenOut {
522 fh: handle,
523 open_flags: 0,
524 padding: 0,
525 };
526
527 response.buffer.clear();
529 let len = (FuseOutHeader::SIZE
530 + size_of::<FuseEntryOut>()
531 + size_of::<FuseOpenOut>()) as u32;
532 let header = FuseOutHeader::success(ctx.unique, len);
533 response.write_struct(&header);
534 response.write_struct(&entry);
535 response.write_struct(&open_out);
536 }
537 Err(e) => response.write_error(ctx.unique, e.to_errno()),
538 }
539 }
540
541 fn handle_unlink(&self, ctx: &RequestContext, body: &[u8], response: &mut ResponseBuilder) {
546 let name = self.parse_name(body);
547
548 match self.fs.unlink(ctx.nodeid, name) {
549 Ok(()) => response.write_empty(ctx.unique),
550 Err(e) => response.write_error(ctx.unique, e.to_errno()),
551 }
552 }
553
554 fn handle_rmdir(&self, ctx: &RequestContext, body: &[u8], response: &mut ResponseBuilder) {
555 let name = self.parse_name(body);
556
557 match self.fs.rmdir(ctx.nodeid, name) {
558 Ok(()) => response.write_empty(ctx.unique),
559 Err(e) => response.write_error(ctx.unique, e.to_errno()),
560 }
561 }
562
563 fn handle_rename(&self, ctx: &RequestContext, body: &[u8], response: &mut ResponseBuilder) {
564 if body.len() < size_of::<FuseRenameIn>() {
565 response.write_error(ctx.unique, libc::EINVAL);
566 return;
567 }
568
569 let rename_in = unsafe { &*(body.as_ptr() as *const FuseRenameIn) };
570 let names = &body[size_of::<FuseRenameIn>()..];
571
572 let parts: Vec<&[u8]> = names.splitn(2, |&b| b == 0).collect();
574 if parts.len() < 2 {
575 response.write_error(ctx.unique, libc::EINVAL);
576 return;
577 }
578
579 let old_name = OsStr::from_bytes(parts[0]);
580 let new_name = OsStr::from_bytes(parts[1].split(|&b| b == 0).next().unwrap_or(&[]));
581
582 match self
583 .fs
584 .rename(ctx.nodeid, old_name, rename_in.newdir, new_name, 0)
585 {
586 Ok(()) => response.write_empty(ctx.unique),
587 Err(e) => response.write_error(ctx.unique, e.to_errno()),
588 }
589 }
590
591 fn handle_open(&self, ctx: &RequestContext, body: &[u8], response: &mut ResponseBuilder) {
596 if body.len() < size_of::<FuseOpenIn>() {
597 response.write_error(ctx.unique, libc::EINVAL);
598 return;
599 }
600
601 let open_in = unsafe { &*(body.as_ptr() as *const FuseOpenIn) };
602
603 match self.fs.open(ctx.nodeid, open_in.flags) {
604 Ok(handle) => {
605 let open_out = FuseOpenOut {
606 fh: handle,
607 open_flags: 0,
608 padding: 0,
609 };
610 response.write_data(ctx.unique, &open_out);
611 }
612 Err(e) => response.write_error(ctx.unique, e.to_errno()),
613 }
614 }
615
616 fn handle_read(&self, ctx: &RequestContext, body: &[u8], response: &mut ResponseBuilder) {
617 if body.len() < size_of::<FuseReadIn>() {
618 response.write_error(ctx.unique, libc::EINVAL);
619 return;
620 }
621
622 let read_in = unsafe { &*(body.as_ptr() as *const FuseReadIn) };
623
624 match self.fs.read(read_in.fh, read_in.offset, read_in.size) {
625 Ok(data) => response.write_bytes(ctx.unique, &data),
626 Err(e) => response.write_error(ctx.unique, e.to_errno()),
627 }
628 }
629
630 fn handle_write(&self, ctx: &RequestContext, body: &[u8], response: &mut ResponseBuilder) {
631 if body.len() < size_of::<FuseWriteIn>() {
632 response.write_error(ctx.unique, libc::EINVAL);
633 return;
634 }
635
636 let write_in = unsafe { &*(body.as_ptr() as *const FuseWriteIn) };
637 let data = &body[size_of::<FuseWriteIn>()..];
638
639 match self
640 .fs
641 .write(write_in.fh, write_in.offset, data, write_in.write_flags)
642 {
643 Ok(written) => {
644 let write_out = FuseWriteOut {
645 size: written,
646 padding: 0,
647 };
648 response.write_data(ctx.unique, &write_out);
649 }
650 Err(e) => response.write_error(ctx.unique, e.to_errno()),
651 }
652 }
653
654 fn handle_release(&self, ctx: &RequestContext, body: &[u8], response: &mut ResponseBuilder) {
655 if body.len() < size_of::<FuseReleaseIn>() {
656 response.write_error(ctx.unique, libc::EINVAL);
657 return;
658 }
659
660 let release_in = unsafe { &*(body.as_ptr() as *const FuseReleaseIn) };
661
662 match self.fs.release(release_in.fh) {
663 Ok(()) => response.write_empty(ctx.unique),
664 Err(e) => response.write_error(ctx.unique, e.to_errno()),
665 }
666 }
667
668 fn handle_flush(&self, ctx: &RequestContext, body: &[u8], response: &mut ResponseBuilder) {
669 if body.len() < size_of::<FuseFlushIn>() {
670 response.write_error(ctx.unique, libc::EINVAL);
671 return;
672 }
673
674 let flush_in = unsafe { &*(body.as_ptr() as *const FuseFlushIn) };
675
676 match self.fs.flush(flush_in.fh) {
677 Ok(()) => response.write_empty(ctx.unique),
678 Err(e) => response.write_error(ctx.unique, e.to_errno()),
679 }
680 }
681
682 fn handle_fsync(&self, ctx: &RequestContext, body: &[u8], response: &mut ResponseBuilder) {
683 if body.len() < size_of::<FuseFsyncIn>() {
684 response.write_error(ctx.unique, libc::EINVAL);
685 return;
686 }
687
688 let fsync_in = unsafe { &*(body.as_ptr() as *const FuseFsyncIn) };
689 let datasync = fsync_in.fsync_flags & 1 != 0;
690
691 match self.fs.fsync(fsync_in.fh, datasync) {
692 Ok(()) => response.write_empty(ctx.unique),
693 Err(e) => response.write_error(ctx.unique, e.to_errno()),
694 }
695 }
696
697 fn handle_lseek(&self, ctx: &RequestContext, body: &[u8], response: &mut ResponseBuilder) {
698 if body.len() < size_of::<FuseLseekIn>() {
699 response.write_error(ctx.unique, libc::EINVAL);
700 return;
701 }
702
703 let lseek_in = unsafe { &*(body.as_ptr() as *const FuseLseekIn) };
704
705 match self
706 .fs
707 .lseek(lseek_in.fh, lseek_in.offset as i64, lseek_in.whence)
708 {
709 Ok(offset) => {
710 let lseek_out = FuseLseekOut { offset };
711 response.write_data(ctx.unique, &lseek_out);
712 }
713 Err(e) => response.write_error(ctx.unique, e.to_errno()),
714 }
715 }
716
717 fn handle_fallocate(&self, ctx: &RequestContext, body: &[u8], response: &mut ResponseBuilder) {
718 if body.len() < size_of::<FuseFallocateIn>() {
719 response.write_error(ctx.unique, libc::EINVAL);
720 return;
721 }
722
723 let fallocate_in = unsafe { &*(body.as_ptr() as *const FuseFallocateIn) };
724
725 match self.fs.fallocate(
726 fallocate_in.fh,
727 fallocate_in.mode,
728 fallocate_in.offset,
729 fallocate_in.length,
730 ) {
731 Ok(()) => response.write_empty(ctx.unique),
732 Err(e) => response.write_error(ctx.unique, e.to_errno()),
733 }
734 }
735
736 fn handle_opendir(&self, ctx: &RequestContext, body: &[u8], response: &mut ResponseBuilder) {
741 if body.len() < size_of::<FuseOpenIn>() {
742 response.write_error(ctx.unique, libc::EINVAL);
743 return;
744 }
745
746 match self.fs.opendir(ctx.nodeid) {
747 Ok(handle) => {
748 let open_out = FuseOpenOut {
749 fh: handle,
750 open_flags: 0,
751 padding: 0,
752 };
753 response.write_data(ctx.unique, &open_out);
754 }
755 Err(e) => response.write_error(ctx.unique, e.to_errno()),
756 }
757 }
758
759 fn handle_readdir(&self, ctx: &RequestContext, body: &[u8], response: &mut ResponseBuilder) {
760 if body.len() < size_of::<FuseReadIn>() {
761 response.write_error(ctx.unique, libc::EINVAL);
762 return;
763 }
764
765 let read_in = unsafe { &*(body.as_ptr() as *const FuseReadIn) };
766
767 match self.fs.readdir(read_in.fh, read_in.offset) {
768 Ok(entries) => {
769 let mut dirent_buf = Vec::new();
770 let mut offset = read_in.offset + 1;
771
772 for entry in entries {
773 let name_bytes = entry.name.as_bytes();
774 let entry_size = FuseDirent::size(name_bytes.len());
775
776 if dirent_buf.len() + entry_size > read_in.size as usize {
778 break;
779 }
780
781 let dirent = FuseDirent {
782 ino: entry.ino,
783 off: offset,
784 namelen: name_bytes.len() as u32,
785 typ: entry.file_type.to_dirent_type(),
786 };
787
788 let dirent_bytes = unsafe {
790 std::slice::from_raw_parts(
791 std::ptr::from_ref::<FuseDirent>(&dirent) as *const u8,
792 size_of::<FuseDirent>(),
793 )
794 };
795 dirent_buf.extend_from_slice(dirent_bytes);
796
797 dirent_buf.extend_from_slice(name_bytes);
799
800 let padding = entry_size - size_of::<FuseDirent>() - name_bytes.len();
802 dirent_buf.extend(std::iter::repeat_n(0u8, padding));
803
804 offset += 1;
805 }
806
807 response.write_bytes(ctx.unique, &dirent_buf);
808 }
809 Err(e) => response.write_error(ctx.unique, e.to_errno()),
810 }
811 }
812
813 fn handle_releasedir(&self, ctx: &RequestContext, body: &[u8], response: &mut ResponseBuilder) {
814 if body.len() < size_of::<FuseReleaseIn>() {
815 response.write_error(ctx.unique, libc::EINVAL);
816 return;
817 }
818
819 let release_in = unsafe { &*(body.as_ptr() as *const FuseReleaseIn) };
820
821 match self.fs.releasedir(release_in.fh) {
822 Ok(()) => response.write_empty(ctx.unique),
823 Err(e) => response.write_error(ctx.unique, e.to_errno()),
824 }
825 }
826
827 fn handle_fsyncdir(&self, ctx: &RequestContext, body: &[u8], response: &mut ResponseBuilder) {
828 if body.len() < size_of::<FuseFsyncIn>() {
829 response.write_error(ctx.unique, libc::EINVAL);
830 return;
831 }
832
833 let fsync_in = unsafe { &*(body.as_ptr() as *const FuseFsyncIn) };
834 let datasync = fsync_in.fsync_flags & 1 != 0;
835
836 match self.fs.fsyncdir(fsync_in.fh, datasync) {
837 Ok(()) => response.write_empty(ctx.unique),
838 Err(e) => response.write_error(ctx.unique, e.to_errno()),
839 }
840 }
841
842 fn handle_getxattr(&self, ctx: &RequestContext, body: &[u8], response: &mut ResponseBuilder) {
847 if body.len() < size_of::<FuseGetxattrIn>() {
848 response.write_error(ctx.unique, libc::EINVAL);
849 return;
850 }
851
852 let getxattr_in = unsafe { &*(body.as_ptr() as *const FuseGetxattrIn) };
853 let name = self.parse_name(&body[size_of::<FuseGetxattrIn>()..]);
854
855 match self.fs.getxattr(ctx.nodeid, name, getxattr_in.size) {
856 Ok(value) => {
857 if getxattr_in.size == 0 {
858 let out = FuseGetxattrOut {
860 size: value.len() as u32,
861 padding: 0,
862 };
863 response.write_data(ctx.unique, &out);
864 } else {
865 response.write_bytes(ctx.unique, &value);
866 }
867 }
868 Err(e) => response.write_error(ctx.unique, e.to_errno()),
869 }
870 }
871
872 fn handle_setxattr(&self, ctx: &RequestContext, body: &[u8], response: &mut ResponseBuilder) {
873 if body.len() < size_of::<FuseSetxattrIn>() {
874 response.write_error(ctx.unique, libc::EINVAL);
875 return;
876 }
877
878 let setxattr_in = unsafe { &*(body.as_ptr() as *const FuseSetxattrIn) };
879 let rest = &body[size_of::<FuseSetxattrIn>()..];
880
881 if let Some(null_pos) = rest.iter().position(|&b| b == 0) {
883 let name = OsStr::from_bytes(&rest[..null_pos]);
884 let value = &rest[null_pos + 1..][..setxattr_in.size as usize];
885
886 match self.fs.setxattr(ctx.nodeid, name, value, setxattr_in.flags) {
887 Ok(()) => response.write_empty(ctx.unique),
888 Err(e) => response.write_error(ctx.unique, e.to_errno()),
889 }
890 } else {
891 response.write_error(ctx.unique, libc::EINVAL);
892 }
893 }
894
895 fn handle_removexattr(
896 &self,
897 ctx: &RequestContext,
898 body: &[u8],
899 response: &mut ResponseBuilder,
900 ) {
901 let name = self.parse_name(body);
902
903 match self.fs.removexattr(ctx.nodeid, name) {
904 Ok(()) => response.write_empty(ctx.unique),
905 Err(e) => response.write_error(ctx.unique, e.to_errno()),
906 }
907 }
908
909 fn handle_statfs(&self, ctx: &RequestContext, response: &mut ResponseBuilder) {
914 match self.fs.statfs() {
915 Ok(st) => {
916 let statfs_out = FuseStatfsOut { st };
917 response.write_data(ctx.unique, &statfs_out);
918 }
919 Err(e) => response.write_error(ctx.unique, e.to_errno()),
920 }
921 }
922
923 fn handle_access(&self, ctx: &RequestContext, body: &[u8], response: &mut ResponseBuilder) {
924 if body.len() < size_of::<FuseAccessIn>() {
925 response.write_error(ctx.unique, libc::EINVAL);
926 return;
927 }
928
929 let access_in = unsafe { &*(body.as_ptr() as *const FuseAccessIn) };
930
931 match self.fs.access(ctx.nodeid, access_in.mask) {
932 Ok(()) => response.write_empty(ctx.unique),
933 Err(e) => response.write_error(ctx.unique, e.to_errno()),
934 }
935 }
936
937 fn parse_name<'a>(&self, body: &'a [u8]) -> &'a OsStr {
942 let name_end = body.iter().position(|&b| b == 0).unwrap_or(body.len());
943 OsStr::from_bytes(&body[..name_end])
944 }
945
946 fn make_entry_out(&self, inode: u64, attr: &FuseAttr) -> FuseEntryOut {
947 FuseEntryOut {
948 nodeid: inode,
949 generation: 0,
950 entry_valid: self.config.entry_timeout,
951 attr_valid: self.config.attr_timeout,
952 entry_valid_nsec: 0,
953 attr_valid_nsec: 0,
954 attr: *attr,
955 }
956 }
957}
958
959impl std::fmt::Debug for FuseDispatcher {
960 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
961 f.debug_struct("FuseDispatcher")
962 .field("config", &self.config)
963 .field(
964 "initialized",
965 &self.initialized.load(std::sync::atomic::Ordering::Relaxed),
966 )
967 .finish()
968 }
969}
970
971#[cfg(test)]
976mod tests {
977 use super::*;
978 use tempfile::TempDir;
979
980 fn setup_dispatcher() -> (TempDir, FuseDispatcher) {
981 let temp = TempDir::new().expect("failed to create temp dir");
982 let fs = Arc::new(PassthroughFs::new(temp.path()).expect("failed to create fs"));
983 let dispatcher = FuseDispatcher::new(fs, DispatcherConfig::default());
984 (temp, dispatcher)
985 }
986
987 fn make_header(opcode: FuseOpcode, nodeid: u64, body_len: usize) -> Vec<u8> {
988 let header = FuseInHeader {
989 len: (FuseInHeader::SIZE + body_len) as u32,
990 opcode: opcode as u32,
991 unique: 1,
992 nodeid,
993 uid: 0,
994 gid: 0,
995 pid: 0,
996 padding: 0,
997 };
998
999 let header_bytes = unsafe {
1000 std::slice::from_raw_parts(
1001 &header as *const FuseInHeader as *const u8,
1002 FuseInHeader::SIZE,
1003 )
1004 };
1005 header_bytes.to_vec()
1006 }
1007
1008 fn parse_response_header(response: &[u8]) -> FuseOutHeader {
1009 assert!(response.len() >= FuseOutHeader::SIZE);
1010 unsafe { *(response.as_ptr() as *const FuseOutHeader) }
1011 }
1012
1013 #[test]
1014 fn test_init() {
1015 let (_temp, dispatcher) = setup_dispatcher();
1016
1017 let init_in = FuseInitIn {
1018 major: FUSE_KERNEL_VERSION,
1019 minor: FUSE_KERNEL_MINOR_VERSION,
1020 max_readahead: 128 * 1024,
1021 flags: 0,
1022 };
1023
1024 let mut request = make_header(FuseOpcode::Init, 0, size_of::<FuseInitIn>());
1025 let init_bytes = unsafe {
1026 std::slice::from_raw_parts(
1027 &init_in as *const FuseInitIn as *const u8,
1028 size_of::<FuseInitIn>(),
1029 )
1030 };
1031 request.extend_from_slice(init_bytes);
1032
1033 let response = dispatcher.dispatch(&request).unwrap();
1034 let header = parse_response_header(&response);
1035
1036 assert_eq!(header.error, 0);
1037 assert!(
1038 dispatcher
1039 .initialized
1040 .load(std::sync::atomic::Ordering::Relaxed)
1041 );
1042 }
1043
1044 #[test]
1045 fn test_getattr_root() {
1046 let (_temp, dispatcher) = setup_dispatcher();
1047
1048 let request = make_header(FuseOpcode::Getattr, 1, 0);
1049 let response = dispatcher.dispatch(&request).unwrap();
1050 let header = parse_response_header(&response);
1051
1052 assert_eq!(header.error, 0);
1053 assert!(response.len() > FuseOutHeader::SIZE);
1054 }
1055
1056 #[test]
1057 fn test_lookup_nonexistent() {
1058 let (_temp, dispatcher) = setup_dispatcher();
1059
1060 let name = b"nonexistent\0";
1061 let mut request = make_header(FuseOpcode::Lookup, 1, name.len());
1062 request.extend_from_slice(name);
1063
1064 let response = dispatcher.dispatch(&request).unwrap();
1065 let header = parse_response_header(&response);
1066
1067 assert_eq!(header.error, -libc::ENOENT);
1068 }
1069
1070 #[test]
1071 fn test_lookup_existing() {
1072 let (temp, dispatcher) = setup_dispatcher();
1073
1074 std::fs::write(temp.path().join("test.txt"), "hello").unwrap();
1076
1077 let name = b"test.txt\0";
1078 let mut request = make_header(FuseOpcode::Lookup, 1, name.len());
1079 request.extend_from_slice(name);
1080
1081 let response = dispatcher.dispatch(&request).unwrap();
1082 let header = parse_response_header(&response);
1083
1084 assert_eq!(header.error, 0);
1085 }
1086
1087 #[test]
1088 fn test_mkdir_and_rmdir() {
1089 let (_temp, dispatcher) = setup_dispatcher();
1090
1091 let mkdir_in = FuseMkdirIn {
1093 mode: 0o755,
1094 umask: 0,
1095 };
1096 let name = b"testdir\0";
1097
1098 let mut request = make_header(FuseOpcode::Mkdir, 1, size_of::<FuseMkdirIn>() + name.len());
1099 let mkdir_bytes = unsafe {
1100 std::slice::from_raw_parts(
1101 &mkdir_in as *const FuseMkdirIn as *const u8,
1102 size_of::<FuseMkdirIn>(),
1103 )
1104 };
1105 request.extend_from_slice(mkdir_bytes);
1106 request.extend_from_slice(name);
1107
1108 let response = dispatcher.dispatch(&request).unwrap();
1109 let header = parse_response_header(&response);
1110 assert_eq!(header.error, 0);
1111
1112 let mut request = make_header(FuseOpcode::Rmdir, 1, name.len());
1114 request.extend_from_slice(name);
1115
1116 let response = dispatcher.dispatch(&request).unwrap();
1117 let header = parse_response_header(&response);
1118 assert_eq!(header.error, 0);
1119 }
1120
1121 #[test]
1122 fn test_open_read_write_release() {
1123 let (temp, dispatcher) = setup_dispatcher();
1124
1125 std::fs::write(temp.path().join("test.txt"), "initial").unwrap();
1127
1128 let name = b"test.txt\0";
1130 let mut request = make_header(FuseOpcode::Lookup, 1, name.len());
1131 request.extend_from_slice(name);
1132 let response = dispatcher.dispatch(&request).unwrap();
1133 let header = parse_response_header(&response);
1134 assert_eq!(header.error, 0);
1135
1136 let entry = unsafe {
1138 &*((response.as_ptr() as *const u8).add(FuseOutHeader::SIZE) as *const FuseEntryOut)
1139 };
1140 let inode = entry.nodeid;
1141
1142 let open_in = FuseOpenIn {
1144 flags: libc::O_RDWR as u32,
1145 unused: 0,
1146 };
1147 let mut request = make_header(FuseOpcode::Open, inode, size_of::<FuseOpenIn>());
1148 let open_bytes = unsafe {
1149 std::slice::from_raw_parts(
1150 &open_in as *const FuseOpenIn as *const u8,
1151 size_of::<FuseOpenIn>(),
1152 )
1153 };
1154 request.extend_from_slice(open_bytes);
1155
1156 let response = dispatcher.dispatch(&request).unwrap();
1157 let header = parse_response_header(&response);
1158 assert_eq!(header.error, 0);
1159
1160 let open_out = unsafe {
1162 &*((response.as_ptr() as *const u8).add(FuseOutHeader::SIZE) as *const FuseOpenOut)
1163 };
1164 let fh = open_out.fh;
1165
1166 let read_in = FuseReadIn {
1168 fh,
1169 offset: 0,
1170 size: 100,
1171 read_flags: 0,
1172 lock_owner: 0,
1173 flags: 0,
1174 padding: 0,
1175 };
1176 let mut request = make_header(FuseOpcode::Read, inode, size_of::<FuseReadIn>());
1177 let read_bytes = unsafe {
1178 std::slice::from_raw_parts(
1179 &read_in as *const FuseReadIn as *const u8,
1180 size_of::<FuseReadIn>(),
1181 )
1182 };
1183 request.extend_from_slice(read_bytes);
1184
1185 let response = dispatcher.dispatch(&request).unwrap();
1186 let header = parse_response_header(&response);
1187 assert_eq!(header.error, 0);
1188
1189 let data = &response[FuseOutHeader::SIZE..];
1190 assert_eq!(data, b"initial");
1191
1192 let release_in = FuseReleaseIn {
1194 fh,
1195 flags: 0,
1196 release_flags: 0,
1197 lock_owner: 0,
1198 };
1199 let mut request = make_header(FuseOpcode::Release, inode, size_of::<FuseReleaseIn>());
1200 let release_bytes = unsafe {
1201 std::slice::from_raw_parts(
1202 &release_in as *const FuseReleaseIn as *const u8,
1203 size_of::<FuseReleaseIn>(),
1204 )
1205 };
1206 request.extend_from_slice(release_bytes);
1207
1208 let response = dispatcher.dispatch(&request).unwrap();
1209 let header = parse_response_header(&response);
1210 assert_eq!(header.error, 0);
1211 }
1212
1213 #[test]
1214 fn test_statfs() {
1215 let (_temp, dispatcher) = setup_dispatcher();
1216
1217 let request = make_header(FuseOpcode::Statfs, 1, 0);
1218 let response = dispatcher.dispatch(&request).unwrap();
1219 let header = parse_response_header(&response);
1220
1221 assert_eq!(header.error, 0);
1222 assert!(response.len() >= FuseOutHeader::SIZE + size_of::<FuseStatfsOut>());
1223 }
1224
1225 #[test]
1226 fn test_unknown_opcode() {
1227 let (_temp, dispatcher) = setup_dispatcher();
1228
1229 let header = FuseInHeader {
1230 len: FuseInHeader::SIZE as u32,
1231 opcode: 9999, unique: 1,
1233 nodeid: 1,
1234 uid: 0,
1235 gid: 0,
1236 pid: 0,
1237 padding: 0,
1238 };
1239
1240 let request = unsafe {
1241 std::slice::from_raw_parts(
1242 &header as *const FuseInHeader as *const u8,
1243 FuseInHeader::SIZE,
1244 )
1245 };
1246
1247 let result = dispatcher.dispatch(request);
1248 assert!(result.is_err());
1249 }
1250
1251 #[test]
1252 fn test_unsupported_opcode() {
1253 let (_temp, dispatcher) = setup_dispatcher();
1254
1255 let request = make_header(FuseOpcode::Ioctl, 1, 0);
1257 let response = dispatcher.dispatch(&request).unwrap();
1258 let header = parse_response_header(&response);
1259
1260 assert_eq!(header.error, -libc::ENOSYS);
1261 }
1262
1263 #[test]
1264 fn test_opendir_readdir_releasedir() {
1265 let (temp, dispatcher) = setup_dispatcher();
1266
1267 std::fs::write(temp.path().join("file1.txt"), "").unwrap();
1269 std::fs::write(temp.path().join("file2.txt"), "").unwrap();
1270
1271 let open_in = FuseOpenIn {
1273 flags: 0,
1274 unused: 0,
1275 };
1276 let mut request = make_header(FuseOpcode::Opendir, 1, size_of::<FuseOpenIn>());
1277 let open_bytes = unsafe {
1278 std::slice::from_raw_parts(
1279 &open_in as *const FuseOpenIn as *const u8,
1280 size_of::<FuseOpenIn>(),
1281 )
1282 };
1283 request.extend_from_slice(open_bytes);
1284
1285 let response = dispatcher.dispatch(&request).unwrap();
1286 let header = parse_response_header(&response);
1287 assert_eq!(header.error, 0);
1288
1289 let open_out = unsafe {
1290 &*((response.as_ptr() as *const u8).add(FuseOutHeader::SIZE) as *const FuseOpenOut)
1291 };
1292 let fh = open_out.fh;
1293
1294 let read_in = FuseReadIn {
1296 fh,
1297 offset: 0,
1298 size: 4096,
1299 read_flags: 0,
1300 lock_owner: 0,
1301 flags: 0,
1302 padding: 0,
1303 };
1304 let mut request = make_header(FuseOpcode::Readdir, 1, size_of::<FuseReadIn>());
1305 let read_bytes = unsafe {
1306 std::slice::from_raw_parts(
1307 &read_in as *const FuseReadIn as *const u8,
1308 size_of::<FuseReadIn>(),
1309 )
1310 };
1311 request.extend_from_slice(read_bytes);
1312
1313 let response = dispatcher.dispatch(&request).unwrap();
1314 let header = parse_response_header(&response);
1315 assert_eq!(header.error, 0);
1316
1317 let release_in = FuseReleaseIn {
1319 fh,
1320 flags: 0,
1321 release_flags: 0,
1322 lock_owner: 0,
1323 };
1324 let mut request = make_header(FuseOpcode::Releasedir, 1, size_of::<FuseReleaseIn>());
1325 let release_bytes = unsafe {
1326 std::slice::from_raw_parts(
1327 &release_in as *const FuseReleaseIn as *const u8,
1328 size_of::<FuseReleaseIn>(),
1329 )
1330 };
1331 request.extend_from_slice(release_bytes);
1332
1333 let response = dispatcher.dispatch(&request).unwrap();
1334 let header = parse_response_header(&response);
1335 assert_eq!(header.error, 0);
1336 }
1337
1338 #[test]
1339 fn test_response_builder() {
1340 let mut builder = ResponseBuilder::new();
1341
1342 builder.write_error(123, libc::ENOENT);
1344 let response = builder.as_bytes();
1345 let header = parse_response_header(response);
1346 assert_eq!(header.unique, 123);
1347 assert_eq!(header.error, -libc::ENOENT);
1348 assert_eq!(header.len as usize, FuseOutHeader::SIZE);
1349
1350 builder.write_empty(456);
1352 let response = builder.as_bytes();
1353 let header = parse_response_header(response);
1354 assert_eq!(header.unique, 456);
1355 assert_eq!(header.error, 0);
1356 assert_eq!(header.len as usize, FuseOutHeader::SIZE);
1357
1358 builder.write_bytes(789, b"hello");
1360 let response = builder.as_bytes();
1361 let header = parse_response_header(response);
1362 assert_eq!(header.unique, 789);
1363 assert_eq!(header.error, 0);
1364 assert_eq!(header.len as usize, FuseOutHeader::SIZE + 5);
1365 assert_eq!(&response[FuseOutHeader::SIZE..], b"hello");
1366 }
1367}