Skip to content

Commit 1722f55

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 4112174 commit 1722f55

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

472565
/// State of [`NewSuperBlock`] that indicates that [`NewSuperBlock::init`] needs to be called
473566
/// eventually.
474567
pub struct NeedsInit;
475568

569+
/// State of [`NewSuperBlock`] that indicates that [`NewSuperBlock::init_data`] needs to be called
570+
/// eventually.
571+
pub struct NeedsData;
572+
476573
/// State of [`NewSuperBlock`] that indicates that [`NewSuperBlock::init_root`] needs to be called
477574
/// eventually.
478575
pub struct NeedsRoot;
@@ -526,11 +623,7 @@ impl<'a, T: FileSystem + ?Sized> NewSuperBlock<'a, T, NeedsInit> {
526623
}
527624

528625
/// Initialises the superblock.
529-
pub fn init(
530-
self,
531-
params: &SuperParams,
532-
data: T::Data,
533-
) -> Result<NewSuperBlock<'a, T, NeedsRoot>> {
626+
pub fn init(self, params: &SuperParams) -> Result<NewSuperBlock<'a, T, NeedsData>> {
534627
// SAFETY: Since this is a new super block, we hold the only reference to it.
535628
let sb = unsafe { &mut *self.sb.0.get() };
536629

@@ -547,16 +640,38 @@ impl<'a, T: FileSystem + ?Sized> NewSuperBlock<'a, T, NeedsInit> {
547640
sb.s_blocksize = 1 << sb.s_blocksize_bits;
548641
sb.s_flags |= bindings::SB_RDONLY;
549642

550-
// No failures are allowed beyond this point, otherwise we'll leak `data`.
551-
sb.s_fs_info = data.into_foreign().cast_mut();
552-
553643
Ok(NewSuperBlock {
554644
sb: self.sb,
555645
_p: PhantomData,
556646
})
557647
}
558648
}
559649

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

604719
unsafe extern "C" fn get_tree_callback(fc: *mut bindings::fs_context) -> core::ffi::c_int {
605-
// SAFETY: `fc` is valid per the callback contract. `fill_super_callback` also has
606-
// the right type and is a valid callback.
607-
unsafe { bindings::get_tree_nodev(fc, Some(Self::fill_super_callback)) }
720+
match T::SUPER_TYPE {
721+
// SAFETY: `fc` is valid per the callback contract. `fill_super_callback` also has
722+
// the right type and is a valid callback.
723+
Super::BlockDev => unsafe {
724+
bindings::get_tree_bdev(fc, Some(Self::fill_super_callback))
725+
},
726+
// SAFETY: `fc` is valid per the callback contract. `fill_super_callback` also has
727+
// the right type and is a valid callback.
728+
Super::Independent => unsafe {
729+
bindings::get_tree_nodev(fc, Some(Self::fill_super_callback))
730+
},
731+
}
608732
}
609733

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