linux-fsdevel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC PATCH v2 00/10] Rust PuzleFS filesystem driver
@ 2023-07-26 16:45 Ariel Miculas
  2023-07-26 16:45 ` [RFC PATCH v2 01/10] samples: puzzlefs: add initial puzzlefs sample, copied from rust_fs.rs Ariel Miculas
                   ` (9 more replies)
  0 siblings, 10 replies; 20+ messages in thread
From: Ariel Miculas @ 2023-07-26 16:45 UTC (permalink / raw)
  To: rust-for-linux
  Cc: linux-kernel, linux-fsdevel, tycho, brauner, viro, ojeda,
	alex.gaynor, wedsonaf, Ariel Miculas

Hello,

This is a proof of concept driver written for the PuzzleFS
next-generation container filesystem [1]. This patch series is based on
top of the puzzlefs_dependencies [4] branch, which contain the
backported filesystem abstractions of Wedson Almeida Filho [2][3] and
the additional third-party crates capnp [5] and hex [6]. The code is
also available on github [11]. I've adapted the user space puzzlefs code
[1] so that the puzzlefs kernel module could present the directory
hierarchy and implement the basic read functionality.  For some
additional context, puzzlefs was started by Tycho Andersen and it's the
successor of atomfs. This FOSDEM presentation from 2019 [10] covers the
rationale for a new oci image format and presents a higher level
overview of our goals with puzzlefs.

Changes since v1:
* the main change is that I've replaced serde_cbor with Cap'n proto at
  Kent Overstreet's suggestion; this simplifies the dependency tree of
  puzzlefs and results in cleaner code
* I've also modified the filesystem abstractions to follow the latest
  "Rust for Linux" best practices (in the puzzlefs_dependencies_v2
  branch [4])

I've split the rest of the cover letter in following sections (using a
markdown style):
* Example: it describes a practical example of what was achieved
* Limitations: it presents the existing limitations of this POC
* Setup: it shows how to setup the necessary environment for testing the
  puzzlefs driver

# Example
An example is provided below:

```
~ # cat /proc/filesystems | grep puzzlefs
nodev   puzzlefs
~ # mount -t puzzlefs -o oci_root_dir="/home/puzzlefs_oci" -o image_manifest="c4
3e5ab9d0cee1dcfbf442d18023b34410de3deb0f6dbffcec72732b6830db09" none /mnt
~ # ls -lR /mnt/
/mnt/:
total 0
drwxr-xr-x    2 0        0                0 Jun  8 12:26 dir-1
drwxr-xr-x    2 0        0                0 Jun  8 12:26 dir-2
drwxr-xr-x    2 0        0                0 Jun  8 12:26 dir-3
drwxr-xr-x    2 0        0                0 Jun  8 12:26 dir-4
-rw-r--r--    1 0        0                0 Jun  8 12:26 file1
-rw-r--r--    1 0        0                0 Jun  8 12:26 file2

/mnt/dir-1:
total 0

/mnt/dir-2:
total 0

/mnt/dir-3:
total 0

/mnt/dir-4:
total 0
~ # cat /mnt/file2
ana are mere bla bla bla
~ # wc /mnt/file1
      202       202      5454 /mnt/file1
```

In this example, /home/puzzlefs_oci is the puzzlefs oci directory:
```
~ # ls -lR /home/puzzlefs_oci/
/home/puzzlefs_oci/:
total 8
drwxr-xr-x    3 1000     1000             0 Jul 26 14:37 blobs
-rw-r--r--    1 1000     1000           267 Jul 26 14:37 index.json
-rw-r--r--    1 1000     1000            37 Jul 26 14:37 oci-layout

/home/puzzlefs_oci/blobs:
total 0
drwxr-xr-x    2 1000     1000             0 Jul 26 14:37 sha256

/home/puzzlefs_oci/blobs/sha256:
total 16
-rw-------    1 1000     1000           272 Jul 26 14:37 c43e5ab9d0cee1dcfbf442d18023b34410de3deb0f6dbffcec72732b6830db09
-rw-------    1 1000     1000          5479 Jul 26 14:37 d86a87b19bd9a2fec0d31687c1d669cdb59e8fb579b0b2bfe354473699732bfb
-rw-------    1 1000     1000           752 Jul 26 14:37 fe6ee75ca93ded15da5f6e0abf335e6a4eb24277c59acd47282d8ed2475f3384
```

`c43e5ab9d0cee1dcfbf442d18023b34410de3deb0f6dbffcec72732b6830db09` is the puzzlefs image manifest
hash for the `first_try` tag:
```
❯ cat index.json | jq .
{
  "schemaVersion": -1,
  "manifests": [
    {
      "digest": "sha256:c43e5ab9d0cee1dcfbf442d18023b34410de3deb0f6dbffcec72732b6830db09",
      "size": 272,
      "media_type": "application/vnd.puzzlefs.image.rootfs.v1",
      "annotations": {
        "org.opencontainers.image.ref.name": "first_try"
      }
    }
  ],
  "annotations": {}
}
```

I will describe how to build a puzzlefs image in step 5 of the `Setup` section.

# Limitations
One limitation is that the puzzlefs driver doesn't implement any lookup
functionality and instead it inserts every directory entry into the
dcache during init (see the `DCACHE_BASED` constant). This is similar to
how the sample `rust_fs` driver works, but the goal is to implement
proper lookup functions.  However, more filesystem abstractions need to
be implemented before this can be achieved.

# Setup
My setup is based on Wedson's tutorial [8]. Next, I will describe the
necessary steps to build an initrd and run a custom kernel under qemu.

1. Get the code from the puzzlefs_rfc_v2 branch on github [11]

2. Follow the rust quickstart guide [9]

3. configure and build the kernel
```
$ make LLVM=1 allnoconfig qemu-busybox-min.config rust.config
$ make LLVM=1 -j$(nproc)
```

4. setup busybox
```
git clone git://git.busybox.net/busybox
cd busybox
make menuconfig # enable 'Build static binary' config
make
make install
```
This will create the `_install` directory with the rootfs inside it.

5. create a home directory in the rootfs and copy a puzzlefs oci
directory in home/puzzlefs_oci
To create a puzzlefs oci directory:
* clone the puzzlefs repository [1]

* run `make release`

* create a simple filesystem structure with a few directories and files
  (I've created one at ../test-puzzlefs/simple_rootfs)

* build a puzzlefs oci image at
  `~/work/busybox/_install/home/puzzlefs_oci` (replace this path with
  your busybox path) with the tag `first_try`:
```
$ target/release/puzzlefs build ../test-puzzlefs/simple_rootfs \
 ~/work/busybox/_install/home/puzzlefs_oci first_try
```

* get first_try's image manifest from index.json (inside `puzzlefs_oci`)
```
$ cat index.json | jq . | grep digest
      "digest": "sha256:c43e5ab9d0cee1dcfbf442d18023b34410de3deb0f6dbffcec72732b6830db09",
```

6. add the following 'init' script in the busybox rootfs (rootfs path
defaults to `./_install'):
```
#!/bin/sh
mount -t devtmpfs none /dev
mkdir -p /proc
mount -t proc none /proc

ifconfig lo up
udhcpc -i eth0

mkdir /mnt
mount -t puzzlefs -o oci_root_dir="/home/puzzlefs_oci" -o \
image_manifest="c43e5ab9d0cee1dcfbf442d18023b34410de3deb0f6dbffcec72732b6830db09" \
none /mnt

setsid sh -c 'exec sh -l </dev/ttyS0 >/dev/ttyS0 2>&1'
```

Make sure to replace the `image_manifest` with your own digest. This
init script will be passed to rdinit in the kernel command line.

7. generate the initramfs

Assuming busybox is in `~/work/busybox`:
```
cd ~/work/busybox/_install && find . | cpio -H newc -o | gzip > ../ramdisk.img
```
This will generate a compressed ramdisk image in
`~/work/busybox/ramdisk.img`.

8. run with qemu (this assumes the linux tree is at '../linux' and busybox
is at '../busybox'):
```
qemu-system-x86_64 \
    -accel kvm \
    -cpu host \
    -m 4G \
    -initrd ../busybox/ramdisk.img \
    -kernel ../linux/arch/x86/boot/bzImage \
    -nographic \
    -append 'console=ttyS0 nokaslr debug rdinit=/init' \
    -nic user,model=rtl8139 \
    -no-reboot
```

9. Check whether puzzlefs has been successfully mounted
```
~ # mount | grep puzzlefs
none on /mnt type puzzlefs (rw,relatime)
~ # ls /mnt/
dir-1  dir-2  dir-3  dir-4  file1  file2
```

[1] https://github.com/project-machine/puzzlefs
[2] https://github.com/wedsonaf/linux/tree/fs
[3] https://github.com/Rust-for-Linux/linux/issues/1004
[4] https://github.com/ariel-miculas/linux/tree/puzzlefs_dependencies_v2
[5] https://docs.rs/capnp/latest/capnp/
[6] https://docs.rs/hex/0.4.3/hex/
[7] https://hackmd.io/@cyphar/ociv2-brainstorm
[8] https://www.youtube.com/watch?v=tPs1uRqOnlk
[9] https://docs.kernel.org/rust/quick-start.html
[10] https://archive.fosdem.org/2019/schedule/event/containers_atomfs/
[11] https://github.com/ariel-miculas/linux/tree/puzzlefs_rfc_v2

Ariel Miculas (10):
  samples: puzzlefs: add initial puzzlefs sample, copied from rust_fs.rs
  kernel: configs: enable rust samples in rust.config
  rust: kernel: add an abstraction over vfsmount to allow cloning a new
    private mount
  rust: file: Add a new RegularFile newtype useful for reading files
  samples: puzzlefs: add basic deserializing support for the puzzlefs
    metadata
  rust: file: pass the filesystem context to the open function
  samples: puzzlefs: populate the directory entries with the inodes from
    the puzzlefs metadata file
  rust: puzzlefs: read the puzzlefs image manifest instead of an
    individual metadata layer
  rust: puzzlefs: add support for reading files
  rust: puzzlefs: add oci_root_dir and image_manifest filesystem
    parameters

 kernel/configs/rust.config                  |   10 +
 rust/alloc/vec/mod.rs                       |   17 +
 rust/bindings/bindings_helper.h             |    1 +
 rust/helpers.c                              |    6 +
 rust/kernel/error.rs                        |    6 +-
 rust/kernel/file.rs                         |  146 +-
 rust/kernel/fs.rs                           |    4 +-
 rust/kernel/lib.rs                          |    1 +
 rust/kernel/mount.rs                        |   71 +
 samples/rust/Kconfig                        |   10 +
 samples/rust/Makefile                       |    1 +
 samples/rust/puzzle.rs                      |    5 +
 samples/rust/puzzle/error.rs                |  106 +
 samples/rust/puzzle/inode.rs                |  101 +
 samples/rust/puzzle/oci.rs                  |   71 +
 samples/rust/puzzle/types.rs                |  340 ++
 samples/rust/puzzle/types/manifest.capnp    |   15 +
 samples/rust/puzzle/types/manifest_capnp.rs |  773 ++++
 samples/rust/puzzle/types/metadata.capnp    |   69 +
 samples/rust/puzzle/types/metadata_capnp.rs | 4458 +++++++++++++++++++
 samples/rust/puzzlefs.rs                    |  220 +
 samples/rust/rust_fs.rs                     |    3 +-
 22 files changed, 6425 insertions(+), 9 deletions(-)
 create mode 100644 rust/kernel/mount.rs
 create mode 100644 samples/rust/puzzle.rs
 create mode 100644 samples/rust/puzzle/error.rs
 create mode 100644 samples/rust/puzzle/inode.rs
 create mode 100644 samples/rust/puzzle/oci.rs
 create mode 100644 samples/rust/puzzle/types.rs
 create mode 100644 samples/rust/puzzle/types/manifest.capnp
 create mode 100644 samples/rust/puzzle/types/manifest_capnp.rs
 create mode 100644 samples/rust/puzzle/types/metadata.capnp
 create mode 100644 samples/rust/puzzle/types/metadata_capnp.rs
 create mode 100644 samples/rust/puzzlefs.rs

-- 
2.41.0


^ permalink raw reply	[flat|nested] 20+ messages in thread

* [RFC PATCH v2 01/10] samples: puzzlefs: add initial puzzlefs sample, copied from rust_fs.rs
  2023-07-26 16:45 [RFC PATCH v2 00/10] Rust PuzleFS filesystem driver Ariel Miculas
@ 2023-07-26 16:45 ` Ariel Miculas
  2023-07-26 16:45 ` [RFC PATCH v2 02/10] kernel: configs: enable rust samples in rust.config Ariel Miculas
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 20+ messages in thread
From: Ariel Miculas @ 2023-07-26 16:45 UTC (permalink / raw)
  To: rust-for-linux
  Cc: linux-kernel, linux-fsdevel, tycho, brauner, viro, ojeda,
	alex.gaynor, wedsonaf, Ariel Miculas

Signed-off-by: Ariel Miculas <amiculas@cisco.com>
---
 samples/rust/Kconfig     |  10 ++++
 samples/rust/Makefile    |   1 +
 samples/rust/puzzlefs.rs | 105 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 116 insertions(+)
 create mode 100644 samples/rust/puzzlefs.rs

diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig
index 2bd736f99189..05ca21fbba06 100644
--- a/samples/rust/Kconfig
+++ b/samples/rust/Kconfig
@@ -40,6 +40,16 @@ config SAMPLE_RUST_FS
 
 	  If unsure, say N.
 
+config SAMPLE_PUZZLEFS
+	tristate "Puzzlefs file system"
+	help
+	  This option builds the Rust puzzlefs file system sample.
+
+	  To compile this as a module, choose M here:
+	  the module will be called puzzlefs.
+
+	  If unsure, say N.
+
 config SAMPLE_RUST_HOSTPROGS
 	bool "Host programs"
 	help
diff --git a/samples/rust/Makefile b/samples/rust/Makefile
index e5941037e673..364a38dbf90b 100644
--- a/samples/rust/Makefile
+++ b/samples/rust/Makefile
@@ -3,5 +3,6 @@
 obj-$(CONFIG_SAMPLE_RUST_MINIMAL)		+= rust_minimal.o
 obj-$(CONFIG_SAMPLE_RUST_PRINT)			+= rust_print.o
 obj-$(CONFIG_SAMPLE_RUST_FS)			+= rust_fs.o
+obj-$(CONFIG_SAMPLE_PUZZLEFS)			+= puzzlefs.o
 
 subdir-$(CONFIG_SAMPLE_RUST_HOSTPROGS)		+= hostprogs
diff --git a/samples/rust/puzzlefs.rs b/samples/rust/puzzlefs.rs
new file mode 100644
index 000000000000..0cf42762e81a
--- /dev/null
+++ b/samples/rust/puzzlefs.rs
@@ -0,0 +1,105 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Rust file system sample.
+
+use kernel::module_fs;
+use kernel::prelude::*;
+use kernel::{c_str, file, fs, io_buffer::IoBufferWriter};
+
+module_fs! {
+    type: PuzzleFsModule,
+    name: "puzzlefs",
+    author: "Ariel Miculas",
+    license: "GPL",
+}
+
+struct PuzzleFsModule;
+
+#[vtable]
+impl fs::Context<Self> for PuzzleFsModule {
+    type Data = ();
+
+    kernel::define_fs_params! {(),
+        {flag, "flag", |_, v| { pr_info!("flag passed-in: {v}\n"); Ok(()) } },
+        {flag_no, "flagno", |_, v| { pr_info!("flagno passed-in: {v}\n"); Ok(()) } },
+        {bool, "bool", |_, v| { pr_info!("bool passed-in: {v}\n"); Ok(()) } },
+        {u32, "u32", |_, v| { pr_info!("u32 passed-in: {v}\n"); Ok(()) } },
+        {u32oct, "u32oct", |_, v| { pr_info!("u32oct passed-in: {v}\n"); Ok(()) } },
+        {u32hex, "u32hex", |_, v| { pr_info!("u32hex passed-in: {v}\n"); Ok(()) } },
+        {s32, "s32", |_, v| { pr_info!("s32 passed-in: {v}\n"); Ok(()) } },
+        {u64, "u64", |_, v| { pr_info!("u64 passed-in: {v}\n"); Ok(()) } },
+        {string, "string", |_, v| { pr_info!("string passed-in: {v}\n"); Ok(()) } },
+        {enum, "enum", [("first", 10), ("second", 20)], |_, v| {
+            pr_info!("enum passed-in: {v}\n"); Ok(()) }
+        },
+    }
+
+    fn try_new() -> Result {
+        Ok(())
+    }
+}
+
+impl fs::Type for PuzzleFsModule {
+    type Context = Self;
+    type INodeData = &'static [u8];
+    const SUPER_TYPE: fs::Super = fs::Super::Independent;
+    const NAME: &'static CStr = c_str!("puzzlefs");
+    const FLAGS: i32 = fs::flags::USERNS_MOUNT;
+    const DCACHE_BASED: bool = true;
+
+    fn fill_super(_data: (), sb: fs::NewSuperBlock<'_, Self>) -> Result<&fs::SuperBlock<Self>> {
+        let sb = sb.init(
+            (),
+            &fs::SuperParams {
+                magic: 0x72757374,
+                ..fs::SuperParams::DEFAULT
+            },
+        )?;
+        let root = sb.try_new_populated_root_dentry(
+            &[],
+            kernel::fs_entries![
+                file("test1", 0o600, "abc\n".as_bytes(), FsFile),
+                file("test2", 0o600, "def\n".as_bytes(), FsFile),
+                char("test3", 0o600, [].as_slice(), (10, 125)),
+                sock("test4", 0o755, [].as_slice()),
+                fifo("test5", 0o755, [].as_slice()),
+                block("test6", 0o755, [].as_slice(), (1, 1)),
+                dir(
+                    "dir1",
+                    0o755,
+                    [].as_slice(),
+                    [
+                        file("test1", 0o600, "abc\n".as_bytes(), FsFile),
+                        file("test2", 0o600, "def\n".as_bytes(), FsFile),
+                    ]
+                ),
+            ],
+        )?;
+        let sb = sb.init_root(root)?;
+        Ok(sb)
+    }
+}
+
+struct FsFile;
+
+#[vtable]
+impl file::Operations for FsFile {
+    type OpenData = &'static [u8];
+
+    fn open(_context: &Self::OpenData, _file: &file::File) -> Result<Self::Data> {
+        Ok(())
+    }
+
+    fn read(
+        _data: (),
+        file: &file::File,
+        writer: &mut impl IoBufferWriter,
+        offset: u64,
+    ) -> Result<usize> {
+        file::read_from_slice(
+            file.inode::<PuzzleFsModule>().ok_or(EINVAL)?.fs_data(),
+            writer,
+            offset,
+        )
+    }
+}
-- 
2.41.0


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [RFC PATCH v2 02/10] kernel: configs: enable rust samples in rust.config
  2023-07-26 16:45 [RFC PATCH v2 00/10] Rust PuzleFS filesystem driver Ariel Miculas
  2023-07-26 16:45 ` [RFC PATCH v2 01/10] samples: puzzlefs: add initial puzzlefs sample, copied from rust_fs.rs Ariel Miculas
@ 2023-07-26 16:45 ` Ariel Miculas
  2023-07-26 16:45 ` [RFC PATCH v2 03/10] rust: kernel: add an abstraction over vfsmount to allow cloning a new private mount Ariel Miculas
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 20+ messages in thread
From: Ariel Miculas @ 2023-07-26 16:45 UTC (permalink / raw)
  To: rust-for-linux
  Cc: linux-kernel, linux-fsdevel, tycho, brauner, viro, ojeda,
	alex.gaynor, wedsonaf, Ariel Miculas

Signed-off-by: Ariel Miculas <amiculas@cisco.com>
---
 kernel/configs/rust.config | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/kernel/configs/rust.config b/kernel/configs/rust.config
index 38a7c5362c9c..63798ae5f3a5 100644
--- a/kernel/configs/rust.config
+++ b/kernel/configs/rust.config
@@ -1 +1,11 @@
 CONFIG_RUST=y
+
+CONFIG_MODULES=y
+CONFIG_SAMPLES=y
+CONFIG_SAMPLES_RUST=y
+CONFIG_SAMPLE_RUST_MINIMAL=m
+CONFIG_SAMPLE_RUST_PRINT=m
+CONFIG_SAMPLE_RUST_FS=m
+CONFIG_SAMPLE_PUZZLEFS=y
+CONFIG_SAMPLE_RUST_HOSTPROGS=y
+
-- 
2.41.0


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [RFC PATCH v2 03/10] rust: kernel: add an abstraction over vfsmount to allow cloning a new private mount
  2023-07-26 16:45 [RFC PATCH v2 00/10] Rust PuzleFS filesystem driver Ariel Miculas
  2023-07-26 16:45 ` [RFC PATCH v2 01/10] samples: puzzlefs: add initial puzzlefs sample, copied from rust_fs.rs Ariel Miculas
  2023-07-26 16:45 ` [RFC PATCH v2 02/10] kernel: configs: enable rust samples in rust.config Ariel Miculas
@ 2023-07-26 16:45 ` Ariel Miculas
  2023-07-26 22:34   ` Trevor Gross
  2023-07-26 16:45 ` [RFC PATCH v2 04/10] rust: file: Add a new RegularFile newtype useful for reading files Ariel Miculas
                   ` (6 subsequent siblings)
  9 siblings, 1 reply; 20+ messages in thread
From: Ariel Miculas @ 2023-07-26 16:45 UTC (permalink / raw)
  To: rust-for-linux
  Cc: linux-kernel, linux-fsdevel, tycho, brauner, viro, ojeda,
	alex.gaynor, wedsonaf, Ariel Miculas

Signed-off-by: Ariel Miculas <amiculas@cisco.com>
---
 rust/bindings/bindings_helper.h |  1 +
 rust/kernel/lib.rs              |  1 +
 rust/kernel/mount.rs            | 71 +++++++++++++++++++++++++++++++++
 3 files changed, 73 insertions(+)
 create mode 100644 rust/kernel/mount.rs

diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index e5fc3b1d408d..205ef50dfd4c 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -18,6 +18,7 @@
 #include <linux/uio.h>
 #include <linux/uaccess.h>
 #include <linux/delay.h>
+#include <linux/namei.h>
 
 /* `bindgen` gets confused at certain things. */
 const gfp_t BINDINGS_GFP_KERNEL = GFP_KERNEL;
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index 08f67833afcf..7dc07bdb5d55 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -46,6 +46,7 @@
 pub mod ioctl;
 pub mod iov_iter;
 pub mod mm;
+pub mod mount;
 pub mod pages;
 pub mod prelude;
 pub mod print;
diff --git a/rust/kernel/mount.rs b/rust/kernel/mount.rs
new file mode 100644
index 000000000000..d08830b27571
--- /dev/null
+++ b/rust/kernel/mount.rs
@@ -0,0 +1,71 @@
+//! Mount interface
+//!
+//! C headers: [`include/linux/mount.h`](../../../../include/linux/mount.h)
+
+use kernel::bindings;
+use kernel::error::from_err_ptr;
+use kernel::pr_err;
+use kernel::prelude::*;
+use kernel::str::CStr;
+use kernel::types::Opaque;
+
+/// Wraps the kernel's `struct path`.
+#[repr(transparent)]
+pub struct Path(pub(crate) Opaque<bindings::path>);
+
+/// Wraps the kernel's `struct vfsmount`.
+#[repr(transparent)]
+#[derive(Debug)]
+pub struct Vfsmount {
+    vfsmount: *mut bindings::vfsmount,
+}
+
+// SAFETY: No one besides us has the raw pointer, so we can safely transfer Vfsmount to another thread
+unsafe impl Send for Vfsmount {}
+// SAFETY: It's OK to access `Vfsmount` through references from other threads because we're not
+// accessing any properties from the underlying raw pointer
+unsafe impl Sync for Vfsmount {}
+
+impl Vfsmount {
+    /// Create a new private mount clone based on a path name
+    pub fn new_private_mount(path_name: &CStr) -> Result<Self> {
+        let path: Path = Path(Opaque::uninit());
+        // SAFETY: path_name is a &CStr, so it's a valid string pointer; path is an uninitialized
+        // struct stored on the stack and it's ok because kern_path expects an out parameter
+        let err = unsafe {
+            bindings::kern_path(
+                path_name.as_ptr() as *const i8,
+                bindings::LOOKUP_FOLLOW | bindings::LOOKUP_DIRECTORY,
+                path.0.get(),
+            )
+        };
+        if err != 0 {
+            pr_err!("failed to resolve '{}': {}\n", path_name, err);
+            return Err(EINVAL);
+        }
+
+        // SAFETY: path is a struct stored on the stack and it is  initialized because the call to
+        // kern_path succeeded
+        let vfsmount = unsafe { from_err_ptr(bindings::clone_private_mount(path.0.get()))? };
+
+        // Don't inherit atime flags
+        // SAFETY: we called from_err_ptr so it's safe to dereference this pointer
+        unsafe {
+            (*vfsmount).mnt_flags &=
+                !(bindings::MNT_NOATIME | bindings::MNT_NODIRATIME | bindings::MNT_RELATIME) as i32;
+        }
+        Ok(Self { vfsmount })
+    }
+
+    /// Returns a raw pointer to vfsmount
+    pub fn get(&self) -> *mut bindings::vfsmount {
+        self.vfsmount
+    }
+}
+
+impl Drop for Vfsmount {
+    fn drop(&mut self) {
+        // SAFETY new_private_mount makes sure to return a valid pointer
+        unsafe { bindings::kern_unmount(self.vfsmount) };
+    }
+}
-- 
2.41.0


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [RFC PATCH v2 04/10] rust: file: Add a new RegularFile newtype useful for reading files
  2023-07-26 16:45 [RFC PATCH v2 00/10] Rust PuzleFS filesystem driver Ariel Miculas
                   ` (2 preceding siblings ...)
  2023-07-26 16:45 ` [RFC PATCH v2 03/10] rust: kernel: add an abstraction over vfsmount to allow cloning a new private mount Ariel Miculas
@ 2023-07-26 16:45 ` Ariel Miculas
  2023-07-26 23:52   ` Trevor Gross
  2023-07-26 16:45 ` [RFC PATCH v2 05/10] samples: puzzlefs: add basic deserializing support for the puzzlefs metadata Ariel Miculas
                   ` (5 subsequent siblings)
  9 siblings, 1 reply; 20+ messages in thread
From: Ariel Miculas @ 2023-07-26 16:45 UTC (permalink / raw)
  To: rust-for-linux
  Cc: linux-kernel, linux-fsdevel, tycho, brauner, viro, ojeda,
	alex.gaynor, wedsonaf, Ariel Miculas

Implement from_path, from_path_in_root_mnt, read_with_offset,
read_to_end and get_file_size methods for a RegularFile newtype.

Signed-off-by: Ariel Miculas <amiculas@cisco.com>
---
 rust/helpers.c       |   6 ++
 rust/kernel/error.rs |   4 +-
 rust/kernel/file.rs  | 129 ++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 135 insertions(+), 4 deletions(-)

diff --git a/rust/helpers.c b/rust/helpers.c
index eed8ace52fb5..9e860a554cda 100644
--- a/rust/helpers.c
+++ b/rust/helpers.c
@@ -213,6 +213,12 @@ void *rust_helper_alloc_inode_sb(struct super_block *sb,
 }
 EXPORT_SYMBOL_GPL(rust_helper_alloc_inode_sb);
 
+loff_t rust_helper_i_size_read(const struct inode *inode)
+{
+	return i_size_read(inode);
+}
+EXPORT_SYMBOL_GPL(rust_helper_i_size_read);
+
 /*
  * We use `bindgen`'s `--size_t-is-usize` option to bind the C `size_t` type
  * as the Rust `usize` type, so we can use it in contexts where Rust
diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs
index 05fcab6abfe6..e061c83f806a 100644
--- a/rust/kernel/error.rs
+++ b/rust/kernel/error.rs
@@ -273,9 +273,7 @@ pub fn to_result(err: core::ffi::c_int) -> Result {
 ///     }
 /// }
 /// ```
-// TODO: Remove `dead_code` marker once an in-kernel client is available.
-#[allow(dead_code)]
-pub(crate) fn from_err_ptr<T>(ptr: *mut T) -> Result<*mut T> {
+pub fn from_err_ptr<T>(ptr: *mut T) -> Result<*mut T> {
     // CAST: Casting a pointer to `*const core::ffi::c_void` is always valid.
     let const_ptr: *const core::ffi::c_void = ptr.cast();
     // SAFETY: The FFI function does not deref the pointer.
diff --git a/rust/kernel/file.rs b/rust/kernel/file.rs
index 494939ba74df..a3002c416dbb 100644
--- a/rust/kernel/file.rs
+++ b/rust/kernel/file.rs
@@ -8,11 +8,13 @@
 use crate::{
     bindings,
     cred::Credential,
-    error::{code::*, from_result, Error, Result},
+    error::{code::*, from_err_ptr, from_result, Error, Result},
     fs,
     io_buffer::{IoBufferReader, IoBufferWriter},
     iov_iter::IovIter,
     mm,
+    mount::Vfsmount,
+    str::CStr,
     sync::CondVar,
     types::ARef,
     types::AlwaysRefCounted,
@@ -20,6 +22,7 @@
     types::Opaque,
     user_ptr::{UserSlicePtr, UserSlicePtrReader, UserSlicePtrWriter},
 };
+use alloc::vec::Vec;
 use core::convert::{TryFrom, TryInto};
 use core::{marker, mem, ptr};
 use macros::vtable;
@@ -201,6 +204,130 @@ unsafe fn dec_ref(obj: ptr::NonNull<Self>) {
     }
 }
 
+/// A newtype over file, specific to regular files
+pub struct RegularFile(ARef<File>);
+impl RegularFile {
+    /// Creates a new instance of Self if the file is a regular file
+    ///
+    /// # Safety
+    ///
+    /// The caller must ensure file_ptr.f_inode is initialized to a valid pointer (e.g. file_ptr is
+    /// a pointer returned by path_openat); It must also ensure that file_ptr's reference count was
+    /// incremented at least once
+    fn create_if_regular(file_ptr: ptr::NonNull<bindings::file>) -> Result<RegularFile> {
+        // SAFETY: file_ptr is a NonNull pointer
+        let inode = unsafe { core::ptr::addr_of!((*file_ptr.as_ptr()).f_inode).read() };
+        // SAFETY: the caller must ensure f_inode is initialized to a valid pointer
+        unsafe {
+            if bindings::S_IFMT & ((*inode).i_mode) as u32 != bindings::S_IFREG {
+                return Err(EINVAL);
+            }
+        }
+        // SAFETY: the safety requirements state that file_ptr's reference count was incremented at
+        // least once
+        Ok(RegularFile(unsafe { ARef::from_raw(file_ptr.cast()) }))
+    }
+    /// Constructs a new [`struct file`] wrapper from a path.
+    pub fn from_path(filename: &CStr, flags: i32, mode: u16) -> Result<Self> {
+        let file_ptr = unsafe {
+            // SAFETY: filename is a reference, so it's a valid pointer
+            from_err_ptr(bindings::filp_open(
+                filename.as_ptr() as *const i8,
+                flags,
+                mode,
+            ))?
+        };
+        let file_ptr = ptr::NonNull::new(file_ptr).ok_or(ENOENT)?;
+
+        // SAFETY: `filp_open` initializes the refcount with 1
+        Self::create_if_regular(file_ptr)
+    }
+
+    /// Constructs a new [`struct file`] wrapper from a path and a vfsmount.
+    pub fn from_path_in_root_mnt(
+        mount: &Vfsmount,
+        filename: &CStr,
+        flags: i32,
+        mode: u16,
+    ) -> Result<Self> {
+        let file_ptr = {
+            let mnt = mount.get();
+            // construct a path from vfsmount, see file_open_root_mnt
+            let raw_path = bindings::path {
+                mnt,
+                // SAFETY: Vfsmount structure stores a valid vfsmount object
+                dentry: unsafe { (*mnt).mnt_root },
+            };
+            unsafe {
+                // SAFETY: raw_path and filename are both references
+                from_err_ptr(bindings::file_open_root(
+                    &raw_path,
+                    filename.as_ptr() as *const i8,
+                    flags,
+                    mode,
+                ))?
+            }
+        };
+        let file_ptr = ptr::NonNull::new(file_ptr).ok_or(ENOENT)?;
+
+        // SAFETY: `file_open_root` initializes the refcount with 1
+        Self::create_if_regular(file_ptr)
+    }
+
+    /// Read from the file into the specified buffer
+    pub fn read_with_offset(&self, buf: &mut [u8], offset: u64) -> Result<usize> {
+        Ok({
+            // kernel_read_file expects a pointer to a "void *" buffer
+            let mut ptr_to_buf = buf.as_mut_ptr() as *mut core::ffi::c_void;
+            // Unless we give a non-null pointer to the file size:
+            // 1. we cannot give a non-zero value for the offset
+            // 2. we cannot have offset 0 and buffer_size > file_size
+            let mut file_size = 0;
+
+            // SAFETY: 'file' is valid because it's taken from Self, 'buf' and 'file_size` are
+            // references to the stack variables 'ptr_to_buf' and 'file_size'; ptr_to_buf is also
+            // a pointer to a valid buffer that was obtained from a reference
+            let result = unsafe {
+                bindings::kernel_read_file(
+                    self.0 .0.get(),
+                    offset.try_into()?,
+                    &mut ptr_to_buf,
+                    buf.len(),
+                    &mut file_size,
+                    bindings::kernel_read_file_id_READING_UNKNOWN,
+                )
+            };
+
+            // kernel_read_file returns the number of bytes read on success or negative on error.
+            if result < 0 {
+                return Err(Error::from_errno(result.try_into()?));
+            }
+
+            result.try_into()?
+        })
+    }
+
+    /// Allocate and return a vector containing the contents of the entire file
+    pub fn read_to_end(&self) -> Result<Vec<u8>> {
+        let file_size = self.get_file_size()?;
+        let mut buffer = Vec::try_with_capacity(file_size)?;
+        buffer.try_resize(file_size, 0)?;
+        self.read_with_offset(&mut buffer, 0)?;
+        Ok(buffer)
+    }
+
+    fn get_file_size(&self) -> Result<usize> {
+        // SAFETY: 'file' is valid because it's taken from Self
+        let file_size = unsafe { bindings::i_size_read((*self.0 .0.get()).f_inode) };
+
+        if file_size < 0 {
+            return Err(EINVAL);
+        }
+
+        Ok(file_size.try_into()?)
+    }
+}
+
 /// A file descriptor reservation.
 ///
 /// This allows the creation of a file descriptor in two steps: first, we reserve a slot for it,
-- 
2.41.0


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [RFC PATCH v2 05/10] samples: puzzlefs: add basic deserializing support for the puzzlefs metadata
  2023-07-26 16:45 [RFC PATCH v2 00/10] Rust PuzleFS filesystem driver Ariel Miculas
                   ` (3 preceding siblings ...)
  2023-07-26 16:45 ` [RFC PATCH v2 04/10] rust: file: Add a new RegularFile newtype useful for reading files Ariel Miculas
@ 2023-07-26 16:45 ` Ariel Miculas
  2023-07-26 16:45 ` [RFC PATCH v2 06/10] rust: file: pass the filesystem context to the open function Ariel Miculas
                   ` (4 subsequent siblings)
  9 siblings, 0 replies; 20+ messages in thread
From: Ariel Miculas @ 2023-07-26 16:45 UTC (permalink / raw)
  To: rust-for-linux
  Cc: linux-kernel, linux-fsdevel, tycho, brauner, viro, ojeda,
	alex.gaynor, wedsonaf, Ariel Miculas

Puzzlefs uses capnproto for storing the filesystem metadata, defined in
manifest.capnp and metadata.capnp. The files manifest_capnp.rs and
metadata_capnp.rs were generated with (Cap'n Proto version 0.10.4):
```
capnp compile -orust manifest.capnp
capnp compile -orust metadata.capnp
```
, formatted with rustfmt and I have also added the following lines:
```
```
to avoid warnings.

Ideally, manifest_capnp.rs and metadata_capnp.rs should be automatically
generated at build time. However, this means the capnp binary becomes a
dependency for the build and I didn't want this. Besides, the metadata
schemas are not expected to change frequently, so it's acceptable to
manually regenerate the two rust files when this happens.

The code is adapted from the puzzlefs FUSE driver [1]. The data
structures required for the filesystem metadata are defined in types.rs.
Each structure has a `from_capnp` method used to convert between the
capnp memory layout and the native Rust structures.  This leaves room
for improvement [2].

The deserializing code also uses `NoAllocBufferSegments` which is not
yet merged in capnproto-rust upstream [3]. When a new release is
published, then we'll be able to use capnproto-rust in the kernel with
only a few patches to it.

inode.rs implements the PuzzleFS structure which contains a list of
metadata layers.

Link: https://github.com/project-machine/puzzlefs [1]
Link: https://lore.kernel.org/rust-for-linux/ZI3ZnFk2uELYFX0R@moria.home.lan/ [2]
Link: https://github.com/capnproto/capnproto-rust/pull/423 [3]

Signed-off-by: Ariel Miculas <amiculas@cisco.com>
---
 rust/kernel/error.rs                        |    2 +-
 samples/rust/puzzle.rs                      |    4 +
 samples/rust/puzzle/error.rs                |  106 +
 samples/rust/puzzle/inode.rs                |   36 +
 samples/rust/puzzle/types.rs                |  340 ++
 samples/rust/puzzle/types/manifest.capnp    |   15 +
 samples/rust/puzzle/types/manifest_capnp.rs |  773 ++++
 samples/rust/puzzle/types/metadata.capnp    |   69 +
 samples/rust/puzzle/types/metadata_capnp.rs | 4458 +++++++++++++++++++
 samples/rust/puzzlefs.rs                    |    4 +
 10 files changed, 5806 insertions(+), 1 deletion(-)
 create mode 100644 samples/rust/puzzle.rs
 create mode 100644 samples/rust/puzzle/error.rs
 create mode 100644 samples/rust/puzzle/inode.rs
 create mode 100644 samples/rust/puzzle/types.rs
 create mode 100644 samples/rust/puzzle/types/manifest.capnp
 create mode 100644 samples/rust/puzzle/types/manifest_capnp.rs
 create mode 100644 samples/rust/puzzle/types/metadata.capnp
 create mode 100644 samples/rust/puzzle/types/metadata_capnp.rs

diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs
index e061c83f806a..7d25046a61b8 100644
--- a/rust/kernel/error.rs
+++ b/rust/kernel/error.rs
@@ -99,7 +99,7 @@ impl Error {
     ///
     /// It is a bug to pass an out-of-range `errno`. `EINVAL` would
     /// be returned in such a case.
-    pub(crate) fn from_errno(errno: core::ffi::c_int) -> Error {
+    pub fn from_errno(errno: core::ffi::c_int) -> Error {
         if errno < -(bindings::MAX_ERRNO as i32) || errno >= 0 {
             // TODO: Make it a `WARN_ONCE` once available.
             crate::pr_warn!(
diff --git a/samples/rust/puzzle.rs b/samples/rust/puzzle.rs
new file mode 100644
index 000000000000..a809cddfb817
--- /dev/null
+++ b/samples/rust/puzzle.rs
@@ -0,0 +1,4 @@
+pub(crate) mod error;
+pub(crate) mod types;
+pub(crate) use types::{manifest_capnp, metadata_capnp};
+pub(crate) mod inode;
diff --git a/samples/rust/puzzle/error.rs b/samples/rust/puzzle/error.rs
new file mode 100644
index 000000000000..d0a8112776aa
--- /dev/null
+++ b/samples/rust/puzzle/error.rs
@@ -0,0 +1,106 @@
+use alloc::collections::TryReserveError;
+use core::ffi::c_int;
+use core::fmt::{self, Display};
+use kernel::prelude::EINVAL;
+
+pub(crate) enum WireFormatError {
+    InvalidSerializedData,
+    KernelError(kernel::error::Error),
+    TryReserveError(TryReserveError),
+    CapnpError(capnp::Error),
+    FromIntError(core::num::TryFromIntError),
+    FromSliceError(core::array::TryFromSliceError),
+    HexError(hex::FromHexError),
+}
+
+impl WireFormatError {
+    pub(crate) fn to_errno(&self) -> c_int {
+        match self {
+            WireFormatError::InvalidSerializedData => kernel::error::Error::to_errno(EINVAL),
+            WireFormatError::KernelError(e) => kernel::error::Error::to_errno(*e),
+            WireFormatError::TryReserveError(_) => kernel::error::Error::to_errno(EINVAL),
+            WireFormatError::CapnpError(_) => kernel::error::Error::to_errno(EINVAL),
+            WireFormatError::FromIntError(_) => kernel::error::Error::to_errno(EINVAL),
+            WireFormatError::FromSliceError(_) => kernel::error::Error::to_errno(EINVAL),
+            WireFormatError::HexError(_) => kernel::error::Error::to_errno(EINVAL),
+        }
+    }
+
+    pub(crate) fn from_errno(errno: kernel::error::Error) -> Self {
+        Self::KernelError(errno)
+    }
+}
+
+impl Display for WireFormatError {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match self {
+            WireFormatError::InvalidSerializedData => f.write_str("invalid serialized data"),
+            WireFormatError::KernelError(e) => {
+                f.write_fmt(format_args!("Kernel error {}", e.to_errno()))
+            }
+            WireFormatError::TryReserveError(_) => f.write_str("TryReserveError"),
+            WireFormatError::CapnpError(_) => f.write_str("Capnp error"),
+            WireFormatError::FromIntError(_) => f.write_str("TryFromIntError"),
+            WireFormatError::FromSliceError(_) => f.write_str("TryFromSliceError"),
+            WireFormatError::HexError(_) => f.write_str("HexError"),
+        }
+    }
+}
+
+pub(crate) type Result<T> = kernel::error::Result<T, WireFormatError>;
+
+// TODO figure out how to use thiserror
+#[allow(unused_qualifications)]
+impl core::convert::From<kernel::error::Error> for WireFormatError {
+    #[allow(deprecated)]
+    fn from(source: kernel::error::Error) -> Self {
+        WireFormatError::KernelError(source)
+    }
+}
+
+#[allow(unused_qualifications)]
+impl core::convert::From<TryReserveError> for WireFormatError {
+    #[allow(deprecated)]
+    fn from(source: TryReserveError) -> Self {
+        WireFormatError::TryReserveError(source)
+    }
+}
+
+#[allow(unused_qualifications)]
+impl core::convert::From<capnp::Error> for WireFormatError {
+    #[allow(deprecated)]
+    fn from(source: capnp::Error) -> Self {
+        WireFormatError::CapnpError(source)
+    }
+}
+
+#[allow(unused_qualifications)]
+impl core::convert::From<core::array::TryFromSliceError> for WireFormatError {
+    #[allow(deprecated)]
+    fn from(source: core::array::TryFromSliceError) -> Self {
+        WireFormatError::FromSliceError(source)
+    }
+}
+
+#[allow(unused_qualifications)]
+impl core::convert::From<core::num::TryFromIntError> for WireFormatError {
+    #[allow(deprecated)]
+    fn from(source: core::num::TryFromIntError) -> Self {
+        WireFormatError::FromIntError(source)
+    }
+}
+
+impl core::convert::From<hex::FromHexError> for WireFormatError {
+    #[allow(deprecated)]
+    fn from(source: hex::FromHexError) -> Self {
+        WireFormatError::HexError(source)
+    }
+}
+
+#[allow(unused_qualifications)]
+impl core::convert::From<WireFormatError> for kernel::error::Error {
+    #[allow(deprecated)]
+    fn from(source: WireFormatError) -> Self {
+        kernel::error::Error::from_errno(source.to_errno())
+    }
+}
diff --git a/samples/rust/puzzle/inode.rs b/samples/rust/puzzle/inode.rs
new file mode 100644
index 000000000000..d792f661aa00
--- /dev/null
+++ b/samples/rust/puzzle/inode.rs
@@ -0,0 +1,36 @@
+// This contents of this file is taken from puzzlefs.rs (the userspace implementation)
+// It is named inode.rs instead puzzlefs.rs since the root of this kernel module already has that name
+
+use crate::puzzle::error::Result;
+use crate::puzzle::error::WireFormatError;
+use crate::puzzle::types as format;
+use crate::puzzle::types::{Inode, InodeMode, MetadataBlob};
+use alloc::vec::Vec;
+use kernel::prelude::ENOENT;
+
+pub(crate) struct PuzzleFS {
+    layers: Vec<format::MetadataBlob>,
+}
+
+impl PuzzleFS {
+    pub(crate) fn new(md: MetadataBlob) -> Result<Self> {
+        let mut v = Vec::new();
+        v.try_push(md)?;
+        Ok(PuzzleFS { layers: v })
+    }
+
+    pub(crate) fn find_inode(&self, ino: u64) -> Result<Inode> {
+        for layer in self.layers.iter() {
+            if let Some(inode) = layer.find_inode(ino)? {
+                let inode = Inode::from_capnp(inode)?;
+                if let InodeMode::Wht = inode.mode {
+                    // TODO: seems like this should really be an Option.
+                    return Err(WireFormatError::from_errno(ENOENT));
+                }
+                return Ok(inode);
+            }
+        }
+
+        Err(WireFormatError::from_errno(ENOENT))
+    }
+}
diff --git a/samples/rust/puzzle/types.rs b/samples/rust/puzzle/types.rs
new file mode 100644
index 000000000000..559be491bf78
--- /dev/null
+++ b/samples/rust/puzzle/types.rs
@@ -0,0 +1,340 @@
+use crate::puzzle::error::Result;
+use crate::puzzle::error::WireFormatError;
+use alloc::vec::Vec;
+use capnp::{message, serialize};
+use core::fmt;
+use hex::{encode_hex_iter, FromHexError};
+use kernel::file;
+use kernel::str::CStr;
+
+pub(crate) mod manifest_capnp;
+pub(crate) mod metadata_capnp;
+
+pub(crate) const SHA256_BLOCK_SIZE: usize = 32;
+
+#[derive(Debug)]
+pub(crate) struct Rootfs {
+    pub(crate) metadatas: Vec<BlobRef>,
+    #[allow(dead_code)]
+    pub(crate) manifest_version: u64,
+}
+
+impl Rootfs {
+    pub(crate) fn open(file: file::RegularFile) -> Result<Rootfs> {
+        let manifest_buffer = file.read_to_end()?;
+        let message_reader = serialize::read_message_from_flat_slice_no_alloc(
+            &mut &manifest_buffer[..],
+            ::capnp::message::ReaderOptions::new(),
+        )?;
+        let rootfs = message_reader.get_root::<crate::manifest_capnp::rootfs::Reader<'_>>()?;
+        Self::from_capnp(rootfs)
+    }
+
+    pub(crate) fn from_capnp(reader: crate::manifest_capnp::rootfs::Reader<'_>) -> Result<Self> {
+        let metadatas = reader.get_metadatas()?;
+
+        let mut metadata_vec = Vec::new();
+        for blobref in metadatas.iter() {
+            metadata_vec.try_push(BlobRef::from_capnp(blobref)?)?;
+        }
+
+        Ok(Rootfs {
+            metadatas: metadata_vec,
+            manifest_version: reader.get_manifest_version(),
+        })
+    }
+}
+
+// TODO: should this be an ociv1 digest and include size and media type?
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub(crate) struct BlobRef {
+    pub(crate) digest: [u8; SHA256_BLOCK_SIZE],
+    pub(crate) offset: u64,
+    pub(crate) compressed: bool,
+}
+
+impl BlobRef {
+    pub(crate) fn from_capnp(reader: metadata_capnp::blob_ref::Reader<'_>) -> Result<Self> {
+        let digest = reader.get_digest()?;
+        Ok(BlobRef {
+            digest: digest.try_into()?,
+            offset: reader.get_offset(),
+            compressed: reader.get_compressed(),
+        })
+    }
+}
+
+#[derive(Debug, PartialEq, Eq)]
+pub(crate) struct DirEnt {
+    pub(crate) ino: Ino,
+    pub(crate) name: Vec<u8>,
+}
+
+#[derive(Debug, PartialEq, Eq)]
+pub(crate) struct DirList {
+    // TODO: flags instead?
+    pub(crate) look_below: bool,
+    pub(crate) entries: Vec<DirEnt>,
+}
+
+#[derive(Debug, PartialEq, Eq)]
+pub(crate) struct FileChunk {
+    pub(crate) blob: BlobRef,
+    pub(crate) len: u64,
+}
+
+pub(crate) type Ino = u64;
+
+impl FileChunk {
+    pub(crate) fn from_capnp(reader: metadata_capnp::file_chunk::Reader<'_>) -> Result<Self> {
+        let len = reader.get_len();
+        let blob = BlobRef::from_capnp(reader.get_blob()?)?;
+
+        Ok(FileChunk { blob, len })
+    }
+}
+
+#[derive(Debug, PartialEq, Eq)]
+pub(crate) struct Inode {
+    pub(crate) ino: Ino,
+    pub(crate) mode: InodeMode,
+    pub(crate) uid: u32,
+    pub(crate) gid: u32,
+    pub(crate) permissions: u16,
+    pub(crate) additional: Option<InodeAdditional>,
+}
+
+impl Inode {
+    pub(crate) fn from_capnp(reader: metadata_capnp::inode::Reader<'_>) -> Result<Self> {
+        Ok(Inode {
+            ino: reader.get_ino(),
+            mode: InodeMode::from_capnp(reader.get_mode())?,
+            uid: reader.get_uid(),
+            gid: reader.get_gid(),
+            permissions: reader.get_permissions(),
+            additional: InodeAdditional::from_capnp(reader.get_additional()?)?,
+        })
+    }
+}
+
+#[derive(Debug, PartialEq, Eq)]
+pub(crate) enum InodeMode {
+    Unknown,
+    Fifo,
+    Chr { major: u64, minor: u64 },
+    Dir { dir_list: DirList },
+    Blk { major: u64, minor: u64 },
+    File { chunks: Vec<FileChunk> },
+    Lnk,
+    Sock,
+    Wht,
+}
+
+impl InodeMode {
+    fn from_capnp(reader: metadata_capnp::inode::mode::Reader<'_>) -> Result<Self> {
+        match reader.which() {
+            Ok(metadata_capnp::inode::mode::Unknown(())) => Ok(InodeMode::Unknown),
+            Ok(metadata_capnp::inode::mode::Fifo(())) => Ok(InodeMode::Fifo),
+            Ok(metadata_capnp::inode::mode::Lnk(())) => Ok(InodeMode::Lnk),
+            Ok(metadata_capnp::inode::mode::Sock(())) => Ok(InodeMode::Sock),
+            Ok(metadata_capnp::inode::mode::Wht(())) => Ok(InodeMode::Wht),
+            Ok(metadata_capnp::inode::mode::Chr(reader)) => {
+                let r = reader?;
+                Ok(InodeMode::Chr {
+                    major: r.get_major(),
+                    minor: r.get_minor(),
+                })
+            }
+            Ok(metadata_capnp::inode::mode::Blk(reader)) => {
+                let r = reader?;
+                Ok(InodeMode::Blk {
+                    major: r.get_major(),
+                    minor: r.get_minor(),
+                })
+            }
+            Ok(metadata_capnp::inode::mode::File(reader)) => {
+                let r = reader?;
+                let mut chunks = Vec::new();
+                for chunk in r.get_chunks()?.iter() {
+                    chunks.try_push(FileChunk::from_capnp(chunk)?)?;
+                }
+
+                Ok(InodeMode::File { chunks })
+            }
+            Ok(metadata_capnp::inode::mode::Dir(reader)) => {
+                let r = reader?;
+                let mut entries = Vec::new();
+                for entry in r.get_entries()?.iter() {
+                    let ino = entry.get_ino();
+                    let dir_entry = Vec::from_iter_fallible(entry.get_name()?.iter().cloned())?;
+                    entries.try_push(DirEnt {
+                        ino,
+                        name: dir_entry,
+                    })?;
+                }
+                let look_below = r.get_look_below();
+                Ok(InodeMode::Dir {
+                    dir_list: DirList {
+                        look_below,
+                        entries,
+                    },
+                })
+            }
+            Err(::capnp::NotInSchema(_e)) => Err(WireFormatError::InvalidSerializedData),
+        }
+    }
+}
+
+#[derive(Debug, PartialEq, Eq)]
+pub(crate) struct InodeAdditional {
+    pub(crate) xattrs: Vec<Xattr>,
+    pub(crate) symlink_target: Option<Vec<u8>>,
+}
+
+impl InodeAdditional {
+    pub(crate) fn from_capnp(
+        reader: metadata_capnp::inode_additional::Reader<'_>,
+    ) -> Result<Option<Self>> {
+        if !(reader.has_xattrs() || reader.has_symlink_target()) {
+            return Ok(None);
+        }
+
+        let mut xattrs = Vec::new();
+        if reader.has_xattrs() {
+            for capnp_xattr in reader.get_xattrs()? {
+                let xattr = Xattr::from_capnp(capnp_xattr)?;
+                xattrs.try_push(xattr)?;
+            }
+        }
+
+        let symlink_target = if reader.has_symlink_target() {
+            Some(Vec::from_iter_fallible(
+                reader.get_symlink_target()?.iter().cloned(),
+            )?)
+        } else {
+            None
+        };
+
+        Ok(Some(InodeAdditional {
+            xattrs,
+            symlink_target,
+        }))
+    }
+}
+
+#[derive(Debug, PartialEq, Eq)]
+pub(crate) struct Xattr {
+    pub(crate) key: Vec<u8>,
+    pub(crate) val: Vec<u8>,
+}
+
+impl Xattr {
+    pub(crate) fn from_capnp(reader: metadata_capnp::xattr::Reader<'_>) -> Result<Self> {
+        let key = Vec::from_iter_fallible(reader.get_key()?.iter().cloned())?;
+        let val = Vec::from_iter_fallible(reader.get_val()?.iter().cloned())?;
+        Ok(Xattr { key, val })
+    }
+}
+
+pub(crate) struct MetadataBlob {
+    reader: message::TypedReader<
+        ::capnp::serialize::NoAllocBufferSegments<Vec<u8>>,
+        metadata_capnp::inode_vector::Owned,
+    >,
+}
+
+impl MetadataBlob {
+    pub(crate) fn new(f: file::RegularFile) -> Result<MetadataBlob> {
+        // We know the loaded message is safe, so we're allowing unlimited reads.
+        let unlimited_reads = message::ReaderOptions {
+            traversal_limit_in_words: None,
+            nesting_limit: 64,
+        };
+        let metadata_buffer = f.read_to_end()?;
+        let segments =
+            ::capnp::serialize::NoAllocBufferSegments::try_new(metadata_buffer, unlimited_reads)?;
+        let reader = message::Reader::new(segments, unlimited_reads).into_typed();
+
+        Ok(MetadataBlob { reader })
+    }
+
+    pub(crate) fn get_inode_vector(
+        &self,
+    ) -> ::capnp::Result<::capnp::struct_list::Reader<'_, metadata_capnp::inode::Owned>> {
+        self.reader.get()?.get_inodes()
+    }
+
+    pub(crate) fn find_inode(&self, ino: Ino) -> Result<Option<metadata_capnp::inode::Reader<'_>>> {
+        let mut left = 0;
+        let inodes = self.get_inode_vector()?;
+        let mut right = inodes.len() - 1;
+
+        while left <= right {
+            let mid = left + (right - left) / 2;
+            let i = inodes.get(mid);
+
+            if i.get_ino() == ino {
+                return Ok(Some(i));
+            }
+
+            if i.get_ino() < ino {
+                left = mid + 1;
+            } else {
+                // don't underflow...
+                if mid == 0 {
+                    break;
+                }
+                right = mid - 1;
+            };
+        }
+
+        Ok(None)
+    }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub(crate) struct Digest([u8; SHA256_BLOCK_SIZE]);
+
+impl Digest {
+    pub(crate) fn underlying(&self) -> [u8; SHA256_BLOCK_SIZE] {
+        let mut dest = [0_u8; SHA256_BLOCK_SIZE];
+        dest.copy_from_slice(&self.0);
+        dest
+    }
+}
+
+impl fmt::Display for Digest {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        let mut hex_string =
+            Vec::from_iter_fallible(encode_hex_iter(&self.underlying())).map_err(|_| fmt::Error)?;
+        // append NUL character
+        hex_string.try_push(0).map_err(|_| fmt::Error)?;
+        let hex_string = CStr::from_bytes_with_nul(&hex_string).map_err(|_| fmt::Error)?;
+        write!(f, "{}", hex_string)
+    }
+}
+
+impl TryFrom<&CStr> for Digest {
+    type Error = FromHexError;
+    fn try_from(s: &CStr) -> kernel::error::Result<Self, Self::Error> {
+        let digest = hex::decode(s)?;
+        let digest: [u8; SHA256_BLOCK_SIZE] = digest
+            .try_into()
+            .map_err(|_| FromHexError::InvalidStringLength)?;
+        Ok(Digest(digest))
+    }
+}
+
+impl TryFrom<BlobRef> for Digest {
+    type Error = WireFormatError;
+    fn try_from(v: BlobRef) -> kernel::error::Result<Self, Self::Error> {
+        Ok(Digest(v.digest))
+    }
+}
+
+impl TryFrom<&BlobRef> for Digest {
+    type Error = WireFormatError;
+    fn try_from(v: &BlobRef) -> kernel::error::Result<Self, Self::Error> {
+        Ok(Digest(v.digest))
+    }
+}
diff --git a/samples/rust/puzzle/types/manifest.capnp b/samples/rust/puzzle/types/manifest.capnp
new file mode 100644
index 000000000000..24d8a6f1fe19
--- /dev/null
+++ b/samples/rust/puzzle/types/manifest.capnp
@@ -0,0 +1,15 @@
+@0xe0b6d460b1f22bb5;
+
+using Metadata = import "metadata.capnp";
+
+struct VerityData {
+        digest@0: Data;
+        verity@1: Data;
+}
+
+struct Rootfs {
+        metadatas@0: List(Metadata.BlobRef);
+        fsVerityData@1: List(VerityData);
+        manifestVersion@2: UInt64;
+}
+
diff --git a/samples/rust/puzzle/types/manifest_capnp.rs b/samples/rust/puzzle/types/manifest_capnp.rs
new file mode 100644
index 000000000000..9ea37abfadae
--- /dev/null
+++ b/samples/rust/puzzle/types/manifest_capnp.rs
@@ -0,0 +1,773 @@
+// @generated by the capnpc-rust plugin to the Cap'n Proto schema compiler.
+// DO NOT EDIT.
+// source: manifest.capnp
+#![allow(unreachable_pub)]
+#![allow(dead_code)]
+pub mod verity_data {
+    #[derive(Copy, Clone)]
+    pub struct Owned(());
+    impl ::capnp::introspect::Introspect for Owned {
+        fn introspect() -> ::capnp::introspect::Type {
+            ::capnp::introspect::TypeVariant::Struct(::capnp::introspect::RawBrandedStructSchema {
+                generic: &_private::RAW_SCHEMA,
+                field_types: _private::get_field_types,
+                annotation_types: _private::get_annotation_types,
+            })
+            .into()
+        }
+    }
+    impl ::capnp::traits::Owned for Owned {
+        type Reader<'a> = Reader<'a>;
+        type Builder<'a> = Builder<'a>;
+    }
+    impl ::capnp::traits::OwnedStruct for Owned {
+        type Reader<'a> = Reader<'a>;
+        type Builder<'a> = Builder<'a>;
+    }
+    #[cfg(feature = "alloc")]
+    impl ::capnp::traits::Pipelined for Owned {
+        type Pipeline = Pipeline;
+    }
+
+    pub struct Reader<'a> {
+        reader: ::capnp::private::layout::StructReader<'a>,
+    }
+    impl<'a> ::core::marker::Copy for Reader<'a> {}
+    impl<'a> ::core::clone::Clone for Reader<'a> {
+        fn clone(&self) -> Self {
+            *self
+        }
+    }
+
+    impl<'a> ::capnp::traits::HasTypeId for Reader<'a> {
+        const TYPE_ID: u64 = _private::TYPE_ID;
+    }
+    impl<'a> ::core::convert::From<::capnp::private::layout::StructReader<'a>> for Reader<'a> {
+        fn from(reader: ::capnp::private::layout::StructReader<'a>) -> Self {
+            Self { reader }
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    impl<'a> ::core::convert::From<Reader<'a>> for ::capnp::dynamic_value::Reader<'a> {
+        fn from(reader: Reader<'a>) -> Self {
+            Self::Struct(::capnp::dynamic_struct::Reader::new(
+                reader.reader,
+                ::capnp::schema::StructSchema::new(::capnp::introspect::RawBrandedStructSchema {
+                    generic: &_private::RAW_SCHEMA,
+                    field_types: _private::get_field_types,
+                    annotation_types: _private::get_annotation_types,
+                }),
+            ))
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    impl<'a> ::core::fmt::Debug for Reader<'a> {
+        fn fmt(
+            &self,
+            f: &mut ::core::fmt::Formatter<'_>,
+        ) -> ::core::result::Result<(), ::core::fmt::Error> {
+            core::fmt::Debug::fmt(
+                &::core::convert::Into::<::capnp::dynamic_value::Reader<'_>>::into(*self),
+                f,
+            )
+        }
+    }
+
+    impl<'a> ::capnp::traits::FromPointerReader<'a> for Reader<'a> {
+        fn get_from_pointer(
+            reader: &::capnp::private::layout::PointerReader<'a>,
+            default: ::core::option::Option<&'a [::capnp::Word]>,
+        ) -> ::capnp::Result<Self> {
+            ::core::result::Result::Ok(reader.get_struct(default)?.into())
+        }
+    }
+
+    impl<'a> ::capnp::traits::IntoInternalStructReader<'a> for Reader<'a> {
+        fn into_internal_struct_reader(self) -> ::capnp::private::layout::StructReader<'a> {
+            self.reader
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    impl<'a> ::capnp::traits::Imbue<'a> for Reader<'a> {
+        fn imbue(&mut self, cap_table: &'a ::capnp::private::layout::CapTable) {
+            self.reader
+                .imbue(::capnp::private::layout::CapTableReader::Plain(cap_table))
+        }
+    }
+
+    impl<'a> Reader<'a> {
+        pub fn reborrow(&self) -> Reader<'_> {
+            Self { ..*self }
+        }
+
+        pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
+            self.reader.total_size()
+        }
+        #[inline]
+        pub fn get_digest(self) -> ::capnp::Result<::capnp::data::Reader<'a>> {
+            ::capnp::traits::FromPointerReader::get_from_pointer(
+                &self.reader.get_pointer_field(0),
+                ::core::option::Option::None,
+            )
+        }
+        #[inline]
+        pub fn has_digest(&self) -> bool {
+            !self.reader.get_pointer_field(0).is_null()
+        }
+        #[inline]
+        pub fn get_verity(self) -> ::capnp::Result<::capnp::data::Reader<'a>> {
+            ::capnp::traits::FromPointerReader::get_from_pointer(
+                &self.reader.get_pointer_field(1),
+                ::core::option::Option::None,
+            )
+        }
+        #[inline]
+        pub fn has_verity(&self) -> bool {
+            !self.reader.get_pointer_field(1).is_null()
+        }
+    }
+
+    pub struct Builder<'a> {
+        builder: ::capnp::private::layout::StructBuilder<'a>,
+    }
+    impl<'a> ::capnp::traits::HasStructSize for Builder<'a> {
+        const STRUCT_SIZE: ::capnp::private::layout::StructSize =
+            ::capnp::private::layout::StructSize {
+                data: 0,
+                pointers: 2,
+            };
+    }
+    impl<'a> ::capnp::traits::HasTypeId for Builder<'a> {
+        const TYPE_ID: u64 = _private::TYPE_ID;
+    }
+    impl<'a> ::core::convert::From<::capnp::private::layout::StructBuilder<'a>> for Builder<'a> {
+        fn from(builder: ::capnp::private::layout::StructBuilder<'a>) -> Self {
+            Self { builder }
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    impl<'a> ::core::convert::From<Builder<'a>> for ::capnp::dynamic_value::Builder<'a> {
+        fn from(builder: Builder<'a>) -> Self {
+            Self::Struct(::capnp::dynamic_struct::Builder::new(
+                builder.builder,
+                ::capnp::schema::StructSchema::new(::capnp::introspect::RawBrandedStructSchema {
+                    generic: &_private::RAW_SCHEMA,
+                    field_types: _private::get_field_types,
+                    annotation_types: _private::get_annotation_types,
+                }),
+            ))
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    impl<'a> ::capnp::traits::ImbueMut<'a> for Builder<'a> {
+        fn imbue_mut(&mut self, cap_table: &'a mut ::capnp::private::layout::CapTable) {
+            self.builder
+                .imbue(::capnp::private::layout::CapTableBuilder::Plain(cap_table))
+        }
+    }
+
+    impl<'a> ::capnp::traits::FromPointerBuilder<'a> for Builder<'a> {
+        fn init_pointer(builder: ::capnp::private::layout::PointerBuilder<'a>, _size: u32) -> Self {
+            builder
+                .init_struct(<Self as ::capnp::traits::HasStructSize>::STRUCT_SIZE)
+                .into()
+        }
+        fn get_from_pointer(
+            builder: ::capnp::private::layout::PointerBuilder<'a>,
+            default: ::core::option::Option<&'a [::capnp::Word]>,
+        ) -> ::capnp::Result<Self> {
+            ::core::result::Result::Ok(
+                builder
+                    .get_struct(
+                        <Self as ::capnp::traits::HasStructSize>::STRUCT_SIZE,
+                        default,
+                    )?
+                    .into(),
+            )
+        }
+    }
+
+    impl<'a> ::capnp::traits::SetPointerBuilder for Reader<'a> {
+        fn set_pointer_builder(
+            mut pointer: ::capnp::private::layout::PointerBuilder<'_>,
+            value: Self,
+            canonicalize: bool,
+        ) -> ::capnp::Result<()> {
+            pointer.set_struct(&value.reader, canonicalize)
+        }
+    }
+
+    impl<'a> Builder<'a> {
+        pub fn into_reader(self) -> Reader<'a> {
+            self.builder.into_reader().into()
+        }
+        pub fn reborrow(&mut self) -> Builder<'_> {
+            Builder {
+                builder: self.builder.reborrow(),
+            }
+        }
+        pub fn reborrow_as_reader(&self) -> Reader<'_> {
+            self.builder.as_reader().into()
+        }
+
+        pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
+            self.builder.as_reader().total_size()
+        }
+        #[inline]
+        pub fn get_digest(self) -> ::capnp::Result<::capnp::data::Builder<'a>> {
+            ::capnp::traits::FromPointerBuilder::get_from_pointer(
+                self.builder.get_pointer_field(0),
+                ::core::option::Option::None,
+            )
+        }
+        #[inline]
+        pub fn set_digest(&mut self, value: ::capnp::data::Reader<'_>) {
+            self.builder.reborrow().get_pointer_field(0).set_data(value);
+        }
+        #[inline]
+        pub fn init_digest(self, size: u32) -> ::capnp::data::Builder<'a> {
+            self.builder.get_pointer_field(0).init_data(size)
+        }
+        #[inline]
+        pub fn has_digest(&self) -> bool {
+            !self.builder.is_pointer_field_null(0)
+        }
+        #[inline]
+        pub fn get_verity(self) -> ::capnp::Result<::capnp::data::Builder<'a>> {
+            ::capnp::traits::FromPointerBuilder::get_from_pointer(
+                self.builder.get_pointer_field(1),
+                ::core::option::Option::None,
+            )
+        }
+        #[inline]
+        pub fn set_verity(&mut self, value: ::capnp::data::Reader<'_>) {
+            self.builder.reborrow().get_pointer_field(1).set_data(value);
+        }
+        #[inline]
+        pub fn init_verity(self, size: u32) -> ::capnp::data::Builder<'a> {
+            self.builder.get_pointer_field(1).init_data(size)
+        }
+        #[inline]
+        pub fn has_verity(&self) -> bool {
+            !self.builder.is_pointer_field_null(1)
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    pub struct Pipeline {
+        _typeless: ::capnp::any_pointer::Pipeline,
+    }
+    #[cfg(feature = "alloc")]
+    impl ::capnp::capability::FromTypelessPipeline for Pipeline {
+        fn new(typeless: ::capnp::any_pointer::Pipeline) -> Self {
+            Self {
+                _typeless: typeless,
+            }
+        }
+    }
+    #[cfg(feature = "alloc")]
+    impl Pipeline {}
+    mod _private {
+        pub static ENCODED_NODE: [::capnp::Word; 48] = [
+            ::capnp::word(0, 0, 0, 0, 5, 0, 6, 0),
+            ::capnp::word(255, 223, 52, 143, 102, 148, 140, 205),
+            ::capnp::word(15, 0, 0, 0, 1, 0, 0, 0),
+            ::capnp::word(181, 43, 242, 177, 96, 212, 182, 224),
+            ::capnp::word(2, 0, 7, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(21, 0, 0, 0, 210, 0, 0, 0),
+            ::capnp::word(33, 0, 0, 0, 7, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(29, 0, 0, 0, 119, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(109, 97, 110, 105, 102, 101, 115, 116),
+            ::capnp::word(46, 99, 97, 112, 110, 112, 58, 86),
+            ::capnp::word(101, 114, 105, 116, 121, 68, 97, 116),
+            ::capnp::word(97, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 1, 0, 1, 0),
+            ::capnp::word(8, 0, 0, 0, 3, 0, 4, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 1, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(41, 0, 0, 0, 58, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(36, 0, 0, 0, 3, 0, 1, 0),
+            ::capnp::word(48, 0, 0, 0, 2, 0, 1, 0),
+            ::capnp::word(1, 0, 0, 0, 1, 0, 0, 0),
+            ::capnp::word(0, 0, 1, 0, 1, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(45, 0, 0, 0, 58, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(40, 0, 0, 0, 3, 0, 1, 0),
+            ::capnp::word(52, 0, 0, 0, 2, 0, 1, 0),
+            ::capnp::word(100, 105, 103, 101, 115, 116, 0, 0),
+            ::capnp::word(13, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(13, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(118, 101, 114, 105, 116, 121, 0, 0),
+            ::capnp::word(13, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(13, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+        ];
+        pub fn get_field_types(index: u16) -> ::capnp::introspect::Type {
+            match index {
+                0 => <::capnp::data::Owned as ::capnp::introspect::Introspect>::introspect(),
+                1 => <::capnp::data::Owned as ::capnp::introspect::Introspect>::introspect(),
+                _ => panic!("invalid field index {}", index),
+            }
+        }
+        pub fn get_annotation_types(
+            child_index: Option<u16>,
+            index: u32,
+        ) -> ::capnp::introspect::Type {
+            panic!("invalid annotation indices ({:?}, {}) ", child_index, index)
+        }
+        pub static RAW_SCHEMA: ::capnp::introspect::RawStructSchema =
+            ::capnp::introspect::RawStructSchema {
+                encoded_node: &ENCODED_NODE,
+                nonunion_members: NONUNION_MEMBERS,
+                members_by_discriminant: MEMBERS_BY_DISCRIMINANT,
+            };
+        pub static NONUNION_MEMBERS: &[u16] = &[0, 1];
+        pub static MEMBERS_BY_DISCRIMINANT: &[u16] = &[];
+        pub const TYPE_ID: u64 = 0xcd8c_9466_8f34_dfff;
+    }
+}
+
+pub mod rootfs {
+    #[derive(Copy, Clone)]
+    pub struct Owned(());
+    impl ::capnp::introspect::Introspect for Owned {
+        fn introspect() -> ::capnp::introspect::Type {
+            ::capnp::introspect::TypeVariant::Struct(::capnp::introspect::RawBrandedStructSchema {
+                generic: &_private::RAW_SCHEMA,
+                field_types: _private::get_field_types,
+                annotation_types: _private::get_annotation_types,
+            })
+            .into()
+        }
+    }
+    impl ::capnp::traits::Owned for Owned {
+        type Reader<'a> = Reader<'a>;
+        type Builder<'a> = Builder<'a>;
+    }
+    impl ::capnp::traits::OwnedStruct for Owned {
+        type Reader<'a> = Reader<'a>;
+        type Builder<'a> = Builder<'a>;
+    }
+    #[cfg(feature = "alloc")]
+    impl ::capnp::traits::Pipelined for Owned {
+        type Pipeline = Pipeline;
+    }
+
+    pub struct Reader<'a> {
+        reader: ::capnp::private::layout::StructReader<'a>,
+    }
+    impl<'a> ::core::marker::Copy for Reader<'a> {}
+    impl<'a> ::core::clone::Clone for Reader<'a> {
+        fn clone(&self) -> Self {
+            *self
+        }
+    }
+
+    impl<'a> ::capnp::traits::HasTypeId for Reader<'a> {
+        const TYPE_ID: u64 = _private::TYPE_ID;
+    }
+    impl<'a> ::core::convert::From<::capnp::private::layout::StructReader<'a>> for Reader<'a> {
+        fn from(reader: ::capnp::private::layout::StructReader<'a>) -> Self {
+            Self { reader }
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    impl<'a> ::core::convert::From<Reader<'a>> for ::capnp::dynamic_value::Reader<'a> {
+        fn from(reader: Reader<'a>) -> Self {
+            Self::Struct(::capnp::dynamic_struct::Reader::new(
+                reader.reader,
+                ::capnp::schema::StructSchema::new(::capnp::introspect::RawBrandedStructSchema {
+                    generic: &_private::RAW_SCHEMA,
+                    field_types: _private::get_field_types,
+                    annotation_types: _private::get_annotation_types,
+                }),
+            ))
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    impl<'a> ::core::fmt::Debug for Reader<'a> {
+        fn fmt(
+            &self,
+            f: &mut ::core::fmt::Formatter<'_>,
+        ) -> ::core::result::Result<(), ::core::fmt::Error> {
+            core::fmt::Debug::fmt(
+                &::core::convert::Into::<::capnp::dynamic_value::Reader<'_>>::into(*self),
+                f,
+            )
+        }
+    }
+
+    impl<'a> ::capnp::traits::FromPointerReader<'a> for Reader<'a> {
+        fn get_from_pointer(
+            reader: &::capnp::private::layout::PointerReader<'a>,
+            default: ::core::option::Option<&'a [::capnp::Word]>,
+        ) -> ::capnp::Result<Self> {
+            ::core::result::Result::Ok(reader.get_struct(default)?.into())
+        }
+    }
+
+    impl<'a> ::capnp::traits::IntoInternalStructReader<'a> for Reader<'a> {
+        fn into_internal_struct_reader(self) -> ::capnp::private::layout::StructReader<'a> {
+            self.reader
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    impl<'a> ::capnp::traits::Imbue<'a> for Reader<'a> {
+        fn imbue(&mut self, cap_table: &'a ::capnp::private::layout::CapTable) {
+            self.reader
+                .imbue(::capnp::private::layout::CapTableReader::Plain(cap_table))
+        }
+    }
+
+    impl<'a> Reader<'a> {
+        pub fn reborrow(&self) -> Reader<'_> {
+            Self { ..*self }
+        }
+
+        pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
+            self.reader.total_size()
+        }
+        #[inline]
+        pub fn get_metadatas(
+            self,
+        ) -> ::capnp::Result<::capnp::struct_list::Reader<'a, crate::metadata_capnp::blob_ref::Owned>>
+        {
+            ::capnp::traits::FromPointerReader::get_from_pointer(
+                &self.reader.get_pointer_field(0),
+                ::core::option::Option::None,
+            )
+        }
+        #[inline]
+        pub fn has_metadatas(&self) -> bool {
+            !self.reader.get_pointer_field(0).is_null()
+        }
+        #[inline]
+        pub fn get_fs_verity_data(
+            self,
+        ) -> ::capnp::Result<
+            ::capnp::struct_list::Reader<'a, crate::manifest_capnp::verity_data::Owned>,
+        > {
+            ::capnp::traits::FromPointerReader::get_from_pointer(
+                &self.reader.get_pointer_field(1),
+                ::core::option::Option::None,
+            )
+        }
+        #[inline]
+        pub fn has_fs_verity_data(&self) -> bool {
+            !self.reader.get_pointer_field(1).is_null()
+        }
+        #[inline]
+        pub fn get_manifest_version(self) -> u64 {
+            self.reader.get_data_field::<u64>(0)
+        }
+    }
+
+    pub struct Builder<'a> {
+        builder: ::capnp::private::layout::StructBuilder<'a>,
+    }
+    impl<'a> ::capnp::traits::HasStructSize for Builder<'a> {
+        const STRUCT_SIZE: ::capnp::private::layout::StructSize =
+            ::capnp::private::layout::StructSize {
+                data: 1,
+                pointers: 2,
+            };
+    }
+    impl<'a> ::capnp::traits::HasTypeId for Builder<'a> {
+        const TYPE_ID: u64 = _private::TYPE_ID;
+    }
+    impl<'a> ::core::convert::From<::capnp::private::layout::StructBuilder<'a>> for Builder<'a> {
+        fn from(builder: ::capnp::private::layout::StructBuilder<'a>) -> Self {
+            Self { builder }
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    impl<'a> ::core::convert::From<Builder<'a>> for ::capnp::dynamic_value::Builder<'a> {
+        fn from(builder: Builder<'a>) -> Self {
+            Self::Struct(::capnp::dynamic_struct::Builder::new(
+                builder.builder,
+                ::capnp::schema::StructSchema::new(::capnp::introspect::RawBrandedStructSchema {
+                    generic: &_private::RAW_SCHEMA,
+                    field_types: _private::get_field_types,
+                    annotation_types: _private::get_annotation_types,
+                }),
+            ))
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    impl<'a> ::capnp::traits::ImbueMut<'a> for Builder<'a> {
+        fn imbue_mut(&mut self, cap_table: &'a mut ::capnp::private::layout::CapTable) {
+            self.builder
+                .imbue(::capnp::private::layout::CapTableBuilder::Plain(cap_table))
+        }
+    }
+
+    impl<'a> ::capnp::traits::FromPointerBuilder<'a> for Builder<'a> {
+        fn init_pointer(builder: ::capnp::private::layout::PointerBuilder<'a>, _size: u32) -> Self {
+            builder
+                .init_struct(<Self as ::capnp::traits::HasStructSize>::STRUCT_SIZE)
+                .into()
+        }
+        fn get_from_pointer(
+            builder: ::capnp::private::layout::PointerBuilder<'a>,
+            default: ::core::option::Option<&'a [::capnp::Word]>,
+        ) -> ::capnp::Result<Self> {
+            ::core::result::Result::Ok(
+                builder
+                    .get_struct(
+                        <Self as ::capnp::traits::HasStructSize>::STRUCT_SIZE,
+                        default,
+                    )?
+                    .into(),
+            )
+        }
+    }
+
+    impl<'a> ::capnp::traits::SetPointerBuilder for Reader<'a> {
+        fn set_pointer_builder(
+            mut pointer: ::capnp::private::layout::PointerBuilder<'_>,
+            value: Self,
+            canonicalize: bool,
+        ) -> ::capnp::Result<()> {
+            pointer.set_struct(&value.reader, canonicalize)
+        }
+    }
+
+    impl<'a> Builder<'a> {
+        pub fn into_reader(self) -> Reader<'a> {
+            self.builder.into_reader().into()
+        }
+        pub fn reborrow(&mut self) -> Builder<'_> {
+            Builder {
+                builder: self.builder.reborrow(),
+            }
+        }
+        pub fn reborrow_as_reader(&self) -> Reader<'_> {
+            self.builder.as_reader().into()
+        }
+
+        pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
+            self.builder.as_reader().total_size()
+        }
+        #[inline]
+        pub fn get_metadatas(
+            self,
+        ) -> ::capnp::Result<
+            ::capnp::struct_list::Builder<'a, crate::metadata_capnp::blob_ref::Owned>,
+        > {
+            ::capnp::traits::FromPointerBuilder::get_from_pointer(
+                self.builder.get_pointer_field(0),
+                ::core::option::Option::None,
+            )
+        }
+        #[inline]
+        pub fn set_metadatas(
+            &mut self,
+            value: ::capnp::struct_list::Reader<'a, crate::metadata_capnp::blob_ref::Owned>,
+        ) -> ::capnp::Result<()> {
+            ::capnp::traits::SetPointerBuilder::set_pointer_builder(
+                self.builder.reborrow().get_pointer_field(0),
+                value,
+                false,
+            )
+        }
+        #[inline]
+        pub fn init_metadatas(
+            self,
+            size: u32,
+        ) -> ::capnp::struct_list::Builder<'a, crate::metadata_capnp::blob_ref::Owned> {
+            ::capnp::traits::FromPointerBuilder::init_pointer(
+                self.builder.get_pointer_field(0),
+                size,
+            )
+        }
+        #[inline]
+        pub fn has_metadatas(&self) -> bool {
+            !self.builder.is_pointer_field_null(0)
+        }
+        #[inline]
+        pub fn get_fs_verity_data(
+            self,
+        ) -> ::capnp::Result<
+            ::capnp::struct_list::Builder<'a, crate::manifest_capnp::verity_data::Owned>,
+        > {
+            ::capnp::traits::FromPointerBuilder::get_from_pointer(
+                self.builder.get_pointer_field(1),
+                ::core::option::Option::None,
+            )
+        }
+        #[inline]
+        pub fn set_fs_verity_data(
+            &mut self,
+            value: ::capnp::struct_list::Reader<'a, crate::manifest_capnp::verity_data::Owned>,
+        ) -> ::capnp::Result<()> {
+            ::capnp::traits::SetPointerBuilder::set_pointer_builder(
+                self.builder.reborrow().get_pointer_field(1),
+                value,
+                false,
+            )
+        }
+        #[inline]
+        pub fn init_fs_verity_data(
+            self,
+            size: u32,
+        ) -> ::capnp::struct_list::Builder<'a, crate::manifest_capnp::verity_data::Owned> {
+            ::capnp::traits::FromPointerBuilder::init_pointer(
+                self.builder.get_pointer_field(1),
+                size,
+            )
+        }
+        #[inline]
+        pub fn has_fs_verity_data(&self) -> bool {
+            !self.builder.is_pointer_field_null(1)
+        }
+        #[inline]
+        pub fn get_manifest_version(self) -> u64 {
+            self.builder.get_data_field::<u64>(0)
+        }
+        #[inline]
+        pub fn set_manifest_version(&mut self, value: u64) {
+            self.builder.set_data_field::<u64>(0, value);
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    pub struct Pipeline {
+        _typeless: ::capnp::any_pointer::Pipeline,
+    }
+    #[cfg(feature = "alloc")]
+    impl ::capnp::capability::FromTypelessPipeline for Pipeline {
+        fn new(typeless: ::capnp::any_pointer::Pipeline) -> Self {
+            Self {
+                _typeless: typeless,
+            }
+        }
+    }
+    #[cfg(feature = "alloc")]
+    impl Pipeline {}
+    mod _private {
+        pub static ENCODED_NODE: [::capnp::Word; 73] = [
+            ::capnp::word(0, 0, 0, 0, 5, 0, 6, 0),
+            ::capnp::word(216, 230, 154, 208, 49, 166, 59, 237),
+            ::capnp::word(15, 0, 0, 0, 1, 0, 1, 0),
+            ::capnp::word(181, 43, 242, 177, 96, 212, 182, 224),
+            ::capnp::word(2, 0, 7, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(21, 0, 0, 0, 178, 0, 0, 0),
+            ::capnp::word(29, 0, 0, 0, 7, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(25, 0, 0, 0, 175, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(109, 97, 110, 105, 102, 101, 115, 116),
+            ::capnp::word(46, 99, 97, 112, 110, 112, 58, 82),
+            ::capnp::word(111, 111, 116, 102, 115, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 1, 0, 1, 0),
+            ::capnp::word(12, 0, 0, 0, 3, 0, 4, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 1, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(69, 0, 0, 0, 82, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(68, 0, 0, 0, 3, 0, 1, 0),
+            ::capnp::word(96, 0, 0, 0, 2, 0, 1, 0),
+            ::capnp::word(1, 0, 0, 0, 1, 0, 0, 0),
+            ::capnp::word(0, 0, 1, 0, 1, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(93, 0, 0, 0, 106, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(92, 0, 0, 0, 3, 0, 1, 0),
+            ::capnp::word(120, 0, 0, 0, 2, 0, 1, 0),
+            ::capnp::word(2, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 1, 0, 2, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(117, 0, 0, 0, 130, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(116, 0, 0, 0, 3, 0, 1, 0),
+            ::capnp::word(128, 0, 0, 0, 2, 0, 1, 0),
+            ::capnp::word(109, 101, 116, 97, 100, 97, 116, 97),
+            ::capnp::word(115, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(14, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 3, 0, 1, 0),
+            ::capnp::word(16, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(70, 64, 38, 97, 160, 43, 222, 175),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(14, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(102, 115, 86, 101, 114, 105, 116, 121),
+            ::capnp::word(68, 97, 116, 97, 0, 0, 0, 0),
+            ::capnp::word(14, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 3, 0, 1, 0),
+            ::capnp::word(16, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(255, 223, 52, 143, 102, 148, 140, 205),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(14, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(109, 97, 110, 105, 102, 101, 115, 116),
+            ::capnp::word(86, 101, 114, 115, 105, 111, 110, 0),
+            ::capnp::word(9, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(9, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+        ];
+        pub fn get_field_types(index: u16) -> ::capnp::introspect::Type {
+            match index {
+        0 => <::capnp::struct_list::Owned<crate::metadata_capnp::blob_ref::Owned> as ::capnp::introspect::Introspect>::introspect(),
+        1 => <::capnp::struct_list::Owned<crate::manifest_capnp::verity_data::Owned> as ::capnp::introspect::Introspect>::introspect(),
+        2 => <u64 as ::capnp::introspect::Introspect>::introspect(),
+        _ => panic!("invalid field index {}", index),
+      }
+        }
+        pub fn get_annotation_types(
+            child_index: Option<u16>,
+            index: u32,
+        ) -> ::capnp::introspect::Type {
+            panic!("invalid annotation indices ({:?}, {}) ", child_index, index)
+        }
+        pub static RAW_SCHEMA: ::capnp::introspect::RawStructSchema =
+            ::capnp::introspect::RawStructSchema {
+                encoded_node: &ENCODED_NODE,
+                nonunion_members: NONUNION_MEMBERS,
+                members_by_discriminant: MEMBERS_BY_DISCRIMINANT,
+            };
+        pub static NONUNION_MEMBERS: &[u16] = &[0, 1, 2];
+        pub static MEMBERS_BY_DISCRIMINANT: &[u16] = &[];
+        pub const TYPE_ID: u64 = 0xed3b_a631_d09a_e6d8;
+    }
+}
diff --git a/samples/rust/puzzle/types/metadata.capnp b/samples/rust/puzzle/types/metadata.capnp
new file mode 100644
index 000000000000..a37bae703c72
--- /dev/null
+++ b/samples/rust/puzzle/types/metadata.capnp
@@ -0,0 +1,69 @@
+@0x84ae5e6e88b7cbb7;
+
+struct Chr {
+    major@0: UInt64;
+    minor@1: UInt64;
+}
+
+struct DirEntry {
+    ino@0: UInt64;
+    name@1: Data;
+}
+
+struct Dir {
+    entries@0: List(DirEntry);
+    lookBelow@1: Bool;
+}
+
+struct Blk {
+    major@0: UInt64;
+    minor@1: UInt64;
+}
+
+struct FileChunk {
+    blob@0: BlobRef;
+    len@1: UInt64;
+}
+
+struct File {
+    chunks@0: List(FileChunk);
+}
+
+struct BlobRef {
+    digest@0: Data;
+    offset@1: UInt64;
+    compressed@2: Bool;
+}
+
+struct Xattr {
+    key@0: Data;
+    val@1: Data;
+}
+
+struct InodeAdditional {
+    xattrs@0: List(Xattr);
+    symlinkTarget@1: Data;
+}
+
+struct Inode {
+    ino@0: UInt64;
+    mode: union {
+          unknown@1: Void;
+          fifo@2: Void;
+          chr@3: Chr;
+          dir@4: Dir;
+          blk@5: Blk;
+          file@6: File;
+          lnk@7: Void;
+          sock@8: Void;
+          wht@9: Void;
+      }
+    uid@10: UInt32;
+    gid@11: UInt32;
+    permissions@12: UInt16;
+    additional@13: InodeAdditional;
+}
+
+struct InodeVector {
+    inodes@0: List(Inode);
+}
diff --git a/samples/rust/puzzle/types/metadata_capnp.rs b/samples/rust/puzzle/types/metadata_capnp.rs
new file mode 100644
index 000000000000..a56254cbafd8
--- /dev/null
+++ b/samples/rust/puzzle/types/metadata_capnp.rs
@@ -0,0 +1,4458 @@
+// @generated by the capnpc-rust plugin to the Cap'n Proto schema compiler.
+// DO NOT EDIT.
+// source: metadata.capnp
+#![allow(unreachable_pub)]
+#![allow(dead_code)]
+pub mod chr {
+    #[derive(Copy, Clone)]
+    pub struct Owned(());
+    impl ::capnp::introspect::Introspect for Owned {
+        fn introspect() -> ::capnp::introspect::Type {
+            ::capnp::introspect::TypeVariant::Struct(::capnp::introspect::RawBrandedStructSchema {
+                generic: &_private::RAW_SCHEMA,
+                field_types: _private::get_field_types,
+                annotation_types: _private::get_annotation_types,
+            })
+            .into()
+        }
+    }
+    impl ::capnp::traits::Owned for Owned {
+        type Reader<'a> = Reader<'a>;
+        type Builder<'a> = Builder<'a>;
+    }
+    impl ::capnp::traits::OwnedStruct for Owned {
+        type Reader<'a> = Reader<'a>;
+        type Builder<'a> = Builder<'a>;
+    }
+    #[cfg(feature = "alloc")]
+    impl ::capnp::traits::Pipelined for Owned {
+        type Pipeline = Pipeline;
+    }
+
+    pub struct Reader<'a> {
+        reader: ::capnp::private::layout::StructReader<'a>,
+    }
+    impl<'a> ::core::marker::Copy for Reader<'a> {}
+    impl<'a> ::core::clone::Clone for Reader<'a> {
+        fn clone(&self) -> Self {
+            *self
+        }
+    }
+
+    impl<'a> ::capnp::traits::HasTypeId for Reader<'a> {
+        const TYPE_ID: u64 = _private::TYPE_ID;
+    }
+    impl<'a> ::core::convert::From<::capnp::private::layout::StructReader<'a>> for Reader<'a> {
+        fn from(reader: ::capnp::private::layout::StructReader<'a>) -> Self {
+            Self { reader }
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    impl<'a> ::core::convert::From<Reader<'a>> for ::capnp::dynamic_value::Reader<'a> {
+        fn from(reader: Reader<'a>) -> Self {
+            Self::Struct(::capnp::dynamic_struct::Reader::new(
+                reader.reader,
+                ::capnp::schema::StructSchema::new(::capnp::introspect::RawBrandedStructSchema {
+                    generic: &_private::RAW_SCHEMA,
+                    field_types: _private::get_field_types,
+                    annotation_types: _private::get_annotation_types,
+                }),
+            ))
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    impl<'a> ::core::fmt::Debug for Reader<'a> {
+        fn fmt(
+            &self,
+            f: &mut ::core::fmt::Formatter<'_>,
+        ) -> ::core::result::Result<(), ::core::fmt::Error> {
+            core::fmt::Debug::fmt(
+                &::core::convert::Into::<::capnp::dynamic_value::Reader<'_>>::into(*self),
+                f,
+            )
+        }
+    }
+
+    impl<'a> ::capnp::traits::FromPointerReader<'a> for Reader<'a> {
+        fn get_from_pointer(
+            reader: &::capnp::private::layout::PointerReader<'a>,
+            default: ::core::option::Option<&'a [::capnp::Word]>,
+        ) -> ::capnp::Result<Self> {
+            ::core::result::Result::Ok(reader.get_struct(default)?.into())
+        }
+    }
+
+    impl<'a> ::capnp::traits::IntoInternalStructReader<'a> for Reader<'a> {
+        fn into_internal_struct_reader(self) -> ::capnp::private::layout::StructReader<'a> {
+            self.reader
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    impl<'a> ::capnp::traits::Imbue<'a> for Reader<'a> {
+        fn imbue(&mut self, cap_table: &'a ::capnp::private::layout::CapTable) {
+            self.reader
+                .imbue(::capnp::private::layout::CapTableReader::Plain(cap_table))
+        }
+    }
+
+    impl<'a> Reader<'a> {
+        pub fn reborrow(&self) -> Reader<'_> {
+            Self { ..*self }
+        }
+
+        pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
+            self.reader.total_size()
+        }
+        #[inline]
+        pub fn get_major(self) -> u64 {
+            self.reader.get_data_field::<u64>(0)
+        }
+        #[inline]
+        pub fn get_minor(self) -> u64 {
+            self.reader.get_data_field::<u64>(1)
+        }
+    }
+
+    pub struct Builder<'a> {
+        builder: ::capnp::private::layout::StructBuilder<'a>,
+    }
+    impl<'a> ::capnp::traits::HasStructSize for Builder<'a> {
+        const STRUCT_SIZE: ::capnp::private::layout::StructSize =
+            ::capnp::private::layout::StructSize {
+                data: 2,
+                pointers: 0,
+            };
+    }
+    impl<'a> ::capnp::traits::HasTypeId for Builder<'a> {
+        const TYPE_ID: u64 = _private::TYPE_ID;
+    }
+    impl<'a> ::core::convert::From<::capnp::private::layout::StructBuilder<'a>> for Builder<'a> {
+        fn from(builder: ::capnp::private::layout::StructBuilder<'a>) -> Self {
+            Self { builder }
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    impl<'a> ::core::convert::From<Builder<'a>> for ::capnp::dynamic_value::Builder<'a> {
+        fn from(builder: Builder<'a>) -> Self {
+            Self::Struct(::capnp::dynamic_struct::Builder::new(
+                builder.builder,
+                ::capnp::schema::StructSchema::new(::capnp::introspect::RawBrandedStructSchema {
+                    generic: &_private::RAW_SCHEMA,
+                    field_types: _private::get_field_types,
+                    annotation_types: _private::get_annotation_types,
+                }),
+            ))
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    impl<'a> ::capnp::traits::ImbueMut<'a> for Builder<'a> {
+        fn imbue_mut(&mut self, cap_table: &'a mut ::capnp::private::layout::CapTable) {
+            self.builder
+                .imbue(::capnp::private::layout::CapTableBuilder::Plain(cap_table))
+        }
+    }
+
+    impl<'a> ::capnp::traits::FromPointerBuilder<'a> for Builder<'a> {
+        fn init_pointer(builder: ::capnp::private::layout::PointerBuilder<'a>, _size: u32) -> Self {
+            builder
+                .init_struct(<Self as ::capnp::traits::HasStructSize>::STRUCT_SIZE)
+                .into()
+        }
+        fn get_from_pointer(
+            builder: ::capnp::private::layout::PointerBuilder<'a>,
+            default: ::core::option::Option<&'a [::capnp::Word]>,
+        ) -> ::capnp::Result<Self> {
+            ::core::result::Result::Ok(
+                builder
+                    .get_struct(
+                        <Self as ::capnp::traits::HasStructSize>::STRUCT_SIZE,
+                        default,
+                    )?
+                    .into(),
+            )
+        }
+    }
+
+    impl<'a> ::capnp::traits::SetPointerBuilder for Reader<'a> {
+        fn set_pointer_builder(
+            mut pointer: ::capnp::private::layout::PointerBuilder<'_>,
+            value: Self,
+            canonicalize: bool,
+        ) -> ::capnp::Result<()> {
+            pointer.set_struct(&value.reader, canonicalize)
+        }
+    }
+
+    impl<'a> Builder<'a> {
+        pub fn into_reader(self) -> Reader<'a> {
+            self.builder.into_reader().into()
+        }
+        pub fn reborrow(&mut self) -> Builder<'_> {
+            Builder {
+                builder: self.builder.reborrow(),
+            }
+        }
+        pub fn reborrow_as_reader(&self) -> Reader<'_> {
+            self.builder.as_reader().into()
+        }
+
+        pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
+            self.builder.as_reader().total_size()
+        }
+        #[inline]
+        pub fn get_major(self) -> u64 {
+            self.builder.get_data_field::<u64>(0)
+        }
+        #[inline]
+        pub fn set_major(&mut self, value: u64) {
+            self.builder.set_data_field::<u64>(0, value);
+        }
+        #[inline]
+        pub fn get_minor(self) -> u64 {
+            self.builder.get_data_field::<u64>(1)
+        }
+        #[inline]
+        pub fn set_minor(&mut self, value: u64) {
+            self.builder.set_data_field::<u64>(1, value);
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    pub struct Pipeline {
+        _typeless: ::capnp::any_pointer::Pipeline,
+    }
+    #[cfg(feature = "alloc")]
+    impl ::capnp::capability::FromTypelessPipeline for Pipeline {
+        fn new(typeless: ::capnp::any_pointer::Pipeline) -> Self {
+            Self {
+                _typeless: typeless,
+            }
+        }
+    }
+    #[cfg(feature = "alloc")]
+    impl Pipeline {}
+    mod _private {
+        pub static ENCODED_NODE: [::capnp::Word; 47] = [
+            ::capnp::word(0, 0, 0, 0, 5, 0, 6, 0),
+            ::capnp::word(112, 90, 179, 204, 206, 228, 254, 162),
+            ::capnp::word(15, 0, 0, 0, 1, 0, 2, 0),
+            ::capnp::word(183, 203, 183, 136, 110, 94, 174, 132),
+            ::capnp::word(0, 0, 7, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(21, 0, 0, 0, 154, 0, 0, 0),
+            ::capnp::word(29, 0, 0, 0, 7, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(25, 0, 0, 0, 119, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(109, 101, 116, 97, 100, 97, 116, 97),
+            ::capnp::word(46, 99, 97, 112, 110, 112, 58, 67),
+            ::capnp::word(104, 114, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 1, 0, 1, 0),
+            ::capnp::word(8, 0, 0, 0, 3, 0, 4, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 1, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(41, 0, 0, 0, 50, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(36, 0, 0, 0, 3, 0, 1, 0),
+            ::capnp::word(48, 0, 0, 0, 2, 0, 1, 0),
+            ::capnp::word(1, 0, 0, 0, 1, 0, 0, 0),
+            ::capnp::word(0, 0, 1, 0, 1, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(45, 0, 0, 0, 50, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(40, 0, 0, 0, 3, 0, 1, 0),
+            ::capnp::word(52, 0, 0, 0, 2, 0, 1, 0),
+            ::capnp::word(109, 97, 106, 111, 114, 0, 0, 0),
+            ::capnp::word(9, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(9, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(109, 105, 110, 111, 114, 0, 0, 0),
+            ::capnp::word(9, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(9, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+        ];
+        pub fn get_field_types(index: u16) -> ::capnp::introspect::Type {
+            match index {
+                0 => <u64 as ::capnp::introspect::Introspect>::introspect(),
+                1 => <u64 as ::capnp::introspect::Introspect>::introspect(),
+                _ => panic!("invalid field index {}", index),
+            }
+        }
+        pub fn get_annotation_types(
+            child_index: Option<u16>,
+            index: u32,
+        ) -> ::capnp::introspect::Type {
+            panic!("invalid annotation indices ({:?}, {}) ", child_index, index)
+        }
+        pub static RAW_SCHEMA: ::capnp::introspect::RawStructSchema =
+            ::capnp::introspect::RawStructSchema {
+                encoded_node: &ENCODED_NODE,
+                nonunion_members: NONUNION_MEMBERS,
+                members_by_discriminant: MEMBERS_BY_DISCRIMINANT,
+            };
+        pub static NONUNION_MEMBERS: &[u16] = &[0, 1];
+        pub static MEMBERS_BY_DISCRIMINANT: &[u16] = &[];
+        pub const TYPE_ID: u64 = 0xa2fe_e4ce_ccb3_5a70;
+    }
+}
+
+pub mod dir_entry {
+    #[derive(Copy, Clone)]
+    pub struct Owned(());
+    impl ::capnp::introspect::Introspect for Owned {
+        fn introspect() -> ::capnp::introspect::Type {
+            ::capnp::introspect::TypeVariant::Struct(::capnp::introspect::RawBrandedStructSchema {
+                generic: &_private::RAW_SCHEMA,
+                field_types: _private::get_field_types,
+                annotation_types: _private::get_annotation_types,
+            })
+            .into()
+        }
+    }
+    impl ::capnp::traits::Owned for Owned {
+        type Reader<'a> = Reader<'a>;
+        type Builder<'a> = Builder<'a>;
+    }
+    impl ::capnp::traits::OwnedStruct for Owned {
+        type Reader<'a> = Reader<'a>;
+        type Builder<'a> = Builder<'a>;
+    }
+    #[cfg(feature = "alloc")]
+    impl ::capnp::traits::Pipelined for Owned {
+        type Pipeline = Pipeline;
+    }
+
+    pub struct Reader<'a> {
+        reader: ::capnp::private::layout::StructReader<'a>,
+    }
+    impl<'a> ::core::marker::Copy for Reader<'a> {}
+    impl<'a> ::core::clone::Clone for Reader<'a> {
+        fn clone(&self) -> Self {
+            *self
+        }
+    }
+
+    impl<'a> ::capnp::traits::HasTypeId for Reader<'a> {
+        const TYPE_ID: u64 = _private::TYPE_ID;
+    }
+    impl<'a> ::core::convert::From<::capnp::private::layout::StructReader<'a>> for Reader<'a> {
+        fn from(reader: ::capnp::private::layout::StructReader<'a>) -> Self {
+            Self { reader }
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    impl<'a> ::core::convert::From<Reader<'a>> for ::capnp::dynamic_value::Reader<'a> {
+        fn from(reader: Reader<'a>) -> Self {
+            Self::Struct(::capnp::dynamic_struct::Reader::new(
+                reader.reader,
+                ::capnp::schema::StructSchema::new(::capnp::introspect::RawBrandedStructSchema {
+                    generic: &_private::RAW_SCHEMA,
+                    field_types: _private::get_field_types,
+                    annotation_types: _private::get_annotation_types,
+                }),
+            ))
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    impl<'a> ::core::fmt::Debug for Reader<'a> {
+        fn fmt(
+            &self,
+            f: &mut ::core::fmt::Formatter<'_>,
+        ) -> ::core::result::Result<(), ::core::fmt::Error> {
+            core::fmt::Debug::fmt(
+                &::core::convert::Into::<::capnp::dynamic_value::Reader<'_>>::into(*self),
+                f,
+            )
+        }
+    }
+
+    impl<'a> ::capnp::traits::FromPointerReader<'a> for Reader<'a> {
+        fn get_from_pointer(
+            reader: &::capnp::private::layout::PointerReader<'a>,
+            default: ::core::option::Option<&'a [::capnp::Word]>,
+        ) -> ::capnp::Result<Self> {
+            ::core::result::Result::Ok(reader.get_struct(default)?.into())
+        }
+    }
+
+    impl<'a> ::capnp::traits::IntoInternalStructReader<'a> for Reader<'a> {
+        fn into_internal_struct_reader(self) -> ::capnp::private::layout::StructReader<'a> {
+            self.reader
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    impl<'a> ::capnp::traits::Imbue<'a> for Reader<'a> {
+        fn imbue(&mut self, cap_table: &'a ::capnp::private::layout::CapTable) {
+            self.reader
+                .imbue(::capnp::private::layout::CapTableReader::Plain(cap_table))
+        }
+    }
+
+    impl<'a> Reader<'a> {
+        pub fn reborrow(&self) -> Reader<'_> {
+            Self { ..*self }
+        }
+
+        pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
+            self.reader.total_size()
+        }
+        #[inline]
+        pub fn get_ino(self) -> u64 {
+            self.reader.get_data_field::<u64>(0)
+        }
+        #[inline]
+        pub fn get_name(self) -> ::capnp::Result<::capnp::data::Reader<'a>> {
+            ::capnp::traits::FromPointerReader::get_from_pointer(
+                &self.reader.get_pointer_field(0),
+                ::core::option::Option::None,
+            )
+        }
+        #[inline]
+        pub fn has_name(&self) -> bool {
+            !self.reader.get_pointer_field(0).is_null()
+        }
+    }
+
+    pub struct Builder<'a> {
+        builder: ::capnp::private::layout::StructBuilder<'a>,
+    }
+    impl<'a> ::capnp::traits::HasStructSize for Builder<'a> {
+        const STRUCT_SIZE: ::capnp::private::layout::StructSize =
+            ::capnp::private::layout::StructSize {
+                data: 1,
+                pointers: 1,
+            };
+    }
+    impl<'a> ::capnp::traits::HasTypeId for Builder<'a> {
+        const TYPE_ID: u64 = _private::TYPE_ID;
+    }
+    impl<'a> ::core::convert::From<::capnp::private::layout::StructBuilder<'a>> for Builder<'a> {
+        fn from(builder: ::capnp::private::layout::StructBuilder<'a>) -> Self {
+            Self { builder }
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    impl<'a> ::core::convert::From<Builder<'a>> for ::capnp::dynamic_value::Builder<'a> {
+        fn from(builder: Builder<'a>) -> Self {
+            Self::Struct(::capnp::dynamic_struct::Builder::new(
+                builder.builder,
+                ::capnp::schema::StructSchema::new(::capnp::introspect::RawBrandedStructSchema {
+                    generic: &_private::RAW_SCHEMA,
+                    field_types: _private::get_field_types,
+                    annotation_types: _private::get_annotation_types,
+                }),
+            ))
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    impl<'a> ::capnp::traits::ImbueMut<'a> for Builder<'a> {
+        fn imbue_mut(&mut self, cap_table: &'a mut ::capnp::private::layout::CapTable) {
+            self.builder
+                .imbue(::capnp::private::layout::CapTableBuilder::Plain(cap_table))
+        }
+    }
+
+    impl<'a> ::capnp::traits::FromPointerBuilder<'a> for Builder<'a> {
+        fn init_pointer(builder: ::capnp::private::layout::PointerBuilder<'a>, _size: u32) -> Self {
+            builder
+                .init_struct(<Self as ::capnp::traits::HasStructSize>::STRUCT_SIZE)
+                .into()
+        }
+        fn get_from_pointer(
+            builder: ::capnp::private::layout::PointerBuilder<'a>,
+            default: ::core::option::Option<&'a [::capnp::Word]>,
+        ) -> ::capnp::Result<Self> {
+            ::core::result::Result::Ok(
+                builder
+                    .get_struct(
+                        <Self as ::capnp::traits::HasStructSize>::STRUCT_SIZE,
+                        default,
+                    )?
+                    .into(),
+            )
+        }
+    }
+
+    impl<'a> ::capnp::traits::SetPointerBuilder for Reader<'a> {
+        fn set_pointer_builder(
+            mut pointer: ::capnp::private::layout::PointerBuilder<'_>,
+            value: Self,
+            canonicalize: bool,
+        ) -> ::capnp::Result<()> {
+            pointer.set_struct(&value.reader, canonicalize)
+        }
+    }
+
+    impl<'a> Builder<'a> {
+        pub fn into_reader(self) -> Reader<'a> {
+            self.builder.into_reader().into()
+        }
+        pub fn reborrow(&mut self) -> Builder<'_> {
+            Builder {
+                builder: self.builder.reborrow(),
+            }
+        }
+        pub fn reborrow_as_reader(&self) -> Reader<'_> {
+            self.builder.as_reader().into()
+        }
+
+        pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
+            self.builder.as_reader().total_size()
+        }
+        #[inline]
+        pub fn get_ino(self) -> u64 {
+            self.builder.get_data_field::<u64>(0)
+        }
+        #[inline]
+        pub fn set_ino(&mut self, value: u64) {
+            self.builder.set_data_field::<u64>(0, value);
+        }
+        #[inline]
+        pub fn get_name(self) -> ::capnp::Result<::capnp::data::Builder<'a>> {
+            ::capnp::traits::FromPointerBuilder::get_from_pointer(
+                self.builder.get_pointer_field(0),
+                ::core::option::Option::None,
+            )
+        }
+        #[inline]
+        pub fn set_name(&mut self, value: ::capnp::data::Reader<'_>) {
+            self.builder.reborrow().get_pointer_field(0).set_data(value);
+        }
+        #[inline]
+        pub fn init_name(self, size: u32) -> ::capnp::data::Builder<'a> {
+            self.builder.get_pointer_field(0).init_data(size)
+        }
+        #[inline]
+        pub fn has_name(&self) -> bool {
+            !self.builder.is_pointer_field_null(0)
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    pub struct Pipeline {
+        _typeless: ::capnp::any_pointer::Pipeline,
+    }
+    #[cfg(feature = "alloc")]
+    impl ::capnp::capability::FromTypelessPipeline for Pipeline {
+        fn new(typeless: ::capnp::any_pointer::Pipeline) -> Self {
+            Self {
+                _typeless: typeless,
+            }
+        }
+    }
+    #[cfg(feature = "alloc")]
+    impl Pipeline {}
+    mod _private {
+        pub static ENCODED_NODE: [::capnp::Word; 47] = [
+            ::capnp::word(0, 0, 0, 0, 5, 0, 6, 0),
+            ::capnp::word(229, 236, 93, 69, 176, 175, 227, 153),
+            ::capnp::word(15, 0, 0, 0, 1, 0, 1, 0),
+            ::capnp::word(183, 203, 183, 136, 110, 94, 174, 132),
+            ::capnp::word(1, 0, 7, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(21, 0, 0, 0, 194, 0, 0, 0),
+            ::capnp::word(29, 0, 0, 0, 7, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(25, 0, 0, 0, 119, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(109, 101, 116, 97, 100, 97, 116, 97),
+            ::capnp::word(46, 99, 97, 112, 110, 112, 58, 68),
+            ::capnp::word(105, 114, 69, 110, 116, 114, 121, 0),
+            ::capnp::word(0, 0, 0, 0, 1, 0, 1, 0),
+            ::capnp::word(8, 0, 0, 0, 3, 0, 4, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 1, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(41, 0, 0, 0, 34, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(36, 0, 0, 0, 3, 0, 1, 0),
+            ::capnp::word(48, 0, 0, 0, 2, 0, 1, 0),
+            ::capnp::word(1, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 1, 0, 1, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(45, 0, 0, 0, 42, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(40, 0, 0, 0, 3, 0, 1, 0),
+            ::capnp::word(52, 0, 0, 0, 2, 0, 1, 0),
+            ::capnp::word(105, 110, 111, 0, 0, 0, 0, 0),
+            ::capnp::word(9, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(9, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(110, 97, 109, 101, 0, 0, 0, 0),
+            ::capnp::word(13, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(13, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+        ];
+        pub fn get_field_types(index: u16) -> ::capnp::introspect::Type {
+            match index {
+                0 => <u64 as ::capnp::introspect::Introspect>::introspect(),
+                1 => <::capnp::data::Owned as ::capnp::introspect::Introspect>::introspect(),
+                _ => panic!("invalid field index {}", index),
+            }
+        }
+        pub fn get_annotation_types(
+            child_index: Option<u16>,
+            index: u32,
+        ) -> ::capnp::introspect::Type {
+            panic!("invalid annotation indices ({:?}, {}) ", child_index, index)
+        }
+        pub static RAW_SCHEMA: ::capnp::introspect::RawStructSchema =
+            ::capnp::introspect::RawStructSchema {
+                encoded_node: &ENCODED_NODE,
+                nonunion_members: NONUNION_MEMBERS,
+                members_by_discriminant: MEMBERS_BY_DISCRIMINANT,
+            };
+        pub static NONUNION_MEMBERS: &[u16] = &[0, 1];
+        pub static MEMBERS_BY_DISCRIMINANT: &[u16] = &[];
+        pub const TYPE_ID: u64 = 0x99e3_afb0_455d_ece5;
+    }
+}
+
+pub mod dir {
+    #[derive(Copy, Clone)]
+    pub struct Owned(());
+    impl ::capnp::introspect::Introspect for Owned {
+        fn introspect() -> ::capnp::introspect::Type {
+            ::capnp::introspect::TypeVariant::Struct(::capnp::introspect::RawBrandedStructSchema {
+                generic: &_private::RAW_SCHEMA,
+                field_types: _private::get_field_types,
+                annotation_types: _private::get_annotation_types,
+            })
+            .into()
+        }
+    }
+    impl ::capnp::traits::Owned for Owned {
+        type Reader<'a> = Reader<'a>;
+        type Builder<'a> = Builder<'a>;
+    }
+    impl ::capnp::traits::OwnedStruct for Owned {
+        type Reader<'a> = Reader<'a>;
+        type Builder<'a> = Builder<'a>;
+    }
+    #[cfg(feature = "alloc")]
+    impl ::capnp::traits::Pipelined for Owned {
+        type Pipeline = Pipeline;
+    }
+
+    pub struct Reader<'a> {
+        reader: ::capnp::private::layout::StructReader<'a>,
+    }
+    impl<'a> ::core::marker::Copy for Reader<'a> {}
+    impl<'a> ::core::clone::Clone for Reader<'a> {
+        fn clone(&self) -> Self {
+            *self
+        }
+    }
+
+    impl<'a> ::capnp::traits::HasTypeId for Reader<'a> {
+        const TYPE_ID: u64 = _private::TYPE_ID;
+    }
+    impl<'a> ::core::convert::From<::capnp::private::layout::StructReader<'a>> for Reader<'a> {
+        fn from(reader: ::capnp::private::layout::StructReader<'a>) -> Self {
+            Self { reader }
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    impl<'a> ::core::convert::From<Reader<'a>> for ::capnp::dynamic_value::Reader<'a> {
+        fn from(reader: Reader<'a>) -> Self {
+            Self::Struct(::capnp::dynamic_struct::Reader::new(
+                reader.reader,
+                ::capnp::schema::StructSchema::new(::capnp::introspect::RawBrandedStructSchema {
+                    generic: &_private::RAW_SCHEMA,
+                    field_types: _private::get_field_types,
+                    annotation_types: _private::get_annotation_types,
+                }),
+            ))
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    impl<'a> ::core::fmt::Debug for Reader<'a> {
+        fn fmt(
+            &self,
+            f: &mut ::core::fmt::Formatter<'_>,
+        ) -> ::core::result::Result<(), ::core::fmt::Error> {
+            core::fmt::Debug::fmt(
+                &::core::convert::Into::<::capnp::dynamic_value::Reader<'_>>::into(*self),
+                f,
+            )
+        }
+    }
+
+    impl<'a> ::capnp::traits::FromPointerReader<'a> for Reader<'a> {
+        fn get_from_pointer(
+            reader: &::capnp::private::layout::PointerReader<'a>,
+            default: ::core::option::Option<&'a [::capnp::Word]>,
+        ) -> ::capnp::Result<Self> {
+            ::core::result::Result::Ok(reader.get_struct(default)?.into())
+        }
+    }
+
+    impl<'a> ::capnp::traits::IntoInternalStructReader<'a> for Reader<'a> {
+        fn into_internal_struct_reader(self) -> ::capnp::private::layout::StructReader<'a> {
+            self.reader
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    impl<'a> ::capnp::traits::Imbue<'a> for Reader<'a> {
+        fn imbue(&mut self, cap_table: &'a ::capnp::private::layout::CapTable) {
+            self.reader
+                .imbue(::capnp::private::layout::CapTableReader::Plain(cap_table))
+        }
+    }
+
+    impl<'a> Reader<'a> {
+        pub fn reborrow(&self) -> Reader<'_> {
+            Self { ..*self }
+        }
+
+        pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
+            self.reader.total_size()
+        }
+        #[inline]
+        pub fn get_entries(
+            self,
+        ) -> ::capnp::Result<
+            ::capnp::struct_list::Reader<'a, crate::metadata_capnp::dir_entry::Owned>,
+        > {
+            ::capnp::traits::FromPointerReader::get_from_pointer(
+                &self.reader.get_pointer_field(0),
+                ::core::option::Option::None,
+            )
+        }
+        #[inline]
+        pub fn has_entries(&self) -> bool {
+            !self.reader.get_pointer_field(0).is_null()
+        }
+        #[inline]
+        pub fn get_look_below(self) -> bool {
+            self.reader.get_bool_field(0)
+        }
+    }
+
+    pub struct Builder<'a> {
+        builder: ::capnp::private::layout::StructBuilder<'a>,
+    }
+    impl<'a> ::capnp::traits::HasStructSize for Builder<'a> {
+        const STRUCT_SIZE: ::capnp::private::layout::StructSize =
+            ::capnp::private::layout::StructSize {
+                data: 1,
+                pointers: 1,
+            };
+    }
+    impl<'a> ::capnp::traits::HasTypeId for Builder<'a> {
+        const TYPE_ID: u64 = _private::TYPE_ID;
+    }
+    impl<'a> ::core::convert::From<::capnp::private::layout::StructBuilder<'a>> for Builder<'a> {
+        fn from(builder: ::capnp::private::layout::StructBuilder<'a>) -> Self {
+            Self { builder }
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    impl<'a> ::core::convert::From<Builder<'a>> for ::capnp::dynamic_value::Builder<'a> {
+        fn from(builder: Builder<'a>) -> Self {
+            Self::Struct(::capnp::dynamic_struct::Builder::new(
+                builder.builder,
+                ::capnp::schema::StructSchema::new(::capnp::introspect::RawBrandedStructSchema {
+                    generic: &_private::RAW_SCHEMA,
+                    field_types: _private::get_field_types,
+                    annotation_types: _private::get_annotation_types,
+                }),
+            ))
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    impl<'a> ::capnp::traits::ImbueMut<'a> for Builder<'a> {
+        fn imbue_mut(&mut self, cap_table: &'a mut ::capnp::private::layout::CapTable) {
+            self.builder
+                .imbue(::capnp::private::layout::CapTableBuilder::Plain(cap_table))
+        }
+    }
+
+    impl<'a> ::capnp::traits::FromPointerBuilder<'a> for Builder<'a> {
+        fn init_pointer(builder: ::capnp::private::layout::PointerBuilder<'a>, _size: u32) -> Self {
+            builder
+                .init_struct(<Self as ::capnp::traits::HasStructSize>::STRUCT_SIZE)
+                .into()
+        }
+        fn get_from_pointer(
+            builder: ::capnp::private::layout::PointerBuilder<'a>,
+            default: ::core::option::Option<&'a [::capnp::Word]>,
+        ) -> ::capnp::Result<Self> {
+            ::core::result::Result::Ok(
+                builder
+                    .get_struct(
+                        <Self as ::capnp::traits::HasStructSize>::STRUCT_SIZE,
+                        default,
+                    )?
+                    .into(),
+            )
+        }
+    }
+
+    impl<'a> ::capnp::traits::SetPointerBuilder for Reader<'a> {
+        fn set_pointer_builder(
+            mut pointer: ::capnp::private::layout::PointerBuilder<'_>,
+            value: Self,
+            canonicalize: bool,
+        ) -> ::capnp::Result<()> {
+            pointer.set_struct(&value.reader, canonicalize)
+        }
+    }
+
+    impl<'a> Builder<'a> {
+        pub fn into_reader(self) -> Reader<'a> {
+            self.builder.into_reader().into()
+        }
+        pub fn reborrow(&mut self) -> Builder<'_> {
+            Builder {
+                builder: self.builder.reborrow(),
+            }
+        }
+        pub fn reborrow_as_reader(&self) -> Reader<'_> {
+            self.builder.as_reader().into()
+        }
+
+        pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
+            self.builder.as_reader().total_size()
+        }
+        #[inline]
+        pub fn get_entries(
+            self,
+        ) -> ::capnp::Result<
+            ::capnp::struct_list::Builder<'a, crate::metadata_capnp::dir_entry::Owned>,
+        > {
+            ::capnp::traits::FromPointerBuilder::get_from_pointer(
+                self.builder.get_pointer_field(0),
+                ::core::option::Option::None,
+            )
+        }
+        #[inline]
+        pub fn set_entries(
+            &mut self,
+            value: ::capnp::struct_list::Reader<'a, crate::metadata_capnp::dir_entry::Owned>,
+        ) -> ::capnp::Result<()> {
+            ::capnp::traits::SetPointerBuilder::set_pointer_builder(
+                self.builder.reborrow().get_pointer_field(0),
+                value,
+                false,
+            )
+        }
+        #[inline]
+        pub fn init_entries(
+            self,
+            size: u32,
+        ) -> ::capnp::struct_list::Builder<'a, crate::metadata_capnp::dir_entry::Owned> {
+            ::capnp::traits::FromPointerBuilder::init_pointer(
+                self.builder.get_pointer_field(0),
+                size,
+            )
+        }
+        #[inline]
+        pub fn has_entries(&self) -> bool {
+            !self.builder.is_pointer_field_null(0)
+        }
+        #[inline]
+        pub fn get_look_below(self) -> bool {
+            self.builder.get_bool_field(0)
+        }
+        #[inline]
+        pub fn set_look_below(&mut self, value: bool) {
+            self.builder.set_bool_field(0, value);
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    pub struct Pipeline {
+        _typeless: ::capnp::any_pointer::Pipeline,
+    }
+    #[cfg(feature = "alloc")]
+    impl ::capnp::capability::FromTypelessPipeline for Pipeline {
+        fn new(typeless: ::capnp::any_pointer::Pipeline) -> Self {
+            Self {
+                _typeless: typeless,
+            }
+        }
+    }
+    #[cfg(feature = "alloc")]
+    impl Pipeline {}
+    mod _private {
+        pub static ENCODED_NODE: [::capnp::Word; 52] = [
+            ::capnp::word(0, 0, 0, 0, 5, 0, 6, 0),
+            ::capnp::word(4, 150, 39, 131, 249, 197, 58, 163),
+            ::capnp::word(15, 0, 0, 0, 1, 0, 1, 0),
+            ::capnp::word(183, 203, 183, 136, 110, 94, 174, 132),
+            ::capnp::word(1, 0, 7, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(21, 0, 0, 0, 154, 0, 0, 0),
+            ::capnp::word(29, 0, 0, 0, 7, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(25, 0, 0, 0, 119, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(109, 101, 116, 97, 100, 97, 116, 97),
+            ::capnp::word(46, 99, 97, 112, 110, 112, 58, 68),
+            ::capnp::word(105, 114, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 1, 0, 1, 0),
+            ::capnp::word(8, 0, 0, 0, 3, 0, 4, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 1, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(41, 0, 0, 0, 66, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(36, 0, 0, 0, 3, 0, 1, 0),
+            ::capnp::word(64, 0, 0, 0, 2, 0, 1, 0),
+            ::capnp::word(1, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 1, 0, 1, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(61, 0, 0, 0, 82, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(60, 0, 0, 0, 3, 0, 1, 0),
+            ::capnp::word(72, 0, 0, 0, 2, 0, 1, 0),
+            ::capnp::word(101, 110, 116, 114, 105, 101, 115, 0),
+            ::capnp::word(14, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 3, 0, 1, 0),
+            ::capnp::word(16, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(229, 236, 93, 69, 176, 175, 227, 153),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(14, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(108, 111, 111, 107, 66, 101, 108, 111),
+            ::capnp::word(119, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(1, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(1, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+        ];
+        pub fn get_field_types(index: u16) -> ::capnp::introspect::Type {
+            match index {
+        0 => <::capnp::struct_list::Owned<crate::metadata_capnp::dir_entry::Owned> as ::capnp::introspect::Introspect>::introspect(),
+        1 => <bool as ::capnp::introspect::Introspect>::introspect(),
+        _ => panic!("invalid field index {}", index),
+      }
+        }
+        pub fn get_annotation_types(
+            child_index: Option<u16>,
+            index: u32,
+        ) -> ::capnp::introspect::Type {
+            panic!("invalid annotation indices ({:?}, {}) ", child_index, index)
+        }
+        pub static RAW_SCHEMA: ::capnp::introspect::RawStructSchema =
+            ::capnp::introspect::RawStructSchema {
+                encoded_node: &ENCODED_NODE,
+                nonunion_members: NONUNION_MEMBERS,
+                members_by_discriminant: MEMBERS_BY_DISCRIMINANT,
+            };
+        pub static NONUNION_MEMBERS: &[u16] = &[0, 1];
+        pub static MEMBERS_BY_DISCRIMINANT: &[u16] = &[];
+        pub const TYPE_ID: u64 = 0xa33a_c5f9_8327_9604;
+    }
+}
+
+pub mod blk {
+    #[derive(Copy, Clone)]
+    pub struct Owned(());
+    impl ::capnp::introspect::Introspect for Owned {
+        fn introspect() -> ::capnp::introspect::Type {
+            ::capnp::introspect::TypeVariant::Struct(::capnp::introspect::RawBrandedStructSchema {
+                generic: &_private::RAW_SCHEMA,
+                field_types: _private::get_field_types,
+                annotation_types: _private::get_annotation_types,
+            })
+            .into()
+        }
+    }
+    impl ::capnp::traits::Owned for Owned {
+        type Reader<'a> = Reader<'a>;
+        type Builder<'a> = Builder<'a>;
+    }
+    impl ::capnp::traits::OwnedStruct for Owned {
+        type Reader<'a> = Reader<'a>;
+        type Builder<'a> = Builder<'a>;
+    }
+    #[cfg(feature = "alloc")]
+    impl ::capnp::traits::Pipelined for Owned {
+        type Pipeline = Pipeline;
+    }
+
+    pub struct Reader<'a> {
+        reader: ::capnp::private::layout::StructReader<'a>,
+    }
+    impl<'a> ::core::marker::Copy for Reader<'a> {}
+    impl<'a> ::core::clone::Clone for Reader<'a> {
+        fn clone(&self) -> Self {
+            *self
+        }
+    }
+
+    impl<'a> ::capnp::traits::HasTypeId for Reader<'a> {
+        const TYPE_ID: u64 = _private::TYPE_ID;
+    }
+    impl<'a> ::core::convert::From<::capnp::private::layout::StructReader<'a>> for Reader<'a> {
+        fn from(reader: ::capnp::private::layout::StructReader<'a>) -> Self {
+            Self { reader }
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    impl<'a> ::core::convert::From<Reader<'a>> for ::capnp::dynamic_value::Reader<'a> {
+        fn from(reader: Reader<'a>) -> Self {
+            Self::Struct(::capnp::dynamic_struct::Reader::new(
+                reader.reader,
+                ::capnp::schema::StructSchema::new(::capnp::introspect::RawBrandedStructSchema {
+                    generic: &_private::RAW_SCHEMA,
+                    field_types: _private::get_field_types,
+                    annotation_types: _private::get_annotation_types,
+                }),
+            ))
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    impl<'a> ::core::fmt::Debug for Reader<'a> {
+        fn fmt(
+            &self,
+            f: &mut ::core::fmt::Formatter<'_>,
+        ) -> ::core::result::Result<(), ::core::fmt::Error> {
+            core::fmt::Debug::fmt(
+                &::core::convert::Into::<::capnp::dynamic_value::Reader<'_>>::into(*self),
+                f,
+            )
+        }
+    }
+
+    impl<'a> ::capnp::traits::FromPointerReader<'a> for Reader<'a> {
+        fn get_from_pointer(
+            reader: &::capnp::private::layout::PointerReader<'a>,
+            default: ::core::option::Option<&'a [::capnp::Word]>,
+        ) -> ::capnp::Result<Self> {
+            ::core::result::Result::Ok(reader.get_struct(default)?.into())
+        }
+    }
+
+    impl<'a> ::capnp::traits::IntoInternalStructReader<'a> for Reader<'a> {
+        fn into_internal_struct_reader(self) -> ::capnp::private::layout::StructReader<'a> {
+            self.reader
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    impl<'a> ::capnp::traits::Imbue<'a> for Reader<'a> {
+        fn imbue(&mut self, cap_table: &'a ::capnp::private::layout::CapTable) {
+            self.reader
+                .imbue(::capnp::private::layout::CapTableReader::Plain(cap_table))
+        }
+    }
+
+    impl<'a> Reader<'a> {
+        pub fn reborrow(&self) -> Reader<'_> {
+            Self { ..*self }
+        }
+
+        pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
+            self.reader.total_size()
+        }
+        #[inline]
+        pub fn get_major(self) -> u64 {
+            self.reader.get_data_field::<u64>(0)
+        }
+        #[inline]
+        pub fn get_minor(self) -> u64 {
+            self.reader.get_data_field::<u64>(1)
+        }
+    }
+
+    pub struct Builder<'a> {
+        builder: ::capnp::private::layout::StructBuilder<'a>,
+    }
+    impl<'a> ::capnp::traits::HasStructSize for Builder<'a> {
+        const STRUCT_SIZE: ::capnp::private::layout::StructSize =
+            ::capnp::private::layout::StructSize {
+                data: 2,
+                pointers: 0,
+            };
+    }
+    impl<'a> ::capnp::traits::HasTypeId for Builder<'a> {
+        const TYPE_ID: u64 = _private::TYPE_ID;
+    }
+    impl<'a> ::core::convert::From<::capnp::private::layout::StructBuilder<'a>> for Builder<'a> {
+        fn from(builder: ::capnp::private::layout::StructBuilder<'a>) -> Self {
+            Self { builder }
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    impl<'a> ::core::convert::From<Builder<'a>> for ::capnp::dynamic_value::Builder<'a> {
+        fn from(builder: Builder<'a>) -> Self {
+            Self::Struct(::capnp::dynamic_struct::Builder::new(
+                builder.builder,
+                ::capnp::schema::StructSchema::new(::capnp::introspect::RawBrandedStructSchema {
+                    generic: &_private::RAW_SCHEMA,
+                    field_types: _private::get_field_types,
+                    annotation_types: _private::get_annotation_types,
+                }),
+            ))
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    impl<'a> ::capnp::traits::ImbueMut<'a> for Builder<'a> {
+        fn imbue_mut(&mut self, cap_table: &'a mut ::capnp::private::layout::CapTable) {
+            self.builder
+                .imbue(::capnp::private::layout::CapTableBuilder::Plain(cap_table))
+        }
+    }
+
+    impl<'a> ::capnp::traits::FromPointerBuilder<'a> for Builder<'a> {
+        fn init_pointer(builder: ::capnp::private::layout::PointerBuilder<'a>, _size: u32) -> Self {
+            builder
+                .init_struct(<Self as ::capnp::traits::HasStructSize>::STRUCT_SIZE)
+                .into()
+        }
+        fn get_from_pointer(
+            builder: ::capnp::private::layout::PointerBuilder<'a>,
+            default: ::core::option::Option<&'a [::capnp::Word]>,
+        ) -> ::capnp::Result<Self> {
+            ::core::result::Result::Ok(
+                builder
+                    .get_struct(
+                        <Self as ::capnp::traits::HasStructSize>::STRUCT_SIZE,
+                        default,
+                    )?
+                    .into(),
+            )
+        }
+    }
+
+    impl<'a> ::capnp::traits::SetPointerBuilder for Reader<'a> {
+        fn set_pointer_builder(
+            mut pointer: ::capnp::private::layout::PointerBuilder<'_>,
+            value: Self,
+            canonicalize: bool,
+        ) -> ::capnp::Result<()> {
+            pointer.set_struct(&value.reader, canonicalize)
+        }
+    }
+
+    impl<'a> Builder<'a> {
+        pub fn into_reader(self) -> Reader<'a> {
+            self.builder.into_reader().into()
+        }
+        pub fn reborrow(&mut self) -> Builder<'_> {
+            Builder {
+                builder: self.builder.reborrow(),
+            }
+        }
+        pub fn reborrow_as_reader(&self) -> Reader<'_> {
+            self.builder.as_reader().into()
+        }
+
+        pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
+            self.builder.as_reader().total_size()
+        }
+        #[inline]
+        pub fn get_major(self) -> u64 {
+            self.builder.get_data_field::<u64>(0)
+        }
+        #[inline]
+        pub fn set_major(&mut self, value: u64) {
+            self.builder.set_data_field::<u64>(0, value);
+        }
+        #[inline]
+        pub fn get_minor(self) -> u64 {
+            self.builder.get_data_field::<u64>(1)
+        }
+        #[inline]
+        pub fn set_minor(&mut self, value: u64) {
+            self.builder.set_data_field::<u64>(1, value);
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    pub struct Pipeline {
+        _typeless: ::capnp::any_pointer::Pipeline,
+    }
+    #[cfg(feature = "alloc")]
+    impl ::capnp::capability::FromTypelessPipeline for Pipeline {
+        fn new(typeless: ::capnp::any_pointer::Pipeline) -> Self {
+            Self {
+                _typeless: typeless,
+            }
+        }
+    }
+    #[cfg(feature = "alloc")]
+    impl Pipeline {}
+    mod _private {
+        pub static ENCODED_NODE: [::capnp::Word; 47] = [
+            ::capnp::word(0, 0, 0, 0, 5, 0, 6, 0),
+            ::capnp::word(105, 137, 149, 129, 43, 27, 209, 242),
+            ::capnp::word(15, 0, 0, 0, 1, 0, 2, 0),
+            ::capnp::word(183, 203, 183, 136, 110, 94, 174, 132),
+            ::capnp::word(0, 0, 7, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(21, 0, 0, 0, 154, 0, 0, 0),
+            ::capnp::word(29, 0, 0, 0, 7, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(25, 0, 0, 0, 119, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(109, 101, 116, 97, 100, 97, 116, 97),
+            ::capnp::word(46, 99, 97, 112, 110, 112, 58, 66),
+            ::capnp::word(108, 107, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 1, 0, 1, 0),
+            ::capnp::word(8, 0, 0, 0, 3, 0, 4, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 1, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(41, 0, 0, 0, 50, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(36, 0, 0, 0, 3, 0, 1, 0),
+            ::capnp::word(48, 0, 0, 0, 2, 0, 1, 0),
+            ::capnp::word(1, 0, 0, 0, 1, 0, 0, 0),
+            ::capnp::word(0, 0, 1, 0, 1, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(45, 0, 0, 0, 50, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(40, 0, 0, 0, 3, 0, 1, 0),
+            ::capnp::word(52, 0, 0, 0, 2, 0, 1, 0),
+            ::capnp::word(109, 97, 106, 111, 114, 0, 0, 0),
+            ::capnp::word(9, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(9, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(109, 105, 110, 111, 114, 0, 0, 0),
+            ::capnp::word(9, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(9, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+        ];
+        pub fn get_field_types(index: u16) -> ::capnp::introspect::Type {
+            match index {
+                0 => <u64 as ::capnp::introspect::Introspect>::introspect(),
+                1 => <u64 as ::capnp::introspect::Introspect>::introspect(),
+                _ => panic!("invalid field index {}", index),
+            }
+        }
+        pub fn get_annotation_types(
+            child_index: Option<u16>,
+            index: u32,
+        ) -> ::capnp::introspect::Type {
+            panic!("invalid annotation indices ({:?}, {}) ", child_index, index)
+        }
+        pub static RAW_SCHEMA: ::capnp::introspect::RawStructSchema =
+            ::capnp::introspect::RawStructSchema {
+                encoded_node: &ENCODED_NODE,
+                nonunion_members: NONUNION_MEMBERS,
+                members_by_discriminant: MEMBERS_BY_DISCRIMINANT,
+            };
+        pub static NONUNION_MEMBERS: &[u16] = &[0, 1];
+        pub static MEMBERS_BY_DISCRIMINANT: &[u16] = &[];
+        pub const TYPE_ID: u64 = 0xf2d1_1b2b_8195_8969;
+    }
+}
+
+pub mod file_chunk {
+    #[derive(Copy, Clone)]
+    pub struct Owned(());
+    impl ::capnp::introspect::Introspect for Owned {
+        fn introspect() -> ::capnp::introspect::Type {
+            ::capnp::introspect::TypeVariant::Struct(::capnp::introspect::RawBrandedStructSchema {
+                generic: &_private::RAW_SCHEMA,
+                field_types: _private::get_field_types,
+                annotation_types: _private::get_annotation_types,
+            })
+            .into()
+        }
+    }
+    impl ::capnp::traits::Owned for Owned {
+        type Reader<'a> = Reader<'a>;
+        type Builder<'a> = Builder<'a>;
+    }
+    impl ::capnp::traits::OwnedStruct for Owned {
+        type Reader<'a> = Reader<'a>;
+        type Builder<'a> = Builder<'a>;
+    }
+    #[cfg(feature = "alloc")]
+    impl ::capnp::traits::Pipelined for Owned {
+        type Pipeline = Pipeline;
+    }
+
+    pub struct Reader<'a> {
+        reader: ::capnp::private::layout::StructReader<'a>,
+    }
+    impl<'a> ::core::marker::Copy for Reader<'a> {}
+    impl<'a> ::core::clone::Clone for Reader<'a> {
+        fn clone(&self) -> Self {
+            *self
+        }
+    }
+
+    impl<'a> ::capnp::traits::HasTypeId for Reader<'a> {
+        const TYPE_ID: u64 = _private::TYPE_ID;
+    }
+    impl<'a> ::core::convert::From<::capnp::private::layout::StructReader<'a>> for Reader<'a> {
+        fn from(reader: ::capnp::private::layout::StructReader<'a>) -> Self {
+            Self { reader }
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    impl<'a> ::core::convert::From<Reader<'a>> for ::capnp::dynamic_value::Reader<'a> {
+        fn from(reader: Reader<'a>) -> Self {
+            Self::Struct(::capnp::dynamic_struct::Reader::new(
+                reader.reader,
+                ::capnp::schema::StructSchema::new(::capnp::introspect::RawBrandedStructSchema {
+                    generic: &_private::RAW_SCHEMA,
+                    field_types: _private::get_field_types,
+                    annotation_types: _private::get_annotation_types,
+                }),
+            ))
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    impl<'a> ::core::fmt::Debug for Reader<'a> {
+        fn fmt(
+            &self,
+            f: &mut ::core::fmt::Formatter<'_>,
+        ) -> ::core::result::Result<(), ::core::fmt::Error> {
+            core::fmt::Debug::fmt(
+                &::core::convert::Into::<::capnp::dynamic_value::Reader<'_>>::into(*self),
+                f,
+            )
+        }
+    }
+
+    impl<'a> ::capnp::traits::FromPointerReader<'a> for Reader<'a> {
+        fn get_from_pointer(
+            reader: &::capnp::private::layout::PointerReader<'a>,
+            default: ::core::option::Option<&'a [::capnp::Word]>,
+        ) -> ::capnp::Result<Self> {
+            ::core::result::Result::Ok(reader.get_struct(default)?.into())
+        }
+    }
+
+    impl<'a> ::capnp::traits::IntoInternalStructReader<'a> for Reader<'a> {
+        fn into_internal_struct_reader(self) -> ::capnp::private::layout::StructReader<'a> {
+            self.reader
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    impl<'a> ::capnp::traits::Imbue<'a> for Reader<'a> {
+        fn imbue(&mut self, cap_table: &'a ::capnp::private::layout::CapTable) {
+            self.reader
+                .imbue(::capnp::private::layout::CapTableReader::Plain(cap_table))
+        }
+    }
+
+    impl<'a> Reader<'a> {
+        pub fn reborrow(&self) -> Reader<'_> {
+            Self { ..*self }
+        }
+
+        pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
+            self.reader.total_size()
+        }
+        #[inline]
+        pub fn get_blob(self) -> ::capnp::Result<crate::metadata_capnp::blob_ref::Reader<'a>> {
+            ::capnp::traits::FromPointerReader::get_from_pointer(
+                &self.reader.get_pointer_field(0),
+                ::core::option::Option::None,
+            )
+        }
+        #[inline]
+        pub fn has_blob(&self) -> bool {
+            !self.reader.get_pointer_field(0).is_null()
+        }
+        #[inline]
+        pub fn get_len(self) -> u64 {
+            self.reader.get_data_field::<u64>(0)
+        }
+    }
+
+    pub struct Builder<'a> {
+        builder: ::capnp::private::layout::StructBuilder<'a>,
+    }
+    impl<'a> ::capnp::traits::HasStructSize for Builder<'a> {
+        const STRUCT_SIZE: ::capnp::private::layout::StructSize =
+            ::capnp::private::layout::StructSize {
+                data: 1,
+                pointers: 1,
+            };
+    }
+    impl<'a> ::capnp::traits::HasTypeId for Builder<'a> {
+        const TYPE_ID: u64 = _private::TYPE_ID;
+    }
+    impl<'a> ::core::convert::From<::capnp::private::layout::StructBuilder<'a>> for Builder<'a> {
+        fn from(builder: ::capnp::private::layout::StructBuilder<'a>) -> Self {
+            Self { builder }
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    impl<'a> ::core::convert::From<Builder<'a>> for ::capnp::dynamic_value::Builder<'a> {
+        fn from(builder: Builder<'a>) -> Self {
+            Self::Struct(::capnp::dynamic_struct::Builder::new(
+                builder.builder,
+                ::capnp::schema::StructSchema::new(::capnp::introspect::RawBrandedStructSchema {
+                    generic: &_private::RAW_SCHEMA,
+                    field_types: _private::get_field_types,
+                    annotation_types: _private::get_annotation_types,
+                }),
+            ))
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    impl<'a> ::capnp::traits::ImbueMut<'a> for Builder<'a> {
+        fn imbue_mut(&mut self, cap_table: &'a mut ::capnp::private::layout::CapTable) {
+            self.builder
+                .imbue(::capnp::private::layout::CapTableBuilder::Plain(cap_table))
+        }
+    }
+
+    impl<'a> ::capnp::traits::FromPointerBuilder<'a> for Builder<'a> {
+        fn init_pointer(builder: ::capnp::private::layout::PointerBuilder<'a>, _size: u32) -> Self {
+            builder
+                .init_struct(<Self as ::capnp::traits::HasStructSize>::STRUCT_SIZE)
+                .into()
+        }
+        fn get_from_pointer(
+            builder: ::capnp::private::layout::PointerBuilder<'a>,
+            default: ::core::option::Option<&'a [::capnp::Word]>,
+        ) -> ::capnp::Result<Self> {
+            ::core::result::Result::Ok(
+                builder
+                    .get_struct(
+                        <Self as ::capnp::traits::HasStructSize>::STRUCT_SIZE,
+                        default,
+                    )?
+                    .into(),
+            )
+        }
+    }
+
+    impl<'a> ::capnp::traits::SetPointerBuilder for Reader<'a> {
+        fn set_pointer_builder(
+            mut pointer: ::capnp::private::layout::PointerBuilder<'_>,
+            value: Self,
+            canonicalize: bool,
+        ) -> ::capnp::Result<()> {
+            pointer.set_struct(&value.reader, canonicalize)
+        }
+    }
+
+    impl<'a> Builder<'a> {
+        pub fn into_reader(self) -> Reader<'a> {
+            self.builder.into_reader().into()
+        }
+        pub fn reborrow(&mut self) -> Builder<'_> {
+            Builder {
+                builder: self.builder.reborrow(),
+            }
+        }
+        pub fn reborrow_as_reader(&self) -> Reader<'_> {
+            self.builder.as_reader().into()
+        }
+
+        pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
+            self.builder.as_reader().total_size()
+        }
+        #[inline]
+        pub fn get_blob(self) -> ::capnp::Result<crate::metadata_capnp::blob_ref::Builder<'a>> {
+            ::capnp::traits::FromPointerBuilder::get_from_pointer(
+                self.builder.get_pointer_field(0),
+                ::core::option::Option::None,
+            )
+        }
+        #[inline]
+        pub fn set_blob(
+            &mut self,
+            value: crate::metadata_capnp::blob_ref::Reader<'_>,
+        ) -> ::capnp::Result<()> {
+            ::capnp::traits::SetPointerBuilder::set_pointer_builder(
+                self.builder.reborrow().get_pointer_field(0),
+                value,
+                false,
+            )
+        }
+        #[inline]
+        pub fn init_blob(self) -> crate::metadata_capnp::blob_ref::Builder<'a> {
+            ::capnp::traits::FromPointerBuilder::init_pointer(self.builder.get_pointer_field(0), 0)
+        }
+        #[inline]
+        pub fn has_blob(&self) -> bool {
+            !self.builder.is_pointer_field_null(0)
+        }
+        #[inline]
+        pub fn get_len(self) -> u64 {
+            self.builder.get_data_field::<u64>(0)
+        }
+        #[inline]
+        pub fn set_len(&mut self, value: u64) {
+            self.builder.set_data_field::<u64>(0, value);
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    pub struct Pipeline {
+        _typeless: ::capnp::any_pointer::Pipeline,
+    }
+    #[cfg(feature = "alloc")]
+    impl ::capnp::capability::FromTypelessPipeline for Pipeline {
+        fn new(typeless: ::capnp::any_pointer::Pipeline) -> Self {
+            Self {
+                _typeless: typeless,
+            }
+        }
+    }
+    #[cfg(feature = "alloc")]
+    impl Pipeline {
+        pub fn get_blob(&self) -> crate::metadata_capnp::blob_ref::Pipeline {
+            ::capnp::capability::FromTypelessPipeline::new(self._typeless.get_pointer_field(0))
+        }
+    }
+    mod _private {
+        pub static ENCODED_NODE: [::capnp::Word; 48] = [
+            ::capnp::word(0, 0, 0, 0, 5, 0, 6, 0),
+            ::capnp::word(210, 21, 210, 230, 255, 156, 237, 140),
+            ::capnp::word(15, 0, 0, 0, 1, 0, 1, 0),
+            ::capnp::word(183, 203, 183, 136, 110, 94, 174, 132),
+            ::capnp::word(1, 0, 7, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(21, 0, 0, 0, 202, 0, 0, 0),
+            ::capnp::word(33, 0, 0, 0, 7, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(29, 0, 0, 0, 119, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(109, 101, 116, 97, 100, 97, 116, 97),
+            ::capnp::word(46, 99, 97, 112, 110, 112, 58, 70),
+            ::capnp::word(105, 108, 101, 67, 104, 117, 110, 107),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 1, 0, 1, 0),
+            ::capnp::word(8, 0, 0, 0, 3, 0, 4, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 1, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(41, 0, 0, 0, 42, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(36, 0, 0, 0, 3, 0, 1, 0),
+            ::capnp::word(48, 0, 0, 0, 2, 0, 1, 0),
+            ::capnp::word(1, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 1, 0, 1, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(45, 0, 0, 0, 34, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(40, 0, 0, 0, 3, 0, 1, 0),
+            ::capnp::word(52, 0, 0, 0, 2, 0, 1, 0),
+            ::capnp::word(98, 108, 111, 98, 0, 0, 0, 0),
+            ::capnp::word(16, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(70, 64, 38, 97, 160, 43, 222, 175),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(16, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(108, 101, 110, 0, 0, 0, 0, 0),
+            ::capnp::word(9, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(9, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+        ];
+        pub fn get_field_types(index: u16) -> ::capnp::introspect::Type {
+            match index {
+        0 => <crate::metadata_capnp::blob_ref::Owned as ::capnp::introspect::Introspect>::introspect(),
+        1 => <u64 as ::capnp::introspect::Introspect>::introspect(),
+        _ => panic!("invalid field index {}", index),
+      }
+        }
+        pub fn get_annotation_types(
+            child_index: Option<u16>,
+            index: u32,
+        ) -> ::capnp::introspect::Type {
+            panic!("invalid annotation indices ({:?}, {}) ", child_index, index)
+        }
+        pub static RAW_SCHEMA: ::capnp::introspect::RawStructSchema =
+            ::capnp::introspect::RawStructSchema {
+                encoded_node: &ENCODED_NODE,
+                nonunion_members: NONUNION_MEMBERS,
+                members_by_discriminant: MEMBERS_BY_DISCRIMINANT,
+            };
+        pub static NONUNION_MEMBERS: &[u16] = &[0, 1];
+        pub static MEMBERS_BY_DISCRIMINANT: &[u16] = &[];
+        pub const TYPE_ID: u64 = 0x8ced_9cff_e6d2_15d2;
+    }
+}
+
+pub mod file {
+    #[derive(Copy, Clone)]
+    pub struct Owned(());
+    impl ::capnp::introspect::Introspect for Owned {
+        fn introspect() -> ::capnp::introspect::Type {
+            ::capnp::introspect::TypeVariant::Struct(::capnp::introspect::RawBrandedStructSchema {
+                generic: &_private::RAW_SCHEMA,
+                field_types: _private::get_field_types,
+                annotation_types: _private::get_annotation_types,
+            })
+            .into()
+        }
+    }
+    impl ::capnp::traits::Owned for Owned {
+        type Reader<'a> = Reader<'a>;
+        type Builder<'a> = Builder<'a>;
+    }
+    impl ::capnp::traits::OwnedStruct for Owned {
+        type Reader<'a> = Reader<'a>;
+        type Builder<'a> = Builder<'a>;
+    }
+    #[cfg(feature = "alloc")]
+    impl ::capnp::traits::Pipelined for Owned {
+        type Pipeline = Pipeline;
+    }
+
+    pub struct Reader<'a> {
+        reader: ::capnp::private::layout::StructReader<'a>,
+    }
+    impl<'a> ::core::marker::Copy for Reader<'a> {}
+    impl<'a> ::core::clone::Clone for Reader<'a> {
+        fn clone(&self) -> Self {
+            *self
+        }
+    }
+
+    impl<'a> ::capnp::traits::HasTypeId for Reader<'a> {
+        const TYPE_ID: u64 = _private::TYPE_ID;
+    }
+    impl<'a> ::core::convert::From<::capnp::private::layout::StructReader<'a>> for Reader<'a> {
+        fn from(reader: ::capnp::private::layout::StructReader<'a>) -> Self {
+            Self { reader }
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    impl<'a> ::core::convert::From<Reader<'a>> for ::capnp::dynamic_value::Reader<'a> {
+        fn from(reader: Reader<'a>) -> Self {
+            Self::Struct(::capnp::dynamic_struct::Reader::new(
+                reader.reader,
+                ::capnp::schema::StructSchema::new(::capnp::introspect::RawBrandedStructSchema {
+                    generic: &_private::RAW_SCHEMA,
+                    field_types: _private::get_field_types,
+                    annotation_types: _private::get_annotation_types,
+                }),
+            ))
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    impl<'a> ::core::fmt::Debug for Reader<'a> {
+        fn fmt(
+            &self,
+            f: &mut ::core::fmt::Formatter<'_>,
+        ) -> ::core::result::Result<(), ::core::fmt::Error> {
+            core::fmt::Debug::fmt(
+                &::core::convert::Into::<::capnp::dynamic_value::Reader<'_>>::into(*self),
+                f,
+            )
+        }
+    }
+
+    impl<'a> ::capnp::traits::FromPointerReader<'a> for Reader<'a> {
+        fn get_from_pointer(
+            reader: &::capnp::private::layout::PointerReader<'a>,
+            default: ::core::option::Option<&'a [::capnp::Word]>,
+        ) -> ::capnp::Result<Self> {
+            ::core::result::Result::Ok(reader.get_struct(default)?.into())
+        }
+    }
+
+    impl<'a> ::capnp::traits::IntoInternalStructReader<'a> for Reader<'a> {
+        fn into_internal_struct_reader(self) -> ::capnp::private::layout::StructReader<'a> {
+            self.reader
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    impl<'a> ::capnp::traits::Imbue<'a> for Reader<'a> {
+        fn imbue(&mut self, cap_table: &'a ::capnp::private::layout::CapTable) {
+            self.reader
+                .imbue(::capnp::private::layout::CapTableReader::Plain(cap_table))
+        }
+    }
+
+    impl<'a> Reader<'a> {
+        pub fn reborrow(&self) -> Reader<'_> {
+            Self { ..*self }
+        }
+
+        pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
+            self.reader.total_size()
+        }
+        #[inline]
+        pub fn get_chunks(
+            self,
+        ) -> ::capnp::Result<
+            ::capnp::struct_list::Reader<'a, crate::metadata_capnp::file_chunk::Owned>,
+        > {
+            ::capnp::traits::FromPointerReader::get_from_pointer(
+                &self.reader.get_pointer_field(0),
+                ::core::option::Option::None,
+            )
+        }
+        #[inline]
+        pub fn has_chunks(&self) -> bool {
+            !self.reader.get_pointer_field(0).is_null()
+        }
+    }
+
+    pub struct Builder<'a> {
+        builder: ::capnp::private::layout::StructBuilder<'a>,
+    }
+    impl<'a> ::capnp::traits::HasStructSize for Builder<'a> {
+        const STRUCT_SIZE: ::capnp::private::layout::StructSize =
+            ::capnp::private::layout::StructSize {
+                data: 0,
+                pointers: 1,
+            };
+    }
+    impl<'a> ::capnp::traits::HasTypeId for Builder<'a> {
+        const TYPE_ID: u64 = _private::TYPE_ID;
+    }
+    impl<'a> ::core::convert::From<::capnp::private::layout::StructBuilder<'a>> for Builder<'a> {
+        fn from(builder: ::capnp::private::layout::StructBuilder<'a>) -> Self {
+            Self { builder }
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    impl<'a> ::core::convert::From<Builder<'a>> for ::capnp::dynamic_value::Builder<'a> {
+        fn from(builder: Builder<'a>) -> Self {
+            Self::Struct(::capnp::dynamic_struct::Builder::new(
+                builder.builder,
+                ::capnp::schema::StructSchema::new(::capnp::introspect::RawBrandedStructSchema {
+                    generic: &_private::RAW_SCHEMA,
+                    field_types: _private::get_field_types,
+                    annotation_types: _private::get_annotation_types,
+                }),
+            ))
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    impl<'a> ::capnp::traits::ImbueMut<'a> for Builder<'a> {
+        fn imbue_mut(&mut self, cap_table: &'a mut ::capnp::private::layout::CapTable) {
+            self.builder
+                .imbue(::capnp::private::layout::CapTableBuilder::Plain(cap_table))
+        }
+    }
+
+    impl<'a> ::capnp::traits::FromPointerBuilder<'a> for Builder<'a> {
+        fn init_pointer(builder: ::capnp::private::layout::PointerBuilder<'a>, _size: u32) -> Self {
+            builder
+                .init_struct(<Self as ::capnp::traits::HasStructSize>::STRUCT_SIZE)
+                .into()
+        }
+        fn get_from_pointer(
+            builder: ::capnp::private::layout::PointerBuilder<'a>,
+            default: ::core::option::Option<&'a [::capnp::Word]>,
+        ) -> ::capnp::Result<Self> {
+            ::core::result::Result::Ok(
+                builder
+                    .get_struct(
+                        <Self as ::capnp::traits::HasStructSize>::STRUCT_SIZE,
+                        default,
+                    )?
+                    .into(),
+            )
+        }
+    }
+
+    impl<'a> ::capnp::traits::SetPointerBuilder for Reader<'a> {
+        fn set_pointer_builder(
+            mut pointer: ::capnp::private::layout::PointerBuilder<'_>,
+            value: Self,
+            canonicalize: bool,
+        ) -> ::capnp::Result<()> {
+            pointer.set_struct(&value.reader, canonicalize)
+        }
+    }
+
+    impl<'a> Builder<'a> {
+        pub fn into_reader(self) -> Reader<'a> {
+            self.builder.into_reader().into()
+        }
+        pub fn reborrow(&mut self) -> Builder<'_> {
+            Builder {
+                builder: self.builder.reborrow(),
+            }
+        }
+        pub fn reborrow_as_reader(&self) -> Reader<'_> {
+            self.builder.as_reader().into()
+        }
+
+        pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
+            self.builder.as_reader().total_size()
+        }
+        #[inline]
+        pub fn get_chunks(
+            self,
+        ) -> ::capnp::Result<
+            ::capnp::struct_list::Builder<'a, crate::metadata_capnp::file_chunk::Owned>,
+        > {
+            ::capnp::traits::FromPointerBuilder::get_from_pointer(
+                self.builder.get_pointer_field(0),
+                ::core::option::Option::None,
+            )
+        }
+        #[inline]
+        pub fn set_chunks(
+            &mut self,
+            value: ::capnp::struct_list::Reader<'a, crate::metadata_capnp::file_chunk::Owned>,
+        ) -> ::capnp::Result<()> {
+            ::capnp::traits::SetPointerBuilder::set_pointer_builder(
+                self.builder.reborrow().get_pointer_field(0),
+                value,
+                false,
+            )
+        }
+        #[inline]
+        pub fn init_chunks(
+            self,
+            size: u32,
+        ) -> ::capnp::struct_list::Builder<'a, crate::metadata_capnp::file_chunk::Owned> {
+            ::capnp::traits::FromPointerBuilder::init_pointer(
+                self.builder.get_pointer_field(0),
+                size,
+            )
+        }
+        #[inline]
+        pub fn has_chunks(&self) -> bool {
+            !self.builder.is_pointer_field_null(0)
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    pub struct Pipeline {
+        _typeless: ::capnp::any_pointer::Pipeline,
+    }
+    #[cfg(feature = "alloc")]
+    impl ::capnp::capability::FromTypelessPipeline for Pipeline {
+        fn new(typeless: ::capnp::any_pointer::Pipeline) -> Self {
+            Self {
+                _typeless: typeless,
+            }
+        }
+    }
+    #[cfg(feature = "alloc")]
+    impl Pipeline {}
+    mod _private {
+        pub static ENCODED_NODE: [::capnp::Word; 36] = [
+            ::capnp::word(0, 0, 0, 0, 5, 0, 6, 0),
+            ::capnp::word(212, 62, 195, 177, 129, 127, 161, 252),
+            ::capnp::word(15, 0, 0, 0, 1, 0, 0, 0),
+            ::capnp::word(183, 203, 183, 136, 110, 94, 174, 132),
+            ::capnp::word(1, 0, 7, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(21, 0, 0, 0, 162, 0, 0, 0),
+            ::capnp::word(29, 0, 0, 0, 7, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(25, 0, 0, 0, 63, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(109, 101, 116, 97, 100, 97, 116, 97),
+            ::capnp::word(46, 99, 97, 112, 110, 112, 58, 70),
+            ::capnp::word(105, 108, 101, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 1, 0, 1, 0),
+            ::capnp::word(4, 0, 0, 0, 3, 0, 4, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 1, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(13, 0, 0, 0, 58, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(8, 0, 0, 0, 3, 0, 1, 0),
+            ::capnp::word(36, 0, 0, 0, 2, 0, 1, 0),
+            ::capnp::word(99, 104, 117, 110, 107, 115, 0, 0),
+            ::capnp::word(14, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 3, 0, 1, 0),
+            ::capnp::word(16, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(210, 21, 210, 230, 255, 156, 237, 140),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(14, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+        ];
+        pub fn get_field_types(index: u16) -> ::capnp::introspect::Type {
+            match index {
+        0 => <::capnp::struct_list::Owned<crate::metadata_capnp::file_chunk::Owned> as ::capnp::introspect::Introspect>::introspect(),
+        _ => panic!("invalid field index {}", index),
+      }
+        }
+        pub fn get_annotation_types(
+            child_index: Option<u16>,
+            index: u32,
+        ) -> ::capnp::introspect::Type {
+            panic!("invalid annotation indices ({:?}, {}) ", child_index, index)
+        }
+        pub static RAW_SCHEMA: ::capnp::introspect::RawStructSchema =
+            ::capnp::introspect::RawStructSchema {
+                encoded_node: &ENCODED_NODE,
+                nonunion_members: NONUNION_MEMBERS,
+                members_by_discriminant: MEMBERS_BY_DISCRIMINANT,
+            };
+        pub static NONUNION_MEMBERS: &[u16] = &[0];
+        pub static MEMBERS_BY_DISCRIMINANT: &[u16] = &[];
+        pub const TYPE_ID: u64 = 0xfca1_7f81_b1c3_3ed4;
+    }
+}
+
+pub mod blob_ref {
+    #[derive(Copy, Clone)]
+    pub struct Owned(());
+    impl ::capnp::introspect::Introspect for Owned {
+        fn introspect() -> ::capnp::introspect::Type {
+            ::capnp::introspect::TypeVariant::Struct(::capnp::introspect::RawBrandedStructSchema {
+                generic: &_private::RAW_SCHEMA,
+                field_types: _private::get_field_types,
+                annotation_types: _private::get_annotation_types,
+            })
+            .into()
+        }
+    }
+    impl ::capnp::traits::Owned for Owned {
+        type Reader<'a> = Reader<'a>;
+        type Builder<'a> = Builder<'a>;
+    }
+    impl ::capnp::traits::OwnedStruct for Owned {
+        type Reader<'a> = Reader<'a>;
+        type Builder<'a> = Builder<'a>;
+    }
+    #[cfg(feature = "alloc")]
+    impl ::capnp::traits::Pipelined for Owned {
+        type Pipeline = Pipeline;
+    }
+
+    pub struct Reader<'a> {
+        reader: ::capnp::private::layout::StructReader<'a>,
+    }
+    impl<'a> ::core::marker::Copy for Reader<'a> {}
+    impl<'a> ::core::clone::Clone for Reader<'a> {
+        fn clone(&self) -> Self {
+            *self
+        }
+    }
+
+    impl<'a> ::capnp::traits::HasTypeId for Reader<'a> {
+        const TYPE_ID: u64 = _private::TYPE_ID;
+    }
+    impl<'a> ::core::convert::From<::capnp::private::layout::StructReader<'a>> for Reader<'a> {
+        fn from(reader: ::capnp::private::layout::StructReader<'a>) -> Self {
+            Self { reader }
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    impl<'a> ::core::convert::From<Reader<'a>> for ::capnp::dynamic_value::Reader<'a> {
+        fn from(reader: Reader<'a>) -> Self {
+            Self::Struct(::capnp::dynamic_struct::Reader::new(
+                reader.reader,
+                ::capnp::schema::StructSchema::new(::capnp::introspect::RawBrandedStructSchema {
+                    generic: &_private::RAW_SCHEMA,
+                    field_types: _private::get_field_types,
+                    annotation_types: _private::get_annotation_types,
+                }),
+            ))
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    impl<'a> ::core::fmt::Debug for Reader<'a> {
+        fn fmt(
+            &self,
+            f: &mut ::core::fmt::Formatter<'_>,
+        ) -> ::core::result::Result<(), ::core::fmt::Error> {
+            core::fmt::Debug::fmt(
+                &::core::convert::Into::<::capnp::dynamic_value::Reader<'_>>::into(*self),
+                f,
+            )
+        }
+    }
+
+    impl<'a> ::capnp::traits::FromPointerReader<'a> for Reader<'a> {
+        fn get_from_pointer(
+            reader: &::capnp::private::layout::PointerReader<'a>,
+            default: ::core::option::Option<&'a [::capnp::Word]>,
+        ) -> ::capnp::Result<Self> {
+            ::core::result::Result::Ok(reader.get_struct(default)?.into())
+        }
+    }
+
+    impl<'a> ::capnp::traits::IntoInternalStructReader<'a> for Reader<'a> {
+        fn into_internal_struct_reader(self) -> ::capnp::private::layout::StructReader<'a> {
+            self.reader
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    impl<'a> ::capnp::traits::Imbue<'a> for Reader<'a> {
+        fn imbue(&mut self, cap_table: &'a ::capnp::private::layout::CapTable) {
+            self.reader
+                .imbue(::capnp::private::layout::CapTableReader::Plain(cap_table))
+        }
+    }
+
+    impl<'a> Reader<'a> {
+        pub fn reborrow(&self) -> Reader<'_> {
+            Self { ..*self }
+        }
+
+        pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
+            self.reader.total_size()
+        }
+        #[inline]
+        pub fn get_digest(self) -> ::capnp::Result<::capnp::data::Reader<'a>> {
+            ::capnp::traits::FromPointerReader::get_from_pointer(
+                &self.reader.get_pointer_field(0),
+                ::core::option::Option::None,
+            )
+        }
+        #[inline]
+        pub fn has_digest(&self) -> bool {
+            !self.reader.get_pointer_field(0).is_null()
+        }
+        #[inline]
+        pub fn get_offset(self) -> u64 {
+            self.reader.get_data_field::<u64>(0)
+        }
+        #[inline]
+        pub fn get_compressed(self) -> bool {
+            self.reader.get_bool_field(64)
+        }
+    }
+
+    pub struct Builder<'a> {
+        builder: ::capnp::private::layout::StructBuilder<'a>,
+    }
+    impl<'a> ::capnp::traits::HasStructSize for Builder<'a> {
+        const STRUCT_SIZE: ::capnp::private::layout::StructSize =
+            ::capnp::private::layout::StructSize {
+                data: 2,
+                pointers: 1,
+            };
+    }
+    impl<'a> ::capnp::traits::HasTypeId for Builder<'a> {
+        const TYPE_ID: u64 = _private::TYPE_ID;
+    }
+    impl<'a> ::core::convert::From<::capnp::private::layout::StructBuilder<'a>> for Builder<'a> {
+        fn from(builder: ::capnp::private::layout::StructBuilder<'a>) -> Self {
+            Self { builder }
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    impl<'a> ::core::convert::From<Builder<'a>> for ::capnp::dynamic_value::Builder<'a> {
+        fn from(builder: Builder<'a>) -> Self {
+            Self::Struct(::capnp::dynamic_struct::Builder::new(
+                builder.builder,
+                ::capnp::schema::StructSchema::new(::capnp::introspect::RawBrandedStructSchema {
+                    generic: &_private::RAW_SCHEMA,
+                    field_types: _private::get_field_types,
+                    annotation_types: _private::get_annotation_types,
+                }),
+            ))
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    impl<'a> ::capnp::traits::ImbueMut<'a> for Builder<'a> {
+        fn imbue_mut(&mut self, cap_table: &'a mut ::capnp::private::layout::CapTable) {
+            self.builder
+                .imbue(::capnp::private::layout::CapTableBuilder::Plain(cap_table))
+        }
+    }
+
+    impl<'a> ::capnp::traits::FromPointerBuilder<'a> for Builder<'a> {
+        fn init_pointer(builder: ::capnp::private::layout::PointerBuilder<'a>, _size: u32) -> Self {
+            builder
+                .init_struct(<Self as ::capnp::traits::HasStructSize>::STRUCT_SIZE)
+                .into()
+        }
+        fn get_from_pointer(
+            builder: ::capnp::private::layout::PointerBuilder<'a>,
+            default: ::core::option::Option<&'a [::capnp::Word]>,
+        ) -> ::capnp::Result<Self> {
+            ::core::result::Result::Ok(
+                builder
+                    .get_struct(
+                        <Self as ::capnp::traits::HasStructSize>::STRUCT_SIZE,
+                        default,
+                    )?
+                    .into(),
+            )
+        }
+    }
+
+    impl<'a> ::capnp::traits::SetPointerBuilder for Reader<'a> {
+        fn set_pointer_builder(
+            mut pointer: ::capnp::private::layout::PointerBuilder<'_>,
+            value: Self,
+            canonicalize: bool,
+        ) -> ::capnp::Result<()> {
+            pointer.set_struct(&value.reader, canonicalize)
+        }
+    }
+
+    impl<'a> Builder<'a> {
+        pub fn into_reader(self) -> Reader<'a> {
+            self.builder.into_reader().into()
+        }
+        pub fn reborrow(&mut self) -> Builder<'_> {
+            Builder {
+                builder: self.builder.reborrow(),
+            }
+        }
+        pub fn reborrow_as_reader(&self) -> Reader<'_> {
+            self.builder.as_reader().into()
+        }
+
+        pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
+            self.builder.as_reader().total_size()
+        }
+        #[inline]
+        pub fn get_digest(self) -> ::capnp::Result<::capnp::data::Builder<'a>> {
+            ::capnp::traits::FromPointerBuilder::get_from_pointer(
+                self.builder.get_pointer_field(0),
+                ::core::option::Option::None,
+            )
+        }
+        #[inline]
+        pub fn set_digest(&mut self, value: ::capnp::data::Reader<'_>) {
+            self.builder.reborrow().get_pointer_field(0).set_data(value);
+        }
+        #[inline]
+        pub fn init_digest(self, size: u32) -> ::capnp::data::Builder<'a> {
+            self.builder.get_pointer_field(0).init_data(size)
+        }
+        #[inline]
+        pub fn has_digest(&self) -> bool {
+            !self.builder.is_pointer_field_null(0)
+        }
+        #[inline]
+        pub fn get_offset(self) -> u64 {
+            self.builder.get_data_field::<u64>(0)
+        }
+        #[inline]
+        pub fn set_offset(&mut self, value: u64) {
+            self.builder.set_data_field::<u64>(0, value);
+        }
+        #[inline]
+        pub fn get_compressed(self) -> bool {
+            self.builder.get_bool_field(64)
+        }
+        #[inline]
+        pub fn set_compressed(&mut self, value: bool) {
+            self.builder.set_bool_field(64, value);
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    pub struct Pipeline {
+        _typeless: ::capnp::any_pointer::Pipeline,
+    }
+    #[cfg(feature = "alloc")]
+    impl ::capnp::capability::FromTypelessPipeline for Pipeline {
+        fn new(typeless: ::capnp::any_pointer::Pipeline) -> Self {
+            Self {
+                _typeless: typeless,
+            }
+        }
+    }
+    #[cfg(feature = "alloc")]
+    impl Pipeline {}
+    mod _private {
+        pub static ENCODED_NODE: [::capnp::Word; 63] = [
+            ::capnp::word(0, 0, 0, 0, 5, 0, 6, 0),
+            ::capnp::word(70, 64, 38, 97, 160, 43, 222, 175),
+            ::capnp::word(15, 0, 0, 0, 1, 0, 2, 0),
+            ::capnp::word(183, 203, 183, 136, 110, 94, 174, 132),
+            ::capnp::word(1, 0, 7, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(21, 0, 0, 0, 186, 0, 0, 0),
+            ::capnp::word(29, 0, 0, 0, 7, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(25, 0, 0, 0, 175, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(109, 101, 116, 97, 100, 97, 116, 97),
+            ::capnp::word(46, 99, 97, 112, 110, 112, 58, 66),
+            ::capnp::word(108, 111, 98, 82, 101, 102, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 1, 0, 1, 0),
+            ::capnp::word(12, 0, 0, 0, 3, 0, 4, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 1, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(69, 0, 0, 0, 58, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(64, 0, 0, 0, 3, 0, 1, 0),
+            ::capnp::word(76, 0, 0, 0, 2, 0, 1, 0),
+            ::capnp::word(1, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 1, 0, 1, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(73, 0, 0, 0, 58, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(68, 0, 0, 0, 3, 0, 1, 0),
+            ::capnp::word(80, 0, 0, 0, 2, 0, 1, 0),
+            ::capnp::word(2, 0, 0, 0, 64, 0, 0, 0),
+            ::capnp::word(0, 0, 1, 0, 2, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(77, 0, 0, 0, 90, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(76, 0, 0, 0, 3, 0, 1, 0),
+            ::capnp::word(88, 0, 0, 0, 2, 0, 1, 0),
+            ::capnp::word(100, 105, 103, 101, 115, 116, 0, 0),
+            ::capnp::word(13, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(13, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(111, 102, 102, 115, 101, 116, 0, 0),
+            ::capnp::word(9, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(9, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(99, 111, 109, 112, 114, 101, 115, 115),
+            ::capnp::word(101, 100, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(1, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(1, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+        ];
+        pub fn get_field_types(index: u16) -> ::capnp::introspect::Type {
+            match index {
+                0 => <::capnp::data::Owned as ::capnp::introspect::Introspect>::introspect(),
+                1 => <u64 as ::capnp::introspect::Introspect>::introspect(),
+                2 => <bool as ::capnp::introspect::Introspect>::introspect(),
+                _ => panic!("invalid field index {}", index),
+            }
+        }
+        pub fn get_annotation_types(
+            child_index: Option<u16>,
+            index: u32,
+        ) -> ::capnp::introspect::Type {
+            panic!("invalid annotation indices ({:?}, {}) ", child_index, index)
+        }
+        pub static RAW_SCHEMA: ::capnp::introspect::RawStructSchema =
+            ::capnp::introspect::RawStructSchema {
+                encoded_node: &ENCODED_NODE,
+                nonunion_members: NONUNION_MEMBERS,
+                members_by_discriminant: MEMBERS_BY_DISCRIMINANT,
+            };
+        pub static NONUNION_MEMBERS: &[u16] = &[0, 1, 2];
+        pub static MEMBERS_BY_DISCRIMINANT: &[u16] = &[];
+        pub const TYPE_ID: u64 = 0xafde_2ba0_6126_4046;
+    }
+}
+
+pub mod xattr {
+    #[derive(Copy, Clone)]
+    pub struct Owned(());
+    impl ::capnp::introspect::Introspect for Owned {
+        fn introspect() -> ::capnp::introspect::Type {
+            ::capnp::introspect::TypeVariant::Struct(::capnp::introspect::RawBrandedStructSchema {
+                generic: &_private::RAW_SCHEMA,
+                field_types: _private::get_field_types,
+                annotation_types: _private::get_annotation_types,
+            })
+            .into()
+        }
+    }
+    impl ::capnp::traits::Owned for Owned {
+        type Reader<'a> = Reader<'a>;
+        type Builder<'a> = Builder<'a>;
+    }
+    impl ::capnp::traits::OwnedStruct for Owned {
+        type Reader<'a> = Reader<'a>;
+        type Builder<'a> = Builder<'a>;
+    }
+    #[cfg(feature = "alloc")]
+    impl ::capnp::traits::Pipelined for Owned {
+        type Pipeline = Pipeline;
+    }
+
+    pub struct Reader<'a> {
+        reader: ::capnp::private::layout::StructReader<'a>,
+    }
+    impl<'a> ::core::marker::Copy for Reader<'a> {}
+    impl<'a> ::core::clone::Clone for Reader<'a> {
+        fn clone(&self) -> Self {
+            *self
+        }
+    }
+
+    impl<'a> ::capnp::traits::HasTypeId for Reader<'a> {
+        const TYPE_ID: u64 = _private::TYPE_ID;
+    }
+    impl<'a> ::core::convert::From<::capnp::private::layout::StructReader<'a>> for Reader<'a> {
+        fn from(reader: ::capnp::private::layout::StructReader<'a>) -> Self {
+            Self { reader }
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    impl<'a> ::core::convert::From<Reader<'a>> for ::capnp::dynamic_value::Reader<'a> {
+        fn from(reader: Reader<'a>) -> Self {
+            Self::Struct(::capnp::dynamic_struct::Reader::new(
+                reader.reader,
+                ::capnp::schema::StructSchema::new(::capnp::introspect::RawBrandedStructSchema {
+                    generic: &_private::RAW_SCHEMA,
+                    field_types: _private::get_field_types,
+                    annotation_types: _private::get_annotation_types,
+                }),
+            ))
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    impl<'a> ::core::fmt::Debug for Reader<'a> {
+        fn fmt(
+            &self,
+            f: &mut ::core::fmt::Formatter<'_>,
+        ) -> ::core::result::Result<(), ::core::fmt::Error> {
+            core::fmt::Debug::fmt(
+                &::core::convert::Into::<::capnp::dynamic_value::Reader<'_>>::into(*self),
+                f,
+            )
+        }
+    }
+
+    impl<'a> ::capnp::traits::FromPointerReader<'a> for Reader<'a> {
+        fn get_from_pointer(
+            reader: &::capnp::private::layout::PointerReader<'a>,
+            default: ::core::option::Option<&'a [::capnp::Word]>,
+        ) -> ::capnp::Result<Self> {
+            ::core::result::Result::Ok(reader.get_struct(default)?.into())
+        }
+    }
+
+    impl<'a> ::capnp::traits::IntoInternalStructReader<'a> for Reader<'a> {
+        fn into_internal_struct_reader(self) -> ::capnp::private::layout::StructReader<'a> {
+            self.reader
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    impl<'a> ::capnp::traits::Imbue<'a> for Reader<'a> {
+        fn imbue(&mut self, cap_table: &'a ::capnp::private::layout::CapTable) {
+            self.reader
+                .imbue(::capnp::private::layout::CapTableReader::Plain(cap_table))
+        }
+    }
+
+    impl<'a> Reader<'a> {
+        pub fn reborrow(&self) -> Reader<'_> {
+            Self { ..*self }
+        }
+
+        pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
+            self.reader.total_size()
+        }
+        #[inline]
+        pub fn get_key(self) -> ::capnp::Result<::capnp::data::Reader<'a>> {
+            ::capnp::traits::FromPointerReader::get_from_pointer(
+                &self.reader.get_pointer_field(0),
+                ::core::option::Option::None,
+            )
+        }
+        #[inline]
+        pub fn has_key(&self) -> bool {
+            !self.reader.get_pointer_field(0).is_null()
+        }
+        #[inline]
+        pub fn get_val(self) -> ::capnp::Result<::capnp::data::Reader<'a>> {
+            ::capnp::traits::FromPointerReader::get_from_pointer(
+                &self.reader.get_pointer_field(1),
+                ::core::option::Option::None,
+            )
+        }
+        #[inline]
+        pub fn has_val(&self) -> bool {
+            !self.reader.get_pointer_field(1).is_null()
+        }
+    }
+
+    pub struct Builder<'a> {
+        builder: ::capnp::private::layout::StructBuilder<'a>,
+    }
+    impl<'a> ::capnp::traits::HasStructSize for Builder<'a> {
+        const STRUCT_SIZE: ::capnp::private::layout::StructSize =
+            ::capnp::private::layout::StructSize {
+                data: 0,
+                pointers: 2,
+            };
+    }
+    impl<'a> ::capnp::traits::HasTypeId for Builder<'a> {
+        const TYPE_ID: u64 = _private::TYPE_ID;
+    }
+    impl<'a> ::core::convert::From<::capnp::private::layout::StructBuilder<'a>> for Builder<'a> {
+        fn from(builder: ::capnp::private::layout::StructBuilder<'a>) -> Self {
+            Self { builder }
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    impl<'a> ::core::convert::From<Builder<'a>> for ::capnp::dynamic_value::Builder<'a> {
+        fn from(builder: Builder<'a>) -> Self {
+            Self::Struct(::capnp::dynamic_struct::Builder::new(
+                builder.builder,
+                ::capnp::schema::StructSchema::new(::capnp::introspect::RawBrandedStructSchema {
+                    generic: &_private::RAW_SCHEMA,
+                    field_types: _private::get_field_types,
+                    annotation_types: _private::get_annotation_types,
+                }),
+            ))
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    impl<'a> ::capnp::traits::ImbueMut<'a> for Builder<'a> {
+        fn imbue_mut(&mut self, cap_table: &'a mut ::capnp::private::layout::CapTable) {
+            self.builder
+                .imbue(::capnp::private::layout::CapTableBuilder::Plain(cap_table))
+        }
+    }
+
+    impl<'a> ::capnp::traits::FromPointerBuilder<'a> for Builder<'a> {
+        fn init_pointer(builder: ::capnp::private::layout::PointerBuilder<'a>, _size: u32) -> Self {
+            builder
+                .init_struct(<Self as ::capnp::traits::HasStructSize>::STRUCT_SIZE)
+                .into()
+        }
+        fn get_from_pointer(
+            builder: ::capnp::private::layout::PointerBuilder<'a>,
+            default: ::core::option::Option<&'a [::capnp::Word]>,
+        ) -> ::capnp::Result<Self> {
+            ::core::result::Result::Ok(
+                builder
+                    .get_struct(
+                        <Self as ::capnp::traits::HasStructSize>::STRUCT_SIZE,
+                        default,
+                    )?
+                    .into(),
+            )
+        }
+    }
+
+    impl<'a> ::capnp::traits::SetPointerBuilder for Reader<'a> {
+        fn set_pointer_builder(
+            mut pointer: ::capnp::private::layout::PointerBuilder<'_>,
+            value: Self,
+            canonicalize: bool,
+        ) -> ::capnp::Result<()> {
+            pointer.set_struct(&value.reader, canonicalize)
+        }
+    }
+
+    impl<'a> Builder<'a> {
+        pub fn into_reader(self) -> Reader<'a> {
+            self.builder.into_reader().into()
+        }
+        pub fn reborrow(&mut self) -> Builder<'_> {
+            Builder {
+                builder: self.builder.reborrow(),
+            }
+        }
+        pub fn reborrow_as_reader(&self) -> Reader<'_> {
+            self.builder.as_reader().into()
+        }
+
+        pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
+            self.builder.as_reader().total_size()
+        }
+        #[inline]
+        pub fn get_key(self) -> ::capnp::Result<::capnp::data::Builder<'a>> {
+            ::capnp::traits::FromPointerBuilder::get_from_pointer(
+                self.builder.get_pointer_field(0),
+                ::core::option::Option::None,
+            )
+        }
+        #[inline]
+        pub fn set_key(&mut self, value: ::capnp::data::Reader<'_>) {
+            self.builder.reborrow().get_pointer_field(0).set_data(value);
+        }
+        #[inline]
+        pub fn init_key(self, size: u32) -> ::capnp::data::Builder<'a> {
+            self.builder.get_pointer_field(0).init_data(size)
+        }
+        #[inline]
+        pub fn has_key(&self) -> bool {
+            !self.builder.is_pointer_field_null(0)
+        }
+        #[inline]
+        pub fn get_val(self) -> ::capnp::Result<::capnp::data::Builder<'a>> {
+            ::capnp::traits::FromPointerBuilder::get_from_pointer(
+                self.builder.get_pointer_field(1),
+                ::core::option::Option::None,
+            )
+        }
+        #[inline]
+        pub fn set_val(&mut self, value: ::capnp::data::Reader<'_>) {
+            self.builder.reborrow().get_pointer_field(1).set_data(value);
+        }
+        #[inline]
+        pub fn init_val(self, size: u32) -> ::capnp::data::Builder<'a> {
+            self.builder.get_pointer_field(1).init_data(size)
+        }
+        #[inline]
+        pub fn has_val(&self) -> bool {
+            !self.builder.is_pointer_field_null(1)
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    pub struct Pipeline {
+        _typeless: ::capnp::any_pointer::Pipeline,
+    }
+    #[cfg(feature = "alloc")]
+    impl ::capnp::capability::FromTypelessPipeline for Pipeline {
+        fn new(typeless: ::capnp::any_pointer::Pipeline) -> Self {
+            Self {
+                _typeless: typeless,
+            }
+        }
+    }
+    #[cfg(feature = "alloc")]
+    impl Pipeline {}
+    mod _private {
+        pub static ENCODED_NODE: [::capnp::Word; 47] = [
+            ::capnp::word(0, 0, 0, 0, 5, 0, 6, 0),
+            ::capnp::word(64, 205, 159, 7, 194, 75, 43, 217),
+            ::capnp::word(15, 0, 0, 0, 1, 0, 0, 0),
+            ::capnp::word(183, 203, 183, 136, 110, 94, 174, 132),
+            ::capnp::word(2, 0, 7, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(21, 0, 0, 0, 170, 0, 0, 0),
+            ::capnp::word(29, 0, 0, 0, 7, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(25, 0, 0, 0, 119, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(109, 101, 116, 97, 100, 97, 116, 97),
+            ::capnp::word(46, 99, 97, 112, 110, 112, 58, 88),
+            ::capnp::word(97, 116, 116, 114, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 1, 0, 1, 0),
+            ::capnp::word(8, 0, 0, 0, 3, 0, 4, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 1, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(41, 0, 0, 0, 34, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(36, 0, 0, 0, 3, 0, 1, 0),
+            ::capnp::word(48, 0, 0, 0, 2, 0, 1, 0),
+            ::capnp::word(1, 0, 0, 0, 1, 0, 0, 0),
+            ::capnp::word(0, 0, 1, 0, 1, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(45, 0, 0, 0, 34, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(40, 0, 0, 0, 3, 0, 1, 0),
+            ::capnp::word(52, 0, 0, 0, 2, 0, 1, 0),
+            ::capnp::word(107, 101, 121, 0, 0, 0, 0, 0),
+            ::capnp::word(13, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(13, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(118, 97, 108, 0, 0, 0, 0, 0),
+            ::capnp::word(13, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(13, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+        ];
+        pub fn get_field_types(index: u16) -> ::capnp::introspect::Type {
+            match index {
+                0 => <::capnp::data::Owned as ::capnp::introspect::Introspect>::introspect(),
+                1 => <::capnp::data::Owned as ::capnp::introspect::Introspect>::introspect(),
+                _ => panic!("invalid field index {}", index),
+            }
+        }
+        pub fn get_annotation_types(
+            child_index: Option<u16>,
+            index: u32,
+        ) -> ::capnp::introspect::Type {
+            panic!("invalid annotation indices ({:?}, {}) ", child_index, index)
+        }
+        pub static RAW_SCHEMA: ::capnp::introspect::RawStructSchema =
+            ::capnp::introspect::RawStructSchema {
+                encoded_node: &ENCODED_NODE,
+                nonunion_members: NONUNION_MEMBERS,
+                members_by_discriminant: MEMBERS_BY_DISCRIMINANT,
+            };
+        pub static NONUNION_MEMBERS: &[u16] = &[0, 1];
+        pub static MEMBERS_BY_DISCRIMINANT: &[u16] = &[];
+        pub const TYPE_ID: u64 = 0xd92b_4bc2_079f_cd40;
+    }
+}
+
+pub mod inode_additional {
+    #[derive(Copy, Clone)]
+    pub struct Owned(());
+    impl ::capnp::introspect::Introspect for Owned {
+        fn introspect() -> ::capnp::introspect::Type {
+            ::capnp::introspect::TypeVariant::Struct(::capnp::introspect::RawBrandedStructSchema {
+                generic: &_private::RAW_SCHEMA,
+                field_types: _private::get_field_types,
+                annotation_types: _private::get_annotation_types,
+            })
+            .into()
+        }
+    }
+    impl ::capnp::traits::Owned for Owned {
+        type Reader<'a> = Reader<'a>;
+        type Builder<'a> = Builder<'a>;
+    }
+    impl ::capnp::traits::OwnedStruct for Owned {
+        type Reader<'a> = Reader<'a>;
+        type Builder<'a> = Builder<'a>;
+    }
+    #[cfg(feature = "alloc")]
+    impl ::capnp::traits::Pipelined for Owned {
+        type Pipeline = Pipeline;
+    }
+
+    pub struct Reader<'a> {
+        reader: ::capnp::private::layout::StructReader<'a>,
+    }
+    impl<'a> ::core::marker::Copy for Reader<'a> {}
+    impl<'a> ::core::clone::Clone for Reader<'a> {
+        fn clone(&self) -> Self {
+            *self
+        }
+    }
+
+    impl<'a> ::capnp::traits::HasTypeId for Reader<'a> {
+        const TYPE_ID: u64 = _private::TYPE_ID;
+    }
+    impl<'a> ::core::convert::From<::capnp::private::layout::StructReader<'a>> for Reader<'a> {
+        fn from(reader: ::capnp::private::layout::StructReader<'a>) -> Self {
+            Self { reader }
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    impl<'a> ::core::convert::From<Reader<'a>> for ::capnp::dynamic_value::Reader<'a> {
+        fn from(reader: Reader<'a>) -> Self {
+            Self::Struct(::capnp::dynamic_struct::Reader::new(
+                reader.reader,
+                ::capnp::schema::StructSchema::new(::capnp::introspect::RawBrandedStructSchema {
+                    generic: &_private::RAW_SCHEMA,
+                    field_types: _private::get_field_types,
+                    annotation_types: _private::get_annotation_types,
+                }),
+            ))
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    impl<'a> ::core::fmt::Debug for Reader<'a> {
+        fn fmt(
+            &self,
+            f: &mut ::core::fmt::Formatter<'_>,
+        ) -> ::core::result::Result<(), ::core::fmt::Error> {
+            core::fmt::Debug::fmt(
+                &::core::convert::Into::<::capnp::dynamic_value::Reader<'_>>::into(*self),
+                f,
+            )
+        }
+    }
+
+    impl<'a> ::capnp::traits::FromPointerReader<'a> for Reader<'a> {
+        fn get_from_pointer(
+            reader: &::capnp::private::layout::PointerReader<'a>,
+            default: ::core::option::Option<&'a [::capnp::Word]>,
+        ) -> ::capnp::Result<Self> {
+            ::core::result::Result::Ok(reader.get_struct(default)?.into())
+        }
+    }
+
+    impl<'a> ::capnp::traits::IntoInternalStructReader<'a> for Reader<'a> {
+        fn into_internal_struct_reader(self) -> ::capnp::private::layout::StructReader<'a> {
+            self.reader
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    impl<'a> ::capnp::traits::Imbue<'a> for Reader<'a> {
+        fn imbue(&mut self, cap_table: &'a ::capnp::private::layout::CapTable) {
+            self.reader
+                .imbue(::capnp::private::layout::CapTableReader::Plain(cap_table))
+        }
+    }
+
+    impl<'a> Reader<'a> {
+        pub fn reborrow(&self) -> Reader<'_> {
+            Self { ..*self }
+        }
+
+        pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
+            self.reader.total_size()
+        }
+        #[inline]
+        pub fn get_xattrs(
+            self,
+        ) -> ::capnp::Result<::capnp::struct_list::Reader<'a, crate::metadata_capnp::xattr::Owned>>
+        {
+            ::capnp::traits::FromPointerReader::get_from_pointer(
+                &self.reader.get_pointer_field(0),
+                ::core::option::Option::None,
+            )
+        }
+        #[inline]
+        pub fn has_xattrs(&self) -> bool {
+            !self.reader.get_pointer_field(0).is_null()
+        }
+        #[inline]
+        pub fn get_symlink_target(self) -> ::capnp::Result<::capnp::data::Reader<'a>> {
+            ::capnp::traits::FromPointerReader::get_from_pointer(
+                &self.reader.get_pointer_field(1),
+                ::core::option::Option::None,
+            )
+        }
+        #[inline]
+        pub fn has_symlink_target(&self) -> bool {
+            !self.reader.get_pointer_field(1).is_null()
+        }
+    }
+
+    pub struct Builder<'a> {
+        builder: ::capnp::private::layout::StructBuilder<'a>,
+    }
+    impl<'a> ::capnp::traits::HasStructSize for Builder<'a> {
+        const STRUCT_SIZE: ::capnp::private::layout::StructSize =
+            ::capnp::private::layout::StructSize {
+                data: 0,
+                pointers: 2,
+            };
+    }
+    impl<'a> ::capnp::traits::HasTypeId for Builder<'a> {
+        const TYPE_ID: u64 = _private::TYPE_ID;
+    }
+    impl<'a> ::core::convert::From<::capnp::private::layout::StructBuilder<'a>> for Builder<'a> {
+        fn from(builder: ::capnp::private::layout::StructBuilder<'a>) -> Self {
+            Self { builder }
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    impl<'a> ::core::convert::From<Builder<'a>> for ::capnp::dynamic_value::Builder<'a> {
+        fn from(builder: Builder<'a>) -> Self {
+            Self::Struct(::capnp::dynamic_struct::Builder::new(
+                builder.builder,
+                ::capnp::schema::StructSchema::new(::capnp::introspect::RawBrandedStructSchema {
+                    generic: &_private::RAW_SCHEMA,
+                    field_types: _private::get_field_types,
+                    annotation_types: _private::get_annotation_types,
+                }),
+            ))
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    impl<'a> ::capnp::traits::ImbueMut<'a> for Builder<'a> {
+        fn imbue_mut(&mut self, cap_table: &'a mut ::capnp::private::layout::CapTable) {
+            self.builder
+                .imbue(::capnp::private::layout::CapTableBuilder::Plain(cap_table))
+        }
+    }
+
+    impl<'a> ::capnp::traits::FromPointerBuilder<'a> for Builder<'a> {
+        fn init_pointer(builder: ::capnp::private::layout::PointerBuilder<'a>, _size: u32) -> Self {
+            builder
+                .init_struct(<Self as ::capnp::traits::HasStructSize>::STRUCT_SIZE)
+                .into()
+        }
+        fn get_from_pointer(
+            builder: ::capnp::private::layout::PointerBuilder<'a>,
+            default: ::core::option::Option<&'a [::capnp::Word]>,
+        ) -> ::capnp::Result<Self> {
+            ::core::result::Result::Ok(
+                builder
+                    .get_struct(
+                        <Self as ::capnp::traits::HasStructSize>::STRUCT_SIZE,
+                        default,
+                    )?
+                    .into(),
+            )
+        }
+    }
+
+    impl<'a> ::capnp::traits::SetPointerBuilder for Reader<'a> {
+        fn set_pointer_builder(
+            mut pointer: ::capnp::private::layout::PointerBuilder<'_>,
+            value: Self,
+            canonicalize: bool,
+        ) -> ::capnp::Result<()> {
+            pointer.set_struct(&value.reader, canonicalize)
+        }
+    }
+
+    impl<'a> Builder<'a> {
+        pub fn into_reader(self) -> Reader<'a> {
+            self.builder.into_reader().into()
+        }
+        pub fn reborrow(&mut self) -> Builder<'_> {
+            Builder {
+                builder: self.builder.reborrow(),
+            }
+        }
+        pub fn reborrow_as_reader(&self) -> Reader<'_> {
+            self.builder.as_reader().into()
+        }
+
+        pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
+            self.builder.as_reader().total_size()
+        }
+        #[inline]
+        pub fn get_xattrs(
+            self,
+        ) -> ::capnp::Result<::capnp::struct_list::Builder<'a, crate::metadata_capnp::xattr::Owned>>
+        {
+            ::capnp::traits::FromPointerBuilder::get_from_pointer(
+                self.builder.get_pointer_field(0),
+                ::core::option::Option::None,
+            )
+        }
+        #[inline]
+        pub fn set_xattrs(
+            &mut self,
+            value: ::capnp::struct_list::Reader<'a, crate::metadata_capnp::xattr::Owned>,
+        ) -> ::capnp::Result<()> {
+            ::capnp::traits::SetPointerBuilder::set_pointer_builder(
+                self.builder.reborrow().get_pointer_field(0),
+                value,
+                false,
+            )
+        }
+        #[inline]
+        pub fn init_xattrs(
+            self,
+            size: u32,
+        ) -> ::capnp::struct_list::Builder<'a, crate::metadata_capnp::xattr::Owned> {
+            ::capnp::traits::FromPointerBuilder::init_pointer(
+                self.builder.get_pointer_field(0),
+                size,
+            )
+        }
+        #[inline]
+        pub fn has_xattrs(&self) -> bool {
+            !self.builder.is_pointer_field_null(0)
+        }
+        #[inline]
+        pub fn get_symlink_target(self) -> ::capnp::Result<::capnp::data::Builder<'a>> {
+            ::capnp::traits::FromPointerBuilder::get_from_pointer(
+                self.builder.get_pointer_field(1),
+                ::core::option::Option::None,
+            )
+        }
+        #[inline]
+        pub fn set_symlink_target(&mut self, value: ::capnp::data::Reader<'_>) {
+            self.builder.reborrow().get_pointer_field(1).set_data(value);
+        }
+        #[inline]
+        pub fn init_symlink_target(self, size: u32) -> ::capnp::data::Builder<'a> {
+            self.builder.get_pointer_field(1).init_data(size)
+        }
+        #[inline]
+        pub fn has_symlink_target(&self) -> bool {
+            !self.builder.is_pointer_field_null(1)
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    pub struct Pipeline {
+        _typeless: ::capnp::any_pointer::Pipeline,
+    }
+    #[cfg(feature = "alloc")]
+    impl ::capnp::capability::FromTypelessPipeline for Pipeline {
+        fn new(typeless: ::capnp::any_pointer::Pipeline) -> Self {
+            Self {
+                _typeless: typeless,
+            }
+        }
+    }
+    #[cfg(feature = "alloc")]
+    impl Pipeline {}
+    mod _private {
+        pub static ENCODED_NODE: [::capnp::Word; 53] = [
+            ::capnp::word(0, 0, 0, 0, 5, 0, 6, 0),
+            ::capnp::word(211, 144, 125, 86, 42, 166, 175, 153),
+            ::capnp::word(15, 0, 0, 0, 1, 0, 0, 0),
+            ::capnp::word(183, 203, 183, 136, 110, 94, 174, 132),
+            ::capnp::word(2, 0, 7, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(21, 0, 0, 0, 250, 0, 0, 0),
+            ::capnp::word(33, 0, 0, 0, 7, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(29, 0, 0, 0, 119, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(109, 101, 116, 97, 100, 97, 116, 97),
+            ::capnp::word(46, 99, 97, 112, 110, 112, 58, 73),
+            ::capnp::word(110, 111, 100, 101, 65, 100, 100, 105),
+            ::capnp::word(116, 105, 111, 110, 97, 108, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 1, 0, 1, 0),
+            ::capnp::word(8, 0, 0, 0, 3, 0, 4, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 1, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(41, 0, 0, 0, 58, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(36, 0, 0, 0, 3, 0, 1, 0),
+            ::capnp::word(64, 0, 0, 0, 2, 0, 1, 0),
+            ::capnp::word(1, 0, 0, 0, 1, 0, 0, 0),
+            ::capnp::word(0, 0, 1, 0, 1, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(61, 0, 0, 0, 114, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(60, 0, 0, 0, 3, 0, 1, 0),
+            ::capnp::word(72, 0, 0, 0, 2, 0, 1, 0),
+            ::capnp::word(120, 97, 116, 116, 114, 115, 0, 0),
+            ::capnp::word(14, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 3, 0, 1, 0),
+            ::capnp::word(16, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(64, 205, 159, 7, 194, 75, 43, 217),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(14, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(115, 121, 109, 108, 105, 110, 107, 84),
+            ::capnp::word(97, 114, 103, 101, 116, 0, 0, 0),
+            ::capnp::word(13, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(13, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+        ];
+        pub fn get_field_types(index: u16) -> ::capnp::introspect::Type {
+            match index {
+        0 => <::capnp::struct_list::Owned<crate::metadata_capnp::xattr::Owned> as ::capnp::introspect::Introspect>::introspect(),
+        1 => <::capnp::data::Owned as ::capnp::introspect::Introspect>::introspect(),
+        _ => panic!("invalid field index {}", index),
+      }
+        }
+        pub fn get_annotation_types(
+            child_index: Option<u16>,
+            index: u32,
+        ) -> ::capnp::introspect::Type {
+            panic!("invalid annotation indices ({:?}, {}) ", child_index, index)
+        }
+        pub static RAW_SCHEMA: ::capnp::introspect::RawStructSchema =
+            ::capnp::introspect::RawStructSchema {
+                encoded_node: &ENCODED_NODE,
+                nonunion_members: NONUNION_MEMBERS,
+                members_by_discriminant: MEMBERS_BY_DISCRIMINANT,
+            };
+        pub static NONUNION_MEMBERS: &[u16] = &[0, 1];
+        pub static MEMBERS_BY_DISCRIMINANT: &[u16] = &[];
+        pub const TYPE_ID: u64 = 0x99af_a62a_567d_90d3;
+    }
+}
+
+pub mod inode {
+    #[derive(Copy, Clone)]
+    pub struct Owned(());
+    impl ::capnp::introspect::Introspect for Owned {
+        fn introspect() -> ::capnp::introspect::Type {
+            ::capnp::introspect::TypeVariant::Struct(::capnp::introspect::RawBrandedStructSchema {
+                generic: &_private::RAW_SCHEMA,
+                field_types: _private::get_field_types,
+                annotation_types: _private::get_annotation_types,
+            })
+            .into()
+        }
+    }
+    impl ::capnp::traits::Owned for Owned {
+        type Reader<'a> = Reader<'a>;
+        type Builder<'a> = Builder<'a>;
+    }
+    impl ::capnp::traits::OwnedStruct for Owned {
+        type Reader<'a> = Reader<'a>;
+        type Builder<'a> = Builder<'a>;
+    }
+    #[cfg(feature = "alloc")]
+    impl ::capnp::traits::Pipelined for Owned {
+        type Pipeline = Pipeline;
+    }
+
+    pub struct Reader<'a> {
+        reader: ::capnp::private::layout::StructReader<'a>,
+    }
+    impl<'a> ::core::marker::Copy for Reader<'a> {}
+    impl<'a> ::core::clone::Clone for Reader<'a> {
+        fn clone(&self) -> Self {
+            *self
+        }
+    }
+
+    impl<'a> ::capnp::traits::HasTypeId for Reader<'a> {
+        const TYPE_ID: u64 = _private::TYPE_ID;
+    }
+    impl<'a> ::core::convert::From<::capnp::private::layout::StructReader<'a>> for Reader<'a> {
+        fn from(reader: ::capnp::private::layout::StructReader<'a>) -> Self {
+            Self { reader }
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    impl<'a> ::core::convert::From<Reader<'a>> for ::capnp::dynamic_value::Reader<'a> {
+        fn from(reader: Reader<'a>) -> Self {
+            Self::Struct(::capnp::dynamic_struct::Reader::new(
+                reader.reader,
+                ::capnp::schema::StructSchema::new(::capnp::introspect::RawBrandedStructSchema {
+                    generic: &_private::RAW_SCHEMA,
+                    field_types: _private::get_field_types,
+                    annotation_types: _private::get_annotation_types,
+                }),
+            ))
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    impl<'a> ::core::fmt::Debug for Reader<'a> {
+        fn fmt(
+            &self,
+            f: &mut ::core::fmt::Formatter<'_>,
+        ) -> ::core::result::Result<(), ::core::fmt::Error> {
+            core::fmt::Debug::fmt(
+                &::core::convert::Into::<::capnp::dynamic_value::Reader<'_>>::into(*self),
+                f,
+            )
+        }
+    }
+
+    impl<'a> ::capnp::traits::FromPointerReader<'a> for Reader<'a> {
+        fn get_from_pointer(
+            reader: &::capnp::private::layout::PointerReader<'a>,
+            default: ::core::option::Option<&'a [::capnp::Word]>,
+        ) -> ::capnp::Result<Self> {
+            ::core::result::Result::Ok(reader.get_struct(default)?.into())
+        }
+    }
+
+    impl<'a> ::capnp::traits::IntoInternalStructReader<'a> for Reader<'a> {
+        fn into_internal_struct_reader(self) -> ::capnp::private::layout::StructReader<'a> {
+            self.reader
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    impl<'a> ::capnp::traits::Imbue<'a> for Reader<'a> {
+        fn imbue(&mut self, cap_table: &'a ::capnp::private::layout::CapTable) {
+            self.reader
+                .imbue(::capnp::private::layout::CapTableReader::Plain(cap_table))
+        }
+    }
+
+    impl<'a> Reader<'a> {
+        pub fn reborrow(&self) -> Reader<'_> {
+            Self { ..*self }
+        }
+
+        pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
+            self.reader.total_size()
+        }
+        #[inline]
+        pub fn get_ino(self) -> u64 {
+            self.reader.get_data_field::<u64>(0)
+        }
+        #[inline]
+        pub fn get_mode(self) -> crate::metadata_capnp::inode::mode::Reader<'a> {
+            self.reader.into()
+        }
+        #[inline]
+        pub fn get_uid(self) -> u32 {
+            self.reader.get_data_field::<u32>(3)
+        }
+        #[inline]
+        pub fn get_gid(self) -> u32 {
+            self.reader.get_data_field::<u32>(4)
+        }
+        #[inline]
+        pub fn get_permissions(self) -> u16 {
+            self.reader.get_data_field::<u16>(5)
+        }
+        #[inline]
+        pub fn get_additional(
+            self,
+        ) -> ::capnp::Result<crate::metadata_capnp::inode_additional::Reader<'a>> {
+            ::capnp::traits::FromPointerReader::get_from_pointer(
+                &self.reader.get_pointer_field(1),
+                ::core::option::Option::None,
+            )
+        }
+        #[inline]
+        pub fn has_additional(&self) -> bool {
+            !self.reader.get_pointer_field(1).is_null()
+        }
+    }
+
+    pub struct Builder<'a> {
+        builder: ::capnp::private::layout::StructBuilder<'a>,
+    }
+    impl<'a> ::capnp::traits::HasStructSize for Builder<'a> {
+        const STRUCT_SIZE: ::capnp::private::layout::StructSize =
+            ::capnp::private::layout::StructSize {
+                data: 3,
+                pointers: 2,
+            };
+    }
+    impl<'a> ::capnp::traits::HasTypeId for Builder<'a> {
+        const TYPE_ID: u64 = _private::TYPE_ID;
+    }
+    impl<'a> ::core::convert::From<::capnp::private::layout::StructBuilder<'a>> for Builder<'a> {
+        fn from(builder: ::capnp::private::layout::StructBuilder<'a>) -> Self {
+            Self { builder }
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    impl<'a> ::core::convert::From<Builder<'a>> for ::capnp::dynamic_value::Builder<'a> {
+        fn from(builder: Builder<'a>) -> Self {
+            Self::Struct(::capnp::dynamic_struct::Builder::new(
+                builder.builder,
+                ::capnp::schema::StructSchema::new(::capnp::introspect::RawBrandedStructSchema {
+                    generic: &_private::RAW_SCHEMA,
+                    field_types: _private::get_field_types,
+                    annotation_types: _private::get_annotation_types,
+                }),
+            ))
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    impl<'a> ::capnp::traits::ImbueMut<'a> for Builder<'a> {
+        fn imbue_mut(&mut self, cap_table: &'a mut ::capnp::private::layout::CapTable) {
+            self.builder
+                .imbue(::capnp::private::layout::CapTableBuilder::Plain(cap_table))
+        }
+    }
+
+    impl<'a> ::capnp::traits::FromPointerBuilder<'a> for Builder<'a> {
+        fn init_pointer(builder: ::capnp::private::layout::PointerBuilder<'a>, _size: u32) -> Self {
+            builder
+                .init_struct(<Self as ::capnp::traits::HasStructSize>::STRUCT_SIZE)
+                .into()
+        }
+        fn get_from_pointer(
+            builder: ::capnp::private::layout::PointerBuilder<'a>,
+            default: ::core::option::Option<&'a [::capnp::Word]>,
+        ) -> ::capnp::Result<Self> {
+            ::core::result::Result::Ok(
+                builder
+                    .get_struct(
+                        <Self as ::capnp::traits::HasStructSize>::STRUCT_SIZE,
+                        default,
+                    )?
+                    .into(),
+            )
+        }
+    }
+
+    impl<'a> ::capnp::traits::SetPointerBuilder for Reader<'a> {
+        fn set_pointer_builder(
+            mut pointer: ::capnp::private::layout::PointerBuilder<'_>,
+            value: Self,
+            canonicalize: bool,
+        ) -> ::capnp::Result<()> {
+            pointer.set_struct(&value.reader, canonicalize)
+        }
+    }
+
+    impl<'a> Builder<'a> {
+        pub fn into_reader(self) -> Reader<'a> {
+            self.builder.into_reader().into()
+        }
+        pub fn reborrow(&mut self) -> Builder<'_> {
+            Builder {
+                builder: self.builder.reborrow(),
+            }
+        }
+        pub fn reborrow_as_reader(&self) -> Reader<'_> {
+            self.builder.as_reader().into()
+        }
+
+        pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
+            self.builder.as_reader().total_size()
+        }
+        #[inline]
+        pub fn get_ino(self) -> u64 {
+            self.builder.get_data_field::<u64>(0)
+        }
+        #[inline]
+        pub fn set_ino(&mut self, value: u64) {
+            self.builder.set_data_field::<u64>(0, value);
+        }
+        #[inline]
+        pub fn get_mode(self) -> crate::metadata_capnp::inode::mode::Builder<'a> {
+            self.builder.into()
+        }
+        #[inline]
+        pub fn init_mode(mut self) -> crate::metadata_capnp::inode::mode::Builder<'a> {
+            self.builder.set_data_field::<u16>(4, 0);
+            self.builder.reborrow().get_pointer_field(0).clear();
+            self.builder.into()
+        }
+        #[inline]
+        pub fn get_uid(self) -> u32 {
+            self.builder.get_data_field::<u32>(3)
+        }
+        #[inline]
+        pub fn set_uid(&mut self, value: u32) {
+            self.builder.set_data_field::<u32>(3, value);
+        }
+        #[inline]
+        pub fn get_gid(self) -> u32 {
+            self.builder.get_data_field::<u32>(4)
+        }
+        #[inline]
+        pub fn set_gid(&mut self, value: u32) {
+            self.builder.set_data_field::<u32>(4, value);
+        }
+        #[inline]
+        pub fn get_permissions(self) -> u16 {
+            self.builder.get_data_field::<u16>(5)
+        }
+        #[inline]
+        pub fn set_permissions(&mut self, value: u16) {
+            self.builder.set_data_field::<u16>(5, value);
+        }
+        #[inline]
+        pub fn get_additional(
+            self,
+        ) -> ::capnp::Result<crate::metadata_capnp::inode_additional::Builder<'a>> {
+            ::capnp::traits::FromPointerBuilder::get_from_pointer(
+                self.builder.get_pointer_field(1),
+                ::core::option::Option::None,
+            )
+        }
+        #[inline]
+        pub fn set_additional(
+            &mut self,
+            value: crate::metadata_capnp::inode_additional::Reader<'_>,
+        ) -> ::capnp::Result<()> {
+            ::capnp::traits::SetPointerBuilder::set_pointer_builder(
+                self.builder.reborrow().get_pointer_field(1),
+                value,
+                false,
+            )
+        }
+        #[inline]
+        pub fn init_additional(self) -> crate::metadata_capnp::inode_additional::Builder<'a> {
+            ::capnp::traits::FromPointerBuilder::init_pointer(self.builder.get_pointer_field(1), 0)
+        }
+        #[inline]
+        pub fn has_additional(&self) -> bool {
+            !self.builder.is_pointer_field_null(1)
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    pub struct Pipeline {
+        _typeless: ::capnp::any_pointer::Pipeline,
+    }
+    #[cfg(feature = "alloc")]
+    impl ::capnp::capability::FromTypelessPipeline for Pipeline {
+        fn new(typeless: ::capnp::any_pointer::Pipeline) -> Self {
+            Self {
+                _typeless: typeless,
+            }
+        }
+    }
+    #[cfg(feature = "alloc")]
+    impl Pipeline {
+        pub fn get_mode(&self) -> crate::metadata_capnp::inode::mode::Pipeline {
+            ::capnp::capability::FromTypelessPipeline::new(self._typeless.noop())
+        }
+        pub fn get_additional(&self) -> crate::metadata_capnp::inode_additional::Pipeline {
+            ::capnp::capability::FromTypelessPipeline::new(self._typeless.get_pointer_field(1))
+        }
+    }
+    mod _private {
+        pub static ENCODED_NODE: [::capnp::Word; 102] = [
+            ::capnp::word(0, 0, 0, 0, 5, 0, 6, 0),
+            ::capnp::word(161, 114, 128, 2, 173, 206, 117, 185),
+            ::capnp::word(15, 0, 0, 0, 1, 0, 3, 0),
+            ::capnp::word(183, 203, 183, 136, 110, 94, 174, 132),
+            ::capnp::word(2, 0, 7, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(21, 0, 0, 0, 170, 0, 0, 0),
+            ::capnp::word(29, 0, 0, 0, 7, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(25, 0, 0, 0, 87, 1, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(109, 101, 116, 97, 100, 97, 116, 97),
+            ::capnp::word(46, 99, 97, 112, 110, 112, 58, 73),
+            ::capnp::word(110, 111, 100, 101, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 1, 0, 1, 0),
+            ::capnp::word(24, 0, 0, 0, 3, 0, 4, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 1, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(153, 0, 0, 0, 34, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(148, 0, 0, 0, 3, 0, 1, 0),
+            ::capnp::word(160, 0, 0, 0, 2, 0, 1, 0),
+            ::capnp::word(1, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(1, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(54, 46, 90, 92, 135, 239, 140, 194),
+            ::capnp::word(157, 0, 0, 0, 42, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(2, 0, 0, 0, 3, 0, 0, 0),
+            ::capnp::word(0, 0, 1, 0, 10, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(133, 0, 0, 0, 34, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(128, 0, 0, 0, 3, 0, 1, 0),
+            ::capnp::word(140, 0, 0, 0, 2, 0, 1, 0),
+            ::capnp::word(3, 0, 0, 0, 4, 0, 0, 0),
+            ::capnp::word(0, 0, 1, 0, 11, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(137, 0, 0, 0, 34, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(132, 0, 0, 0, 3, 0, 1, 0),
+            ::capnp::word(144, 0, 0, 0, 2, 0, 1, 0),
+            ::capnp::word(4, 0, 0, 0, 5, 0, 0, 0),
+            ::capnp::word(0, 0, 1, 0, 12, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(141, 0, 0, 0, 98, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(140, 0, 0, 0, 3, 0, 1, 0),
+            ::capnp::word(152, 0, 0, 0, 2, 0, 1, 0),
+            ::capnp::word(5, 0, 0, 0, 1, 0, 0, 0),
+            ::capnp::word(0, 0, 1, 0, 13, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(149, 0, 0, 0, 90, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(148, 0, 0, 0, 3, 0, 1, 0),
+            ::capnp::word(160, 0, 0, 0, 2, 0, 1, 0),
+            ::capnp::word(105, 110, 111, 0, 0, 0, 0, 0),
+            ::capnp::word(9, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(9, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(109, 111, 100, 101, 0, 0, 0, 0),
+            ::capnp::word(117, 105, 100, 0, 0, 0, 0, 0),
+            ::capnp::word(8, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(8, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(103, 105, 100, 0, 0, 0, 0, 0),
+            ::capnp::word(8, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(8, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(112, 101, 114, 109, 105, 115, 115, 105),
+            ::capnp::word(111, 110, 115, 0, 0, 0, 0, 0),
+            ::capnp::word(7, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(7, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(97, 100, 100, 105, 116, 105, 111, 110),
+            ::capnp::word(97, 108, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(16, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(211, 144, 125, 86, 42, 166, 175, 153),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(16, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+        ];
+        pub fn get_field_types(index: u16) -> ::capnp::introspect::Type {
+            match index {
+        0 => <u64 as ::capnp::introspect::Introspect>::introspect(),
+        1 => <crate::metadata_capnp::inode::mode::Owned as ::capnp::introspect::Introspect>::introspect(),
+        2 => <u32 as ::capnp::introspect::Introspect>::introspect(),
+        3 => <u32 as ::capnp::introspect::Introspect>::introspect(),
+        4 => <u16 as ::capnp::introspect::Introspect>::introspect(),
+        5 => <crate::metadata_capnp::inode_additional::Owned as ::capnp::introspect::Introspect>::introspect(),
+        _ => panic!("invalid field index {}", index),
+      }
+        }
+        pub fn get_annotation_types(
+            child_index: Option<u16>,
+            index: u32,
+        ) -> ::capnp::introspect::Type {
+            panic!("invalid annotation indices ({:?}, {}) ", child_index, index)
+        }
+        pub static RAW_SCHEMA: ::capnp::introspect::RawStructSchema =
+            ::capnp::introspect::RawStructSchema {
+                encoded_node: &ENCODED_NODE,
+                nonunion_members: NONUNION_MEMBERS,
+                members_by_discriminant: MEMBERS_BY_DISCRIMINANT,
+            };
+        pub static NONUNION_MEMBERS: &[u16] = &[0, 1, 2, 3, 4, 5];
+        pub static MEMBERS_BY_DISCRIMINANT: &[u16] = &[];
+        pub const TYPE_ID: u64 = 0xb975_cead_0280_72a1;
+    }
+
+    pub mod mode {
+        pub use self::Which::{Blk, Chr, Dir, Fifo, File, Lnk, Sock, Unknown, Wht};
+
+        #[derive(Copy, Clone)]
+        pub struct Owned(());
+        impl ::capnp::introspect::Introspect for Owned {
+            fn introspect() -> ::capnp::introspect::Type {
+                ::capnp::introspect::TypeVariant::Struct(
+                    ::capnp::introspect::RawBrandedStructSchema {
+                        generic: &_private::RAW_SCHEMA,
+                        field_types: _private::get_field_types,
+                        annotation_types: _private::get_annotation_types,
+                    },
+                )
+                .into()
+            }
+        }
+        impl ::capnp::traits::Owned for Owned {
+            type Reader<'a> = Reader<'a>;
+            type Builder<'a> = Builder<'a>;
+        }
+        impl ::capnp::traits::OwnedStruct for Owned {
+            type Reader<'a> = Reader<'a>;
+            type Builder<'a> = Builder<'a>;
+        }
+        #[cfg(feature = "alloc")]
+        impl ::capnp::traits::Pipelined for Owned {
+            type Pipeline = Pipeline;
+        }
+
+        pub struct Reader<'a> {
+            reader: ::capnp::private::layout::StructReader<'a>,
+        }
+        impl<'a> ::core::marker::Copy for Reader<'a> {}
+        impl<'a> ::core::clone::Clone for Reader<'a> {
+            fn clone(&self) -> Self {
+                *self
+            }
+        }
+
+        impl<'a> ::capnp::traits::HasTypeId for Reader<'a> {
+            const TYPE_ID: u64 = _private::TYPE_ID;
+        }
+        impl<'a> ::core::convert::From<::capnp::private::layout::StructReader<'a>> for Reader<'a> {
+            fn from(reader: ::capnp::private::layout::StructReader<'a>) -> Self {
+                Self { reader }
+            }
+        }
+
+        #[cfg(feature = "alloc")]
+        impl<'a> ::core::convert::From<Reader<'a>> for ::capnp::dynamic_value::Reader<'a> {
+            fn from(reader: Reader<'a>) -> Self {
+                Self::Struct(::capnp::dynamic_struct::Reader::new(
+                    reader.reader,
+                    ::capnp::schema::StructSchema::new(
+                        ::capnp::introspect::RawBrandedStructSchema {
+                            generic: &_private::RAW_SCHEMA,
+                            field_types: _private::get_field_types,
+                            annotation_types: _private::get_annotation_types,
+                        },
+                    ),
+                ))
+            }
+        }
+
+        #[cfg(feature = "alloc")]
+        impl<'a> ::core::fmt::Debug for Reader<'a> {
+            fn fmt(
+                &self,
+                f: &mut ::core::fmt::Formatter<'_>,
+            ) -> ::core::result::Result<(), ::core::fmt::Error> {
+                core::fmt::Debug::fmt(
+                    &::core::convert::Into::<::capnp::dynamic_value::Reader<'_>>::into(*self),
+                    f,
+                )
+            }
+        }
+
+        impl<'a> ::capnp::traits::FromPointerReader<'a> for Reader<'a> {
+            fn get_from_pointer(
+                reader: &::capnp::private::layout::PointerReader<'a>,
+                default: ::core::option::Option<&'a [::capnp::Word]>,
+            ) -> ::capnp::Result<Self> {
+                ::core::result::Result::Ok(reader.get_struct(default)?.into())
+            }
+        }
+
+        impl<'a> ::capnp::traits::IntoInternalStructReader<'a> for Reader<'a> {
+            fn into_internal_struct_reader(self) -> ::capnp::private::layout::StructReader<'a> {
+                self.reader
+            }
+        }
+
+        #[cfg(feature = "alloc")]
+        impl<'a> ::capnp::traits::Imbue<'a> for Reader<'a> {
+            fn imbue(&mut self, cap_table: &'a ::capnp::private::layout::CapTable) {
+                self.reader
+                    .imbue(::capnp::private::layout::CapTableReader::Plain(cap_table))
+            }
+        }
+
+        impl<'a> Reader<'a> {
+            pub fn reborrow(&self) -> Reader<'_> {
+                Self { ..*self }
+            }
+
+            pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
+                self.reader.total_size()
+            }
+            #[inline]
+            pub fn has_chr(&self) -> bool {
+                if self.reader.get_data_field::<u16>(4) != 2 {
+                    return false;
+                }
+                !self.reader.get_pointer_field(0).is_null()
+            }
+            #[inline]
+            pub fn has_dir(&self) -> bool {
+                if self.reader.get_data_field::<u16>(4) != 3 {
+                    return false;
+                }
+                !self.reader.get_pointer_field(0).is_null()
+            }
+            #[inline]
+            pub fn has_blk(&self) -> bool {
+                if self.reader.get_data_field::<u16>(4) != 4 {
+                    return false;
+                }
+                !self.reader.get_pointer_field(0).is_null()
+            }
+            #[inline]
+            pub fn has_file(&self) -> bool {
+                if self.reader.get_data_field::<u16>(4) != 5 {
+                    return false;
+                }
+                !self.reader.get_pointer_field(0).is_null()
+            }
+            #[inline]
+            pub fn which(self) -> ::core::result::Result<WhichReader<'a>, ::capnp::NotInSchema> {
+                match self.reader.get_data_field::<u16>(4) {
+                    0 => ::core::result::Result::Ok(Unknown(())),
+                    1 => ::core::result::Result::Ok(Fifo(())),
+                    2 => ::core::result::Result::Ok(Chr(
+                        ::capnp::traits::FromPointerReader::get_from_pointer(
+                            &self.reader.get_pointer_field(0),
+                            ::core::option::Option::None,
+                        ),
+                    )),
+                    3 => ::core::result::Result::Ok(Dir(
+                        ::capnp::traits::FromPointerReader::get_from_pointer(
+                            &self.reader.get_pointer_field(0),
+                            ::core::option::Option::None,
+                        ),
+                    )),
+                    4 => ::core::result::Result::Ok(Blk(
+                        ::capnp::traits::FromPointerReader::get_from_pointer(
+                            &self.reader.get_pointer_field(0),
+                            ::core::option::Option::None,
+                        ),
+                    )),
+                    5 => ::core::result::Result::Ok(File(
+                        ::capnp::traits::FromPointerReader::get_from_pointer(
+                            &self.reader.get_pointer_field(0),
+                            ::core::option::Option::None,
+                        ),
+                    )),
+                    6 => ::core::result::Result::Ok(Lnk(())),
+                    7 => ::core::result::Result::Ok(Sock(())),
+                    8 => ::core::result::Result::Ok(Wht(())),
+                    x => ::core::result::Result::Err(::capnp::NotInSchema(x)),
+                }
+            }
+        }
+
+        pub struct Builder<'a> {
+            builder: ::capnp::private::layout::StructBuilder<'a>,
+        }
+        impl<'a> ::capnp::traits::HasStructSize for Builder<'a> {
+            const STRUCT_SIZE: ::capnp::private::layout::StructSize =
+                ::capnp::private::layout::StructSize {
+                    data: 3,
+                    pointers: 2,
+                };
+        }
+        impl<'a> ::capnp::traits::HasTypeId for Builder<'a> {
+            const TYPE_ID: u64 = _private::TYPE_ID;
+        }
+        impl<'a> ::core::convert::From<::capnp::private::layout::StructBuilder<'a>> for Builder<'a> {
+            fn from(builder: ::capnp::private::layout::StructBuilder<'a>) -> Self {
+                Self { builder }
+            }
+        }
+
+        #[cfg(feature = "alloc")]
+        impl<'a> ::core::convert::From<Builder<'a>> for ::capnp::dynamic_value::Builder<'a> {
+            fn from(builder: Builder<'a>) -> Self {
+                Self::Struct(::capnp::dynamic_struct::Builder::new(
+                    builder.builder,
+                    ::capnp::schema::StructSchema::new(
+                        ::capnp::introspect::RawBrandedStructSchema {
+                            generic: &_private::RAW_SCHEMA,
+                            field_types: _private::get_field_types,
+                            annotation_types: _private::get_annotation_types,
+                        },
+                    ),
+                ))
+            }
+        }
+
+        #[cfg(feature = "alloc")]
+        impl<'a> ::capnp::traits::ImbueMut<'a> for Builder<'a> {
+            fn imbue_mut(&mut self, cap_table: &'a mut ::capnp::private::layout::CapTable) {
+                self.builder
+                    .imbue(::capnp::private::layout::CapTableBuilder::Plain(cap_table))
+            }
+        }
+
+        impl<'a> ::capnp::traits::FromPointerBuilder<'a> for Builder<'a> {
+            fn init_pointer(
+                builder: ::capnp::private::layout::PointerBuilder<'a>,
+                _size: u32,
+            ) -> Self {
+                builder
+                    .init_struct(<Self as ::capnp::traits::HasStructSize>::STRUCT_SIZE)
+                    .into()
+            }
+            fn get_from_pointer(
+                builder: ::capnp::private::layout::PointerBuilder<'a>,
+                default: ::core::option::Option<&'a [::capnp::Word]>,
+            ) -> ::capnp::Result<Self> {
+                ::core::result::Result::Ok(
+                    builder
+                        .get_struct(
+                            <Self as ::capnp::traits::HasStructSize>::STRUCT_SIZE,
+                            default,
+                        )?
+                        .into(),
+                )
+            }
+        }
+
+        impl<'a> ::capnp::traits::SetPointerBuilder for Reader<'a> {
+            fn set_pointer_builder(
+                mut pointer: ::capnp::private::layout::PointerBuilder<'_>,
+                value: Self,
+                canonicalize: bool,
+            ) -> ::capnp::Result<()> {
+                pointer.set_struct(&value.reader, canonicalize)
+            }
+        }
+
+        impl<'a> Builder<'a> {
+            pub fn into_reader(self) -> Reader<'a> {
+                self.builder.into_reader().into()
+            }
+            pub fn reborrow(&mut self) -> Builder<'_> {
+                Builder {
+                    builder: self.builder.reborrow(),
+                }
+            }
+            pub fn reborrow_as_reader(&self) -> Reader<'_> {
+                self.builder.as_reader().into()
+            }
+
+            pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
+                self.builder.as_reader().total_size()
+            }
+            #[inline]
+            pub fn set_unknown(&mut self, _value: ()) {
+                self.builder.set_data_field::<u16>(4, 0);
+            }
+            #[inline]
+            pub fn set_fifo(&mut self, _value: ()) {
+                self.builder.set_data_field::<u16>(4, 1);
+            }
+            #[inline]
+            pub fn set_chr(
+                &mut self,
+                value: crate::metadata_capnp::chr::Reader<'_>,
+            ) -> ::capnp::Result<()> {
+                self.builder.set_data_field::<u16>(4, 2);
+                ::capnp::traits::SetPointerBuilder::set_pointer_builder(
+                    self.builder.reborrow().get_pointer_field(0),
+                    value,
+                    false,
+                )
+            }
+            #[inline]
+            pub fn init_chr(self) -> crate::metadata_capnp::chr::Builder<'a> {
+                self.builder.set_data_field::<u16>(4, 2);
+                ::capnp::traits::FromPointerBuilder::init_pointer(
+                    self.builder.get_pointer_field(0),
+                    0,
+                )
+            }
+            #[inline]
+            pub fn has_chr(&self) -> bool {
+                if self.builder.get_data_field::<u16>(4) != 2 {
+                    return false;
+                }
+                !self.builder.is_pointer_field_null(0)
+            }
+            #[inline]
+            pub fn set_dir(
+                &mut self,
+                value: crate::metadata_capnp::dir::Reader<'_>,
+            ) -> ::capnp::Result<()> {
+                self.builder.set_data_field::<u16>(4, 3);
+                ::capnp::traits::SetPointerBuilder::set_pointer_builder(
+                    self.builder.reborrow().get_pointer_field(0),
+                    value,
+                    false,
+                )
+            }
+            #[inline]
+            pub fn init_dir(self) -> crate::metadata_capnp::dir::Builder<'a> {
+                self.builder.set_data_field::<u16>(4, 3);
+                ::capnp::traits::FromPointerBuilder::init_pointer(
+                    self.builder.get_pointer_field(0),
+                    0,
+                )
+            }
+            #[inline]
+            pub fn has_dir(&self) -> bool {
+                if self.builder.get_data_field::<u16>(4) != 3 {
+                    return false;
+                }
+                !self.builder.is_pointer_field_null(0)
+            }
+            #[inline]
+            pub fn set_blk(
+                &mut self,
+                value: crate::metadata_capnp::blk::Reader<'_>,
+            ) -> ::capnp::Result<()> {
+                self.builder.set_data_field::<u16>(4, 4);
+                ::capnp::traits::SetPointerBuilder::set_pointer_builder(
+                    self.builder.reborrow().get_pointer_field(0),
+                    value,
+                    false,
+                )
+            }
+            #[inline]
+            pub fn init_blk(self) -> crate::metadata_capnp::blk::Builder<'a> {
+                self.builder.set_data_field::<u16>(4, 4);
+                ::capnp::traits::FromPointerBuilder::init_pointer(
+                    self.builder.get_pointer_field(0),
+                    0,
+                )
+            }
+            #[inline]
+            pub fn has_blk(&self) -> bool {
+                if self.builder.get_data_field::<u16>(4) != 4 {
+                    return false;
+                }
+                !self.builder.is_pointer_field_null(0)
+            }
+            #[inline]
+            pub fn set_file(
+                &mut self,
+                value: crate::metadata_capnp::file::Reader<'_>,
+            ) -> ::capnp::Result<()> {
+                self.builder.set_data_field::<u16>(4, 5);
+                ::capnp::traits::SetPointerBuilder::set_pointer_builder(
+                    self.builder.reborrow().get_pointer_field(0),
+                    value,
+                    false,
+                )
+            }
+            #[inline]
+            pub fn init_file(self) -> crate::metadata_capnp::file::Builder<'a> {
+                self.builder.set_data_field::<u16>(4, 5);
+                ::capnp::traits::FromPointerBuilder::init_pointer(
+                    self.builder.get_pointer_field(0),
+                    0,
+                )
+            }
+            #[inline]
+            pub fn has_file(&self) -> bool {
+                if self.builder.get_data_field::<u16>(4) != 5 {
+                    return false;
+                }
+                !self.builder.is_pointer_field_null(0)
+            }
+            #[inline]
+            pub fn set_lnk(&mut self, _value: ()) {
+                self.builder.set_data_field::<u16>(4, 6);
+            }
+            #[inline]
+            pub fn set_sock(&mut self, _value: ()) {
+                self.builder.set_data_field::<u16>(4, 7);
+            }
+            #[inline]
+            pub fn set_wht(&mut self, _value: ()) {
+                self.builder.set_data_field::<u16>(4, 8);
+            }
+            #[inline]
+            pub fn which(self) -> ::core::result::Result<WhichBuilder<'a>, ::capnp::NotInSchema> {
+                match self.builder.get_data_field::<u16>(4) {
+                    0 => ::core::result::Result::Ok(Unknown(())),
+                    1 => ::core::result::Result::Ok(Fifo(())),
+                    2 => ::core::result::Result::Ok(Chr(
+                        ::capnp::traits::FromPointerBuilder::get_from_pointer(
+                            self.builder.get_pointer_field(0),
+                            ::core::option::Option::None,
+                        ),
+                    )),
+                    3 => ::core::result::Result::Ok(Dir(
+                        ::capnp::traits::FromPointerBuilder::get_from_pointer(
+                            self.builder.get_pointer_field(0),
+                            ::core::option::Option::None,
+                        ),
+                    )),
+                    4 => ::core::result::Result::Ok(Blk(
+                        ::capnp::traits::FromPointerBuilder::get_from_pointer(
+                            self.builder.get_pointer_field(0),
+                            ::core::option::Option::None,
+                        ),
+                    )),
+                    5 => ::core::result::Result::Ok(File(
+                        ::capnp::traits::FromPointerBuilder::get_from_pointer(
+                            self.builder.get_pointer_field(0),
+                            ::core::option::Option::None,
+                        ),
+                    )),
+                    6 => ::core::result::Result::Ok(Lnk(())),
+                    7 => ::core::result::Result::Ok(Sock(())),
+                    8 => ::core::result::Result::Ok(Wht(())),
+                    x => ::core::result::Result::Err(::capnp::NotInSchema(x)),
+                }
+            }
+        }
+
+        #[cfg(feature = "alloc")]
+        pub struct Pipeline {
+            _typeless: ::capnp::any_pointer::Pipeline,
+        }
+        #[cfg(feature = "alloc")]
+        impl ::capnp::capability::FromTypelessPipeline for Pipeline {
+            fn new(typeless: ::capnp::any_pointer::Pipeline) -> Self {
+                Self {
+                    _typeless: typeless,
+                }
+            }
+        }
+        #[cfg(feature = "alloc")]
+        impl Pipeline {}
+        mod _private {
+            pub static ENCODED_NODE: [::capnp::Word; 152] = [
+                ::capnp::word(0, 0, 0, 0, 5, 0, 6, 0),
+                ::capnp::word(54, 46, 90, 92, 135, 239, 140, 194),
+                ::capnp::word(21, 0, 0, 0, 1, 0, 3, 0),
+                ::capnp::word(161, 114, 128, 2, 173, 206, 117, 185),
+                ::capnp::word(2, 0, 7, 0, 1, 0, 9, 0),
+                ::capnp::word(4, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(21, 0, 0, 0, 210, 0, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(25, 0, 0, 0, 255, 1, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(109, 101, 116, 97, 100, 97, 116, 97),
+                ::capnp::word(46, 99, 97, 112, 110, 112, 58, 73),
+                ::capnp::word(110, 111, 100, 101, 46, 109, 111, 100),
+                ::capnp::word(101, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(36, 0, 0, 0, 3, 0, 4, 0),
+                ::capnp::word(0, 0, 255, 255, 0, 0, 0, 0),
+                ::capnp::word(0, 0, 1, 0, 1, 0, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(237, 0, 0, 0, 66, 0, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(232, 0, 0, 0, 3, 0, 1, 0),
+                ::capnp::word(244, 0, 0, 0, 2, 0, 1, 0),
+                ::capnp::word(1, 0, 254, 255, 0, 0, 0, 0),
+                ::capnp::word(0, 0, 1, 0, 2, 0, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(241, 0, 0, 0, 42, 0, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(236, 0, 0, 0, 3, 0, 1, 0),
+                ::capnp::word(248, 0, 0, 0, 2, 0, 1, 0),
+                ::capnp::word(2, 0, 253, 255, 0, 0, 0, 0),
+                ::capnp::word(0, 0, 1, 0, 3, 0, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(245, 0, 0, 0, 34, 0, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(240, 0, 0, 0, 3, 0, 1, 0),
+                ::capnp::word(252, 0, 0, 0, 2, 0, 1, 0),
+                ::capnp::word(3, 0, 252, 255, 0, 0, 0, 0),
+                ::capnp::word(0, 0, 1, 0, 4, 0, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(249, 0, 0, 0, 34, 0, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(244, 0, 0, 0, 3, 0, 1, 0),
+                ::capnp::word(0, 1, 0, 0, 2, 0, 1, 0),
+                ::capnp::word(4, 0, 251, 255, 0, 0, 0, 0),
+                ::capnp::word(0, 0, 1, 0, 5, 0, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(253, 0, 0, 0, 34, 0, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(248, 0, 0, 0, 3, 0, 1, 0),
+                ::capnp::word(4, 1, 0, 0, 2, 0, 1, 0),
+                ::capnp::word(5, 0, 250, 255, 0, 0, 0, 0),
+                ::capnp::word(0, 0, 1, 0, 6, 0, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(1, 1, 0, 0, 42, 0, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(252, 0, 0, 0, 3, 0, 1, 0),
+                ::capnp::word(8, 1, 0, 0, 2, 0, 1, 0),
+                ::capnp::word(6, 0, 249, 255, 0, 0, 0, 0),
+                ::capnp::word(0, 0, 1, 0, 7, 0, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(5, 1, 0, 0, 34, 0, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(0, 1, 0, 0, 3, 0, 1, 0),
+                ::capnp::word(12, 1, 0, 0, 2, 0, 1, 0),
+                ::capnp::word(7, 0, 248, 255, 0, 0, 0, 0),
+                ::capnp::word(0, 0, 1, 0, 8, 0, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(9, 1, 0, 0, 42, 0, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(4, 1, 0, 0, 3, 0, 1, 0),
+                ::capnp::word(16, 1, 0, 0, 2, 0, 1, 0),
+                ::capnp::word(8, 0, 247, 255, 0, 0, 0, 0),
+                ::capnp::word(0, 0, 1, 0, 9, 0, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(13, 1, 0, 0, 34, 0, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(8, 1, 0, 0, 3, 0, 1, 0),
+                ::capnp::word(20, 1, 0, 0, 2, 0, 1, 0),
+                ::capnp::word(117, 110, 107, 110, 111, 119, 110, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(102, 105, 102, 111, 0, 0, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(99, 104, 114, 0, 0, 0, 0, 0),
+                ::capnp::word(16, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(112, 90, 179, 204, 206, 228, 254, 162),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(16, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(100, 105, 114, 0, 0, 0, 0, 0),
+                ::capnp::word(16, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(4, 150, 39, 131, 249, 197, 58, 163),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(16, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(98, 108, 107, 0, 0, 0, 0, 0),
+                ::capnp::word(16, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(105, 137, 149, 129, 43, 27, 209, 242),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(16, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(102, 105, 108, 101, 0, 0, 0, 0),
+                ::capnp::word(16, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(212, 62, 195, 177, 129, 127, 161, 252),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(16, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(108, 110, 107, 0, 0, 0, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(115, 111, 99, 107, 0, 0, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(119, 104, 116, 0, 0, 0, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+                ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ];
+            pub fn get_field_types(index: u16) -> ::capnp::introspect::Type {
+                match index {
+          0 => <() as ::capnp::introspect::Introspect>::introspect(),
+          1 => <() as ::capnp::introspect::Introspect>::introspect(),
+          2 => <crate::metadata_capnp::chr::Owned as ::capnp::introspect::Introspect>::introspect(),
+          3 => <crate::metadata_capnp::dir::Owned as ::capnp::introspect::Introspect>::introspect(),
+          4 => <crate::metadata_capnp::blk::Owned as ::capnp::introspect::Introspect>::introspect(),
+          5 => <crate::metadata_capnp::file::Owned as ::capnp::introspect::Introspect>::introspect(),
+          6 => <() as ::capnp::introspect::Introspect>::introspect(),
+          7 => <() as ::capnp::introspect::Introspect>::introspect(),
+          8 => <() as ::capnp::introspect::Introspect>::introspect(),
+          _ => panic!("invalid field index {}", index),
+        }
+            }
+            pub fn get_annotation_types(
+                child_index: Option<u16>,
+                index: u32,
+            ) -> ::capnp::introspect::Type {
+                panic!("invalid annotation indices ({:?}, {}) ", child_index, index)
+            }
+            pub static RAW_SCHEMA: ::capnp::introspect::RawStructSchema =
+                ::capnp::introspect::RawStructSchema {
+                    encoded_node: &ENCODED_NODE,
+                    nonunion_members: NONUNION_MEMBERS,
+                    members_by_discriminant: MEMBERS_BY_DISCRIMINANT,
+                };
+            pub static NONUNION_MEMBERS: &[u16] = &[];
+            pub static MEMBERS_BY_DISCRIMINANT: &[u16] = &[0, 1, 2, 3, 4, 5, 6, 7, 8];
+            pub const TYPE_ID: u64 = 0xc28c_ef87_5c5a_2e36;
+        }
+        pub enum Which<A0, A1, A2, A3> {
+            Unknown(()),
+            Fifo(()),
+            Chr(A0),
+            Dir(A1),
+            Blk(A2),
+            File(A3),
+            Lnk(()),
+            Sock(()),
+            Wht(()),
+        }
+        pub type WhichReader<'a> = Which<
+            ::capnp::Result<crate::metadata_capnp::chr::Reader<'a>>,
+            ::capnp::Result<crate::metadata_capnp::dir::Reader<'a>>,
+            ::capnp::Result<crate::metadata_capnp::blk::Reader<'a>>,
+            ::capnp::Result<crate::metadata_capnp::file::Reader<'a>>,
+        >;
+        pub type WhichBuilder<'a> = Which<
+            ::capnp::Result<crate::metadata_capnp::chr::Builder<'a>>,
+            ::capnp::Result<crate::metadata_capnp::dir::Builder<'a>>,
+            ::capnp::Result<crate::metadata_capnp::blk::Builder<'a>>,
+            ::capnp::Result<crate::metadata_capnp::file::Builder<'a>>,
+        >;
+    }
+}
+
+pub mod inode_vector {
+    #[derive(Copy, Clone)]
+    pub struct Owned(());
+    impl ::capnp::introspect::Introspect for Owned {
+        fn introspect() -> ::capnp::introspect::Type {
+            ::capnp::introspect::TypeVariant::Struct(::capnp::introspect::RawBrandedStructSchema {
+                generic: &_private::RAW_SCHEMA,
+                field_types: _private::get_field_types,
+                annotation_types: _private::get_annotation_types,
+            })
+            .into()
+        }
+    }
+    impl ::capnp::traits::Owned for Owned {
+        type Reader<'a> = Reader<'a>;
+        type Builder<'a> = Builder<'a>;
+    }
+    impl ::capnp::traits::OwnedStruct for Owned {
+        type Reader<'a> = Reader<'a>;
+        type Builder<'a> = Builder<'a>;
+    }
+    #[cfg(feature = "alloc")]
+    impl ::capnp::traits::Pipelined for Owned {
+        type Pipeline = Pipeline;
+    }
+
+    pub struct Reader<'a> {
+        reader: ::capnp::private::layout::StructReader<'a>,
+    }
+    impl<'a> ::core::marker::Copy for Reader<'a> {}
+    impl<'a> ::core::clone::Clone for Reader<'a> {
+        fn clone(&self) -> Self {
+            *self
+        }
+    }
+
+    impl<'a> ::capnp::traits::HasTypeId for Reader<'a> {
+        const TYPE_ID: u64 = _private::TYPE_ID;
+    }
+    impl<'a> ::core::convert::From<::capnp::private::layout::StructReader<'a>> for Reader<'a> {
+        fn from(reader: ::capnp::private::layout::StructReader<'a>) -> Self {
+            Self { reader }
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    impl<'a> ::core::convert::From<Reader<'a>> for ::capnp::dynamic_value::Reader<'a> {
+        fn from(reader: Reader<'a>) -> Self {
+            Self::Struct(::capnp::dynamic_struct::Reader::new(
+                reader.reader,
+                ::capnp::schema::StructSchema::new(::capnp::introspect::RawBrandedStructSchema {
+                    generic: &_private::RAW_SCHEMA,
+                    field_types: _private::get_field_types,
+                    annotation_types: _private::get_annotation_types,
+                }),
+            ))
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    impl<'a> ::core::fmt::Debug for Reader<'a> {
+        fn fmt(
+            &self,
+            f: &mut ::core::fmt::Formatter<'_>,
+        ) -> ::core::result::Result<(), ::core::fmt::Error> {
+            core::fmt::Debug::fmt(
+                &::core::convert::Into::<::capnp::dynamic_value::Reader<'_>>::into(*self),
+                f,
+            )
+        }
+    }
+
+    impl<'a> ::capnp::traits::FromPointerReader<'a> for Reader<'a> {
+        fn get_from_pointer(
+            reader: &::capnp::private::layout::PointerReader<'a>,
+            default: ::core::option::Option<&'a [::capnp::Word]>,
+        ) -> ::capnp::Result<Self> {
+            ::core::result::Result::Ok(reader.get_struct(default)?.into())
+        }
+    }
+
+    impl<'a> ::capnp::traits::IntoInternalStructReader<'a> for Reader<'a> {
+        fn into_internal_struct_reader(self) -> ::capnp::private::layout::StructReader<'a> {
+            self.reader
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    impl<'a> ::capnp::traits::Imbue<'a> for Reader<'a> {
+        fn imbue(&mut self, cap_table: &'a ::capnp::private::layout::CapTable) {
+            self.reader
+                .imbue(::capnp::private::layout::CapTableReader::Plain(cap_table))
+        }
+    }
+
+    impl<'a> Reader<'a> {
+        pub fn reborrow(&self) -> Reader<'_> {
+            Self { ..*self }
+        }
+
+        pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
+            self.reader.total_size()
+        }
+        #[inline]
+        pub fn get_inodes(
+            self,
+        ) -> ::capnp::Result<::capnp::struct_list::Reader<'a, crate::metadata_capnp::inode::Owned>>
+        {
+            ::capnp::traits::FromPointerReader::get_from_pointer(
+                &self.reader.get_pointer_field(0),
+                ::core::option::Option::None,
+            )
+        }
+        #[inline]
+        pub fn has_inodes(&self) -> bool {
+            !self.reader.get_pointer_field(0).is_null()
+        }
+    }
+
+    pub struct Builder<'a> {
+        builder: ::capnp::private::layout::StructBuilder<'a>,
+    }
+    impl<'a> ::capnp::traits::HasStructSize for Builder<'a> {
+        const STRUCT_SIZE: ::capnp::private::layout::StructSize =
+            ::capnp::private::layout::StructSize {
+                data: 0,
+                pointers: 1,
+            };
+    }
+    impl<'a> ::capnp::traits::HasTypeId for Builder<'a> {
+        const TYPE_ID: u64 = _private::TYPE_ID;
+    }
+    impl<'a> ::core::convert::From<::capnp::private::layout::StructBuilder<'a>> for Builder<'a> {
+        fn from(builder: ::capnp::private::layout::StructBuilder<'a>) -> Self {
+            Self { builder }
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    impl<'a> ::core::convert::From<Builder<'a>> for ::capnp::dynamic_value::Builder<'a> {
+        fn from(builder: Builder<'a>) -> Self {
+            Self::Struct(::capnp::dynamic_struct::Builder::new(
+                builder.builder,
+                ::capnp::schema::StructSchema::new(::capnp::introspect::RawBrandedStructSchema {
+                    generic: &_private::RAW_SCHEMA,
+                    field_types: _private::get_field_types,
+                    annotation_types: _private::get_annotation_types,
+                }),
+            ))
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    impl<'a> ::capnp::traits::ImbueMut<'a> for Builder<'a> {
+        fn imbue_mut(&mut self, cap_table: &'a mut ::capnp::private::layout::CapTable) {
+            self.builder
+                .imbue(::capnp::private::layout::CapTableBuilder::Plain(cap_table))
+        }
+    }
+
+    impl<'a> ::capnp::traits::FromPointerBuilder<'a> for Builder<'a> {
+        fn init_pointer(builder: ::capnp::private::layout::PointerBuilder<'a>, _size: u32) -> Self {
+            builder
+                .init_struct(<Self as ::capnp::traits::HasStructSize>::STRUCT_SIZE)
+                .into()
+        }
+        fn get_from_pointer(
+            builder: ::capnp::private::layout::PointerBuilder<'a>,
+            default: ::core::option::Option<&'a [::capnp::Word]>,
+        ) -> ::capnp::Result<Self> {
+            ::core::result::Result::Ok(
+                builder
+                    .get_struct(
+                        <Self as ::capnp::traits::HasStructSize>::STRUCT_SIZE,
+                        default,
+                    )?
+                    .into(),
+            )
+        }
+    }
+
+    impl<'a> ::capnp::traits::SetPointerBuilder for Reader<'a> {
+        fn set_pointer_builder(
+            mut pointer: ::capnp::private::layout::PointerBuilder<'_>,
+            value: Self,
+            canonicalize: bool,
+        ) -> ::capnp::Result<()> {
+            pointer.set_struct(&value.reader, canonicalize)
+        }
+    }
+
+    impl<'a> Builder<'a> {
+        pub fn into_reader(self) -> Reader<'a> {
+            self.builder.into_reader().into()
+        }
+        pub fn reborrow(&mut self) -> Builder<'_> {
+            Builder {
+                builder: self.builder.reborrow(),
+            }
+        }
+        pub fn reborrow_as_reader(&self) -> Reader<'_> {
+            self.builder.as_reader().into()
+        }
+
+        pub fn total_size(&self) -> ::capnp::Result<::capnp::MessageSize> {
+            self.builder.as_reader().total_size()
+        }
+        #[inline]
+        pub fn get_inodes(
+            self,
+        ) -> ::capnp::Result<::capnp::struct_list::Builder<'a, crate::metadata_capnp::inode::Owned>>
+        {
+            ::capnp::traits::FromPointerBuilder::get_from_pointer(
+                self.builder.get_pointer_field(0),
+                ::core::option::Option::None,
+            )
+        }
+        #[inline]
+        pub fn set_inodes(
+            &mut self,
+            value: ::capnp::struct_list::Reader<'a, crate::metadata_capnp::inode::Owned>,
+        ) -> ::capnp::Result<()> {
+            ::capnp::traits::SetPointerBuilder::set_pointer_builder(
+                self.builder.reborrow().get_pointer_field(0),
+                value,
+                false,
+            )
+        }
+        #[inline]
+        pub fn init_inodes(
+            self,
+            size: u32,
+        ) -> ::capnp::struct_list::Builder<'a, crate::metadata_capnp::inode::Owned> {
+            ::capnp::traits::FromPointerBuilder::init_pointer(
+                self.builder.get_pointer_field(0),
+                size,
+            )
+        }
+        #[inline]
+        pub fn has_inodes(&self) -> bool {
+            !self.builder.is_pointer_field_null(0)
+        }
+    }
+
+    #[cfg(feature = "alloc")]
+    pub struct Pipeline {
+        _typeless: ::capnp::any_pointer::Pipeline,
+    }
+    #[cfg(feature = "alloc")]
+    impl ::capnp::capability::FromTypelessPipeline for Pipeline {
+        fn new(typeless: ::capnp::any_pointer::Pipeline) -> Self {
+            Self {
+                _typeless: typeless,
+            }
+        }
+    }
+    #[cfg(feature = "alloc")]
+    impl Pipeline {}
+    mod _private {
+        pub static ENCODED_NODE: [::capnp::Word; 37] = [
+            ::capnp::word(0, 0, 0, 0, 5, 0, 6, 0),
+            ::capnp::word(35, 20, 59, 240, 58, 227, 99, 162),
+            ::capnp::word(15, 0, 0, 0, 1, 0, 0, 0),
+            ::capnp::word(183, 203, 183, 136, 110, 94, 174, 132),
+            ::capnp::word(1, 0, 7, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(21, 0, 0, 0, 218, 0, 0, 0),
+            ::capnp::word(33, 0, 0, 0, 7, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(29, 0, 0, 0, 63, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(109, 101, 116, 97, 100, 97, 116, 97),
+            ::capnp::word(46, 99, 97, 112, 110, 112, 58, 73),
+            ::capnp::word(110, 111, 100, 101, 86, 101, 99, 116),
+            ::capnp::word(111, 114, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 1, 0, 1, 0),
+            ::capnp::word(4, 0, 0, 0, 3, 0, 4, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 1, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(13, 0, 0, 0, 58, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(8, 0, 0, 0, 3, 0, 1, 0),
+            ::capnp::word(36, 0, 0, 0, 2, 0, 1, 0),
+            ::capnp::word(105, 110, 111, 100, 101, 115, 0, 0),
+            ::capnp::word(14, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 3, 0, 1, 0),
+            ::capnp::word(16, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(161, 114, 128, 2, 173, 206, 117, 185),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(14, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+            ::capnp::word(0, 0, 0, 0, 0, 0, 0, 0),
+        ];
+        pub fn get_field_types(index: u16) -> ::capnp::introspect::Type {
+            match index {
+        0 => <::capnp::struct_list::Owned<crate::metadata_capnp::inode::Owned> as ::capnp::introspect::Introspect>::introspect(),
+        _ => panic!("invalid field index {}", index),
+      }
+        }
+        pub fn get_annotation_types(
+            child_index: Option<u16>,
+            index: u32,
+        ) -> ::capnp::introspect::Type {
+            panic!("invalid annotation indices ({:?}, {}) ", child_index, index)
+        }
+        pub static RAW_SCHEMA: ::capnp::introspect::RawStructSchema =
+            ::capnp::introspect::RawStructSchema {
+                encoded_node: &ENCODED_NODE,
+                nonunion_members: NONUNION_MEMBERS,
+                members_by_discriminant: MEMBERS_BY_DISCRIMINANT,
+            };
+        pub static NONUNION_MEMBERS: &[u16] = &[0];
+        pub static MEMBERS_BY_DISCRIMINANT: &[u16] = &[];
+        pub const TYPE_ID: u64 = 0xa263_e33a_f03b_1423;
+    }
+}
diff --git a/samples/rust/puzzlefs.rs b/samples/rust/puzzlefs.rs
index 0cf42762e81a..9afd82745b64 100644
--- a/samples/rust/puzzlefs.rs
+++ b/samples/rust/puzzlefs.rs
@@ -6,6 +6,10 @@
 use kernel::prelude::*;
 use kernel::{c_str, file, fs, io_buffer::IoBufferWriter};
 
+mod puzzle;
+// Required by the autogenerated '_capnp.rs' files
+use puzzle::{manifest_capnp, metadata_capnp};
+
 module_fs! {
     type: PuzzleFsModule,
     name: "puzzlefs",
-- 
2.41.0


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [RFC PATCH v2 06/10] rust: file: pass the filesystem context to the open function
  2023-07-26 16:45 [RFC PATCH v2 00/10] Rust PuzleFS filesystem driver Ariel Miculas
                   ` (4 preceding siblings ...)
  2023-07-26 16:45 ` [RFC PATCH v2 05/10] samples: puzzlefs: add basic deserializing support for the puzzlefs metadata Ariel Miculas
@ 2023-07-26 16:45 ` Ariel Miculas
  2023-07-27 13:32   ` Ariel Miculas
  2023-07-26 16:45 ` [RFC PATCH v2 07/10] samples: puzzlefs: populate the directory entries with the inodes from the puzzlefs metadata file Ariel Miculas
                   ` (3 subsequent siblings)
  9 siblings, 1 reply; 20+ messages in thread
From: Ariel Miculas @ 2023-07-26 16:45 UTC (permalink / raw)
  To: rust-for-linux
  Cc: linux-kernel, linux-fsdevel, tycho, brauner, viro, ojeda,
	alex.gaynor, wedsonaf, Ariel Miculas

This allows us to create a Vfsmount structure and pass it to the read
callback.

Signed-off-by: Ariel Miculas <amiculas@cisco.com>
---
 rust/kernel/file.rs      | 17 +++++++++++++++--
 samples/rust/puzzlefs.rs | 40 +++++++++++++++++++++++++++++++++++-----
 samples/rust/rust_fs.rs  |  3 ++-
 3 files changed, 52 insertions(+), 8 deletions(-)

diff --git a/rust/kernel/file.rs b/rust/kernel/file.rs
index a3002c416dbb..af1eb1ee9267 100644
--- a/rust/kernel/file.rs
+++ b/rust/kernel/file.rs
@@ -457,9 +457,15 @@ impl<A: OpenAdapter<T::OpenData>, T: Operations> OperationsVtable<A, T> {
             // `fileref` never outlives this function, so it is guaranteed to be
             // valid.
             let fileref = unsafe { File::from_ptr(file) };
+
+            // SAFETY: into_foreign was called in fs::NewSuperBlock<..., NeedsInit>::init and
+            // it is valid until from_foreign will be called in fs::Tables::free_callback
+            let fs_info =
+                unsafe { <T::Filesystem as fs::Type>::Data::borrow((*(*inode).i_sb).s_fs_info) };
+
             // SAFETY: `arg` was previously returned by `A::convert` and must
             // be a valid non-null pointer.
-            let ptr = T::open(unsafe { &*arg }, fileref)?.into_foreign();
+            let ptr = T::open(fs_info, unsafe { &*arg }, fileref)?.into_foreign();
             // SAFETY: The C contract guarantees that `private_data` is available
             // for implementers of the file operations (no other C code accesses
             // it), so we know that there are no concurrent threads/CPUs accessing
@@ -930,10 +936,17 @@ pub trait Operations {
     /// The type of the context data passed to [`Operations::open`].
     type OpenData: Sync = ();
 
+    /// Data associated with each file system instance.
+    type Filesystem: fs::Type;
+
     /// Creates a new instance of this file.
     ///
     /// Corresponds to the `open` function pointer in `struct file_operations`.
-    fn open(context: &Self::OpenData, file: &File) -> Result<Self::Data>;
+    fn open(
+        fs_info: <<Self::Filesystem as fs::Type>::Data as ForeignOwnable>::Borrowed<'_>,
+        context: &Self::OpenData,
+        file: &File,
+    ) -> Result<Self::Data>;
 
     /// Cleans up after the last reference to the file goes away.
     ///
diff --git a/samples/rust/puzzlefs.rs b/samples/rust/puzzlefs.rs
index 9afd82745b64..8a64e0bd437d 100644
--- a/samples/rust/puzzlefs.rs
+++ b/samples/rust/puzzlefs.rs
@@ -3,8 +3,14 @@
 //! Rust file system sample.
 
 use kernel::module_fs;
+use kernel::mount::Vfsmount;
 use kernel::prelude::*;
-use kernel::{c_str, file, fs, io_buffer::IoBufferWriter};
+use kernel::{
+    c_str, file, fmt, fs,
+    io_buffer::IoBufferWriter,
+    str::CString,
+    sync::{Arc, ArcBorrow},
+};
 
 mod puzzle;
 // Required by the autogenerated '_capnp.rs' files
@@ -19,6 +25,12 @@
 
 struct PuzzleFsModule;
 
+#[derive(Debug)]
+struct PuzzlefsInfo {
+    base_path: CString,
+    vfs_mount: Arc<Vfsmount>,
+}
+
 #[vtable]
 impl fs::Context<Self> for PuzzleFsModule {
     type Data = ();
@@ -46,14 +58,23 @@ fn try_new() -> Result {
 impl fs::Type for PuzzleFsModule {
     type Context = Self;
     type INodeData = &'static [u8];
+    type Data = Box<PuzzlefsInfo>;
     const SUPER_TYPE: fs::Super = fs::Super::Independent;
     const NAME: &'static CStr = c_str!("puzzlefs");
     const FLAGS: i32 = fs::flags::USERNS_MOUNT;
     const DCACHE_BASED: bool = true;
 
     fn fill_super(_data: (), sb: fs::NewSuperBlock<'_, Self>) -> Result<&fs::SuperBlock<Self>> {
+        let base_path = CString::try_from_fmt(fmt!("hello world"))?;
+        pr_info!("base_path {:?}\n", base_path);
+        let vfs_mount = Vfsmount::new_private_mount(c_str!("/home/puzzlefs_oci"))?;
+        pr_info!("vfs_mount {:?}\n", vfs_mount);
+
         let sb = sb.init(
-            (),
+            Box::try_new(PuzzlefsInfo {
+                base_path,
+                vfs_mount: Arc::try_new(vfs_mount)?,
+            })?,
             &fs::SuperParams {
                 magic: 0x72757374,
                 ..fs::SuperParams::DEFAULT
@@ -88,14 +109,23 @@ fn fill_super(_data: (), sb: fs::NewSuperBlock<'_, Self>) -> Result<&fs::SuperBl
 
 #[vtable]
 impl file::Operations for FsFile {
+    // must be the same as INodeData
     type OpenData = &'static [u8];
+    type Filesystem = PuzzleFsModule;
+    // this is an Arc because Data must be ForeignOwnable and the only implementors of it are Box,
+    // Arc and (); we cannot pass a reference to read, so we share Vfsmount using and Arc
+    type Data = Arc<Vfsmount>;
 
-    fn open(_context: &Self::OpenData, _file: &file::File) -> Result<Self::Data> {
-        Ok(())
+    fn open(
+        fs_info: &PuzzlefsInfo,
+        _context: &Self::OpenData,
+        _file: &file::File,
+    ) -> Result<Self::Data> {
+        Ok(fs_info.vfs_mount.clone())
     }
 
     fn read(
-        _data: (),
+        data: ArcBorrow<'_, Vfsmount>,
         file: &file::File,
         writer: &mut impl IoBufferWriter,
         offset: u64,
diff --git a/samples/rust/rust_fs.rs b/samples/rust/rust_fs.rs
index 7527681ee024..c58ed1560e06 100644
--- a/samples/rust/rust_fs.rs
+++ b/samples/rust/rust_fs.rs
@@ -85,8 +85,9 @@ fn fill_super(_data: (), sb: fs::NewSuperBlock<'_, Self>) -> Result<&fs::SuperBl
 #[vtable]
 impl file::Operations for FsFile {
     type OpenData = &'static [u8];
+    type Filesystem = RustFs;
 
-    fn open(_context: &Self::OpenData, _file: &file::File) -> Result<Self::Data> {
+    fn open(_fs_info: (), _context: &Self::OpenData, _file: &file::File) -> Result<Self::Data> {
         Ok(())
     }
 
-- 
2.41.0


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [RFC PATCH v2 07/10] samples: puzzlefs: populate the directory entries with the inodes from the puzzlefs metadata file
  2023-07-26 16:45 [RFC PATCH v2 00/10] Rust PuzleFS filesystem driver Ariel Miculas
                   ` (5 preceding siblings ...)
  2023-07-26 16:45 ` [RFC PATCH v2 06/10] rust: file: pass the filesystem context to the open function Ariel Miculas
@ 2023-07-26 16:45 ` Ariel Miculas
  2023-07-26 16:45 ` [RFC PATCH v2 08/10] rust: puzzlefs: read the puzzlefs image manifest instead of an individual metadata layer Ariel Miculas
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 20+ messages in thread
From: Ariel Miculas @ 2023-07-26 16:45 UTC (permalink / raw)
  To: rust-for-linux
  Cc: linux-kernel, linux-fsdevel, tycho, brauner, viro, ojeda,
	alex.gaynor, wedsonaf, Ariel Miculas

I've used the same strategy as in try_new_populated_root_dentry from
rust/kernel/fs.rs, that is, recursively traverse the filesystem starting
from the root and create the necessary dentries. This is not ideal
because it creates all the dentries up front, but there are no rust
filesystem abstractions for implementing lookups yet.

Signed-off-by: Ariel Miculas <amiculas@cisco.com>
---
 rust/alloc/vec/mod.rs    |  17 +++++
 rust/kernel/fs.rs        |   4 +-
 samples/rust/puzzlefs.rs | 141 +++++++++++++++++++++++++++++----------
 3 files changed, 127 insertions(+), 35 deletions(-)

diff --git a/rust/alloc/vec/mod.rs b/rust/alloc/vec/mod.rs
index e8344c77939e..b755cf0b936c 100644
--- a/rust/alloc/vec/mod.rs
+++ b/rust/alloc/vec/mod.rs
@@ -2499,6 +2499,15 @@ unsafe fn split_at_spare_mut_with_len(
     }
 }
 
+impl<T: Clone> Vec<T> {
+    /// Try to clone the vector using the global allocator
+    #[inline]
+    #[stable(feature = "kernel", since = "1.0.0")]
+    pub fn try_clone(&self) -> Result<Self, TryReserveError> {
+        self.try_clone_in(Global)
+    }
+}
+
 impl<T: Clone, A: Allocator> Vec<T, A> {
     /// Resizes the `Vec` in-place so that `len` is equal to `new_len`.
     ///
@@ -2623,6 +2632,14 @@ pub fn try_extend_from_slice(&mut self, other: &[T]) -> Result<(), TryReserveErr
         self.try_spec_extend(other.iter())
     }
 
+    /// Tries to clone the vector using the given allocator
+    #[stable(feature = "kernel", since = "1.0.0")]
+    pub fn try_clone_in(&self, allocator: A) -> Result<Self, TryReserveError> {
+        let mut new_vec = Vec::try_with_capacity_in(self.len(), allocator)?;
+        new_vec.try_extend_from_slice(&self)?;
+        Ok(new_vec)
+    }
+
     /// Copies elements from `src` range to the end of the vector.
     ///
     /// # Panics
diff --git a/rust/kernel/fs.rs b/rust/kernel/fs.rs
index 10f90f78fc54..709b79fa0030 100644
--- a/rust/kernel/fs.rs
+++ b/rust/kernel/fs.rs
@@ -781,7 +781,9 @@ impl SuperParams {
 ///
 /// The superblock is a newly-created one and this is the only active pointer to it.
 pub struct NewSuperBlock<'a, T: Type + ?Sized, S = NeedsInit> {
-    sb: &'a mut SuperBlock<T>,
+    /// Pointer to the superblock; this fields is public so puzzlefs can call
+    /// try_new_dcache_dir_inode when populating the directory hierarchy
+    pub sb: &'a mut SuperBlock<T>,
 
     // This also forces `'a` to be invariant.
     _p: PhantomData<&'a mut &'a S>,
diff --git a/samples/rust/puzzlefs.rs b/samples/rust/puzzlefs.rs
index 8a64e0bd437d..1f0073716d91 100644
--- a/samples/rust/puzzlefs.rs
+++ b/samples/rust/puzzlefs.rs
@@ -6,16 +6,19 @@
 use kernel::mount::Vfsmount;
 use kernel::prelude::*;
 use kernel::{
-    c_str, file, fmt, fs,
+    c_str, file, fs,
     io_buffer::IoBufferWriter,
-    str::CString,
     sync::{Arc, ArcBorrow},
 };
 
 mod puzzle;
 // Required by the autogenerated '_capnp.rs' files
+use puzzle::inode::PuzzleFS;
+use puzzle::types::{Inode, InodeMode, MetadataBlob};
 use puzzle::{manifest_capnp, metadata_capnp};
 
+use kernel::fs::{DEntry, INodeParams, NeedsRoot, NewSuperBlock, RootDEntry};
+
 module_fs! {
     type: PuzzleFsModule,
     name: "puzzlefs",
@@ -27,7 +30,6 @@
 
 #[derive(Debug)]
 struct PuzzlefsInfo {
-    base_path: CString,
     vfs_mount: Arc<Vfsmount>,
 }
 
@@ -55,9 +57,81 @@ fn try_new() -> Result {
     }
 }
 
+fn puzzlefs_populate_dir(
+    sb: &NewSuperBlock<'_, PuzzleFsModule, NeedsRoot>,
+    pfs: &PuzzleFS,
+    parent: &DEntry<PuzzleFsModule>,
+    ino: u64,
+    name: &CStr,
+    recursion: usize,
+) -> Result {
+    if recursion == 0 {
+        return Err(E2BIG);
+    }
+
+    let inode = Arc::try_new(pfs.find_inode(ino).map_err(|_| EINVAL)?)?;
+    match &inode.mode {
+        InodeMode::File { chunks: _ } => {
+            let params = INodeParams {
+                mode: inode.permissions,
+                ino: inode.ino,
+                value: inode.clone(),
+            };
+            let creator = fs::file_creator::<_, FsFile>();
+            let inode = creator(sb, params)?;
+            sb.try_new_dentry(inode, parent, name)?;
+        }
+        InodeMode::Dir { dir_list } => {
+            let params = INodeParams {
+                mode: inode.permissions,
+                ino: inode.ino,
+                value: inode.clone(),
+            };
+
+            let new_dentry;
+            let new_parent = if name.as_bytes() != c_str!("").as_bytes() {
+                let dcache_inode = sb.sb.try_new_dcache_dir_inode(params)?;
+                new_dentry = sb.try_new_dentry(dcache_inode, parent, name)?;
+                &new_dentry
+            } else {
+                parent
+            };
+
+            for entry in &dir_list.entries {
+                let mut name = entry.name.try_clone()?;
+                // append NUL terminator
+                name.try_push(0)?;
+                let name = CStr::from_bytes_with_nul(&name)?;
+                puzzlefs_populate_dir(sb, pfs, new_parent, entry.ino, name, recursion - 1)?;
+            }
+        }
+        _ => todo!(),
+    }
+
+    Ok(())
+}
+
+/// Creates a new root dentry populated with the given entries.
+fn try_new_populated_root_puzzlefs_dentry(
+    sb: &NewSuperBlock<'_, PuzzleFsModule, NeedsRoot>,
+    pfs: &PuzzleFS,
+    root_value: <PuzzleFsModule as fs::Type>::INodeData,
+) -> Result<RootDEntry<PuzzleFsModule>> {
+    let root_inode = sb.sb.try_new_dcache_dir_inode(INodeParams {
+        mode: 0o755,
+        ino: root_value.ino,
+        value: root_value,
+    })?;
+    let root = sb.try_new_root_dentry(root_inode)?;
+    let ino = 1u64;
+    puzzlefs_populate_dir(sb, pfs, &root, ino, c_str!(""), 10)?;
+    Ok(root)
+}
+
 impl fs::Type for PuzzleFsModule {
     type Context = Self;
-    type INodeData = &'static [u8];
+    // this is Arc so it can be cloned in puzzlefs_populate_dir
+    type INodeData = Arc<Inode>;
     type Data = Box<PuzzlefsInfo>;
     const SUPER_TYPE: fs::Super = fs::Super::Independent;
     const NAME: &'static CStr = c_str!("puzzlefs");
@@ -65,41 +139,35 @@ impl fs::Type for PuzzleFsModule {
     const DCACHE_BASED: bool = true;
 
     fn fill_super(_data: (), sb: fs::NewSuperBlock<'_, Self>) -> Result<&fs::SuperBlock<Self>> {
-        let base_path = CString::try_from_fmt(fmt!("hello world"))?;
-        pr_info!("base_path {:?}\n", base_path);
         let vfs_mount = Vfsmount::new_private_mount(c_str!("/home/puzzlefs_oci"))?;
         pr_info!("vfs_mount {:?}\n", vfs_mount);
 
+        let arc_vfs_mount = Arc::try_new(vfs_mount)?;
+
         let sb = sb.init(
             Box::try_new(PuzzlefsInfo {
-                base_path,
-                vfs_mount: Arc::try_new(vfs_mount)?,
+                vfs_mount: arc_vfs_mount.clone(),
             })?,
             &fs::SuperParams {
                 magic: 0x72757374,
                 ..fs::SuperParams::DEFAULT
             },
         )?;
-        let root = sb.try_new_populated_root_dentry(
-            &[],
-            kernel::fs_entries![
-                file("test1", 0o600, "abc\n".as_bytes(), FsFile),
-                file("test2", 0o600, "def\n".as_bytes(), FsFile),
-                char("test3", 0o600, [].as_slice(), (10, 125)),
-                sock("test4", 0o755, [].as_slice()),
-                fifo("test5", 0o755, [].as_slice()),
-                block("test6", 0o755, [].as_slice(), (1, 1)),
-                dir(
-                    "dir1",
-                    0o755,
-                    [].as_slice(),
-                    [
-                        file("test1", 0o600, "abc\n".as_bytes(), FsFile),
-                        file("test2", 0o600, "def\n".as_bytes(), FsFile),
-                    ]
-                ),
-            ],
+
+        let file = file::RegularFile::from_path_in_root_mnt(
+            &arc_vfs_mount,
+            c_str!("997eed138af30d187e87d682dd2ae9f240fae78f668907a0519460b397c82467"),
+            file::flags::O_RDONLY.try_into().unwrap(),
+            0,
         )?;
+
+        // TODO: figure out how to go from WireFormatError to kernel::error::Error
+        let metadata = MetadataBlob::new(file).map_err(|_| EINVAL)?;
+
+        let mut puzzlefs = PuzzleFS::new(metadata).map_err(|_| EINVAL)?;
+        let root_inode = Arc::try_new(puzzlefs.find_inode(1).map_err(|_| EINVAL)?)?;
+
+        let root = try_new_populated_root_puzzlefs_dentry(&sb, &mut puzzlefs, root_inode)?;
         let sb = sb.init_root(root)?;
         Ok(sb)
     }
@@ -110,7 +178,7 @@ fn fill_super(_data: (), sb: fs::NewSuperBlock<'_, Self>) -> Result<&fs::SuperBl
 #[vtable]
 impl file::Operations for FsFile {
     // must be the same as INodeData
-    type OpenData = &'static [u8];
+    type OpenData = Arc<Inode>;
     type Filesystem = PuzzleFsModule;
     // this is an Arc because Data must be ForeignOwnable and the only implementors of it are Box,
     // Arc and (); we cannot pass a reference to read, so we share Vfsmount using and Arc
@@ -126,14 +194,19 @@ fn open(
 
     fn read(
         data: ArcBorrow<'_, Vfsmount>,
-        file: &file::File,
+        _file: &file::File,
         writer: &mut impl IoBufferWriter,
         offset: u64,
     ) -> Result<usize> {
-        file::read_from_slice(
-            file.inode::<PuzzleFsModule>().ok_or(EINVAL)?.fs_data(),
-            writer,
-            offset,
-        )
+        let mut buf = Vec::try_with_capacity(writer.len())?;
+        buf.try_resize(writer.len(), 0)?;
+        let file = file::RegularFile::from_path_in_root_mnt(
+            &data,
+            c_str!("data"),
+            file::flags::O_RDONLY.try_into().unwrap(),
+            0,
+        )?;
+        let nr_bytes_read = file.read_with_offset(&mut buf[..], offset)?;
+        file::read_from_slice(&buf[..nr_bytes_read], writer, 0)
     }
 }
-- 
2.41.0


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [RFC PATCH v2 08/10] rust: puzzlefs: read the puzzlefs image manifest instead of an individual metadata layer
  2023-07-26 16:45 [RFC PATCH v2 00/10] Rust PuzleFS filesystem driver Ariel Miculas
                   ` (6 preceding siblings ...)
  2023-07-26 16:45 ` [RFC PATCH v2 07/10] samples: puzzlefs: populate the directory entries with the inodes from the puzzlefs metadata file Ariel Miculas
@ 2023-07-26 16:45 ` Ariel Miculas
  2023-07-26 16:45 ` [RFC PATCH v2 09/10] rust: puzzlefs: add support for reading files Ariel Miculas
  2023-07-26 16:45 ` [RFC PATCH v2 10/10] rust: puzzlefs: add oci_root_dir and image_manifest filesystem parameters Ariel Miculas
  9 siblings, 0 replies; 20+ messages in thread
From: Ariel Miculas @ 2023-07-26 16:45 UTC (permalink / raw)
  To: rust-for-linux
  Cc: linux-kernel, linux-fsdevel, tycho, brauner, viro, ojeda,
	alex.gaynor, wedsonaf, Ariel Miculas

The puzzlefs image manifest is the file referenced by index.json and it
contains the array of metadata layers that describe the puzzlefs image.
This file represents the root of the puzzlefs filesystem since we can't
parse json files. When this filesystem is mounted, usermode will need to
read the tag from index.json, find the corresponding puzzlefs image
manifest and pass it to the kernel module.

Due to the lack of BTreeMap in the kernel, only image manifests without
fs verity information are supported for now.

Signed-off-by: Ariel Miculas <amiculas@cisco.com>
---
 samples/rust/puzzle.rs       |  1 +
 samples/rust/puzzle/inode.rs | 22 ++++++++++++----
 samples/rust/puzzle/oci.rs   | 51 ++++++++++++++++++++++++++++++++++++
 samples/rust/puzzlefs.rs     | 23 ++++++++--------
 4 files changed, 80 insertions(+), 17 deletions(-)
 create mode 100644 samples/rust/puzzle/oci.rs

diff --git a/samples/rust/puzzle.rs b/samples/rust/puzzle.rs
index a809cddfb817..e74a248c39ff 100644
--- a/samples/rust/puzzle.rs
+++ b/samples/rust/puzzle.rs
@@ -2,3 +2,4 @@
 pub(crate) mod types;
 pub(crate) use types::{manifest_capnp, metadata_capnp};
 pub(crate) mod inode;
+pub(crate) mod oci;
diff --git a/samples/rust/puzzle/inode.rs b/samples/rust/puzzle/inode.rs
index d792f661aa00..f63cdbc1eac4 100644
--- a/samples/rust/puzzle/inode.rs
+++ b/samples/rust/puzzle/inode.rs
@@ -3,20 +3,32 @@
 
 use crate::puzzle::error::Result;
 use crate::puzzle::error::WireFormatError;
+use crate::puzzle::oci::Image;
 use crate::puzzle::types as format;
-use crate::puzzle::types::{Inode, InodeMode, MetadataBlob};
+use crate::puzzle::types::{Digest, Inode, InodeMode};
 use alloc::vec::Vec;
+use kernel::mount::Vfsmount;
 use kernel::prelude::ENOENT;
+use kernel::str::CStr;
+use kernel::sync::Arc;
 
 pub(crate) struct PuzzleFS {
+    pub(crate) oci: Image,
     layers: Vec<format::MetadataBlob>,
 }
 
 impl PuzzleFS {
-    pub(crate) fn new(md: MetadataBlob) -> Result<Self> {
-        let mut v = Vec::new();
-        v.try_push(md)?;
-        Ok(PuzzleFS { layers: v })
+    pub(crate) fn open(vfsmount: Arc<Vfsmount>, rootfs_path: &CStr) -> Result<PuzzleFS> {
+        let oci = Image::open(vfsmount)?;
+        let rootfs = oci.open_rootfs_blob(rootfs_path)?;
+
+        let mut layers = Vec::new();
+        for md in rootfs.metadatas.iter() {
+            let digest = Digest::try_from(md)?;
+            layers.try_push(oci.open_metadata_blob(&digest)?)?;
+        }
+
+        Ok(PuzzleFS { oci, layers })
     }
 
     pub(crate) fn find_inode(&self, ino: u64) -> Result<Inode> {
diff --git a/samples/rust/puzzle/oci.rs b/samples/rust/puzzle/oci.rs
new file mode 100644
index 000000000000..becb2b868450
--- /dev/null
+++ b/samples/rust/puzzle/oci.rs
@@ -0,0 +1,51 @@
+use crate::puzzle::error::Result;
+use crate::puzzle::types::{Digest, MetadataBlob, Rootfs};
+use kernel::c_str;
+use kernel::file;
+use kernel::file::RegularFile;
+use kernel::mount::Vfsmount;
+use kernel::pr_info;
+use kernel::str::{CStr, CString};
+use kernel::sync::Arc;
+
+pub(crate) struct Image {
+    vfs_mount: Arc<Vfsmount>,
+}
+
+impl Image {
+    pub(crate) fn open(vfsmount: Arc<Vfsmount>) -> Result<Self> {
+        Ok(Image {
+            vfs_mount: vfsmount,
+        })
+    }
+
+    pub(crate) fn blob_path_relative(&self) -> &CStr {
+        c_str!("blobs/sha256")
+    }
+
+    fn open_raw_blob(&self, digest: &Digest) -> Result<RegularFile> {
+        let filename =
+            CString::try_from_fmt(format_args!("{}/{digest}", self.blob_path_relative()))?;
+        pr_info!("trying to open {:?}\n", &filename);
+
+        let file = RegularFile::from_path_in_root_mnt(
+            &self.vfs_mount,
+            &filename,
+            file::flags::O_RDONLY.try_into().unwrap(),
+            0,
+        )?;
+
+        Ok(file)
+    }
+
+    pub(crate) fn open_metadata_blob(&self, digest: &Digest) -> Result<MetadataBlob> {
+        let f = self.open_raw_blob(digest)?;
+        MetadataBlob::new(f)
+    }
+
+    pub(crate) fn open_rootfs_blob(&self, path: &CStr) -> Result<Rootfs> {
+        let digest = Digest::try_from(path)?;
+        let rootfs = Rootfs::open(self.open_raw_blob(&digest)?)?;
+        Ok(rootfs)
+    }
+}
diff --git a/samples/rust/puzzlefs.rs b/samples/rust/puzzlefs.rs
index 1f0073716d91..76dc59403db3 100644
--- a/samples/rust/puzzlefs.rs
+++ b/samples/rust/puzzlefs.rs
@@ -14,7 +14,7 @@
 mod puzzle;
 // Required by the autogenerated '_capnp.rs' files
 use puzzle::inode::PuzzleFS;
-use puzzle::types::{Inode, InodeMode, MetadataBlob};
+use puzzle::types::{Inode, InodeMode};
 use puzzle::{manifest_capnp, metadata_capnp};
 
 use kernel::fs::{DEntry, INodeParams, NeedsRoot, NewSuperBlock, RootDEntry};
@@ -69,7 +69,7 @@ fn puzzlefs_populate_dir(
         return Err(E2BIG);
     }
 
-    let inode = Arc::try_new(pfs.find_inode(ino).map_err(|_| EINVAL)?)?;
+    let inode = Arc::try_new(pfs.find_inode(ino)?)?;
     match &inode.mode {
         InodeMode::File { chunks: _ } => {
             let params = INodeParams {
@@ -154,18 +154,17 @@ fn fill_super(_data: (), sb: fs::NewSuperBlock<'_, Self>) -> Result<&fs::SuperBl
             },
         )?;
 
-        let file = file::RegularFile::from_path_in_root_mnt(
-            &arc_vfs_mount,
-            c_str!("997eed138af30d187e87d682dd2ae9f240fae78f668907a0519460b397c82467"),
-            file::flags::O_RDONLY.try_into().unwrap(),
-            0,
-        )?;
+        let puzzlefs = PuzzleFS::open(
+            arc_vfs_mount,
+            c_str!("2d6602d678140540dc7e96de652a76a8b16e8aca190bae141297bcffdcae901b"),
+        );
 
-        // TODO: figure out how to go from WireFormatError to kernel::error::Error
-        let metadata = MetadataBlob::new(file).map_err(|_| EINVAL)?;
+        if let Err(ref e) = puzzlefs {
+            pr_info!("error opening puzzlefs {e}\n");
+        }
 
-        let mut puzzlefs = PuzzleFS::new(metadata).map_err(|_| EINVAL)?;
-        let root_inode = Arc::try_new(puzzlefs.find_inode(1).map_err(|_| EINVAL)?)?;
+        let mut puzzlefs = puzzlefs?;
+        let root_inode = Arc::try_new(puzzlefs.find_inode(1)?)?;
 
         let root = try_new_populated_root_puzzlefs_dentry(&sb, &mut puzzlefs, root_inode)?;
         let sb = sb.init_root(root)?;
-- 
2.41.0


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [RFC PATCH v2 09/10] rust: puzzlefs: add support for reading files
  2023-07-26 16:45 [RFC PATCH v2 00/10] Rust PuzleFS filesystem driver Ariel Miculas
                   ` (7 preceding siblings ...)
  2023-07-26 16:45 ` [RFC PATCH v2 08/10] rust: puzzlefs: read the puzzlefs image manifest instead of an individual metadata layer Ariel Miculas
@ 2023-07-26 16:45 ` Ariel Miculas
  2023-07-26 16:45 ` [RFC PATCH v2 10/10] rust: puzzlefs: add oci_root_dir and image_manifest filesystem parameters Ariel Miculas
  9 siblings, 0 replies; 20+ messages in thread
From: Ariel Miculas @ 2023-07-26 16:45 UTC (permalink / raw)
  To: rust-for-linux
  Cc: linux-kernel, linux-fsdevel, tycho, brauner, viro, ojeda,
	alex.gaynor, wedsonaf, Ariel Miculas

Each file has an associated list of file chunks, which are identified
using a content addressable blob and an offset. These were generated by
the `puzzlefs build` command, which uses FastCDC to split a filesystem
into chunks.

Signed-off-by: Ariel Miculas <amiculas@cisco.com>
---
 samples/rust/puzzle/inode.rs | 61 +++++++++++++++++++++++++++++++++---
 samples/rust/puzzle/oci.rs   | 32 +++++++++++++++----
 samples/rust/puzzlefs.rs     | 54 +++++++++++++------------------
 3 files changed, 105 insertions(+), 42 deletions(-)

diff --git a/samples/rust/puzzle/inode.rs b/samples/rust/puzzle/inode.rs
index f63cdbc1eac4..03c9f6bee75f 100644
--- a/samples/rust/puzzle/inode.rs
+++ b/samples/rust/puzzle/inode.rs
@@ -7,10 +7,10 @@
 use crate::puzzle::types as format;
 use crate::puzzle::types::{Digest, Inode, InodeMode};
 use alloc::vec::Vec;
+use core::cmp::min;
 use kernel::mount::Vfsmount;
-use kernel::prelude::ENOENT;
+use kernel::prelude::{ENOENT, ENOTDIR};
 use kernel::str::CStr;
-use kernel::sync::Arc;
 
 pub(crate) struct PuzzleFS {
     pub(crate) oci: Image,
@@ -18,8 +18,9 @@ pub(crate) struct PuzzleFS {
 }
 
 impl PuzzleFS {
-    pub(crate) fn open(vfsmount: Arc<Vfsmount>, rootfs_path: &CStr) -> Result<PuzzleFS> {
-        let oci = Image::open(vfsmount)?;
+    pub(crate) fn open(oci_root_dir: &CStr, rootfs_path: &CStr) -> Result<PuzzleFS> {
+        let vfs_mount = Vfsmount::new_private_mount(oci_root_dir)?;
+        let oci = Image::open(vfs_mount)?;
         let rootfs = oci.open_rootfs_blob(rootfs_path)?;
 
         let mut layers = Vec::new();
@@ -46,3 +47,55 @@ pub(crate) fn find_inode(&self, ino: u64) -> Result<Inode> {
         Err(WireFormatError::from_errno(ENOENT))
     }
 }
+
+pub(crate) fn file_read(
+    oci: &Image,
+    inode: &Inode,
+    offset: usize,
+    data: &mut [u8],
+) -> Result<usize> {
+    let chunks = match &inode.mode {
+        InodeMode::File { chunks } => chunks,
+        _ => return Err(WireFormatError::from_errno(ENOTDIR)),
+    };
+
+    // TODO: fix all this casting...
+    let end = offset + data.len();
+
+    let mut file_offset = 0;
+    let mut buf_offset = 0;
+    for chunk in chunks {
+        // have we read enough?
+        if file_offset > end {
+            break;
+        }
+
+        // should we skip this chunk?
+        if file_offset + (chunk.len as usize) < offset {
+            file_offset += chunk.len as usize;
+            continue;
+        }
+
+        let addl_offset = if offset > file_offset {
+            offset - file_offset
+        } else {
+            0
+        };
+
+        // ok, need to read this chunk; how much?
+        let left_in_buf = data.len() - buf_offset;
+        let to_read = min(left_in_buf, chunk.len as usize - addl_offset);
+
+        let start = buf_offset;
+        let finish = start + to_read;
+        file_offset += addl_offset;
+
+        // how many did we actually read?
+        let n = oci.fill_from_chunk(chunk.blob, addl_offset as u64, &mut data[start..finish])?;
+        file_offset += n;
+        buf_offset += n;
+    }
+
+    // discard any extra if we hit EOF
+    Ok(buf_offset)
+}
diff --git a/samples/rust/puzzle/oci.rs b/samples/rust/puzzle/oci.rs
index becb2b868450..5aa60ded8419 100644
--- a/samples/rust/puzzle/oci.rs
+++ b/samples/rust/puzzle/oci.rs
@@ -1,19 +1,21 @@
-use crate::puzzle::error::Result;
+use crate::puzzle::error::{Result, WireFormatError};
+use crate::puzzle::types as format;
 use crate::puzzle::types::{Digest, MetadataBlob, Rootfs};
 use kernel::c_str;
 use kernel::file;
 use kernel::file::RegularFile;
 use kernel::mount::Vfsmount;
-use kernel::pr_info;
+use kernel::pr_debug;
+use kernel::prelude::ENOTSUPP;
 use kernel::str::{CStr, CString};
-use kernel::sync::Arc;
 
+#[derive(Debug)]
 pub(crate) struct Image {
-    vfs_mount: Arc<Vfsmount>,
+    pub(crate) vfs_mount: Vfsmount,
 }
 
 impl Image {
-    pub(crate) fn open(vfsmount: Arc<Vfsmount>) -> Result<Self> {
+    pub(crate) fn open(vfsmount: Vfsmount) -> Result<Self> {
         Ok(Image {
             vfs_mount: vfsmount,
         })
@@ -26,7 +28,7 @@ pub(crate) fn blob_path_relative(&self) -> &CStr {
     fn open_raw_blob(&self, digest: &Digest) -> Result<RegularFile> {
         let filename =
             CString::try_from_fmt(format_args!("{}/{digest}", self.blob_path_relative()))?;
-        pr_info!("trying to open {:?}\n", &filename);
+        pr_debug!("trying to open {:?}\n", &*filename);
 
         let file = RegularFile::from_path_in_root_mnt(
             &self.vfs_mount,
@@ -48,4 +50,22 @@ pub(crate) fn open_rootfs_blob(&self, path: &CStr) -> Result<Rootfs> {
         let rootfs = Rootfs::open(self.open_raw_blob(&digest)?)?;
         Ok(rootfs)
     }
+
+    pub(crate) fn fill_from_chunk(
+        &self,
+        chunk: format::BlobRef,
+        addl_offset: u64,
+        buf: &mut [u8],
+    ) -> Result<usize> {
+        let digest = &<Digest>::try_from(chunk)?;
+
+        let blob = if chunk.compressed {
+            return Err(WireFormatError::KernelError(ENOTSUPP));
+        } else {
+            self.open_raw_blob(digest)?
+        };
+
+        let n = blob.read_with_offset(buf, chunk.offset + addl_offset)?;
+        Ok(n)
+    }
 }
diff --git a/samples/rust/puzzlefs.rs b/samples/rust/puzzlefs.rs
index 76dc59403db3..dad7ecc76eca 100644
--- a/samples/rust/puzzlefs.rs
+++ b/samples/rust/puzzlefs.rs
@@ -3,7 +3,6 @@
 //! Rust file system sample.
 
 use kernel::module_fs;
-use kernel::mount::Vfsmount;
 use kernel::prelude::*;
 use kernel::{
     c_str, file, fs,
@@ -13,7 +12,7 @@
 
 mod puzzle;
 // Required by the autogenerated '_capnp.rs' files
-use puzzle::inode::PuzzleFS;
+use puzzle::inode::{file_read, PuzzleFS};
 use puzzle::types::{Inode, InodeMode};
 use puzzle::{manifest_capnp, metadata_capnp};
 
@@ -28,9 +27,8 @@
 
 struct PuzzleFsModule;
 
-#[derive(Debug)]
 struct PuzzlefsInfo {
-    vfs_mount: Arc<Vfsmount>,
+    puzzlefs: Arc<PuzzleFS>,
 }
 
 #[vtable]
@@ -139,14 +137,20 @@ impl fs::Type for PuzzleFsModule {
     const DCACHE_BASED: bool = true;
 
     fn fill_super(_data: (), sb: fs::NewSuperBlock<'_, Self>) -> Result<&fs::SuperBlock<Self>> {
-        let vfs_mount = Vfsmount::new_private_mount(c_str!("/home/puzzlefs_oci"))?;
-        pr_info!("vfs_mount {:?}\n", vfs_mount);
+        let puzzlefs = PuzzleFS::open(
+            c_str!("/home/puzzlefs_oci"),
+            c_str!("2d6602d678140540dc7e96de652a76a8b16e8aca190bae141297bcffdcae901b"),
+        );
 
-        let arc_vfs_mount = Arc::try_new(vfs_mount)?;
+        if let Err(ref e) = puzzlefs {
+            pr_info!("error opening puzzlefs {e}\n");
+        }
+
+        let puzzlefs = Arc::try_new(puzzlefs?)?;
 
         let sb = sb.init(
             Box::try_new(PuzzlefsInfo {
-                vfs_mount: arc_vfs_mount.clone(),
+                puzzlefs: puzzlefs.clone(),
             })?,
             &fs::SuperParams {
                 magic: 0x72757374,
@@ -154,19 +158,9 @@ fn fill_super(_data: (), sb: fs::NewSuperBlock<'_, Self>) -> Result<&fs::SuperBl
             },
         )?;
 
-        let puzzlefs = PuzzleFS::open(
-            arc_vfs_mount,
-            c_str!("2d6602d678140540dc7e96de652a76a8b16e8aca190bae141297bcffdcae901b"),
-        );
-
-        if let Err(ref e) = puzzlefs {
-            pr_info!("error opening puzzlefs {e}\n");
-        }
-
-        let mut puzzlefs = puzzlefs?;
         let root_inode = Arc::try_new(puzzlefs.find_inode(1)?)?;
 
-        let root = try_new_populated_root_puzzlefs_dentry(&sb, &mut puzzlefs, root_inode)?;
+        let root = try_new_populated_root_puzzlefs_dentry(&sb, &puzzlefs, root_inode)?;
         let sb = sb.init_root(root)?;
         Ok(sb)
     }
@@ -180,32 +174,28 @@ impl file::Operations for FsFile {
     type OpenData = Arc<Inode>;
     type Filesystem = PuzzleFsModule;
     // this is an Arc because Data must be ForeignOwnable and the only implementors of it are Box,
-    // Arc and (); we cannot pass a reference to read, so we share Vfsmount using and Arc
-    type Data = Arc<Vfsmount>;
+    // Arc and (); we cannot pass a reference to the read callback, so we share PuzzleFS using Arc
+    type Data = Arc<PuzzleFS>;
 
     fn open(
         fs_info: &PuzzlefsInfo,
         _context: &Self::OpenData,
         _file: &file::File,
     ) -> Result<Self::Data> {
-        Ok(fs_info.vfs_mount.clone())
+        Ok(fs_info.puzzlefs.clone())
     }
 
     fn read(
-        data: ArcBorrow<'_, Vfsmount>,
-        _file: &file::File,
+        data: ArcBorrow<'_, PuzzleFS>,
+        file: &file::File,
         writer: &mut impl IoBufferWriter,
         offset: u64,
     ) -> Result<usize> {
+        let inode = file.inode::<PuzzleFsModule>().ok_or(EINVAL)?.fs_data();
         let mut buf = Vec::try_with_capacity(writer.len())?;
         buf.try_resize(writer.len(), 0)?;
-        let file = file::RegularFile::from_path_in_root_mnt(
-            &data,
-            c_str!("data"),
-            file::flags::O_RDONLY.try_into().unwrap(),
-            0,
-        )?;
-        let nr_bytes_read = file.read_with_offset(&mut buf[..], offset)?;
-        file::read_from_slice(&buf[..nr_bytes_read], writer, 0)
+        let read = file_read(&data.oci, inode, offset as usize, &mut buf)?;
+        buf.truncate(read);
+        file::read_from_slice(&buf, writer, 0)
     }
 }
-- 
2.41.0


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [RFC PATCH v2 10/10] rust: puzzlefs: add oci_root_dir and image_manifest filesystem parameters
  2023-07-26 16:45 [RFC PATCH v2 00/10] Rust PuzleFS filesystem driver Ariel Miculas
                   ` (8 preceding siblings ...)
  2023-07-26 16:45 ` [RFC PATCH v2 09/10] rust: puzzlefs: add support for reading files Ariel Miculas
@ 2023-07-26 16:45 ` Ariel Miculas
  2023-07-26 21:08   ` Trevor Gross
  9 siblings, 1 reply; 20+ messages in thread
From: Ariel Miculas @ 2023-07-26 16:45 UTC (permalink / raw)
  To: rust-for-linux
  Cc: linux-kernel, linux-fsdevel, tycho, brauner, viro, ojeda,
	alex.gaynor, wedsonaf, Ariel Miculas

These parameters are passed when mounting puzzlefs using '-o' option of
mount:
-o oci_root_dir="/path/to/oci/dir"
-o image_manifest="root_hash_of_image_manifest"

For a particular manifest in the manifests array in index.json (located
in the oci_root_dir), the root hash of the image manifest is found in
the digest field.

It would be nicer if we could pass the tag, but we don't support json
deserialization.

Example of mount:
mount -t puzzlefs -o oci_root_dir="/home/puzzlefs_oci" -o \
image_manifest="2d6602d678140540dc7e96de652a76a8b16e8aca190bae141297bcffdcae901b" \
none /mnt

Signed-off-by: Ariel Miculas <amiculas@cisco.com>
---
 samples/rust/puzzlefs.rs | 63 ++++++++++++++++++++++++++--------------
 1 file changed, 41 insertions(+), 22 deletions(-)

diff --git a/samples/rust/puzzlefs.rs b/samples/rust/puzzlefs.rs
index dad7ecc76eca..4e9a8aedf0c1 100644
--- a/samples/rust/puzzlefs.rs
+++ b/samples/rust/puzzlefs.rs
@@ -7,6 +7,7 @@
 use kernel::{
     c_str, file, fs,
     io_buffer::IoBufferWriter,
+    str::CString,
     sync::{Arc, ArcBorrow},
 };
 
@@ -31,27 +32,29 @@ struct PuzzlefsInfo {
     puzzlefs: Arc<PuzzleFS>,
 }
 
+#[derive(Default)]
+struct PuzzleFsParams {
+    oci_root_dir: Option<CString>,
+    image_manifest: Option<CString>,
+}
+
 #[vtable]
 impl fs::Context<Self> for PuzzleFsModule {
-    type Data = ();
-
-    kernel::define_fs_params! {(),
-        {flag, "flag", |_, v| { pr_info!("flag passed-in: {v}\n"); Ok(()) } },
-        {flag_no, "flagno", |_, v| { pr_info!("flagno passed-in: {v}\n"); Ok(()) } },
-        {bool, "bool", |_, v| { pr_info!("bool passed-in: {v}\n"); Ok(()) } },
-        {u32, "u32", |_, v| { pr_info!("u32 passed-in: {v}\n"); Ok(()) } },
-        {u32oct, "u32oct", |_, v| { pr_info!("u32oct passed-in: {v}\n"); Ok(()) } },
-        {u32hex, "u32hex", |_, v| { pr_info!("u32hex passed-in: {v}\n"); Ok(()) } },
-        {s32, "s32", |_, v| { pr_info!("s32 passed-in: {v}\n"); Ok(()) } },
-        {u64, "u64", |_, v| { pr_info!("u64 passed-in: {v}\n"); Ok(()) } },
-        {string, "string", |_, v| { pr_info!("string passed-in: {v}\n"); Ok(()) } },
-        {enum, "enum", [("first", 10), ("second", 20)], |_, v| {
-            pr_info!("enum passed-in: {v}\n"); Ok(()) }
-        },
+    type Data = Box<PuzzleFsParams>;
+
+    kernel::define_fs_params! {Box<PuzzleFsParams>,
+        {string, "oci_root_dir", |s, v| {
+                                      s.oci_root_dir = Some(CString::try_from_fmt(format_args!("{v}"))?);
+                                      Ok(())
+                                  }},
+        {string, "image_manifest", |s, v| {
+                                      s.image_manifest = Some(CString::try_from_fmt(format_args!("{v}"))?);
+                                      Ok(())
+                                  }},
     }
 
-    fn try_new() -> Result {
-        Ok(())
+    fn try_new() -> Result<Self::Data> {
+        Ok(Box::try_new(PuzzleFsParams::default())?)
     }
 }
 
@@ -136,11 +139,27 @@ impl fs::Type for PuzzleFsModule {
     const FLAGS: i32 = fs::flags::USERNS_MOUNT;
     const DCACHE_BASED: bool = true;
 
-    fn fill_super(_data: (), sb: fs::NewSuperBlock<'_, Self>) -> Result<&fs::SuperBlock<Self>> {
-        let puzzlefs = PuzzleFS::open(
-            c_str!("/home/puzzlefs_oci"),
-            c_str!("2d6602d678140540dc7e96de652a76a8b16e8aca190bae141297bcffdcae901b"),
-        );
+    fn fill_super(
+        data: Box<PuzzleFsParams>,
+        sb: fs::NewSuperBlock<'_, Self>,
+    ) -> Result<&fs::SuperBlock<Self>> {
+        let oci_root_dir = match data.oci_root_dir {
+            Some(val) => val,
+            None => {
+                pr_err!("missing oci_root_dir parameter!\n");
+                return Err(ENOTSUPP);
+            }
+        };
+
+        let image_manifest = match data.image_manifest {
+            Some(val) => val,
+            None => {
+                pr_err!("missing image_manifest parameter!\n");
+                return Err(ENOTSUPP);
+            }
+        };
+
+        let puzzlefs = PuzzleFS::open(&oci_root_dir, &image_manifest);
 
         if let Err(ref e) = puzzlefs {
             pr_info!("error opening puzzlefs {e}\n");
-- 
2.41.0


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* Re: [RFC PATCH v2 10/10] rust: puzzlefs: add oci_root_dir and image_manifest filesystem parameters
  2023-07-26 16:45 ` [RFC PATCH v2 10/10] rust: puzzlefs: add oci_root_dir and image_manifest filesystem parameters Ariel Miculas
@ 2023-07-26 21:08   ` Trevor Gross
  2023-07-26 23:47     ` Wedson Almeida Filho
  0 siblings, 1 reply; 20+ messages in thread
From: Trevor Gross @ 2023-07-26 21:08 UTC (permalink / raw)
  To: Ariel Miculas
  Cc: rust-for-linux, linux-kernel, linux-fsdevel, tycho, brauner, viro,
	ojeda, alex.gaynor, wedsonaf

On Wed, Jul 26, 2023 at 12:55 PM Ariel Miculas <amiculas@cisco.com> wrote:
>
> These parameters are passed when mounting puzzlefs using '-o' option of
> mount:
> -o oci_root_dir="/path/to/oci/dir"
> -o image_manifest="root_hash_of_image_manifest"
>
> For a particular manifest in the manifests array in index.json (located
> in the oci_root_dir), the root hash of the image manifest is found in
> the digest field.
>
> It would be nicer if we could pass the tag, but we don't support json
> deserialization.
>
> Example of mount:
> mount -t puzzlefs -o oci_root_dir="/home/puzzlefs_oci" -o \
> image_manifest="2d6602d678140540dc7e96de652a76a8b16e8aca190bae141297bcffdcae901b" \
> none /mnt
>
> Signed-off-by: Ariel Miculas <amiculas@cisco.com>
> ---
>  samples/rust/puzzlefs.rs | 63 ++++++++++++++++++++++++++--------------
>  1 file changed, 41 insertions(+), 22 deletions(-)
>
> diff --git a/samples/rust/puzzlefs.rs b/samples/rust/puzzlefs.rs
> index dad7ecc76eca..4e9a8aedf0c1 100644
> --- a/samples/rust/puzzlefs.rs
> +++ b/samples/rust/puzzlefs.rs
> @@ -7,6 +7,7 @@
>  use kernel::{
>      c_str, file, fs,
>      io_buffer::IoBufferWriter,
> +    str::CString,
>      sync::{Arc, ArcBorrow},
>  };
>
> @@ -31,27 +32,29 @@ struct PuzzlefsInfo {
>      puzzlefs: Arc<PuzzleFS>,
>  }
>
> +#[derive(Default)]
> +struct PuzzleFsParams {
> +    oci_root_dir: Option<CString>,
> +    image_manifest: Option<CString>,
> +}
> +
>  #[vtable]
>  impl fs::Context<Self> for PuzzleFsModule {
> -    type Data = ();
> -
> -    kernel::define_fs_params! {(),
> -        {flag, "flag", |_, v| { pr_info!("flag passed-in: {v}\n"); Ok(()) } },
> -        {flag_no, "flagno", |_, v| { pr_info!("flagno passed-in: {v}\n"); Ok(()) } },
> -        {bool, "bool", |_, v| { pr_info!("bool passed-in: {v}\n"); Ok(()) } },
> -        {u32, "u32", |_, v| { pr_info!("u32 passed-in: {v}\n"); Ok(()) } },
> -        {u32oct, "u32oct", |_, v| { pr_info!("u32oct passed-in: {v}\n"); Ok(()) } },
> -        {u32hex, "u32hex", |_, v| { pr_info!("u32hex passed-in: {v}\n"); Ok(()) } },
> -        {s32, "s32", |_, v| { pr_info!("s32 passed-in: {v}\n"); Ok(()) } },
> -        {u64, "u64", |_, v| { pr_info!("u64 passed-in: {v}\n"); Ok(()) } },
> -        {string, "string", |_, v| { pr_info!("string passed-in: {v}\n"); Ok(()) } },
> -        {enum, "enum", [("first", 10), ("second", 20)], |_, v| {
> -            pr_info!("enum passed-in: {v}\n"); Ok(()) }
> -        },
> +    type Data = Box<PuzzleFsParams>;
> +
> +    kernel::define_fs_params! {Box<PuzzleFsParams>,
> +        {string, "oci_root_dir", |s, v| {
> +                                      s.oci_root_dir = Some(CString::try_from_fmt(format_args!("{v}"))?);
> +                                      Ok(())
> +                                  }},
> +        {string, "image_manifest", |s, v| {
> +                                      s.image_manifest = Some(CString::try_from_fmt(format_args!("{v}"))?);
> +                                      Ok(())
> +                                  }},
>      }
>
> -    fn try_new() -> Result {
> -        Ok(())
> +    fn try_new() -> Result<Self::Data> {
> +        Ok(Box::try_new(PuzzleFsParams::default())?)
>      }
>  }
>
> @@ -136,11 +139,27 @@ impl fs::Type for PuzzleFsModule {
>      const FLAGS: i32 = fs::flags::USERNS_MOUNT;
>      const DCACHE_BASED: bool = true;
>
> -    fn fill_super(_data: (), sb: fs::NewSuperBlock<'_, Self>) -> Result<&fs::SuperBlock<Self>> {
> -        let puzzlefs = PuzzleFS::open(
> -            c_str!("/home/puzzlefs_oci"),
> -            c_str!("2d6602d678140540dc7e96de652a76a8b16e8aca190bae141297bcffdcae901b"),
> -        );
> +    fn fill_super(
> +        data: Box<PuzzleFsParams>,
> +        sb: fs::NewSuperBlock<'_, Self>,
> +    ) -> Result<&fs::SuperBlock<Self>> {
> +        let oci_root_dir = match data.oci_root_dir {
> +            Some(val) => val,
> +            None => {
> +                pr_err!("missing oci_root_dir parameter!\n");
> +                return Err(ENOTSUPP);
> +            }
> +        };
> +
> +        let image_manifest = match data.image_manifest {
> +            Some(val) => val,
> +            None => {
> +                pr_err!("missing image_manifest parameter!\n");
> +                return Err(ENOTSUPP);
> +            }
> +        };
> +

The guard syntax (available since 1.65) can make these kinds of match statements
cleaner:

    let Some(oci_root_dir) = data.oci_root_dir else {
        pr_err!("missing oci_root_dir parameter!\n");
        return Err(ENOTSUPP);
    }

    let Some(image_manifest) ...

> +        let puzzlefs = PuzzleFS::open(&oci_root_dir, &image_manifest);
>
>          if let Err(ref e) = puzzlefs {
>              pr_info!("error opening puzzlefs {e}\n");
> --
> 2.41.0
>
>

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [RFC PATCH v2 03/10] rust: kernel: add an abstraction over vfsmount to allow cloning a new private mount
  2023-07-26 16:45 ` [RFC PATCH v2 03/10] rust: kernel: add an abstraction over vfsmount to allow cloning a new private mount Ariel Miculas
@ 2023-07-26 22:34   ` Trevor Gross
  2023-07-27 13:10     ` Ariel Miculas
  0 siblings, 1 reply; 20+ messages in thread
From: Trevor Gross @ 2023-07-26 22:34 UTC (permalink / raw)
  To: Ariel Miculas
  Cc: rust-for-linux, linux-kernel, linux-fsdevel, tycho, brauner, viro,
	ojeda, alex.gaynor, wedsonaf

On Wed, Jul 26, 2023 at 12:54 PM Ariel Miculas <amiculas@cisco.com> wrote:
> [...]
> +++ b/rust/kernel/mount.rs
> @@ -0,0 +1,71 @@
> [...]
> +impl Vfsmount {
> +    /// Create a new private mount clone based on a path name
> +    pub fn new_private_mount(path_name: &CStr) -> Result<Self> {
> +        let path: Path = Path(Opaque::uninit());
> +        // SAFETY: path_name is a &CStr, so it's a valid string pointer; path is an uninitialized
> +        // struct stored on the stack and it's ok because kern_path expects an out parameter
> +        let err = unsafe {
> +            bindings::kern_path(
> +                path_name.as_ptr() as *const i8,
> +                bindings::LOOKUP_FOLLOW | bindings::LOOKUP_DIRECTORY,
> +                path.0.get(),
> +            )
> +        };

Nit: I believe that `.cast::<i8>()` is preferred over `as *const T`
because it makes it harder to
accidentally change constness or indirection level. This isn't always
followed in kernel, but
good practice.

> +        if err != 0 {
> +            pr_err!("failed to resolve '{}': {}\n", path_name, err);
> +            return Err(EINVAL);
> +        }
> +
> +        // SAFETY: path is a struct stored on the stack and it is  initialized because the call to
> +        // kern_path succeeded
> +        let vfsmount = unsafe { from_err_ptr(bindings::clone_private_mount(path.0.get()))? };
> +
> +        // Don't inherit atime flags
> +        // SAFETY: we called from_err_ptr so it's safe to dereference this pointer
> +        unsafe {
> +            (*vfsmount).mnt_flags &=
> +                !(bindings::MNT_NOATIME | bindings::MNT_NODIRATIME | bindings::MNT_RELATIME) as i32;
> +        }

Nit: it looks a bit cleaner to OR the flags outside of the unsafe
block, which is also nice because then
the unsafe block only contains the unsafe ops (could do this above
too). Also - I think maybe style
might encourage `// CAST:` where `as` is used?

    let new_flags =
        !(bindings::MNT_NOATIME | bindings::MNT_NODIRATIME |
bindings::MNT_RELATIME) as i32;

    // SAFETY: we called from_err_ptr so it's safe to dereference this pointer
    unsafe { (*vfsmount).mnt_flags &= new_flags }

> +        Ok(Self { vfsmount })
> +    }
> +
> +    /// Returns a raw pointer to vfsmount
> +    pub fn get(&self) -> *mut bindings::vfsmount {
> +        self.vfsmount
> +    }
> +}
> +
> +impl Drop for Vfsmount {
> +    fn drop(&mut self) {
> +        // SAFETY new_private_mount makes sure to return a valid pointer
> +        unsafe { bindings::kern_unmount(self.vfsmount) };
> +    }
> +}
> --
> 2.41.0
>
>

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [RFC PATCH v2 10/10] rust: puzzlefs: add oci_root_dir and image_manifest filesystem parameters
  2023-07-26 21:08   ` Trevor Gross
@ 2023-07-26 23:47     ` Wedson Almeida Filho
  2023-07-27  0:02       ` Trevor Gross
  0 siblings, 1 reply; 20+ messages in thread
From: Wedson Almeida Filho @ 2023-07-26 23:47 UTC (permalink / raw)
  To: Trevor Gross
  Cc: Ariel Miculas, rust-for-linux, linux-kernel, linux-fsdevel, tycho,
	brauner, viro, ojeda, alex.gaynor

On Wed, 26 Jul 2023 at 18:08, Trevor Gross <tmgross@umich.edu> wrote:
>
> On Wed, Jul 26, 2023 at 12:55 PM Ariel Miculas <amiculas@cisco.com> wrote:
> >
> > These parameters are passed when mounting puzzlefs using '-o' option of
> > mount:
> > -o oci_root_dir="/path/to/oci/dir"
> > -o image_manifest="root_hash_of_image_manifest"
> >
> > For a particular manifest in the manifests array in index.json (located
> > in the oci_root_dir), the root hash of the image manifest is found in
> > the digest field.
> >
> > It would be nicer if we could pass the tag, but we don't support json
> > deserialization.
> >
> > Example of mount:
> > mount -t puzzlefs -o oci_root_dir="/home/puzzlefs_oci" -o \
> > image_manifest="2d6602d678140540dc7e96de652a76a8b16e8aca190bae141297bcffdcae901b" \
> > none /mnt
> >
> > Signed-off-by: Ariel Miculas <amiculas@cisco.com>
> > ---
> >  samples/rust/puzzlefs.rs | 63 ++++++++++++++++++++++++++--------------
> >  1 file changed, 41 insertions(+), 22 deletions(-)
> >
> > diff --git a/samples/rust/puzzlefs.rs b/samples/rust/puzzlefs.rs
> > index dad7ecc76eca..4e9a8aedf0c1 100644
> > --- a/samples/rust/puzzlefs.rs
> > +++ b/samples/rust/puzzlefs.rs
> > @@ -7,6 +7,7 @@
> >  use kernel::{
> >      c_str, file, fs,
> >      io_buffer::IoBufferWriter,
> > +    str::CString,
> >      sync::{Arc, ArcBorrow},
> >  };
> >
> > @@ -31,27 +32,29 @@ struct PuzzlefsInfo {
> >      puzzlefs: Arc<PuzzleFS>,
> >  }
> >
> > +#[derive(Default)]
> > +struct PuzzleFsParams {
> > +    oci_root_dir: Option<CString>,
> > +    image_manifest: Option<CString>,
> > +}
> > +
> >  #[vtable]
> >  impl fs::Context<Self> for PuzzleFsModule {
> > -    type Data = ();
> > -
> > -    kernel::define_fs_params! {(),
> > -        {flag, "flag", |_, v| { pr_info!("flag passed-in: {v}\n"); Ok(()) } },
> > -        {flag_no, "flagno", |_, v| { pr_info!("flagno passed-in: {v}\n"); Ok(()) } },
> > -        {bool, "bool", |_, v| { pr_info!("bool passed-in: {v}\n"); Ok(()) } },
> > -        {u32, "u32", |_, v| { pr_info!("u32 passed-in: {v}\n"); Ok(()) } },
> > -        {u32oct, "u32oct", |_, v| { pr_info!("u32oct passed-in: {v}\n"); Ok(()) } },
> > -        {u32hex, "u32hex", |_, v| { pr_info!("u32hex passed-in: {v}\n"); Ok(()) } },
> > -        {s32, "s32", |_, v| { pr_info!("s32 passed-in: {v}\n"); Ok(()) } },
> > -        {u64, "u64", |_, v| { pr_info!("u64 passed-in: {v}\n"); Ok(()) } },
> > -        {string, "string", |_, v| { pr_info!("string passed-in: {v}\n"); Ok(()) } },
> > -        {enum, "enum", [("first", 10), ("second", 20)], |_, v| {
> > -            pr_info!("enum passed-in: {v}\n"); Ok(()) }
> > -        },
> > +    type Data = Box<PuzzleFsParams>;
> > +
> > +    kernel::define_fs_params! {Box<PuzzleFsParams>,
> > +        {string, "oci_root_dir", |s, v| {
> > +                                      s.oci_root_dir = Some(CString::try_from_fmt(format_args!("{v}"))?);
> > +                                      Ok(())
> > +                                  }},
> > +        {string, "image_manifest", |s, v| {
> > +                                      s.image_manifest = Some(CString::try_from_fmt(format_args!("{v}"))?);
> > +                                      Ok(())
> > +                                  }},
> >      }
> >
> > -    fn try_new() -> Result {
> > -        Ok(())
> > +    fn try_new() -> Result<Self::Data> {
> > +        Ok(Box::try_new(PuzzleFsParams::default())?)
> >      }
> >  }
> >
> > @@ -136,11 +139,27 @@ impl fs::Type for PuzzleFsModule {
> >      const FLAGS: i32 = fs::flags::USERNS_MOUNT;
> >      const DCACHE_BASED: bool = true;
> >
> > -    fn fill_super(_data: (), sb: fs::NewSuperBlock<'_, Self>) -> Result<&fs::SuperBlock<Self>> {
> > -        let puzzlefs = PuzzleFS::open(
> > -            c_str!("/home/puzzlefs_oci"),
> > -            c_str!("2d6602d678140540dc7e96de652a76a8b16e8aca190bae141297bcffdcae901b"),
> > -        );
> > +    fn fill_super(
> > +        data: Box<PuzzleFsParams>,
> > +        sb: fs::NewSuperBlock<'_, Self>,
> > +    ) -> Result<&fs::SuperBlock<Self>> {
> > +        let oci_root_dir = match data.oci_root_dir {
> > +            Some(val) => val,
> > +            None => {
> > +                pr_err!("missing oci_root_dir parameter!\n");
> > +                return Err(ENOTSUPP);
> > +            }
> > +        };
> > +
> > +        let image_manifest = match data.image_manifest {
> > +            Some(val) => val,
> > +            None => {
> > +                pr_err!("missing image_manifest parameter!\n");
> > +                return Err(ENOTSUPP);
> > +            }
> > +        };
> > +
>
> The guard syntax (available since 1.65) can make these kinds of match statements
> cleaner:

This is unstable though.

We try to stay away from unstable features unless we really need them,
which I feel is not the case here.

>     let Some(oci_root_dir) = data.oci_root_dir else {
>         pr_err!("missing oci_root_dir parameter!\n");
>         return Err(ENOTSUPP);
>     }
>
>     let Some(image_manifest) ...
>
> > +        let puzzlefs = PuzzleFS::open(&oci_root_dir, &image_manifest);
> >
> >          if let Err(ref e) = puzzlefs {
> >              pr_info!("error opening puzzlefs {e}\n");
> > --
> > 2.41.0
> >
> >

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [RFC PATCH v2 04/10] rust: file: Add a new RegularFile newtype useful for reading files
  2023-07-26 16:45 ` [RFC PATCH v2 04/10] rust: file: Add a new RegularFile newtype useful for reading files Ariel Miculas
@ 2023-07-26 23:52   ` Trevor Gross
  2023-07-27 13:18     ` Ariel Miculas
  0 siblings, 1 reply; 20+ messages in thread
From: Trevor Gross @ 2023-07-26 23:52 UTC (permalink / raw)
  To: Ariel Miculas
  Cc: rust-for-linux, linux-kernel, linux-fsdevel, tycho, brauner, viro,
	ojeda, alex.gaynor, wedsonaf

On Wed, Jul 26, 2023 at 12:54 PM Ariel Miculas <amiculas@cisco.com> wrote:
>
> Implement from_path, from_path_in_root_mnt, read_with_offset,
> read_to_end and get_file_size methods for a RegularFile newtype.
>
> Signed-off-by: Ariel Miculas <amiculas@cisco.com>
> ---
>  rust/helpers.c       |   6 ++
>  rust/kernel/error.rs |   4 +-
>  rust/kernel/file.rs  | 129 ++++++++++++++++++++++++++++++++++++++++++-
>  3 files changed, 135 insertions(+), 4 deletions(-)
>
> diff --git a/rust/helpers.c b/rust/helpers.c
> index eed8ace52fb5..9e860a554cda 100644
> --- a/rust/helpers.c
> +++ b/rust/helpers.c
> @@ -213,6 +213,12 @@ void *rust_helper_alloc_inode_sb(struct super_block *sb,
>  }
>  EXPORT_SYMBOL_GPL(rust_helper_alloc_inode_sb);
>
> +loff_t rust_helper_i_size_read(const struct inode *inode)
> +{
> +       return i_size_read(inode);
> +}
> +EXPORT_SYMBOL_GPL(rust_helper_i_size_read);
> +
>  /*
>   * We use `bindgen`'s `--size_t-is-usize` option to bind the C `size_t` type
>   * as the Rust `usize` type, so we can use it in contexts where Rust
> diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs
> index 05fcab6abfe6..e061c83f806a 100644
> --- a/rust/kernel/error.rs
> +++ b/rust/kernel/error.rs
> @@ -273,9 +273,7 @@ pub fn to_result(err: core::ffi::c_int) -> Result {
>  ///     }
>  /// }
>  /// ```
> -// TODO: Remove `dead_code` marker once an in-kernel client is available.
> -#[allow(dead_code)]
> -pub(crate) fn from_err_ptr<T>(ptr: *mut T) -> Result<*mut T> {
> +pub fn from_err_ptr<T>(ptr: *mut T) -> Result<*mut T> {
>      // CAST: Casting a pointer to `*const core::ffi::c_void` is always valid.
>      let const_ptr: *const core::ffi::c_void = ptr.cast();
>      // SAFETY: The FFI function does not deref the pointer.
> diff --git a/rust/kernel/file.rs b/rust/kernel/file.rs
> index 494939ba74df..a3002c416dbb 100644
> --- a/rust/kernel/file.rs
> +++ b/rust/kernel/file.rs
> @@ -8,11 +8,13 @@
>  use crate::{
>      bindings,
>      cred::Credential,
> -    error::{code::*, from_result, Error, Result},
> +    error::{code::*, from_err_ptr, from_result, Error, Result},
>      fs,
>      io_buffer::{IoBufferReader, IoBufferWriter},
>      iov_iter::IovIter,
>      mm,
> +    mount::Vfsmount,
> +    str::CStr,
>      sync::CondVar,
>      types::ARef,
>      types::AlwaysRefCounted,
> @@ -20,6 +22,7 @@
>      types::Opaque,
>      user_ptr::{UserSlicePtr, UserSlicePtrReader, UserSlicePtrWriter},
>  };
> +use alloc::vec::Vec;
>  use core::convert::{TryFrom, TryInto};
>  use core::{marker, mem, ptr};
>  use macros::vtable;
> @@ -201,6 +204,130 @@ unsafe fn dec_ref(obj: ptr::NonNull<Self>) {
>      }
>  }
>
> +/// A newtype over file, specific to regular files
> +pub struct RegularFile(ARef<File>);
> +impl RegularFile {
> +    /// Creates a new instance of Self if the file is a regular file
> +    ///
> +    /// # Safety
> +    ///
> +    /// The caller must ensure file_ptr.f_inode is initialized to a valid pointer (e.g. file_ptr is
> +    /// a pointer returned by path_openat); It must also ensure that file_ptr's reference count was
> +    /// incremented at least once
> +    fn create_if_regular(file_ptr: ptr::NonNull<bindings::file>) -> Result<RegularFile> {

This function should be unsafe, correct? You instead take a
`&bindings::file` instead of `NonNull` but still keep it unsafe, so
the "valid pointer" invariant is always reached.

Or, could this take `&kernel::file::File` instead to reduce some duplication?

> +        // SAFETY: file_ptr is a NonNull pointer
> +        let inode = unsafe { core::ptr::addr_of!((*file_ptr.as_ptr()).f_inode).read() };
> +        // SAFETY: the caller must ensure f_inode is initialized to a valid pointer
> +        unsafe {
> +            if bindings::S_IFMT & ((*inode).i_mode) as u32 != bindings::S_IFREG {
> +                return Err(EINVAL);
> +            }
> +        }

Nit: factor `unsafe { ((*inode).i_mode) }` out so it doesn't look like
the whole statement is unsafe

> +        // SAFETY: the safety requirements state that file_ptr's reference count was incremented at
> +        // least once
> +        Ok(RegularFile(unsafe { ARef::from_raw(file_ptr.cast()) }))
> +    }
> +    /// Constructs a new [`struct file`] wrapper from a path.
> +    pub fn from_path(filename: &CStr, flags: i32, mode: u16) -> Result<Self> {
> +        let file_ptr = unsafe {
> +            // SAFETY: filename is a reference, so it's a valid pointer
> +            from_err_ptr(bindings::filp_open(
> +                filename.as_ptr() as *const i8,
> +                flags,
> +                mode,
> +            ))?
> +        };

I mentioned in another email that `.cast::<i8>()` can be more
idiomatic and a bit safer than `as`, it's a stylistic choice but there
are a few places it could be changed here if desired

Also, the `// SAFETY` comments need to go outside the unsafe block

> +        let file_ptr = ptr::NonNull::new(file_ptr).ok_or(ENOENT)?;
> +
> +        // SAFETY: `filp_open` initializes the refcount with 1
> +        Self::create_if_regular(file_ptr)
> +    }

Will need unsafe block if `create_if_regular` becomes unsafe

> +
> +    /// Constructs a new [`struct file`] wrapper from a path and a vfsmount.
> +    pub fn from_path_in_root_mnt(
> +        mount: &Vfsmount,
> +        filename: &CStr,
> +        flags: i32,
> +        mode: u16,
> +    ) -> Result<Self> {
> +        let file_ptr = {
> +            let mnt = mount.get();
> +            // construct a path from vfsmount, see file_open_root_mnt
> +            let raw_path = bindings::path {
> +                mnt,
> +                // SAFETY: Vfsmount structure stores a valid vfsmount object
> +                dentry: unsafe { (*mnt).mnt_root },
> +            };
> +            unsafe {
> +                // SAFETY: raw_path and filename are both references
> +                from_err_ptr(bindings::file_open_root(
> +                    &raw_path,
> +                    filename.as_ptr() as *const i8,
> +                    flags,
> +                    mode,
> +                ))?
> +            }
> +        };

Is there a reason to use the larger scope block, rather than moving
`mnt` and `raw_path` out and doing `let file_ptr = unsafe { ... }`? If
so, a comment would be good.

> +        let file_ptr = ptr::NonNull::new(file_ptr).ok_or(ENOENT)?;
> +
> +        // SAFETY: `file_open_root` initializes the refcount with 1
> +        Self::create_if_regular(file_ptr)
> +    }
> +
> +    /// Read from the file into the specified buffer
> +    pub fn read_with_offset(&self, buf: &mut [u8], offset: u64) -> Result<usize> {
> +        Ok({
> +            // kernel_read_file expects a pointer to a "void *" buffer
> +            let mut ptr_to_buf = buf.as_mut_ptr() as *mut core::ffi::c_void;
> +            // Unless we give a non-null pointer to the file size:
> +            // 1. we cannot give a non-zero value for the offset
> +            // 2. we cannot have offset 0 and buffer_size > file_size
> +            let mut file_size = 0;
> +
> +            // SAFETY: 'file' is valid because it's taken from Self, 'buf' and 'file_size` are
> +            // references to the stack variables 'ptr_to_buf' and 'file_size'; ptr_to_buf is also
> +            // a pointer to a valid buffer that was obtained from a reference
> +            let result = unsafe {
> +                bindings::kernel_read_file(
> +                    self.0 .0.get(),

Is this spacing intentional? If so, `(self.0).0.get()` may be cleaner

> +                    offset.try_into()?,
> +                    &mut ptr_to_buf,
> +                    buf.len(),
> +                    &mut file_size,
> +                    bindings::kernel_read_file_id_READING_UNKNOWN,
> +                )
> +            };
> +
> +            // kernel_read_file returns the number of bytes read on success or negative on error.
> +            if result < 0 {
> +                return Err(Error::from_errno(result.try_into()?));
> +            }
> +
> +            result.try_into()?
> +        })
> +    }

I think you could remove the block here and just return `Ok(result.try_into()?)`

> +
> +    /// Allocate and return a vector containing the contents of the entire file
> +    pub fn read_to_end(&self) -> Result<Vec<u8>> {
> +        let file_size = self.get_file_size()?;
> +        let mut buffer = Vec::try_with_capacity(file_size)?;
> +        buffer.try_resize(file_size, 0)?;
> +        self.read_with_offset(&mut buffer, 0)?;
> +        Ok(buffer)
> +    }
> +
> +    fn get_file_size(&self) -> Result<usize> {
> +        // SAFETY: 'file' is valid because it's taken from Self
> +        let file_size = unsafe { bindings::i_size_read((*self.0 .0.get()).f_inode) };
> +
> +        if file_size < 0 {
> +            return Err(EINVAL);
> +        }
> +
> +        Ok(file_size.try_into()?)
> +    }
> +}
> +
>  /// A file descriptor reservation.
>  ///
>  /// This allows the creation of a file descriptor in two steps: first, we reserve a slot for it,
> --
> 2.41.0
>
>

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [RFC PATCH v2 10/10] rust: puzzlefs: add oci_root_dir and image_manifest filesystem parameters
  2023-07-26 23:47     ` Wedson Almeida Filho
@ 2023-07-27  0:02       ` Trevor Gross
  2023-07-27  8:06         ` Wedson Almeida Filho
  0 siblings, 1 reply; 20+ messages in thread
From: Trevor Gross @ 2023-07-27  0:02 UTC (permalink / raw)
  To: Wedson Almeida Filho
  Cc: Ariel Miculas, rust-for-linux, linux-kernel, linux-fsdevel, tycho,
	brauner, viro, ojeda, alex.gaynor

On Wed, Jul 26, 2023 at 7:48 PM Wedson Almeida Filho <wedsonaf@gmail.com> wrote:
>
> On Wed, 26 Jul 2023 at 18:08, Trevor Gross <tmgross@umich.edu> wrote:
> >
> [...]
> > The guard syntax (available since 1.65) can make these kinds of match statements
> > cleaner:
>
> This is unstable though.
>
> We try to stay away from unstable features unless we really need them,
> which I feel is not the case here.
>

Let/else has been stable since 1.65 and is in pretty heavy use, the
kernel is on 1.68.2 correct? Could you be thinking of let chains
(multiple matches joined with `&&`/`||`)?

>     let Some(oci_root_dir) = data.oci_root_dir else {
>         pr_err!("missing oci_root_dir parameter!\n");
>         return Err(ENOTSUPP);
>     }
> [...]

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [RFC PATCH v2 10/10] rust: puzzlefs: add oci_root_dir and image_manifest filesystem parameters
  2023-07-27  0:02       ` Trevor Gross
@ 2023-07-27  8:06         ` Wedson Almeida Filho
  0 siblings, 0 replies; 20+ messages in thread
From: Wedson Almeida Filho @ 2023-07-27  8:06 UTC (permalink / raw)
  To: Trevor Gross
  Cc: Ariel Miculas, rust-for-linux, linux-kernel, linux-fsdevel, tycho,
	brauner, viro, ojeda, alex.gaynor

On Wed, 26 Jul 2023 at 21:02, Trevor Gross <tmgross@umich.edu> wrote:
>
> On Wed, Jul 26, 2023 at 7:48 PM Wedson Almeida Filho <wedsonaf@gmail.com> wrote:
> >
> > On Wed, 26 Jul 2023 at 18:08, Trevor Gross <tmgross@umich.edu> wrote:
> > >
> > [...]
> > > The guard syntax (available since 1.65) can make these kinds of match statements
> > > cleaner:
> >
> > This is unstable though.
> >
> > We try to stay away from unstable features unless we really need them,
> > which I feel is not the case here.
> >
>
> Let/else has been stable since 1.65 and is in pretty heavy use, the
> kernel is on 1.68.2 correct? Could you be thinking of let chains
> (multiple matches joined with `&&`/`||`)?

Oh, my bad. I got it mixed with "if let guard" (issue 51114).

And yeah, we're on 1.68.2, so this code can benefit from this simplification.

Thanks!

>
> >     let Some(oci_root_dir) = data.oci_root_dir else {
> >         pr_err!("missing oci_root_dir parameter!\n");
> >         return Err(ENOTSUPP);
> >     }
> > [...]

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [RFC PATCH v2 03/10] rust: kernel: add an abstraction over vfsmount to allow cloning a new private mount
  2023-07-26 22:34   ` Trevor Gross
@ 2023-07-27 13:10     ` Ariel Miculas
  0 siblings, 0 replies; 20+ messages in thread
From: Ariel Miculas @ 2023-07-27 13:10 UTC (permalink / raw)
  To: Trevor Gross
  Cc: Ariel Miculas, rust-for-linux, linux-kernel, linux-fsdevel, tycho,
	brauner, viro, ojeda, alex.gaynor, wedsonaf

Thanks, I've applied your suggestions on
https://github.com/ariel-miculas/linux/tree/puzzlefs_rfc

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [RFC PATCH v2 04/10] rust: file: Add a new RegularFile newtype useful for reading files
  2023-07-26 23:52   ` Trevor Gross
@ 2023-07-27 13:18     ` Ariel Miculas
  0 siblings, 0 replies; 20+ messages in thread
From: Ariel Miculas @ 2023-07-27 13:18 UTC (permalink / raw)
  To: Trevor Gross
  Cc: Ariel Miculas, rust-for-linux, linux-kernel, linux-fsdevel, tycho,
	brauner, viro, ojeda, alex.gaynor, wedsonaf

On Thu, Jul 27, 2023 at 2:52 AM Trevor Gross <tmgross@umich.edu> wrote:
>
> On Wed, Jul 26, 2023 at 12:54 PM Ariel Miculas <amiculas@cisco.com> wrote:
> >
> > Implement from_path, from_path_in_root_mnt, read_with_offset,
> > read_to_end and get_file_size methods for a RegularFile newtype.
> >
> > Signed-off-by: Ariel Miculas <amiculas@cisco.com>
> > ---
> >  rust/helpers.c       |   6 ++
> >  rust/kernel/error.rs |   4 +-
> >  rust/kernel/file.rs  | 129 ++++++++++++++++++++++++++++++++++++++++++-
> >  3 files changed, 135 insertions(+), 4 deletions(-)
> >
> > diff --git a/rust/helpers.c b/rust/helpers.c
> > index eed8ace52fb5..9e860a554cda 100644
> > --- a/rust/helpers.c
> > +++ b/rust/helpers.c
> > @@ -213,6 +213,12 @@ void *rust_helper_alloc_inode_sb(struct super_block *sb,
> >  }
> >  EXPORT_SYMBOL_GPL(rust_helper_alloc_inode_sb);
> >
> > +loff_t rust_helper_i_size_read(const struct inode *inode)
> > +{
> > +       return i_size_read(inode);
> > +}
> > +EXPORT_SYMBOL_GPL(rust_helper_i_size_read);
> > +
> >  /*
> >   * We use `bindgen`'s `--size_t-is-usize` option to bind the C `size_t` type
> >   * as the Rust `usize` type, so we can use it in contexts where Rust
> > diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs
> > index 05fcab6abfe6..e061c83f806a 100644
> > --- a/rust/kernel/error.rs
> > +++ b/rust/kernel/error.rs
> > @@ -273,9 +273,7 @@ pub fn to_result(err: core::ffi::c_int) -> Result {
> >  ///     }
> >  /// }
> >  /// ```
> > -// TODO: Remove `dead_code` marker once an in-kernel client is available.
> > -#[allow(dead_code)]
> > -pub(crate) fn from_err_ptr<T>(ptr: *mut T) -> Result<*mut T> {
> > +pub fn from_err_ptr<T>(ptr: *mut T) -> Result<*mut T> {
> >      // CAST: Casting a pointer to `*const core::ffi::c_void` is always valid.
> >      let const_ptr: *const core::ffi::c_void = ptr.cast();
> >      // SAFETY: The FFI function does not deref the pointer.
> > diff --git a/rust/kernel/file.rs b/rust/kernel/file.rs
> > index 494939ba74df..a3002c416dbb 100644
> > --- a/rust/kernel/file.rs
> > +++ b/rust/kernel/file.rs
> > @@ -8,11 +8,13 @@
> >  use crate::{
> >      bindings,
> >      cred::Credential,
> > -    error::{code::*, from_result, Error, Result},
> > +    error::{code::*, from_err_ptr, from_result, Error, Result},
> >      fs,
> >      io_buffer::{IoBufferReader, IoBufferWriter},
> >      iov_iter::IovIter,
> >      mm,
> > +    mount::Vfsmount,
> > +    str::CStr,
> >      sync::CondVar,
> >      types::ARef,
> >      types::AlwaysRefCounted,
> > @@ -20,6 +22,7 @@
> >      types::Opaque,
> >      user_ptr::{UserSlicePtr, UserSlicePtrReader, UserSlicePtrWriter},
> >  };
> > +use alloc::vec::Vec;
> >  use core::convert::{TryFrom, TryInto};
> >  use core::{marker, mem, ptr};
> >  use macros::vtable;
> > @@ -201,6 +204,130 @@ unsafe fn dec_ref(obj: ptr::NonNull<Self>) {
> >      }
> >  }
> >
> > +/// A newtype over file, specific to regular files
> > +pub struct RegularFile(ARef<File>);
> > +impl RegularFile {
> > +    /// Creates a new instance of Self if the file is a regular file
> > +    ///
> > +    /// # Safety
> > +    ///
> > +    /// The caller must ensure file_ptr.f_inode is initialized to a valid pointer (e.g. file_ptr is
> > +    /// a pointer returned by path_openat); It must also ensure that file_ptr's reference count was
> > +    /// incremented at least once
> > +    fn create_if_regular(file_ptr: ptr::NonNull<bindings::file>) -> Result<RegularFile> {
>
> This function should be unsafe, correct? You instead take a
> `&bindings::file` instead of `NonNull` but still keep it unsafe, so
> the "valid pointer" invariant is always reached.
>
> Or, could this take `&kernel::file::File` instead to reduce some duplication?
>
Yes, this should have been unsafe, I've also changed it to receive a
`*mut bindings::file` instead of NonNull.
> > +        // SAFETY: file_ptr is a NonNull pointer
> > +        let inode = unsafe { core::ptr::addr_of!((*file_ptr.as_ptr()).f_inode).read() };
> > +        // SAFETY: the caller must ensure f_inode is initialized to a valid pointer
> > +        unsafe {
> > +            if bindings::S_IFMT & ((*inode).i_mode) as u32 != bindings::S_IFREG {
> > +                return Err(EINVAL);
> > +            }
> > +        }
>
> Nit: factor `unsafe { ((*inode).i_mode) }` out so it doesn't look like
> the whole statement is unsafe
>
> > +        // SAFETY: the safety requirements state that file_ptr's reference count was incremented at
> > +        // least once
> > +        Ok(RegularFile(unsafe { ARef::from_raw(file_ptr.cast()) }))
> > +    }
> > +    /// Constructs a new [`struct file`] wrapper from a path.
> > +    pub fn from_path(filename: &CStr, flags: i32, mode: u16) -> Result<Self> {
> > +        let file_ptr = unsafe {
> > +            // SAFETY: filename is a reference, so it's a valid pointer
> > +            from_err_ptr(bindings::filp_open(
> > +                filename.as_ptr() as *const i8,
> > +                flags,
> > +                mode,
> > +            ))?
> > +        };
>
> I mentioned in another email that `.cast::<i8>()` can be more
> idiomatic and a bit safer than `as`, it's a stylistic choice but there
> are a few places it could be changed here if desired
Thanks, didn't know about this.
>
> Also, the `// SAFETY` comments need to go outside the unsafe block
>
> > +        let file_ptr = ptr::NonNull::new(file_ptr).ok_or(ENOENT)?;
> > +
> > +        // SAFETY: `filp_open` initializes the refcount with 1
> > +        Self::create_if_regular(file_ptr)
> > +    }
>
> Will need unsafe block if `create_if_regular` becomes unsafe
>
> > +
> > +    /// Constructs a new [`struct file`] wrapper from a path and a vfsmount.
> > +    pub fn from_path_in_root_mnt(
> > +        mount: &Vfsmount,
> > +        filename: &CStr,
> > +        flags: i32,
> > +        mode: u16,
> > +    ) -> Result<Self> {
> > +        let file_ptr = {
> > +            let mnt = mount.get();
> > +            // construct a path from vfsmount, see file_open_root_mnt
> > +            let raw_path = bindings::path {
> > +                mnt,
> > +                // SAFETY: Vfsmount structure stores a valid vfsmount object
> > +                dentry: unsafe { (*mnt).mnt_root },
> > +            };
> > +            unsafe {
> > +                // SAFETY: raw_path and filename are both references
> > +                from_err_ptr(bindings::file_open_root(
> > +                    &raw_path,
> > +                    filename.as_ptr() as *const i8,
> > +                    flags,
> > +                    mode,
> > +                ))?
> > +            }
> > +        };
>
> Is there a reason to use the larger scope block, rather than moving
> `mnt` and `raw_path` out and doing `let file_ptr = unsafe { ... }`? If
> so, a comment would be good.
No, that's just how the code has evolved from splitting unsafe blocks,
I've changed this in
https://github.com/ariel-miculas/linux/tree/puzzlefs_rfc
>
> > +        let file_ptr = ptr::NonNull::new(file_ptr).ok_or(ENOENT)?;
> > +
> > +        // SAFETY: `file_open_root` initializes the refcount with 1
> > +        Self::create_if_regular(file_ptr)
> > +    }
> > +
> > +    /// Read from the file into the specified buffer
> > +    pub fn read_with_offset(&self, buf: &mut [u8], offset: u64) -> Result<usize> {
> > +        Ok({
> > +            // kernel_read_file expects a pointer to a "void *" buffer
> > +            let mut ptr_to_buf = buf.as_mut_ptr() as *mut core::ffi::c_void;
> > +            // Unless we give a non-null pointer to the file size:
> > +            // 1. we cannot give a non-zero value for the offset
> > +            // 2. we cannot have offset 0 and buffer_size > file_size
> > +            let mut file_size = 0;
> > +
> > +            // SAFETY: 'file' is valid because it's taken from Self, 'buf' and 'file_size` are
> > +            // references to the stack variables 'ptr_to_buf' and 'file_size'; ptr_to_buf is also
> > +            // a pointer to a valid buffer that was obtained from a reference
> > +            let result = unsafe {
> > +                bindings::kernel_read_file(
> > +                    self.0 .0.get(),
>
> Is this spacing intentional? If so, `(self.0).0.get()` may be cleaner
No, it's not intentional, this is rustfmt`s creation.
>
> > +                    offset.try_into()?,
> > +                    &mut ptr_to_buf,
> > +                    buf.len(),
> > +                    &mut file_size,
> > +                    bindings::kernel_read_file_id_READING_UNKNOWN,
> > +                )
> > +            };
> > +
> > +            // kernel_read_file returns the number of bytes read on success or negative on error.
> > +            if result < 0 {
> > +                return Err(Error::from_errno(result.try_into()?));
> > +            }
> > +
> > +            result.try_into()?
> > +        })
> > +    }
>
> I think you could remove the block here and just return `Ok(result.try_into()?)`
Good idea.
>
> > +
> > +    /// Allocate and return a vector containing the contents of the entire file
> > +    pub fn read_to_end(&self) -> Result<Vec<u8>> {
> > +        let file_size = self.get_file_size()?;
> > +        let mut buffer = Vec::try_with_capacity(file_size)?;
> > +        buffer.try_resize(file_size, 0)?;
> > +        self.read_with_offset(&mut buffer, 0)?;
> > +        Ok(buffer)
> > +    }
> > +
> > +    fn get_file_size(&self) -> Result<usize> {
> > +        // SAFETY: 'file' is valid because it's taken from Self
> > +        let file_size = unsafe { bindings::i_size_read((*self.0 .0.get()).f_inode) };
> > +
> > +        if file_size < 0 {
> > +            return Err(EINVAL);
> > +        }
> > +
> > +        Ok(file_size.try_into()?)
> > +    }
> > +}
> > +
> >  /// A file descriptor reservation.
> >  ///
> >  /// This allows the creation of a file descriptor in two steps: first, we reserve a slot for it,
> > --
> > 2.41.0
> >
> >
>

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [RFC PATCH v2 06/10] rust: file: pass the filesystem context to the open function
  2023-07-26 16:45 ` [RFC PATCH v2 06/10] rust: file: pass the filesystem context to the open function Ariel Miculas
@ 2023-07-27 13:32   ` Ariel Miculas
  0 siblings, 0 replies; 20+ messages in thread
From: Ariel Miculas @ 2023-07-27 13:32 UTC (permalink / raw)
  To: Ariel Miculas
  Cc: rust-for-linux, linux-kernel, linux-fsdevel, tycho, brauner, viro,
	ojeda, alex.gaynor, wedsonaf

On Wed, Jul 26, 2023 at 7:58 PM Ariel Miculas <amiculas@cisco.com> wrote:
>
> This allows us to create a Vfsmount structure and pass it to the read
> callback.
>
> Signed-off-by: Ariel Miculas <amiculas@cisco.com>
> ---
>  rust/kernel/file.rs      | 17 +++++++++++++++--
>  samples/rust/puzzlefs.rs | 40 +++++++++++++++++++++++++++++++++++-----
>  samples/rust/rust_fs.rs  |  3 ++-
>  3 files changed, 52 insertions(+), 8 deletions(-)
>
> diff --git a/rust/kernel/file.rs b/rust/kernel/file.rs
> index a3002c416dbb..af1eb1ee9267 100644
> --- a/rust/kernel/file.rs
> +++ b/rust/kernel/file.rs
> @@ -457,9 +457,15 @@ impl<A: OpenAdapter<T::OpenData>, T: Operations> OperationsVtable<A, T> {
>              // `fileref` never outlives this function, so it is guaranteed to be
>              // valid.
>              let fileref = unsafe { File::from_ptr(file) };
> +
> +            // SAFETY: into_foreign was called in fs::NewSuperBlock<..., NeedsInit>::init and
> +            // it is valid until from_foreign will be called in fs::Tables::free_callback
> +            let fs_info =
> +                unsafe { <T::Filesystem as fs::Type>::Data::borrow((*(*inode).i_sb).s_fs_info) };
> +
>              // SAFETY: `arg` was previously returned by `A::convert` and must
>              // be a valid non-null pointer.
> -            let ptr = T::open(unsafe { &*arg }, fileref)?.into_foreign();
> +            let ptr = T::open(fs_info, unsafe { &*arg }, fileref)?.into_foreign();
>              // SAFETY: The C contract guarantees that `private_data` is available
>              // for implementers of the file operations (no other C code accesses
>              // it), so we know that there are no concurrent threads/CPUs accessing
> @@ -930,10 +936,17 @@ pub trait Operations {
>      /// The type of the context data passed to [`Operations::open`].
>      type OpenData: Sync = ();
>
> +    /// Data associated with each file system instance.
> +    type Filesystem: fs::Type;
> +
>      /// Creates a new instance of this file.
>      ///
>      /// Corresponds to the `open` function pointer in `struct file_operations`.
> -    fn open(context: &Self::OpenData, file: &File) -> Result<Self::Data>;
> +    fn open(
> +        fs_info: <<Self::Filesystem as fs::Type>::Data as ForeignOwnable>::Borrowed<'_>,
> +        context: &Self::OpenData,
> +        file: &File,
> +    ) -> Result<Self::Data>;
>
>      /// Cleans up after the last reference to the file goes away.
>      ///
> diff --git a/samples/rust/puzzlefs.rs b/samples/rust/puzzlefs.rs
> index 9afd82745b64..8a64e0bd437d 100644
> --- a/samples/rust/puzzlefs.rs
> +++ b/samples/rust/puzzlefs.rs
> @@ -3,8 +3,14 @@
>  //! Rust file system sample.
>
>  use kernel::module_fs;
> +use kernel::mount::Vfsmount;
>  use kernel::prelude::*;
> -use kernel::{c_str, file, fs, io_buffer::IoBufferWriter};
> +use kernel::{
> +    c_str, file, fmt, fs,
> +    io_buffer::IoBufferWriter,
> +    str::CString,
> +    sync::{Arc, ArcBorrow},
> +};
>
>  mod puzzle;
>  // Required by the autogenerated '_capnp.rs' files
> @@ -19,6 +25,12 @@
>
>  struct PuzzleFsModule;
>
> +#[derive(Debug)]
> +struct PuzzlefsInfo {
> +    base_path: CString,
> +    vfs_mount: Arc<Vfsmount>,
> +}
> +
>  #[vtable]
>  impl fs::Context<Self> for PuzzleFsModule {
>      type Data = ();
> @@ -46,14 +58,23 @@ fn try_new() -> Result {
>  impl fs::Type for PuzzleFsModule {
>      type Context = Self;
>      type INodeData = &'static [u8];
> +    type Data = Box<PuzzlefsInfo>;
>      const SUPER_TYPE: fs::Super = fs::Super::Independent;
>      const NAME: &'static CStr = c_str!("puzzlefs");
>      const FLAGS: i32 = fs::flags::USERNS_MOUNT;
>      const DCACHE_BASED: bool = true;
>
>      fn fill_super(_data: (), sb: fs::NewSuperBlock<'_, Self>) -> Result<&fs::SuperBlock<Self>> {
> +        let base_path = CString::try_from_fmt(fmt!("hello world"))?;
> +        pr_info!("base_path {:?}\n", base_path);
> +        let vfs_mount = Vfsmount::new_private_mount(c_str!("/home/puzzlefs_oci"))?;
> +        pr_info!("vfs_mount {:?}\n", vfs_mount);
> +
>          let sb = sb.init(
> -            (),
> +            Box::try_new(PuzzlefsInfo {
> +                base_path,
> +                vfs_mount: Arc::try_new(vfs_mount)?,
> +            })?,
>              &fs::SuperParams {
>                  magic: 0x72757374,
>                  ..fs::SuperParams::DEFAULT
> @@ -88,14 +109,23 @@ fn fill_super(_data: (), sb: fs::NewSuperBlock<'_, Self>) -> Result<&fs::SuperBl
>
>  #[vtable]
>  impl file::Operations for FsFile {
> +    // must be the same as INodeData
>      type OpenData = &'static [u8];
> +    type Filesystem = PuzzleFsModule;
> +    // this is an Arc because Data must be ForeignOwnable and the only implementors of it are Box,
> +    // Arc and (); we cannot pass a reference to read, so we share Vfsmount using and Arc
> +    type Data = Arc<Vfsmount>;
>
> -    fn open(_context: &Self::OpenData, _file: &file::File) -> Result<Self::Data> {
> -        Ok(())
> +    fn open(
> +        fs_info: &PuzzlefsInfo,
> +        _context: &Self::OpenData,
> +        _file: &file::File,
> +    ) -> Result<Self::Data> {
> +        Ok(fs_info.vfs_mount.clone())
>      }
>
>      fn read(
> -        _data: (),
> +        data: ArcBorrow<'_, Vfsmount>,
>          file: &file::File,
>          writer: &mut impl IoBufferWriter,
>          offset: u64,
> diff --git a/samples/rust/rust_fs.rs b/samples/rust/rust_fs.rs
> index 7527681ee024..c58ed1560e06 100644
> --- a/samples/rust/rust_fs.rs
> +++ b/samples/rust/rust_fs.rs
> @@ -85,8 +85,9 @@ fn fill_super(_data: (), sb: fs::NewSuperBlock<'_, Self>) -> Result<&fs::SuperBl
>  #[vtable]
>  impl file::Operations for FsFile {
>      type OpenData = &'static [u8];
> +    type Filesystem = RustFs;
>
> -    fn open(_context: &Self::OpenData, _file: &file::File) -> Result<Self::Data> {
> +    fn open(_fs_info: (), _context: &Self::OpenData, _file: &file::File) -> Result<Self::Data> {
>          Ok(())
>      }
>
> --
> 2.41.0
>
>
Hey Wedson,

Is it ok to couple file::Operations with fs::Type? I didn't find a
better way to implement this.
I'm asking because I've seen you've gone to great lengths to decouple them.

Cheers,
Ariel

^ permalink raw reply	[flat|nested] 20+ messages in thread

end of thread, other threads:[~2023-07-27 13:33 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-07-26 16:45 [RFC PATCH v2 00/10] Rust PuzleFS filesystem driver Ariel Miculas
2023-07-26 16:45 ` [RFC PATCH v2 01/10] samples: puzzlefs: add initial puzzlefs sample, copied from rust_fs.rs Ariel Miculas
2023-07-26 16:45 ` [RFC PATCH v2 02/10] kernel: configs: enable rust samples in rust.config Ariel Miculas
2023-07-26 16:45 ` [RFC PATCH v2 03/10] rust: kernel: add an abstraction over vfsmount to allow cloning a new private mount Ariel Miculas
2023-07-26 22:34   ` Trevor Gross
2023-07-27 13:10     ` Ariel Miculas
2023-07-26 16:45 ` [RFC PATCH v2 04/10] rust: file: Add a new RegularFile newtype useful for reading files Ariel Miculas
2023-07-26 23:52   ` Trevor Gross
2023-07-27 13:18     ` Ariel Miculas
2023-07-26 16:45 ` [RFC PATCH v2 05/10] samples: puzzlefs: add basic deserializing support for the puzzlefs metadata Ariel Miculas
2023-07-26 16:45 ` [RFC PATCH v2 06/10] rust: file: pass the filesystem context to the open function Ariel Miculas
2023-07-27 13:32   ` Ariel Miculas
2023-07-26 16:45 ` [RFC PATCH v2 07/10] samples: puzzlefs: populate the directory entries with the inodes from the puzzlefs metadata file Ariel Miculas
2023-07-26 16:45 ` [RFC PATCH v2 08/10] rust: puzzlefs: read the puzzlefs image manifest instead of an individual metadata layer Ariel Miculas
2023-07-26 16:45 ` [RFC PATCH v2 09/10] rust: puzzlefs: add support for reading files Ariel Miculas
2023-07-26 16:45 ` [RFC PATCH v2 10/10] rust: puzzlefs: add oci_root_dir and image_manifest filesystem parameters Ariel Miculas
2023-07-26 21:08   ` Trevor Gross
2023-07-26 23:47     ` Wedson Almeida Filho
2023-07-27  0:02       ` Trevor Gross
2023-07-27  8:06         ` Wedson Almeida Filho

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).