Skip to content

Commit 1781c4c

Browse files
authored
Merge pull request #818 from wedsonaf/fs-context
rust: define fs context
2 parents dab7c39 + 3c3ccce commit 1781c4c

File tree

5 files changed

+248
-7
lines changed

5 files changed

+248
-7
lines changed

rust/bindings/bindings_helper.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#include <linux/uaccess.h>
3737
#include <linux/uio.h>
3838
#include <uapi/linux/android/binder.h>
39+
#include <linux/fs_parser.h>
3940

4041
/* `bindgen` gets confused at certain things. */
4142
const gfp_t BINDINGS_GFP_KERNEL = GFP_KERNEL;
@@ -44,3 +45,5 @@ const __poll_t BINDINGS_EPOLLIN = EPOLLIN;
4445
const __poll_t BINDINGS_EPOLLOUT = EPOLLOUT;
4546
const __poll_t BINDINGS_EPOLLERR = EPOLLERR;
4647
const __poll_t BINDINGS_EPOLLHUP = EPOLLHUP;
48+
49+
const loff_t BINDINGS_MAX_LFS_FILESIZE = MAX_LFS_FILESIZE;

rust/bindings/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,5 @@ pub use bindings_raw::*;
5353
pub const GFP_KERNEL: gfp_t = BINDINGS_GFP_KERNEL;
5454
pub const __GFP_ZERO: gfp_t = BINDINGS___GFP_ZERO;
5555
pub const __GFP_HIGHMEM: gfp_t = ___GFP_HIGHMEM;
56+
57+
pub const MAX_LFS_FILESIZE: loff_t = BINDINGS_MAX_LFS_FILESIZE;

rust/helpers.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -633,6 +633,18 @@ struct dentry *rust_helper_dget(struct dentry *dentry)
633633
}
634634
EXPORT_SYMBOL_GPL(rust_helper_dget);
635635

