@@ -105,11 +105,14 @@ cfg_has_statx! {{
105105 flags: i32 ,
106106 mask: u32 ,
107107 ) -> Option <io:: Result <FileAttr >> {
108- use crate :: sync:: atomic:: { AtomicBool , Ordering } ;
108+ use crate :: sync:: atomic:: { AtomicU8 , Ordering } ;
109109
110110 // 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 ) ;
113116 syscall! {
114117 fn statx(
115118 fd: c_int,
@@ -120,50 +123,60 @@ cfg_has_statx! {{
120123 ) -> c_int
121124 }
122125
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 ) ;
133140 return None ;
134141 }
135- _ => return Some ( Err ( err ) ) ,
142+ STATX_STATE . store ( 2 , Ordering :: Relaxed ) ;
136143 }
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+ }
163147
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) ) ;
166151 }
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) } ) )
167180 }
168181
169182} else {
0 commit comments