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