Skip to content

Commit 1ae1641

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 <[email protected]>
1 parent ad6cae3 commit 1ae1641

File tree

2 files changed

+75
-20
lines changed

2 files changed

+75
-20
lines changed

rust/kernel/fs.rs

Lines changed: 64 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ pub const MAX_LFS_FILESIZE: i64 = bindings::MAX_LFS_FILESIZE;
1212
/// Read-only file systems.
1313
pub mod ro {
1414
use crate::error::{code::*, from_result, to_result, Error, Result};
15-
use crate::types::{ARef, AlwaysRefCounted, Either, Opaque};
15+
use crate::types::{ARef, AlwaysRefCounted, Either, ForeignOwnable, Opaque};
1616
use crate::{
1717
bindings, folio::LockedFolio, init::PinInit, str::CStr, time::Time, try_pin_init,
1818
ThisModule,
@@ -22,6 +22,9 @@ pub mod ro {
2222

2323
/// A read-only file system type.
2424
pub trait Type {
25+
/// Data associated with each file system instance (super-block).
26+
type Data: ForeignOwnable + Send + Sync;
27+
2528
/// The name of the file system type.
2629
const NAME: &'static CStr;
2730

@@ -178,7 +181,7 @@ pub mod ro {
178181
fs.owner = module.0;
179182
fs.name = T::NAME.as_char_ptr();
180183
fs.init_fs_context = Some(Self::init_fs_context_callback::<T>);
181-
fs.kill_sb = Some(Self::kill_sb_callback);
184+
fs.kill_sb = Some(Self::kill_sb_callback::<T>);
182185
fs.fs_flags = 0;
183186

184187
// SAFETY: Pointers stored in `fs` are static so will live for as long as the
@@ -199,10 +202,22 @@ pub mod ro {
199202
})
200203
}
201204

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

@@ -429,6 +444,15 @@ pub mod ro {
429444
pub struct SuperBlock<T: Type + ?Sized>(Opaque<bindings::super_block>, PhantomData<T>);
430445

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

483+
/// State of [`NewSuperBlock`] that indicates that [`NewSuperBlock::init`] needs to be called
484+
/// eventually.
485+
pub struct NeedsInit;
486+
487+
/// State of [`NewSuperBlock`] that indicates that [`NewSuperBlock::init_root`] needs to be
488+
/// called eventually.
489+
pub struct NeedsRoot;
490+
459491
/// Required superblock parameters.
460492
///
461493
/// This is used in [`NewSuperBlock::init`].
@@ -477,17 +509,19 @@ pub mod ro {
477509

478510
/// A superblock that is still being initialised.
479511
///
512+
/// It uses type states to ensure that callers use the right sequence of calls.
513+
///
480514
/// # Invariants
481515
///
482516
/// The superblock is a newly-created one and this is the only active pointer to it.
483-
pub struct NewSuperBlock<'a, T: Type + ?Sized> {
517+
pub struct NewSuperBlock<'a, T: Type + ?Sized, S = NeedsInit> {
484518
sb: &'a mut SuperBlock<T>,
485519

486520
// This also forces `'a` to be invariant.
487-
_p: PhantomData<&'a mut &'a ()>,
521+
_p: PhantomData<&'a mut &'a S>,
488522
}
489523

490-
impl<'a, T: Type + ?Sized> NewSuperBlock<'a, T> {
524+
impl<'a, T: Type + ?Sized> NewSuperBlock<'a, T, NeedsInit> {
491525
/// Creates a new instance of [`NewSuperBlock`].
492526
///
493527
/// # Safety
@@ -504,7 +538,11 @@ pub mod ro {
504538
}
505539

506540
/// Initialises the superblock.
507-
pub fn init(self, params: &SuperParams, root: ARef<INode<T>>) -> Result<&'a SuperBlock<T>> {
541+
pub fn init(
542+
self,
543+
params: &SuperParams,
544+
data: T::Data,
545+
) -> Result<NewSuperBlock<'a, T, NeedsRoot>> {
508546
// SAFETY: Since this is a new super block, we hold the only reference to it.
509547
let sb = unsafe { &mut *self.sb.0.get() };
510548

@@ -521,27 +559,41 @@ pub mod ro {
521559
sb.s_blocksize = 1 << sb.s_blocksize_bits;
522560
sb.s_flags |= bindings::SB_RDONLY;
523561

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

529580
// SAFETY: `d_make_root` requires that `inode` be valid and referenced, which is the
530581
// case for this call.
531582
//
532583
// It takes over the inode, even on failure, so we don't need to clean it up.
533-
let dentry = unsafe { bindings::d_make_root(ManuallyDrop::new(root).0.get()) };
584+
let dentry = unsafe { bindings::d_make_root(ManuallyDrop::new(inode).0.get()) };
534585
if dentry.is_null() {
535586
return Err(ENOMEM);
536587
}
537588

589+
// SAFETY: Since this is a new superblock, we hold the only reference to it.
590+
let sb = unsafe { &mut *self.sb.0.get() };
538591
sb.s_root = dentry;
539-
540592
Ok(self.sb)
541593
}
542594
}
543595

544-
impl<T: Type + ?Sized> core::ops::Deref for NewSuperBlock<'_, T> {
596+
impl<T: Type + ?Sized> core::ops::Deref for NewSuperBlock<'_, T, NeedsRoot> {
545597
type Target = SuperBlock<T>;
546598

547599
fn deref(&self) -> &Self::Target {
@@ -909,6 +961,7 @@ pub mod ro {
909961
/// struct MyFs;
910962
/// impl fs::ro::Type for MyFs {
911963
/// // ...
964+
/// # type Data = ();
912965
/// # const NAME: &'static CStr = c_str!("myfs");
913966
/// # fn fill_super(
914967
/// # _: fs::ro::NewSuperBlock<'_, Self>

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::ro::Type 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::ro::Type 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)