about summary refs log tree commit diff
path: root/src/libstd/sys
diff options
context:
space:
mode:
authorMazdak Farrokhzad <twingoow@gmail.com>2019-10-25 13:12:48 +0200
committerGitHub <noreply@github.com>2019-10-25 13:12:48 +0200
commitf1d747a99dd32919ca719ae49d2e7673c6ab689e (patch)
tree4be405bef3d3d0b5c0c012e8cb2901f08f48af3c /src/libstd/sys
parent8bb039fb83f7f18df5e1d71607981c8d4902b7b3 (diff)
parent10f1bc77b3c404ebc1d386fc14453b6b32cf02bb (diff)
downloadrust-f1d747a99dd32919ca719ae49d2e7673c6ab689e.tar.gz
rust-f1d747a99dd32919ca719ae49d2e7673c6ab689e.zip
Rollup merge of #65685 - oxalica:statx-eperm, r=alexcrichton
Fix check of `statx` and handle EPERM

Should fix #65662

https://github.com/rust-lang/rust/issues/65662#issuecomment-544593939
> I think a reasonable solution might be to do something like try to stat AT_CWD initially and if that fails with EPERM or ENOSYS we disable the syscall entirely, otherwise it's cached as always good to use.

r? @alexcrichton
Diffstat (limited to 'src/libstd/sys')
-rw-r--r--src/libstd/sys/unix/fs.rs97
1 files changed, 55 insertions, 42 deletions
diff --git a/src/libstd/sys/unix/fs.rs b/src/libstd/sys/unix/fs.rs
index 39cc120594a..5e1f10c03ce 100644
--- a/src/libstd/sys/unix/fs.rs
+++ b/src/libstd/sys/unix/fs.rs
@@ -105,11 +105,14 @@ cfg_has_statx! {{
         flags: i32,
         mask: u32,
     ) -> Option<io::Result<FileAttr>> {
-        use crate::sync::atomic::{AtomicBool, Ordering};
+        use crate::sync::atomic::{AtomicU8, Ordering};
 
         // Linux kernel prior to 4.11 or glibc prior to glibc 2.28 don't support `statx`
-        // We store the availability in a global to avoid unnecessary syscalls
-        static HAS_STATX: AtomicBool = AtomicBool::new(true);
+        // We store the availability in global to avoid unnecessary syscalls.
+        // 0: Unknown
+        // 1: Not available
+        // 2: Available
+        static STATX_STATE: AtomicU8 = AtomicU8::new(0);
         syscall! {
             fn statx(
                 fd: c_int,
@@ -120,50 +123,60 @@ cfg_has_statx! {{
             ) -> c_int
         }
 
-        if !HAS_STATX.load(Ordering::Relaxed) {
-            return None;
-        }
-
-        let mut buf: libc::statx = mem::zeroed();
-        let ret = cvt(statx(fd, path, flags, mask, &mut buf));
-        match ret {
-            Err(err) => match err.raw_os_error() {
-                Some(libc::ENOSYS) => {
-                    HAS_STATX.store(false, Ordering::Relaxed);
+        match STATX_STATE.load(Ordering::Relaxed) {
+            0 => {
+                // It is a trick to call `statx` with NULL pointers to check if the syscall
+                // is available. According to the manual, it is expected to fail with EFAULT.
+                // We do this mainly for performance, since it is nearly hundreds times
+                // faster than a normal successfull call.
+                let err = cvt(statx(0, ptr::null(), 0, libc::STATX_ALL, ptr::null_mut()))
+                    .err()
+                    .and_then(|e| e.raw_os_error());
+                // We don't check `err == Some(libc::ENOSYS)` because the syscall may be limited
+                // and returns `EPERM`. Listing all possible errors seems not a good idea.
+                // See: https://github.com/rust-lang/rust/issues/65662
+                if err != Some(libc::EFAULT) {
+                    STATX_STATE.store(1, Ordering::Relaxed);
                     return None;
                 }
-                _ => return Some(Err(err)),
+                STATX_STATE.store(2, Ordering::Relaxed);
             }
-            Ok(_) => {
-                // We cannot fill `stat64` exhaustively because of private padding fields.
-                let mut stat: stat64 = mem::zeroed();
-                // `c_ulong` on gnu-mips, `dev_t` otherwise
-                stat.st_dev = libc::makedev(buf.stx_dev_major, buf.stx_dev_minor) as _;
-                stat.st_ino = buf.stx_ino as libc::ino64_t;
-                stat.st_nlink = buf.stx_nlink as libc::nlink_t;
-                stat.st_mode = buf.stx_mode as libc::mode_t;
-                stat.st_uid = buf.stx_uid as libc::uid_t;
-                stat.st_gid = buf.stx_gid as libc::gid_t;
-                stat.st_rdev = libc::makedev(buf.stx_rdev_major, buf.stx_rdev_minor) as _;
-                stat.st_size = buf.stx_size as off64_t;
-                stat.st_blksize = buf.stx_blksize as libc::blksize_t;
-                stat.st_blocks = buf.stx_blocks as libc::blkcnt64_t;
-                stat.st_atime = buf.stx_atime.tv_sec as libc::time_t;
-                // `i64` on gnu-x86_64-x32, `c_ulong` otherwise.
-                stat.st_atime_nsec = buf.stx_atime.tv_nsec as _;
-                stat.st_mtime = buf.stx_mtime.tv_sec as libc::time_t;
-                stat.st_mtime_nsec = buf.stx_mtime.tv_nsec as _;
-                stat.st_ctime = buf.stx_ctime.tv_sec as libc::time_t;
-                stat.st_ctime_nsec = buf.stx_ctime.tv_nsec as _;
-
-                let extra = StatxExtraFields {
-                    stx_mask: buf.stx_mask,
-                    stx_btime: buf.stx_btime,
-                };
+            1 => return None,
+            _ => {}
+        }
 
-                Some(Ok(FileAttr { stat, statx_extra_fields: Some(extra) }))
-            }
+        let mut buf: libc::statx = mem::zeroed();
+        if let Err(err) = cvt(statx(fd, path, flags, mask, &mut buf)) {
+            return Some(Err(err));
         }
+
+        // We cannot fill `stat64` exhaustively because of private padding fields.
+        let mut stat: stat64 = mem::zeroed();
+        // `c_ulong` on gnu-mips, `dev_t` otherwise
+        stat.st_dev = libc::makedev(buf.stx_dev_major, buf.stx_dev_minor) as _;
+        stat.st_ino = buf.stx_ino as libc::ino64_t;
+        stat.st_nlink = buf.stx_nlink as libc::nlink_t;
+        stat.st_mode = buf.stx_mode as libc::mode_t;
+        stat.st_uid = buf.stx_uid as libc::uid_t;
+        stat.st_gid = buf.stx_gid as libc::gid_t;
+        stat.st_rdev = libc::makedev(buf.stx_rdev_major, buf.stx_rdev_minor) as _;
+        stat.st_size = buf.stx_size as off64_t;
+        stat.st_blksize = buf.stx_blksize as libc::blksize_t;
+        stat.st_blocks = buf.stx_blocks as libc::blkcnt64_t;
+        stat.st_atime = buf.stx_atime.tv_sec as libc::time_t;
+        // `i64` on gnu-x86_64-x32, `c_ulong` otherwise.
+        stat.st_atime_nsec = buf.stx_atime.tv_nsec as _;
+        stat.st_mtime = buf.stx_mtime.tv_sec as libc::time_t;
+        stat.st_mtime_nsec = buf.stx_mtime.tv_nsec as _;
+        stat.st_ctime = buf.stx_ctime.tv_sec as libc::time_t;
+        stat.st_ctime_nsec = buf.stx_ctime.tv_nsec as _;
+
+        let extra = StatxExtraFields {
+            stx_mask: buf.stx_mask,
+            stx_btime: buf.stx_btime,
+        };
+
+        Some(Ok(FileAttr { stat, statx_extra_fields: Some(extra) }))
     }
 
 } else {