1use std::convert::TryFrom;
7use std::error;
8use std::fmt;
9use std::fmt::Display;
10use std::fmt::Formatter;
11use std::path::Path;
12
13use log::warn;
14use nix::unistd::Gid;
15use nix::unistd::Pid;
16use nix::unistd::Uid;
17#[cfg(feature = "serializable")]
18use serde::Deserialize;
19#[cfg(feature = "serializable")]
20use serde::Serialize;
21
22use crate::Errno;
23use crate::ll::argument::ArgumentIterator;
24use crate::ll::fuse_abi as abi;
25use crate::ll::fuse_abi::fuse_in_header;
26use crate::ll::fuse_abi::fuse_opcode;
27
28#[derive(Debug)]
30pub(crate) enum RequestError {
31 ShortReadHeader(usize),
33 UnknownOperation(u32),
35 ShortRead(usize, usize),
37 InsufficientData,
39}
40
41#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
52#[cfg_attr(feature = "serializable", derive(Serialize, Deserialize))]
53pub struct RequestId(pub u64);
54impl From<RequestId> for u64 {
55 fn from(fh: RequestId) -> Self {
56 fh.0
57 }
58}
59
60#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
80#[cfg_attr(feature = "serializable", derive(Serialize, Deserialize))]
81pub struct INodeNo(pub u64);
82
83impl INodeNo {
84 pub const ROOT: INodeNo = INodeNo(1);
86}
87
88impl From<INodeNo> for u64 {
89 fn from(fh: INodeNo) -> Self {
90 fh.0
91 }
92}
93
94impl Display for INodeNo {
95 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
96 Display::fmt(&self.0, f)
97 }
98}
99
100#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
132#[cfg_attr(feature = "serializable", derive(Serialize, Deserialize))]
133pub struct FileHandle(pub u64);
134
135impl From<FileHandle> for u64 {
136 fn from(fh: FileHandle) -> Self {
137 fh.0
138 }
139}
140
141impl Display for FileHandle {
142 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
143 Display::fmt(&self.0, f)
144 }
145}
146
147#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
153#[cfg_attr(feature = "serializable", derive(Serialize, Deserialize))]
154pub struct LockOwner(pub u64);
155
156impl Display for LockOwner {
157 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
158 Display::fmt(&self.0, f)
159 }
160}
161
162#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
163pub(crate) struct Lock {
164 pub(crate) range: (u64, u64),
167 pub(crate) typ: i32,
169 pub(crate) pid: u32,
170}
171impl Lock {
172 fn from_abi(x: &abi::fuse_file_lock) -> Lock {
173 Lock {
174 range: (x.start, x.end),
175 typ: x.typ,
176 pid: x.pid,
177 }
178 }
179}
180
181#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
183#[cfg_attr(feature = "serializable", derive(Serialize, Deserialize))]
184pub struct Version(
185 pub u32,
187 pub u32,
189);
190
191impl Display for Version {
192 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
193 write!(f, "{}.{}", self.0, self.1)
194 }
195}
196
197#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
199pub(crate) struct FilenameInDir<'a> {
200 pub(crate) dir: INodeNo,
202 pub(crate) name: &'a Path,
206}
207
208impl fmt::Display for RequestError {
209 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
210 match self {
211 RequestError::ShortReadHeader(len) => write!(
212 f,
213 "Short read of FUSE request header ({len} < {})",
214 size_of::<fuse_in_header>()
215 ),
216 RequestError::UnknownOperation(opcode) => write!(f, "Unknown FUSE opcode ({opcode})"),
217 RequestError::ShortRead(len, total) => {
218 write!(f, "Short read of FUSE request ({len} < {total})")
219 }
220 RequestError::InsufficientData => write!(f, "Insufficient argument data"),
221 }
222 }
223}
224
225impl error::Error for RequestError {}
226
227fn validate_off_t(name: &'static str, value: u64) -> Result<(), Errno> {
228 if value > i64::MAX as u64 {
231 warn!("{name}={value} is out of range");
232 return Err(Errno::EINVAL);
233 }
234 Ok(())
235}
236
237mod op {
238 use std::cmp;
239 use std::convert::TryInto;
240 use std::ffi::OsStr;
241 use std::fmt::Display;
242 use std::mem::offset_of;
243 use std::num::NonZeroU32;
244 use std::path::Path;
245 use std::time::SystemTime;
246
247 use zerocopy::FromZeros;
248 use zerocopy::IntoBytes;
249
250 use crate::AccessFlags;
251 use crate::CopyFileRangeFlags;
252 use crate::Errno;
253 use crate::IoctlFlags;
254 use crate::OpenFlags;
255 use crate::PollEvents;
256 use crate::PollFlags;
257 use crate::PollHandle;
258 use crate::WriteFlags;
259 use crate::bsd_file_flags::BsdFileFlags;
260 use crate::ll::ResponseData;
261 use crate::ll::TimeOrNow;
262 use crate::ll::argument::ArgumentIterator;
263 use crate::ll::flags::fattr_flags::FattrFlags;
264 use crate::ll::flags::fsync_flags::FsyncFlags;
265 use crate::ll::flags::getattr_flags::GetattrFlags;
266 use crate::ll::flags::init_flags::InitFlags;
267 use crate::ll::flags::read_flags::ReadFlags;
268 use crate::ll::flags::release_flags::ReleaseFlags;
269 use crate::ll::request::FileHandle;
270 use crate::ll::request::FilenameInDir;
271 use crate::ll::request::INodeNo;
272 use crate::ll::request::Lock;
273 use crate::ll::request::LockOwner;
274 use crate::ll::request::Operation;
275 use crate::ll::request::RequestId;
276 use crate::ll::request::abi::*;
277 use crate::ll::request::validate_off_t;
278 use crate::time::system_time_from_time;
279
280 #[derive(Debug)]
286 pub(crate) struct Lookup<'a> {
287 #[expect(dead_code)]
288 header: &'a fuse_in_header,
289 name: &'a OsStr,
290 }
291 impl<'a> Lookup<'a> {
292 pub(crate) fn name(&self) -> &'a Path {
293 self.name.as_ref()
294 }
295 }
296 #[derive(Debug)]
309 pub(crate) struct Forget<'a> {
310 #[expect(dead_code)]
311 header: &'a fuse_in_header,
312 arg: &'a fuse_forget_in,
313 }
314 impl Forget<'_> {
315 pub(crate) fn nlookup(&self) -> u64 {
317 self.arg.nlookup
318 }
319 }
320
321 #[derive(Debug)]
323 pub(crate) struct GetAttr<'a> {
324 #[expect(dead_code)]
325 header: &'a fuse_in_header,
326 arg: &'a fuse_getattr_in,
327 }
328
329 impl GetAttr<'_> {
330 pub(crate) fn getattr_flags(&self) -> GetattrFlags {
332 GetattrFlags::from_bits_retain(self.arg.getattr_flags)
333 }
334
335 pub(crate) fn file_handle(&self) -> Option<FileHandle> {
336 if self.getattr_flags().contains(GetattrFlags::FUSE_GETATTR_FH) {
337 Some(FileHandle(self.arg.fh))
338 } else {
339 None
340 }
341 }
342 }
343
344 #[derive(Debug)]
346 pub(crate) struct SetAttr<'a> {
347 #[expect(dead_code)]
348 header: &'a fuse_in_header,
349 arg: &'a fuse_setattr_in,
350 }
351 impl SetAttr<'_> {
352 fn valid(&self) -> FattrFlags {
353 FattrFlags::from_bits_retain(self.arg.valid)
354 }
355 pub(crate) fn mode(&self) -> Option<u32> {
356 if self.valid().contains(FattrFlags::FATTR_MODE) {
357 Some(self.arg.mode)
358 } else {
359 None
360 }
361 }
362 pub(crate) fn uid(&self) -> Option<u32> {
363 if self.valid().contains(FattrFlags::FATTR_UID) {
364 Some(self.arg.uid)
365 } else {
366 None
367 }
368 }
369 pub(crate) fn gid(&self) -> Option<u32> {
370 if self.valid().contains(FattrFlags::FATTR_GID) {
371 Some(self.arg.gid)
372 } else {
373 None
374 }
375 }
376 pub(crate) fn size(&self) -> Option<u64> {
377 if self.valid().contains(FattrFlags::FATTR_SIZE) {
378 Some(self.arg.size)
379 } else {
380 None
381 }
382 }
383 pub(crate) fn atime(&self) -> Option<TimeOrNow> {
384 if self.valid().contains(FattrFlags::FATTR_ATIME) {
385 Some(if self.arg.atime_now() {
386 TimeOrNow::Now
387 } else {
388 TimeOrNow::SpecificTime(system_time_from_time(
389 self.arg.atime,
390 self.arg.atimensec,
391 ))
392 })
393 } else {
394 None
395 }
396 }
397 pub(crate) fn mtime(&self) -> Option<TimeOrNow> {
398 if self.valid().contains(FattrFlags::FATTR_MTIME) {
399 Some(if self.arg.mtime_now() {
400 TimeOrNow::Now
401 } else {
402 TimeOrNow::SpecificTime(system_time_from_time(
403 self.arg.mtime,
404 self.arg.mtimensec,
405 ))
406 })
407 } else {
408 None
409 }
410 }
411 pub(crate) fn ctime(&self) -> Option<SystemTime> {
412 if self.valid().contains(FattrFlags::FATTR_CTIME) {
413 Some(system_time_from_time(self.arg.ctime, self.arg.ctimensec))
414 } else {
415 None
416 }
417 }
418 pub(crate) fn file_handle(&self) -> Option<FileHandle> {
423 if self.valid().contains(FattrFlags::FATTR_FH) {
424 Some(FileHandle(self.arg.fh))
425 } else {
426 None
427 }
428 }
429 pub(crate) fn crtime(&self) -> Option<SystemTime> {
430 #[cfg(target_os = "macos")]
431 if self.valid().contains(FattrFlags::FATTR_CRTIME) {
432 if self.arg.crtime == 0xffffffff83da4f80 {
436 None
437 } else {
438 Some(
439 SystemTime::UNIX_EPOCH
440 + std::time::Duration::new(self.arg.crtime, self.arg.crtimensec),
441 )
442 }
443 } else {
444 None
445 }
446 #[cfg(not(target_os = "macos"))]
447 None
448 }
449 pub(crate) fn chgtime(&self) -> Option<SystemTime> {
450 #[cfg(target_os = "macos")]
451 if self.valid().contains(FattrFlags::FATTR_CHGTIME) {
452 Some(
453 SystemTime::UNIX_EPOCH
454 + std::time::Duration::new(self.arg.chgtime, self.arg.chgtimensec),
455 )
456 } else {
457 None
458 }
459 #[cfg(not(target_os = "macos"))]
460 None
461 }
462 pub(crate) fn bkuptime(&self) -> Option<SystemTime> {
463 #[cfg(target_os = "macos")]
464 if self.valid().contains(FattrFlags::FATTR_BKUPTIME) {
465 Some(
466 SystemTime::UNIX_EPOCH
467 + std::time::Duration::new(self.arg.bkuptime, self.arg.bkuptimensec),
468 )
469 } else {
470 None
471 }
472 #[cfg(not(target_os = "macos"))]
473 None
474 }
475 pub(crate) fn flags(&self) -> Option<BsdFileFlags> {
476 #[cfg(target_os = "macos")]
477 if self.valid().contains(FattrFlags::FATTR_FLAGS) {
478 Some(BsdFileFlags::from_bits_retain(self.arg.flags))
479 } else {
480 None
481 }
482 #[cfg(not(target_os = "macos"))]
483 None
484 }
485
486 }
488 impl Display for SetAttr<'_> {
489 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
490 write!(
491 f,
492 "SETATTR mode: {:?}, uid: {:?}, gid: {:?}, size: {:?}, atime: {:?}, \
493 mtime: {:?}, ctime: {:?}, file_handle: {:?}, crtime: {:?}, chgtime: {:?}, \
494 bkuptime: {:?}, flags: {:?}",
495 self.mode(),
496 self.uid(),
497 self.gid(),
498 self.size(),
499 self.atime(),
500 self.mtime(),
501 self.ctime(),
502 self.file_handle(),
503 self.crtime(),
504 self.chgtime(),
505 self.bkuptime(),
506 self.flags()
507 )
508 }
509 }
510
511 #[derive(Debug)]
513 pub(crate) struct ReadLink<'a> {
514 #[expect(dead_code)]
515 header: &'a fuse_in_header,
516 }
517
518 #[derive(Debug)]
520 pub(crate) struct SymLink<'a> {
521 #[expect(dead_code)]
522 header: &'a fuse_in_header,
523 target: &'a Path,
524 link_name: &'a Path,
525 }
526 impl<'a> SymLink<'a> {
527 pub(crate) fn target(&self) -> &'a Path {
528 self.target
529 }
530 pub(crate) fn link_name(&self) -> &'a Path {
531 self.link_name
532 }
533 }
534
535 #[derive(Debug)]
538 pub(crate) struct MkNod<'a> {
539 #[expect(dead_code)]
540 header: &'a fuse_in_header,
541 arg: &'a fuse_mknod_in,
542 name: &'a Path,
543 }
544 impl<'a> MkNod<'a> {
545 pub(crate) fn name(&self) -> &'a Path {
546 self.name
547 }
548 pub(crate) fn mode(&self) -> u32 {
549 self.arg.mode
550 }
551 pub(crate) fn umask(&self) -> u32 {
552 self.arg.umask
553 }
554 pub(crate) fn rdev(&self) -> u32 {
555 self.arg.rdev
556 }
557 }
558
559 #[derive(Debug)]
561 pub(crate) struct MkDir<'a> {
562 #[expect(dead_code)]
563 header: &'a fuse_in_header,
564 arg: &'a fuse_mkdir_in,
565 name: &'a Path,
566 }
567 impl<'a> MkDir<'a> {
568 pub(crate) fn name(&self) -> &'a Path {
569 self.name
570 }
571 pub(crate) fn mode(&self) -> u32 {
572 self.arg.mode
573 }
574 pub(crate) fn umask(&self) -> u32 {
575 self.arg.umask
576 }
577 }
578
579 #[derive(Debug)]
581 pub(crate) struct Unlink<'a> {
582 #[expect(dead_code)]
583 header: &'a fuse_in_header,
584 name: &'a Path,
585 }
586 impl<'a> Unlink<'a> {
587 pub(crate) fn name(&self) -> &'a Path {
588 self.name
589 }
590 }
591
592 #[derive(Debug)]
594 pub(crate) struct RmDir<'a> {
595 #[expect(dead_code)]
596 header: &'a fuse_in_header,
597 pub(crate) name: &'a Path,
598 }
599 impl<'a> RmDir<'a> {
600 pub(crate) fn name(&self) -> &'a Path {
601 self.name
602 }
603 }
604
605 #[derive(Debug)]
607 pub(crate) struct Rename<'a> {
608 header: &'a fuse_in_header,
609 arg: &'a fuse_rename_in,
610 name: &'a Path,
611 newname: &'a Path,
612 }
613 impl<'a> Rename<'a> {
614 pub(crate) fn src(&self) -> FilenameInDir<'a> {
615 FilenameInDir::<'a> {
616 dir: INodeNo(self.header.nodeid),
617 name: self.name,
618 }
619 }
620 pub(crate) fn dest(&self) -> FilenameInDir<'a> {
621 FilenameInDir::<'a> {
622 dir: INodeNo(self.arg.newdir),
623 name: self.newname,
624 }
625 }
626 }
627
628 #[derive(Debug)]
630 pub(crate) struct Link<'a> {
631 header: &'a fuse_in_header,
632 arg: &'a fuse_link_in,
633 name: &'a Path,
634 }
635 impl<'a> Link<'a> {
636 pub(crate) fn inode_no(&self) -> INodeNo {
639 INodeNo(self.arg.oldnodeid)
640 }
641 pub(crate) fn dest(&self) -> FilenameInDir<'a> {
642 FilenameInDir::<'a> {
643 dir: INodeNo(self.header.nodeid),
644 name: self.name,
645 }
646 }
647 }
648
649 #[derive(Debug)]
659 pub(crate) struct Open<'a> {
660 #[expect(dead_code)]
661 header: &'a fuse_in_header,
662 arg: &'a fuse_open_in,
663 }
664 impl Open<'_> {
665 pub(crate) fn flags(&self) -> OpenFlags {
666 OpenFlags(self.arg.flags)
667 }
668 }
669
670 #[derive(Debug)]
678 pub(crate) struct Read<'a> {
679 #[expect(dead_code)]
680 header: &'a fuse_in_header,
681 arg: &'a fuse_read_in,
682 }
683 impl Read<'_> {
684 pub(crate) fn file_handle(&self) -> FileHandle {
686 FileHandle(self.arg.fh)
687 }
688 pub(crate) fn offset(&self) -> Result<u64, Errno> {
689 validate_off_t("fuse_read_in.arg.offset", self.arg.offset)?;
690 Ok(self.arg.offset)
691 }
692 pub(crate) fn size(&self) -> u32 {
693 self.arg.size
694 }
695 pub(crate) fn lock_owner(&self) -> Option<LockOwner> {
697 if self.read_flags().contains(ReadFlags::FUSE_READ_LOCKOWNER) {
698 Some(LockOwner(self.arg.lock_owner))
699 } else {
700 None
701 }
702 }
703 pub(crate) fn flags(&self) -> OpenFlags {
705 OpenFlags(self.arg.flags)
706 }
707 pub(crate) fn read_flags(&self) -> ReadFlags {
708 ReadFlags::from_bits_retain(self.arg.read_flags)
709 }
710 }
711
712 #[derive(Debug)]
719 pub(crate) struct Write<'a> {
720 #[expect(dead_code)]
721 header: &'a fuse_in_header,
722 arg: &'a fuse_write_in,
723 data: &'a [u8],
724 }
725 impl<'a> Write<'a> {
726 pub(crate) fn file_handle(&self) -> FileHandle {
728 FileHandle(self.arg.fh)
729 }
730 pub(crate) fn offset(&self) -> Result<u64, Errno> {
731 let offset = self.arg.offset as u64;
732 validate_off_t("fuse_write_in.arg.offset", offset)?;
733 Ok(offset)
734 }
735 pub(crate) fn data(&self) -> &'a [u8] {
736 self.data
737 }
738 pub(crate) fn write_flags(&self) -> WriteFlags {
742 WriteFlags::from_bits_retain(self.arg.write_flags)
743 }
744 pub(crate) fn lock_owner(&self) -> Option<LockOwner> {
746 if self
747 .write_flags()
748 .contains(WriteFlags::FUSE_WRITE_LOCKOWNER)
749 {
750 Some(LockOwner(self.arg.lock_owner))
751 } else {
752 None
753 }
754 }
755 pub(crate) fn flags(&self) -> OpenFlags {
757 OpenFlags(self.arg.flags)
758 }
759 }
760
761 #[derive(Debug)]
763 pub(crate) struct StatFs<'a> {
764 #[expect(dead_code)]
765 header: &'a fuse_in_header,
766 }
767
768 #[derive(Debug)]
776 pub(crate) struct Release<'a> {
777 #[expect(dead_code)]
778 header: &'a fuse_in_header,
779 arg: &'a fuse_release_in,
780 }
781 impl Release<'_> {
782 pub(crate) fn release_flags(&self) -> ReleaseFlags {
784 ReleaseFlags::from_bits_retain(self.arg.release_flags)
785 }
786 pub(crate) fn flush(&self) -> bool {
787 self.release_flags()
788 .contains(ReleaseFlags::FUSE_RELEASE_FLUSH)
789 }
790 pub(crate) fn file_handle(&self) -> FileHandle {
792 FileHandle(self.arg.fh)
793 }
794 pub(crate) fn flags(&self) -> OpenFlags {
796 OpenFlags(self.arg.flags)
797 }
798 pub(crate) fn lock_owner(&self) -> Option<LockOwner> {
799 if self
800 .release_flags()
801 .contains(ReleaseFlags::FUSE_RELEASE_FLOCK_UNLOCK)
802 {
803 Some(LockOwner(self.arg.lock_owner))
804 } else {
805 None
806 }
807 }
808 }
809
810 #[derive(Debug)]
812 pub(crate) struct FSync<'a> {
813 #[expect(dead_code)]
814 header: &'a fuse_in_header,
815 arg: &'a fuse_fsync_in,
816 }
817 impl FSync<'_> {
818 pub(crate) fn file_handle(&self) -> FileHandle {
820 FileHandle(self.arg.fh)
821 }
822 pub(crate) fn fdatasync(&self) -> bool {
824 FsyncFlags::from_bits_retain(self.arg.fsync_flags)
825 .contains(FsyncFlags::FUSE_FSYNC_FDATASYNC)
826 }
827 }
828
829 #[derive(Debug)]
831 pub(crate) struct SetXAttr<'a> {
832 #[expect(dead_code)]
833 header: &'a fuse_in_header,
834 arg: &'a fuse_setxattr_in,
835 name: &'a OsStr,
836 value: &'a [u8],
837 }
838 impl<'a> SetXAttr<'a> {
839 pub(crate) fn name(&self) -> &'a OsStr {
840 self.name
841 }
842 pub(crate) fn value(&self) -> &'a [u8] {
843 self.value
844 }
845 pub(crate) fn flags(&self) -> i32 {
847 self.arg.flags
848 }
849 pub(crate) fn position(&self) -> u32 {
852 #[cfg(target_os = "macos")]
853 return self.arg.position;
854 #[cfg(not(target_os = "macos"))]
855 0
856 }
857 }
858
859 #[derive(Debug)]
864 pub(crate) struct GetXAttr<'a> {
865 #[expect(dead_code)]
866 header: &'a fuse_in_header,
867 arg: &'a fuse_getxattr_in,
868 name: &'a OsStr,
869 }
870
871 #[derive(Debug)]
875 pub(crate) struct GetXAttrSize();
876
877 #[derive(Debug)]
878 pub(crate) enum GetXAttrSizeEnum {
880 GetSize(GetXAttrSize),
882 #[allow(dead_code)]
885 Size(NonZeroU32),
886 }
887 impl<'a> GetXAttr<'a> {
888 pub(crate) fn name(&self) -> &'a OsStr {
890 self.name
891 }
892 pub(crate) fn size(&self) -> GetXAttrSizeEnum {
899 let s: Result<NonZeroU32, _> = self.arg.size.try_into();
900 match s {
901 Ok(s) => GetXAttrSizeEnum::Size(s),
902 Err(_) => GetXAttrSizeEnum::GetSize(GetXAttrSize()),
903 }
904 }
905 pub(crate) fn size_u32(&self) -> u32 {
907 self.arg.size
908 }
909 }
910
911 #[derive(Debug)]
913 pub(crate) struct ListXAttr<'a> {
914 #[expect(dead_code)]
915 header: &'a fuse_in_header,
916 arg: &'a fuse_getxattr_in,
917 }
918 impl ListXAttr<'_> {
919 pub(crate) fn size(&self) -> u32 {
925 self.arg.size
926 }
927 }
928
929 #[derive(Debug)]
934 pub(crate) struct RemoveXAttr<'a> {
935 #[expect(dead_code)]
936 header: &'a fuse_in_header,
937 name: &'a OsStr,
938 }
939 impl<'a> RemoveXAttr<'a> {
940 pub(crate) fn name(&self) -> &'a OsStr {
942 self.name
943 }
944 }
945
946 #[derive(Debug)]
958 pub(crate) struct Flush<'a> {
959 #[expect(dead_code)]
960 header: &'a fuse_in_header,
961 arg: &'a fuse_flush_in,
962 }
963 impl Flush<'_> {
964 pub(crate) fn file_handle(&self) -> FileHandle {
966 FileHandle(self.arg.fh)
967 }
968 pub(crate) fn lock_owner(&self) -> LockOwner {
969 LockOwner(self.arg.lock_owner)
970 }
971 }
972
973 #[derive(Debug)]
974 pub(crate) struct Init<'a> {
975 #[expect(dead_code)]
976 header: &'a fuse_in_header,
977 arg: Box<fuse_init_in>,
980 }
981 impl Init<'_> {
982 pub(crate) fn capabilities(&self) -> InitFlags {
983 let flags = InitFlags::from_bits_retain(u64::from(self.arg.flags));
984 if flags.contains(InitFlags::FUSE_INIT_EXT) {
985 return InitFlags::from_bits_retain(
986 u64::from(self.arg.flags) | (u64::from(self.arg.flags2) << 32),
987 );
988 }
989 flags
990 }
991 pub(crate) fn max_readahead(&self) -> u32 {
992 self.arg.max_readahead
993 }
994 pub(crate) fn version(&self) -> super::Version {
995 super::Version(self.arg.major, self.arg.minor)
996 }
997
998 pub(crate) fn reply(&self, config: &crate::KernelConfig) -> ResponseData {
999 let flags = config.requested | InitFlags::FUSE_INIT_EXT;
1000 let flags = flags & self.capabilities();
1002
1003 let mut init = fuse_init_out {
1004 major: FUSE_KERNEL_VERSION,
1005 minor: FUSE_KERNEL_MINOR_VERSION,
1006 max_readahead: config.max_readahead,
1007 flags: flags.pair().0,
1008 max_background: config.max_background,
1009 congestion_threshold: config.congestion_threshold(),
1010 max_write: config.max_write,
1011 time_gran: config.time_gran.as_nanos() as u32,
1012 max_pages: config.max_pages(),
1013 unused2: 0,
1014 flags2: flags.pair().1,
1015 max_stack_depth: 0,
1016 reserved: [0; 6],
1017 };
1018
1019 if flags.contains(InitFlags::FUSE_PASSTHROUGH) {
1020 init.max_stack_depth = config.max_stack_depth;
1021 }
1022
1023 let init = init.as_bytes();
1024 let init = if self.arg.minor < 5 {
1025 &init[..FUSE_COMPAT_INIT_OUT_SIZE]
1026 } else if self.arg.minor < 23 {
1027 &init[..FUSE_COMPAT_22_INIT_OUT_SIZE]
1028 } else {
1029 init
1030 };
1031 ResponseData::new_data(init)
1032 }
1033
1034 pub(crate) fn reply_version_only(&self) -> ResponseData {
1037 let init = fuse_init_out {
1038 major: FUSE_KERNEL_VERSION,
1039 minor: FUSE_KERNEL_MINOR_VERSION,
1040 max_readahead: 0,
1041 flags: 0,
1042 max_background: 0,
1043 congestion_threshold: 0,
1044 max_write: 0,
1045 time_gran: 0,
1046 max_pages: 0,
1047 unused2: 0,
1048 flags2: 0,
1049 max_stack_depth: 0,
1050 reserved: [0; 6],
1051 };
1052 ResponseData::new_data(init.as_bytes())
1053 }
1054 }
1055
1056 #[derive(Debug)]
1067 pub(crate) struct OpenDir<'a> {
1068 #[expect(dead_code)]
1069 header: &'a fuse_in_header,
1070 arg: &'a fuse_open_in,
1071 }
1072 impl OpenDir<'_> {
1073 pub(crate) fn flags(&self) -> OpenFlags {
1075 OpenFlags(self.arg.flags)
1076 }
1077 }
1078
1079 #[derive(Debug)]
1081 pub(crate) struct ReadDir<'a> {
1082 #[expect(dead_code)]
1083 header: &'a fuse_in_header,
1084 arg: &'a fuse_read_in,
1085 }
1086 impl ReadDir<'_> {
1087 pub(crate) fn file_handle(&self) -> FileHandle {
1089 FileHandle(self.arg.fh)
1090 }
1091 pub(crate) fn offset(&self) -> u64 {
1092 self.arg.offset
1093 }
1094 pub(crate) fn size(&self) -> u32 {
1095 self.arg.size
1096 }
1097 }
1098
1099 #[derive(Debug)]
1103 pub(crate) struct ReleaseDir<'a> {
1104 #[expect(dead_code)]
1105 header: &'a fuse_in_header,
1106 arg: &'a fuse_release_in,
1107 }
1108 impl ReleaseDir<'_> {
1109 pub(crate) fn file_handle(&self) -> FileHandle {
1111 FileHandle(self.arg.fh)
1112 }
1113 pub(crate) fn release_flags(&self) -> ReleaseFlags {
1115 ReleaseFlags::from_bits_retain(self.arg.release_flags)
1116 }
1117 pub(crate) fn flush(&self) -> bool {
1118 self.release_flags()
1119 .contains(ReleaseFlags::FUSE_RELEASE_FLUSH)
1120 }
1121 pub(crate) fn lock_owner(&self) -> Option<LockOwner> {
1122 if self
1123 .release_flags()
1124 .contains(ReleaseFlags::FUSE_RELEASE_FLOCK_UNLOCK)
1125 {
1126 Some(LockOwner(self.arg.lock_owner))
1127 } else {
1128 None
1129 }
1130 }
1131 pub(crate) fn flags(&self) -> OpenFlags {
1132 OpenFlags(self.arg.flags)
1133 }
1134 }
1135
1136 #[derive(Debug)]
1138 pub(crate) struct FSyncDir<'a> {
1139 #[expect(dead_code)]
1140 header: &'a fuse_in_header,
1141 arg: &'a fuse_fsync_in,
1142 }
1143 impl FSyncDir<'_> {
1144 pub(crate) fn file_handle(&self) -> FileHandle {
1146 FileHandle(self.arg.fh)
1147 }
1148 pub(crate) fn fdatasync(&self) -> bool {
1150 FsyncFlags::from_bits_retain(self.arg.fsync_flags)
1151 .contains(FsyncFlags::FUSE_FSYNC_FDATASYNC)
1152 }
1153 }
1154
1155 #[derive(Debug)]
1157 pub(crate) struct GetLk<'a> {
1158 #[expect(dead_code)]
1159 header: &'a fuse_in_header,
1160 arg: &'a fuse_lk_in,
1161 }
1162 impl GetLk<'_> {
1163 pub(crate) fn file_handle(&self) -> FileHandle {
1165 FileHandle(self.arg.fh)
1166 }
1167 pub(crate) fn lock(&self) -> Lock {
1168 Lock::from_abi(&self.arg.lk)
1169 }
1170 pub(crate) fn lock_owner(&self) -> LockOwner {
1171 LockOwner(self.arg.owner)
1172 }
1173 }
1174
1175 #[derive(Debug)]
1184 pub(crate) struct SetLk<'a> {
1185 #[expect(dead_code)]
1186 header: &'a fuse_in_header,
1187 arg: &'a fuse_lk_in,
1188 wait: bool,
1191 }
1192 impl SetLk<'_> {
1193 pub(crate) fn file_handle(&self) -> FileHandle {
1195 FileHandle(self.arg.fh)
1196 }
1197 pub(crate) fn lock(&self) -> Lock {
1198 Lock::from_abi(&self.arg.lk)
1199 }
1200 pub(crate) fn lock_owner(&self) -> LockOwner {
1201 LockOwner(self.arg.owner)
1202 }
1203 pub(crate) fn sleep(&self) -> bool {
1206 self.wait
1207 }
1208 }
1209
1210 #[derive(Debug)]
1215 pub(crate) struct Access<'a> {
1216 #[expect(dead_code)]
1217 header: &'a fuse_in_header,
1218 arg: &'a fuse_access_in,
1219 }
1220 impl Access<'_> {
1221 pub(crate) fn mask(&self) -> AccessFlags {
1222 AccessFlags::from_bits_retain(self.arg.mask)
1223 }
1224 }
1225
1226 #[derive(Debug)]
1238 pub(crate) struct Create<'a> {
1239 #[expect(dead_code)]
1240 header: &'a fuse_in_header,
1241 arg: &'a fuse_create_in,
1242 name: &'a Path,
1243 }
1244 impl<'a> Create<'a> {
1245 pub(crate) fn name(&self) -> &'a Path {
1246 self.name
1247 }
1248 pub(crate) fn mode(&self) -> u32 {
1249 self.arg.mode
1250 }
1251 pub(crate) fn flags(&self) -> i32 {
1253 self.arg.flags
1254 }
1255 pub(crate) fn umask(&self) -> u32 {
1256 self.arg.umask
1257 }
1258 }
1259
1260 #[derive(Debug)]
1298 pub(crate) struct Interrupt<'a> {
1299 #[expect(dead_code)]
1300 header: &'a fuse_in_header,
1301 arg: &'a fuse_interrupt_in,
1302 }
1303 impl Interrupt<'_> {
1304 pub(crate) fn unique(&self) -> RequestId {
1305 RequestId(self.arg.unique)
1306 }
1307 }
1308
1309 #[derive(Debug)]
1313 pub(crate) struct BMap<'a> {
1314 #[expect(dead_code)]
1315 header: &'a fuse_in_header,
1316 arg: &'a fuse_bmap_in,
1317 }
1318 impl BMap<'_> {
1319 pub(crate) fn block_size(&self) -> u32 {
1320 self.arg.blocksize
1321 }
1322 pub(crate) fn block(&self) -> u64 {
1323 self.arg.block
1324 }
1325 }
1326
1327 #[derive(Debug)]
1328 pub(crate) struct Destroy<'a> {
1329 #[expect(dead_code)]
1330 header: &'a fuse_in_header,
1331 }
1332
1333 #[derive(Debug)]
1335 pub(crate) struct IoCtl<'a> {
1336 #[expect(dead_code)]
1337 header: &'a fuse_in_header,
1338 arg: &'a fuse_ioctl_in,
1339 data: &'a [u8],
1340 }
1341 impl IoCtl<'_> {
1342 pub(crate) fn in_data(&self) -> &[u8] {
1343 &self.data[..self.arg.in_size as usize]
1344 }
1345 pub(crate) fn unrestricted(&self) -> bool {
1346 self.flags().contains(IoctlFlags::FUSE_IOCTL_UNRESTRICTED)
1347 }
1348 pub(crate) fn file_handle(&self) -> FileHandle {
1350 FileHandle(self.arg.fh)
1351 }
1352 pub(crate) fn flags(&self) -> IoctlFlags {
1353 IoctlFlags::from_bits_retain(self.arg.flags)
1354 }
1355 pub(crate) fn command(&self) -> u32 {
1357 self.arg.cmd
1358 }
1359 pub(crate) fn out_size(&self) -> u32 {
1360 self.arg.out_size
1361 }
1362 }
1363
1364 #[derive(Debug)]
1366 pub(crate) struct Poll<'a> {
1367 #[expect(dead_code)]
1368 header: &'a fuse_in_header,
1369 arg: &'a fuse_poll_in,
1370 }
1371 impl Poll<'_> {
1372 pub(crate) fn file_handle(&self) -> FileHandle {
1374 FileHandle(self.arg.fh)
1375 }
1376
1377 pub(crate) fn kernel_handle(&self) -> PollHandle {
1379 PollHandle(self.arg.kh)
1380 }
1381
1382 pub(crate) fn events(&self) -> PollEvents {
1384 PollEvents::from_bits_retain(self.arg.events)
1385 }
1386
1387 pub(crate) fn flags(&self) -> PollFlags {
1389 PollFlags::from_bits_retain(self.arg.flags)
1390 }
1391 }
1392
1393 #[derive(Debug)]
1395 pub(crate) struct NotifyReply<'a> {
1396 #[expect(dead_code)]
1397 header: &'a fuse_in_header,
1398 #[expect(dead_code)]
1399 arg: &'a [u8],
1400 }
1401
1402 #[derive(Debug)]
1404 pub(crate) struct BatchForget<'a> {
1405 #[expect(dead_code)]
1406 header: &'a fuse_in_header,
1407 #[expect(dead_code)]
1408 arg: &'a fuse_batch_forget_in,
1409 nodes: &'a [fuse_forget_one],
1410 }
1411 impl<'a> BatchForget<'a> {
1412 pub(crate) fn nodes(&self) -> &'a [fuse_forget_one] {
1413 self.nodes
1414 }
1415 }
1416
1417 #[derive(Debug)]
1421 pub(crate) struct FAllocate<'a> {
1422 #[expect(dead_code)]
1423 header: &'a fuse_in_header,
1424 arg: &'a fuse_fallocate_in,
1425 }
1426 impl FAllocate<'_> {
1427 pub(crate) fn file_handle(&self) -> FileHandle {
1429 FileHandle(self.arg.fh)
1430 }
1431 pub(crate) fn offset(&self) -> Result<u64, Errno> {
1432 validate_off_t("fuse_allocate_in.arg.offset", self.arg.offset)?;
1433 Ok(self.arg.offset)
1434 }
1435 pub(crate) fn len(&self) -> Result<u64, Errno> {
1436 validate_off_t("fuse_allocate_in.arg.length", self.arg.length)?;
1437 Ok(self.arg.length)
1438 }
1439 pub(crate) fn mode(&self) -> i32 {
1441 self.arg.mode
1442 }
1443 }
1444
1445 #[derive(Debug)]
1449 pub(crate) struct ReadDirPlus<'a> {
1450 #[expect(dead_code)]
1451 header: &'a fuse_in_header,
1452 arg: &'a fuse_read_in,
1453 }
1454 impl ReadDirPlus<'_> {
1455 pub(crate) fn file_handle(&self) -> FileHandle {
1457 FileHandle(self.arg.fh)
1458 }
1459 pub(crate) fn offset(&self) -> u64 {
1460 self.arg.offset
1461 }
1462 pub(crate) fn size(&self) -> u32 {
1463 self.arg.size
1464 }
1465 }
1466
1467 #[derive(Debug)]
1471 pub(crate) struct Rename2<'a> {
1472 #[expect(dead_code)]
1473 header: &'a fuse_in_header,
1474 arg: &'a fuse_rename2_in,
1475 name: &'a Path,
1476 newname: &'a Path,
1477 old_parent: INodeNo,
1478 }
1479 impl<'a> Rename2<'a> {
1480 pub(crate) fn from(&self) -> FilenameInDir<'a> {
1481 FilenameInDir::<'a> {
1482 dir: self.old_parent,
1483 name: self.name,
1484 }
1485 }
1486 pub(crate) fn to(&self) -> FilenameInDir<'a> {
1487 FilenameInDir::<'a> {
1488 dir: INodeNo(self.arg.newdir),
1489 name: self.newname,
1490 }
1491 }
1492 pub(crate) fn flags(&self) -> crate::RenameFlags {
1497 crate::RenameFlags::from_bits_retain(self.arg.flags)
1498 }
1499 }
1500
1501 #[derive(Debug)]
1505 pub(crate) struct Lseek<'a> {
1506 #[expect(dead_code)]
1507 header: &'a fuse_in_header,
1508 arg: &'a fuse_lseek_in,
1509 }
1510 impl Lseek<'_> {
1511 pub(crate) fn file_handle(&self) -> FileHandle {
1513 FileHandle(self.arg.fh)
1514 }
1515 pub(crate) fn offset(&self) -> i64 {
1516 self.arg.offset
1517 }
1518 pub(crate) fn whence(&self) -> i32 {
1520 self.arg.whence
1521 }
1522 }
1523
1524 #[derive(Debug, Clone, Copy)]
1526 pub(crate) struct CopyFileRangeFile {
1527 pub(crate) inode: INodeNo,
1528 pub(crate) file_handle: FileHandle,
1530 pub(crate) offset: u64,
1531 }
1532 #[derive(Debug)]
1533 pub(crate) struct CopyFileRange<'a> {
1534 header: &'a fuse_in_header,
1535 arg: &'a fuse_copy_file_range_in,
1536 }
1537 impl CopyFileRange<'_> {
1538 pub(crate) fn src(&self) -> Result<CopyFileRangeFile, Errno> {
1540 let offset = self.arg.off_in as u64;
1541 validate_off_t("fuse_copy_file_range_in.arg.off_in", offset)?;
1542 Ok(CopyFileRangeFile {
1543 inode: INodeNo(self.header.nodeid),
1544 file_handle: FileHandle(self.arg.fh_in),
1545 offset,
1546 })
1547 }
1548 pub(crate) fn dest(&self) -> Result<CopyFileRangeFile, Errno> {
1550 let offset = self.arg.off_out as u64;
1551 validate_off_t("fuse_copy_file_range_in.arg.off_out", offset)?;
1552 Ok(CopyFileRangeFile {
1553 inode: INodeNo(self.arg.nodeid_out),
1554 file_handle: FileHandle(self.arg.fh_out),
1555 offset,
1556 })
1557 }
1558 pub(crate) fn len(&self) -> u64 {
1560 self.arg.len
1561 }
1562 pub(crate) fn flags(&self) -> CopyFileRangeFlags {
1563 CopyFileRangeFlags::from_bits_retain(self.arg.flags)
1564 }
1565 }
1566
1567 #[cfg(target_os = "macos")]
1570 #[derive(Debug)]
1571 pub(crate) struct SetVolName<'a> {
1572 #[expect(dead_code)]
1573 header: &'a fuse_in_header,
1574 name: &'a OsStr,
1575 }
1576 #[cfg(target_os = "macos")]
1577 impl<'a> SetVolName<'a> {
1578 pub(crate) fn name(&self) -> &'a OsStr {
1579 self.name
1580 }
1581 }
1582
1583 #[cfg(target_os = "macos")]
1586 #[derive(Debug)]
1587 pub(crate) struct GetXTimes<'a> {
1588 header: &'a fuse_in_header,
1589 }
1590 #[cfg(target_os = "macos")]
1591 impl GetXTimes<'_> {
1592 pub(crate) fn nodeid(&self) -> INodeNo {
1593 INodeNo(self.header.nodeid)
1594 }
1595 }
1596 #[cfg(target_os = "macos")]
1599 #[derive(Debug)]
1600 pub(crate) struct Exchange<'a> {
1601 #[expect(dead_code)]
1602 header: &'a fuse_in_header,
1603 arg: &'a fuse_exchange_in,
1604 oldname: &'a Path,
1605 newname: &'a Path,
1606 }
1607 #[cfg(target_os = "macos")]
1608 impl<'a> Exchange<'a> {
1609 pub(crate) fn from(&self) -> FilenameInDir<'a> {
1610 FilenameInDir::<'a> {
1611 dir: INodeNo(self.arg.olddir),
1612 name: self.oldname,
1613 }
1614 }
1615 pub(crate) fn to(&self) -> FilenameInDir<'a> {
1616 FilenameInDir::<'a> {
1617 dir: INodeNo(self.arg.newdir),
1618 name: self.newname,
1619 }
1620 }
1621 pub(crate) fn options(&self) -> u64 {
1622 self.arg.options
1623 }
1624 }
1625 #[derive(Debug)]
1627 pub(crate) struct CuseInit<'a> {
1628 #[expect(dead_code)]
1629 header: &'a fuse_in_header,
1630 #[expect(dead_code)]
1631 arg: &'a fuse_init_in,
1632 }
1633
1634 pub(crate) fn parse<'a>(
1635 header: &'a fuse_in_header,
1636 opcode: &fuse_opcode,
1637 data: &'a [u8],
1638 ) -> Option<Operation<'a>> {
1639 let mut data = ArgumentIterator::new(data);
1640 Some(match opcode {
1641 fuse_opcode::FUSE_LOOKUP => Operation::Lookup(Lookup {
1642 header,
1643 name: data.fetch_str()?,
1644 }),
1645 fuse_opcode::FUSE_FORGET => Operation::Forget(Forget {
1646 header,
1647 arg: data.fetch()?,
1648 }),
1649 fuse_opcode::FUSE_GETATTR => Operation::GetAttr(GetAttr {
1650 header,
1651
1652 arg: data.fetch()?,
1653 }),
1654 fuse_opcode::FUSE_SETATTR => Operation::SetAttr(SetAttr {
1655 header,
1656 arg: data.fetch()?,
1657 }),
1658 fuse_opcode::FUSE_READLINK => Operation::ReadLink(ReadLink { header }),
1659 fuse_opcode::FUSE_SYMLINK => Operation::SymLink(SymLink {
1660 header,
1661 link_name: data.fetch_str()?.as_ref(),
1662 target: data.fetch_str()?.as_ref(),
1663 }),
1664 fuse_opcode::FUSE_MKNOD => Operation::MkNod(MkNod {
1665 header,
1666 arg: data.fetch()?,
1667 name: data.fetch_str()?.as_ref(),
1668 }),
1669 fuse_opcode::FUSE_MKDIR => Operation::MkDir(MkDir {
1670 header,
1671 arg: data.fetch()?,
1672 name: data.fetch_str()?.as_ref(),
1673 }),
1674 fuse_opcode::FUSE_UNLINK => Operation::Unlink(Unlink {
1675 header,
1676 name: data.fetch_str()?.as_ref(),
1677 }),
1678 fuse_opcode::FUSE_RMDIR => Operation::RmDir(RmDir {
1679 header,
1680 name: data.fetch_str()?.as_ref(),
1681 }),
1682 fuse_opcode::FUSE_RENAME => Operation::Rename(Rename {
1683 header,
1684 arg: data.fetch()?,
1685 name: data.fetch_str()?.as_ref(),
1686 newname: data.fetch_str()?.as_ref(),
1687 }),
1688 fuse_opcode::FUSE_LINK => Operation::Link(Link {
1689 header,
1690 arg: data.fetch()?,
1691 name: data.fetch_str()?.as_ref(),
1692 }),
1693 fuse_opcode::FUSE_OPEN => Operation::Open(Open {
1694 header,
1695 arg: data.fetch()?,
1696 }),
1697 fuse_opcode::FUSE_READ => Operation::Read(Read {
1698 header,
1699 arg: data.fetch()?,
1700 }),
1701 fuse_opcode::FUSE_WRITE => Operation::Write({
1702 let out = Write {
1703 header,
1704 arg: data.fetch()?,
1705 data: data.fetch_all(),
1706 };
1707 assert!(out.data().len() == out.arg.size as usize);
1708 out
1709 }),
1710 fuse_opcode::FUSE_STATFS => Operation::StatFs(StatFs { header }),
1711 fuse_opcode::FUSE_RELEASE => Operation::Release(Release {
1712 header,
1713 arg: data.fetch()?,
1714 }),
1715 fuse_opcode::FUSE_FSYNC => Operation::FSync(FSync {
1716 header,
1717 arg: data.fetch()?,
1718 }),
1719 fuse_opcode::FUSE_SETXATTR => Operation::SetXAttr({
1720 let out = SetXAttr {
1721 header,
1722 arg: data.fetch()?,
1723 name: data.fetch_str()?,
1724 value: data.fetch_all(),
1725 };
1726 assert!(out.value.len() == out.arg.size as usize);
1727 out
1728 }),
1729 fuse_opcode::FUSE_GETXATTR => Operation::GetXAttr(GetXAttr {
1730 header,
1731 arg: data.fetch()?,
1732 name: data.fetch_str()?,
1733 }),
1734 fuse_opcode::FUSE_LISTXATTR => Operation::ListXAttr(ListXAttr {
1735 header,
1736 arg: data.fetch()?,
1737 }),
1738 fuse_opcode::FUSE_REMOVEXATTR => Operation::RemoveXAttr(RemoveXAttr {
1739 header,
1740 name: data.fetch_str()?,
1741 }),
1742 fuse_opcode::FUSE_FLUSH => Operation::Flush(Flush {
1743 header,
1744 arg: data.fetch()?,
1745 }),
1746 fuse_opcode::FUSE_INIT => Operation::Init(Init {
1747 header,
1748 arg: {
1749 let mut arg = fuse_init_in::new_zeroed();
1753 let data = data.fetch_all();
1754 if data.len() < offset_of!(fuse_init_in, flags2) {
1756 return None;
1757 }
1758 let prefix = cmp::min(data.len(), arg.as_bytes().len());
1759 arg.as_mut_bytes()[..prefix].copy_from_slice(&data[..prefix]);
1760 Box::new(arg)
1761 },
1762 }),
1763 fuse_opcode::FUSE_OPENDIR => Operation::OpenDir(OpenDir {
1764 header,
1765 arg: data.fetch()?,
1766 }),
1767 fuse_opcode::FUSE_READDIR => Operation::ReadDir(ReadDir {
1768 header,
1769 arg: data.fetch()?,
1770 }),
1771 fuse_opcode::FUSE_RELEASEDIR => Operation::ReleaseDir(ReleaseDir {
1772 header,
1773 arg: data.fetch()?,
1774 }),
1775 fuse_opcode::FUSE_FSYNCDIR => Operation::FSyncDir(FSyncDir {
1776 header,
1777 arg: data.fetch()?,
1778 }),
1779 fuse_opcode::FUSE_GETLK => Operation::GetLk(GetLk {
1780 header,
1781 arg: data.fetch()?,
1782 }),
1783 fuse_opcode::FUSE_SETLK => Operation::SetLk(SetLk {
1784 header,
1785 arg: data.fetch()?,
1786 wait: false,
1787 }),
1788 fuse_opcode::FUSE_SETLKW => Operation::SetLk(SetLk {
1789 header,
1790 arg: data.fetch()?,
1791 wait: true,
1792 }),
1793 fuse_opcode::FUSE_ACCESS => Operation::Access(Access {
1794 header,
1795 arg: data.fetch()?,
1796 }),
1797 fuse_opcode::FUSE_CREATE => Operation::Create(Create {
1798 header,
1799 arg: data.fetch()?,
1800 name: data.fetch_str()?.as_ref(),
1801 }),
1802 fuse_opcode::FUSE_INTERRUPT => Operation::Interrupt(Interrupt {
1803 header,
1804 arg: data.fetch()?,
1805 }),
1806 fuse_opcode::FUSE_BMAP => Operation::BMap(BMap {
1807 header,
1808 arg: data.fetch()?,
1809 }),
1810 fuse_opcode::FUSE_DESTROY => Operation::Destroy(Destroy { header }),
1811 fuse_opcode::FUSE_IOCTL => Operation::IoCtl(IoCtl {
1812 header,
1813 arg: data.fetch()?,
1814 data: data.fetch_all(),
1815 }),
1816 fuse_opcode::FUSE_POLL => Operation::Poll(Poll {
1817 header,
1818 arg: data.fetch()?,
1819 }),
1820 fuse_opcode::FUSE_NOTIFY_REPLY => Operation::NotifyReply(NotifyReply {
1821 header,
1822 arg: data.fetch_all(),
1823 }),
1824 fuse_opcode::FUSE_BATCH_FORGET => {
1825 let arg = data.fetch()?;
1826 Operation::BatchForget(BatchForget {
1827 header,
1828 arg,
1829 nodes: data.fetch_slice(arg.count as usize)?,
1830 })
1831 }
1832 fuse_opcode::FUSE_FALLOCATE => Operation::FAllocate(FAllocate {
1833 header,
1834 arg: data.fetch()?,
1835 }),
1836 fuse_opcode::FUSE_READDIRPLUS => Operation::ReadDirPlus(ReadDirPlus {
1837 header,
1838 arg: data.fetch()?,
1839 }),
1840 fuse_opcode::FUSE_RENAME2 => Operation::Rename2(Rename2 {
1841 header,
1842 arg: data.fetch()?,
1843 name: data.fetch_str()?.as_ref(),
1844 newname: data.fetch_str()?.as_ref(),
1845 old_parent: INodeNo(header.nodeid),
1846 }),
1847 fuse_opcode::FUSE_LSEEK => Operation::Lseek(Lseek {
1848 header,
1849 arg: data.fetch()?,
1850 }),
1851 fuse_opcode::FUSE_COPY_FILE_RANGE => Operation::CopyFileRange(CopyFileRange {
1852 header,
1853 arg: data.fetch()?,
1854 }),
1855
1856 #[cfg(target_os = "macos")]
1857 fuse_opcode::FUSE_SETVOLNAME => Operation::SetVolName(SetVolName {
1858 header,
1859 name: data.fetch_str()?,
1860 }),
1861 #[cfg(target_os = "macos")]
1862 fuse_opcode::FUSE_GETXTIMES => Operation::GetXTimes(GetXTimes { header }),
1863 #[cfg(target_os = "macos")]
1864 fuse_opcode::FUSE_EXCHANGE => Operation::Exchange(Exchange {
1865 header,
1866 arg: data.fetch()?,
1867 oldname: data.fetch_str()?.as_ref(),
1868 newname: data.fetch_str()?.as_ref(),
1869 }),
1870
1871 fuse_opcode::CUSE_INIT => Operation::CuseInit(CuseInit {
1872 header,
1873 arg: data.fetch()?,
1874 }),
1875 })
1876 }
1877}
1878use op::*;
1879
1880#[derive(Debug)]
1883#[allow(missing_docs)]
1884pub(crate) enum Operation<'a> {
1885 Lookup(Lookup<'a>),
1886 Forget(Forget<'a>),
1887 GetAttr(GetAttr<'a>),
1888 SetAttr(SetAttr<'a>),
1889 ReadLink(#[expect(dead_code)] ReadLink<'a>),
1890 SymLink(SymLink<'a>),
1891 MkNod(MkNod<'a>),
1892 MkDir(MkDir<'a>),
1893 Unlink(Unlink<'a>),
1894 RmDir(RmDir<'a>),
1895 Rename(Rename<'a>),
1896 Link(Link<'a>),
1897 Open(Open<'a>),
1898 Read(Read<'a>),
1899 Write(Write<'a>),
1900 StatFs(#[expect(dead_code)] StatFs<'a>),
1901 Release(Release<'a>),
1902 FSync(FSync<'a>),
1903 SetXAttr(SetXAttr<'a>),
1904 GetXAttr(GetXAttr<'a>),
1905 ListXAttr(ListXAttr<'a>),
1906 RemoveXAttr(RemoveXAttr<'a>),
1907 Flush(Flush<'a>),
1908 Init(Init<'a>),
1909 OpenDir(OpenDir<'a>),
1910 ReadDir(ReadDir<'a>),
1911 ReleaseDir(ReleaseDir<'a>),
1912 FSyncDir(FSyncDir<'a>),
1913 GetLk(GetLk<'a>),
1914 SetLk(SetLk<'a>),
1915 Access(Access<'a>),
1916 Create(Create<'a>),
1917 Interrupt(Interrupt<'a>),
1918 BMap(BMap<'a>),
1919 Destroy(Destroy<'a>),
1920 IoCtl(IoCtl<'a>),
1921 Poll(Poll<'a>),
1922 NotifyReply(#[expect(dead_code)] NotifyReply<'a>),
1923 BatchForget(BatchForget<'a>),
1924 FAllocate(FAllocate<'a>),
1925 ReadDirPlus(ReadDirPlus<'a>),
1926 Rename2(Rename2<'a>),
1927 Lseek(Lseek<'a>),
1928 CopyFileRange(CopyFileRange<'a>),
1929
1930 #[cfg(target_os = "macos")]
1931 SetVolName(SetVolName<'a>),
1932 #[cfg(target_os = "macos")]
1933 GetXTimes(GetXTimes<'a>),
1934 #[cfg(target_os = "macos")]
1935 Exchange(Exchange<'a>),
1936
1937 #[allow(dead_code)]
1938 CuseInit(CuseInit<'a>),
1939}
1940
1941impl fmt::Display for Operation<'_> {
1942 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1943 match self {
1944 Operation::Lookup(x) => write!(f, "LOOKUP name {:?}", x.name()),
1945 Operation::Forget(x) => write!(f, "FORGET nlookup {}", x.nlookup()),
1946 Operation::GetAttr(_) => write!(f, "GETATTR"),
1947 Operation::SetAttr(x) => x.fmt(f),
1948 Operation::ReadLink(_) => write!(f, "READLINK"),
1949 Operation::SymLink(x) => {
1950 write!(
1951 f,
1952 "SYMLINK target {:?}, link_name {:?}",
1953 x.target(),
1954 x.link_name()
1955 )
1956 }
1957 Operation::MkNod(x) => write!(
1958 f,
1959 "MKNOD name {:?}, mode {:#05o}, rdev {}",
1960 x.name(),
1961 x.mode(),
1962 x.rdev()
1963 ),
1964 Operation::MkDir(x) => write!(f, "MKDIR name {:?}, mode {:#05o}", x.name(), x.mode()),
1965 Operation::Unlink(x) => write!(f, "UNLINK name {:?}", x.name()),
1966 Operation::RmDir(x) => write!(f, "RMDIR name {:?}", x.name),
1967 Operation::Rename(x) => write!(f, "RENAME src {:?}, dest {:?}", x.src(), x.dest()),
1968 Operation::Link(x) => write!(f, "LINK ino {:?}, dest {:?}", x.inode_no(), x.dest()),
1969 Operation::Open(x) => write!(f, "OPEN flags {:#x}", x.flags()),
1970 Operation::Read(x) => write!(
1971 f,
1972 "READ fh {:?}, offset {:?}, size {}",
1973 x.file_handle(),
1974 x.offset(),
1975 x.size()
1976 ),
1977 Operation::Write(x) => write!(
1978 f,
1979 "WRITE fh {:?}, offset {:?}, size {}, write flags {:#x}",
1980 x.file_handle(),
1981 x.offset(),
1982 x.data().len(),
1983 x.write_flags()
1984 ),
1985 Operation::StatFs(_) => write!(f, "STATFS"),
1986 Operation::Release(x) => write!(
1987 f,
1988 "RELEASE fh {:?}, flags {:#x}, flush {}, lock owner {:?}",
1989 x.file_handle(),
1990 x.flags(),
1991 x.flush(),
1992 x.lock_owner()
1993 ),
1994 Operation::FSync(x) => write!(
1995 f,
1996 "FSYNC fh {:?}, fsync fdatasync {}",
1997 x.file_handle(),
1998 x.fdatasync()
1999 ),
2000 Operation::SetXAttr(x) => write!(
2001 f,
2002 "SETXATTR name {:?}, size {}, flags {:#x}",
2003 x.name(),
2004 x.value().len(),
2005 x.flags()
2006 ),
2007 Operation::GetXAttr(x) => {
2008 write!(f, "GETXATTR name {:?}, size {:?}", x.name(), x.size())
2009 }
2010 Operation::ListXAttr(x) => write!(f, "LISTXATTR size {}", x.size()),
2011 Operation::RemoveXAttr(x) => write!(f, "REMOVEXATTR name {:?}", x.name()),
2012 Operation::Flush(x) => write!(
2013 f,
2014 "FLUSH fh {:?}, lock owner {:?}",
2015 x.file_handle(),
2016 x.lock_owner()
2017 ),
2018 Operation::Init(x) => write!(
2019 f,
2020 "INIT kernel ABI {}, capabilities {:#x}, max readahead {}",
2021 x.version(),
2022 x.capabilities(),
2023 x.max_readahead()
2024 ),
2025 Operation::OpenDir(x) => write!(f, "OPENDIR flags {:#x}", x.flags()),
2026 Operation::ReadDir(x) => write!(
2027 f,
2028 "READDIR fh {:?}, offset {}, size {}",
2029 x.file_handle(),
2030 x.offset(),
2031 x.size()
2032 ),
2033 Operation::ReleaseDir(x) => write!(
2034 f,
2035 "RELEASEDIR fh {:?}, flags {:#x}, flush {}, lock owner {:?}",
2036 x.file_handle(),
2037 x.flags(),
2038 x.flush(),
2039 x.lock_owner()
2040 ),
2041 Operation::FSyncDir(x) => write!(
2042 f,
2043 "FSYNCDIR fh {:?}, fsync fdatasync: {}",
2044 x.file_handle(),
2045 x.fdatasync()
2046 ),
2047 Operation::GetLk(x) => write!(
2048 f,
2049 "GETLK fh {:?}, lock owner {:?}",
2050 x.file_handle(),
2051 x.lock_owner()
2052 ),
2053 Operation::SetLk(x) => write!(
2054 f,
2055 "{} fh {:?}, lock owner {:?}",
2056 if x.sleep() { "SETLKW" } else { "SETLK" },
2057 x.file_handle(),
2058 x.lock_owner()
2059 ),
2060 Operation::Access(x) => write!(f, "ACCESS mask {:#05o}", x.mask()),
2061 Operation::Create(x) => write!(
2062 f,
2063 "CREATE name {:?}, mode {:#05o}, flags {:#x}",
2064 x.name(),
2065 x.mode(),
2066 x.flags()
2067 ),
2068 Operation::Interrupt(x) => write!(f, "INTERRUPT unique {:?}", x.unique()),
2069 Operation::BMap(x) => write!(f, "BMAP blocksize {}, ids {}", x.block_size(), x.block()),
2070 Operation::Destroy(_) => write!(f, "DESTROY"),
2071 Operation::IoCtl(x) => write!(
2072 f,
2073 "IOCTL fh {:?}, cmd {}, data size {}, flags {:#x}",
2074 x.file_handle(),
2075 x.command(),
2076 x.in_data().len(),
2077 x.flags()
2078 ),
2079 Operation::Poll(x) => write!(f, "POLL fh {:?}", x.file_handle()),
2080 Operation::NotifyReply(_) => write!(f, "NOTIFYREPLY"),
2081 Operation::BatchForget(x) => write!(f, "BATCHFORGET nodes {:?}", x.nodes()),
2082 Operation::FAllocate(_) => write!(f, "FALLOCATE"),
2083 Operation::ReadDirPlus(x) => write!(
2084 f,
2085 "READDIRPLUS fh {:?}, offset {}, size {}",
2086 x.file_handle(),
2087 x.offset(),
2088 x.size()
2089 ),
2090 Operation::Rename2(x) => write!(f, "RENAME2 from {:?}, to {:?}", x.from(), x.to()),
2091 Operation::Lseek(x) => write!(
2092 f,
2093 "LSEEK fh {:?}, offset {}, whence {}",
2094 x.file_handle(),
2095 x.offset(),
2096 x.whence()
2097 ),
2098 Operation::CopyFileRange(x) => write!(
2099 f,
2100 "COPY_FILE_RANGE src {:?}, dest {:?}, len {}",
2101 x.src(),
2102 x.dest(),
2103 x.len()
2104 ),
2105
2106 #[cfg(target_os = "macos")]
2107 Operation::SetVolName(x) => write!(f, "SETVOLNAME name {:?}", x.name()),
2108 #[cfg(target_os = "macos")]
2109 Operation::GetXTimes(_) => write!(f, "GETXTIMES"),
2110 #[cfg(target_os = "macos")]
2111 Operation::Exchange(x) => write!(
2112 f,
2113 "EXCHANGE from {:?}, to {:?}, options {:#x}",
2114 x.from(),
2115 x.to(),
2116 x.options()
2117 ),
2118
2119 Operation::CuseInit(_) => write!(f, "CUSE_INIT"),
2120 }
2121 }
2122}
2123
2124#[derive(Debug)]
2126pub(crate) struct AnyRequest<'a> {
2127 header: &'a fuse_in_header,
2128 data: &'a [u8],
2129}
2130
2131impl<'a> AnyRequest<'a> {
2132 pub(crate) fn unique(&self) -> RequestId {
2138 RequestId(self.header.unique)
2139 }
2140
2141 pub(crate) fn nodeid(&self) -> INodeNo {
2143 INodeNo(self.header.nodeid)
2144 }
2145
2146 pub(crate) fn uid(&self) -> Uid {
2148 Uid::from_raw(self.header.uid)
2149 }
2150
2151 #[cfg_attr(not(test), expect(dead_code))]
2153 fn gid(&self) -> Gid {
2154 Gid::from_raw(self.header.gid)
2155 }
2156
2157 #[cfg_attr(not(test), expect(dead_code))]
2159 fn pid(&self) -> Pid {
2160 Pid::from_raw(self.header.pid as i32)
2161 }
2162
2163 pub(crate) fn operation(&self) -> Result<Operation<'a>, RequestError> {
2164 let opcode = fuse_opcode::try_from(self.header.opcode).map_err(
2166 |e: num_enum::TryFromPrimitiveError<_>| RequestError::UnknownOperation(e.number),
2167 )?;
2168 op::parse(self.header, &opcode, self.data).ok_or(RequestError::InsufficientData)
2170 }
2171
2172 pub(crate) fn header(&self) -> &fuse_in_header {
2173 self.header
2174 }
2175}
2176
2177impl fmt::Display for AnyRequest<'_> {
2178 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2179 if let Ok(op) = self.operation() {
2180 write!(
2181 f,
2182 "FUSE({:3}) ino {:#018x} {}",
2183 self.header.unique, self.header.nodeid, op
2184 )
2185 } else {
2186 write!(
2187 f,
2188 "FUSE({:3}) ino {:#018x}",
2189 self.header.unique, self.header.nodeid
2190 )
2191 }
2192 }
2193}
2194
2195impl<'a> TryFrom<&'a [u8]> for AnyRequest<'a> {
2196 type Error = RequestError;
2197
2198 fn try_from(data: &'a [u8]) -> Result<Self, Self::Error> {
2199 let data_len = data.len();
2202 let mut arg_iter = ArgumentIterator::new(data);
2203 let header: &fuse_in_header = arg_iter
2205 .fetch()
2206 .ok_or_else(|| RequestError::ShortReadHeader(arg_iter.len()))?;
2207 if data_len < header.len as usize {
2209 return Err(RequestError::ShortRead(data_len, header.len as usize));
2210 }
2211 Ok(Self {
2212 header,
2213 data: &data[size_of::<fuse_in_header>()..header.len as usize],
2214 })
2215 }
2216}
2217
2218#[cfg(test)]
2219mod tests {
2220 use std::ffi::OsStr;
2221
2222 use crate::ll::request::*;
2223 use crate::ll::test::AlignedData;
2224
2225 #[cfg(target_endian = "big")]
2226 const INIT_REQUEST: AlignedData<[u8; 104]> = AlignedData([
2227 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x1a, 0xde, 0xad, 0xbe, 0xef, 0xba, 0xad, 0xd0, 0x0d, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0xc0, 0x01, 0xd0, 0x0d, 0xc0, 0x01, 0xca, 0xfe, 0xc0, 0xde, 0xba, 0x5e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2238 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2239 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2240 ]);
2241
2242 #[cfg(target_endian = "little")]
2243 const INIT_REQUEST: AlignedData<[u8; 104]> = AlignedData([
2244 0x68, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x0d, 0xf0, 0xad, 0xba, 0xef, 0xbe, 0xad, 0xde, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x0d, 0xd0, 0x01, 0xc0, 0xfe, 0xca, 0x01, 0xc0, 0x5e, 0xba, 0xde, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2255 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2256 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2257 ]);
2258
2259 #[cfg(target_endian = "big")]
2260 const MKNOD_REQUEST: AlignedData<[u8; 56]> = [
2261 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x08, 0xde, 0xad, 0xbe, 0xef, 0xba, 0xad, 0xd0, 0x0d, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0xc0, 0x01, 0xd0, 0x0d, 0xc0, 0x01, 0xca, 0xfe, 0xc0, 0xde, 0xba, 0x5e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xa4, 0x00, 0x00, 0x00, 0x00, 0x66, 0x6f, 0x6f, 0x2e, 0x74, 0x78, 0x74, 0x00, ];
2269
2270 #[cfg(target_endian = "little")]
2271 const MKNOD_REQUEST: AlignedData<[u8; 64]> = AlignedData([
2272 0x40, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0d, 0xf0, 0xad, 0xba, 0xef, 0xbe, 0xad, 0xde, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x0d, 0xd0, 0x01, 0xc0, 0xfe, 0xca, 0x01, 0xc0, 0x5e, 0xba, 0xde, 0xc0, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xed, 0x01, 0x00, 0x00, 0xe7, 0x03, 0x00, 0x00, 0x66, 0x6f, 0x6f, 0x2e, 0x74, 0x78, 0x74, 0x00, ]);
2281
2282 #[test]
2283 fn short_read_header() {
2284 match AnyRequest::try_from(&INIT_REQUEST[..20]) {
2285 Err(RequestError::ShortReadHeader(20)) => (),
2286 _ => panic!("Unexpected request parsing result"),
2287 }
2288 }
2289
2290 #[test]
2291 fn short_read() {
2292 match AnyRequest::try_from(&INIT_REQUEST[..48]) {
2293 Err(RequestError::ShortRead(48, 104)) => (),
2294 _ => panic!("Unexpected request parsing result"),
2295 }
2296 }
2297
2298 #[test]
2299 fn init() {
2300 let req = AnyRequest::try_from(&INIT_REQUEST[..]).unwrap();
2301 assert_eq!(req.header.len, 104);
2302 assert_eq!(req.header.opcode, 26);
2303 assert_eq!(req.unique(), RequestId(0xdead_beef_baad_f00d));
2304 assert_eq!(req.nodeid(), INodeNo(0x1122_3344_5566_7788));
2305 assert_eq!(req.uid(), Uid::from_raw(0xc001_d00d));
2306 assert_eq!(req.gid(), Gid::from_raw(0xc001_cafe));
2307 assert_eq!(req.pid(), Pid::from_raw(0xc0de_ba5e_u32 as i32));
2308 match req.operation().unwrap() {
2309 Operation::Init(x) => {
2310 assert_eq!(x.version(), Version(7, 8));
2311 assert_eq!(x.max_readahead(), 4096);
2312 }
2313 _ => panic!("Unexpected request operation"),
2314 }
2315 }
2316
2317 #[test]
2318 fn mknod() {
2319 let req = AnyRequest::try_from(&MKNOD_REQUEST[..]).unwrap();
2320 assert_eq!(req.header.len, 64);
2321 assert_eq!(req.header.opcode, 8);
2322 assert_eq!(req.unique(), RequestId(0xdead_beef_baad_f00d));
2323 assert_eq!(req.nodeid(), INodeNo(0x1122_3344_5566_7788));
2324 assert_eq!(req.uid(), Uid::from_raw(0xc001_d00d));
2325 assert_eq!(req.gid(), Gid::from_raw(0xc001_cafe));
2326 assert_eq!(req.pid(), Pid::from_raw(0xc0de_ba5e_u32 as i32));
2327 match req.operation().unwrap() {
2328 Operation::MkNod(x) => {
2329 assert_eq!(x.mode(), 0o644);
2330 assert_eq!(x.umask(), 0o755);
2331 assert_eq!(x.name(), OsStr::new("foo.txt"));
2332 }
2333 _ => panic!("Unexpected request operation"),
2334 }
2335 }
2336}