From c787fe3c70bdb84d0c82d6c592080ca2f1d7902f Mon Sep 17 00:00:00 2001 From: oxalica Date: Tue, 22 Oct 2019 12:02:32 +0800 Subject: [PATCH 1/2] Fix check of `statx` --- src/libstd/sys/unix/fs.rs | 50 ++++++++++++++++++++++++++------------- 1 file changed, 34 insertions(+), 16 deletions(-) diff --git a/src/libstd/sys/unix/fs.rs b/src/libstd/sys/unix/fs.rs index 39cc120594aab..54b2aee94001f 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> { - 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,21 +123,36 @@ 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); - return None; + match STATX_STATE.load(Ordering::Relaxed) { + // For the first time, we try to call on current working directory + // to check if it is available. + 0 => { + let mut buf: libc::statx = mem::zeroed(); + let err = cvt(statx( + libc::AT_FDCWD, + b".\0".as_ptr().cast(), + 0, + libc::STATX_ALL, + &mut buf, + )) + .err() + .and_then(|e| e.raw_os_error()); + // `seccomp` will emit `EPERM` on denied syscall. + // See: https://github.com/rust-lang/rust/issues/65662 + if err == Some(libc::ENOSYS) || err == Some(libc::EPERM) { + STATX_STATE.store(1, Ordering::Relaxed); + } else { + STATX_STATE.store(2, Ordering::Relaxed); } - _ => return Some(Err(err)), + try_statx(fd, path, flags, mask) } - Ok(_) => { + 1 => None, + _ => { + 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 From 10f1bc77b3c404ebc1d386fc14453b6b32cf02bb Mon Sep 17 00:00:00 2001 From: oxalica Date: Wed, 23 Oct 2019 02:43:45 +0800 Subject: [PATCH 2/2] Some tweaks --- src/libstd/sys/unix/fs.rs | 93 ++++++++++++++++++--------------------- 1 file changed, 44 insertions(+), 49 deletions(-) diff --git a/src/libstd/sys/unix/fs.rs b/src/libstd/sys/unix/fs.rs index 54b2aee94001f..5e1f10c03ceda 100644 --- a/src/libstd/sys/unix/fs.rs +++ b/src/libstd/sys/unix/fs.rs @@ -124,64 +124,59 @@ cfg_has_statx! {{ } match STATX_STATE.load(Ordering::Relaxed) { - // For the first time, we try to call on current working directory - // to check if it is available. 0 => { - let mut buf: libc::statx = mem::zeroed(); - let err = cvt(statx( - libc::AT_FDCWD, - b".\0".as_ptr().cast(), - 0, - libc::STATX_ALL, - &mut buf, - )) + // 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()); - // `seccomp` will emit `EPERM` on denied syscall. + // 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::ENOSYS) || err == Some(libc::EPERM) { + if err != Some(libc::EFAULT) { STATX_STATE.store(1, Ordering::Relaxed); - } else { - STATX_STATE.store(2, Ordering::Relaxed); + return None; } - try_statx(fd, path, flags, mask) + STATX_STATE.store(2, Ordering::Relaxed); } - 1 => None, - _ => { - 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, - }; + 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 {