From patchwork Wed Jul 26 16:45:33 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ariel Miculas X-Patchwork-Id: 13328377 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 247BCC001DF for ; Wed, 26 Jul 2023 16:48:17 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231721AbjGZQsP (ORCPT ); Wed, 26 Jul 2023 12:48:15 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34730 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231862AbjGZQry (ORCPT ); Wed, 26 Jul 2023 12:47:54 -0400 Received: from aer-iport-3.cisco.com (aer-iport-3.cisco.com [173.38.203.53]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id A100926B9 for ; Wed, 26 Jul 2023 09:47:31 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=cisco.com; i=@cisco.com; l=9636; q=dns/txt; s=iport; t=1690390051; x=1691599651; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=PFmcSAyq0q+twl25ouvozy9tQ/05/gbjPTooN2X1b1Q=; b=f7NgAZ7SBIH/RmXrzQ8+vH2vQp2rGh8F+jy0TefklB9OVutpD4e+yZ50 GKmyO3Qx9+I+EdDqCYdTxeW6+zbeInmK6U/DdZJF5F4Q40sL4U2fSL1qb s8bXINmNblkX9YQXTlOynDxJcG7JbKx2hiGGKdv8AQK46BqRMXo1e2B4k w=; X-CSE-ConnectionGUID: N5IyJgQqSHigT8C597hZ2g== X-CSE-MsgGUID: V0qXxUGWTayV3mKfou9ceA== X-IronPort-AV: E=Sophos;i="6.01,232,1684800000"; d="scan'208";a="8394938" Received: from aer-iport-nat.cisco.com (HELO aer-core-7.cisco.com) ([173.38.203.22]) by aer-iport-3.cisco.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 26 Jul 2023 16:46:26 +0000 Received: from archlinux-cisco.cisco.com (dhcp-10-61-98-211.cisco.com [10.61.98.211]) (authenticated bits=0) by aer-core-7.cisco.com (8.15.2/8.15.2) with ESMTPSA id 36QGjqU1022602 (version=TLSv1.2 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=NO); Wed, 26 Jul 2023 16:46:25 GMT From: Ariel Miculas To: rust-for-linux@vger.kernel.org Cc: linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org, tycho@tycho.pizza, brauner@kernel.org, viro@zeniv.linux.org.uk, ojeda@kernel.org, alex.gaynor@gmail.com, wedsonaf@gmail.com, Ariel Miculas Subject: [RFC PATCH v2 09/10] rust: puzzlefs: add support for reading files Date: Wed, 26 Jul 2023 19:45:33 +0300 Message-ID: <20230726164535.230515-10-amiculas@cisco.com> X-Mailer: git-send-email 2.41.0 In-Reply-To: <20230726164535.230515-1-amiculas@cisco.com> References: <20230726164535.230515-1-amiculas@cisco.com> MIME-Version: 1.0 X-Authenticated-User: amiculas X-Outbound-SMTP-Client: 10.61.98.211, dhcp-10-61-98-211.cisco.com X-Outbound-Node: aer-core-7.cisco.com Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org 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 --- 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, rootfs_path: &CStr) -> Result { - let oci = Image::open(vfsmount)?; + pub(crate) fn open(oci_root_dir: &CStr, rootfs_path: &CStr) -> Result { + 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 { Err(WireFormatError::from_errno(ENOENT)) } } + +pub(crate) fn file_read( + oci: &Image, + inode: &Inode, + offset: usize, + data: &mut [u8], +) -> Result { + 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, + pub(crate) vfs_mount: Vfsmount, } impl Image { - pub(crate) fn open(vfsmount: Arc) -> Result { + pub(crate) fn open(vfsmount: Vfsmount) -> Result { 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 { 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 { 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 { + let 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, + puzzlefs: Arc, } #[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> { - 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; 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; + // Arc and (); we cannot pass a reference to the read callback, so we share PuzzleFS using Arc + type Data = Arc; fn open( fs_info: &PuzzlefsInfo, _context: &Self::OpenData, _file: &file::File, ) -> Result { - 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 { + let inode = file.inode::().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) } }