From: Wedson Almeida Filho <wedsonaf@gmail.com>
To: Alexander Viro <viro@zeniv.linux.org.uk>,
Christian Brauner <brauner@kernel.org>,
Matthew Wilcox <willy@infradead.org>,
Dave Chinner <david@fromorbit.com>
Cc: Kent Overstreet <kent.overstreet@gmail.com>,
Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
linux-fsdevel@vger.kernel.org, rust-for-linux@vger.kernel.org,
linux-kernel@vger.kernel.org,
Wedson Almeida Filho <walmeida@microsoft.com>
Subject: [RFC PATCH v2 21/30] rust: fs: introduce more inode types
Date: Tue, 14 May 2024 10:17:02 -0300 [thread overview]
Message-ID: <20240514131711.379322-22-wedsonaf@gmail.com> (raw)
In-Reply-To: <20240514131711.379322-1-wedsonaf@gmail.com>
From: Wedson Almeida Filho <walmeida@microsoft.com>
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 <walmeida@microsoft.com>
---
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<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::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<Self::FileSystem>>,
+ _inode: &'a INode<Self::FileSystem>,
+ ) -> Result<Either<CString, &'a CStr>> {
+ Err(ENOTSUPP)
+ }
+
/// Returns the inode corresponding to the directory entry with the given name.
fn lookup(
_parent: &Locked<&INode<Self::FileSystem>, ReadSem>,
@@ -134,6 +146,52 @@ pub fn init(mut self, params: Params) -> Result<ARef<INode<T>>> {
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<T: FileSystem + ?Sized>(*const bindings::inode_operations, PhantomData<T>);
impl<T: FileSystem + ?Sized> Ops<T> {
+ /// 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<U: Operations<FileSystem = T> + ?Sized>() -> Self {
struct Table<T: Operations + ?Sized>(PhantomData<T>);
@@ -255,7 +337,11 @@ impl<T: Operations + ?Sized> Table<T> {
} 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::<core::ffi::c_char>(),
+ 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::<core::ffi::c_char>()
+ }
+ }
+ }
}
Self(&Table::<U>::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<RoFs> = file::Ops::new::<RoFs>();
const DIR_IOPS: inode::Ops<RoFs> = inode::Ops::new::<RoFs>();
const FILE_AOPS: address_space::Ops<RoFs> = address_space::Ops::new::<RoFs>();
+const LNK_IOPS: inode::Ops<RoFs> = inode::Ops::new::<Link>();
struct RoFs;
@@ -68,6 +75,11 @@ fn iget(sb: &sb::SuperBlock<Self>, e: &'static Entry) -> Result<ARef<INode<Self>
.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<RoFs>>,
+ inode: &'a INode<RoFs>,
+ ) -> Result<Either<CString, &'a CStr>> {
+ 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;
--
2.34.1
next prev parent reply other threads:[~2024-05-14 13:18 UTC|newest]
Thread overview: 35+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-05-14 13:16 [RFC PATCH v2 00/30] Rust abstractions for VFS Wedson Almeida Filho
2024-05-14 13:16 ` [RFC PATCH v2 01/30] rust: fs: add registration/unregistration of file systems Wedson Almeida Filho
2024-05-14 13:16 ` [RFC PATCH v2 02/30] rust: fs: introduce the `module_fs` macro Wedson Almeida Filho
2024-05-14 13:16 ` [RFC PATCH v2 03/30] samples: rust: add initial ro file system sample Wedson Almeida Filho
2024-05-14 13:16 ` [RFC PATCH v2 04/30] rust: fs: introduce `FileSystem::fill_super` Wedson Almeida Filho
2024-05-20 19:38 ` Darrick J. Wong
2024-05-14 13:16 ` [RFC PATCH v2 05/30] rust: fs: introduce `INode<T>` Wedson Almeida Filho
2024-05-14 13:16 ` [RFC PATCH v2 06/30] rust: fs: introduce `DEntry<T>` Wedson Almeida Filho
2024-05-14 13:16 ` [RFC PATCH v2 07/30] rust: fs: introduce `FileSystem::init_root` Wedson Almeida Filho
2024-05-14 13:16 ` [RFC PATCH v2 08/30] rust: file: move `kernel::file` to `kernel::fs::file` Wedson Almeida Filho
2024-05-14 13:16 ` [RFC PATCH v2 09/30] rust: fs: generalise `File` for different file systems Wedson Almeida Filho
2024-05-14 13:16 ` [RFC PATCH v2 10/30] rust: fs: add empty file operations Wedson Almeida Filho
2024-05-14 13:16 ` [RFC PATCH v2 11/30] rust: fs: introduce `file::Operations::read_dir` Wedson Almeida Filho
2024-05-14 13:16 ` [RFC PATCH v2 12/30] rust: fs: introduce `file::Operations::seek` Wedson Almeida Filho
2024-05-14 13:16 ` [RFC PATCH v2 13/30] rust: fs: introduce `file::Operations::read` Wedson Almeida Filho
2024-05-14 13:16 ` [RFC PATCH v2 14/30] rust: fs: add empty inode operations Wedson Almeida Filho
2024-05-14 13:16 ` [RFC PATCH v2 15/30] rust: fs: introduce `inode::Operations::lookup` Wedson Almeida Filho
2024-05-14 13:16 ` [RFC PATCH v2 16/30] rust: folio: introduce basic support for folios Wedson Almeida Filho
2024-05-14 13:16 ` [RFC PATCH v2 17/30] rust: fs: add empty address space operations Wedson Almeida Filho
2024-05-14 13:16 ` [RFC PATCH v2 18/30] rust: fs: introduce `address_space::Operations::read_folio` Wedson Almeida Filho
2024-05-14 13:17 ` [RFC PATCH v2 19/30] rust: fs: introduce `FileSystem::read_xattr` Wedson Almeida Filho
2024-05-14 13:17 ` [RFC PATCH v2 20/30] rust: fs: introduce `FileSystem::statfs` Wedson Almeida Filho
2024-05-14 13:17 ` Wedson Almeida Filho [this message]
2024-05-14 13:17 ` [RFC PATCH v2 22/30] rust: fs: add per-superblock data Wedson Almeida Filho
2024-05-14 13:17 ` [RFC PATCH v2 23/30] rust: fs: allow file systems backed by a block device Wedson Almeida Filho
2024-05-14 13:17 ` [RFC PATCH v2 24/30] rust: fs: allow per-inode data Wedson Almeida Filho
2024-05-14 13:17 ` [RFC PATCH v2 25/30] rust: fs: export file type from mode constants Wedson Almeida Filho
2024-05-14 13:17 ` [RFC PATCH v2 26/30] rust: fs: allow populating i_lnk Wedson Almeida Filho
2024-05-14 13:17 ` [RFC PATCH v2 27/30] rust: fs: add `iomap` module Wedson Almeida Filho
2024-05-20 19:32 ` Darrick J. Wong
2024-05-14 13:17 ` [RFC PATCH v2 28/30] rust: fs: add memalloc_nofs support Wedson Almeida Filho
2024-05-14 13:17 ` [RFC PATCH v2 29/30] tarfs: introduce tar fs Wedson Almeida Filho
2024-05-14 13:17 ` [RFC PATCH v2 30/30] WIP: fs: ext2: add rust ro ext2 implementation Wedson Almeida Filho
2024-05-20 20:01 ` Darrick J. Wong
2024-05-31 14:34 ` [RFC PATCH v2 00/30] Rust abstractions for VFS Danilo Krummrich
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20240514131711.379322-22-wedsonaf@gmail.com \
--to=wedsonaf@gmail.com \
--cc=brauner@kernel.org \
--cc=david@fromorbit.com \
--cc=gregkh@linuxfoundation.org \
--cc=kent.overstreet@gmail.com \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=rust-for-linux@vger.kernel.org \
--cc=viro@zeniv.linux.org.uk \
--cc=walmeida@microsoft.com \
--cc=willy@infradead.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.