Skip to main content

arcbox_fs/
fuse.rs

1//! FUSE protocol implementation.
2//!
3//! This module implements the FUSE (Filesystem in Userspace) protocol structures
4//! for communication between the kernel and userspace filesystem implementation.
5//!
6//! Reference: <https://github.com/libfuse/libfuse/blob/master/include/fuse_kernel.h>
7
8// Allow casts for FUSE protocol binary compatibility
9#![allow(
10    clippy::cast_sign_loss,
11    clippy::cast_possible_wrap,
12    clippy::cast_possible_truncation
13)]
14
15use std::mem::size_of;
16
17// ============================================================================
18// Constants
19// ============================================================================
20
21/// FUSE kernel protocol major version.
22pub const FUSE_KERNEL_VERSION: u32 = 7;
23
24/// FUSE kernel protocol minor version.
25pub const FUSE_KERNEL_MINOR_VERSION: u32 = 38;
26
27/// Root inode number.
28pub const FUSE_ROOT_ID: u64 = 1;
29
30// FUSE init flags
31pub const FUSE_ASYNC_READ: u32 = 1 << 0;
32pub const FUSE_POSIX_LOCKS: u32 = 1 << 1;
33pub const FUSE_FILE_OPS: u32 = 1 << 2;
34pub const FUSE_ATOMIC_O_TRUNC: u32 = 1 << 3;
35pub const FUSE_EXPORT_SUPPORT: u32 = 1 << 4;
36pub const FUSE_BIG_WRITES: u32 = 1 << 5;
37pub const FUSE_DONT_MASK: u32 = 1 << 6;
38pub const FUSE_SPLICE_WRITE: u32 = 1 << 7;
39pub const FUSE_SPLICE_MOVE: u32 = 1 << 8;
40pub const FUSE_SPLICE_READ: u32 = 1 << 9;
41pub const FUSE_FLOCK_LOCKS: u32 = 1 << 10;
42pub const FUSE_HAS_IOCTL_DIR: u32 = 1 << 11;
43pub const FUSE_AUTO_INVAL_DATA: u32 = 1 << 12;
44pub const FUSE_DO_READDIRPLUS: u32 = 1 << 13;
45pub const FUSE_READDIRPLUS_AUTO: u32 = 1 << 14;
46pub const FUSE_ASYNC_DIO: u32 = 1 << 15;
47pub const FUSE_WRITEBACK_CACHE: u32 = 1 << 16;
48pub const FUSE_NO_OPEN_SUPPORT: u32 = 1 << 17;
49pub const FUSE_PARALLEL_DIROPS: u32 = 1 << 18;
50pub const FUSE_HANDLE_KILLPRIV: u32 = 1 << 19;
51pub const FUSE_POSIX_ACL: u32 = 1 << 20;
52pub const FUSE_ABORT_ERROR: u32 = 1 << 21;
53pub const FUSE_MAX_PAGES: u32 = 1 << 22;
54pub const FUSE_CACHE_SYMLINKS: u32 = 1 << 23;
55pub const FUSE_NO_OPENDIR_SUPPORT: u32 = 1 << 24;
56pub const FUSE_EXPLICIT_INVAL_DATA: u32 = 1 << 25;
57/// DAX support — direct host page mapping into guest address space.
58pub const FUSE_MAP_ALIGNMENT: u32 = 1 << 26;
59
60/// FUSE DAX protocol: sentinel value indicating no open file handle.
61///
62/// When the guest issues `FUSE_SETUPMAPPING` during a path that does not
63/// hold an open file descriptor (e.g. `execve` demand-loading a mapped
64/// executable), it sets `fh` to this value. The FUSE server must then open
65/// the inode internally (read-only), perform the mapping, and close the
66/// temporary fd — `mmap(2)` keeps the mapping alive regardless.
67pub const FUSE_NO_FH: u64 = u64::MAX;
68
69/// FUSE_SETUPMAPPING flags.
70pub const FUSE_SETUPMAPPING_FLAG_WRITE: u64 = 1 << 0;
71pub const FUSE_SETUPMAPPING_FLAG_READ: u64 = 1 << 1;
72
73// Setattr valid flags
74pub const FATTR_MODE: u32 = 1 << 0;
75pub const FATTR_UID: u32 = 1 << 1;
76pub const FATTR_GID: u32 = 1 << 2;
77pub const FATTR_SIZE: u32 = 1 << 3;
78pub const FATTR_ATIME: u32 = 1 << 4;
79pub const FATTR_MTIME: u32 = 1 << 5;
80pub const FATTR_FH: u32 = 1 << 6;
81pub const FATTR_ATIME_NOW: u32 = 1 << 7;
82pub const FATTR_MTIME_NOW: u32 = 1 << 8;
83pub const FATTR_LOCKOWNER: u32 = 1 << 9;
84pub const FATTR_CTIME: u32 = 1 << 10;
85
86// Open flags
87pub const FOPEN_DIRECT_IO: u32 = 1 << 0;
88pub const FOPEN_KEEP_CACHE: u32 = 1 << 1;
89pub const FOPEN_NONSEEKABLE: u32 = 1 << 2;
90pub const FOPEN_CACHE_DIR: u32 = 1 << 3;
91pub const FOPEN_STREAM: u32 = 1 << 4;
92
93// ============================================================================
94// Opcodes
95// ============================================================================
96
97/// FUSE operation codes.
98#[derive(Debug, Clone, Copy, PartialEq, Eq)]
99#[repr(u32)]
100pub enum FuseOpcode {
101    Lookup = 1,
102    Forget = 2,
103    Getattr = 3,
104    Setattr = 4,
105    Readlink = 5,
106    Symlink = 6,
107    Mknod = 8,
108    Mkdir = 9,
109    Unlink = 10,
110    Rmdir = 11,
111    Rename = 12,
112    Link = 13,
113    Open = 14,
114    Read = 15,
115    Write = 16,
116    Statfs = 17,
117    Release = 18,
118    Fsync = 20,
119    Setxattr = 21,
120    Getxattr = 22,
121    Listxattr = 23,
122    Removexattr = 24,
123    Flush = 25,
124    Init = 26,
125    Opendir = 27,
126    Readdir = 28,
127    Releasedir = 29,
128    Fsyncdir = 30,
129    Getlk = 31,
130    Setlk = 32,
131    Setlkw = 33,
132    Access = 34,
133    Create = 35,
134    Interrupt = 36,
135    Bmap = 37,
136    Destroy = 38,
137    Ioctl = 39,
138    Poll = 40,
139    NotifyReply = 41,
140    BatchForget = 42,
141    Fallocate = 43,
142    Readdirplus = 44,
143    Rename2 = 45,
144    Lseek = 46,
145    CopyFileRange = 47,
146    SetupMapping = 48,
147    RemoveMapping = 49,
148}
149
150/// FUSE request header.
151#[derive(Debug, Clone, Copy)]
152#[repr(C)]
153pub struct FuseInHeader {
154    /// Total message length.
155    pub len: u32,
156    /// Operation code.
157    pub opcode: u32,
158    /// Unique request ID.
159    pub unique: u64,
160    /// Node ID.
161    pub nodeid: u64,
162    /// User ID.
163    pub uid: u32,
164    /// Group ID.
165    pub gid: u32,
166    /// Process ID.
167    pub pid: u32,
168    /// Padding.
169    pub padding: u32,
170}
171
172/// FUSE response header.
173#[derive(Debug, Clone, Copy)]
174#[repr(C)]
175pub struct FuseOutHeader {
176    /// Total message length.
177    pub len: u32,
178    /// Error code (0 on success, negative errno on error).
179    pub error: i32,
180    /// Unique request ID (must match request).
181    pub unique: u64,
182}
183
184/// File attributes.
185#[derive(Debug, Clone, Copy, Default)]
186#[repr(C)]
187pub struct FuseAttr {
188    pub ino: u64,
189    pub size: u64,
190    pub blocks: u64,
191    pub atime: u64,
192    pub mtime: u64,
193    pub ctime: u64,
194    pub atimensec: u32,
195    pub mtimensec: u32,
196    pub ctimensec: u32,
197    pub mode: u32,
198    pub nlink: u32,
199    pub uid: u32,
200    pub gid: u32,
201    pub rdev: u32,
202    pub blksize: u32,
203    pub padding: u32,
204}
205
206/// Filesystem statistics.
207#[derive(Debug, Clone, Copy, Default)]
208#[repr(C)]
209pub struct StatFs {
210    /// Total data blocks in filesystem.
211    pub blocks: u64,
212    /// Free blocks in filesystem.
213    pub bfree: u64,
214    /// Free blocks available to unprivileged user.
215    pub bavail: u64,
216    /// Total file nodes in filesystem.
217    pub files: u64,
218    /// Free file nodes in filesystem.
219    pub ffree: u64,
220    /// Optimal transfer block size.
221    pub bsize: u32,
222    /// Maximum length of filenames.
223    pub namelen: u32,
224    /// Fragment size.
225    pub frsize: u32,
226}
227
228// ============================================================================
229// Request Structures
230// ============================================================================
231
232/// FUSE_INIT request.
233#[derive(Debug, Clone, Copy)]
234#[repr(C)]
235pub struct FuseInitIn {
236    /// Major version supported by kernel.
237    pub major: u32,
238    /// Minor version supported by kernel.
239    pub minor: u32,
240    /// Maximum readahead size.
241    pub max_readahead: u32,
242    /// Flags.
243    pub flags: u32,
244}
245
246/// FUSE_GETATTR request.
247#[derive(Debug, Clone, Copy)]
248#[repr(C)]
249pub struct FuseGetattrIn {
250    /// Getattr flags.
251    pub getattr_flags: u32,
252    /// Padding.
253    pub dummy: u32,
254    /// File handle (if FUSE_GETATTR_FH is set).
255    pub fh: u64,
256}
257
258/// FUSE_SETATTR request.
259#[derive(Debug, Clone, Copy)]
260#[repr(C)]
261pub struct FuseSetattrIn {
262    /// Valid attribute mask.
263    pub valid: u32,
264    /// Padding.
265    pub padding: u32,
266    /// File handle.
267    pub fh: u64,
268    /// New size.
269    pub size: u64,
270    /// Lock owner.
271    pub lock_owner: u64,
272    /// New access time (seconds).
273    pub atime: u64,
274    /// New modification time (seconds).
275    pub mtime: u64,
276    /// New ctime (seconds).
277    pub ctime: u64,
278    /// Access time nanoseconds.
279    pub atimensec: u32,
280    /// Modification time nanoseconds.
281    pub mtimensec: u32,
282    /// Ctime nanoseconds.
283    pub ctimensec: u32,
284    /// New mode.
285    pub mode: u32,
286    /// Unused.
287    pub unused4: u32,
288    /// New UID.
289    pub uid: u32,
290    /// New GID.
291    pub gid: u32,
292    /// Unused.
293    pub unused5: u32,
294}
295
296/// FUSE_MKNOD request.
297#[derive(Debug, Clone, Copy)]
298#[repr(C)]
299pub struct FuseMknodIn {
300    /// File mode.
301    pub mode: u32,
302    /// Device number.
303    pub rdev: u32,
304    /// Umask.
305    pub umask: u32,
306    /// Padding.
307    pub padding: u32,
308}
309
310/// FUSE_MKDIR request.
311#[derive(Debug, Clone, Copy)]
312#[repr(C)]
313pub struct FuseMkdirIn {
314    /// Directory mode.
315    pub mode: u32,
316    /// Umask.
317    pub umask: u32,
318}
319
320/// FUSE_RENAME request.
321#[derive(Debug, Clone, Copy)]
322#[repr(C)]
323pub struct FuseRenameIn {
324    /// New parent directory inode.
325    pub newdir: u64,
326}
327
328/// FUSE_RENAME2 request.
329#[derive(Debug, Clone, Copy)]
330#[repr(C)]
331pub struct FuseRename2In {
332    /// New parent directory inode.
333    pub newdir: u64,
334    /// Rename flags.
335    pub flags: u32,
336    /// Padding.
337    pub padding: u32,
338}
339
340/// FUSE_LINK request.
341#[derive(Debug, Clone, Copy)]
342#[repr(C)]
343pub struct FuseLinkIn {
344    /// Old inode number.
345    pub oldnodeid: u64,
346}
347
348/// FUSE_OPEN request.
349#[derive(Debug, Clone, Copy)]
350#[repr(C)]
351pub struct FuseOpenIn {
352    /// Open flags.
353    pub flags: u32,
354    /// Unused.
355    pub unused: u32,
356}
357
358/// FUSE_CREATE request.
359#[derive(Debug, Clone, Copy)]
360#[repr(C)]
361pub struct FuseCreateIn {
362    /// Open flags.
363    pub flags: u32,
364    /// File mode.
365    pub mode: u32,
366    /// Umask.
367    pub umask: u32,
368    /// Padding.
369    pub padding: u32,
370}
371
372/// FUSE_READ request.
373#[derive(Debug, Clone, Copy)]
374#[repr(C)]
375pub struct FuseReadIn {
376    /// File handle.
377    pub fh: u64,
378    /// Read offset.
379    pub offset: u64,
380    /// Number of bytes to read.
381    pub size: u32,
382    /// Read flags.
383    pub read_flags: u32,
384    /// Lock owner.
385    pub lock_owner: u64,
386    /// Open flags.
387    pub flags: u32,
388    /// Padding.
389    pub padding: u32,
390}
391
392/// FUSE_WRITE request.
393#[derive(Debug, Clone, Copy)]
394#[repr(C)]
395pub struct FuseWriteIn {
396    /// File handle.
397    pub fh: u64,
398    /// Write offset.
399    pub offset: u64,
400    /// Number of bytes to write.
401    pub size: u32,
402    /// Write flags.
403    pub write_flags: u32,
404    /// Lock owner.
405    pub lock_owner: u64,
406    /// Open flags.
407    pub flags: u32,
408    /// Padding.
409    pub padding: u32,
410}
411
412/// FUSE_RELEASE request.
413#[derive(Debug, Clone, Copy)]
414#[repr(C)]
415pub struct FuseReleaseIn {
416    /// File handle.
417    pub fh: u64,
418    /// Open flags.
419    pub flags: u32,
420    /// Release flags.
421    pub release_flags: u32,
422    /// Lock owner.
423    pub lock_owner: u64,
424}
425
426/// FUSE_FLUSH request.
427#[derive(Debug, Clone, Copy)]
428#[repr(C)]
429pub struct FuseFlushIn {
430    /// File handle.
431    pub fh: u64,
432    /// Unused.
433    pub unused: u32,
434    /// Padding.
435    pub padding: u32,
436    /// Lock owner.
437    pub lock_owner: u64,
438}
439
440/// FUSE_FSYNC request.
441#[derive(Debug, Clone, Copy)]
442#[repr(C)]
443pub struct FuseFsyncIn {
444    /// File handle.
445    pub fh: u64,
446    /// Fsync flags (1 = datasync).
447    pub fsync_flags: u32,
448    /// Padding.
449    pub padding: u32,
450}
451
452/// FUSE_SETXATTR request.
453#[derive(Debug, Clone, Copy)]
454#[repr(C)]
455pub struct FuseSetxattrIn {
456    /// Attribute value size.
457    pub size: u32,
458    /// Setxattr flags.
459    pub flags: u32,
460}
461
462/// FUSE_GETXATTR request.
463#[derive(Debug, Clone, Copy)]
464#[repr(C)]
465pub struct FuseGetxattrIn {
466    /// Maximum size of attribute value.
467    pub size: u32,
468    /// Padding.
469    pub padding: u32,
470}
471
472/// FUSE_ACCESS request.
473#[derive(Debug, Clone, Copy)]
474#[repr(C)]
475pub struct FuseAccessIn {
476    /// Access mode mask.
477    pub mask: u32,
478    /// Padding.
479    pub padding: u32,
480}
481
482/// FUSE_LSEEK request.
483#[derive(Debug, Clone, Copy)]
484#[repr(C)]
485pub struct FuseLseekIn {
486    /// File handle.
487    pub fh: u64,
488    /// Seek offset.
489    pub offset: u64,
490    /// Seek whence (SEEK_SET, SEEK_CUR, SEEK_END, etc.).
491    pub whence: u32,
492    /// Padding.
493    pub padding: u32,
494}
495
496/// FUSE_FALLOCATE request.
497#[derive(Debug, Clone, Copy)]
498#[repr(C)]
499pub struct FuseFallocateIn {
500    /// File handle.
501    pub fh: u64,
502    /// Offset.
503    pub offset: u64,
504    /// Length.
505    pub length: u64,
506    /// Fallocate mode.
507    pub mode: u32,
508    /// Padding.
509    pub padding: u32,
510}
511
512/// FUSE_FORGET request.
513#[derive(Debug, Clone, Copy)]
514#[repr(C)]
515pub struct FuseForgetIn {
516    /// Number of lookups to forget.
517    pub nlookup: u64,
518}
519
520/// Single forget entry for batch forget.
521#[derive(Debug, Clone, Copy)]
522#[repr(C)]
523pub struct FuseForgetOne {
524    /// Inode number.
525    pub nodeid: u64,
526    /// Number of lookups to forget.
527    pub nlookup: u64,
528}
529
530/// FUSE_BATCH_FORGET request.
531#[derive(Debug, Clone, Copy)]
532#[repr(C)]
533pub struct FuseBatchForgetIn {
534    /// Number of entries.
535    pub count: u32,
536    /// Padding.
537    pub dummy: u32,
538}
539
540/// FUSE_SETUPMAPPING request — maps a file region into the DAX window.
541#[derive(Debug, Clone, Copy)]
542#[repr(C)]
543pub struct FuseSetupMappingIn {
544    /// File handle.
545    pub fh: u64,
546    /// File offset.
547    pub foffset: u64,
548    /// Mapping length.
549    pub len: u64,
550    /// Flags (FUSE_SETUPMAPPING_FLAG_WRITE, FUSE_SETUPMAPPING_FLAG_READ).
551    pub flags: u64,
552    /// Offset within the DAX window.
553    pub moffset: u64,
554}
555
556/// FUSE_REMOVEMAPPING request header.
557#[derive(Debug, Clone, Copy)]
558#[repr(C)]
559pub struct FuseRemoveMappingIn {
560    /// Number of mappings to remove.
561    pub count: u32,
562    pub dummy: u32,
563}
564
565/// FUSE_REMOVEMAPPING — one mapping entry to remove.
566#[derive(Debug, Clone, Copy)]
567#[repr(C)]
568pub struct FuseRemoveMappingOne {
569    /// Offset within the DAX window.
570    pub moffset: u64,
571    /// Length.
572    pub len: u64,
573}
574
575// ============================================================================
576// Response Structures
577// ============================================================================
578
579/// FUSE_INIT response.
580#[derive(Debug, Clone, Copy)]
581#[repr(C)]
582pub struct FuseInitOut {
583    /// Major version.
584    pub major: u32,
585    /// Minor version.
586    pub minor: u32,
587    /// Maximum readahead size.
588    pub max_readahead: u32,
589    /// Flags.
590    pub flags: u32,
591    /// Maximum background requests.
592    pub max_background: u16,
593    /// Congestion threshold.
594    pub congestion_threshold: u16,
595    /// Maximum write size.
596    pub max_write: u32,
597    /// Time granularity (nanoseconds).
598    pub time_gran: u32,
599    /// Maximum pages for a single request.
600    pub max_pages: u16,
601    /// Page-size alignment for DAX mappings, expressed as a power-of-two shift
602    /// (e.g. 12 = 4 KiB, 14 = 16 KiB).  Kernel reads this from offset 30 of
603    /// the init response body; writing to `unused[0]` (offset 32) was a silent
604    /// off-by-one that made Apple Silicon guests treat the host as 4 KiB.
605    pub map_alignment: u16,
606    /// Extended feature flags introduced in FUSE 7.36 (offset 32).  Reserved;
607    /// must be zero until ArcBox requires a feature gated behind this field.
608    pub flags2: u32,
609    /// Reserved tail per FUSE 7.36+ (unused[7] = 28 bytes, offsets 36..64).
610    pub unused: [u32; 7],
611}
612
613impl Default for FuseInitOut {
614    fn default() -> Self {
615        Self {
616            major: FUSE_KERNEL_VERSION,
617            minor: FUSE_KERNEL_MINOR_VERSION,
618            max_readahead: 128 * 1024,
619            flags: FUSE_ASYNC_READ | FUSE_BIG_WRITES | FUSE_WRITEBACK_CACHE,
620            max_background: 16,
621            congestion_threshold: 12,
622            max_write: 128 * 1024,
623            time_gran: 1,
624            max_pages: 32,
625            map_alignment: 0,
626            flags2: 0,
627            unused: [0; 7],
628        }
629    }
630}
631
632// Wire format sanity: the FUSE 7.36+ fuse_init_out must be exactly 64 bytes.
633// Changing any field width would silently corrupt the FUSE_INIT handshake.
634const _: () = assert!(std::mem::size_of::<FuseInitOut>() == 64);
635
636/// Entry response (for lookup, mkdir, mknod, symlink, link, create).
637#[derive(Debug, Clone, Copy, Default)]
638#[repr(C)]
639pub struct FuseEntryOut {
640    /// Inode number.
641    pub nodeid: u64,
642    /// Inode generation.
643    pub generation: u64,
644    /// Cache timeout for entry (seconds).
645    pub entry_valid: u64,
646    /// Cache timeout for attributes (seconds).
647    pub attr_valid: u64,
648    /// Entry timeout nanoseconds.
649    pub entry_valid_nsec: u32,
650    /// Attribute timeout nanoseconds.
651    pub attr_valid_nsec: u32,
652    /// File attributes.
653    pub attr: FuseAttr,
654}
655
656/// Attribute response.
657#[derive(Debug, Clone, Copy, Default)]
658#[repr(C)]
659pub struct FuseAttrOut {
660    /// Cache timeout for attributes (seconds).
661    pub attr_valid: u64,
662    /// Attribute timeout nanoseconds.
663    pub attr_valid_nsec: u32,
664    /// Padding.
665    pub dummy: u32,
666    /// File attributes.
667    pub attr: FuseAttr,
668}
669
670/// Open response.
671#[derive(Debug, Clone, Copy, Default)]
672#[repr(C)]
673pub struct FuseOpenOut {
674    /// File handle.
675    pub fh: u64,
676    /// Open flags.
677    pub open_flags: u32,
678    /// Padding.
679    pub padding: u32,
680}
681
682/// Write response.
683#[derive(Debug, Clone, Copy, Default)]
684#[repr(C)]
685pub struct FuseWriteOut {
686    /// Number of bytes written.
687    pub size: u32,
688    /// Padding.
689    pub padding: u32,
690}
691
692/// Getxattr response.
693#[derive(Debug, Clone, Copy, Default)]
694#[repr(C)]
695pub struct FuseGetxattrOut {
696    /// Attribute value size.
697    pub size: u32,
698    /// Padding.
699    pub padding: u32,
700}
701
702/// Statfs response.
703#[derive(Debug, Clone, Copy, Default)]
704#[repr(C)]
705pub struct FuseStatfsOut {
706    /// Filesystem statistics.
707    pub st: StatFs,
708}
709
710/// Lseek response.
711#[derive(Debug, Clone, Copy, Default)]
712#[repr(C)]
713pub struct FuseLseekOut {
714    /// New offset.
715    pub offset: u64,
716}
717
718/// Directory entry for readdir.
719#[derive(Debug, Clone, Copy)]
720#[repr(C)]
721pub struct FuseDirent {
722    /// Inode number.
723    pub ino: u64,
724    /// Offset to next entry.
725    pub off: u64,
726    /// Name length.
727    pub namelen: u32,
728    /// File type (DT_*).
729    pub typ: u32,
730    // Followed by name[namelen] (not null-terminated, but padded to 8-byte boundary)
731}
732
733impl FuseDirent {
734    /// Returns the total size of this entry including the name.
735    #[must_use]
736    pub const fn size(namelen: usize) -> usize {
737        // Header size + name length, rounded up to 8-byte boundary
738        let base = size_of::<Self>() + namelen;
739        (base + 7) & !7
740    }
741}
742
743// ============================================================================
744// Opcode Conversion
745// ============================================================================
746
747impl FuseOpcode {
748    /// Tries to convert a u32 to a `FuseOpcode`.
749    #[must_use]
750    pub fn from_u32(value: u32) -> Option<Self> {
751        match value {
752            1 => Some(Self::Lookup),
753            2 => Some(Self::Forget),
754            3 => Some(Self::Getattr),
755            4 => Some(Self::Setattr),
756            5 => Some(Self::Readlink),
757            6 => Some(Self::Symlink),
758            8 => Some(Self::Mknod),
759            9 => Some(Self::Mkdir),
760            10 => Some(Self::Unlink),
761            11 => Some(Self::Rmdir),
762            12 => Some(Self::Rename),
763            13 => Some(Self::Link),
764            14 => Some(Self::Open),
765            15 => Some(Self::Read),
766            16 => Some(Self::Write),
767            17 => Some(Self::Statfs),
768            18 => Some(Self::Release),
769            20 => Some(Self::Fsync),
770            21 => Some(Self::Setxattr),
771            22 => Some(Self::Getxattr),
772            23 => Some(Self::Listxattr),
773            24 => Some(Self::Removexattr),
774            25 => Some(Self::Flush),
775            26 => Some(Self::Init),
776            27 => Some(Self::Opendir),
777            28 => Some(Self::Readdir),
778            29 => Some(Self::Releasedir),
779            30 => Some(Self::Fsyncdir),
780            31 => Some(Self::Getlk),
781            32 => Some(Self::Setlk),
782            33 => Some(Self::Setlkw),
783            34 => Some(Self::Access),
784            35 => Some(Self::Create),
785            36 => Some(Self::Interrupt),
786            37 => Some(Self::Bmap),
787            38 => Some(Self::Destroy),
788            39 => Some(Self::Ioctl),
789            40 => Some(Self::Poll),
790            41 => Some(Self::NotifyReply),
791            42 => Some(Self::BatchForget),
792            43 => Some(Self::Fallocate),
793            44 => Some(Self::Readdirplus),
794            45 => Some(Self::Rename2),
795            46 => Some(Self::Lseek),
796            47 => Some(Self::CopyFileRange),
797            48 => Some(Self::SetupMapping),
798            49 => Some(Self::RemoveMapping),
799            _ => None,
800        }
801    }
802}
803
804// ============================================================================
805// Header Size Constants
806// ============================================================================
807
808impl FuseInHeader {
809    /// Size of the input header.
810    pub const SIZE: usize = size_of::<Self>();
811}
812
813impl FuseOutHeader {
814    /// Size of the output header.
815    pub const SIZE: usize = size_of::<Self>();
816
817    /// Creates a success response header.
818    #[must_use]
819    pub const fn success(unique: u64, len: u32) -> Self {
820        Self {
821            len,
822            error: 0,
823            unique,
824        }
825    }
826
827    /// Creates an error response header.
828    #[must_use]
829    pub const fn error(unique: u64, errno: i32) -> Self {
830        Self {
831            len: Self::SIZE as u32,
832            error: -errno,
833            unique,
834        }
835    }
836}