From patchwork Tue May 14 13:16:42 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wedson Almeida Filho X-Patchwork-Id: 13664119 Received: from mail-pl1-f178.google.com (mail-pl1-f178.google.com [209.85.214.178]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 725A91448FA; Tue, 14 May 2024 13:17:35 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.178 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715692656; cv=none; b=cdPJdGWpLhOFw+skeMuucJuYVNP3N8llzD+5ubgMynJr1bOdegu0PHKHanLcrthAABcGRkXybiTpvZMQScq3cSnx4hP/BO4cD7Di0ZrBGYoRCsM0RQ3urnwYcm9FyDA1F74GNsJIvaQfYBoK130482mAU8ISIFLPDsBp2/v2pNY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715692656; c=relaxed/simple; bh=ulT/s5cX3j0svT95px7lkL8CsGhoceWqlVXavAXUDtA=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=p1zlYIhVjPefvIG0Jx77L71N9VdauPcHvo2tWTlBDu+3sqD3GoXpglOsEou8oplXVzO+/p9k9q1EcPB26NBGymUAvo21fPpaBNhBnF88ikd3dEzwf88pfWPKov3QMo6mTvsMbH8qd25RRJF2QF3fk81yd8OTJMuKlaaHprWM2D0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=TPZ/+k1F; arc=none smtp.client-ip=209.85.214.178 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="TPZ/+k1F" Received: by mail-pl1-f178.google.com with SMTP id d9443c01a7336-1ec4b2400b6so46101265ad.3; Tue, 14 May 2024 06:17:35 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1715692655; x=1716297455; 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=zxXuPD4yM3GcJ9ABIVPjOPrjHsRflgIYjf+iCopwXMY=; b=TPZ/+k1Fo0NFxBhGFcSzq0ZoKO4dF3UGvHX7CLJXeueGHehDFxrSbtGVJA3xXetNKG /G4dsWSNPlkOGFo7D4B7dLJEzZZ0oZvA7kLeu1060773ay5V/XAwpsbTOLA9dAKOX6Lr lWUU7hB7sIC8Qd8G3teeF6KpceWGm98w5WvWXJvqSaYUpPIERf68oCC4EWJcAdqGxNm/ DJk5rDMIxtChdOD96In+mIucaj8y5jD+uahC01+FWSZ+EaxsjvQw2sRMp9x5j/G2CEcA FFLc9J/nCVhJy4fweH7sKfzSS/qHprNTMsn5KCk91G0YHwT8a4+q0zuhl3t1++VSUMSL 1ssQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1715692655; x=1716297455; 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=zxXuPD4yM3GcJ9ABIVPjOPrjHsRflgIYjf+iCopwXMY=; b=sT7XBkEbbeGStmk2j5TvBY1aaWhgm4lwVdyRRbWJDtZYg4OA+f/pMRjbOQo4AN7ryP dvJFvAEJq1xM86PXUW4lRJX2VE6t6cFq+C9RM25BMBky5e6XjeYs3PlhHf3sIzIyvuUz crZrJq843lbnRdHCLsHIqtF14LmYb70u0ALJtsTF5n3/q6dXEIj/Mrbom6E7XsEzXTHG GNKxno2l+n4sZ1yqBgssi4jC8g+Ou4LrzfD+3yZ6vynp6PCbdChSIq5ADkQfD0bvEV/E lwIROvW/Ox9+c5zQih+cQA/MW6VdDZqLfqt2cqndjMUpNf9lWU2UIawlg0l692Ktnbow HEfg== X-Forwarded-Encrypted: i=1; AJvYcCVZXTp4E8R2SY7nV2xhkEM3D0gipHL48GBaboipEnPRC4VqC+uB2KuMSQQvRylIDHuciI8FMz4Fms1yHHWor7LUgmbHNSQmBtEbM+RAk7gREsFlajs/j0sMwSWssyc9Zl4WuS3li8jIiIwieVFfnrhZVC5PBiDw03uiF5GrEVfybEJsQJppTjB0W1yV X-Gm-Message-State: AOJu0YyjJTTLHDcJrVV8LX92yh9iucARHVLOBxWFddLwuKWgdBqgxwsS ttabhP5dqhuxENOEi2oh63AkyQlfXcqS6xCAOJ7px0Ir7lmn2R83 X-Google-Smtp-Source: AGHT+IFxVcCYWwJhDo8hWiX/8GuxvpmQzY3PlTbvlhuiaypmP9HyXTHk6tHmOXY1QMWi3EBtWm06Ug== X-Received: by 2002:a17:902:8e8b:b0:1e6:7700:1698 with SMTP id d9443c01a7336-1ef43e231f7mr116523535ad.35.1715692654698; Tue, 14 May 2024 06:17:34 -0700 (PDT) Received: from wedsonaf-dev.. ([50.204.89.32]) by smtp.googlemail.com with ESMTPSA id d9443c01a7336-1ef0b9d18a4sm97277335ad.56.2024.05.14.06.17.33 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 14 May 2024 06:17:34 -0700 (PDT) From: Wedson Almeida Filho To: Alexander Viro , Christian Brauner , Matthew Wilcox , Dave Chinner Cc: Kent Overstreet , Greg Kroah-Hartman , linux-fsdevel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, Wedson Almeida Filho Subject: [RFC PATCH v2 01/30] rust: fs: add registration/unregistration of file systems Date: Tue, 14 May 2024 10:16:42 -0300 Message-Id: <20240514131711.379322-2-wedsonaf@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240514131711.379322-1-wedsonaf@gmail.com> References: <20240514131711.379322-1-wedsonaf@gmail.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 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/kernel/error.rs | 2 -- rust/kernel/fs.rs | 75 ++++++++++++++++++++++++++++++++++++++++++++ rust/kernel/lib.rs | 1 + 3 files changed, 76 insertions(+), 2 deletions(-) create mode 100644 rust/kernel/fs.rs diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs index b248a4c22fb4..f4fa2847e210 100644 --- a/rust/kernel/error.rs +++ b/rust/kernel/error.rs @@ -308,8 +308,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..cc1ed7ed2f54 --- /dev/null +++ b/rust/kernel/fs.rs @@ -0,0 +1,75 @@ +// 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`](srctree/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::{ffi, 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, +} + +// 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 { + 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: *mut bindings::fs_context) -> 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 4b629aa94735..e664f80b8141 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -31,6 +31,7 @@ mod build_assert; pub mod error; pub mod file; +pub mod fs; pub mod init; pub mod ioctl; #[cfg(CONFIG_KUNIT)] From patchwork Tue May 14 13:16:43 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wedson Almeida Filho X-Patchwork-Id: 13664120 Received: from mail-pl1-f175.google.com (mail-pl1-f175.google.com [209.85.214.175]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 397C9144D3A; Tue, 14 May 2024 13:17:37 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.175 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715692658; cv=none; b=jIK8Dei5oeY9B/p4bRHnkWhr0nt7lcMoP0XeSwVXx5w7IaJGxaadB4C37/0qh7oFrGkmRzNVXrVJlxfLvRkfpSEOzw7raZAWzaqYUehYgReaLKqrWm7XyTETX24kA/VLUjg0tDuN9WwiPm8vjzfQYTY7zCh/KQUcSWqZwjdBmBU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715692658; c=relaxed/simple; bh=FnRY1u4YwwnOW6PD08/fpU/dFshvY9j1Vl1jbm0E9ig=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=uaGT7y8obV6ThHrJ6I6Nj1GrREuS5S52F5IfOCUn5cutpO0BW/LRFhtSKBIPfWcEJ0BWA0pCcEye7BY53ocNEN9FYLjqNIPeMGBx+3ukyzZ4zslKCdCcEnx9R0MElgaO89tUU7Z6BnCHau52+RS7/UOHRqDPJSd2CwcOnQqdavs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=RYOSYhLo; arc=none smtp.client-ip=209.85.214.175 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="RYOSYhLo" Received: by mail-pl1-f175.google.com with SMTP id d9443c01a7336-1ec92e355bfso52579165ad.3; Tue, 14 May 2024 06:17:37 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1715692656; x=1716297456; 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=rGJsPpk+V4pg7g+WZPwCWfXSyJlMwREYYXFaZU3M6G8=; b=RYOSYhLoKJEpE4Df0CLYiu02f0X6POzYBIGjHnzBqaiuoVT5tCMOzRUqfuHd+j9DqH ykoT2WNJ8f9CXr21VKrCENsf8XYDntp0Y/piWSCEcizpza2o8El2i8WAuDTMq2YhPoDE 0xkDZjq+W3FmQrzH55R2N5JQ+MQD16PFSS3sCwyf1rzNhNF05BG1zN36eS/4HbGR5BW5 IuEcMBMJ7QQgsE4vT5q3wEUjze3lCSp+N7IcSsVD/18FULZAE/Si8HRy1oAhlk2Opqof MYqTsoFeNPCE8hJX4oo6LXAVLZYOxCEuApxInqCgOAQgNbGZqmaF+FvhICha6sDuCgG8 GDOg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1715692656; x=1716297456; 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=rGJsPpk+V4pg7g+WZPwCWfXSyJlMwREYYXFaZU3M6G8=; b=bjfG4ODxqBZtKjRnz5DbdNiv42UAqutgIXqrWsa+HPuI7CX1DRkyDkn5OBCxBendVX vX4Lu4vTWyoUGqr77z52Gy/5iZFJ23EyvVeOdeAzZtxjiDrYfu/ODnPQjy+Pprhj2CK0 TFSebRrHMBBm4y+XU6vSGSX6kU7Ku/JdTGmxhNjVLmQrVjM2m4JVjHQ/53AYJDbZNYKl Rn5M/WUxHlo0cUo7a7SGoMLYZIaGpweKlLUNLRH0veJY3xwgEqTXtIBvuDrJ8Jhzm7zD /vJTIr7E9M1Q4g1M3YZRbtE5KD0hg+sDRb2TRDPLrbSV6WYpfGLOkpZWNhc1DKbr+8P9 z0bw== X-Forwarded-Encrypted: i=1; AJvYcCUnybLwGZtMlOD25okeK8eH0TdH5cUxsExv1t/LcPjMzcFGN1ls9VHZi0EZG0OQOwUBUqIq+AOywIvn1FmbOjezAZaRW3zMeuEZ5THyRAf0ypzfdNnuwsiV66PHY/Dib0uGb9CEECVO49G6FP9njcYEg7fjeMpJuABZ1ZO4pg42tAOmrvQMp17QWyMm X-Gm-Message-State: AOJu0YycHY3W9kqiUavZPC7FfiOSQJJ091BVtXcp+jprVJoEFD8uZPSz o71r2bt8Zp2MMVA3Ku9mKIJ1WVnZ1Li/tyIKcepYwHYRmianCmFu X-Google-Smtp-Source: AGHT+IGvQohBR+X3oXCSN6bsf4d42+dJ3p18mG6Ty4uUDbTdiA3IVkQ43Vq6nCxoQQUWu4dQl6VUSw== X-Received: by 2002:a17:902:aa03:b0:1ec:5f64:6e74 with SMTP id d9443c01a7336-1ef43d29b65mr141454895ad.23.1715692656519; Tue, 14 May 2024 06:17:36 -0700 (PDT) Received: from wedsonaf-dev.. ([50.204.89.32]) by smtp.googlemail.com with ESMTPSA id d9443c01a7336-1ef0b9d18a4sm97277335ad.56.2024.05.14.06.17.35 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 14 May 2024 06:17:36 -0700 (PDT) From: Wedson Almeida Filho To: Alexander Viro , Christian Brauner , Matthew Wilcox , Dave Chinner Cc: Kent Overstreet , Greg Kroah-Hartman , linux-fsdevel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, Wedson Almeida Filho Subject: [RFC PATCH v2 02/30] rust: fs: introduce the `module_fs` macro Date: Tue, 14 May 2024 10:16:43 -0300 Message-Id: <20240514131711.379322-3-wedsonaf@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240514131711.379322-1-wedsonaf@gmail.com> References: <20240514131711.379322-1-wedsonaf@gmail.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 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 cc1ed7ed2f54..fb7a9b200b85 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::{ffi, pin::Pin}; +use core::{ffi, marker::PhantomData, pin::Pin}; use macros::{pin_data, pinned_drop}; /// A file system type. @@ -73,3 +73,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::fs; +/// use kernel::prelude::*; +/// +/// 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 = kernel::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 Tue May 14 13:16:44 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wedson Almeida Filho X-Patchwork-Id: 13664121 Received: from mail-pl1-f181.google.com (mail-pl1-f181.google.com [209.85.214.181]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 10B501459ED; Tue, 14 May 2024 13:17:39 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.181 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715692661; cv=none; b=czNNg2Eki24jnT4Nkqu1hmOj+Mh0M//BlK2z9cQeE7ULuf9kg4Qx1cDdPrE8GbGslr6uVB2zD6eGa9tW425/7lijd0TjA9Xe3HH9XxUgdl9xUG6iC/Tz1pCbNXfLX1ykgRmxRll4PtlfCT86IbddjLpHHkK5sqh6OMnG99CR8k0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715692661; c=relaxed/simple; bh=DBzT/f9hz5kzEs1Lm2UwR9EBpOc9RzF/B/rScSdn5yk=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=hVTC1eZ90m/DwMjla8Su0aWJRTixuHbAtul/CaReg+rVUhD3awBpARdqoE7I+2t0hyyD3kpyA3lK6HMh19YDiFPS4MgZhb/fBlqHb4+f8TsTjBIpBB8QUORnyPooBSN/yRIfhCww/CVYhVVH/CLjlmDUCf3Rxeh1Z+sOlCme6UQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=eQ4H+8lq; arc=none smtp.client-ip=209.85.214.181 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="eQ4H+8lq" Received: by mail-pl1-f181.google.com with SMTP id d9443c01a7336-1ec69e3dbcfso43594535ad.0; Tue, 14 May 2024 06:17:39 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1715692659; x=1716297459; 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=RB+WIMSatnqqUVhcX1WxfgkTneiX5pxrA/ncOnhz7vc=; b=eQ4H+8lq+XpOKKs5qjYNn1SNVLKvcFRfTVSse9ZvoNlpqeIRAzDHyln4F8sNrcHued L971ZrIZr/G9rijh/2s7pS6wglVWL0YjaSe4aniWd5ovLpbvpPDOEZuveCM5jxZAXK4A 7Wvf3PfGhwABqvzIiTtZK3whukTHw0eKu57+Zwlv4l+m661fc8Ek26vJJiS29caQ5Yt+ PWI9+1t0WG4nDOgYPQ+vRLB60fCAZNIMkX1qe+Z/DvsBPOtEfQnbJ06NL9ZwOHRhlYQ0 5koTUdWtI8f5rWEWsI9jgGUyeNX74QXyyesAIXZhA22xyeaYP/OkwkZfholQcK977NZe Sh9w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1715692659; x=1716297459; 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=RB+WIMSatnqqUVhcX1WxfgkTneiX5pxrA/ncOnhz7vc=; b=Gjeh2exRrCt+q5ohkpKiun+qfuwg3zgopngrkWUyvWqT3DZ/fSY2NmTe49Jot+Oq/V DN7nkke32iQoL+lq+EeTR5jKeE89ToU8azKAlKWkei1MapXYPCVTOj4RtforIqbjekIt vKTxELaGPJtp+xJCohkcwIHalVDYui80N3fL/kT1iJCQDFC8Ak1Ef1grz83xrsN7oYxu vIJ0tsU3q5pZEgOxqcgv/s1uDv0dyGLFXr2bFXAxfo6C3q9l0/6xKA4bOJeq77pzft9R yOUiBfmnWKbPfKozmz64DCt4GzoamlnSdOf9TmyPSB97nD1M36wjMejQGmvHHVyvs7zC udxA== X-Forwarded-Encrypted: i=1; AJvYcCV/km0a13R3Hkv5f4MQPd81NzKKP5HgbcOQuD8dLj+se9V28tq+N+yU0LDxlB3nTIfRh6kDs/ldV1EyQLJBB69wFZUL5G5dpjZ1OkJc5rzrStgMCsc1l0XUzgO0uO663fTufR3v5SLSnqNLseLBq3F8UPfLk7kGE2zspRk/FlIICo9w+duGxEyhEQ7k X-Gm-Message-State: AOJu0YwDYkTS2iXIByJxsyrpWHp+c14wEY70w96+tWi3HWbHsUixT2Vd D6/DTZbHXdbupnPK++/wC1SVEVBkjjH+brXmK7fIEqMDTN15WVLP X-Google-Smtp-Source: AGHT+IE0XbCRPPJ26mkbp8Q1xkHdIdFzDb4rGbrIWWlGfet7iN0OnUqPUqA0afyCPKhWnRhWgAL1oQ== X-Received: by 2002:a17:902:bf04:b0:1e2:1df:449b with SMTP id d9443c01a7336-1ef44182635mr121080315ad.69.1715692659332; Tue, 14 May 2024 06:17:39 -0700 (PDT) Received: from wedsonaf-dev.. ([50.204.89.32]) by smtp.googlemail.com with ESMTPSA id d9443c01a7336-1ef0b9d18a4sm97277335ad.56.2024.05.14.06.17.36 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 14 May 2024 06:17:39 -0700 (PDT) From: Wedson Almeida Filho To: Alexander Viro , Christian Brauner , Matthew Wilcox , Dave Chinner Cc: Kent Overstreet , Greg Kroah-Hartman , linux-fsdevel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, Wedson Almeida Filho Subject: [RFC PATCH v2 03/30] samples: rust: add initial ro file system sample Date: Tue, 14 May 2024 10:16:44 -0300 Message-Id: <20240514131711.379322-4-wedsonaf@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240514131711.379322-1-wedsonaf@gmail.com> References: <20240514131711.379322-1-wedsonaf@gmail.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 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..d465b107a07d --- /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_rofs"); +} From patchwork Tue May 14 13:16:45 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wedson Almeida Filho X-Patchwork-Id: 13664122 Received: from mail-pl1-f180.google.com (mail-pl1-f180.google.com [209.85.214.180]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 30B14145A17; Tue, 14 May 2024 13:17:41 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.180 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715692663; cv=none; b=HM8k9MKJuv4BEUev6JlUeSPVS0hqHlI/qr6aIt1Iu1vJL6GoqSbpwrcJ/A6o3WBhDACEUu2HET2gRhCasgJlQd4Jb9cmW9K/jj6mON2wcnzZeklI6C5o/govY/AIlyM3vDY6Fs6oTzU2BJ2weTrALQGPNyZYrYL/7pRVejgirMU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715692663; c=relaxed/simple; bh=5xqavvGys7g3ABa54TtHy/alV4DqbS4d1zcSPFLukFQ=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=lfRr71FhPkwC5c/jhYayLrRjY497Rxq4IRxyURui1AlHGwe8kHY7ZXyRG7pEm+R92bIAP4Ea2r8kC226YdQgrRoHkStIDnpBfKk03fGFuVMXUzcT/6JXJOIRn6HVAYxRc91NYzkg3y2/qgWZQUOTkN4pplGPdeNfUB4wqS4XGr8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=fn4KbQzw; arc=none smtp.client-ip=209.85.214.180 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="fn4KbQzw" Received: by mail-pl1-f180.google.com with SMTP id d9443c01a7336-1ee954e0aa6so43652085ad.3; Tue, 14 May 2024 06:17:41 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1715692661; x=1716297461; 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=wuesYbLbHeSB+zClXdY7SRve4n+ZWNY3LE5efcHNCmU=; b=fn4KbQzwop3ML1uO8VaAyV5hDFX054cKrJxvldzzOkexdiYby0nH3/78p/PB4XxPsk x0AGBR07Wox4K4rnE8nzjUZqxSNjWqUx2tF+2eYXX2LOxAqyqm8S6jfOT0yPNpBlT8/Y aNI3TLxGVIE6n6eQikMKgoQQ4FyBCfG83bCmr4U3c6O08aspUpBJ0OgaZfc/ZxVrqPwt 6llWhu4Sn2HU4eWZge6ju7/NwfTFp5SI6qinOcpsavIzszGvsFyosfLyDoDiEuKfpTVw hdyfoc8VFzmhs5go+ytTVm/XsfEx/xWlLoVomnOoIMB/QXCW/ZHi/+yVB36J9cCs7UiB oHQA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1715692661; x=1716297461; 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=wuesYbLbHeSB+zClXdY7SRve4n+ZWNY3LE5efcHNCmU=; b=gb1SLcuJAI1cwB4nKFRkYfCWWnYHcQlODzE7J0O7PsK8k0/rnkkZq7s/25190FjkKH pRqrFc1kutKAGTxf/gyu6CDwdWRsGc4jS5A/N9uEfWmZJKJzqpUBIet4hknsegLmvkHE /+o7fjeWJE9IZNnwG0dvWkjXf4UJqWsc+n40kytYkO2atmglQvX3+je48XBAFLzYc4Wz FVwsxf800kTjNb5t75o1Z2kyOj+UIsFTKrC+HhCOnYNsF12nN5v3zuf5hIJlhI3klgiH ksdMzn4glLY3cIlfcwl8Q5QaoIgpiKwfW56Mm0Fz3rW7CcHctG+KJXOtMp4fSmvd7/+F 3BwA== X-Forwarded-Encrypted: i=1; AJvYcCVCZN9gkqw7pwBH4IKifazz1XuU0dftLUOe81UQq6d5p8wMF1k3J7wOPMdQjcsHkt/W/85tyq2HwWQ9y5tt40KTmo5CT4jS8qBq/8w82iZTggNnYRGPyvrs6sNXQHk8TQfUlZR1j34Hm7Uy8JSTj5I4NJvWtBYKYrrmDQBugeOHvwhXJYTVo9oNV5LJ X-Gm-Message-State: AOJu0YzVaEYDXXgp3fjqlHlpHrxOihl6Bx28LptLuKcPDRYVO7QBoeWF 6Qo9sQKhy5iEMP7A1VHVGSwYv4NTRHMukKTlLHsHHVfdV06BaDM7 X-Google-Smtp-Source: AGHT+IGNE2lcBVGmYnn4QqEQWv5ai9pgS+Z9r0Rn4/Vy1u42BrzI2Gvjdz2Ays8h3+bLqfOLbm2Rxg== X-Received: by 2002:a17:903:41c2:b0:1e4:6e70:25d8 with SMTP id d9443c01a7336-1ef43d2e2c3mr155172775ad.13.1715692661366; Tue, 14 May 2024 06:17:41 -0700 (PDT) Received: from wedsonaf-dev.. ([50.204.89.32]) by smtp.googlemail.com with ESMTPSA id d9443c01a7336-1ef0b9d18a4sm97277335ad.56.2024.05.14.06.17.39 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 14 May 2024 06:17:41 -0700 (PDT) From: Wedson Almeida Filho To: Alexander Viro , Christian Brauner , Matthew Wilcox , Dave Chinner Cc: Kent Overstreet , Greg Kroah-Hartman , linux-fsdevel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, Wedson Almeida Filho Subject: [RFC PATCH v2 04/30] rust: fs: introduce `FileSystem::fill_super` Date: Tue, 14 May 2024 10:16:45 -0300 Message-Id: <20240514131711.379322-5-wedsonaf@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240514131711.379322-1-wedsonaf@gmail.com> References: <20240514131711.379322-1-wedsonaf@gmail.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 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/kernel/fs.rs | 147 ++++++++++++++++++++++++++++++-- rust/kernel/fs/sb.rs | 50 +++++++++++ samples/rust/rust_rofs.rs | 6 ++ 4 files changed, 202 insertions(+), 6 deletions(-) create mode 100644 rust/kernel/fs/sb.rs diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index 1bef4dff3019..dabb5a787e0d 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -32,3 +33,7 @@ const gfp_t RUST_CONST_HELPER___GFP_ZERO = __GFP_ZERO; const slab_flags_t RUST_CONST_HELPER_SLAB_RECLAIM_ACCOUNT = SLAB_RECLAIM_ACCOUNT; const slab_flags_t RUST_CONST_HELPER_SLAB_ACCOUNT = SLAB_ACCOUNT; + +const unsigned long RUST_CONST_HELPER_SB_RDONLY = SB_RDONLY; + +const loff_t RUST_CONST_HELPER_MAX_LFS_FILESIZE = MAX_LFS_FILESIZE; diff --git a/rust/kernel/fs.rs b/rust/kernel/fs.rs index fb7a9b200b85..263b4b6186ae 100644 --- a/rust/kernel/fs.rs +++ b/rust/kernel/fs.rs @@ -6,16 +6,30 @@ //! //! C headers: [`include/linux/fs.h`](srctree/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::{ffi, marker::PhantomData, pin::Pin}; use macros::{pin_data, pinned_drop}; +use sb::SuperBlock; + +pub mod sb; + +/// The offset of a file in a file system. +/// +/// This is C's `loff_t`. +pub type Offset = i64; + +/// Maximum size of an inode. +pub const MAX_LFS_FILESIZE: Offset = bindings::MAX_LFS_FILESIZE; /// A file system type. pub trait FileSystem { /// The name of the file system type. const NAME: &'static CStr; + + /// Initialises the new superblock. + fn fill_super(sb: &mut SuperBlock) -> Result; } /// A registration of a file system. @@ -46,7 +60,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; @@ -57,11 +71,22 @@ pub fn new(module: &'static ThisModule) -> impl PinInit< }) } - unsafe extern "C" fn init_fs_context_callback(_fc: *mut bindings::fs_context) -> ffi::c_int { - from_result(|| Err(ENOTSUPP)) + unsafe extern "C" fn init_fs_context_callback( + fc_ptr: *mut bindings::fs_context, + ) -> ffi::c_int { + 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] @@ -74,6 +99,113 @@ fn drop(self: Pin<&mut Self>) { } } +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) -> 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, + ) -> ffi::c_int { + from_result(|| { + // SAFETY: The callback contract guarantees that `sb_ptr` is a unique pointer to a + // newly-created superblock. + let new_sb = unsafe { SuperBlock::from_raw_mut(sb_ptr) }; + + // SAFETY: The callback contract guarantees that `sb_ptr`, from which `new_sb` is + // derived, is valid for write. + let sb = unsafe { &mut *new_sb.0.get() }; + sb.s_op = &Tables::::SUPER_BLOCK; + sb.s_flags |= bindings::SB_RDONLY; + + T::fill_super(new_sb)?; + + // 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(sb) }; + 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.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 { @@ -100,7 +232,7 @@ fn init(module: &'static ThisModule) -> impl PinInit { /// /// ``` /// # mod module_fs_sample { -/// use kernel::fs; +/// use kernel::fs::{sb::SuperBlock, self}; /// use kernel::prelude::*; /// /// kernel::module_fs! { @@ -114,6 +246,9 @@ fn init(module: &'static ThisModule) -> impl PinInit { /// struct MyFs; /// impl fs::FileSystem for MyFs { /// const NAME: &'static CStr = kernel::c_str!("myfs"); +/// fn fill_super(_: &mut SuperBlock) -> Result { +/// todo!() +/// } /// } /// # } /// ``` diff --git a/rust/kernel/fs/sb.rs b/rust/kernel/fs/sb.rs new file mode 100644 index 000000000000..113d3c0d8148 --- /dev/null +++ b/rust/kernel/fs/sb.rs @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! File system super blocks. +//! +//! This module allows Rust code to use superblocks. +//! +//! C headers: [`include/linux/fs.h`](srctree/include/linux/fs.h) + +use super::FileSystem; +use crate::{bindings, types::Opaque}; +use core::marker::PhantomData; + +/// A file system super block. +/// +/// Wraps the kernel's `struct super_block`. +#[repr(transparent)] +pub struct SuperBlock( + pub(crate) Opaque, + PhantomData, +); + +impl SuperBlock { + /// Creates a new superblock mutable reference from the given raw pointer. + /// + /// # Safety + /// + /// Callers must ensure that: + /// + /// * `ptr` is valid and remains so for the lifetime of the returned object. + /// * `ptr` has the correct file system type. + /// * `ptr` is the only active pointer to the superblock. + pub(crate) unsafe fn from_raw_mut<'a>(ptr: *mut bindings::super_block) -> &'a mut Self { + // SAFETY: The safety requirements guarantee that the cast below is ok. + unsafe { &mut *ptr.cast::() } + } + + /// Returns whether the superblock is mounted in read-only mode. + pub fn rdonly(&self) -> bool { + // SAFETY: `s_flags` only changes during init, so it is safe to read it. + unsafe { (*self.0.get()).s_flags & bindings::SB_RDONLY != 0 } + } + + /// Sets the magic number of the superblock. + pub fn set_magic(&mut self, magic: usize) -> &mut Self { + // SAFETY: This is a new superblock that is being initialised, so it's ok to write to its + // fields. + unsafe { (*self.0.get()).s_magic = magic as core::ffi::c_ulong }; + self + } +} diff --git a/samples/rust/rust_rofs.rs b/samples/rust/rust_rofs.rs index d465b107a07d..022addf68891 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::sb; use kernel::prelude::*; use kernel::{c_str, fs}; @@ -16,4 +17,9 @@ struct RoFs; impl fs::FileSystem for RoFs { const NAME: &'static CStr = c_str!("rust_rofs"); + + fn fill_super(sb: &mut sb::SuperBlock) -> Result { + sb.set_magic(0x52555354); + Ok(()) + } } From patchwork Tue May 14 13:16:46 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wedson Almeida Filho X-Patchwork-Id: 13664123 Received: from mail-pl1-f172.google.com (mail-pl1-f172.google.com [209.85.214.172]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4B80E145FE5; Tue, 14 May 2024 13:17:44 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715692665; cv=none; b=bCk8DLB1XOH2wIcmoOFCSFRnzWNX9+OLu3CzMpRI3M/1Ld0/bPvHxjq5oIh70kzQNuqQlRlcQfMBHlE9CYHelVpN0hsPQUetsD34hXLEUbKLJt8R3UEOwHtGigqkL9M+6fzzECp2rWMTcIiHxkdxyM8uQdzOlP17vgaJSLnKtYo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715692665; c=relaxed/simple; bh=RZNTYnBFblAGe2jSYR7GVo5fVOthNmES7NcxVvRDXm0=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=pAXd0ZzSsgx1b9vnr2CWkti7Sd7dQPJZ/yPfrOfDczqXE9CUkFUYPeB2fpTzvzAv6BjqA5ta38jbV4lgsGVK/SL/Y6sEcvYWVEExVAePcegbwuqtqa23MJoHqguqnoRWDTihFTdlDsM/LwL1h1ozGU/WEapfU/hrstMe35/AYHY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=hurjez/h; arc=none smtp.client-ip=209.85.214.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="hurjez/h" Received: by mail-pl1-f172.google.com with SMTP id d9443c01a7336-1e651a9f3ffso31087485ad.1; Tue, 14 May 2024 06:17:44 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1715692663; x=1716297463; 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=HHtb+LC/c7u+3DtN2ygh96hbKct7H/SXE7wrRgiAkgI=; b=hurjez/h4SZgnGwOm1OziTBfrde0x09OFLG8Zno4e9/SeCbM6Aox2FlmkQrvLbVC3i 4IrKVNINsG4g8eb84r3/r5fVdGmGAnRAzF56T9IA8798MvxT2FxnyhRs3bMsdKrH1fwq colNujtymEeXt5bjbu0seaOhbjH5FcsaO/0L5J+EC9gCq72qoiNQZij7mglLugffAjNR 0cn847eonRSd7IfTizSr2sEe8OlLhq7UfckzQ8lZQreTiQqyQOEO3yf5KDeVBySYRvi2 p4fo4BU3pfidAp60HN5rX/oYmMvgODQeSDX9Bmy2lX4nwjCnpDVecSodJiEoTmYCUdLl b4Aw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1715692663; x=1716297463; 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=HHtb+LC/c7u+3DtN2ygh96hbKct7H/SXE7wrRgiAkgI=; b=feQLy4cvzmM31exCeK+1I4quF6qAlji6rT/RnAp2VH4+I3IgWFXBxRS4x5Hx5m2/0t fL1wXTjO5esq5l8vG4f5ZngfVxHkQoo8E0FHCxBQlOKiPbCpUQcdJcxauHvxtau2InTI 9XNL1LqLAJoujt+p6o5Z90trtdqbF9i7v/HWEAH4pbMAkXgmIw32kfDz+ZTH02T/DDB4 kzXh9w2ZqJAu7AQ1gdkvhlkX+upr1oD6gJujejX01pUP3zkVe6BcdJWp9qtYa0dMATgt eea7gKqgpY2I+knFNFoSFK2Rt+lChA8bZp1CVYjTIWygFp/+XI7LN8mCGHY7r8DOgHHq /d9Q== X-Forwarded-Encrypted: i=1; AJvYcCXZ/HS1XeU3ajk2i08h8YUSdGMqdbKWT+Oc6Jd9axnM2MZWm4KdRYq7kXTk4KM3bUmNBeBuihinqeE+Zk5kSrZLz0m1AwwiUAxpA5wweJyC1YbpYYBSDrjAbJoRGON4D3EEls4lUlKU7sMxPuU3hk8GXqoIGYTKMSS+GJfNWgg2aTNr5i1wfiZkJqEi X-Gm-Message-State: AOJu0YyrrEADmVgr8gsB2tRoANLQre4ApnMBcrmh9wSP58HYEf9WYida S5F6RJu0/9bCYLZMRQx+PoqQywg+60KV4c2TClRSR3in6ZVAPg21 X-Google-Smtp-Source: AGHT+IGoEMUcg1atFweFeaUt1I3jjzgOSm6DZ4vKImCmjNmsu+1JQNchOGR2w5pz1lr1zfC9UWBWXA== X-Received: by 2002:a17:903:2782:b0:1e3:f012:568d with SMTP id d9443c01a7336-1ef43d1ae15mr112197365ad.15.1715692663533; Tue, 14 May 2024 06:17:43 -0700 (PDT) Received: from wedsonaf-dev.. ([50.204.89.32]) by smtp.googlemail.com with ESMTPSA id d9443c01a7336-1ef0b9d18a4sm97277335ad.56.2024.05.14.06.17.41 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 14 May 2024 06:17:43 -0700 (PDT) From: Wedson Almeida Filho To: Alexander Viro , Christian Brauner , Matthew Wilcox , Dave Chinner Cc: Kent Overstreet , Greg Kroah-Hartman , linux-fsdevel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, Wedson Almeida Filho Subject: [RFC PATCH v2 05/30] rust: fs: introduce `INode` Date: Tue, 14 May 2024 10:16:46 -0300 Message-Id: <20240514131711.379322-6-wedsonaf@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240514131711.379322-1-wedsonaf@gmail.com> References: <20240514131711.379322-1-wedsonaf@gmail.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 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/block.rs | 9 +++++ rust/kernel/fs.rs | 20 +++++++++++ rust/kernel/fs/inode.rs | 78 +++++++++++++++++++++++++++++++++++++++++ rust/kernel/fs/sb.rs | 15 +++++++- 5 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 rust/kernel/fs/inode.rs diff --git a/rust/helpers.c b/rust/helpers.c index 318e3e85dddd..c697c1c4c9d7 100644 --- a/rust/helpers.c +++ b/rust/helpers.c @@ -164,6 +164,13 @@ struct file *rust_helper_get_file(struct file *f) } EXPORT_SYMBOL_GPL(rust_helper_get_file); + +loff_t rust_helper_i_size_read(const struct inode *inode) +{ + return i_size_read(inode); +} +EXPORT_SYMBOL_GPL(rust_helper_i_size_read); + unsigned long rust_helper_copy_to_user(void __user *to, const void *from, unsigned long n) { diff --git a/rust/kernel/block.rs b/rust/kernel/block.rs index 38d2a3089ae7..868623d7c873 100644 --- a/rust/kernel/block.rs +++ b/rust/kernel/block.rs @@ -5,6 +5,7 @@ //! C headers: [`include/linux/blk_types.h`](../../include/linux/blk_types.h) use crate::bindings; +use crate::fs::inode::INode; use crate::types::Opaque; /// The type used for indexing onto a disc or disc partition. @@ -35,4 +36,12 @@ pub(crate) unsafe fn from_raw<'a>(ptr: *mut bindings::block_device) -> &'a Self // SAFETY: The safety requirements guarantee that the cast below is ok. unsafe { &*ptr.cast::() } } + + /// Returns the inode associated with this block device. + pub fn inode(&self) -> &INode { + // SAFETY: `bd_inode` is never reassigned. + let ptr = unsafe { (*self.0.get()).bd_inode }; + // SAFET: `ptr` is valid as long as the block device remains valid as well. + unsafe { INode::from_raw(ptr) } + } } diff --git a/rust/kernel/fs.rs b/rust/kernel/fs.rs index 263b4b6186ae..89dcd5537830 100644 --- a/rust/kernel/fs.rs +++ b/rust/kernel/fs.rs @@ -13,6 +13,7 @@ use macros::{pin_data, pinned_drop}; use sb::SuperBlock; +pub mod inode; pub mod sb; /// The offset of a file in a file system. @@ -28,10 +29,29 @@ pub trait FileSystem { /// The name of the file system type. const NAME: &'static CStr; + /// Determines if an implementation doesn't specify the required types. + /// + /// This is meant for internal use only. + #[doc(hidden)] + const IS_UNSPECIFIED: bool = false; + /// Initialises the new superblock. fn fill_super(sb: &mut SuperBlock) -> Result; } +/// A file system that is unspecified. +/// +/// Attempting to get super-block or inode data from it will result in a build error. +pub struct UnspecifiedFS; + +impl FileSystem for UnspecifiedFS { + const NAME: &'static CStr = crate::c_str!("unspecified"); + const IS_UNSPECIFIED: bool = true; + fn fill_super(_: &mut SuperBlock) -> Result { + Err(ENOTSUPP) + } +} + /// A registration of a file system. #[pin_data(PinnedDrop)] pub struct Registration { diff --git a/rust/kernel/fs/inode.rs b/rust/kernel/fs/inode.rs new file mode 100644 index 000000000000..bcb9c8ce59a9 --- /dev/null +++ b/rust/kernel/fs/inode.rs @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! File system inodes. +//! +//! This module allows Rust code to implement inodes. +//! +//! C headers: [`include/linux/fs.h`](srctree/include/linux/fs.h) + +use super::{sb::SuperBlock, FileSystem, Offset, UnspecifiedFS}; +use crate::bindings; +use crate::types::{AlwaysRefCounted, Opaque}; +use core::{marker::PhantomData, ptr}; + +/// The number of an inode. +pub type Ino = u64; + +/// A node (inode) in the file index. +/// +/// 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( + pub(crate) Opaque, + PhantomData, +); + +impl INode { + /// Creates a new inode reference from the given raw pointer. + /// + /// # Safety + /// + /// Callers must ensure that: + /// + /// * `ptr` is valid and remains so for the lifetime of the returned object. + /// * `ptr` has the correct file system type, or `T` is [`super::UnspecifiedFS`]. + #[allow(dead_code)] + pub(crate) unsafe fn from_raw<'a>(ptr: *mut bindings::inode) -> &'a Self { + // SAFETY: The safety requirements guarantee that the cast below is ok. + unsafe { &*ptr.cast::() } + } + + /// 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 { SuperBlock::from_raw((*self.0.get()).i_sb) } + } + + /// Returns the size of the inode contents. + pub fn size(&self) -> Offset { + // 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.as_ref().0.get()) } + } +} diff --git a/rust/kernel/fs/sb.rs b/rust/kernel/fs/sb.rs index 113d3c0d8148..f48e0e2695fa 100644 --- a/rust/kernel/fs/sb.rs +++ b/rust/kernel/fs/sb.rs @@ -20,6 +20,19 @@ pub struct SuperBlock( ); impl SuperBlock { + /// Creates a new superblock reference from the given raw pointer. + /// + /// # Safety + /// + /// Callers must ensure that: + /// + /// * `ptr` is valid and remains so for the lifetime of the returned object. + /// * `ptr` has the correct file system type, or `T` is [`super::UnspecifiedFS`]. + pub(crate) unsafe fn from_raw<'a>(ptr: *mut bindings::super_block) -> &'a Self { + // SAFETY: The safety requirements guarantee that the cast below is ok. + unsafe { &*ptr.cast::() } + } + /// Creates a new superblock mutable reference from the given raw pointer. /// /// # Safety @@ -27,7 +40,7 @@ impl SuperBlock { /// Callers must ensure that: /// /// * `ptr` is valid and remains so for the lifetime of the returned object. - /// * `ptr` has the correct file system type. + /// * `ptr` has the correct file system type, or `T` is [`super::UnspecifiedFS`]. /// * `ptr` is the only active pointer to the superblock. pub(crate) unsafe fn from_raw_mut<'a>(ptr: *mut bindings::super_block) -> &'a mut Self { // SAFETY: The safety requirements guarantee that the cast below is ok. From patchwork Tue May 14 13:16:47 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wedson Almeida Filho X-Patchwork-Id: 13664124 Received: from mail-pl1-f179.google.com (mail-pl1-f179.google.com [209.85.214.179]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id ABCCB14601C; Tue, 14 May 2024 13:17:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.179 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715692667; cv=none; b=UImMWkN8R6GSPIB5TDFrSYHdrvmjXB6oj2SmTLl5S8hiVW0aWY0nJm1JuLAmpmksB5krXkxBV1MEgiDelvqT78wjX5+RX38h95WTObAMUNTYOlV46nM4ewWboZLPykGv+jiwPIljfNBdxXoOHJM2q/mVOu1AyhKXsJi9/d6JGeI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715692667; c=relaxed/simple; bh=EwDN7VQvAaW1waEupY0eIODNkceeiT8B7VVRlXG69iw=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=StuWGDSJMVboPBjXmBDEigvdP5veJPAjSIZSItLPoelN09OXGNcAdaNX0DyWxsN7V9tKWbyHUZ2gR9r/1IBwtHQOZB7vuYuHE24SyFXFN8stE3r4Wk5Mqa0mx0tMqHe1Eb8PcfdUk/p9kQSjrg9hElCKcGZeo9YgDrcpq9UZEyY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=RDshmNRX; arc=none smtp.client-ip=209.85.214.179 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="RDshmNRX" Received: by mail-pl1-f179.google.com with SMTP id d9443c01a7336-1ee7963db64so46676105ad.1; Tue, 14 May 2024 06:17:45 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1715692665; x=1716297465; 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=s9qSOOnN3derkGGBY28iQxMG2lMAOqyU86LT6GkN1mg=; b=RDshmNRXgnEROHLdd3yLybzO0Xcsyby590rGDdNUOxNnKUl2QgPrFTfa8XwRWv3ZOH DKV1h+w3eHfXy3jEcihf7Wd90u2LpQCKb/EdbtCH6EPTZAZ6X5txR4h6k/sJVB+PHsiS sUZLG2Clha5him5QSiSfOj+n3Yb44pSm7z6NTzEkOvb5vEUu5dNOMXCbSNcDRa5w+HsV GjT21IOonGIHFDbp55QkAMkl6K2dEYaqOvLlAaJoJkXXu675T0hyjan1idIx9KWbW/68 +iwfglg8i51NEd4L3egfzoHL2qDwbJO5rGHaURCJFJHwnNMKbStG6v3uacXQjUCN/pMR zqKA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1715692665; x=1716297465; 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=s9qSOOnN3derkGGBY28iQxMG2lMAOqyU86LT6GkN1mg=; b=dhcGKEI04P84GB7rZ9sA+b50dLPIRpzr12/k6G5/Y+XZ1EtvAFdUvCEi876n+BkO66 MX3xu80YSJ71XoD9rvbbaHelxGbTWJF6L3xVdwkmF5VuYZJ98Qivxos5cWtpQUT8TdHy InUB9OqKsSyFW9e2di+zXBMDGov+xHmTViM048C9OupToEF+3xdsYFfDV4E+hVaFw149 31voJPnWEQtPKOT4oGI38d+xxkvizpTZnHCxkx+6N4bSwXnSHCysnNop4Fb9zYCIKK6o yZ6AzyFKF5XuQ/xV7lkeTanBLqAnfFqAPRsSPRFXL9LySFUB0j1qSvUoud5PBbX4aO2e vldw== X-Forwarded-Encrypted: i=1; AJvYcCVUNvIwTVJBls9BXzFC+R1azCG9Oj91cYNWEqf5j0KGCVhTKG0FHa86dKbSFkJp026DOu3PkMNa3jjbMtYUQ5xaMtYA5M6tSEvOZ/aWsqLOdZR9LKOPPhuDujVi0YqFCB2iungMohs2Xqu/mjUI3lrCxdn9EqmEcYTQ+15sLaoDLc2d7Ukxrtonp4cs X-Gm-Message-State: AOJu0YxpY6tnpLpJoEShp/RV1Ts7+QF9qA3CEb2rQ5+q9FkIsKHPIbpV 2AGXXd4Wmq2Lm5xnucxAIe+2CFC94bECdFAY5YgXmmLJId5rgQ2c X-Google-Smtp-Source: AGHT+IHdW4Whs1GE/MIA4u1iZpDAMjMeTAe0DVQP8NtdsS/SXscTCS8UHeBe22rczXQSRR/PuJjZ5w== X-Received: by 2002:a17:902:6b82:b0:1e3:dfdc:6972 with SMTP id d9443c01a7336-1ef43d15755mr136009995ad.9.1715692664910; Tue, 14 May 2024 06:17:44 -0700 (PDT) Received: from wedsonaf-dev.. ([50.204.89.32]) by smtp.googlemail.com with ESMTPSA id d9443c01a7336-1ef0b9d18a4sm97277335ad.56.2024.05.14.06.17.43 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 14 May 2024 06:17:44 -0700 (PDT) From: Wedson Almeida Filho To: Alexander Viro , Christian Brauner , Matthew Wilcox , Dave Chinner Cc: Kent Overstreet , Greg Kroah-Hartman , linux-fsdevel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, Wedson Almeida Filho Subject: [RFC PATCH v2 06/30] rust: fs: introduce `DEntry` Date: Tue, 14 May 2024 10:16:47 -0300 Message-Id: <20240514131711.379322-7-wedsonaf@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240514131711.379322-1-wedsonaf@gmail.com> References: <20240514131711.379322-1-wedsonaf@gmail.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Wedson Almeida Filho Signed-off-by: Wedson Almeida Filho --- rust/helpers.c | 6 ++ rust/kernel/error.rs | 2 - rust/kernel/fs.rs | 1 + rust/kernel/fs/dentry.rs | 137 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 144 insertions(+), 2 deletions(-) create mode 100644 rust/kernel/fs/dentry.rs diff --git a/rust/helpers.c b/rust/helpers.c index c697c1c4c9d7..c7fe6917251e 100644 --- a/rust/helpers.c +++ b/rust/helpers.c @@ -165,6 +165,12 @@ struct file *rust_helper_get_file(struct file *f) EXPORT_SYMBOL_GPL(rust_helper_get_file); +struct dentry *rust_helper_dget(struct dentry *dentry) +{ + return dget(dentry); +} +EXPORT_SYMBOL_GPL(rust_helper_dget); + loff_t rust_helper_i_size_read(const struct inode *inode) { return i_size_read(inode); diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs index f4fa2847e210..bb13bd4a7fa6 100644 --- a/rust/kernel/error.rs +++ b/rust/kernel/error.rs @@ -261,8 +261,6 @@ pub fn to_result(err: core::ffi::c_int) -> Result { /// from_err_ptr(unsafe { bindings::devm_platform_ioremap_resource(pdev.to_ptr(), index) }) /// } /// ``` -// TODO: Remove `dead_code` marker once an in-kernel client is available. -#[allow(dead_code)] pub(crate) fn from_err_ptr(ptr: *mut T) -> Result<*mut T> { // CAST: Casting a pointer to `*const core::ffi::c_void` is always valid. let const_ptr: *const core::ffi::c_void = ptr.cast(); diff --git a/rust/kernel/fs.rs b/rust/kernel/fs.rs index 89dcd5537830..4f07da71e1ec 100644 --- a/rust/kernel/fs.rs +++ b/rust/kernel/fs.rs @@ -13,6 +13,7 @@ use macros::{pin_data, pinned_drop}; use sb::SuperBlock; +pub mod dentry; pub mod inode; pub mod sb; diff --git a/rust/kernel/fs/dentry.rs b/rust/kernel/fs/dentry.rs new file mode 100644 index 000000000000..6a36a48cd28b --- /dev/null +++ b/rust/kernel/fs/dentry.rs @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! File system directory entries. +//! +//! This module allows Rust code to use dentries. +//! +//! C headers: [`include/linux/dcache.h`](srctree/include/linux/dcache.h) + +use super::{inode::INode, FileSystem, SuperBlock}; +use crate::bindings; +use crate::error::{code::*, from_err_ptr, Result}; +use crate::types::{ARef, AlwaysRefCounted, Opaque}; +use core::{marker::PhantomData, mem::ManuallyDrop, ops::Deref, ptr}; + +/// A directory entry. +/// +/// Wraps the kernel's `struct dentry`. +/// +/// # Invariants +/// +/// Instances of this type are always ref-counted, that is, a call to `dget` ensures that the +/// allocation remains valid at least until the matching call to `dput`. +#[repr(transparent)] +pub struct DEntry(pub(crate) Opaque, PhantomData); + +// SAFETY: The type invariants guarantee that `DEntry` is always ref-counted. +unsafe impl AlwaysRefCounted for DEntry { + fn inc_ref(&self) { + // SAFETY: The existence of a shared reference means that the refcount is nonzero. + unsafe { bindings::dget(self.0.get()) }; + } + + unsafe fn dec_ref(obj: ptr::NonNull) { + // SAFETY: The safety requirements guarantee that the refcount is nonzero. + unsafe { bindings::dput(obj.as_ref().0.get()) } + } +} + +impl DEntry { + /// Creates a new [`DEntry`] from a raw C pointer. + /// + /// # Safety + /// + /// * `ptr` must be valid for at least the lifetime of the returned reference. + /// * `ptr` has the correct file system type, or `T` is [`super::UnspecifiedFS`]. + #[allow(dead_code)] + pub(crate) unsafe fn from_raw<'a>(ptr: *mut bindings::dentry) -> &'a Self { + // SAFETY: The safety requirements guarantee that the reference is and remains valid. + unsafe { &*ptr.cast::() } + } + + /// Returns the superblock of the dentry. + pub fn super_block(&self) -> &SuperBlock { + // `d_sb` is immutable, so it's safe to read it. + unsafe { SuperBlock::from_raw((*self.0.get()).d_sb) } + } +} + +/// A dentry that is known to be unhashed. +pub struct Unhashed<'a, T: FileSystem + ?Sized>(pub(crate) &'a DEntry); + +impl Unhashed<'_, T> { + /// Splices a disconnected dentry into the tree if one exists. + pub fn splice_alias(self, inode: Option>>) -> Result>>> { + let inode_ptr = if let Some(i) = inode { + // Reject inode if it belongs to a different superblock. + if !ptr::eq(i.super_block(), self.0.super_block()) { + return Err(EINVAL); + } + + ManuallyDrop::new(i).0.get() + } else { + ptr::null_mut() + }; + + // SAFETY: Both inode and dentry are known to be valid. + let ptr = from_err_ptr(unsafe { bindings::d_splice_alias(inode_ptr, self.0 .0.get()) })?; + + // SAFETY: The C API guarantees that if a dentry is returned, the refcount has been + // incremented. + Ok(ptr::NonNull::new(ptr).map(|v| unsafe { ARef::from_raw(v.cast::>()) })) + } + + /// Returns the name of the dentry. + /// + /// Being unhashed guarantees that the name won't change. + pub fn name(&self) -> &[u8] { + // SAFETY: The name is immutable, so it is ok to read it. + let name = unsafe { &*ptr::addr_of!((*self.0 .0.get()).d_name) }; + + // This ensures that a `u32` is representable in `usize`. If it isn't, we'll get a build + // break. + const _: usize = 0xffffffff; + + // SAFETY: The union is just allow an easy way to get the `hash` and `len` at once. `len` + // is always valid. + let len = unsafe { name.__bindgen_anon_1.__bindgen_anon_1.len } as usize; + + // SAFETY: The name is immutable, so it is ok to read it. + unsafe { core::slice::from_raw_parts(name.name, len) } + } +} + +impl Deref for Unhashed<'_, T> { + type Target = DEntry; + + fn deref(&self) -> &Self::Target { + self.0 + } +} + +/// A dentry that is meant to be used as the root of a file system. +pub struct Root(ARef>); + +impl Root { + /// Creates a root dentry. + pub fn try_new(inode: ARef>) -> Result> { + // 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_ptr = unsafe { bindings::d_make_root(ManuallyDrop::new(inode).0.get()) }; + let dentry = ptr::NonNull::new(dentry_ptr).ok_or(ENOMEM)?; + + // SAFETY: `dentry` is valid and referenced. It reference ownership is transferred to + // `ARef`. + Ok(Root(unsafe { ARef::from_raw(dentry.cast::>()) })) + } +} + +impl Deref for Root { + type Target = DEntry; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} From patchwork Tue May 14 13:16:48 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wedson Almeida Filho X-Patchwork-Id: 13664125 Received: from mail-pl1-f176.google.com (mail-pl1-f176.google.com [209.85.214.176]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B72D01465B1; Tue, 14 May 2024 13:17:46 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.176 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715692668; cv=none; b=pngYHNj7lO3uiPwyP8ZFz6gkXongHxLQ0dfPaWGMFuBgPfQw5l2gx5JSa2QuYWap7JdQKEvjjX1ittMy9r6LoFsLe/j/CExTX62gTVw9UWZnP2g/Hk1dBBk3c5vXxAR9k1vMrXJAIlblf0EYI6ECIZtu0po4BfEQUcmiwmhNcqI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715692668; c=relaxed/simple; bh=96ZscxFcekwLOU1G/Ob62zVtXDhnn2IgCI17Vhgumoc=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=pd7bVmAF8yXnNTOVOJ+nDkDJfUF//71r/QkDCZS0sz1ev1/M5hKlFKcsHgmFoMnnj7yWhAO5P4py+/xgW9b70EZ6LuAxJvQt8yHpxthOFNg+7PbXAzh06KY7AuUDveqcaNDwCaN9p9AbHyRvfpT549WZto1NuGkbiks940GJ+9I= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=K9zodidV; arc=none smtp.client-ip=209.85.214.176 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="K9zodidV" Received: by mail-pl1-f176.google.com with SMTP id d9443c01a7336-1edfc57ac0cso44971395ad.3; Tue, 14 May 2024 06:17:46 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1715692666; x=1716297466; 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=kOZvxCrC9Itj0+EN+G6LfcoJOefBPRrAg+vdgGoRsY4=; b=K9zodidVLgIpRMccRRzZUClO5LAvknM7h/RX2zX+b253d8TQs3Cs7sTxlFF6o0JyTP dzj12QBckS4CtfecrKk6wapvYTtUQ2ucWdDThql2d4xi/UFUfXcz6Kr6Dh2RJKmGVUxW QOXXYEUwO0EGfHh37L15M/xNSZUDL1TCJzIYc6IBk6wzGgLUQFUtn7aaJ5CIz5gre5nW gZZ+i+BLsDYM7+kVuhFy+sokSOM0kezahnxxRM1u3sYAUTH8xZP+hejQtYRkIGj71doh w1+Yo6Kom2bbxnEAUoI2Og9hBIrs3dSVeYhgayh6WzFySK3YN0zw2KAttawFTCIh2yvj HAjQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1715692666; x=1716297466; 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=kOZvxCrC9Itj0+EN+G6LfcoJOefBPRrAg+vdgGoRsY4=; b=nxVgst9HXj8As6a2JilO4OZ6Gk4EIwrkFf8n1mHJja83tZxtxYSGwdQbUg1lIz5vNx WkP4dvhO1e1bpPFMHJopIiNfmPCKyWnpHemZ8jXs6PTLLVEx1pwG6N6KqG/NCpQHWhik /lGD3ErZrrjyECyXQxwF1lVIRm+ggJtiCS1wQWSOQEJn2Tm0COoLhHjm+aeniRmGfuDK H8evdLDG0tT9XZbZE6OjQ7/ckx9MGI1HLkhWkX5oiuzE3u3+Fwws9WgKBa55l/vZyP3o sDr+5lO1oqX7F8XeuRjkLjMJukBrNLSSf12AKgAGbN1jGOgbkAak1YiFQxqm5m5CB6il hONw== X-Forwarded-Encrypted: i=1; AJvYcCXPuKweJ+qbWCKJ9eoK0Acw61Ki005I25Y1MWOK2WtHN0u6A0pUesUWSgxh9H87mnfWfEnjotpWtRdmiPDpEv9qcTZgWAAmtbJ7zSKIkqC2YM69FsSD6tifrVjWe+4fm6ebI1sPmdQpzS0g7yfMJ6uyw88a47Z/qquP12Jb4WovPDrt8LfzLMgklyba X-Gm-Message-State: AOJu0YzN13I59dcqvA7rbmuc7yO7hT1dgJ2MAK1txrHI5Ltx9XF//lxk DYRHvd8JrUtKdua4m9zUVSrNiEbXajmFPEjfmN6+wWRaB/wDgE7B X-Google-Smtp-Source: AGHT+IGyeBYMGLQBdDwZ1lWpcvuan3YvNcfX8WBE9Omp5M7OJ7qekVSXOf7kKkcIZstZ6T6aYUGr+A== X-Received: by 2002:a17:902:9a95:b0:1e4:7adf:b855 with SMTP id d9443c01a7336-1ef43e23223mr109980805ad.35.1715692665874; Tue, 14 May 2024 06:17:45 -0700 (PDT) Received: from wedsonaf-dev.. ([50.204.89.32]) by smtp.googlemail.com with ESMTPSA id d9443c01a7336-1ef0b9d18a4sm97277335ad.56.2024.05.14.06.17.45 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 14 May 2024 06:17:45 -0700 (PDT) From: Wedson Almeida Filho To: Alexander Viro , Christian Brauner , Matthew Wilcox , Dave Chinner Cc: Kent Overstreet , Greg Kroah-Hartman , linux-fsdevel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, Wedson Almeida Filho Subject: [RFC PATCH v2 07/30] rust: fs: introduce `FileSystem::init_root` Date: Tue, 14 May 2024 10:16:48 -0300 Message-Id: <20240514131711.379322-8-wedsonaf@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240514131711.379322-1-wedsonaf@gmail.com> References: <20240514131711.379322-1-wedsonaf@gmail.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 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 `inode::New` 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 | 11 ++++ rust/kernel/fs.rs | 56 ++++++++----------- rust/kernel/fs/inode.rs | 111 +++++++++++++++++++++++++++++++++++++- rust/kernel/fs/sb.rs | 48 ++++++++++++++++- samples/rust/rust_rofs.rs | 25 +++++++-- 5 files changed, 211 insertions(+), 40 deletions(-) diff --git a/rust/helpers.c b/rust/helpers.c index c7fe6917251e..87301e1ace65 100644 --- a/rust/helpers.c +++ b/rust/helpers.c @@ -164,6 +164,17 @@ struct file *rust_helper_get_file(struct file *f) } EXPORT_SYMBOL_GPL(rust_helper_get_file); +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); struct dentry *rust_helper_dget(struct dentry *dentry) { diff --git a/rust/kernel/fs.rs b/rust/kernel/fs.rs index 4f07da71e1ec..f32c2f89f781 100644 --- a/rust/kernel/fs.rs +++ b/rust/kernel/fs.rs @@ -9,7 +9,7 @@ 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::{ffi, marker::PhantomData, pin::Pin}; +use core::{ffi, marker::PhantomData, mem::ManuallyDrop, pin::Pin, ptr}; use macros::{pin_data, pinned_drop}; use sb::SuperBlock; @@ -38,6 +38,12 @@ pub trait FileSystem { /// Initialises the new superblock. fn fill_super(sb: &mut SuperBlock) -> Result; + + /// Initialises and returns the root inode of the given superblock. + /// + /// This is called during initialisation of a superblock after [`FileSystem::fill_super`] has + /// completed successfully. + fn init_root(sb: &SuperBlock) -> Result>; } /// A file system that is unspecified. @@ -51,6 +57,10 @@ impl FileSystem for UnspecifiedFS { fn fill_super(_: &mut SuperBlock) -> Result { Err(ENOTSUPP) } + + fn init_root(_: &SuperBlock) -> Result> { + Err(ENOTSUPP) + } } /// A registration of a file system. @@ -154,41 +164,18 @@ impl Tables { T::fill_super(new_sb)?; - // 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(sb) }; - if inode.is_null() { - return Err(ENOMEM); - } - - // SAFETY: `inode` is valid for write. - unsafe { bindings::set_nlink(inode, 2) }; + let root = T::init_root(new_sb)?; - { - // 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 }; + // Reject root inode if it belongs to a different superblock. + if !ptr::eq(root.super_block(), new_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) }; - if dentry.is_null() { - return Err(ENOMEM); - } + let dentry = ManuallyDrop::new(root).0.get(); - sb.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) }) @@ -253,7 +240,7 @@ fn init(module: &'static ThisModule) -> impl PinInit { /// /// ``` /// # mod module_fs_sample { -/// use kernel::fs::{sb::SuperBlock, self}; +/// use kernel::fs::{dentry, inode::INode, sb::SuperBlock, self}; /// use kernel::prelude::*; /// /// kernel::module_fs! { @@ -270,6 +257,9 @@ fn init(module: &'static ThisModule) -> impl PinInit { /// fn fill_super(_: &mut SuperBlock) -> Result { /// todo!() /// } +/// fn init_root(_sb: &SuperBlock) -> Result> { +/// todo!() +/// } /// } /// # } /// ``` diff --git a/rust/kernel/fs/inode.rs b/rust/kernel/fs/inode.rs index bcb9c8ce59a9..4ccbb4145918 100644 --- a/rust/kernel/fs/inode.rs +++ b/rust/kernel/fs/inode.rs @@ -7,8 +7,10 @@ //! C headers: [`include/linux/fs.h`](srctree/include/linux/fs.h) use super::{sb::SuperBlock, FileSystem, Offset, UnspecifiedFS}; -use crate::bindings; -use crate::types::{AlwaysRefCounted, Opaque}; +use crate::error::Result; +use crate::types::{ARef, AlwaysRefCounted, Opaque}; +use crate::{bindings, block, time::Timespec}; +use core::mem::ManuallyDrop; use core::{marker::PhantomData, ptr}; /// The number of an inode. @@ -76,3 +78,108 @@ unsafe fn dec_ref(obj: ptr::NonNull) { unsafe { bindings::iput(obj.as_ref().0.get()) } } } + +/// An inode that is locked and hasn't been initialised yet. +/// +/// # Invariants +/// +/// The inode is a new one, locked, and valid for write. +pub struct New( + pub(crate) ptr::NonNull, + pub(crate) PhantomData, +); + +impl New { + /// Initialises the new inode with the given parameters. + pub fn init(mut self, params: Params) -> Result>> { + // SAFETY: This is a new inode, so it's safe to manipulate it mutably. + let inode = unsafe { self.0.as_mut() }; + let mode = match params.typ { + Type::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); + } + + let manual = ManuallyDrop::new(self); + // SAFETY: We transferred ownership of the refcount to `ARef` by preventing `drop` from + // being called with the `ManuallyDrop` instance created above. + Ok(unsafe { ARef::from_raw(manual.0.cast::>()) }) + } +} + +impl Drop for New { + 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.as_ptr()) }; + } +} + +/// The type of an inode. +#[derive(Copy, Clone)] +pub enum Type { + /// Directory type. + Dir, +} + +/// Required inode parameters. +/// +/// This is used when creating new inodes. +pub struct Params { + /// 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: Type, + + /// Size of the contents of the inode. + /// + /// Its maximum value is [`super::MAX_LFS_FILESIZE`]. + pub size: Offset, + + /// Number of blocks. + pub blocks: block::Count, + + /// 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, +} diff --git a/rust/kernel/fs/sb.rs b/rust/kernel/fs/sb.rs index f48e0e2695fa..fa10f3db5593 100644 --- a/rust/kernel/fs/sb.rs +++ b/rust/kernel/fs/sb.rs @@ -6,9 +6,12 @@ //! //! C headers: [`include/linux/fs.h`](srctree/include/linux/fs.h) +use super::inode::{self, INode, Ino}; use super::FileSystem; -use crate::{bindings, types::Opaque}; -use core::marker::PhantomData; +use crate::bindings; +use crate::error::{code::*, Result}; +use crate::types::{ARef, Either, Opaque}; +use core::{marker::PhantomData, ptr}; /// A file system super block. /// @@ -60,4 +63,45 @@ pub fn set_magic(&mut self, magic: usize) -> &mut Self { unsafe { (*self.0.get()).s_magic = magic as core::ffi::c_ulong }; self } + + /// 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>, inode::New>> { + // SAFETY: All superblock-related state needed by `iget_locked` is initialised by C code + // before calling `fill_super_callback`, or by `fill_super_callback` itself before calling + // `super_params`, which is the first function to see a new superblock. + let inode = + ptr::NonNull::new(unsafe { bindings::iget_locked(self.0.get(), ino) }).ok_or(ENOMEM)?; + + // SAFETY: `inode` is a valid pointer returned by `iget_locked`. + unsafe { bindings::spin_lock(ptr::addr_of_mut!((*inode.as_ptr()).i_lock)) }; + + // SAFETY: `inode` is valid and was locked by the previous lock. + let state = unsafe { *ptr::addr_of!((*inode.as_ptr()).i_state) }; + + // SAFETY: `inode` is a valid pointer returned by `iget_locked`. + unsafe { bindings::spin_unlock(ptr::addr_of_mut!((*inode.as_ptr()).i_lock)) }; + + 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 + // `inode::New`. + Ok(Either::Right(inode::New(inode, PhantomData))) + } + } + + /// Creates an inode with the given inode number. + /// + /// Fails with `EEXIST` if an inode with the given number already exists. + pub fn create_inode(&self, ino: Ino) -> Result> { + if let Either::Right(new) = self.get_or_create_inode(ino)? { + Ok(new) + } else { + Err(EEXIST) + } + } } diff --git a/samples/rust/rust_rofs.rs b/samples/rust/rust_rofs.rs index 022addf68891..d32c4645ebe8 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::sb; +use kernel::fs::{dentry, inode, sb::SuperBlock}; use kernel::prelude::*; -use kernel::{c_str, fs}; +use kernel::{c_str, fs, time::UNIX_EPOCH, types::Either}; kernel::module_fs! { type: RoFs, @@ -18,8 +18,27 @@ impl fs::FileSystem for RoFs { const NAME: &'static CStr = c_str!("rust_rofs"); - fn fill_super(sb: &mut sb::SuperBlock) -> Result { + fn fill_super(sb: &mut SuperBlock) -> Result { sb.set_magic(0x52555354); Ok(()) } + + fn init_root(sb: &SuperBlock) -> Result> { + let inode = match sb.get_or_create_inode(1)? { + Either::Left(existing) => existing, + Either::Right(new) => new.init(inode::Params { + typ: inode::Type::Dir, + mode: 0o555, + size: 1, + blocks: 1, + nlink: 2, + uid: 0, + gid: 0, + atime: UNIX_EPOCH, + ctime: UNIX_EPOCH, + mtime: UNIX_EPOCH, + })?, + }; + dentry::Root::try_new(inode) + } } From patchwork Tue May 14 13:16:49 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wedson Almeida Filho X-Patchwork-Id: 13664126 Received: from mail-pl1-f172.google.com (mail-pl1-f172.google.com [209.85.214.172]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B04E3146A9F; Tue, 14 May 2024 13:17:47 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715692669; cv=none; b=PQokvjv0l7vBx7PBP/ci0gc3nokpArSAhBNEIfNr/pdyRf4JSOyYyOFdRkBwBrsIEM+DCwp0oOD4RWdIFOMIY1aMl6vnxqeaz2aBQ6CcUHE2+5s8BFH3tZzn22MlukFzfO1/YbxSZVyNa2ttQXqPWENroV76e5/7I3Sku1SgoCs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715692669; c=relaxed/simple; bh=guYIJsy3kCGaE8JFJGrMHROjk2P2zWMTT8kD0m4RCl8=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=mGeTdevpHUAFOt1dIOBeY364q1t36kSFsUuXmopmxRhf8cGziqeWg8y88k8Nft7Z9j5Y82jxGC1gYgKIEy/yn2SlEvG2mVarBVb2Vfa8LXxlS6YreSuag6Pw8KzfGYcVPnERRj4+zhCKn7XZnMnC5GCSheY4eiiJFuKfESS+vXI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=HAozbOGR; arc=none smtp.client-ip=209.85.214.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="HAozbOGR" Received: by mail-pl1-f172.google.com with SMTP id d9443c01a7336-1ec4b2400b6so46103605ad.3; Tue, 14 May 2024 06:17:47 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1715692667; x=1716297467; 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=PMcl/ifCfcA1uYeFeWl4JOo8kfKdMw4mW78nzbs+K7U=; b=HAozbOGRT8A+40UZOF1XdLBhkiicFpDKmQefCWaQ8Cis8UYKsq6bGH7nderUf4NTUy bWfxnZtMuwGXTkF1l7yY85Gt3lR4ulaiwCA+FNL5W+4f3b6Ji7jxhGiB/7nhCVgi/Ipx 1TDHRRT6mi6BTaZo5nne557NMX4tpZ6PjJb7TWTSZygo8zlRJ/sETdfgFCAb8sv9u1bW uIWUbzhk2rl81rW4wOcpQO5ObjaUKkHVhWIfBygIkJUmi4Y7bbSjD/gr2UkRvNAXZwWR cBHDhgzWcnZLdPJZWxMGnB6iV4ov4AQXHOkZV90KLRhe8jpweqN0e8VoJH84fqQGggHu za7w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1715692667; x=1716297467; 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=PMcl/ifCfcA1uYeFeWl4JOo8kfKdMw4mW78nzbs+K7U=; b=Npak1TQDT/L3oXcIDGRrZTfzcZ7wX9XFUrvor6+3egaFYqmHD29NYD+C7pUAlKc64E ydqPF8bZDcf8GKOiCSWHwZ0i1D+QYNcIW3pcjpmhLHdrpxFLkh+3iI3hdMHNnJ+Q8PKB PT1CmT520fA+JZKOsYaY82AfC65vnhLlAFPvppFrO5nMKFCVQswyXZCkeYcttckfg8Xo OkqaEtvNhW8CP4ihpK9zH6NcnQZAK5nfp0YbSUc2yMwZapogOrBJ38QDlfAVrr0zrCdP +V20kfMiBhQR8i+PkLNlVQQYEZ+pRzDOtLLliFBSl1MHlDa1RMguCm3r8reHFOcoe6zJ lMjw== X-Forwarded-Encrypted: i=1; AJvYcCVf67d/3p2D3PyaVM7hD/qT/CufB9kPiTfHcTbICTA5k2dVxeiM/mS4xKtRXZaiBfXqeUmRl8bxT2I9VNZqMI+zM/z53JDiNz6eyPVmYKwQFIH0LEPjKSysnLehuOLS643Q9Ijq9+XxtZpnxHGTgCDgWrh5fO86xywF7hc0Al130US5ivnb525P+L3c X-Gm-Message-State: AOJu0Yy0pF7ixXEQnb1F3auMgV51TngR+Mnpsj2bULPq+JkYfPa640LL dAEYUNtkg4hGXbZZdl4DqS/WGDYJ1oneESkO4B4ZEn7iuN96hrhj X-Google-Smtp-Source: AGHT+IFrvKjZ3mHAOmVF9/Ju8y89aj8T02fV5ZC9uqfRALJvdI/cz7D+y61X4xh5e9VPaz+KKtJzBA== X-Received: by 2002:a17:902:f705:b0:1ee:b47e:7085 with SMTP id d9443c01a7336-1ef43c0c957mr156856905ad.12.1715692666895; Tue, 14 May 2024 06:17:46 -0700 (PDT) Received: from wedsonaf-dev.. ([50.204.89.32]) by smtp.googlemail.com with ESMTPSA id d9443c01a7336-1ef0b9d18a4sm97277335ad.56.2024.05.14.06.17.46 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 14 May 2024 06:17:46 -0700 (PDT) From: Wedson Almeida Filho To: Alexander Viro , Christian Brauner , Matthew Wilcox , Dave Chinner Cc: Kent Overstreet , Greg Kroah-Hartman , linux-fsdevel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, Wedson Almeida Filho Subject: [RFC PATCH v2 08/30] rust: file: move `kernel::file` to `kernel::fs::file` Date: Tue, 14 May 2024 10:16:49 -0300 Message-Id: <20240514131711.379322-9-wedsonaf@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240514131711.379322-1-wedsonaf@gmail.com> References: <20240514131711.379322-1-wedsonaf@gmail.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Wedson Almeida Filho This is in preparation for making `File` parametrised on the file system type, so we can get a typed inode in file system implementations that have data attached to inodes. Signed-off-by: Wedson Almeida Filho --- rust/kernel/fs.rs | 1 + rust/kernel/{ => fs}/file.rs | 2 +- rust/kernel/lib.rs | 1 - 3 files changed, 2 insertions(+), 2 deletions(-) rename rust/kernel/{ => fs}/file.rs (99%) diff --git a/rust/kernel/fs.rs b/rust/kernel/fs.rs index f32c2f89f781..20fb6107eb4b 100644 --- a/rust/kernel/fs.rs +++ b/rust/kernel/fs.rs @@ -14,6 +14,7 @@ use sb::SuperBlock; pub mod dentry; +pub mod file; pub mod inode; pub mod sb; diff --git a/rust/kernel/file.rs b/rust/kernel/fs/file.rs similarity index 99% rename from rust/kernel/file.rs rename to rust/kernel/fs/file.rs index b7ded0cdd063..908e2672676f 100644 --- a/rust/kernel/file.rs +++ b/rust/kernel/fs/file.rs @@ -76,7 +76,7 @@ pub mod flags { /// # Examples /// /// ``` - /// use kernel::file; + /// use kernel::fs::file; /// # fn do_something() {} /// # let flags = 0; /// if (flags & file::flags::O_ACCMODE) == file::flags::O_RDONLY { diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index e664f80b8141..81065d1bd679 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -30,7 +30,6 @@ pub mod block; mod build_assert; pub mod error; -pub mod file; pub mod fs; pub mod init; pub mod ioctl; From patchwork Tue May 14 13:16:50 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wedson Almeida Filho X-Patchwork-Id: 13664127 Received: from mail-pl1-f173.google.com (mail-pl1-f173.google.com [209.85.214.173]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C7E8E1474C9; Tue, 14 May 2024 13:17:48 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.173 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715692670; cv=none; b=g+ypy/OmXuQsiAbv5ulFGg8N9VIYG+KRZvndkQ9i79QcJZAdcJGU7ifBKQM7ThfEwUCfFvAvCjSYluWntowolT6Ic5dZtYALMmZ9UIr4EvQvLYIlqJ0OS6AxeL4eCRd1qeS4hJiiLlKz38GzjJMSJSv3mnLQbg2BZiWnoquZq64= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715692670; c=relaxed/simple; bh=2dU2vW9r6+E0UljRNqvE72JR11F6DrWRygMa+KrItSo=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=dm4gV92KPAax7KlOYv50nj4Wmf8hW8YFeckAERa0dGCPUuMgY2OaENEMx8sjQ42Wrlo3WNOiWgdbax8+Wvb4+6uRZcG/lh9vJ+g+pTGmBy7Ccg+RYrDb4kXQm2HSek6ofBfh8ur3kVruOJXuOIysXoo3d+pfa3K1CpesDAyBlyM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=SywO1aFs; arc=none smtp.client-ip=209.85.214.173 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="SywO1aFs" Received: by mail-pl1-f173.google.com with SMTP id d9443c01a7336-1ed96772f92so44793435ad.0; Tue, 14 May 2024 06:17:48 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1715692668; x=1716297468; 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=Cb0aTJs1VPGRE71afQXU2F9h4FNf65p4Qc8Pwqu07To=; b=SywO1aFsU1ntCX5e8ORE0EOS45Nelh14tSesra83fkLihVe3oVVh7oSPYTgkW2Q1C5 WJYcSCKuw861rM0/5GZqGyckBD2gEdl8ABNvE1VlbgmoZ9Hu0LpuLtoitY1R2r2jwdyP cJOlzzvEL7V6PIWwTtD2/WM2nkeRR0MtrIDnQG1drPd5E2uPiQIMM9KSpTId4ww3hi+E xRgBbtrCGKjqSRn9bWuUABTI6p+GcOCRssMrahhM55vl1mBBwAz4tQNZvkv2M8daI9iO QnSwGluFPcaAn+jVRTZYzihb0o+MYx26WS3nVceL6Vln9pHDo3iTrBfWwYVrMCxR/MV+ ZzSg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1715692668; x=1716297468; 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=Cb0aTJs1VPGRE71afQXU2F9h4FNf65p4Qc8Pwqu07To=; b=JtvNr8P6yHeS//BhWLWH9syEe+Sby5aVFu6xpWGKNr2xz2EBs5b9iE1T+Myg72BLgg G5RgGhq4dpRcIDuynpLcblAafv9U3363NI2g+QvCx2EUg7Uxl8/jeoPUHvFsuGUVT0np BPncGn3qMzTVzqwChzw36kDA7uq1+/9tdzAIFCrOBlQQRcv5saIVT27O2b04Md2HMn49 rR5TjJvybr5WtuTwitC9VQ0/xhiimB6v5VdpYZ4vGRHCJ6AQpqBpBTC7/kL/L48mdtvi s4G018LxwaYl9N//A3n9ZTpxmJn6iNbx+4r3olAUItBHt20+V5Ts+lAv6LG8Ur87krBR 0iwg== X-Forwarded-Encrypted: i=1; AJvYcCUY9BOEPvlnQ2QMbc1Xo1uWt51rJtp8LpY0FGMy2XJKfEBS0UlzZm4GRx4OtWzH4PDUiOI5y3mmIqVEYqW7RbXeTRYN+b0ABevYbIRtVOjSYX3Ea1pFtTolzajhRAn7gdWAkBNz4c1j3QRgWLcjz1zR+PDjJE3mCrtRZN8gjmIk9ZqwWfiu6PTP9cGB X-Gm-Message-State: AOJu0YxSTJBQcRNWhE7HpIGf8NOQ9FvJPGvbLC7CWTKJ0Tdfb4GZskCJ FllnYcQM25x3Uy3vnlhPpTJTYfn16v/rjoYWqTaiI1rXTYsMhgBG X-Google-Smtp-Source: AGHT+IHc29e6vne8CBHNjaXL31oZJLb84ryZavfdmGOgVfoH6/KYa3aqF7scyQY5CXTSy0i8uGudsg== X-Received: by 2002:a17:903:2290:b0:1e0:b2d5:5f46 with SMTP id d9443c01a7336-1ef440495b7mr146491315ad.46.1715692667893; Tue, 14 May 2024 06:17:47 -0700 (PDT) Received: from wedsonaf-dev.. ([50.204.89.32]) by smtp.googlemail.com with ESMTPSA id d9443c01a7336-1ef0b9d18a4sm97277335ad.56.2024.05.14.06.17.47 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 14 May 2024 06:17:47 -0700 (PDT) From: Wedson Almeida Filho To: Alexander Viro , Christian Brauner , Matthew Wilcox , Dave Chinner Cc: Kent Overstreet , Greg Kroah-Hartman , linux-fsdevel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, Wedson Almeida Filho Subject: [RFC PATCH v2 09/30] rust: fs: generalise `File` for different file systems Date: Tue, 14 May 2024 10:16:50 -0300 Message-Id: <20240514131711.379322-10-wedsonaf@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240514131711.379322-1-wedsonaf@gmail.com> References: <20240514131711.379322-1-wedsonaf@gmail.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Wedson Almeida Filho This is in preparation for allowing file operation implementations for different file systems. Also add an unspecified file system so that users of the `File` type that don't care about the file system may continue to do so. Signed-off-by: Wedson Almeida Filho --- rust/kernel/fs/file.rs | 53 ++++++++++++++++++++++++++++------------- rust/kernel/fs/inode.rs | 1 - 2 files changed, 36 insertions(+), 18 deletions(-) diff --git a/rust/kernel/fs/file.rs b/rust/kernel/fs/file.rs index 908e2672676f..b8386a396251 100644 --- a/rust/kernel/fs/file.rs +++ b/rust/kernel/fs/file.rs @@ -2,15 +2,18 @@ //! Files and file descriptors. //! -//! C headers: [`include/linux/fs.h`](../../../../include/linux/fs.h) and -//! [`include/linux/file.h`](../../../../include/linux/file.h) +//! This module allows Rust code to interact with and implement files. +//! +//! C headers: [`include/linux/fs.h`](srctree/include/linux/fs.h) and +//! [`include/linux/file.h`](srctree/include/linux/file.h) +use super::{dentry::DEntry, inode::INode, FileSystem, UnspecifiedFS}; use crate::{ bindings, error::{code::*, Error, Result}, types::{ARef, AlwaysRefCounted, Opaque}, }; -use core::ptr; +use core::{marker::PhantomData, ptr}; /// Flags associated with a [`File`]. pub mod flags { @@ -95,6 +98,8 @@ pub mod flags { pub const O_RDWR: u32 = bindings::O_RDWR; } +/// A file. +/// /// Wraps the kernel's `struct file`. /// /// # Refcounting @@ -139,7 +144,7 @@ pub mod flags { /// * The Rust borrow-checker normally ensures this by enforcing that the `ARef` from which /// a `&File` is created outlives the `&File`. /// -/// * Using the unsafe [`File::from_ptr`] means that it is up to the caller to ensure that the +/// * Using the unsafe [`File::from_raw`] means that it is up to the caller to ensure that the /// `&File` only exists while the reference count is positive. /// /// * You can think of `fdget` as using an fd to look up an `ARef` in the `struct @@ -154,20 +159,20 @@ pub mod flags { /// closed. /// * A light refcount must be dropped before returning to userspace. #[repr(transparent)] -pub struct File(Opaque); +pub struct File(Opaque, PhantomData); // SAFETY: By design, the only way to access a `File` is via an immutable reference or an `ARef`. // This means that the only situation in which a `File` can be accessed mutably is when the // refcount drops to zero and the destructor runs. It is safe for that to happen on any thread, so // it is ok for this type to be `Send`. -unsafe impl Send for File {} +unsafe impl Send for File {} // SAFETY: All methods defined on `File` that take `&self` are safe to call even if other threads // are concurrently accessing the same `struct file`, because those methods either access immutable // properties or have proper synchronization to ensure that such accesses are safe. -unsafe impl Sync for File {} +unsafe impl Sync for File {} -impl File { +impl File { /// Constructs a new `struct file` wrapper from a file descriptor. /// /// The file descriptor belongs to the current process. @@ -187,15 +192,17 @@ pub fn fget(fd: u32) -> Result, BadFdError> { /// /// # Safety /// - /// The caller must ensure that `ptr` points at a valid file and that the file's refcount is - /// positive for the duration of 'a. - pub unsafe fn from_ptr<'a>(ptr: *const bindings::file) -> &'a File { + /// Callers must ensure that: + /// + /// * `ptr` is valid and remains so for the duration of 'a. + /// * `ptr` has the correct file system type, or `T` is [`UnspecifiedFS`]. + pub unsafe fn from_raw<'a>(ptr: *const bindings::file) -> &'a Self { // SAFETY: The caller guarantees that the pointer is not dangling and stays valid for the // duration of 'a. The cast is okay because `File` is `repr(transparent)`. // // INVARIANT: The safety requirements guarantee that the refcount does not hit zero during // 'a. - unsafe { &*ptr.cast() } + unsafe { &*ptr.cast::() } } /// Returns a raw pointer to the inner C struct. @@ -215,20 +222,32 @@ pub fn flags(&self) -> u32 { // TODO: Replace with `read_once` when available on the Rust side. unsafe { core::ptr::addr_of!((*self.as_ptr()).f_flags).read_volatile() } } + + /// Returns the inode associated with the file. + pub fn inode(&self) -> &INode { + // SAFETY: `f_inode` is an immutable field, so it's safe to read it. + unsafe { INode::from_raw((*self.0.get()).f_inode) } + } + + /// Returns the dentry associated with the file. + pub fn dentry(&self) -> &DEntry { + // SAFETY: `f_path` is an immutable field, so it's safe to read it. And will remain safe to + // read while the `&self` is valid. + unsafe { DEntry::from_raw((*self.0.get()).f_path.dentry) } + } } // SAFETY: The type invariants guarantee that `File` is always ref-counted. This implementation // makes `ARef` own a normal refcount. -unsafe impl AlwaysRefCounted for File { +unsafe impl AlwaysRefCounted for File { fn inc_ref(&self) { // SAFETY: The existence of a shared reference means that the refcount is nonzero. unsafe { bindings::get_file(self.as_ptr()) }; } - unsafe fn dec_ref(obj: ptr::NonNull) { - // SAFETY: To call this method, the caller passes us ownership of a normal refcount, so we - // may drop it. The cast is okay since `File` has the same representation as `struct file`. - unsafe { bindings::fput(obj.cast().as_ptr()) } + unsafe fn dec_ref(obj: ptr::NonNull) { + // SAFETY: The safety requirements guarantee that the refcount is nonzero. + unsafe { bindings::fput(obj.as_ref().0.get()) } } } diff --git a/rust/kernel/fs/inode.rs b/rust/kernel/fs/inode.rs index 4ccbb4145918..11df493314ea 100644 --- a/rust/kernel/fs/inode.rs +++ b/rust/kernel/fs/inode.rs @@ -39,7 +39,6 @@ impl INode { /// /// * `ptr` is valid and remains so for the lifetime of the returned object. /// * `ptr` has the correct file system type, or `T` is [`super::UnspecifiedFS`]. - #[allow(dead_code)] pub(crate) unsafe fn from_raw<'a>(ptr: *mut bindings::inode) -> &'a Self { // SAFETY: The safety requirements guarantee that the cast below is ok. unsafe { &*ptr.cast::() } From patchwork Tue May 14 13:16:51 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wedson Almeida Filho X-Patchwork-Id: 13664128 Received: from mail-pl1-f177.google.com (mail-pl1-f177.google.com [209.85.214.177]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id BC0BE14A601; Tue, 14 May 2024 13:17:49 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.177 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715692671; cv=none; b=JbGm5htwgFP9ZEmpyNAwG7qmzvo/e7WxQHq9lJXdxlFhc+aaZAE5jbXCP1dUVrqyvqbFiaW7yazeMhaxilnRfTkRUvz8nKsYUP7ahhSNzDme/j4Vt0YAVF7nH9TPILHjyiEJNE6X29z5xbl9bxxqWGTq1IgL+mlNPZhqA7u7EAw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715692671; c=relaxed/simple; bh=h30CdDcKy1aa9nOwMaEOcIznaigfbz3L1bFiMg+w6P0=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=GSbbB7FZr6VFPY5SUtM46q+dlLDPLg+Y4MTbUv/BV75rB/ltB8iImhEqiNbArust9uzMazuwsepMpZXniiu4pyhJSeyS2ohs3T03T71Ffeqd4pIQjZDZGM2vLLWc0KnVBejo+ioucGkTKFJcwIcp+MLsp2P/9J/w/kvlAQBAR8k= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=VABahaYF; arc=none smtp.client-ip=209.85.214.177 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="VABahaYF" Received: by mail-pl1-f177.google.com with SMTP id d9443c01a7336-1ee5235f5c9so42972365ad.2; Tue, 14 May 2024 06:17:49 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1715692669; x=1716297469; 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=UswTy1g0h8WhpaF1uYYHqxzWfUsMnuzmqG3fJ4x5ZR4=; b=VABahaYFQ7nuVgf1tdqMTn/HJ0FPosc+BwUoUk2JrybQVELWa+rPyJI4AePTBR9C1z +cjcyXg3RLlnKHSJSJglPIbeszkN/TXF+EC7EZnHxZVQn0MnPSD6Z1K66uh8dY7uZ7K+ JJiGrM7/vuNfUutpYbj8axRVpiwQ9x6lwRM83KfBT5VOSgur6FXp40Kmfi71zREkOf80 72sfoEvwQYvuXezUJ4a8A3KWrtrjBO7BMsaKNkPMN/aHoGYK3SHEM6ggEEUsUHLGJyAP 2CCpO+m+/wCqIYk4osVkDZRuuqmTrFUe6VleTCFTbsnpUyij+KKz73gyqFZMi4+He1pM a4Pw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1715692669; x=1716297469; 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=UswTy1g0h8WhpaF1uYYHqxzWfUsMnuzmqG3fJ4x5ZR4=; b=dY1ZHF29hZnXIqbn0ULCF8v56QHaIA5acH8RVCbL/APzO6acovXwDSmSCmBD0j5nxS 2dPpVZ/o09d5Ax4TBpl9KkNFjQv1v5vN9uUz73AuvP7AQi3tm50T2xr4chMm2rsuuHvW avxpPziMnuujn0eI2Wf7YCxt/VaYQN1aVmOtlFvQyfHkbzr41DDEzHeUrINhKBZ1lrNr rNQAVb5I6QPfoDgZCoSWcf4G9sl+XLzNR3WORh7rFynaZCIJFoPSuyToF83adcyFrsU7 Xu5qLx5a/uMkB12ffwE0LbnDDxTbiuRn8FjllSoIpS9cjwGnq4ednHqs4D8ASHyq5WxX slPA== X-Forwarded-Encrypted: i=1; AJvYcCUcTIDrQMZzvDQuBmf8fyIlhL02eXpBp/O5I/ztCDnZ6YS31VAo7erdQPD/fjO6eRpms3+m8zIX7D9lrfZnPq0kKjbEG1g9Fe7C65jfTN0tAKL3FoV4HLSCg4/4qROV/beh0l7H/7LUo4hzZcn17l/o0Gv9FPb4iZRdzEUzIgGSEF96TdULK5ljMvqW X-Gm-Message-State: AOJu0Yx2sKkxmJ6mn5/b+JVoTRUqV0mH0ZsojV+IpIfwImcyiSwUrlCV oOqt4PLzTHmnC2nughZE+OCYOqxTrk4rFTZh3Rrfdyy18FZZfvsu X-Google-Smtp-Source: AGHT+IGXZ7t2RoJZuqSV7ivH2R4Y+O4hlmvY+r3Qs37Cap83W+F43/fSEwpRoAr96KAI/09AwX/U0w== X-Received: by 2002:a17:902:bd86:b0:1ea:5aff:c8ce with SMTP id d9443c01a7336-1ef43d2e1cfmr117956635ad.29.1715692669005; Tue, 14 May 2024 06:17:49 -0700 (PDT) Received: from wedsonaf-dev.. ([50.204.89.32]) by smtp.googlemail.com with ESMTPSA id d9443c01a7336-1ef0b9d18a4sm97277335ad.56.2024.05.14.06.17.48 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 14 May 2024 06:17:48 -0700 (PDT) From: Wedson Almeida Filho To: Alexander Viro , Christian Brauner , Matthew Wilcox , Dave Chinner Cc: Kent Overstreet , Greg Kroah-Hartman , linux-fsdevel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, Wedson Almeida Filho Subject: [RFC PATCH v2 10/30] rust: fs: add empty file operations Date: Tue, 14 May 2024 10:16:51 -0300 Message-Id: <20240514131711.379322-11-wedsonaf@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240514131711.379322-1-wedsonaf@gmail.com> References: <20240514131711.379322-1-wedsonaf@gmail.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Wedson Almeida Filho This is in preparation for allowing modules to implement different file callbacks, which will be introduced in subsequent patches. Signed-off-by: Wedson Almeida Filho --- rust/kernel/fs/file.rs | 57 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/rust/kernel/fs/file.rs b/rust/kernel/fs/file.rs index b8386a396251..67dd3ecf7d98 100644 --- a/rust/kernel/fs/file.rs +++ b/rust/kernel/fs/file.rs @@ -14,6 +14,7 @@ types::{ARef, AlwaysRefCounted, Opaque}, }; use core::{marker::PhantomData, ptr}; +use macros::vtable; /// Flags associated with a [`File`]. pub mod flags { @@ -268,3 +269,59 @@ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.pad("EBADF") } } + +/// Operations implemented by files. +#[vtable] +pub trait Operations { + /// File system that these operations are compatible with. + type FileSystem: FileSystem + ?Sized; +} + +/// Represents file operations. +#[allow(dead_code)] +pub struct Ops(pub(crate) *const bindings::file_operations, PhantomData); + +impl Ops { + /// Creates file operations from a type that implements the [`Operations`] trait. + pub const fn new + ?Sized>() -> Self { + struct Table(PhantomData); + impl Table { + const TABLE: bindings::file_operations = bindings::file_operations { + owner: ptr::null_mut(), + llseek: None, + read: None, + write: None, + read_iter: None, + write_iter: None, + iopoll: None, + iterate_shared: None, + 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, + }; + } + Self(&Table::::TABLE, PhantomData) + } +} From patchwork Tue May 14 13:16:52 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wedson Almeida Filho X-Patchwork-Id: 13664129 Received: from mail-pl1-f181.google.com (mail-pl1-f181.google.com [209.85.214.181]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A5A3814F135; Tue, 14 May 2024 13:17:50 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.181 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715692672; cv=none; b=s+IPEWT+yiM/tKxYIb2ZlzbzmVabf/Jy/e4a2cJ1ShOr+mEqGsWbYLDSJSeFAiyvYSmXImltprE0a4Q1MljdfmkIdcBphF8Ikbp91Q7t6JXwqS6SZR2mEPhRBbU2F4HIG9+R4D9Cx8czb1Us5pnuSYrpPWjAPzXcpFkfW8K1XUw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715692672; c=relaxed/simple; bh=lvShv7o989WUL7BP6s6yJWWP7E2ugWSm7FrWqYQBpeY=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=fxf5g01J6/xk/JGRGxamoXK9WfUIO6t9FDmUOk8eqAQIh2hTnkXMMz+jQKdd3sN7awU7/SDGo7hZf781etYjvG83Xl7YpLp6kwq2HHzkdAjeiJun4LIDQbLZUN+iMNyb1K9N6tAahknmksjd2LRujPBv+5WWelGaVQQCNPcuX3s= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=SWl1wlB5; arc=none smtp.client-ip=209.85.214.181 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="SWl1wlB5" Received: by mail-pl1-f181.google.com with SMTP id d9443c01a7336-1eb0e08bfd2so32609735ad.1; Tue, 14 May 2024 06:17:50 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1715692670; x=1716297470; 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=IcsGyGuiqU3jPIGnlfpE9jCbCuDL1CH0W6DIoxyhi2A=; b=SWl1wlB5EJjAq+/vJrzDtX0Rib5ssvlRR/larl2ycSS6EnlRMxNL+zSmvSW/XAWlmy DUCEIt3XkT7Sg4AsEfd6WPcS6TjDH/xyWsJwqXX7dbvR1Y0l8R4lD4Z9Z5AjC5qU6hDm +G1HbEcMu8xQxl5qnyETLfdkTmTiA5l6Pukh/lvbi6c5oprWhuCV1wO6RUC3vSADSPMP 6uRAC1pSkR0MbDNN6pD+OY2zogZJG3zq0PLo6HrOW9mixMnPyQzx2km+06dU+fJB64PU owqDms/KLjDN8ujtOiFY5I2XrzOUJzGDaeaBLpSV8ymRDkkSGT6ulfYkh0j0mYqCP+eq Rfhg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1715692670; x=1716297470; 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=IcsGyGuiqU3jPIGnlfpE9jCbCuDL1CH0W6DIoxyhi2A=; b=qoIR7nr0qjuyiXzaukj0NawEi4oL0gnKHJRwylHFzILYyTw1re/ke2GRiocUuSmBZ0 jyKKzdvdBS0yihBXJHASXA6QRcAqMxjBzG2exeVl8a8JSs2iaMRVddcujcRWaGqUALfi 7g0NyL1MaYTxpKOHnVs+a5MOGyohXZgZF20ag0pNm98YbW4Jg3R6ZDuUBI8Noa/CcgbO S2rnyUEddaPjrOhu7yJS4MxldY5xqZMQC/IYK0s2QN5AJif5L+bIzRRyMZ6hvI+P5UgM 27skWZ7yYxqYUbfQQv+uqvNk0ZgVSBTnTuRxIBKSwD2NpTQv8UHJTRUOAa/Nejyzdzqk YKWA== X-Forwarded-Encrypted: i=1; AJvYcCXvSuw0qvSYV7wYRvadR1ad9WDSoGKJu7BVkVYyJAie+N3+qBJ7wp1QX/+v8izkeN6c8eUHiiwYbDBihL3J+9EQ5FApuV4eWHLYLuLwTLgk2l3NK/2MCf+IuedD7XsY+2Q2PBqiB/QvRzsGokAkjyL8ZGEMlFHaCHOkIpMN8q1M3gjvY2FEqL0OlFaY X-Gm-Message-State: AOJu0YyU8CdKsklR3lKpIKg36WPGO6NxAL3nsbp1d2Y8EK4jlUQNqN3g DXHCgoGn7kCE6ne5gcPL38SgL+CQgkr3LK946R0JYh6lnqOe1JrA X-Google-Smtp-Source: AGHT+IGMv4o2rW6xKOQ5T8ig/4Rd5TSPWGzO+2tpLor2HW7gD3KrbE+1N7SdAEt/p3VRvshAcpZEew== X-Received: by 2002:a17:902:650d:b0:1eb:5c0f:6e78 with SMTP id d9443c01a7336-1ef43c0cf75mr111764415ad.11.1715692669968; Tue, 14 May 2024 06:17:49 -0700 (PDT) Received: from wedsonaf-dev.. ([50.204.89.32]) by smtp.googlemail.com with ESMTPSA id d9443c01a7336-1ef0b9d18a4sm97277335ad.56.2024.05.14.06.17.49 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 14 May 2024 06:17:49 -0700 (PDT) From: Wedson Almeida Filho To: Alexander Viro , Christian Brauner , Matthew Wilcox , Dave Chinner Cc: Kent Overstreet , Greg Kroah-Hartman , linux-fsdevel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, Wedson Almeida Filho Subject: [RFC PATCH v2 11/30] rust: fs: introduce `file::Operations::read_dir` Date: Tue, 14 May 2024 10:16:52 -0300 Message-Id: <20240514131711.379322-12-wedsonaf@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240514131711.379322-1-wedsonaf@gmail.com> References: <20240514131711.379322-1-wedsonaf@gmail.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Wedson Almeida Filho Allows 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/helpers.c | 12 +++ rust/kernel/fs/file.rs | 176 ++++++++++++++++++++++++++++++++++++-- rust/kernel/fs/inode.rs | 31 +++++-- samples/rust/rust_rofs.rs | 85 +++++++++++++++--- 4 files changed, 279 insertions(+), 25 deletions(-) diff --git a/rust/helpers.c b/rust/helpers.c index 87301e1ace65..deb2d21f3096 100644 --- a/rust/helpers.c +++ b/rust/helpers.c @@ -195,6 +195,18 @@ unsigned long rust_helper_copy_to_user(void __user *to, const void *from, } EXPORT_SYMBOL_GPL(rust_helper_copy_to_user); +void rust_helper_inode_lock_shared(struct inode *inode) +{ + inode_lock_shared(inode); +} +EXPORT_SYMBOL_GPL(rust_helper_inode_lock_shared); + +void rust_helper_inode_unlock_shared(struct inode *inode) +{ + inode_unlock_shared(inode); +} +EXPORT_SYMBOL_GPL(rust_helper_inode_unlock_shared); + /* * `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/file.rs b/rust/kernel/fs/file.rs index 67dd3ecf7d98..6d61723f440d 100644 --- a/rust/kernel/fs/file.rs +++ b/rust/kernel/fs/file.rs @@ -7,13 +7,13 @@ //! C headers: [`include/linux/fs.h`](srctree/include/linux/fs.h) and //! [`include/linux/file.h`](srctree/include/linux/file.h) -use super::{dentry::DEntry, inode::INode, FileSystem, UnspecifiedFS}; +use super::{dentry::DEntry, inode, inode::INode, inode::Ino, FileSystem, Offset, UnspecifiedFS}; use crate::{ bindings, - error::{code::*, Error, Result}, - types::{ARef, AlwaysRefCounted, Opaque}, + error::{code::*, from_result, Error, Result}, + types::{ARef, AlwaysRefCounted, Locked, Opaque}, }; -use core::{marker::PhantomData, ptr}; +use core::{marker::PhantomData, mem::ManuallyDrop, ptr}; use macros::vtable; /// Flags associated with a [`File`]. @@ -275,10 +275,20 @@ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { pub trait Operations { /// File system that these operations are compatible with. type FileSystem: FileSystem + ?Sized; + + /// Reads directory entries from directory files. + /// + /// [`DirEmitter::pos`] holds the current position of the directory reader. + fn read_dir( + _file: &File, + _inode: &Locked<&INode, inode::ReadSem>, + _emitter: &mut DirEmitter, + ) -> Result { + Err(EINVAL) + } } /// Represents file operations. -#[allow(dead_code)] pub struct Ops(pub(crate) *const bindings::file_operations, PhantomData); impl Ops { @@ -294,7 +304,11 @@ impl Table { read_iter: None, write_iter: None, iopoll: None, - iterate_shared: None, + iterate_shared: if T::HAS_READ_DIR { + Some(Self::read_dir_callback) + } else { + None + }, poll: None, unlocked_ioctl: None, compat_ioctl: None, @@ -321,7 +335,157 @@ impl Table { uring_cmd: None, uring_cmd_iopoll: None, }; + + unsafe extern "C" fn read_dir_callback( + file_ptr: *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 the duration of the + // callback. Since this callback is specifically for filesystem T, we know `T` + // is the right filesystem. + let file = unsafe { File::from_raw(file_ptr) }; + + // 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(); + + // SAFETY: The C API guarantees that the inode's rw semaphore is locked in read + // mode. It does not expect callees to unlock it, so we make the locked object + // manually dropped to avoid unlocking it. + let locked = ManuallyDrop::new(unsafe { Locked::new(file.inode()) }); + + // 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(file, &locked, emitter) { + Ok(_) => Ok(0), + Err(e) => { + if emitter.pos() == orig_pos { + Err(e) + } else { + Ok(0) + } + } + } + }) + } } Self(&Table::::TABLE, PhantomData) } } + +/// The types of directory entries reported by [`Operations::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: inode::Type) -> Self { + match value { + inode::Type::Dir => DirEntryType::Dir, + } + } +} + +impl 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), + } + } +} + +/// Directory entry emitter. +/// +/// This is used in [`Operations::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) -> Offset { + 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: Offset, 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 + } +} diff --git a/rust/kernel/fs/inode.rs b/rust/kernel/fs/inode.rs index 11df493314ea..d84d8d2f7076 100644 --- a/rust/kernel/fs/inode.rs +++ b/rust/kernel/fs/inode.rs @@ -6,9 +6,9 @@ //! //! C headers: [`include/linux/fs.h`](srctree/include/linux/fs.h) -use super::{sb::SuperBlock, FileSystem, Offset, UnspecifiedFS}; +use super::{file, sb::SuperBlock, FileSystem, Offset, UnspecifiedFS}; use crate::error::Result; -use crate::types::{ARef, AlwaysRefCounted, Opaque}; +use crate::types::{ARef, AlwaysRefCounted, Lockable, Opaque}; use crate::{bindings, block, time::Timespec}; use core::mem::ManuallyDrop; use core::{marker::PhantomData, ptr}; @@ -78,6 +78,22 @@ unsafe fn dec_ref(obj: ptr::NonNull) { } } +/// Indicates that the an inode's rw semapahore is locked in read (shared) mode. +pub struct ReadSem; + +unsafe impl Lockable for INode { + fn raw_lock(&self) { + // SAFETY: Since there's a reference to the inode, it must be valid. + unsafe { bindings::inode_lock_shared(self.0.get()) }; + } + + unsafe fn unlock(&self) { + // SAFETY: Since there's a reference to the inode, it must be valid. Additionally, the + // safety requirements of this functino require that the inode be locked in read mode. + unsafe { bindings::inode_unlock_shared(self.0.get()) }; + } +} + /// An inode that is locked and hasn't been initialised yet. /// /// # Invariants @@ -95,9 +111,6 @@ pub fn init(mut self, params: Params) -> Result>> { let inode = unsafe { self.0.as_mut() }; let mode = match params.typ { Type::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 }; @@ -126,6 +139,14 @@ pub fn init(mut self, params: Params) -> Result>> { // being called with the `ManuallyDrop` instance created above. Ok(unsafe { ARef::from_raw(manual.0.cast::>()) }) } + + /// Sets the file operations on this new inode. + pub fn set_fops(&mut self, fops: file::Ops) -> &mut Self { + // SAFETY: By the type invariants, it's ok to modify the inode. + let inode = unsafe { self.0.as_mut() }; + inode.__bindgen_anon_3.i_fop = fops.0; + self + } } impl Drop for New { diff --git a/samples/rust/rust_rofs.rs b/samples/rust/rust_rofs.rs index d32c4645ebe8..9da01346d8f8 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::{dentry, inode, sb::SuperBlock}; +use kernel::fs::{dentry, file, file::File, inode, inode::INode, sb::SuperBlock}; use kernel::prelude::*; -use kernel::{c_str, fs, time::UNIX_EPOCH, types::Either}; +use kernel::{c_str, fs, time::UNIX_EPOCH, types::Either, types::Locked}; kernel::module_fs! { type: RoFs, @@ -14,6 +14,32 @@ license: "GPL", } +struct Entry { + name: &'static [u8], + ino: u64, + etype: inode::Type, +} + +const ENTRIES: [Entry; 3] = [ + Entry { + name: b".", + ino: 1, + etype: inode::Type::Dir, + }, + Entry { + name: b"..", + ino: 1, + etype: inode::Type::Dir, + }, + Entry { + name: b"subdir", + ino: 2, + etype: inode::Type::Dir, + }, +]; + +const DIR_FOPS: file::Ops = file::Ops::new::(); + struct RoFs; impl fs::FileSystem for RoFs { const NAME: &'static CStr = c_str!("rust_rofs"); @@ -26,19 +52,50 @@ fn fill_super(sb: &mut SuperBlock) -> Result { fn init_root(sb: &SuperBlock) -> Result> { let inode = match sb.get_or_create_inode(1)? { Either::Left(existing) => existing, - Either::Right(new) => new.init(inode::Params { - typ: inode::Type::Dir, - mode: 0o555, - size: 1, - blocks: 1, - nlink: 2, - uid: 0, - gid: 0, - atime: UNIX_EPOCH, - ctime: UNIX_EPOCH, - mtime: UNIX_EPOCH, - })?, + Either::Right(mut new) => { + new.set_fops(DIR_FOPS); + new.init(inode::Params { + typ: inode::Type::Dir, + mode: 0o555, + size: ENTRIES.len().try_into()?, + blocks: 1, + nlink: 2, + uid: 0, + gid: 0, + atime: UNIX_EPOCH, + ctime: UNIX_EPOCH, + mtime: UNIX_EPOCH, + })? + } }; dentry::Root::try_new(inode) } } + +#[vtable] +impl file::Operations for RoFs { + type FileSystem = Self; + + fn read_dir( + _file: &File, + inode: &Locked<&INode, inode::ReadSem>, + emitter: &mut file::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 Tue May 14 13:16:53 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wedson Almeida Filho X-Patchwork-Id: 13664130 Received: from mail-pl1-f182.google.com (mail-pl1-f182.google.com [209.85.214.182]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C546D154440; Tue, 14 May 2024 13:17:51 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.182 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715692673; cv=none; b=I+l+GOnilySu2qIISU9rU62jxPxGiiVBjq/tBzqv2aavxJ0KLM6zBIYuCZw4wLd/r+StGvEWtv/wOvGG9drv+LFhqm6nW9tESir8qqw4i+BpQAk7zEWllUSaRGVVuDXjigCEZwPX678uvfQT5Ji1DO2iskoLC4t4X49XlKr/ggA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715692673; c=relaxed/simple; bh=wuB3It+TBAp7wnBu5M8JzEodoSWd6CPbneTQkHAhkdc=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=rBxuLhqmcMh+6cDAXnbZmyKiLdY/ZSKBmYb9FZhIghRgvJxEWHrQ1oKdrCjAiVx4KwEi8a3cyNzEajPlM/7kHlqKt9uaFCPLx+CBUO+LPw0HoIWss69SCozzOEmvkUIgk0WZNkTvZ1rkWb9mEsMRd9Ac6Q4pwjEBGYzZF/GO4Ng= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=Y4P4ewg+; arc=none smtp.client-ip=209.85.214.182 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="Y4P4ewg+" Received: by mail-pl1-f182.google.com with SMTP id d9443c01a7336-1ec4b2400b6so46104505ad.3; Tue, 14 May 2024 06:17:51 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1715692671; x=1716297471; 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=s+bYrCazLpxO9BHsH1eQHGRSngtjj3DrEG5TYkqZ9Eg=; b=Y4P4ewg+1CdWqJpkxhliNdEt/nv485OtRLBsPBC3J16Q9rJ8u0VsaVSk9JPJfkguZn sirU5/hCzlaPzzrWlEQOGXXxQXv+YxwabB0pHnFZ9z8SpsRIgBTSSfv9s2oeiPTqGYAE rwErYy+fS95yZKvpjkiJKvKGPeP+dKGdpVEZFPRngGENq7bNQSNPnxqmDHez1VQWEc6m KdYIU8mlf+nPP9c2nYYvRccmcgfJZ8IQ6/Lf/9Ysh3KiHvCRbOrG5FFGwjCSrSuGpYNy rrgSBC9E3FuvVZpeYAYJS8kDmuKB5BFrEA/JXYTke0Q2tNGP7flJbtNWwPzBs1+rsDIB dzJw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1715692671; x=1716297471; 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=s+bYrCazLpxO9BHsH1eQHGRSngtjj3DrEG5TYkqZ9Eg=; b=V81dODsNLYE63Dtb1ibAx2mw58BWluyVesrbGqW6rYMYwqTJVan1B30M/1CUF95PxL hduWHPxTFz4jjqx94egqihtIDZd8TgUqhqXHReKkWAwA6weDSlxtNMf8gHcsXFSCGnIs gfEjT4yh1WK4ND7aSmdJY1+UrATerKxhDJVOCHN8XirsDARYN1I6AFBVnPP/Hjr9XQ+N S6P3s9dkw0TZu1ksDCcK/jeTzLetA6enDY7n5vKNN10qhvxK1iBi+DK+8tKUsUA7K46S PuRYuEmBDcI0iA7ZOnuWW+vwk7Xg1gXykQjEI1Hx5OSlGA91HqACw/I32GfuzSNsx4uE nSfg== X-Forwarded-Encrypted: i=1; AJvYcCXGEBZx+6UtadFtMTutaGrSkPaXxwZ1Ci2cw7JSxLqz5qXwFel888kkIt06rUZRPUJX+izwJLht2aPreD5RueUqMi+79fEppiCfnzz+rQjg2GDNdrFuP2B/StVEJlSv0r91KGSXEgkCWNjuTfeAzWwlqC1jXHQZRMHcFbJRPPvbpSo90xhZN+rkXR+N X-Gm-Message-State: AOJu0Yys4kBrsS21PGTcpUGbYJbc0/xq0LKMsVrVmLb0V2BV1GJNjQnj rchJzzx04BqX8Y16v5Wvp5hCW9bpGQ07gGxNn5yERdq41Ik4g2NC X-Google-Smtp-Source: AGHT+IEuUtcs7ZCUJg+RWNBMdj2mRL3TnlznFWMlAE2CI2Rq0C6LUGI6l8XrGMrJgbIgQVugTU8ZnQ== X-Received: by 2002:a17:903:228f:b0:1eb:152a:5a6e with SMTP id d9443c01a7336-1ef43c0c9b5mr170815675ad.3.1715692670896; Tue, 14 May 2024 06:17:50 -0700 (PDT) Received: from wedsonaf-dev.. ([50.204.89.32]) by smtp.googlemail.com with ESMTPSA id d9443c01a7336-1ef0b9d18a4sm97277335ad.56.2024.05.14.06.17.50 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 14 May 2024 06:17:50 -0700 (PDT) From: Wedson Almeida Filho To: Alexander Viro , Christian Brauner , Matthew Wilcox , Dave Chinner Cc: Kent Overstreet , Greg Kroah-Hartman , linux-fsdevel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, Wedson Almeida Filho Subject: [RFC PATCH v2 12/30] rust: fs: introduce `file::Operations::seek` Date: Tue, 14 May 2024 10:16:53 -0300 Message-Id: <20240514131711.379322-13-wedsonaf@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240514131711.379322-1-wedsonaf@gmail.com> References: <20240514131711.379322-1-wedsonaf@gmail.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Wedson Almeida Filho This allows file systems to customise their behaviour when callers want to seek to a different file location, which may also be used when reading directory entries. Signed-off-by: Wedson Almeida Filho --- rust/kernel/fs/file.rs | 73 ++++++++++++++++++++++++++++++++++++++- samples/rust/rust_rofs.rs | 6 +++- 2 files changed, 77 insertions(+), 2 deletions(-) diff --git a/rust/kernel/fs/file.rs b/rust/kernel/fs/file.rs index 6d61723f440d..77eb6d230568 100644 --- a/rust/kernel/fs/file.rs +++ b/rust/kernel/fs/file.rs @@ -270,12 +270,65 @@ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { } } +/// Indicates how to interpret the `offset` argument in [`Operations::seek`]. +#[repr(u32)] +pub enum Whence { + /// `offset` bytes from the start of the file. + Set = bindings::SEEK_SET, + + /// `offset` bytes from the end of the file. + End = bindings::SEEK_END, + + /// `offset` bytes from the current location. + Cur = bindings::SEEK_CUR, + + /// The next location greater than or equal to `offset` that contains data. + Data = bindings::SEEK_DATA, + + /// The next location greater than or equal to `offset` that contains a hole. + Hole = bindings::SEEK_HOLE, +} + +impl TryFrom for Whence { + type Error = crate::error::Error; + + fn try_from(v: i32) -> Result { + match v { + v if v == Self::Set as i32 => Ok(Self::Set), + v if v == Self::End as i32 => Ok(Self::End), + v if v == Self::Cur as i32 => Ok(Self::Cur), + v if v == Self::Data as i32 => Ok(Self::Data), + v if v == Self::Hole as i32 => Ok(Self::Hole), + _ => Err(EDOM), + } + } +} + +/// Generic implementation of [`Operations::seek`]. +pub fn generic_seek( + file: &File, + offset: Offset, + whence: Whence, +) -> Result { + let n = unsafe { bindings::generic_file_llseek(file.0.get(), offset, whence as i32) }; + if n < 0 { + Err(Error::from_errno(n.try_into()?)) + } else { + Ok(n) + } +} + /// Operations implemented by files. #[vtable] pub trait Operations { /// File system that these operations are compatible with. type FileSystem: FileSystem + ?Sized; + /// Seeks the file to the given offset. + fn seek(_file: &File, _offset: Offset, _whence: Whence) -> Result { + Err(EINVAL) + } + /// Reads directory entries from directory files. /// /// [`DirEmitter::pos`] holds the current position of the directory reader. @@ -298,7 +351,11 @@ pub const fn new + ?Sized>() -> Self { impl Table { const TABLE: bindings::file_operations = bindings::file_operations { owner: ptr::null_mut(), - llseek: None, + llseek: if T::HAS_SEEK { + Some(Self::seek_callback) + } else { + None + }, read: None, write: None, read_iter: None, @@ -336,6 +393,20 @@ impl Table { uring_cmd_iopoll: None, }; + unsafe extern "C" fn seek_callback( + file_ptr: *mut bindings::file, + offset: bindings::loff_t, + whence: i32, + ) -> bindings::loff_t { + from_result(|| { + // SAFETY: The C API guarantees that `file` is valid for the duration of the + // callback. Since this callback is specifically for filesystem T, we know `T` + // is the right filesystem. + let file = unsafe { File::from_raw(file_ptr) }; + T::seek(file, offset, whence.try_into()?) + }) + } + unsafe extern "C" fn read_dir_callback( file_ptr: *mut bindings::file, ctx_ptr: *mut bindings::dir_context, diff --git a/samples/rust/rust_rofs.rs b/samples/rust/rust_rofs.rs index 9da01346d8f8..abec084360da 100644 --- a/samples/rust/rust_rofs.rs +++ b/samples/rust/rust_rofs.rs @@ -2,7 +2,7 @@ //! Rust read-only file system sample. -use kernel::fs::{dentry, file, file::File, inode, inode::INode, sb::SuperBlock}; +use kernel::fs::{dentry, file, file::File, inode, inode::INode, sb::SuperBlock, Offset}; use kernel::prelude::*; use kernel::{c_str, fs, time::UNIX_EPOCH, types::Either, types::Locked}; @@ -76,6 +76,10 @@ fn init_root(sb: &SuperBlock) -> Result> { impl file::Operations for RoFs { type FileSystem = Self; + fn seek(file: &File, offset: Offset, whence: file::Whence) -> Result { + file::generic_seek(file, offset, whence) + } + fn read_dir( _file: &File, inode: &Locked<&INode, inode::ReadSem>, From patchwork Tue May 14 13:16:54 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wedson Almeida Filho X-Patchwork-Id: 13664131 Received: from mail-pl1-f180.google.com (mail-pl1-f180.google.com [209.85.214.180]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8D8AF154BF7; Tue, 14 May 2024 13:17:52 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.180 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715692674; cv=none; b=bobyU1dqXvwt8GqzZFAGq0tBkPSv2y4AV60j+IFuuo+5x8mfSYT/Bkkrv6GkjnxS5FFqULMzvulwtnp3N44qZDM7ZSoPBTJrLwbVDTjayar4WYH0XB1Uup6PFVCcX/MGfbaJUBqO46NJGSJ3dX0TJeng6L3wwkhQVQ/C4PazOi4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715692674; c=relaxed/simple; bh=kKoj43kcpdB4g3eGetsZvcZ682MK2Cdc/C4K6JHs0D0=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=kC8dt+/inpeqWj+StXS0Wbh8fiRbeq3egpdP/y3P14EzNum89EupqTubs/SmmvvUEzIoWWnkkBIfjcwaFIt0CNGV9uB+SvC0MS5Cr29/0E9jHp2l6zW7VYPgPjWbQu8A1nwI/ZhIyRGjdcXujcq4zRGLUCRDIIJl7tfPRpcDCj0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=hxj6gUQJ; arc=none smtp.client-ip=209.85.214.180 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="hxj6gUQJ" Received: by mail-pl1-f180.google.com with SMTP id d9443c01a7336-1eb24e3a2d9so48931195ad.1; Tue, 14 May 2024 06:17:52 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1715692672; x=1716297472; 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=6cg/flIhVL4ROGj8221a6AJ0d2eWqZBDXs4XmumwzXA=; b=hxj6gUQJb6Tag+KIDmXd974CfLRaMZWfpVcYwpI7oO5uZ6WUVXXFjE1KjqzAVCxK9L i7nzgnJkLy0RjCdV/csTcl0y5PcQoJYedLRXe+5w5vwTTRg/AIef+UUXuyyPpZcnVftT SICpU5yArnjwyxStrugubwhPlNk9hV+Fsx8f1IYZC2KE1150VWC8KAJCwS914gkOQvE/ vznN+S1qX3aFw/CbjSQYFb1EzW2D2eJwZ2usqNUtXiTe1h/CiGL1Kf23MUIUsE+RCp2j KemWN6028qlZ4pkqn58qV9ec0bdaUJm+wVXWfoJlW+mX3bTq+9FZCDxZKlQakAwZ7uG1 jbww== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1715692672; x=1716297472; 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=6cg/flIhVL4ROGj8221a6AJ0d2eWqZBDXs4XmumwzXA=; b=EdZUIK/L/rbSEMCo1VI3vafa/35D9hEwtnqMm1wx5mLnPDWn6UDl9/zF4qGbHbBHMq iUIWEdiJpDjjYoDZCLxcA7jhRBohXJelN4+72WcwIXa0hd5MV1cEOmtFknpSQZcy8Iqz g/E0NFUewehbrE9nj4gO/TXwkSkqSBZXFWBy785SQB9xv/pVEjvUh5Lf2g4d1P5oIv32 7gig3u8CqB5Ql4+/Svk/Nd5cUvF0J+KZ0m7ANj0dVTjVF7mKamymN7q0mtPIqDCGfY8h FVmFuXS5qF9TEWFlmxaZLDg4D+uXm28A+PszhaQOKTWvkDPv4rMrGQ87BYtHoKrFl8yi XaUQ== X-Forwarded-Encrypted: i=1; AJvYcCWCb0vV3vHOXp8W+8COSXOUfvw9NotIHiPVOMi+mOGjOB67y/wDmT8iqBnx2rAI9mRSjTIIWO8JAeK42ProazHLSc7Zo+7aU0Nl0vgG/GDOrhD0U5nYqZqMzW+My8l9dCRggqveFjglv3HFbIBwSqGDgg4C5Ay5LNdbkdHZjiwcuXOxKxRWtUGCMNey X-Gm-Message-State: AOJu0Yww6p97ckM1DBAcKRxAZYSZFAnLF6eqiu/FEHE0ug40upgoUECr 4m1/7nMQHNcfFM6cFiUcyaGx76+hLT+QMTFsdC3VUnNHLiz4mpA1 X-Google-Smtp-Source: AGHT+IHs9v/uAfsfRjkW5AgULW5XdYuVday9qIJmZI1dl3llnLjW9rifR5daSxgoRHhxHneWExKOyw== X-Received: by 2002:a17:902:76c4:b0:1e4:3909:47c0 with SMTP id d9443c01a7336-1ef4404e0c5mr153954985ad.62.1715692671864; Tue, 14 May 2024 06:17:51 -0700 (PDT) Received: from wedsonaf-dev.. ([50.204.89.32]) by smtp.googlemail.com with ESMTPSA id d9443c01a7336-1ef0b9d18a4sm97277335ad.56.2024.05.14.06.17.51 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 14 May 2024 06:17:51 -0700 (PDT) From: Wedson Almeida Filho To: Alexander Viro , Christian Brauner , Matthew Wilcox , Dave Chinner Cc: Kent Overstreet , Greg Kroah-Hartman , linux-fsdevel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, Wedson Almeida Filho Subject: [RFC PATCH v2 13/30] rust: fs: introduce `file::Operations::read` Date: Tue, 14 May 2024 10:16:54 -0300 Message-Id: <20240514131711.379322-14-wedsonaf@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240514131711.379322-1-wedsonaf@gmail.com> References: <20240514131711.379322-1-wedsonaf@gmail.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Wedson Almeida Filho This allows file systems to customise their behaviour when callers want to read from a file. Signed-off-by: Wedson Almeida Filho --- rust/kernel/fs/file.rs | 35 ++++++++++++++++++++++++++++++++++- rust/kernel/user.rs | 1 - samples/rust/rust_rofs.rs | 6 +++++- 3 files changed, 39 insertions(+), 3 deletions(-) diff --git a/rust/kernel/fs/file.rs b/rust/kernel/fs/file.rs index 77eb6d230568..2ba456a1eee1 100644 --- a/rust/kernel/fs/file.rs +++ b/rust/kernel/fs/file.rs @@ -12,6 +12,7 @@ bindings, error::{code::*, from_result, Error, Result}, types::{ARef, AlwaysRefCounted, Locked, Opaque}, + user, }; use core::{marker::PhantomData, mem::ManuallyDrop, ptr}; use macros::vtable; @@ -324,6 +325,15 @@ pub trait Operations { /// File system that these operations are compatible with. type FileSystem: FileSystem + ?Sized; + /// Reads data from this file into the caller's buffer. + fn read( + _file: &File, + _buffer: &mut user::Writer, + _offset: &mut Offset, + ) -> Result { + Err(EINVAL) + } + /// Seeks the file to the given offset. fn seek(_file: &File, _offset: Offset, _whence: Whence) -> Result { Err(EINVAL) @@ -356,7 +366,11 @@ impl Table { } else { None }, - read: None, + read: if T::HAS_READ { + Some(Self::read_callback) + } else { + None + }, write: None, read_iter: None, write_iter: None, @@ -407,6 +421,25 @@ impl Table { }) } + unsafe extern "C" fn read_callback( + file_ptr: *mut bindings::file, + ptr: *mut core::ffi::c_char, + len: usize, + offset: *mut bindings::loff_t, + ) -> isize { + from_result(|| { + // SAFETY: The C API guarantees that `file` is valid for the duration of the + // callback. Since this callback is specifically for filesystem T, we know `T` + // is the right filesystem. + let file = unsafe { File::from_raw(file_ptr) }; + let mut writer = user::Writer::new(ptr, len); + + // SAFETY: The C API guarantees that `offset` is valid for read and write. + let read = T::read(file, &mut writer, unsafe { &mut *offset })?; + Ok(isize::try_from(read)?) + }) + } + unsafe extern "C" fn read_dir_callback( file_ptr: *mut bindings::file, ctx_ptr: *mut bindings::dir_context, diff --git a/rust/kernel/user.rs b/rust/kernel/user.rs index 35a673ebcd58..20fb887f4640 100644 --- a/rust/kernel/user.rs +++ b/rust/kernel/user.rs @@ -11,7 +11,6 @@ pub struct Writer { } impl Writer { - #[allow(dead_code)] pub(crate) fn new(ptr: *mut i8, len: usize) -> Self { Self { ptr: ptr.cast::(), diff --git a/samples/rust/rust_rofs.rs b/samples/rust/rust_rofs.rs index abec084360da..f4be5908369c 100644 --- a/samples/rust/rust_rofs.rs +++ b/samples/rust/rust_rofs.rs @@ -4,7 +4,7 @@ use kernel::fs::{dentry, file, file::File, inode, inode::INode, sb::SuperBlock, Offset}; use kernel::prelude::*; -use kernel::{c_str, fs, time::UNIX_EPOCH, types::Either, types::Locked}; +use kernel::{c_str, fs, time::UNIX_EPOCH, types::Either, types::Locked, user}; kernel::module_fs! { type: RoFs, @@ -80,6 +80,10 @@ fn seek(file: &File, offset: Offset, whence: file::Whence) -> Result, _: &mut user::Writer, _: &mut Offset) -> Result { + Err(EISDIR) + } + fn read_dir( _file: &File, inode: &Locked<&INode, inode::ReadSem>, From patchwork Tue May 14 13:16:55 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wedson Almeida Filho X-Patchwork-Id: 13664132 Received: from mail-pl1-f177.google.com (mail-pl1-f177.google.com [209.85.214.177]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5D2501552E3; Tue, 14 May 2024 13:17:53 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.177 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715692675; cv=none; b=VPIimG6F3cx7mZE2ftuah9A2ffddQSw166rgarCbSYiwGZm0bOe+1PmPvC80RI5eVEN6HEzuSIGDw6rswn9vyLiMI0EnWk0MMfQ1Bat6WzDao0GGSup3jHsp3bRREmPWuh+2vrH7BB4FFwOLQwk2foWyMbeherDv8MhmCMGq6K0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715692675; c=relaxed/simple; bh=Bsu4aVsU1f2mWSnfl7Emzs4XvgDxaKv2CMGwS96ITvc=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=TdgJQ6kTFZBLBFX1m1kr4Vg1QgDw/BP3IRP3HrGAS7E3KBHoGysE1Y3BvVyXPVBm7934boG2WzhqRC1kTGatXgOQYc4y+VPFItGte6DOuJO7Ai8iT+NBRpg8yHynYeiKE83mH8kgiuvIC+/l8pk5E01Od2x9ViYFaYWnWQhKZOg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=RUlh17V4; arc=none smtp.client-ip=209.85.214.177 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="RUlh17V4" Received: by mail-pl1-f177.google.com with SMTP id d9443c01a7336-1ee7963db64so46677245ad.1; Tue, 14 May 2024 06:17:53 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1715692673; x=1716297473; 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=41lZAkvFJI0TX2nT3AaHdmxxznrE9S1HoBUIRdkD6nE=; b=RUlh17V4wjRJ1kQSmp0MKdAvpp4G1qSbILV+6ZHUM7uIjixB5PFedlr4w3ujEQE/iq 9ARQFL80UfpqP1mbaV7XJux1ftFo85jPkjoFGP/msbTzfzMSBZpiVKdSOrPXdaH4chpR ntgVEeDTgV4X7i2zWY7+revUhsbhdfM4aYaJdqIfDg5Ib++ANEZeSYrGdN75AT1HAsjh 6uIbAjrETf5P2Rds6RJpe02ata+/7c3HgBb7E10Emzt4nl4LNsR2DrgJhCK2ojMnLcOR dokb5fdN65niHJY0C8otopQJSPfnYlXX0MbM/GUfKkmH3x5rt08jWlrgLQB764QmBUHK ux1g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1715692673; x=1716297473; 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=41lZAkvFJI0TX2nT3AaHdmxxznrE9S1HoBUIRdkD6nE=; b=fpCQeksSen/SkBpSJuFkvTBKk7F4NGl2UIaoyR5GIh/SmMxN0ZYjqSyWSDYN1kNDoB OVhNRzu03DUdMCit9ba8sL21r8ez1MZLk50nJ2UqzyXEQwS3qcWw25uLpqVq952u3oVg gIVSQlNaMpH/EUAs6yGg+YyTv2C83aFFZHEPjolnAmPdxxggszqc6sAOhY0lvtK5yrOR xfHLRJBypdKm9esbc2ntc2cGwJVpZmCk/7xT44IuCvgzLiYx5efubWPB4cpVOrDYnYaU CUHinDXu4IEQ7Ko+vZ+AVvcNKycwhpBgRwh+3gbAdJNxJnpgkysGbN+8ZC0ttlEcV/HX lYRw== X-Forwarded-Encrypted: i=1; AJvYcCW77v0H5eu7YSKqgX5I3G9u1i09SKLh1uHhXsir7d31JdLTvUYoPWfmfc7ryqfkXditBmiOIQtt41R+si2pbSZoEZo/SssTeoqMh1njZQ5ZccAXSfGbqnvI+uAxVwLcfB7ssP1DEbIml9YGNUaohWPLMzzPm9B6nIcr4NDPnqHqwjNhfGztLzLOtlvB X-Gm-Message-State: AOJu0YwlsO1bt+KUkT6YWVifsk4bjzhbuLFiUlB0DnM+A5n1aibQbEiV 50Cp8HlLF3VO3AL3W3QxTElgA+Ck/1h+3UM0y+0Yzu5jaK5bxpM7 X-Google-Smtp-Source: AGHT+IHacAzjvn03qQrbcW6J1nRF6deOobtxlsm4HKKcWOwaYTYmM/7tK9YZHCcX+VFKi94xCoRgvQ== X-Received: by 2002:a17:902:e804:b0:1e6:401a:bd91 with SMTP id d9443c01a7336-1ef440596d5mr156775655ad.57.1715692672772; Tue, 14 May 2024 06:17:52 -0700 (PDT) Received: from wedsonaf-dev.. ([50.204.89.32]) by smtp.googlemail.com with ESMTPSA id d9443c01a7336-1ef0b9d18a4sm97277335ad.56.2024.05.14.06.17.52 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 14 May 2024 06:17:52 -0700 (PDT) From: Wedson Almeida Filho To: Alexander Viro , Christian Brauner , Matthew Wilcox , Dave Chinner Cc: Kent Overstreet , Greg Kroah-Hartman , linux-fsdevel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, Wedson Almeida Filho Subject: [RFC PATCH v2 14/30] rust: fs: add empty inode operations Date: Tue, 14 May 2024 10:16:55 -0300 Message-Id: <20240514131711.379322-15-wedsonaf@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240514131711.379322-1-wedsonaf@gmail.com> References: <20240514131711.379322-1-wedsonaf@gmail.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Wedson Almeida Filho This is in preparation for allowing modules to implement different inode callbacks, which will be introduced in subsequent patches. Signed-off-by: Wedson Almeida Filho --- rust/kernel/fs/inode.rs | 48 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/rust/kernel/fs/inode.rs b/rust/kernel/fs/inode.rs index d84d8d2f7076..3d65b917af0e 100644 --- a/rust/kernel/fs/inode.rs +++ b/rust/kernel/fs/inode.rs @@ -12,10 +12,18 @@ use crate::{bindings, block, time::Timespec}; use core::mem::ManuallyDrop; use core::{marker::PhantomData, ptr}; +use macros::vtable; /// The number of an inode. pub type Ino = u64; +/// Operations implemented by inodes. +#[vtable] +pub trait Operations { + /// File system that these operations are compatible with. + type FileSystem: FileSystem + ?Sized; +} + /// A node (inode) in the file index. /// /// Wraps the kernel's `struct inode`. @@ -203,3 +211,43 @@ pub struct Params { /// Last access time. pub atime: Timespec, } + +/// Represents inode operations. +pub struct Ops(*const bindings::inode_operations, PhantomData); + +impl Ops { + /// Creates the inode operations from a type that implements the [`Operations`] trait. + pub const fn new + ?Sized>() -> Self { + struct Table(PhantomData); + impl Table { + const TABLE: bindings::inode_operations = bindings::inode_operations { + lookup: None, + 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, + }; + } + Self(&Table::::TABLE, PhantomData) + } +} From patchwork Tue May 14 13:16:56 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wedson Almeida Filho X-Patchwork-Id: 13664133 Received: from mail-pl1-f170.google.com (mail-pl1-f170.google.com [209.85.214.170]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8DE921553B4; Tue, 14 May 2024 13:17:54 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.170 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715692676; cv=none; b=tPI5hMZstuCGLlpEtIjugI+kCigqaxOKOvzxyWG3nTAkKTW9/lme574nw+P0Qz6SJA0WYjNmgbb0QrRagw+SA+oGhc47HkJ1/1GsvigPR/4+jRioPgstKeDaV7y6ZdB+CzNtRusiT2c/dGTFf6BlFS3kdI52SBpNYNoslJn1mbM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715692676; c=relaxed/simple; bh=3Yd39esXT+u62EdLtna/vNE2HE8gT4z3hM3aUbO9QMk=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=ptL7nkuLFNtHw8hnL8TkwrOAXsGFkLVjCoGdqKSo+jvU6CvAteEMKsozrg7ByitPLMDBZXT8PiO0Dk+jxNvPLSm1IhP6LJaBEDI6j9eDq9k6hxcxy0JGryfMRJ9YgLOiNyqfL5OoLeHAGNWYkcIuOH8HmAE5Wsk/iRbKuJW4mQ4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=UOENLJ2e; arc=none smtp.client-ip=209.85.214.170 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="UOENLJ2e" Received: by mail-pl1-f170.google.com with SMTP id d9443c01a7336-1eb0e08bfd2so32610405ad.1; Tue, 14 May 2024 06:17:54 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1715692674; x=1716297474; 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=LPU1PJsyXhU7+DXDV5zAoABFidh9cJJCkXkEr28GmXk=; b=UOENLJ2e5G41UFgCyrHk7D1T7fwgcmlLGBAqN0Be8TV+t6Jx/uncEHN2SV/6oBwKeL A/bTYQjUFQuqCMCxD39iK8sDJ57X7rEMF2w6TB7JtOlqGOBBdS+mBBugwLL87qKE9ZfS krhr4TfSaGJus8MM7e7EAiJYKWBzHCJ6ayRHXL1coR1TK31QGmCuu7OedysolxpsPDzL 9TmodVv99jvulnnHw+YRmZHPwniXJ1mEnESorSgQVMvQgPBcU4amXzm54tVVjkwwtPM4 koU+EvuKQNwru7CDhE+Yk3IZPBN+cd3Ry4nMpYfC4MMXCCN1b74KGrTDfJhx7DbjR1B3 zuPw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1715692674; x=1716297474; 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=LPU1PJsyXhU7+DXDV5zAoABFidh9cJJCkXkEr28GmXk=; b=coSAHyXsyjNtIg8nlo9yXZnOp2QvoWQkSoBxTxVC118AZ3k1SxuYp/SxHls8Pjn2k9 cg62Zn13n40dchVDEBNcD54VCVHaShr7ZDNuGlzdYRzo0DGorqZzoOCtPAYSQ+StD6IZ Fhhqbezls7mnZqQBpU43esfu9ZuTamKjuJNjt5QkF3owL1SlhkPe45jvBmiypZ4vcQIQ trmjjuv+JadhtWG4Fy8KRQtt15lIjEB9vmc1aVq23pjAE/95wNDB5rZ87+hd1LZ4SyoL jtua5xkpvihtNdZ+pulXyc/giOdEyo6pm3zHiiUuhqiujU1YfvFSSi+/OjHyhdRJ4VBe rudQ== X-Forwarded-Encrypted: i=1; AJvYcCVqW5ao4UM+C9234peLwu17Y77/8Xa43NNfNw7c087DP/7EmtA2NyyosiFTlUWcMT3P4JIRDuMk7aM+VXNSM02Aem8Lakt9u/UIpf6rbh8SNSkuPur4yJNqanMSN12IoHuPsjAh+VoOYVVl0bXuevezSbe0QBnFHFq/Gf7z91SvvDPolkLW+ytj7ujf X-Gm-Message-State: AOJu0Yxq8qC6MxiOrIP4PXS71x/lr7XE32Q2efJ61MsiQ42PhA2bPO2h lcw6X4UxMRej58HLoJrgmJqRfVLVt568+TRzSCvHEWxLowAg1R1h X-Google-Smtp-Source: AGHT+IGZgiXvKq66GWWkIsokJFEZ4y+wsWbeZwDVtxGtQYiDniSMBXMpzmoUlEnQkpk4DBvhoGWUiA== X-Received: by 2002:a17:902:da8e:b0:1e4:48a6:968b with SMTP id d9443c01a7336-1ef43c0cf05mr141137145ad.13.1715692673747; Tue, 14 May 2024 06:17:53 -0700 (PDT) Received: from wedsonaf-dev.. ([50.204.89.32]) by smtp.googlemail.com with ESMTPSA id d9443c01a7336-1ef0b9d18a4sm97277335ad.56.2024.05.14.06.17.52 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 14 May 2024 06:17:53 -0700 (PDT) From: Wedson Almeida Filho To: Alexander Viro , Christian Brauner , Matthew Wilcox , Dave Chinner Cc: Kent Overstreet , Greg Kroah-Hartman , linux-fsdevel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, Wedson Almeida Filho Subject: [RFC PATCH v2 15/30] rust: fs: introduce `inode::Operations::lookup` Date: Tue, 14 May 2024 10:16:56 -0300 Message-Id: <20240514131711.379322-16-wedsonaf@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240514131711.379322-1-wedsonaf@gmail.com> References: <20240514131711.379322-1-wedsonaf@gmail.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 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/dentry.rs | 1 - rust/kernel/fs/inode.rs | 58 ++++++++++++++++++++++++----- samples/rust/rust_rofs.rs | 77 +++++++++++++++++++++++++++++---------- 4 files changed, 105 insertions(+), 32 deletions(-) diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs index bb13bd4a7fa6..15628d2fa3b2 100644 --- a/rust/kernel/error.rs +++ b/rust/kernel/error.rs @@ -129,7 +129,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/dentry.rs b/rust/kernel/fs/dentry.rs index 6a36a48cd28b..c93debb70ea3 100644 --- a/rust/kernel/fs/dentry.rs +++ b/rust/kernel/fs/dentry.rs @@ -43,7 +43,6 @@ impl DEntry { /// /// * `ptr` must be valid for at least the lifetime of the returned reference. /// * `ptr` has the correct file system type, or `T` is [`super::UnspecifiedFS`]. - #[allow(dead_code)] pub(crate) unsafe fn from_raw<'a>(ptr: *mut bindings::dentry) -> &'a Self { // SAFETY: The safety requirements guarantee that the reference is and remains valid. unsafe { &*ptr.cast::() } diff --git a/rust/kernel/fs/inode.rs b/rust/kernel/fs/inode.rs index 3d65b917af0e..c314d036c87e 100644 --- a/rust/kernel/fs/inode.rs +++ b/rust/kernel/fs/inode.rs @@ -6,9 +6,9 @@ //! //! C headers: [`include/linux/fs.h`](srctree/include/linux/fs.h) -use super::{file, sb::SuperBlock, FileSystem, Offset, UnspecifiedFS}; -use crate::error::Result; -use crate::types::{ARef, AlwaysRefCounted, Lockable, Opaque}; +use super::{dentry, dentry::DEntry, file, sb::SuperBlock, FileSystem, Offset, UnspecifiedFS}; +use crate::error::{code::*, Result}; +use crate::types::{ARef, AlwaysRefCounted, Lockable, Locked, Opaque}; use crate::{bindings, block, time::Timespec}; use core::mem::ManuallyDrop; use core::{marker::PhantomData, ptr}; @@ -22,6 +22,14 @@ pub trait Operations { /// File system that these operations are compatible with. type FileSystem: FileSystem + ?Sized; + + /// Returns the inode corresponding to the directory entry with the given name. + fn lookup( + _parent: &Locked<&INode, ReadSem>, + _dentry: dentry::Unhashed<'_, Self::FileSystem>, + ) -> Result>>> { + Err(ENOTSUPP) + } } /// A node (inode) in the file index. @@ -118,12 +126,7 @@ pub fn init(mut self, params: Params) -> Result>> { // SAFETY: This is a new inode, so it's safe to manipulate it mutably. let inode = unsafe { self.0.as_mut() }; let mode = match params.typ { - Type::Dir => { - // 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 - } + Type::Dir => bindings::S_IFDIR, }; inode.i_mode = (params.mode & 0o777) | u16::try_from(mode)?; @@ -148,6 +151,14 @@ pub fn init(mut self, params: Params) -> Result>> { Ok(unsafe { ARef::from_raw(manual.0.cast::>()) }) } + /// Sets the inode operations on this new inode. + pub fn set_iops(&mut self, iops: Ops) -> &mut Self { + // SAFETY: By the type invariants, it's ok to modify the inode. + let inode = unsafe { self.0.as_mut() }; + inode.i_op = iops.0; + self + } + /// Sets the file operations on this new inode. pub fn set_fops(&mut self, fops: file::Ops) -> &mut Self { // SAFETY: By the type invariants, it's ok to modify the inode. @@ -221,7 +232,11 @@ pub const fn new + ?Sized>() -> Self { struct Table(PhantomData); impl Table { const TABLE: bindings::inode_operations = bindings::inode_operations { - lookup: None, + lookup: if T::HAS_LOOKUP { + Some(Self::lookup_callback) + } else { + None + }, get_link: None, permission: None, get_inode_acl: None, @@ -247,6 +262,29 @@ impl Table { fileattr_get: None, get_offset_ctx: None, }; + + extern "C" fn lookup_callback( + parent_ptr: *mut bindings::inode, + dentry_ptr: *mut bindings::dentry, + _flags: u32, + ) -> *mut bindings::dentry { + // SAFETY: The C API guarantees that `parent_ptr` is a valid inode. + let parent = unsafe { INode::from_raw(parent_ptr) }; + + // SAFETY: The C API guarantees that `dentry_ptr` is a valid dentry. + let dentry = unsafe { DEntry::from_raw(dentry_ptr) }; + + // SAFETY: The C API guarantees that the inode's rw semaphore is locked at least in + // read mode. It does not expect callees to unlock it, so we make the locked object + // manually dropped to avoid unlocking it. + let locked = ManuallyDrop::new(unsafe { Locked::new(parent) }); + + match T::lookup(&locked, dentry::Unhashed(dentry)) { + Err(e) => e.to_ptr(), + Ok(None) => ptr::null_mut(), + Ok(Some(ret)) => ManuallyDrop::new(ret).0.get(), + } + } } Self(&Table::::TABLE, PhantomData) } diff --git a/samples/rust/rust_rofs.rs b/samples/rust/rust_rofs.rs index f4be5908369c..2a87e524e0e1 100644 --- a/samples/rust/rust_rofs.rs +++ b/samples/rust/rust_rofs.rs @@ -2,9 +2,11 @@ //! Rust read-only file system sample. -use kernel::fs::{dentry, file, file::File, inode, inode::INode, sb::SuperBlock, Offset}; +use kernel::fs::{ + dentry, dentry::DEntry, file, file::File, inode, inode::INode, sb::SuperBlock, Offset, +}; use kernel::prelude::*; -use kernel::{c_str, fs, time::UNIX_EPOCH, types::Either, types::Locked, user}; +use kernel::{c_str, fs, time::UNIX_EPOCH, types::ARef, types::Either, types::Locked, user}; kernel::module_fs! { type: RoFs, @@ -39,8 +41,36 @@ struct Entry { ]; const DIR_FOPS: file::Ops = file::Ops::new::(); +const DIR_IOPS: inode::Ops = inode::Ops::new::(); struct RoFs; + +impl RoFs { + fn iget(sb: &SuperBlock, e: &'static Entry) -> Result>> { + let mut new = match sb.get_or_create_inode(e.ino)? { + Either::Left(existing) => return Ok(existing), + Either::Right(new) => new, + }; + + match e.etype { + inode::Type::Dir => new.set_iops(DIR_IOPS).set_fops(DIR_FOPS), + }; + + new.init(inode::Params { + typ: e.etype, + mode: 0o555, + size: ENTRIES.len().try_into()?, + blocks: 1, + nlink: 2, + uid: 0, + gid: 0, + atime: UNIX_EPOCH, + ctime: UNIX_EPOCH, + mtime: UNIX_EPOCH, + }) + } +} + impl fs::FileSystem for RoFs { const NAME: &'static CStr = c_str!("rust_rofs"); @@ -50,28 +80,35 @@ fn fill_super(sb: &mut SuperBlock) -> Result { } fn init_root(sb: &SuperBlock) -> Result> { - let inode = match sb.get_or_create_inode(1)? { - Either::Left(existing) => existing, - Either::Right(mut new) => { - new.set_fops(DIR_FOPS); - new.init(inode::Params { - typ: inode::Type::Dir, - mode: 0o555, - size: ENTRIES.len().try_into()?, - blocks: 1, - nlink: 2, - uid: 0, - gid: 0, - atime: UNIX_EPOCH, - ctime: UNIX_EPOCH, - mtime: UNIX_EPOCH, - })? - } - }; + let inode = Self::iget(sb, &ENTRIES[0])?; dentry::Root::try_new(inode) } } +#[vtable] +impl inode::Operations for RoFs { + type FileSystem = Self; + + fn lookup( + parent: &Locked<&INode, inode::ReadSem>, + dentry: dentry::Unhashed<'_, Self>, + ) -> Result>>> { + if parent.ino() != 1 { + return dentry.splice_alias(None); + } + + let name = dentry.name(); + for e in &ENTRIES { + if name == e.name { + let inode = Self::iget(parent.super_block(), e)?; + return dentry.splice_alias(Some(inode)); + } + } + + dentry.splice_alias(None) + } +} + #[vtable] impl file::Operations for RoFs { type FileSystem = Self; From patchwork Tue May 14 13:16:57 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wedson Almeida Filho X-Patchwork-Id: 13664134 Received: from mail-pl1-f171.google.com (mail-pl1-f171.google.com [209.85.214.171]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9E8BE156C40; Tue, 14 May 2024 13:17:55 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.171 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715692677; cv=none; b=gFl7ehrXtxcYA1rVib4ZcihzvbFC5d0ADio7h5c5Q8cN+j/4a4LuclaFsbNJguh5qNs7h46p+oTW89rFNdZjMINr6kXEwjwFnF4gdJlXGoF+Fo3Pjjv828bKx0pmaziJs1aknAXnlfGJHurRzCZVncYAvScxR7ubVqw+Fj5qWU8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715692677; c=relaxed/simple; bh=lGWIXcC2QoW/J24LXyjhj1HCMJ3h8BrwmaxqFP8da5w=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=AATzaE0MIYtzfkRDcS6VwV1O/K2zes4bCZMuAKbDbt22sapyFoZyIrAhpX1Font58p/IDrHo8a64o8nH57ddtsABGywuFqEbdkJms5x2VMGHKk4kX4tfqtvRBeGNKtNpxSV3y5iUTaVJR/Qifjkb7OUoosW9sHU+hNcC8+TlHF0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=G5ULopEQ; arc=none smtp.client-ip=209.85.214.171 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="G5ULopEQ" Received: by mail-pl1-f171.google.com with SMTP id d9443c01a7336-1ee954e0aa6so43653995ad.3; Tue, 14 May 2024 06:17:55 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1715692675; x=1716297475; 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=mK0XIO8J3i30WJQvHbe+iKmgi4AcBMHK0nXgYSbXICI=; b=G5ULopEQgXtDJup4Ogs3LBa9j+Yq9pL4Ve8HRgdbimCclW3/sudi3r83+aVG+6L76V Jc1+y+fj2kK1tJ2eSaJgVfjaaZK+BXmskClkOfyQn8Js6+3ANDCzmx3IhyifE8vBOhF+ rJ+MhG0lqJFqX8/8F3w6MVJrpUmmMGdkSureNe3o9DSkw7OQwxYr8Zc5Qa1BUlTB4JHJ s0xSfqMa+rdMMn32IoFc1R56P0bohtAr/3mRz55SuACxT1lx5C+mRrJVn8GA25YgmjS4 NvRoE4GGeJX6UfcRdToje/NW4rUl6mikFn9++L9UlKC1iMKF5ztqYipD3vdrqmnvRNb2 PL6A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1715692675; x=1716297475; 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=mK0XIO8J3i30WJQvHbe+iKmgi4AcBMHK0nXgYSbXICI=; b=gcmnqShzD9YciV4LlumB8t0TcfqErc6TtplH2+pDf4KdBu39eAHag+oMB5LgAYDfvi JSi61XeQzUWp6GoWN3JUm6+ko7hapkaqq4m4iUBLC8Pd+JR71IwzT+MzGS1J5Km0z4DR wHW0OW032nhMYNyi0gBY1ssp5Yxiy9Kr5EUcdzdmU9HUctnVjR2KOcliYATnlB+MFHtU YDLyssjn3MrYh1j5RanGlDm9awthrsmegCNOYgyd6jTHEZf24/QammCYjt0G3Nrg9Bsg pQeFPJ+xyN39DxATJ07NbtnBJzcgZaebtItH6OEKgkTLb73UZiBTef5N3m/m2NOsK5SE 1T0g== X-Forwarded-Encrypted: i=1; AJvYcCX+Wo9Dd1io9M5ChdqSqYuywEN6NraLbd6VgnGysq1Y/8o+1e+G3R2qM8cxyB+fzv+N/WB0r689C5QOzAEhD7eWWFwXpG2yvBFbMPUbycwW3qjLN7bj8CRCV4Id0fApCryG4WrrF/uywHBlUl+QycLblkWGOOgT2yiOXda9TO1GzTu0rM8iaH+XouWa X-Gm-Message-State: AOJu0YxWGqqzCM6Bvz0B4oGYna0qWPlZES9D2fKLS8KnB+WESJ941uH5 i/eQAH+nvuifgmq6GnYX5DWBFKqfSxkS6hp4oW+OdhDv/Ug2SyHq X-Google-Smtp-Source: AGHT+IHXXUBFZAvUqG+1Lr6XAHwj81MEbFOSTY4ATjGCqVwviodaeSDM7Eu0v5ZOyDqc3yhAMc7Vpw== X-Received: by 2002:a17:902:7d95:b0:1ee:b2ff:c93a with SMTP id d9443c01a7336-1ef43f3e4admr118114945ad.40.1715692674704; Tue, 14 May 2024 06:17:54 -0700 (PDT) Received: from wedsonaf-dev.. ([50.204.89.32]) by smtp.googlemail.com with ESMTPSA id d9443c01a7336-1ef0b9d18a4sm97277335ad.56.2024.05.14.06.17.53 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 14 May 2024 06:17:54 -0700 (PDT) From: Wedson Almeida Filho To: Alexander Viro , Christian Brauner , Matthew Wilcox , Dave Chinner Cc: Kent Overstreet , Greg Kroah-Hartman , linux-fsdevel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, Wedson Almeida Filho Subject: [RFC PATCH v2 16/30] rust: folio: introduce basic support for folios Date: Tue, 14 May 2024 10:16:57 -0300 Message-Id: <20240514131711.379322-17-wedsonaf@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240514131711.379322-1-wedsonaf@gmail.com> References: <20240514131711.379322-1-wedsonaf@gmail.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 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/helpers.c | 94 ++++++++++ rust/kernel/folio.rs | 306 ++++++++++++++++++++++++++++++++ rust/kernel/lib.rs | 1 + 4 files changed, 404 insertions(+) create mode 100644 rust/kernel/folio.rs diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index dabb5a787e0d..fd22b1eafb1d 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -37,3 +38,5 @@ const slab_flags_t RUST_CONST_HELPER_SLAB_ACCOUNT = SLAB_ACCOUNT; const unsigned long RUST_CONST_HELPER_SB_RDONLY = SB_RDONLY; const loff_t RUST_CONST_HELPER_MAX_LFS_FILESIZE = MAX_LFS_FILESIZE; + +const size_t RUST_CONST_HELPER_PAGE_SIZE = PAGE_SIZE; diff --git a/rust/helpers.c b/rust/helpers.c index deb2d21f3096..acff58e6caff 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 @@ -164,6 +168,96 @@ struct file *rust_helper_get_file(struct file *f) } EXPORT_SYMBOL_GPL(rust_helper_get_file); +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 folio *rust_helper_folio_alloc(gfp_t gfp, unsigned int order) +{ + return folio_alloc(gfp, order); +} +EXPORT_SYMBOL_GPL(rust_helper_folio_alloc); + +struct page *rust_helper_folio_page(struct folio *folio, size_t n) +{ + return folio_page(folio, n); +} +EXPORT_SYMBOL_GPL(rust_helper_folio_page); + +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_lock(struct folio *folio) +{ + folio_lock(folio); +} +EXPORT_SYMBOL_GPL(rust_helper_folio_lock); + +bool rust_helper_folio_test_uptodate(struct folio *folio) +{ + return folio_test_uptodate(folio); +} +EXPORT_SYMBOL_GPL(rust_helper_folio_test_uptodate); + +void rust_helper_folio_mark_uptodate(struct folio *folio) +{ + folio_mark_uptodate(folio); +} +EXPORT_SYMBOL_GPL(rust_helper_folio_mark_uptodate); + +bool rust_helper_folio_test_highmem(struct folio *folio) +{ + return folio_test_highmem(folio); +} +EXPORT_SYMBOL_GPL(rust_helper_folio_test_highmem); + +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); diff --git a/rust/kernel/folio.rs b/rust/kernel/folio.rs new file mode 100644 index 000000000000..20f51db920e4 --- /dev/null +++ b/rust/kernel/folio.rs @@ -0,0 +1,306 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Groups of contiguous pages, folios. +//! +//! C headers: [`include/linux/mm.h`](srctree/include/linux/mm.h) + +use crate::error::{code::*, Result}; +use crate::fs::{self, inode::INode, FileSystem}; +use crate::types::{self, ARef, AlwaysRefCounted, Locked, Opaque, ScopeGuard}; +use core::{cmp::min, marker::PhantomData, ops::Deref, ptr}; + +/// The type of a [`Folio`] is unspecified. +pub struct Unspecified; + +/// The [`Folio`] instance is a page-cache one. +pub struct PageCache(PhantomData); + +/// A folio. +/// +/// The `S` type parameter specifies the type of folio. +/// +/// 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, PhantomData); + +// 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.as_ref().0.get()) } + } +} + +impl Folio { + /// Creates a new folio reference from the given raw pointer. + /// + /// # Safety + /// + /// Callers must ensure that: + /// * `ptr` is valid and remains so for the lifetime of the returned reference. + /// * The folio has the right state. + #[allow(dead_code)] + pub(crate) unsafe fn from_raw<'a>(ptr: *mut bindings::folio) -> &'a Self { + // SAFETY: The safety requirements guarantee that the cast below is ok. + unsafe { &*ptr.cast::() } + } + + /// Returns the byte position of this folio in its file. + pub fn pos(&self) -> fs::Offset { + // 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()) } + } + + /// Returns true if the folio is in highmem. + pub fn test_highmem(&self) -> bool { + // SAFETY: The folio is valid because the shared reference implies a non-zero refcount. + unsafe { bindings::folio_test_highmem(self.0.get()) } + } + + /// Returns whether the folio is up to date. + pub fn test_uptodate(&self) -> bool { + // SAFETY: The folio is valid because the shared reference implies a non-zero refcount. + unsafe { bindings::folio_test_uptodate(self.0.get()) } + } + + /// Consumes the folio and returns an owned mapped reference. + /// + /// # Safety + /// + /// Callers must ensure that the folio is not concurrently mapped for write. + pub unsafe fn map_owned(folio: ARef, offset: usize) -> Result> { + // SAFETY: The safety requirements of this function satisfy those of `map`. + let guard = unsafe { folio.map(offset)? }; + let to_unmap = guard.page; + let data = &guard[0] as *const u8; + let data_len = guard.len(); + core::mem::forget(guard); + Ok(Mapped { + _folio: folio, + to_unmap, + data, + data_len, + _p: PhantomData, + }) + } + + /// Maps the contents of a folio page into a slice. + /// + /// # Safety + /// + /// Callers must ensure that the folio is not concurrently mapped for write. + pub unsafe fn map(&self, offset: usize) -> Result> { + if offset >= self.size() { + return Err(EDOM); + } + + let page_index = offset / bindings::PAGE_SIZE; + let page_offset = offset % bindings::PAGE_SIZE; + + // SAFETY: We just checked that the index is within bounds of the folio. + let page = unsafe { bindings::folio_page(self.0.get(), page_index) }; + + // SAFETY: `page` is valid because it was returned by `folio_page` above. + let ptr = unsafe { bindings::kmap(page) }; + + let size = if self.test_highmem() { + bindings::PAGE_SIZE + } else { + self.size() + }; + + // SAFETY: We just mapped `ptr`, so it's valid for read. + let data = unsafe { + core::slice::from_raw_parts(ptr.cast::().add(page_offset), size - page_offset) + }; + Ok(MapGuard { data, page }) + } +} + +impl Folio> { + /// Returns the inode for which this folio holds data. + pub fn inode(&self) -> &INode { + // SAFETY: The type parameter guarantees that this is a page-cache folio, so host is + // populated. + unsafe { + INode::from_raw((*(*self.0.get()).__bindgen_anon_1.__bindgen_anon_1.mapping).host) + } + } +} + +/// An owned mapped folio. +/// +/// That is, a mapped version of a folio that holds a reference to it. +/// +/// The lifetime is used to tie the mapping to other lifetime, for example, the lifetime of a lock +/// guard. This allows the mapping to exist only while a lock is held. +/// +/// # Invariants +/// +/// `to_unmap` is a mapped page of the folio. The byte range starting at `data` and extending for +/// `data_len` bytes is within the mapped page. +pub struct Mapped<'a, S = Unspecified> { + _folio: ARef>, + to_unmap: *mut bindings::page, + data: *const u8, + data_len: usize, + _p: PhantomData<&'a ()>, +} + +impl Mapped<'_, S> { + /// Limits the length of the mapped region. + pub fn cap_len(&mut self, new_len: usize) { + if new_len < self.data_len { + self.data_len = new_len; + } + } +} + +impl Deref for Mapped<'_, S> { + type Target = [u8]; + + fn deref(&self) -> &Self::Target { + // SAFETY: By the type invariant, we know that `data` and `data_len` form a valid slice. + unsafe { core::slice::from_raw_parts(self.data, self.data_len) } + } +} + +impl Drop for Mapped<'_, S> { + fn drop(&mut self) { + // SAFETY: By the type invariant, we know that `to_unmap` is mapped. + unsafe { bindings::kunmap(self.to_unmap) }; + } +} + +/// A mapped [`Folio`]. +pub struct MapGuard<'a> { + data: &'a [u8], + page: *mut bindings::page, +} + +impl 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) }; + } +} + +// SAFETY: `raw_lock` calls folio_lock, which actually locks the folio. +unsafe impl types::Lockable for Folio { + fn raw_lock(&self) { + // SAFETY: The folio is valid because the shared reference implies a non-zero refcount. + unsafe { bindings::folio_lock(self.0.get()) } + } + + unsafe fn unlock(&self) { + // SAFETY: The safety requirements guarantee that the folio is locked. + unsafe { bindings::folio_unlock(self.0.get()) } + } +} + +impl>, S> Locked { + /// 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.deref().0.get()) } + } + + /// Runs `cb` with the mapped folio for `len` bytes starting at `offset`. + /// + /// It may require more than one callback if the folio needs to be mapped one page at a time + /// (for example, when in highmem). + 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; + + if self.test_uptodate() { + return Err(EIO); + } + + // Check that we don't overflow the folio. + let end = offset.checked_add(len).ok_or(EDOM)?; + if end > self.deref().size() { + return Err(EINVAL); + } + + while remaining > 0 { + let map_size = if self.test_highmem() { + bindings::PAGE_SIZE - (next_offset & (bindings::PAGE_SIZE - 1)) + } else { + self.size() - next_offset + }; + let usable = min(remaining, map_size); + // 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.deref().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(()) + }) + } +} diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 81065d1bd679..445599d4bff6 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -30,6 +30,7 @@ pub mod block; mod build_assert; pub mod error; +pub mod folio; pub mod fs; pub mod init; pub mod ioctl; From patchwork Tue May 14 13:16:58 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wedson Almeida Filho X-Patchwork-Id: 13664135 Received: from mail-pl1-f178.google.com (mail-pl1-f178.google.com [209.85.214.178]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3F2C7157A4F; Tue, 14 May 2024 13:17:56 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.178 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715692678; cv=none; b=g1ngXYoWjd7ZhdQW9adUBDyGJDRpMZIthM7qJpanGZnXtu98ZfVeggEBxowTZvQIh+vCZAIdtDUsW3RAMwce06pqX8uHMwma+Vigvn0C/hlevnH9y70Qk/K3FhBgwYCrxRLngB2+DKRJmnkXNMuAx3nlqgIQI6PmhNV4adr0Xzc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715692678; c=relaxed/simple; bh=8SY2/SxS95If8t/1X4ktPCyr7ZVvSxGtAjJMfTRXBa8=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=quRconoO+XUS6fJwYNAfk4XtRWVhz39pNx/DCtTljALBacbFZ7yVbpjWE74i9PNVUr7d5HIUjw0THPc9pNmSbvQ/BdK4ScQKyn459XJgeoCzXgaj5FGa2K4F+T7sLs7MIdpOHnJgqwEwqe2OhJYfGK7LpFFs9gw8ez34jWSd0fM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=HzbChkMt; arc=none smtp.client-ip=209.85.214.178 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="HzbChkMt" Received: by mail-pl1-f178.google.com with SMTP id d9443c01a7336-1ee0132a6f3so41405355ad.0; Tue, 14 May 2024 06:17:56 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1715692675; x=1716297475; 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=uw29FmbYY/ZMdqp5jn6wzGeMh2AecAQ5bSa0KGNtRuc=; b=HzbChkMtUYjC59WF2jE9wCl6NW+XMDj06hEl6AQcznhEjLAMlJ9Qq4O2VBzMvOyRCf qYL58RLffuKUuBra4zYc0It7mKd2eZaWB1BUCPKvGDeKp5mNMNHGx4W4Jwo2PgUslt6e VeUfRv3c6HLiTW+xk3dKis6QfWCgPyGho6jD1EDxb6ioFTSq0htXMIcM0r+9Qe+qFcG4 NHMTPwW/fv6ncxVTTufDmaOHU26yNN9u79STgksS7jFhd3Tefj6XoO2ANFPsNiK97qmg Vfc8PArmjcveCLpn5m3wbLcDIZnbnQ8VD4JFx6k7wr544qIRBDxzjje+T399nBhzv9kn FCCg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1715692675; x=1716297475; 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=uw29FmbYY/ZMdqp5jn6wzGeMh2AecAQ5bSa0KGNtRuc=; b=p2dJBbxMiq3z11KpodkOTXhbTr3ZR5/okYeLGKTsnNogN8K5yF+axynonDDMb3u9C/ hM2tI6Q6pfYYRD1rsSgpz/luqY6Ieramg9zh6xrMlvzvBm7VdyY5XGm3yhWIPFzbSAba Xb+bQBw6Mw0o15LdXiOwQj3EhPGHhgzjW6CIRGN5WDPPyhFfxEuIiZ4VeHIujjXooBhU 7t+hQC6bUL2rpMShiNEg+Eh9gepUQpmFx4o9iqniVGMGxauoRumzJH0yPezKWc1AV+Rb vcj/CYmR/Obm6XbidyqnQlbxk57lxP8DN8Mpc8LQJ6WnJJr/6d6tvpfZ+CEOhCEnbHj2 stHw== X-Forwarded-Encrypted: i=1; AJvYcCVGjcbhWCakZthuiSuCpuNvNOnWGxGmS4yAwqxvyR5T3PCtCD0ZEn3oLyGq9JSLLZYijEN2Vt7I8FWZXTBOAfILqKyzuZ4WIh3ShqeGmXE3dycBlUQPksmQ6mTQPdOK4mhKot0VRqn2EPFzZCme9LADNHHa1+/A2x2KCnXSgSA0F2OLWJKJKfkFXHK1 X-Gm-Message-State: AOJu0YxHr6bEpls8PjJcfCAr5bVc5XXX5xqncbb3driCGjpPAL/kn/sX vOBarqzmeoIIyMx/akZesMjwohIx1ERB99dUW2TAXzAg0cPdHtKh X-Google-Smtp-Source: AGHT+IFZxNMcBaAuKtiyDp/33ySTFIbK5YEZkhbCNH3USgPgh/uhBfrgBSxSOCZl5HRb3sHbNnEdjw== X-Received: by 2002:a17:903:245:b0:1e4:4000:a576 with SMTP id d9443c01a7336-1ef43f4cf7cmr141689345ad.43.1715692675632; Tue, 14 May 2024 06:17:55 -0700 (PDT) Received: from wedsonaf-dev.. ([50.204.89.32]) by smtp.googlemail.com with ESMTPSA id d9443c01a7336-1ef0b9d18a4sm97277335ad.56.2024.05.14.06.17.54 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 14 May 2024 06:17:55 -0700 (PDT) From: Wedson Almeida Filho To: Alexander Viro , Christian Brauner , Matthew Wilcox , Dave Chinner Cc: Kent Overstreet , Greg Kroah-Hartman , linux-fsdevel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, Wedson Almeida Filho Subject: [RFC PATCH v2 17/30] rust: fs: add empty address space operations Date: Tue, 14 May 2024 10:16:58 -0300 Message-Id: <20240514131711.379322-18-wedsonaf@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240514131711.379322-1-wedsonaf@gmail.com> References: <20240514131711.379322-1-wedsonaf@gmail.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Wedson Almeida Filho This is in preparation for allowing modules to implement different address space callbacks, which will be introduced in subsequent patches. Signed-off-by: Wedson Almeida Filho --- rust/kernel/fs.rs | 1 + rust/kernel/fs/address_space.rs | 58 +++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 rust/kernel/fs/address_space.rs diff --git a/rust/kernel/fs.rs b/rust/kernel/fs.rs index 20fb6107eb4b..f1c1972fabcf 100644 --- a/rust/kernel/fs.rs +++ b/rust/kernel/fs.rs @@ -13,6 +13,7 @@ use macros::{pin_data, pinned_drop}; use sb::SuperBlock; +pub mod address_space; pub mod dentry; pub mod file; pub mod inode; diff --git a/rust/kernel/fs/address_space.rs b/rust/kernel/fs/address_space.rs new file mode 100644 index 000000000000..5b4fcb568f46 --- /dev/null +++ b/rust/kernel/fs/address_space.rs @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! File system address spaces. +//! +//! This module allows Rust code implement address space operations. +//! +//! C headers: [`include/linux/fs.h`](srctree/include/linux/fs.h) + +use super::FileSystem; +use crate::bindings; +use core::marker::PhantomData; +use macros::vtable; + +/// Operations implemented by address spaces. +#[vtable] +pub trait Operations { + /// File system that these operations are compatible with. + type FileSystem: FileSystem + ?Sized; +} + +/// Represents address space operations. +#[allow(dead_code)] +pub struct Ops( + pub(crate) *const bindings::address_space_operations, + pub(crate) PhantomData, +); + +impl Ops { + /// Creates the address space operations from a type that implements the [`Operations`] trait. + pub const fn new + ?Sized>() -> Self { + struct Table(PhantomData); + impl Table { + const TABLE: bindings::address_space_operations = bindings::address_space_operations { + writepage: None, + read_folio: None, + 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_folio: None, + swap_activate: None, + swap_deactivate: None, + swap_rw: None, + }; + } + Self(&Table::::TABLE, PhantomData) + } +} From patchwork Tue May 14 13:16:59 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wedson Almeida Filho X-Patchwork-Id: 13664136 Received: from mail-pl1-f180.google.com (mail-pl1-f180.google.com [209.85.214.180]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B4CEC1581F5; Tue, 14 May 2024 13:17:57 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.180 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715692679; cv=none; b=L1xwvxtdAbywUEYnYxLnr00vmMcBXAaB1Pqy6JmC318szP5F8SfK+t6vqvhmky1UUOfrmpX9rIYOs501PGXxLDHfDlUTSgnO6140cyNCaD42QGHzQ88HVuaNyz/pmkXqHmopGn6O0MPDmy/nmxjOKFBS90OSPHoMmzGUPkXjhG0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715692679; c=relaxed/simple; bh=Mhw2NB5w8PjE8bdfQ9exSZQao0oZ/hV9IrJUXzE+2OI=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=ePirj/O2a+rWkyoyCZVqyYulAhxvIwfTmIhG9V0cb651w3sEZfmvPgOCz6EMdB3rjyeHbN39SLW1kFl+rP7BZb4TSyAlzJTlRz+kc5b1lFYdUlkf6A6CaRFBTWUsg2YCF1Swtsx/gTq0zHh+wiM3/SxHTw0RvctYRMllHEzbCHs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=dme5CEh2; arc=none smtp.client-ip=209.85.214.180 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="dme5CEh2" Received: by mail-pl1-f180.google.com with SMTP id d9443c01a7336-1ec69e3dbcfso43597695ad.0; Tue, 14 May 2024 06:17:57 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1715692677; x=1716297477; 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=H8RGtd5B3kfKXLsTm7I/2Z3SzqH3F7mfyT0pCA+knV0=; b=dme5CEh2GhK4dYkNcV3srdrQYtF8VAlNacXsy1rrZBNwQgq2qkOT+Oip6euTsieL9r f/Z38xhCLO+zJoQe1KNahMnugfVzXl4nMlVwt+h6yWLwAZznIM0e9DPkWwcsKxV/awkY HKNGvxq8xvwC/4uQq7bXn+L4ueLNLuO/JnLbgR0Fr8TDKRz25QkyhKieqh90UFWE5uzS acdqpDvVk8zshhT9VcdGCsQFiYWY8E7dnYRr9rbaoyvsWxaSRVmUvHXBVK383zDpFj9o PNys+gzxwHfDemsPby0nayqpTB5SNkKyl6upvJH9XA5JX3Wg4Zrjem/xumZTsUbakhcI ArjA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1715692677; x=1716297477; 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=H8RGtd5B3kfKXLsTm7I/2Z3SzqH3F7mfyT0pCA+knV0=; b=YuWAJeVvKmtUiC1E96aAthgWCb1QmMBZq+TXKjAA3PQCL9NBmxN1Mk5ZnqpMzsuUH6 wjiLwP8JYRQLGee8Aig4cQ/a1tJdGeTKq07SyULPR7NyE0RHn4NNWcVqdqRPbYwnjVB0 T4AO22pslni3OUYDDxwY2GrD+cDQJtLWSXK7QaUIlfhZFoktwO3W2ZADupUnfZy+74ZU OkuiBlNNXy5vNgPjpmPZB7GfIJX9BTysBzluKS4yLAHEzo5BZwi1DUd0Xf8mQc1ZAmhv Gf/Ym3p2+9kRzgl0AWkrhZ7BjlKQfalF1ilP/QVBizGrzTRyzQhJwJGOuN08KgGqTGcv 5btg== X-Forwarded-Encrypted: i=1; AJvYcCURa3xh0ptJ2tZa9E3sXkwQk+6JjDxCtglh3IF4sJ6/efdUukFSOFvcH1aqM2WTOmk3OVMLm8JuosropOMp8SmuEmZuFVSY/Y5gXuObB/KUmRWyyNXylwHjc7C0nKrjMXI9EoCzHXhje3X1FXtnoPhswd2S0yhdmI4IkePtN82FGBQF1Ijgq9FtrZjt X-Gm-Message-State: AOJu0Yx0EiTzmStg982izKdhE4ugOmYJ+yaHeE6bFMpzAM65rlGUlm8R X7a511Mc5cNTsWvSpn2JgNaJoksqbgfkhZwQxF14KWHTdiI/cM0t X-Google-Smtp-Source: AGHT+IEp91zv640NlP+Kp1bf1SwRXohBkiR0OHQL4DImncKJXCDlLG4u8+08GZfL3PzxjMY7j6RU/A== X-Received: by 2002:a17:902:d485:b0:1e4:200e:9c2b with SMTP id d9443c01a7336-1ef43d15794mr172216745ad.21.1715692676651; Tue, 14 May 2024 06:17:56 -0700 (PDT) Received: from wedsonaf-dev.. ([50.204.89.32]) by smtp.googlemail.com with ESMTPSA id d9443c01a7336-1ef0b9d18a4sm97277335ad.56.2024.05.14.06.17.55 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 14 May 2024 06:17:56 -0700 (PDT) From: Wedson Almeida Filho To: Alexander Viro , Christian Brauner , Matthew Wilcox , Dave Chinner Cc: Kent Overstreet , Greg Kroah-Hartman , linux-fsdevel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, Wedson Almeida Filho Subject: [RFC PATCH v2 18/30] rust: fs: introduce `address_space::Operations::read_folio` Date: Tue, 14 May 2024 10:16:59 -0300 Message-Id: <20240514131711.379322-19-wedsonaf@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240514131711.379322-1-wedsonaf@gmail.com> References: <20240514131711.379322-1-wedsonaf@gmail.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 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/helpers.c | 6 +++ rust/kernel/folio.rs | 1 - rust/kernel/fs/address_space.rs | 40 ++++++++++++++++++-- rust/kernel/fs/file.rs | 7 ++++ rust/kernel/fs/inode.rs | 20 +++++++++- samples/rust/rust_rofs.rs | 67 ++++++++++++++++++++++++++------- 6 files changed, 122 insertions(+), 19 deletions(-) diff --git a/rust/helpers.c b/rust/helpers.c index acff58e6caff..2db5df578df2 100644 --- a/rust/helpers.c +++ b/rust/helpers.c @@ -282,6 +282,12 @@ loff_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); + unsigned long rust_helper_copy_to_user(void __user *to, const void *from, unsigned long n) { diff --git a/rust/kernel/folio.rs b/rust/kernel/folio.rs index 20f51db920e4..077328b733e4 100644 --- a/rust/kernel/folio.rs +++ b/rust/kernel/folio.rs @@ -49,7 +49,6 @@ impl Folio { /// Callers must ensure that: /// * `ptr` is valid and remains so for the lifetime of the returned reference. /// * The folio has the right state. - #[allow(dead_code)] pub(crate) unsafe fn from_raw<'a>(ptr: *mut bindings::folio) -> &'a Self { // SAFETY: The safety requirements guarantee that the cast below is ok. unsafe { &*ptr.cast::() } diff --git a/rust/kernel/fs/address_space.rs b/rust/kernel/fs/address_space.rs index 5b4fcb568f46..e539d690235b 100644 --- a/rust/kernel/fs/address_space.rs +++ b/rust/kernel/fs/address_space.rs @@ -6,8 +6,9 @@ //! //! C headers: [`include/linux/fs.h`](srctree/include/linux/fs.h) -use super::FileSystem; -use crate::bindings; +use super::{file::File, FileSystem}; +use crate::error::{from_result, Result}; +use crate::{bindings, folio::Folio, folio::PageCache, types::Locked}; use core::marker::PhantomData; use macros::vtable; @@ -16,10 +17,15 @@ pub trait Operations { /// File system that these operations are compatible with. type FileSystem: FileSystem + ?Sized; + + /// Reads the contents of the inode into the given folio. + fn read_folio( + file: Option<&File>, + folio: Locked<&Folio>>, + ) -> Result; } /// Represents address space operations. -#[allow(dead_code)] pub struct Ops( pub(crate) *const bindings::address_space_operations, pub(crate) PhantomData, @@ -32,7 +38,11 @@ pub const fn new + ?Sized>() -> Self { impl Table { const TABLE: bindings::address_space_operations = bindings::address_space_operations { writepage: None, - read_folio: None, + read_folio: if T::HAS_READ_FOLIO { + Some(Self::read_folio_callback) + } else { + None + }, writepages: None, dirty_folio: None, readahead: None, @@ -52,6 +62,28 @@ impl Table { swap_deactivate: None, swap_rw: None, }; + + extern "C" fn read_folio_callback( + file_ptr: *mut bindings::file, + folio_ptr: *mut bindings::folio, + ) -> i32 { + from_result(|| { + let file = if file_ptr.is_null() { + None + } else { + // SAFETY: The C API guarantees that `file_ptr` is a valid file if non-null. + Some(unsafe { File::from_raw(file_ptr) }) + }; + + // SAFETY: The C API guarantees that `folio_ptr` is a valid folio. + let folio = unsafe { Folio::from_raw(folio_ptr) }; + + // SAFETY: The C contract guarantees that the folio is valid and locked, with + // ownership of the lock transferred to the callee (this function). + T::read_folio(file, unsafe { Locked::new(folio) })?; + Ok(0) + }) + } } Self(&Table::::TABLE, PhantomData) } diff --git a/rust/kernel/fs/file.rs b/rust/kernel/fs/file.rs index 2ba456a1eee1..0828676eae1c 100644 --- a/rust/kernel/fs/file.rs +++ b/rust/kernel/fs/file.rs @@ -355,6 +355,12 @@ fn read_dir( pub struct Ops(pub(crate) *const bindings::file_operations, PhantomData); impl Ops { + /// Returns file operations for page-cache-based ro files. + pub fn generic_ro_file() -> Self { + // SAFETY: This is a constant in C, it never changes. + Self(unsafe { &bindings::generic_ro_fops }, PhantomData) + } + /// Creates file operations from a type that implements the [`Operations`] trait. pub const fn new + ?Sized>() -> Self { struct Table(PhantomData); @@ -516,6 +522,7 @@ impl From for DirEntryType { fn from(value: inode::Type) -> Self { match value { inode::Type::Dir => DirEntryType::Dir, + inode::Type::Reg => DirEntryType::Reg, } } } diff --git a/rust/kernel/fs/inode.rs b/rust/kernel/fs/inode.rs index c314d036c87e..1a41c824d30d 100644 --- a/rust/kernel/fs/inode.rs +++ b/rust/kernel/fs/inode.rs @@ -6,7 +6,9 @@ //! //! C headers: [`include/linux/fs.h`](srctree/include/linux/fs.h) -use super::{dentry, dentry::DEntry, file, sb::SuperBlock, FileSystem, Offset, UnspecifiedFS}; +use super::{ + address_space, dentry, dentry::DEntry, file, sb::SuperBlock, FileSystem, Offset, UnspecifiedFS, +}; use crate::error::{code::*, Result}; use crate::types::{ARef, AlwaysRefCounted, Lockable, Locked, Opaque}; use crate::{bindings, block, time::Timespec}; @@ -127,6 +129,11 @@ pub fn init(mut self, params: Params) -> Result>> { let inode = unsafe { self.0.as_mut() }; let mode = match params.typ { Type::Dir => bindings::S_IFDIR, + Type::Reg => { + // 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)?; @@ -166,6 +173,14 @@ pub fn set_fops(&mut self, fops: file::Ops) -> &mut Self { inode.__bindgen_anon_3.i_fop = fops.0; self } + + /// Sets the address space operations on this new inode. + pub fn set_aops(&mut self, aops: address_space::Ops) -> &mut Self { + // SAFETY: By the type invariants, it's ok to modify the inode. + let inode = unsafe { self.0.as_mut() }; + inode.i_data.a_ops = aops.0; + self + } } impl Drop for New { @@ -181,6 +196,9 @@ fn drop(&mut self) { pub enum Type { /// Directory type. Dir, + + /// Regular file type. + Reg, } /// Required inode parameters. diff --git a/samples/rust/rust_rofs.rs b/samples/rust/rust_rofs.rs index 2a87e524e0e1..8005fd14b2e1 100644 --- a/samples/rust/rust_rofs.rs +++ b/samples/rust/rust_rofs.rs @@ -3,10 +3,11 @@ //! Rust read-only file system sample. use kernel::fs::{ - dentry, dentry::DEntry, file, file::File, inode, inode::INode, sb::SuperBlock, Offset, + address_space, dentry, dentry::DEntry, file, file::File, inode, inode::INode, sb, Offset, }; use kernel::prelude::*; -use kernel::{c_str, fs, time::UNIX_EPOCH, types::ARef, types::Either, types::Locked, user}; +use kernel::types::{ARef, Either, Locked}; +use kernel::{c_str, folio::Folio, folio::PageCache, fs, time::UNIX_EPOCH, user}; kernel::module_fs! { type: RoFs, @@ -20,6 +21,7 @@ struct Entry { name: &'static [u8], ino: u64, etype: inode::Type, + contents: &'static [u8], } const ENTRIES: [Entry; 3] = [ @@ -27,41 +29,53 @@ struct Entry { name: b".", ino: 1, etype: inode::Type::Dir, + contents: b"", }, Entry { name: b"..", ino: 1, etype: inode::Type::Dir, + contents: b"", }, Entry { - name: b"subdir", + name: b"test.txt", ino: 2, - etype: inode::Type::Dir, + etype: inode::Type::Reg, + contents: b"hello world\n", }, ]; const DIR_FOPS: file::Ops = file::Ops::new::(); const DIR_IOPS: inode::Ops = inode::Ops::new::(); +const FILE_AOPS: address_space::Ops = address_space::Ops::new::(); struct RoFs; impl RoFs { - fn iget(sb: &SuperBlock, e: &'static Entry) -> Result>> { + fn iget(sb: &sb::SuperBlock, e: &'static Entry) -> Result>> { let mut new = match sb.get_or_create_inode(e.ino)? { Either::Left(existing) => return Ok(existing), Either::Right(new) => new, }; - match e.etype { - inode::Type::Dir => new.set_iops(DIR_IOPS).set_fops(DIR_FOPS), + let (mode, nlink, size) = match e.etype { + inode::Type::Dir => { + new.set_iops(DIR_IOPS).set_fops(DIR_FOPS); + (0o555, 2, ENTRIES.len().try_into()?) + } + inode::Type::Reg => { + new.set_fops(file::Ops::generic_ro_file()) + .set_aops(FILE_AOPS); + (0o444, 1, e.contents.len().try_into()?) + } }; new.init(inode::Params { typ: e.etype, - mode: 0o555, - size: ENTRIES.len().try_into()?, - blocks: 1, - nlink: 2, + mode, + size, + blocks: (u64::try_from(size)? + 511) / 512, + nlink, uid: 0, gid: 0, atime: UNIX_EPOCH, @@ -74,12 +88,12 @@ fn iget(sb: &SuperBlock, e: &'static Entry) -> Result>> { impl fs::FileSystem for RoFs { const NAME: &'static CStr = c_str!("rust_rofs"); - fn fill_super(sb: &mut SuperBlock) -> Result { + fn fill_super(sb: &mut sb::SuperBlock) -> Result { sb.set_magic(0x52555354); Ok(()) } - fn init_root(sb: &SuperBlock) -> Result> { + fn init_root(sb: &sb::SuperBlock) -> Result> { let inode = Self::iget(sb, &ENTRIES[0])?; dentry::Root::try_new(inode) } @@ -109,6 +123,33 @@ fn lookup( } } +#[vtable] +impl address_space::Operations for RoFs { + type FileSystem = Self; + + fn read_folio(_: Option<&File>, mut folio: Locked<&Folio>>) -> Result { + let data = match folio.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(()) + } +} + #[vtable] impl file::Operations for RoFs { type FileSystem = Self; From patchwork Tue May 14 13:17:00 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wedson Almeida Filho X-Patchwork-Id: 13664137 Received: from mail-pl1-f176.google.com (mail-pl1-f176.google.com [209.85.214.176]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 87EAA1586CF; Tue, 14 May 2024 13:17:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.176 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715692680; cv=none; b=Q6bOJCWTZ6t4Co6jLYtvcgfJKmfkPuDVns4T3UUSduXEScUF8p+v4mXeWyQzZ0vIKDZuSX5P5VqBppxCx9ynfq8mVqkAiNEjFcRlplzaR826ly+VuWqiZwuseCskmIW4NNvZ/hRhSxm4MLjpAumC2QOVYwnDxLUSNuZX2TtkMYQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715692680; c=relaxed/simple; bh=P1Y29bq9+LcLbOgLeKOnPMmkPqRmbdzZHebQ4PhfnTE=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=ZzpDLf2g03ZXwO5aFmtO+guGXvLP5PdMYZFv5S5/0dWYnx1Amzdo3xxmAva9wgNkkqKZmMhMvQpxJC051iyzLLCD/J6l53SZgMcc0Ybpuhv4XQiNIThwGVDTZQnY2QNE+U3FFZLTYCeCo0dM9IySBOhB9/YnT9DAh6L0k1PBQQM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=Fks7Bld2; arc=none smtp.client-ip=209.85.214.176 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="Fks7Bld2" Received: by mail-pl1-f176.google.com with SMTP id d9443c01a7336-1e4c4fb6af3so32209515ad.0; Tue, 14 May 2024 06:17:58 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1715692678; x=1716297478; 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=qpGDp9fRLrURPLEmIGvXCPdUWcy0HBXB6rrBzMfqs+Y=; b=Fks7Bld2S0IPey/E78AtRzrFu9Wse+oU0Y9MiDj53tHw+e7YnzNpaKdTnDNlnM7Ks+ ZWs0arnuSFw3JiwKlzlp3Q9mgPsrotY1+wV3AMbHJLoDkUfvD6HaHTeOfupiB6Ko0mQ7 bkSGL+FKJe6gDOWNbYXoFewj3e2og2cCF+qfPdjxkshVSAGhq+v5MG+GdMf/0UDyaq1e Igx3SXlqCy2Bpboo0S6LUWefOzRnzD83fq5lu/F3R3Yw5E0c3931ooz599kAAJldmXmf sdt09e2AwZLXyVJi45Se6UA5C/LV9r0+AfS4SBsRSX2H3BRbMjGJYFQ1B93nQQtkxisv t06Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1715692678; x=1716297478; 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=qpGDp9fRLrURPLEmIGvXCPdUWcy0HBXB6rrBzMfqs+Y=; b=O6WOA4cwjDbdaLfXZFgOts5RhkR1p7HwesuSdsynsKDzsA55iBXNwBRUPQ7lNq6LOj jXEQCW3hrw+v1PrXQNE66aHadtFX+U/Oc5mwNF/edjy3u3/WJM29ReCAL5z5b5BFHfMg HoWtbO84iZ+UvLMo3QojHtOBIit+lIFmtmyBngfGKRF0vD8jhp71t6lFTGPVBFKmQghA dIkUBil5RTmTjD4CqZL79cJZnHCs1GmT2hpgKdqXrrkiHXMH/TdEdop+aprhswvftrKy 4n2uObxZWZ4Pi67Rbn5dLk6eq4e3u2likNRHhmgUNvJYoKuEK2RvufiBLk7FowwiLnXV JrPQ== X-Forwarded-Encrypted: i=1; AJvYcCXPWBvdelvdbdPfzwbEASdLUF6xUttEFIM2fZvm3VYKxrzYJtIHgukiEO6W8x/+ln/zFnMLoAF1YN79Xgzc30/gBMbkXSJKsytGhHfTWvgLRZJiaifkJAldNaFsnfXJLhxhy/g15uG4pWaLBOQEyFsVCtw7sDlmz1h1ZqFCUpntb1V1Y+Ggtan3H6zB X-Gm-Message-State: AOJu0YwJkmIdBGbSX/3EgSW6aw9fAtAgxlGGp4aGZuDt0lWG9n2w+kQj nvuPQawqFM7E+TfJ+ePH+Vy6F8tO9KyRwoRQAKhesvMgYxCBdcFq X-Google-Smtp-Source: AGHT+IFCD96BUC/8Um46B6V+HDUgD4CYxm17GE5CLu/u4RfVJITffTDQhxIgjjSJsyR3QuP6jH1BuA== X-Received: by 2002:a17:902:ec83:b0:1e4:1fb8:321f with SMTP id d9443c01a7336-1ef42f7561dmr197098045ad.20.1715692677864; Tue, 14 May 2024 06:17:57 -0700 (PDT) Received: from wedsonaf-dev.. ([50.204.89.32]) by smtp.googlemail.com with ESMTPSA id d9443c01a7336-1ef0b9d18a4sm97277335ad.56.2024.05.14.06.17.56 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 14 May 2024 06:17:57 -0700 (PDT) From: Wedson Almeida Filho To: Alexander Viro , Christian Brauner , Matthew Wilcox , Dave Chinner Cc: Kent Overstreet , Greg Kroah-Hartman , linux-fsdevel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, Wedson Almeida Filho Subject: [RFC PATCH v2 19/30] rust: fs: introduce `FileSystem::read_xattr` Date: Tue, 14 May 2024 10:17:00 -0300 Message-Id: <20240514131711.379322-20-wedsonaf@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240514131711.379322-1-wedsonaf@gmail.com> References: <20240514131711.379322-1-wedsonaf@gmail.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 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 | 59 +++++++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+) diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index fd22b1eafb1d..2133f95e8be5 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -22,6 +22,7 @@ #include #include #include +#include /* `bindgen` gets confused at certain things. */ const size_t RUST_CONST_HELPER_ARCH_SLAB_MINALIGN = ARCH_SLAB_MINALIGN; diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs index 15628d2fa3b2..f40a2bdf28d4 100644 --- a/rust/kernel/error.rs +++ b/rust/kernel/error.rs @@ -77,6 +77,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."); declare_err!(ESTALE, "Stale file handle."); declare_err!(EUCLEAN, "Structure needs cleaning."); } diff --git a/rust/kernel/fs.rs b/rust/kernel/fs.rs index f1c1972fabcf..5b8f9c346767 100644 --- a/rust/kernel/fs.rs +++ b/rust/kernel/fs.rs @@ -10,6 +10,8 @@ use crate::types::Opaque; use crate::{bindings, init::PinInit, str::CStr, try_pin_init, ThisModule}; use core::{ffi, marker::PhantomData, mem::ManuallyDrop, pin::Pin, ptr}; +use dentry::DEntry; +use inode::INode; use macros::{pin_data, pinned_drop}; use sb::SuperBlock; @@ -46,6 +48,19 @@ pub trait FileSystem { /// This is called during initialisation of a superblock after [`FileSystem::fill_super`] has /// completed successfully. fn init_root(sb: &SuperBlock) -> 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( + _dentry: &DEntry, + _inode: &INode, + _name: &CStr, + _outbuf: &mut [u8], + ) -> Result { + Err(EOPNOTSUPP) + } } /// A file system that is unspecified. @@ -162,6 +177,7 @@ impl Tables { // derived, is valid for write. let sb = unsafe { &mut *new_sb.0.get() }; sb.s_op = &Tables::::SUPER_BLOCK; + sb.s_xattr = &Tables::::XATTR_HANDLERS[0]; sb.s_flags |= bindings::SB_RDONLY; T::fill_super(new_sb)?; @@ -214,6 +230,49 @@ impl Tables { free_cached_objects: None, 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_ptr: *mut bindings::dentry, + inode_ptr: *mut bindings::inode, + name: *const ffi::c_char, + buffer: *mut ffi::c_void, + size: usize, + ) -> ffi::c_int { + from_result(|| { + // SAFETY: The C API guarantees that `inode_ptr` is a valid dentry. + let dentry = unsafe { DEntry::from_raw(dentry_ptr) }; + + // SAFETY: The C API guarantees that `inode_ptr` is a valid inode. + let inode = unsafe { INode::from_raw(inode_ptr) }; + + // 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) }; + + let (buf_ptr, size) = if buffer.is_null() { + (ptr::NonNull::dangling().as_ptr(), 0) + } else { + (buffer.cast::(), size) + }; + + // SAFETY: The C API guarantees that `buffer` is at least `size` bytes in length. + let buf = unsafe { core::slice::from_raw_parts_mut(buf_ptr, size) }; + let len = T::read_xattr(dentry, inode, name, buf)?; + Ok(len.try_into()?) + }) + } } /// Kernel module that exposes a single file system implemented by `T`. From patchwork Tue May 14 13:17:01 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wedson Almeida Filho X-Patchwork-Id: 13664138 Received: from mail-pl1-f182.google.com (mail-pl1-f182.google.com [209.85.214.182]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C0BA615887F; Tue, 14 May 2024 13:17:59 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.182 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715692681; cv=none; b=NbqPZdoHQW5dxfXnKp7qCkt98Sq1HrwvbPDe03k4/Y7anyTDHtZFwMsPSAKtDBmO7VYQeCGhSy+eSL1mRi6Apmw1hFoL95UyRb6QbvUca5ho70AxSYx/uXkGpbTxi+dZeuLMszXZ08xzaT8/x96FTJIUrpr3w3l/as95zMX4F+Q= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715692681; c=relaxed/simple; bh=sbOCEX61ofppF4aNBQ7FMDtOYlb/Aj76VyesYf1uaYk=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=RFQukpUi91lV19mkvrV7TicxAj3pT3gTM2qGa/j8sIjg4krldGd9Ne1xfBebAiQKFOAYhdh7QqEi7WOAOvpc3BkMvyvkh82uHzMkySjDGhUyw8zJrxFOiiEiAIiNxfGcbYnFdzyoy7hxCiGN/r+1d3czolklAGFeM0cobq8eiyA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=KoqW3Kt4; arc=none smtp.client-ip=209.85.214.182 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="KoqW3Kt4" Received: by mail-pl1-f182.google.com with SMTP id d9443c01a7336-1f082d92864so8597835ad.1; Tue, 14 May 2024 06:17:59 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1715692679; x=1716297479; 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=RlWuS83qckqBozbqSeNOt3eZOxNv3j8cNRYJL66TP/4=; b=KoqW3Kt4/Vfb5nCBWZLWZZOsPgjZoFn6MPaa/oYEzbFJqo48lkaUfhtl631QH/Tr4p aASCCW7qS3/GtlIkF5irktWDm/BKdE4t4aeVkxk31SN9ROV9wkAb3u+r2zSF6ACLggAf LDT4VB0/Bjt8v+Jr6kGdkFtkg0rrjtmea2g82WZmOias9UoiW8WiFkjxS7Nz+7zw5P2L DOKitWbXISOyC1HzuFWztRsmDeJjoT2XUGFlukgnrTIWLb+9IsdWwJhl/cL/EZygPpcp 29alaBg0ynXyS60KPPX4AULdlvWRV5GLXFs3AoJiNeZAi1W4xtPzK5CbkLzgwfkmMPBO USUA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1715692679; x=1716297479; 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=RlWuS83qckqBozbqSeNOt3eZOxNv3j8cNRYJL66TP/4=; b=P7wbyxnAKAw6st4uG7bbuQjRuknRt0ANurGUC/TIQC9uHM4ZVEh+2ocqIvZK4gq8gm ogmDQRSnUUZ5MOffwrdzy6202Yvbie5hEXjEEgNSwwvqIeSBVs8wWg6aRy1UFtewmc9I bq4BMpaiT20kDexq+9SkdjNJ7sjhKMOHMeSap/gEFS0XovZsFipafBjk2bT6QEHwyAHP ivNzn3jPVma1tXkZ68PQSbpUDA1kyA9vFBZj+X3uIhcCgYz27YizShRoeTcuQhWPDyEW SOfHhEj3qYmQ/smgKCa5pv5UCZA9/Fl/4DrRjkjXWNUMsgSoEOjdWGKsqk7fOcxBpvMl k9xg== X-Forwarded-Encrypted: i=1; AJvYcCV7BRCiei0PUToYDaoFkfFtD2tsQgqHvi8cBrxouwhva97x+at4DEHLc9421cRRVkZVmJzdL7qat0byCQ3IgLurR01YR6Y9lzlirDhA9pQ/xGqyc14q36HBtnD/UVnPNJ9BjYtdKjEu6TynriQSz+OEz+vRrZmqTdnuTnL7sPwWwkUCbVTAuk5NeIRQ X-Gm-Message-State: AOJu0YxUpMlib3VHKJmzhUWT1Ye+XwyfeZZXGmkb3zbGRoOkL2wMpUQU cklJSxu8mgnXLyTVm7jKYUVvLmEbVzkiZBnAJBL7x+5lrQojE2ml X-Google-Smtp-Source: AGHT+IEauAuZv2HzfNt0arOphxLzv/8JrunpucjwNHMIob3iHZeXW1iAL5fdZ1GlSL+SvlHGAsBncA== X-Received: by 2002:a17:903:234f:b0:1e5:5c8c:67ec with SMTP id d9443c01a7336-1ef43c0f5d1mr148271385ad.5.1715692679049; Tue, 14 May 2024 06:17:59 -0700 (PDT) Received: from wedsonaf-dev.. ([50.204.89.32]) by smtp.googlemail.com with ESMTPSA id d9443c01a7336-1ef0b9d18a4sm97277335ad.56.2024.05.14.06.17.58 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 14 May 2024 06:17:58 -0700 (PDT) From: Wedson Almeida Filho To: Alexander Viro , Christian Brauner , Matthew Wilcox , Dave Chinner Cc: Kent Overstreet , Greg Kroah-Hartman , linux-fsdevel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, Wedson Almeida Filho Subject: [RFC PATCH v2 20/30] rust: fs: introduce `FileSystem::statfs` Date: Tue, 14 May 2024 10:17:01 -0300 Message-Id: <20240514131711.379322-21-wedsonaf@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240514131711.379322-1-wedsonaf@gmail.com> References: <20240514131711.379322-1-wedsonaf@gmail.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 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 | 47 ++++++++++++++++++++++++++++++++- 3 files changed, 48 insertions(+), 1 deletion(-) diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index 2133f95e8be5..f4c7c3951dbe 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs index f40a2bdf28d4..edada157879a 100644 --- a/rust/kernel/error.rs +++ b/rust/kernel/error.rs @@ -79,6 +79,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."); declare_err!(ESTALE, "Stale file handle."); declare_err!(EUCLEAN, "Structure needs cleaning."); } diff --git a/rust/kernel/fs.rs b/rust/kernel/fs.rs index 5b8f9c346767..51de73008857 100644 --- a/rust/kernel/fs.rs +++ b/rust/kernel/fs.rs @@ -61,6 +61,31 @@ fn read_xattr( ) -> Result { Err(EOPNOTSUPP) } + + /// Get filesystem statistics. + fn statfs(_dentry: &DEntry) -> Result { + Err(ENOSYS) + } +} + +/// File system stats. +/// +/// A subset of C's `kstatfs`. +pub struct Stat { + /// Magic number of the file system. + pub magic: usize, + + /// The maximum length of a file name. + pub namelen: isize, + + /// Block size. + pub bsize: isize, + + /// Number of files in the file system. + pub files: u64, + + /// Number of blocks in the file system. + pub blocks: u64, } /// A file system that is unspecified. @@ -213,7 +238,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, @@ -231,6 +256,26 @@ impl Tables { shutdown: None, }; + unsafe extern "C" fn statfs_callback( + dentry_ptr: *mut bindings::dentry, + buf: *mut bindings::kstatfs, + ) -> ffi::c_int { + from_result(|| { + // SAFETY: The C API guarantees that `dentry_ptr` is a valid dentry. + let dentry = unsafe { DEntry::from_raw(dentry_ptr) }; + let s = T::statfs(dentry)?; + + // SAFETY: The C API guarantees that `buf` is valid for read and write. + let buf = unsafe { &mut *buf }; + buf.f_type = s.magic as ffi::c_long; + buf.f_namelen = s.namelen as ffi::c_long; + buf.f_bsize = s.bsize as ffi::c_long; + buf.f_files = s.files; + buf.f_blocks = s.blocks; + 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 Tue May 14 13:17:02 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wedson Almeida Filho X-Patchwork-Id: 13664139 Received: from mail-pl1-f169.google.com (mail-pl1-f169.google.com [209.85.214.169]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C36C7158D71; Tue, 14 May 2024 13:18:00 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.169 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715692682; cv=none; b=cbQG2e9MUBUSRovx38h+l3QinPxhcFFnzrvL8D5moC6UVb0UYVegxKljq+LN0XO4Mmdl05l33aD6IgWD0dqH/Rn3+inbwm4/YNoL6RsK5ydFy3HM8lfFXo6AJzSR/Eo8lLHMIjiRaHnRijHJRapUaSoFHX8bqCZhCSh6fX58hOA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715692682; c=relaxed/simple; bh=RywbUaAT/EYW5MpK30YcRz29IPNhRSI+g/oBxyh8h1E=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=Lwv/Uj0pvYYBABx5Z9Daj8bztWqGdArK/tBwX87QmeKL+hS6GCghEIB8BLa4HM4wfYxKL4JhdgVQfjSu1hTLtTfT40rkSNw9pbsHhLUARaNc38O6r+rUgWKkgiV4xvEvkgN/kAWXrOi31cnvRP48LeBQ00I+ppXNGGGvFR3jjAA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=H5rfFb63; arc=none smtp.client-ip=209.85.214.169 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="H5rfFb63" Received: by mail-pl1-f169.google.com with SMTP id d9443c01a7336-1ed835f3c3cso48641165ad.3; Tue, 14 May 2024 06:18:00 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1715692680; x=1716297480; 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=hLqaimEXO3VdUIpcOo6tJgmfUsLAFcxXUsmbWwkqX78=; b=H5rfFb63y7MKUvxtVJn9BTTv8Irby+l0QEvHU4HksBEsBK+3s4jTRgG/mEbN4tOYjY m1Xz5xP11oCw0DVek+6tmPEilZC41IqLboqA9oNgwh5qk3leFaSDtEXCCY/RXB1b9rce 5FdDrKmbmO/UwrxKqfpia7NPe01qXO2QIYf6k/YKoAsPjoG2V3DVvnnsCF0jZSrZZnWv 7WrTH84E4bbDh8SEy3SBdsKNnhcZ9xDl5sT3WTDBv7wSBuynwalV9Ks2GgPg0LMxJG7O i7YbKm7fgPmJkmmwgm/ySUt8ObvuCkmtH4YNS+XkgpfCcQAwB6jFEkIfXIuXcEwWPUyj Qnjw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1715692680; x=1716297480; 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=hLqaimEXO3VdUIpcOo6tJgmfUsLAFcxXUsmbWwkqX78=; b=k9JWG6IVqxZaztj+rh6sN4RYfW5aJoJN0BkEwRR1VKPjP0dBJ2M+rKFYKVk1PWC8k0 RiLhZAUNbsMpC/Eoqke6d6BAyaBoyEjwfBT7cAP3wU7Kbg6kOwSWijG2A/2S/rcjGKwE /lX1ooWZAUENmiXs91D4Sv38o/GeBEa7yhtyOsQZ0idaFp6eLOc30tDYkCdo7TQ7Gocy AdND1UFs8ZEBwmh8VZtEpw0+5U9vv5DulXCszBhp85PYUxGWlVn5LyZ6h3s+08nR/fWi zPdXq3Eiih8VX3c8Q4ABaQwCC0gACtbkOmnNI/vsYDsrrZXOeZ/Qf6mtEfHktz90VLnS pOkQ== X-Forwarded-Encrypted: i=1; AJvYcCXnEwdDEF11XDZ11KNq+tBU2Q7KSSiblJ7Z0FOYeOX7JE5L5xUfbIZF0Y7Pg2o1t7K52K0oARHVTrMw3/qaVTeqfPahMqCJ9CAWQx3HtH4pHZrmHJb2dVAhOdsiG07JXnj9QtCm2QQTDSu/2xmkK0z9yN35S48wlhsW4jhlUBo5ExupkTDT0KntMzfS X-Gm-Message-State: AOJu0Yy/K/x+Ce7bRs7c4U+r7zpm9yJf+oolOdbLQshBQGetWh1wjpdA h+lt9KdFYQZm+zAWWOI9CAfAUjtYhVyYfQljVfOphkTopgl3YGKW X-Google-Smtp-Source: AGHT+IEqPomU5IXx8ghDZu5aqnLcgRuh9JWmg6bIGejvgyfZYgEwnB3eJghqcnMJ3KiY0LiyUV67FA== X-Received: by 2002:a17:902:a514:b0:1ed:8d7c:d58e with SMTP id d9443c01a7336-1ef43e2836emr134109465ad.29.1715692680017; Tue, 14 May 2024 06:18:00 -0700 (PDT) Received: from wedsonaf-dev.. ([50.204.89.32]) by smtp.googlemail.com with ESMTPSA id d9443c01a7336-1ef0b9d18a4sm97277335ad.56.2024.05.14.06.17.59 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 14 May 2024 06:17:59 -0700 (PDT) From: Wedson Almeida Filho To: Alexander Viro , Christian Brauner , Matthew Wilcox , Dave Chinner Cc: Kent Overstreet , Greg Kroah-Hartman , linux-fsdevel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, Wedson Almeida Filho Subject: [RFC PATCH v2 21/30] rust: fs: introduce more inode types Date: Tue, 14 May 2024 10:17:02 -0300 Message-Id: <20240514131711.379322-22-wedsonaf@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240514131711.379322-1-wedsonaf@gmail.com> References: <20240514131711.379322-1-wedsonaf@gmail.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 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 | 13 ++++ rust/kernel/fs/file.rs | 5 ++ rust/kernel/fs/inode.rs | 131 +++++++++++++++++++++++++++++++++++++- samples/rust/rust_rofs.rs | 43 ++++++++++++- 4 files changed, 187 insertions(+), 5 deletions(-) diff --git a/rust/helpers.c b/rust/helpers.c index 2db5df578df2..360a1d38ac19 100644 --- a/rust/helpers.c +++ b/rust/helpers.c @@ -288,6 +288,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); + unsigned long rust_helper_copy_to_user(void __user *to, const void *from, unsigned long n) { @@ -307,6 +313,13 @@ void rust_helper_inode_unlock_shared(struct inode *inode) } EXPORT_SYMBOL_GPL(rust_helper_inode_unlock_shared); +void rust_helper_set_delayed_call(struct delayed_call *call, + void (*fn)(void *), void *arg) +{ + set_delayed_call(call, fn, arg); +} +EXPORT_SYMBOL_GPL(rust_helper_set_delayed_call); + /* * `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/file.rs b/rust/kernel/fs/file.rs index 0828676eae1c..a819724b75f8 100644 --- a/rust/kernel/fs/file.rs +++ b/rust/kernel/fs/file.rs @@ -521,8 +521,13 @@ pub enum DirEntryType { impl From for DirEntryType { fn from(value: inode::Type) -> Self { match value { + inode::Type::Fifo => DirEntryType::Fifo, + inode::Type::Chr(_, _) => DirEntryType::Chr, inode::Type::Dir => DirEntryType::Dir, + inode::Type::Blk(_, _) => DirEntryType::Blk, inode::Type::Reg => DirEntryType::Reg, + inode::Type::Lnk => DirEntryType::Lnk, + inode::Type::Sock => DirEntryType::Sock, } } } diff --git a/rust/kernel/fs/inode.rs b/rust/kernel/fs/inode.rs index 1a41c824d30d..75b68d697a6e 100644 --- a/rust/kernel/fs/inode.rs +++ b/rust/kernel/fs/inode.rs @@ -10,8 +10,8 @@ address_space, dentry, dentry::DEntry, file, sb::SuperBlock, FileSystem, Offset, UnspecifiedFS, }; use crate::error::{code::*, Result}; -use crate::types::{ARef, AlwaysRefCounted, Lockable, Locked, Opaque}; -use crate::{bindings, block, time::Timespec}; +use crate::types::{ARef, AlwaysRefCounted, Either, ForeignOwnable, Lockable, Locked, Opaque}; +use crate::{bindings, block, str::CStr, str::CString, time::Timespec}; use core::mem::ManuallyDrop; use core::{marker::PhantomData, ptr}; use macros::vtable; @@ -25,6 +25,18 @@ pub trait Operations { /// File system that these operations are compatible with. type FileSystem: FileSystem + ?Sized; + /// Returns the string that represents the name of the file a symbolic link inode points to. + /// + /// When `dentry` is `None`, `get_link` is called with the RCU read-side lock held, so it may + /// not sleep. Implementations must return `Err(ECHILD)` for it to be called again without + /// holding the RCU lock. + fn get_link<'a>( + _dentry: Option<&DEntry>, + _inode: &'a INode, + ) -> Result> { + Err(ENOTSUPP) + } + /// Returns the inode corresponding to the directory entry with the given name. fn lookup( _parent: &Locked<&INode, ReadSem>, @@ -134,6 +146,52 @@ pub fn init(mut self, params: Params) -> Result>> { unsafe { bindings::mapping_set_large_folios(inode.i_mapping) }; bindings::S_IFREG } + Type::Lnk => { + // If we are using `page_get_link`, we need to prevent the use of high mem. + if !inode.i_op.is_null() { + // SAFETY: We just checked that `i_op` is non-null, and we always just set it + // to valid values. + if unsafe { + (*inode.i_op).get_link == bindings::page_symlink_inode_operations.get_link + } { + // SAFETY: `inode` is valid for write as it's a new inode. + unsafe { bindings::inode_nohighmem(inode) }; + } + } + bindings::S_IFLNK + } + Type::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 + } + Type::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 + } + Type::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 + } + Type::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)?; @@ -194,11 +252,26 @@ fn drop(&mut self) { /// The type of an inode. #[derive(Copy, Clone)] pub enum Type { + /// 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. @@ -245,6 +318,15 @@ pub struct Params { pub struct Ops(*const bindings::inode_operations, PhantomData); impl Ops { + /// Returns inode operations for symbolic links that are stored in a single page. + pub fn page_symlink_inode() -> Self { + // SAFETY: This is a constant in C, it never changes. + Self( + unsafe { &bindings::page_symlink_inode_operations }, + PhantomData, + ) + } + /// Creates the inode operations from a type that implements the [`Operations`] trait. pub const fn new + ?Sized>() -> Self { struct Table(PhantomData); @@ -255,7 +337,11 @@ impl Table { } else { None }, - get_link: None, + get_link: if T::HAS_GET_LINK { + Some(Self::get_link_callback) + } else { + None + }, permission: None, get_inode_acl: None, readlink: None, @@ -303,6 +389,45 @@ extern "C" fn lookup_callback( Ok(Some(ret)) => ManuallyDrop::new(ret).0.get(), } } + + extern "C" fn get_link_callback( + dentry_ptr: *mut bindings::dentry, + inode_ptr: *mut bindings::inode, + delayed_call: *mut bindings::delayed_call, + ) -> *const core::ffi::c_char { + extern "C" fn drop_cstring(ptr: *mut core::ffi::c_void) { + // SAFETY: The argument came from a previous call to `into_foreign` below. + unsafe { CString::from_foreign(ptr) }; + } + + let dentry = if dentry_ptr.is_null() { + None + } else { + // SAFETY: The C API guarantees that `dentry_ptr` is a valid dentry when it's + // non-null. + Some(unsafe { DEntry::from_raw(dentry_ptr) }) + }; + + // SAFETY: The C API guarantees that `parent_ptr` is a valid inode. + let inode = unsafe { INode::from_raw(inode_ptr) }; + + match T::get_link(dentry, inode) { + Err(e) => e.to_ptr::(), + Ok(Either::Right(str)) => str.as_char_ptr(), + Ok(Either::Left(str)) => { + let ptr = str.into_foreign(); + unsafe { + bindings::set_delayed_call( + delayed_call, + Some(drop_cstring), + ptr.cast_mut(), + ) + }; + + ptr.cast::() + } + } + } } Self(&Table::::TABLE, PhantomData) } diff --git a/samples/rust/rust_rofs.rs b/samples/rust/rust_rofs.rs index 8005fd14b2e1..7a09e2db878d 100644 --- a/samples/rust/rust_rofs.rs +++ b/samples/rust/rust_rofs.rs @@ -7,7 +7,7 @@ }; use kernel::prelude::*; use kernel::types::{ARef, Either, Locked}; -use kernel::{c_str, folio::Folio, folio::PageCache, fs, time::UNIX_EPOCH, user}; +use kernel::{c_str, folio::Folio, folio::PageCache, fs, str::CString, time::UNIX_EPOCH, user}; kernel::module_fs! { type: RoFs, @@ -24,7 +24,7 @@ struct Entry { contents: &'static [u8], } -const ENTRIES: [Entry; 3] = [ +const ENTRIES: [Entry; 4] = [ Entry { name: b".", ino: 1, @@ -43,11 +43,18 @@ struct Entry { etype: inode::Type::Reg, contents: b"hello world\n", }, + Entry { + name: b"link.txt", + ino: 3, + etype: inode::Type::Lnk, + contents: b"./test.txt", + }, ]; const DIR_FOPS: file::Ops = file::Ops::new::(); const DIR_IOPS: inode::Ops = inode::Ops::new::(); const FILE_AOPS: address_space::Ops = address_space::Ops::new::(); +const LNK_IOPS: inode::Ops = inode::Ops::new::(); struct RoFs; @@ -68,6 +75,11 @@ fn iget(sb: &sb::SuperBlock, e: &'static Entry) -> Result .set_aops(FILE_AOPS); (0o444, 1, e.contents.len().try_into()?) } + inode::Type::Lnk => { + new.set_iops(LNK_IOPS); + (0o444, 1, e.contents.len().try_into()?) + } + _ => return Err(ENOENT), }; new.init(inode::Params { @@ -123,6 +135,33 @@ fn lookup( } } +struct Link; +#[vtable] +impl inode::Operations for Link { + type FileSystem = RoFs; + + fn get_link<'a>( + dentry: Option<&DEntry>, + inode: &'a INode, + ) -> Result> { + if dentry.is_none() { + return Err(ECHILD); + } + + let name_buf = match inode.ino() { + 3 => ENTRIES[3].contents, + _ => return Err(EINVAL), + }; + let mut name = Box::new_slice( + name_buf.len().checked_add(1).ok_or(ENOMEM)?, + b'\0', + GFP_NOFS, + )?; + name[..name_buf.len()].copy_from_slice(name_buf); + Ok(Either::Left(name.try_into()?)) + } +} + #[vtable] impl address_space::Operations for RoFs { type FileSystem = Self; From patchwork Tue May 14 13:17:03 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wedson Almeida Filho X-Patchwork-Id: 13664140 Received: from mail-pl1-f178.google.com (mail-pl1-f178.google.com [209.85.214.178]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B5814158DC6; Tue, 14 May 2024 13:18:01 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.178 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715692683; cv=none; b=bRhkLNLHVK5EgXdFqLCIF1ElTgh+leRJc0mb0elR4ehG4kEnSllJqaDCxlJEtVR99He0yrKQaCkp3KM4hT7BAWDv1Vt9+85zZxwz9VyQQe4DqwBxu6i996pJCU9XYQRW1pMpYC6sKrJHv3gguw/0WVy7B5sa7si3fQ4oWVPu2cw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715692683; c=relaxed/simple; bh=nqHJIwr5EZghdDpCfTkWNNMp7x4S2W1hE11AYrRKpTg=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=svKVPLAHWgdjSuBBP4QmZ7yPjp9U/qrmU13NQ9M2cKvbVOJyvcPhgC3pBhXgej4uFum42wZDURgWen8RBt3d46I15sV2V+Yry6QeKb5h2opVuFFHnH1aekPUp8fQmNjUe4fY1TH8aNTl1iW5PIOVUAPkNx2fFtptkDRJVE/Ki5U= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=VDD7FM2+; arc=none smtp.client-ip=209.85.214.178 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="VDD7FM2+" Received: by mail-pl1-f178.google.com with SMTP id d9443c01a7336-1ec41d82b8bso52518125ad.2; Tue, 14 May 2024 06:18:01 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1715692681; x=1716297481; 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=XQjruP9C1AriCa2NTVu4vY4iAh6hhbFru1ekZHix3gc=; b=VDD7FM2+gjtNrJJFm/DiF5Dlrey3m5D0IXcdOyJnZQa9zMAM0NOGRyq8vQlou20Xk5 NWoEpJtN9UHTpyKa3thjpVr0sG8vxW5KJ+6adUO4JDhi9srT9px0iBlQqWyYhp86suJA OaCdhJTEz8W29IEUsyTPbxVMWCgFTT7tQAgLMoGc/03JLz+bB9Byx/GuWTBnCZZnVVJ9 BuTtS6IrKhc6av7MnE9YBkqnq6adwDu5viZhnWdjh9SzVrCuScB1eU7488c2HQ7kGJof huejEaMnEotswN40uRid8UpqOgnJTElA9yEI3msJPmeMrwMaNrxRNa6foUgWuo/druZO HYow== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1715692681; x=1716297481; 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=XQjruP9C1AriCa2NTVu4vY4iAh6hhbFru1ekZHix3gc=; b=hG1cRtF8uR8CYOnsI6gyhZFnytogGEqRKiZqbbNM/kgr/XMmoh412OAD75QFTjNeGp 5P9NtBasjt0aKj0jM/dADggMxam728cqRVnq/iRlypLq6x4lqyaX+WmpWv+HQoMOD8la /bxB0myqoJLO3aRnohljjjURE04nAIdJZQIFgTqP/IVbgmQBJmCu+rIxcjWHoSDw07vO D3FQV+llogFVCJH7XHziUSVjMJkeN34zy2PfPrsqfPw+DtOrJYOkBC/jptXs/dhoT6Hv tr5YDvgCm2UcdK10yvgIoGd4OsFwyYNvZHIJ5+vh1Z/aTuNHb7pfxjzdDvZV2aW7Zkez zKKA== X-Forwarded-Encrypted: i=1; AJvYcCWbS+c0bmcnufj4Z253ZSozCe1eCBZADO7nkaXoULUePGoz5GHonvT5C5grqjnJVwi+3LF6116HoomQVNO3JOKrjyGJ5JIgsjHAfdHXFOMguQ2yiPKikQKzyZ8GqaxlCZJvMkyKEapuAJMoR5RMDa8+Xl8hZEMeiAc8vuy6wPflsKD6urZojZTNlq5y X-Gm-Message-State: AOJu0YxpDooNfWx9iVw9mkR83yEoABU5AMIXP7z3vgcESBGxQDFkBJeK Yo1hKcOmO9UYOp6ruj+hYfj4SvneUdZcTPL50TsYcEsKTnH7gfku X-Google-Smtp-Source: AGHT+IF0ccCnxDgMImegz6x0hQMqkVTDNZ3JGlK9EGVqqovM/HatpeSxdHFfozw6V8+PvA/W2iWdNw== X-Received: by 2002:a17:902:e94e:b0:1e2:688e:597d with SMTP id d9443c01a7336-1ef43d29892mr188026885ad.21.1715692680921; Tue, 14 May 2024 06:18:00 -0700 (PDT) Received: from wedsonaf-dev.. ([50.204.89.32]) by smtp.googlemail.com with ESMTPSA id d9443c01a7336-1ef0b9d18a4sm97277335ad.56.2024.05.14.06.18.00 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 14 May 2024 06:18:00 -0700 (PDT) From: Wedson Almeida Filho To: Alexander Viro , Christian Brauner , Matthew Wilcox , Dave Chinner Cc: Kent Overstreet , Greg Kroah-Hartman , linux-fsdevel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, Wedson Almeida Filho Subject: [RFC PATCH v2 22/30] rust: fs: add per-superblock data Date: Tue, 14 May 2024 10:17:03 -0300 Message-Id: <20240514131711.379322-23-wedsonaf@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240514131711.379322-1-wedsonaf@gmail.com> References: <20240514131711.379322-1-wedsonaf@gmail.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 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 | 46 ++++++++++++++++++++++++++++--------- rust/kernel/fs/sb.rs | 48 +++++++++++++++++++++++++++++++++++---- samples/rust/rust_rofs.rs | 3 ++- 3 files changed, 81 insertions(+), 16 deletions(-) diff --git a/rust/kernel/fs.rs b/rust/kernel/fs.rs index 51de73008857..387e87e3edaf 100644 --- a/rust/kernel/fs.rs +++ b/rust/kernel/fs.rs @@ -7,7 +7,7 @@ //! C headers: [`include/linux/fs.h`](srctree/include/linux/fs.h) use crate::error::{code::*, from_result, to_result, Error, Result}; -use crate::types::Opaque; +use crate::types::{ForeignOwnable, Opaque}; use crate::{bindings, init::PinInit, str::CStr, try_pin_init, ThisModule}; use core::{ffi, marker::PhantomData, mem::ManuallyDrop, pin::Pin, ptr}; use dentry::DEntry; @@ -31,6 +31,9 @@ /// 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; @@ -40,8 +43,8 @@ pub trait FileSystem { #[doc(hidden)] const IS_UNSPECIFIED: bool = false; - /// Initialises the new superblock. - fn fill_super(sb: &mut SuperBlock) -> Result; + /// Initialises the new superblock and returns the data to attach to it. + fn fill_super(sb: &mut SuperBlock) -> Result; /// Initialises and returns the root inode of the given superblock. /// @@ -94,9 +97,10 @@ pub struct Stat { pub struct UnspecifiedFS; impl FileSystem for UnspecifiedFS { + type Data = (); const NAME: &'static CStr = crate::c_str!("unspecified"); const IS_UNSPECIFIED: bool = true; - fn fill_super(_: &mut SuperBlock) -> Result { + fn fill_super(_: &mut SuperBlock) -> Result { Err(ENOTSUPP) } @@ -134,7 +138,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 @@ -155,10 +159,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) }; + } } } @@ -205,12 +221,19 @@ impl Tables { sb.s_xattr = &Tables::::XATTR_HANDLERS[0]; sb.s_flags |= bindings::SB_RDONLY; - T::fill_super(new_sb)?; + let data = T::fill_super(new_sb)?; + + // N.B.: Even on failure, `kill_sb` is called and frees the data. + sb.s_fs_info = data.into_foreign().cast_mut(); - let root = T::init_root(new_sb)?; + // SAFETY: The callback contract guarantees that `sb_ptr` is a unique pointer to a + // newly-created (and initialised above) superblock. And we have just initialised + // `s_fs_info`. + let sb = unsafe { SuperBlock::from_raw(sb_ptr) }; + let root = T::init_root(sb)?; // Reject root inode if it belongs to a different superblock. - if !ptr::eq(root.super_block(), new_sb) { + if !ptr::eq(root.super_block(), sb) { return Err(EINVAL); } @@ -346,7 +369,7 @@ fn init(module: &'static ThisModule) -> impl PinInit { /// /// ``` /// # mod module_fs_sample { -/// use kernel::fs::{dentry, inode::INode, sb::SuperBlock, self}; +/// use kernel::fs::{dentry, inode::INode, sb, sb::SuperBlock, self}; /// use kernel::prelude::*; /// /// kernel::module_fs! { @@ -359,8 +382,9 @@ fn init(module: &'static ThisModule) -> impl PinInit { /// /// struct MyFs; /// impl fs::FileSystem for MyFs { +/// type Data = (); /// const NAME: &'static CStr = kernel::c_str!("myfs"); -/// fn fill_super(_: &mut SuperBlock) -> Result { +/// fn fill_super(_: &mut SuperBlock) -> Result { /// todo!() /// } /// fn init_root(_sb: &SuperBlock) -> Result> { diff --git a/rust/kernel/fs/sb.rs b/rust/kernel/fs/sb.rs index fa10f3db5593..7c0c52e6da0a 100644 --- a/rust/kernel/fs/sb.rs +++ b/rust/kernel/fs/sb.rs @@ -10,19 +10,37 @@ use super::FileSystem; use crate::bindings; use crate::error::{code::*, Result}; -use crate::types::{ARef, Either, Opaque}; +use crate::types::{ARef, Either, ForeignOwnable, Opaque}; use core::{marker::PhantomData, ptr}; +/// A typestate for [`SuperBlock`] that indicates that it's a new one, so not fully initialized +/// yet. +pub struct New; + +/// A typestate for [`SuperBlock`] that indicates that it's ready to be used. +pub struct Ready; + +// SAFETY: Instances of `SuperBlock` are only created after initialising the data. +unsafe impl DataInited for Ready {} + +/// Indicates that a superblock in this typestate has data initialized. +/// +/// # Safety +/// +/// Implementers must ensure that `s_fs_info` is properly initialised in this state. +#[doc(hidden)] +pub unsafe trait DataInited {} + /// A file system super block. /// /// Wraps the kernel's `struct super_block`. #[repr(transparent)] -pub struct SuperBlock( +pub struct SuperBlock( pub(crate) Opaque, - PhantomData, + PhantomData<(S, T)>, ); -impl SuperBlock { +impl SuperBlock { /// Creates a new superblock reference from the given raw pointer. /// /// # Safety @@ -31,6 +49,7 @@ impl SuperBlock { /// /// * `ptr` is valid and remains so for the lifetime of the returned object. /// * `ptr` has the correct file system type, or `T` is [`super::UnspecifiedFS`]. + /// * `ptr` in the right typestate. pub(crate) unsafe fn from_raw<'a>(ptr: *mut bindings::super_block) -> &'a Self { // SAFETY: The safety requirements guarantee that the cast below is ok. unsafe { &*ptr.cast::() } @@ -44,6 +63,7 @@ pub(crate) unsafe fn from_raw<'a>(ptr: *mut bindings::super_block) -> &'a Self { /// /// * `ptr` is valid and remains so for the lifetime of the returned object. /// * `ptr` has the correct file system type, or `T` is [`super::UnspecifiedFS`]. + /// * `ptr` in the right typestate. /// * `ptr` is the only active pointer to the superblock. pub(crate) unsafe fn from_raw_mut<'a>(ptr: *mut bindings::super_block) -> &'a mut Self { // SAFETY: The safety requirements guarantee that the cast below is ok. @@ -55,7 +75,9 @@ pub fn rdonly(&self) -> bool { // SAFETY: `s_flags` only changes during init, so it is safe to read it. unsafe { (*self.0.get()).s_flags & bindings::SB_RDONLY != 0 } } +} +impl SuperBlock { /// Sets the magic number of the superblock. pub fn set_magic(&mut self, magic: usize) -> &mut Self { // SAFETY: This is a new superblock that is being initialised, so it's ok to write to its @@ -63,8 +85,26 @@ pub fn set_magic(&mut self, magic: usize) -> &mut Self { unsafe { (*self.0.get()).s_magic = magic as core::ffi::c_ulong }; self } +} + +impl SuperBlock { + /// Returns the data associated with the superblock. + pub fn data(&self) -> ::Borrowed<'_> { + if T::IS_UNSPECIFIED { + crate::build_error!("super block data type is unspecified"); + } + + // SAFETY: This method is only available if the typestate implements `DataInited`, whose + // safety requirements include `s_fs_info` being properly initialised. + 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. + /// + /// This method is not callable from a superblock where data isn't inited yet because it would + /// allow one to get access to the uninited data via `inode::New::init()` -> + /// `INode::super_block()` -> `SuperBlock::data()`. pub fn get_or_create_inode(&self, ino: Ino) -> Result>, inode::New>> { // SAFETY: All superblock-related state needed by `iget_locked` is initialised by C code // before calling `fill_super_callback`, or by `fill_super_callback` itself before calling diff --git a/samples/rust/rust_rofs.rs b/samples/rust/rust_rofs.rs index 7a09e2db878d..7027ca067f8f 100644 --- a/samples/rust/rust_rofs.rs +++ b/samples/rust/rust_rofs.rs @@ -98,9 +98,10 @@ fn iget(sb: &sb::SuperBlock, e: &'static Entry) -> Result } impl fs::FileSystem for RoFs { + type Data = (); const NAME: &'static CStr = c_str!("rust_rofs"); - fn fill_super(sb: &mut sb::SuperBlock) -> Result { + fn fill_super(sb: &mut sb::SuperBlock) -> Result { sb.set_magic(0x52555354); Ok(()) } From patchwork Tue May 14 13:17:04 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wedson Almeida Filho X-Patchwork-Id: 13664141 Received: from mail-pl1-f169.google.com (mail-pl1-f169.google.com [209.85.214.169]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DE0D415A49F; Tue, 14 May 2024 13:18:02 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.169 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715692685; cv=none; b=brggDZ8pmidKKJHpWuYdGnxOiyh3JfvC/MWYX7/MPBUUwWfrgF2m8ohDuJlGvD5QGcdiTxNANsDMzXkOPIj80RvEYNQsUIqGT+QBMKN0iodA4dGgm/hmNcLSKsTB0zR88CTZH/NNl5XV8fgajHkxJ3b4rXaZhxvU3MRQ+J3rEik= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715692685; c=relaxed/simple; bh=ZhtDkz8UOLR97RnukFHRGunVhnN/R3NSPvjQ2sWx2GY=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=gluVMDletcJ+XO2zaRpjvlb1gtonYGAL8093WtwI/7znWULRhbes12ZW0Ai//mQuSt9EwuUyDH0vOJbXay3jAb1ErwmWS7BTJvrLsBdgB5/ND2itClSgPR+HIesgABR01GU3ns4o354LX/6fYghZSRWOMApfuP+6YybX3EEsXQ4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=QwqIuUQp; arc=none smtp.client-ip=209.85.214.169 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="QwqIuUQp" Received: by mail-pl1-f169.google.com with SMTP id d9443c01a7336-1eecc71311eso46832195ad.3; Tue, 14 May 2024 06:18:02 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1715692682; x=1716297482; 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=Sp5gsASxdoGRSDEZiV9AJvmVQW1l9r+kB3byBBeTAfE=; b=QwqIuUQpnGU0SDqhEasWd+lmj0W6Bse/Pk7zNmUSBJvSd/oBgSs4GOKSY0jdKV4hAF xqS3el6OC6j21QxKb26RdfqLY8C06a2DdQUd5oYk65htl3P86DJh1iJZIExSMAGpvwp+ UPx5SA5QqjDKzBesDPVDi/fc6M0wiJ4hE/fCTLSEeKlQRVnHmRle3vKVXc7xr3mvB57u 10+bccPHsw8niQDto4FTkMUxyqzqju27jaoBhiDyjGbynKjL1PurCQqV/yjM+K9zECvI BU2pVGZ9fUTDJgzBEsaiD7EuGgqnOiJUn6D8TPYBt63DxkhqZyMPW+yQv7glJN/6a+23 kD4g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1715692682; x=1716297482; 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=Sp5gsASxdoGRSDEZiV9AJvmVQW1l9r+kB3byBBeTAfE=; b=d5Ax0SJRQdtA4MdIE4fqyMsUGqucsXmCckkAybwyeOHujY6IJum8ZJQbJUPNOKFqkG GlXu7TZUJOMpBeIdV49RANtL4KSf3lQHIw5R7F3EUiNDw1Q5lrJOOAK+cXotwhMr8B1V sXTXAOctvWbr8iSymRg2OS3SZxRGkiMrKrUzJCkkQVkXu6R6MmnNXzCVZwoYxi7+Pymx NcUo25c54lkbX6t9ExMtlivabH1jxghWns100beb3IV6cCcJjGMAvTSaOgbvuJKIyYta j878DvL6voHI+TsSC6brODAOWpxHkkNzSEQvc3JOh5HBjEsuhX2x673Fte+G+XDuJOiW //Fg== X-Forwarded-Encrypted: i=1; AJvYcCX00398ULyGkMV25u9ub7MyrXxRA9YboffNCywQARu/nqIQKegMkzcDSVSlZRrbBiUH0zWjkNDL/8n6THVlo4RniCmq4uRv0sK45XPzTpxlZqkiTOqOFVgefF05aRIx0vl6kl2+2rheb5HfQZX07sBRl6h1DSe6L3aT2i8geMoHTGMEdWlylY+WE0ov X-Gm-Message-State: AOJu0YzddBV5xJ6J0+RoSG2zVQY9vaCm55iJMC6SSW3usuhrmkSexuyC 9rxH2WeZKDtU1Z5hsqX4Kqkb3K4uhQTQOw3W0Vwja3tvtEQ+PIo9 X-Google-Smtp-Source: AGHT+IGpWRC30NXviFcJi/80gURNQ5j0Zh8nsXZ8artk0J4li4OHh4Nga5gTbj1YVpehDxLjszrvnQ== X-Received: by 2002:a17:902:8c96:b0:1eb:fc2:1eed with SMTP id d9443c01a7336-1ef43e27b96mr155157355ad.41.1715692682065; Tue, 14 May 2024 06:18:02 -0700 (PDT) Received: from wedsonaf-dev.. ([50.204.89.32]) by smtp.googlemail.com with ESMTPSA id d9443c01a7336-1ef0b9d18a4sm97277335ad.56.2024.05.14.06.18.01 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 14 May 2024 06:18:01 -0700 (PDT) From: Wedson Almeida Filho To: Alexander Viro , Christian Brauner , Matthew Wilcox , Dave Chinner Cc: Kent Overstreet , Greg Kroah-Hartman , linux-fsdevel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, Wedson Almeida Filho Subject: [RFC PATCH v2 23/30] rust: fs: allow file systems backed by a block device Date: Tue, 14 May 2024 10:17:04 -0300 Message-Id: <20240514131711.379322-24-wedsonaf@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240514131711.379322-1-wedsonaf@gmail.com> References: <20240514131711.379322-1-wedsonaf@gmail.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 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/helpers.c | 14 +++ rust/kernel/block.rs | 1 - rust/kernel/fs.rs | 60 ++++++++--- rust/kernel/fs/inode.rs | 221 +++++++++++++++++++++++++++++++++++++- rust/kernel/fs/sb.rs | 49 ++++++++- samples/rust/rust_rofs.rs | 2 +- 6 files changed, 328 insertions(+), 19 deletions(-) diff --git a/rust/helpers.c b/rust/helpers.c index 360a1d38ac19..6c6d18df055f 100644 --- a/rust/helpers.c +++ b/rust/helpers.c @@ -21,6 +21,7 @@ */ #include +#include #include #include #include @@ -258,6 +259,13 @@ void rust_helper_kunmap_local(const void *vaddr) } EXPORT_SYMBOL_GPL(rust_helper_kunmap_local); +struct folio *rust_helper_read_mapping_folio(struct address_space *mapping, + pgoff_t index, struct file *file) +{ + return read_mapping_folio(mapping, index, file); +} +EXPORT_SYMBOL_GPL(rust_helper_read_mapping_folio); + void rust_helper_i_uid_write(struct inode *inode, uid_t uid) { i_uid_write(inode, uid); @@ -294,6 +302,12 @@ unsigned int rust_helper_MKDEV(unsigned int major, unsigned int minor) } EXPORT_SYMBOL_GPL(rust_helper_MKDEV); +sector_t rust_helper_bdev_nr_sectors(struct block_device *bdev) +{ + return bdev_nr_sectors(bdev); +} +EXPORT_SYMBOL_GPL(rust_helper_bdev_nr_sectors); + unsigned long rust_helper_copy_to_user(void __user *to, const void *from, unsigned long n) { diff --git a/rust/kernel/block.rs b/rust/kernel/block.rs index 868623d7c873..4d669bd5dce9 100644 --- a/rust/kernel/block.rs +++ b/rust/kernel/block.rs @@ -31,7 +31,6 @@ impl Device { /// /// Callers must ensure that `ptr` is valid and remains so for the lifetime of the returned /// object. - #[allow(dead_code)] pub(crate) unsafe fn from_raw<'a>(ptr: *mut bindings::block_device) -> &'a Self { // SAFETY: The safety requirements guarantee that the cast below is ok. unsafe { &*ptr.cast::() } diff --git a/rust/kernel/fs.rs b/rust/kernel/fs.rs index 387e87e3edaf..864aca24d12c 100644 --- a/rust/kernel/fs.rs +++ b/rust/kernel/fs.rs @@ -26,6 +26,11 @@ /// This is C's `loff_t`. pub type Offset = i64; +/// An index into the page cache. +/// +/// This is C's `pgoff_t`. +pub type PageOffset = usize; + /// Maximum size of an inode. pub const MAX_LFS_FILESIZE: Offset = bindings::MAX_LFS_FILESIZE; @@ -37,6 +42,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: sb::Type = sb::Type::Independent; + /// Determines if an implementation doesn't specify the required types. /// /// This is meant for internal use only. @@ -44,7 +52,10 @@ pub trait FileSystem { const IS_UNSPECIFIED: bool = false; /// Initialises the new superblock and returns the data to attach to it. - fn fill_super(sb: &mut SuperBlock) -> Result; + fn fill_super( + sb: &mut SuperBlock, + mapper: Option, + ) -> Result; /// Initialises and returns the root inode of the given superblock. /// @@ -100,7 +111,7 @@ impl FileSystem for UnspecifiedFS { type Data = (); const NAME: &'static CStr = crate::c_str!("unspecified"); const IS_UNSPECIFIED: bool = true; - fn fill_super(_: &mut SuperBlock) -> Result { + fn fill_super(_: &mut SuperBlock, _: Option) -> Result { Err(ENOTSUPP) } @@ -139,7 +150,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 sb::Type::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`). @@ -162,9 +175,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 + // `sb::Type::BlockDev`, so `kill_block_super` is the appropriate function to call + // for cleanup. + sb::Type::BlockDev => unsafe { bindings::kill_block_super(sb_ptr) }, + // SAFETY: In `get_tree_callback` we always call `get_tree_nodev` for + // `sb::Type::Independent`, so `kill_anon_super` is the appropriate function to call + // for cleanup. + sb::Type::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 }; @@ -200,9 +220,18 @@ impl Tables { }; unsafe extern "C" fn get_tree_callback(fc: *mut bindings::fs_context) -> 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. + sb::Type::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. + sb::Type::Independent => unsafe { + bindings::get_tree_nodev(fc, Some(Self::fill_super_callback)) + }, + } } unsafe extern "C" fn fill_super_callback( @@ -221,7 +250,14 @@ impl Tables { sb.s_xattr = &Tables::::XATTR_HANDLERS[0]; sb.s_flags |= bindings::SB_RDONLY; - let data = T::fill_super(new_sb)?; + let mapper = if matches!(T::SUPER_TYPE, sb::Type::BlockDev) { + // SAFETY: This is the only mapper created for this inode, so it is unique. + Some(unsafe { new_sb.bdev().inode().mapper() }) + } else { + None + }; + + let data = T::fill_super(new_sb, mapper)?; // N.B.: Even on failure, `kill_sb` is called and frees the data. sb.s_fs_info = data.into_foreign().cast_mut(); @@ -369,7 +405,7 @@ fn init(module: &'static ThisModule) -> impl PinInit { /// /// ``` /// # mod module_fs_sample { -/// use kernel::fs::{dentry, inode::INode, sb, sb::SuperBlock, self}; +/// use kernel::fs::{dentry, inode::INode, inode::Mapper, sb, sb::SuperBlock, self}; /// use kernel::prelude::*; /// /// kernel::module_fs! { @@ -384,7 +420,7 @@ fn init(module: &'static ThisModule) -> impl PinInit { /// impl fs::FileSystem for MyFs { /// type Data = (); /// const NAME: &'static CStr = kernel::c_str!("myfs"); -/// fn fill_super(_: &mut SuperBlock) -> Result { +/// fn fill_super(_: &mut SuperBlock, _: Option) -> Result { /// todo!() /// } /// fn init_root(_sb: &SuperBlock) -> Result> { diff --git a/rust/kernel/fs/inode.rs b/rust/kernel/fs/inode.rs index 75b68d697a6e..5b3602362521 100644 --- a/rust/kernel/fs/inode.rs +++ b/rust/kernel/fs/inode.rs @@ -7,13 +7,16 @@ //! C headers: [`include/linux/fs.h`](srctree/include/linux/fs.h) use super::{ - address_space, dentry, dentry::DEntry, file, sb::SuperBlock, FileSystem, Offset, UnspecifiedFS, + address_space, dentry, dentry::DEntry, file, sb::SuperBlock, FileSystem, Offset, PageOffset, + UnspecifiedFS, }; -use crate::error::{code::*, Result}; +use crate::error::{code::*, from_err_ptr, Result}; use crate::types::{ARef, AlwaysRefCounted, Either, ForeignOwnable, Lockable, Locked, Opaque}; -use crate::{bindings, block, str::CStr, str::CString, time::Timespec}; +use crate::{ + bindings, block, build_error, folio, folio::Folio, str::CStr, str::CString, time::Timespec, +}; use core::mem::ManuallyDrop; -use core::{marker::PhantomData, ptr}; +use core::{cmp, marker::PhantomData, ops::Deref, ptr}; use macros::vtable; /// The number of an inode. @@ -93,6 +96,129 @@ pub fn size(&self) -> Offset { // SAFETY: `self` is guaranteed to be valid by the existence of a shared reference. unsafe { bindings::i_size_read(self.0.get()) } } + + /// Returns a mapper for this inode. + /// + /// # Safety + /// + /// Callers must ensure that mappers are unique for a given inode and range. For inodes that + /// back a block device, a mapper is always created when the filesystem is mounted; so callers + /// in such situations must ensure that that mapper is never used. + pub unsafe fn mapper(&self) -> Mapper { + Mapper { + inode: self.into(), + begin: 0, + end: Offset::MAX, + } + } + + /// Returns a mapped folio at the given offset. + /// + /// # Safety + /// + /// Callers must ensure that there are no concurrent mutable mappings of the folio. + pub unsafe fn mapped_folio( + &self, + offset: Offset, + ) -> Result>> { + let page_index = offset >> bindings::PAGE_SHIFT; + let page_offset = offset & ((bindings::PAGE_SIZE - 1) as Offset); + let folio = self.read_mapping_folio(page_index.try_into()?)?; + + // SAFETY: The safety requirements guarantee that there are no concurrent mutable mappings + // of the folio. + unsafe { Folio::map_owned(folio, page_offset.try_into()?) } + } + + /// Returns the folio at the given page index. + pub fn read_mapping_folio( + &self, + index: PageOffset, + ) -> Result>>> { + let folio = from_err_ptr(unsafe { + bindings::read_mapping_folio( + (*self.0.get()).i_mapping, + index.try_into()?, + ptr::null_mut(), + ) + })?; + let ptr = ptr::NonNull::new(folio) + .ok_or(EIO)? + .cast::>>(); + // SAFETY: The folio returned by read_mapping_folio has had its refcount incremented. + Ok(unsafe { ARef::from_raw(ptr) }) + } + + /// Iterate over the given range, one folio at a time. + /// + /// # Safety + /// + /// Callers must ensure that there are no concurrent mutable mappings of the folio. + pub unsafe fn for_each_page( + &self, + first: Offset, + len: Offset, + mut cb: impl FnMut(&[u8]) -> Result>, + ) -> Result> { + if first >= self.size() { + return Ok(None); + } + let mut remain = cmp::min(len, self.size() - first); + first.checked_add(remain).ok_or(EIO)?; + + let mut next = first; + while remain > 0 { + // SAFETY: The safety requirements of this function satisfy those of `mapped_folio`. + let data = unsafe { self.mapped_folio(next)? }; + let avail = cmp::min(data.len(), remain.try_into().unwrap_or(usize::MAX)); + let ret = cb(&data[..avail])?; + if ret.is_some() { + return Ok(ret); + } + + next += avail as Offset; + remain -= avail as Offset; + } + + Ok(None) + } +} + +impl>> Locked { + /// Returns a mapped folio at the given offset. + // TODO: This conflicts with Locked::write. Once we settle on a way to handle reading + // the contents of certain inodes (e.g., directories, links), then we switch to that and + // remove this. + pub fn mapped_folio<'a>( + &'a self, + offset: Offset, + ) -> Result>> + where + T: 'a, + { + if T::IS_UNSPECIFIED { + build_error!("unspecified file systems cannot safely map folios"); + } + + // SAFETY: The inode is locked in read mode, so it's ok to map its contents. + unsafe { self.deref().mapped_folio(offset) } + } + + /// Iterate over the given range, one folio at a time. + // TODO: This has the same issue as mapped_folio above. + pub fn for_each_page( + &self, + first: Offset, + len: Offset, + cb: impl FnMut(&[u8]) -> Result>, + ) -> Result> { + if T::IS_UNSPECIFIED { + build_error!("unspecified file systems cannot safely map folios"); + } + + // SAFETY: The inode is locked in read mode, so it's ok to map its contents. + unsafe { self.deref().for_each_page(first, len, cb) } + } } // SAFETY: The type invariants guarantee that `INode` is always ref-counted. @@ -111,6 +237,7 @@ unsafe fn dec_ref(obj: ptr::NonNull) { /// Indicates that the an inode's rw semapahore is locked in read (shared) mode. pub struct ReadSem; +// SAFETY: `raw_lock` calls `inode_lock_shared` which locks the inode in shared mode. unsafe impl Lockable for INode { fn raw_lock(&self) { // SAFETY: Since there's a reference to the inode, it must be valid. @@ -432,3 +559,89 @@ extern "C" fn drop_cstring(ptr: *mut core::ffi::c_void) { Self(&Table::::TABLE, PhantomData) } } + +/// Allows mapping the contents of the inode. +/// +/// # Invariants +/// +/// Mappers are unique per range per inode. +pub struct Mapper { + inode: ARef>, + begin: Offset, + end: Offset, +} + +// SAFETY: All inode and folio operations are safe from any thread. +unsafe impl Send for Mapper {} + +// SAFETY: All inode and folio operations are safe from any thread. +unsafe impl Sync for Mapper {} + +impl Mapper { + /// Splits the mapper into two ranges. + /// + /// The first range is from the beginning of `self` up to and including `offset - 1`. The + /// second range is from `offset` to the end of `self`. + pub fn split_at(mut self, offset: Offset) -> (Self, Self) { + let inode = self.inode.clone(); + if offset <= self.begin { + ( + Self { + inode, + begin: offset, + end: offset, + }, + self, + ) + } else if offset >= self.end { + ( + self, + Self { + inode, + begin: offset, + end: offset, + }, + ) + } else { + let end = self.end; + self.end = offset; + ( + self, + Self { + inode, + begin: offset, + end, + }, + ) + } + } + + /// Returns a mapped folio at the given offset. + pub fn mapped_folio(&self, offset: Offset) -> Result>> { + if offset < self.begin || offset >= self.end { + return Err(ERANGE); + } + + // SAFETY: By the type invariant, there are no other mutable mappings of the folio. + let mut map = unsafe { self.inode.mapped_folio(offset) }?; + map.cap_len((self.end - offset).try_into()?); + Ok(map) + } + + /// Iterate over the given range, one folio at a time. + pub fn for_each_page( + &self, + first: Offset, + len: Offset, + cb: impl FnMut(&[u8]) -> Result>, + ) -> Result> { + if first < self.begin || first >= self.end { + return Err(ERANGE); + } + + let actual_len = cmp::min(len, self.end - first); + + // SAFETY: By the type invariant, there are no other mutable mappings of the folio. + unsafe { self.inode.for_each_page(first, actual_len, cb) } + } +} diff --git a/rust/kernel/fs/sb.rs b/rust/kernel/fs/sb.rs index 7c0c52e6da0a..93c7b2770163 100644 --- a/rust/kernel/fs/sb.rs +++ b/rust/kernel/fs/sb.rs @@ -8,11 +8,22 @@ use super::inode::{self, INode, Ino}; use super::FileSystem; -use crate::bindings; use crate::error::{code::*, Result}; use crate::types::{ARef, Either, ForeignOwnable, Opaque}; +use crate::{bindings, block, build_error}; use core::{marker::PhantomData, ptr}; +/// Type of superblock keying. +/// +/// It determines how C's `fs_context_operations::get_tree` is implemented. +pub enum Type { + /// Multiple independent superblocks may exist. + Independent, + + /// Uses a block device. + BlockDev, +} + /// A typestate for [`SuperBlock`] that indicates that it's a new one, so not fully initialized /// yet. pub struct New; @@ -75,6 +86,28 @@ pub fn rdonly(&self) -> bool { // SAFETY: `s_flags` only changes during init, so it is safe to read it. unsafe { (*self.0.get()).s_flags & bindings::SB_RDONLY != 0 } } + + /// Returns the block device associated with the superblock. + pub fn bdev(&self) -> &block::Device { + if !matches!(T::SUPER_TYPE, Type::BlockDev) { + build_error!("bdev is only available in blockdev superblocks"); + } + + // SAFETY: The superblock is valid and given that it's a blockdev superblock it must have a + // valid `s_bdev` that remains valid while the superblock (`self`) is valid. + unsafe { block::Device::from_raw((*self.0.get()).s_bdev) } + } + + /// Returns the number of sectors in the underlying block device. + pub fn sector_count(&self) -> block::Sector { + if !matches!(T::SUPER_TYPE, Type::BlockDev) { + build_error!("sector_count is only available in blockdev superblocks"); + } + + // SAFETY: The superblock is valid and given that it's a blockdev superblock it must have a + // valid `s_bdev`. + unsafe { bindings::bdev_nr_sectors((*self.0.get()).s_bdev) } + } } impl SuperBlock { @@ -85,6 +118,20 @@ pub fn set_magic(&mut self, magic: usize) -> &mut Self { unsafe { (*self.0.get()).s_magic = magic as core::ffi::c_ulong }; self } + + /// Sets the device blocksize, subjected to the minimum accepted by the device. + /// + /// Returns the actual value set. + pub fn min_blocksize(&mut self, size: i32) -> i32 { + if !matches!(T::SUPER_TYPE, Type::BlockDev) { + build_error!("min_blocksize is only available in blockdev superblocks"); + } + + // SAFETY: This a new superblock that is being initialised, so it it's ok to set the block + // size. Additionally, we've checked that this is the superblock is backed by a block + // device, so it is also valid. + unsafe { bindings::sb_min_blocksize(self.0.get(), size) } + } } impl SuperBlock { diff --git a/samples/rust/rust_rofs.rs b/samples/rust/rust_rofs.rs index 7027ca067f8f..fea3360b6e7a 100644 --- a/samples/rust/rust_rofs.rs +++ b/samples/rust/rust_rofs.rs @@ -101,7 +101,7 @@ impl fs::FileSystem for RoFs { type Data = (); const NAME: &'static CStr = c_str!("rust_rofs"); - fn fill_super(sb: &mut sb::SuperBlock) -> Result { + fn fill_super(sb: &mut sb::SuperBlock, _: Option) -> Result { sb.set_magic(0x52555354); Ok(()) } From patchwork Tue May 14 13:17:05 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wedson Almeida Filho X-Patchwork-Id: 13664143 Received: from mail-pl1-f174.google.com (mail-pl1-f174.google.com [209.85.214.174]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E7C9E15B0F9; Tue, 14 May 2024 13:18:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.174 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715692686; cv=none; b=Rjr3iAKSeijqcQRLkw3ssYSKlV+6NHE66eZgUqLajdXc7imMUevdS2wHcuUhpagEYAwSEC4nmmuyN18QwEnq+i1NjcIH4bMSroXqpwRmD+uGXpQZPS/DtqNtajsDvNKd+5/oOMPIKCZSog9w3K5qQNx6r4tUeQFkTubjyawixjI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715692686; c=relaxed/simple; bh=VXGbCQDnecMt6mt+aq4tlwfDABY1crJXoO0vieyH/ms=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=rILrE3P/kR7nVOHjv7+VjnwEBjzP+fe0iG/JpI55qImcgGESBttmMt+fQTMjYq+E0Dg9wJyXVXsgXgvWm8jJ2ngVKaA9weH+i2LpVNDCJyI8xEO1eyftb0TeJE6/oRNShTb+UsFi0rDybyue1jX45c24Qin6Qcl9GSL/IsG8E58= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=AxPUAUgy; arc=none smtp.client-ip=209.85.214.174 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="AxPUAUgy" Received: by mail-pl1-f174.google.com with SMTP id d9443c01a7336-1ed012c1afbso44539545ad.1; Tue, 14 May 2024 06:18:03 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1715692683; x=1716297483; 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=znND3ftEI4hHOqwMzLtbBr8oQP+s8h6w3BvrEV2G89A=; b=AxPUAUgy2LX+p3RMpHOnWv1dQ8tZ613GjXg0HMfqppOV5sfdfU2tQH/JBOixRZaS3n W7i/crB77dK7wOONQ7UEm0yFvLqoWIMoiolCiaiwnRbVVsOfmujpuado+vFoJf8UujNQ +S8u5+2bwmzp5LLFRjukSLr7gsEflIgmowkkSt5wzNmQBiomRHTgHdBLjIDzWPe/4KoN /c+MTbbeqN4/IHst5uw5EkYQTxLyH0Q7osNzKtVP58sivaqDPdCbK120BcUrFsWNNgqm QUr6MX/eFPB5sczswDBbh2sjvLtkB1HUmR3B9fAEbZTu8xDTYLVwb83lArUhDtXv90cS IaKQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1715692683; x=1716297483; 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=znND3ftEI4hHOqwMzLtbBr8oQP+s8h6w3BvrEV2G89A=; b=gDHARms4pkzazluu0cjkcJdH5/geim4Gqxn3U6Sffzamis48IRn/8wOQSGtNb0ffaJ vVq1fwAIdADGqHwFUaxEfL2FJjHrOA04mAACyPZhSbeZuMYL/dVNMQhIPmoAnre1LQ/S /xoSTBOimP53qe9dmXzMPUmNefqqIJT0jBWzRTuX76Ke9I/nyVQM03FZK5piLFgpqtpc RjDlrwRLQ06UTVpsrteDZGqn90NDgMP+sDTIRE2tk6CT5S3X/POEtptv6DhImOZYMFAw gAUbO/jSgkdxVYe/zN1lupkJJJdFzQnlGK9ckNgd5PLVqZJ7oYPoWiFa0GHvPy4cCiWE PyQQ== X-Forwarded-Encrypted: i=1; AJvYcCWwKy4s/mR5xOgbkIOF7fP76zuGqjmJlQStJoNekCtUsjWHBZyZyb2DMNDt0d5SxbCHDYI6lHTqQCGA24+ZknrLOv1TZleOtbEltirxh6GwsgD7JpVWMHBZ/D3ZddLu20SqSUbOAB2lF2xcBo7hJm7o67TIChXLnEDpR6bcARlBNL52zabazXid4WLr X-Gm-Message-State: AOJu0YwAifTC21p2pGjo+UgbHuV2e4SQDk/fSWfFADfEGaQcYc/Snwpd ZWRNb9++/5zyQhqboLhuSsvHldamMtCAP3OVjcuYMlHuzrMBrz4n X-Google-Smtp-Source: AGHT+IGoHGDX0jiy5IZ34a3V+2976VMs1A2S7Hst5FzQXDoIe4OCQK74KtUh8IFsgw3czxFbp9NZZQ== X-Received: by 2002:a17:903:24d:b0:1e0:b60f:5de3 with SMTP id d9443c01a7336-1ef42d69b4fmr177636065ad.7.1715692683091; Tue, 14 May 2024 06:18:03 -0700 (PDT) Received: from wedsonaf-dev.. ([50.204.89.32]) by smtp.googlemail.com with ESMTPSA id d9443c01a7336-1ef0b9d18a4sm97277335ad.56.2024.05.14.06.18.02 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 14 May 2024 06:18:02 -0700 (PDT) From: Wedson Almeida Filho To: Alexander Viro , Christian Brauner , Matthew Wilcox , Dave Chinner Cc: Kent Overstreet , Greg Kroah-Hartman , linux-fsdevel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, Wedson Almeida Filho Subject: [RFC PATCH v2 24/30] rust: fs: allow per-inode data Date: Tue, 14 May 2024 10:17:05 -0300 Message-Id: <20240514131711.379322-25-wedsonaf@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240514131711.379322-1-wedsonaf@gmail.com> References: <20240514131711.379322-1-wedsonaf@gmail.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 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 | 19 ++++-- rust/kernel/fs/inode.rs | 123 ++++++++++++++++++++++++++++++++++++-- rust/kernel/mem_cache.rs | 2 - samples/rust/rust_rofs.rs | 13 ++-- 5 files changed, 143 insertions(+), 21 deletions(-) diff --git a/rust/helpers.c b/rust/helpers.c index 6c6d18df055f..edf12868962c 100644 --- a/rust/helpers.c +++ b/rust/helpers.c @@ -266,6 +266,13 @@ struct folio *rust_helper_read_mapping_folio(struct address_space *mapping, } EXPORT_SYMBOL_GPL(rust_helper_read_mapping_folio); +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 864aca24d12c..d64fe1a5812f 100644 --- a/rust/kernel/fs.rs +++ b/rust/kernel/fs.rs @@ -8,8 +8,8 @@ use crate::error::{code::*, from_result, to_result, Error, Result}; use crate::types::{ForeignOwnable, Opaque}; -use crate::{bindings, init::PinInit, str::CStr, try_pin_init, ThisModule}; -use core::{ffi, marker::PhantomData, mem::ManuallyDrop, pin::Pin, ptr}; +use crate::{bindings, init::PinInit, mem_cache::MemCache, str::CStr, try_pin_init, ThisModule}; +use core::{ffi, marker::PhantomData, mem::size_of, mem::ManuallyDrop, pin::Pin, ptr}; use dentry::DEntry; use inode::INode; use macros::{pin_data, pinned_drop}; @@ -39,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; @@ -109,6 +112,7 @@ pub struct Stat { impl FileSystem for UnspecifiedFS { type Data = (); + type INodeData = (); const NAME: &'static CStr = crate::c_str!("unspecified"); const IS_UNSPECIFIED: bool = true; fn fill_super(_: &mut SuperBlock, _: Option) -> Result { @@ -125,6 +129,7 @@ fn init_root(_: &SuperBlock) -> Result> { pub struct Registration { #[pin] fs: Opaque, + inode_cache: Option, } // SAFETY: `Registration` doesn't provide any `&self` methods, so it is safe to pass references @@ -139,6 +144,7 @@ impl Registration { /// Creates the initialiser of a new file system registration. pub fn new(module: &'static ThisModule) -> impl PinInit { try_pin_init!(Self { + inode_cache: INode::::new_cache()?, 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()) }; @@ -284,8 +290,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(INode::::alloc_inode_callback) + } else { + None + }, + destroy_inode: Some(INode::::destroy_inode_callback), free_inode: None, dirty_inode: None, write_inode: None, @@ -419,6 +429,7 @@ fn init(module: &'static ThisModule) -> impl PinInit { /// struct MyFs; /// impl fs::FileSystem for MyFs { /// type Data = (); +/// type INodeData = (); /// const NAME: &'static CStr = kernel::c_str!("myfs"); /// fn fill_super(_: &mut SuperBlock, _: Option) -> Result { /// todo!() diff --git a/rust/kernel/fs/inode.rs b/rust/kernel/fs/inode.rs index 5b3602362521..5230ff2fe0dd 100644 --- a/rust/kernel/fs/inode.rs +++ b/rust/kernel/fs/inode.rs @@ -13,9 +13,10 @@ use crate::error::{code::*, from_err_ptr, Result}; use crate::types::{ARef, AlwaysRefCounted, Either, ForeignOwnable, Lockable, Locked, Opaque}; use crate::{ - bindings, block, build_error, folio, folio::Folio, str::CStr, str::CString, time::Timespec, + bindings, block, build_error, container_of, folio, folio::Folio, mem_cache::MemCache, + str::CStr, str::CString, time::Timespec, }; -use core::mem::ManuallyDrop; +use core::mem::{size_of, ManuallyDrop, MaybeUninit}; use core::{cmp, marker::PhantomData, ops::Deref, ptr}; use macros::vtable; @@ -91,6 +92,18 @@ pub fn super_block(&self) -> &SuperBlock { unsafe { SuperBlock::from_raw((*self.0.get()).i_sb) } } + /// Returns the data associated with the inode. + pub fn data(&self) -> &T::INodeData { + if T::IS_UNSPECIFIED { + crate::build_error!("inode data type is unspecified"); + } + let outerp = container_of!(self.0.get(), WithData, 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) -> Offset { // SAFETY: `self` is guaranteed to be valid by the existence of a shared reference. @@ -182,6 +195,87 @@ pub unsafe fn for_each_page( Ok(None) } + + pub(crate) fn new_cache() -> Result> { + Ok(if size_of::() == 0 { + None + } else { + Some(MemCache::try_new::>( + T::NAME, + Some(Self::inode_init_once_callback), + )?) + }) + } + + 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 `inode::WithData` + // instance whose inode part can be initialised. + unsafe { bindings::inode_init_once(ptr::addr_of_mut!((*ptr).inode)) }; + } + + pub(crate) 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, super::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(); + } + + // SAFETY: `ptr` was just allocated, so it is valid for dereferencing. + unsafe { ptr::addr_of_mut!((*ptr).inode) } + } + + pub(crate) 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, super::Registration, fs) }; + let ptr = container_of!(inode, WithData, 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::(), + ) + }; + } + } } impl>> Locked { @@ -251,6 +345,11 @@ unsafe fn unlock(&self) { } } +struct WithData { + data: MaybeUninit, + inode: bindings::inode, +} + /// An inode that is locked and hasn't been initialised yet. /// /// # Invariants @@ -263,9 +362,18 @@ pub struct New( impl New { /// Initialises the new inode with the given parameters. - pub fn init(mut self, params: Params) -> Result>> { - // SAFETY: This is a new inode, so it's safe to manipulate it mutably. - let inode = unsafe { self.0.as_mut() }; + pub fn init(self, params: Params) -> Result>> { + let outerp = container_of!(self.0.as_ptr(), WithData, 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 { Type::Dir => bindings::S_IFDIR, Type::Reg => { @@ -404,7 +512,7 @@ pub enum Type { /// Required inode parameters. /// /// This is used when creating new inodes. -pub struct Params { +pub struct Params { /// 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, @@ -439,6 +547,9 @@ pub struct Params { /// Last access time. pub atime: Timespec, + + /// Value to attach to this node. + pub value: T, } /// Represents inode operations. diff --git a/rust/kernel/mem_cache.rs b/rust/kernel/mem_cache.rs index e7e2720ff6cd..cbf1b7e75334 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 fea3360b6e7a..5b6c3f50adf4 100644 --- a/samples/rust/rust_rofs.rs +++ b/samples/rust/rust_rofs.rs @@ -93,12 +93,14 @@ fn iget(sb: &sb::SuperBlock, e: &'static Entry) -> Result atime: UNIX_EPOCH, ctime: UNIX_EPOCH, mtime: UNIX_EPOCH, + value: e, }) } } impl fs::FileSystem for RoFs { type Data = (); + type INodeData = &'static Entry; const NAME: &'static CStr = c_str!("rust_rofs"); fn fill_super(sb: &mut sb::SuperBlock, _: Option) -> Result { @@ -149,10 +151,7 @@ fn get_link<'a>( return Err(ECHILD); } - let name_buf = match inode.ino() { - 3 => ENTRIES[3].contents, - _ => return Err(EINVAL), - }; + let name_buf = inode.data().contents; let mut name = Box::new_slice( name_buf.len().checked_add(1).ok_or(ENOMEM)?, b'\0', @@ -168,11 +167,7 @@ impl address_space::Operations for RoFs { type FileSystem = Self; fn read_folio(_: Option<&File>, mut folio: Locked<&Folio>>) -> Result { - let data = match folio.inode().ino() { - 2 => ENTRIES[2].contents, - _ => return Err(EINVAL), - }; - + let data = folio.inode().data().contents; let pos = usize::try_from(folio.pos()).unwrap_or(usize::MAX); let copied = if pos >= data.len() { 0 From patchwork Tue May 14 13:17:06 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wedson Almeida Filho X-Patchwork-Id: 13664142 Received: from mail-pl1-f178.google.com (mail-pl1-f178.google.com [209.85.214.178]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C4A3715B548; Tue, 14 May 2024 13:18:04 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.178 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715692686; cv=none; b=T3I6J/LqnoY3IJXUsvr6pysWzML6iP8z41LlfbLZMIu0HaTEYs4QsRiz1pMl2rsyF0zZXnT0JRzPN0QzqtHVOllDDAM+zJVL30WrsOJjbd3S/v+OIEySfrLOcVziv77YWCC2INf8wtJYMHTQLr6tEZHah2X3xnBxTj+7ot1g5gs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715692686; c=relaxed/simple; bh=D7M8pVfsTC+fSZDKePS+b8y4ahUIRhAr9xpbzUfnsDw=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=mYid40QstFctTEMWlUS/UldC6ysozyutdnD37EkFNhd+TzZRYdbzvM5nDkIZnnj1+KBtWtbUEBtJ/OztH7eg9M+A6PcugCM3tqDmN2ldMDjbJUgxIRwupVe6SwJzPo2vJq0wyLdsLTIP0vXw9EpHFWWFEbUcJhRzik/gOsEwQLc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=dFDcfdj8; arc=none smtp.client-ip=209.85.214.178 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="dFDcfdj8" Received: by mail-pl1-f178.google.com with SMTP id d9443c01a7336-1ecd3867556so42342155ad.0; Tue, 14 May 2024 06:18:04 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1715692684; x=1716297484; 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=9Tvk+4/cuasxtbCoAfEn0BfCmyFKQZmSR0C0/ucGLyo=; b=dFDcfdj8pQNRTweaurimWIJCywJHGrB2DcY3yTJ91hhlSUVFrpi9JvyTwlF48u8/eX wInrPQrlY/XWQoOJ5cgiuFKjXcKLnQT65qDUK/35jWqCWcmQEH90imuP2JsZhxYZu9XM 0Hley0N1O/CF8KUxWNg2r79YkUgLJeL7Vt1wbuXizfqfNXDGCwe7Ug8LaH984l3hpgUZ YqzuiYzDns+h7i/3U8CyMaggH5r8NdaFMCaUPEQNg6cuIdcfnt2ZdyymqW6w7Tjy1NYh FuEOdj3RAy7RkRtGMsQekW3POYKXeI/xX9te0C14DrSg4sD+SngWG6gBX84ZuRHwCMpn Vekg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1715692684; x=1716297484; 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=9Tvk+4/cuasxtbCoAfEn0BfCmyFKQZmSR0C0/ucGLyo=; b=FzhO1XfPG0gMrrG0OT360wfbut2kvbwYfUDavSjPVYgo6Rn5IEixBgbK8OfSmsojcK xtzbzTsXg9pj1ckSPPfCyDqIe2F9l1IVmmy9/Od52RCr0pLkP0YymkKu6+dF2qyGI8xY bMK8AyEoEKnkP79sTJz2K+xfrVw1UVbQl0SgHIWtzJIpCmqpIyQVRaE1PIHImwk3b6rn LMGlpKC1O52N0puMZpPRV9tmVf38PiYBWgbhRpoycDuTCA3H/appKJB4GKFELnl2IOQl gfyiaDiINNt9YwF+rnqBd1yGjoDq2oUkFMHJkhQf93psl/yRNMBS+O1hqrKtYQ1UixFu 8RdQ== X-Forwarded-Encrypted: i=1; AJvYcCV6JIJjCHZ8YvoFfOTKdEfRNCnOysIgb8iIswTUQhYMV21Rm/xXMSEfqMcdQij3Wc7AzXi6t5mHPdSLV7TtIZUPEimwAzRToA4BIZXM3JxJRTOlz7JS9Sj24S2URS4Jss+iJvyooZiPWu3ms+TxZy7iQDpX2ErrAf9OfF2kcgmIdfMdzVdN/1I1DqDO X-Gm-Message-State: AOJu0Yx23ESPJfW+4biFREr7ewZfzZU9N3/0V20orQ1qSUJjOlRlPWwO R+mm2VChZT2KyRlYyvs4AGXhq0zORu8njmYYMPCbv0uXUza63Mwk X-Google-Smtp-Source: AGHT+IHFc1KLRutr9+TMQCXqLb/r6uooGYKc4ocbDY2OD5+gzl/iFmilc83xdO0NRL6HAhYef4qn2w== X-Received: by 2002:a17:903:1c9:b0:1e3:e081:d29b with SMTP id d9443c01a7336-1ef4404fc55mr159384335ad.45.1715692684001; Tue, 14 May 2024 06:18:04 -0700 (PDT) Received: from wedsonaf-dev.. ([50.204.89.32]) by smtp.googlemail.com with ESMTPSA id d9443c01a7336-1ef0b9d18a4sm97277335ad.56.2024.05.14.06.18.03 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 14 May 2024 06:18:03 -0700 (PDT) From: Wedson Almeida Filho To: Alexander Viro , Christian Brauner , Matthew Wilcox , Dave Chinner Cc: Kent Overstreet , Greg Kroah-Hartman , linux-fsdevel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, Wedson Almeida Filho Subject: [RFC PATCH v2 25/30] rust: fs: export file type from mode constants Date: Tue, 14 May 2024 10:17:06 -0300 Message-Id: <20240514131711.379322-26-wedsonaf@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240514131711.379322-1-wedsonaf@gmail.com> References: <20240514131711.379322-1-wedsonaf@gmail.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 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 d64fe1a5812f..4d90b23735bc 100644 --- a/rust/kernel/fs.rs +++ b/rust/kernel/fs.rs @@ -31,6 +31,33 @@ /// This is C's `pgoff_t`. pub type PageOffset = usize; +/// 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: u16 = bindings::S_IFMT as u16; + + /// File type constant for block devices. + pub const S_IFBLK: u16 = bindings::S_IFBLK as u16; + + /// File type constant for char devices. + pub const S_IFCHR: u16 = bindings::S_IFCHR as u16; + + /// File type constant for directories. + pub const S_IFDIR: u16 = bindings::S_IFDIR as u16; + + /// File type constant for pipes. + pub const S_IFIFO: u16 = bindings::S_IFIFO as u16; + + /// File type constant for symbolic links. + pub const S_IFLNK: u16 = bindings::S_IFLNK as u16; + + /// File type constant for regular files. + pub const S_IFREG: u16 = bindings::S_IFREG as u16; + + /// File type constant for sockets. + pub const S_IFSOCK: u16 = bindings::S_IFSOCK as u16; +} + /// Maximum size of an inode. pub const MAX_LFS_FILESIZE: Offset = bindings::MAX_LFS_FILESIZE; From patchwork Tue May 14 13:17:07 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wedson Almeida Filho X-Patchwork-Id: 13664144 Received: from mail-pl1-f181.google.com (mail-pl1-f181.google.com [209.85.214.181]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B380E144D10; Tue, 14 May 2024 13:18:05 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.181 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715692687; cv=none; b=lA8iYRTXJZC8FemlJHmgimmUEZAuAZLOZdGTjpQsO7Ux+20kFMiOo9hHn7y2I3fnlgMeFZhU7HGZjvAAxd6XjT9o04uVkJgkTGDmwBfDQvQMbkkI0b1ZBFhA+uNE30QiUs4+na2y/AdhqdrT0P9V6NX4LUW0QKNbG15r4iPPiks= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715692687; c=relaxed/simple; bh=oKKKcGDsAVvN6kc943QPv5br805N5TRJBcdskzMsU2o=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=YNP4BYaPjwO2mqk6jCGahMD1b/3VGoIRDU6AnUiGySaPz3bgqbL+0YGJEatSgGJbhevHGNcoWrh7wTZffqHmYQi+oPq5JOtJ5iZ8BC1g9AGKZ+EKRSAEHORa9qHAxpjoB+QQTbkUEzZa8mj+iIsMtQ1eOQdGXDrVBESD9ipk4NQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=MOQoRym4; arc=none smtp.client-ip=209.85.214.181 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="MOQoRym4" Received: by mail-pl1-f181.google.com with SMTP id d9443c01a7336-1ee5235f5c9so42975755ad.2; Tue, 14 May 2024 06:18:05 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1715692685; x=1716297485; 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=DCzmFrau90TAwq/dSawTTHAfKetirw11Aj2X0VbDWSo=; b=MOQoRym492aak6kagIkmueyBwfOgg9QeDTKADLggivmRedN7RTUzPInSaAxh7OzRfa 1nn+T3Gf9a9hymGXzuywC78gRYTRt3RF7huLb290WkD27WroXAf/kubQ2LTer0Z6GzJD hYIrxNDCli4/uTPnYacVxqypjIDRsPG34zUHlVtaw42BmLpbWpUvg26ANH6le8x/iHFI P7Zhv7Wcfbh6pipJGkddm2kvMpEOKv9zAdQscenSyASfb2XMptrXhTYXSIO854ECaKo2 WgC2F8aWPsKZAAtRdxHZap08yzD0K4dVQgw30HandVICmlQ3pxn80mrXN+4aOlbXZbS9 JDQQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1715692685; x=1716297485; 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=DCzmFrau90TAwq/dSawTTHAfKetirw11Aj2X0VbDWSo=; b=uqjxDWCCCQh4w0z0GS7mwwJ3cAXoB4lJ1TKXXWaezUiUsI8ttRZ6F0RQiDlMHr/Y7O qxUYTMI4yOz1nL2H+xFuq6mV2lcQns1b4EqcX003ayHg1FeL7AEWqN9f3FFtXsJG2CLO WuAw4aygdSNpEhUS/By2NL8MgnDLWwD0APZ1SbCEF66q8IaPRflpsgmo+6SEc/H2CQ/X qqd+FGuSExBOKR0ub9f04NUwQqaZvGGti1cR0Ws+hwLVn4CVK4FF6UybG92PzOTrc3Ot FDVjU7ruAYZ5DUoVw+hJu2CQTZir5LfZtBiwP9qJNK517BVDnRqNmLTn8e22znh6419V bggw== X-Forwarded-Encrypted: i=1; AJvYcCXk8DnqLGd5ch46vSD35AnbMNxJ//oJRgp8edZmZ8GCYEOMVJvWb8jSeQIUCZlrBvJQhysVrSgWpklTvu+F47lWI7SEDHnm2HPkdJqRftlJ0lP+53LN4G9zuKqxEaWnEYncdx7J8dVNEecWCaZEscn07vOy3+rv9BGw5AvauR+gX9+qdvbGqoxlQmjm X-Gm-Message-State: AOJu0Ywn3JheqrSR28mlj/ACX1aBGIF+u1ndpwp+i73VbJhCpNkyNxZQ uB9f25/R5/LSWwr3avjtsoug4TpicrRjTq+UecT/02k37+NtBR1m X-Google-Smtp-Source: AGHT+IG3EsvAG0GsoCpOHzO+54+s+5hzgq3h/bNRZnn4zxAFrlD5PruuV+7qgLd5a3qyV5z5YZpO1Q== X-Received: by 2002:a17:903:32cd:b0:1ee:b0db:e74a with SMTP id d9443c01a7336-1ef43e28493mr164341695ad.40.1715692684914; Tue, 14 May 2024 06:18:04 -0700 (PDT) Received: from wedsonaf-dev.. ([50.204.89.32]) by smtp.googlemail.com with ESMTPSA id d9443c01a7336-1ef0b9d18a4sm97277335ad.56.2024.05.14.06.18.04 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 14 May 2024 06:18:04 -0700 (PDT) From: Wedson Almeida Filho To: Alexander Viro , Christian Brauner , Matthew Wilcox , Dave Chinner Cc: Kent Overstreet , Greg Kroah-Hartman , linux-fsdevel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, Wedson Almeida Filho Subject: [RFC PATCH v2 26/30] rust: fs: allow populating i_lnk Date: Tue, 14 May 2024 10:17:07 -0300 Message-Id: <20240514131711.379322-27-wedsonaf@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240514131711.379322-1-wedsonaf@gmail.com> References: <20240514131711.379322-1-wedsonaf@gmail.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Wedson Almeida Filho Allow Rust file systems to store a string that represents the destination of a symbolic link inode in the inode itself. Signed-off-by: Wedson Almeida Filho --- rust/kernel/fs/file.rs | 6 ++--- rust/kernel/fs/inode.rs | 32 +++++++++++++++++++++---- samples/rust/rust_rofs.rs | 50 ++++++++++++--------------------------- 3 files changed, 45 insertions(+), 43 deletions(-) diff --git a/rust/kernel/fs/file.rs b/rust/kernel/fs/file.rs index a819724b75f8..9db70eff1169 100644 --- a/rust/kernel/fs/file.rs +++ b/rust/kernel/fs/file.rs @@ -518,15 +518,15 @@ pub enum DirEntryType { Wht = bindings::DT_WHT, } -impl From for DirEntryType { - fn from(value: inode::Type) -> Self { +impl From<&inode::Type> for DirEntryType { + fn from(value: &inode::Type) -> Self { match value { inode::Type::Fifo => DirEntryType::Fifo, inode::Type::Chr(_, _) => DirEntryType::Chr, inode::Type::Dir => DirEntryType::Dir, inode::Type::Blk(_, _) => DirEntryType::Blk, inode::Type::Reg => DirEntryType::Reg, - inode::Type::Lnk => DirEntryType::Lnk, + inode::Type::Lnk(_) => DirEntryType::Lnk, inode::Type::Sock => DirEntryType::Sock, } } diff --git a/rust/kernel/fs/inode.rs b/rust/kernel/fs/inode.rs index 5230ff2fe0dd..b2b7d000080e 100644 --- a/rust/kernel/fs/inode.rs +++ b/rust/kernel/fs/inode.rs @@ -7,8 +7,8 @@ //! C headers: [`include/linux/fs.h`](srctree/include/linux/fs.h) use super::{ - address_space, dentry, dentry::DEntry, file, sb::SuperBlock, FileSystem, Offset, PageOffset, - UnspecifiedFS, + address_space, dentry, dentry::DEntry, file, mode, sb::SuperBlock, FileSystem, Offset, + PageOffset, UnspecifiedFS, }; use crate::error::{code::*, from_err_ptr, Result}; use crate::types::{ARef, AlwaysRefCounted, Either, ForeignOwnable, Lockable, Locked, Opaque}; @@ -255,6 +255,17 @@ pub(crate) fn new_cache() -> Result> { let ptr = container_of!(inode, WithData, inode).cast_mut(); if !is_bad { + // SAFETY: The API contract guarantees that `inode` is valid. + if unsafe { (*inode).i_mode & mode::S_IFMT == mode::S_IFLNK } { + // SAFETY: We just checked that the inode is a link. + let lnk = unsafe { (*inode).__bindgen_anon_4.i_link }; + if !lnk.is_null() { + // SAFETY: This value is on link inode are only populated from with the result + // of `CString::into_foreign`. + unsafe { CString::from_foreign(lnk.cast::()) }; + } + } + // 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()) }; @@ -381,7 +392,7 @@ pub fn init(self, params: Params) -> Result>> { unsafe { bindings::mapping_set_large_folios(inode.i_mapping) }; bindings::S_IFREG } - Type::Lnk => { + Type::Lnk(str) => { // If we are using `page_get_link`, we need to prevent the use of high mem. if !inode.i_op.is_null() { // SAFETY: We just checked that `i_op` is non-null, and we always just set it @@ -393,6 +404,9 @@ pub fn init(self, params: Params) -> Result>> { unsafe { bindings::inode_nohighmem(inode) }; } } + if let Some(s) = str { + inode.__bindgen_anon_4.i_link = s.into_foreign().cast::().cast_mut(); + } bindings::S_IFLNK } Type::Fifo => { @@ -485,7 +499,6 @@ fn drop(&mut self) { } /// The type of an inode. -#[derive(Copy, Clone)] pub enum Type { /// Named pipe (first-in, first-out) type. Fifo, @@ -503,7 +516,7 @@ pub enum Type { Reg, /// Symbolic link type. - Lnk, + Lnk(Option), /// Named unix-domain socket type. Sock, @@ -565,6 +578,15 @@ pub fn page_symlink_inode() -> Self { ) } + /// Returns inode operations for symbolic links that are stored in the `i_lnk` field. + pub fn simple_symlink_inode() -> Self { + // SAFETY: This is a constant in C, it never changes. + Self( + unsafe { &bindings::simple_symlink_inode_operations }, + PhantomData, + ) + } + /// Creates the inode operations from a type that implements the [`Operations`] trait. pub const fn new + ?Sized>() -> Self { struct Table(PhantomData); diff --git a/samples/rust/rust_rofs.rs b/samples/rust/rust_rofs.rs index 5b6c3f50adf4..5d0d1936459d 100644 --- a/samples/rust/rust_rofs.rs +++ b/samples/rust/rust_rofs.rs @@ -7,7 +7,7 @@ }; use kernel::prelude::*; use kernel::types::{ARef, Either, Locked}; -use kernel::{c_str, folio::Folio, folio::PageCache, fs, str::CString, time::UNIX_EPOCH, user}; +use kernel::{c_str, folio::Folio, folio::PageCache, fs, time::UNIX_EPOCH, user}; kernel::module_fs! { type: RoFs, @@ -46,7 +46,7 @@ struct Entry { Entry { name: b"link.txt", ino: 3, - etype: inode::Type::Lnk, + etype: inode::Type::Lnk(None), contents: b"./test.txt", }, ]; @@ -54,7 +54,6 @@ struct Entry { const DIR_FOPS: file::Ops = file::Ops::new::(); const DIR_IOPS: inode::Ops = inode::Ops::new::(); const FILE_AOPS: address_space::Ops = address_space::Ops::new::(); -const LNK_IOPS: inode::Ops = inode::Ops::new::(); struct RoFs; @@ -65,25 +64,30 @@ fn iget(sb: &sb::SuperBlock, e: &'static Entry) -> Result Either::Right(new) => new, }; - let (mode, nlink, size) = match e.etype { + let (mode, nlink, size, typ) = match e.etype { inode::Type::Dir => { new.set_iops(DIR_IOPS).set_fops(DIR_FOPS); - (0o555, 2, ENTRIES.len().try_into()?) + (0o555, 2, ENTRIES.len().try_into()?, inode::Type::Dir) } inode::Type::Reg => { new.set_fops(file::Ops::generic_ro_file()) .set_aops(FILE_AOPS); - (0o444, 1, e.contents.len().try_into()?) + (0o444, 1, e.contents.len().try_into()?, inode::Type::Reg) } - inode::Type::Lnk => { - new.set_iops(LNK_IOPS); - (0o444, 1, e.contents.len().try_into()?) + inode::Type::Lnk(_) => { + new.set_iops(inode::Ops::simple_symlink_inode()); + ( + 0o444, + 1, + e.contents.len().try_into()?, + inode::Type::Lnk(Some(e.contents.try_into()?)), + ) } _ => return Err(ENOENT), }; new.init(inode::Params { - typ: e.etype, + typ, mode, size, blocks: (u64::try_from(size)? + 511) / 512, @@ -138,30 +142,6 @@ fn lookup( } } -struct Link; -#[vtable] -impl inode::Operations for Link { - type FileSystem = RoFs; - - fn get_link<'a>( - dentry: Option<&DEntry>, - inode: &'a INode, - ) -> Result> { - if dentry.is_none() { - return Err(ECHILD); - } - - let name_buf = inode.data().contents; - let mut name = Box::new_slice( - name_buf.len().checked_add(1).ok_or(ENOMEM)?, - b'\0', - GFP_NOFS, - )?; - name[..name_buf.len()].copy_from_slice(name_buf); - Ok(Either::Left(name.try_into()?)) - } -} - #[vtable] impl address_space::Operations for RoFs { type FileSystem = Self; @@ -212,7 +192,7 @@ fn read_dir( } for e in ENTRIES.iter().skip(pos.try_into()?) { - if !emitter.emit(1, e.name, e.ino, e.etype.into()) { + if !emitter.emit(1, e.name, e.ino, (&e.etype).into()) { break; } } From patchwork Tue May 14 13:17:08 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wedson Almeida Filho X-Patchwork-Id: 13664145 Received: from mail-pl1-f171.google.com (mail-pl1-f171.google.com [209.85.214.171]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9CDD915CD73; Tue, 14 May 2024 13:18:06 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.171 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715692688; cv=none; b=hoQuc6pyTjDe3mzzUcf5MYHcKoPVzBLRHYsnwBNIDd6qmTjYphiJQ4TXfmND/GwnMJvb/OiPbIo2VL9p8mvFo2uorLpOluNDbslmoOSttCcqIuMX650rnm8mNZyrsa9o6uPkeLPq6Cq6fdvi5EZ6rLrRy8wdf7u/NDO58dkba5w= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715692688; c=relaxed/simple; bh=KpI7laXRagsoBm2/ecjcy0/NX4B2qwZNWyZ211Mng3o=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=lss1aaXz6nTXV6goDYTTAMlyM6flsgJCD4Z06s15zkrZvwsmg+IYIP4qYzijqkzI8Us3q6BfROsylv0OkVOtKyJ7+H3nHWAnaDhwfIWYPgcy9FbVHp6+DRf2GCAS5KcBpz8K5edYEbXbhRnC8aU4TnWsPsq/oGHIsBg1tZ/e/70= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=YdxhE4FZ; arc=none smtp.client-ip=209.85.214.171 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="YdxhE4FZ" Received: by mail-pl1-f171.google.com with SMTP id d9443c01a7336-1edf506b216so37987155ad.2; Tue, 14 May 2024 06:18:06 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1715692686; x=1716297486; 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=ditYxIjvIwYfZMbjRiDiDMz0imCc7L1hhHyY6o5PGUE=; b=YdxhE4FZ4n6K2vmgBFKQs15BSxcVQ+3md5t9T1UhsVsR1eBsDXb8onZdjJYdKWwS+A 9c0nRLpLErfzFbm7LzkEH17Azgq73IWCLQTWkxV2hYYWDqld06MM0YXB1wlS5VHVNLmL gbN1gQkklIZYzik7E+cyecAZrnmgkQummm18Xn9a5q9Bs1v0WgTuwNYxkRO0LCeQ8C7x fyuR1MFipaqo+n3Zb6tYv7+cLcySjfmhKoGnPTIaVnNJq8KFx0CyYV/pmhLGp5QGRCXq RKY2Y9LCbrjuOXv6IB/PEyxiobYUnyWRl2KtwkTkzQFU25DDsE/7t3Z3RB1bEVkSzbdQ XOBQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1715692686; x=1716297486; 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=ditYxIjvIwYfZMbjRiDiDMz0imCc7L1hhHyY6o5PGUE=; b=jK87r24ArtANb1bPXFDnGT9k/hpipK3TjCllFVy8I+O34vJ647dxzXpgjS3xoQYtyu b8IejjFacVPNpWS2DozYKdvtDqav5GkcOSEoOxyj2W4CXuIAngEYrY31FK8L5zSltC0P RM4z+Dkv5Zy6DeEIJ/43bWR2MREMUm1auGKFz/4CW1G2Be4IXQPUBKEFpKM3wwQ/a9Ui fKNBwCVox05z7hYu2iXR8ql4XLyJneAbNsZcmk4PxlGRpyJUt9vOixrwN6oollsBVc9Y S7R3Ug39/SdJ7y2uhID3y/NnwnajjK/TICyJswnhZ2Th7PQpiNycwdPVWYIJ+aXgIl8Q 0JYQ== X-Forwarded-Encrypted: i=1; AJvYcCU7v7oyHuJihus916+kCxPbmC0+N6wwqnecirpX7XjxvBPwp3/7F77bMiP0yDUXJ2cnEYkMXf4jBlA9xh+G1w8ZL9MJSzwqFq7EiH8xJu5Nym2VIHa0Q8FEPQ5wlY4le8iJuoVBylDThbg1+6llfYa5izmZySvcQkfRkeIaiGqBqYv48931RXlbxznc X-Gm-Message-State: AOJu0YzUUcVRiBFMJD1Aw0hp3/5Eby83Bng3oo+RDZknsPVo0dCWB4z4 cnpik4VXjmIKmW2mXToTqTaHNQcXu/EIRZ6qU6eYGjRRNJU4c65a X-Google-Smtp-Source: AGHT+IE0gyW+xtC1hhGIfGBO+8KKiijIyQzhfltWcILbAgD7XVPpxiQ9tkxyxF35/PoF78LZmD0Y+w== X-Received: by 2002:a17:903:2cf:b0:1e4:436e:801b with SMTP id d9443c01a7336-1ef441c113dmr163356935ad.67.1715692685848; Tue, 14 May 2024 06:18:05 -0700 (PDT) Received: from wedsonaf-dev.. ([50.204.89.32]) by smtp.googlemail.com with ESMTPSA id d9443c01a7336-1ef0b9d18a4sm97277335ad.56.2024.05.14.06.18.05 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 14 May 2024 06:18:05 -0700 (PDT) From: Wedson Almeida Filho To: Alexander Viro , Christian Brauner , Matthew Wilcox , Dave Chinner Cc: Kent Overstreet , Greg Kroah-Hartman , linux-fsdevel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, Wedson Almeida Filho Subject: [RFC PATCH v2 27/30] rust: fs: add `iomap` module Date: Tue, 14 May 2024 10:17:08 -0300 Message-Id: <20240514131711.379322-28-wedsonaf@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240514131711.379322-1-wedsonaf@gmail.com> References: <20240514131711.379322-1-wedsonaf@gmail.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Wedson Almeida Filho Allow file systems to implement their address space operations via iomap, which delegates a lot of the complexity to common code. Signed-off-by: Wedson Almeida Filho --- rust/bindings/bindings_helper.h | 1 + rust/kernel/fs.rs | 1 + rust/kernel/fs/iomap.rs | 281 ++++++++++++++++++++++++++++++++ 3 files changed, 283 insertions(+) create mode 100644 rust/kernel/fs/iomap.rs diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index f4c7c3951dbe..629fce394dbe 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include diff --git a/rust/kernel/fs.rs b/rust/kernel/fs.rs index 4d90b23735bc..7a1c4884c370 100644 --- a/rust/kernel/fs.rs +++ b/rust/kernel/fs.rs @@ -19,6 +19,7 @@ pub mod dentry; pub mod file; pub mod inode; +pub mod iomap; pub mod sb; /// The offset of a file in a file system. diff --git a/rust/kernel/fs/iomap.rs b/rust/kernel/fs/iomap.rs new file mode 100644 index 000000000000..e48e200e555e --- /dev/null +++ b/rust/kernel/fs/iomap.rs @@ -0,0 +1,281 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! File system io maps. +//! +//! This module allows Rust code to use iomaps to implement filesystems. +//! +//! C headers: [`include/linux/iomap.h`](srctree/include/linux/iomap.h) + +use super::{address_space, FileSystem, INode, Offset}; +use crate::error::{from_result, Result}; +use crate::{bindings, block}; +use core::marker::PhantomData; + +/// The type of mapping. +/// +/// This is used in [`Map`]. +#[repr(u16)] +pub enum Type { + /// No blocks allocated, need allocation. + Hole = bindings::IOMAP_HOLE as u16, + + /// Delayed allocation blocks. + DelAlloc = bindings::IOMAP_DELALLOC as u16, + + /// Blocks allocated at the given address. + Mapped = bindings::IOMAP_MAPPED as u16, + + /// Blocks allocated at the given address in unwritten state. + Unwritten = bindings::IOMAP_UNWRITTEN as u16, + + /// Data inline in the inode. + Inline = bindings::IOMAP_INLINE as u16, +} + +/// Flags usable in [`Map`], in [`Map::set_flags`] in particular. +pub mod map_flags { + /// Indicates that the blocks have been newly allocated and need zeroing for areas that no data + /// is copied to. + pub const NEW: u16 = bindings::IOMAP_F_NEW as u16; + + /// Indicates that the inode has uncommitted metadata needed to access written data and + /// requires fdatasync to commit them to persistent storage. This needs to take into account + /// metadata changes that *may* be made at IO completion, such as file size updates from direct + /// IO. + pub const DIRTY: u16 = bindings::IOMAP_F_DIRTY as u16; + + /// Indicates that the blocks are shared, and will need to be unshared as part a write. + pub const SHARED: u16 = bindings::IOMAP_F_SHARED as u16; + + /// Indicates that the iomap contains the merge of multiple block mappings. + pub const MERGED: u16 = bindings::IOMAP_F_MERGED as u16; + + /// Indicates that the file system requires the use of buffer heads for this mapping. + pub const BUFFER_HEAD: u16 = bindings::IOMAP_F_BUFFER_HEAD as u16; + + /// Indicates that the iomap is for an extended attribute extent rather than a file data + /// extent. + pub const XATTR: u16 = bindings::IOMAP_F_XATTR as u16; + + /// Indicates to the iomap_end method that the file size has changed as the result of this + /// write operation. + pub const SIZE_CHANGED: u16 = bindings::IOMAP_F_SIZE_CHANGED as u16; + + /// Indicates that the iomap is not valid any longer and the file range it covers needs to be + /// remapped by the high level before the operation can proceed. + pub const STALE: u16 = bindings::IOMAP_F_STALE as u16; + + /// Flags from 0x1000 up are for file system specific usage. + pub const PRIVATE: u16 = bindings::IOMAP_F_PRIVATE as u16; +} + +/// A map from address space to block device. +#[repr(transparent)] +pub struct Map<'a>(pub bindings::iomap, PhantomData<&'a ()>); + +impl<'a> Map<'a> { + /// Sets the map type. + pub fn set_type(&mut self, t: Type) -> &mut Self { + self.0.type_ = t as u16; + self + } + + /// Sets the file offset, in bytes. + pub fn set_offset(&mut self, v: Offset) -> &mut Self { + self.0.offset = v; + self + } + + /// Sets the length of the mapping, in bytes. + pub fn set_length(&mut self, len: u64) -> &mut Self { + self.0.length = len; + self + } + + /// Sets the mapping flags. + /// + /// Values come from the [`map_flags`] module. + pub fn set_flags(&mut self, flags: u16) -> &mut Self { + self.0.flags = flags; + self + } + + /// Sets the disk offset of the mapping, in bytes. + pub fn set_addr(&mut self, addr: u64) -> &mut Self { + self.0.addr = addr; + self + } + + /// Sets the block device of the mapping. + pub fn set_bdev(&mut self, bdev: Option<&'a block::Device>) -> &mut Self { + self.0.bdev = if let Some(b) = bdev { + b.0.get() + } else { + core::ptr::null_mut() + }; + self + } +} + +/// Flags passed to [`Operations::begin`] and [`Operations::end`]. +pub mod flags { + /// Writing, must allocate block. + pub const WRITE: u32 = bindings::IOMAP_WRITE; + + /// Zeroing operation, may skip holes. + pub const ZERO: u32 = bindings::IOMAP_ZERO; + + /// Report extent status, e.g. FIEMAP. + pub const REPORT: u32 = bindings::IOMAP_REPORT; + + /// Mapping for page fault. + pub const FAULT: u32 = bindings::IOMAP_FAULT; + + /// Direct I/O. + pub const DIRECT: u32 = bindings::IOMAP_DIRECT; + + /// Do not block. + pub const NOWAIT: u32 = bindings::IOMAP_NOWAIT; + + /// Only pure overwrites allowed. + pub const OVERWRITE_ONLY: u32 = bindings::IOMAP_OVERWRITE_ONLY; + + /// `unshare_file_range`. + pub const UNSHARE: u32 = bindings::IOMAP_UNSHARE; + + /// DAX mapping. + pub const DAX: u32 = bindings::IOMAP_DAX; +} + +/// Operations implemented by iomap users. +pub trait Operations { + /// File system that these operations are compatible with. + type FileSystem: FileSystem + ?Sized; + + /// Returns the existing mapping at `pos`, or reserves space starting at `pos` for up to + /// `length`, as long as it can be done as a single mapping. The actual length is returned in + /// `iomap`. + /// + /// The values of `flags` come from the [`flags`] module. + fn begin<'a>( + inode: &'a INode, + pos: Offset, + length: Offset, + flags: u32, + map: &mut Map<'a>, + srcmap: &mut Map<'a>, + ) -> Result; + + /// Commits and/or unreserves space previously allocated using [`Operations::begin`]. `writte`n + /// indicates the length of the successful write operation which needs to be commited, while + /// the rest needs to be unreserved. `written` might be zero if no data was written. + /// + /// The values of `flags` come from the [`flags`] module. + fn end<'a>( + _inode: &'a INode, + _pos: Offset, + _length: Offset, + _written: isize, + _flags: u32, + _map: &Map<'a>, + ) -> Result { + Ok(()) + } +} + +/// Returns address space oprerations backed by iomaps. +pub const fn ro_aops() -> address_space::Ops { + struct Table(PhantomData); + impl Table { + const MAP_TABLE: bindings::iomap_ops = bindings::iomap_ops { + iomap_begin: Some(Self::iomap_begin_callback), + iomap_end: Some(Self::iomap_end_callback), + }; + + extern "C" fn iomap_begin_callback( + inode_ptr: *mut bindings::inode, + pos: Offset, + length: Offset, + flags: u32, + map: *mut bindings::iomap, + srcmap: *mut bindings::iomap, + ) -> i32 { + from_result(|| { + // SAFETY: The C API guarantees that `inode_ptr` is a valid inode. + let inode = unsafe { INode::from_raw(inode_ptr) }; + T::begin( + inode, + pos, + length, + flags, + // SAFETY: The C API guarantees that `map` is valid for write. + unsafe { &mut *map.cast::>() }, + // SAFETY: The C API guarantees that `srcmap` is valid for write. + unsafe { &mut *srcmap.cast::>() }, + )?; + Ok(0) + }) + } + + extern "C" fn iomap_end_callback( + inode_ptr: *mut bindings::inode, + pos: Offset, + length: Offset, + written: isize, + flags: u32, + map: *mut bindings::iomap, + ) -> i32 { + from_result(|| { + // SAFETY: The C API guarantees that `inode_ptr` is a valid inode. + let inode = unsafe { INode::from_raw(inode_ptr) }; + // SAFETY: The C API guarantees that `map` is valid for read. + T::end(inode, pos, length, written, flags, unsafe { + &*map.cast::>() + })?; + Ok(0) + }) + } + + const TABLE: bindings::address_space_operations = bindings::address_space_operations { + writepage: None, + read_folio: Some(Self::read_folio_callback), + writepages: None, + dirty_folio: None, + readahead: Some(Self::readahead_callback), + write_begin: None, + write_end: None, + bmap: Some(Self::bmap_callback), + invalidate_folio: Some(bindings::iomap_invalidate_folio), + release_folio: Some(bindings::iomap_release_folio), + free_folio: None, + direct_IO: Some(bindings::noop_direct_IO), + migrate_folio: None, + launder_folio: None, + is_partially_uptodate: None, + is_dirty_writeback: None, + error_remove_folio: 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 { + // SAFETY: `folio` is just forwarded from C and `Self::MAP_TABLE` is always valid. + unsafe { bindings::iomap_read_folio(folio, &Self::MAP_TABLE) } + } + + extern "C" fn readahead_callback(rac: *mut bindings::readahead_control) { + // SAFETY: `rac` is just forwarded from C and `Self::MAP_TABLE` is always valid. + unsafe { bindings::iomap_readahead(rac, &Self::MAP_TABLE) } + } + + extern "C" fn bmap_callback(mapping: *mut bindings::address_space, block: u64) -> u64 { + // SAFETY: `mapping` is just forwarded from C and `Self::MAP_TABLE` is always valid. + unsafe { bindings::iomap_bmap(mapping, block, &Self::MAP_TABLE) } + } + } + address_space::Ops(&Table::::TABLE, PhantomData) +} From patchwork Tue May 14 13:17:09 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wedson Almeida Filho X-Patchwork-Id: 13664146 Received: from mail-pl1-f172.google.com (mail-pl1-f172.google.com [209.85.214.172]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 710C515E21C; Tue, 14 May 2024 13:18:07 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715692689; cv=none; b=WvAF/szPB8Bv5uSKFaU44lIHaA0+VBGIxD+IBNUNCoZGXXDtA1o40qtUHZp8hqNPnbGu5rFFRDrU9xVdtFK0BIGS+OgJiu9V1GhG/Gk3LoyceSOH3czsE4dbeJVSjp/dijN3KXnleLHa7QLIgz6rbJyE/8PdAqkgKVpUYCHtjs8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715692689; c=relaxed/simple; bh=6z1RI8g33lkqRGyE2YmVG8hRvjhcPCdneDUfYaUM/ZY=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=TqaJvK8tbrMnM3e2DABgej2ddcLuJWQksxoX1XSzH599NH57uB7VA9pnF7hZvhD1TmaqAvpEfXEk7UopXpUB6gc1/ruT9I/rozVxu8GIcwERtrMc3eQISuB/ox+uzvJIsJVXgFcLSTZQdNsfMdQe+NqcD5R6vgSozG+PfhlcSC8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=jNuQIRdW; arc=none smtp.client-ip=209.85.214.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="jNuQIRdW" Received: by mail-pl1-f172.google.com with SMTP id d9443c01a7336-1f082d92864so8599825ad.1; Tue, 14 May 2024 06:18:07 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1715692687; x=1716297487; 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=mzlX6MytMtV0hY1Vkeg8NRqNISRVbi1aOoZP9vK22EI=; b=jNuQIRdW2zlSs4TMpMFBIiGalf7H9y2cU1YEp1ApHKK6aeey+q+3tKQgvUBUXCnz22 z3qhTAPSUVbVMenGuDK2Owy9ojQ6iMWG/GJCDKGdo2kcqJV/G7f4tXhJ+8SkJ09tZmkv rLpT44/0h2IJbx5diVDnnbLG8yXi9tHw9mI/P4eO2W2/6N+sM0RSCjXT6L0qkj2axdgM zao6O9WI8MkrpdHAfEiMoj9+qLYPkWmgde9NoX4LM+SFDth+xDfgLWp0BcU/xx04+guP ZYGFSEdabhcgDWgT2EMXj1IE1ZfPZIf9Z9pUkSP08W3XPKBA8zYla6gz3K8ayGaSK/Om nRNQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1715692687; x=1716297487; 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=mzlX6MytMtV0hY1Vkeg8NRqNISRVbi1aOoZP9vK22EI=; b=qnRtjYVDTc5tAyISgWJr5UFd0XgL+R/5WB8ctXDzdlTPG5lRkIU12o2x4ptuY2MHeb gmTu46afsI/vziWs76fO2AEqj5D8/sYqAx7nVyyo7Nra82NwIZlND13NpG8bw7doBVjk 14LALtGKDvNaNkA+/jxNN79pZyarTDs17nf0q3PvDYh3VIULIbx3B/RZvsXd5DsoJKCX j2oHnzMapc2rK3Mt/wF4C0mABlXykVxIQWUhV/VfpXeSm7uFC1rPoNVfocbN2MfD5cTr 55fsz6QjBTXof5xn9zW2nBcilU7i4G/7Xnf6mKKZVGGh4JJ+7OoRp7Yno1xUuQlIz0a6 J0NA== X-Forwarded-Encrypted: i=1; AJvYcCUUqd1DAvs+GIgNoAL3Ou0FSDejdTggBx9P0lYxgX6o90w0ZxpoWOVWSEIJn3ukLAhpuqCN/Z4I5XOD+O7E3nnMvUqCxK1TtzwnwgI1qgK6nvogARQeU/6/FKsSDbey16FBamgfTcMAvHwG9rwXA3GBbalkh+INaU+OmBWAI1LGfTnU74+hIk14nleo X-Gm-Message-State: AOJu0YwZsz7nJrGqSeYmEvbdxYRc/ACp+eGTDq3eW0cO/ZembKniYkaS BpyKz1bEEsM7S6yq6678zEcFXXmHbsj6CeLUZ5hJ0VmKPMjG+Enw X-Google-Smtp-Source: AGHT+IFcS2xpKzPg2drbQuSK/tq8J0Pxz68uGmcCBBRMfNmdMiVNACt4X4HGf60/AnW8PrvGFTj4YQ== X-Received: by 2002:a17:903:11cf:b0:1e4:9d6f:593 with SMTP id d9443c01a7336-1ef43e27274mr148874305ad.36.1715692686828; Tue, 14 May 2024 06:18:06 -0700 (PDT) Received: from wedsonaf-dev.. ([50.204.89.32]) by smtp.googlemail.com with ESMTPSA id d9443c01a7336-1ef0b9d18a4sm97277335ad.56.2024.05.14.06.18.06 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 14 May 2024 06:18:06 -0700 (PDT) From: Wedson Almeida Filho To: Alexander Viro , Christian Brauner , Matthew Wilcox , Dave Chinner Cc: Kent Overstreet , Greg Kroah-Hartman , linux-fsdevel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, Wedson Almeida Filho , Wedson Almeida Filho Subject: [RFC PATCH v2 28/30] rust: fs: add memalloc_nofs support Date: Tue, 14 May 2024 10:17:09 -0300 Message-Id: <20240514131711.379322-29-wedsonaf@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240514131711.379322-1-wedsonaf@gmail.com> References: <20240514131711.379322-1-wedsonaf@gmail.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Wedson Almeida Filho When used in filesystems obviates the need for GFP_NOFS. Signed-off-by: Wedson Almeida Filho --- rust/helpers.c | 12 ++++++++++++ rust/kernel/fs.rs | 12 ++++++++++++ 2 files changed, 24 insertions(+) diff --git a/rust/helpers.c b/rust/helpers.c index edf12868962c..c26aa07cb20f 100644 --- a/rust/helpers.c +++ b/rust/helpers.c @@ -273,6 +273,18 @@ void *rust_helper_alloc_inode_sb(struct super_block *sb, } EXPORT_SYMBOL_GPL(rust_helper_alloc_inode_sb); +unsigned int rust_helper_memalloc_nofs_save(void) +{ + return memalloc_nofs_save(); +} +EXPORT_SYMBOL_GPL(rust_helper_memalloc_nofs_save); + +void rust_helper_memalloc_nofs_restore(unsigned int flags) +{ + memalloc_nofs_restore(flags); +} +EXPORT_SYMBOL_GPL(rust_helper_memalloc_nofs_restore); + 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 7a1c4884c370..b7a654546d23 100644 --- a/rust/kernel/fs.rs +++ b/rust/kernel/fs.rs @@ -417,6 +417,18 @@ impl Tables { } } +/// Calls `cb` in a nofs allocation context. +/// +/// That is, if an allocation happens within `cb`, it will have the `__GFP_FS` bit cleared. +pub fn memalloc_nofs(cb: impl FnOnce() -> T) -> T { + // SAFETY: Function is safe to be called from any context. + let flags = unsafe { bindings::memalloc_nofs_save() }; + let ret = cb(); + // SAFETY: Function is safe to be called from any context. + unsafe { bindings::memalloc_nofs_restore(flags) }; + ret +} + /// Kernel module that exposes a single file system implemented by `T`. #[pin_data] pub struct Module { From patchwork Tue May 14 13:17:10 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wedson Almeida Filho X-Patchwork-Id: 13664147 Received: from mail-pl1-f176.google.com (mail-pl1-f176.google.com [209.85.214.176]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A580115FA92; Tue, 14 May 2024 13:18:08 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.176 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715692691; cv=none; b=YwRGiYcC+l//Y8WuxzravuMJ+H7Fe5t2zgFqvaUYxpvvnxNL5kga2oBW5P27qcWCTNIIqT4+RB7d0uZrqXDCiKe7GJOK2AhFWNmyA5UgEQ5hkCZ7FU4a/yG03ZUIa5RJYIk1mP2wr8HJ03XdPsOwPofu1E6WEa3ACO+FWT4kGNc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715692691; c=relaxed/simple; bh=NkyBIPvyqKD3QaaIvYVVRWJ6C2c5hVFwRcCGK0ZYyF4=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=P+4/5N6XtrUGJCCVAHG5rF4xxF777tR0F/J0yIxfJ4he8r62ketJ0zar5gcTK+rjKPwKmQWQe3PZo4EK7osbbJHJZeX0yEYPjY9kaFGw/hMZBaia5Lcm8bFSH6FAR7e9+8xb4BngZUEmg1cIU6zYFXV1bW/CXbgiJdAA6wJRawo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=j/Rgps5E; arc=none smtp.client-ip=209.85.214.176 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="j/Rgps5E" Received: by mail-pl1-f176.google.com with SMTP id d9443c01a7336-1ed96772f92so44798045ad.0; Tue, 14 May 2024 06:18:08 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1715692688; x=1716297488; 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=AUAgZILvsJPk3lUOQX5V2zM1SwWQswShe/1otOCn5pk=; b=j/Rgps5EI9HJBZQSLpZEMg5Xy2KWJShM1Z4R5SFZuCYGC4t6NWf5mubWGFtcTRRIhu 6xdWm6HhrGPi274IFO5wAfc3uX5x1ILCUa+KU9gWXxx4sPDJts1RcuLZcrN/E1FaBIlc KuAFBBYkHl6e2gzJWVuDzhVHepELbnFKNpgxRz0KsNEfEHkAZDfrJcX8nm8B4AlrdiX+ fCFMEPSgmfpNAEcix03ghpz15pKv2zwx9XyWqvu1eRI8wHIHV899pg7xoCoxT/5SfZPU azoB2euhvwzqenQKHbeK2KAZIwYj0NMPoYXQfSgvbyZaFcqUaTsrYJ7nyt5At09vlgMD HiLg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1715692688; x=1716297488; 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=AUAgZILvsJPk3lUOQX5V2zM1SwWQswShe/1otOCn5pk=; b=hRGp3issiC7KJvMMwPRJYbAhpkvms96odjHeescHjBAERhW+Cxmuv1EuswJt0Zkh36 YFv9qLePx9yr3+14cBj1XMs1+Pf3qqWd7jLLn1Squq5PvwRvVaFMQcAw6NhtuAmImgvO ShMkRQ+RoXfhHc1OO8BzCq+E6yryUBugXlJ7L3xxJ/ygvkrEQdGTInRKefhUloqd5WOz UuENnrcHt2ZYRB08DEAe5xgRIMjoMMlaq4xTXDwhxOXylgQjcAdfx8M8waH68n8iuUF5 boOaa16p5rgiN56761ujKBBSP52nT0bQt4HUTwXFLpe7//jUH426Fmr8oMwODyI5ID3I bXpA== X-Forwarded-Encrypted: i=1; AJvYcCXTNWITev+7CBq9pbN+3LNahg5+miDe9gDdUmFitt/M/QBjwyIeyalhoQdEj7PX+fOQ3uqYqk9F/WvJIomWIL1GMqxfL9Y4UGi9QXK4n5Jz/9N3+VX0TZ4QWHNgkkAb9gCQzPEcEQ2iVytTfqFyJx3jMI+pMXt0SIjwej3ixNTTzp81FKb/foXeqQdn X-Gm-Message-State: AOJu0Yy/jhwIx/sAd0RnYZ4yQShwPB2mYmhrUwk6ZouIN2Pv0gG/nFXz d4WtbLKkcu1XWREmiviBD6/Zd1S7CgMaaEmYnYr0IQkLukJzNW5u X-Google-Smtp-Source: AGHT+IFpimOtA0H+30BM3dZuOxd//sP8WnqJBhG572jFOU9vsmOgSflGyA3bx+dH4Jc0ddDwLunjPw== X-Received: by 2002:a17:903:1ce:b0:1e3:d4eb:a0f2 with SMTP id d9443c01a7336-1ef4404956emr147449585ad.51.1715692687803; Tue, 14 May 2024 06:18:07 -0700 (PDT) Received: from wedsonaf-dev.. ([50.204.89.32]) by smtp.googlemail.com with ESMTPSA id d9443c01a7336-1ef0b9d18a4sm97277335ad.56.2024.05.14.06.18.07 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 14 May 2024 06:18:07 -0700 (PDT) From: Wedson Almeida Filho To: Alexander Viro , Christian Brauner , Matthew Wilcox , Dave Chinner Cc: Kent Overstreet , Greg Kroah-Hartman , linux-fsdevel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, Wedson Almeida Filho Subject: [RFC PATCH v2 29/30] tarfs: introduce tar fs Date: Tue, 14 May 2024 10:17:10 -0300 Message-Id: <20240514131711.379322-30-wedsonaf@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240514131711.379322-1-wedsonaf@gmail.com> References: <20240514131711.379322-1-wedsonaf@gmail.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 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 | 15 ++ fs/tarfs/Makefile | 8 + fs/tarfs/defs.rs | 80 ++++++ fs/tarfs/tar.rs | 394 ++++++++++++++++++++++++++++++ scripts/generate_rust_analyzer.py | 2 +- 7 files changed, 500 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 a46b0cbc4d8f..2cbd99d6784c 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -337,6 +337,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 6ecc9b0a53f2..d8bbda73e3a9 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..fd4f1ae0f83d --- /dev/null +++ b/fs/tarfs/Kconfig @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: GPL-2.0-only +# + +config TARFS_FS + tristate "TAR file system support" + depends on RUST && BLOCK + 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..a3f6e468e566 --- /dev/null +++ b/fs/tarfs/tar.rs @@ -0,0 +1,394 @@ +// 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::{ + self, address_space, dentry, dentry::DEntry, file, file::File, inode, inode::INode, + inode::Type, iomap, sb, sb::SuperBlock, Offset, Stat, +}; +use kernel::types::{ARef, Either, FromBytes, Locked}; +use kernel::{c_str, prelude::*, str::CString, user}; + +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: usize = 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, + mapper: inode::Mapper, +} + +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 mut 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 b = h.mapper.mapped_folio(offset.try_into()?)?; + let idata = Inode::from_bytes(&b, 0).ok_or(EIO)?; + + let mode = idata.mode.value(); + + // Ignore inodes that have unknown mode bits. + if (mode & !(fs::mode::S_IFMT | 0o777)) != 0 { + return Err(ENOENT); + } + + const DIR_FOPS: file::Ops = file::Ops::new::(); + const DIR_IOPS: inode::Ops = inode::Ops::new::(); + const FILE_AOPS: address_space::Ops = iomap::ro_aops::(); + + let size = idata.size.value(); + let doffset = idata.offset.value(); + let secs = u64::from(idata.lmtime.value()) | (u64::from(idata.hmtime & 0xf) << 32); + let ts = kernel::time::Timespec::new(secs, 0)?; + let typ = match mode & fs::mode::S_IFMT { + fs::mode::S_IFREG => { + inode + .set_fops(file::Ops::generic_ro_file()) + .set_aops(FILE_AOPS); + Type::Reg + } + fs::mode::S_IFDIR => { + inode.set_iops(DIR_IOPS).set_fops(DIR_FOPS); + Type::Dir + } + fs::mode::S_IFLNK => { + inode.set_iops(inode::Ops::simple_symlink_inode()); + Type::Lnk(Some(Self::get_link(sb, doffset, size)?)) + } + fs::mode::S_IFSOCK => Type::Sock, + fs::mode::S_IFIFO => Type::Fifo, + fs::mode::S_IFCHR => Type::Chr((doffset >> 32) as u32, doffset as u32), + fs::mode::S_IFBLK => Type::Blk((doffset >> 32) as u32, doffset as u32), + _ => return Err(ENOENT), + }; + inode.init(inode::Params { + typ, + mode: mode & 0o777, + size: size.try_into()?, + 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 { + let ret = + sb.data() + .mapper + .for_each_page(offset as Offset, name.len().try_into()?, |data| { + if data != &name[..data.len()] { + return Ok(Some(())); + } + name = &name[data.len()..]; + Ok(None) + })?; + Ok(ret.is_none()) + } + + fn read_name(sb: &SuperBlock, name: &mut [u8], offset: u64) -> Result { + let mut copy_to = 0; + sb.data() + .mapper + .for_each_page(offset as Offset, name.len().try_into()?, |data| { + name[copy_to..][..data.len()].copy_from_slice(data); + copy_to += data.len(); + Ok(None::<()>) + })?; + Ok(()) + } + + fn get_link(sb: &SuperBlock, offset: u64, len: u64) -> Result { + let name_len: usize = len.try_into()?; + let alloc_len = name_len.checked_add(1).ok_or(ENOMEM)?; + let mut name = Box::new_slice(alloc_len, b'\0', GFP_NOFS)?; + Self::read_name(sb, &mut name[..name_len], offset)?; + Ok(name.try_into()?) + } +} + +impl fs::FileSystem for TarFs { + type Data = Box; + type INodeData = INodeData; + const NAME: &'static CStr = c_str!("tar"); + const SUPER_TYPE: sb::Type = sb::Type::BlockDev; + + fn fill_super( + sb: &mut SuperBlock, + mapper: Option, + ) -> Result { + let Some(mapper) = mapper else { + return Err(EINVAL); + }; + + let scount = sb.sector_count(); + if scount < SECTORS_PER_BLOCK { + pr_err!("Block device is too small: sector count={scount}\n"); + return Err(ENXIO); + } + + if sb.min_blocksize(SECTOR_SIZE as i32) != SECTOR_SIZE as i32 { + pr_err!("Block size not supported\n"); + return Err(EIO); + } + + let tarfs = { + let offset = (scount - 1) * SECTOR_SIZE; + let mapped = mapper.mapped_folio(offset.try_into()?)?; + let hdr = Header::from_bytes(&mapped, 0).ok_or(EIO)?; + let inode_table_offset = hdr.inode_table_offset.value(); + let inode_count = hdr.inode_count.value(); + drop(mapped); + Box::new( + TarFs { + inode_table_offset, + inode_count, + data_size: scount.checked_mul(SECTOR_SIZE).ok_or(ERANGE)?, + mapper, + }, + GFP_KERNEL, + )? + }; + + // 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); + } + + sb.set_magic(TARFS_MAGIC); + Ok(tarfs) + } + + fn init_root(sb: &SuperBlock) -> Result> { + let inode = Self::iget(sb, 1)?; + dentry::Root::try_new(inode) + } + + fn read_xattr( + _: &DEntry, + 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(dentry: &DEntry) -> Result { + let data = dentry.super_block().data(); + Ok(Stat { + magic: TARFS_MAGIC, + namelen: isize::MAX, + bsize: TARFS_BSIZE as _, + blocks: data.inode_table_offset / TARFS_BSIZE, + files: data.inode_count, + }) + } +} + +impl iomap::Operations for TarFs { + type FileSystem = Self; + + fn begin<'a>( + inode: &'a INode, + pos: Offset, + length: Offset, + _flags: u32, + map: &mut iomap::Map<'a>, + _srcmap: &mut iomap::Map<'a>, + ) -> Result { + let size = (inode.size() + 511) & !511; + if pos >= size { + map.set_offset(pos) + .set_length(length.try_into()?) + .set_flags(iomap::map_flags::MERGED) + .set_type(iomap::Type::Hole); + return Ok(()); + } + + map.set_offset(pos) + .set_length(core::cmp::min(length, size - pos) as u64) + .set_flags(iomap::map_flags::MERGED) + .set_type(iomap::Type::Mapped) + .set_bdev(Some(inode.super_block().bdev())) + .set_addr(u64::try_from(pos)? + inode.data().offset); + + Ok(()) + } +} + +#[vtable] +impl inode::Operations for TarFs { + type FileSystem = Self; + + fn lookup( + parent: &Locked<&INode, inode::ReadSem>, + dentry: dentry::Unhashed<'_, Self>, + ) -> Result>>> { + let sb = parent.super_block(); + let name = dentry.name(); + + let inode = sb.data().mapper.for_each_page( + parent.data().offset.try_into()?, + parent.size(), + |data| { + for e in DirEntry::from_bytes_to_slice(data).ok_or(EIO)? { + if Self::name_eq(sb, name, e.name_offset.value())? { + return Ok(Some(Self::iget(sb, e.ino.value())?)); + } + } + Ok(None) + }, + )?; + + dentry.splice_alias(inode) + } +} + +#[vtable] +impl file::Operations for TarFs { + type FileSystem = Self; + + fn seek(file: &File, offset: Offset, whence: file::Whence) -> Result { + file::generic_seek(file, offset, whence) + } + + fn read(_: &File, _: &mut user::Writer, _: &mut Offset) -> Result { + Err(EISDIR) + } + + fn read_dir( + _file: &File, + inode: &Locked<&INode, inode::ReadSem>, + emitter: &mut file::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 sizeu = u64::try_from(inode.size())?; + if inode.data().offset.checked_add(sizeu).ok_or(EIO)? > sb.data().data_size { + return Err(EIO); + } + + sb.data().mapper.for_each_page( + inode.data().offset as i64 + pos, + inode.size() - pos, + |data| { + for e in DirEntry::from_bytes_to_slice(data).ok_or(EIO)? { + let name_len = usize::try_from(e.name_len.value())?; + if name_len > name.len() { + name.resize(name_len, 0, GFP_NOFS)?; + } + + 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(), + file::DirEntryType::try_from(u32::from(e.etype))?, + ) { + return Ok(Some(())); + } + } + Ok(None) + }, + )?; + + Ok(()) + } +} diff --git a/scripts/generate_rust_analyzer.py b/scripts/generate_rust_analyzer.py index f270c7b0cf34..6985b9e37429 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: From patchwork Tue May 14 13:17:11 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Wedson Almeida Filho X-Patchwork-Id: 13664148 Received: from mail-pl1-f176.google.com (mail-pl1-f176.google.com [209.85.214.176]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E2B1516D4D4; Tue, 14 May 2024 13:18:09 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.176 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715692692; cv=none; b=Bfprar03YFel2j8RDFwOG1DcQdzXYGxTvvlWhqFh/0vBqMgsDG7iyQLZSEYvtWcJYkUZfHLV2lPPgWYgF870zLQxALaDqMZ4kGXK20BJNJ7u7Cpw3dct6UoXJ7rZEfRe968s2SyfcjIhCKOytkJ3oiTKu2hIK0LY0cwroV1K8zA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1715692692; c=relaxed/simple; bh=p41Jp5EYVfGBDeVAOVl/zsa3dPK/Cb6MVZmuxA54sNc=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=eA/pR8tnEjctvuHaS2woMLWw0qSCWZ49a38YRvAKSnVZmkSBmoOVFHld6sVWU45DnCYHCEpbdwUJ9sNTpAMG2x34WdjNWsdcT+iZI9DPHQfa4Ry905/bpI3Sv7LEOYU/lrHbqKIy8PnLMa+p4twoUrF9Ohw7QvHqKwBLgbomeJ8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=PZDBNAZG; arc=none smtp.client-ip=209.85.214.176 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="PZDBNAZG" Received: by mail-pl1-f176.google.com with SMTP id d9443c01a7336-1ec69e3dbcfso43600605ad.0; Tue, 14 May 2024 06:18:09 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1715692689; x=1716297489; 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=Ac/6w8eF1/WlkpgOl3mXtqTlZQH+FEfp7jqPuIxAGeU=; b=PZDBNAZGGYe4fv1q4qayXccZK8OtiLu4EPRGjN7Ri7eCf6r/hIpUVBykMqz3dX8av0 Nvkp9B3HSaLqOo2Kzqt7CgSihHZHQDtpq1V56/+U9doiMw5DUW8Ij3uRsix+HYk3fMjd 8cOPEnWTOfi95HKrArNz+rHBaz6/gwD3v2l3WkSQyUpd7tnEMfN++QaVJNkNuvds5Rce zZ+kmaIQF8LiWE1L7kpqzlHpNaQWMvKivfcBLMzi0JZxjLXw2UZx3qUvhME6V2JMhzrU Ihku4vTMeXH9I7sD1qVMphHHYsVTgOCqtEVt17RB5voNXul3LRMpPkcwp4vUXiJxDdcT +DEw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1715692689; x=1716297489; 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=Ac/6w8eF1/WlkpgOl3mXtqTlZQH+FEfp7jqPuIxAGeU=; b=wYpghHm7pBNV7PUdjL3GnutAZvCzBWJ//os24e5ChTrW4HHRVsw2xXEDFQLKPyjTZ8 YodQtx9OKFMSX90lVAjUmkG11RIKXkxnxmIa7DnHoIAlNCmSy8r9I/sdNpia9VNGGXR1 AWc2ZTc12jPl8xLIlj0j0yxE65VIKWJfweBbr3DWGNGVbmJ56BcRDB8N0EyQK+7SQ6rZ VLcdIgqKMmprn5TY3p1iir1X1KSRw7+ENbXsTmWXNxvuT+ep7X+MBTQvvRSktR3GN/nY 2xz9HI/FNQanTbwgvBZOpuhoWPtMlgFpwT6AeC7GtfZnAjoQQZ1XBKDHNDfnAv4Cjb9C Vhyw== X-Forwarded-Encrypted: i=1; AJvYcCVVZgyC+QWAZT19OhI36ak2wyJJMosR262JhldzT4YsaeuhAMgoUCtF6eihMcSuga5iBj+pgXVy1XCspRrq5nu1x7Haz6TkRCl2XQaC2Wg0fmPaLw36Wa90KtC0YcDjuCSBqkMvMG0ti+V/03lrHvIaQ9Rs5esCmeG0/0XddLaFtaM4DNrc1fXubGJN X-Gm-Message-State: AOJu0YwDgud/rABgQ8AsBgPCkEkW0b/q6x4oQCdF/5wtKnKidmYvwkBN GrnfBILDkXEirceNjwSUyoWJVHo02sEYthoS637FB6o8kWZkm2I7 X-Google-Smtp-Source: AGHT+IFUEWOJAbO6+4ufK9Zg10BKKcqWXq5o8D/uKQjVC20XowtdZrymelxo6YOYB03vsfJ+FIXc0A== X-Received: by 2002:a17:903:234e:b0:1e3:cdd1:dd80 with SMTP id d9443c01a7336-1ef43c0cba2mr162831555ad.6.1715692688780; Tue, 14 May 2024 06:18:08 -0700 (PDT) Received: from wedsonaf-dev.. ([50.204.89.32]) by smtp.googlemail.com with ESMTPSA id d9443c01a7336-1ef0b9d18a4sm97277335ad.56.2024.05.14.06.18.07 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 14 May 2024 06:18:08 -0700 (PDT) From: Wedson Almeida Filho To: Alexander Viro , Christian Brauner , Matthew Wilcox , Dave Chinner Cc: Kent Overstreet , Greg Kroah-Hartman , linux-fsdevel@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, Wedson Almeida Filho Subject: [RFC PATCH v2 30/30] WIP: fs: ext2: add rust ro ext2 implementation Date: Tue, 14 May 2024 10:17:11 -0300 Message-Id: <20240514131711.379322-31-wedsonaf@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240514131711.379322-1-wedsonaf@gmail.com> References: <20240514131711.379322-1-wedsonaf@gmail.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: Wedson Almeida Filho Signed-off-by: Wedson Almeida Filho --- fs/Kconfig | 1 + fs/Makefile | 1 + fs/rust-ext2/Kconfig | 13 + fs/rust-ext2/Makefile | 8 + fs/rust-ext2/defs.rs | 173 +++++++++++++ fs/rust-ext2/ext2.rs | 551 ++++++++++++++++++++++++++++++++++++++++++ rust/kernel/lib.rs | 3 + 7 files changed, 750 insertions(+) create mode 100644 fs/rust-ext2/Kconfig create mode 100644 fs/rust-ext2/Makefile create mode 100644 fs/rust-ext2/defs.rs create mode 100644 fs/rust-ext2/ext2.rs diff --git a/fs/Kconfig b/fs/Kconfig index 2cbd99d6784c..cf0cac5c5b1e 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -338,6 +338,7 @@ source "fs/ufs/Kconfig" source "fs/erofs/Kconfig" source "fs/vboxsf/Kconfig" source "fs/tarfs/Kconfig" +source "fs/rust-ext2/Kconfig" endif # MISC_FILESYSTEMS diff --git a/fs/Makefile b/fs/Makefile index d8bbda73e3a9..c1a3007efc7d 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -130,3 +130,4 @@ obj-$(CONFIG_EROFS_FS) += erofs/ obj-$(CONFIG_VBOXSF_FS) += vboxsf/ obj-$(CONFIG_ZONEFS_FS) += zonefs/ obj-$(CONFIG_TARFS_FS) += tarfs/ +obj-$(CONFIG_RUST_EXT2_FS) += rust-ext2/ diff --git a/fs/rust-ext2/Kconfig b/fs/rust-ext2/Kconfig new file mode 100644 index 000000000000..976371655ca6 --- /dev/null +++ b/fs/rust-ext2/Kconfig @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0-only +# + +config RUST_EXT2_FS + tristate "Rust second extended fs support" + depends on RUST && BLOCK + help + Ext2 is a standard Linux file system for hard disks. + + To compile this file system support as a module, choose M here: the + module will be called rust_ext2. + + If unsure, say Y. diff --git a/fs/rust-ext2/Makefile b/fs/rust-ext2/Makefile new file mode 100644 index 000000000000..ac960b5f89d7 --- /dev/null +++ b/fs/rust-ext2/Makefile @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for the linux tarfs filesystem routines. +# + +obj-$(CONFIG_RUST_EXT2_FS) += rust_ext2.o + +rust_ext2-y := ext2.o diff --git a/fs/rust-ext2/defs.rs b/fs/rust-ext2/defs.rs new file mode 100644 index 000000000000..5f84852b4961 --- /dev/null +++ b/fs/rust-ext2/defs.rs @@ -0,0 +1,173 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Definitions of tarfs structures. + +use kernel::types::LE; + +pub(crate) const EXT2_SUPER_MAGIC: u16 = 0xEF53; + +pub(crate) const EXT2_MAX_BLOCK_LOG_SIZE: u32 = 16; + +pub(crate) const EXT2_GOOD_OLD_REV: u32 = 0; /* The good old (original) format */ +pub(crate) const EXT2_DYNAMIC_REV: u32 = 1; /* V2 format w/ dynamic inode sizes */ + +pub(crate) const EXT2_GOOD_OLD_INODE_SIZE: u16 = 128; + +pub(crate) const EXT2_ROOT_INO: u32 = 2; /* Root inode */ + +/* First non-reserved inode for old ext2 filesystems. */ +pub(crate) const EXT2_GOOD_OLD_FIRST_INO: u32 = 11; + +pub(crate) const EXT2_FEATURE_INCOMPAT_FILETYPE: u32 = 0x0002; + +/* + * Constants relative to the data blocks + */ +pub(crate) const EXT2_NDIR_BLOCKS: usize = 12; +pub(crate) const EXT2_IND_BLOCK: usize = EXT2_NDIR_BLOCKS; +pub(crate) const EXT2_DIND_BLOCK: usize = EXT2_IND_BLOCK + 1; +pub(crate) const EXT2_TIND_BLOCK: usize = EXT2_DIND_BLOCK + 1; +pub(crate) const EXT2_N_BLOCKS: usize = EXT2_TIND_BLOCK + 1; + +kernel::derive_readable_from_bytes! { + #[repr(C)] + pub(crate) struct Super { + pub(crate) inodes_count: LE, + pub(crate) blocks_count: LE, + pub(crate) r_blocks_count: LE, + pub(crate) free_blocks_count: LE, /* Free blocks count */ + pub(crate) free_inodes_count: LE, /* Free inodes count */ + pub(crate) first_data_block: LE, /* First Data Block */ + pub(crate) log_block_size: LE, /* Block size */ + pub(crate) log_frag_size: LE, /* Fragment size */ + pub(crate) blocks_per_group: LE, /* # Blocks per group */ + pub(crate) frags_per_group: LE, /* # Fragments per group */ + pub(crate) inodes_per_group: LE, /* # Inodes per group */ + pub(crate) mtime: LE, /* Mount time */ + pub(crate) wtime: LE, /* Write time */ + pub(crate) mnt_count: LE, /* Mount count */ + pub(crate) max_mnt_count: LE, /* Maximal mount count */ + pub(crate) magic: LE, /* Magic signature */ + pub(crate) state: LE, /* File system state */ + pub(crate) errors: LE, /* Behaviour when detecting errors */ + pub(crate) minor_rev_level: LE, /* minor revision level */ + pub(crate) lastcheck: LE, /* time of last check */ + pub(crate) checkinterval: LE, /* max. time between checks */ + pub(crate) creator_os: LE, /* OS */ + pub(crate) rev_level: LE, /* Revision level */ + pub(crate) def_resuid: LE, /* Default uid for reserved blocks */ + pub(crate) def_resgid: LE, /* Default gid for reserved blocks */ + /* + * These fields are for EXT2_DYNAMIC_REV superblocks only. + * + * Note: the difference between the compatible feature set and + * the incompatible feature set is that if there is a bit set + * in the incompatible feature set that the kernel doesn't + * know about, it should refuse to mount the filesystem. + * + * e2fsck's requirements are more strict; if it doesn't know + * about a feature in either the compatible or incompatible + * feature set, it must abort and not try to meddle with + * things it doesn't understand... + */ + pub(crate) first_ino: LE, /* First non-reserved inode */ + pub(crate) inode_size: LE, /* size of inode structure */ + pub(crate) block_group_nr: LE, /* block group # of this superblock */ + pub(crate) feature_compat: LE, /* compatible feature set */ + pub(crate) feature_incompat: LE, /* incompatible feature set */ + pub(crate) feature_ro_compat: LE, /* readonly-compatible feature set */ + pub(crate) uuid: [u8; 16], /* 128-bit uuid for volume */ + pub(crate) volume_name: [u8; 16], /* volume name */ + pub(crate) last_mounted: [u8; 64], /* directory where last mounted */ + pub(crate) algorithm_usage_bitmap: LE, /* For compression */ + /* + * Performance hints. Directory preallocation should only + * happen if the EXT2_COMPAT_PREALLOC flag is on. + */ + pub(crate) prealloc_blocks: u8, /* Nr of blocks to try to preallocate*/ + pub(crate) prealloc_dir_blocks: u8, /* Nr to preallocate for dirs */ + padding1: u16, + /* + * Journaling support valid if EXT3_FEATURE_COMPAT_HAS_JOURNAL set. + */ + pub(crate) journal_uuid: [u8; 16], /* uuid of journal superblock */ + pub(crate) journal_inum: u32, /* inode number of journal file */ + pub(crate) journal_dev: u32, /* device number of journal file */ + pub(crate) last_orphan: u32, /* start of list of inodes to delete */ + pub(crate) hash_seed: [u32; 4], /* HTREE hash seed */ + pub(crate) def_hash_version: u8, /* Default hash version to use */ + pub(crate) reserved_char_pad: u8, + pub(crate) reserved_word_pad: u16, + pub(crate) default_mount_opts: LE, + pub(crate) first_meta_bg: LE, /* First metablock block group */ + reserved: [u32; 190], /* Padding to the end of the block */ + } + + #[repr(C)] + #[derive(Clone, Copy)] + pub(crate) struct Group { + /// Blocks bitmap block. + pub block_bitmap: LE, + + /// Inodes bitmap block. + pub inode_bitmap: LE, + + /// Inodes table block. + pub inode_table: LE, + + /// Number of free blocks. + pub free_blocks_count: LE, + + /// Number of free inodes. + pub free_inodes_count: LE, + + /// Number of directories. + pub used_dirs_count: LE, + + pad: LE, + reserved: [u32; 3], + } + + #[repr(C)] + pub(crate) struct INode { + pub mode: LE, /* File mode */ + pub uid: LE, /* Low 16 bits of Owner Uid */ + pub size: LE, /* Size in bytes */ + pub atime: LE, /* Access time */ + pub ctime: LE, /* Creation time */ + pub mtime: LE, /* Modification time */ + pub dtime: LE, /* Deletion Time */ + pub gid: LE, /* Low 16 bits of Group Id */ + pub links_count: LE, /* Links count */ + pub blocks: LE, /* Blocks count */ + pub flags: LE, /* File flags */ + pub reserved1: LE, + pub block: [LE; EXT2_N_BLOCKS],/* Pointers to blocks */ + pub generation: LE, /* File version (for NFS) */ + pub file_acl: LE, /* File ACL */ + pub dir_acl: LE, /* Directory ACL */ + pub faddr: LE, /* Fragment address */ + pub frag: u8, /* Fragment number */ + pub fsize: u8, /* Fragment size */ + pub pad1: LE, + pub uid_high: LE, + pub gid_high: LE, + pub reserved2: LE, + } + + #[repr(C)] + pub(crate) struct DirEntry { + pub(crate) inode: LE, /* Inode number */ + pub(crate) rec_len: LE, /* Directory entry length */ + pub(crate) name_len: u8, /* Name length */ + pub(crate) file_type: u8, /* Only if the "filetype" feature flag is set. */ + } +} + +pub(crate) const FT_REG_FILE: u8 = 1; +pub(crate) const FT_DIR: u8 = 2; +pub(crate) const FT_CHRDEV: u8 = 3; +pub(crate) const FT_BLKDEV: u8 = 4; +pub(crate) const FT_FIFO: u8 = 5; +pub(crate) const FT_SOCK: u8 = 6; +pub(crate) const FT_SYMLINK: u8 = 7; diff --git a/fs/rust-ext2/ext2.rs b/fs/rust-ext2/ext2.rs new file mode 100644 index 000000000000..2d6b1e7ca156 --- /dev/null +++ b/fs/rust-ext2/ext2.rs @@ -0,0 +1,551 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Ext2 file system. + +use alloc::vec::Vec; +use core::mem::size_of; +use defs::*; +use kernel::fs::{ + self, address_space, dentry, dentry::DEntry, file, file::File, inode, inode::INode, iomap, sb, + sb::SuperBlock, Offset, +}; +use kernel::types::{ARef, Either, FromBytes, Locked, LE}; +use kernel::{block, c_str, prelude::*, str::CString, time::Timespec, user, PAGE_SIZE}; + +pub mod defs; + +kernel::module_fs! { + type: Ext2Fs, + name: "ext2", + author: "Wedson Almeida Filho ", + description: "ext2 file system", + license: "GPL", +} + +const SB_OFFSET: Offset = 1024; + +struct INodeData { + data_blocks: [u32; defs::EXT2_N_BLOCKS], +} + +struct Ext2Fs { + mapper: inode::Mapper, + block_size: u32, + has_file_type: bool, + _block_size_bits: u32, + inodes_per_block: u32, + inodes_per_group: u32, + inode_count: u32, + inode_size: u16, + first_ino: u32, + group: Vec, +} + +impl Ext2Fs { + fn iget(sb: &SuperBlock, ino: u32) -> Result>> { + let s = sb.data(); + if (ino != EXT2_ROOT_INO && ino < s.first_ino) || ino > s.inode_count { + return Err(ENOENT); + } + let group = ((ino - 1) / s.inodes_per_group) as usize; + let offset = (ino - 1) % s.inodes_per_group; + + if group >= s.group.len() { + return Err(ENOENT); + } + + // Create an inode or find an existing (cached) one. + let mut inode = match sb.get_or_create_inode(ino.into())? { + Either::Left(existing) => return Ok(existing), + Either::Right(new) => new, + }; + + let inodes_block = Offset::from(s.group[group].inode_table.value()); + let inode_block = inodes_block + Offset::from(offset / s.inodes_per_block); + let offset = (offset % s.inodes_per_block) as usize; + let b = sb + .data() + .mapper + .mapped_folio(inode_block * Offset::from(s.block_size))?; + let idata = defs::INode::from_bytes(&b, offset * s.inode_size as usize).ok_or(EIO)?; + let mode = idata.mode.value(); + + if idata.links_count.value() == 0 && (mode == 0 || idata.dtime.value() != 0) { + return Err(ESTALE); + } + + const DIR_FOPS: file::Ops = file::Ops::new::(); + const DIR_IOPS: inode::Ops = inode::Ops::new::(); + const FILE_AOPS: address_space::Ops = iomap::ro_aops::(); + + let mut size = idata.size.value().into(); + let typ = match mode & fs::mode::S_IFMT { + fs::mode::S_IFREG => { + size |= Offset::from(idata.dir_acl.value()) + .checked_shl(32) + .ok_or(EUCLEAN)?; + inode + .set_aops(FILE_AOPS) + .set_fops(file::Ops::generic_ro_file()); + inode::Type::Reg + } + fs::mode::S_IFDIR => { + inode + .set_iops(DIR_IOPS) + .set_fops(DIR_FOPS) + .set_aops(FILE_AOPS); + inode::Type::Dir + } + fs::mode::S_IFLNK => { + if idata.blocks.value() == 0 { + const OFFSET: usize = core::mem::offset_of!(defs::INode, block); + let name = &b[offset * usize::from(s.inode_size) + OFFSET..]; + let name_len = size as usize; + if name_len > name.len() || name_len == 0 { + return Err(EIO); + } + inode.set_iops(inode::Ops::simple_symlink_inode()); + inode::Type::Lnk(Some(CString::try_from(&name[..name_len])?)) + } else { + inode + .set_aops(FILE_AOPS) + .set_iops(inode::Ops::page_symlink_inode()); + inode::Type::Lnk(None) + } + } + fs::mode::S_IFSOCK => inode::Type::Sock, + fs::mode::S_IFIFO => inode::Type::Fifo, + fs::mode::S_IFCHR => { + let (major, minor) = decode_dev(&idata.block); + inode::Type::Chr(major, minor) + } + fs::mode::S_IFBLK => { + let (major, minor) = decode_dev(&idata.block); + inode::Type::Blk(major, minor) + } + _ => return Err(ENOENT), + }; + inode.init(inode::Params { + typ, + mode: mode & 0o777, + size, + blocks: idata.blocks.value().into(), + nlink: idata.links_count.value().into(), + uid: u32::from(idata.uid.value()) | u32::from(idata.uid_high.value()) << 16, + gid: u32::from(idata.gid.value()) | u32::from(idata.gid_high.value()) << 16, + ctime: Timespec::new(idata.ctime.value().into(), 0)?, + mtime: Timespec::new(idata.mtime.value().into(), 0)?, + atime: Timespec::new(idata.atime.value().into(), 0)?, + value: INodeData { + data_blocks: core::array::from_fn(|i| idata.block[i].value()), + }, + }) + } + + fn offsets<'a>(&self, mut block: u64, out: &'a mut [u32]) -> Option<&'a [u32]> { + let ptrs = u64::from(self.block_size / size_of::() as u32); + let ptr_mask = ptrs - 1; + let ptr_bits = ptrs.trailing_zeros(); + + if block < EXT2_NDIR_BLOCKS as u64 { + out[0] = block as u32; + return Some(&out[..1]); + } + + block -= EXT2_NDIR_BLOCKS as u64; + if block < ptrs { + out[0] = EXT2_IND_BLOCK as u32; + out[1] = block as u32; + return Some(&out[..2]); + } + + block -= ptrs; + if block < (1 << (2 * ptr_bits)) { + out[0] = EXT2_DIND_BLOCK as u32; + out[1] = (block >> ptr_bits) as u32; + out[2] = (block & ptr_mask) as u32; + return Some(&out[..3]); + } + + block -= ptrs * ptrs; + if block < ptrs * ptrs * ptrs { + out[0] = EXT2_TIND_BLOCK as u32; + out[1] = (block >> (2 * ptr_bits)) as u32; + out[2] = ((block >> ptr_bits) & ptr_mask) as u32; + out[3] = (block & ptr_mask) as u32; + return Some(&out[..4]); + } + + None + } + + fn offset_to_block(inode: &INode, block: Offset) -> Result { + let s = inode.super_block().data(); + let mut indices = [0u32; 4]; + let boffsets = s.offsets(block as u64, &mut indices).ok_or(EIO)?; + let mut boffset = inode.data().data_blocks[boffsets[0] as usize]; + let mapper = &s.mapper; + for i in &boffsets[1..] { + let b = mapper.mapped_folio(Offset::from(boffset) * Offset::from(s.block_size))?; + let table = LE::::from_bytes_to_slice(&b).ok_or(EIO)?; + boffset = table[*i as usize].value(); + } + Ok(boffset.into()) + } + + fn check_descriptors(s: &Super, groups: &[Group]) -> Result { + for (i, g) in groups.iter().enumerate() { + let first = i as u32 * s.blocks_per_group.value() + s.first_data_block.value(); + let last = if i == groups.len() - 1 { + s.blocks_count.value() + } else { + first + s.blocks_per_group.value() - 1 + }; + + if g.block_bitmap.value() < first || g.block_bitmap.value() > last { + pr_err!( + "Block bitmap for group {i} no in group (block {})\n", + g.block_bitmap.value() + ); + return Err(EINVAL); + } + + if g.inode_bitmap.value() < first || g.inode_bitmap.value() > last { + pr_err!( + "Inode bitmap for group {i} no in group (block {})\n", + g.inode_bitmap.value() + ); + return Err(EINVAL); + } + + if g.inode_table.value() < first || g.inode_table.value() > last { + pr_err!( + "Inode table for group {i} no in group (block {})\n", + g.inode_table.value() + ); + return Err(EINVAL); + } + } + Ok(()) + } +} + +impl fs::FileSystem for Ext2Fs { + type Data = Box; + type INodeData = INodeData; + const NAME: &'static CStr = c_str!("rust-ext2"); + const SUPER_TYPE: sb::Type = sb::Type::BlockDev; + + fn fill_super( + sb: &mut SuperBlock, + mapper: Option, + ) -> Result { + let Some(mapper) = mapper else { + return Err(EINVAL); + }; + + if sb.min_blocksize(PAGE_SIZE as i32) == 0 { + pr_err!("Unable to set block size\n"); + return Err(EINVAL); + } + + // Map the super block and check the magic number. + let mapped = mapper.mapped_folio(SB_OFFSET)?; + let s = Super::from_bytes(&mapped, 0).ok_or(EIO)?; + + if s.magic.value() != EXT2_SUPER_MAGIC { + return Err(EINVAL); + } + + // Check for unsupported flags. + let mut has_file_type = false; + if s.rev_level.value() >= EXT2_DYNAMIC_REV { + let features = s.feature_incompat.value(); + if features & !EXT2_FEATURE_INCOMPAT_FILETYPE != 0 { + pr_err!("Unsupported incompatible feature: {:x}\n", features); + return Err(EINVAL); + } + + has_file_type = features & EXT2_FEATURE_INCOMPAT_FILETYPE != 0; + + let features = s.feature_ro_compat.value(); + if !sb.rdonly() && features != 0 { + pr_err!("Unsupported rw incompatible feature: {:x}\n", features); + return Err(EINVAL); + } + } + + // Set the block size. + let block_size_bits = s.log_block_size.value(); + if block_size_bits > EXT2_MAX_BLOCK_LOG_SIZE - 10 { + pr_err!("Invalid log block size: {}\n", block_size_bits); + return Err(EINVAL); + } + + let block_size = 1024u32 << block_size_bits; + if sb.min_blocksize(block_size as i32) != block_size as i32 { + pr_err!("Bad block size: {}\n", block_size); + return Err(ENXIO); + } + + // Get the first inode and the inode size. + let (inode_size, first_ino) = if s.rev_level.value() == EXT2_GOOD_OLD_REV { + (EXT2_GOOD_OLD_INODE_SIZE, EXT2_GOOD_OLD_FIRST_INO) + } else { + let size = s.inode_size.value(); + if size < EXT2_GOOD_OLD_INODE_SIZE + || !size.is_power_of_two() + || u32::from(size) > block_size + { + pr_err!("Unsupported inode size: {}\n", size); + return Err(EINVAL); + } + (size, s.first_ino.value()) + }; + + // Get the number of inodes per group and per block. + let inode_count = s.inodes_count.value(); + let inodes_per_group = s.inodes_per_group.value(); + let inodes_per_block = block_size / u32::from(inode_size); + if inodes_per_group == 0 || inodes_per_block == 0 { + return Err(EINVAL); + } + + if inodes_per_group > block_size * 8 || inodes_per_group < inodes_per_block { + pr_err!("Bad inodes per group: {}\n", inodes_per_group); + return Err(EINVAL); + } + + // Check the size of the groups. + let itb_per_group = inodes_per_group / inodes_per_block; + let blocks_per_group = s.blocks_per_group.value(); + if blocks_per_group > block_size * 8 || blocks_per_group <= itb_per_group + 3 { + pr_err!("Bad blocks per group: {}\n", blocks_per_group); + return Err(EINVAL); + } + + let blocks_count = s.blocks_count.value(); + if block::Sector::from(blocks_count) > sb.sector_count() >> (1 + block_size_bits) { + pr_err!( + "Block count ({blocks_count}) exceeds size of device ({})\n", + sb.sector_count() >> (1 + block_size_bits) + ); + return Err(EINVAL); + } + + let group_count = (blocks_count - s.first_data_block.value() - 1) / blocks_per_group + 1; + if group_count * inodes_per_group != inode_count { + pr_err!( + "Unexpected inode count: {inode_count} vs {}", + group_count * inodes_per_group + ); + return Err(EINVAL); + } + + let mut groups = Vec::new(); + groups.reserve(group_count as usize, GFP_NOFS)?; + + let mut remain = group_count; + let mut offset = (SB_OFFSET / Offset::from(block_size) + 1) * Offset::from(block_size); + while remain > 0 { + let b = mapper.mapped_folio(offset)?; + for g in Group::from_bytes_to_slice(&b).ok_or(EIO)? { + groups.push(*g, GFP_NOFS)?; + remain -= 1; + if remain == 0 { + break; + } + } + offset += Offset::try_from(b.len())?; + } + + Self::check_descriptors(s, &groups)?; + + sb.set_magic(s.magic.value().into()); + drop(mapped); + Ok(Box::new( + Ext2Fs { + mapper, + block_size, + _block_size_bits: block_size_bits, + has_file_type, + inodes_per_group, + inodes_per_block, + inode_count, + inode_size, + first_ino, + group: groups, + }, + GFP_KERNEL, + )?) + } + + fn init_root(sb: &SuperBlock) -> Result> { + let inode = Self::iget(sb, EXT2_ROOT_INO)?; + dentry::Root::try_new(inode) + } +} + +fn rec_len(d: &DirEntry) -> u32 { + let len = d.rec_len.value(); + + if PAGE_SIZE >= 65536 && len == u16::MAX { + 1u32 << 16 + } else { + len.into() + } +} + +#[vtable] +impl file::Operations for Ext2Fs { + type FileSystem = Self; + + fn seek(file: &File, offset: Offset, whence: file::Whence) -> Result { + file::generic_seek(file, offset, whence) + } + + fn read(_: &File, _: &mut user::Writer, _: &mut Offset) -> Result { + Err(EISDIR) + } + + fn read_dir( + _file: &File, + inode: &Locked<&INode, inode::ReadSem>, + emitter: &mut file::DirEmitter, + ) -> Result { + let has_file_type = inode.super_block().data().has_file_type; + + inode.for_each_page(emitter.pos(), Offset::MAX, |data| { + let mut offset = 0usize; + let mut acc: Offset = 0; + let limit = data.len().saturating_sub(size_of::()); + while offset < limit { + let dirent = DirEntry::from_bytes(data, offset).ok_or(EIO)?; + offset += size_of::(); + + let name_len = usize::from(dirent.name_len); + if data.len() - offset < name_len { + return Err(EIO); + } + + let name = &data[offset..][..name_len]; + let rec_len = rec_len(dirent); + offset = offset - size_of::() + rec_len as usize; + if rec_len == 0 || offset > data.len() { + return Err(EIO); + } + + acc += Offset::from(rec_len); + let ino = dirent.inode.value(); + if ino == 0 { + continue; + } + + let t = if !has_file_type { + file::DirEntryType::Unknown + } else { + match dirent.file_type { + FT_REG_FILE => file::DirEntryType::Reg, + FT_DIR => file::DirEntryType::Dir, + FT_SYMLINK => file::DirEntryType::Lnk, + FT_CHRDEV => file::DirEntryType::Chr, + FT_BLKDEV => file::DirEntryType::Blk, + FT_FIFO => file::DirEntryType::Fifo, + FT_SOCK => file::DirEntryType::Sock, + _ => continue, + } + }; + + if !emitter.emit(acc, name, ino.into(), t) { + return Ok(Some(())); + } + acc = 0; + } + Ok(None) + })?; + Ok(()) + } +} + +#[vtable] +impl inode::Operations for Ext2Fs { + type FileSystem = Self; + + fn lookup( + parent: &Locked<&INode, inode::ReadSem>, + dentry: dentry::Unhashed<'_, Self>, + ) -> Result>>> { + let inode = parent.for_each_page(0, Offset::MAX, |data| { + let mut offset = 0usize; + while data.len() - offset > size_of::() { + let dirent = DirEntry::from_bytes(data, offset).ok_or(EIO)?; + offset += size_of::(); + + let name_len = usize::from(dirent.name_len); + if data.len() - offset < name_len { + return Err(EIO); + } + + let name = &data[offset..][..name_len]; + + offset = offset - size_of::() + usize::from(dirent.rec_len.value()); + if offset > data.len() { + return Err(EIO); + } + + let ino = dirent.inode.value(); + if ino != 0 && name == dentry.name() { + return Ok(Some(Self::iget(parent.super_block(), ino)?)); + } + } + Ok(None) + })?; + + dentry.splice_alias(inode) + } +} + +impl iomap::Operations for Ext2Fs { + type FileSystem = Self; + + fn begin<'a>( + inode: &'a INode, + pos: Offset, + length: Offset, + _flags: u32, + map: &mut iomap::Map<'a>, + _srcmap: &mut iomap::Map<'a>, + ) -> Result { + let size = inode.size(); + if pos >= size { + map.set_offset(pos) + .set_length(length.try_into()?) + .set_flags(iomap::map_flags::MERGED) + .set_type(iomap::Type::Hole); + return Ok(()); + } + + let block_size = inode.super_block().data().block_size as Offset; + let block = pos / block_size; + + let boffset = Self::offset_to_block(inode, block)?; + map.set_offset(block * block_size) + .set_length(block_size as u64) + .set_flags(iomap::map_flags::MERGED) + .set_type(iomap::Type::Mapped) + .set_bdev(Some(inode.super_block().bdev())) + .set_addr(boffset * block_size as u64); + + Ok(()) + } +} + +fn decode_dev(block: &[LE]) -> (u32, u32) { + let v = block[0].value(); + if v != 0 { + ((v >> 8) & 255, v & 255) + } else { + let v = block[1].value(); + ((v & 0xfff00) >> 8, (v & 0xff) | ((v >> 12) & 0xfff00)) + } +} diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 445599d4bff6..732bc9939f7f 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -165,3 +165,6 @@ macro_rules! container_of { ptr.wrapping_sub(offset) as *const $type }} } + +/// The size in bytes of a page of memory. +pub const PAGE_SIZE: usize = bindings::PAGE_SIZE;