636+
void rust_helper_lockdep_register_key(struct lock_class_key *key)
637+
{
638+
lockdep_register_key(key);
639+
}
640+
EXPORT_SYMBOL_GPL(rust_helper_lockdep_register_key);
641+
642+
void rust_helper_lockdep_unregister_key(struct lock_class_key *key)
643+
{
644+
lockdep_unregister_key(key);
645+
}
646+
EXPORT_SYMBOL_GPL(rust_helper_lockdep_unregister_key);
647+
636648
/*
637649
* We use `bindgen`'s `--size_t-is-usize` option to bind the C `size_t` type
638650
* as the Rust `usize` type, so we can use it in contexts where Rust

rust/kernel/fs.rs

Lines changed: 218 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,164 @@
44
//!
55
//! C headers: [`include/linux/fs.h`](../../../../include/linux/fs.h)
66
7-
use crate::{bindings, error::code::*, str::CStr, to_result, AlwaysRefCounted, Result, ThisModule};
7+
use crate::{
8+
bindings, error::code::*, error::from_kernel_result, str::CStr, to_result,
9+
types::PointerWrapper, AlwaysRefCounted, Result, ScopeGuard, ThisModule,
10+
};
811
use core::{cell::UnsafeCell, marker::PhantomPinned, pin::Pin, ptr};
12+
use macros::vtable;
13+
14+
/// A file system context.
15+
///
16+
/// It is used to gather configuration to then mount or reconfigure a file system.
17+
#[vtable]
18+
pub trait Context<T: Type + ?Sized> {
19+
/// Type of the data associated with the context.
20+
type Data: PointerWrapper + Send + Sync + 'static;
21+
22+
/// Creates a new context.
23+
fn try_new() -> Result<Self::Data>;
24+
}
25+
26+
struct Tables<T: Type + ?Sized>(T);
27+
impl<T: Type + ?Sized> Tables<T> {
28+
const CONTEXT: bindings::fs_context_operations = bindings::fs_context_operations {
29+
free: Some(Self::free_callback),
30+
parse_param: None,
31+
get_tree: Some(Self::get_tree_callback),
32+
reconfigure: Some(Self::reconfigure_callback),
33+
parse_monolithic: None,
34+
dup: None,
35+
};
36+
37+
unsafe extern "C" fn free_callback(fc: *mut bindings::fs_context) {
38+
// SAFETY: The callback contract guarantees that `fc` is valid.
39+
let ptr = unsafe { (*fc).fs_private };
40+
if !ptr.is_null() {
41+
// SAFETY: `fs_private` was initialised with the result of a `to_pointer` call in
42+
// `init_fs_context_callback`, so it's ok to call `from_pointer` here.
43+
unsafe { <T::Context as Context<T>>::Data::from_pointer(ptr) };
44+
}
45+
}
46+
47+
unsafe extern "C" fn fill_super_callback(
48+
sb_ptr: *mut bindings::super_block,
49+
_fc: *mut bindings::fs_context,
50+
) -> core::ffi::c_int {
51+
from_kernel_result! {
52+
// The following is temporary code to create the root inode and dentry. It will be
53+
// replaced with calls to Rust code.
54+
55+
// SAFETY: The callback contract guarantees that `sb_ptr` is the only pointer to a
56+
// newly-allocated superblock, so it is safe to mutably reference it.
57+
let sb = unsafe { &mut *sb_ptr };
58+
59+
sb.s_maxbytes = bindings::MAX_LFS_FILESIZE;
60+
sb.s_blocksize = crate::PAGE_SIZE as _;
61+
sb.s_blocksize_bits = bindings::PAGE_SHIFT as _;
62+
sb.s_magic = T::MAGIC as _;
63+
sb.s_op = &Tables::<T>::SUPER_BLOCK;
64+
sb.s_time_gran = 1;
65+
66+
// Create and initialise the root inode.
67+
let inode = unsafe { bindings::new_inode(sb) };
68+
if inode.is_null() {
69+
return Err(ENOMEM);
70+
}
71+
72+
{
73+
// SAFETY: This is a newly-created inode. No other references to it exist, so it is
74+
// safe to mutably dereference it.
75+
let inode = unsafe { &mut *inode };
76+
77+
// SAFETY: `current_time` requires that `inode.sb` be valid, which is the case here
78+
// since we allocated the inode through the superblock.
79+
let time = unsafe { bindings::current_time(inode) };
80+
inode.i_ino = 1;
81+
inode.i_mode = (bindings::S_IFDIR | 0o755) as _;
82+
inode.i_mtime = time;
83+
inode.i_atime = time;
84+
inode.i_ctime = time;
85+
86+
// SAFETY: `simple_dir_operations` never changes, it's safe to reference it.
87+
inode.__bindgen_anon_3.i_fop = unsafe { &bindings::simple_dir_operations };
88+
89+
// SAFETY: `simple_dir_inode_operations` never changes, it's safe to reference it.
90+
inode.i_op = unsafe { &bindings::simple_dir_inode_operations };
91+
92+
// SAFETY: `inode` is valid for write.
93+
unsafe { bindings::set_nlink(inode, 2) };
94+
}
95+
96+
// SAFETY: `d_make_root` requires that `inode` be valid and referenced, which is the
97+
// case for this call.
98+
//
99+
// It takes over the inode, even on failure, so we don't need to clean it up.
100+
let dentry = unsafe { bindings::d_make_root(inode) };
101+
if dentry.is_null() {
102+
return Err(ENOMEM);
103+
}
104+
105+
sb.s_root = dentry;
106+
Ok(0)
107+
}
108+
}
109+
110+
unsafe extern "C" fn get_tree_callback(fc: *mut bindings::fs_context) -> core::ffi::c_int {
111+
// SAFETY: `fc` is valid per the callback contract. `fill_super_callback` also has the
112+
// right type and is a valid callback.
113+
unsafe { bindings::get_tree_nodev(fc, Some(Self::fill_super_callback)) }
114+
}
115+
116+
unsafe extern "C" fn reconfigure_callback(_fc: *mut bindings::fs_context) -> core::ffi::c_int {
117+
EINVAL.to_kernel_errno()
118+
}
119+
120+
const SUPER_BLOCK: bindings::super_operations = bindings::super_operations {
121+
alloc_inode: None,
122+
destroy_inode: None,
123+
free_inode: None,
124+
dirty_inode: None,
125+
write_inode: None,
126+
drop_inode: None,
127+
evict_inode: None,
128+
put_super: None,
129+
sync_fs: None,
130+
freeze_super: None,
131+
freeze_fs: None,
132+
thaw_super: None,
133+
unfreeze_fs: None,
134+
statfs: None,
135+
remount_fs: None,
136+
umount_begin: None,
137+
show_options: None,
138+
show_devname: None,
139+
show_path: None,
140+
show_stats: None,
141+
#[cfg(CONFIG_QUOTA)]
142+
quota_read: None,
143+
#[cfg(CONFIG_QUOTA)]
144+
quota_write: None,
145+
#[cfg(CONFIG_QUOTA)]
146+
get_dquots: None,
147+
nr_cached_objects: None,
148+
free_cached_objects: None,
149+
};
150+
}
9151

10152
/// A file system type.
11153
pub trait Type {
154+
/// The context used to build fs configuration before it is mounted or reconfigured.
155+
type Context: Context<Self> + ?Sized;
156+
12157
/// The name of the file system type.
13158
const NAME: &'static CStr;
14159

160+
/// The magic number associated with the file system.
161+
///
162+
/// This is normally one of the values in `include/uapi/linux/magic.h`.
163+
const MAGIC: u32;
164+
15165
/// The flags of this file system type.
16166
///
17167
/// It is a combination of the flags in the [`flags`] module.
@@ -78,7 +228,7 @@ impl Registration {
78228
/// The file system is described by the [`Type`] argument.
79229
///
80230
/// It is automatically unregistered when the registration is dropped.
81-
pub fn register<T: Type>(self: Pin<&mut Self>, module: &'static ThisModule) -> Result {
231+
pub fn register<T: Type + ?Sized>(self: Pin<&mut Self>, module: &'static ThisModule) -> Result {
82232
// SAFETY: We never move out of `this`.
83233
let this = unsafe { self.get_unchecked_mut() };
84234

@@ -92,20 +242,81 @@ impl Registration {
92242
fs.fs_flags = T::FLAGS;
93243
fs.init_fs_context = Some(Self::init_fs_context_callback::<T>);
94244
fs.kill_sb = Some(Self::kill_sb_callback::<T>);
245+
246+
// SAFETY: This block registers all fs type keys with lockdep. We just need the memory
247+
// locations to be owned by the caller, which is the case.
248+
unsafe {
249+
bindings::lockdep_register_key(&mut fs.s_lock_key);
250+
bindings::lockdep_register_key(&mut fs.s_umount_key);
251+
bindings::lockdep_register_key(&mut fs.s_vfs_rename_key);
252+
bindings::lockdep_register_key(&mut fs.i_lock_key);
253+
bindings::lockdep_register_key(&mut fs.i_mutex_key);
254+
bindings::lockdep_register_key(&mut fs.invalidate_lock_key);
255+
bindings::lockdep_register_key(&mut fs.i_mutex_dir_key);
256+
for key in &mut fs.s_writers_key {
257+
bindings::lockdep_register_key(key);
258+
}
259+
}
260+
261+
let ptr = this.fs.get();
262+
263+
// SAFETY: `ptr` as valid as it points to the `self.fs`.
264+
let key_guard = ScopeGuard::new(|| unsafe { Self::unregister_keys(ptr) });
265+
95266
// SAFETY: Pointers stored in `fs` are either static so will live for as long as the
96267
// registration is active (it is undone in `drop`).
97-
to_result(unsafe { bindings::register_filesystem(this.fs.get()) })?;
268+
to_result(unsafe { bindings::register_filesystem(ptr) })?;
269+
key_guard.dismiss();
98270
this.is_registered = true;
99271
Ok(())
100272
}
101273

102-
unsafe extern "C" fn init_fs_context_callback<T: Type>(
103-
_fc_ptr: *mut bindings::fs_context,
274+
/// Unregisters the lockdep keys in the file system type.
275+
///
276+
/// # Safety
277+
///
278+
/// `fs` must be non-null and valid.
279+
unsafe fn unregister_keys(fs: *mut bindings::file_system_type) {
280+
// SAFETY: This block unregisters all fs type keys from lockdep. They must have been
281+
// registered before.
282+
unsafe {
283+
bindings::lockdep_unregister_key(ptr::addr_of_mut!((*fs).s_lock_key));
284+
bindings::lockdep_unregister_key(ptr::addr_of_mut!((*fs).s_umount_key));
285+
bindings::lockdep_unregister_key(ptr::addr_of_mut!((*fs).s_vfs_rename_key));
286+
bindings::lockdep_unregister_key(ptr::addr_of_mut!((*fs).i_lock_key));
287+
bindings::lockdep_unregister_key(ptr::addr_of_mut!((*fs).i_mutex_key));
288+
bindings::lockdep_unregister_key(ptr::addr_of_mut!((*fs).invalidate_lock_key));
289+
bindings::lockdep_unregister_key(ptr::addr_of_mut!((*fs).i_mutex_dir_key));
290+
for i in 0..(*fs).s_writers_key.len() {
291+
bindings::lockdep_unregister_key(ptr::addr_of_mut!((*fs).s_writers_key[i]));
292+
}
293+
}
294+
}
295+
296+
unsafe extern "C" fn init_fs_context_callback<T: Type + ?Sized>(
297+
fc_ptr: *mut bindings::fs_context,
104298
) -> core::ffi::c_int {
105-
EINVAL.to_kernel_errno()
299+
from_kernel_result! {
300+
let data = T::Context::try_new()?;
301+
// SAFETY: The callback contract guarantees that `fc_ptr` is the only pointer to a
302+
// newly-allocated fs context, so it is safe to mutably reference it.
303+
let fc = unsafe { &mut *fc_ptr };
304+
fc.fs_private = data.into_pointer() as _;
305+
fc.ops = &Tables::<T>::CONTEXT;
306+
Ok(0)
307+
}
106308
}
107309

108-
unsafe extern "C" fn kill_sb_callback<T: Type>(_sb_ptr: *mut bindings::super_block) {}
310+
unsafe extern "C" fn kill_sb_callback<T: Type + ?Sized>(sb_ptr: *mut bindings::super_block) {
311+
// SAFETY: We always call `get_tree_nodev` from `get_tree_callback`, so we never have a
312+
// device, so it is ok to call the function below. Additionally, the callback contract
313+
// guarantees that `sb_ptr` is valid.
314+
unsafe { bindings::kill_anon_super(sb_ptr) }
315+
316+
// SAFETY: The callback contract guarantees that `sb_ptr` is valid, and the `kill_sb`
317+
// callback being called implies that the `s_type` is also valid.
318+
unsafe { Self::unregister_keys((*sb_ptr).s_type) };
319+
}
109320
}
110321

111322
impl Drop for Registration {

samples/rust/rust_fs.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,22 @@ module! {
1313
}
1414

1515
struct RustFs;
16+
17+
#[vtable]
18+
impl fs::Context<Self> for RustFs {
19+
type Data = ();
20+
21+
fn try_new() -> Result {
22+
pr_info!("context created!\n");
23+
Ok(())
24+
}
25+
}
26+
1627
impl fs::Type for RustFs {
28+
type Context = Self;
1729
const NAME: &'static CStr = c_str!("rustfs");
1830
const FLAGS: i32 = fs::flags::USERNS_MOUNT;
31+
const MAGIC: u32 = 0x72757374;
1932
}
2033

2134
struct FsModule {

0 commit comments

Comments
 (0)