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+ /// 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]
84244pub 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/// ```
0 commit comments