@@ -105,11 +105,14 @@ cfg_has_statx! {{
105
105
flags: i32 ,
106
106
mask: u32 ,
107
107
) -> Option <io:: Result <FileAttr >> {
108
- use crate :: sync:: atomic:: { AtomicBool , Ordering } ;
108
+ use crate :: sync:: atomic:: { AtomicU8 , Ordering } ;
109
109
110
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 ) ;
111
+ // We store the availability in global to avoid unnecessary syscalls.
112
+ // 0: Unknown
113
+ // 1: Not available
114
+ // 2: Available
115
+ static STATX_STATE : AtomicU8 = AtomicU8 :: new( 0 ) ;
113
116
syscall! {
114
117
fn statx(
115
118
fd: c_int,
@@ -120,50 +123,60 @@ cfg_has_statx! {{
120
123
) -> c_int
121
124
}
122
125
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 ) ;
126
+ match STATX_STATE . load( Ordering :: Relaxed ) {
127
+ 0 => {
128
+ // It is a trick to call `statx` with NULL pointers to check if the syscall
129
+ // is available. According to the manual, it is expected to fail with EFAULT.
130
+ // We do this mainly for performance, since it is nearly hundreds times
131
+ // faster than a normal successfull call.
132
+ let err = cvt( statx( 0 , ptr:: null( ) , 0 , libc:: STATX_ALL , ptr:: null_mut( ) ) )
133
+ . err( )
134
+ . and_then( |e| e. raw_os_error( ) ) ;
135
+ // We don't check `err == Some(libc::ENOSYS)` because the syscall may be limited
136
+ // and returns `EPERM`. Listing all possible errors seems not a good idea.
137
+ // See: https://github.com/rust-lang/rust/issues/65662
138
+ if err != Some ( libc:: EFAULT ) {
139
+ STATX_STATE . store( 1 , Ordering :: Relaxed ) ;
133
140
return None ;
134
141
}
135
- _ => return Some ( Err ( err ) ) ,
142
+ STATX_STATE . store ( 2 , Ordering :: Relaxed ) ;
136
143
}
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
- } ;
144
+ 1 => return None ,
145
+ _ => { }
146
+ }
163
147
164
- Some ( Ok ( FileAttr { stat, statx_extra_fields: Some ( extra) } ) )
165
- }
148
+ let mut buf: libc:: statx = mem:: zeroed( ) ;
149
+ if let Err ( err) = cvt( statx( fd, path, flags, mask, & mut buf) ) {
150
+ return Some ( Err ( err) ) ;
166
151
}
152
+
153
+ // We cannot fill `stat64` exhaustively because of private padding fields.
154
+ let mut stat: stat64 = mem:: zeroed( ) ;
155
+ // `c_ulong` on gnu-mips, `dev_t` otherwise
156
+ stat. st_dev = libc:: makedev( buf. stx_dev_major, buf. stx_dev_minor) as _;
157
+ stat. st_ino = buf. stx_ino as libc:: ino64_t;
158
+ stat. st_nlink = buf. stx_nlink as libc:: nlink_t;
159
+ stat. st_mode = buf. stx_mode as libc:: mode_t;
160
+ stat. st_uid = buf. stx_uid as libc:: uid_t;
161
+ stat. st_gid = buf. stx_gid as libc:: gid_t;
162
+ stat. st_rdev = libc:: makedev( buf. stx_rdev_major, buf. stx_rdev_minor) as _;
163
+ stat. st_size = buf. stx_size as off64_t;
164
+ stat. st_blksize = buf. stx_blksize as libc:: blksize_t;
165
+ stat. st_blocks = buf. stx_blocks as libc:: blkcnt64_t;
166
+ stat. st_atime = buf. stx_atime. tv_sec as libc:: time_t;
167
+ // `i64` on gnu-x86_64-x32, `c_ulong` otherwise.
168
+ stat. st_atime_nsec = buf. stx_atime. tv_nsec as _;
169
+ stat. st_mtime = buf. stx_mtime. tv_sec as libc:: time_t;
170
+ stat. st_mtime_nsec = buf. stx_mtime. tv_nsec as _;
171
+ stat. st_ctime = buf. stx_ctime. tv_sec as libc:: time_t;
172
+ stat. st_ctime_nsec = buf. stx_ctime. tv_nsec as _;
173
+
174
+ let extra = StatxExtraFields {
175
+ stx_mask: buf. stx_mask,
176
+ stx_btime: buf. stx_btime,
177
+ } ;
178
+
179
+ Some ( Ok ( FileAttr { stat, statx_extra_fields: Some ( extra) } ) )
167
180
}
168
181
169
182
} else {
0 commit comments