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