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