Skip to content

Commit 067a113

Browse files
committed
Fix the fstat/statat/etc. fallback when statx fails with EPERM.
On some old non-y2038-safe container runtimes, `statx` fails with `EPERM`, so use `crate::fs::statx` instead of calling the statx syscall directly. This includes code to check whether it was a "real" `EPERM` we should return to the user, or an `EPERM` which indicates that statx isn't supported, allowing rustix to fall back to the old non-y2038-safe syscalls. Also, update the crate-level comment mentionintg that rustix does sometimes do dynamic feature detection in order to support y2038 and LFS. And fix a copy and paste in a comment in `renameat2`.
1 parent 43025d8 commit 067a113

File tree

6 files changed

+40
-13
lines changed

6 files changed

+40
-13
lines changed

src/backend/libc/fs/syscalls.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -385,7 +385,7 @@ pub(crate) fn renameat2(
385385
new_path: &CStr,
386386
flags: RenameFlags,
387387
) -> io::Result<()> {
388-
// `getrandom` wasn't supported in glibc until 2.28.
388+
// `renameat2` wasn't supported in glibc until 2.28.
389389
weak_or_syscall! {
390390
fn renameat2(
391391
olddirfd: c::c_int,
@@ -442,14 +442,13 @@ pub(crate) fn symlinkat(
442442

443443
#[cfg(not(target_os = "redox"))]
444444
pub(crate) fn statat(dirfd: BorrowedFd<'_>, path: &CStr, flags: AtFlags) -> io::Result<Stat> {
445-
// 32-bit and mips64 Linux: `struct stat64` is not y2038 compatible; use
446-
// `statx`.
445+
// See the comments in `fstat` about using `crate::fs::statx` here.
447446
#[cfg(all(
448447
any(target_os = "android", target_os = "linux"),
449448
any(target_pointer_width = "32", target_arch = "mips64"),
450449
))]
451450
{
452-
match statx(dirfd, path, flags, StatxFlags::BASIC_STATS) {
451+
match crate::fs::statx(dirfd, path, flags, StatxFlags::BASIC_STATS) {
453452
Ok(x) => statx_to_stat(x),
454453
Err(io::Errno::NOSYS) => statat_old(dirfd, path, flags),
455454
Err(err) => Err(err),
@@ -1108,12 +1107,16 @@ pub(crate) fn sync() {
11081107
pub(crate) fn fstat(fd: BorrowedFd<'_>) -> io::Result<Stat> {
11091108
// 32-bit and mips64 Linux: `struct stat64` is not y2038 compatible; use
11101109
// `statx`.
1110+
//
1111+
// And, some old platforms don't support `statx`, and some fail with a
1112+
// confusing error code, so we call `crate::fs::statx` to handle that. If
1113+
// `statx` isn't available, fall back to the buggy system call.
11111114
#[cfg(all(
11121115
any(target_os = "android", target_os = "linux"),
11131116
any(target_pointer_width = "32", target_arch = "mips64"),
11141117
))]
11151118
{
1116-
match statx(fd, cstr!(""), AtFlags::EMPTY_PATH, StatxFlags::BASIC_STATS) {
1119+
match crate::fs::statx(fd, cstr!(""), AtFlags::EMPTY_PATH, StatxFlags::BASIC_STATS) {
11171120
Ok(x) => statx_to_stat(x),
11181121
Err(io::Errno::NOSYS) => fstat_old(fd),
11191122
Err(err) => Err(err),

src/backend/linux_raw/fs/syscalls.rs

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -442,9 +442,15 @@ pub(crate) fn sync() {
442442

443443
#[inline]
444444
pub(crate) fn fstat(fd: BorrowedFd<'_>) -> io::Result<Stat> {
445+
// 32-bit and mips64 Linux: `struct stat64` is not y2038 compatible; use
446+
// `statx`.
447+
//
448+
// And, some old platforms don't support `statx`, and some fail with a
449+
// confusing error code, so we call `crate::fs::statx` to handle that. If
450+
// `statx` isn't available, fall back to the buggy system call.
445451
#[cfg(any(target_pointer_width = "32", target_arch = "mips64"))]
446452
{
447-
match statx(fd, cstr!(""), AtFlags::EMPTY_PATH, StatxFlags::BASIC_STATS) {
453+
match crate::fs::statx(fd, cstr!(""), AtFlags::EMPTY_PATH, StatxFlags::BASIC_STATS) {
448454
Ok(x) => statx_to_stat(x),
449455
Err(io::Errno::NOSYS) => fstat_old(fd),
450456
Err(err) => Err(err),
@@ -478,9 +484,10 @@ fn fstat_old(fd: BorrowedFd<'_>) -> io::Result<Stat> {
478484

479485
#[inline]
480486
pub(crate) fn stat(filename: &CStr) -> io::Result<Stat> {
487+
// See the comments in `fstat` about using `crate::fs::statx` here.
481488
#[cfg(any(target_pointer_width = "32", target_arch = "mips64"))]
482489
{
483-
match statx(
490+
match crate::fs::statx(
484491
crate::fs::cwd().as_fd(),
485492
filename,
486493
AtFlags::empty(),
@@ -537,9 +544,10 @@ fn stat_old(filename: &CStr) -> io::Result<Stat> {
537544

538545
#[inline]
539546
pub(crate) fn statat(dirfd: BorrowedFd<'_>, filename: &CStr, flags: AtFlags) -> io::Result<Stat> {
547+
// See the comments in `fstat` about using `crate::fs::statx` here.
540548
#[cfg(any(target_pointer_width = "32", target_arch = "mips64"))]
541549
{
542-
match statx(dirfd, filename, flags, StatxFlags::BASIC_STATS) {
550+
match crate::fs::statx(dirfd, filename, flags, StatxFlags::BASIC_STATS) {
543551
Ok(x) => statx_to_stat(x),
544552
Err(io::Errno::NOSYS) => statat_old(dirfd, filename, flags),
545553
Err(err) => Err(err),
@@ -591,9 +599,10 @@ fn statat_old(dirfd: BorrowedFd<'_>, filename: &CStr, flags: AtFlags) -> io::Res
591599

592600
#[inline]
593601
pub(crate) fn lstat(filename: &CStr) -> io::Result<Stat> {
602+
// See the comments in `fstat` about using `crate::fs::statx` here.
594603
#[cfg(any(target_pointer_width = "32", target_arch = "mips64"))]
595604
{
596-
match statx(
605+
match crate::fs::statx(
597606
crate::fs::cwd().as_fd(),
598607
filename,
599608
AtFlags::SYMLINK_NOFOLLOW,
@@ -1310,6 +1319,8 @@ fn _utimensat(
13101319
// Assert that `Timestamps` has the expected layout.
13111320
let _ = unsafe { transmute::<Timestamps, [__kernel_timespec; 2]>(times.clone()) };
13121321

1322+
// `utimensat_time64` was introduced in Linux 5.1. The old `utimensat`
1323+
// syscall is not y2038-compatible on 32-bit architectures.
13131324
#[cfg(target_pointer_width = "32")]
13141325
unsafe {
13151326
match ret(syscall_readonly!(
@@ -1400,9 +1411,9 @@ pub(crate) fn accessat(
14001411
}
14011412

14021413
// Linux's `faccessat` syscall doesn't have a flags argument, so if we have
1403-
// any flags, use the newer `faccessat2` which does. Unless we're on
1404-
// Android where using newer system calls can cause seccomp to abort the
1405-
// process.
1414+
// any flags, use the newer `faccessat2` introduced in Linux 5.8 which
1415+
// does. Unless we're on Android where using newer system calls can cause
1416+
// seccomp to abort the process.
14061417
#[cfg(not(target_os = "android"))]
14071418
if !flags.is_empty() {
14081419
unsafe {

src/backend/linux_raw/runtime/syscalls.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,9 @@ pub(crate) fn sigtimedwait(set: &Sigset, timeout: Option<Timespec>) -> io::Resul
193193
let mut info = MaybeUninit::<Siginfo>::uninit();
194194
let timeout_ptr = optional_as_ptr(timeout.as_ref());
195195

196+
// `rt_sigtimedwait_time64` was introduced in Linux 5.1. The old
197+
// `rt_sigtimedwait` syscall is not y2038-compatible on 32-bit
198+
// architectures.
196199
#[cfg(target_pointer_width = "32")]
197200
unsafe {
198201
match ret_c_int(syscall!(

src/backend/linux_raw/time/syscalls.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ unsafe fn clock_getres_old(which_clock: ClockId, result: &mut MaybeUninit<__kern
6363
#[cfg(feature = "time")]
6464
#[inline]
6565
pub(crate) fn clock_settime(which_clock: ClockId, timespec: __kernel_timespec) -> io::Result<()> {
66+
// `clock_settime64` was introduced in Linux 5.1. The old `clock_settime`
67+
// syscall is not y2038-compatible on 32-bit architectures.
6668
#[cfg(target_pointer_width = "32")]
6769
unsafe {
6870
match ret(syscall_readonly!(

src/fs/at.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,13 @@ pub fn statat<P: path::Arg, Fd: AsFd>(dirfd: Fd, path: P, flags: AtFlags) -> io:
263263
/// `faccessat(dirfd, path, access, flags)`—Tests permissions for a file or
264264
/// directory.
265265
///
266+
/// On Linux before 5.8, this function uses the `faccessat` system call which
267+
/// doesn't support any flags. This function emulates support for the
268+
/// [`AtFlags::EACCESS`] flag by checking whether the uid and gid of the
269+
/// process match the effective uid and gid, in which case the `EACCESS` flag
270+
/// can be ignored. In Linux 5.8 and beyond `faccessat2` is used, which
271+
/// supports flags.
272+
///
266273
/// # References
267274
/// - [POSIX]
268275
/// - [Linux]

src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,8 @@
7171
//! running under seccomp.
7272
//!
7373
//! Things they don't do include:
74-
//! - Detecting whether functions are supported at runtime.
74+
//! - Detecting whether functions are supported at runtime, except in specific
75+
//! cases where new interfaces need to be detected to support y2038 and LFS.
7576
//! - Hiding significant differences between platforms.
7677
//! - Restricting ambient authorities.
7778
//! - Imposing sandboxing features such as filesystem path or network address

0 commit comments

Comments
 (0)