Skip to content

Commit f4e6b57

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 26093bb commit f4e6b57

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

@@ -420,6 +435,14 @@ pub struct INodeParams {
420435
pub struct SuperBlock<T: FileSystem + ?Sized>(Opaque<bindings::super_block>, PhantomData<T>);
421436

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

472+
/// State of [`NewSuperBlock`] that indicates that [`NewSuperBlock::init`] needs to be called
473+
/// eventually.
474+
pub struct NeedsInit;
475+
476+
/// State of [`NewSuperBlock`] that indicates that [`NewSuperBlock::init_root`] needs to be called
477+
/// eventually.
478+
pub struct NeedsRoot;
479+
449480
/// Required superblock parameters.
450481
///
451482
/// This is used in [`NewSuperBlock::init`].
@@ -467,17 +498,19 @@ pub struct SuperParams {
467498

468499
/// A superblock that is still being initialised.
469500
///
501+
//// It uses type states to ensure that callers use the right sequence of calls.
502+
///
470503
/// # Invariants
471504
///
472505
/// The superblock is a newly-created one and this is the only active pointer to it.
473-
pub struct NewSuperBlock<'a, T: FileSystem + ?Sized> {
506+
pub struct NewSuperBlock<'a, T: FileSystem + ?Sized, S = NeedsInit> {
474507
sb: &'a mut SuperBlock<T>,
475508

476509
// This also forces `'a` to be invariant.
477-
_p: PhantomData<&'a mut &'a ()>,
510+
_p: PhantomData<&'a mut &'a S>,
478511
}
479512

480-
impl<'a, T: FileSystem + ?Sized> NewSuperBlock<'a, T> {
513+
impl<'a, T: FileSystem + ?Sized> NewSuperBlock<'a, T, NeedsInit> {
481514
/// Creates a new instance of [`NewSuperBlock`].
482515
///
483516
/// # Safety
@@ -493,7 +526,11 @@ impl<'a, T: FileSystem + ?Sized> NewSuperBlock<'a, T> {
493526
}
494527

495528
/// Initialises the superblock.
496-
pub fn init(self, params: &SuperParams, root: ARef<INode<T>>) -> Result<&'a SuperBlock<T>> {
529+
pub fn init(
530+
self,
531+
params: &SuperParams,
532+
data: T::Data,
533+
) -> Result<NewSuperBlock<'a, T, NeedsRoot>> {
497534
// SAFETY: Since this is a new super block, we hold the only reference to it.
498535
let sb = unsafe { &mut *self.sb.0.get() };
499536

@@ -510,27 +547,42 @@ impl<'a, T: FileSystem + ?Sized> NewSuperBlock<'a, T> {
510547
sb.s_blocksize = 1 << sb.s_blocksize_bits;
511548
sb.s_flags |= bindings::SB_RDONLY;
512549

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

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

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

529581
Ok(self.sb)
530582
}
531583
}
532584

533-
impl<T: FileSystem + ?Sized> core::ops::Deref for NewSuperBlock<'_, T> {
585+
impl<T: FileSystem + ?Sized> core::ops::Deref for NewSuperBlock<'_, T, NeedsRoot> {
534586
type Target = SuperBlock<T>;
535587

536588
fn deref(&self) -> &Self::Target {
@@ -953,6 +1005,7 @@ impl<T: FileSystem + ?Sized + Sync + Send> crate::InPlaceModule for Module<T> {
9531005
///
9541006
/// struct MyFs;
9551007
/// impl fs::FileSystem for MyFs {
1008+
/// type Data = ();
9561009
/// const NAME: &'static CStr = c_str!("myfs");
9571010
/// fn fill_super(_: NewSuperBlock<'_, Self>) -> Result<&SuperBlock<Self>> {
9581011
/// 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)