Skip to content

vfs abstractions and tarfs #1037

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 29 commits into
base: rust-next
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
a3fe8d8
xattr: make the xattr array itself const
wedsonaf Sep 29, 2023
484ec70
rust: introduce `InPlaceModule`
wedsonaf Sep 29, 2023
da1a2b6
samples: rust: add in-place initialisation sample
wedsonaf Sep 29, 2023
883e433
rust: add the `container_of` macro
wedsonaf Sep 29, 2023
14513c0
rust: init: introduce `Opaque::try_ffi_init`
wedsonaf Sep 29, 2023
c7d0fb2
rust: time: introduce `time` module
wedsonaf Sep 29, 2023
ca4a93c
rust: types: add little-endian type
wedsonaf Sep 29, 2023
a44bdcc
rust: types: introduce `FromBytes` trait
wedsonaf Sep 29, 2023
caf9b29
rust: mem_cache: introduce `MemCache`
wedsonaf Sep 29, 2023
b0bc357
kbuild: rust: allow modules to allocate memory
wedsonaf Sep 29, 2023
528babd
rust: fs: add registration/unregistration of file systems
wedsonaf Sep 29, 2023
e909f43
rust: fs: introduce the `module_fs` macro
wedsonaf Sep 29, 2023
ad07f4b
samples: rust: add initial ro file system sample
wedsonaf Sep 29, 2023
626056a
rust: fs: introduce `FileSystem::super_params`
wedsonaf Sep 29, 2023
a448dc5
rust: fs: introduce `INode<T>`
wedsonaf Sep 29, 2023
b26f77a
rust: fs: introduce `FileSystem::init_root`
wedsonaf Sep 29, 2023
ac0f637
rust: fs: introduce `FileSystem::read_dir`
wedsonaf Sep 29, 2023
14b32d0
rust: fs: introduce `FileSystem::lookup`
wedsonaf Sep 29, 2023
5e601b9
rust: folio: introduce basic support for folios
wedsonaf Sep 29, 2023
c02d2b9
rust: fs: introduce `FileSystem::read_folio`
wedsonaf Sep 29, 2023
ce0acb6
rust: fs: introduce `FileSystem::read_xattr`
wedsonaf Sep 29, 2023
3f94966
rust: fs: introduce `FileSystem::statfs`
wedsonaf Sep 29, 2023
6032d93
rust: fs: introduce more inode types
wedsonaf Sep 29, 2023
1cf6e5e
rust: fs: add per-superblock data
wedsonaf Sep 29, 2023
516d0e4
rust: fs: add basic support for fs buffer heads
wedsonaf Sep 29, 2023
0605dba
rust: fs: allow file systems backed by a block device
wedsonaf Sep 29, 2023
b40e37b
rust: fs: allow per-inode data
wedsonaf Sep 29, 2023
80fda66
rust: fs: export file type from mode constants
wedsonaf Sep 29, 2023
7189177
tarfs: introduce tar fs
wedsonaf Sep 29, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
rust: fs: allow file systems backed by a block device
Allow Rust file systems that are backed by block devices (in addition to
in-memory ones).

Signed-off-by: Wedson Almeida Filho <[email protected]>
  • Loading branch information
wedsonaf committed Oct 18, 2023
commit 0605dba93970521eb01aba81e3a85a7737b09516
1 change: 1 addition & 0 deletions rust/bindings/bindings_helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
*/

#include <kunit/test.h>
#include <linux/bio.h>
#include <linux/buffer_head.h>
#include <linux/errname.h>
#include <linux/fs.h>
Expand Down
14 changes: 14 additions & 0 deletions rust/helpers.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
*/

#include <kunit/test-bug.h>
#include <linux/blkdev.h>
#include <linux/buffer_head.h>
#include <linux/bug.h>
#include <linux/build_bug.h>
Expand Down Expand Up @@ -252,6 +253,13 @@ unsigned int rust_helper_MKDEV(unsigned int major, unsigned int minor)
EXPORT_SYMBOL_GPL(rust_helper_MKDEV);

#ifdef CONFIG_BUFFER_HEAD
struct buffer_head *rust_helper_sb_bread(struct super_block *sb,
sector_t block)
{
return sb_bread(sb, block);
}
EXPORT_SYMBOL_GPL(rust_helper_sb_bread);

