[RFC PATCH v2 08/10] rust: puzzlefs: read the puzzlefs image manifest instead of an individual metadata layer

From: Ariel Miculas
Date: Wed Jul 26 2023 - 12:58:13 EST


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@xxxxxxxxx>
---
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