|
| 1 | +// SPDX-License-Identifier: GPL-2.0 |
| 2 | + |
| 3 | +//! Kernel file systems. |
| 4 | +//! |
| 5 | +//! This module allows Rust code to register new kernel file systems. |
| 6 | +//! |
| 7 | +//! C headers: [`include/linux/fs.h`](../../include/linux/fs.h) |
| 8 | +
|
| 9 | +use crate::error::{code::*, from_result, to_result, Error}; |
| 10 | +use crate::types::Opaque; |
| 11 | +use crate::{bindings, init::PinInit, str::CStr, try_pin_init, ThisModule}; |
| 12 | +use core::{marker::PhantomPinned, pin::Pin}; |
| 13 | +use macros::{pin_data, pinned_drop}; |
| 14 | + |
| 15 | +/// A file system type. |
| 16 | +pub trait FileSystem { |
| 17 | + /// The name of the file system type. |
| 18 | + const NAME: &'static CStr; |
| 19 | +} |
| 20 | + |
| 21 | +/// A registration of a file system. |
| 22 | +#[pin_data(PinnedDrop)] |
| 23 | +pub struct Registration { |
| 24 | + #[pin] |
| 25 | + fs: Opaque<bindings::file_system_type>, |
| 26 | + #[pin] |
| 27 | + _pin: PhantomPinned, |
| 28 | +} |
| 29 | + |
| 30 | +// SAFETY: `Registration` doesn't provide any `&self` methods, so it is safe to pass references |
| 31 | +// to it around. |
| 32 | +unsafe impl Sync for Registration {} |
| 33 | + |
| 34 | +// SAFETY: Both registration and unregistration are implemented in C and safe to be performed |
| 35 | +// from any thread, so `Registration` is `Send`. |
| 36 | +unsafe impl Send for Registration {} |
| 37 | + |
| 38 | +impl Registration { |
| 39 | + /// Creates the initialiser of a new file system registration. |
| 40 | + pub fn new<T: FileSystem + ?Sized>(module: &'static ThisModule) -> impl PinInit<Self, Error> { |
| 41 | + try_pin_init!(Self { |
| 42 | + _pin: PhantomPinned, |
| 43 | + fs <- Opaque::try_ffi_init(|fs_ptr: *mut bindings::file_system_type| { |
| 44 | + // SAFETY: `try_ffi_init` guarantees that `fs_ptr` is valid for write. |
| 45 | + unsafe { fs_ptr.write(bindings::file_system_type::default()) }; |
| 46 | + |
| 47 | + // SAFETY: `try_ffi_init` guarantees that `fs_ptr` is valid for write, and it has |
| 48 | + // just been initialised above, so it's also valid for read. |
| 49 | + let fs = unsafe { &mut *fs_ptr }; |
| 50 | + fs.owner = module.0; |
| 51 | + fs.name = T::NAME.as_char_ptr(); |
| 52 | + fs.init_fs_context = Some(Self::init_fs_context_callback); |
| 53 | + fs.kill_sb = Some(Self::kill_sb_callback); |
| 54 | + fs.fs_flags = 0; |
| 55 | + |
| 56 | + // SAFETY: Pointers stored in `fs` are static so will live for as long as the |
| 57 | + // registration is active (it is undone in `drop`). |
| 58 | + to_result(unsafe { bindings::register_filesystem(fs_ptr) }) |
| 59 | + }), |
| 60 | + }) |
| 61 | + } |
| 62 | + |
| 63 | + unsafe extern "C" fn init_fs_context_callback( |
| 64 | + _fc_ptr: *mut bindings::fs_context, |
| 65 | + ) -> core::ffi::c_int { |
| 66 | + from_result(|| Err(ENOTSUPP)) |
| 67 | + } |
| 68 | + |
| 69 | + unsafe extern "C" fn kill_sb_callback(_sb_ptr: *mut bindings::super_block) {} |
| 70 | +} |
| 71 | + |
| 72 | +#[pinned_drop] |
| 73 | +impl PinnedDrop for Registration { |
| 74 | + fn drop(self: Pin<&mut Self>) { |
| 75 | + // SAFETY: If an instance of `Self` has been successfully created, a call to |
| 76 | + // `register_filesystem` has necessarily succeeded. So it's ok to call |
| 77 | + // `unregister_filesystem` on the previously registered fs. |
| 78 | + unsafe { bindings::unregister_filesystem(self.fs.get()) }; |
| 79 | + } |
| 80 | +} |
0 commit comments