Skip to content

Commit 9fc7772

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 ecc6d49 commit 9fc7772

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

@@ -176,7 +179,7 @@ impl Registration {
176179
fs.owner = module.0;
177180
fs.name = T::NAME.as_char_ptr();
178181
fs.init_fs_context = Some(Self::init_fs_context_callback::<T>);
179-
fs.kill_sb = Some(Self::kill_sb_callback);
182+
fs.kill_sb = Some(Self::kill_sb_callback::<T>);
180183
fs.fs_flags = 0;
181184

182185
// SAFETY: Pointers stored in `fs` are static so will live for as long as the
@@ -197,10 +200,22 @@ impl Registration {
197200
})
198201
}
199202

200-
unsafe extern "C" fn kill_sb_callback(sb_ptr: *mut bindings::super_block) {
203+
unsafe extern "C" fn kill_sb_callback<T: FileSystem + ?Sized>(
204+
sb_ptr: *mut bindings::super_block,
205+
) {
201206
// SAFETY: In `get_tree_callback` we always call `get_tree_nodev`, so `kill_anon_super` is
202207
// the appropriate function to call for cleanup.
203208
unsafe { bindings::kill_anon_super(sb_ptr) };
209+
210+
// SAFETY: The C API contract guarantees that `sb_ptr` is valid for read.
211+
let ptr = unsafe { (*sb_ptr).s_fs_info };
212+
if !ptr.is_null() {
213+
// SAFETY: The only place where `s_fs_info` is assigned is `NewSuperBlock::init`, where
214+
// it's initialised with the result of an `into_foreign` call. We checked above that
215+
// `ptr` is non-null because it would be null if we never reached the point where we
216+
// init the field.
217+
unsafe { T::Data::from_foreign(ptr) };
218+
}
204219
}
205220
}
206221

@@ -428,6 +443,14 @@ pub struct INodeParams {
428443
pub struct SuperBlock<T: FileSystem + ?Sized>(Opaque<bindings::super_block>, PhantomData<T>);
429444

430445
impl<T: FileSystem + ?Sized> SuperBlock<T> {
446+
/// Returns the data associated with the superblock.
447+
pub fn data(&self) -> <T::Data as ForeignOwnable>::Borrowed<'_> {
448+
// SAFETY: This method is only available after the `NeedsData` typestate, so `s_fs_info`
449+
// has been initialised initialised with the result of a call to `T::into_foreign`.
450+
let ptr = unsafe { (*self.0.get()).s_fs_info };
451+
unsafe { T::Data::borrow(ptr) }
452+
}
453+
431454
/// Tries to get an existing inode or create a new one if it doesn't exist yet.
432455
pub fn get_or_create_inode(&self, ino: u64) -> Result<Either<ARef<INode<T>>, NewINode<T>>> {
433456
// SAFETY: The only initialisation missing from the superblock is the root, and this
@@ -454,6 +477,14 @@ impl<T: FileSystem + ?Sized> SuperBlock<T> {
454477
}
455478
}
456479

480+
/// State of [`NewSuperBlock`] that indicates that [`NewSuperBlock::init`] needs to be called
481+
/// eventually.
482+
pub struct NeedsInit;
483+
484+
/// State of [`NewSuperBlock`] that indicates that [`NewSuperBlock::init_root`] needs to be called
485+
/// eventually.
486+
pub struct NeedsRoot;
487+
457488
/// Required superblock parameters.
458489
///
459490
/// This is used in [`NewSuperBlock::init`].
@@ -475,17 +506,19 @@ pub struct SuperParams {
475506

476507
/// A superblock that is still being initialised.
477508
///
509+
//// It uses type states to ensure that callers use the right sequence of calls.
510+
///
478511
/// # Invariants
479512
///
480513
/// The superblock is a newly-created one and this is the only active pointer to it.
481-
pub struct NewSuperBlock<'a, T: FileSystem + ?Sized> {
514+
pub struct NewSuperBlock<'a, T: FileSystem + ?Sized, S = NeedsInit> {
482515
sb: &'a mut SuperBlock<T>,
483516

484517
// This also forces `'a` to be invariant.
485-
_p: PhantomData<&'a mut &'a ()>,
518+
_p: PhantomData<&'a mut &'a S>,
486519
}
487520

488-
impl<'a, T: FileSystem + ?Sized> NewSuperBlock<'a, T> {
521+
impl<'a, T: FileSystem + ?Sized> NewSuperBlock<'a, T, NeedsInit> {
489522
/// Creates a new instance of [`NewSuperBlock`].
490523
///
491524
/// # Safety
@@ -501,7 +534,11 @@ impl<'a, T: FileSystem + ?Sized> NewSuperBlock<'a, T> {
501534
}
502535

503536
/// Initialises the superblock.
504-
pub fn init(self, params: &SuperParams, root: ARef<INode<T>>) -> Result<&'a SuperBlock<T>> {
537+
pub fn init(
538+
self,
539+
params: &SuperParams,
540+
data: T::Data,
541+
) -> Result<NewSuperBlock<'a, T, NeedsRoot>> {
505542
// SAFETY: Since this is a new super block, we hold the only reference to it.
506543
let sb = unsafe { &mut *self.sb.0.get() };
507544

@@ -518,27 +555,42 @@ impl<'a, T: FileSystem + ?Sized> NewSuperBlock<'a, T> {
518555
sb.s_blocksize = 1 << sb.s_blocksize_bits;
519556
sb.s_flags |= bindings::SB_RDONLY;
520557

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

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

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

537589
Ok(self.sb)
538590
}
539591
}
540592

541-
impl<T: FileSystem + ?Sized> core::ops::Deref for NewSuperBlock<'_, T> {
593+
impl<T: FileSystem + ?Sized> core::ops::Deref for NewSuperBlock<'_, T, NeedsRoot> {
542594
type Target = SuperBlock<T>;
543595

544596
fn deref(&self) -> &Self::Target {
@@ -903,6 +955,7 @@ impl<T: FileSystem + ?Sized + Sync + Send> crate::InPlaceModule for Module<T> {
903955
///
904956
/// struct MyFs;
905957
/// impl fs::FileSystem for MyFs {
958+
/// type Data = ();
906959
/// const NAME: &'static CStr = c_str!("myfs");
907960
/// fn fill_super(_: NewSuperBlock<'_, Self>) -> Result<&SuperBlock<Self>> {
908961
/// 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(

0 commit comments

Comments
 (0)