void rust_helper_get_bh(struct buffer_head *bh)
{
get_bh(bh);
Expand All @@ -265,6 +273,12 @@ void rust_helper_put_bh(struct buffer_head *bh)
EXPORT_SYMBOL_GPL(rust_helper_put_bh);
#endif

sector_t rust_helper_bdev_nr_sectors(struct block_device *bdev)
{
return bdev_nr_sectors(bdev);
}
EXPORT_SYMBOL_GPL(rust_helper_bdev_nr_sectors);

/*
* `bindgen` binds the C `size_t` type as the Rust `usize` type, so we can
* use it in contexts where Rust expects a `usize` like slice (array) indices.
Expand Down
177 changes: 165 additions & 12 deletions rust/kernel/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,9 @@
//! C headers: [`include/linux/fs.h`](../../include/linux/fs.h)

use crate::error::{code::*, from_result, to_result, Error, Result};
use crate::types::{ARef, AlwaysRefCounted, Either, ForeignOwnable, Opaque};
use crate::{
bindings, folio::LockedFolio, init::PinInit, str::CStr, time::Timespec, try_pin_init,
ThisModule,
};
use crate::folio::{LockedFolio, UniqueFolio};
use crate::types::{ARef, AlwaysRefCounted, Either, ForeignOwnable, Opaque, ScopeGuard};
use crate::{bindings, init::PinInit, str::CStr, time::Timespec, try_pin_init, ThisModule};
use core::{marker::PhantomData, marker::PhantomPinned, mem::ManuallyDrop, pin::Pin, ptr};
use macros::{pin_data, pinned_drop};

Expand All @@ -21,6 +19,17 @@ pub mod buffer;
/// Maximum size of an inode.
pub const MAX_LFS_FILESIZE: i64 = bindings::MAX_LFS_FILESIZE;

/// Type of superblock keying.
///
/// It determines how C's `fs_context_operations::get_tree` is implemented.
pub enum Super {
/// Multiple independent superblocks may exist.
Independent,

/// Uses a block device.
BlockDev,
}

/// A file system type.
pub trait FileSystem {
/// Data associated with each file system instance (super-block).
Expand All @@ -29,6 +38,9 @@ pub trait FileSystem {
/// The name of the file system type.
const NAME: &'static CStr;

/// Determines how superblocks for this file system type are keyed.
const SUPER_TYPE: Super = Super::Independent;

/// Returns the parameters to initialise a super block.
fn super_params(sb: &NewSuperBlock<Self>) -> Result<SuperParams<Self::Data>>;

Expand Down Expand Up @@ -181,7 +193,9 @@ impl Registration {
fs.name = T::NAME.as_char_ptr();
fs.init_fs_context = Some(Self::init_fs_context_callback::<T>);
fs.kill_sb = Some(Self::kill_sb_callback::<T>);
fs.fs_flags = 0;
fs.fs_flags = if let Super::BlockDev = T::SUPER_TYPE {
bindings::FS_REQUIRES_DEV as i32
} else { 0 };

// SAFETY: Pointers stored in `fs` are static so will live for as long as the
// registration is active (it is undone in `drop`).
Expand All @@ -204,9 +218,16 @@ impl Registration {
unsafe extern "C" fn kill_sb_callback<T: FileSystem + ?Sized>(
sb_ptr: *mut bindings::super_block,
) {
// SAFETY: In `get_tree_callback` we always call `get_tree_nodev`, so `kill_anon_super` is
// the appropriate function to call for cleanup.
unsafe { bindings::kill_anon_super(sb_ptr) };
match T::SUPER_TYPE {
// SAFETY: In `get_tree_callback` we always call `get_tree_bdev` for
// `Super::BlockDev`, so `kill_block_super` is the appropriate function to call
// for cleanup.
Super::BlockDev => unsafe { bindings::kill_block_super(sb_ptr) },
// SAFETY: In `get_tree_callback` we always call `get_tree_nodev` for
// `Super::Independent`, so `kill_anon_super` is the appropriate function to call
// for cleanup.
Super::Independent => unsafe { bindings::kill_anon_super(sb_ptr) },
}

// SAFETY: The C API contract guarantees that `sb_ptr` is valid for read.
let ptr = unsafe { (*sb_ptr).s_fs_info };
Expand Down Expand Up @@ -479,6 +500,65 @@ impl<T: FileSystem + ?Sized> SuperBlock<T> {
})))
}
}

/// Reads a block from the block device.
#[cfg(CONFIG_BUFFER_HEAD)]
pub fn bread(&self, block: u64) -> Result<ARef<buffer::Head>> {
// Fail requests for non-blockdev file systems. This is a compile-time check.
match T::SUPER_TYPE {
Super::BlockDev => {}
_ => return Err(EIO),
}

// SAFETY: This function is only valid after the `NeedsInit` typestate, so the block size
// is known and the superblock can be used to read blocks.
let ptr =
ptr::NonNull::new(unsafe { bindings::sb_bread(self.0.get(), block) }).ok_or(EIO)?;
// SAFETY: `sb_bread` returns a referenced buffer head. Ownership of the increment is
// passed to the `ARef` instance.
Ok(unsafe { ARef::from_raw(ptr.cast()) })
}

/// Reads `size` bytes starting from `offset` bytes.
///
/// Returns an iterator that returns slices based on blocks.
#[cfg(CONFIG_BUFFER_HEAD)]
pub fn read(
&self,
offset: u64,
size: u64,
) -> Result<impl Iterator<Item = Result<buffer::View>> + '_> {
struct BlockIter<'a, T: FileSystem + ?Sized> {
sb: &'a SuperBlock<T>,
next_offset: u64,
end: u64,
}
impl<'a, T: FileSystem + ?Sized> Iterator for BlockIter<'a, T> {
type Item = Result<buffer::View>;

fn next(&mut self) -> Option<Self::Item> {
if self.next_offset >= self.end {
return None;
}

// SAFETY: The superblock is valid and has had its block size initialised.
let block_size = unsafe { (*self.sb.0.get()).s_blocksize };
let bh = match self.sb.bread(self.next_offset / block_size) {
Ok(bh) => bh,
Err(e) => return Some(Err(e)),
};
let boffset = self.next_offset & (block_size - 1);
let bsize = core::cmp::min(self.end - self.next_offset, block_size - boffset);
self.next_offset += bsize;
Some(Ok(buffer::View::new(bh, boffset as usize, bsize as usize)))
}
}
Ok(BlockIter {
sb: self,
next_offset: offset,
end: offset.checked_add(size).ok_or(ERANGE)?,
})
}
}

/// Required superblock parameters.
Expand Down Expand Up @@ -511,6 +591,70 @@ pub struct SuperParams<T: ForeignOwnable + Send + Sync> {
#[repr(transparent)]
pub struct NewSuperBlock<T: FileSystem + ?Sized>(bindings::super_block, PhantomData<T>);

impl<T: FileSystem + ?Sized> NewSuperBlock<T> {
/// Reads sectors.
///
/// `count` must be such that the total size doesn't exceed a page.
pub fn sread(&self, sector: u64, count: usize, folio: &mut UniqueFolio) -> Result {
// Fail requests for non-blockdev file systems. This is a compile-time check.
match T::SUPER_TYPE {
// The superblock is valid and given that it's a blockdev superblock it must have a
// valid `s_bdev`.
Super::BlockDev => {}
_ => return Err(EIO),
}

crate::build_assert!(count * (bindings::SECTOR_SIZE as usize) <= bindings::PAGE_SIZE);

// Read the sectors.
let mut bio = bindings::bio::default();
let bvec = Opaque::<bindings::bio_vec>::uninit();

// SAFETY: `bio` and `bvec` are allocated on the stack, they're both valid.
unsafe {
bindings::bio_init(
&mut bio,
self.0.s_bdev,
bvec.get(),
1,
bindings::req_op_REQ_OP_READ,
)
};

// SAFETY: `bio` was just initialised with `bio_init` above, so it's safe to call
// `bio_uninit` on the way out.
let mut bio =
ScopeGuard::new_with_data(bio, |mut b| unsafe { bindings::bio_uninit(&mut b) });

// SAFETY: We have one free `bvec` (initialsied above). We also know that size won't exceed
// a page size (build_assert above).
unsafe {
bindings::bio_add_folio_nofail(
&mut *bio,
folio.0 .0.get(),
count * (bindings::SECTOR_SIZE as usize),
0,
)
};
bio.bi_iter.bi_sector = sector;

// SAFETY: The bio was fully initialised above.
to_result(unsafe { bindings::submit_bio_wait(&mut *bio) })?;
Ok(())
}

/// Returns the number of sectors in the underlying block device.
pub fn sector_count(&self) -> Result<u64> {
// Fail requests for non-blockdev file systems. This is a compile-time check.
match T::SUPER_TYPE {
// The superblock is valid and given that it's a blockdev superblock it must have a
// valid `s_bdev`.
Super::BlockDev => Ok(unsafe { bindings::bdev_nr_sectors(self.0.s_bdev) }),
_ => Err(EIO),
}
}
}

struct Tables<T: FileSystem + ?Sized>(T);
impl<T: FileSystem + ?Sized> Tables<T> {
const CONTEXT: bindings::fs_context_operations = bindings::fs_context_operations {
Expand All @@ -523,9 +667,18 @@ impl<T: FileSystem + ?Sized> Tables<T> {
};

unsafe extern "C" fn get_tree_callback(fc: *mut bindings::fs_context) -> core::ffi::c_int {
// SAFETY: `fc` is valid per the callback contract. `fill_super_callback` also has
// the right type and is a valid callback.
unsafe { bindings::get_tree_nodev(fc, Some(Self::fill_super_callback)) }
match T::SUPER_TYPE {
// SAFETY: `fc` is valid per the callback contract. `fill_super_callback` also has
// the right type and is a valid callback.
Super::BlockDev => unsafe {
bindings::get_tree_bdev(fc, Some(Self::fill_super_callback))
},
// SAFETY: `fc` is valid per the callback contract. `fill_super_callback` also has
// the right type and is a valid callback.
Super::Independent => unsafe {
bindings::get_tree_nodev(fc, Some(Self::fill_super_callback))
},
}
}

unsafe extern "C" fn fill_super_callback(
Expand Down
1 change: 0 additions & 1 deletion rust/kernel/fs/buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ pub struct View {
}

impl View {
#[allow(dead_code)]
pub(crate) fn new(head: ARef<Head>, offset: usize, size: usize) -> Self {
Self { head, size, offset }
}
Expand Down