@@ -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.
@@ -155,9 +219,7 @@ impl<T: FileSystem + ?Sized> NewINode<T> {
155219
156220 let mode = match params. typ {
157221 INodeType :: Dir => {
158- // SAFETY: `simple_dir_operations` never changes, it's safe to reference it.
159- inode. __bindgen_anon_3 . i_fop = unsafe { & bindings:: simple_dir_operations } ;
160-
222+ inode. __bindgen_anon_3 . i_fop = & Tables :: < T > :: DIR_FILE_OPERATIONS ;
161223 // SAFETY: `simple_dir_inode_operations` never changes, it's safe to reference it.
162224 inode. i_op = unsafe { & bindings:: simple_dir_inode_operations } ;
163225 bindings:: S_IFDIR
@@ -425,6 +487,126 @@ impl<T: FileSystem + ?Sized> Tables<T> {
425487 free_cached_objects : None ,
426488 shutdown : None ,
427489 } ;
490+
491+ const DIR_FILE_OPERATIONS : bindings:: file_operations = bindings:: file_operations {
492+ owner : ptr:: null_mut ( ) ,
493+ llseek : Some ( bindings:: generic_file_llseek) ,
494+ read : Some ( bindings:: generic_read_dir) ,
495+ write : None ,
496+ read_iter : None ,
497+ write_iter : None ,
498+ iopoll : None ,
499+ iterate_shared : Some ( Self :: read_dir_callback) ,
500+ poll : None ,
501+ unlocked_ioctl : None ,
502+ compat_ioctl : None ,
503+ mmap : None ,
504+ mmap_supported_flags : 0 ,
505+ open : None ,
506+ flush : None ,
507+ release : None ,
508+ fsync : None ,
509+ fasync : None ,
510+ lock : None ,
511+ get_unmapped_area : None ,
512+ check_flags : None ,
513+ flock : None ,
514+ splice_write : None ,
515+ splice_read : None ,
516+ splice_eof : None ,
517+ setlease : None ,
518+ fallocate : None ,
519+ show_fdinfo : None ,
520+ copy_file_range : None ,
521+ remap_file_range : None ,
522+ fadvise : None ,
523+ uring_cmd : None ,
524+ uring_cmd_iopoll : None ,
525+ } ;
526+
527+ unsafe extern "C" fn read_dir_callback (
528+ file : * mut bindings:: file ,
529+ ctx_ptr : * mut bindings:: dir_context ,
530+ ) -> core:: ffi:: c_int {
531+ from_result ( || {
532+ // SAFETY: The C API guarantees that `file` is valid for read. And since `f_inode` is
533+ // immutable, we can read it directly.
534+ let inode = unsafe { & * ( * file) . f_inode . cast :: < INode < T > > ( ) } ;
535+
536+ // SAFETY: The C API guarantees that this is the only reference to the `dir_context`
537+ // instance.
538+ let emitter = unsafe { & mut * ctx_ptr. cast :: < DirEmitter > ( ) } ;
539+ let orig_pos = emitter. pos ( ) ;
540+
541+ // Call the module implementation. We ignore errors if directory entries have been
542+ // succesfully emitted: this is because we want users to see them before the error.
543+ match T :: read_dir ( inode, emitter) {
544+ Ok ( _) => Ok ( 0 ) ,
545+ Err ( e) => {
546+ if emitter. pos ( ) == orig_pos {
547+ Err ( e)
548+ } else {
549+ Ok ( 0 )
550+ }
551+ }
552+ }
553+ } )
554+ }
555+ }
556+
557+ /// Directory entry emitter.
558+ ///
559+ /// This is used in [`FileSystem::read_dir`] implementations to report the directory entry.
560+ #[ repr( transparent) ]
561+ pub struct DirEmitter ( bindings:: dir_context ) ;
562+
563+ impl DirEmitter {
564+ /// Returns the current position of the emitter.
565+ pub fn pos ( & self ) -> i64 {
566+ self . 0 . pos
567+ }
568+
569+ /// Emits a directory entry.
570+ ///
571+ /// `pos_inc` is the number with which to increment the current position on success.
572+ ///
573+ /// `name` is the name of the entry.
574+ ///
575+ /// `ino` is the inode number of the entry.
576+ ///
577+ /// `etype` is the type of the entry.
578+ ///
579+ /// Returns `false` when the entry could not be emitted, possibly because the user-provided
580+ /// buffer is full.
581+ pub fn emit ( & mut self , pos_inc : i64 , name : & [ u8 ] , ino : Ino , etype : DirEntryType ) -> bool {
582+ let Ok ( name_len) = i32:: try_from ( name. len ( ) ) else {
583+ return false ;
584+ } ;
585+
586+ let Some ( actor) = self . 0 . actor else {
587+ return false ;
588+ } ;
589+
590+ let Some ( new_pos) = self . 0 . pos . checked_add ( pos_inc) else {
591+ return false ;
592+ } ;
593+
594+ // SAFETY: `name` is valid at least for the duration of the `actor` call.
595+ let ret = unsafe {
596+ actor (
597+ & mut self . 0 ,
598+ name. as_ptr ( ) . cast ( ) ,
599+ name_len,
600+ self . 0 . pos ,
601+ ino,
602+ etype as _ ,
603+ )
604+ } ;
605+ if ret {
606+ self . 0 . pos = new_pos;
607+ }
608+ ret
609+ }
428610}
429611
430612/// Kernel module that exposes a single file system implemented by `T`.
@@ -453,7 +635,7 @@ impl<T: FileSystem + ?Sized + Sync + Send> crate::InPlaceModule for Module<T> {
453635///
454636/// ```
455637/// # mod module_ro_fs_sample {
456- /// use kernel::fs::{NewSuperBlock, SuperBlock};
638+ /// use kernel::fs::{DirEmitter, INode, NewSuperBlock, SuperBlock};
457639/// use kernel::prelude::*;
458640/// use kernel::{c_str, fs};
459641///
@@ -471,6 +653,9 @@ impl<T: FileSystem + ?Sized + Sync + Send> crate::InPlaceModule for Module<T> {
471653/// fn fill_super(_: NewSuperBlock<'_, Self>) -> Result<&SuperBlock<Self>> {
472654/// todo!()
473655/// }
656+ /// fn read_dir(_: &INode<Self>, _: &mut DirEmitter) -> Result {
657+ /// todo!()
658+ /// }
474659/// }
475660/// # }
476661/// ```
0 commit comments