Skip to content

Commit b147c5f

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 a95339a commit b147c5f

File tree

4 files changed

+157
-21
lines changed

4 files changed

+157
-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>
@@ -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

@@ -172,7 +186,9 @@ impl Registration {
172186
fs.name = T::NAME.as_char_ptr();
173187
fs.init_fs_context = Some(Self::init_fs_context_callback::<T>);
174188
fs.kill_sb = Some(Self::kill_sb_callback::<T>);
175-
fs.fs_flags = 0;
189+
fs.fs_flags = if let Super::BlockDev = T::SUPER_TYPE {
190+
bindings::FS_REQUIRES_DEV as i32
191+
} else { 0 };
176192

177193
// SAFETY: Pointers stored in `fs` are static so will live for as long as the
178194
// registration is active (it is undone in `drop`).
@@ -195,9 +211,16 @@ impl Registration {
195211
unsafe extern "C" fn kill_sb_callback<T: FileSystem + ?Sized>(
196212
sb_ptr: *mut bindings::super_block,
197213
) {
198-
// SAFETY: In `get_tree_callback` we always call `get_tree_nodev`, so `kill_anon_super` is
199-
// the appropriate function to call for cleanup.
200-
unsafe { bindings::kill_anon_super(sb_ptr) };
214+
match T::SUPER_TYPE {
215+
// SAFETY: In `get_tree_callback` we always call `get_tree_bdev` for
216+
// `Super::BlockDev`, so `kill_block_super` is the appropriate function to call
217+
// for cleanup.
218+
Super::BlockDev => unsafe { bindings::kill_block_super(sb_ptr) },
219+
// SAFETY: In `get_tree_callback` we always call `get_tree_nodev` for
220+
// `Super::Independent`, so `kill_anon_super` is the appropriate function to call
221+
// for cleanup.
222+
Super::Independent => unsafe { bindings::kill_anon_super(sb_ptr) },
223+
}
201224

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

475568
/// State of [`NewSuperBlock`] that indicates that [`NewSuperBlock::init`] needs to be called
476569
/// eventually.
477570
pub struct NeedsInit;
478571

572+
/// State of [`NewSuperBlock`] that indicates that [`NewSuperBlock::init_data`] needs to be called
573+
/// eventually.
574+
pub struct NeedsData;
575+
479576
/// State of [`NewSuperBlock`] that indicates that [`NewSuperBlock::init_root`] needs to be called
480577
/// eventually.
481578
pub struct NeedsRoot;
@@ -529,11 +626,7 @@ impl<'a, T: FileSystem + ?Sized> NewSuperBlock<'a, T, NeedsInit> {
529626
}
530627

531628
/// Initialises the superblock.
532-
pub fn init(
533-
self,
534-
params: &SuperParams,
535-
data: T::Data,
536-
) -> Result<NewSuperBlock<'a, T, NeedsRoot>> {
629+
pub fn init(self, params: &SuperParams) -> Result<NewSuperBlock<'a, T, NeedsData>> {
537630
// SAFETY: Since this is a new super block, we hold the only reference to it.
538631
let sb = unsafe { &mut *self.sb.0.get() };
539632

@@ -550,16 +643,38 @@ impl<'a, T: FileSystem + ?Sized> NewSuperBlock<'a, T, NeedsInit> {
550643
sb.s_blocksize = 1 << sb.s_blocksize_bits;
551644
sb.s_flags |= bindings::SB_RDONLY;
552645

553-
// No failures are allowed beyond this point, otherwise we'll leak `data`.
554-
sb.s_fs_info = data.into_foreign().cast_mut();
555-
556646
Ok(NewSuperBlock {
557647
sb: self.sb,
558648
_p: PhantomData,
559649
})
560650
}
561651
}
562652

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

607722
unsafe extern "C" fn get_tree_callback(fc: *mut bindings::fs_context) -> core::ffi::c_int {
608-
// SAFETY: `fc` is valid per the callback contract. `fill_super_callback` also has
609-
// the right type and is a valid callback.
610-
unsafe { bindings::get_tree_nodev(fc, Some(Self::fill_super_callback)) }
723+
match T::SUPER_TYPE {
724+
// SAFETY: `fc` is valid per the callback contract. `fill_super_callback` also has
725+
// the right type and is a valid callback.
726+
Super::BlockDev => unsafe {
727+
bindings::get_tree_bdev(fc, Some(Self::fill_super_callback))
728+
},
729+
// SAFETY: `fc` is valid per the callback contract. `fill_super_callback` also has
730+
// the right type and is a valid callback.
731+
Super::Independent => unsafe {
732+
bindings::get_tree_nodev(fc, Some(Self::fill_super_callback))
733+
},
734+
}
611735
}
612736

613737
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)