Skip to content

Commit 626056a

Browse files
committed
rust: fs: introduce FileSystem::super_params
Allow Rust file systems to initialise superblocks, which allows them to be mounted (though they are still empty). Some scaffolding code is added to create an empty directory as the root. It is replaced by proper inode creation in a subsequent patch in this series. Signed-off-by: Wedson Almeida Filho <[email protected]>
1 parent ad07f4b commit 626056a

File tree

4 files changed

+189
-6
lines changed

4 files changed

+189
-6
lines changed

rust/bindings/bindings_helper.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <kunit/test.h>
1010
#include <linux/errname.h>
1111
#include <linux/fs.h>
12+
#include <linux/fs_context.h>
1213
#include <linux/slab.h>
1314
#include <linux/refcount.h>
1415
#include <linux/wait.h>
@@ -22,3 +23,7 @@ const gfp_t BINDINGS___GFP_ZERO = __GFP_ZERO;
2223
const slab_flags_t BINDINGS_SLAB_RECLAIM_ACCOUNT = SLAB_RECLAIM_ACCOUNT;
2324
const slab_flags_t BINDINGS_SLAB_MEM_SPREAD = SLAB_MEM_SPREAD;
2425
const slab_flags_t BINDINGS_SLAB_ACCOUNT = SLAB_ACCOUNT;
26+
27+
const unsigned long BINDINGS_SB_RDONLY = SB_RDONLY;
28+
29+
const loff_t BINDINGS_MAX_LFS_FILESIZE = MAX_LFS_FILESIZE;

rust/bindings/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,7 @@ pub const __GFP_ZERO: gfp_t = BINDINGS___GFP_ZERO;
5555
pub const SLAB_RECLAIM_ACCOUNT: slab_flags_t = BINDINGS_SLAB_RECLAIM_ACCOUNT;
5656
pub const SLAB_MEM_SPREAD: slab_flags_t = BINDINGS_SLAB_MEM_SPREAD;
5757
pub const SLAB_ACCOUNT: slab_flags_t = BINDINGS_SLAB_ACCOUNT;
58+
59+
pub const SB_RDONLY: core::ffi::c_ulong = BINDINGS_SB_RDONLY;
60+
61+
pub const MAX_LFS_FILESIZE: loff_t = BINDINGS_MAX_LFS_FILESIZE;

rust/kernel/fs.rs

Lines changed: 170 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,22 @@
66
//!
77
//! C headers: [`include/linux/fs.h`](../../include/linux/fs.h)
88
9-
use crate::error::{code::*, from_result, to_result, Error};
9+
use crate::error::{code::*, from_result, to_result, Error, Result};
1010
use crate::types::Opaque;
1111
use crate::{bindings, init::PinInit, str::CStr, try_pin_init, ThisModule};
1212
use core::{marker::PhantomData, marker::PhantomPinned, pin::Pin};
1313
use macros::{pin_data, pinned_drop};
1414

15+
/// Maximum size of an inode.
16+
pub const MAX_LFS_FILESIZE: i64 = bindings::MAX_LFS_FILESIZE;
17+
1518
/// A file system type.
1619
pub trait FileSystem {
1720
/// The name of the file system type.
1821
const NAME: &'static CStr;
22+
23+
/// Returns the parameters to initialise a super block.
24+
fn super_params(sb: &NewSuperBlock<Self>) -> Result<SuperParams>;
1925
}
2026

2127
/// A registration of a file system.
@@ -49,7 +55,7 @@ impl Registration {
4955
let fs = unsafe { &mut *fs_ptr };
5056
fs.owner = module.0;
5157
fs.name = T::NAME.as_char_ptr();
52-
fs.init_fs_context = Some(Self::init_fs_context_callback);
58+
fs.init_fs_context = Some(Self::init_fs_context_callback::<T>);
5359
fs.kill_sb = Some(Self::kill_sb_callback);
5460
fs.fs_flags = 0;
5561

@@ -60,13 +66,22 @@ impl Registration {
6066
})
6167
}
6268

