From nobody Tue Sep 9 23:10:15 2025 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 BA362C001B0 for ; Wed, 26 Jul 2023 16:50:07 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231685AbjGZQuG (ORCPT ); Wed, 26 Jul 2023 12:50:06 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:35898 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231815AbjGZQt5 (ORCPT ); Wed, 26 Jul 2023 12:49:57 -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 79DDFC0 for ; Wed, 26 Jul 2023 09:49:55 -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=1690390195; x=1691599795; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=PFmcSAyq0q+twl25ouvozy9tQ/05/gbjPTooN2X1b1Q=; b=Fj4MCxivyAmCbDxkVtQ8WOCJje1We5BuijVJSFfIdcAX+Vg1x5H1WLZ9 GtUxTgIyNyr/7f32qKyh0Ehb6Zu+V1YdhaFtZIa5U2SnOSE/Wj9q+9sqh ZrZiJYbw9Muv5W4D5twXTi+sFgF1RmGw8APz/s/xQpmqUDzZS00biorrG Y=; 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 Content-Transfer-Encoding: quoted-printable 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-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" 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; =20 pub(crate) struct PuzzleFS { pub(crate) oci: Image, @@ -18,8 +18,9 @@ pub(crate) struct PuzzleFS { } =20 impl PuzzleFS { - pub(crate) fn open(vfsmount: Arc, rootfs_path: &CStr) -> Res= ult { - let oci =3D Image::open(vfsmount)?; + pub(crate) fn open(oci_root_dir: &CStr, rootfs_path: &CStr) -> Result<= PuzzleFS> { + let vfs_mount =3D Vfsmount::new_private_mount(oci_root_dir)?; + let oci =3D Image::open(vfs_mount)?; let rootfs =3D oci.open_rootfs_blob(rootfs_path)?; =20 let mut layers =3D 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 =3D match &inode.mode { + InodeMode::File { chunks } =3D> chunks, + _ =3D> return Err(WireFormatError::from_errno(ENOTDIR)), + }; + + // TODO: fix all this casting... + let end =3D offset + data.len(); + + let mut file_offset =3D 0; + let mut buf_offset =3D 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 +=3D chunk.len as usize; + continue; + } + + let addl_offset =3D if offset > file_offset { + offset - file_offset + } else { + 0 + }; + + // ok, need to read this chunk; how much? + let left_in_buf =3D data.len() - buf_offset; + let to_read =3D min(left_in_buf, chunk.len as usize - addl_offset); + + let start =3D buf_offset; + let finish =3D start + to_read; + file_offset +=3D addl_offset; + + // how many did we actually read? + let n =3D oci.fill_from_chunk(chunk.blob, addl_offset as u64, &mut= data[start..finish])?; + file_offset +=3D n; + buf_offset +=3D 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; =20 +#[derive(Debug)] pub(crate) struct Image { - vfs_mount: Arc, + pub(crate) vfs_mount: Vfsmount, } =20 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 =3D CString::try_from_fmt(format_args!("{}/{digest}", self.blob_pa= th_relative()))?; - pr_info!("trying to open {:?}\n", &filename); + pr_debug!("trying to open {:?}\n", &*filename); =20 let file =3D RegularFile::from_path_in_root_mnt( &self.vfs_mount, @@ -48,4 +50,22 @@ pub(crate) fn open_rootfs_blob(&self, path: &CStr) -> Re= sult { let rootfs =3D 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 =3D &::try_from(chunk)?; + + let blob =3D if chunk.compressed { + return Err(WireFormatError::KernelError(ENOTSUPP)); + } else { + self.open_raw_blob(digest)? + }; + + let n =3D 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. =20 use kernel::module_fs; -use kernel::mount::Vfsmount; use kernel::prelude::*; use kernel::{ c_str, file, fs, @@ -13,7 +12,7 @@ =20 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}; =20 @@ -28,9 +27,8 @@ =20 struct PuzzleFsModule; =20 -#[derive(Debug)] struct PuzzlefsInfo { - vfs_mount: Arc, + puzzlefs: Arc, } =20 #[vtable] @@ -139,14 +137,20 @@ impl fs::Type for PuzzleFsModule { const DCACHE_BASED: bool =3D true; =20 fn fill_super(_data: (), sb: fs::NewSuperBlock<'_, Self>) -> Result<&f= s::SuperBlock> { - let vfs_mount =3D Vfsmount::new_private_mount(c_str!("/home/puzzle= fs_oci"))?; - pr_info!("vfs_mount {:?}\n", vfs_mount); + let puzzlefs =3D PuzzleFS::open( + c_str!("/home/puzzlefs_oci"), + c_str!("2d6602d678140540dc7e96de652a76a8b16e8aca190bae141297bc= ffdcae901b"), + ); =20 - let arc_vfs_mount =3D Arc::try_new(vfs_mount)?; + if let Err(ref e) =3D puzzlefs { + pr_info!("error opening puzzlefs {e}\n"); + } + + let puzzlefs =3D Arc::try_new(puzzlefs?)?; =20 let sb =3D 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<'_, Sel= f>) -> Result<&fs::SuperBl }, )?; =20 - let puzzlefs =3D PuzzleFS::open( - arc_vfs_mount, - c_str!("2d6602d678140540dc7e96de652a76a8b16e8aca190bae141297bc= ffdcae901b"), - ); - - if let Err(ref e) =3D puzzlefs { - pr_info!("error opening puzzlefs {e}\n"); - } - - let mut puzzlefs =3D puzzlefs?; let root_inode =3D Arc::try_new(puzzlefs.find_inode(1)?)?; =20 - let root =3D try_new_populated_root_puzzlefs_dentry(&sb, &mut puzz= lefs, root_inode)?; + let root =3D try_new_populated_root_puzzlefs_dentry(&sb, &puzzlefs= , root_inode)?; let sb =3D sb.init_root(root)?; Ok(sb) } @@ -180,32 +174,28 @@ impl file::Operations for FsFile { type OpenData =3D Arc; type Filesystem =3D PuzzleFsModule; // this is an Arc because Data must be ForeignOwnable and the only imp= lementors of it are Box, - // Arc and (); we cannot pass a reference to read, so we share Vfsmoun= t using and Arc - type Data =3D Arc; + // Arc and (); we cannot pass a reference to the read callback, so we = share PuzzleFS using Arc + type Data =3D Arc; =20 fn open( fs_info: &PuzzlefsInfo, _context: &Self::OpenData, _file: &file::File, ) -> Result { - Ok(fs_info.vfs_mount.clone()) + Ok(fs_info.puzzlefs.clone()) } =20 fn read( - data: ArcBorrow<'_, Vfsmount>, - _file: &file::File, + data: ArcBorrow<'_, PuzzleFS>, + file: &file::File, writer: &mut impl IoBufferWriter, offset: u64, ) -> Result { + let inode =3D file.inode::().ok_or(EINVAL)?.fs_dat= a(); let mut buf =3D Vec::try_with_capacity(writer.len())?; buf.try_resize(writer.len(), 0)?; - let file =3D file::RegularFile::from_path_in_root_mnt( - &data, - c_str!("data"), - file::flags::O_RDONLY.try_into().unwrap(), - 0, - )?; - let nr_bytes_read =3D file.read_with_offset(&mut buf[..], offset)?; - file::read_from_slice(&buf[..nr_bytes_read], writer, 0) + let read =3D file_read(&data.oci, inode, offset as usize, &mut buf= )?; + buf.truncate(read); + file::read_from_slice(&buf, writer, 0) } } --=20 2.41.0