@@ -22,6 +22,81 @@ pub trait FileSystem {
2222
2323 /// Initialises a super block for this file system type.
2424 fn fill_super ( sb : NewSuperBlock < ' _ , Self > ) -> Result < & SuperBlock < Self > > ;
25+
26+ /// Reads directory entries from directory inodes.
27+ ///
28+ /// `pos` is the current position of the directory reader.
29+ ///
30+ /// `report` is a closure that takes the following arguments, in order: the entry name, its
31+ /// position, its inode number, and its type. It returns `true` if the entry was copied to
32+ /// caller or `false` otherwise (in which case, the `read_dir` implementation is advised to
33+ /// stop calling `report`).
34+ ///
35+ /// On success, returns the new position of the reader.
36+ fn read_dir (
37+ inode : & INode < Self > ,
38+ pos : i64 ,
39+ report : impl FnMut ( & [ u8 ] , i64 , u64 , DirEntryType ) -> bool ,
40+ ) -> Result < i64 > ;
41+ }
42+
43+ /// The types of directory entries reported by [`FileSystem::read_dir`].
44+ #[ repr( u32 ) ]
45+ #[ derive( Copy , Clone ) ]
46+ pub enum DirEntryType {
47+ /// Unknown type.
48+ Unknown = bindings:: DT_UNKNOWN ,
49+
50+ /// Named pipe (first-in, first-out) type.
51+ Fifo = bindings:: DT_FIFO ,
52+
53+ /// Character device type.
54+ Chr = bindings:: DT_CHR ,
55+
56+ /// Directory type.
57+ Dir = bindings:: DT_DIR ,
58+
59+ /// Block device type.
60+ Blk = bindings:: DT_BLK ,
61+
62+ /// Regular file type.
63+ Reg = bindings:: DT_REG ,
64+
65+ /// Symbolic link type.
66+ Lnk = bindings:: DT_LNK ,
67+
68+ /// Named unix-domain socket type.
69+ Sock = bindings:: DT_SOCK ,
70+
71+ /// White-out type.
72+ Wht = bindings:: DT_WHT ,
73+ }
74+
75+ impl From < INodeType > for DirEntryType {
76+ fn from ( value : INodeType ) -> Self {
77+ match value {
78+ INodeType :: Dir => DirEntryType :: Dir ,
79+ }
80+ }
81+ }
82+
83+ impl core:: convert:: TryFrom < u32 > for DirEntryType {
84+ type Error = crate :: error:: Error ;
85+
86+ fn try_from ( v : u32 ) -> Result < Self > {
87+ match v {
88+ v if v == Self :: Unknown as u32 => Ok ( Self :: Unknown ) ,
89+ v if v == Self :: Fifo as u32 => Ok ( Self :: Fifo ) ,
90+ v if v == Self :: Chr as u32 => Ok ( Self :: Chr ) ,
91+ v if v == Self :: Dir as u32 => Ok ( Self :: Dir ) ,
92+ v if v == Self :: Blk as u32 => Ok ( Self :: Blk ) ,
93+ v if v == Self :: Reg as u32 => Ok ( Self :: Reg ) ,
94+ v if v == Self :: Lnk as u32 => Ok ( Self :: Lnk ) ,
95+ v if v == Self :: Sock as u32 => Ok ( Self :: Sock ) ,
96+ v if v == Self :: Wht as u32 => Ok ( Self :: Wht ) ,
97+ _ => Err ( EDOM ) ,
98+ }
99+ }
25100}
26101
27102/// A registration of a file system.
@@ -149,9 +224,7 @@ impl<T: FileSystem + ?Sized> NewINode<T> {
149224
150225 let mode = match params. typ {
151226 INodeType :: Dir => {
152- // SAFETY: `simple_dir_operations` never changes, it's safe to reference it.
153- inode. __bindgen_anon_3 . i_fop = unsafe { & bindings:: simple_dir_operations } ;
154-
227+ inode. __bindgen_anon_3 . i_fop = & Tables :: < T > :: DIR_FILE_OPERATIONS ;
155228 // SAFETY: `simple_dir_inode_operations` never changes, it's safe to reference it.
156229 inode. i_op = unsafe { & bindings:: simple_dir_inode_operations } ;
157230 bindings:: S_IFDIR
@@ -419,6 +492,70 @@ impl<T: FileSystem + ?Sized> Tables<T> {
419492 free_cached_objects : None ,
420493 shutdown : None ,
421494 } ;
495+
496+ const DIR_FILE_OPERATIONS : bindings:: file_operations = bindings:: file_operations {
497+ owner : ptr:: null_mut ( ) ,
498+ llseek : Some ( bindings:: generic_file_llseek) ,
499+ read : Some ( bindings:: generic_read_dir) ,
500+ write : None ,
501+ read_iter : None ,
502+ write_iter : None ,
503+ iopoll : None ,
504+ iterate_shared : Some ( Self :: read_dir_callback) ,
505+ poll : None ,
506+ unlocked_ioctl : None ,
507+ compat_ioctl : None ,
508+ mmap : None ,
509+ mmap_supported_flags : 0 ,
510+ open : None ,
511+ flush : None ,
512+ release : None ,
513+ fsync : None ,
514+ fasync : None ,
515+ lock : None ,
516+ get_unmapped_area : None ,
517+ check_flags : None ,
518+ flock : None ,
519+ splice_write : None ,
520+ splice_read : None ,
521+ splice_eof : None ,
522+ setlease : None ,
523+ fallocate : None ,
524+ show_fdinfo : None ,
525+ copy_file_range : None ,
526+ remap_file_range : None ,
527+ fadvise : None ,
528+ uring_cmd : None ,
529+ uring_cmd_iopoll : None ,
530+ } ;
531+
532+ unsafe extern "C" fn read_dir_callback (
533+ file : * mut bindings:: file ,
534+ ctx_ptr : * mut bindings:: dir_context ,
535+ ) -> core:: ffi:: c_int {
536+ from_result ( || {
537+ // SAFETY: The C API guarantees that `file` is valid for read. And since `f_inode` is
538+ // immutable, we can read it directly.
539+ let inode = unsafe { & * ( * file) . f_inode . cast :: < INode < T > > ( ) } ;
540+
541+ // SAFETY: The C API guarantees that this is the only reference to `dir_context`.
542+ let ctx = unsafe { & mut * ctx_ptr } ;
543+ let new_pos = T :: read_dir ( inode, ctx. pos , |name, foffset, ino, typ| {
544+ let Ok ( name_len) = i32:: try_from ( name. len ( ) ) else {
545+ return false ;
546+ } ;
547+ let Some ( actor) = ctx. actor else {
548+ return false ;
549+ } ;
550+
551+ // SAFETY: `ctx` is valid by the C API convention, and `name` is valid at least for
552+ // the duration of the `actor` call.
553+ unsafe { actor ( ctx, name. as_ptr ( ) . cast ( ) , name_len, foffset, ino, typ as _ ) }
554+ } ) ?;
555+ ctx. pos = new_pos;
556+ Ok ( 0 )
557+ } )
558+ }
422559}
423560
424561/// Kernel module that exposes a single file system implemented by `T`.
@@ -447,7 +584,7 @@ impl<T: FileSystem + ?Sized + Sync + Send> crate::InPlaceModule for Module<T> {
447584///
448585/// ```
449586/// # mod module_ro_fs_sample {
450- /// use kernel::fs::{NewSuperBlock, SuperBlock};
587+ /// use kernel::fs::{DirEntryType, INode, NewSuperBlock, SuperBlock};
451588/// use kernel::prelude::*;
452589/// use kernel::{c_str, fs};
453590///
@@ -465,6 +602,13 @@ impl<T: FileSystem + ?Sized + Sync + Send> crate::InPlaceModule for Module<T> {
465602/// fn fill_super(_: NewSuperBlock<'_, Self>) -> Result<&SuperBlock<Self>> {
466603/// todo!()
467604/// }
605+ /// fn read_dir(
606+ /// _: &INode<Self>,
607+ /// _: i64,
608+ /// _: impl FnMut(&[u8], i64, u64, DirEntryType) -> bool,
609+ /// ) -> Result<i64> {
610+ /// todo!()
611+ /// }
468612/// }
469613/// # }
470614/// ```
0 commit comments