[RFC PATCH v2 09/10] rust: puzzlefs: add support for reading files

From: Ariel Miculas
Date: Wed Jul 26 2023 - 12:50:07 EST


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