@@ -25,6 +25,8 @@ use sys_common::{AsInner, FromInner};
25
25
26
26
#[ cfg( any( target_os = "linux" , target_os = "emscripten" , target_os = "l4re" ) ) ]
27
27
use 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} ;
28
30
#[ cfg( target_os = "android" ) ]
29
31
use libc:: { stat as stat64, fstat as fstat64, lstat as lstat64, lseek64,
30
32
dirent as dirent64, open as open64} ;
@@ -48,20 +50,24 @@ pub struct FileAttr {
48
50
stat : stat64 ,
49
51
}
50
52
51
- pub struct ReadDir {
53
+ // all DirEntry's will have a reference to this struct
54
+ struct InnerReadDir {
52
55
dirp : Dir ,
53
- root : Arc < PathBuf > ,
56
+ root : PathBuf ,
54
57
}
55
58
59
+ #[ derive( Clone ) ]
60
+ pub struct ReadDir ( Arc < InnerReadDir > ) ;
61
+
56
62
struct Dir ( * mut libc:: DIR ) ;
57
63
58
64
unsafe impl Send for Dir { }
59
65
unsafe impl Sync for Dir { }
60
66
61
67
pub struct DirEntry {
62
68
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
65
71
// on Solaris and Fuchsia because a) it uses a zero-length
66
72
// array to store the name, b) its lifetime between readdir
67
73
// calls is not guaranteed.
@@ -207,7 +213,7 @@ impl fmt::Debug for ReadDir {
207
213
fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
208
214
// This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame.
209
215
// Thus the result will be e g 'ReadDir("/home")'
210
- fmt:: Debug :: fmt ( & * self . root , f)
216
+ fmt:: Debug :: fmt ( & * self . 0 . root , f)
211
217
}
212
218
}
213
219
@@ -240,7 +246,7 @@ impl Iterator for ReadDir {
240
246
entry : * entry_ptr,
241
247
name : :: slice:: from_raw_parts ( name as * const u8 ,
242
248
namelen as usize ) . to_owned ( ) . into_boxed_slice ( ) ,
243
- root : self . root . clone ( )
249
+ dir : self . clone ( )
244
250
} ;
245
251
if ret. name_bytes ( ) != b"." && ret. name_bytes ( ) != b".." {
246
252
return Some ( Ok ( ret) )
@@ -254,11 +260,11 @@ impl Iterator for ReadDir {
254
260
unsafe {
255
261
let mut ret = DirEntry {
256
262
entry : mem:: zeroed ( ) ,
257
- root : self . root . clone ( )
263
+ dir : self . clone ( ) ,
258
264
} ;
259
265
let mut entry_ptr = ptr:: null_mut ( ) ;
260
266
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 {
262
268
return Some ( Err ( Error :: last_os_error ( ) ) )
263
269
}
264
270
if entry_ptr. is_null ( ) {
@@ -281,13 +287,27 @@ impl Drop for Dir {
281
287
282
288
impl DirEntry {
283
289
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 ( ) ) )
285
291
}
286
292
287
293
pub fn file_name ( & self ) -> OsString {
288
294
OsStr :: from_bytes ( self . name_bytes ( ) ) . to_os_string ( )
289
295
}
290
296
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" ) ) ) ]
291
311
pub fn metadata ( & self ) -> io:: Result < FileAttr > {
292
312
lstat ( & self . path ( ) )
293
313
}
@@ -664,14 +684,15 @@ impl fmt::Debug for File {
664
684
}
665
685
666
686
pub fn readdir ( p : & Path ) -> io:: Result < ReadDir > {
667
- let root = Arc :: new ( p. to_path_buf ( ) ) ;
687
+ let root = p. to_path_buf ( ) ;
668
688
let p = cstr ( p) ?;
669
689
unsafe {
670
690
let ptr = libc:: opendir ( p. as_ptr ( ) ) ;
671
691
if ptr. is_null ( ) {
672
692
Err ( Error :: last_os_error ( ) )
673
693
} else {
674
- Ok ( ReadDir { dirp : Dir ( ptr) , root : root } )
694
+ let inner = InnerReadDir { dirp : Dir ( ptr) , root } ;
695
+ Ok ( ReadDir ( Arc :: new ( inner) ) )
675
696
}
676
697
}
677
698
}
0 commit comments