From patchwork Wed Oct 18 12:25:00 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wedson Almeida Filho X-Patchwork-Id: 13426951 Received: from lindbergh.monkeyblade.net (lindbergh.monkeyblade.net [23.128.96.19]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3BFDE341A1; Wed, 18 Oct 2023 12:25:46 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="Rh2MO0Fu" Received: from mail-pl1-x633.google.com (mail-pl1-x633.google.com [IPv6:2607:f8b0:4864:20::633]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 707E6112; Wed, 18 Oct 2023 05:25:44 -0700 (PDT) Received: by mail-pl1-x633.google.com with SMTP id d9443c01a7336-1c9d922c039so54580295ad.3; Wed, 18 Oct 2023 05:25:44 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1697631944; x=1698236744; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=Xex43RS2jNp3vQd6oNEUXyjdswTtSBFkdu+lQ9eZYV0=; b=Rh2MO0FuxoyWaDoGp0i2hcbU0P0nq6f6x179mXNCuu/9vRQy1TYejZKFcmDhLBVJxG Mg+b2/3VYUzHvCeCbPq8BTlZm+j4Urz9ZoRVKN+x54cbpyWFtNX2NN9dhZaLRcW9FYDN ZeOw25McPwyFP8s7dxNMuCqAy01j3Hs8H778S8X31nmJO//HeqbENyByvt6wqPCwRqdL pLWz5I5irHjpQKrk9tbHd5inuBZechh0FetkwEq+7HZ8aC3EAbLjZRDC2gRmQRn7+SAW Ha0xrB4SDgUCYDIJHEaDVDcpu6ZSHz+QlrrHjcqylaPowiVcXrwBxVgZrs6TU7oUdsbb ToKQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1697631944; x=1698236744; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=Xex43RS2jNp3vQd6oNEUXyjdswTtSBFkdu+lQ9eZYV0=; b=Rf77AjxiAqBOpcfydXhx+VBWD6E29bUz90LdQxEGuMncSfxiDZjNIiPrBxJvpvcBCN YEtwVowN9vW1q4NTJalnMSP+Rm6HHKXA7hLBrd7fhbF2fon8+jWz90wl+NI/AIb1bpCT jLgvLA+ORBgbcyYfvNlhIe00vCFN8bnvhgA2jNI5Ah9CALQDfYju+fTYx5TGpkCVjyPv vMYsoDC0F2mTRzuwwa6pWZLG+/2IiX8UB8nSrO0Jcp9TKYDIoKOG+uxKY9KB1CIPZwyP XylWpuRz4lbSn/TmXRuwKU5CUpU40uCfP/eKRqquH9Bwc7qZMDmN87kQTVetJLuVdYSo xBMQ== X-Gm-Message-State: AOJu0Yzyva50sIbZYNxQPXOgeOFPpw2L3PA3+kggUDQbj+8pDdSASk8N KsBwac34cCST0ki7CnE8V9o= X-Google-Smtp-Source: AGHT+IHkRBzV2dTXTNWcXbjYFGuqrxzZoK1XnpQG76IA1lMQfND1KnbOY7w4b+5ctUXQ1kZ0GTAcow== X-Received: by 2002:a17:903:32d0:b0:1ca:1be4:bda4 with SMTP id i16-20020a17090332d000b001ca1be4bda4mr5525882plr.4.1697631943795; Wed, 18 Oct 2023 05:25:43 -0700 (PDT) Received: from wedsonaf-dev.. ([2804:389:7122:43b8:9b73:6339:3351:cce0]) by smtp.googlemail.com with ESMTPSA id j1-20020a170902c3c100b001c736b0037fsm3411046plj.231.2023.10.18.05.25.39 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 18 Oct 2023 05:25:43 -0700 (PDT) From: Wedson Almeida Filho To: Alexander Viro , Christian Brauner , Matthew Wilcox Cc: Kent Overstreet , Greg Kroah-Hartman , linux-fsdevel@vger.kernel.org, rust-for-linux@vger.kernel.org, Wedson Almeida Filho Subject: [RFC PATCH 01/19] rust: fs: add registration/unregistration of file systems Date: Wed, 18 Oct 2023 09:25:00 -0300 Message-Id: <20231018122518.128049-2-wedsonaf@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20231018122518.128049-1-wedsonaf@gmail.com> References: <20231018122518.128049-1-wedsonaf@gmail.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM, RCVD_IN_DNSWL_BLOCKED,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net From: Wedson Almeida Filho Allow basic registration and unregistration of Rust file system types. Unregistration happens automatically when a registration variable is dropped (e.g., when it goes out of scope). File systems registered this way are visible in `/proc/filesystems` but cannot be mounted yet because `init_fs_context` fails. Signed-off-by: Wedson Almeida Filho --- rust/bindings/bindings_helper.h | 1 + rust/kernel/error.rs | 2 - rust/kernel/fs.rs | 80 +++++++++++++++++++++++++++++++++ rust/kernel/lib.rs | 1 + 4 files changed, 82 insertions(+), 2 deletions(-) create mode 100644 rust/kernel/fs.rs diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index 3b620ae07021..9c23037b33d0 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -8,6 +8,7 @@ #include #include +#include #include #include #include diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs index 05fcab6abfe6..e6d7ce46be55 100644 --- a/rust/kernel/error.rs +++ b/rust/kernel/error.rs @@ -320,8 +320,6 @@ pub(crate) fn from_err_ptr(ptr: *mut T) -> Result<*mut T> { /// }) /// } /// ``` -// TODO: Remove `dead_code` marker once an in-kernel client is available. -#[allow(dead_code)] pub(crate) fn from_result(f: F) -> T where T: From, diff --git a/rust/kernel/fs.rs b/rust/kernel/fs.rs new file mode 100644 index 000000000000..f3fb09db41ba --- /dev/null +++ b/rust/kernel/fs.rs @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Kernel file systems. +//! +//! This module allows Rust code to register new kernel file systems. +//! +//! C headers: [`include/linux/fs.h`](../../include/linux/fs.h) + +use crate::error::{code::*, from_result, to_result, Error}; +use crate::types::Opaque; +use crate::{bindings, init::PinInit, str::CStr, try_pin_init, ThisModule}; +use core::{marker::PhantomPinned, pin::Pin}; +use macros::{pin_data, pinned_drop}; + +/// A file system type. +pub trait FileSystem { + /// The name of the file system type. + const NAME: &'static CStr; +} + +/// A registration of a file system. +#[pin_data(PinnedDrop)] +pub struct Registration { + #[pin] + fs: Opaque, + #[pin] + _pin: PhantomPinned, +} + +// SAFETY: `Registration` doesn't provide any `&self` methods, so it is safe to pass references +// to it around. +unsafe impl Sync for Registration {} + +// SAFETY: Both registration and unregistration are implemented in C and safe to be performed +// from any thread, so `Registration` is `Send`. +unsafe impl Send for Registration {} + +impl Registration { + /// Creates the initialiser of a new file system registration. + pub fn new(module: &'static ThisModule) -> impl PinInit { + try_pin_init!(Self { + _pin: PhantomPinned, + fs <- Opaque::try_ffi_init(|fs_ptr: *mut bindings::file_system_type| { + // SAFETY: `try_ffi_init` guarantees that `fs_ptr` is valid for write. + unsafe { fs_ptr.write(bindings::file_system_type::default()) }; + + // SAFETY: `try_ffi_init` guarantees that `fs_ptr` is valid for write, and it has + // just been initialised above, so it's also valid for read. + let fs = unsafe { &mut *fs_ptr }; + fs.owner = module.0; + fs.name = T::NAME.as_char_ptr(); + fs.init_fs_context = Some(Self::init_fs_context_callback); + fs.kill_sb = Some(Self::kill_sb_callback); + fs.fs_flags = 0; + + // SAFETY: Pointers stored in `fs` are static so will live for as long as the + // registration is active (it is undone in `drop`). + to_result(unsafe { bindings::register_filesystem(fs_ptr) }) + }), + }) + } + + unsafe extern "C" fn init_fs_context_callback( + _fc_ptr: *mut bindings::fs_context, + ) -> core::ffi::c_int { + from_result(|| Err(ENOTSUPP)) + } + + unsafe extern "C" fn kill_sb_callback(_sb_ptr: *mut bindings::super_block) {} +} + +#[pinned_drop] +impl PinnedDrop for Registration { + fn drop(self: Pin<&mut Self>) { + // SAFETY: If an instance of `Self` has been successfully created, a call to + // `register_filesystem` has necessarily succeeded. So it's ok to call + // `unregister_filesystem` on the previously registered fs. + unsafe { bindings::unregister_filesystem(self.fs.get()) }; + } +} diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 187d58f906a5..00059b80c240 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -34,6 +34,7 @@ mod allocator; mod build_assert; pub mod error; +pub mod fs; pub mod init; pub mod ioctl; #[cfg(CONFIG_KUNIT)] From patchwork Wed Oct 18 12:25:01 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wedson Almeida Filho X-Patchwork-Id: 13426952 Received: from lindbergh.monkeyblade.net (lindbergh.monkeyblade.net [23.128.96.19]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1BD6518C19; Wed, 18 Oct 2023 12:25:50 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="QZl4C7mZ" Received: from mail-pl1-x62a.google.com (mail-pl1-x62a.google.com [IPv6:2607:f8b0:4864:20::62a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 7B1D7118; Wed, 18 Oct 2023 05:25:48 -0700 (PDT) Received: by mail-pl1-x62a.google.com with SMTP id d9443c01a7336-1caad0bcc95so1770835ad.0; Wed, 18 Oct 2023 05:25:48 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1697631948; x=1698236748; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=at7ARN8aRdzn5IC/IbI2nC6fhRb6Ugq4eVty6+/dMiY=; b=QZl4C7mZShHYVI6GraBv8bzmSxdo2JE//LRMTQ8yLUUZt65VSQ2yOf0egNqfWv4/3l Wj/WINF/VFNlJqYu+m/N/4oLAJCodjBnnK5C0YOPqizNtThhlBlrlBbM3eixrB4eXu5B 65fY4Smd6CRNIRFWuLHWlZaNVb5JxY50webLig3qgJgyJjQzuGYE26PnVEP9w86lYoGb Hqx4vyrWAe/aJezpiMPWatR1d8Il/2Tq7T75/HWOALR3AHUIFiYulpucrZHWfS8EuECQ W0R3Jlols6jXaUXV8REXCVsKEfuA4SGFyPFlAmghfKRILOIZwqSooI71ERkFwJbPELkF 8Alw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1697631948; x=1698236748; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=at7ARN8aRdzn5IC/IbI2nC6fhRb6Ugq4eVty6+/dMiY=; b=jBBN5D2v4JYbHLZwA6/WXwUZrlTH745ZPQBCvanwr2nl8P3VvNxaNn05UvfZrpC35g k21V8BxZJw36wQ7PQDEPFmFleLojw51xbauJE4MwT5q/DYedn0qXqBlc89nGoJpJCEh3 fvYdUrJiYokEi9N/WRUKKSNy6Xwfb3BAre9szF1RJHy6Vt0waPmC+iH5M17SS0GajMZU YrRZSvOYa72wh5ETcUVH/ichZTEgkFv9DAnQfmur6/0uoClNpkU/U6R+0AF4dWp/rnJG Y8gsgLfeZIrO7fpfD8YviOdP+XyUeqxrC0e4Th7D5BiaGjGtFEcgVLYObpVEZ4+fSA8O 9njw== X-Gm-Message-State: AOJu0YwtH4/Gkr9A3RZ/Wr1ODKuKyFCnJm9YNerVhL29Mv13iU9v+P8x BqZAnGTH0iJL+3f9PEtRU0Y= X-Google-Smtp-Source: AGHT+IG3BgHG9EBP7UAXFrvuxJVUqHqq3mDriTGUcBz7Brmq0oqJCrErxZvdCbnpAA/nfR7nZ2lwCA== X-Received: by 2002:a17:903:24d:b0:1ca:8b74:17f2 with SMTP id j13-20020a170903024d00b001ca8b7417f2mr5622696plh.6.1697631947921; Wed, 18 Oct 2023 05:25:47 -0700 (PDT) Received: from wedsonaf-dev.. ([2804:389:7122:43b8:9b73:6339:3351:cce0]) by smtp.googlemail.com with ESMTPSA id j1-20020a170902c3c100b001c736b0037fsm3411046plj.231.2023.10.18.05.25.44 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 18 Oct 2023 05:25:47 -0700 (PDT) From: Wedson Almeida Filho To: Alexander Viro , Christian Brauner , Matthew Wilcox Cc: Kent Overstreet , Greg Kroah-Hartman , linux-fsdevel@vger.kernel.org, rust-for-linux@vger.kernel.org, Wedson Almeida Filho Subject: [RFC PATCH 02/19] rust: fs: introduce the `module_fs` macro Date: Wed, 18 Oct 2023 09:25:01 -0300 Message-Id: <20231018122518.128049-3-wedsonaf@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20231018122518.128049-1-wedsonaf@gmail.com> References: <20231018122518.128049-1-wedsonaf@gmail.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM, RCVD_IN_DNSWL_BLOCKED,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net From: Wedson Almeida Filho Simplify the declaration of modules that only expose a file system type. They can now do it using the `module_fs` macro. Signed-off-by: Wedson Almeida Filho --- rust/kernel/fs.rs | 56 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/rust/kernel/fs.rs b/rust/kernel/fs.rs index f3fb09db41ba..1df54c234101 100644 --- a/rust/kernel/fs.rs +++ b/rust/kernel/fs.rs @@ -9,7 +9,7 @@ use crate::error::{code::*, from_result, to_result, Error}; use crate::types::Opaque; use crate::{bindings, init::PinInit, str::CStr, try_pin_init, ThisModule}; -use core::{marker::PhantomPinned, pin::Pin}; +use core::{marker::PhantomData, marker::PhantomPinned, pin::Pin}; use macros::{pin_data, pinned_drop}; /// A file system type. @@ -78,3 +78,57 @@ fn drop(self: Pin<&mut Self>) { unsafe { bindings::unregister_filesystem(self.fs.get()) }; } } + +/// Kernel module that exposes a single file system implemented by `T`. +#[pin_data] +pub struct Module { + #[pin] + fs_reg: Registration, + _p: PhantomData, +} + +impl crate::InPlaceModule for Module { + fn init(module: &'static ThisModule) -> impl PinInit { + try_pin_init!(Self { + fs_reg <- Registration::new::(module), + _p: PhantomData, + }) + } +} + +/// Declares a kernel module that exposes a single file system. +/// +/// The `type` argument must be a type which implements the [`FileSystem`] trait. Also accepts +/// various forms of kernel metadata. +/// +/// # Examples +/// +/// ``` +/// # mod module_fs_sample { +/// use kernel::prelude::*; +/// use kernel::{c_str, fs}; +/// +/// kernel::module_fs! { +/// type: MyFs, +/// name: "myfs", +/// author: "Rust for Linux Contributors", +/// description: "My Rust fs", +/// license: "GPL", +/// } +/// +/// struct MyFs; +/// impl fs::FileSystem for MyFs { +/// const NAME: &'static CStr = c_str!("myfs"); +/// } +/// # } +/// ``` +#[macro_export] +macro_rules! module_fs { + (type: $type:ty, $($f:tt)*) => { + type ModuleType = $crate::fs::Module<$type>; + $crate::macros::module! { + type: ModuleType, + $($f)* + } + } +} From patchwork Wed Oct 18 12:25:02 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wedson Almeida Filho X-Patchwork-Id: 13426953 Received: from lindbergh.monkeyblade.net (lindbergh.monkeyblade.net [23.128.96.19]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7EBD91CF89; Wed, 18 Oct 2023 12:25:54 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="PdGvdu00" Received: from mail-pl1-x633.google.com (mail-pl1-x633.google.com [IPv6:2607:f8b0:4864:20::633]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 7F432114; Wed, 18 Oct 2023 05:25:52 -0700 (PDT) Received: by mail-pl1-x633.google.com with SMTP id d9443c01a7336-1c9a1762b43so54658475ad.1; Wed, 18 Oct 2023 05:25:52 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1697631952; x=1698236752; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=3+P20rWuZA6yxfRBKzWJs22aPk/q3dgVSUAZONRYMjw=; b=PdGvdu00sRSA1Yaa8GQHorCi1ikzK7miFi0PT447y6PSw1GHzuCuxUEVsjcBreQw1v 35e0DY8+ObBLC1IZINlSp9xz8IQapzw5IxdXpXh7rwyEucFrfSJXrxk/a2ZVCAub/DBX bmF3f92cAo9AQdE7ZIvz6b2V+yWEh/nlorvRpG6c+tIU8rLaJ7pf1YeOxOpVOK/0B5pU gou4o5QTJN4DFGhtCzKPUqeVaHRHu4fnTTg3c6rXZqPz8GCJSVVm76Pl0CCmTs12+xWl MbYFuxqUDlMf0DHyTDnzShKkvF5Tn7OxXkTNeAeXs9YqX9GptecG+GnFVN6fg8E09RAv uvZA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1697631952; x=1698236752; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=3+P20rWuZA6yxfRBKzWJs22aPk/q3dgVSUAZONRYMjw=; b=NRNvFBlpMUvTsQvIc8/jgMlfQNsgZ49h8NZRHzlJdecP+uuVjyV0OeFk8bfxc2NUgU 4K2v984lMEYIi8AcrGtVeeeTtFPW02hWrwSeSlA36m9jXpIwnyhbKb3cC/0KzQGBuWhU 1zdArUJuCzqLn6BEOs7w6FpjXTmOgvxhzOXB1UzLGsXammUmVcCrvafVnbriDA6kXwTM WKfXsGOvlWjdbyLxtSzf3O4j1mUqbF3m1g47LgS6sU3JoJV6AdrWuq3Dk2Z87jFCQJew 83hA3fKNWI7F2MnP2VJP/m3031JaJOywBMQqpNLDxPaTccrWnfAreLQipZepF9q7asRF DLHw== X-Gm-Message-State: AOJu0YyKZD71nn7aZbfb24leRwa9u1CD59ObaS3uv/IESVLoS8Jg/0m0 2uYH5muxaYwc0mxOeMrvOiCAykreKWc= X-Google-Smtp-Source: AGHT+IExtLOqLAdx/C44xqAH7PSDp+Ode8C4dtrhLN5L0D5UrJBHynOp5U70WUKrkqTijfMiNwnDKA== X-Received: by 2002:a17:902:e84e:b0:1c9:bf02:6638 with SMTP id t14-20020a170902e84e00b001c9bf026638mr6329839plg.51.1697631951913; Wed, 18 Oct 2023 05:25:51 -0700 (PDT) Received: from wedsonaf-dev.. ([2804:389:7122:43b8:9b73:6339:3351:cce0]) by smtp.googlemail.com with ESMTPSA id j1-20020a170902c3c100b001c736b0037fsm3411046plj.231.2023.10.18.05.25.48 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 18 Oct 2023 05:25:51 -0700 (PDT) From: Wedson Almeida Filho To: Alexander Viro , Christian Brauner , Matthew Wilcox Cc: Kent Overstreet , Greg Kroah-Hartman , linux-fsdevel@vger.kernel.org, rust-for-linux@vger.kernel.org, Wedson Almeida Filho Subject: [RFC PATCH 03/19] samples: rust: add initial ro file system sample Date: Wed, 18 Oct 2023 09:25:02 -0300 Message-Id: <20231018122518.128049-4-wedsonaf@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20231018122518.128049-1-wedsonaf@gmail.com> References: <20231018122518.128049-1-wedsonaf@gmail.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM, RCVD_IN_DNSWL_BLOCKED,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net From: Wedson Almeida Filho Introduce a basic sample that for now only registers the file system and doesn't really provide any functionality beyond having it listed in `/proc/filesystems`. New functionality will be added to the sample in subsequent patches as their abstractions are introduced. Signed-off-by: Wedson Almeida Filho --- samples/rust/Kconfig | 10 ++++++++++ samples/rust/Makefile | 1 + samples/rust/rust_rofs.rs | 19 +++++++++++++++++++ 3 files changed, 30 insertions(+) create mode 100644 samples/rust/rust_rofs.rs diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig index 59f44a8b6958..2f26c5c52813 100644 --- a/samples/rust/Kconfig +++ b/samples/rust/Kconfig @@ -41,6 +41,16 @@ config SAMPLE_RUST_PRINT If unsure, say N. +config SAMPLE_RUST_ROFS + tristate "Read-only file system" + help + This option builds the Rust read-only file system sample. + + To compile this as a module, choose M here: + the module will be called rust_rofs. + + If unsure, say N. + config SAMPLE_RUST_HOSTPROGS bool "Host programs" help diff --git a/samples/rust/Makefile b/samples/rust/Makefile index 791fc18180e9..df1e4341ae95 100644 --- a/samples/rust/Makefile +++ b/samples/rust/Makefile @@ -3,5 +3,6 @@ obj-$(CONFIG_SAMPLE_RUST_MINIMAL) += rust_minimal.o obj-$(CONFIG_SAMPLE_RUST_INPLACE) += rust_inplace.o obj-$(CONFIG_SAMPLE_RUST_PRINT) += rust_print.o +obj-$(CONFIG_SAMPLE_RUST_ROFS) += rust_rofs.o subdir-$(CONFIG_SAMPLE_RUST_HOSTPROGS) += hostprogs diff --git a/samples/rust/rust_rofs.rs b/samples/rust/rust_rofs.rs new file mode 100644 index 000000000000..1c00b1da8b94 --- /dev/null +++ b/samples/rust/rust_rofs.rs @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Rust read-only file system sample. + +use kernel::prelude::*; +use kernel::{c_str, fs}; + +kernel::module_fs! { + type: RoFs, + name: "rust_rofs", + author: "Rust for Linux Contributors", + description: "Rust read-only file system sample", + license: "GPL", +} + +struct RoFs; +impl fs::FileSystem for RoFs { + const NAME: &'static CStr = c_str!("rust-fs"); +} From patchwork Wed Oct 18 12:25:03 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wedson Almeida Filho X-Patchwork-Id: 13426954 Received: from lindbergh.monkeyblade.net (lindbergh.monkeyblade.net [23.128.96.19]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 048F218E30; Wed, 18 Oct 2023 12:25:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="Ewd2j9jC" Received: from mail-pl1-x62c.google.com (mail-pl1-x62c.google.com [IPv6:2607:f8b0:4864:20::62c]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id A7F1D112; Wed, 18 Oct 2023 05:25:56 -0700 (PDT) Received: by mail-pl1-x62c.google.com with SMTP id d9443c01a7336-1c0ecb9a075so46362565ad.2; Wed, 18 Oct 2023 05:25:56 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1697631956; x=1698236756; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=sjSzpmMOCT4LT2rUtlPcM7n6DjRhsgcGb736/u49gDg=; b=Ewd2j9jCBjIERVtOfQQRLUDWnp7I002KuSZ9TxAgjnWj7CGTzdgrNSolSxZbsFFVFI bps4G55/vqY6WQ+DHJiK8DRY9NmbtB92ROThjfmvJyuumTvM/9qbdAuBm6Z0ns23IJuN X7v35yBPXnN6dLRL1aWij3gnV5sozCzIQiWpWEcW2gsZ5zoWAhweoUoY0ArvqaSvKzbF ahheIZGBC8HXSqFyCGMrTPeFqLYTnulXzCaLp87uU7dGSXeeeS4f7urJDtym8+w5zw/o cCjt1EWW2YnEHLwrrjQhgqXQ2fbma20QA2khrAZDWkrWhEa4WjR5/4cMBO5k6zldirDS qRoQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1697631956; x=1698236756; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=sjSzpmMOCT4LT2rUtlPcM7n6DjRhsgcGb736/u49gDg=; b=iMEyfuWpQmv5hC1Zd0D/me3qDI122wBr8rt26XPXaYoyaOXRPyeNsy4h5TMtoSy93G TDiDr1+iQGG5jubxoBQIpf7xvcFRjORzgUFK4hFwNBLdg1e+s3Sr/wvGTX2NsEQGg93y iX2cyPizjcVU4cKd/y3QVhCyPOaZpOsss4PbJxaGpfHODPMSQP1m7WgkauLTEyPaquaC MB1id6iQYSXD4dRHZ84NQCfxfJB9hGsCii/bdrlBtuEABiPHgoUucegmltlx8IDpkJjX LFfMkJMZHkw1+hkmqj28oHnjoe8aN9RrV2RNVxsKYKGXxqJhDwIbL4L1H2lfhBc6zZpY PO6A== X-Gm-Message-State: AOJu0YzAtBAVEUn4wjthRVgXPCNR+Pxg7rJutTMcRaW7692LWp1N7OfE Kg9pP6kUBvoIuH0r0lzIqtk= X-Google-Smtp-Source: AGHT+IEHljYLnC89JKm6iOfPV87ciAsr2h6+vZ7wTinfr7t5Iz4HcfpApQZRogtGDAiTONOmMQz7mg== X-Received: by 2002:a17:902:ce8d:b0:1c8:9a60:387f with SMTP id f13-20020a170902ce8d00b001c89a60387fmr5835248plg.56.1697631956015; Wed, 18 Oct 2023 05:25:56 -0700 (PDT) Received: from wedsonaf-dev.. ([2804:389:7122:43b8:9b73:6339:3351:cce0]) by smtp.googlemail.com with ESMTPSA id j1-20020a170902c3c100b001c736b0037fsm3411046plj.231.2023.10.18.05.25.52 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 18 Oct 2023 05:25:55 -0700 (PDT) From: Wedson Almeida Filho To: Alexander Viro , Christian Brauner , Matthew Wilcox Cc: Kent Overstreet , Greg Kroah-Hartman , linux-fsdevel@vger.kernel.org, rust-for-linux@vger.kernel.org, Wedson Almeida Filho Subject: [RFC PATCH 04/19] rust: fs: introduce `FileSystem::super_params` Date: Wed, 18 Oct 2023 09:25:03 -0300 Message-Id: <20231018122518.128049-5-wedsonaf@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20231018122518.128049-1-wedsonaf@gmail.com> References: <20231018122518.128049-1-wedsonaf@gmail.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM, RCVD_IN_DNSWL_NONE,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net From: Wedson Almeida Filho Allow Rust file systems to initialise superblocks, which allows them to be mounted (though they are still empty). Some scaffolding code is added to create an empty directory as the root. It is replaced by proper inode creation in a subsequent patch in this series. Signed-off-by: Wedson Almeida Filho --- rust/bindings/bindings_helper.h | 5 + rust/bindings/lib.rs | 4 + rust/kernel/fs.rs | 176 ++++++++++++++++++++++++++++++-- samples/rust/rust_rofs.rs | 10 ++ 4 files changed, 189 insertions(+), 6 deletions(-) diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index 9c23037b33d0..ca1898ce9527 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -22,3 +23,7 @@ const gfp_t BINDINGS___GFP_ZERO = __GFP_ZERO; const slab_flags_t BINDINGS_SLAB_RECLAIM_ACCOUNT = SLAB_RECLAIM_ACCOUNT; const slab_flags_t BINDINGS_SLAB_MEM_SPREAD = SLAB_MEM_SPREAD; const slab_flags_t BINDINGS_SLAB_ACCOUNT = SLAB_ACCOUNT; + +const unsigned long BINDINGS_SB_RDONLY = SB_RDONLY; + +const loff_t BINDINGS_MAX_LFS_FILESIZE = MAX_LFS_FILESIZE; diff --git a/rust/bindings/lib.rs b/rust/bindings/lib.rs index 6a8c6cd17e45..426915d3fb57 100644 --- a/rust/bindings/lib.rs +++ b/rust/bindings/lib.rs @@ -55,3 +55,7 @@ mod bindings_helper { pub const SLAB_RECLAIM_ACCOUNT: slab_flags_t = BINDINGS_SLAB_RECLAIM_ACCOUNT; pub const SLAB_MEM_SPREAD: slab_flags_t = BINDINGS_SLAB_MEM_SPREAD; pub const SLAB_ACCOUNT: slab_flags_t = BINDINGS_SLAB_ACCOUNT; + +pub const SB_RDONLY: core::ffi::c_ulong = BINDINGS_SB_RDONLY; + +pub const MAX_LFS_FILESIZE: loff_t = BINDINGS_MAX_LFS_FILESIZE; diff --git a/rust/kernel/fs.rs b/rust/kernel/fs.rs index 1df54c234101..31cf643aaded 100644 --- a/rust/kernel/fs.rs +++ b/rust/kernel/fs.rs @@ -6,16 +6,22 @@ //! //! C headers: [`include/linux/fs.h`](../../include/linux/fs.h) -use crate::error::{code::*, from_result, to_result, Error}; +use crate::error::{code::*, from_result, to_result, Error, Result}; use crate::types::Opaque; use crate::{bindings, init::PinInit, str::CStr, try_pin_init, ThisModule}; use core::{marker::PhantomData, marker::PhantomPinned, pin::Pin}; use macros::{pin_data, pinned_drop}; +/// Maximum size of an inode. +pub const MAX_LFS_FILESIZE: i64 = bindings::MAX_LFS_FILESIZE; + /// A file system type. pub trait FileSystem { /// The name of the file system type. const NAME: &'static CStr; + + /// Returns the parameters to initialise a super block. + fn super_params(sb: &NewSuperBlock) -> Result; } /// A registration of a file system. @@ -49,7 +55,7 @@ pub fn new(module: &'static ThisModule) -> impl PinInit< let fs = unsafe { &mut *fs_ptr }; fs.owner = module.0; fs.name = T::NAME.as_char_ptr(); - fs.init_fs_context = Some(Self::init_fs_context_callback); + fs.init_fs_context = Some(Self::init_fs_context_callback::); fs.kill_sb = Some(Self::kill_sb_callback); fs.fs_flags = 0; @@ -60,13 +66,22 @@ pub fn new(module: &'static ThisModule) -> impl PinInit< }) } - unsafe extern "C" fn init_fs_context_callback( - _fc_ptr: *mut bindings::fs_context, + unsafe extern "C" fn init_fs_context_callback( + fc_ptr: *mut bindings::fs_context, ) -> core::ffi::c_int { - from_result(|| Err(ENOTSUPP)) + from_result(|| { + // SAFETY: The C callback API guarantees that `fc_ptr` is valid. + let fc = unsafe { &mut *fc_ptr }; + fc.ops = &Tables::::CONTEXT; + Ok(0) + }) } - unsafe extern "C" fn kill_sb_callback(_sb_ptr: *mut bindings::super_block) {} + unsafe extern "C" fn kill_sb_callback(sb_ptr: *mut bindings::super_block) { + // SAFETY: In `get_tree_callback` we always call `get_tree_nodev`, so `kill_anon_super` is + // the appropriate function to call for cleanup. + unsafe { bindings::kill_anon_super(sb_ptr) }; + } } #[pinned_drop] @@ -79,6 +94,151 @@ fn drop(self: Pin<&mut Self>) { } } +/// A file system super block. +/// +/// Wraps the kernel's `struct super_block`. +#[repr(transparent)] +pub struct SuperBlock(Opaque, PhantomData); + +/// Required superblock parameters. +/// +/// This is returned by implementations of [`FileSystem::super_params`]. +pub struct SuperParams { + /// The magic number of the superblock. + pub magic: u32, + + /// The size of a block in powers of 2 (i.e., for a value of `n`, the size is `2^n`). + pub blocksize_bits: u8, + + /// Maximum size of a file. + /// + /// The maximum allowed value is [`MAX_LFS_FILESIZE`]. + pub maxbytes: i64, + + /// Granularity of c/m/atime in ns (cannot be worse than a second). + pub time_gran: u32, +} + +/// A superblock that is still being initialised. +/// +/// # Invariants +/// +/// The superblock is a newly-created one and this is the only active pointer to it. +#[repr(transparent)] +pub struct NewSuperBlock(bindings::super_block, PhantomData); + +struct Tables(T); +impl Tables { + const CONTEXT: bindings::fs_context_operations = bindings::fs_context_operations { + free: None, + parse_param: None, + get_tree: Some(Self::get_tree_callback), + reconfigure: None, + parse_monolithic: None, + dup: None, + }; + + unsafe extern "C" fn get_tree_callback(fc: *mut bindings::fs_context) -> core::ffi::c_int { + // SAFETY: `fc` is valid per the callback contract. `fill_super_callback` also has + // the right type and is a valid callback. + unsafe { bindings::get_tree_nodev(fc, Some(Self::fill_super_callback)) } + } + + unsafe extern "C" fn fill_super_callback( + sb_ptr: *mut bindings::super_block, + _fc: *mut bindings::fs_context, + ) -> core::ffi::c_int { + from_result(|| { + // SAFETY: The callback contract guarantees that `sb_ptr` is a unique pointer to a + // newly-created superblock. + let sb = unsafe { &mut *sb_ptr.cast() }; + let params = T::super_params(sb)?; + + sb.0.s_magic = params.magic as _; + sb.0.s_op = &Tables::::SUPER_BLOCK; + sb.0.s_maxbytes = params.maxbytes; + sb.0.s_time_gran = params.time_gran; + sb.0.s_blocksize_bits = params.blocksize_bits; + sb.0.s_blocksize = 1; + if sb.0.s_blocksize.leading_zeros() < params.blocksize_bits.into() { + return Err(EINVAL); + } + sb.0.s_blocksize = 1 << sb.0.s_blocksize_bits; + sb.0.s_flags |= bindings::SB_RDONLY; + + // The following is scaffolding code that will be removed in a subsequent patch. It is + // needed to build a root dentry, otherwise core code will BUG(). + // SAFETY: `sb` is the superblock being initialised, it is valid for read and write. + let inode = unsafe { bindings::new_inode(&mut sb.0) }; + if inode.is_null() { + return Err(ENOMEM); + } + + // SAFETY: `inode` is valid for write. + unsafe { bindings::set_nlink(inode, 2) }; + + { + // SAFETY: This is a newly-created inode. No other references to it exist, so it is + // safe to mutably dereference it. + let inode = unsafe { &mut *inode }; + inode.i_ino = 1; + inode.i_mode = (bindings::S_IFDIR | 0o755) as _; + + // SAFETY: `simple_dir_operations` never changes, it's safe to reference it. + inode.__bindgen_anon_3.i_fop = unsafe { &bindings::simple_dir_operations }; + + // SAFETY: `simple_dir_inode_operations` never changes, it's safe to reference it. + inode.i_op = unsafe { &bindings::simple_dir_inode_operations }; + } + + // SAFETY: `d_make_root` requires that `inode` be valid and referenced, which is the + // case for this call. + // + // It takes over the inode, even on failure, so we don't need to clean it up. + let dentry = unsafe { bindings::d_make_root(inode) }; + if dentry.is_null() { + return Err(ENOMEM); + } + + sb.0.s_root = dentry; + + Ok(0) + }) + } + + const SUPER_BLOCK: bindings::super_operations = bindings::super_operations { + alloc_inode: None, + destroy_inode: None, + free_inode: None, + dirty_inode: None, + write_inode: None, + drop_inode: None, + evict_inode: None, + put_super: None, + sync_fs: None, + freeze_super: None, + freeze_fs: None, + thaw_super: None, + unfreeze_fs: None, + statfs: None, + remount_fs: None, + umount_begin: None, + show_options: None, + show_devname: None, + show_path: None, + show_stats: None, + #[cfg(CONFIG_QUOTA)] + quota_read: None, + #[cfg(CONFIG_QUOTA)] + quota_write: None, + #[cfg(CONFIG_QUOTA)] + get_dquots: None, + nr_cached_objects: None, + free_cached_objects: None, + shutdown: None, + }; +} + /// Kernel module that exposes a single file system implemented by `T`. #[pin_data] pub struct Module { @@ -105,6 +265,7 @@ fn init(module: &'static ThisModule) -> impl PinInit { /// /// ``` /// # mod module_fs_sample { +/// use kernel::fs::{NewSuperBlock, SuperParams}; /// use kernel::prelude::*; /// use kernel::{c_str, fs}; /// @@ -119,6 +280,9 @@ fn init(module: &'static ThisModule) -> impl PinInit { /// struct MyFs; /// impl fs::FileSystem for MyFs { /// const NAME: &'static CStr = c_str!("myfs"); +/// fn super_params(_: &NewSuperBlock) -> Result { +/// todo!() +/// } /// } /// # } /// ``` diff --git a/samples/rust/rust_rofs.rs b/samples/rust/rust_rofs.rs index 1c00b1da8b94..9878bf88b991 100644 --- a/samples/rust/rust_rofs.rs +++ b/samples/rust/rust_rofs.rs @@ -2,6 +2,7 @@ //! Rust read-only file system sample. +use kernel::fs::{NewSuperBlock, SuperParams}; use kernel::prelude::*; use kernel::{c_str, fs}; @@ -16,4 +17,13 @@ struct RoFs; impl fs::FileSystem for RoFs { const NAME: &'static CStr = c_str!("rust-fs"); + + fn super_params(_sb: &NewSuperBlock) -> Result { + Ok(SuperParams { + magic: 0x52555354, + blocksize_bits: 12, + maxbytes: fs::MAX_LFS_FILESIZE, + time_gran: 1, + }) + } } From patchwork Wed Oct 18 12:25:04 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wedson Almeida Filho X-Patchwork-Id: 13426955 Received: from lindbergh.monkeyblade.net (lindbergh.monkeyblade.net [23.128.96.19]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id AB275341AF; Wed, 18 Oct 2023 12:26:02 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="J5BsO1Sa" Received: from mail-pl1-x62a.google.com (mail-pl1-x62a.google.com [IPv6:2607:f8b0:4864:20::62a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id E8D08113; Wed, 18 Oct 2023 05:26:00 -0700 (PDT) Received: by mail-pl1-x62a.google.com with SMTP id d9443c01a7336-1ca72f8ff3aso23083795ad.0; Wed, 18 Oct 2023 05:26:00 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1697631960; x=1698236760; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=lklRFykrq/fpkimZQTlOYKCnVo2EhWeW5QXx0pyDJS4=; b=J5BsO1SaCMyucI+Z7ziPKP0NQgfacbSFHEAfU5lQaZm+WYD+nj47u/mg1MY4mrjtFg lgzjENtT5rMUYIf9tHov+jhrPIsFXsNTqNaUUl++x9qjOcpXbTC6BZzAn5uNhItNpouT 4y8H0OrTcFfMBqFJQpak0VOCfnMh2eEhQrtv509faY/EZWwNKf9ThEWGGrHab173681s OGCeZXzTX3j67aIfZGvZmyPwBjbFCNxc9C80sRJyZyzJJpox1Pitvqomn/xYesiA+Wj2 LrKIWHDKAwEXGco+D5/9QEXdOqW7198Uyi/U0Rr3mfJdfwYP0gx1+L7z8yPWhuosZqdw 6GmQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1697631960; x=1698236760; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=lklRFykrq/fpkimZQTlOYKCnVo2EhWeW5QXx0pyDJS4=; b=BU/wk77tXPx31LkaY6KWsg96ALVmQuKowvh+I5CdpXrEjAlFe4XVWXJ4GVRc0MtSCz IPJ7yh9wQUmEvdI9ltadas/WrZMwKtxkmOSbN4JUMFREqLBD2HN75Ah007fuponCnu3e PJY66R8I2IwZ8+0AvImCYgvkGiV0rIQ66yaAYoDiImn1iHbSeuMbvVqmnXLCwJ1fWG9f Hg1g4uGTX+jvsOJpmGhh09nM48kDVKfavjt9V8QkwNGZSYEoQ2zikspuuKegnc0QOLXW sky7BzsmQWTk3EO8uzjIRGsxGQ/71bn+rF5v3EsmObpK2RII9mgOyExfCBVC3FA0bOyI yXqw== X-Gm-Message-State: AOJu0YxMl/U1J2DHQkSwBHi+EwlPgkPcGpKZYe071OVUS4gHr3EaQH0X NfNAeGbFty3UDWMsW4yESpI= X-Google-Smtp-Source: AGHT+IHCJio9SLQo+NCeOqNaQsEGNbvSAmKDMCDv7U8I4tFhAMgV1wV+NR+vxT/LTqO5kseEdemd1w== X-Received: by 2002:a17:903:32ce:b0:1c9:dbd3:94f7 with SMTP id i14-20020a17090332ce00b001c9dbd394f7mr6194268plr.65.1697631960265; Wed, 18 Oct 2023 05:26:00 -0700 (PDT) Received: from wedsonaf-dev.. ([2804:389:7122:43b8:9b73:6339:3351:cce0]) by smtp.googlemail.com with ESMTPSA id j1-20020a170902c3c100b001c736b0037fsm3411046plj.231.2023.10.18.05.25.56 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 18 Oct 2023 05:25:59 -0700 (PDT) From: Wedson Almeida Filho To: Alexander Viro , Christian Brauner , Matthew Wilcox Cc: Kent Overstreet , Greg Kroah-Hartman , linux-fsdevel@vger.kernel.org, rust-for-linux@vger.kernel.org, Wedson Almeida Filho Subject: [RFC PATCH 05/19] rust: fs: introduce `INode` Date: Wed, 18 Oct 2023 09:25:04 -0300 Message-Id: <20231018122518.128049-6-wedsonaf@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20231018122518.128049-1-wedsonaf@gmail.com> References: <20231018122518.128049-1-wedsonaf@gmail.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM, RCVD_IN_DNSWL_BLOCKED,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net From: Wedson Almeida Filho Allow Rust file systems to handle typed and ref-counted inodes. This is in preparation for creating new inodes (for example, to create the root inode of a new superblock), which comes in the next patch in the series. Signed-off-by: Wedson Almeida Filho --- rust/helpers.c | 7 +++++++ rust/kernel/fs.rs | 53 +++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 58 insertions(+), 2 deletions(-) diff --git a/rust/helpers.c b/rust/helpers.c index 4c86fe4a7e05..fe45f8ddb31f 100644 --- a/rust/helpers.c +++ b/rust/helpers.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -144,6 +145,12 @@ struct kunit *rust_helper_kunit_get_current_test(void) } EXPORT_SYMBOL_GPL(rust_helper_kunit_get_current_test); +off_t rust_helper_i_size_read(const struct inode *inode) +{ + return i_size_read(inode); +} +EXPORT_SYMBOL_GPL(rust_helper_i_size_read); + /* * `bindgen` binds the C `size_t` type as the Rust `usize` type, so we can * use it in contexts where Rust expects a `usize` like slice (array) indices. diff --git a/rust/kernel/fs.rs b/rust/kernel/fs.rs index 31cf643aaded..30fa1f312f33 100644 --- a/rust/kernel/fs.rs +++ b/rust/kernel/fs.rs @@ -7,9 +7,9 @@ //! C headers: [`include/linux/fs.h`](../../include/linux/fs.h) use crate::error::{code::*, from_result, to_result, Error, Result}; -use crate::types::Opaque; +use crate::types::{AlwaysRefCounted, Opaque}; use crate::{bindings, init::PinInit, str::CStr, try_pin_init, ThisModule}; -use core::{marker::PhantomData, marker::PhantomPinned, pin::Pin}; +use core::{marker::PhantomData, marker::PhantomPinned, pin::Pin, ptr}; use macros::{pin_data, pinned_drop}; /// Maximum size of an inode. @@ -94,6 +94,55 @@ fn drop(self: Pin<&mut Self>) { } } +/// The number of an inode. +pub type Ino = u64; + +/// A node in the file system index (inode). +/// +/// Wraps the kernel's `struct inode`. +/// +/// # Invariants +/// +/// Instances of this type are always ref-counted, that is, a call to `ihold` ensures that the +/// allocation remains valid at least until the matching call to `iput`. +#[repr(transparent)] +pub struct INode(Opaque, PhantomData); + +impl INode { + /// Returns the number of the inode. + pub fn ino(&self) -> Ino { + // SAFETY: `i_ino` is immutable, and `self` is guaranteed to be valid by the existence of a + // shared reference (&self) to it. + unsafe { (*self.0.get()).i_ino } + } + + /// Returns the super-block that owns the inode. + pub fn super_block(&self) -> &SuperBlock { + // SAFETY: `i_sb` is immutable, and `self` is guaranteed to be valid by the existence of a + // shared reference (&self) to it. + unsafe { &*(*self.0.get()).i_sb.cast() } + } + + /// Returns the size of the inode contents. + pub fn size(&self) -> i64 { + // SAFETY: `self` is guaranteed to be valid by the existence of a shared reference. + unsafe { bindings::i_size_read(self.0.get()) } + } +} + +// SAFETY: The type invariants guarantee that `INode` is always ref-counted. +unsafe impl AlwaysRefCounted for INode { + fn inc_ref(&self) { + // SAFETY: The existence of a shared reference means that the refcount is nonzero. + unsafe { bindings::ihold(self.0.get()) }; + } + + unsafe fn dec_ref(obj: ptr::NonNull) { + // SAFETY: The safety requirements guarantee that the refcount is nonzero. + unsafe { bindings::iput(obj.cast().as_ptr()) } + } +} + /// A file system super block. /// /// Wraps the kernel's `struct super_block`. From patchwork Wed Oct 18 12:25:05 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wedson Almeida Filho X-Patchwork-Id: 13426956 Received: from lindbergh.monkeyblade.net (lindbergh.monkeyblade.net [23.128.96.19]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 93A0834CD8; Wed, 18 Oct 2023 12:26:07 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="JmkzsGeT" Received: from mail-pl1-x62b.google.com (mail-pl1-x62b.google.com [IPv6:2607:f8b0:4864:20::62b]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id A103810F; Wed, 18 Oct 2023 05:26:05 -0700 (PDT) Received: by mail-pl1-x62b.google.com with SMTP id d9443c01a7336-1ca215cc713so26763595ad.3; Wed, 18 Oct 2023 05:26:05 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1697631965; x=1698236765; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=vfXrH0ngDYVWfqSiSt/qHAsoIVeF72UwAYxZtAykxtw=; b=JmkzsGeTQHEK3TF+XIJlZT6u1HaCSuIt18HoWXvjvfd3R1l0Y5J4rsDwo6mtvfSgON 0Q9Mr8AbFcHZ9wbNMrtCt9v/Jr/f7I2amtlxObDseCxTUeHtvRiM4k/aHZYcgWSoUlpe ToJrVENQBm1ivJkNZ64SEIkMnSUAIdByCNc1gjdBLKS4bHnY7ZR4AwPzQzSC71mGM4ME +Jw/zvn3r0D/t/nAyHZO+5nzZHrLU0LQjvfEHPbzmZeFTZy8n23CJ2pFM7XV1/jbk1Jq rv+8kImxz+9BK0sIOawv3aTUfA6k5XZau2u/RV85dKmMLzvNvh5tyAok4sYmQlMmrlBE KSug== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1697631965; x=1698236765; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=vfXrH0ngDYVWfqSiSt/qHAsoIVeF72UwAYxZtAykxtw=; b=S8Pl53SjtwBYc+NMnS+WWbtwNt/VSdWYdGBwvFHNl2nKLtJHJIKQmmjpVhfO4VHFSy 5D7pcHbJfsY+dZWSs/q39s3F2otkY8idB7cyYawT/ichNSLdf5QT/iYzCiECypKgF7Qy 34f66eLoVD/2YjAs3K7BEf4GNGAhGWyYnyXRA37QwBO4TOTzyqbEPm7ANV/QrrB+OCRe 5m3DP6Hgqvb7Svcg+Q4aeC/w8ymR8Xr8fU/x/1ry3ErFwIJ3V8iUCogxyuqrwFH1N3eN qxV9HO/0SnslS8cbRkg5tsZOnC1dek+a4smzWM8DlnDQ1w7d6erEC1qmdZbhGW7xmKif yiJQ== X-Gm-Message-State: AOJu0Yz2keupYsewOgKWWRXQVjgv8xHQimVbQfYByFOAVLKcVCiEPeQs aHXTWapetTm2i2wVArqMdYI= X-Google-Smtp-Source: AGHT+IFmu1LYmaqPb9u0A7F701UcFReA8OCRcFUK2e/RMJcPNXzo9dEt76JmfnTNgbCFV0vpkStiYA== X-Received: by 2002:a17:903:7c4:b0:1ca:79b6:ce42 with SMTP id ko4-20020a17090307c400b001ca79b6ce42mr4311720plb.47.1697631964904; Wed, 18 Oct 2023 05:26:04 -0700 (PDT) Received: from wedsonaf-dev.. ([2804:389:7122:43b8:9b73:6339:3351:cce0]) by smtp.googlemail.com with ESMTPSA id j1-20020a170902c3c100b001c736b0037fsm3411046plj.231.2023.10.18.05.26.00 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 18 Oct 2023 05:26:04 -0700 (PDT) From: Wedson Almeida Filho To: Alexander Viro , Christian Brauner , Matthew Wilcox Cc: Kent Overstreet , Greg Kroah-Hartman , linux-fsdevel@vger.kernel.org, rust-for-linux@vger.kernel.org, Wedson Almeida Filho Subject: [RFC PATCH 06/19] rust: fs: introduce `FileSystem::init_root` Date: Wed, 18 Oct 2023 09:25:05 -0300 Message-Id: <20231018122518.128049-7-wedsonaf@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20231018122518.128049-1-wedsonaf@gmail.com> References: <20231018122518.128049-1-wedsonaf@gmail.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM, RCVD_IN_DNSWL_NONE,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net From: Wedson Almeida Filho Allow Rust file systems to specify their root directory. Also allow them to create (and do cache lookups of) directory inodes. (More types of inodes are added in subsequent patches in the series.) The `NewINode` type ensures that a new inode is properly initialised before it is marked so. It also facilitates error paths by automatically marking inodes as failed if they're not properly initialised. Signed-off-by: Wedson Almeida Filho --- rust/helpers.c | 12 +++ rust/kernel/fs.rs | 178 +++++++++++++++++++++++++++++++------- samples/rust/rust_rofs.rs | 22 ++++- 3 files changed, 181 insertions(+), 31 deletions(-) diff --git a/rust/helpers.c b/rust/helpers.c index fe45f8ddb31f..c5a2bec6467d 100644 --- a/rust/helpers.c +++ b/rust/helpers.c @@ -145,6 +145,18 @@ struct kunit *rust_helper_kunit_get_current_test(void) } EXPORT_SYMBOL_GPL(rust_helper_kunit_get_current_test); +void rust_helper_i_uid_write(struct inode *inode, uid_t uid) +{ + i_uid_write(inode, uid); +} +EXPORT_SYMBOL_GPL(rust_helper_i_uid_write); + +void rust_helper_i_gid_write(struct inode *inode, gid_t gid) +{ + i_gid_write(inode, gid); +} +EXPORT_SYMBOL_GPL(rust_helper_i_gid_write); + off_t rust_helper_i_size_read(const struct inode *inode) { return i_size_read(inode); diff --git a/rust/kernel/fs.rs b/rust/kernel/fs.rs index 30fa1f312f33..f3a41cf57502 100644 --- a/rust/kernel/fs.rs +++ b/rust/kernel/fs.rs @@ -7,9 +7,9 @@ //! C headers: [`include/linux/fs.h`](../../include/linux/fs.h) use crate::error::{code::*, from_result, to_result, Error, Result}; -use crate::types::{AlwaysRefCounted, Opaque}; -use crate::{bindings, init::PinInit, str::CStr, try_pin_init, ThisModule}; -use core::{marker::PhantomData, marker::PhantomPinned, pin::Pin, ptr}; +use crate::types::{ARef, AlwaysRefCounted, Either, Opaque}; +use crate::{bindings, init::PinInit, str::CStr, time::Timespec, try_pin_init, ThisModule}; +use core::{marker::PhantomData, marker::PhantomPinned, mem::ManuallyDrop, pin::Pin, ptr}; use macros::{pin_data, pinned_drop}; /// Maximum size of an inode. @@ -22,6 +22,12 @@ pub trait FileSystem { /// Returns the parameters to initialise a super block. fn super_params(sb: &NewSuperBlock) -> Result; + + /// Initialises and returns the root inode of the given superblock. + /// + /// This is called during initialisation of a superblock after [`FileSystem::super_params`] has + /// completed successfully. + fn init_root(sb: &SuperBlock) -> Result>>; } /// A registration of a file system. @@ -143,12 +149,136 @@ unsafe fn dec_ref(obj: ptr::NonNull) { } } +/// An inode that is locked and hasn't been initialised yet. +#[repr(transparent)] +pub struct NewINode(ARef>); + +impl NewINode { + /// Initialises the new inode with the given parameters. + pub fn init(self, params: INodeParams) -> Result>> { + // SAFETY: This is a new inode, so it's safe to manipulate it mutably. + let inode = unsafe { &mut *self.0 .0.get() }; + + let mode = match params.typ { + INodeType::Dir => { + // SAFETY: `simple_dir_operations` never changes, it's safe to reference it. + inode.__bindgen_anon_3.i_fop = unsafe { &bindings::simple_dir_operations }; + + // SAFETY: `simple_dir_inode_operations` never changes, it's safe to reference it. + inode.i_op = unsafe { &bindings::simple_dir_inode_operations }; + bindings::S_IFDIR + } + }; + + inode.i_mode = (params.mode & 0o777) | u16::try_from(mode)?; + inode.i_size = params.size; + inode.i_blocks = params.blocks; + + inode.__i_ctime = params.ctime.into(); + inode.i_mtime = params.mtime.into(); + inode.i_atime = params.atime.into(); + + // SAFETY: inode is a new inode, so it is valid for write. + unsafe { + bindings::set_nlink(inode, params.nlink); + bindings::i_uid_write(inode, params.uid); + bindings::i_gid_write(inode, params.gid); + bindings::unlock_new_inode(inode); + } + + // SAFETY: We are manually destructuring `self` and preventing `drop` from being called. + Ok(unsafe { (&ManuallyDrop::new(self).0 as *const ARef>).read() }) + } +} + +impl Drop for NewINode { + fn drop(&mut self) { + // SAFETY: The new inode failed to be turned into an initialised inode, so it's safe (and + // in fact required) to call `iget_failed` on it. + unsafe { bindings::iget_failed(self.0 .0.get()) }; + } +} + +/// The type of the inode. +#[derive(Copy, Clone)] +pub enum INodeType { + /// Directory type. + Dir, +} + +/// Required inode parameters. +/// +/// This is used when creating new inodes. +pub struct INodeParams { + /// The access mode. It's a mask that grants execute (1), write (2) and read (4) access to + /// everyone, the owner group, and the owner. + pub mode: u16, + + /// Type of inode. + /// + /// Also carries additional per-type data. + pub typ: INodeType, + + /// Size of the contents of the inode. + /// + /// Its maximum value is [`MAX_LFS_FILESIZE`]. + pub size: i64, + + /// Number of blocks. + pub blocks: u64, + + /// Number of links to the inode. + pub nlink: u32, + + /// User id. + pub uid: u32, + + /// Group id. + pub gid: u32, + + /// Creation time. + pub ctime: Timespec, + + /// Last modification time. + pub mtime: Timespec, + + /// Last access time. + pub atime: Timespec, +} + /// A file system super block. /// /// Wraps the kernel's `struct super_block`. #[repr(transparent)] pub struct SuperBlock(Opaque, PhantomData); +impl SuperBlock { + /// Tries to get an existing inode or create a new one if it doesn't exist yet. + pub fn get_or_create_inode(&self, ino: Ino) -> Result>, NewINode>> { + // SAFETY: The only initialisation missing from the superblock is the root, and this + // function is needed to create the root, so it's safe to call it. + let inode = + ptr::NonNull::new(unsafe { bindings::iget_locked(self.0.get(), ino) }).ok_or(ENOMEM)?; + + // SAFETY: `inode` is valid for read, but there could be concurrent writers (e.g., if it's + // an already-initialised inode), so we use `read_volatile` to read its current state. + let state = unsafe { ptr::read_volatile(ptr::addr_of!((*inode.as_ptr()).i_state)) }; + if state & u64::from(bindings::I_NEW) == 0 { + // The inode is cached. Just return it. + // + // SAFETY: `inode` had its refcount incremented by `iget_locked`; this increment is now + // owned by `ARef`. + Ok(Either::Left(unsafe { ARef::from_raw(inode.cast()) })) + } else { + // SAFETY: The new inode is valid but not fully initialised yet, so it's ok to create a + // `NewINode`. + Ok(Either::Right(NewINode(unsafe { + ARef::from_raw(inode.cast()) + }))) + } + } +} + /// Required superblock parameters. /// /// This is returned by implementations of [`FileSystem::super_params`]. @@ -215,41 +345,28 @@ impl Tables { sb.0.s_blocksize = 1 << sb.0.s_blocksize_bits; sb.0.s_flags |= bindings::SB_RDONLY; - // The following is scaffolding code that will be removed in a subsequent patch. It is - // needed to build a root dentry, otherwise core code will BUG(). - // SAFETY: `sb` is the superblock being initialised, it is valid for read and write. - let inode = unsafe { bindings::new_inode(&mut sb.0) }; - if inode.is_null() { - return Err(ENOMEM); - } - - // SAFETY: `inode` is valid for write. - unsafe { bindings::set_nlink(inode, 2) }; - - { - // SAFETY: This is a newly-created inode. No other references to it exist, so it is - // safe to mutably dereference it. - let inode = unsafe { &mut *inode }; - inode.i_ino = 1; - inode.i_mode = (bindings::S_IFDIR | 0o755) as _; - - // SAFETY: `simple_dir_operations` never changes, it's safe to reference it. - inode.__bindgen_anon_3.i_fop = unsafe { &bindings::simple_dir_operations }; + // SAFETY: The callback contract guarantees that `sb_ptr` is a unique pointer to a + // newly-created (and initialised above) superblock. + let sb = unsafe { &mut *sb_ptr.cast() }; + let root = T::init_root(sb)?; - // SAFETY: `simple_dir_inode_operations` never changes, it's safe to reference it. - inode.i_op = unsafe { &bindings::simple_dir_inode_operations }; + // Reject root inode if it belongs to a different superblock. + if !ptr::eq(root.super_block(), sb) { + return Err(EINVAL); } // SAFETY: `d_make_root` requires that `inode` be valid and referenced, which is the // case for this call. // // It takes over the inode, even on failure, so we don't need to clean it up. - let dentry = unsafe { bindings::d_make_root(inode) }; + let dentry = unsafe { bindings::d_make_root(ManuallyDrop::new(root).0.get()) }; if dentry.is_null() { return Err(ENOMEM); } - sb.0.s_root = dentry; + // SAFETY: The callback contract guarantees that `sb_ptr` is a unique pointer to a + // newly-created (and initialised above) superblock. + unsafe { (*sb_ptr).s_root = dentry }; Ok(0) }) @@ -314,9 +431,9 @@ fn init(module: &'static ThisModule) -> impl PinInit { /// /// ``` /// # mod module_fs_sample { -/// use kernel::fs::{NewSuperBlock, SuperParams}; +/// use kernel::fs::{INode, NewSuperBlock, SuperBlock, SuperParams}; /// use kernel::prelude::*; -/// use kernel::{c_str, fs}; +/// use kernel::{c_str, fs, types::ARef}; /// /// kernel::module_fs! { /// type: MyFs, @@ -332,6 +449,9 @@ fn init(module: &'static ThisModule) -> impl PinInit { /// fn super_params(_: &NewSuperBlock) -> Result { /// todo!() /// } +/// fn init_root(_sb: &SuperBlock) -> Result>> { +/// todo!() +/// } /// } /// # } /// ``` diff --git a/samples/rust/rust_rofs.rs b/samples/rust/rust_rofs.rs index 9878bf88b991..9e5f4c7d1c06 100644 --- a/samples/rust/rust_rofs.rs +++ b/samples/rust/rust_rofs.rs @@ -2,9 +2,9 @@ //! Rust read-only file system sample. -use kernel::fs::{NewSuperBlock, SuperParams}; +use kernel::fs::{INode, INodeParams, INodeType, NewSuperBlock, SuperBlock, SuperParams}; use kernel::prelude::*; -use kernel::{c_str, fs}; +use kernel::{c_str, fs, time::UNIX_EPOCH, types::ARef, types::Either}; kernel::module_fs! { type: RoFs, @@ -26,4 +26,22 @@ fn super_params(_sb: &NewSuperBlock) -> Result { time_gran: 1, }) } + + fn init_root(sb: &SuperBlock) -> Result>> { + match sb.get_or_create_inode(1)? { + Either::Left(existing) => Ok(existing), + Either::Right(new) => new.init(INodeParams { + typ: INodeType::Dir, + mode: 0o555, + size: 1, + blocks: 1, + nlink: 2, + uid: 0, + gid: 0, + atime: UNIX_EPOCH, + ctime: UNIX_EPOCH, + mtime: UNIX_EPOCH, + }), + } + } } From patchwork Wed Oct 18 12:25:06 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wedson Almeida Filho X-Patchwork-Id: 13426957 Received: from lindbergh.monkeyblade.net (lindbergh.monkeyblade.net [23.128.96.19]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 70BBF34CD8; Wed, 18 Oct 2023 12:26:12 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="dLaC7rG6" Received: from mail-pl1-x636.google.com (mail-pl1-x636.google.com [IPv6:2607:f8b0:4864:20::636]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 123F510F; Wed, 18 Oct 2023 05:26:10 -0700 (PDT) Received: by mail-pl1-x636.google.com with SMTP id d9443c01a7336-1c888b3a25aso44296675ad.0; Wed, 18 Oct 2023 05:26:10 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1697631969; x=1698236769; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=XQfRqske9UjvRdn/X8+SlAcIjXiT81OZyHev4WJ/7us=; b=dLaC7rG6TTHfHnd6udtfR5th5XipTyXlzw2n8ZNlWd3eYK3VKNUKTD+ISb5tsD8n8f +XVsUxUAeq6xOtH6GbF9k/rpdJRO3PCuecwxt9ggwpj2DDKJUqYCAvo4K0/x7n3KmPUy SX6honsWKkg2ad4su/hAYGOF+9SvGXcQJbCo2fFHCiyxDpYDM2bQiHSjStgtBf7fulV5 1B095Rs7RfjBKtCIm9+IWlY5L4LHdW0uEzxrQc1Iqw7gDr3oJs8OOfeFP8K+o4l7iqar WoZfSm7fWhJvJGKsq0JpWgOSNo6aBEBTj0ChhkiTbyLM7UD5DNVzlKGJNkLI6DT02yQS k9NQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1697631969; x=1698236769; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=XQfRqske9UjvRdn/X8+SlAcIjXiT81OZyHev4WJ/7us=; b=APTFudB18PLkNcZjexq4QXaxS7Yug0rMpTGV3rz08VjpqEF07TqDLv44QpwodOulO1 B/MZTvSqDy2BfeCANoav5UVzpm8HjpW69Trdavxf4fDUpDIo3JnOykGbiGrnbyKNm1Yq 3OFIDbNIWYfUzurUWsXV2C96YeGbdyZiNKzePVLexr9uMW+0j5ZkzCsAxkfBUWjhiYkQ B/kt8k5F09YIObEtuo4wS4Oe+gZRvHlQtdOWDUyXt7Vzk6wSTadfpBP8U8llMT21G1GN ppX5MDl5IG9YDsTNsyqnN7dQT/gcVCGKZ8B3FPQqPm/7xvlqjtuHGGB4keQdifV4TEF6 qkOQ== X-Gm-Message-State: AOJu0Yxot2nRiHpp9o7FDGgvX4ZBB7E5bYtQen36d0oUMBbQjDiFWgRm SlH6V9Y7ga1hlAXlXcQ5btw= X-Google-Smtp-Source: AGHT+IEBF0C1PT+8JLZU2VMZ95DnDX+wvC93KMcFKddQHcTyEjYu+C3a5E7D/7nlpt9Re1KMDj+Sow== X-Received: by 2002:a17:902:c412:b0:1c3:1f0c:fb82 with SMTP id k18-20020a170902c41200b001c31f0cfb82mr5566145plk.41.1697631969398; Wed, 18 Oct 2023 05:26:09 -0700 (PDT) Received: from wedsonaf-dev.. ([2804:389:7122:43b8:9b73:6339:3351:cce0]) by smtp.googlemail.com with ESMTPSA id j1-20020a170902c3c100b001c736b0037fsm3411046plj.231.2023.10.18.05.26.05 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 18 Oct 2023 05:26:09 -0700 (PDT) From: Wedson Almeida Filho To: Alexander Viro , Christian Brauner , Matthew Wilcox Cc: Kent Overstreet , Greg Kroah-Hartman , linux-fsdevel@vger.kernel.org, rust-for-linux@vger.kernel.org, Wedson Almeida Filho Subject: [RFC PATCH 07/19] rust: fs: introduce `FileSystem::read_dir` Date: Wed, 18 Oct 2023 09:25:06 -0300 Message-Id: <20231018122518.128049-8-wedsonaf@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20231018122518.128049-1-wedsonaf@gmail.com> References: <20231018122518.128049-1-wedsonaf@gmail.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM, RCVD_IN_DNSWL_BLOCKED,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net From: Wedson Almeida Filho Allow Rust file systems to report the contents of their directory inodes. The reported entries cannot be opened yet. Signed-off-by: Wedson Almeida Filho --- rust/kernel/fs.rs | 193 +++++++++++++++++++++++++++++++++++++- samples/rust/rust_rofs.rs | 49 +++++++++- 2 files changed, 236 insertions(+), 6 deletions(-) diff --git a/rust/kernel/fs.rs b/rust/kernel/fs.rs index f3a41cf57502..89611c44e4c5 100644 --- a/rust/kernel/fs.rs +++ b/rust/kernel/fs.rs @@ -28,6 +28,70 @@ pub trait FileSystem { /// This is called during initialisation of a superblock after [`FileSystem::super_params`] has /// completed successfully. fn init_root(sb: &SuperBlock) -> Result>>; + + /// Reads directory entries from directory inodes. + /// + /// [`DirEmitter::pos`] holds the current position of the directory reader. + fn read_dir(inode: &INode, emitter: &mut DirEmitter) -> Result; +} + +/// The types of directory entries reported by [`FileSystem::read_dir`]. +#[repr(u32)] +#[derive(Copy, Clone)] +pub enum DirEntryType { + /// Unknown type. + Unknown = bindings::DT_UNKNOWN, + + /// Named pipe (first-in, first-out) type. + Fifo = bindings::DT_FIFO, + + /// Character device type. + Chr = bindings::DT_CHR, + + /// Directory type. + Dir = bindings::DT_DIR, + + /// Block device type. + Blk = bindings::DT_BLK, + + /// Regular file type. + Reg = bindings::DT_REG, + + /// Symbolic link type. + Lnk = bindings::DT_LNK, + + /// Named unix-domain socket type. + Sock = bindings::DT_SOCK, + + /// White-out type. + Wht = bindings::DT_WHT, +} + +impl From for DirEntryType { + fn from(value: INodeType) -> Self { + match value { + INodeType::Dir => DirEntryType::Dir, + } + } +} + +impl core::convert::TryFrom for DirEntryType { + type Error = crate::error::Error; + + fn try_from(v: u32) -> Result { + match v { + v if v == Self::Unknown as u32 => Ok(Self::Unknown), + v if v == Self::Fifo as u32 => Ok(Self::Fifo), + v if v == Self::Chr as u32 => Ok(Self::Chr), + v if v == Self::Dir as u32 => Ok(Self::Dir), + v if v == Self::Blk as u32 => Ok(Self::Blk), + v if v == Self::Reg as u32 => Ok(Self::Reg), + v if v == Self::Lnk as u32 => Ok(Self::Lnk), + v if v == Self::Sock as u32 => Ok(Self::Sock), + v if v == Self::Wht as u32 => Ok(Self::Wht), + _ => Err(EDOM), + } + } } /// A registration of a file system. @@ -161,9 +225,7 @@ pub fn init(self, params: INodeParams) -> Result>> { let mode = match params.typ { INodeType::Dir => { - // SAFETY: `simple_dir_operations` never changes, it's safe to reference it. - inode.__bindgen_anon_3.i_fop = unsafe { &bindings::simple_dir_operations }; - + inode.__bindgen_anon_3.i_fop = &Tables::::DIR_FILE_OPERATIONS; // SAFETY: `simple_dir_inode_operations` never changes, it's safe to reference it. inode.i_op = unsafe { &bindings::simple_dir_inode_operations }; bindings::S_IFDIR @@ -403,6 +465,126 @@ impl Tables { free_cached_objects: None, shutdown: None, }; + + const DIR_FILE_OPERATIONS: bindings::file_operations = bindings::file_operations { + owner: ptr::null_mut(), + llseek: Some(bindings::generic_file_llseek), + read: Some(bindings::generic_read_dir), + write: None, + read_iter: None, + write_iter: None, + iopoll: None, + iterate_shared: Some(Self::read_dir_callback), + poll: None, + unlocked_ioctl: None, + compat_ioctl: None, + mmap: None, + mmap_supported_flags: 0, + open: None, + flush: None, + release: None, + fsync: None, + fasync: None, + lock: None, + get_unmapped_area: None, + check_flags: None, + flock: None, + splice_write: None, + splice_read: None, + splice_eof: None, + setlease: None, + fallocate: None, + show_fdinfo: None, + copy_file_range: None, + remap_file_range: None, + fadvise: None, + uring_cmd: None, + uring_cmd_iopoll: None, + }; + + unsafe extern "C" fn read_dir_callback( + file: *mut bindings::file, + ctx_ptr: *mut bindings::dir_context, + ) -> core::ffi::c_int { + from_result(|| { + // SAFETY: The C API guarantees that `file` is valid for read. And since `f_inode` is + // immutable, we can read it directly. + let inode = unsafe { &*(*file).f_inode.cast::>() }; + + // SAFETY: The C API guarantees that this is the only reference to the `dir_context` + // instance. + let emitter = unsafe { &mut *ctx_ptr.cast::() }; + let orig_pos = emitter.pos(); + + // Call the module implementation. We ignore errors if directory entries have been + // succesfully emitted: this is because we want users to see them before the error. + match T::read_dir(inode, emitter) { + Ok(_) => Ok(0), + Err(e) => { + if emitter.pos() == orig_pos { + Err(e) + } else { + Ok(0) + } + } + } + }) + } +} + +/// Directory entry emitter. +/// +/// This is used in [`FileSystem::read_dir`] implementations to report the directory entry. +#[repr(transparent)] +pub struct DirEmitter(bindings::dir_context); + +impl DirEmitter { + /// Returns the current position of the emitter. + pub fn pos(&self) -> i64 { + self.0.pos + } + + /// Emits a directory entry. + /// + /// `pos_inc` is the number with which to increment the current position on success. + /// + /// `name` is the name of the entry. + /// + /// `ino` is the inode number of the entry. + /// + /// `etype` is the type of the entry. + /// + /// Returns `false` when the entry could not be emitted, possibly because the user-provided + /// buffer is full. + pub fn emit(&mut self, pos_inc: i64, name: &[u8], ino: Ino, etype: DirEntryType) -> bool { + let Ok(name_len) = i32::try_from(name.len()) else { + return false; + }; + + let Some(actor) = self.0.actor else { + return false; + }; + + let Some(new_pos) = self.0.pos.checked_add(pos_inc) else { + return false; + }; + + // SAFETY: `name` is valid at least for the duration of the `actor` call. + let ret = unsafe { + actor( + &mut self.0, + name.as_ptr().cast(), + name_len, + self.0.pos, + ino, + etype as _, + ) + }; + if ret { + self.0.pos = new_pos; + } + ret + } } /// Kernel module that exposes a single file system implemented by `T`. @@ -431,7 +613,7 @@ fn init(module: &'static ThisModule) -> impl PinInit { /// /// ``` /// # mod module_fs_sample { -/// use kernel::fs::{INode, NewSuperBlock, SuperBlock, SuperParams}; +/// use kernel::fs::{DirEmitter, INode, NewSuperBlock, SuperBlock, SuperParams}; /// use kernel::prelude::*; /// use kernel::{c_str, fs, types::ARef}; /// @@ -452,6 +634,9 @@ fn init(module: &'static ThisModule) -> impl PinInit { /// fn init_root(_sb: &SuperBlock) -> Result>> { /// todo!() /// } +/// fn read_dir(_: &INode, _: &mut DirEmitter) -> Result { +/// todo!() +/// } /// } /// # } /// ``` diff --git a/samples/rust/rust_rofs.rs b/samples/rust/rust_rofs.rs index 9e5f4c7d1c06..4e61a94afa70 100644 --- a/samples/rust/rust_rofs.rs +++ b/samples/rust/rust_rofs.rs @@ -2,7 +2,9 @@ //! Rust read-only file system sample. -use kernel::fs::{INode, INodeParams, INodeType, NewSuperBlock, SuperBlock, SuperParams}; +use kernel::fs::{ + DirEmitter, INode, INodeParams, INodeType, NewSuperBlock, SuperBlock, SuperParams, +}; use kernel::prelude::*; use kernel::{c_str, fs, time::UNIX_EPOCH, types::ARef, types::Either}; @@ -14,6 +16,30 @@ license: "GPL", } +struct Entry { + name: &'static [u8], + ino: u64, + etype: INodeType, +} + +const ENTRIES: [Entry; 3] = [ + Entry { + name: b".", + ino: 1, + etype: INodeType::Dir, + }, + Entry { + name: b"..", + ino: 1, + etype: INodeType::Dir, + }, + Entry { + name: b"subdir", + ino: 2, + etype: INodeType::Dir, + }, +]; + struct RoFs; impl fs::FileSystem for RoFs { const NAME: &'static CStr = c_str!("rust-fs"); @@ -33,7 +59,7 @@ fn init_root(sb: &SuperBlock) -> Result>> { Either::Right(new) => new.init(INodeParams { typ: INodeType::Dir, mode: 0o555, - size: 1, + size: ENTRIES.len().try_into()?, blocks: 1, nlink: 2, uid: 0, @@ -44,4 +70,23 @@ fn init_root(sb: &SuperBlock) -> Result>> { }), } } + + fn read_dir(inode: &INode, emitter: &mut DirEmitter) -> Result { + if inode.ino() != 1 { + return Ok(()); + } + + let pos = emitter.pos(); + if pos >= ENTRIES.len().try_into()? { + return Ok(()); + } + + for e in ENTRIES.iter().skip(pos.try_into()?) { + if !emitter.emit(1, e.name, e.ino, e.etype.into()) { + break; + } + } + + Ok(()) + } } From patchwork Wed Oct 18 12:25:07 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wedson Almeida Filho X-Patchwork-Id: 13426958 Received: from lindbergh.monkeyblade.net (lindbergh.monkeyblade.net [23.128.96.19]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1060E35891; Wed, 18 Oct 2023 12:26:16 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="ZmOy+nj8" Received: from mail-pf1-x42d.google.com (mail-pf1-x42d.google.com [IPv6:2607:f8b0:4864:20::42d]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id CA4F611A; Wed, 18 Oct 2023 05:26:14 -0700 (PDT) Received: by mail-pf1-x42d.google.com with SMTP id d2e1a72fcca58-6bd96cfb99cso2729654b3a.2; Wed, 18 Oct 2023 05:26:14 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1697631974; x=1698236774; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=+VI9w5cgDuGKLdDBL1zMH5+SdDPFuOUVLpieG9kVjiA=; b=ZmOy+nj8lg3BGJ8Jux2fahGgwJciANI6iceb7hgfDHEQ1xnaPNhd8cs5ThWpEpZyuD /jzEb4FWD8333IZOxEKPxIzp1yMDufV7Rpg0dVxe6IGwVI8t4UCvHXVIeqsSCBByfHT8 p6ds6ZrOmTEcxkl0G+fVsnoSh/IvfqdAxweApWY1nyJUc7YSupWcjztKyhRqUjWasNSP WqDrqhk1H+u8JULhHht2I+PNQIud9fGpAVkZ3LDkrK8TsYSLA8WFwSRQrFuPS8+KHCy8 QmGsnx7r6gi7/HjPHIPvHUJoMOSum2Wq7nxvcqwPYA4izvx0bb04dWgjmi+iGZ52/fN0 gitw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1697631974; x=1698236774; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=+VI9w5cgDuGKLdDBL1zMH5+SdDPFuOUVLpieG9kVjiA=; b=i3F5Dzj4B5oq6QKN6LzZvjhbrDVl2wqrUCWUr4Eji79vKCpD3cqjebZWLP7M0K7Pyz 0SsB4SVL45glGypuLl45o3ZpEJ0X7ews3tk/gxlROZOjI3eKD+y20Ue5LLuJEWUlkXMv GgIq3AJl6Abr16TMLRWFbJn1hvW0NcE8AMcf9qWhGcQfCO/5f4pDLnAPyRUOYL4c4xf3 IbAhZskocj2VNGrhejKWssZTO9F6F5MKpwFQnoTrEuWgpNYydG3/dH7FGHxpkI0xk5L6 SQSteppE0/AIUMCCp03uBykwysv3q0pBmOwlhSCYT+bU82o3AafoGOzTetp+8qUToUBt +JcA== X-Gm-Message-State: AOJu0YxhVQsUmEu8arGRNql5mJtu7OOTp7D3L10VSy0+m0oCrlavw33U DKgeXTzL9mljm5NdiYch+FEiLmnhAzM= X-Google-Smtp-Source: AGHT+IHAiqGG2CZaJruE1lNlAxnhiRC/F1x94UGSUpCWeH0bK/UFwCuObKzFu0tj/1yEKEAvFrjs3w== X-Received: by 2002:a05:6a21:a101:b0:171:8e16:ea86 with SMTP id aq1-20020a056a21a10100b001718e16ea86mr4174703pzc.31.1697631974217; Wed, 18 Oct 2023 05:26:14 -0700 (PDT) Received: from wedsonaf-dev.. ([2804:389:7122:43b8:9b73:6339:3351:cce0]) by smtp.googlemail.com with ESMTPSA id j1-20020a170902c3c100b001c736b0037fsm3411046plj.231.2023.10.18.05.26.09 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 18 Oct 2023 05:26:13 -0700 (PDT) From: Wedson Almeida Filho To: Alexander Viro , Christian Brauner , Matthew Wilcox Cc: Kent Overstreet , Greg Kroah-Hartman , linux-fsdevel@vger.kernel.org, rust-for-linux@vger.kernel.org, Wedson Almeida Filho Subject: [RFC PATCH 08/19] rust: fs: introduce `FileSystem::lookup` Date: Wed, 18 Oct 2023 09:25:07 -0300 Message-Id: <20231018122518.128049-9-wedsonaf@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20231018122518.128049-1-wedsonaf@gmail.com> References: <20231018122518.128049-1-wedsonaf@gmail.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM, RCVD_IN_DNSWL_BLOCKED,SPF_HELO_NONE,SPF_PASS,T_FILL_THIS_FORM_SHORT autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net From: Wedson Almeida Filho Allow Rust file systems to create inodes that are children of a directory inode when they're looked up by name. Signed-off-by: Wedson Almeida Filho --- rust/kernel/error.rs | 1 - rust/kernel/fs.rs | 65 +++++++++++++++++++++++++++++++++++++-- samples/rust/rust_rofs.rs | 25 +++++++++++++++ 3 files changed, 88 insertions(+), 3 deletions(-) diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs index e6d7ce46be55..484fa7c11de1 100644 --- a/rust/kernel/error.rs +++ b/rust/kernel/error.rs @@ -131,7 +131,6 @@ pub fn to_errno(self) -> core::ffi::c_int { } /// Returns the error encoded as a pointer. - #[allow(dead_code)] pub(crate) fn to_ptr(self) -> *mut T { // SAFETY: self.0 is a valid error due to its invariant. unsafe { bindings::ERR_PTR(self.0.into()) as *mut _ } diff --git a/rust/kernel/fs.rs b/rust/kernel/fs.rs index 89611c44e4c5..681fef8e3af1 100644 --- a/rust/kernel/fs.rs +++ b/rust/kernel/fs.rs @@ -33,6 +33,9 @@ pub trait FileSystem { /// /// [`DirEmitter::pos`] holds the current position of the directory reader. fn read_dir(inode: &INode, emitter: &mut DirEmitter) -> Result; + + /// Returns the inode corresponding to the directory entry with the given name. + fn lookup(parent: &INode, name: &[u8]) -> Result>>; } /// The types of directory entries reported by [`FileSystem::read_dir`]. @@ -226,8 +229,7 @@ pub fn init(self, params: INodeParams) -> Result>> { let mode = match params.typ { INodeType::Dir => { inode.__bindgen_anon_3.i_fop = &Tables::::DIR_FILE_OPERATIONS; - // SAFETY: `simple_dir_inode_operations` never changes, it's safe to reference it. - inode.i_op = unsafe { &bindings::simple_dir_inode_operations }; + inode.i_op = &Tables::::DIR_INODE_OPERATIONS; bindings::S_IFDIR } }; @@ -530,6 +532,62 @@ impl Tables { } }) } + + const DIR_INODE_OPERATIONS: bindings::inode_operations = bindings::inode_operations { + lookup: Some(Self::lookup_callback), + get_link: None, + permission: None, + get_inode_acl: None, + readlink: None, + create: None, + link: None, + unlink: None, + symlink: None, + mkdir: None, + rmdir: None, + mknod: None, + rename: None, + setattr: None, + getattr: None, + listxattr: None, + fiemap: None, + update_time: None, + atomic_open: None, + tmpfile: None, + get_acl: None, + set_acl: None, + fileattr_set: None, + fileattr_get: None, + get_offset_ctx: None, + }; + + extern "C" fn lookup_callback( + parent_ptr: *mut bindings::inode, + dentry: *mut bindings::dentry, + _flags: u32, + ) -> *mut bindings::dentry { + // SAFETY: The C API guarantees that `parent_ptr` is a valid inode. + let parent = unsafe { &*parent_ptr.cast::>() }; + + // SAFETY: The C API guarantees that `dentry` is valid for read. Since the name is + // immutable, it's ok to read its length directly. + let len = unsafe { (*dentry).d_name.__bindgen_anon_1.__bindgen_anon_1.len }; + let Ok(name_len) = usize::try_from(len) else { + return ENOENT.to_ptr(); + }; + + // SAFETY: The C API guarantees that `dentry` is valid for read. Since the name is + // immutable, it's ok to read it directly. + let name = unsafe { core::slice::from_raw_parts((*dentry).d_name.name, name_len) }; + match T::lookup(parent, name) { + Err(e) => e.to_ptr(), + // SAFETY: The returned inode is valid and referenced (by the type invariants), so + // it is ok to transfer this increment to `d_splice_alias`. + Ok(inode) => unsafe { + bindings::d_splice_alias(ManuallyDrop::new(inode).0.get(), dentry) + }, + } + } } /// Directory entry emitter. @@ -637,6 +695,9 @@ fn init(module: &'static ThisModule) -> impl PinInit { /// fn read_dir(_: &INode, _: &mut DirEmitter) -> Result { /// todo!() /// } +/// fn lookup(_: &INode, _: &[u8]) -> Result>> { +/// todo!() +/// } /// } /// # } /// ``` diff --git a/samples/rust/rust_rofs.rs b/samples/rust/rust_rofs.rs index 4e61a94afa70..4cc8525884a9 100644 --- a/samples/rust/rust_rofs.rs +++ b/samples/rust/rust_rofs.rs @@ -89,4 +89,29 @@ fn read_dir(inode: &INode, emitter: &mut DirEmitter) -> Result { Ok(()) } + + fn lookup(parent: &INode, name: &[u8]) -> Result>> { + if parent.ino() != 1 { + return Err(ENOENT); + } + + match name { + b"subdir" => match parent.super_block().get_or_create_inode(2)? { + Either::Left(existing) => Ok(existing), + Either::Right(new) => new.init(INodeParams { + typ: INodeType::Dir, + mode: 0o555, + size: 0, + blocks: 1, + nlink: 2, + uid: 0, + gid: 0, + atime: UNIX_EPOCH, + ctime: UNIX_EPOCH, + mtime: UNIX_EPOCH, + }), + }, + _ => Err(ENOENT), + } + } } From patchwork Wed Oct 18 12:25:08 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wedson Almeida Filho X-Patchwork-Id: 13426959 Received: from lindbergh.monkeyblade.net (lindbergh.monkeyblade.net [23.128.96.19]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 40A4D358A3; Wed, 18 Oct 2023 12:26:21 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="AaNZigKQ" Received: from mail-pl1-x62f.google.com (mail-pl1-x62f.google.com [IPv6:2607:f8b0:4864:20::62f]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 09305116; Wed, 18 Oct 2023 05:26:19 -0700 (PDT) Received: by mail-pl1-x62f.google.com with SMTP id d9443c01a7336-1caa371dcd8so7635195ad.0; Wed, 18 Oct 2023 05:26:19 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1697631978; x=1698236778; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=faga32+UB08pXXYjH8Xoi0IpDu5Cj+Cqa9Vnfplfui4=; b=AaNZigKQf5HquBYd488kxYjmfeFcFMHzz9CStJG4rpKiJu0jPR28F7F32EII7Pj24T 2ZPgR6lPvhEMQv5N1ybbO1CcJOKjvfL7QcWdEu6HzdxLiR8RGRPyJKWbLvauaMCUrkGm IohmL/SRP07u++ypscbywF20+PYkAX+1KCdVZj572fYH+QjA/EfkOmnHpWTTw7fwdL4r 8hS0nQXYsfo8Ych3389b2iCl0TlaS9IupQy9ToNJJoShTmesvPW3WPB0nAvKZrY5gbXs 23fGXcSaDzH18WdUys9SOJIgCRYpw+l5kIMNm4IygUHvviyK5LYfjLYBAeuGsd3bVHPw CM7Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1697631978; x=1698236778; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=faga32+UB08pXXYjH8Xoi0IpDu5Cj+Cqa9Vnfplfui4=; b=tfsWrCtZSaYU2aW03UTC5No0psmjosyLJCaqNnfUiowuSwJ4GFVyAaI4r4KyqMcfVs Cnk4x1nFXQ5OEHNvH+X54y1JUkgfEtNPaaE3Gueo++PM+rbsO0GJ5d5mNlbX7fvbz0Hl ZzfOYjVxGsGJfjHy18ahGGCkO+w2RjLmgjqJ8baEXCo00opBlL5sLEU/Gn7/p6DFRFe4 J0sDkto0ol+DSeJdvUcW4T5TM++IigTGlWhYcsnW6e3RV7YnVvnfs/25gHPe1q4svCDF hrpXxP4RML27+YyoOkX7gnByxAB5oHHRktU7nBplLzXmonJcXSTUQLcC0lF/UyYgppc5 0pJQ== X-Gm-Message-State: AOJu0Yymbz89TKYxmkViAAECPEhsSoX+RhqvDjkRZ/drSU1NKVCDwznv eKGbtXhfAfDRUuGoiujq7og= X-Google-Smtp-Source: AGHT+IGuqmopLtWEmMFS8GxV6IVwolnbr7D96FufPBfeXCR6T08KFASr57fpppBgMIzJ5gtM6lA6ag== X-Received: by 2002:a17:903:246:b0:1c7:5a63:43bb with SMTP id j6-20020a170903024600b001c75a6343bbmr6044847plh.8.1697631978312; Wed, 18 Oct 2023 05:26:18 -0700 (PDT) Received: from wedsonaf-dev.. ([2804:389:7122:43b8:9b73:6339:3351:cce0]) by smtp.googlemail.com with ESMTPSA id j1-20020a170902c3c100b001c736b0037fsm3411046plj.231.2023.10.18.05.26.14 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 18 Oct 2023 05:26:18 -0700 (PDT) From: Wedson Almeida Filho To: Alexander Viro , Christian Brauner , Matthew Wilcox Cc: Kent Overstreet , Greg Kroah-Hartman , linux-fsdevel@vger.kernel.org, rust-for-linux@vger.kernel.org, Wedson Almeida Filho Subject: [RFC PATCH 09/19] rust: folio: introduce basic support for folios Date: Wed, 18 Oct 2023 09:25:08 -0300 Message-Id: <20231018122518.128049-10-wedsonaf@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20231018122518.128049-1-wedsonaf@gmail.com> References: <20231018122518.128049-1-wedsonaf@gmail.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM, RCVD_IN_DNSWL_BLOCKED,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net From: Wedson Almeida Filho Allow Rust file systems to handle ref-counted folios. Provide the minimum needed to implement `read_folio` (part of `struct address_space_operations`) in read-only file systems and to read uncached blocks. Signed-off-by: Wedson Almeida Filho --- rust/bindings/bindings_helper.h | 3 + rust/bindings/lib.rs | 2 + rust/helpers.c | 81 ++++++++++++ rust/kernel/folio.rs | 215 ++++++++++++++++++++++++++++++++ rust/kernel/lib.rs | 1 + 5 files changed, 302 insertions(+) create mode 100644 rust/kernel/folio.rs diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index ca1898ce9527..53a99ea512d1 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -27,3 +28,5 @@ const slab_flags_t BINDINGS_SLAB_ACCOUNT = SLAB_ACCOUNT; const unsigned long BINDINGS_SB_RDONLY = SB_RDONLY; const loff_t BINDINGS_MAX_LFS_FILESIZE = MAX_LFS_FILESIZE; + +const size_t BINDINGS_PAGE_SIZE = PAGE_SIZE; diff --git a/rust/bindings/lib.rs b/rust/bindings/lib.rs index 426915d3fb57..a96b7f08e57d 100644 --- a/rust/bindings/lib.rs +++ b/rust/bindings/lib.rs @@ -59,3 +59,5 @@ mod bindings_helper { pub const SB_RDONLY: core::ffi::c_ulong = BINDINGS_SB_RDONLY; pub const MAX_LFS_FILESIZE: loff_t = BINDINGS_MAX_LFS_FILESIZE; + +pub const PAGE_SIZE: usize = BINDINGS_PAGE_SIZE; diff --git a/rust/helpers.c b/rust/helpers.c index c5a2bec6467d..f2ce3e7b688c 100644 --- a/rust/helpers.c +++ b/rust/helpers.c @@ -23,10 +23,14 @@ #include #include #include +#include #include #include #include +#include +#include #include +#include #include #include #include @@ -145,6 +149,77 @@ struct kunit *rust_helper_kunit_get_current_test(void) } EXPORT_SYMBOL_GPL(rust_helper_kunit_get_current_test); +void *rust_helper_kmap(struct page *page) +{ + return kmap(page); +} +EXPORT_SYMBOL_GPL(rust_helper_kmap); + +void rust_helper_kunmap(struct page *page) +{ + kunmap(page); +} +EXPORT_SYMBOL_GPL(rust_helper_kunmap); + +void rust_helper_folio_get(struct folio *folio) +{ + folio_get(folio); +} +EXPORT_SYMBOL_GPL(rust_helper_folio_get); + +void rust_helper_folio_put(struct folio *folio) +{ + folio_put(folio); +} +EXPORT_SYMBOL_GPL(rust_helper_folio_put); + +struct page *rust_helper_folio_page(struct folio *folio, size_t n) +{ + return folio_page(folio, n); +} + +loff_t rust_helper_folio_pos(struct folio *folio) +{ + return folio_pos(folio); +} +EXPORT_SYMBOL_GPL(rust_helper_folio_pos); + +size_t rust_helper_folio_size(struct folio *folio) +{ + return folio_size(folio); +} +EXPORT_SYMBOL_GPL(rust_helper_folio_size); + +void rust_helper_folio_mark_uptodate(struct folio *folio) +{ + folio_mark_uptodate(folio); +} +EXPORT_SYMBOL_GPL(rust_helper_folio_mark_uptodate); + +void rust_helper_folio_set_error(struct folio *folio) +{ + folio_set_error(folio); +} +EXPORT_SYMBOL_GPL(rust_helper_folio_set_error); + +void rust_helper_flush_dcache_folio(struct folio *folio) +{ + flush_dcache_folio(folio); +} +EXPORT_SYMBOL_GPL(rust_helper_flush_dcache_folio); + +void *rust_helper_kmap_local_folio(struct folio *folio, size_t offset) +{ + return kmap_local_folio(folio, offset); +} +EXPORT_SYMBOL_GPL(rust_helper_kmap_local_folio); + +void rust_helper_kunmap_local(const void *vaddr) +{ + kunmap_local(vaddr); +} +EXPORT_SYMBOL_GPL(rust_helper_kunmap_local); + void rust_helper_i_uid_write(struct inode *inode, uid_t uid) { i_uid_write(inode, uid); @@ -163,6 +238,12 @@ off_t rust_helper_i_size_read(const struct inode *inode) } EXPORT_SYMBOL_GPL(rust_helper_i_size_read); +void rust_helper_mapping_set_large_folios(struct address_space *mapping) +{ + mapping_set_large_folios(mapping); +} +EXPORT_SYMBOL_GPL(rust_helper_mapping_set_large_folios); + /* * `bindgen` binds the C `size_t` type as the Rust `usize` type, so we can * use it in contexts where Rust expects a `usize` like slice (array) indices. diff --git a/rust/kernel/folio.rs b/rust/kernel/folio.rs new file mode 100644 index 000000000000..ef8a08b97962 --- /dev/null +++ b/rust/kernel/folio.rs @@ -0,0 +1,215 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Groups of contiguous pages, folios. +//! +//! C headers: [`include/linux/mm.h`](../../include/linux/mm.h) + +use crate::error::{code::*, Result}; +use crate::types::{ARef, AlwaysRefCounted, Opaque, ScopeGuard}; +use core::{cmp::min, ptr}; + +/// Wraps the kernel's `struct folio`. +/// +/// # Invariants +/// +/// Instances of this type are always ref-counted, that is, a call to `folio_get` ensures that the +/// allocation remains valid at least until the matching call to `folio_put`. +#[repr(transparent)] +pub struct Folio(pub(crate) Opaque); + +// SAFETY: The type invariants guarantee that `Folio` is always ref-counted. +unsafe impl AlwaysRefCounted for Folio { + fn inc_ref(&self) { + // SAFETY: The existence of a shared reference means that the refcount is nonzero. + unsafe { bindings::folio_get(self.0.get()) }; + } + + unsafe fn dec_ref(obj: ptr::NonNull) { + // SAFETY: The safety requirements guarantee that the refcount is nonzero. + unsafe { bindings::folio_put(obj.cast().as_ptr()) } + } +} + +impl Folio { + /// Tries to allocate a new folio. + /// + /// On success, returns a folio made up of 2^order pages. + pub fn try_new(order: u32) -> Result { + if order > bindings::MAX_ORDER { + return Err(EDOM); + } + + // SAFETY: We checked that `order` is within the max allowed value. + let f = ptr::NonNull::new(unsafe { bindings::folio_alloc(bindings::GFP_KERNEL, order) }) + .ok_or(ENOMEM)?; + + // SAFETY: The folio returned by `folio_alloc` is referenced. The ownership of the + // reference is transferred to the `ARef` instance. + Ok(UniqueFolio(unsafe { ARef::from_raw(f.cast()) })) + } + + /// Returns the byte position of this folio in its file. + pub fn pos(&self) -> i64 { + // SAFETY: The folio is valid because the shared reference implies a non-zero refcount. + unsafe { bindings::folio_pos(self.0.get()) } + } + + /// Returns the byte size of this folio. + pub fn size(&self) -> usize { + // SAFETY: The folio is valid because the shared reference implies a non-zero refcount. + unsafe { bindings::folio_size(self.0.get()) } + } + + /// Flushes the data cache for the pages that make up the folio. + pub fn flush_dcache(&self) { + // SAFETY: The folio is valid because the shared reference implies a non-zero refcount. + unsafe { bindings::flush_dcache_folio(self.0.get()) } + } +} + +/// A [`Folio`] that has a single reference to it. +pub struct UniqueFolio(pub(crate) ARef); + +impl UniqueFolio { + /// Maps the contents of a folio page into a slice. + pub fn map_page(&self, page_index: usize) -> Result> { + if page_index >= self.0.size() / bindings::PAGE_SIZE { + return Err(EDOM); + } + + // SAFETY: We just checked that the index is within bounds of the folio. + let page = unsafe { bindings::folio_page(self.0 .0.get(), page_index) }; + + // SAFETY: `page` is valid because it was returned by `folio_page` above. + let ptr = unsafe { bindings::kmap(page) }; + + // SAFETY: We just mapped `ptr`, so it's valid for read. + let data = unsafe { core::slice::from_raw_parts(ptr.cast::(), bindings::PAGE_SIZE) }; + + Ok(MapGuard { data, page }) + } +} + +/// A mapped [`UniqueFolio`]. +pub struct MapGuard<'a> { + data: &'a [u8], + page: *mut bindings::page, +} + +impl core::ops::Deref for MapGuard<'_> { + type Target = [u8]; + + fn deref(&self) -> &Self::Target { + self.data + } +} + +impl Drop for MapGuard<'_> { + fn drop(&mut self) { + // SAFETY: A `MapGuard` instance is only created when `kmap` succeeds, so it's ok to unmap + // it when the guard is dropped. + unsafe { bindings::kunmap(self.page) }; + } +} + +/// A locked [`Folio`]. +pub struct LockedFolio<'a>(&'a Folio); + +impl LockedFolio<'_> { + /// Creates a new locked folio from a raw pointer. + /// + /// # Safety + /// + /// Callers must ensure that the folio is valid and locked. Additionally, that the + /// responsibility of unlocking is transferred to the new instance of [`LockedFolio`]. Lastly, + /// that the returned [`LockedFolio`] doesn't outlive the refcount that keeps it alive. + #[allow(dead_code)] + pub(crate) unsafe fn from_raw(folio: *const bindings::folio) -> Self { + let ptr = folio.cast(); + // SAFETY: The safety requirements ensure that `folio` (from which `ptr` is derived) is + // valid and will remain valid while the `LockedFolio` instance lives. + Self(unsafe { &*ptr }) + } + + /// Marks the folio as being up to date. + pub fn mark_uptodate(&mut self) { + // SAFETY: The folio is valid because the shared reference implies a non-zero refcount. + unsafe { bindings::folio_mark_uptodate(self.0 .0.get()) } + } + + /// Sets the error flag on the folio. + pub fn set_error(&mut self) { + // SAFETY: The folio is valid because the shared reference implies a non-zero refcount. + unsafe { bindings::folio_set_error(self.0 .0.get()) } + } + + fn for_each_page( + &mut self, + offset: usize, + len: usize, + mut cb: impl FnMut(&mut [u8]) -> Result, + ) -> Result { + let mut remaining = len; + let mut next_offset = offset; + + // Check that we don't overflow the folio. + let end = offset.checked_add(len).ok_or(EDOM)?; + if end > self.size() { + return Err(EINVAL); + } + + while remaining > 0 { + let page_offset = next_offset & (bindings::PAGE_SIZE - 1); + let usable = min(remaining, bindings::PAGE_SIZE - page_offset); + // SAFETY: The folio is valid because the shared reference implies a non-zero refcount; + // `next_offset` is also guaranteed be lesss than the folio size. + let ptr = unsafe { bindings::kmap_local_folio(self.0 .0.get(), next_offset) }; + + // SAFETY: `ptr` was just returned by the `kmap_local_folio` above. + let _guard = ScopeGuard::new(|| unsafe { bindings::kunmap_local(ptr) }); + + // SAFETY: `kmap_local_folio` maps whole page so we know it's mapped for at least + // `usable` bytes. + let s = unsafe { core::slice::from_raw_parts_mut(ptr.cast::(), usable) }; + cb(s)?; + + next_offset += usable; + remaining -= usable; + } + + Ok(()) + } + + /// Writes the given slice into the folio. + pub fn write(&mut self, offset: usize, data: &[u8]) -> Result { + let mut remaining = data; + + self.for_each_page(offset, data.len(), |s| { + s.copy_from_slice(&remaining[..s.len()]); + remaining = &remaining[s.len()..]; + Ok(()) + }) + } + + /// Writes zeroes into the folio. + pub fn zero_out(&mut self, offset: usize, len: usize) -> Result { + self.for_each_page(offset, len, |s| { + s.fill(0); + Ok(()) + }) + } +} + +impl core::ops::Deref for LockedFolio<'_> { + type Target = Folio; + fn deref(&self) -> &Self::Target { + self.0 + } +} + +impl Drop for LockedFolio<'_> { + fn drop(&mut self) { + // SAFETY: The folio is valid because the shared reference implies a non-zero refcount. + unsafe { bindings::folio_unlock(self.0 .0.get()) } + } +} diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 00059b80c240..0e85b380da64 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -34,6 +34,7 @@ mod allocator; mod build_assert; pub mod error; +pub mod folio; pub mod fs; pub mod init; pub mod ioctl; From patchwork Wed Oct 18 12:25:09 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wedson Almeida Filho X-Patchwork-Id: 13426960 Received: from lindbergh.monkeyblade.net (lindbergh.monkeyblade.net [23.128.96.19]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5DA351DDFC; Wed, 18 Oct 2023 12:26:25 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="fd3rpBty" Received: from mail-pl1-x635.google.com (mail-pl1-x635.google.com [IPv6:2607:f8b0:4864:20::635]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 8F052112; Wed, 18 Oct 2023 05:26:23 -0700 (PDT) Received: by mail-pl1-x635.google.com with SMTP id d9443c01a7336-1ca6809fb8aso22946905ad.1; Wed, 18 Oct 2023 05:26:23 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1697631983; x=1698236783; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=Tw7Bt710S9wSgq7lYwmiNaJcrZn0u8YLmYwj+jhFGJE=; b=fd3rpBty1UIm35fgsgMx0xcQzlrZ/kpHW7btmTFnsF6238b0yXSm5GJN52BwYC2Xj3 5AhspMX8cKDRm/ALYmNtPa9nFVhmoEqbpqSYpeu/9I+fTIVYlkGGrVI6+PKNIEOyoTTM gsVwT6pxN5UygXVyLIVQDp4ehlq/Qhr22EzmC/3iTVEKQpE8AsL8g3MXBUEWsddk+9SM DDj3QIw/iGLNByhW6+Rf8JLc9avaEcKp1Ei3WCmZUeFLdKLtSOiI7g6lLLM9czeu2cv5 ciw2a/iwmhkf2N4rzT39NCl/FOXjX9FZjnyt0/aqyJmDAZlSqCqaq4pk5mzOVQb55dfm Epfg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1697631983; x=1698236783; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=Tw7Bt710S9wSgq7lYwmiNaJcrZn0u8YLmYwj+jhFGJE=; b=ZytczmKWgTbxAc37wN3BwY69B1GW3EPTQLdHrBwzFNoCsIt8aRPWgQWLmmfABHweAN cWEHqC1vaEbVvXZqJ6O7u5BktqzzRcjj0clplXpFBkw388aqQzkoMWZBnBPQ1mdttg0/ yJQHgdZM41DSrtBoFSptdyP2fh1KRtlXQdhlTCB6tw1rM8nGyrCGb0903iOeQbhQyi02 WbH1g27wQ3IoTXkbDOCZBqq0vq2HmJSDHz8d+L95PAwT9e9YPrUvtX1fxCVqLTjRijyJ MVk6V7U3/oouJDjMBIm0pHc/7TbIn/mOTonOLi+A/4RHx+XueR4TCgN5bi5IapvurKo+ F6aA== X-Gm-Message-State: AOJu0YyWQWYvSgW+Y5a64tl6ov/Ap3hDviEZrQiNSZa8Ht5iVIRzO7Jo o1PokKAoZbHKJRZYimeHbjc= X-Google-Smtp-Source: AGHT+IHX6mepXTgcyTGX7wAyKHqMQZGOk55mDoa3YY/X49KACM8npsQb+3j1IqlhtuYu4pPtY0SJRQ== X-Received: by 2002:a17:903:32d0:b0:1c9:e0f9:a676 with SMTP id i16-20020a17090332d000b001c9e0f9a676mr4846526plr.6.1697631982965; Wed, 18 Oct 2023 05:26:22 -0700 (PDT) Received: from wedsonaf-dev.. ([2804:389:7122:43b8:9b73:6339:3351:cce0]) by smtp.googlemail.com with ESMTPSA id j1-20020a170902c3c100b001c736b0037fsm3411046plj.231.2023.10.18.05.26.18 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 18 Oct 2023 05:26:22 -0700 (PDT) From: Wedson Almeida Filho To: Alexander Viro , Christian Brauner , Matthew Wilcox Cc: Kent Overstreet , Greg Kroah-Hartman , linux-fsdevel@vger.kernel.org, rust-for-linux@vger.kernel.org, Wedson Almeida Filho Subject: [RFC PATCH 10/19] rust: fs: introduce `FileSystem::read_folio` Date: Wed, 18 Oct 2023 09:25:09 -0300 Message-Id: <20231018122518.128049-11-wedsonaf@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20231018122518.128049-1-wedsonaf@gmail.com> References: <20231018122518.128049-1-wedsonaf@gmail.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM, RCVD_IN_DNSWL_BLOCKED,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net From: Wedson Almeida Filho Allow Rust file systems to create regular file inodes backed by the page cache. The contents of such files are read into folios via `read_folio`. Signed-off-by: Wedson Almeida Filho --- rust/kernel/folio.rs | 1 - rust/kernel/fs.rs | 75 +++++++++++++++++++++++++++++++++++++-- samples/rust/rust_rofs.rs | 69 ++++++++++++++++++++++++----------- 3 files changed, 122 insertions(+), 23 deletions(-) diff --git a/rust/kernel/folio.rs b/rust/kernel/folio.rs index ef8a08b97962..b7f80291b0e1 100644 --- a/rust/kernel/folio.rs +++ b/rust/kernel/folio.rs @@ -123,7 +123,6 @@ impl LockedFolio<'_> { /// Callers must ensure that the folio is valid and locked. Additionally, that the /// responsibility of unlocking is transferred to the new instance of [`LockedFolio`]. Lastly, /// that the returned [`LockedFolio`] doesn't outlive the refcount that keeps it alive. - #[allow(dead_code)] pub(crate) unsafe fn from_raw(folio: *const bindings::folio) -> Self { let ptr = folio.cast(); // SAFETY: The safety requirements ensure that `folio` (from which `ptr` is derived) is diff --git a/rust/kernel/fs.rs b/rust/kernel/fs.rs index 681fef8e3af1..ee3dce87032b 100644 --- a/rust/kernel/fs.rs +++ b/rust/kernel/fs.rs @@ -8,7 +8,10 @@ use crate::error::{code::*, from_result, to_result, Error, Result}; use crate::types::{ARef, AlwaysRefCounted, Either, Opaque}; -use crate::{bindings, init::PinInit, str::CStr, time::Timespec, try_pin_init, ThisModule}; +use crate::{ + bindings, folio::LockedFolio, init::PinInit, str::CStr, time::Timespec, try_pin_init, + ThisModule, +}; use core::{marker::PhantomData, marker::PhantomPinned, mem::ManuallyDrop, pin::Pin, ptr}; use macros::{pin_data, pinned_drop}; @@ -36,6 +39,9 @@ pub trait FileSystem { /// Returns the inode corresponding to the directory entry with the given name. fn lookup(parent: &INode, name: &[u8]) -> Result>>; + + /// Reads the contents of the inode into the given folio. + fn read_folio(inode: &INode, folio: LockedFolio<'_>) -> Result; } /// The types of directory entries reported by [`FileSystem::read_dir`]. @@ -74,6 +80,7 @@ impl From for DirEntryType { fn from(value: INodeType) -> Self { match value { INodeType::Dir => DirEntryType::Dir, + INodeType::Reg => DirEntryType::Reg, } } } @@ -232,6 +239,15 @@ pub fn init(self, params: INodeParams) -> Result>> { inode.i_op = &Tables::::DIR_INODE_OPERATIONS; bindings::S_IFDIR } + INodeType::Reg => { + // SAFETY: `generic_ro_fops` never changes, it's safe to reference it. + inode.__bindgen_anon_3.i_fop = unsafe { &bindings::generic_ro_fops }; + inode.i_data.a_ops = &Tables::::FILE_ADDRESS_SPACE_OPERATIONS; + + // SAFETY: The `i_mapping` pointer doesn't change and is valid. + unsafe { bindings::mapping_set_large_folios(inode.i_mapping) }; + bindings::S_IFREG + } }; inode.i_mode = (params.mode & 0o777) | u16::try_from(mode)?; @@ -268,6 +284,9 @@ fn drop(&mut self) { pub enum INodeType { /// Directory type. Dir, + + /// Regular file type. + Reg, } /// Required inode parameters. @@ -588,6 +607,55 @@ extern "C" fn lookup_callback( }, } } + + const FILE_ADDRESS_SPACE_OPERATIONS: bindings::address_space_operations = + bindings::address_space_operations { + writepage: None, + read_folio: Some(Self::read_folio_callback), + writepages: None, + dirty_folio: None, + readahead: None, + write_begin: None, + write_end: None, + bmap: None, + invalidate_folio: None, + release_folio: None, + free_folio: None, + direct_IO: None, + migrate_folio: None, + launder_folio: None, + is_partially_uptodate: None, + is_dirty_writeback: None, + error_remove_page: None, + swap_activate: None, + swap_deactivate: None, + swap_rw: None, + }; + + extern "C" fn read_folio_callback( + _file: *mut bindings::file, + folio: *mut bindings::folio, + ) -> i32 { + from_result(|| { + // SAFETY: All pointers are valid and stable. + let inode = unsafe { + &*(*(*folio) + .__bindgen_anon_1 + .page + .__bindgen_anon_1 + .__bindgen_anon_1 + .mapping) + .host + .cast::>() + }; + + // SAFETY: The C contract guarantees that the folio is valid and locked, with ownership + // of the lock transferred to the callee (this function). The folio is also guaranteed + // not to outlive this function. + T::read_folio(inode, unsafe { LockedFolio::from_raw(folio) })?; + Ok(0) + }) + } } /// Directory entry emitter. @@ -673,7 +741,7 @@ fn init(module: &'static ThisModule) -> impl PinInit { /// # mod module_fs_sample { /// use kernel::fs::{DirEmitter, INode, NewSuperBlock, SuperBlock, SuperParams}; /// use kernel::prelude::*; -/// use kernel::{c_str, fs, types::ARef}; +/// use kernel::{c_str, folio::LockedFolio, fs, types::ARef}; /// /// kernel::module_fs! { /// type: MyFs, @@ -698,6 +766,9 @@ fn init(module: &'static ThisModule) -> impl PinInit { /// fn lookup(_: &INode, _: &[u8]) -> Result>> { /// todo!() /// } +/// fn read_folio(_: &INode, _: LockedFolio<'_>) -> Result { +/// todo!() +/// } /// } /// # } /// ``` diff --git a/samples/rust/rust_rofs.rs b/samples/rust/rust_rofs.rs index 4cc8525884a9..ef651ad38185 100644 --- a/samples/rust/rust_rofs.rs +++ b/samples/rust/rust_rofs.rs @@ -6,7 +6,7 @@ DirEmitter, INode, INodeParams, INodeType, NewSuperBlock, SuperBlock, SuperParams, }; use kernel::prelude::*; -use kernel::{c_str, fs, time::UNIX_EPOCH, types::ARef, types::Either}; +use kernel::{c_str, folio::LockedFolio, fs, time::UNIX_EPOCH, types::ARef, types::Either}; kernel::module_fs! { type: RoFs, @@ -20,6 +20,7 @@ struct Entry { name: &'static [u8], ino: u64, etype: INodeType, + contents: &'static [u8], } const ENTRIES: [Entry; 3] = [ @@ -27,16 +28,19 @@ struct Entry { name: b".", ino: 1, etype: INodeType::Dir, + contents: b"", }, Entry { name: b"..", ino: 1, etype: INodeType::Dir, + contents: b"", }, Entry { - name: b"subdir", + name: b"test.txt", ino: 2, - etype: INodeType::Dir, + etype: INodeType::Reg, + contents: b"hello\n", }, ]; @@ -95,23 +99,48 @@ fn lookup(parent: &INode, name: &[u8]) -> Result>> { return Err(ENOENT); } - match name { - b"subdir" => match parent.super_block().get_or_create_inode(2)? { - Either::Left(existing) => Ok(existing), - Either::Right(new) => new.init(INodeParams { - typ: INodeType::Dir, - mode: 0o555, - size: 0, - blocks: 1, - nlink: 2, - uid: 0, - gid: 0, - atime: UNIX_EPOCH, - ctime: UNIX_EPOCH, - mtime: UNIX_EPOCH, - }), - }, - _ => Err(ENOENT), + for e in &ENTRIES { + if name == e.name { + return match parent.super_block().get_or_create_inode(e.ino)? { + Either::Left(existing) => Ok(existing), + Either::Right(new) => new.init(INodeParams { + typ: e.etype, + mode: 0o444, + size: e.contents.len().try_into()?, + blocks: 1, + nlink: 1, + uid: 0, + gid: 0, + atime: UNIX_EPOCH, + ctime: UNIX_EPOCH, + mtime: UNIX_EPOCH, + }), + }; + } } + + Err(ENOENT) + } + + fn read_folio(inode: &INode, mut folio: LockedFolio<'_>) -> Result { + let data = match inode.ino() { + 2 => ENTRIES[2].contents, + _ => return Err(EINVAL), + }; + + let pos = usize::try_from(folio.pos()).unwrap_or(usize::MAX); + let copied = if pos >= data.len() { + 0 + } else { + let to_copy = core::cmp::min(data.len() - pos, folio.size()); + folio.write(0, &data[pos..][..to_copy])?; + to_copy + }; + + folio.zero_out(copied, folio.size() - copied)?; + folio.mark_uptodate(); + folio.flush_dcache(); + + Ok(()) } } From patchwork Wed Oct 18 12:25:10 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wedson Almeida Filho X-Patchwork-Id: 13426961 Received: from lindbergh.monkeyblade.net (lindbergh.monkeyblade.net [23.128.96.19]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4864F2E3E2; Wed, 18 Oct 2023 12:26:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="l6H2IcdA" Received: from mail-pl1-x630.google.com (mail-pl1-x630.google.com [IPv6:2607:f8b0:4864:20::630]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id E6AA0116; Wed, 18 Oct 2023 05:26:27 -0700 (PDT) Received: by mail-pl1-x630.google.com with SMTP id d9443c01a7336-1c5cd27b1acso56935005ad.2; Wed, 18 Oct 2023 05:26:27 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1697631987; x=1698236787; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=TOXJ6HCqnTLSNdE+9pmC2+R+Ar90JbQvM+mVG2JfMR8=; b=l6H2IcdAtcJ5znLozguRdMvBzON4CR8fO4yVgukzlvZTOqFqDStDjq0MFLFMQqdv3K W4P+ndjG2Eq4oI1iWBJjNwUxLQPSmmuxOqHX+O4z9IdkPGpsRykCPGswpftGTLAvrwho toJt9Iue9nX+LXiB7XRvitW4mksINFhJzt+/VIr644rIi860MALp83so+x9XO2I9D2IK hUa7hLaeCaxLP+nOLxArg+WE0qEgqY53/+ze+zyjhQU4WOPaGVe2x373YLeY3ILHsxHj qMq2r0OsrfMg32JWIqsdxAk5fJ+79EqNuLRExd5AwoYwWJ7b4775p2XoKLF3CRbLFZBE lSlA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1697631987; x=1698236787; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=TOXJ6HCqnTLSNdE+9pmC2+R+Ar90JbQvM+mVG2JfMR8=; b=BZoK0QpVmIDxwE1uE5vSZTk+BiflnOw16NbAQ8R+JQLdYDCbXfmnH7/rgCJFe3nc7c e6bKLM/ryg+M3CjbYBhbJmufiMJD5QsOoIBTFhpwAlEEoY8OwO8PPjq+GqYNi1WvQ+I4 Vc0l6jMel+rgCPYJ9F3LC3vNFfyXZ/bIJI8YBGWO665PrVs5h/v1q9zGvienTtCBHF7z RSF6CtBqPXBUibK3cJU0cQozdCzZfU9lNJtlekdctpfynYGfvP8c4pwrnE98+aGO5qu4 LGehAckalwSi5hPz5TMR7Ghk9IeqgLPuelGVHvZhwVVr8Wt6oCQBTFxN2v6jq9m9yTUa HK4w== X-Gm-Message-State: AOJu0YxVfVQKPbcxttax33hKk2UqerEZ+Rut4fw/eTfhYn5DUxvKiRct wwKR97Ffv8GqPAgWJ3bc5h0= X-Google-Smtp-Source: AGHT+IHmI+V3WZWkIG5bC05LPMNEeVYGYnLJAfVX+xOxPrJPBJdygDxXPnb/BwmwGl65cxUCLX/UBQ== X-Received: by 2002:a17:902:cf47:b0:1bb:9f07:5e0 with SMTP id e7-20020a170902cf4700b001bb9f0705e0mr4697508plg.60.1697631987295; Wed, 18 Oct 2023 05:26:27 -0700 (PDT) Received: from wedsonaf-dev.. ([2804:389:7122:43b8:9b73:6339:3351:cce0]) by smtp.googlemail.com with ESMTPSA id j1-20020a170902c3c100b001c736b0037fsm3411046plj.231.2023.10.18.05.26.23 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 18 Oct 2023 05:26:27 -0700 (PDT) From: Wedson Almeida Filho To: Alexander Viro , Christian Brauner , Matthew Wilcox Cc: Kent Overstreet , Greg Kroah-Hartman , linux-fsdevel@vger.kernel.org, rust-for-linux@vger.kernel.org, Wedson Almeida Filho Subject: [RFC PATCH 11/19] rust: fs: introduce `FileSystem::read_xattr` Date: Wed, 18 Oct 2023 09:25:10 -0300 Message-Id: <20231018122518.128049-12-wedsonaf@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20231018122518.128049-1-wedsonaf@gmail.com> References: <20231018122518.128049-1-wedsonaf@gmail.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM, RCVD_IN_DNSWL_NONE,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net From: Wedson Almeida Filho Allow Rust file systems to expose xattrs associated with inodes. `overlayfs` uses an xattr to indicate that a directory is opaque (i.e., that lower layers should not be looked up). The planned file systems need to support opaque directories, so they must be able to implement this. Signed-off-by: Wedson Almeida Filho --- rust/bindings/bindings_helper.h | 1 + rust/kernel/error.rs | 2 ++ rust/kernel/fs.rs | 43 +++++++++++++++++++++++++++++++++ 3 files changed, 46 insertions(+) diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index 53a99ea512d1..fa754c5e85a2 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -15,6 +15,7 @@ #include #include #include +#include /* `bindgen` gets confused at certain things. */ const size_t BINDINGS_ARCH_SLAB_MINALIGN = ARCH_SLAB_MINALIGN; diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs index 484fa7c11de1..6c167583b275 100644 --- a/rust/kernel/error.rs +++ b/rust/kernel/error.rs @@ -81,6 +81,8 @@ macro_rules! declare_err { declare_err!(EIOCBQUEUED, "iocb queued, will get completion event."); declare_err!(ERECALLCONFLICT, "Conflict with recalled state."); declare_err!(ENOGRACE, "NFS file lock reclaim refused."); + declare_err!(ENODATA, "No data available."); + declare_err!(EOPNOTSUPP, "Operation not supported on transport endpoint."); } /// Generic integer kernel error. diff --git a/rust/kernel/fs.rs b/rust/kernel/fs.rs index ee3dce87032b..adf9cbee16d2 100644 --- a/rust/kernel/fs.rs +++ b/rust/kernel/fs.rs @@ -42,6 +42,14 @@ pub trait FileSystem { /// Reads the contents of the inode into the given folio. fn read_folio(inode: &INode, folio: LockedFolio<'_>) -> Result; + + /// Reads an xattr. + /// + /// Returns the number of bytes written to `outbuf`. If it is too small, returns the number of + /// bytes needs to hold the attribute. + fn read_xattr(_inode: &INode, _name: &CStr, _outbuf: &mut [u8]) -> Result { + Err(EOPNOTSUPP) + } } /// The types of directory entries reported by [`FileSystem::read_dir`]. @@ -418,6 +426,7 @@ impl Tables { sb.0.s_magic = params.magic as _; sb.0.s_op = &Tables::::SUPER_BLOCK; + sb.0.s_xattr = &Tables::::XATTR_HANDLERS[0]; sb.0.s_maxbytes = params.maxbytes; sb.0.s_time_gran = params.time_gran; sb.0.s_blocksize_bits = params.blocksize_bits; @@ -487,6 +496,40 @@ impl Tables { shutdown: None, }; + const XATTR_HANDLERS: [*const bindings::xattr_handler; 2] = [&Self::XATTR_HANDLER, ptr::null()]; + + const XATTR_HANDLER: bindings::xattr_handler = bindings::xattr_handler { + name: ptr::null(), + prefix: crate::c_str!("").as_char_ptr(), + flags: 0, + list: None, + get: Some(Self::xattr_get_callback), + set: None, + }; + + unsafe extern "C" fn xattr_get_callback( + _handler: *const bindings::xattr_handler, + _dentry: *mut bindings::dentry, + inode_ptr: *mut bindings::inode, + name: *const core::ffi::c_char, + buffer: *mut core::ffi::c_void, + size: usize, + ) -> core::ffi::c_int { + from_result(|| { + // SAFETY: The C API guarantees that `inode_ptr` is a valid inode. + let inode = unsafe { &*inode_ptr.cast::>() }; + + // SAFETY: The c API guarantees that `name` is a valid null-terminated string. It + // also guarantees that it's valid for the duration of the callback. + let name = unsafe { CStr::from_char_ptr(name) }; + + // SAFETY: The C API guarantees that `buffer` is at least `size` bytes in length. + let buf = unsafe { core::slice::from_raw_parts_mut(buffer.cast(), size) }; + let len = T::read_xattr(inode, name, buf)?; + Ok(len.try_into()?) + }) + } + const DIR_FILE_OPERATIONS: bindings::file_operations = bindings::file_operations { owner: ptr::null_mut(), llseek: Some(bindings::generic_file_llseek), From patchwork Wed Oct 18 12:25:11 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wedson Almeida Filho X-Patchwork-Id: 13426962 Received: from lindbergh.monkeyblade.net (lindbergh.monkeyblade.net [23.128.96.19]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9603A2E3E2; Wed, 18 Oct 2023 12:26:33 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="Ir5ojnam" Received: from mail-pl1-x62b.google.com (mail-pl1-x62b.google.com [IPv6:2607:f8b0:4864:20::62b]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 70F0310F; Wed, 18 Oct 2023 05:26:32 -0700 (PDT) Received: by mail-pl1-x62b.google.com with SMTP id d9443c01a7336-1c9bf22fe05so44152525ad.2; Wed, 18 Oct 2023 05:26:32 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1697631992; x=1698236792; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=g3z7jtZCawQRFiOqSOKhrwT4h1/aQJhIrBv9urAW4S4=; b=Ir5ojnamww0a1FwJtVQJWO9nDqS7BjirzTfs62xLJ+aVnWhNnWQnKYOTjjtftfZND2 WFJv3Gq+VqqsAanHjlZmOyFnMpaU0sGmVxsFDFwa6lrSdMonOEqHQwmK5bcEHaMe++Uu wyWd+JPXBzCBI9TymePqW1MCqVXWEcGnJlb08iH8q9jK/+IVKpFsf/gL4dnOQ8ykjwtE YM+k4mLHUJqwfWjuUIU/Zm6jjZ0K3cCNfTV4pGYy18EOZ/9Gtn6SmHltLMaj7hiNym7q +21QVlAzUlg/xpiYj9YsjdIXno/G36Vs12JStEPEuj6OFZSSnm19tqiQrCobtndT17R/ TKGA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1697631992; x=1698236792; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=g3z7jtZCawQRFiOqSOKhrwT4h1/aQJhIrBv9urAW4S4=; b=a8QF4frNjHmg7TlEJTZg/MM8ytASGu3+ElZJjClQu5w+bQzcHgKZr0R3/21yFeYEqj tarCEaySyJrMbXZedgI53yvkWY9wr5QpMffCD6Mieep8s0mFY2pkNVZREUgKmwzKBqkN x9yiQH6JbiWjG5rtuwZp3Pt7NVZgNKDSy2jt3wIh/4JLL+3FUHiUcqx4du+s0u1LqXH/ svJFwypUb4mX3pJeiqxynXrwuBtX+uUyGco2ogQJmk5bck25M7FTlsrw2AnypHG57qmL U0hprCkWpFv36MD83nEbvRHY1JpihdYDJ24NwXReQ5TlgiPGwZsskxN5/viePpApiBvQ zizw== X-Gm-Message-State: AOJu0YwYDH6cb3JrSzDin+ua9XiPhMvpLDDZtmYt/oFKYl1UIhbkq70k tlM8UanjZ98CcYqjoie3W7M= X-Google-Smtp-Source: AGHT+IH8rNj3uMZrih2JbOxA4Yw21v1GLCW55MULU2mj7HkocA8Pcp2kr5/60c3qf0iEX7aGhDHoTg== X-Received: by 2002:a17:902:c412:b0:1c3:1f0c:fb82 with SMTP id k18-20020a170902c41200b001c31f0cfb82mr5567108plk.41.1697631991812; Wed, 18 Oct 2023 05:26:31 -0700 (PDT) Received: from wedsonaf-dev.. ([2804:389:7122:43b8:9b73:6339:3351:cce0]) by smtp.googlemail.com with ESMTPSA id j1-20020a170902c3c100b001c736b0037fsm3411046plj.231.2023.10.18.05.26.27 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 18 Oct 2023 05:26:31 -0700 (PDT) From: Wedson Almeida Filho To: Alexander Viro , Christian Brauner , Matthew Wilcox Cc: Kent Overstreet , Greg Kroah-Hartman , linux-fsdevel@vger.kernel.org, rust-for-linux@vger.kernel.org, Wedson Almeida Filho Subject: [RFC PATCH 12/19] rust: fs: introduce `FileSystem::statfs` Date: Wed, 18 Oct 2023 09:25:11 -0300 Message-Id: <20231018122518.128049-13-wedsonaf@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20231018122518.128049-1-wedsonaf@gmail.com> References: <20231018122518.128049-1-wedsonaf@gmail.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM, RCVD_IN_DNSWL_NONE,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net From: Wedson Almeida Filho Allow Rust file systems to expose their stats. `overlayfs` requires that this be implemented by all file systems that are part of an overlay. The planned file systems need to be overlayed with overlayfs, so they must be able to implement this. Signed-off-by: Wedson Almeida Filho --- rust/bindings/bindings_helper.h | 1 + rust/kernel/error.rs | 1 + rust/kernel/fs.rs | 52 ++++++++++++++++++++++++++++++++- 3 files changed, 53 insertions(+), 1 deletion(-) diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index fa754c5e85a2..e2b2ccc835e3 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs index 6c167583b275..829756cf6c48 100644 --- a/rust/kernel/error.rs +++ b/rust/kernel/error.rs @@ -83,6 +83,7 @@ macro_rules! declare_err { declare_err!(ENOGRACE, "NFS file lock reclaim refused."); declare_err!(ENODATA, "No data available."); declare_err!(EOPNOTSUPP, "Operation not supported on transport endpoint."); + declare_err!(ENOSYS, "Invalid system call number."); } /// Generic integer kernel error. diff --git a/rust/kernel/fs.rs b/rust/kernel/fs.rs index adf9cbee16d2..8f34da50e694 100644 --- a/rust/kernel/fs.rs +++ b/rust/kernel/fs.rs @@ -50,6 +50,31 @@ pub trait FileSystem { fn read_xattr(_inode: &INode, _name: &CStr, _outbuf: &mut [u8]) -> Result { Err(EOPNOTSUPP) } + + /// Get filesystem statistics. + fn statfs(_sb: &SuperBlock) -> Result { + Err(ENOSYS) + } +} + +/// File system stats. +/// +/// A subset of C's `kstatfs`. +pub struct Stat { + /// Magic number of the file system. + pub magic: u32, + + /// The maximum length of a file name. + pub namelen: i64, + + /// Block size. + pub bsize: i64, + + /// Number of files in the file system. + pub files: u64, + + /// Number of blocks in the file system. + pub blocks: u64, } /// The types of directory entries reported by [`FileSystem::read_dir`]. @@ -478,7 +503,7 @@ impl Tables { freeze_fs: None, thaw_super: None, unfreeze_fs: None, - statfs: None, + statfs: Some(Self::statfs_callback), remount_fs: None, umount_begin: None, show_options: None, @@ -496,6 +521,31 @@ impl Tables { shutdown: None, }; + unsafe extern "C" fn statfs_callback( + dentry: *mut bindings::dentry, + buf: *mut bindings::kstatfs, + ) -> core::ffi::c_int { + from_result(|| { + // SAFETY: The C API guarantees that `dentry` is valid for read. `d_sb` is + // immutable, so it's safe to read it. The superblock is guaranteed to be valid dor + // the duration of the call. + let sb = unsafe { &*(*dentry).d_sb.cast::>() }; + let s = T::statfs(sb)?; + + // SAFETY: The C API guarantees that `buf` is valid for read and write. + let buf = unsafe { &mut *buf }; + buf.f_type = s.magic.into(); + buf.f_namelen = s.namelen; + buf.f_bsize = s.bsize; + buf.f_files = s.files; + buf.f_blocks = s.blocks; + buf.f_bfree = 0; + buf.f_bavail = 0; + buf.f_ffree = 0; + Ok(0) + }) + } + const XATTR_HANDLERS: [*const bindings::xattr_handler; 2] = [&Self::XATTR_HANDLER, ptr::null()]; const XATTR_HANDLER: bindings::xattr_handler = bindings::xattr_handler { From patchwork Wed Oct 18 12:25:12 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wedson Almeida Filho X-Patchwork-Id: 13426963 Received: from lindbergh.monkeyblade.net (lindbergh.monkeyblade.net [23.128.96.19]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3858634CD8; Wed, 18 Oct 2023 12:26:38 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="SWwC7ejq" Received: from mail-pl1-x62d.google.com (mail-pl1-x62d.google.com [IPv6:2607:f8b0:4864:20::62d]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 92551A3; Wed, 18 Oct 2023 05:26:36 -0700 (PDT) Received: by mail-pl1-x62d.google.com with SMTP id d9443c01a7336-1c9e072472bso43378095ad.2; Wed, 18 Oct 2023 05:26:36 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1697631996; x=1698236796; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=AAu3pHskPx/aJCWIgEBHcZgsDQetzFIBW3HnwZzPP4A=; b=SWwC7ejqeVTMQ0PRDV0w5QJlNQjmekgkyR9Uf0NM4W3OQNjT1MkUOEof5k4sX1HDhx sWQO3Xf4QPCrtF+A2Ojb1hbxPe6zce6CGX0h4JLGKE+8MqGp1NtXN9VXA/A1XxiIL/6N p2jSCwJAgamntFWxJbgdph1EoP2PSfwYrkO0Sd8kDqnQ1e62dhS0+txpVMagHIKFCpCZ u1E/sCW7BFaGzYHsK7r96Li5Tr/yx3vhHS8yX9C2PrEdauc61LnpGU721VbdtkixqgjD MACyYZy1J3EHafHqmyO93GXLraLsCecTFu3MyCJkTd4D8knkA45jtLOZujgXFJi8aE/a 4WDA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1697631996; x=1698236796; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=AAu3pHskPx/aJCWIgEBHcZgsDQetzFIBW3HnwZzPP4A=; b=feCn1uINVrxHRGwLJXVdQRfq6LIR0wNVNW8VulAHkpcdb7sPJpBFWlMuHiYtUTsPue mtL/uwrhleP2oCCrE0d8xfa60H8yrflpf90HlYmBKNrQTrMCmOmq1XDUWTSH6zD9ngVF 63wa9xFR8WTA6lX4J3F6da4dsEoGTpQ6ufnRKdF0mfKcS3R4RfyGgytjsfz909M+Pmpa pf8FTI3Ph3C4eCuxv+iS1Ao/BhbVpdFHXkZ6fkfNWjHDCjoq/pLDx+O99eJJEg5hStUE HjmlC6wLPBF6/SoL5DCLv/GJzOlBBr9grCjlvh6RCTSLQLjASmRm7Azw+6wXolHCA3VA Bzzw== X-Gm-Message-State: AOJu0YyfVyZI2Jy1YamJa4mWq8WuzdhL1QVxRCc2KntKUU6HY/ihf6HM /TDgzyb/L0XhscpHUbWEgvA= X-Google-Smtp-Source: AGHT+IEI0RDYwCYuWpJg3YZyfeHMjcAhdxWPSZlm/x9z7fif5xmQVVB933XrGK2H6DVXDQbg9QJr/Q== X-Received: by 2002:a17:903:41ce:b0:1ca:4d35:b2ec with SMTP id u14-20020a17090341ce00b001ca4d35b2ecmr6056864ple.15.1697631995907; Wed, 18 Oct 2023 05:26:35 -0700 (PDT) Received: from wedsonaf-dev.. ([2804:389:7122:43b8:9b73:6339:3351:cce0]) by smtp.googlemail.com with ESMTPSA id j1-20020a170902c3c100b001c736b0037fsm3411046plj.231.2023.10.18.05.26.32 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 18 Oct 2023 05:26:35 -0700 (PDT) From: Wedson Almeida Filho To: Alexander Viro , Christian Brauner , Matthew Wilcox Cc: Kent Overstreet , Greg Kroah-Hartman , linux-fsdevel@vger.kernel.org, rust-for-linux@vger.kernel.org, Wedson Almeida Filho Subject: [RFC PATCH 13/19] rust: fs: introduce more inode types Date: Wed, 18 Oct 2023 09:25:12 -0300 Message-Id: <20231018122518.128049-14-wedsonaf@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20231018122518.128049-1-wedsonaf@gmail.com> References: <20231018122518.128049-1-wedsonaf@gmail.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM, RCVD_IN_DNSWL_BLOCKED,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net From: Wedson Almeida Filho Allow Rust file system modules to create inodes that are symlinks, pipes, sockets, char devices and block devices (in addition to the already-supported directories and regular files). Signed-off-by: Wedson Almeida Filho --- rust/helpers.c | 6 +++ rust/kernel/fs.rs | 88 +++++++++++++++++++++++++++++++++++++++ samples/rust/rust_rofs.rs | 9 +++- 3 files changed, 102 insertions(+), 1 deletion(-) diff --git a/rust/helpers.c b/rust/helpers.c index f2ce3e7b688c..af335d1912e7 100644 --- a/rust/helpers.c +++ b/rust/helpers.c @@ -244,6 +244,12 @@ void rust_helper_mapping_set_large_folios(struct address_space *mapping) } EXPORT_SYMBOL_GPL(rust_helper_mapping_set_large_folios); +unsigned int rust_helper_MKDEV(unsigned int major, unsigned int minor) +{ + return MKDEV(major, minor); +} +EXPORT_SYMBOL_GPL(rust_helper_MKDEV); + /* * `bindgen` binds the C `size_t` type as the Rust `usize` type, so we can * use it in contexts where Rust expects a `usize` like slice (array) indices. diff --git a/rust/kernel/fs.rs b/rust/kernel/fs.rs index 8f34da50e694..5b7eaa16d254 100644 --- a/rust/kernel/fs.rs +++ b/rust/kernel/fs.rs @@ -112,8 +112,13 @@ pub enum DirEntryType { impl From for DirEntryType { fn from(value: INodeType) -> Self { match value { + INodeType::Fifo => DirEntryType::Fifo, + INodeType::Chr(_, _) => DirEntryType::Chr, INodeType::Dir => DirEntryType::Dir, + INodeType::Blk(_, _) => DirEntryType::Blk, INodeType::Reg => DirEntryType::Reg, + INodeType::Lnk => DirEntryType::Lnk, + INodeType::Sock => DirEntryType::Sock, } } } @@ -281,6 +286,46 @@ pub fn init(self, params: INodeParams) -> Result>> { unsafe { bindings::mapping_set_large_folios(inode.i_mapping) }; bindings::S_IFREG } + INodeType::Lnk => { + inode.i_op = &Tables::::LNK_INODE_OPERATIONS; + inode.i_data.a_ops = &Tables::::FILE_ADDRESS_SPACE_OPERATIONS; + + // SAFETY: `inode` is valid for write as it's a new inode. + unsafe { bindings::inode_nohighmem(inode) }; + bindings::S_IFLNK + } + INodeType::Fifo => { + // SAFETY: `inode` is valid for write as it's a new inode. + unsafe { bindings::init_special_inode(inode, bindings::S_IFIFO as _, 0) }; + bindings::S_IFIFO + } + INodeType::Sock => { + // SAFETY: `inode` is valid for write as it's a new inode. + unsafe { bindings::init_special_inode(inode, bindings::S_IFSOCK as _, 0) }; + bindings::S_IFSOCK + } + INodeType::Chr(major, minor) => { + // SAFETY: `inode` is valid for write as it's a new inode. + unsafe { + bindings::init_special_inode( + inode, + bindings::S_IFCHR as _, + bindings::MKDEV(major, minor & bindings::MINORMASK), + ) + }; + bindings::S_IFCHR + } + INodeType::Blk(major, minor) => { + // SAFETY: `inode` is valid for write as it's a new inode. + unsafe { + bindings::init_special_inode( + inode, + bindings::S_IFBLK as _, + bindings::MKDEV(major, minor & bindings::MINORMASK), + ) + }; + bindings::S_IFBLK + } }; inode.i_mode = (params.mode & 0o777) | u16::try_from(mode)?; @@ -315,11 +360,26 @@ fn drop(&mut self) { /// The type of the inode. #[derive(Copy, Clone)] pub enum INodeType { + /// Named pipe (first-in, first-out) type. + Fifo, + + /// Character device type. + Chr(u32, u32), + /// Directory type. Dir, + /// Block device type. + Blk(u32, u32), + /// Regular file type. Reg, + + /// Symbolic link type. + Lnk, + + /// Named unix-domain socket type. + Sock, } /// Required inode parameters. @@ -701,6 +761,34 @@ extern "C" fn lookup_callback( } } + const LNK_INODE_OPERATIONS: bindings::inode_operations = bindings::inode_operations { + lookup: None, + get_link: Some(bindings::page_get_link), + permission: None, + get_inode_acl: None, + readlink: None, + create: None, + link: None, + unlink: None, + symlink: None, + mkdir: None, + rmdir: None, + mknod: None, + rename: None, + setattr: None, + getattr: None, + listxattr: None, + fiemap: None, + update_time: None, + atomic_open: None, + tmpfile: None, + get_acl: None, + set_acl: None, + fileattr_set: None, + fileattr_get: None, + get_offset_ctx: None, + }; + const FILE_ADDRESS_SPACE_OPERATIONS: bindings::address_space_operations = bindings::address_space_operations { writepage: None, diff --git a/samples/rust/rust_rofs.rs b/samples/rust/rust_rofs.rs index ef651ad38185..95ce28efa1c3 100644 --- a/samples/rust/rust_rofs.rs +++ b/samples/rust/rust_rofs.rs @@ -23,7 +23,7 @@ struct Entry { contents: &'static [u8], } -const ENTRIES: [Entry; 3] = [ +const ENTRIES: [Entry; 4] = [ Entry { name: b".", ino: 1, @@ -42,6 +42,12 @@ struct Entry { etype: INodeType::Reg, contents: b"hello\n", }, + Entry { + name: b"link.txt", + ino: 3, + etype: INodeType::Lnk, + contents: b"./test.txt", + }, ]; struct RoFs; @@ -125,6 +131,7 @@ fn lookup(parent: &INode, name: &[u8]) -> Result>> { fn read_folio(inode: &INode, mut folio: LockedFolio<'_>) -> Result { let data = match inode.ino() { 2 => ENTRIES[2].contents, + 3 => ENTRIES[3].contents, _ => return Err(EINVAL), }; From patchwork Wed Oct 18 12:25:13 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wedson Almeida Filho X-Patchwork-Id: 13426964 Received: from lindbergh.monkeyblade.net (lindbergh.monkeyblade.net [23.128.96.19]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5963B3588D; Wed, 18 Oct 2023 12:26:42 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="hMjjVlYs" Received: from mail-pl1-x632.google.com (mail-pl1-x632.google.com [IPv6:2607:f8b0:4864:20::632]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id AB9D698; Wed, 18 Oct 2023 05:26:40 -0700 (PDT) Received: by mail-pl1-x632.google.com with SMTP id d9443c01a7336-1c5c91bec75so46212735ad.3; Wed, 18 Oct 2023 05:26:40 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1697632000; x=1698236800; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=BrF6Arw8KaTBf2vf2RpOGiYaWBYg9JY9IvGW6oCUD8s=; b=hMjjVlYsnFHwq4jEpXJ6or9uDqM2i1ewOpiMJi55SAR6khIhe2dxn/vHyUna+Y7Has mhPKJZ0SzMXzMLqH7CNqLEHp8tFY8Nk/2YTa/xd27IVgZO36q5UedNeUQCjpDU/w5R26 A3q1A5LaPg9PYsejg8ve2RwApTqcBTcoXsrQQwk8RL8Llu565Si9xMnJ6U+pJUAwUIA0 7BQ4jQ5swnHkl97uc2QHmgJ+8ekvnvcwqVMsCju7XH4I7JvyUbkG2D+eFQOhHWsQxySA +dqMAEcrhxjw+qRXssy9aTUgPMpVzZ5tMGUxk6xQX65hM23EewNA7A1CpgBjHdhMl8ep SBgA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1697632000; x=1698236800; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=BrF6Arw8KaTBf2vf2RpOGiYaWBYg9JY9IvGW6oCUD8s=; b=C2Z6vEF/jeisREdbRWWrxrWko5A5U5CxsOPNgIgLO9selaqG3m989lBYBGnnyoqPsL AqHznzlfvWN+DILqJUaKUcvHKkutxf61CpipDXbirIhlFc4ANx+kf0H4LIdKKPayw0nO i8I326erKgvPMrmOSENym6v++g9EPHqsEqv9n2aEbyIrmVb6vRSDdImw40fkCGmOzBfH 6qhaB//x4R1SQHBBWfAkuMD3ZZ6RteLXFtR6jMfO3g5Xn8s5r6DmPrQelMBedj5O8rfg rRMY2Gc+Oaqc9EuWxRZeVwEbMbB2b5/knDFHBZpoU+AywIfUcSFO8hsUt5jbSGK8nMv5 1AIA== X-Gm-Message-State: AOJu0YyGHyfoK11pxNMoKr5SVtHGz4/qoQqSXzSM1kjhyxNGjD9jUrY7 CNrwqjUzIoCOyjaOm3UZEVU= X-Google-Smtp-Source: AGHT+IEmjFJfsKiZHETfpeXzb8w6Bz66rmrnXd6VftqnE4L0JQm7jLthzP8//r8z0zuWbq252ryeQA== X-Received: by 2002:a17:902:e88b:b0:1c9:dd73:dc9e with SMTP id w11-20020a170902e88b00b001c9dd73dc9emr5998926plg.44.1697632000016; Wed, 18 Oct 2023 05:26:40 -0700 (PDT) Received: from wedsonaf-dev.. ([2804:389:7122:43b8:9b73:6339:3351:cce0]) by smtp.googlemail.com with ESMTPSA id j1-20020a170902c3c100b001c736b0037fsm3411046plj.231.2023.10.18.05.26.36 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 18 Oct 2023 05:26:39 -0700 (PDT) From: Wedson Almeida Filho To: Alexander Viro , Christian Brauner , Matthew Wilcox Cc: Kent Overstreet , Greg Kroah-Hartman , linux-fsdevel@vger.kernel.org, rust-for-linux@vger.kernel.org, Wedson Almeida Filho Subject: [RFC PATCH 14/19] rust: fs: add per-superblock data Date: Wed, 18 Oct 2023 09:25:13 -0300 Message-Id: <20231018122518.128049-15-wedsonaf@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20231018122518.128049-1-wedsonaf@gmail.com> References: <20231018122518.128049-1-wedsonaf@gmail.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM, RCVD_IN_DNSWL_BLOCKED,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net From: Wedson Almeida Filho Allow Rust file systems to associate [typed] data to super blocks when they're created. Since we only have a pointer-sized field in which to store the state, it must implement the `ForeignOwnable` trait. Signed-off-by: Wedson Almeida Filho --- rust/kernel/fs.rs | 42 +++++++++++++++++++++++++++++++++------ samples/rust/rust_rofs.rs | 4 +++- 2 files changed, 39 insertions(+), 7 deletions(-) diff --git a/rust/kernel/fs.rs b/rust/kernel/fs.rs index 5b7eaa16d254..e9a9362d2897 100644 --- a/rust/kernel/fs.rs +++ b/rust/kernel/fs.rs @@ -7,7 +7,7 @@ //! C headers: [`include/linux/fs.h`](../../include/linux/fs.h) use crate::error::{code::*, from_result, to_result, Error, Result}; -use crate::types::{ARef, AlwaysRefCounted, Either, Opaque}; +use crate::types::{ARef, AlwaysRefCounted, Either, ForeignOwnable, Opaque}; use crate::{ bindings, folio::LockedFolio, init::PinInit, str::CStr, time::Timespec, try_pin_init, ThisModule, @@ -20,11 +20,14 @@ /// A file system type. pub trait FileSystem { + /// Data associated with each file system instance (super-block). + type Data: ForeignOwnable + Send + Sync; + /// The name of the file system type. const NAME: &'static CStr; /// Returns the parameters to initialise a super block. - fn super_params(sb: &NewSuperBlock) -> Result; + fn super_params(sb: &NewSuperBlock) -> Result>; /// Initialises and returns the root inode of the given superblock. /// @@ -174,7 +177,7 @@ pub fn new(module: &'static ThisModule) -> impl PinInit< fs.owner = module.0; fs.name = T::NAME.as_char_ptr(); fs.init_fs_context = Some(Self::init_fs_context_callback::); - fs.kill_sb = Some(Self::kill_sb_callback); + fs.kill_sb = Some(Self::kill_sb_callback::); fs.fs_flags = 0; // SAFETY: Pointers stored in `fs` are static so will live for as long as the @@ -195,10 +198,22 @@ pub fn new(module: &'static ThisModule) -> impl PinInit< }) } - unsafe extern "C" fn kill_sb_callback(sb_ptr: *mut bindings::super_block) { + unsafe extern "C" fn kill_sb_callback( + sb_ptr: *mut bindings::super_block, + ) { // SAFETY: In `get_tree_callback` we always call `get_tree_nodev`, so `kill_anon_super` is // the appropriate function to call for cleanup. unsafe { bindings::kill_anon_super(sb_ptr) }; + + // SAFETY: The C API contract guarantees that `sb_ptr` is valid for read. + let ptr = unsafe { (*sb_ptr).s_fs_info }; + if !ptr.is_null() { + // SAFETY: The only place where `s_fs_info` is assigned is `NewSuperBlock::init`, where + // it's initialised with the result of an `into_foreign` call. We checked above that + // `ptr` is non-null because it would be null if we never reached the point where we + // init the field. + unsafe { T::Data::from_foreign(ptr) }; + } } } @@ -429,6 +444,14 @@ pub struct INodeParams { pub struct SuperBlock(Opaque, PhantomData); impl SuperBlock { + /// Returns the data associated with the superblock. + pub fn data(&self) -> ::Borrowed<'_> { + // SAFETY: This method is only available after the `NeedsData` typestate, so `s_fs_info` + // has been initialised initialised with the result of a call to `T::into_foreign`. + let ptr = unsafe { (*self.0.get()).s_fs_info }; + unsafe { T::Data::borrow(ptr) } + } + /// Tries to get an existing inode or create a new one if it doesn't exist yet. pub fn get_or_create_inode(&self, ino: Ino) -> Result>, NewINode>> { // SAFETY: The only initialisation missing from the superblock is the root, and this @@ -458,7 +481,7 @@ pub fn get_or_create_inode(&self, ino: Ino) -> Result>, New /// Required superblock parameters. /// /// This is returned by implementations of [`FileSystem::super_params`]. -pub struct SuperParams { +pub struct SuperParams { /// The magic number of the superblock. pub magic: u32, @@ -472,6 +495,9 @@ pub struct SuperParams { /// Granularity of c/m/atime in ns (cannot be worse than a second). pub time_gran: u32, + + /// Data to be associated with the superblock. + pub data: T, } /// A superblock that is still being initialised. @@ -522,6 +548,9 @@ impl Tables { sb.0.s_blocksize = 1 << sb.0.s_blocksize_bits; sb.0.s_flags |= bindings::SB_RDONLY; + // N.B.: Even on failure, `kill_sb` is called and frees the data. + sb.0.s_fs_info = params.data.into_foreign().cast_mut(); + // SAFETY: The callback contract guarantees that `sb_ptr` is a unique pointer to a // newly-created (and initialised above) superblock. let sb = unsafe { &mut *sb_ptr.cast() }; @@ -934,8 +963,9 @@ fn init(module: &'static ThisModule) -> impl PinInit { /// /// struct MyFs; /// impl fs::FileSystem for MyFs { +/// type Data = (); /// const NAME: &'static CStr = c_str!("myfs"); -/// fn super_params(_: &NewSuperBlock) -> Result { +/// fn super_params(_: &NewSuperBlock) -> Result> { /// todo!() /// } /// fn init_root(_sb: &SuperBlock) -> Result>> { diff --git a/samples/rust/rust_rofs.rs b/samples/rust/rust_rofs.rs index 95ce28efa1c3..093425650f26 100644 --- a/samples/rust/rust_rofs.rs +++ b/samples/rust/rust_rofs.rs @@ -52,14 +52,16 @@ struct Entry { struct RoFs; impl fs::FileSystem for RoFs { + type Data = (); const NAME: &'static CStr = c_str!("rust-fs"); - fn super_params(_sb: &NewSuperBlock) -> Result { + fn super_params(_sb: &NewSuperBlock) -> Result> { Ok(SuperParams { magic: 0x52555354, blocksize_bits: 12, maxbytes: fs::MAX_LFS_FILESIZE, time_gran: 1, + data: (), }) } From patchwork Wed Oct 18 12:25:14 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wedson Almeida Filho X-Patchwork-Id: 13426965 Received: from lindbergh.monkeyblade.net (lindbergh.monkeyblade.net [23.128.96.19]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id CA95F3589F; Wed, 18 Oct 2023 12:26:46 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="EyeqwKD4" Received: from mail-pl1-x631.google.com (mail-pl1-x631.google.com [IPv6:2607:f8b0:4864:20::631]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 8458498; Wed, 18 Oct 2023 05:26:45 -0700 (PDT) Received: by mail-pl1-x631.google.com with SMTP id d9443c01a7336-1caa371dcd8so7638785ad.0; Wed, 18 Oct 2023 05:26:45 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1697632005; x=1698236805; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=P61MlmjSe7FvOE0BP4exDriXbJ7DtpV2uo0UJk2lGNE=; b=EyeqwKD4xdSujiWwLPP214P2B5IpkIFrCK65XLtYHiZqX/sYuA3QHCbZraT7v0gwuf VcN6vUeodOF0xM4j9e9rjn7h+vAMDXAx38iA95jD+vUxf+f1PzMybOLV6vpxmyb9O9YU nBfaYjLNPbj8D1Dh4NYwwYV0ZauIxsQEY5/X63hh80AvuIxeTXs3bFsqQVnTaDdPhG1j W05IhQpI2RAo/LDuctzsJRoyTYp5AdaHfTeD31CUyRkRza0UlqQNFOIN37pAQaHK8Ugy gklrukWassIc1dF08QRaykiJATb9KzH0jXnv1VxWb7UUTFgZrLcqS6cKN89+HU2BlD6w cXiQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1697632005; x=1698236805; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=P61MlmjSe7FvOE0BP4exDriXbJ7DtpV2uo0UJk2lGNE=; b=C3i7dIj+YgNFnS/FVIB3W4Ty3onGYI+RIPD2sjVVSPu537qWVHrjQ86o+eF4RMR6as gvhharIaxHwBpQLzTHHRRiS4FsrqnEomsusVygyDJUjrAPvcDU1jk+S3LKMI5baXbCIr wEzGJcoKHn0vLX1JU/5PZ3cy5xgNmgUJeiaZ+dTSjzwo/kM5LsXPFvUaOX6zdSNDVhRe eDMeA0D1EnuvtGqftBWCEQuFgsj5YLuGV8n11XRBSkS31uzKNDrmOdd458SVthEdsX9F tMuBWwZFitVUaEPSrwLJixKBlTiry1VjH9vrH5uYYVeyiOvfVhijUuGe4myk9CFXzIeA afeQ== X-Gm-Message-State: AOJu0YyTwiCad54m63eLKaaBsAjCzjFgvCncwHhMHPgO1kB46VWkb6mH DDtguUT/sl5c/P0l84Xm6IA= X-Google-Smtp-Source: AGHT+IGJiScp1ydvE92KBOPsTi3tQLxd/SrIvUI5Yyt54VAn3eBvbK1Es43KLCTPy6UIGK9mbUXrbQ== X-Received: by 2002:a17:902:bd0c:b0:1c9:b5a6:44a0 with SMTP id p12-20020a170902bd0c00b001c9b5a644a0mr4664021pls.23.1697632004899; Wed, 18 Oct 2023 05:26:44 -0700 (PDT) Received: from wedsonaf-dev.. ([2804:389:7122:43b8:9b73:6339:3351:cce0]) by smtp.googlemail.com with ESMTPSA id j1-20020a170902c3c100b001c736b0037fsm3411046plj.231.2023.10.18.05.26.40 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 18 Oct 2023 05:26:44 -0700 (PDT) From: Wedson Almeida Filho To: Alexander Viro , Christian Brauner , Matthew Wilcox Cc: Kent Overstreet , Greg Kroah-Hartman , linux-fsdevel@vger.kernel.org, rust-for-linux@vger.kernel.org, Wedson Almeida Filho Subject: [RFC PATCH 15/19] rust: fs: add basic support for fs buffer heads Date: Wed, 18 Oct 2023 09:25:14 -0300 Message-Id: <20231018122518.128049-16-wedsonaf@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20231018122518.128049-1-wedsonaf@gmail.com> References: <20231018122518.128049-1-wedsonaf@gmail.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM, RCVD_IN_DNSWL_NONE,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net From: Wedson Almeida Filho Introduce the abstractions that will be used by modules to handle buffer heads, which will be used to access cached blocks from block devices. All dead-code annotations are removed in the next commit in the series. Signed-off-by: Wedson Almeida Filho --- rust/bindings/bindings_helper.h | 1 + rust/helpers.c | 15 ++++++++ rust/kernel/fs.rs | 3 ++ rust/kernel/fs/buffer.rs | 61 +++++++++++++++++++++++++++++++++ 4 files changed, 80 insertions(+) create mode 100644 rust/kernel/fs/buffer.rs diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index e2b2ccc835e3..d328375f7cb7 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -7,6 +7,7 @@ */ #include +#include #include #include #include diff --git a/rust/helpers.c b/rust/helpers.c index af335d1912e7..a5393c6b93f2 100644 --- a/rust/helpers.c +++ b/rust/helpers.c @@ -21,6 +21,7 @@ */ #include +#include #include #include #include @@ -250,6 +251,20 @@ unsigned int rust_helper_MKDEV(unsigned int major, unsigned int minor) } EXPORT_SYMBOL_GPL(rust_helper_MKDEV); +#ifdef CONFIG_BUFFER_HEAD +void rust_helper_get_bh(struct buffer_head *bh) +{ + get_bh(bh); +} +EXPORT_SYMBOL_GPL(rust_helper_get_bh); + +void rust_helper_put_bh(struct buffer_head *bh) +{ + put_bh(bh); +} +EXPORT_SYMBOL_GPL(rust_helper_put_bh); +#endif + /* * `bindgen` binds the C `size_t` type as the Rust `usize` type, so we can * use it in contexts where Rust expects a `usize` like slice (array) indices. diff --git a/rust/kernel/fs.rs b/rust/kernel/fs.rs index e9a9362d2897..4f04cb1d3c6f 100644 --- a/rust/kernel/fs.rs +++ b/rust/kernel/fs.rs @@ -15,6 +15,9 @@ use core::{marker::PhantomData, marker::PhantomPinned, mem::ManuallyDrop, pin::Pin, ptr}; use macros::{pin_data, pinned_drop}; +#[cfg(CONFIG_BUFFER_HEAD)] +pub mod buffer; + /// Maximum size of an inode. pub const MAX_LFS_FILESIZE: i64 = bindings::MAX_LFS_FILESIZE; diff --git a/rust/kernel/fs/buffer.rs b/rust/kernel/fs/buffer.rs new file mode 100644 index 000000000000..6052af8822b3 --- /dev/null +++ b/rust/kernel/fs/buffer.rs @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! File system buffers. +//! +//! C headers: [`include/linux/buffer_head.h`](../../../include/linux/buffer_head.h) + +use crate::types::{ARef, AlwaysRefCounted, Opaque}; +use core::ptr; + +/// Wraps the kernel's `struct buffer_head`. +/// +/// # Invariants +/// +/// Instances of this type are always ref-counted, that is, a call to `get_bh` ensures that the +/// allocation remains valid at least until the matching call to `put_bh`. +#[repr(transparent)] +pub struct Head(Opaque); + +// SAFETY: The type invariants guarantee that `INode` is always ref-counted. +unsafe impl AlwaysRefCounted for Head { + fn inc_ref(&self) { + // SAFETY: The existence of a shared reference means that the refcount is nonzero. + unsafe { bindings::get_bh(self.0.get()) }; + } + + unsafe fn dec_ref(obj: ptr::NonNull) { + // SAFETY: The safety requirements guarantee that the refcount is nonzero. + unsafe { bindings::put_bh(obj.cast().as_ptr()) } + } +} + +impl Head { + /// Returns the block data associated with the given buffer head. + pub fn data(&self) -> &[u8] { + let h = self.0.get(); + // SAFETY: The existence of a shared reference guarantees that the buffer head is + // available and so we can access its contents. + unsafe { core::slice::from_raw_parts((*h).b_data.cast(), (*h).b_size) } + } +} + +/// A view of a buffer. +/// +/// It may contain just a contiguous subset of the buffer. +pub struct View { + head: ARef, + offset: usize, + size: usize, +} + +impl View { + #[allow(dead_code)] + pub(crate) fn new(head: ARef, offset: usize, size: usize) -> Self { + Self { head, size, offset } + } + + /// Returns the view of the buffer head. + pub fn data(&self) -> &[u8] { + &self.head.data()[self.offset..][..self.size] + } +} From patchwork Wed Oct 18 12:25:15 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wedson Almeida Filho X-Patchwork-Id: 13426966 Received: from lindbergh.monkeyblade.net (lindbergh.monkeyblade.net [23.128.96.19]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 958A93589F; Wed, 18 Oct 2023 12:26:52 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="Hd40ALlE" Received: from mail-pl1-x631.google.com (mail-pl1-x631.google.com [IPv6:2607:f8b0:4864:20::631]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id CEB2B98; Wed, 18 Oct 2023 05:26:49 -0700 (PDT) Received: by mail-pl1-x631.google.com with SMTP id d9443c01a7336-1c871a095ceso47718905ad.2; Wed, 18 Oct 2023 05:26:49 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1697632009; x=1698236809; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=XIRlHb8w4YCxwuLbKyafSjcNKaAgvRUfsH7H7yMCuts=; b=Hd40ALlE+fkuSTcVe1HhwBw0TycVh8HQlNgCibOi3pGMaEK0E3iBuRg9wb133kSMDb VAZCUioHNthTk+a1bhRTQ4tx9VrAkw8plhbDFXUGRHgUTB+izhavu6BqD7M40nUfM4ld plJLmQ8LOCsp7Jl7cMtcB5hd5nuey/55pkHIFl6hKfnZMS6uZxtsB0Roy9KigVkpzHjH W/PDNKxgArsUygkDuNYxxNNx6fVkXSnQdn1fIFmGl2w2q/BdNhdIbYObXIxbJlgZoFAT 1ikSBiYBPLUwLaRtNJnU1q1+Tf2dLVwJFWN1IUMB5DNDC9J/roWNFW1kImZGif5ti0Jg TxAw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1697632009; x=1698236809; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=XIRlHb8w4YCxwuLbKyafSjcNKaAgvRUfsH7H7yMCuts=; b=FTj6qepLoIfEtPW06y7jPCKiKPzdj51MUXierpAXGOJ7cy3xNsLVBXPxtt+WtayTrx kfLpEuq4xakDZsOxb6XafLFzGSsQGjpb/QQjrKakv+D/4FGTar9TWgrJAt1z+3VNkLxM wRerwDnGckJYk7dcISdr3Vk/6ZNQHM/UHnkUv5M+AA+V6QqFQTEP5kans09yq+EwftrV J26a7NmzRnAKUkNtKiT1y4qefmxYsbFFMvz3s+oSnrZJAdX9salZvXEG9Y/LjNnTCzOq krgNnlDMWabXhiix3Pgc7xs6xPmaP4aM6q5G8WCZLYdzQjV34Z7HtnObx+fc1RXFOtn/ A4Ag== X-Gm-Message-State: AOJu0YyMEb/wAMx8qOdlVk42XqJ9IxOblYgr+u2TF4nkkHIx98n8iygx 3naESk+hJugohIyBYE6QA9Y= X-Google-Smtp-Source: AGHT+IGUody+YcT2caZetiZtfnANTx3tDKRbbIbPF9yfafpVCA5C6Pu45Xc+9xKQiXkRCkPDC648+g== X-Received: by 2002:a17:903:2308:b0:1c6:21b4:30bb with SMTP id d8-20020a170903230800b001c621b430bbmr5868140plh.15.1697632009145; Wed, 18 Oct 2023 05:26:49 -0700 (PDT) Received: from wedsonaf-dev.. ([2804:389:7122:43b8:9b73:6339:3351:cce0]) by smtp.googlemail.com with ESMTPSA id j1-20020a170902c3c100b001c736b0037fsm3411046plj.231.2023.10.18.05.26.45 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 18 Oct 2023 05:26:48 -0700 (PDT) From: Wedson Almeida Filho To: Alexander Viro , Christian Brauner , Matthew Wilcox Cc: Kent Overstreet , Greg Kroah-Hartman , linux-fsdevel@vger.kernel.org, rust-for-linux@vger.kernel.org, Wedson Almeida Filho Subject: [RFC PATCH 16/19] rust: fs: allow file systems backed by a block device Date: Wed, 18 Oct 2023 09:25:15 -0300 Message-Id: <20231018122518.128049-17-wedsonaf@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20231018122518.128049-1-wedsonaf@gmail.com> References: <20231018122518.128049-1-wedsonaf@gmail.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM, RCVD_IN_DNSWL_NONE,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net From: Wedson Almeida Filho Allow Rust file systems that are backed by block devices (in addition to in-memory ones). Signed-off-by: Wedson Almeida Filho --- rust/bindings/bindings_helper.h | 1 + rust/helpers.c | 14 +++ rust/kernel/fs.rs | 177 +++++++++++++++++++++++++++++--- rust/kernel/fs/buffer.rs | 1 - 4 files changed, 180 insertions(+), 13 deletions(-) diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index d328375f7cb7..8403f13d4d48 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -7,6 +7,7 @@ */ #include +#include #include #include #include diff --git a/rust/helpers.c b/rust/helpers.c index a5393c6b93f2..bc19f3b7b93e 100644 --- a/rust/helpers.c +++ b/rust/helpers.c @@ -21,6 +21,7 @@ */ #include +#include #include #include #include @@ -252,6 +253,13 @@ unsigned int rust_helper_MKDEV(unsigned int major, unsigned int minor) EXPORT_SYMBOL_GPL(rust_helper_MKDEV); #ifdef CONFIG_BUFFER_HEAD +struct buffer_head *rust_helper_sb_bread(struct super_block *sb, + sector_t block) +{ + return sb_bread(sb, block); +} +EXPORT_SYMBOL_GPL(rust_helper_sb_bread); + void rust_helper_get_bh(struct buffer_head *bh) { get_bh(bh); @@ -265,6 +273,12 @@ void rust_helper_put_bh(struct buffer_head *bh) EXPORT_SYMBOL_GPL(rust_helper_put_bh); #endif +sector_t rust_helper_bdev_nr_sectors(struct block_device *bdev) +{ + return bdev_nr_sectors(bdev); +} +EXPORT_SYMBOL_GPL(rust_helper_bdev_nr_sectors); + /* * `bindgen` binds the C `size_t` type as the Rust `usize` type, so we can * use it in contexts where Rust expects a `usize` like slice (array) indices. diff --git a/rust/kernel/fs.rs b/rust/kernel/fs.rs index 4f04cb1d3c6f..b1ad5c110dbb 100644 --- a/rust/kernel/fs.rs +++ b/rust/kernel/fs.rs @@ -7,11 +7,9 @@ //! C headers: [`include/linux/fs.h`](../../include/linux/fs.h) use crate::error::{code::*, from_result, to_result, Error, Result}; -use crate::types::{ARef, AlwaysRefCounted, Either, ForeignOwnable, Opaque}; -use crate::{ - bindings, folio::LockedFolio, init::PinInit, str::CStr, time::Timespec, try_pin_init, - ThisModule, -}; +use crate::folio::{LockedFolio, UniqueFolio}; +use crate::types::{ARef, AlwaysRefCounted, Either, ForeignOwnable, Opaque, ScopeGuard}; +use crate::{bindings, init::PinInit, str::CStr, time::Timespec, try_pin_init, ThisModule}; use core::{marker::PhantomData, marker::PhantomPinned, mem::ManuallyDrop, pin::Pin, ptr}; use macros::{pin_data, pinned_drop}; @@ -21,6 +19,17 @@ /// Maximum size of an inode. pub const MAX_LFS_FILESIZE: i64 = bindings::MAX_LFS_FILESIZE; +/// Type of superblock keying. +/// +/// It determines how C's `fs_context_operations::get_tree` is implemented. +pub enum Super { + /// Multiple independent superblocks may exist. + Independent, + + /// Uses a block device. + BlockDev, +} + /// A file system type. pub trait FileSystem { /// Data associated with each file system instance (super-block). @@ -29,6 +38,9 @@ pub trait FileSystem { /// The name of the file system type. const NAME: &'static CStr; + /// Determines how superblocks for this file system type are keyed. + const SUPER_TYPE: Super = Super::Independent; + /// Returns the parameters to initialise a super block. fn super_params(sb: &NewSuperBlock) -> Result>; @@ -181,7 +193,9 @@ pub fn new(module: &'static ThisModule) -> impl PinInit< fs.name = T::NAME.as_char_ptr(); fs.init_fs_context = Some(Self::init_fs_context_callback::); fs.kill_sb = Some(Self::kill_sb_callback::); - fs.fs_flags = 0; + fs.fs_flags = if let Super::BlockDev = T::SUPER_TYPE { + bindings::FS_REQUIRES_DEV as i32 + } else { 0 }; // SAFETY: Pointers stored in `fs` are static so will live for as long as the // registration is active (it is undone in `drop`). @@ -204,9 +218,16 @@ pub fn new(module: &'static ThisModule) -> impl PinInit< unsafe extern "C" fn kill_sb_callback( sb_ptr: *mut bindings::super_block, ) { - // SAFETY: In `get_tree_callback` we always call `get_tree_nodev`, so `kill_anon_super` is - // the appropriate function to call for cleanup. - unsafe { bindings::kill_anon_super(sb_ptr) }; + match T::SUPER_TYPE { + // SAFETY: In `get_tree_callback` we always call `get_tree_bdev` for + // `Super::BlockDev`, so `kill_block_super` is the appropriate function to call + // for cleanup. + Super::BlockDev => unsafe { bindings::kill_block_super(sb_ptr) }, + // SAFETY: In `get_tree_callback` we always call `get_tree_nodev` for + // `Super::Independent`, so `kill_anon_super` is the appropriate function to call + // for cleanup. + Super::Independent => unsafe { bindings::kill_anon_super(sb_ptr) }, + } // SAFETY: The C API contract guarantees that `sb_ptr` is valid for read. let ptr = unsafe { (*sb_ptr).s_fs_info }; @@ -479,6 +500,65 @@ pub fn get_or_create_inode(&self, ino: Ino) -> Result>, New }))) } } + + /// Reads a block from the block device. + #[cfg(CONFIG_BUFFER_HEAD)] + pub fn bread(&self, block: u64) -> Result> { + // Fail requests for non-blockdev file systems. This is a compile-time check. + match T::SUPER_TYPE { + Super::BlockDev => {} + _ => return Err(EIO), + } + + // SAFETY: This function is only valid after the `NeedsInit` typestate, so the block size + // is known and the superblock can be used to read blocks. + let ptr = + ptr::NonNull::new(unsafe { bindings::sb_bread(self.0.get(), block) }).ok_or(EIO)?; + // SAFETY: `sb_bread` returns a referenced buffer head. Ownership of the increment is + // passed to the `ARef` instance. + Ok(unsafe { ARef::from_raw(ptr.cast()) }) + } + + /// Reads `size` bytes starting from `offset` bytes. + /// + /// Returns an iterator that returns slices based on blocks. + #[cfg(CONFIG_BUFFER_HEAD)] + pub fn read( + &self, + offset: u64, + size: u64, + ) -> Result> + '_> { + struct BlockIter<'a, T: FileSystem + ?Sized> { + sb: &'a SuperBlock, + next_offset: u64, + end: u64, + } + impl<'a, T: FileSystem + ?Sized> Iterator for BlockIter<'a, T> { + type Item = Result; + + fn next(&mut self) -> Option { + if self.next_offset >= self.end { + return None; + } + + // SAFETY: The superblock is valid and has had its block size initialised. + let block_size = unsafe { (*self.sb.0.get()).s_blocksize }; + let bh = match self.sb.bread(self.next_offset / block_size) { + Ok(bh) => bh, + Err(e) => return Some(Err(e)), + }; + let boffset = self.next_offset & (block_size - 1); + let bsize = core::cmp::min(self.end - self.next_offset, block_size - boffset); + self.next_offset += bsize; + Some(Ok(buffer::View::new(bh, boffset as usize, bsize as usize))) + } + } + Ok(BlockIter { + sb: self, + next_offset: offset, + end: offset.checked_add(size).ok_or(ERANGE)?, + }) + } } /// Required superblock parameters. @@ -511,6 +591,70 @@ pub struct SuperParams { #[repr(transparent)] pub struct NewSuperBlock(bindings::super_block, PhantomData); +impl NewSuperBlock { + /// Reads sectors. + /// + /// `count` must be such that the total size doesn't exceed a page. + pub fn sread(&self, sector: u64, count: usize, folio: &mut UniqueFolio) -> Result { + // Fail requests for non-blockdev file systems. This is a compile-time check. + match T::SUPER_TYPE { + // The superblock is valid and given that it's a blockdev superblock it must have a + // valid `s_bdev`. + Super::BlockDev => {} + _ => return Err(EIO), + } + + crate::build_assert!(count * (bindings::SECTOR_SIZE as usize) <= bindings::PAGE_SIZE); + + // Read the sectors. + let mut bio = bindings::bio::default(); + let bvec = Opaque::::uninit(); + + // SAFETY: `bio` and `bvec` are allocated on the stack, they're both valid. + unsafe { + bindings::bio_init( + &mut bio, + self.0.s_bdev, + bvec.get(), + 1, + bindings::req_op_REQ_OP_READ, + ) + }; + + // SAFETY: `bio` was just initialised with `bio_init` above, so it's safe to call + // `bio_uninit` on the way out. + let mut bio = + ScopeGuard::new_with_data(bio, |mut b| unsafe { bindings::bio_uninit(&mut b) }); + + // SAFETY: We have one free `bvec` (initialsied above). We also know that size won't exceed + // a page size (build_assert above). + unsafe { + bindings::bio_add_folio_nofail( + &mut *bio, + folio.0 .0.get(), + count * (bindings::SECTOR_SIZE as usize), + 0, + ) + }; + bio.bi_iter.bi_sector = sector; + + // SAFETY: The bio was fully initialised above. + to_result(unsafe { bindings::submit_bio_wait(&mut *bio) })?; + Ok(()) + } + + /// Returns the number of sectors in the underlying block device. + pub fn sector_count(&self) -> Result { + // Fail requests for non-blockdev file systems. This is a compile-time check. + match T::SUPER_TYPE { + // The superblock is valid and given that it's a blockdev superblock it must have a + // valid `s_bdev`. + Super::BlockDev => Ok(unsafe { bindings::bdev_nr_sectors(self.0.s_bdev) }), + _ => Err(EIO), + } + } +} + struct Tables(T); impl Tables { const CONTEXT: bindings::fs_context_operations = bindings::fs_context_operations { @@ -523,9 +667,18 @@ impl Tables { }; unsafe extern "C" fn get_tree_callback(fc: *mut bindings::fs_context) -> core::ffi::c_int { - // SAFETY: `fc` is valid per the callback contract. `fill_super_callback` also has - // the right type and is a valid callback. - unsafe { bindings::get_tree_nodev(fc, Some(Self::fill_super_callback)) } + match T::SUPER_TYPE { + // SAFETY: `fc` is valid per the callback contract. `fill_super_callback` also has + // the right type and is a valid callback. + Super::BlockDev => unsafe { + bindings::get_tree_bdev(fc, Some(Self::fill_super_callback)) + }, + // SAFETY: `fc` is valid per the callback contract. `fill_super_callback` also has + // the right type and is a valid callback. + Super::Independent => unsafe { + bindings::get_tree_nodev(fc, Some(Self::fill_super_callback)) + }, + } } unsafe extern "C" fn fill_super_callback( diff --git a/rust/kernel/fs/buffer.rs b/rust/kernel/fs/buffer.rs index 6052af8822b3..de23d0fee66c 100644 --- a/rust/kernel/fs/buffer.rs +++ b/rust/kernel/fs/buffer.rs @@ -49,7 +49,6 @@ pub struct View { } impl View { - #[allow(dead_code)] pub(crate) fn new(head: ARef, offset: usize, size: usize) -> Self { Self { head, size, offset } } From patchwork Wed Oct 18 12:25:16 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wedson Almeida Filho X-Patchwork-Id: 13426967 Received: from lindbergh.monkeyblade.net (lindbergh.monkeyblade.net [23.128.96.19]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B36983589F; Wed, 18 Oct 2023 12:26:57 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="Bcc7ShLo" Received: from mail-pl1-x634.google.com (mail-pl1-x634.google.com [IPv6:2607:f8b0:4864:20::634]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 35C9FA3; Wed, 18 Oct 2023 05:26:55 -0700 (PDT) Received: by mail-pl1-x634.google.com with SMTP id d9443c01a7336-1c9daca2b85so51353115ad.1; Wed, 18 Oct 2023 05:26:55 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1697632014; x=1698236814; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=V4oGNCYkMKOT/RkEtS/bAaMFpJ0/Ena/JQgVAj+ViHo=; b=Bcc7ShLo99LZjwHpkMQq4cI+s5WrbjUa1JheJ4NuBALzebPWzir59vuHac4QqsWe5O mDLJ17Y0DZMqVXT6BwW4Jm++yuBJnqEETQaPYPZ8OszJJv/m/T0e9lUcFJsXoPAhZZz1 B99yma+tokR1u4P5soBGlDYoLgCBtcqLT9o83DtAdBXCOYkNrDCV9jICVHvwaBx2hy0J IoGnmqjLPJqxUwU2S1Jen5Pn7lNEhfIVr3n/KofWpN/9IYGMQALw8fbtnHgnw5Tr7HM6 QcHcMEo7x0qkZTiNj5qrVHrOa3PUT6ib3pNt8wcwiludM89EeAcfQa9z+n5w6W9a06Tj 8Dxg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1697632014; x=1698236814; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=V4oGNCYkMKOT/RkEtS/bAaMFpJ0/Ena/JQgVAj+ViHo=; b=le7QSlmieTVinVv6JY1Gwz1TnRe7UCH9n+sr7gTqXLLIqYWlG0BGAYeWVD0V7ZnrxC CQXzbsjnJFjg3I4IdFVTBX7J00CLzPb2EUZDi+MS9TaJWm3VhjgHlDQPVCU4VYiJ9Y1Q xNLqjjUZTiqJOmUeIEm0q2vpBUS5NMaemqSKo581dGF2si0Pytg+32mYvFCYHocW7yTI PE5TY7YPiy/T1ABHqy+wCMQI6PmX1kEYhsrnmed1HpszjZ2l68Ay6pBtmigsd6+bjzLL NvFRdfyIedA01mzsBV+sxi0KfOmTGBkHVRGv9uKYv6LL5uD1+RvChwGwqcLFg0nZ7NJY uKwg== X-Gm-Message-State: AOJu0YyMN9S//eWAhBz3M4PYfjqfA0tJFWdWFaQmkwZEd/WwB/Jv2zJQ mcqcZkggkltjiBStUf5rujA= X-Google-Smtp-Source: AGHT+IGhVCRC4nGm0aTH85dmW+gmyNDm2vQ9FljuKqJ0Sz8FTe8woVLMwfnO+sSHuwSw36zRU0ETpg== X-Received: by 2002:a17:903:41cc:b0:1b8:8682:62fb with SMTP id u12-20020a17090341cc00b001b8868262fbmr7551078ple.4.1697632014298; Wed, 18 Oct 2023 05:26:54 -0700 (PDT) Received: from wedsonaf-dev.. ([2804:389:7122:43b8:9b73:6339:3351:cce0]) by smtp.googlemail.com with ESMTPSA id j1-20020a170902c3c100b001c736b0037fsm3411046plj.231.2023.10.18.05.26.49 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 18 Oct 2023 05:26:54 -0700 (PDT) From: Wedson Almeida Filho To: Alexander Viro , Christian Brauner , Matthew Wilcox Cc: Kent Overstreet , Greg Kroah-Hartman , linux-fsdevel@vger.kernel.org, rust-for-linux@vger.kernel.org, Wedson Almeida Filho Subject: [RFC PATCH 17/19] rust: fs: allow per-inode data Date: Wed, 18 Oct 2023 09:25:16 -0300 Message-Id: <20231018122518.128049-18-wedsonaf@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20231018122518.128049-1-wedsonaf@gmail.com> References: <20231018122518.128049-1-wedsonaf@gmail.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM, RCVD_IN_DNSWL_BLOCKED,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net From: Wedson Almeida Filho Allow Rust file systems to attach extra [typed] data to each inode. If no data is needed, use the regular inode kmem_cache, otherwise we create a new one. Signed-off-by: Wedson Almeida Filho --- rust/helpers.c | 7 +++ rust/kernel/fs.rs | 128 +++++++++++++++++++++++++++++++++++--- rust/kernel/mem_cache.rs | 2 - samples/rust/rust_rofs.rs | 9 ++- 4 files changed, 131 insertions(+), 15 deletions(-) diff --git a/rust/helpers.c b/rust/helpers.c index bc19f3b7b93e..7b12a6d4cf5c 100644 --- a/rust/helpers.c +++ b/rust/helpers.c @@ -222,6 +222,13 @@ void rust_helper_kunmap_local(const void *vaddr) } EXPORT_SYMBOL_GPL(rust_helper_kunmap_local); +void *rust_helper_alloc_inode_sb(struct super_block *sb, + struct kmem_cache *cache, gfp_t gfp) +{ + return alloc_inode_sb(sb, cache, gfp); +} +EXPORT_SYMBOL_GPL(rust_helper_alloc_inode_sb); + void rust_helper_i_uid_write(struct inode *inode, uid_t uid) { i_uid_write(inode, uid); diff --git a/rust/kernel/fs.rs b/rust/kernel/fs.rs index b1ad5c110dbb..b07203758674 100644 --- a/rust/kernel/fs.rs +++ b/rust/kernel/fs.rs @@ -9,8 +9,12 @@ use crate::error::{code::*, from_result, to_result, Error, Result}; use crate::folio::{LockedFolio, UniqueFolio}; use crate::types::{ARef, AlwaysRefCounted, Either, ForeignOwnable, Opaque, ScopeGuard}; -use crate::{bindings, init::PinInit, str::CStr, time::Timespec, try_pin_init, ThisModule}; -use core::{marker::PhantomData, marker::PhantomPinned, mem::ManuallyDrop, pin::Pin, ptr}; +use crate::{ + bindings, container_of, init::PinInit, mem_cache::MemCache, str::CStr, time::Timespec, + try_pin_init, ThisModule, +}; +use core::mem::{size_of, ManuallyDrop, MaybeUninit}; +use core::{marker::PhantomData, marker::PhantomPinned, pin::Pin, ptr}; use macros::{pin_data, pinned_drop}; #[cfg(CONFIG_BUFFER_HEAD)] @@ -35,6 +39,9 @@ pub trait FileSystem { /// Data associated with each file system instance (super-block). type Data: ForeignOwnable + Send + Sync; + /// Type of data associated with each inode. + type INodeData: Send + Sync; + /// The name of the file system type. const NAME: &'static CStr; @@ -165,6 +172,7 @@ fn try_from(v: u32) -> Result { pub struct Registration { #[pin] fs: Opaque, + inode_cache: Option, #[pin] _pin: PhantomPinned, } @@ -182,6 +190,14 @@ impl Registration { pub fn new(module: &'static ThisModule) -> impl PinInit { try_pin_init!(Self { _pin: PhantomPinned, + inode_cache: if size_of::() == 0 { + None + } else { + Some(MemCache::try_new::>( + T::NAME, + Some(Self::inode_init_once_callback::), + )?) + }, fs <- Opaque::try_ffi_init(|fs_ptr: *mut bindings::file_system_type| { // SAFETY: `try_ffi_init` guarantees that `fs_ptr` is valid for write. unsafe { fs_ptr.write(bindings::file_system_type::default()) }; @@ -239,6 +255,16 @@ pub fn new(module: &'static ThisModule) -> impl PinInit< unsafe { T::Data::from_foreign(ptr) }; } } + + unsafe extern "C" fn inode_init_once_callback( + outer_inode: *mut core::ffi::c_void, + ) { + let ptr = outer_inode.cast::>(); + + // SAFETY: This is only used in `new`, so we know that we have a valid `INodeWithData` + // instance whose inode part can be initialised. + unsafe { bindings::inode_init_once(ptr::addr_of_mut!((*ptr).inode)) }; + } } #[pinned_drop] @@ -280,6 +306,15 @@ pub fn super_block(&self) -> &SuperBlock { unsafe { &*(*self.0.get()).i_sb.cast() } } + /// Returns the data associated with the inode. + pub fn data(&self) -> &T::INodeData { + let outerp = container_of!(self.0.get(), INodeWithData, inode); + // SAFETY: `self` is guaranteed to be valid by the existence of a shared reference + // (`&self`) to it. Additionally, we know `T::INodeData` is always initialised in an + // `INode`. + unsafe { &*(*outerp).data.as_ptr() } + } + /// Returns the size of the inode contents. pub fn size(&self) -> i64 { // SAFETY: `self` is guaranteed to be valid by the existence of a shared reference. @@ -300,15 +335,29 @@ unsafe fn dec_ref(obj: ptr::NonNull) { } } +struct INodeWithData { + data: MaybeUninit, + inode: bindings::inode, +} + /// An inode that is locked and hasn't been initialised yet. #[repr(transparent)] pub struct NewINode(ARef>); impl NewINode { /// Initialises the new inode with the given parameters. - pub fn init(self, params: INodeParams) -> Result>> { - // SAFETY: This is a new inode, so it's safe to manipulate it mutably. - let inode = unsafe { &mut *self.0 .0.get() }; + pub fn init(self, params: INodeParams) -> Result>> { + let outerp = container_of!(self.0 .0.get(), INodeWithData, inode); + + // SAFETY: This is a newly-created inode. No other references to it exist, so it is + // safe to mutably dereference it. + let outer = unsafe { &mut *outerp.cast_mut() }; + + // N.B. We must always write this to a newly allocated inode because the free callback + // expects the data to be initialised and drops it. + outer.data.write(params.value); + + let inode = &mut outer.inode; let mode = match params.typ { INodeType::Dir => { @@ -424,7 +473,7 @@ pub enum INodeType { /// Required inode parameters. /// /// This is used when creating new inodes. -pub struct INodeParams { +pub struct INodeParams { /// The access mode. It's a mask that grants execute (1), write (2) and read (4) access to /// everyone, the owner group, and the owner. pub mode: u16, @@ -459,6 +508,9 @@ pub struct INodeParams { /// Last access time. pub atime: Timespec, + + /// Value to attach to this node. + pub value: T, } /// A file system super block. @@ -735,8 +787,12 @@ impl Tables { } const SUPER_BLOCK: bindings::super_operations = bindings::super_operations { - alloc_inode: None, - destroy_inode: None, + alloc_inode: if size_of::() != 0 { + Some(Self::alloc_inode_callback) + } else { + None + }, + destroy_inode: Some(Self::destroy_inode_callback), free_inode: None, dirty_inode: None, write_inode: None, @@ -766,6 +822,61 @@ impl Tables { shutdown: None, }; + unsafe extern "C" fn alloc_inode_callback( + sb: *mut bindings::super_block, + ) -> *mut bindings::inode { + // SAFETY: The callback contract guarantees that `sb` is valid for read. + let super_type = unsafe { (*sb).s_type }; + + // SAFETY: This callback is only used in `Registration`, so `super_type` is necessarily + // embedded in a `Registration`, which is guaranteed to be valid because it has a + // superblock associated to it. + let reg = unsafe { &*container_of!(super_type, Registration, fs) }; + + // SAFETY: `sb` and `cache` are guaranteed to be valid by the callback contract and by + // the existence of a superblock respectively. + let ptr = unsafe { + bindings::alloc_inode_sb(sb, MemCache::ptr(®.inode_cache), bindings::GFP_KERNEL) + } + .cast::>(); + if ptr.is_null() { + return ptr::null_mut(); + } + ptr::addr_of_mut!((*ptr).inode) + } + + unsafe extern "C" fn destroy_inode_callback(inode: *mut bindings::inode) { + // SAFETY: By the C contract, `inode` is a valid pointer. + let is_bad = unsafe { bindings::is_bad_inode(inode) }; + + // SAFETY: The inode is guaranteed to be valid by the callback contract. Additionally, the + // superblock is also guaranteed to still be valid by the inode existence. + let super_type = unsafe { (*(*inode).i_sb).s_type }; + + // SAFETY: This callback is only used in `Registration`, so `super_type` is necessarily + // embedded in a `Registration`, which is guaranteed to be valid because it has a + // superblock associated to it. + let reg = unsafe { &*container_of!(super_type, Registration, fs) }; + let ptr = container_of!(inode, INodeWithData, inode).cast_mut(); + + if !is_bad { + // SAFETY: The code either initialises the data or marks the inode as bad. Since the + // inode is not bad, the data is initialised, and thus safe to drop. + unsafe { ptr::drop_in_place((*ptr).data.as_mut_ptr()) }; + } + + if size_of::() == 0 { + // SAFETY: When the size of `INodeData` is zero, we don't use a separate mem_cache, so + // it is allocated from the regular mem_cache, which is what `free_inode_nonrcu` uses + // to free the inode. + unsafe { bindings::free_inode_nonrcu(inode) }; + } else { + // The callback contract guarantees that the inode was previously allocated via the + // `alloc_inode_callback` callback, so it is safe to free it back to the cache. + unsafe { bindings::kmem_cache_free(MemCache::ptr(®.inode_cache), ptr.cast()) }; + } + } + unsafe extern "C" fn statfs_callback( dentry: *mut bindings::dentry, buf: *mut bindings::kstatfs, @@ -1120,6 +1231,7 @@ fn init(module: &'static ThisModule) -> impl PinInit { /// struct MyFs; /// impl fs::FileSystem for MyFs { /// type Data = (); +/// type INodeData =(); /// const NAME: &'static CStr = c_str!("myfs"); /// fn super_params(_: &NewSuperBlock) -> Result> { /// todo!() diff --git a/rust/kernel/mem_cache.rs b/rust/kernel/mem_cache.rs index 05e5f2bc9781..bf6ce2d2d3e1 100644 --- a/rust/kernel/mem_cache.rs +++ b/rust/kernel/mem_cache.rs @@ -20,7 +20,6 @@ impl MemCache { /// Allocates a new `kmem_cache` for type `T`. /// /// `init` is called by the C code when entries are allocated. - #[allow(dead_code)] pub(crate) fn try_new( name: &'static CStr, init: Option, @@ -43,7 +42,6 @@ pub(crate) fn try_new( /// Returns the pointer to the `kmem_cache` instance, or null if it's `None`. /// /// This is a helper for functions like `alloc_inode_sb` where the cache is optional. - #[allow(dead_code)] pub(crate) fn ptr(c: &Option) -> *mut bindings::kmem_cache { match c { Some(m) => m.ptr.as_ptr(), diff --git a/samples/rust/rust_rofs.rs b/samples/rust/rust_rofs.rs index 093425650f26..dfe745439842 100644 --- a/samples/rust/rust_rofs.rs +++ b/samples/rust/rust_rofs.rs @@ -53,6 +53,7 @@ struct Entry { struct RoFs; impl fs::FileSystem for RoFs { type Data = (); + type INodeData = &'static Entry; const NAME: &'static CStr = c_str!("rust-fs"); fn super_params(_sb: &NewSuperBlock) -> Result> { @@ -79,6 +80,7 @@ fn init_root(sb: &SuperBlock) -> Result>> { atime: UNIX_EPOCH, ctime: UNIX_EPOCH, mtime: UNIX_EPOCH, + value: &ENTRIES[0], }), } } @@ -122,6 +124,7 @@ fn lookup(parent: &INode, name: &[u8]) -> Result>> { atime: UNIX_EPOCH, ctime: UNIX_EPOCH, mtime: UNIX_EPOCH, + value: e, }), }; } @@ -131,11 +134,7 @@ fn lookup(parent: &INode, name: &[u8]) -> Result>> { } fn read_folio(inode: &INode, mut folio: LockedFolio<'_>) -> Result { - let data = match inode.ino() { - 2 => ENTRIES[2].contents, - 3 => ENTRIES[3].contents, - _ => return Err(EINVAL), - }; + let data = inode.data().contents; let pos = usize::try_from(folio.pos()).unwrap_or(usize::MAX); let copied = if pos >= data.len() { From patchwork Wed Oct 18 12:25:17 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wedson Almeida Filho X-Patchwork-Id: 13426968 Received: from lindbergh.monkeyblade.net (lindbergh.monkeyblade.net [23.128.96.19]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D973636AF4; Wed, 18 Oct 2023 12:27:00 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="SYkpmUX0" Received: from mail-pl1-x62b.google.com (mail-pl1-x62b.google.com [IPv6:2607:f8b0:4864:20::62b]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id B0804112; Wed, 18 Oct 2023 05:26:59 -0700 (PDT) Received: by mail-pl1-x62b.google.com with SMTP id d9443c01a7336-1c9bca1d96cso46359195ad.3; Wed, 18 Oct 2023 05:26:59 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1697632019; x=1698236819; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=g8+DWlglX7SVD5ZBkaFTJfIFUorn1UXuo+uacl/Dd2w=; b=SYkpmUX08FCj/ef78QRCPACay7Z4AKpGKL8Qo233rDQjL7MQYasFx5MQjNpcylJr5G 4ab8y4jDfcxMZzuVVp/4BrRdhP+vzS6HmaRKrtxhMYP5RmrQvx8BjtBey08hxkoFNdeE lCi/KUuzduXqzciwBbRqOvTum6coDX4h+VFzd/FbafBetU8U3Jd3p/xbaYjsFQmVoaJq NKgES9gVhZ/3HqL4fhWonRQCagKVjwZcPsqAmupvzCq05z1vjL4E59vSC+SmJgX7fC5m lDRZ1Pal1MoeR1hAky7A/URRP5MV9BZ0ilK3T7XxefS+PaQbCPHHgEUe7FCVgqEPa7Xl j3/g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1697632019; x=1698236819; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=g8+DWlglX7SVD5ZBkaFTJfIFUorn1UXuo+uacl/Dd2w=; b=hnydRF7YiV46PBvjUWJrQhWVwlp/pyEGCdgkIa7FOYmi3aql9Vz8/K/w4pKWQGvyHk ZhHLRw3wN5Hu2N2vuevy7cQ2HHt1QyY0TLUCWfE5bPfWQgAlu4B3CcL6Hg8yG3KS0J61 HNrUucfEQ2ZUNLUy4QcUabXFwSD419/OeIqzfJVb+3wdZaHobLbUkkz8SoyDbw/fc7ML ep2MGRMH9KiwlwNnyK9Lt9iFliO14T7HfzBpX3YoY7zfUCvCK8kJPB31TSQL5c4pIICO 1LQxra29HW2xbfYSiprYORsM6r/dahqFvexDxVA3yPyWIf0fG0Mg3acm6fbxQVCB6FWt z0GQ== X-Gm-Message-State: AOJu0YwO//8Dh8Sn+KEnLcCW3Ir5+DhwpQ9GQram78N9AH1M07aFfX48 W8k+5bI7YpVyddB6PzrtpIRIv5YWdk4= X-Google-Smtp-Source: AGHT+IEPEG68E5b+I+fTMfrD4FGmDQKMn4J9FhpuHwxW1NHdKzro4p29A2dxB3N/1w74bpJ3ejSh0Q== X-Received: by 2002:a17:903:2283:b0:1c9:e2ed:66fe with SMTP id b3-20020a170903228300b001c9e2ed66femr5583262plh.52.1697632019096; Wed, 18 Oct 2023 05:26:59 -0700 (PDT) Received: from wedsonaf-dev.. ([2804:389:7122:43b8:9b73:6339:3351:cce0]) by smtp.googlemail.com with ESMTPSA id j1-20020a170902c3c100b001c736b0037fsm3411046plj.231.2023.10.18.05.26.54 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 18 Oct 2023 05:26:58 -0700 (PDT) From: Wedson Almeida Filho To: Alexander Viro , Christian Brauner , Matthew Wilcox Cc: Kent Overstreet , Greg Kroah-Hartman , linux-fsdevel@vger.kernel.org, rust-for-linux@vger.kernel.org, Wedson Almeida Filho Subject: [RFC PATCH 18/19] rust: fs: export file type from mode constants Date: Wed, 18 Oct 2023 09:25:17 -0300 Message-Id: <20231018122518.128049-19-wedsonaf@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20231018122518.128049-1-wedsonaf@gmail.com> References: <20231018122518.128049-1-wedsonaf@gmail.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM, RCVD_IN_DNSWL_NONE,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net From: Wedson Almeida Filho Allow Rust file system modules to use these constants if needed. Signed-off-by: Wedson Almeida Filho --- rust/kernel/fs.rs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/rust/kernel/fs.rs b/rust/kernel/fs.rs index b07203758674..235a86ed1127 100644 --- a/rust/kernel/fs.rs +++ b/rust/kernel/fs.rs @@ -20,6 +20,33 @@ #[cfg(CONFIG_BUFFER_HEAD)] pub mod buffer; +/// Contains constants related to Linux file modes. +pub mod mode { + /// A bitmask used to the file type from a mode value. + pub const S_IFMT: u32 = bindings::S_IFMT; + + /// File type constant for block devices. + pub const S_IFBLK: u32 = bindings::S_IFBLK; + + /// File type constant for char devices. + pub const S_IFCHR: u32 = bindings::S_IFCHR; + + /// File type constant for directories. + pub const S_IFDIR: u32 = bindings::S_IFDIR; + + /// File type constant for pipes. + pub const S_IFIFO: u32 = bindings::S_IFIFO; + + /// File type constant for symbolic links. + pub const S_IFLNK: u32 = bindings::S_IFLNK; + + /// File type constant for regular files. + pub const S_IFREG: u32 = bindings::S_IFREG; + + /// File type constant for sockets. + pub const S_IFSOCK: u32 = bindings::S_IFSOCK; +} + /// Maximum size of an inode. pub const MAX_LFS_FILESIZE: i64 = bindings::MAX_LFS_FILESIZE; From patchwork Wed Oct 18 12:25:18 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wedson Almeida Filho X-Patchwork-Id: 13426969 Received: from lindbergh.monkeyblade.net (lindbergh.monkeyblade.net [23.128.96.19]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 99131341A5; Wed, 18 Oct 2023 12:27:06 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="G40kn9DB" Received: from mail-pl1-x634.google.com (mail-pl1-x634.google.com [IPv6:2607:f8b0:4864:20::634]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 408B1112; Wed, 18 Oct 2023 05:27:04 -0700 (PDT) Received: by mail-pl1-x634.google.com with SMTP id d9443c01a7336-1c9d407bb15so57083175ad.0; Wed, 18 Oct 2023 05:27:04 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1697632024; x=1698236824; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=WW97z1/PhNr6yRKp2DsDMj86gMe+7lyob+R76qs++n4=; b=G40kn9DB/dl2OpeoLOPTVOB0AUjKmHkSE+5jKpeR+vu1CITOFijR28hDTfI0wrdaof NCsDXPy9HD2aLae9aXYff5BhRO/oTYZBFQjNlQ9JHtTPBuA6qqS+7y/W8oc+AIohLzdj i2DPI2Kbqm330iLH0idn2f5GIZylvUC76H0+MhFkk9oxVbHNhWibkcMB9C25RvsjfSt5 YCHpzkz6asQmPib5TO0XBp4SpQJgm44od3eRr8yuwV9cEkw8JHbRkcGKBIz2dAcKbSGM xRgXBL+/YIieuc2HxMfKshRpk8DQ6pjfo+HDrPxtGajpCYXzLoIYC2TJLytmcPz+mLH8 8t6g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1697632024; x=1698236824; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=WW97z1/PhNr6yRKp2DsDMj86gMe+7lyob+R76qs++n4=; b=L1WiCsC+bUGfp3ityD2DicYRICDMFf8VOJ19AR2Ykb2kp6kpXwinuTqwW3CxGC8q22 e7JBfj3zt9lHWFMtwfC+qZRwIYDw5lHDnLA+ifsC0i5LKnOHAo62Wx4gRvZd6dgDa8aq 0K+p4FhQS4ak20mAAeUHB0oGe09qmaOD1EcZxRgqcZn5GkjkFtiyGpzxhSlMjlWLfR2v wajuPxO3hPuWLwLesOhfsrQbSSY4eaCW6KLAZYh+pxgA3qbZR71UDHeKTSsz/NtQovQp ceQaVCpGDjN1qpfmwm3qXXXGQnYWdXwR8sNYfaVp1S7qb7jhEzqf6HUm1NGLth8nkeIi 636A== X-Gm-Message-State: AOJu0Yyh1rRirVyN4I9S/7K7vPwdUM/5IW8lHVX9lSqXbw8UhzXenXzU hyCA4ynSUFytjtZ9bDPZLxA= X-Google-Smtp-Source: AGHT+IFJMhkaG5JqJbKqTYW4s3HNsRTDHmpOm68nw+3WbyX3+kZ7yOAy+rfixB8SUYGtMBEjy44Y0g== X-Received: by 2002:a17:902:fb87:b0:1ca:1c55:abcf with SMTP id lg7-20020a170902fb8700b001ca1c55abcfmr4724487plb.3.1697632023529; Wed, 18 Oct 2023 05:27:03 -0700 (PDT) Received: from wedsonaf-dev.. ([2804:389:7122:43b8:9b73:6339:3351:cce0]) by smtp.googlemail.com with ESMTPSA id j1-20020a170902c3c100b001c736b0037fsm3411046plj.231.2023.10.18.05.26.59 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 18 Oct 2023 05:27:03 -0700 (PDT) From: Wedson Almeida Filho To: Alexander Viro , Christian Brauner , Matthew Wilcox Cc: Kent Overstreet , Greg Kroah-Hartman , linux-fsdevel@vger.kernel.org, rust-for-linux@vger.kernel.org, Wedson Almeida Filho Subject: [RFC PATCH 19/19] tarfs: introduce tar fs Date: Wed, 18 Oct 2023 09:25:18 -0300 Message-Id: <20231018122518.128049-20-wedsonaf@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20231018122518.128049-1-wedsonaf@gmail.com> References: <20231018122518.128049-1-wedsonaf@gmail.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM, RCVD_IN_DNSWL_BLOCKED,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net From: Wedson Almeida Filho It is a file system based on tar files and an index appended to them (to facilitate finding fs entries without having to traverse the whole tar file). Signed-off-by: Wedson Almeida Filho --- fs/Kconfig | 1 + fs/Makefile | 1 + fs/tarfs/Kconfig | 16 ++ fs/tarfs/Makefile | 8 + fs/tarfs/defs.rs | 80 ++++++++ fs/tarfs/tar.rs | 322 ++++++++++++++++++++++++++++++ scripts/generate_rust_analyzer.py | 2 +- 7 files changed, 429 insertions(+), 1 deletion(-) create mode 100644 fs/tarfs/Kconfig create mode 100644 fs/tarfs/Makefile create mode 100644 fs/tarfs/defs.rs create mode 100644 fs/tarfs/tar.rs diff --git a/fs/Kconfig b/fs/Kconfig index aa7e03cc1941..f4b8c33ea624 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -331,6 +331,7 @@ source "fs/sysv/Kconfig" source "fs/ufs/Kconfig" source "fs/erofs/Kconfig" source "fs/vboxsf/Kconfig" +source "fs/tarfs/Kconfig" endif # MISC_FILESYSTEMS diff --git a/fs/Makefile b/fs/Makefile index f9541f40be4e..e3389f8b049d 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -129,3 +129,4 @@ obj-$(CONFIG_EFIVAR_FS) += efivarfs/ obj-$(CONFIG_EROFS_FS) += erofs/ obj-$(CONFIG_VBOXSF_FS) += vboxsf/ obj-$(CONFIG_ZONEFS_FS) += zonefs/ +obj-$(CONFIG_TARFS_FS) += tarfs/ diff --git a/fs/tarfs/Kconfig b/fs/tarfs/Kconfig new file mode 100644 index 000000000000..d3e19eb2adbc --- /dev/null +++ b/fs/tarfs/Kconfig @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: GPL-2.0-only +# + +config TARFS_FS + tristate "TAR file system support" + depends on RUST && BLOCK + select BUFFER_HEAD + help + This is a simple read-only file system intended for mounting + tar files that have had an index appened to them. + + To compile this file system support as a module, choose M here: the + module will be called tarfs. + + If you don't know whether you need it, then you don't need it: + answer N. diff --git a/fs/tarfs/Makefile b/fs/tarfs/Makefile new file mode 100644 index 000000000000..011c5d64fbe3 --- /dev/null +++ b/fs/tarfs/Makefile @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for the linux tarfs filesystem routines. +# + +obj-$(CONFIG_TARFS_FS) += tarfs.o + +tarfs-y := tar.o diff --git a/fs/tarfs/defs.rs b/fs/tarfs/defs.rs new file mode 100644 index 000000000000..7481b75aaab2 --- /dev/null +++ b/fs/tarfs/defs.rs @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Definitions of tarfs structures. + +use kernel::types::LE; + +/// Flags used in [`Inode::flags`]. +pub mod inode_flags { + /// Indicates that the inode is opaque. + /// + /// When set, inode will have the "trusted.overlay.opaque" set to "y" at runtime. + pub const OPAQUE: u8 = 0x1; +} + +kernel::derive_readable_from_bytes! { + /// An inode in the tarfs inode table. + #[repr(C)] + pub struct Inode { + /// The mode of the inode. + /// + /// The bottom 9 bits are the rwx bits for owner, group, all. + /// + /// The bits in the [`S_IFMT`] mask represent the file mode. + pub mode: LE, + + /// Tarfs flags for the inode. + /// + /// Values are drawn from the [`inode_flags`] module. + pub flags: u8, + + /// The bottom 4 bits represent the top 4 bits of mtime. + pub hmtime: u8, + + /// The owner of the inode. + pub owner: LE, + + /// The group of the inode. + pub group: LE, + + /// The bottom 32 bits of mtime. + pub lmtime: LE, + + /// Size of the contents of the inode. + pub size: LE, + + /// Either the offset to the data, or the major and minor numbers of a device. + /// + /// For the latter, the 32 LSB are the minor, and the 32 MSB are the major numbers. + pub offset: LE, + } + + /// An entry in a tarfs directory entry table. + #[repr(C)] + pub struct DirEntry { + /// The inode number this entry refers to. + pub ino: LE, + + /// The offset to the name of the entry. + pub name_offset: LE, + + /// The length of the name of the entry. + pub name_len: LE, + + /// The type of entry. + pub etype: u8, + + /// Unused padding. + pub _padding: [u8; 7], + } + + /// The super-block of a tarfs instance. + #[repr(C)] + pub struct Header { + /// The offset to the beginning of the inode-table. + pub inode_table_offset: LE, + + /// The number of inodes in the file system. + pub inode_count: LE, + } +} diff --git a/fs/tarfs/tar.rs b/fs/tarfs/tar.rs new file mode 100644 index 000000000000..1a71b1ccf8e7 --- /dev/null +++ b/fs/tarfs/tar.rs @@ -0,0 +1,322 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! File system based on tar files and an index. + +use core::mem::size_of; +use defs::*; +use kernel::fs::{ + DirEmitter, DirEntryType, INode, INodeParams, INodeType, NewSuperBlock, Stat, Super, + SuperBlock, SuperParams, +}; +use kernel::types::{ARef, Either, FromBytes}; +use kernel::{c_str, folio::Folio, folio::LockedFolio, fs, prelude::*}; + +pub mod defs; + +kernel::module_fs! { + type: TarFs, + name: "tarfs", + author: "Wedson Almeida Filho ", + description: "File system for indexed tar files", + license: "GPL", +} + +const SECTOR_SIZE: u64 = 512; +const TARFS_BSIZE: u64 = 1 << TARFS_BSIZE_BITS; +const TARFS_BSIZE_BITS: u8 = 12; +const SECTORS_PER_BLOCK: u64 = TARFS_BSIZE / SECTOR_SIZE; +const TARFS_MAGIC: u32 = 0x54415246; + +static_assert!(SECTORS_PER_BLOCK > 0); + +struct INodeData { + offset: u64, + flags: u8, +} + +struct TarFs { + data_size: u64, + inode_table_offset: u64, + inode_count: u64, +} + +impl TarFs { + fn iget(sb: &SuperBlock, ino: u64) -> Result>> { + // Check that the inode number is valid. + let h = sb.data(); + if ino == 0 || ino > h.inode_count { + return Err(ENOENT); + } + + // Create an inode or find an existing (cached) one. + let inode = match sb.get_or_create_inode(ino)? { + Either::Left(existing) => return Ok(existing), + Either::Right(new) => new, + }; + + static_assert!((TARFS_BSIZE as usize) % size_of::() == 0); + + // Load inode details from storage. + let offset = h.inode_table_offset + (ino - 1) * u64::try_from(size_of::())?; + + let bh = sb.bread(offset / TARFS_BSIZE)?; + let b = bh.data(); + let idata = Inode::from_bytes(b, (offset & (TARFS_BSIZE - 1)) as usize).ok_or(EIO)?; + + let mode = idata.mode.value(); + + // Ignore inodes that have unknown mode bits. + if (u32::from(mode) & !(fs::mode::S_IFMT | 0o777)) != 0 { + return Err(ENOENT); + } + + let doffset = idata.offset.value(); + let size = idata.size.value().try_into()?; + let secs = u64::from(idata.lmtime.value()) | (u64::from(idata.hmtime & 0xf) << 32); + let ts = kernel::time::Timespec::new(secs, 0)?; + let typ = match u32::from(mode) & fs::mode::S_IFMT { + fs::mode::S_IFREG => INodeType::Reg, + fs::mode::S_IFDIR => INodeType::Dir, + fs::mode::S_IFLNK => INodeType::Lnk, + fs::mode::S_IFSOCK => INodeType::Sock, + fs::mode::S_IFIFO => INodeType::Fifo, + fs::mode::S_IFCHR => INodeType::Chr((doffset >> 32) as u32, doffset as u32), + fs::mode::S_IFBLK => INodeType::Blk((doffset >> 32) as u32, doffset as u32), + _ => return Err(ENOENT), + }; + inode.init(INodeParams { + typ, + mode: mode & 0o777, + size, + blocks: (idata.size.value() + TARFS_BSIZE - 1) / TARFS_BSIZE, + nlink: 1, + uid: idata.owner.value(), + gid: idata.group.value(), + ctime: ts, + mtime: ts, + atime: ts, + value: INodeData { + offset: doffset, + flags: idata.flags, + }, + }) + } + + fn name_eq(sb: &SuperBlock, mut name: &[u8], offset: u64) -> Result { + for v in sb.read(offset, name.len().try_into()?)? { + let v = v?; + let b = v.data(); + if b != &name[..b.len()] { + return Ok(false); + } + name = &name[b.len()..]; + } + Ok(true) + } + + fn read_name(sb: &SuperBlock, mut name: &mut [u8], offset: u64) -> Result { + for v in sb.read(offset, name.len().try_into()?)? { + let v = v?; + let b = v.data(); + name[..b.len()].copy_from_slice(b); + name = &mut name[b.len()..]; + } + Ok(true) + } +} + +impl fs::FileSystem for TarFs { + type Data = Box; + type INodeData = INodeData; + const NAME: &'static CStr = c_str!("tar"); + const SUPER_TYPE: Super = Super::BlockDev; + + fn super_params(sb: &NewSuperBlock) -> Result> { + let scount = sb.sector_count()?; + if scount < SECTORS_PER_BLOCK { + pr_err!("Block device is too small: sector count={scount}\n"); + return Err(ENXIO); + } + + let tarfs = { + let mut folio = Folio::try_new(0)?; + sb.sread( + (scount / SECTORS_PER_BLOCK - 1) * SECTORS_PER_BLOCK, + SECTORS_PER_BLOCK as usize, + &mut folio, + )?; + let mapped = folio.map_page(0)?; + let hdr = + Header::from_bytes(&mapped, (TARFS_BSIZE - SECTOR_SIZE) as usize).ok_or(EIO)?; + Box::try_new(TarFs { + inode_table_offset: hdr.inode_table_offset.value(), + inode_count: hdr.inode_count.value(), + data_size: scount.checked_mul(SECTOR_SIZE).ok_or(ERANGE)?, + })? + }; + + // Check that the inode table starts within the device data and is aligned to the block + // size. + if tarfs.inode_table_offset >= tarfs.data_size { + pr_err!( + "inode table offset beyond data size: {} >= {}\n", + tarfs.inode_table_offset, + tarfs.data_size + ); + return Err(E2BIG); + } + + if tarfs.inode_table_offset % SECTOR_SIZE != 0 { + pr_err!( + "inode table offset not aligned to sector size: {}\n", + tarfs.inode_table_offset, + ); + return Err(EDOM); + } + + // Check that the last inode is within bounds (and that there is no overflow when + // calculating its offset). + let offset = tarfs + .inode_count + .checked_mul(u64::try_from(size_of::())?) + .ok_or(ERANGE)? + .checked_add(tarfs.inode_table_offset) + .ok_or(ERANGE)?; + if offset > tarfs.data_size { + pr_err!( + "inode table extends beyond the data size : {} > {}\n", + tarfs.inode_table_offset + (tarfs.inode_count * size_of::() as u64), + tarfs.data_size, + ); + return Err(E2BIG); + } + + Ok(SuperParams { + magic: TARFS_MAGIC, + blocksize_bits: TARFS_BSIZE_BITS, + maxbytes: fs::MAX_LFS_FILESIZE, + time_gran: 1000000000, + data: tarfs, + }) + } + + fn init_root(sb: &SuperBlock) -> Result>> { + Self::iget(sb, 1) + } + + fn read_dir(inode: &INode, emitter: &mut DirEmitter) -> Result { + let sb = inode.super_block(); + let mut name = Vec::::new(); + let pos = emitter.pos(); + + if pos < 0 || pos % size_of::() as i64 != 0 { + return Err(ENOENT); + } + + if pos >= inode.size() { + return Ok(()); + } + + // Make sure the inode data doesn't overflow the data area. + let size = u64::try_from(inode.size())?; + if inode.data().offset.checked_add(size).ok_or(EIO)? > sb.data().data_size { + return Err(EIO); + } + + for v in sb.read(inode.data().offset + pos as u64, size - pos as u64)? { + for e in DirEntry::from_bytes_to_slice(v?.data()).ok_or(EIO)? { + let name_len = usize::try_from(e.name_len.value())?; + if name_len > name.len() { + name.try_resize(name_len, 0)?; + } + + Self::read_name(sb, &mut name[..name_len], e.name_offset.value())?; + + if !emitter.emit( + size_of::() as i64, + &name[..name_len], + e.ino.value(), + DirEntryType::try_from(u32::from(e.etype))?, + ) { + return Ok(()); + } + } + } + + Ok(()) + } + + fn lookup(parent: &INode, name: &[u8]) -> Result>> { + let name_len = u64::try_from(name.len())?; + let sb = parent.super_block(); + + for v in sb.read(parent.data().offset, parent.size().try_into()?)? { + for e in DirEntry::from_bytes_to_slice(v?.data()).ok_or(EIO)? { + if e.name_len.value() != name_len || e.name_len.value() > usize::MAX as u64 { + continue; + } + if Self::name_eq(sb, name, e.name_offset.value())? { + return Self::iget(sb, e.ino.value()); + } + } + } + + Err(ENOENT) + } + + fn read_folio(inode: &INode, mut folio: LockedFolio<'_>) -> Result { + let pos = u64::try_from(folio.pos()).unwrap_or(u64::MAX); + let size = u64::try_from(inode.size())?; + let sb = inode.super_block(); + + let copied = if pos >= size { + 0 + } else { + let offset = inode.data().offset.checked_add(pos).ok_or(ERANGE)?; + let len = core::cmp::min(size - pos, folio.size().try_into()?); + let mut foffset = 0; + + if offset.checked_add(len).ok_or(ERANGE)? > sb.data().data_size { + return Err(EIO); + } + + for v in sb.read(offset, len)? { + let v = v?; + folio.write(foffset, v.data())?; + foffset += v.data().len(); + } + foffset + }; + + folio.zero_out(copied, folio.size() - copied)?; + folio.mark_uptodate(); + folio.flush_dcache(); + + Ok(()) + } + + fn read_xattr(inode: &INode, name: &CStr, outbuf: &mut [u8]) -> Result { + if inode.data().flags & inode_flags::OPAQUE == 0 + || name.as_bytes() != b"trusted.overlay.opaque" + { + return Err(ENODATA); + } + + if !outbuf.is_empty() { + outbuf[0] = b'y'; + } + + Ok(1) + } + + fn statfs(sb: &SuperBlock) -> Result { + let data = sb.data(); + Ok(Stat { + magic: TARFS_MAGIC, + namelen: i64::MAX, + bsize: TARFS_BSIZE as _, + blocks: data.inode_table_offset / TARFS_BSIZE, + files: data.inode_count, + }) + } +} diff --git a/scripts/generate_rust_analyzer.py b/scripts/generate_rust_analyzer.py index fc52bc41d3e7..8dc74991894e 100755 --- a/scripts/generate_rust_analyzer.py +++ b/scripts/generate_rust_analyzer.py @@ -116,7 +116,7 @@ def generate_crates(srctree, objtree, sysroot_src, external_src, cfgs): # Then, the rest outside of `rust/`. # # We explicitly mention the top-level folders we want to cover. - extra_dirs = map(lambda dir: srctree / dir, ("samples", "drivers")) + extra_dirs = map(lambda dir: srctree / dir, ("samples", "drivers", "fs")) if external_src is not None: extra_dirs = [external_src] for folder in extra_dirs: