glibc/sysdeps/unix/sysv/linux/fstatat64.c
Adhemerval Zanella 551101e824 io: Do not implement fstat with fstatat
AT_EMPTY_PATH is a requirement to implement fstat over fstatat,
however it does not prevent the kernel to read the path argument.
It is not an issue, but on x86-64 with SMAP-capable CPUs the kernel is
forced to perform expensive user memory access.  After that regular
lookup is performed which adds even more overhead.

Instead, issue the fstat syscall directly on LFS fstat implementation
(32 bit architectures will still continue to use statx, which is
required to have 64 bit time_t support).  it should be even a
small performance gain on non x86_64, since there is no need
to handle the path argument.

Checked on x86_64-linux-gnu.
2023-09-27 09:30:24 -03:00

186 lines
5.6 KiB
C

/* Get file status. Linux version.
Copyright (C) 2020-2023 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#define __fstatat __redirect___fstatat
#define fstatat __redirect_fstatat
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <sysdep.h>
#include <time.h>
#include <sys/sysmacros.h>
#include <internal-stat.h>
#if __TIMESIZE == 64 \
&& (__WORDSIZE == 32 \
&& (!defined __SYSCALL_WORDSIZE || __SYSCALL_WORDSIZE == 32))
/* Sanity check to avoid newer 32-bit ABI to support non-LFS calls. */
_Static_assert (sizeof (__off_t) == sizeof (__off64_t),
"__blkcnt_t and __blkcnt64_t must match");
_Static_assert (sizeof (__ino_t) == sizeof (__ino64_t),
"__blkcnt_t and __blkcnt64_t must match");
_Static_assert (sizeof (__blkcnt_t) == sizeof (__blkcnt64_t),
"__blkcnt_t and __blkcnt64_t must match");
#endif
#if FSTATAT_USE_STATX
static inline int
fstatat64_time64_statx (int fd, const char *file, struct __stat64_t64 *buf,
int flag)
{
/* 32-bit kABI with default 64-bit time_t, e.g. arc, riscv32. Also
64-bit time_t support is done through statx syscall. */
struct statx tmp;
int r = INTERNAL_SYSCALL_CALL (statx, fd, file, AT_NO_AUTOMOUNT | flag,
STATX_BASIC_STATS, &tmp);
if (r != 0)
return r;
*buf = (struct __stat64_t64) {
.st_dev = __gnu_dev_makedev (tmp.stx_dev_major, tmp.stx_dev_minor),
.st_rdev = __gnu_dev_makedev (tmp.stx_rdev_major, tmp.stx_rdev_minor),
.st_ino = tmp.stx_ino,
.st_mode = tmp.stx_mode,
.st_nlink = tmp.stx_nlink,
.st_uid = tmp.stx_uid,
.st_gid = tmp.stx_gid,
.st_atime = tmp.stx_atime.tv_sec,
.st_atim.tv_nsec = tmp.stx_atime.tv_nsec,
.st_mtime = tmp.stx_mtime.tv_sec,
.st_mtim.tv_nsec = tmp.stx_mtime.tv_nsec,
.st_ctime = tmp.stx_ctime.tv_sec,
.st_ctim.tv_nsec = tmp.stx_ctime.tv_nsec,
.st_size = tmp.stx_size,
.st_blocks = tmp.stx_blocks,
.st_blksize = tmp.stx_blksize,
};
return r;
}
#endif
/* Only statx supports 64-bit timestamps for 32-bit architectures with
__ASSUME_STATX, so there is no point in building the fallback. */
#if !FSTATAT_USE_STATX || (FSTATAT_USE_STATX && !defined __ASSUME_STATX)
static inline int
fstatat64_time64_stat (int fd, const char *file, struct __stat64_t64 *buf,
int flag)
{
int r;
#if XSTAT_IS_XSTAT64
# ifdef __NR_newfstatat
/* 64-bit kABI, e.g. aarch64, ia64, powerpc64*, s390x, riscv64, and
x86_64. */
r = INTERNAL_SYSCALL_CALL (newfstatat, fd, file, buf, flag);
# elif defined __NR_fstatat64
# if STAT64_IS_KERNEL_STAT64
/* 64-bit kABI outlier, e.g. alpha */
r = INTERNAL_SYSCALL_CALL (fstatat64, fd, file, buf, flag);
# else
/* 64-bit kABI outlier, e.g. sparc64. */
struct kernel_stat64 kst64;
r = INTERNAL_SYSCALL_CALL (fstatat64, fd, file, &kst64, flag);
if (r == 0)
__cp_stat64_kstat64 (buf, &kst64);
# endif
# endif
#else
# ifdef __NR_fstatat64
/* All kABIs with non-LFS support and with old 32-bit time_t support
e.g. arm, csky, i386, hppa, m68k, microblaze, nios2, sh, powerpc32,
and sparc32. */
struct stat64 st64;
r = INTERNAL_SYSCALL_CALL (fstatat64, fd, file, &st64, flag);
if (r == 0)
{
/* Clear both pad and reserved fields. */
memset (buf, 0, sizeof (*buf));
buf->st_dev = st64.st_dev,
buf->st_ino = st64.st_ino;
buf->st_mode = st64.st_mode;
buf->st_nlink = st64.st_nlink;
buf->st_uid = st64.st_uid;
buf->st_gid = st64.st_gid;
buf->st_rdev = st64.st_rdev;
buf->st_size = st64.st_size;
buf->st_blksize = st64.st_blksize;
buf->st_blocks = st64.st_blocks;
buf->st_atim = valid_timespec_to_timespec64 (st64.st_atim);
buf->st_mtim = valid_timespec_to_timespec64 (st64.st_mtim);
buf->st_ctim = valid_timespec_to_timespec64 (st64.st_ctim);
}
# else
/* 64-bit kabi outlier, e.g. mips64 and mips64-n32. */
struct kernel_stat kst;
r = INTERNAL_SYSCALL_CALL (newfstatat, fd, file, &kst, flag);
if (r == 0)
__cp_kstat_stat64_t64 (&kst, buf);
# endif
#endif
return r;
}
#endif
int
__fstatat64_time64 (int fd, const char *file, struct __stat64_t64 *buf,
int flag)
{
int r;
#if FSTATAT_USE_STATX
r = fstatat64_time64_statx (fd, file, buf, flag);
# ifndef __ASSUME_STATX
if (r == -ENOSYS)
r = fstatat64_time64_stat (fd, file, buf, flag);
# endif
#else
r = fstatat64_time64_stat (fd, file, buf, flag);
#endif
return INTERNAL_SYSCALL_ERROR_P (r)
? INLINE_SYSCALL_ERROR_RETURN_VALUE (-r)
: 0;
}
#if __TIMESIZE != 64
hidden_def (__fstatat64_time64)
int
__fstatat64 (int fd, const char *file, struct stat64 *buf, int flags)
{
struct __stat64_t64 st_t64;
return __fstatat64_time64 (fd, file, &st_t64, flags)
?: __cp_stat64_t64_stat64 (&st_t64, buf);
}
#endif
#undef __fstatat
#undef fstatat
hidden_def (__fstatat64)
weak_alias (__fstatat64, fstatat64)
#if XSTAT_IS_XSTAT64
strong_alias (__fstatat64, __fstatat)
weak_alias (__fstatat64, fstatat)
strong_alias (__fstatat64, __GI___fstatat);
#endif