@@ -28,6 +28,70 @@ pub trait FileSystem {
2828 /// This is called during initialisation of a superblock after [`FileSystem::super_params`] has
2929 /// completed successfully.
3030 fn init_root ( sb : & SuperBlock < Self > ) -> Result < ARef < INode < Self > > > ;
31+
32+ /// Reads directory entries from directory inodes.
33+ ///
34+ /// [`DirEmitter::pos`] holds the current position of the directory reader.
35+ fn read_dir ( inode : & INode < Self > , emitter : & mut DirEmitter ) -> Result ;
36+ }
37+
38+ /// The types of directory entries reported by [`FileSystem::read_dir`].
39+ #[ repr( u32 ) ]
40+ #[ derive( Copy , Clone ) ]
41+ pub enum DirEntryType {
42+ /// Unknown type.
43+ Unknown = bindings:: DT_UNKNOWN ,
44+
45+ /// Named pipe (first-in, first-out) type.
46+ Fifo = bindings:: DT_FIFO ,
47+
48+ /// Character device type.
49+ Chr = bindings:: DT_CHR ,
50+
51+ /// Directory type.
52+ Dir = bindings:: DT_DIR ,
53+
54+ /// Block device type.
55+ Blk = bindings:: DT_BLK ,
56+
57+ /// Regular file type.
58+ Reg = bindings:: DT_REG ,
59+
60+ /// Symbolic link type.
61+ Lnk = bindings:: DT_LNK ,
62+
63+ /// Named unix-domain socket type.
64+ Sock = bindings:: DT_SOCK ,
65+
66+ /// White-out type.
67+ Wht = bindings:: DT_WHT ,
68+ }
69+
70+ impl From < INodeType > for DirEntryType {
71+ fn from ( value : INodeType ) -> Self {
72+ match value {
73+ INodeType :: Dir => DirEntryType :: Dir ,
74+ }
75+ }
76+ }
77+
78+ impl core:: convert:: TryFrom < u32 > for DirEntryType {
79+ type Error = crate :: error:: Error ;
80+
81+ fn try_from ( v : u32 ) -> Result < Self > {
82+ match v {
83+ v if v == Self :: Unknown as u32 => Ok ( Self :: Unknown ) ,
84+ v if v == Self :: Fifo as u32 => Ok ( Self :: Fifo ) ,
85+ v if v == Self :: Chr as u32 => Ok ( Self :: Chr ) ,
86+ v if v == Self :: Dir as u32 => Ok ( Self :: Dir ) ,
87+ v if v == Self :: Blk as u32 => Ok ( Self :: Blk ) ,
88+ v if v == Self :: Reg as u32 => Ok ( Self :: Reg ) ,
89+ v if v == Self :: Lnk as u32 => Ok ( Self :: Lnk ) ,
90+ v if v == Self :: Sock as u32 => Ok ( Self :: Sock ) ,
91+ v if v == Self :: Wht as u32 => Ok ( Self :: Wht ) ,
92+ _ => Err ( EDOM ) ,
93+ }
94+ }
3195}
3296
3397/// A registration of a file system.
@@ -161,9 +225,7 @@ impl<T: FileSystem + ?Sized> NewINode<T> {
161225
162226 let mode = match params. typ {
163227 INodeType :: Dir => {
164- // SAFETY: `simple_dir_operations` never changes, it's safe to reference it.
165- inode. __bindgen_anon_3 . i_fop = unsafe { & bindings:: simple_dir_operations } ;
166-
228+ inode. __bindgen_anon_3 . i_fop = & Tables :: < T > :: DIR_FILE_OPERATIONS ;
167229 // SAFETY: `simple_dir_inode_operations` never changes, it's safe to reference it.
168230 inode. i_op = unsafe { & bindings:: simple_dir_inode_operations } ;
169231 bindings:: S_IFDIR
@@ -403,6 +465,126 @@ impl<T: FileSystem + ?Sized> Tables<T> {
403465 free_cached_objects : None ,
404466 shutdown : None ,
405467 } ;
468+
469+ const DIR_FILE_OPERATIONS : bindings:: file_operations = bindings:: file_operations {
470+ owner : ptr:: null_mut ( ) ,
471+ llseek : Some ( bindings:: generic_file_llseek) ,
472+ read : Some ( bindings:: generic_read_dir) ,
473+ write : None ,
474+ read_iter : None ,
475+ write_iter : None ,
476+ iopoll : None ,
477+ iterate_shared : Some ( Self :: read_dir_callback) ,
478+ poll : None ,
479+ unlocked_ioctl : None ,
480+ compat_ioctl : None ,
481+ mmap : None ,
482+ mmap_supported_flags : 0 ,
483+ open : None ,
484+ flush : None ,
485+ release : None ,
486+ fsync : None ,
487+ fasync : None ,
488+ lock : None ,
489+ get_unmapped_area : None ,
490+ check_flags : None ,
491+ flock : None ,
492+ splice_write : None ,
493+ splice_read : None ,
494+ splice_eof : None ,
495+ setlease : None ,
496+ fallocate : None ,
497+ show_fdinfo : None ,
498+ copy_file_range : None ,
499+ remap_file_range : None ,
500+ fadvise : None ,
501+ uring_cmd : None ,
502+ uring_cmd_iopoll : None ,
503+ } ;
504+
505+ unsafe extern "C" fn read_dir_callback (
506+ file : * mut bindings:: file ,
507+ ctx_ptr : * mut bindings:: dir_context ,
508+ ) -> core:: ffi:: c_int {
509+ from_result ( || {
510+ // SAFETY: The C API guarantees that `file` is valid for read. And since `f_inode` is
511+ // immutable, we can read it directly.
512+ let inode = unsafe { & * ( * file) . f_inode . cast :: < INode < T > > ( ) } ;
513+
514+ // SAFETY: The C API guarantees that this is the only reference to the `dir_context`
515+ // instance.
516+ let emitter = unsafe { & mut * ctx_ptr. cast :: < DirEmitter > ( ) } ;
517+ let orig_pos = emitter. pos ( ) ;
518+
519+ // Call the module implementation. We ignore errors if directory entries have been
520+ // succesfully emitted: this is because we want users to see them before the error.
521+ match T :: read_dir ( inode, emitter) {
522+ Ok ( _) => Ok ( 0 ) ,
523+ Err ( e) => {
524+ if emitter. pos ( ) == orig_pos {
525+ Err ( e)
526+ } else {
527+ Ok ( 0 )
528+ }
529+ }
530+ }
531+ } )
532+ }
533+ }
534+
535+ /// Directory entry emitter.
536+ ///
537+ /// This is used in [`FileSystem::read_dir`] implementations to report the directory entry.
538+ #[ repr( transparent) ]
539+ pub struct DirEmitter ( bindings:: dir_context ) ;
540+
541+ impl DirEmitter {
542+ /// Returns the current position of the emitter.
543+ pub fn pos ( & self ) -> i64 {
544+ self . 0 . pos
545+ }
546+
547+ /// Emits a directory entry.
548+ ///
549+ /// `pos_inc` is the number with which to increment the current position on success.
550+ ///
551+ /// `name` is the name of the entry.
552+ ///
553+ /// `ino` is the inode number of the entry.
554+ ///
555+ /// `etype` is the type of the entry.
556+ ///
557+ /// Returns `false` when the entry could not be emitted, possibly because the user-provided
558+ /// buffer is full.
559+ pub fn emit ( & mut self , pos_inc : i64 , name : & [ u8 ] , ino : Ino , etype : DirEntryType ) -> bool {
560+ let Ok ( name_len) = i32:: try_from ( name. len ( ) ) else {
561+ return false ;
562+ } ;
563+
564+ let Some ( actor) = self . 0 . actor else {
565+ return false ;
566+ } ;
567+
568+ let Some ( new_pos) = self . 0 . pos . checked_add ( pos_inc) else {
569+ return false ;
570+ } ;
571+
572+ // SAFETY: `name` is valid at least for the duration of the `actor` call.
573+ let ret = unsafe {
574+ actor (
575+ & mut self . 0 ,
576+ name. as_ptr ( ) . cast ( ) ,
577+ name_len,
578+ self . 0 . pos ,
579+ ino,
580+ etype as _ ,
581+ )
582+ } ;
583+ if ret {
584+ self . 0 . pos = new_pos;
585+ }
586+ ret
587+ }
406588}
407589
408590/// Kernel module that exposes a single file system implemented by `T`.
@@ -431,7 +613,7 @@ impl<T: FileSystem + ?Sized + Sync + Send> crate::InPlaceModule for Module<T> {
431613///
432614/// ```
433615/// # mod module_fs_sample {
434- /// use kernel::fs::{INode, NewSuperBlock, SuperBlock, SuperParams};
616+ /// use kernel::fs::{DirEmitter, INode, NewSuperBlock, SuperBlock, SuperParams};
435617/// use kernel::prelude::*;
436618/// use kernel::{c_str, fs, types::ARef};
437619///
@@ -452,6 +634,9 @@ impl<T: FileSystem + ?Sized + Sync + Send> crate::InPlaceModule for Module<T> {
452634/// fn init_root(_sb: &SuperBlock<Self>) -> Result<ARef<INode<Self>>> {
453635/// todo!()
454636/// }
637+ /// fn read_dir(_: &INode<Self>, _: &mut DirEmitter) -> Result {
638+ /// todo!()
639+ /// }
455640/// }
456641/// # }
457642/// ```
0 commit comments