Skip to content

Commit af63dba

Browse files
committed
rust: fs: also allow file systems backed by a block device
We are introducing a new `NewSuperBlock` typestate because file system implementations need to initialise the block size (`blocksize_bits`) before they can call `bread`, and they may have to call `bread` before they can initialise the per-superblock data. IOW, the new typestate allows the following sequence: enable-bread -> bread -> init data. Signed-off-by: Wedson Almeida Filho <[email protected]>
1 parent 916d9e1 commit af63dba

File tree

4 files changed

+158
-21
lines changed

4 files changed

+158
-21
lines changed

rust/helpers.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
*/
2222

2323
#include <kunit/test-bug.h>
24+
#include <linux/blkdev.h>
2425
#include <linux/buffer_head.h>
2526
#include <linux/bug.h>
2627
#include <linux/build_bug.h>
@@ -234,6 +235,13 @@ unsigned int rust_helper_MKDEV(unsigned int major, unsigned int minor)
234235
}
235236
EXPORT_SYMBOL_GPL(rust_helper_MKDEV);
236237

238+
struct buffer_head *rust_helper_sb_bread(struct super_block *sb,
239+
sector_t block)
240+
{
241+
return sb_bread(sb, block);
242+
}
243+
EXPORT_SYMBOL_GPL(rust_helper_sb_bread);
244+
237245
void rust_helper_get_bh(struct buffer_head *bh)
238246
{
239247
get_bh(bh);
@@ -246,6 +254,12 @@ void rust_helper_put_bh(struct buffer_head *bh)
246254
}
247255
EXPORT_SYMBOL_GPL(rust_helper_put_bh);
248256

