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