@@ -41,11 +41,138 @@ pub use crate::sys_common::fs::remove_dir_all;
41
41
42
42
pub struct File ( FileDesc ) ;
43
43
44
- #[ derive( Clone ) ]
45
- pub struct FileAttr {
46
- stat : stat64 ,
44
+ // FIXME: This should be available on Linux with all `target_arch` and `target_env`.
45
+ // https://github.com/rust-lang/libc/issues/1545
46
+ macro_rules! cfg_has_statx {
47
+ ( { $( $then_tt: tt) * } else { $( $else_tt: tt) * } ) => {
48
+ cfg_if:: cfg_if! {
49
+ if #[ cfg( all( target_os = "linux" , target_env = "gnu" , any(
50
+ target_arch = "x86" ,
51
+ target_arch = "arm" ,
52
+ // target_arch = "mips",
53
+ target_arch = "powerpc" ,
54
+ target_arch = "x86_64" ,
55
+ // target_arch = "aarch64",
56
+ target_arch = "powerpc64" ,
57
+ // target_arch = "mips64",
58
+ // target_arch = "s390x",
59
+ target_arch = "sparc64" ,
60
+ ) ) ) ] {
61
+ $( $then_tt) *
62
+ } else {
63
+ $( $else_tt) *
64
+ }
65
+ }
66
+ } ;
67
+ ( $( $block_inner: tt) * ) => {
68
+ #[ cfg( all( target_os = "linux" , target_env = "gnu" , any(
69
+ target_arch = "x86" ,
70
+ target_arch = "arm" ,
71
+ // target_arch = "mips",
72
+ target_arch = "powerpc" ,
73
+ target_arch = "x86_64" ,
74
+ // target_arch = "aarch64",
75
+ target_arch = "powerpc64" ,
76
+ // target_arch = "mips64",
77
+ // target_arch = "s390x",
78
+ target_arch = "sparc64" ,
79
+ ) ) ) ]
80
+ {
81
+ $( $block_inner) *
82
+ }
83
+ } ;
47
84
}
48
85
86
+ cfg_has_statx ! { {
87
+ #[ derive( Clone ) ]
88
+ pub struct FileAttr {
89
+ stat: stat64,
90
+ statx_extra_fields: Option <StatxExtraFields >,
91
+ }
92
+
93
+ #[ derive( Clone ) ]
94
+ struct StatxExtraFields {
95
+ // This is needed to check if btime is supported by the filesystem.
96
+ stx_mask: u32 ,
97
+ stx_btime: libc:: statx_timestamp,
98
+ }
99
+
100
+ // We prefer `statx` on Linux if available, which contains file creation time.
101
+ // Default `stat64` contains no creation time.
102
+ unsafe fn try_statx(
103
+ fd: c_int,
104
+ path: * const libc:: c_char,
105
+ flags: i32 ,
106
+ mask: u32 ,
107
+ ) -> Option <io:: Result <FileAttr >> {
108
+ use crate :: sync:: atomic:: { AtomicBool , Ordering } ;
109
+
110
+ // Linux kernel prior to 4.11 or glibc prior to glibc 2.28 don't support `statx`
111
+ // We store the availability in a global to avoid unnecessary syscalls
112
+ static HAS_STATX : AtomicBool = AtomicBool :: new( true ) ;
113
+ syscall! {
114
+ fn statx(
115
+ fd: c_int,
116
+ pathname: * const libc:: c_char,
117
+ flags: c_int,
118
+ mask: libc:: c_uint,
119
+ statxbuf: * mut libc:: statx
120
+ ) -> c_int
121
+ }
122
+
123
+ if !HAS_STATX . load( Ordering :: Relaxed ) {
124
+ return None ;
125
+ }
126
+
127
+ let mut buf: libc:: statx = mem:: zeroed( ) ;
128
+ let ret = cvt( statx( fd, path, flags, mask, & mut buf) ) ;
129
+ match ret {
130
+ Err ( err) => match err. raw_os_error( ) {
131
+ Some ( libc:: ENOSYS ) => {
132
+ HAS_STATX . store( false , Ordering :: Relaxed ) ;
133
+ return None ;
134
+ }
135
+ _ => return Some ( Err ( err) ) ,
136
+ }
137
+ Ok ( _) => {
138
+ // We cannot fill `stat64` exhaustively because of private padding fields.
139
+ let mut stat: stat64 = mem:: zeroed( ) ;
140
+ // `c_ulong` on gnu-mips, `dev_t` otherwise
141
+ stat. st_dev = libc:: makedev( buf. stx_dev_major, buf. stx_dev_minor) as _;
142
+ stat. st_ino = buf. stx_ino as libc:: ino64_t;
143
+ stat. st_nlink = buf. stx_nlink as libc:: nlink_t;
144
+ stat. st_mode = buf. stx_mode as libc:: mode_t;
145
+ stat. st_uid = buf. stx_uid as libc:: uid_t;
146
+ stat. st_gid = buf. stx_gid as libc:: gid_t;
147
+ stat. st_rdev = libc:: makedev( buf. stx_rdev_major, buf. stx_rdev_minor) as _;
148
+ stat. st_size = buf. stx_size as off64_t;
149
+ stat. st_blksize = buf. stx_blksize as libc:: blksize_t;
150
+ stat. st_blocks = buf. stx_blocks as libc:: blkcnt64_t;
151
+ stat. st_atime = buf. stx_atime. tv_sec as libc:: time_t;
152
+ // `i64` on gnu-x86_64-x32, `c_ulong` otherwise.
153
+ stat. st_atime_nsec = buf. stx_atime. tv_nsec as _;
154
+ stat. st_mtime = buf. stx_mtime. tv_sec as libc:: time_t;
155
+ stat. st_mtime_nsec = buf. stx_mtime. tv_nsec as _;
156
+ stat. st_ctime = buf. stx_ctime. tv_sec as libc:: time_t;
157
+ stat. st_ctime_nsec = buf. stx_ctime. tv_nsec as _;
158
+
159
+ let extra = StatxExtraFields {
160
+ stx_mask: buf. stx_mask,
161
+ stx_btime: buf. stx_btime,
162
+ } ;
163
+
164
+ Some ( Ok ( FileAttr { stat, statx_extra_fields: Some ( extra) } ) )
165
+ }
166
+ }
167
+ }
168
+
169
+ } else {
170
+ #[ derive( Clone ) ]
171
+ pub struct FileAttr {
172
+ stat: stat64,
173
+ }
174
+ } }
175
+
49
176
// all DirEntry's will have a reference to this struct
50
177
struct InnerReadDir {
51
178
dirp : Dir ,
@@ -97,6 +224,20 @@ pub struct FileType { mode: mode_t }
97
224
#[ derive( Debug ) ]
98
225
pub struct DirBuilder { mode : mode_t }
99
226
227
+ cfg_has_statx ! { {
228
+ impl FileAttr {
229
+ fn from_stat64( stat: stat64) -> Self {
230
+ Self { stat, statx_extra_fields: None }
231
+ }
232
+ }
233
+ } else {
234
+ impl FileAttr {
235
+ fn from_stat64( stat: stat64) -> Self {
236
+ Self { stat }
237
+ }
238
+ }
239
+ } }
240
+
100
241
impl FileAttr {
101
242
pub fn size ( & self ) -> u64 { self . stat . st_size as u64 }
102
243
pub fn perm ( & self ) -> FilePermissions {
@@ -164,6 +305,22 @@ impl FileAttr {
164
305
target_os = "macos" ,
165
306
target_os = "ios" ) ) ) ]
166
307
pub fn created ( & self ) -> io:: Result < SystemTime > {
308
+ cfg_has_statx ! {
309
+ if let Some ( ext) = & self . statx_extra_fields {
310
+ return if ( ext. stx_mask & libc:: STATX_BTIME ) != 0 {
311
+ Ok ( SystemTime :: from( libc:: timespec {
312
+ tv_sec: ext. stx_btime. tv_sec as libc:: time_t,
313
+ tv_nsec: ext. stx_btime. tv_nsec as libc:: c_long,
314
+ } ) )
315
+ } else {
316
+ Err ( io:: Error :: new(
317
+ io:: ErrorKind :: Other ,
318
+ "creation time is not available for the filesystem" ,
319
+ ) )
320
+ } ;
321
+ }
322
+ }
323
+
167
324
Err ( io:: Error :: new ( io:: ErrorKind :: Other ,
168
325
"creation time is not available on this platform \
169
326
currently") )
@@ -306,12 +463,25 @@ impl DirEntry {
306
463
307
464
#[ cfg( any( target_os = "linux" , target_os = "emscripten" , target_os = "android" ) ) ]
308
465
pub fn metadata ( & self ) -> io:: Result < FileAttr > {
309
- let fd = cvt ( unsafe { dirfd ( self . dir . inner . dirp . 0 ) } ) ?;
466
+ let fd = cvt ( unsafe { dirfd ( self . dir . inner . dirp . 0 ) } ) ?;
467
+ let name = self . entry . d_name . as_ptr ( ) ;
468
+
469
+ cfg_has_statx ! {
470
+ if let Some ( ret) = unsafe { try_statx(
471
+ fd,
472
+ name,
473
+ libc:: AT_SYMLINK_NOFOLLOW | libc:: AT_STATX_SYNC_AS_STAT ,
474
+ libc:: STATX_ALL ,
475
+ ) } {
476
+ return ret;
477
+ }
478
+ }
479
+
310
480
let mut stat: stat64 = unsafe { mem:: zeroed ( ) } ;
311
481
cvt ( unsafe {
312
- fstatat64 ( fd, self . entry . d_name . as_ptr ( ) , & mut stat, libc:: AT_SYMLINK_NOFOLLOW )
482
+ fstatat64 ( fd, name , & mut stat, libc:: AT_SYMLINK_NOFOLLOW )
313
483
} ) ?;
314
- Ok ( FileAttr { stat } )
484
+ Ok ( FileAttr :: from_stat64 ( stat) )
315
485
}
316
486
317
487
#[ cfg( not( any( target_os = "linux" , target_os = "emscripten" , target_os = "android" ) ) ) ]
@@ -517,11 +687,24 @@ impl File {
517
687
}
518
688
519
689
pub fn file_attr ( & self ) -> io:: Result < FileAttr > {
690
+ let fd = self . 0 . raw ( ) ;
691
+
692
+ cfg_has_statx ! {
693
+ if let Some ( ret) = unsafe { try_statx(
694
+ fd,
695
+ b"\0 " as * const _ as * const libc:: c_char,
696
+ libc:: AT_EMPTY_PATH | libc:: AT_STATX_SYNC_AS_STAT ,
697
+ libc:: STATX_ALL ,
698
+ ) } {
699
+ return ret;
700
+ }
701
+ }
702
+
520
703
let mut stat: stat64 = unsafe { mem:: zeroed ( ) } ;
521
704
cvt ( unsafe {
522
- fstat64 ( self . 0 . raw ( ) , & mut stat)
705
+ fstat64 ( fd , & mut stat)
523
706
} ) ?;
524
- Ok ( FileAttr { stat } )
707
+ Ok ( FileAttr :: from_stat64 ( stat) )
525
708
}
526
709
527
710
pub fn fsync ( & self ) -> io:: Result < ( ) > {
@@ -798,20 +981,44 @@ pub fn link(src: &Path, dst: &Path) -> io::Result<()> {
798
981
799
982
pub fn stat ( p : & Path ) -> io:: Result < FileAttr > {
800
983
let p = cstr ( p) ?;
984
+
985
+ cfg_has_statx ! {
986
+ if let Some ( ret) = unsafe { try_statx(
987
+ libc:: AT_FDCWD ,
988
+ p. as_ptr( ) ,
989
+ libc:: AT_STATX_SYNC_AS_STAT ,
990
+ libc:: STATX_ALL ,
991
+ ) } {
992
+ return ret;
993
+ }
994
+ }
995
+
801
996
let mut stat: stat64 = unsafe { mem:: zeroed ( ) } ;
802
997
cvt ( unsafe {
803
998
stat64 ( p. as_ptr ( ) , & mut stat)
804
999
} ) ?;
805
- Ok ( FileAttr { stat } )
1000
+ Ok ( FileAttr :: from_stat64 ( stat) )
806
1001
}
807
1002
808
1003
pub fn lstat ( p : & Path ) -> io:: Result < FileAttr > {
809
1004
let p = cstr ( p) ?;
1005
+
1006
+ cfg_has_statx ! {
1007
+ if let Some ( ret) = unsafe { try_statx(
1008
+ libc:: AT_FDCWD ,
1009
+ p. as_ptr( ) ,
1010
+ libc:: AT_SYMLINK_NOFOLLOW | libc:: AT_STATX_SYNC_AS_STAT ,
1011
+ libc:: STATX_ALL ,
1012
+ ) } {
1013
+ return ret;
1014
+ }
1015
+ }
1016
+
810
1017
let mut stat: stat64 = unsafe { mem:: zeroed ( ) } ;
811
1018
cvt ( unsafe {
812
1019
lstat64 ( p. as_ptr ( ) , & mut stat)
813
1020
} ) ?;
814
- Ok ( FileAttr { stat } )
1021
+ Ok ( FileAttr :: from_stat64 ( stat) )
815
1022
}
816
1023
817
1024
pub fn canonicalize ( p : & Path ) -> io:: Result < PathBuf > {
0 commit comments