Skip to content

Commit fc146f9

Browse files
committed
rust: add support for file system parameters
This allows file system contexts to be further initialised with parameters from userspace before a fs is mounted or reconfigured. Signed-off-by: Wedson Almeida Filho <[email protected]>
1 parent 1781c4c commit fc146f9

File tree

5 files changed

+718
-3
lines changed

5 files changed

+718
-3
lines changed

rust/helpers.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
#include <linux/skbuff.h>
3939
#include <linux/uaccess.h>
4040
#include <linux/uio.h>
41+
#include <linux/fs_parser.h>
4142

4243
__noreturn void rust_helper_BUG(void)
4344
{
@@ -645,6 +646,15 @@ void rust_helper_lockdep_unregister_key(struct lock_class_key *key)
645646
}
646647
EXPORT_SYMBOL_GPL(rust_helper_lockdep_unregister_key);
647648

649+
int rust_helper_fs_parse(struct fs_context *fc,
650+
const struct fs_parameter_spec *desc,
651+
struct fs_parameter *param,
652+
struct fs_parse_result *result)
653+
{
654+
return fs_parse(fc, desc, param, result);
655+
}
656+
EXPORT_SYMBOL_GPL(rust_helper_fs_parse);
657+
648658
/*
649659
* We use `bindgen`'s `--size_t-is-usize` option to bind the C `size_t` type
650660
* as the Rust `usize` type, so we can use it in contexts where Rust

rust/kernel/error.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,8 @@ pub mod code {
304304
declare_err!(ERESTARTSYS, "Restart the system call.");
305305

306306
declare_err!(ENOTSUPP, "Operation is not supported.");
307+
308+
declare_err!(ENOPARAM, "Parameter not supported.");
307309
}
308310

309311
/// Generic integer kernel error.

rust/kernel/fs.rs

Lines changed: 154 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@
66
77
use crate::{
88
bindings, error::code::*, error::from_kernel_result, str::CStr, to_result,
9-
types::PointerWrapper, AlwaysRefCounted, Result, ScopeGuard, ThisModule,
9+
types::PointerWrapper, AlwaysRefCounted, Error, Result, ScopeGuard, ThisModule,
1010
};
1111
use core::{cell::UnsafeCell, marker::PhantomPinned, pin::Pin, ptr};
1212
use macros::vtable;
1313

14+
pub mod param;
15+
1416
/// A file system context.
1517
///
1618
/// It is used to gather configuration to then mount or reconfigure a file system.
@@ -19,18 +21,65 @@ pub trait Context<T: Type + ?Sized> {
1921
/// Type of the data associated with the context.
2022
type Data: PointerWrapper + Send + Sync + 'static;
2123

24+
/// The typed file system parameters.
25+
///
26+
/// Users are encouraged to define it using the [`crate::define_fs_params`] macro.
27+
const PARAMS: param::SpecTable<'static, Self::Data> = param::SpecTable::empty();
28+
2229
/// Creates a new context.
2330
fn try_new() -> Result<Self::Data>;
31+
32+
/// Parses a parameter that wasn't specified in [`Self::PARAMS`].
33+
fn parse_unknown_param(
34+
_data: &mut Self::Data,
35+
_name: &CStr,
36+
_value: param::Value<'_>,
37+
) -> Result {
38+
Err(ENOPARAM)
39+
}
40+
41+
/// Parses the whole parameter block, potentially skipping regular handling for parts of it.
42+
///
43+
/// The return value is the portion of the input buffer for which the regular handling
44+
/// (involving [`Self::PARAMS`] and [`Self::parse_unknown_param`]) will still be carried out.
45+
/// If it's `None`, the regular handling is not performed at all.
46+
fn parse_monolithic<'a>(
47+
_data: &mut Self::Data,
48+
_buf: Option<&'a mut [u8]>,
49+
) -> Result<Option<&'a mut [u8]>> {
50+
Ok(None)
51+
}
52+
}
53+
54+
/// Borrows a pointer wrapper value mutably.
55+
///
56+
/// # Safety
57+
///
58+
/// Callers must ensure that `ptr` came from a previous call to `PointerWrapper::into_pointer`, and
59+
/// that no other concurrent users of the pointer run at least until the returned `ScopeGuard` is
60+
/// dropped.
61+
unsafe fn borrow_mut<T: PointerWrapper>(
62+
ptr: *const core::ffi::c_void,
63+
) -> ScopeGuard<T, impl FnOnce(T)> {
64+
// SAFETY: The safety requirements ensure that `ptr` came from a previous call to
65+
// `into_pointer`.
66+
ScopeGuard::new_with_data(unsafe { T::from_pointer(ptr) }, |d| {
67+
d.into_pointer();
68+
})
2469
}
2570

2671
struct Tables<T: Type + ?Sized>(T);
2772
impl<T: Type + ?Sized> Tables<T> {
2873
const CONTEXT: bindings::fs_context_operations = bindings::fs_context_operations {
2974
free: Some(Self::free_callback),
30-
parse_param: None,
75+
parse_param: Some(Self::parse_param_callback),
3176
get_tree: Some(Self::get_tree_callback),
3277
reconfigure: Some(Self::reconfigure_callback),
33-
parse_monolithic: None,
78+
parse_monolithic: if <T::Context as Context<T>>::HAS_PARSE_MONOLITHIC {
79+
Some(Self::parse_monolithic_callback)
80+
} else {
81+
None
82+
},
3483
dup: None,
3584
};
3685

@@ -44,6 +93,54 @@ impl<T: Type + ?Sized> Tables<T> {
4493
}
4594
}
4695

96+
unsafe extern "C" fn parse_param_callback(
97+
fc: *mut bindings::fs_context,
98+
param: *mut bindings::fs_parameter,
99+
) -> core::ffi::c_int {
100+
from_kernel_result! {
101+
// SAFETY: The callback contract guarantees that `fc` is valid.
102+
let ptr = unsafe { (*fc).fs_private };
103+
104+
// SAFETY: The value of `ptr` (coming from `fs_private` was initialised in
105+
// `init_fs_context_callback` to the result of an `into_pointer` call. Since the
106+
// context is valid, `from_pointer` wasn't called yet, so `ptr` is valid. Additionally,
107+
// the callback contract guarantees that callbacks are serialised, so it is ok to
108+
// mutably reference it.
109+
let mut data = unsafe { borrow_mut::<<T::Context as Context<T>>::Data>(ptr) };
110+
let mut result = bindings::fs_parse_result::default();
111+
// SAFETY: All parameters are valid at least for the duration of the call.
112+
let opt =
113+
unsafe { bindings::fs_parse(fc, T::Context::PARAMS.first, param, &mut result) };
114+
115+
// SAFETY: The callback contract guarantees that `param` is valid for the duration of
116+
// the callback.
117+
let param = unsafe { &*param };
118+
if opt >= 0 {
119+
let opt = opt as usize;
120+
if opt >= T::Context::PARAMS.handlers.len() {
121+
return Err(EINVAL);
122+
}
123+
T::Context::PARAMS.handlers[opt].handle_param(&mut data, param, &result)?;
124+
return Ok(0);
125+
}
126+
127+
if opt != ENOPARAM.to_kernel_errno() {
128+
return Err(Error::from_kernel_errno(opt));
129+
}
130+
131+
if !T::Context::HAS_PARSE_UNKNOWN_PARAM {
132+
return Err(ENOPARAM);
133+
}
134+
135+
let val = param::Value::from_fs_parameter(param);
136+
// SAFETY: The callback contract guarantees the parameter key to be valid and last at
137+
// least the duration of the callback.
138+
T::Context::parse_unknown_param(
139+
&mut data, unsafe { CStr::from_char_ptr(param.key) }, val)?;
140+
Ok(0)
141+
}
142+
}
143+
47144
unsafe extern "C" fn fill_super_callback(
48145
sb_ptr: *mut bindings::super_block,
49146
_fc: *mut bindings::fs_context,
@@ -64,6 +161,8 @@ impl<T: Type + ?Sized> Tables<T> {
64161
sb.s_time_gran = 1;
65162

66163
// Create and initialise the root inode.
164+
165+
// SAFETY: `sb` was just created initialised, so it is safe pass it to `new_inode`.
67166
let inode = unsafe { bindings::new_inode(sb) };
68167
if inode.is_null() {
69168
return Err(ENOMEM);
@@ -117,6 +216,39 @@ impl<T: Type + ?Sized> Tables<T> {
117216
EINVAL.to_kernel_errno()
118217
}
119218

219+
unsafe extern "C" fn parse_monolithic_callback(
220+
fc: *mut bindings::fs_context,
221+
buf: *mut core::ffi::c_void,
222+
) -> core::ffi::c_int {
223+
from_kernel_result! {
224+
// SAFETY: The callback contract guarantees that `fc` is valid.
225+
let ptr = unsafe { (*fc).fs_private };
226+
227+
// SAFETY: The value of `ptr` (coming from `fs_private` was initialised in
228+
// `init_fs_context_callback` to the result of an `into_pointer` call. Since the
229+
// context is valid, `from_pointer` wasn't called yet, so `ptr` is valid. Additionally,
230+
// the callback contract guarantees that callbacks are serialised, so it is ok to
231+
// mutably reference it.
232+
let mut data = unsafe { borrow_mut::<<T::Context as Context<T>>::Data>(ptr) };
233+
let page = if buf.is_null() {
234+
None
235+
} else {
236+
// SAFETY: This callback is called to handle the `mount` syscall, which takes a
237+
// page-sized buffer as data.
238+
Some(unsafe { &mut *ptr::slice_from_raw_parts_mut(buf.cast(), crate::PAGE_SIZE) })
239+
};
240+
let regular = T::Context::parse_monolithic(&mut data, page)?;
241+
if let Some(buf) = regular {
242+
// SAFETY: Both `fc` and `buf` are guaranteed to be valid; the former because the
243+
// callback is still ongoing and the latter because its lifefime is tied to that of
244+
// `page`, which is also valid for the duration of the callback.
245+
to_result(
246+
unsafe { bindings::generic_parse_monolithic(fc, buf.as_mut_ptr().cast()) })?;
247+
}
248+
Ok(0)
249+
}
250+
}
251+
120252
const SUPER_BLOCK: bindings::super_operations = bindings::super_operations {
121253
alloc_inode: None,
122254
destroy_inode: None,
@@ -240,6 +372,7 @@ impl Registration {
240372
fs.owner = module.0;
241373
fs.name = T::NAME.as_char_ptr();
242374
fs.fs_flags = T::FLAGS;
375+
fs.parameters = T::Context::PARAMS.first;
243376
fs.init_fs_context = Some(Self::init_fs_context_callback::<T>);
244377
fs.kill_sb = Some(Self::kill_sb_callback::<T>);
245378

@@ -372,3 +505,21 @@ unsafe impl AlwaysRefCounted for DEntry {
372505
unsafe { bindings::dput(obj.cast().as_ptr()) }
373506
}
374507
}
508+
509+
/// Wraps the kernel's `struct filename`.
510+
#[repr(transparent)]
511+
pub struct Filename(pub(crate) UnsafeCell<bindings::filename>);
512+
513+
impl Filename {
514+
/// Creates a reference to a [`Filename`] from a valid pointer.
515+
///
516+
/// # Safety
517+
///
518+
/// The caller must ensure that `ptr` is valid and remains valid for the lifetime of the
519+
/// returned [`Filename`] instance.
520+
pub(crate) unsafe fn from_ptr<'a>(ptr: *const bindings::filename) -> &'a Filename {
521+
// SAFETY: The safety requirements guarantee the validity of the dereference, while the
522+
// `Filename` type being transparent makes the cast ok.
523+
unsafe { &*ptr.cast() }
524+
}
525+
}

0 commit comments

Comments
 (0)