Skip to content

Commit 94240e5

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 <walmeida@microsoft.com>
1 parent 6c80d2c commit 94240e5

4 files changed

Lines changed: 157 additions & 21 deletions

File tree

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>
@@ -235,6 +236,13 @@ unsigned int rust_helper_MKDEV(unsigned int major, unsigned int minor)
235236
EXPORT_SYMBOL_GPL(rust_helper_MKDEV);
236237

237238
#ifdef CONFIG_BUFFER_HEAD
239+
struct buffer_head *rust_helper_sb_bread(struct super_block *sb,
240+
sector_t block)
241+
{
242+
return sb_bread(sb, block);
243+
}
244+
EXPORT_SYMBOL_GPL(rust_helper_sb_bread);
245+
238246
void rust_helper_get_bh(struct buffer_head *bh)
239247
{
240248
get_bh(bh);
@@ -248,6 +256,12 @@ void rust_helper_put_bh(struct buffer_head *bh)
248256
EXPORT_SYMBOL_GPL(rust_helper_put_bh);
249257
#endif
250258

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

rust/kernel/fs.rs

Lines changed: 139 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,17 @@ pub mod buffer;
2121
/// Maximum size of an inode.
2222
pub const MAX_LFS_FILESIZE: i64 = bindings::MAX_LFS_FILESIZE;
2323

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

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

@@ -175,7 +189,9 @@ impl Registration {
175189
fs.name = T::NAME.as_char_ptr();
176190
fs.init_fs_context = Some(Self::init_fs_context_callback::<T>);
177191
fs.kill_sb = Some(Self::kill_sb_callback::<T>);
178-
fs.fs_flags = 0;
192+
fs.fs_flags = if let Super::BlockDev = T::SUPER_TYPE {
193+
bindings::FS_REQUIRES_DEV as i32
194+
} else { 0 };
179195

180196
// SAFETY: Pointers stored in `fs` are static so will live for as long as the
181197
// registration is active (it is undone in `drop`).
@@ -198,9 +214,16 @@ impl Registration {
198214
unsafe extern "C" fn kill_sb_callback<T: FileSystem + ?Sized>(
199215
sb_ptr: *mut bindings::super_block,
200216
) {
201-
// SAFETY: In `get_tree_callback` we always call `get_tree_nodev`, so `kill_anon_super` is
202-
// the appropriate function to call for cleanup.
203-
unsafe { bindings::kill_anon_super(sb_ptr) };
217+
match T::SUPER_TYPE {
218+
// SAFETY: In `get_tree_callback` we always call `get_tree_bdev` for
219+
// `Super::BlockDev`, so `kill_block_super` is the appropriate function to call
220+
// for cleanup.
221+
Super::BlockDev => unsafe { bindings::kill_block_super(sb_ptr) },
222+
// SAFETY: In `get_tree_callback` we always call `get_tree_nodev` for
223+
// `Super::Independent`, so `kill_anon_super` is the appropriate function to call
224+
// for cleanup.
225+
Super::Independent => unsafe { bindings::kill_anon_super(sb_ptr) },
226+
}
204227

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

478571
/// State of [`NewSuperBlock`] that indicates that [`NewSuperBlock::init`] needs to be called
479572
/// eventually.
480573
pub struct NeedsInit;
481574

575+
/// State of [`NewSuperBlock`] that indicates that [`NewSuperBlock::init_data`] needs to be called
576+
/// eventually.
577+
pub struct NeedsData;
578+
482579
/// State of [`NewSuperBlock`] that indicates that [`NewSuperBlock::init_root`] needs to be called
483580
/// eventually.
484581
pub struct NeedsRoot;
@@ -532,11 +629,7 @@ impl<'a, T: FileSystem + ?Sized> NewSuperBlock<'a, T, NeedsInit> {
532629
}
533630

534631
/// Initialises the superblock.
535-
pub fn init(
536-
self,
537-
params: &SuperParams,
538-
data: T::Data,
539-
) -> Result<NewSuperBlock<'a, T, NeedsRoot>> {
632+
pub fn init(self, params: &SuperParams) -> Result<NewSuperBlock<'a, T, NeedsData>> {
540633
// SAFETY: Since this is a new super block, we hold the only reference to it.
541634
let sb = unsafe { &mut *self.sb.0.get() };
542635

@@ -553,16 +646,38 @@ impl<'a, T: FileSystem + ?Sized> NewSuperBlock<'a, T, NeedsInit> {
553646
sb.s_blocksize = 1 << sb.s_blocksize_bits;
554647
sb.s_flags |= bindings::SB_RDONLY;
555648

556-
// No failures are allowed beyond this point, otherwise we'll leak `data`.
557-
sb.s_fs_info = data.into_foreign().cast_mut();
558-
559649
Ok(NewSuperBlock {
560650
sb: self.sb,
561651
_p: PhantomData,
562652
})
563653
}
564654
}
565655

656+
impl<'a, T: FileSystem + ?Sized> NewSuperBlock<'a, T, NeedsData> {
657+
/// Initialises the superblock data.
658+
pub fn init_data(self, data: T::Data) -> NewSuperBlock<'a, T, NeedsRoot> {
659+
// SAFETY: Since this is a new superblock, we hold the only reference to it.
660+
let sb = unsafe { &mut *self.sb.0.get() };
661+
sb.s_fs_info = data.into_foreign().cast_mut();
662+
663+
NewSuperBlock {
664+
sb: self.sb,
665+
_p: PhantomData,
666+
}
667+
}
668+
669+
/// Reads a block from the block device.
670+
#[cfg(CONFIG_BUFFER_HEAD)]
671+
pub fn bread(&self, block: u64) -> Result<ARef<buffer::Head>> {
672+
self.sb.bread(block)
673+
}
674+
675+
/// Returns the number of sectors in the underlying block device.
676+
pub fn sector_count(&self) -> Result<u64> {
677+
self.sb.sector_count()
678+
}
679+
}
680+
566681
impl<'a, T: FileSystem + ?Sized> NewSuperBlock<'a, T, NeedsRoot> {
567682
/// Initialises the root of the superblock.
568683
pub fn init_root(self, inode: ARef<INode<T>>) -> Result<&'a SuperBlock<T>> {
@@ -608,9 +723,18 @@ impl<T: FileSystem + ?Sized> Tables<T> {
608723
};
609724

610725
unsafe extern "C" fn get_tree_callback(fc: *mut bindings::fs_context) -> core::ffi::c_int {
611-
// SAFETY: `fc` is valid per the callback contract. `fill_super_callback` also has
612-
// the right type and is a valid callback.
613-
unsafe { bindings::get_tree_nodev(fc, Some(Self::fill_super_callback)) }
726+
match T::SUPER_TYPE {
727+
// SAFETY: `fc` is valid per the callback contract. `fill_super_callback` also has
728+
// the right type and is a valid callback.
729+
Super::BlockDev => unsafe {
730+
bindings::get_tree_bdev(fc, Some(Self::fill_super_callback))
731+
},
732+
// SAFETY: `fc` is valid per the callback contract. `fill_super_callback` also has
733+
// the right type and is a valid callback.
734+
Super::Independent => unsafe {
735+
bindings::get_tree_nodev(fc, Some(Self::fill_super_callback))
736+
},
737+
}
614738
}
615739

616740
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::FileSystem 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)