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)?;