63-
unsafe extern "C" fn init_fs_context_callback(
64-
_fc_ptr: *mut bindings::fs_context,
69+
unsafe extern "C" fn init_fs_context_callback<T: FileSystem + ?Sized>(
70+
fc_ptr: *mut bindings::fs_context,
6571
) -> core::ffi::c_int {
66-
from_result(|| Err(ENOTSUPP))
72+
from_result(|| {
73+
// SAFETY: The C callback API guarantees that `fc_ptr` is valid.
74+
let fc = unsafe { &mut *fc_ptr };
75+
fc.ops = &Tables::<T>::CONTEXT;
76+
Ok(0)
77+
})
6778
}
6879

69-
unsafe extern "C" fn kill_sb_callback(_sb_ptr: *mut bindings::super_block) {}
80+
unsafe extern "C" fn kill_sb_callback(sb_ptr: *mut bindings::super_block) {
81+
// SAFETY: In `get_tree_callback` we always call `get_tree_nodev`, so `kill_anon_super` is
82+
// the appropriate function to call for cleanup.
83+
unsafe { bindings::kill_anon_super(sb_ptr) };
84+
}
7085
}
7186

7287
#[pinned_drop]
@@ -79,6 +94,151 @@ impl PinnedDrop for Registration {
7994
}
8095
}
8196

