@@ -41,88 +41,137 @@ 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 ,
47
- #[ cfg( target_os = "linux" ) ]
48
- statx_extra_fields : Option < StatxExtraFields > ,
49
- }
50
-
51
- #[ cfg( target_os = "linux" ) ]
52
- #[ derive( Clone ) ]
53
- struct StatxExtraFields {
54
- // This is needed to check if btime is supported by the filesystem.
55
- stx_mask : u32 ,
56
- stx_btime : libc:: statx_timestamp ,
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
+ } ;
57
84
}
58
85
59
- // We prefer `statx` on Linux if available, which contains file creation time.
60
- // Default `stat64` contains no creation time.
61
- #[ cfg( target_os = "linux" ) ]
62
- unsafe fn try_statx (
63
- fd : c_int ,
64
- path : * const libc:: c_char ,
65
- flags : i32 ,
66
- mask : u32 ,
67
- ) -> Option < io:: Result < FileAttr > > {
68
- use crate :: sync:: atomic:: { AtomicBool , Ordering } ;
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
+ }
69
122
70
- // Linux kernel prior to 4.11 or glibc prior to glibc 2.28 don't support `statx`
71
- // We store the availability in a global to avoid unnecessary syscalls
72
- static HAS_STATX : AtomicBool = AtomicBool :: new ( true ) ;
73
- syscall ! {
74
- fn statx(
75
- fd: c_int,
76
- pathname: * const libc:: c_char,
77
- flags: c_int,
78
- mask: libc:: c_uint,
79
- statxbuf: * mut libc:: statx
80
- ) -> c_int
81
- }
123
+ if !HAS_STATX . load( Ordering :: Relaxed ) {
124
+ return None ;
125
+ }
82
126
83
- if !HAS_STATX . load ( Ordering :: Relaxed ) {
84
- return None ;
85
- }
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
+ } ;
86
163
87
- let mut buf: libc:: statx = mem:: zeroed ( ) ;
88
- let ret = cvt ( statx ( fd, path, flags, mask, & mut buf) ) ;
89
- match ret {
90
- Err ( err) => match err. raw_os_error ( ) {
91
- Some ( libc:: ENOSYS ) => {
92
- HAS_STATX . store ( false , Ordering :: Relaxed ) ;
93
- return None ;
164
+ Some ( Ok ( FileAttr { stat, statx_extra_fields: Some ( extra) } ) )
94
165
}
95
- _ => return Some ( Err ( err) ) ,
96
166
}
97
- Ok ( _) => {
98
- // We cannot fill `stat64` exhaustively because of private padding fields.
99
- let mut stat: stat64 = mem:: zeroed ( ) ;
100
- stat. st_dev = libc:: makedev ( buf. stx_dev_major , buf. stx_dev_minor ) ;
101
- stat. st_ino = buf. stx_ino as libc:: ino64_t ;
102
- stat. st_nlink = buf. stx_nlink as libc:: nlink_t ;
103
- stat. st_mode = buf. stx_mode as libc:: mode_t ;
104
- stat. st_uid = buf. stx_uid as libc:: uid_t ;
105
- stat. st_gid = buf. stx_gid as libc:: gid_t ;
106
- stat. st_rdev = libc:: makedev ( buf. stx_rdev_major , buf. stx_rdev_minor ) ;
107
- stat. st_size = buf. stx_size as off64_t ;
108
- stat. st_blksize = buf. stx_blksize as libc:: blksize_t ;
109
- stat. st_blocks = buf. stx_blocks as libc:: blkcnt64_t ;
110
- stat. st_atime = buf. stx_atime . tv_sec as libc:: time_t ;
111
- stat. st_atime_nsec = buf. stx_atime . tv_nsec as libc:: c_long ;
112
- stat. st_mtime = buf. stx_mtime . tv_sec as libc:: time_t ;
113
- stat. st_mtime_nsec = buf. stx_mtime . tv_nsec as libc:: c_long ;
114
- stat. st_ctime = buf. stx_ctime . tv_sec as libc:: time_t ;
115
- stat. st_ctime_nsec = buf. stx_ctime . tv_nsec as libc:: c_long ;
116
-
117
- let extra = StatxExtraFields {
118
- stx_mask : buf. stx_mask ,
119
- stx_btime : buf. stx_btime ,
120
- } ;
167
+ }
121
168
122
- Some ( Ok ( FileAttr { stat, statx_extra_fields : Some ( extra) } ) )
123
- }
169
+ } else {
170
+ #[ derive( Clone ) ]
171
+ pub struct FileAttr {
172
+ stat: stat64,
124
173
}
125
- }
174
+ } }
126
175
127
176
// all DirEntry's will have a reference to this struct
128
177
struct InnerReadDir {
@@ -175,15 +224,21 @@ pub struct FileType { mode: mode_t }
175
224
#[ derive( Debug ) ]
176
225
pub struct DirBuilder { mode : mode_t }
177
226
178
- impl FileAttr {
179
- fn from_stat64 ( stat : stat64 ) -> Self {
180
- Self {
181
- stat,
182
- #[ cfg( target_os = "linux" ) ]
183
- statx_extra_fields : None ,
227
+ cfg_has_statx ! { {
228
+ impl FileAttr {
229
+ fn from_stat64( stat: stat64) -> Self {
230
+ Self { stat, statx_extra_fields: None }
184
231
}
185
232
}
233
+ } else {
234
+ impl FileAttr {
235
+ fn from_stat64( stat: stat64) -> Self {
236
+ Self { stat }
237
+ }
238
+ }
239
+ } }
186
240
241
+ impl FileAttr {
187
242
pub fn size ( & self ) -> u64 { self . stat . st_size as u64 }
188
243
pub fn perm ( & self ) -> FilePermissions {
189
244
FilePermissions { mode : ( self . stat . st_mode as mode_t ) }
@@ -250,8 +305,7 @@ impl FileAttr {
250
305
target_os = "macos" ,
251
306
target_os = "ios" ) ) ) ]
252
307
pub fn created ( & self ) -> io:: Result < SystemTime > {
253
- #[ cfg( target_os = "linux" ) ]
254
- {
308
+ cfg_has_statx ! {
255
309
if let Some ( ext) = & self . statx_extra_fields {
256
310
return if ( ext. stx_mask & libc:: STATX_BTIME ) != 0 {
257
311
Ok ( SystemTime :: from( libc:: timespec {
@@ -412,8 +466,7 @@ impl DirEntry {
412
466
let fd = cvt ( unsafe { dirfd ( self . dir . inner . dirp . 0 ) } ) ?;
413
467
let name = self . entry . d_name . as_ptr ( ) ;
414
468
415
- #[ cfg( target_os = "linux" ) ]
416
- {
469
+ cfg_has_statx ! {
417
470
if let Some ( ret) = unsafe { try_statx(
418
471
fd,
419
472
name,
@@ -636,8 +689,7 @@ impl File {
636
689
pub fn file_attr ( & self ) -> io:: Result < FileAttr > {
637
690
let fd = self . 0 . raw ( ) ;
638
691
639
- #[ cfg( target_os = "linux" ) ]
640
- {
692
+ cfg_has_statx ! {
641
693
if let Some ( ret) = unsafe { try_statx(
642
694
fd,
643
695
b"\0 " as * const _ as * const libc:: c_char,
@@ -930,8 +982,7 @@ pub fn link(src: &Path, dst: &Path) -> io::Result<()> {
930
982
pub fn stat ( p : & Path ) -> io:: Result < FileAttr > {
931
983
let p = cstr ( p) ?;
932
984
933
- #[ cfg( target_os = "linux" ) ]
934
- {
985
+ cfg_has_statx ! {
935
986
if let Some ( ret) = unsafe { try_statx(
936
987
libc:: AT_FDCWD ,
937
988
p. as_ptr( ) ,
@@ -952,8 +1003,7 @@ pub fn stat(p: &Path) -> io::Result<FileAttr> {
952
1003
pub fn lstat ( p : & Path ) -> io:: Result < FileAttr > {
953
1004
let p = cstr ( p) ?;
954
1005
955
- #[ cfg( target_os = "linux" ) ]
956
- {
1006
+ cfg_has_statx ! {
957
1007
if let Some ( ret) = unsafe { try_statx(
958
1008
libc:: AT_FDCWD ,
959
1009
p. as_ptr( ) ,
0 commit comments