@@ -4,11 +4,13 @@ use alloc::ffi::CString;
4
4
use alloc:: string:: String ;
5
5
use alloc:: sync:: Arc ;
6
6
use alloc:: vec:: Vec ;
7
- use core:: mem:: MaybeUninit ;
7
+ use core:: marker:: PhantomData ;
8
+ use core:: mem:: { MaybeUninit , align_of, offset_of, size_of} ;
8
9
use core:: sync:: atomic:: { AtomicU64 , Ordering } ;
9
10
use core:: task:: Poll ;
10
11
use core:: { future, mem} ;
11
12
13
+ use align_address:: Align ;
12
14
use async_lock:: Mutex ;
13
15
use async_trait:: async_trait;
14
16
use fuse_abi:: linux:: * ;
@@ -28,6 +30,7 @@ use crate::fs::{
28
30
SeekWhence , VfsNode ,
29
31
} ;
30
32
use crate :: mm:: device_alloc:: DeviceAlloc ;
33
+ use crate :: syscalls:: Dirent64 ;
31
34
use crate :: time:: { time_t, timespec} ;
32
35
use crate :: { arch, io} ;
33
36
@@ -792,20 +795,24 @@ impl Clone for FuseFileHandle {
792
795
}
793
796
}
794
797
795
- #[ derive( Debug , Clone ) ]
798
+ #[ derive( Debug ) ]
796
799
pub struct FuseDirectoryHandle {
797
800
name : Option < String > ,
801
+ read_position : Mutex < usize > ,
798
802
}
799
803
800
804
impl FuseDirectoryHandle {
801
805
pub fn new ( name : Option < String > ) -> Self {
802
- Self { name }
806
+ Self {
807
+ name,
808
+ read_position : Mutex :: new ( 0 ) ,
809
+ }
803
810
}
804
811
}
805
812
806
813
#[ async_trait]
807
814
impl ObjectInterface for FuseDirectoryHandle {
808
- async fn readdir ( & self ) -> io:: Result < Vec < DirectoryEntry > > {
815
+ async fn getdents ( & self , buf : & mut [ MaybeUninit < u8 > ] ) -> io:: Result < usize > {
809
816
let path: CString = if let Some ( name) = & self . name {
810
817
CString :: new ( "/" . to_string ( ) + name) . unwrap ( )
811
818
} else {
@@ -830,7 +837,8 @@ impl ObjectInterface for FuseDirectoryHandle {
830
837
831
838
// Linux seems to allocate a single page to store the dirfile
832
839
let len = MAX_READ_LEN as u32 ;
833
- let mut offset: usize = 0 ;
840
+ let rsp_offset: & mut usize = & mut * self . read_position . lock ( ) . await ;
841
+ let mut buf_offset: usize = 0 ;
834
842
835
843
// read content of the directory
836
844
let ( mut cmd, rsp_payload_len) = ops:: Read :: create ( fuse_nid, fuse_fh, len, 0 ) ;
@@ -850,31 +858,53 @@ impl ObjectInterface for FuseDirectoryHandle {
850
858
return Err ( io:: Error :: ENOENT ) ;
851
859
}
852
860
853
- let mut entries: Vec < DirectoryEntry > = Vec :: new ( ) ;
854
- while ( rsp. headers . out_header . len as usize ) - offset > core:: mem:: size_of :: < fuse_dirent > ( ) {
861
+ let mut ret = 0 ;
862
+
863
+ while ( rsp. headers . out_header . len as usize ) - * rsp_offset > size_of :: < fuse_dirent > ( ) {
855
864
let dirent = unsafe {
856
865
& * rsp
857
866
. payload
858
867
. as_ref ( )
859
868
. unwrap ( )
860
869
. as_ptr ( )
861
- . byte_add ( offset )
870
+ . byte_add ( * rsp_offset )
862
871
. cast :: < fuse_dirent > ( )
863
872
} ;
864
873
865
- offset += core:: mem:: size_of :: < fuse_dirent > ( ) + dirent. namelen as usize ;
866
- // Align to dirent struct
867
- offset = ( ( offset) + U64_SIZE - 1 ) & ( !( U64_SIZE - 1 ) ) ;
874
+ let dirent_len = offset_of ! ( Dirent64 , d_name) + dirent. namelen as usize + 1 ;
875
+ let next_dirent = ( buf_offset + dirent_len) . align_up ( align_of :: < Dirent64 > ( ) ) ;
868
876
869
- let name: & ' static [ u8 ] = unsafe {
870
- core:: slice:: from_raw_parts (
871
- dirent. name . as_ptr ( ) . cast ( ) ,
872
- dirent. namelen . try_into ( ) . unwrap ( ) ,
873
- )
874
- } ;
875
- entries. push ( DirectoryEntry :: new ( unsafe {
876
- core:: str:: from_utf8_unchecked ( name) . to_string ( )
877
- } ) ) ;
877
+ if next_dirent > buf. len ( ) {
878
+ // target buffer full -> we return the nr. of bytes written (like linux does)
879
+ break ;
880
+ }
881
+
882
+ // could be replaced with slice_as_ptr once maybe_uninit_slice is stabilized.
883
+ let target_dirent = buf[ buf_offset] . as_mut_ptr ( ) . cast :: < Dirent64 > ( ) ;
884
+ unsafe {
885
+ target_dirent. write ( Dirent64 {
886
+ d_ino : dirent. ino ,
887
+ d_off : 0 ,
888
+ d_reclen : ( dirent_len. align_up ( align_of :: < Dirent64 > ( ) ) )
889
+ . try_into ( )
890
+ . unwrap ( ) ,
891
+ d_type : ( dirent. type_ as u8 ) . try_into ( ) . unwrap ( ) ,
892
+ d_name : PhantomData { } ,
893
+ } ) ;
894
+ let nameptr = core:: ptr:: from_mut ( & mut ( * ( target_dirent) ) . d_name ) . cast :: < u8 > ( ) ;
895
+ core:: ptr:: copy_nonoverlapping (
896
+ dirent. name . as_ptr ( ) . cast :: < u8 > ( ) ,
897
+ nameptr,
898
+ dirent. namelen as usize ,
899
+ ) ;
900
+ nameptr. add ( dirent. namelen as usize ) . write_bytes ( 0 , 1 ) ; // zero termination
901
+ }
902
+
903
+ * rsp_offset += core:: mem:: size_of :: < fuse_dirent > ( ) + dirent. namelen as usize ;
904
+ // Align to dirent struct
905
+ * rsp_offset = ( ( * rsp_offset) + U64_SIZE - 1 ) & ( !( U64_SIZE - 1 ) ) ;
906
+ buf_offset = next_dirent;
907
+ ret = buf_offset;
878
908
}
879
909
880
910
let ( cmd, rsp_payload_len) = ops:: Release :: create ( fuse_nid, fuse_fh) ;
@@ -883,7 +913,20 @@ impl ObjectInterface for FuseDirectoryHandle {
883
913
. lock ( )
884
914
. send_command ( cmd, rsp_payload_len) ?;
885
915
886
- Ok ( entries)
916
+ Ok ( ret)
917
+ }
918
+
919
+ /// lseek for a directory entry is the equivalent for seekdir on linux. But on Hermit this is
920
+ /// logically the same operation, so we can just use the same fn in the backend.
921
+ /// Any other offset than 0 is not supported. (Mostly because it doesn't make any sense, as
922
+ /// userspace applications have no way of knowing valid offsets)
923
+ async fn lseek ( & self , offset : isize , _whence : SeekWhence ) -> io:: Result < isize > {
924
+ if offset != 0 {
925
+ error ! ( "Invalid offset for directory lseek ({offset})" ) ;
926
+ return Err ( io:: Error :: EINVAL ) ;
927
+ }
928
+ * self . read_position . lock ( ) . await = offset as usize ;
929
+ Ok ( offset)
887
930
}
888
931
}
889
932
0 commit comments