diff mbox series

[RFC,v2,05/30] rust: fs: introduce `INode<T>`

Message ID 20240514131711.379322-6-wedsonaf@gmail.com (mailing list archive)
State New
Headers show
Series Rust abstractions for VFS | expand

Commit Message

Wedson Almeida Filho May 14, 2024, 1:16 p.m. UTC
From: Wedson Almeida Filho <walmeida@microsoft.com>

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 <walmeida@microsoft.com>
---
 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 mbox series

Patch

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::<Self>() }
     }
+
+    /// 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<Self>) -> 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<Self>) -> 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<T: FileSystem + ?Sized = UnspecifiedFS>(
+    pub(crate) Opaque<bindings::inode>,
+    PhantomData<T>,
+);
+
+impl<T: FileSystem + ?Sized> INode<T> {
+    /// 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::<Self>() }
+    }
+
+    /// 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<T> {
+        // 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<T: FileSystem + ?Sized> AlwaysRefCounted for INode<T> {
+    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<Self>) {
+        // 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<T: FileSystem + ?Sized>(
 );
 
 impl<T: FileSystem + ?Sized> SuperBlock<T> {
+    /// 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::<Self>() }
+    }
+
     /// Creates a new superblock mutable reference from the given raw pointer.
     ///
     /// # Safety
@@ -27,7 +40,7 @@  impl<T: FileSystem + ?Sized> SuperBlock<T> {
     /// 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.