@@ -24,6 +24,81 @@ pub mod ro {
2424
2525 /// Initialises a super block for this file system type.
2626 fn fill_super ( sb : NewSuperBlock < ' _ , Self > ) -> Result < & SuperBlock < Self > > ;
27+
28+ /// Reads directory entries from directory inodes.
29+ ///
30+ /// `pos` is the current position of the directory reader.
31+ ///
32+ /// `report` is a closure that takes the following arguments, in order: the entry name, its
33+ /// position, its inode number, and its type. It returns `true` if the entry was copied to
34+ /// caller or `false` otherwise (in which case, the `read_dir` implementation is advised to
35+ /// stop calling `report`).
36+ ///
37+ /// On success, returns the new position of the reader.
38+ fn read_dir (
39+ inode : & INode < Self > ,
40+ pos : i64 ,
41+ report : impl FnMut ( & [ u8 ] , i64 , u64 , DirEntryType ) -> bool ,
42+ ) -> Result < i64 > ;
43+ }
44+
45+ /// The types of directory entries reported by [`Type::read_dir`].
46+ #[ repr( u32 ) ]
47+ #[ derive( Copy , Clone ) ]
48+ pub enum DirEntryType {
49+ /// Unknown type.
50+ Unknown = bindings:: DT_UNKNOWN ,
51+
52+ /// Named pipe (first-in, first-out) type.
53+ Fifo = bindings:: DT_FIFO ,
54+
55+ /// Character device type.
56+ Chr = bindings:: DT_CHR ,
57+
58+ /// Directory type.
59+ Dir = bindings:: DT_DIR ,
60+
61+ /// Block device type.
62+ Blk = bindings:: DT_BLK ,
63+
64+ /// Regular file type.
65+ Reg = bindings:: DT_REG ,
66+
67+ /// Symbolic link type.
68+ Lnk = bindings:: DT_LNK ,
69+
70+ /// Named unix-domain socket type.
71+ Sock = bindings:: DT_SOCK ,
72+
73+ /// White-out type.
74+ Wht = bindings:: DT_WHT ,
75+ }
76+
77+ impl From < INodeType > for DirEntryType {
78+ fn from ( value : INodeType ) -> Self {
79+ match value {
80+ INodeType :: Dir => DirEntryType :: Dir ,
81+ }
82+ }
83+ }
84+
85+ impl core:: convert:: TryFrom < u32 > for DirEntryType {
86+ type Error = crate :: error:: Error ;
87+
88+ fn try_from ( v : u32 ) -> Result < Self > {
89+ match v {
90+ v if v == Self :: Unknown as u32 => Ok ( Self :: Unknown ) ,
91+ v if v == Self :: Fifo as u32 => Ok ( Self :: Fifo ) ,
92+ v if v == Self :: Chr as u32 => Ok ( Self :: Chr ) ,
93+ v if v == Self :: Dir as u32 => Ok ( Self :: Dir ) ,
94+ v if v == Self :: Blk as u32 => Ok ( Self :: Blk ) ,
95+ v if v == Self :: Reg as u32 => Ok ( Self :: Reg ) ,
96+ v if v == Self :: Lnk as u32 => Ok ( Self :: Lnk ) ,
97+ v if v == Self :: Sock as u32 => Ok ( Self :: Sock ) ,
98+ v if v == Self :: Wht as u32 => Ok ( Self :: Wht ) ,
99+ _ => Err ( EDOM ) ,
100+ }
101+ }
27102 }
28103
29104 /// A registration of a read-only file system.
@@ -149,9 +224,7 @@ pub mod ro {
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
@@ -422,6 +495,70 @@ pub mod ro {
422495 free_cached_objects : None ,
423496 shutdown : None ,
424497 } ;
498+
499+ const DIR_FILE_OPERATIONS : bindings:: file_operations = bindings:: file_operations {
500+ owner : ptr:: null_mut ( ) ,
501+ llseek : Some ( bindings:: generic_file_llseek) ,
502+ read : Some ( bindings:: generic_read_dir) ,
503+ write : None ,
504+ read_iter : None ,
505+ write_iter : None ,
506+ iopoll : None ,
507+ iterate_shared : Some ( Self :: read_dir_callback) ,
508+ poll : None ,
509+ unlocked_ioctl : None ,
510+ compat_ioctl : None ,
511+ mmap : None ,
512+ mmap_supported_flags : 0 ,
513+ open : None ,
514+ flush : None ,
515+ release : None ,
516+ fsync : None ,
517+ fasync : None ,
518+ lock : None ,
519+ get_unmapped_area : None ,
520+ check_flags : None ,
521+ flock : None ,
522+ splice_write : None ,
523+ splice_read : None ,
524+ splice_eof : None ,
525+ setlease : None ,
526+ fallocate : None ,
527+ show_fdinfo : None ,
528+ copy_file_range : None ,
529+ remap_file_range : None ,
530+ fadvise : None ,
531+ uring_cmd : None ,
532+ uring_cmd_iopoll : None ,
533+ } ;
534+
535+ unsafe extern "C" fn read_dir_callback (
536+ file : * mut bindings:: file ,
537+ ctx_ptr : * mut bindings:: dir_context ,
538+ ) -> core:: ffi:: c_int {
539+ from_result ( || {
540+ // SAFETY: The C api guarantees that `file` is valid for read. And since `f_inode`
541+ // is immutable, we can read it directly.
542+ let inode = unsafe { & * ( * file) . f_inode . cast :: < INode < T > > ( ) } ;
543+
544+ // SAFETY: The C api guarantees that this is the only reference to `dir_context`.
545+ let ctx = unsafe { & mut * ctx_ptr } ;
546+ let new_pos = T :: read_dir ( inode, ctx. pos , |name, foffset, ino, typ| {
547+ let Ok ( name_len) = i32:: try_from ( name. len ( ) ) else {
548+ return false ;
549+ } ;
550+ let Some ( actor) = ctx. actor else {
551+ return false ;
552+ } ;
553+
554+ // SAFETY: `ctx` is valid by the C api convention, and name is valid at least
555+ // for the duration of the `actor` call.
556+ unsafe { actor ( ctx, name. as_ptr ( ) . cast ( ) , name_len, foffset, ino, typ as _ ) }
557+ } ) ?;
558+ ctx. pos = new_pos;
559+ Ok ( 0 )
560+ } )
561+ }
425562 }
426563
427564 /// Kernel module that exposes a single read-only file system implemented by `T`.
@@ -470,6 +607,13 @@ pub mod ro {
470607 /// # ) -> Result<&fs::ro::SuperBlock<Self>> {
471608 /// # todo!()
472609 /// # }
610+ /// # fn read_dir(
611+ /// # _: &fs::ro::INode<Self>,
612+ /// # _: i64,
613+ /// # _: impl FnMut(&[u8], i64, u64, fs::ro::DirEntryType) -> bool,
614+ /// # ) -> Result<i64> {
615+ /// # todo!()
616+ /// # }
473617 /// }
474618 /// ```
475619 #[ macro_export]
0 commit comments