@@ -22,6 +22,70 @@ 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+ /// [`DirEmitter::pos`] holds the current position of the directory reader.
29+ fn read_dir ( inode : & INode < Self > , emitter : & mut DirEmitter ) -> Result ;
30+ }
31+
32+ /// The types of directory entries reported by [`FileSystem::read_dir`].
33+ #[ repr( u32 ) ]
34+ #[ derive( Copy , Clone ) ]
35+ pub enum DirEntryType {
36+ /// Unknown type.
37+ Unknown = bindings:: DT_UNKNOWN ,
38+
39+ /// Named pipe (first-in, first-out) type.
40+ Fifo = bindings:: DT_FIFO ,
41+
42+ /// Character device type.
43+ Chr = bindings:: DT_CHR ,
44+
45+ /// Directory type.
46+ Dir = bindings:: DT_DIR ,
47+
48+ /// Block device type.
49+ Blk = bindings:: DT_BLK ,
50+
51+ /// Regular file type.
52+ Reg = bindings:: DT_REG ,
53+
54+ /// Symbolic link type.
55+ Lnk = bindings:: DT_LNK ,
56+
57+ /// Named unix-domain socket type.
58+ Sock = bindings:: DT_SOCK ,
59+
60+ /// White-out type.
61+ Wht = bindings:: DT_WHT ,
62+ }
63+
64+ impl From < INodeType > for DirEntryType {
65+ fn from ( value : INodeType ) -> Self {
66+ match value {
67+ INodeType :: Dir => DirEntryType :: Dir ,
68+ }
69+ }
70+ }
71+
72+ impl core:: convert:: TryFrom < u32 > for DirEntryType {
73+ type Error = crate :: error:: Error ;
74+
75+ fn try_from ( v : u32 ) -> Result < Self > {
76+ match v {
77+ v if v == Self :: Unknown as u32 => Ok ( Self :: Unknown ) ,
78+ v if v == Self :: Fifo as u32 => Ok ( Self :: Fifo ) ,
79+ v if v == Self :: Chr as u32 => Ok ( Self :: Chr ) ,
80+ v if v == Self :: Dir as u32 => Ok ( Self :: Dir ) ,
81+ v if v == Self :: Blk as u32 => Ok ( Self :: Blk ) ,
82+ v if v == Self :: Reg as u32 => Ok ( Self :: Reg ) ,
83+ v if v == Self :: Lnk as u32 => Ok ( Self :: Lnk ) ,
84+ v if v == Self :: Sock as u32 => Ok ( Self :: Sock ) ,
85+ v if v == Self :: Wht as u32 => Ok ( Self :: Wht ) ,
86+ _ => Err ( EDOM ) ,
87+ }
88+ }
2589}
2690
2791/// A registration of a file system.
@@ -149,9 +213,7 @@ impl<T: FileSystem + ?Sized> NewINode<T> {
149213
150214 let mode = match params. typ {
151215 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-
216+ inode. __bindgen_anon_3 . i_fop = & Tables :: < T > :: DIR_FILE_OPERATIONS ;
155217 // SAFETY: `simple_dir_inode_operations` never changes, it's safe to reference it.
156218 inode. i_op = unsafe { & bindings:: simple_dir_inode_operations } ;
157219 bindings:: S_IFDIR
@@ -419,6 +481,115 @@ impl<T: FileSystem + ?Sized> Tables<T> {
419481 free_cached_objects : None ,
420482 shutdown : None ,
421483 } ;
484+
485+ const DIR_FILE_OPERATIONS : bindings:: file_operations = bindings:: file_operations {
486+ owner : ptr:: null_mut ( ) ,
487+ llseek : Some ( bindings:: generic_file_llseek) ,
488+ read : Some ( bindings:: generic_read_dir) ,
489+ write : None ,
490+ read_iter : None ,
491+ write_iter : None ,
492+ iopoll : None ,
493+ iterate_shared : Some ( Self :: read_dir_callback) ,
494+ poll : None ,
495+ unlocked_ioctl : None ,
496+ compat_ioctl : None ,
497+ mmap : None ,
498+ mmap_supported_flags : 0 ,
499+ open : None ,
500+ flush : None ,
501+ release : None ,
502+ fsync : None ,
503+ fasync : None ,
504+ lock : None ,
505+ get_unmapped_area : None ,
506+ check_flags : None ,
507+ flock : None ,
508+ splice_write : None ,
509+ splice_read : None ,
510+ splice_eof : None ,
511+ setlease : None ,
512+ fallocate : None ,
513+ show_fdinfo : None ,
514+ copy_file_range : None ,
515+ remap_file_range : None ,
516+ fadvise : None ,
517+ uring_cmd : None ,
518+ uring_cmd_iopoll : None ,
519+ } ;
520+
521+ unsafe extern "C" fn read_dir_callback (
522+ file : * mut bindings:: file ,
523+ ctx_ptr : * mut bindings:: dir_context ,
524+ ) -> core:: ffi:: c_int {
525+ from_result ( || {
526+ // SAFETY: The C API guarantees that `file` is valid for read. And since `f_inode` is
527+ // immutable, we can read it directly.
528+ let inode = unsafe { & * ( * file) . f_inode . cast :: < INode < T > > ( ) } ;
529+
530+ // SAFETY: The C API guarantees that this is the only reference to the `dir_context`
531+ // instance.
532+ let emitter = unsafe { & mut * ctx_ptr. cast :: < DirEmitter > ( ) } ;
533+ let orig_pos = emitter. pos ( ) ;
534+
535+ // Call the module implementation. We ignore errors if directory entries have been
536+ // succesfully emitted: this is because we want users to see them before the error.
537+ match T :: read_dir ( inode, emitter) {
538+ Ok ( _) => Ok ( 0 ) ,
539+ Err ( e) => {
540+ if emitter. pos ( ) == orig_pos {
541+ Err ( e)
542+ } else {
543+ Ok ( 0 )
544+ }
545+ }
546+ }
547+ } )
548+ }
549+ }
550+
551+ /// Directory entry emitter.
552+ ///
553+ /// This is used in [`FileSystem::read_dir`] implementations to report the directory entry.
554+ #[ repr( transparent) ]
555+ pub struct DirEmitter ( bindings:: dir_context ) ;
556+
557+ impl DirEmitter {
558+ /// Returns the current position of the emitter.
559+ pub fn pos ( & self ) -> i64 {
560+ self . 0 . pos
561+ }
562+
563+ /// Emits a directory entry.
564+ pub fn emit ( & mut self , pos_inc : i64 , name : & [ u8 ] , ino : u64 , etype : DirEntryType ) -> bool {
565+ let Ok ( name_len) = i32:: try_from ( name. len ( ) ) else {
566+ return false ;
567+ } ;
568+
569+ let Some ( actor) = self . 0 . actor else {
570+ return false ;
571+ } ;
572+
573+ let Some ( new_pos) = self . 0 . pos . checked_add ( pos_inc) else {
574+ return false ;
575+ } ;
576+
577+ // SAFETY: `name` is valid at least for the duration of the `actor` call.
578+ let ret = unsafe {
579+ actor (
580+ & mut self . 0 ,
581+ name. as_ptr ( ) . cast ( ) ,
582+ name_len,
583+ self . 0 . pos ,
584+ ino,
585+ etype as _ ,
586+ )
587+ } ;
588+ if ret {
589+ self . 0 . pos = new_pos;
590+ }
591+ ret
592+ }
422593}
423594
424595/// Kernel module that exposes a single file system implemented by `T`.
@@ -447,7 +618,7 @@ impl<T: FileSystem + ?Sized + Sync + Send> crate::InPlaceModule for Module<T> {
447618///
448619/// ```
449620/// # mod module_ro_fs_sample {
450- /// use kernel::fs::{NewSuperBlock, SuperBlock};
621+ /// use kernel::fs::{DirEmitter, INode, NewSuperBlock, SuperBlock};
451622/// use kernel::prelude::*;
452623/// use kernel::{c_str, fs};
453624///
@@ -465,6 +636,9 @@ impl<T: FileSystem + ?Sized + Sync + Send> crate::InPlaceModule for Module<T> {
465636/// fn fill_super(_: NewSuperBlock<'_, Self>) -> Result<&SuperBlock<Self>> {
466637/// todo!()
467638/// }
639+ /// fn read_dir(_: &INode<Self>, _: &mut DirEmitter) -> Result {
640+ /// todo!()
641+ /// }
468642/// }
469643/// # }
470644/// ```
0 commit comments