fuse_backend_rs/abi/
fuse_abi_linux.rs

1// Copyright 2019 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE-BSD-3-Clause file.
4
5//! Linux Fuse Application Binary Interfaces, Version 7.31.
6
7#![allow(missing_docs)]
8
9use std::fmt::{Debug, Formatter};
10use std::mem;
11
12use bitflags::bitflags;
13use vm_memory::ByteValued;
14
15pub use libc::{
16    blksize_t, dev_t, ino64_t, mode_t, nlink_t, off64_t, pread64, preadv64, pwrite64, pwritev64,
17    stat64, statvfs64,
18};
19
20/// Version number of this interface.
21pub const KERNEL_VERSION: u32 = 7;
22
23/// Minor version number of this interface.
24pub const KERNEL_MINOR_VERSION: u32 = 33;
25
26/// Init reply size is FUSE_COMPAT_INIT_OUT_SIZE
27pub const KERNEL_MINOR_VERSION_INIT_OUT_SIZE: u32 = 5;
28
29/// Init reply size is FUSE_COMPAT_22_INIT_OUT_SIZE
30pub const KERNEL_MINOR_VERSION_INIT_22_OUT_SIZE: u32 = 23;
31
32/// Lookup negative dentry using inode number 0
33pub const KERNEL_MINOR_VERSION_LOOKUP_NEGATIVE_ENTRY_ZERO: u32 = 4;
34
35/// The ID of the inode corresponding to the root directory of the file system.
36pub const ROOT_ID: u64 = 1;
37
38// Bitmasks for `fuse_setattr_in.valid`.
39const FATTR_MODE: u32 = 0x1;
40const FATTR_UID: u32 = 0x2;
41const FATTR_GID: u32 = 0x4;
42const FATTR_SIZE: u32 = 0x8;
43const FATTR_ATIME: u32 = 0x10;
44const FATTR_MTIME: u32 = 0x20;
45pub const FATTR_FH: u32 = 0x40;
46const FATTR_ATIME_NOW: u32 = 0x80;
47const FATTR_MTIME_NOW: u32 = 0x100;
48pub const FATTR_LOCKOWNER: u32 = 0x200;
49const FATTR_CTIME: u32 = 0x400;
50const FATTR_KILL_SUIDGID: u32 = 0x800;
51
52bitflags! {
53    pub struct SetattrValid: u32 {
54        const MODE = FATTR_MODE;
55        const UID = FATTR_UID;
56        const GID = FATTR_GID;
57        const SIZE = FATTR_SIZE;
58        const ATIME = FATTR_ATIME;
59        const MTIME = FATTR_MTIME;
60        const ATIME_NOW = FATTR_ATIME_NOW;
61        const MTIME_NOW = FATTR_MTIME_NOW;
62        const CTIME = FATTR_CTIME;
63        const KILL_SUIDGID = FATTR_KILL_SUIDGID;
64    }
65}
66
67// Flags use by the OPEN request/reply.
68
69/// Kill suid and sgid if executable
70pub const FOPEN_IN_KILL_SUIDGID: u32 = 1;
71
72/// Bypass page cache for this open file.
73const FOPEN_DIRECT_IO: u32 = 1;
74
75/// Don't invalidate the data cache on open.
76const FOPEN_KEEP_CACHE: u32 = 2;
77
78/// The file is not seekable.
79const FOPEN_NONSEEKABLE: u32 = 4;
80
81/// allow caching this directory
82const FOPEN_CACHE_DIR: u32 = 8;
83
84/// the file is stream-like (no file position at all)
85const FOPEN_STREAM: u32 = 16;
86
87bitflags! {
88    /// Options controlling the behavior of files opened by the server in response
89    /// to an open or create request.
90    pub struct OpenOptions: u32 {
91        /// Bypass page cache for this open file.
92        const DIRECT_IO = FOPEN_DIRECT_IO;
93        /// Don't invalidate the data cache on open.
94        const KEEP_CACHE = FOPEN_KEEP_CACHE;
95        /// The file is not seekable.
96        const NONSEEKABLE = FOPEN_NONSEEKABLE;
97        /// allow caching this directory
98        const CACHE_DIR = FOPEN_CACHE_DIR;
99        /// the file is stream-like (no file position at all)
100        const STREAM = FOPEN_STREAM;
101    }
102}
103
104// INIT request/reply flags.
105
106/// Asynchronous read requests.
107const ASYNC_READ: u64 = 0x1;
108
109/// Remote locking for POSIX file locks.
110const POSIX_LOCKS: u64 = 0x2;
111
112/// Kernel sends file handle for fstat, etc... (not yet supported).
113const FILE_OPS: u64 = 0x4;
114
115/// Handles the O_TRUNC open flag in the filesystem.
116const ATOMIC_O_TRUNC: u64 = 0x8;
117
118/// FileSystem handles lookups of "." and "..".
119const EXPORT_SUPPORT: u64 = 0x10;
120
121/// FileSystem can handle write size larger than 4kB.
122const BIG_WRITES: u64 = 0x20;
123
124/// Don't apply umask to file mode on create operations.
125const DONT_MASK: u64 = 0x40;
126
127/// Kernel supports splice write on the device.
128const SPLICE_WRITE: u64 = 0x80;
129
130/// Kernel supports splice move on the device.
131const SPLICE_MOVE: u64 = 0x100;
132
133/// Kernel supports splice read on the device.
134const SPLICE_READ: u64 = 0x200;
135
136/// Remote locking for BSD style file locks.
137const FLOCK_LOCKS: u64 = 0x400;
138
139/// Kernel supports ioctl on directories.
140const HAS_IOCTL_DIR: u64 = 0x800;
141
142/// Automatically invalidate cached pages.
143const AUTO_INVAL_DATA: u64 = 0x1000;
144
145/// Do READDIRPLUS (READDIR+LOOKUP in one).
146const DO_READDIRPLUS: u64 = 0x2000;
147
148/// Adaptive readdirplus.
149const READDIRPLUS_AUTO: u64 = 0x4000;
150
151/// Asynchronous direct I/O submission.
152const ASYNC_DIO: u64 = 0x8000;
153
154/// Use writeback cache for buffered writes.
155const WRITEBACK_CACHE: u64 = 0x1_0000;
156
157/// Kernel supports zero-message opens.
158const NO_OPEN_SUPPORT: u64 = 0x2_0000;
159
160/// Allow parallel lookups and readdir.
161const PARALLEL_DIROPS: u64 = 0x4_0000;
162
163/// Fs handles killing suid/sgid/cap on write/chown/trunc.
164const HANDLE_KILLPRIV: u64 = 0x8_0000;
165
166/// FileSystem supports posix acls.
167const POSIX_ACL: u64 = 0x10_0000;
168
169// Reading the fuse device after abort returns ECONNABORTED
170const ABORT_ERROR: u64 = 0x20_0000;
171
172// INIT response init_out.max_pages contains the max number of req pages
173const MAX_PAGES: u64 = 0x40_0000;
174
175// Kernel caches READLINK responses
176const CACHE_SYMLINKS: u64 = 0x80_0000;
177
178// Kernel supports zero-message opendir
179const NO_OPENDIR_SUPPORT: u64 = 0x100_0000;
180
181// Only invalidate cached pages on explicit request
182const EXPLICIT_INVAL_DATA: u64 = 0x200_0000;
183
184// INIT response init_out.map_alignment contains byte alignment for foffset and
185// moffset fields in struct fuse_setupmapping_out and fuse_removemapping_one.
186const MAP_ALIGNMENT: u64 = 0x400_0000;
187
188// Kernel supports auto-mounting directory submounts
189const SUBMOUNTS: u64 = 0x800_0000;
190
191// Filesystem responsible for clearing security.capability xattr and setuid/setgid bits.
192const HANDLE_KILLPRIV_V2: u64 = 0x1000_0000;
193
194// This flag indicates whether the fuse_init_in is extended
195const INIT_EXT: u64 = 0x4000_0000;
196
197// This flag indicates whether the guest kernel enable per-file dax
198const PERFILE_DAX: u64 = 0x2_0000_0000;
199
200// This flag indicates whether to enable fd-passthrough. It was defined in the
201// Anolis kernel but not in the upstream kernel. To avoid collision, we'll set
202// it to the most significant bit.
203const FD_PASSTHROUGH: u64 = 0x8000_0000_0000_0000;
204
205/**
206 *
207 * fuse_attr flags
208 *
209 * upstream kernel use (1 << 0) as FUSE_ATTR_SUBMOUNT,
210 * so FUSE_ATTR_DAX will use (1 << 1)
211 *
212 */
213/// This attribute indicates whether the file supports dax in per-file DAX mode
214pub const FUSE_ATTR_DAX: u32 = 1 << 1;
215
216bitflags! {
217    /// A bitfield passed in as a parameter to and returned from the `init` method of the
218    /// `FileSystem` trait.
219    pub struct FsOptions: u64 {
220        /// Indicates that the filesystem supports asynchronous read requests.
221        ///
222        /// If this capability is not requested/available, the kernel will ensure that there is at
223        /// most one pending read request per file-handle at any time, and will attempt to order
224        /// read requests by increasing offset.
225        ///
226        /// This feature is enabled by default when supported by the kernel.
227        const ASYNC_READ = ASYNC_READ;
228
229        /// Indicates that the filesystem supports "remote" locking.
230        ///
231        /// This feature is not enabled by default and should only be set if the filesystem
232        /// implements the `getlk` and `setlk` methods of the `FileSystem` trait.
233        const POSIX_LOCKS = POSIX_LOCKS;
234
235        /// Kernel sends file handle for fstat, etc... (not yet supported).
236        const FILE_OPS = FILE_OPS;
237
238        /// Indicates that the filesystem supports the `O_TRUNC` open flag. If disabled, and an
239        /// application specifies `O_TRUNC`, fuse first calls `setattr` to truncate the file and
240        /// then calls `open` with `O_TRUNC` filtered out.
241        ///
242        /// This feature is enabled by default when supported by the kernel.
243        const ATOMIC_O_TRUNC = ATOMIC_O_TRUNC;
244
245        /// Indicates that the filesystem supports lookups of "." and "..".
246        ///
247        /// This feature is disabled by default.
248        const EXPORT_SUPPORT = EXPORT_SUPPORT;
249
250        /// FileSystem can handle write size larger than 4kB.
251        const BIG_WRITES = BIG_WRITES;
252
253        /// Indicates that the kernel should not apply the umask to the file mode on create
254        /// operations.
255        ///
256        /// This feature is disabled by default.
257        const DONT_MASK = DONT_MASK;
258
259        /// Indicates that the server should try to use `splice(2)` when writing to the fuse device.
260        /// This may improve performance.
261        ///
262        /// This feature is not currently supported.
263        const SPLICE_WRITE = SPLICE_WRITE;
264
265        /// Indicates that the server should try to move pages instead of copying when writing to /
266        /// reading from the fuse device. This may improve performance.
267        ///
268        /// This feature is not currently supported.
269        const SPLICE_MOVE = SPLICE_MOVE;
270
271        /// Indicates that the server should try to use `splice(2)` when reading from the fuse
272        /// device. This may improve performance.
273        ///
274        /// This feature is not currently supported.
275        const SPLICE_READ = SPLICE_READ;
276
277        /// If set, then calls to `flock` will be emulated using POSIX locks and must
278        /// then be handled by the filesystem's `setlock()` handler.
279        ///
280        /// If not set, `flock` calls will be handled by the FUSE kernel module internally (so any
281        /// access that does not go through the kernel cannot be taken into account).
282        ///
283        /// This feature is disabled by default.
284        const FLOCK_LOCKS = FLOCK_LOCKS;
285
286        /// Indicates that the filesystem supports ioctl's on directories.
287        ///
288        /// This feature is enabled by default when supported by the kernel.
289        const HAS_IOCTL_DIR = HAS_IOCTL_DIR;
290
291        /// Traditionally, while a file is open the FUSE kernel module only asks the filesystem for
292        /// an update of the file's attributes when a client attempts to read beyond EOF. This is
293        /// unsuitable for e.g. network filesystems, where the file contents may change without the
294        /// kernel knowing about it.
295        ///
296        /// If this flag is set, FUSE will check the validity of the attributes on every read. If
297        /// the attributes are no longer valid (i.e., if the *attribute* timeout has expired) then
298        /// FUSE will first send another `getattr` request. If the new mtime differs from the
299        /// previous value, any cached file *contents* will be invalidated as well.
300        ///
301        /// This flag should always be set when available. If all file changes go through the
302        /// kernel, *attribute* validity should be set to a very large number to avoid unnecessary
303        /// `getattr()` calls.
304        ///
305        /// This feature is enabled by default when supported by the kernel.
306        const AUTO_INVAL_DATA = AUTO_INVAL_DATA;
307
308        /// Indicates that the filesystem supports readdirplus.
309        ///
310        /// The feature is not enabled by default and should only be set if the filesystem
311        /// implements the `readdirplus` method of the `FileSystem` trait.
312        const DO_READDIRPLUS = DO_READDIRPLUS;
313
314        /// Indicates that the filesystem supports adaptive readdirplus.
315        ///
316        /// If `DO_READDIRPLUS` is not set, this flag has no effect.
317        ///
318        /// If `DO_READDIRPLUS` is set and this flag is not set, the kernel will always issue
319        /// `readdirplus()` requests to retrieve directory contents.
320        ///
321        /// If `DO_READDIRPLUS` is set and this flag is set, the kernel will issue both `readdir()`
322        /// and `readdirplus()` requests, depending on how much information is expected to be
323        /// required.
324        ///
325        /// This feature is not enabled by default and should only be set if the file system
326        /// implements both the `readdir` and `readdirplus` methods of the `FileSystem` trait.
327        const READDIRPLUS_AUTO = READDIRPLUS_AUTO;
328
329        /// Indicates that the filesystem supports asynchronous direct I/O submission.
330        ///
331        /// If this capability is not requested/available, the kernel will ensure that there is at
332        /// most one pending read and one pending write request per direct I/O file-handle at any
333        /// time.
334        ///
335        /// This feature is enabled by default when supported by the kernel.
336        const ASYNC_DIO = ASYNC_DIO;
337
338        /// Indicates that writeback caching should be enabled. This means that individual write
339        /// request may be buffered and merged in the kernel before they are sent to the file
340        /// system.
341        ///
342        /// This feature is disabled by default.
343        const WRITEBACK_CACHE = WRITEBACK_CACHE;
344
345        /// Indicates support for zero-message opens. If this flag is set in the `capable` parameter
346        /// of the `init` trait method, then the file system may return `ENOSYS` from the open() handler
347        /// to indicate success. Further attempts to open files will be handled in the kernel. (If
348        /// this flag is not set, returning ENOSYS will be treated as an error and signaled to the
349        /// caller).
350        ///
351        /// Setting (or not setting) the field in the `FsOptions` returned from the `init` method
352        /// has no effect.
353        const ZERO_MESSAGE_OPEN = NO_OPEN_SUPPORT;
354
355        /// Indicates support for parallel directory operations. If this flag is unset, the FUSE
356        /// kernel module will ensure that lookup() and readdir() requests are never issued
357        /// concurrently for the same directory.
358        ///
359        /// This feature is enabled by default when supported by the kernel.
360        const PARALLEL_DIROPS = PARALLEL_DIROPS;
361
362        /// Indicates that the file system is responsible for unsetting setuid and setgid bits when a
363        /// file is written, truncated, or its owner is changed.
364        ///
365        /// This feature is enabled by default when supported by the kernel.
366        const HANDLE_KILLPRIV = HANDLE_KILLPRIV;
367
368        /// Indicates support for POSIX ACLs.
369        ///
370        /// If this feature is enabled, the kernel will cache and have responsibility for enforcing
371        /// ACLs. ACL will be stored as xattrs and passed to userspace, which is responsible for
372        /// updating the ACLs in the filesystem, keeping the file mode in sync with the ACL, and
373        /// ensuring inheritance of default ACLs when new filesystem nodes are created. Note that
374        /// this requires that the file system is able to parse and interpret the xattr
375        /// representation of ACLs.
376        ///
377        /// Enabling this feature implicitly turns on the `default_permissions` mount option (even
378        /// if it was not passed to mount(2)).
379        ///
380        /// This feature is disabled by default.
381        const POSIX_ACL = POSIX_ACL;
382
383        /// Indicates support for fuse device abort error.
384        ///
385        /// If this feature is enabled, the kernel will return ECONNABORTED to daemon when a fuse
386        /// connection is aborted. Otherwise, ENODEV is returned.
387        ///
388        /// This feature is enabled by default.
389        const ABORT_ERROR = ABORT_ERROR;
390
391        /// Indicate support for max number of req pages negotiation during INIT request handling.
392        ///
393        /// If this feature is enabled, FUSE INIT response init_out.max_pages will contain the max
394        /// number of req pages.
395        ///
396        /// This feature is enabled by default.
397        const MAX_PAGES = MAX_PAGES;
398
399        /// Indicate support for kernel caching symlinks.
400        ///
401        /// If this feature is enabled, the kernel will cache symlink contents.
402        ///
403        /// This feature is enabled by default.
404        const CACHE_SYMLINKS = CACHE_SYMLINKS;
405
406        /// Indicates support for zero-message opens. If this flag is set in the `capable` parameter
407        /// of the `init` trait method, then the file system may return `ENOSYS` from the opendir()
408        /// handler to indicate success. Further attempts to open files will be handled in the kernel
409        /// (If this flag is not set, returning ENOSYS will be treated as an error and signaled to the
410        /// caller).
411        ///
412        /// Setting (or not setting) the field in the `FsOptions` returned from the `init` method
413        /// has no effect.
414        ///
415        /// This feature is enabled by default.
416        const ZERO_MESSAGE_OPENDIR = NO_OPENDIR_SUPPORT;
417
418        /// Indicates to kernel that it is fully responsible for data cache
419        /// invalidation, then the kernel won't invalidate files data cache on size
420        /// change and only truncate that cache to new size in case the size decreased.
421        ///
422        /// If this feature is enabled, FileSystem should notify kernel when a file's data is changed
423        /// outside of fuse.
424        ///
425        /// This feature is enabled by default.
426        const EXPLICIT_INVAL_DATA = EXPLICIT_INVAL_DATA;
427
428        /// Indicate support for byte alignment negotiation during INIT request handling.
429        ///
430        /// If this feature is enabled, the INIT response init_out.map_alignment contains byte alignment for
431        /// foffset and moffset fields in fuse_setupmapping_out and fuse_removemapping_one.
432        ///
433        /// This feature is enabled by default.
434        const MAP_ALIGNMENT = MAP_ALIGNMENT;
435
436        /// Kernel supports the ATTR_SUBMOUNT flag.
437        const SUBMOUNTS = SUBMOUNTS;
438
439        /// Filesystem responsible for clearing security.capability xattr and setuid/setgid bits.
440        /// 1. clear "security.capability" on write, truncate and chown unconditionally
441        /// 2. sgid is cleared only if group executable bit is set
442        /// 3. clear suid/sgid when one of the following is true:
443        ///  -. setattr has FATTR_SIZE and FATTR_KILL_SUIDGID set.
444        ///  -. setattr has FATTR_UID or FATTR_GID
445        ///  -. open has O_TRUNC and FOPEN_IN_KILL_SUIDGID
446        ///  -. create has O_TRUNC and FOPEN_IN_KILL_SUIDGID flag set.
447        ///  -. write has WRITE_KILL_PRIV
448        const HANDLE_KILLPRIV_V2 = HANDLE_KILLPRIV_V2;
449
450        /// Indicates the kernel support fuse fd passthrough.
451        const FD_PASSTHROUGH = FD_PASSTHROUGH;
452
453        /// The fuse_init_in is extended.
454        const INIT_EXT = INIT_EXT;
455
456        /// Indicates whether the guest kernel enable per-file dax
457        ///
458        /// If this feature is enabled, filesystem will notify guest kernel whether file
459        /// enable DAX by EntryOut.Attr.flags of inode when lookup
460        const PERFILE_DAX = PERFILE_DAX;
461    }
462}
463
464// Release flags.
465pub const RELEASE_FLUSH: u32 = 1;
466pub const RELEASE_FLOCK_UNLOCK: u32 = 2;
467
468// Getattr flags.
469pub const GETATTR_FH: u32 = 1;
470
471// Lock flags.
472pub const LK_FLOCK: u32 = 1;
473
474// Write flags.
475
476/// Delayed write from page cache, file handle is guessed.
477pub const WRITE_CACHE: u32 = 1;
478
479/// `lock_owner` field is valid.
480pub const WRITE_LOCKOWNER: u32 = 2;
481
482/// kill suid and sgid bits
483pub const WRITE_KILL_PRIV: u32 = 4;
484
485// Read flags.
486pub const READ_LOCKOWNER: u32 = 2;
487
488// Ioctl flags.
489
490/// 32bit compat ioctl on 64bit machine
491const IOCTL_COMPAT: u32 = 1;
492
493/// Not restricted to well-formed ioctls, retry allowed
494const IOCTL_UNRESTRICTED: u32 = 2;
495
496/// Retry with new iovecs
497const IOCTL_RETRY: u32 = 4;
498
499/// 32bit ioctl
500const IOCTL_32BIT: u32 = 8;
501
502/// Is a directory
503const IOCTL_DIR: u32 = 16;
504
505/// x32 compat ioctl on 64bit machine (64bit time_t)
506const IOCTL_COMPAT_X32: u32 = 32;
507
508/// Maximum of in_iovecs + out_iovecs
509const IOCTL_MAX_IOV: u32 = 256;
510
511bitflags! {
512    pub struct IoctlFlags: u32 {
513        /// 32bit compat ioctl on 64bit machine
514        const IOCTL_COMPAT = IOCTL_COMPAT;
515
516        /// Not restricted to well-formed ioctls, retry allowed
517        const IOCTL_UNRESTRICTED = IOCTL_UNRESTRICTED;
518
519        /// Retry with new iovecs
520        const IOCTL_RETRY = IOCTL_RETRY;
521
522        /// 32bit ioctl
523        const IOCTL_32BIT = IOCTL_32BIT;
524
525        /// Is a directory
526        const IOCTL_DIR = IOCTL_DIR;
527
528        /// x32 compat ioctl on 64bit machine (64bit time_t)
529        const IOCTL_COMPAT_X32 = IOCTL_COMPAT_X32;
530
531        /// Maximum of in_iovecs + out_iovecs
532        const IOCTL_MAX_IOV = IOCTL_MAX_IOV;
533    }
534}
535
536/// EntryOut flags
537/// Entry is a submount root
538pub const ATTR_SUBMOUNT: u32 = 1;
539
540/// Request poll notify.
541pub const POLL_SCHEDULE_NOTIFY: u32 = 1;
542
543/// Fsync flags
544///
545/// Sync data only, not metadata
546pub const FSYNC_FDATASYNC: u32 = 1;
547
548/// The read buffer is required to be at least 8k, but may be much larger.
549pub const FUSE_MIN_READ_BUFFER: u32 = 8192;
550
551pub const FUSE_COMPAT_ENTRY_OUT_SIZE: usize = 120;
552pub const FUSE_COMPAT_ATTR_OUT_SIZE: usize = 96;
553pub const FUSE_COMPAT_MKNOD_IN_SIZE: usize = 8;
554pub const FUSE_COMPAT_WRITE_IN_SIZE: usize = 24;
555pub const FUSE_COMPAT_STATFS_SIZE: usize = 48;
556pub const FUSE_COMPAT_INIT_OUT_SIZE: usize = 8;
557pub const FUSE_COMPAT_22_INIT_OUT_SIZE: usize = 24;
558
559// Message definitions follow.  It is safe to implement ByteValued for all of these
560// because they are POD types.
561
562#[repr(C)]
563#[derive(Debug, Default, Copy, Clone)]
564pub struct Attr {
565    pub ino: u64,
566    pub size: u64,
567    pub blocks: u64,
568    pub atime: u64,
569    pub mtime: u64,
570    pub ctime: u64,
571    pub atimensec: u32,
572    pub mtimensec: u32,
573    pub ctimensec: u32,
574    pub mode: u32,
575    pub nlink: u32,
576    pub uid: u32,
577    pub gid: u32,
578    pub rdev: u32,
579    pub blksize: u32,
580    pub flags: u32,
581}
582unsafe impl ByteValued for Attr {}
583
584impl From<stat64> for Attr {
585    fn from(st: stat64) -> Attr {
586        Attr::with_flags(st, 0)
587    }
588}
589
590impl Attr {
591    pub fn with_flags(st: stat64, flags: u32) -> Attr {
592        Attr {
593            ino: st.st_ino,
594            size: st.st_size as u64,
595            blocks: st.st_blocks as u64,
596            atime: st.st_atime as u64,
597            mtime: st.st_mtime as u64,
598            ctime: st.st_ctime as u64,
599            atimensec: st.st_atime_nsec as u32,
600            mtimensec: st.st_mtime_nsec as u32,
601            ctimensec: st.st_ctime_nsec as u32,
602            mode: st.st_mode,
603            // In Linux st.st_nlink is u64 on x86_64 and powerpc64, and u32 on other architectures
604            // ref:
605            // linux: https://github.com/rust-lang/rust/blob/1.69.0/library/std/src/os/linux/raw.rs#L333
606            #[allow(clippy::unnecessary_cast)]
607            nlink: st.st_nlink as u32,
608            uid: st.st_uid,
609            gid: st.st_gid,
610            rdev: st.st_rdev as u32,
611            blksize: st.st_blksize as u32,
612            flags,
613        }
614    }
615}
616
617impl From<Attr> for stat64 {
618    fn from(attr: Attr) -> stat64 {
619        // Safe because we are zero-initializing a struct
620        let mut out: stat64 = unsafe { mem::zeroed() };
621        out.st_ino = attr.ino;
622        out.st_size = attr.size as i64;
623        out.st_blocks = attr.blocks as i64;
624        out.st_atime = attr.atime as i64;
625        out.st_mtime = attr.mtime as i64;
626        out.st_ctime = attr.ctime as i64;
627        out.st_atime_nsec = attr.atimensec as i64;
628        out.st_mtime_nsec = attr.mtimensec as i64;
629        out.st_ctime_nsec = attr.ctimensec as i64;
630        out.st_mode = attr.mode as mode_t;
631        out.st_nlink = attr.nlink as nlink_t;
632        out.st_uid = attr.uid;
633        out.st_gid = attr.gid;
634        out.st_rdev = attr.rdev as dev_t;
635        out.st_blksize = attr.blksize as blksize_t;
636
637        out
638    }
639}
640
641#[repr(C)]
642#[derive(Debug, Default, Copy, Clone)]
643pub struct Kstatfs {
644    pub blocks: u64,
645    pub bfree: u64,
646    pub bavail: u64,
647    pub files: u64,
648    pub ffree: u64,
649    pub bsize: u32,
650    pub namelen: u32,
651    pub frsize: u32,
652    pub padding: u32,
653    pub spare: [u32; 6],
654}
655unsafe impl ByteValued for Kstatfs {}
656
657impl From<statvfs64> for Kstatfs {
658    fn from(st: statvfs64) -> Self {
659        Kstatfs {
660            blocks: st.f_blocks,
661            bfree: st.f_bfree,
662            bavail: st.f_bavail,
663            files: st.f_files,
664            ffree: st.f_ffree,
665            bsize: st.f_bsize as u32,
666            namelen: st.f_namemax as u32,
667            frsize: st.f_frsize as u32,
668            ..Default::default()
669        }
670    }
671}
672
673#[repr(C)]
674#[derive(Debug, Default, Copy, Clone)]
675pub struct FileLock {
676    pub start: u64,
677    pub end: u64,
678    pub type_: u32,
679    pub pid: u32, /* tgid */
680}
681unsafe impl ByteValued for FileLock {}
682
683#[repr(u32)]
684#[derive(Debug, Copy, Clone)]
685pub enum Opcode {
686    Lookup = 1,
687    Forget = 2, /* No Reply */
688    Getattr = 3,
689    Setattr = 4,
690    Readlink = 5,
691    Symlink = 6,
692    Mknod = 8,
693    Mkdir = 9,
694    Unlink = 10,
695    Rmdir = 11,
696    Rename = 12,
697    Link = 13,
698    Open = 14,
699    Read = 15,
700    Write = 16,
701    Statfs = 17,
702    Release = 18,
703    Fsync = 20,
704    Setxattr = 21,
705    Getxattr = 22,
706    Listxattr = 23,
707    Removexattr = 24,
708    Flush = 25,
709    Init = 26,
710    Opendir = 27,
711    Readdir = 28,
712    Releasedir = 29,
713    Fsyncdir = 30,
714    Getlk = 31,
715    Setlk = 32,
716    Setlkw = 33,
717    Access = 34,
718    Create = 35,
719    Interrupt = 36,
720    Bmap = 37,
721    Destroy = 38,
722    Ioctl = 39,
723    Poll = 40,
724    NotifyReply = 41,
725    BatchForget = 42,
726    Fallocate = 43,
727    Readdirplus = 44,
728    Rename2 = 45,
729    Lseek = 46,
730    CopyFileRange = 47,
731    SetupMapping = 48,
732    RemoveMapping = 49,
733    MaxOpcode = 50,
734
735    /* Reserved opcodes: helpful to detect structure endian-ness in case of e.g. virtiofs */
736    CuseInitBswapReserved = 1_048_576, /* CUSE_INIT << 8 */
737    InitBswapReserved = 436_207_616,   /* FUSE_INIT << 24 */
738}
739
740impl From<u32> for Opcode {
741    fn from(op: u32) -> Opcode {
742        if op >= Opcode::MaxOpcode as u32 {
743            return Opcode::MaxOpcode;
744        }
745        unsafe { mem::transmute(op) }
746    }
747}
748
749#[repr(u32)]
750#[derive(Debug, Copy, Clone)]
751pub enum NotifyOpcode {
752    Poll = 1,
753    InvalInode = 2,
754    InvalEntry = 3,
755    Store = 4,
756    Retrieve = 5,
757    Delete = 6,
758    CodeMax = 7,
759}
760
761#[repr(C)]
762#[derive(Debug, Default, Copy, Clone)]
763pub struct EntryOut {
764    pub nodeid: u64,      /* Inode ID */
765    pub generation: u64,  /* Inode generation: nodeid:gen must be unique for the fs's lifetime */
766    pub entry_valid: u64, /* Cache timeout for the name */
767    pub attr_valid: u64,  /* Cache timeout for the attributes */
768    pub entry_valid_nsec: u32,
769    pub attr_valid_nsec: u32,
770    pub attr: Attr,
771}
772unsafe impl ByteValued for EntryOut {}
773
774#[repr(C)]
775#[derive(Debug, Default, Copy, Clone)]
776pub struct ForgetIn {
777    pub nlookup: u64,
778}
779unsafe impl ByteValued for ForgetIn {}
780
781#[repr(C)]
782#[derive(Debug, Default, Copy, Clone)]
783pub struct ForgetOne {
784    pub nodeid: u64,
785    pub nlookup: u64,
786}
787unsafe impl ByteValued for ForgetOne {}
788
789#[repr(C)]
790#[derive(Debug, Default, Copy, Clone)]
791pub struct BatchForgetIn {
792    pub count: u32,
793    pub dummy: u32,
794}
795unsafe impl ByteValued for BatchForgetIn {}
796
797#[repr(C)]
798#[derive(Debug, Default, Copy, Clone)]
799pub struct GetattrIn {
800    pub flags: u32,
801    pub dummy: u32,
802    pub fh: u64,
803}
804unsafe impl ByteValued for GetattrIn {}
805
806#[repr(C)]
807#[derive(Debug, Default, Copy, Clone)]
808pub struct AttrOut {
809    pub attr_valid: u64, /* Cache timeout for the attributes */
810    pub attr_valid_nsec: u32,
811    pub dummy: u32,
812    pub attr: Attr,
813}
814unsafe impl ByteValued for AttrOut {}
815
816#[repr(C)]
817#[derive(Debug, Default, Copy, Clone)]
818pub struct MknodIn {
819    pub mode: u32,
820    pub rdev: u32,
821    pub umask: u32,
822    pub padding: u32,
823}
824unsafe impl ByteValued for MknodIn {}
825
826#[repr(C)]
827#[derive(Debug, Default, Copy, Clone)]
828pub struct MkdirIn {
829    pub mode: u32,
830    pub umask: u32,
831}
832unsafe impl ByteValued for MkdirIn {}
833
834#[repr(C)]
835#[derive(Debug, Default, Copy, Clone)]
836pub struct RenameIn {
837    pub newdir: u64,
838}
839unsafe impl ByteValued for RenameIn {}
840
841#[repr(C)]
842#[derive(Debug, Default, Copy, Clone)]
843pub struct Rename2In {
844    pub newdir: u64,
845    pub flags: u32,
846    pub padding: u32,
847}
848unsafe impl ByteValued for Rename2In {}
849
850#[repr(C)]
851#[derive(Debug, Default, Copy, Clone)]
852pub struct LinkIn {
853    pub oldnodeid: u64,
854}
855unsafe impl ByteValued for LinkIn {}
856
857#[repr(C)]
858#[derive(Debug, Default, Copy, Clone)]
859pub struct SetattrIn {
860    pub valid: u32,
861    pub padding: u32,
862    pub fh: u64,
863    pub size: u64,
864    pub lock_owner: u64,
865    pub atime: u64,
866    pub mtime: u64,
867    pub ctime: u64,
868    pub atimensec: u32,
869    pub mtimensec: u32,
870    pub ctimensec: u32,
871    pub mode: u32,
872    pub unused4: u32,
873    pub uid: u32,
874    pub gid: u32,
875    pub unused5: u32,
876}
877unsafe impl ByteValued for SetattrIn {}
878
879impl From<SetattrIn> for stat64 {
880    fn from(setattr: SetattrIn) -> stat64 {
881        // Safe because we are zero-initializing a struct with only POD fields.
882        let mut out: stat64 = unsafe { mem::zeroed() };
883        out.st_mode = setattr.mode as mode_t;
884        out.st_uid = setattr.uid;
885        out.st_gid = setattr.gid;
886        out.st_size = setattr.size as i64;
887        out.st_atime = setattr.atime as i64;
888        out.st_mtime = setattr.mtime as i64;
889        out.st_ctime = setattr.ctime as i64;
890        out.st_atime_nsec = i64::from(setattr.atimensec);
891        out.st_mtime_nsec = i64::from(setattr.mtimensec);
892        out.st_ctime_nsec = i64::from(setattr.ctimensec);
893
894        out
895    }
896}
897
898#[repr(C)]
899#[derive(Debug, Default, Copy, Clone)]
900pub struct OpenIn {
901    pub flags: u32,
902    pub fuse_flags: u32,
903}
904unsafe impl ByteValued for OpenIn {}
905
906#[repr(C)]
907#[derive(Debug, Default, Copy, Clone)]
908pub struct CreateIn {
909    pub flags: u32,
910    pub mode: u32,
911    pub umask: u32,
912    pub fuse_flags: u32,
913}
914unsafe impl ByteValued for CreateIn {}
915
916#[repr(C)]
917#[derive(Debug, Default, Copy, Clone)]
918pub struct OpenOut {
919    pub fh: u64,
920    pub open_flags: u32,
921    pub passthrough: u32,
922}
923unsafe impl ByteValued for OpenOut {}
924
925#[repr(C)]
926#[derive(Debug, Default, Copy, Clone)]
927pub struct ReleaseIn {
928    pub fh: u64,
929    pub flags: u32,
930    pub release_flags: u32,
931    pub lock_owner: u64,
932}
933unsafe impl ByteValued for ReleaseIn {}
934
935#[repr(C)]
936#[derive(Debug, Default, Copy, Clone)]
937pub struct FlushIn {
938    pub fh: u64,
939    pub unused: u32,
940    pub padding: u32,
941    pub lock_owner: u64,
942}
943unsafe impl ByteValued for FlushIn {}
944
945#[repr(C)]
946#[derive(Debug, Default, Copy, Clone)]
947pub struct ReadIn {
948    pub fh: u64,
949    pub offset: u64,
950    pub size: u32,
951    pub read_flags: u32,
952    pub lock_owner: u64,
953    pub flags: u32,
954    pub padding: u32,
955}
956unsafe impl ByteValued for ReadIn {}
957
958#[repr(C)]
959#[derive(Debug, Default, Copy, Clone)]
960pub struct WriteIn {
961    pub fh: u64,
962    pub offset: u64,
963    pub size: u32,
964    pub fuse_flags: u32,
965    pub lock_owner: u64,
966    pub flags: u32,
967    pub padding: u32,
968}
969unsafe impl ByteValued for WriteIn {}
970
971#[repr(C)]
972#[derive(Debug, Default, Copy, Clone)]
973pub struct WriteOut {
974    pub size: u32,
975    pub padding: u32,
976}
977unsafe impl ByteValued for WriteOut {}
978
979#[repr(C)]
980#[derive(Debug, Default, Copy, Clone)]
981pub struct StatfsOut {
982    pub st: Kstatfs,
983}
984unsafe impl ByteValued for StatfsOut {}
985
986#[repr(C)]
987#[derive(Debug, Default, Copy, Clone)]
988pub struct FsyncIn {
989    pub fh: u64,
990    pub fsync_flags: u32,
991    pub padding: u32,
992}
993unsafe impl ByteValued for FsyncIn {}
994
995#[repr(C)]
996#[derive(Debug, Default, Copy, Clone)]
997pub struct SetxattrIn {
998    pub size: u32,
999    pub flags: u32,
1000}
1001unsafe impl ByteValued for SetxattrIn {}
1002
1003#[repr(C)]
1004#[derive(Debug, Default, Copy, Clone)]
1005pub struct GetxattrIn {
1006    pub size: u32,
1007    pub padding: u32,
1008}
1009unsafe impl ByteValued for GetxattrIn {}
1010
1011#[repr(C)]
1012#[derive(Debug, Default, Copy, Clone)]
1013pub struct GetxattrOut {
1014    pub size: u32,
1015    pub padding: u32,
1016}
1017unsafe impl ByteValued for GetxattrOut {}
1018
1019#[repr(C)]
1020#[derive(Debug, Default, Copy, Clone)]
1021pub struct LkIn {
1022    pub fh: u64,
1023    pub owner: u64,
1024    pub lk: FileLock,
1025    pub lk_flags: u32,
1026    pub padding: u32,
1027}
1028unsafe impl ByteValued for LkIn {}
1029
1030#[repr(C)]
1031#[derive(Debug, Default, Copy, Clone)]
1032pub struct LkOut {
1033    pub lk: FileLock,
1034}
1035unsafe impl ByteValued for LkOut {}
1036
1037#[repr(C)]
1038#[derive(Debug, Default, Copy, Clone)]
1039pub struct AccessIn {
1040    pub mask: u32,
1041    pub padding: u32,
1042}
1043unsafe impl ByteValued for AccessIn {}
1044
1045#[repr(C)]
1046#[derive(Debug, Default, Copy, Clone)]
1047pub struct InitIn {
1048    pub major: u32,
1049    pub minor: u32,
1050    pub max_readahead: u32,
1051    pub flags: u32,
1052}
1053unsafe impl ByteValued for InitIn {}
1054
1055//The flag has been extended to 64 bit since fuse 7.36
1056#[repr(C)]
1057#[derive(Debug, Default, Copy, Clone)]
1058pub struct InitIn2 {
1059    pub flags2: u32,
1060    pub unused: [u32; 11],
1061}
1062unsafe impl ByteValued for InitIn2 {}
1063
1064#[repr(C)]
1065#[derive(Debug, Default, Copy, Clone)]
1066pub struct InitOut {
1067    pub major: u32,
1068    pub minor: u32,
1069    pub max_readahead: u32,
1070    pub flags: u32,
1071    pub max_background: u16,
1072    pub congestion_threshold: u16,
1073    pub max_write: u32,
1074    pub time_gran: u32,
1075    pub max_pages: u16,
1076    pub map_alignment: u16,
1077    pub flags2: u32,
1078    pub unused: [u32; 7],
1079}
1080unsafe impl ByteValued for InitOut {}
1081
1082#[repr(C)]
1083#[derive(Debug, Default, Copy, Clone)]
1084pub struct InterruptIn {
1085    pub unique: u64,
1086}
1087unsafe impl ByteValued for InterruptIn {}
1088
1089#[repr(C)]
1090#[derive(Debug, Default, Copy, Clone)]
1091pub struct BmapIn {
1092    pub block: u64,
1093    pub blocksize: u32,
1094    pub padding: u32,
1095}
1096unsafe impl ByteValued for BmapIn {}
1097
1098#[repr(C)]
1099#[derive(Debug, Default, Copy, Clone)]
1100pub struct BmapOut {
1101    pub block: u64,
1102}
1103unsafe impl ByteValued for BmapOut {}
1104
1105#[repr(C)]
1106#[derive(Debug, Default, Copy, Clone)]
1107pub struct IoctlIn {
1108    pub fh: u64,
1109    pub flags: u32,
1110    pub cmd: u32,
1111    pub arg: u64,
1112    pub in_size: u32,
1113    pub out_size: u32,
1114}
1115unsafe impl ByteValued for IoctlIn {}
1116
1117#[repr(C)]
1118#[derive(Debug, Default, Copy, Clone)]
1119pub struct IoctlIovec {
1120    pub base: u64,
1121    pub len: u64,
1122}
1123unsafe impl ByteValued for IoctlIovec {}
1124
1125#[repr(C)]
1126#[derive(Debug, Default, Copy, Clone)]
1127pub struct IoctlOut {
1128    pub result: i32,
1129    pub flags: u32,
1130    pub in_iovs: u32,
1131    pub out_iovs: u32,
1132}
1133unsafe impl ByteValued for IoctlOut {}
1134
1135#[repr(C)]
1136#[derive(Debug, Default, Copy, Clone)]
1137pub struct PollIn {
1138    pub fh: u64,
1139    pub kh: u64,
1140    pub flags: u32,
1141    pub events: u32,
1142}
1143unsafe impl ByteValued for PollIn {}
1144
1145#[repr(C)]
1146#[derive(Debug, Default, Copy, Clone)]
1147pub struct PollOut {
1148    pub revents: u32,
1149    pub padding: u32,
1150}
1151unsafe impl ByteValued for PollOut {}
1152
1153#[repr(C)]
1154#[derive(Debug, Default, Copy, Clone)]
1155pub struct NotifyPollWakeupOut {
1156    pub kh: u64,
1157}
1158unsafe impl ByteValued for NotifyPollWakeupOut {}
1159
1160#[repr(C)]
1161#[derive(Debug, Default, Copy, Clone)]
1162pub struct FallocateIn {
1163    pub fh: u64,
1164    pub offset: u64,
1165    pub length: u64,
1166    pub mode: u32,
1167    pub padding: u32,
1168}
1169unsafe impl ByteValued for FallocateIn {}
1170
1171#[repr(C)]
1172#[derive(Default, Copy, Clone)]
1173pub struct InHeader {
1174    pub len: u32,
1175    pub opcode: u32,
1176    pub unique: u64,
1177    pub nodeid: u64,
1178    pub uid: u32,
1179    pub gid: u32,
1180    pub pid: u32,
1181    pub padding: u32,
1182}
1183unsafe impl ByteValued for InHeader {}
1184
1185impl Debug for InHeader {
1186    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1187        write!(
1188            f,
1189            "InHeader {{ len: {}, opcode: {}, unique: {}, nodeid: 0x{:x}, uid: {}, gid: {}, pid: {}, padding: {} }}",
1190            self.len, self.opcode, self.unique, self.nodeid, self.uid, self.gid, self.pid, self.padding
1191        )
1192    }
1193}
1194
1195#[repr(C)]
1196#[derive(Debug, Default, Copy, Clone)]
1197pub struct OutHeader {
1198    pub len: u32,
1199    pub error: i32,
1200    pub unique: u64,
1201}
1202unsafe impl ByteValued for OutHeader {}
1203
1204#[repr(C)]
1205#[derive(Debug, Default, Copy, Clone)]
1206pub struct Dirent {
1207    pub ino: u64,
1208    pub off: u64,
1209    pub namelen: u32,
1210    pub type_: u32,
1211    // char name[];
1212}
1213unsafe impl ByteValued for Dirent {}
1214
1215#[repr(C)]
1216#[derive(Debug, Default, Copy, Clone)]
1217pub struct Direntplus {
1218    pub entry_out: EntryOut,
1219    pub dirent: Dirent,
1220}
1221unsafe impl ByteValued for Direntplus {}
1222
1223#[repr(C)]
1224#[derive(Debug, Default, Copy, Clone)]
1225pub struct NotifyInvalInodeOut {
1226    pub ino: u64,
1227    pub off: i64,
1228    pub len: i64,
1229}
1230unsafe impl ByteValued for NotifyInvalInodeOut {}
1231
1232#[repr(C)]
1233#[derive(Debug, Default, Copy, Clone)]
1234pub struct NotifyInvalEntryOut {
1235    pub parent: u64,
1236    pub namelen: u32,
1237    pub padding: u32,
1238}
1239unsafe impl ByteValued for NotifyInvalEntryOut {}
1240
1241#[repr(C)]
1242#[derive(Debug, Default, Copy, Clone)]
1243pub struct NotifyDeleteOut {
1244    pub parent: u64,
1245    pub child: u64,
1246    pub namelen: u32,
1247    pub padding: u32,
1248}
1249unsafe impl ByteValued for NotifyDeleteOut {}
1250
1251#[repr(C)]
1252#[derive(Debug, Default, Copy, Clone)]
1253pub struct NotifyStoreOut {
1254    pub nodeid: u64,
1255    pub offset: u64,
1256    pub size: u32,
1257    pub padding: u32,
1258}
1259unsafe impl ByteValued for NotifyStoreOut {}
1260
1261#[repr(C)]
1262#[derive(Debug, Default, Copy, Clone)]
1263pub struct Notify_Retrieve_Out {
1264    pub notify_unique: u64,
1265    pub nodeid: u64,
1266    pub offset: u64,
1267    pub size: u32,
1268    pub padding: u32,
1269}
1270unsafe impl ByteValued for Notify_Retrieve_Out {}
1271
1272/* Matches the size of fuse_write_in */
1273#[repr(C)]
1274#[derive(Debug, Default, Copy, Clone)]
1275pub struct NotifyRetrieveIn {
1276    pub dummy1: u64,
1277    pub offset: u64,
1278    pub size: u32,
1279    pub dummy2: u32,
1280    pub dummy3: u64,
1281    pub dummy4: u64,
1282}
1283unsafe impl ByteValued for NotifyRetrieveIn {}
1284
1285#[repr(C)]
1286#[derive(Debug, Default, Copy, Clone)]
1287pub struct LseekIn {
1288    pub fh: u64,
1289    pub offset: u64,
1290    pub whence: u32,
1291    pub padding: u32,
1292}
1293unsafe impl ByteValued for LseekIn {}
1294
1295#[repr(C)]
1296#[derive(Debug, Default, Copy, Clone)]
1297pub struct LseekOut {
1298    pub offset: u64,
1299}
1300unsafe impl ByteValued for LseekOut {}
1301
1302#[repr(C)]
1303#[derive(Debug, Default, Copy, Clone)]
1304// Returns WriteOut
1305pub struct CopyFileRangeIn {
1306    pub fh_in: u64,
1307    pub offset_in: u64,
1308    pub nodeid_out: u64,
1309    pub fh_out: u64,
1310    pub offset_out: u64,
1311    pub len: u64,
1312    pub flags: u64,
1313}
1314unsafe impl ByteValued for CopyFileRangeIn {}
1315
1316#[cfg(test)]
1317mod tests {
1318    use super::*;
1319
1320    #[test]
1321    fn test_struct_size() {
1322        assert_eq!(std::mem::size_of::<Attr>(), 88);
1323        assert_eq!(std::mem::size_of::<Kstatfs>(), 80);
1324        assert_eq!(std::mem::size_of::<FileLock>(), 24);
1325        assert_eq!(std::mem::size_of::<EntryOut>(), 128);
1326        assert_eq!(std::mem::size_of::<ForgetIn>(), 8);
1327        assert_eq!(std::mem::size_of::<ForgetOne>(), 16);
1328        assert_eq!(std::mem::size_of::<BatchForgetIn>(), 8);
1329        assert_eq!(std::mem::size_of::<GetattrIn>(), 16);
1330        assert_eq!(std::mem::size_of::<AttrOut>(), 104);
1331        assert_eq!(std::mem::size_of::<MknodIn>(), 16);
1332        assert_eq!(std::mem::size_of::<MkdirIn>(), 8);
1333        assert_eq!(std::mem::size_of::<InHeader>(), 40);
1334        assert_eq!(std::mem::size_of::<OutHeader>(), 16);
1335    }
1336
1337    #[test]
1338    fn test_byte_valued() {
1339        let buf = [
1340            0x1u8, 0x2u8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5u8, 0x6u8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
1341        ];
1342        let forget: &ForgetOne = ForgetOne::from_slice(&buf).unwrap();
1343
1344        assert_eq!(forget.nodeid, 0x201u64);
1345        assert_eq!(forget.nlookup, 0x605u64);
1346
1347        let forget = ForgetOne {
1348            nodeid: 0x201u64,
1349            nlookup: 0x605u64,
1350        };
1351        let buf = forget.as_slice();
1352        assert_eq!(buf[0], 0x1u8);
1353        assert_eq!(buf[1], 0x2u8);
1354        assert_eq!(buf[8], 0x5u8);
1355        assert_eq!(buf[9], 0x6u8);
1356    }
1357}