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