Skip to content

Commit 2b2b05b

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 028ff0a commit 2b2b05b

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

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

174177
// SAFETY: Pointers stored in `fs` are static so will live for as long as the
@@ -189,10 +192,22 @@ impl Registration {
189192
})
190193
}
191194

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

@@ -423,6 +438,14 @@ pub struct INodeParams {
423438
pub struct SuperBlock<T: FileSystem + ?Sized>(Opaque<bindings::super_block>, PhantomData<T>);
424439

425440
impl<T: FileSystem + ?Sized> SuperBlock<T> {
441+
/// Returns the data associated with the superblock.
442+
pub fn data(&self) -> <T::Data as ForeignOwnable>::Borrowed<'_> {
443+
// SAFETY: This method is only available after the `NeedsData` typestate, so `s_fs_info`
444+
// has been initialised initialised with the result of a call to `T::into_foreign`.
445+
let ptr = unsafe { (*self.0.get()).s_fs_info };
446+
unsafe { T::Data::borrow(ptr) }
447+
}
448+
426449
/// Tries to get an existing inode or create a new one if it doesn't exist yet.
427450
pub fn get_or_create_inode(&self, ino: Ino) -> Result<Either<ARef<INode<T>>, NewINode<T>>> {
428451
// SAFETY: The only initialisation missing from the superblock is the root, and this
@@ -449,6 +472,14 @@ impl<T: FileSystem + ?Sized> SuperBlock<T> {
449472
}
450473
}
451474

475+
/// State of [`NewSuperBlock`] that indicates that [`NewSuperBlock::init`] needs to be called
476+
/// eventually.
477+
pub struct NeedsInit;
478+
479+
/// State of [`NewSuperBlock`] that indicates that [`NewSuperBlock::init_root`] needs to be called
480+
/// eventually.
481+
pub struct NeedsRoot;
482+
452483
/// Required superblock parameters.
453484
///
454485
/// This is used in [`NewSuperBlock::init`].
@@ -470,17 +501,19 @@ pub struct SuperParams {
470501

471502
/// A superblock that is still being initialised.
472503
///
504+
//// It uses type states to ensure that callers use the right sequence of calls.
505+
///
473506
/// # Invariants
474507
///
475508
/// The superblock is a newly-created one and this is the only active pointer to it.
476-
pub struct NewSuperBlock<'a, T: FileSystem + ?Sized> {
509+
pub struct NewSuperBlock<'a, T: FileSystem + ?Sized, S = NeedsInit> {
477510
sb: &'a mut SuperBlock<T>,
478511

479512
// This also forces `'a` to be invariant.
480-
_p: PhantomData<&'a mut &'a ()>,
513+
_p: PhantomData<&'a mut &'a S>,
481514
}
482515

483-
impl<'a, T: FileSystem + ?Sized> NewSuperBlock<'a, T> {
516+
impl<'a, T: FileSystem + ?Sized> NewSuperBlock<'a, T, NeedsInit> {
484517
/// Creates a new instance of [`NewSuperBlock`].
485518
///
486519
/// # Safety
@@ -496,7 +529,11 @@ impl<'a, T: FileSystem + ?Sized> NewSuperBlock<'a, T> {
496529
}
497530

498531
/// Initialises the superblock.
499-
pub fn init(self, params: &SuperParams, root: ARef<INode<T>>) -> Result<&'a SuperBlock<T>> {
532+
pub fn init(
533+
self,
534+
params: &SuperParams,
535+
data: T::Data,
536+
) -> Result<NewSuperBlock<'a, T, NeedsRoot>> {
500537
// SAFETY: Since this is a new super block, we hold the only reference to it.
501538
let sb = unsafe { &mut *self.sb.0.get() };
502539

@@ -513,27 +550,42 @@ impl<'a, T: FileSystem + ?Sized> NewSuperBlock<'a, T> {
513550
sb.s_blocksize = 1 << sb.s_blocksize_bits;
514551
sb.s_flags |= bindings::SB_RDONLY;
515552

553+
// No failures are allowed beyond this point, otherwise we'll leak `data`.
554+
sb.s_fs_info = data.into_foreign().cast_mut();
555+
556+
Ok(NewSuperBlock {
557+
sb: self.sb,
558+
_p: PhantomData,
559+
})
560+
}
561+
}
562+
563+
impl<'a, T: FileSystem + ?Sized> NewSuperBlock<'a, T, NeedsRoot> {
564+
/// Initialises the root of the superblock.
565+
pub fn init_root(self, inode: ARef<INode<T>>) -> Result<&'a SuperBlock<T>> {
516566
// Reject root inode if it belongs to a different superblock.
517-
if !ptr::eq(root.super_block(), self.sb) {
567+
if !ptr::eq(inode.super_block(), self.sb) {
518568
return Err(EINVAL);
519569
}
520570

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

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

532584
Ok(self.sb)
533585
}
534586
}
535587

536-
impl<T: FileSystem + ?Sized> core::ops::Deref for NewSuperBlock<'_, T> {
588+
impl<T: FileSystem + ?Sized> core::ops::Deref for NewSuperBlock<'_, T, NeedsRoot> {
537589
type Target = SuperBlock<T>;
538590

539591
fn deref(&self) -> &Self::Target {
@@ -956,6 +1008,7 @@ impl<T: FileSystem + ?Sized + Sync + Send> crate::InPlaceModule for Module<T> {
9561008
///
9571009
/// struct MyFs;
9581010
/// impl fs::FileSystem for MyFs {
1011+
/// type Data = ();
9591012
/// const NAME: &'static CStr = c_str!("myfs");
9601013
/// fn fill_super(_: NewSuperBlock<'_, Self>) -> Result<&SuperBlock<Self>> {
9611014
/// 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)