Skip to content

Commit c880af6

Browse files
committed
rust: fs: add per-superblock data
This allows 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 <walmeida@microsoft.com>
1 parent f173873 commit c880af6

2 files changed

Lines changed: 74 additions & 19 deletions

File tree

rust/kernel/fs.rs

Lines changed: 63 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
//! C headers: [`include/linux/fs.h`](../../include/linux/fs.h)
88
99
use crate::error::{code::*, from_result, to_result, Error, Result};
10-
use crate::types::{ARef, AlwaysRefCounted, Either, Opaque};
10+
use crate::types::{ARef, AlwaysRefCounted, Either, ForeignOwnable, Opaque};
1111
use crate::{
1212
bindings, folio::LockedFolio, init::PinInit, str::CStr, time::Timespec, try_pin_init,
1313
ThisModule,
@@ -20,6 +20,9 @@ pub const MAX_LFS_FILESIZE: i64 = bindings::MAX_LFS_FILESIZE;
2020

2121
/// A file system type.
2222
pub trait FileSystem {
23+
/// Data associated with each file system instance (super-block).
24+
type Data: ForeignOwnable + Send + Sync;
25+
2326
/// The name of the file system type.
2427
const NAME: &'static CStr;
2528

@@ -165,7 +168,7 @@ impl Registration {
165168
fs.owner = module.0;
166169
fs.name = T::NAME.as_char_ptr();
167170
fs.init_fs_context = Some(Self::init_fs_context_callback::<T>);
168-
fs.kill_sb = Some(Self::kill_sb_callback);
171+
fs.kill_sb = Some(Self::kill_sb_callback::<T>);
169172
fs.fs_flags = 0;
170173

171174
// SAFETY: Pointers stored in `fs` are static so will live for as long as the
@@ -186,10 +189,22 @@ impl Registration {
186189
})
187190
}
188191

189-
unsafe extern "C" fn kill_sb_callback(sb_ptr: *mut bindings::super_block) {
192+
unsafe extern "C" fn kill_sb_callback<T: FileSystem + ?Sized>(
193+
sb_ptr: *mut bindings::super_block,
194+
) {
190195
// SAFETY: In `get_tree_callback` we always call `get_tree_nodev`, so `kill_anon_super` is
191196
// the appropriate function to call for cleanup.
192197
unsafe { bindings::kill_anon_super(sb_ptr) };
198+
199+
// SAFETY: The C API contract guarantees that `sb_ptr` is valid for read.
200+
let ptr = unsafe { (*sb_ptr).s_fs_info };
201+
if !ptr.is_null() {
202+
// SAFETY: The only place where `s_fs_info` is assigned is `NewSuperBlock::init`, where
203+
// it's initialised with the result of an `into_foreign` call. We checked above that
204+
// `ptr` is non-null because it would be null if we never reached the point where we
205+
// init the field.
206+
unsafe { T::Data::from_foreign(ptr) };
207+
}
193208
}
194209
}
195210

@@ -417,6 +432,14 @@ pub struct INodeParams {
417432
pub struct SuperBlock<T: FileSystem + ?Sized>(Opaque<bindings::super_block>, PhantomData<T>);
418433

419434
impl<T: FileSystem + ?Sized> SuperBlock<T> {
435+
/// Returns the data associated with the superblock.
436+
pub fn data(&self) -> <T::Data as ForeignOwnable>::Borrowed<'_> {
437+
// SAFETY: This method is only available after the `NeedsData` typestate, so `s_fs_info`
438+
// has been initialised initialised with the result of a call to `T::into_foreign`.
439+
let ptr = unsafe { (*self.0.get()).s_fs_info };
440+
unsafe { T::Data::borrow(ptr) }
441+
}
442+
420443
/// Tries to get an existing inode or create a new one if it doesn't exist yet.
421444
pub fn get_or_create_inode(&self, ino: u64) -> Result<Either<ARef<INode<T>>, NewINode<T>>> {
422445
// SAFETY: The only initialisation missing from the superblock is the root, and this
@@ -443,6 +466,14 @@ impl<T: FileSystem + ?Sized> SuperBlock<T> {
443466
}
444467
}
445468

469+
/// State of [`NewSuperBlock`] that indicates that [`NewSuperBlock::init`] needs to be called
470+
/// eventually.
471+
pub struct NeedsInit;
472+
473+
/// State of [`NewSuperBlock`] that indicates that [`NewSuperBlock::init_root`] needs to be called
474+
/// eventually.
475+
pub struct NeedsRoot;
476+
446477
/// Required superblock parameters.
447478
///
448479
/// This is used in [`NewSuperBlock::init`].
@@ -464,17 +495,19 @@ pub struct SuperParams {
464495

465496
/// A superblock that is still being initialised.
466497
///
498+
//// It uses type states to ensure that callers use the right sequence of calls.
499+
///
467500
/// # Invariants
468501
///
469502
/// The superblock is a newly-created one and this is the only active pointer to it.
470-
pub struct NewSuperBlock<'a, T: FileSystem + ?Sized> {
503+
pub struct NewSuperBlock<'a, T: FileSystem + ?Sized, S = NeedsInit> {
471504
sb: &'a mut SuperBlock<T>,
472505

473506
// This also forces `'a` to be invariant.
474-
_p: PhantomData<&'a mut &'a ()>,
507+
_p: PhantomData<&'a mut &'a S>,
475508
}
476509

477-
impl<'a, T: FileSystem + ?Sized> NewSuperBlock<'a, T> {
510+
impl<'a, T: FileSystem + ?Sized> NewSuperBlock<'a, T, NeedsInit> {
478511
/// Creates a new instance of [`NewSuperBlock`].
479512
///
480513
/// # Safety
@@ -490,7 +523,11 @@ impl<'a, T: FileSystem + ?Sized> NewSuperBlock<'a, T> {
490523
}
491524

492525
/// Initialises the superblock.
493-
pub fn init(self, params: &SuperParams, root: ARef<INode<T>>) -> Result<&'a SuperBlock<T>> {
526+
pub fn init(
527+
self,
528+
params: &SuperParams,
529+
data: T::Data,
530+
) -> Result<NewSuperBlock<'a, T, NeedsRoot>> {
494531
// SAFETY: Since this is a new super block, we hold the only reference to it.
495532
let sb = unsafe { &mut *self.sb.0.get() };
496533

@@ -507,27 +544,42 @@ impl<'a, T: FileSystem + ?Sized> NewSuperBlock<'a, T> {
507544
sb.s_blocksize = 1 << sb.s_blocksize_bits;
508545
sb.s_flags |= bindings::SB_RDONLY;
509546

547+
// No failures are allowed beyond this point, otherwise we'll leak `data`.
548+
sb.s_fs_info = data.into_foreign().cast_mut();
549+
550+
Ok(NewSuperBlock {
551+
sb: self.sb,
552+
_p: PhantomData,
553+
})
554+
}
555+
}
556+
557+
impl<'a, T: FileSystem + ?Sized> NewSuperBlock<'a, T, NeedsRoot> {
558+
/// Initialises the root of the superblock.
559+
pub fn init_root(self, inode: ARef<INode<T>>) -> Result<&'a SuperBlock<T>> {
510560
// Reject root inode if it belongs to a different superblock.
511-
if !ptr::eq(root.super_block(), self.sb) {
561+
if !ptr::eq(inode.super_block(), self.sb) {
512562
return Err(EINVAL);
513563
}
514564

515565
// SAFETY: `d_make_root` requires that `inode` be valid and referenced, which is the case
516566
// for this call.
517567
//
518568
// It takes over the inode, even on failure, so we don't need to clean it up.
519-
let dentry = unsafe { bindings::d_make_root(ManuallyDrop::new(root).0.get()) };
569+
let dentry = unsafe { bindings::d_make_root(ManuallyDrop::new(inode).0.get()) };
520570
if dentry.is_null() {
521571
return Err(ENOMEM);
522572
}
523573

574+
// SAFETY: Since this is a new superblock, we hold the only reference to it.
575+
let sb = unsafe { &mut *self.sb.0.get() };
524576
sb.s_root = dentry;
525577

526578
Ok(self.sb)
527579
}
528580
}
529581

530-
impl<T: FileSystem + ?Sized> core::ops::Deref for NewSuperBlock<'_, T> {
582+
impl<T: FileSystem + ?Sized> core::ops::Deref for NewSuperBlock<'_, T, NeedsRoot> {
531583
type Target = SuperBlock<T>;
532584

533585
fn deref(&self) -> &Self::Target {
@@ -939,6 +991,7 @@ impl<T: FileSystem + ?Sized + Sync + Send> crate::InPlaceModule for Module<T> {
939991
///
940992
/// struct MyFs;
941993
/// impl fs::FileSystem for MyFs {
994+
/// type Data = ();
942995
/// const NAME: &'static CStr = c_str!("myfs");
943996
/// fn fill_super(_: NewSuperBlock<'_, Self>) -> Result<&SuperBlock<Self>> {
944997
/// todo!()

samples/rust/rust_rofs.rs

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,19 @@ const ENTRIES: [Entry; 4] = [
5252

5353
struct RoFs;
5454
impl fs::FileSystem for RoFs {
55+
type Data = ();
5556
const NAME: &'static CStr = c_str!("rust-fs");
5657

5758
fn fill_super(sb: NewSuperBlock<'_, Self>) -> Result<&SuperBlock<Self>> {
59+
let sb = sb.init(
60+
&SuperParams {
61+
magic: 0x52555354,
62+
blocksize_bits: 12,
63+
maxbytes: fs::MAX_LFS_FILESIZE,
64+
time_gran: 1,
65+
},
66+
(),
67+
)?;
5868
let root = match sb.get_or_create_inode(1)? {
5969
Either::Left(existing) => existing,
6070
Either::Right(new) => new.init(INodeParams {
@@ -70,15 +80,7 @@ impl fs::FileSystem for RoFs {
7080
mtime: UNIX_EPOCH,
7181
})?,
7282
};
73-
sb.init(
74-
&SuperParams {
75-
magic: 0x52555354,
76-
blocksize_bits: 12,
77-
maxbytes: fs::MAX_LFS_FILESIZE,
78-
time_gran: 1,
79-
},
80-
root,
81-
)
83+
sb.init_root(root)
8284
}
8385

8486
fn read_dir(inode: &INode<Self>, emitter: &mut DirEmitter) -> Result {

0 commit comments

Comments
 (0)