Skip to content

Commit 30864e8

Browse files
committed
samples: puzzlefs: add basic deserializing support for the puzzlefs metadata
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 -o/home/amiculas/work/capnproto-rust/target/release/capnpc-rust manifest.capnp capnp compile -o/home/amiculas/work/capnproto-rust/target/release/capnpc-rust metadata.capnp ``` , formatted with rustfmt and I have also added the following lines: ``` #![allow(unreachable_pub)] ``` to avoid warnings. In `/home/amiculas/work/capnproto-rust` I have the latest version of master for the capnproto-rust repo. 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 merged in capnproto-rust upstream [3], but a new release hasn't yet been created. When the 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/[email protected]/ [2] Link: capnproto/capnproto-rust#423 [3] Signed-off-by: Ariel Miculas <[email protected]>
1 parent 023b4c6 commit 30864e8

File tree

11 files changed

+5439
-1
lines changed

11 files changed

+5439
-1
lines changed

rust/kernel/error.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ impl Error {
9999
///
100100
/// It is a bug to pass an out-of-range `errno`. `EINVAL` would
101101
/// be returned in such a case.
102-
pub(crate) fn from_errno(errno: core::ffi::c_int) -> Error {
102+
pub fn from_errno(errno: core::ffi::c_int) -> Error {
103103
if errno < -(bindings::MAX_ERRNO as i32) || errno >= 0 {
104104
// TODO: Make it a `WARN_ONCE` once available.
105105
crate::pr_warn!(

samples/rust/puzzle.rs

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pub(crate) mod error;
2+
pub(crate) mod types;
3+
pub(crate) use types::{manifest_capnp, metadata_capnp};
4+
pub(crate) mod inode;
5+
pub(crate) mod oci;

samples/rust/puzzle/error.rs

+106
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
use alloc::collections::TryReserveError;
2+
use core::ffi::c_int;
3+
use core::fmt::{self, Display};
4+
use kernel::prelude::EINVAL;
5+
6+
pub(crate) enum WireFormatError {
7+
InvalidSerializedData,
8+
KernelError(kernel::error::Error),
9+
TryReserveError(TryReserveError),
10+
CapnpError(capnp::Error),
11+
FromIntError(core::num::TryFromIntError),
12+
FromSliceError(core::array::TryFromSliceError),
13+
HexError(hex::FromHexError),
14+
}
15+
16+
impl WireFormatError {
17+
pub(crate) fn to_errno(&self) -> c_int {
18+
match self {
19+
WireFormatError::InvalidSerializedData => kernel::error::Error::to_errno(EINVAL),
20+
WireFormatError::KernelError(e) => kernel::error::Error::to_errno(*e),
21+
WireFormatError::TryReserveError(_) => kernel::error::Error::to_errno(EINVAL),
22+
WireFormatError::CapnpError(_) => kernel::error::Error::to_errno(EINVAL),
23+
WireFormatError::FromIntError(_) => kernel::error::Error::to_errno(EINVAL),
24+
WireFormatError::FromSliceError(_) => kernel::error::Error::to_errno(EINVAL),
25+
WireFormatError::HexError(_) => kernel::error::Error::to_errno(EINVAL),
26+
}
27+
}
28+
29+
pub(crate) fn from_errno(errno: kernel::error::Error) -> Self {
30+
Self::KernelError(errno)
31+
}
32+
}
33+
34+
impl Display for WireFormatError {
35+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
36+
match self {
37+
WireFormatError::InvalidSerializedData => f.write_str("invalid serialized data"),
38+
WireFormatError::KernelError(e) => {
39+
f.write_fmt(format_args!("Kernel error {}", e.to_errno()))
40+
}
41+
WireFormatError::TryReserveError(_) => f.write_str("TryReserveError"),
42+
WireFormatError::CapnpError(_) => f.write_str("Capnp error"),
43+
WireFormatError::FromIntError(_) => f.write_str("TryFromIntError"),
44+
WireFormatError::FromSliceError(_) => f.write_str("TryFromSliceError"),
45+
WireFormatError::HexError(_) => f.write_str("HexError"),
46+
}
47+
}
48+
}
49+
50+
pub(crate) type Result<T> = kernel::error::Result<T, WireFormatError>;
51+
52+
// TODO figure out how to use thiserror
53+
#[allow(unused_qualifications)]
54+
impl core::convert::From<kernel::error::Error> for WireFormatError {
55+
#[allow(deprecated)]
56+
fn from(source: kernel::error::Error) -> Self {
57+
WireFormatError::KernelError(source)
58+
}
59+
}
60+
61+
#[allow(unused_qualifications)]
62+
impl core::convert::From<TryReserveError> for WireFormatError {
63+
#[allow(deprecated)]
64+
fn from(source: TryReserveError) -> Self {
65+
WireFormatError::TryReserveError(source)
66+
}
67+
}
68+
69+
#[allow(unused_qualifications)]
70+
impl core::convert::From<capnp::Error> for WireFormatError {
71+
#[allow(deprecated)]
72+
fn from(source: capnp::Error) -> Self {
73+
WireFormatError::CapnpError(source)
74+
}
75+
}
76+
77+
#[allow(unused_qualifications)]
78+
impl core::convert::From<core::array::TryFromSliceError> for WireFormatError {
79+
#[allow(deprecated)]
80+
fn from(source: core::array::TryFromSliceError) -> Self {
81+
WireFormatError::FromSliceError(source)
82+
}
83+
}
84+
85+
#[allow(unused_qualifications)]
86+
impl core::convert::From<core::num::TryFromIntError> for WireFormatError {
87+
#[allow(deprecated)]
88+
fn from(source: core::num::TryFromIntError) -> Self {
89+
WireFormatError::FromIntError(source)
90+
}
91+
}
92+
93+
impl core::convert::From<hex::FromHexError> for WireFormatError {
94+
#[allow(deprecated)]
95+
fn from(source: hex::FromHexError) -> Self {
96+
WireFormatError::HexError(source)
97+
}
98+
}
99+
100+
#[allow(unused_qualifications)]
101+
impl core::convert::From<WireFormatError> for kernel::error::Error {
102+
#[allow(deprecated)]
103+
fn from(source: WireFormatError) -> Self {
104+
kernel::error::Error::from_errno(source.to_errno())
105+
}
106+
}

samples/rust/puzzle/inode.rs

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// This contents of this file is taken from puzzlefs.rs (the userspace implementation)
2+
// It is named inode.rs instead puzzlefs.rs since the root of this kernel module already has that name
3+
4+
use crate::puzzle::error::Result;
5+
use crate::puzzle::error::WireFormatError;
6+
use crate::puzzle::oci::Image;
7+
use crate::puzzle::types as format;
8+
use crate::puzzle::types::{Digest, Inode, InodeMode};
9+
use alloc::vec::Vec;
10+
use kernel::mount::Vfsmount;
11+
use kernel::prelude::ENOENT;
12+
use kernel::str::CStr;
13+
14+
pub(crate) struct PuzzleFS {
15+
pub(crate) oci: Image,
16+
layers: Vec<format::MetadataBlob>,
17+
}
18+
19+
impl PuzzleFS {
20+
pub(crate) fn open(oci_root_dir: &CStr, rootfs_path: &CStr) -> Result<PuzzleFS> {
21+
let vfs_mount = Vfsmount::new_private_mount(oci_root_dir)?;
22+
let oci = Image::open(vfs_mount)?;
23+
let rootfs = oci.open_rootfs_blob(rootfs_path)?;
24+
25+
let mut layers = Vec::new();
26+
for md in rootfs.metadatas.iter() {
27+
let digest = Digest::try_from(md)?;
28+
layers.try_push(oci.open_metadata_blob(&digest)?)?;
29+
}
30+
31+
Ok(PuzzleFS { oci, layers })
32+
}
33+
34+
pub(crate) fn find_inode(&self, ino: u64) -> Result<Inode> {
35+
for layer in self.layers.iter() {
36+
if let Some(inode) = layer.find_inode(ino)? {
37+
let inode = Inode::from_capnp(inode)?;
38+
if let InodeMode::Wht = inode.mode {
39+
// TODO: seems like this should really be an Option.
40+
return Err(WireFormatError::from_errno(ENOENT));
41+
}
42+
return Ok(inode);
43+
}
44+
}
45+
46+
Err(WireFormatError::from_errno(ENOENT))
47+
}
48+
}

samples/rust/puzzle/oci.rs

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
use crate::puzzle::error::Result;
2+
use crate::puzzle::types::{Digest, MetadataBlob, Rootfs};
3+
use kernel::c_str;
4+
use kernel::file;
5+
use kernel::file::RegularFile;
6+
use kernel::mount::Vfsmount;
7+
use kernel::pr_debug;
8+
use kernel::str::{CStr, CString};
9+
10+
#[derive(Debug)]
11+
pub(crate) struct Image {
12+
pub(crate) vfs_mount: Vfsmount,
13+
}
14+
15+
impl Image {
16+
pub(crate) fn open(vfsmount: Vfsmount) -> Result<Self> {
17+
Ok(Image {
18+
vfs_mount: vfsmount,
19+
})
20+
}
21+
22+
pub(crate) fn blob_path_relative(&self) -> &CStr {
23+
c_str!("blobs/sha256")
24+
}
25+
26+
fn open_raw_blob(&self, digest: &Digest) -> Result<RegularFile> {
27+
let filename =
28+
CString::try_from_fmt(format_args!("{}/{digest}", self.blob_path_relative()))?;
29+
pr_debug!("trying to open {:?}\n", &*filename);
30+
31+
let file = RegularFile::from_path_in_root_mnt(
32+
&self.vfs_mount,
33+
&filename,
34+
file::flags::O_RDONLY.try_into().unwrap(),
35+
0,
36+
)?;
37+
38+
Ok(file)
39+
}
40+
41+
pub(crate) fn open_metadata_blob(&self, digest: &Digest) -> Result<MetadataBlob> {
42+
let f = self.open_raw_blob(digest)?;
43+
MetadataBlob::new(f)
44+
}
45+
46+
pub(crate) fn open_rootfs_blob(&self, path: &CStr) -> Result<Rootfs> {
47+
let digest = Digest::try_from(path)?;
48+
let rootfs = Rootfs::open(self.open_raw_blob(&digest)?)?;
49+
Ok(rootfs)
50+
}
51+
}

0 commit comments

Comments
 (0)