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 } ;
1010use crate :: types:: Opaque ;
1111use crate :: { bindings, init:: PinInit , str:: CStr , try_pin_init, ThisModule } ;
1212use core:: { marker:: PhantomData , marker:: PhantomPinned , pin:: Pin } ;
1313use 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.
1619pub trait FileSystem {
1720 /// The name of the file system type.
1821 const NAME : & ' static CStr ;
22+
23+ /// Initialises a super block for this file system type.
24+ fn fill_super ( sb : NewSuperBlock < ' _ , Self > ) -> Result < & SuperBlock < Self > > ;
1925}
2026
2127/// A registration of a file system.
@@ -46,7 +52,7 @@ impl Registration {
4652 * fs = bindings:: file_system_type:: default ( ) ;
4753 fs. owner = module. 0 ;
4854 fs. name = T :: NAME . as_char_ptr( ) ;
49- fs. init_fs_context = Some ( Self :: init_fs_context_callback) ;
55+ fs. init_fs_context = Some ( Self :: init_fs_context_callback:: < T > ) ;
5056 fs. kill_sb = Some ( Self :: kill_sb_callback) ;
5157 fs. fs_flags = 0 ;
5258
@@ -57,13 +63,22 @@ impl Registration {
5763 } )
5864 }
5965
60- unsafe extern "C" fn init_fs_context_callback (
61- _fc_ptr : * mut bindings:: fs_context ,
66+ unsafe extern "C" fn init_fs_context_callback < T : FileSystem + ? Sized > (
67+ fc_ptr : * mut bindings:: fs_context ,
6268 ) -> core:: ffi:: c_int {
63- from_result ( || Err ( ENOTSUPP ) )
69+ from_result ( || {
70+ // SAFETY: The C callback API guarantees that `fc_ptr` is valid.
71+ let fc = unsafe { & mut * fc_ptr } ;
72+ fc. ops = & Tables :: < T > :: CONTEXT ;
73+ Ok ( 0 )
74+ } )
6475 }
6576
66- unsafe extern "C" fn kill_sb_callback ( _sb_ptr : * mut bindings:: super_block ) { }
77+ unsafe extern "C" fn kill_sb_callback ( sb_ptr : * mut bindings:: super_block ) {
78+ // SAFETY: In `get_tree_callback` we always call `get_tree_nodev`, so `kill_anon_super` is
79+ // the appropriate function to call for cleanup.
80+ unsafe { bindings:: kill_anon_super ( sb_ptr) } ;
81+ }
6782}
6883
6984#[ pinned_drop]
@@ -76,6 +91,178 @@ impl PinnedDrop for Registration {
7691 }
7792}
7893
94+ /// A file system super block.
95+ ///
96+ /// Wraps the kernel's `struct super_block`.
97+ #[ repr( transparent) ]
98+ pub struct SuperBlock < T : FileSystem + ?Sized > ( Opaque < bindings:: super_block > , PhantomData < T > ) ;
99+
100+ /// Required superblock parameters.
101+ ///
102+ /// This is used in [`NewSuperBlock::init`].
103+ pub struct SuperParams {
104+ /// The magic number of the superblock.
105+ pub magic : u32 ,
106+
107+ /// The size of a block in powers of 2 (i.e., for a value of `n`, the size is `2^n`).
108+ pub blocksize_bits : u8 ,
109+
110+ /// Maximum size of a file.
111+ ///
112+ /// The maximum allowed value is [`MAX_LFS_FILESIZE`].
113+ pub maxbytes : i64 ,
114+
115+ /// Granularity of c/m/atime in ns (cannot be worse than a second).
116+ pub time_gran : u32 ,
117+ }
118+
119+ /// A superblock that is still being initialised.
120+ ///
121+ /// # Invariants
122+ ///
123+ /// The superblock is a newly-created one and this is the only active pointer to it.
124+ pub struct NewSuperBlock < ' a , T : FileSystem + ?Sized > {
125+ sb : & ' a mut SuperBlock < T > ,
126+
127+ // This also forces `'a` to be invariant.
128+ _p : PhantomData < & ' a mut & ' a ( ) > ,
129+ }
130+
131+ impl < ' a , T : FileSystem + ?Sized > NewSuperBlock < ' a , T > {
132+ /// Creates a new instance of [`NewSuperBlock`].
133+ ///
134+ /// # Safety
135+ ///
136+ /// `sb` must point to a newly-created superblock and it must be the only active pointer to it.
137+ unsafe fn new ( sb : * mut bindings:: super_block ) -> Self {
138+ // INVARIANT: The invariants are satisfied by the safety requirements of this function.
139+ Self {
140+ // SAFETY: The safety requirements ensure that `sb` is valid for read and write.
141+ sb : unsafe { & mut * sb. cast ( ) } ,
142+ _p : PhantomData ,
143+ }
144+ }
145+
146+ /// Initialises the superblock.
147+ pub fn init ( self , params : & SuperParams ) -> Result < & ' a SuperBlock < T > > {
148+ // SAFETY: Since this is a new super block, we hold the only reference to it.
149+ let sb = unsafe { & mut * self . sb . 0 . get ( ) } ;
150+
151+ sb. s_magic = params. magic as _ ;
152+ sb. s_op = & Tables :: < T > :: SUPER_BLOCK ;
153+ sb. s_maxbytes = params. maxbytes ;
154+ sb. s_time_gran = params. time_gran ;
155+ sb. s_blocksize_bits = params. blocksize_bits ;
156+ sb. s_blocksize = 1 ;
157+ if sb. s_blocksize . leading_zeros ( ) < params. blocksize_bits . into ( ) {
158+ return Err ( EINVAL ) ;
159+ }
160+ sb. s_blocksize = 1 << sb. s_blocksize_bits ;
161+ sb. s_flags |= bindings:: SB_RDONLY ;
162+
163+ // The following is scaffolding code that will be removed in a subsequent patch. It is
164+ // needed to build a root dentry, otherwise core code will BUG().
165+ // SAFETY: `sb` is the superblock being initialised, it is valid for read and write.
166+ let inode = unsafe { bindings:: new_inode ( sb) } ;
167+ if inode. is_null ( ) {
168+ return Err ( ENOMEM ) ;
169+ }
170+
171+ // SAFETY: `inode` is valid for write.
172+ unsafe { bindings:: set_nlink ( inode, 2 ) } ;
173+
174+ {
175+ // SAFETY: This is a newly-created inode. No other references to it exist, so it is
176+ // safe to mutably dereference it.
177+ let inode = unsafe { & mut * inode } ;
178+ inode. i_ino = 1 ;
179+ inode. i_mode = ( bindings:: S_IFDIR | 0o755 ) as _ ;
180+
181+ // SAFETY: `simple_dir_operations` never changes, it's safe to reference it.
182+ inode. __bindgen_anon_3 . i_fop = unsafe { & bindings:: simple_dir_operations } ;
183+
184+ // SAFETY: `simple_dir_inode_operations` never changes, it's safe to reference it.
185+ inode. i_op = unsafe { & bindings:: simple_dir_inode_operations } ;
186+ }
187+
188+ // SAFETY: `d_make_root` requires that `inode` be valid and referenced, which is the
189+ // case for this call.
190+ //
191+ // It takes over the inode, even on failure, so we don't need to clean it up.
192+ let dentry = unsafe { bindings:: d_make_root ( inode) } ;
193+ if dentry. is_null ( ) {
194+ return Err ( ENOMEM ) ;
195+ }
196+
197+ sb. s_root = dentry;
198+
199+ Ok ( self . sb )
200+ }
201+ }
202+
203+ struct Tables < T : FileSystem + ?Sized > ( T ) ;
204+ impl < T : FileSystem + ?Sized > Tables < T > {
205+ const CONTEXT : bindings:: fs_context_operations = bindings:: fs_context_operations {
206+ free : None ,
207+ parse_param : None ,
208+ get_tree : Some ( Self :: get_tree_callback) ,
209+ reconfigure : None ,
210+ parse_monolithic : None ,
211+ dup : None ,
212+ } ;
213+
214+ unsafe extern "C" fn get_tree_callback ( fc : * mut bindings:: fs_context ) -> core:: ffi:: c_int {
215+ // SAFETY: `fc` is valid per the callback contract. `fill_super_callback` also has
216+ // the right type and is a valid callback.
217+ unsafe { bindings:: get_tree_nodev ( fc, Some ( Self :: fill_super_callback) ) }
218+ }
219+
220+ unsafe extern "C" fn fill_super_callback (
221+ sb_ptr : * mut bindings:: super_block ,
222+ _fc : * mut bindings:: fs_context ,
223+ ) -> core:: ffi:: c_int {
224+ from_result ( || {
225+ // SAFETY: The callback contract guarantees that `sb_ptr` is a unique pointer to a
226+ // newly-created superblock.
227+ let newsb = unsafe { NewSuperBlock :: new ( sb_ptr) } ;
228+ T :: fill_super ( newsb) ?;
229+ Ok ( 0 )
230+ } )
231+ }
232+
233+ const SUPER_BLOCK : bindings:: super_operations = bindings:: super_operations {
234+ alloc_inode : None ,
235+ destroy_inode : None ,
236+ free_inode : None ,
237+ dirty_inode : None ,
238+ write_inode : None ,
239+ drop_inode : None ,
240+ evict_inode : None ,
241+ put_super : None ,
242+ sync_fs : None ,
243+ freeze_super : None ,
244+ freeze_fs : None ,
245+ thaw_super : None ,
246+ unfreeze_fs : None ,
247+ statfs : None ,
248+ remount_fs : None ,
249+ umount_begin : None ,
250+ show_options : None ,
251+ show_devname : None ,
252+ show_path : None ,
253+ show_stats : None ,
254+ #[ cfg( CONFIG_QUOTA ) ]
255+ quota_read : None ,
256+ #[ cfg( CONFIG_QUOTA ) ]
257+ quota_write : None ,
258+ #[ cfg( CONFIG_QUOTA ) ]
259+ get_dquots : None ,
260+ nr_cached_objects : None ,
261+ free_cached_objects : None ,
262+ shutdown : None ,
263+ } ;
264+ }
265+
79266/// Kernel module that exposes a single file system implemented by `T`.
80267#[ pin_data]
81268pub struct Module < T : FileSystem + ?Sized > {
@@ -95,13 +282,14 @@ impl<T: FileSystem + ?Sized + Sync + Send> crate::InPlaceModule for Module<T> {
95282
96283/// Declares a kernel module that exposes a single file system.
97284///
98- /// The `type` argument must be a type which implements the [`FileSystem`] trait. Also accepts
99- /// various forms of kernel metadata.
285+ /// The `type` argument must be a type which implements the [`FileSystem`] trait. Also accepts various
286+ /// forms of kernel metadata.
100287///
101288/// # Examples
102289///
103290/// ```
104- /// # mod module_fs_sample {
291+ /// # mod module_ro_fs_sample {
292+ /// use kernel::fs::{NewSuperBlock, SuperBlock};
105293/// use kernel::prelude::*;
106294/// use kernel::{c_str, fs};
107295///
@@ -116,6 +304,9 @@ impl<T: FileSystem + ?Sized + Sync + Send> crate::InPlaceModule for Module<T> {
116304/// struct MyFs;
117305/// impl fs::FileSystem for MyFs {
118306/// const NAME: &'static CStr = c_str!("myfs");
307+ /// fn fill_super(_: NewSuperBlock<'_, Self>) -> Result<&SuperBlock<Self>> {
308+ /// todo!()
309+ /// }
119310/// }
120311/// # }
121312/// ```
0 commit comments