257+
sector_t rust_helper_bdev_nr_sectors(struct block_device *bdev)
258+
{
259+
return bdev_nr_sectors(bdev);
260+
}
261+
EXPORT_SYMBOL_GPL(rust_helper_bdev_nr_sectors);
262+
249263
/*
250264
* `bindgen` binds the C `size_t` type as the Rust `usize` type, so we can
251265
* use it in contexts where Rust expects a `usize` like slice (array) indices.

rust/kernel/fs.rs

Lines changed: 140 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,17 @@ pub mod ro {
2222
use core::{marker::PhantomData, marker::PhantomPinned, mem::ManuallyDrop, pin::Pin, ptr};
2323
use macros::{pin_data, pinned_drop};
2424

25+
/// Type of superblock keying.
26+
///
27+
/// It determines how C's `fs_context_operations::get_tree` is implemented.
28+
pub enum Super {
29+
/// Multiple independent superblocks may exist.
30+
Independent,
31+
32+
/// Uses a block device.
33+
BlockDev,
34+
}
35+
2536
/// A read-only file system type.
2637
pub trait Type {
2738
/// Data associated with each file system instance (super-block).
@@ -30,6 +41,9 @@ pub mod ro {
3041
/// The name of the file system type.
3142
const NAME: &'static CStr;
3243

44+
/// Determines how superblocks for this file system type are keyed.
45+
const SUPER_TYPE: Super = Super::Independent;
46+
3347
/// Initialises a super block for this file system type.
3448
fn fill_super(sb: NewSuperBlock<'_, Self>) -> Result<&SuperBlock<Self>>;
3549

@@ -184,7 +198,9 @@ pub mod ro {
184198
fs.name = T::NAME.as_char_ptr();
185199
fs.init_fs_context = Some(Self::init_fs_context_callback::<T>);
186200
fs.kill_sb = Some(Self::kill_sb_callback::<T>);
187-
fs.fs_flags = 0;
201+
fs.fs_flags = if let Super::BlockDev = T::SUPER_TYPE {
202+
bindings::FS_REQUIRES_DEV as i32
203+
} else { 0 };
188204

189205
// SAFETY: Pointers stored in `fs` are static so will live for as long as the
190206
// registration is active (it is undone in `drop`).
@@ -207,9 +223,16 @@ pub mod ro {
207223
unsafe extern "C" fn kill_sb_callback<T: Type + ?Sized>(
208224
sb_ptr: *mut bindings::super_block,
209225
) {
210-
// SAFETY: In `get_tree_callback` we always call `get_tree_nodev`, so `kill_anon_super`
211-
// is the appropriate function to call for cleanup.
212-
unsafe { bindings::kill_anon_super(sb_ptr) };
226+
match T::SUPER_TYPE {
227+
// SAFETY: In `get_tree_callback` we always call `get_tree_bdev` for
228+
// `Super::BlockDev`, so `kill_block_super` is the appropriate function to call
229+
// for cleanup.
230+
Super::BlockDev => unsafe { bindings::kill_block_super(sb_ptr) },
231+
// SAFETY: In `get_tree_callback` we always call `get_tree_nodev` for
232+
// `Super::Independent`, so `kill_anon_super` is the appropriate function to call
233+
// for cleanup.
234+
Super::Independent => unsafe { bindings::kill_anon_super(sb_ptr) },
235+
}
213236

214237
// SAFETY: The C api contract guarantees that `sb_ptr` is valid for read.
215238
let ptr = unsafe { (*sb_ptr).s_fs_info };
@@ -480,12 +503,88 @@ pub mod ro {
480503
})))
481504
}
482505
}
506+
507+
/// Reads a block from the block device.
508+
pub fn bread(&self, block: u64) -> Result<ARef<super::buffer::Head>> {
509+
// Fail requests for non-blockdev file systems. This is a compile-time check.
510+
match T::SUPER_TYPE {
511+
Super::BlockDev => {}
512+
_ => return Err(EIO),
513+
}
514+
515+
// SAFET: This function is only valid after the `NeedsInit` typestate, so the block
516+
// size is known and the superblock can be used to read blocks.
517+
let ptr =
518+
ptr::NonNull::new(unsafe { bindings::sb_bread(self.0.get(), block) }).ok_or(EIO)?;
519+
// SAFETY: `sb_bread` returns a referenced buffer head. Ownership of the increment is
520+
// passed to the `ARef` instance.
521+
Ok(unsafe { ARef::from_raw(ptr.cast()) })
522+
}
523+
524+
/// Reads `size` bytes starting from `offset` bytes.
525+
///
526+
/// Returns an iterator that returns slices based on blocks.
527+
pub fn read(
528+
&self,
529+
offset: u64,
530+
size: u64,
531+
) -> Result<impl Iterator<Item = Result<super::buffer::View>> + '_> {
532+
struct BlockIter<'a, T: Type + ?Sized> {
533+
sb: &'a SuperBlock<T>,
534+
next_offset: u64,
535+
end: u64,
536+
}
537+
impl<'a, T: Type + ?Sized> Iterator for BlockIter<'a, T> {
538+
type Item = Result<super::buffer::View>;
539+
540+
fn next(&mut self) -> Option<Self::Item> {
541+
if self.next_offset >= self.end {
542+
return None;
543+
}
544+
545+
// SAFETY: The superblock is valid and has had its block size initialised.
546+
let block_size = unsafe { (*self.sb.0.get()).s_blocksize };
547+
let bh = match self.sb.bread(self.next_offset / block_size) {
548+
Ok(bh) => bh,
549+
Err(e) => return Some(Err(e)),
550+
};
551+
let boffset = self.next_offset & (block_size - 1);
552+
let bsize = core::cmp::min(self.end - self.next_offset, block_size - boffset);
553+
self.next_offset += bsize;
554+
Some(Ok(super::buffer::View::new(
555+
bh,
556+
boffset as usize,
557+
bsize as usize,
558+
)))
559+
}
560+
}
561+
Ok(BlockIter {
562+
sb: self,
563+
next_offset: offset,
564+
end: offset.checked_add(size).ok_or(ERANGE)?,
565+
})
566+
}
567+
568+
/// Returns the number of sectors in the underlying block device.
569+
pub fn sector_count(&self) -> Result<u64> {
570+
// Fail requests for non-blockdev file systems. This is a compile-time check.
571+
match T::SUPER_TYPE {
572+
// The superblock is valid and given that it's a blockdev superblock it must have a
573+
// valid `s_bdev`.
574+
Super::BlockDev => Ok(unsafe { bindings::bdev_nr_sectors((*self.0.get()).s_bdev) }),
575+
_ => Err(EIO),
576+
}
577+
}
483578
}
484579

485580
/// State of [`NewSuperBlock`] that indicates that [`NewSuperBlock::init`] needs to be called
486581
/// eventually.
487582
pub struct NeedsInit;
488583

584+
/// State of [`NewSuperBlock`] that indicates that [`NewSuperBlock::init_data`] needs to be
585+
/// called eventually.
586+
pub struct NeedsData;
587+
489588
/// State of [`NewSuperBlock`] that indicates that [`NewSuperBlock::init_root`] needs to be
490589
/// called eventually.
491590
pub struct NeedsRoot;
@@ -540,11 +639,7 @@ pub mod ro {
540639
}
541640

542641
/// Initialises the superblock.
543-
pub fn init(
544-
self,
545-
params: &SuperParams,
546-
data: T::Data,
547-
) -> Result<NewSuperBlock<'a, T, NeedsRoot>> {
642+
pub fn init(self, params: &SuperParams) -> Result<NewSuperBlock<'a, T, NeedsData>> {
548643
// SAFETY: Since this is a new super block, we hold the only reference to it.
549644
let sb = unsafe { &mut *self.sb.0.get() };
550645

@@ -561,16 +656,37 @@ pub mod ro {
561656
sb.s_blocksize = 1 << sb.s_blocksize_bits;
562657
sb.s_flags |= bindings::SB_RDONLY;
563658

564-
// No failures are allowed beyond this point, otherwise we'll leak `data`.
565-
sb.s_fs_info = data.into_foreign().cast_mut();
566-
567659
Ok(NewSuperBlock {
568660
sb: self.sb,
569661
_p: PhantomData,
570662
})
571663
}
572664
}
573665

666+
impl<'a, T: Type + ?Sized> NewSuperBlock<'a, T, NeedsData> {
667+
/// Initialises the superblock data.
668+
pub fn init_data(self, data: T::Data) -> NewSuperBlock<'a, T, NeedsRoot> {
669+
// SAFETY: Since this is a new superblock, we hold the only reference to it.
670+
let sb = unsafe { &mut *self.sb.0.get() };
671+
sb.s_fs_info = data.into_foreign().cast_mut();
672+
673+
NewSuperBlock {
674+
sb: self.sb,
675+
_p: PhantomData,
676+
}
677+
}
678+
679+
/// Reads a block from the block device.
680+
pub fn bread(&self, block: u64) -> Result<ARef<super::buffer::Head>> {
681+
self.sb.bread(block)
682+
}
683+
684+
/// Returns the number of sectors in the underlying block device.
685+
pub fn sector_count(&self) -> Result<u64> {
686+
self.sb.sector_count()
687+
}
688+
}
689+
574690
impl<'a, T: Type + ?Sized> NewSuperBlock<'a, T, NeedsRoot> {
575691
/// Initialises the root of the superblock.
576692
pub fn init_root(self, inode: ARef<INode<T>>) -> Result<&'a SuperBlock<T>> {
@@ -615,9 +731,18 @@ pub mod ro {
615731
};
616732

617733
unsafe extern "C" fn get_tree_callback(fc: *mut bindings::fs_context) -> core::ffi::c_int {
618-
// SAFETY: `fc` is valid per the callback contract. `fill_super_callback` also has
619-
// the right type and is a valid callback.
620-
unsafe { bindings::get_tree_nodev(fc, Some(Self::fill_super_callback)) }
734+
match T::SUPER_TYPE {
735+
// SAFETY: `fc` is valid per the callback contract. `fill_super_callback` also has
736+
// the right type and is a valid callback.
737+
Super::BlockDev => unsafe {
738+
bindings::get_tree_bdev(fc, Some(Self::fill_super_callback))
739+
},
740+
// SAFETY: `fc` is valid per the callback contract. `fill_super_callback` also has
741+
// the right type and is a valid callback.
742+
Super::Independent => unsafe {
743+
bindings::get_tree_nodev(fc, Some(Self::fill_super_callback))
744+
},
745+
}
621746
}
622747

623748
unsafe extern "C" fn fill_super_callback(

rust/kernel/fs/buffer.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@ pub struct View {
4949
}
5050

5151
impl View {
52-
#[allow(dead_code)]
5352
pub(crate) fn new(head: ARef<Head>, offset: usize, size: usize) -> Self {
5453
Self { head, size, offset }
5554
}

samples/rust/rust_rofs.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,15 +56,14 @@ impl fs::ro::Type for RoFs {
5656
const NAME: &'static CStr = c_str!("rust-fs");
5757

5858
fn fill_super(sb: NewSuperBlock<'_, Self>) -> Result<&SuperBlock<Self>> {
59-
let sb = sb.init(
60-
&SuperParams {
59+
let sb = sb
60+
.init(&SuperParams {
6161
magic: 0x52555354,
6262
blocksize_bits: 12,
6363
maxbytes: fs::MAX_LFS_FILESIZE,
6464
time_gran: 1,
65-
},
66-
(),
67-
)?;
65+
})?
66+
.init_data(());
6867
let root = match sb.get_or_create_inode(1)? {
6968
Either::Left(existing) => existing,
7069
Either::Right(new) => new.init(INodeParams {

0 commit comments

Comments
 (0)