97+
/// A file system super block.
98+
///
99+
/// Wraps the kernel's `struct super_block`.
100+
#[repr(transparent)]
101+
pub struct SuperBlock<T: FileSystem + ?Sized>(Opaque<bindings::super_block>, PhantomData<T>);
102+
103+
/// Required superblock parameters.
104+
///
105+
/// This is returned by implementations of [`FileSystem::super_params`].
106+
pub struct SuperParams {
107+
/// The magic number of the superblock.
108+
pub magic: u32,
109+
110+
/// The size of a block in powers of 2 (i.e., for a value of `n`, the size is `2^n`).
111+
pub blocksize_bits: u8,
112+
113+
/// Maximum size of a file.
114+
///
115+
/// The maximum allowed value is [`MAX_LFS_FILESIZE`].
116+
pub maxbytes: i64,
117+
118+
/// Granularity of c/m/atime in ns (cannot be worse than a second).
119+
pub time_gran: u32,
120+
}
121+
122+
/// A superblock that is still being initialised.
123+
///
124+
/// # Invariants
125+
///
126+
/// The superblock is a newly-created one and this is the only active pointer to it.
127+
#[repr(transparent)]
128+
pub struct NewSuperBlock<T: FileSystem + ?Sized>(bindings::super_block, PhantomData<T>);
129+
130+
struct Tables<T: FileSystem + ?Sized>(T);
131+
impl<T: FileSystem + ?Sized> Tables<T> {
132+
const CONTEXT: bindings::fs_context_operations = bindings::fs_context_operations {
133+
free: None,
134+
parse_param: None,
135+
get_tree: Some(Self::get_tree_callback),
136+
reconfigure: None,
137+
parse_monolithic: None,
138+
dup: None,
139+
};
140+
141+
unsafe extern "C" fn get_tree_callback(fc: *mut bindings::fs_context) -> core::ffi::c_int {
142+
// SAFETY: `fc` is valid per the callback contract. `fill_super_callback` also has
143+
// the right type and is a valid callback.
144+
unsafe { bindings::get_tree_nodev(fc, Some(Self::fill_super_callback)) }
145+
}
146+
147+
unsafe extern "C" fn fill_super_callback(
148+
sb_ptr: *mut bindings::super_block,
149+
_fc: *mut bindings::fs_context,
150+
) -> core::ffi::c_int {
151+
from_result(|| {
152+
// SAFETY: The callback contract guarantees that `sb_ptr` is a unique pointer to a
153+
// newly-created superblock.
154+
let sb = unsafe { &mut *sb_ptr.cast() };
155+
let params = T::super_params(sb)?;
156+
157+
sb.0.s_magic = params.magic as _;
158+
sb.0.s_op = &Tables::<T>::SUPER_BLOCK;
159+
sb.0.s_maxbytes = params.maxbytes;
160+
sb.0.s_time_gran = params.time_gran;
161+
sb.0.s_blocksize_bits = params.blocksize_bits;
162+
sb.0.s_blocksize = 1;
163+
if sb.0.s_blocksize.leading_zeros() < params.blocksize_bits.into() {
164+
return Err(EINVAL);
165+
}
166+
sb.0.s_blocksize = 1 << sb.0.s_blocksize_bits;
167+
sb.0.s_flags |= bindings::SB_RDONLY;
168+
169+
// The following is scaffolding code that will be removed in a subsequent patch. It is
170+
// needed to build a root dentry, otherwise core code will BUG().
171+
// SAFETY: `sb` is the superblock being initialised, it is valid for read and write.
172+
let inode = unsafe { bindings::new_inode(&mut sb.0) };
173+
if inode.is_null() {
174+
return Err(ENOMEM);
175+
}
176+
177+
// SAFETY: `inode` is valid for write.
178+
unsafe { bindings::set_nlink(inode, 2) };
179+
180+
{
181+
// SAFETY: This is a newly-created inode. No other references to it exist, so it is
182+
// safe to mutably dereference it.
183+
let inode = unsafe { &mut *inode };
184+
inode.i_ino = 1;
185+
inode.i_mode = (bindings::S_IFDIR | 0o755) as _;
186+
187+
// SAFETY: `simple_dir_operations` never changes, it's safe to reference it.
188+
inode.__bindgen_anon_3.i_fop = unsafe { &bindings::simple_dir_operations };
189+
190+
// SAFETY: `simple_dir_inode_operations` never changes, it's safe to reference it.
191+
inode.i_op = unsafe { &bindings::simple_dir_inode_operations };
192+
}
193+
194+
// SAFETY: `d_make_root` requires that `inode` be valid and referenced, which is the
195+
// case for this call.
196+
//
197+
// It takes over the inode, even on failure, so we don't need to clean it up.
198+
let dentry = unsafe { bindings::d_make_root(inode) };
199+
if dentry.is_null() {
200+
return Err(ENOMEM);
201+
}
202+
203+
sb.0.s_root = dentry;
204+
205+
Ok(0)
206+
})
207+
}
208+
209+
const SUPER_BLOCK: bindings::super_operations = bindings::super_operations {
210+
alloc_inode: None,
211+
destroy_inode: None,
212+
free_inode: None,
213+
dirty_inode: None,
214+
write_inode: None,
215+
drop_inode: None,
216+
evict_inode: None,
217+
put_super: None,
218+
sync_fs: None,
219+
freeze_super: None,
220+
freeze_fs: None,
221+
thaw_super: None,
222+
unfreeze_fs: None,
223+
statfs: None,
224+
remount_fs: None,
225+
umount_begin: None,
226+
show_options: None,
227+
show_devname: None,
228+
show_path: None,
229+
show_stats: None,
230+
#[cfg(CONFIG_QUOTA)]
231+
quota_read: None,
232+
#[cfg(CONFIG_QUOTA)]
233+
quota_write: None,
234+
#[cfg(CONFIG_QUOTA)]
235+
get_dquots: None,
236+
nr_cached_objects: None,
237+
free_cached_objects: None,
238+
shutdown: None,
239+
};
240+
}
241+
82242
/// Kernel module that exposes a single file system implemented by `T`.
83243
#[pin_data]
84244
pub struct Module<T: FileSystem + ?Sized> {
@@ -105,6 +265,7 @@ impl<T: FileSystem + ?Sized + Sync + Send> crate::InPlaceModule for Module<T> {
105265
///
106266
/// ```
107267
/// # mod module_fs_sample {
268+
/// use kernel::fs::{NewSuperBlock, SuperParams};
108269
/// use kernel::prelude::*;
109270
/// use kernel::{c_str, fs};
110271
///
@@ -119,6 +280,9 @@ impl<T: FileSystem + ?Sized + Sync + Send> crate::InPlaceModule for Module<T> {
119280
/// struct MyFs;
120281
/// impl fs::FileSystem for MyFs {
121282
/// const NAME: &'static CStr = c_str!("myfs");
283+
/// fn super_params(_: &NewSuperBlock<Self>) -> Result<SuperParams> {
284+
/// todo!()
285+
/// }
122286
/// }
123287
/// # }
124288
/// ```

samples/rust/rust_rofs.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
//! Rust read-only file system sample.
44
5+
use kernel::fs::{NewSuperBlock, SuperParams};
56
use kernel::prelude::*;
67
use kernel::{c_str, fs};
78

@@ -16,4 +17,13 @@ kernel::module_fs! {
1617
struct RoFs;
1718
impl fs::FileSystem for RoFs {
1819
const NAME: &'static CStr = c_str!("rust-fs");
20+
21+
fn super_params(_sb: &NewSuperBlock<Self>) -> Result<SuperParams> {
22+
Ok(SuperParams {
23+
magic: 0x52555354,
24+
blocksize_bits: 12,
25+
maxbytes: fs::MAX_LFS_FILESIZE,
26+
time_gran: 1,
27+
})
28+
}
1929
}

0 commit comments

Comments
 (0)