@@ -25,6 +25,8 @@ use sys_common::{AsInner, FromInner};
2525
2626#[ cfg( any( target_os = "linux" , target_os = "emscripten" , target_os = "l4re" ) ) ]
2727use libc:: { stat64, fstat64, lstat64, off64_t, ftruncate64, lseek64, dirent64, readdir64_r, open64} ;
28+ #[ cfg( any( target_os = "linux" , target_os = "emscripten" , target_os = "android" ) ) ]
29+ use libc:: { fstatat, dirfd} ;
2830#[ cfg( target_os = "android" ) ]
2931use libc:: { stat as stat64, fstat as fstat64, lstat as lstat64, lseek64,
3032 dirent as dirent64, open as open64} ;
@@ -48,20 +50,24 @@ pub struct FileAttr {
4850 stat : stat64 ,
4951}
5052
51- pub struct ReadDir {
53+ // all DirEntry's will have a reference to this struct
54+ struct InnerReadDir {
5255 dirp : Dir ,
53- root : Arc < PathBuf > ,
56+ root : PathBuf ,
5457}
5558
59+ #[ derive( Clone ) ]
60+ pub struct ReadDir ( Arc < InnerReadDir > ) ;
61+
5662struct Dir ( * mut libc:: DIR ) ;
5763
5864unsafe impl Send for Dir { }
5965unsafe impl Sync for Dir { }
6066
6167pub struct DirEntry {
6268 entry : dirent64 ,
63- root : Arc < PathBuf > ,
64- // We need to store an owned copy of the directory name
69+ dir : ReadDir ,
70+ // We need to store an owned copy of the entry name
6571 // on Solaris and Fuchsia because a) it uses a zero-length
6672 // array to store the name, b) its lifetime between readdir
6773 // calls is not guaranteed.
@@ -207,7 +213,7 @@ impl fmt::Debug for ReadDir {
207213 fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
208214 // This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame.
209215 // Thus the result will be e g 'ReadDir("/home")'
210- fmt:: Debug :: fmt ( & * self . root , f)
216+ fmt:: Debug :: fmt ( & * self . 0 . root , f)
211217 }
212218}
213219
@@ -240,7 +246,7 @@ impl Iterator for ReadDir {
240246 entry : * entry_ptr,
241247 name : :: slice:: from_raw_parts ( name as * const u8 ,
242248 namelen as usize ) . to_owned ( ) . into_boxed_slice ( ) ,
243- root : self . root . clone ( )
249+ dir : self . clone ( )
244250 } ;
245251 if ret. name_bytes ( ) != b"." && ret. name_bytes ( ) != b".." {
246252 return Some ( Ok ( ret) )
@@ -254,11 +260,11 @@ impl Iterator for ReadDir {
254260 unsafe {
255261 let mut ret = DirEntry {
256262 entry : mem:: zeroed ( ) ,
257- root : self . root . clone ( )
263+ dir : self . clone ( ) ,
258264 } ;
259265 let mut entry_ptr = ptr:: null_mut ( ) ;
260266 loop {
261- if readdir64_r ( self . dirp . 0 , & mut ret. entry , & mut entry_ptr) != 0 {
267+ if readdir64_r ( self . 0 . dirp . 0 , & mut ret. entry , & mut entry_ptr) != 0 {
262268 return Some ( Err ( Error :: last_os_error ( ) ) )
263269 }
264270 if entry_ptr. is_null ( ) {
@@ -281,13 +287,27 @@ impl Drop for Dir {
281287
282288impl DirEntry {
283289 pub fn path ( & self ) -> PathBuf {
284- self . root . join ( OsStr :: from_bytes ( self . name_bytes ( ) ) )
290+ self . dir . 0 . root . join ( OsStr :: from_bytes ( self . name_bytes ( ) ) )
285291 }
286292
287293 pub fn file_name ( & self ) -> OsString {
288294 OsStr :: from_bytes ( self . name_bytes ( ) ) . to_os_string ( )
289295 }
290296
297+ #[ cfg( any( target_os = "linux" , target_os = "emscripten" , target_os = "android" ) ) ]
298+ pub fn metadata ( & self ) -> io:: Result < FileAttr > {
299+ let fd = cvt ( unsafe { dirfd ( self . dir . 0 . dirp . 0 ) } ) ?;
300+ let mut stat: stat64 = unsafe { mem:: zeroed ( ) } ;
301+ cvt ( unsafe {
302+ fstatat ( fd,
303+ self . entry . d_name . as_ptr ( ) ,
304+ & mut stat as * mut _ as * mut _ ,
305+ libc:: AT_SYMLINK_NOFOLLOW )
306+ } ) ?;
307+ Ok ( FileAttr { stat : stat } )
308+ }
309+
310+ #[ cfg( not( any( target_os = "linux" , target_os = "emscripten" , target_os = "android" ) ) ) ]
291311 pub fn metadata ( & self ) -> io:: Result < FileAttr > {
292312 lstat ( & self . path ( ) )
293313 }
@@ -664,14 +684,15 @@ impl fmt::Debug for File {
664684}
665685
666686pub fn readdir ( p : & Path ) -> io:: Result < ReadDir > {
667- let root = Arc :: new ( p. to_path_buf ( ) ) ;
687+ let root = p. to_path_buf ( ) ;
668688 let p = cstr ( p) ?;
669689 unsafe {
670690 let ptr = libc:: opendir ( p. as_ptr ( ) ) ;
671691 if ptr. is_null ( ) {
672692 Err ( Error :: last_os_error ( ) )
673693 } else {
674- Ok ( ReadDir { dirp : Dir ( ptr) , root : root } )
694+ let inner = InnerReadDir { dirp : Dir ( ptr) , root } ;
695+ Ok ( ReadDir ( Arc :: new ( inner) ) )
675696 }
676697 }
677698}
0 commit comments