@@ -28,6 +28,70 @@ pub trait FileSystem {
28
28
/// This is called during initialisation of a superblock after [`FileSystem::super_params`] has
29
29
/// completed successfully.
30
30
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
+ }
31
95
}
32
96
33
97
/// A registration of a file system.
@@ -161,9 +225,7 @@ impl<T: FileSystem + ?Sized> NewINode<T> {
161
225
162
226
let mode = match params. typ {
163
227
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 ;
167
229
// SAFETY: `simple_dir_inode_operations` never changes, it's safe to reference it.
168
230
inode. i_op = unsafe { & bindings:: simple_dir_inode_operations } ;
169
231
bindings:: S_IFDIR
@@ -403,6 +465,126 @@ impl<T: FileSystem + ?Sized> Tables<T> {
403
465
free_cached_objects : None ,
404
466
shutdown : None ,
405
467
} ;
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
+ }
406
588
}
407
589
408
590
/// 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> {
431
613
///
432
614
/// ```
433
615
/// # mod module_fs_sample {
434
- /// use kernel::fs::{INode, NewSuperBlock, SuperBlock, SuperParams};
616
+ /// use kernel::fs::{DirEmitter, INode, NewSuperBlock, SuperBlock, SuperParams};
435
617
/// use kernel::prelude::*;
436
618
/// use kernel::{c_str, fs, types::ARef};
437
619
///
@@ -452,6 +634,9 @@ impl<T: FileSystem + ?Sized + Sync + Send> crate::InPlaceModule for Module<T> {
452
634
/// fn init_root(_sb: &SuperBlock<Self>) -> Result<ARef<INode<Self>>> {
453
635
/// todo!()
454
636
/// }
637
+ /// fn read_dir(_: &INode<Self>, _: &mut DirEmitter) -> Result {
638
+ /// todo!()
639
+ /// }
455
640
/// }
456
641
/// # }
457
642
/// ```
0 commit comments