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: 126513 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:a985:0:b0:3e4:2afc:c1 with SMTP id t5csp565682vqo; Wed, 26 Jul 2023 10:52:44 -0700 (PDT) X-Google-Smtp-Source: APBJJlFxmQvb7zcCwz7RAxBZTM5//FH87L1QI/BglDTD+PH9iH32DLOIXEi+axpd6Iclk0SdEyzc X-Received: by 2002:a05:6358:6383:b0:133:c03:53c6 with SMTP id k3-20020a056358638300b001330c0353c6mr2192415rwh.14.1690393963721; Wed, 26 Jul 2023 10:52:43 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1690393963; cv=none; d=google.com; s=arc-20160816; b=YrshMkBol7v1R+W/cxEuHqGaRBDbUw7cgN89aCtZHIgjUpFZ+05fq47yRsSb6XtvWv d+X+NJNqyTfWsx4DzvIAbWcpBNxQUi+UhUOQAnHIJXXXn5yBg6xZhpM+GDpJYFCreZyV X8Cf7aZeJGyVWZrBL4HoFNIB0KVGmaiW2USEMAtcFhoInSHaIlTJLzEud2l2gwXkp8sG ayAurNzF0vvY3YlQKFF7rOBp4pqyJURaZnHfcpB5TP66U9ZNBsNGCvUICHS0t4kch7xD cXE0smrhgbqDR+nE9YVuzr2YgEGK1J/M8dEeMJjcvOkVaK5xTe7EPEgNN4vas0xY15f8 S/SQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=NJLUXZui7ZOfnSYBmduifkTps7+S3usbGUxPDDlfunI=; fh=Jpn3R4JmmVOUsuPzBxq53h+Q0nrbpuajN1OLJbY8QB4=; b=uNpAftZCalt3tmvB+N4Xn8Phh+xPJrgkpeXwaghTYry1iAGy1MXM9X67zv6NxoEaW4 CsiB6dZgM9Npg89bLUWsh8wKi2ejODa9wdgGqJCQt+6dS1ouSjO7x5Yjfo4bUWagJFrQ mxNevGT8qXWR23gD/m/+iozz+ThCD5U1p8f1kx6gXBkKsaN6WIy50WnlMBFdFDgD1TlK hi1kNZP9r+94sAMjuhO5ydLc+FlS7r0OVDkVh0Y+AYYBn1mW4PY8s2h9ZucEalWPag4o HG7/p60t4oI1RZ6xFt4aWl7gf287rGLUaJB/mCREK91j3SBZFaHgZS9bbERJXz8xu+TW UZFQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@cisco.com header.s=iport header.b=Fj4MCxiv; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=QUARANTINE sp=NONE dis=NONE) header.from=cisco.com Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id c20-20020a631c54000000b0051b1966e6b6si12937626pgm.521.2023.07.26.10.52.29; Wed, 26 Jul 2023 10:52:43 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@cisco.com header.s=iport header.b=Fj4MCxiv; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=QUARANTINE sp=NONE dis=NONE) header.from=cisco.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232051AbjGZQuF (ORCPT + 99 others); Wed, 26 Jul 2023 12:50:05 -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 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 X-Spam-Status: No, score=-9.6 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIMWL_WL_MED,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF, SPF_HELO_PASS,SPF_NONE,T_SCC_BODY_TEXT_LINE,URIBL_BLOCKED, USER_IN_DEF_DKIM_WL autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1772506541136127699 X-GMAIL-MSGID: 1772506541136127699 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) } }