Skip to content

Commit 1504cd1

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 3685d81 commit 1504cd1

File tree

4 files changed

+154
-21
lines changed

4 files changed

+154
-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: 136 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,17 @@ pub mod buffer;
2020
/// Maximum size of an inode.
2121
pub const MAX_LFS_FILESIZE: i64 = bindings::MAX_LFS_FILESIZE;
2222

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

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

@@ -182,7 +196,9 @@ impl Registration {
182196
fs.name = T::NAME.as_char_ptr();
183197
fs.init_fs_context = Some(Self::init_fs_context_callback::<T>);
184198
fs.kill_sb = Some(Self::kill_sb_callback::<T>);
185-
fs.fs_flags = 0;
199+
fs.fs_flags = if let Super::BlockDev = T::SUPER_TYPE {
200+
bindings::FS_REQUIRES_DEV as i32
201+
} else { 0 };
186202

187203
// SAFETY: Pointers stored in `fs` are static so will live for as long as the
188204
// registration is active (it is undone in `drop`).
@@ -205,9 +221,16 @@ impl Registration {
205221
unsafe extern "C" fn kill_sb_callback<T: FileSystem + ?Sized>(
206222
sb_ptr: *mut bindings::super_block,
207223
) {
208-
// SAFETY: In `get_tree_callback` we always call `get_tree_nodev`, so `kill_anon_super` is
209-
// the appropriate function to call for cleanup.
210-
unsafe { bindings::kill_anon_super(sb_ptr) };
224+
match T::SUPER_TYPE {
225+
// SAFETY: In `get_tree_callback` we always call `get_tree_bdev` for
226+
// `Super::BlockDev`, so `kill_block_super` is the appropriate function to call
227+
// for cleanup.
228+
Super::BlockDev => unsafe { bindings::kill_block_super(sb_ptr) },
229+
// SAFETY: In `get_tree_callback` we always call `get_tree_nodev` for
230+
// `Super::Independent`, so `kill_anon_super` is the appropriate function to call
231+
// for cleanup.
232+
Super::Independent => unsafe { bindings::kill_anon_super(sb_ptr) },
233+
}
211234

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

482573
/// State of [`NewSuperBlock`] that indicates that [`NewSuperBlock::init`] needs to be called
483574
/// eventually.
484575
pub struct NeedsInit;
485576

577+
/// State of [`NewSuperBlock`] that indicates that [`NewSuperBlock::init_data`] needs to be called
578+
/// eventually.
579+
pub struct NeedsData;
580+
486581
/// State of [`NewSuperBlock`] that indicates that [`NewSuperBlock::init_root`] needs to be called
487582
/// eventually.
488583
pub struct NeedsRoot;
@@ -536,11 +631,7 @@ impl<'a, T: FileSystem + ?Sized> NewSuperBlock<'a, T, NeedsInit> {
536631
}
537632

538633
/// Initialises the superblock.
539-
pub fn init(
540-
self,
541-
params: &SuperParams,
542-
data: T::Data,
543-
) -> Result<NewSuperBlock<'a, T, NeedsRoot>> {
634+
pub fn init(self, params: &SuperParams) -> Result<NewSuperBlock<'a, T, NeedsData>> {
544635
// SAFETY: Since this is a new super block, we hold the only reference to it.
545636
let sb = unsafe { &mut *self.sb.0.get() };
546637

@@ -557,16 +648,37 @@ impl<'a, T: FileSystem + ?Sized> NewSuperBlock<'a, T, NeedsInit> {
557648
sb.s_blocksize = 1 << sb.s_blocksize_bits;
558649
sb.s_flags |= bindings::SB_RDONLY;
559650

560-
// No failures are allowed beyond this point, otherwise we'll leak `data`.
561-
sb.s_fs_info = data.into_foreign().cast_mut();
562-
563651
Ok(NewSuperBlock {
564652
sb: self.sb,
565653
_p: PhantomData,
566654
})
567655
}
568656
}
569657

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

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

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