diff mbox series

[15/16] linux: Add {f}stat{at} y2038 support

Message ID 20200723194641.1949404-16-adhemerval.zanella@linaro.org
State New
Headers show
Series Add y2038 support for stat functions | expand

Commit Message

Adhemerval Zanella July 23, 2020, 7:46 p.m. UTC
A new struct __stat{64}_t64 type is added with the required
__timespec64 time definition.  Both non-LFS and LFS support were
done with an extra __NR_statx call plus a conversion to the new
__stat{64}_t64 type.  The statx call is done only for architectures
with support for 32-bit time_t ABI.

Internally some extra routines to copy from/to struct stat{64}
to struct __stat{64} used on multiple implementations (stat, fstat,
lstat, and fstatat) are added on a extra file (stat_t64_cp.c).  Aslo
some extra routines to copy from statx to __stat{64} is added on
statx_cp.c.

Checked with a build for all affected ABIs. I also checked on x86_64,
i686, powerpc, powerpc64le, sparcv9, sparc64, s390, and s390x.
---
 include/sys/stat.h                            |  32 +++++
 sysdeps/generic/struct_stat_time64.h          |   7 +
 sysdeps/unix/sysv/linux/Makefile              |   2 +-
 sysdeps/unix/sysv/linux/fstat.c               |  13 +-
 sysdeps/unix/sysv/linux/fstat64.c             |  20 ++-
 sysdeps/unix/sysv/linux/fstatat.c             |  64 +++++----
 sysdeps/unix/sysv/linux/fstatat64.c           |  88 ++++++++++---
 sysdeps/unix/sysv/linux/lstat.c               |  13 +-
 sysdeps/unix/sysv/linux/lstat64.c             |  19 ++-
 .../unix/sysv/linux/mips/mips64/kstat_cp.h    |  23 +---
 .../unix/sysv/linux/mips/mips64/statx_cp.c    |   3 -
 sysdeps/unix/sysv/linux/stat.c                |  13 +-
 sysdeps/unix/sysv/linux/stat64.c              |  20 ++-
 sysdeps/unix/sysv/linux/stat_t64_cp.c         |  92 +++++++++++++
 sysdeps/unix/sysv/linux/stat_t64_cp.h         |  28 ++++
 sysdeps/unix/sysv/linux/statx_cp.c            |  54 ++++++++
 sysdeps/unix/sysv/linux/statx_cp.h            |   6 +
 sysdeps/unix/sysv/linux/struct_stat_time64.h  | 122 ++++++++++++++++++
 18 files changed, 539 insertions(+), 80 deletions(-)
 create mode 100644 sysdeps/generic/struct_stat_time64.h
 delete mode 100644 sysdeps/unix/sysv/linux/mips/mips64/statx_cp.c
 create mode 100644 sysdeps/unix/sysv/linux/stat_t64_cp.c
 create mode 100644 sysdeps/unix/sysv/linux/stat_t64_cp.h
 create mode 100644 sysdeps/unix/sysv/linux/struct_stat_time64.h

Comments

Joseph Myers July 23, 2020, 8:55 p.m. UTC | #1
On Thu, 23 Jul 2020, Adhemerval Zanella via Libc-alpha wrote:

> A new struct __stat{64}_t64 type is added with the required
> __timespec64 time definition.  Both non-LFS and LFS support were
> done with an extra __NR_statx call plus a conversion to the new
> __stat{64}_t64 type.  The statx call is done only for architectures
> with support for 32-bit time_t ABI.

The combination of 64-bit times with 32-bit offsets is not supposed to be 
supported (no existing glibc configuration supports such a combination); 
_TIME_BITS=64 without _FILE_OFFSET_BITS=64 should be an error.  Thus, the 
__stat_t64 type and functions shouldn't be present.
Adhemerval Zanella July 23, 2020, 9 p.m. UTC | #2
On 23/07/2020 17:55, Joseph Myers wrote:
> On Thu, 23 Jul 2020, Adhemerval Zanella via Libc-alpha wrote:
> 
>> A new struct __stat{64}_t64 type is added with the required
>> __timespec64 time definition.  Both non-LFS and LFS support were
>> done with an extra __NR_statx call plus a conversion to the new
>> __stat{64}_t64 type.  The statx call is done only for architectures
>> with support for 32-bit time_t ABI.
> 
> The combination of 64-bit times with 32-bit offsets is not supposed to be 
> supported (no existing glibc configuration supports such a combination); 
> _TIME_BITS=64 without _FILE_OFFSET_BITS=64 should be an error.  Thus, the 
> __stat_t64 type and functions shouldn't be present.
> 

Ok, I was not sure if the plan was to enforce it, but I agree that it
does make sense.  And this simplifies the require work as well.
Lukasz Majewski July 24, 2020, 10:53 a.m. UTC | #3
Hi Adhemerval,

> A new struct __stat{64}_t64 type is added with the required
> __timespec64 time definition.  Both non-LFS and LFS support were
> done with an extra __NR_statx call plus a conversion to the new
> __stat{64}_t64 type.  The statx call is done only for architectures
> with support for 32-bit time_t ABI.
> 
> Internally some extra routines to copy from/to struct stat{64}
> to struct __stat{64} used on multiple implementations (stat, fstat,
> lstat, and fstatat) are added on a extra file (stat_t64_cp.c).  Aslo
> some extra routines to copy from statx to __stat{64} is added on
> statx_cp.c.
> 
> Checked with a build for all affected ABIs. I also checked on x86_64,
> i686, powerpc, powerpc64le, sparcv9, sparc64, s390, and s390x.

This patch set is a huge step forward for the *stat* conversion to
support 64 bit time.

In fact we will only need redirections when -D__USE_TIME_BITS64 support
is added.

> ---
>  include/sys/stat.h                            |  32 +++++
>  sysdeps/generic/struct_stat_time64.h          |   7 +
>  sysdeps/unix/sysv/linux/Makefile              |   2 +-
>  sysdeps/unix/sysv/linux/fstat.c               |  13 +-
>  sysdeps/unix/sysv/linux/fstat64.c             |  20 ++-
>  sysdeps/unix/sysv/linux/fstatat.c             |  64 +++++----
>  sysdeps/unix/sysv/linux/fstatat64.c           |  88 ++++++++++---
>  sysdeps/unix/sysv/linux/lstat.c               |  13 +-
>  sysdeps/unix/sysv/linux/lstat64.c             |  19 ++-
>  .../unix/sysv/linux/mips/mips64/kstat_cp.h    |  23 +---
>  .../unix/sysv/linux/mips/mips64/statx_cp.c    |   3 -
>  sysdeps/unix/sysv/linux/stat.c                |  13 +-
>  sysdeps/unix/sysv/linux/stat64.c              |  20 ++-
>  sysdeps/unix/sysv/linux/stat_t64_cp.c         |  92 +++++++++++++
>  sysdeps/unix/sysv/linux/stat_t64_cp.h         |  28 ++++
>  sysdeps/unix/sysv/linux/statx_cp.c            |  54 ++++++++
>  sysdeps/unix/sysv/linux/statx_cp.h            |   6 +
>  sysdeps/unix/sysv/linux/struct_stat_time64.h  | 122
> ++++++++++++++++++ 18 files changed, 539 insertions(+), 80
> deletions(-) create mode 100644 sysdeps/generic/struct_stat_time64.h
>  delete mode 100644 sysdeps/unix/sysv/linux/mips/mips64/statx_cp.c
>  create mode 100644 sysdeps/unix/sysv/linux/stat_t64_cp.c
>  create mode 100644 sysdeps/unix/sysv/linux/stat_t64_cp.h
>  create mode 100644 sysdeps/unix/sysv/linux/struct_stat_time64.h
> 
> diff --git a/include/sys/stat.h b/include/sys/stat.h
> index 199173b007..87d4a5ec4f 100644
> --- a/include/sys/stat.h
> +++ b/include/sys/stat.h
> @@ -3,6 +3,8 @@
>  
>  #ifndef _ISOMAC
>  # include <xstatver.h>
> +# include <struct___timespec64.h>
> +# include <struct_stat_time64.h>
>  # include <stdbool.h>
>  
>  static inline bool
> @@ -44,6 +46,36 @@ hidden_proto (__lstat64)
>  hidden_proto (__fstatat64)
>  # endif
>  
> +# if __TIMESIZE == 64
> +#  define __stat_time64  __stat
> +#  define __stat64_time64  __stat64
> +#  define __fstat_time64  __fstat
> +#  define __fstat64_time64  __fstat64
> +#  define __lstat_time64  __lstat
> +#  define __lstat64_time64  __lstat64
> +#  define __fstatat_time64  __fstatat
> +#  define __fstatat64_time64 __fstatat64
> +# else
> +extern int __stat_time64 (const char *file, struct __stat_t64 *buf);
> +libc_hidden_proto (__stat_time64);
> +extern int __stat64_time64 (const char *file, struct __stat64_t64
> *buf); +hidden_proto (__stat64_time64);
> +extern int __lstat_time64 (const char *file, struct __stat_t64 *buf);
> +libc_hidden_proto (__lstat_time64);
> +extern int __lstat64_time64 (const char *file, struct __stat64_t64
> *buf); +hidden_proto (__lstat64_time64);
> +extern int __fstat_time64 (int fd, struct __stat_t64 *buf);
> +libc_hidden_proto (__fstat_time64);
> +extern int __fstat64_time64 (int fd, struct __stat64_t64 *buf);
> +hidden_proto (__fstat64_time64);
> +extern int __fstatat_time64 (int dirfd, const char *pathname,
> +			     struct __stat_t64 *buf, int flags);
> +libc_hidden_proto (__fstatat_time64);
> +extern int __fstatat64_time64 (int dirfd, const char *pathname,
> +			       struct __stat64_t64 *buf, int flags);
> +hidden_proto (__fstatat64_time64);
> +# endif
> +
>  extern int __chmod (const char *__file, __mode_t __mode);
>  libc_hidden_proto (__chmod)
>  extern int __fchmod (int __fd, __mode_t __mode);
> diff --git a/sysdeps/generic/struct_stat_time64.h
> b/sysdeps/generic/struct_stat_time64.h new file mode 100644
> index 0000000000..24bb9f75cb
> --- /dev/null
> +++ b/sysdeps/generic/struct_stat_time64.h
> @@ -0,0 +1,7 @@
> +#ifndef _BITS_STRUCT_STAT_TIME64_H
> +#define _BITS_STRUCT_STAT_TIME64_H 1
> +
> +#define __stat_t64   stat
> +#define __stat64_t64 stat64
> +
> +#endif
> diff --git a/sysdeps/unix/sysv/linux/Makefile
> b/sysdeps/unix/sysv/linux/Makefile index f189f65daf..95a51ee4f2 100644
> --- a/sysdeps/unix/sysv/linux/Makefile
> +++ b/sysdeps/unix/sysv/linux/Makefile
> @@ -273,7 +273,7 @@ sysdep_routines += xstatconv internal_statvfs
> internal_statvfs64 \ open_nocancel open64_nocancel \
>  		   openat_nocancel openat64_nocancel \
>  		   read_nocancel pread64_nocancel \
> -		   write_nocancel statx_cp
> +		   write_nocancel statx_cp stat_t64_cp
>  
>  sysdep_headers += bits/fcntl-linux.h
>  
> diff --git a/sysdeps/unix/sysv/linux/fstat.c
> b/sysdeps/unix/sysv/linux/fstat.c index bdbeded956..0981dbaa95 100644
> --- a/sysdeps/unix/sysv/linux/fstat.c
> +++ b/sysdeps/unix/sysv/linux/fstat.c
> @@ -19,13 +19,24 @@
>  #include <sys/stat.h>
>  #include <kernel_stat.h>
>  #include <fcntl.h>
> +#include <stat_t64_cp.h>
>  
>  #if !XSTAT_IS_XSTAT64
> +int
> +__fstat_time64 (int fd, struct __stat_t64 *buf)
> +{
> +  return __fstatat_time64 (fd, "", buf, AT_EMPTY_PATH);
> +}
> +# if __TIMESIZE != 64
> +libc_hidden_def (__fstat_time64)
> +
>  int
>  __fstat (int fd, struct stat *buf)
>  {
> -  return __fstatat (fd, "", buf, AT_EMPTY_PATH);
> +  struct __stat_t64 st_t64;
> +  return __fstat_time64 (fd, &st_t64) ?: __cp_stat_t64_stat
> (&st_t64, buf); }
> +# endif
>  
>  weak_alias (__fstat, fstat)
>  #endif
> diff --git a/sysdeps/unix/sysv/linux/fstat64.c
> b/sysdeps/unix/sysv/linux/fstat64.c index c2ff1ff577..67667e79d8
> 100644 --- a/sysdeps/unix/sysv/linux/fstat64.c
> +++ b/sysdeps/unix/sysv/linux/fstat64.c
> @@ -19,16 +19,30 @@
>  #define __fstat __redirect___fstat
>  #define fstat   __redirect_fstat
>  #include <sys/stat.h>
> -#undef __fstat
> -#undef fstat
>  #include <fcntl.h>
>  #include <kernel_stat.h>
> +#include <stat_t64_cp.h>
> +
> +int
> +__fstat64_time64 (int fd, struct __stat64_t64 *buf)
> +{
> +  return __fstatat64_time64 (fd, "", buf, AT_EMPTY_PATH);
> +}
> +#if __TIMESIZE != 64
> +hidden_def (__fstat64_time64)
>  
>  int
>  __fstat64 (int fd, struct stat64 *buf)
>  {
> -  return __fstatat64 (fd, "", buf, AT_EMPTY_PATH);
> +  struct __stat64_t64 st_t64;
> +  return __fstat64_time64 (fd, &st_t64)
> +	 ?: __cp_stat64_t64_stat64 (&st_t64, buf);
>  }
> +#endif
> +
> +#undef __fstat
> +#undef fstat
> +
>  hidden_def (__fstat64)
>  weak_alias (__fstat64, fstat64)
>  
> diff --git a/sysdeps/unix/sysv/linux/fstatat.c
> b/sysdeps/unix/sysv/linux/fstatat.c index 03ddb3f493..f65d3b74a6
> 100644 --- a/sysdeps/unix/sysv/linux/fstatat.c
> +++ b/sysdeps/unix/sysv/linux/fstatat.c
> @@ -22,25 +22,28 @@
>  
>  #if !XSTAT_IS_XSTAT64
>  # include <kstat_cp.h>
> +# include <statx_cp.h>
> +# include <stat_t64_cp.h>
>  
>  int
> -__fstatat (int fd, const char *file, struct stat *st, int flag)
> +__fstatat_time64 (int fd, const char *file, struct __stat_t64 *st,
> int flag) {
> -# if STAT_IS_KERNEL_STAT
> -  /* New kABIs which uses generic pre 64-bit time Linux ABI, e.g.
> -     csky, nios2  */
> -  int r = INLINE_SYSCALL_CALL (fstatat64, fd, file, st, flag);
> -  if (r == 0 && (st->__st_ino_pad != 0
> -		 || st->__st_size_pad != 0
> -		 || st->__st_blocks_pad != 0))
> -    return INLINE_SYSCALL_ERROR_RETURN_VALUE (EOVERFLOW);
> -  return r;
> -# else
> -#  ifdef __NR_fstatat64
> -  /* Old KABIs with old non-LFS support, e.g. arm, i386, hppa, m68k,
> mips32,
> -     microblaze, s390, sh, powerpc, and sparc.  */
> +  struct statx stx;
> +  int r = INLINE_SYSCALL_CALL (statx, fd, file, flag,
> STATX_BASIC_STATS,
> +			       &stx);

I'm wondering about the indentation here - I do see 3*TAB and some
spaces. Shouldn't we only use spaces?

And one more question what is the correct alignment:
		(statx, fd, file, flag
		 &stx);

or
		(statx, fd, file, flag
		&stx);

> +  if (r == 0 || errno != ENOSYS)
> +    {
> +      if (r == 0)
> +	__cp_stat_t64_statx (st, &stx);
> +      return r;
> +    }

Great that the statx is used here.

> +
> +# ifdef __NR_fstatat64
> +  /* Both new kABI which uses generic pre 64-bit time Linux ABI
> (e.g. csky
> +     and nios) and old kABI with non-LFS support (e.g. arm, i386,
> hppa, m68k,
> +     mips32, microblaze, s390, sh, powerpc, and sparc32).  */
>    struct stat64 st64;
> -  int r = INLINE_SYSCALL_CALL (fstatat64, fd, file, &st64, flag);
> +  r = INLINE_SYSCALL_CALL (fstatat64, fd, file, &st64, flag);
>    if (r == 0)
>      {
>        if (! in_ino_t_range (st64.st_ino)
> @@ -48,7 +51,7 @@ __fstatat (int fd, const char *file, struct stat
> *st, int flag) || ! in_blkcnt_t_range (st64.st_blocks))
>  	return INLINE_SYSCALL_ERROR_RETURN_VALUE (EOVERFLOW);
>  
> -      /* Clear internal pad and reserved fields.  */
> +      /* Clear both pad and reserved fields.  */
>        memset (st, 0, sizeof (*st));
>  
>        st->st_dev = st64.st_dev,
> @@ -61,22 +64,29 @@ __fstatat (int fd, const char *file, struct stat
> *st, int flag) st->st_size = st64.st_size;
>        st->st_blksize = st64.st_blksize;
>        st->st_blocks  = st64.st_blocks;
> -      st->st_atim.tv_sec = st64.st_atim.tv_sec;
> -      st->st_atim.tv_nsec = st64.st_atim.tv_nsec;
> -      st->st_mtim.tv_sec = st64.st_mtim.tv_sec;
> -      st->st_mtim.tv_nsec = st64.st_mtim.tv_nsec;
> -      st->st_ctim.tv_sec = st64.st_ctim.tv_sec;
> -      st->st_ctim.tv_nsec = st64.st_ctim.tv_nsec;
> +      st->st_atim = valid_timespec_to_timespec64 (st64.st_atim);
> +      st->st_mtim = valid_timespec_to_timespec64 (st64.st_mtim);
> +      st->st_ctim = valid_timespec_to_timespec64 (st64.st_ctim);
>      }
>    return r;
> -#  else
> +# else
>    /* 64-bit kabi outlier, e.g. mips64 and mips64-n32.  */
>    struct kernel_stat kst;
> -  int r = INTERNAL_SYSCALL_CALL (newfstatat, fd, file, &kst, flag);
> -  return r ?: __cp_kstat_stat (&kst, st);
> -#  endif /* __nr_fstatat64  */
> -# endif /* STAT_IS_KERNEL_STAT  */
> +  r = INTERNAL_SYSCALL_CALL (newfstatat, fd, file, &kst, flag);
> +  return r ?: __cp_kstat_stat_t64 (&kst, st);
> +# endif /* __NR_fstatat64  */
> +}
> +# if __TIMESIZE != 64
> +libc_hidden_def (__fstatat_time64)
> +
> +int
> +__fstatat (int fd, const char *file, struct stat *buf, int flags)
> +{
> +  struct __stat_t64 st_t64;
> +  return __fstatat_time64 (fd, file, &st_t64, flags)
> +	 ?: __cp_stat_t64_stat (&st_t64, buf);
>  }
> +# endif
>  
>  weak_alias (__fstatat, fstatat)
>  #endif
> diff --git a/sysdeps/unix/sysv/linux/fstatat64.c
> b/sysdeps/unix/sysv/linux/fstatat64.c index 82fab107a5..b3940f0d20
> 100644 --- a/sysdeps/unix/sysv/linux/fstatat64.c
> +++ b/sysdeps/unix/sysv/linux/fstatat64.c
> @@ -19,56 +19,102 @@
>  #define __fstatat __redirect___fstatat
>  #define fstatat   __redirect_fstatat
>  #include <sys/stat.h>
> -#undef __fstatat
> -#undef fstatat
>  #include <fcntl.h>
> -
> +#include <string.h>
>  #include <kernel_stat.h>
>  #include <sysdep.h>
> -
> +#include <time.h>
>  #include <statx_cp.h>
>  #include <kstat_cp.h>
> +#include <stat_t64_cp.h>
>  
>  int
> -__fstatat64 (int fd, const char *file, struct stat64 *st, int flag)
> +__fstatat64_time64 (int fd, const char *file, struct __stat64_t64
> *st,
> +		    int flag)
>  {
> +  int r;
> +
> +#if (__WORDSIZE == 32 \
> +     && (!defined __SYSCALL_WORDSIZE || __SYSCALL_WORDSIZE == 32))

I do guess that the above condition is to ensure that 32 bit archs
(excluding x86_64-x32 - e.g. arm, riscv, etc) use statx?

> +  struct statx tmp;
> +  r = INLINE_SYSCALL_CALL (statx, fd, file, AT_NO_AUTOMOUNT | flag,
> +			   STATX_BASIC_STATS, &tmp);
> +  if (r == 0 || errno != ENOSYS)
> +    {
> +      if (r == 0)
> +	__cp_stat64_t64_statx (st, &tmp);
> +      return r;
> +    }
> +#endif
> +
>  #if XSTAT_IS_XSTAT64
>  # ifdef __NR_newfstatat
>    /* 64-bit kABI, e.g. aarch64, ia64, powerpc64*, s390x, riscv64, and
>       x86_64.  */
> -  return INLINE_SYSCALL_CALL (newfstatat, fd, file, st, flag);
> +  r = INLINE_SYSCALL_CALL (newfstatat, fd, file, st, flag);
>  # elif defined __NR_fstatat64
>  #  if STAT64_IS_KERNEL_STAT64
> -  /* 64-bit kABI outlier, e.g. alpha.  */
> -  return INLINE_SYSCALL_CALL (fstatat64, fd, file, st, flag);
> +  /* 64-bit kABI outlier, e.g. alpha  */
> +  r = INLINE_SYSCALL_CALL (fstatat64, fd, file, st, flag);
>  #  else
>    /* 64-bit kABI outlier, e.g. sparc64.  */
>    struct kernel_stat64 kst64;
> -  int r = INLINE_SYSCALL_CALL (fstatat64, fd, file, &kst64, flag);
> -  return r ?: __cp_stat64_kstat64 (st, &kst64);
> -#  endif
> -# else
> -  /* 32-bit kABI with default 64-bit time_t, e.g. arc, riscv32.  */
> -  struct statx tmp;
> -  int r = INLINE_SYSCALL_CALL (statx, fd, file, AT_NO_AUTOMOUNT |
> flag,
> -			       STATX_BASIC_STATS, &tmp);
> +  r = INLINE_SYSCALL_CALL (fstatat64, fd, file, &kst64, flag);
>    if (r == 0)
> -    __cp_stat64_statx (st, &tmp);
> -  return r;
> +    r = __cp_stat64_kstat64 (st, &kst64);
> +#  endif
>  # endif
>  #else
>  # ifdef __NR_fstatat64
>    /* All kABIs with non-LFS support, e.g. arm, csky, i386, hppa,
> m68k, microblaze, nios2, sh, powerpc32, and sparc32.  */
> -  return INLINE_SYSCALL_CALL (fstatat64, fd, file, st, flag);
> +  struct stat64 st64;
> +  r = INLINE_SYSCALL_CALL (fstatat64, fd, file, &st64, flag);
> +  if (r == 0)
> +    {
> +      /* Clear both pad and reserved fields.  */
> +      memset (st, 0, sizeof (*st));
> +
> +      st->st_dev = st64.st_dev,
> +      st->st_ino = st64.st_ino;
> +      st->st_mode = st64.st_mode;
> +      st->st_nlink = st64.st_nlink;
> +      st->st_uid = st64.st_uid;
> +      st->st_gid = st64.st_gid;
> +      st->st_rdev = st64.st_rdev;
> +      st->st_size = st64.st_size;
> +      st->st_blksize = st64.st_blksize;
> +      st->st_blocks  = st64.st_blocks;
> +      st->st_atim = valid_timespec_to_timespec64 (st64.st_atim);
> +      st->st_mtim = valid_timespec_to_timespec64 (st64.st_mtim);
> +      st->st_ctim = valid_timespec_to_timespec64 (st64.st_ctim);
> +    }
>  # else
>    /* 64-bit kabi outlier, e.g. mips64 and mips64-n32.  */
>    struct kernel_stat kst;
> -  int r = INTERNAL_SYSCALL_CALL (newfstatat, fd, file, &kst, flag);
> -  return r ?: __cp_kstat_stat64 (&kst, st);
> +  r = INTERNAL_SYSCALL_CALL (newfstatat, fd, file, &kst, flag);
> +  if (r == 0)
> +    r =  __cp_kstat_stat64_t64 (&kst, st);
>  # endif
>  #endif
> +
> +  return r;
>  }
> +#if __TIMESIZE != 64
> +hidden_def (__fstatat64_time64)
> +
> +int
> +__fstatat64 (int fd, const char *file, struct stat64 *st, int flags)
> +{
> +  struct __stat64_t64 st_t64;
> +  return __fstatat64_time64 (fd, file, &st_t64, flags)
> +	 ?: __cp_stat64_t64_stat64 (&st_t64, st);
> +}
> +#endif
> +
> +#undef __fstatat
> +#undef fstatat
> +
>  hidden_def (__fstatat64)
>  weak_alias (__fstatat64, fstatat64)
>  
> diff --git a/sysdeps/unix/sysv/linux/lstat.c
> b/sysdeps/unix/sysv/linux/lstat.c index b0bdeee9e9..803ad78171 100644
> --- a/sysdeps/unix/sysv/linux/lstat.c
> +++ b/sysdeps/unix/sysv/linux/lstat.c
> @@ -19,13 +19,24 @@
>  #include <sys/stat.h>
>  #include <fcntl.h>
>  #include <kernel_stat.h>
> +#include <stat_t64_cp.h>
>  
>  #if !XSTAT_IS_XSTAT64
> +int
> +__lstat_time64 (const char *file, struct __stat_t64 *buf)
> +{
> +  return __fstatat_time64 (AT_FDCWD, file, buf, AT_SYMLINK_NOFOLLOW);
> +}
> +# if __TIMESIZE != 64
> +libc_hidden_def (__lstat_time64)
> +
>  int
>  __lstat (const char *file, struct stat *buf)
>  {
> -  return __fstatat (AT_FDCWD, file, buf, AT_SYMLINK_NOFOLLOW);
> +  struct __stat_t64 st_t64;
> +  return __lstat_time64 (file, &st_t64) ?: __cp_stat_t64_stat
> (&st_t64, buf); }
> +# endif
>  
>  weak_alias (__lstat, lstat)
>  #endif
> diff --git a/sysdeps/unix/sysv/linux/lstat64.c
> b/sysdeps/unix/sysv/linux/lstat64.c index e5f02e9822..971ab8469d
> 100644 --- a/sysdeps/unix/sysv/linux/lstat64.c
> +++ b/sysdeps/unix/sysv/linux/lstat64.c
> @@ -19,19 +19,32 @@
>  #define __lstat __redirect___lstat
>  #define lstat   __redirect_lstat
>  #include <sys/stat.h>
> -#undef __lstat
> -#undef lstat
>  #include <fcntl.h>
>  #include <kernel_stat.h>
> +#include <stat_t64_cp.h>
> +
> +int
> +__lstat64_time64 (const char *file, struct __stat64_t64 *buf)
> +{
> +  return __fstatat64_time64 (AT_FDCWD, file, buf,
> AT_SYMLINK_NOFOLLOW); +}
> +#if __TIMESIZE != 64
> +hidden_def (__lstat64_time64)
>  
>  int
>  __lstat64 (const char *file, struct stat64 *buf)
>  {
> -  return __fstatat64 (AT_FDCWD, file, buf, AT_SYMLINK_NOFOLLOW);
> +  struct __stat64_t64 st_t64;
> +  return __lstat64_time64 (file, &st_t64)
> +	 ?: __cp_stat64_t64_stat64 (&st_t64, buf);
>  }
> +#endif
>  hidden_def (__lstat64)
>  weak_alias (__lstat64, lstat64)
>  
> +#undef __lstat
> +#undef lstat
> +
>  #if XSTAT_IS_XSTAT64
>  strong_alias (__lstat64, __lstat)
>  weak_alias (__lstat64, lstat)
> diff --git a/sysdeps/unix/sysv/linux/mips/mips64/kstat_cp.h
> b/sysdeps/unix/sysv/linux/mips/mips64/kstat_cp.h index
> 7f226416f9..553f4226bc 100644 ---
> a/sysdeps/unix/sysv/linux/mips/mips64/kstat_cp.h +++
> b/sysdeps/unix/sysv/linux/mips/mips64/kstat_cp.h @@ -20,23 +20,21 @@
>  #include <kernel_stat.h>
>  
>  static inline int
> -__cp_kstat_stat (const struct kernel_stat *kst, struct stat *st)
> +__cp_kstat_stat_t64 (const struct kernel_stat *kst, struct
> __stat_t64 *st) {
> +  if (! in_ino_t_range (kst->st_ino)
> +      || ! in_off_t_range (kst->st_size)
> +      || ! in_blkcnt_t_range (kst->st_blocks))
> +    return INLINE_SYSCALL_ERROR_RETURN_VALUE (EOVERFLOW);
> +
>    st->st_dev = kst->st_dev;
> -  memset (&st->st_pad1, 0, sizeof (st->st_pad1));
>    st->st_ino = kst->st_ino;
> -  if (st->st_ino != kst->st_ino)
> -    return INLINE_SYSCALL_ERROR_RETURN_VALUE (EOVERFLOW);
>    st->st_mode = kst->st_mode;
>    st->st_nlink = kst->st_nlink;
>    st->st_uid = kst->st_uid;
>    st->st_gid = kst->st_gid;
>    st->st_rdev = kst->st_rdev;
> -  memset (&st->st_pad2, 0, sizeof (st->st_pad2));
>    st->st_size = kst->st_size;
> -  if (st->st_size != kst->st_size)
> -    return INLINE_SYSCALL_ERROR_RETURN_VALUE (EOVERFLOW);
> -  st->st_pad3 = 0;
>    st->st_atim.tv_sec = kst->st_atime_sec;
>    st->st_atim.tv_nsec = kst->st_atime_nsec;
>    st->st_mtim.tv_sec = kst->st_mtime_sec;
> @@ -45,26 +43,20 @@ __cp_kstat_stat (const struct kernel_stat *kst,
> struct stat *st) st->st_ctim.tv_nsec = kst->st_ctime_nsec;
>    st->st_blksize = kst->st_blksize;
>    st->st_blocks = kst->st_blocks;
> -  if (st->st_blocks != kst->st_blocks)
> -    return INLINE_SYSCALL_ERROR_RETURN_VALUE (EOVERFLOW);
> -  memset (&st->st_pad5, 0, sizeof (st->st_pad5));
>  
>    return 0;
>  }
>  
>  static inline int
> -__cp_kstat_stat64 (const struct kernel_stat *kst, struct stat64 *st)
> +__cp_kstat_stat64_t64 (const struct kernel_stat *kst, struct
> __stat64_t64 *st) {
>    st->st_dev = kst->st_dev;
> -  memset (&st->st_pad1, 0, sizeof (st->st_pad1));
>    st->st_ino = kst->st_ino;
>    st->st_mode = kst->st_mode;
>    st->st_nlink = kst->st_nlink;
>    st->st_uid = kst->st_uid;
>    st->st_gid = kst->st_gid;
>    st->st_rdev = kst->st_rdev;
> -  memset (&st->st_pad2, 0, sizeof (st->st_pad2));
> -  st->st_pad3 = 0;
>    st->st_size = kst->st_size;
>    st->st_blksize = kst->st_blksize;
>    st->st_blocks = kst->st_blocks;
> @@ -74,7 +66,6 @@ __cp_kstat_stat64 (const struct kernel_stat *kst,
> struct stat64 *st) st->st_mtim.tv_nsec = kst->st_mtime_nsec;
>    st->st_ctim.tv_sec = kst->st_ctime_sec;
>    st->st_ctim.tv_nsec = kst->st_ctime_nsec;
> -  memset (&st->st_pad4, 0, sizeof (st->st_pad4));
>  
>    return 0;
>  }
> diff --git a/sysdeps/unix/sysv/linux/mips/mips64/statx_cp.c
> b/sysdeps/unix/sysv/linux/mips/mips64/statx_cp.c deleted file mode
> 100644 index 260cda987e..0000000000
> --- a/sysdeps/unix/sysv/linux/mips/mips64/statx_cp.c
> +++ /dev/null
> @@ -1,3 +0,0 @@
> -/* Override the generic statx_cp.c which is only needed for new
> 32-bit arch
> -   without stat64 family support.
> - */
> diff --git a/sysdeps/unix/sysv/linux/stat.c
> b/sysdeps/unix/sysv/linux/stat.c index a77502eb95..dcbc22da29 100644
> --- a/sysdeps/unix/sysv/linux/stat.c
> +++ b/sysdeps/unix/sysv/linux/stat.c
> @@ -19,13 +19,24 @@
>  #include <sys/stat.h>
>  #include <fcntl.h>
>  #include <kernel_stat.h>
> +#include <stat_t64_cp.h>
>  
>  #if !XSTAT_IS_XSTAT64
> +int
> +__stat_time64 (const char *file, struct __stat_t64 *buf)
> +{
> +  return __fstatat_time64 (AT_FDCWD, file, buf, 0);
> +}
> +# if __TIMESIZE != 64
> +libc_hidden_def (__stat_time64);
> +
>  int
>  __stat (const char *file, struct stat *buf)
>  {
> -  return __fstatat (AT_FDCWD, file, buf, 0);
> +  struct __stat_t64 st_t64;
> +  return __stat_time64 (file, &st_t64) ?: __cp_stat_t64_stat
> (&st_t64, buf); }
> +# endif
>  
>  weak_alias (__stat, stat)
>  #endif
> diff --git a/sysdeps/unix/sysv/linux/stat64.c
> b/sysdeps/unix/sysv/linux/stat64.c index 2f40037c2c..bd8b17ac49 100644
> --- a/sysdeps/unix/sysv/linux/stat64.c
> +++ b/sysdeps/unix/sysv/linux/stat64.c
> @@ -19,16 +19,30 @@
>  #define __stat __redirect___stat
>  #define stat   __redirect_stat
>  #include <sys/stat.h>
> -#undef __stat
> -#undef stat
>  #include <fcntl.h>
>  #include <kernel_stat.h>
> +#include <stat_t64_cp.h>
> +
> +int
> +__stat64_time64 (const char *file, struct __stat64_t64 *buf)
> +{
> +  return __fstatat64_time64 (AT_FDCWD, file, buf, 0);
> +}
> +#if __TIMESIZE != 64
> +hidden_def (__stat64_time64)
>  
>  int
>  __stat64 (const char *file, struct stat64 *buf)
>  {
> -  return __fstatat64 (AT_FDCWD, file, buf, 0);
> +  struct __stat64_t64 st_t64;
> +  return __stat64_time64 (file, &st_t64)
> +	 ?: __cp_stat64_t64_stat64 (&st_t64, buf);
>  }
> +#endif
> +
> +#undef __stat
> +#undef stat
> +
>  hidden_def (__stat64)
>  weak_alias (__stat64, stat64)
>  
> diff --git a/sysdeps/unix/sysv/linux/stat_t64_cp.c
> b/sysdeps/unix/sysv/linux/stat_t64_cp.c new file mode 100644
> index 0000000000..56459b6266
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/stat_t64_cp.c
> @@ -0,0 +1,92 @@
> +/* Struct stat/stat64 to stat/stat64 conversion for Linux.
> +   Copyright (C) 2020 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/>.  */
> +
> +#include <stat_t64_cp.h>
> +#include <string.h>
> +#include <errno.h>
> +#include <time.h>
> +
> +#if __TIMESIZE != 64
> +/* Convert the 64-bit time_t stat ST_T64 to the non-LFS ST stat and
> returns
> +   EOVERFLOW if any of time (access, modification, status) is larger
> than
> +   32-bit time_t.  It is used on non-LFS stat, fstat, lstat, and
> fstatat to
> +   convert from the the 64-bit time_t call.  */
> +int
> +__cp_stat_t64_stat (const struct __stat_t64 *st_t64, struct stat *st)
> +{
> +  if (! in_time_t_range (st_t64->st_atim.tv_sec)
> +      || ! in_time_t_range (st_t64->st_mtim.tv_sec)
> +      || ! in_time_t_range (st_t64->st_ctim.tv_sec))
> +    {
> +      __set_errno (EOVERFLOW);
> +      return -1;
> +    }
> +
> +  /* Clear both pad and reserved fields.  */
> +  memset (st, 0, sizeof (*st));
> +
> +  st->st_dev = st_t64->st_dev,
> +  st->st_ino = st_t64->st_ino;
> +  st->st_mode = st_t64->st_mode;
> +  st->st_nlink = st_t64->st_nlink;
> +  st->st_uid = st_t64->st_uid;
> +  st->st_gid = st_t64->st_gid;
> +  st->st_rdev = st_t64->st_rdev;
> +  st->st_size = st_t64->st_size;
> +  st->st_blksize = st_t64->st_blksize;
> +  st->st_blocks  = st_t64->st_blocks;
> +  st->st_atim = valid_timespec64_to_timespec (st_t64->st_atim);
> +  st->st_mtim = valid_timespec64_to_timespec (st_t64->st_mtim);
> +  st->st_ctim = valid_timespec64_to_timespec (st_t64->st_ctim);
> +
> +  return 0;
> +}
> +
> +int
> +__cp_stat64_t64_stat64 (const struct __stat64_t64 *st64_t64,
> +			struct stat64 *st64)
> +{
> +  if (! in_time_t_range (st64_t64->st_atim.tv_sec)
> +      || ! in_time_t_range (st64_t64->st_mtim.tv_sec)
> +      || ! in_time_t_range (st64_t64->st_ctim.tv_sec))
> +    {
> +      __set_errno (EOVERFLOW);
> +      return -1;
> +    }
> +
> +  /* Clear both pad and reserved fields.  */
> +  memset (st64, 0, sizeof (*st64));
> +
> +  st64->st_dev = st64_t64->st_dev,
> +  st64->st_ino = st64_t64->st_ino;
> +  st64->st_mode = st64_t64->st_mode;
> +  st64->st_nlink = st64_t64->st_nlink;
> +  st64->st_uid = st64_t64->st_uid;
> +  st64->st_gid = st64_t64->st_gid;
> +  st64->st_rdev = st64_t64->st_rdev;
> +  st64->st_size = st64_t64->st_size;
> +  st64->st_blksize = st64_t64->st_blksize;
> +  st64->st_blocks  = st64_t64->st_blocks;
> +  st64->st_atim = valid_timespec64_to_timespec (st64_t64->st_atim);
> +  st64->st_mtim = valid_timespec64_to_timespec (st64_t64->st_mtim);
> +  st64->st_ctim = valid_timespec64_to_timespec (st64_t64->st_ctim);
> +
> +  return 0;
> +}
> +
> +#endif
> diff --git a/sysdeps/unix/sysv/linux/stat_t64_cp.h
> b/sysdeps/unix/sysv/linux/stat_t64_cp.h new file mode 100644
> index 0000000000..ad2bcc7a04
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/stat_t64_cp.h
> @@ -0,0 +1,28 @@
> +/* Copy to/from struct stat with and without 64-bit time_t support.
> +   Copyright (C) 2020 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/>.  */
> +
> +#include <sys/stat.h>
> +
> +#if __TIMESIZE != 64
> +extern int __cp_stat_t64_stat (const struct __stat_t64 *st_t64,
> +			       struct stat *st)
> +  attribute_hidden;
> +extern int __cp_stat64_t64_stat64 (const struct __stat64_t64
> *st64_t64,
> +				   struct stat64 *st64)
> +  attribute_hidden;
> +#endif
> diff --git a/sysdeps/unix/sysv/linux/statx_cp.c
> b/sysdeps/unix/sysv/linux/statx_cp.c index cc6e17929e..4b35e86f8d
> 100644 --- a/sysdeps/unix/sysv/linux/statx_cp.c
> +++ b/sysdeps/unix/sysv/linux/statx_cp.c
> @@ -47,3 +47,57 @@ __cp_stat64_statx (struct stat64 *to, struct statx
> *from) to->st_blksize = from->stx_blksize;
>  }
>  #endif
> +
> +void
> +__cp_stat_t64_statx (struct __stat_t64 *to, const struct statx *from)
> +{
> +  /* Clear both pad and reserved fields.  */
> +  memset (to, 0, sizeof (*to));
> +
> +  to->st_dev = ((from->stx_dev_minor & 0xff) | (from->stx_dev_major
> << 8)
> +		| ((from->stx_dev_minor & ~0xff) << 12));
> +  to->st_ino = from->stx_ino;
> +  to->st_mode = from->stx_mode;
> +  to->st_nlink = from->stx_nlink;
> +  to->st_uid = from->stx_uid;
> +  to->st_gid = from->stx_gid;
> +  to->st_rdev = ((from->stx_rdev_minor & 0xff) |
> (from->stx_rdev_major << 8)
> +		 | ((from->stx_rdev_minor & ~0xff) << 12));
> +  to->st_size = from->stx_size;
> +  to->st_blksize = from->stx_blksize;
> +  to->st_blocks = from->stx_blocks;
> +
> +  to->st_atime = from->stx_atime.tv_sec;
> +  to->st_atim.tv_nsec = from->stx_atime.tv_nsec;
> +  to->st_mtime = from->stx_mtime.tv_sec;
> +  to->st_mtim.tv_nsec = from->stx_mtime.tv_nsec;
> +  to->st_ctime = from->stx_ctime.tv_sec;
> +  to->st_ctim.tv_nsec = from->stx_ctime.tv_nsec;
> +}
> +
> +void
> +__cp_stat64_t64_statx (struct __stat64_t64 *to, const struct statx
> *from) +{
> +  /* Clear both pad and reserved fields.  */
> +  memset (to, 0, sizeof (*to));
> +
> +  to->st_dev = ((from->stx_dev_minor & 0xff) | (from->stx_dev_major
> << 8)
> +		| ((from->stx_dev_minor & ~0xff) << 12));
> +  to->st_ino = from->stx_ino;
> +  to->st_mode = from->stx_mode;
> +  to->st_nlink = from->stx_nlink;
> +  to->st_uid = from->stx_uid;
> +  to->st_gid = from->stx_gid;
> +  to->st_rdev = ((from->stx_rdev_minor & 0xff) |
> (from->stx_rdev_major << 8)
> +		 | ((from->stx_rdev_minor & ~0xff) << 12));
> +  to->st_size = from->stx_size;
> +  to->st_blksize = from->stx_blksize;
> +  to->st_blocks = from->stx_blocks;
> +
> +  to->st_atime = from->stx_atime.tv_sec;
> +  to->st_atim.tv_nsec = from->stx_atime.tv_nsec;
> +  to->st_mtime = from->stx_mtime.tv_sec;
> +  to->st_mtim.tv_nsec = from->stx_mtime.tv_nsec;
> +  to->st_ctime = from->stx_ctime.tv_sec;
> +  to->st_ctim.tv_nsec = from->stx_ctime.tv_nsec;
> +}
> diff --git a/sysdeps/unix/sysv/linux/statx_cp.h
> b/sysdeps/unix/sysv/linux/statx_cp.h index fdbb807a31..bf3186ffac
> 100644 --- a/sysdeps/unix/sysv/linux/statx_cp.h
> +++ b/sysdeps/unix/sysv/linux/statx_cp.h
> @@ -18,3 +18,9 @@
>  
>  extern void __cp_stat64_statx (struct stat64 *to, struct statx *from)
>    attribute_hidden;
> +extern void __cp_stat_t64_statx (struct __stat_t64 *to,
> +				 const struct statx *from)
> +  attribute_hidden;
> +extern void __cp_stat64_t64_statx (struct __stat64_t64 *to,
> +				   const struct statx *from)
> +  attribute_hidden;
> diff --git a/sysdeps/unix/sysv/linux/struct_stat_time64.h
> b/sysdeps/unix/sysv/linux/struct_stat_time64.h new file mode 100644
> index 0000000000..b2a51927d0
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/struct_stat_time64.h
> @@ -0,0 +1,122 @@
> +/* Struct stat with 64-bit time support.
> +   Copyright (C) 2020 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/>.  */
> +
> +#ifndef _BITS_STRUCT_STAT_TIME64_H
> +#define _BITS_STRUCT_STAT_TIME64_H 1
> +
> +#if __TIMESIZE == 64
> +# define __stat_t64   stat
> +# define __stat64_t64 stat64
> +#else
> +# include <endian.h>
> +
> +# ifdef __USE_FILE_OFFSET64
> +#  define __field64(type, type64, name) type64 name
> +# else
> +#  define __field64(type, type64, name) type name
> +# endif
> +
> +/* The definition should be equal to the 'struct __timespec64'
> internal
> +   layout.  */
> +# if BYTE_ORDER == BIG_ENDIAN
> +#  define __fieldts64(name) 					\
> +   __time64_t name; __int32_t :32; __int32_t name ## nsec
> +# else
> +#  define __fieldts64(name)					\
> +   __time64_t name; __int32_t name ## nsec; __int32_t :32
> +# endif
> +
> +/* Workaround for the definition from struct_stat.h  */
> +# undef st_atime
> +# undef st_mtime
> +# undef st_ctime
> +
> +struct __stat_t64
> +  {
> +    __dev_t st_dev;			/* Device.  */
> +    __field64(__ino_t, __ino64_t, st_ino);  /* File serial number.
> */
> +    __mode_t st_mode;			/* File mode.  */
> +    __nlink_t st_nlink;			/* Link count.  */
> +    __uid_t st_uid;			/* User ID of the file's
> owner.	*/
> +    __gid_t st_gid;			/* Group ID of the file's
> group.*/
> +    __dev_t st_rdev;			/* Device number, if
> device.  */
> +    __field64(__off_t, __off64_t, st_size);  /* Size of file, in
> bytes. */
> +    __blksize_t st_blksize;		/* Optimal block size for
> I/O.  */
> +    __field64(__blkcnt_t, __blkcnt64_t, st_blocks);  /* 512-byte
> blocks */ +# ifdef __USE_XOPEN2K8
> +    /* Nanosecond resolution timestamps are stored in a format
> +       equivalent to 'struct timespec'.  This is the type used
> +       whenever possible but the Unix namespace rules do not allow
> the
> +       identifier 'timespec' to appear in the <sys/stat.h> header.
> +       Therefore we have to handle the use of this header in strictly
> +       standard-compliant sources special.  */
> +    struct __timespec64 st_atim;	/* Time of last access.  */
> +    struct __timespec64 st_mtim;	/* Time of last
> modification.  */
> +    struct __timespec64 st_ctim;	/* Time of last status
> change.  */ +#  define st_atime st_atim.tv_sec	/* Backward
> compatibility.  */ +#  define st_mtime st_mtim.tv_sec
> +#  define st_ctime st_ctim.tv_sec
> +# else
> +    __fieldts64 (st_atime);
> +    __fieldts64 (st_mtime);
> +    __fieldts64 (st_ctime);
> +# endif /* __USE_XOPEN2K8  */
> +  };
> +
> +# undef __field64
> +
> +#ifdef __USE_LARGEFILE64
> +struct __stat64_t64
> +  {
> +    __dev_t st_dev;			/* Device.  */
> +    __ino64_t st_ino;			/* file serial
> number.	*/
> +    __mode_t st_mode;			/* File mode.  */
> +    __nlink_t st_nlink;			/* Link count.  */
> +    __uid_t st_uid;			/* User ID of the file's
> owner.	*/
> +    __gid_t st_gid;			/* Group ID of the file's
> group.*/
> +    __dev_t st_rdev;			/* Device number, if
> device.  */
> +    __off64_t st_size;			/* Size of file, in
> bytes.  */
> +    __blksize_t st_blksize;		/* Optimal block size for
> I/O.  */
> +    __blkcnt64_t st_blocks;		/* Number 512-byte blocks
> allocated. */ +# ifdef __USE_XOPEN2K8
> +    /* Nanosecond resolution timestamps are stored in a format
> +       equivalent to 'struct timespec'.  This is the type used
> +       whenever possible but the Unix namespace rules do not allow
> the
> +       identifier 'timespec' to appear in the <sys/stat.h> header.
> +       Therefore we have to handle the use of this header in strictly
> +       standard-compliant sources special.  */
> +    struct __timespec64 st_atim;	/* Time of last access.  */
> +    struct __timespec64 st_mtim;	/* Time of last
> modification.  */
> +    struct __timespec64 st_ctim;	/* Time of last status
> change.  */ +# else
> +    __fieldts64 (st_atime);
> +    __fieldts64 (st_mtime);
> +    __fieldts64 (st_ctime);
> +# endif /* __USE_XOPEN2K8  */
> +  };
> +# endif /* __USE_LARGEFILE64  */
> +
> +# undef __fieldts64
> +
> +# define _STATBUF_ST_BLKSIZE
> +# define _STATBUF_ST_RDEV
> +# define _STATBUF_ST_NSEC
> +
> +# endif /* __TIMESIZE == 64  */
> +
> +#endif /* _BITS_STRUCT_STAT_TIME64_H  */

Despite some very minor comments,

Acked-by: Lukasz Majewski <lukma@denx.de>


Best regards,

Lukasz Majewski

--

DENX Software Engineering GmbH,      Managing Director: Wolfgang Denk
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: (+49)-8142-66989-59 Fax: (+49)-8142-66989-80 Email: lukma@denx.de
Adhemerval Zanella July 30, 2020, 12:42 p.m. UTC | #4
On 24/07/2020 07:53, Lukasz Majewski wrote:
>>  
>> diff --git a/sysdeps/unix/sysv/linux/fstatat.c
>> b/sysdeps/unix/sysv/linux/fstatat.c index 03ddb3f493..f65d3b74a6
>> 100644 --- a/sysdeps/unix/sysv/linux/fstatat.c
>> +++ b/sysdeps/unix/sysv/linux/fstatat.c
>> -#  ifdef __NR_fstatat64
>> -  /* Old KABIs with old non-LFS support, e.g. arm, i386, hppa, m68k,
>> mips32,
>> -     microblaze, s390, sh, powerpc, and sparc.  */
>> +  struct statx stx;
>> +  int r = INLINE_SYSCALL_CALL (statx, fd, file, flag,
>> STATX_BASIC_STATS,
>> +			       &stx);
> 
> I'm wondering about the indentation here - I do see 3*TAB and some
> spaces. Shouldn't we only use spaces?

Afaik the GNU code guidelines requires to use TAB indentations instead
of space in this case.  


> 
> And one more question what is the correct alignment:
> 		(statx, fd, file, flag
> 		 &stx);
> 
> or
> 		(statx, fd, file, flag
> 		&stx);

My understanding the latter.

> 
>> +  if (r == 0 || errno != ENOSYS)
>> +    {
>> +      if (r == 0)
>> +	__cp_stat_t64_statx (st, &stx);
>> +      return r;
>> +    }
> 
> Great that the statx is used here.
> 
>> +
>> +# ifdef __NR_fstatat64
>> +  /* Both new kABI which uses generic pre 64-bit time Linux ABI
>> (e.g. csky
>> +     and nios) and old kABI with non-LFS support (e.g. arm, i386,
>> hppa, m68k,
>> +     mips32, microblaze, s390, sh, powerpc, and sparc32).  */
>>    struct stat64 st64;
>> -  int r = INLINE_SYSCALL_CALL (fstatat64, fd, file, &st64, flag);
>> +  r = INLINE_SYSCALL_CALL (fstatat64, fd, file, &st64, flag);
>>    if (r == 0)
>>      {
>>        if (! in_ino_t_range (st64.st_ino)
>> @@ -48,7 +51,7 @@ __fstatat (int fd, const char *file, struct stat
>> *st, int flag) || ! in_blkcnt_t_range (st64.st_blocks))
>>  	return INLINE_SYSCALL_ERROR_RETURN_VALUE (EOVERFLOW);
>>  
>> -      /* Clear internal pad and reserved fields.  */
>> +      /* Clear both pad and reserved fields.  */
>>        memset (st, 0, sizeof (*st));
>>  
>>        st->st_dev = st64.st_dev,
>> @@ -61,22 +64,29 @@ __fstatat (int fd, const char *file, struct stat
>> *st, int flag) st->st_size = st64.st_size;
>>        st->st_blksize = st64.st_blksize;
>>        st->st_blocks  = st64.st_blocks;
>> -      st->st_atim.tv_sec = st64.st_atim.tv_sec;
>> -      st->st_atim.tv_nsec = st64.st_atim.tv_nsec;
>> -      st->st_mtim.tv_sec = st64.st_mtim.tv_sec;
>> -      st->st_mtim.tv_nsec = st64.st_mtim.tv_nsec;
>> -      st->st_ctim.tv_sec = st64.st_ctim.tv_sec;
>> -      st->st_ctim.tv_nsec = st64.st_ctim.tv_nsec;
>> +      st->st_atim = valid_timespec_to_timespec64 (st64.st_atim);
>> +      st->st_mtim = valid_timespec_to_timespec64 (st64.st_mtim);
>> +      st->st_ctim = valid_timespec_to_timespec64 (st64.st_ctim);
>>      }
>>    return r;
>> -#  else
>> +# else
>>    /* 64-bit kabi outlier, e.g. mips64 and mips64-n32.  */
>>    struct kernel_stat kst;
>> -  int r = INTERNAL_SYSCALL_CALL (newfstatat, fd, file, &kst, flag);
>> -  return r ?: __cp_kstat_stat (&kst, st);
>> -#  endif /* __nr_fstatat64  */
>> -# endif /* STAT_IS_KERNEL_STAT  */
>> +  r = INTERNAL_SYSCALL_CALL (newfstatat, fd, file, &kst, flag);
>> +  return r ?: __cp_kstat_stat_t64 (&kst, st);
>> +# endif /* __NR_fstatat64  */
>> +}
>> +# if __TIMESIZE != 64
>> +libc_hidden_def (__fstatat_time64)
>> +
>> +int
>> +__fstatat (int fd, const char *file, struct stat *buf, int flags)
>> +{
>> +  struct __stat_t64 st_t64;
>> +  return __fstatat_time64 (fd, file, &st_t64, flags)
>> +	 ?: __cp_stat_t64_stat (&st_t64, buf);
>>  }
>> +# endif
>>  
>>  weak_alias (__fstatat, fstatat)
>>  #endif
>> diff --git a/sysdeps/unix/sysv/linux/fstatat64.c
>> b/sysdeps/unix/sysv/linux/fstatat64.c index 82fab107a5..b3940f0d20
>> 100644 --- a/sysdeps/unix/sysv/linux/fstatat64.c
>> +++ b/sysdeps/unix/sysv/linux/fstatat64.c
>> @@ -19,56 +19,102 @@
>>  #define __fstatat __redirect___fstatat
>>  #define fstatat   __redirect_fstatat
>>  #include <sys/stat.h>
>> -#undef __fstatat
>> -#undef fstatat
>>  #include <fcntl.h>
>> -
>> +#include <string.h>
>>  #include <kernel_stat.h>
>>  #include <sysdep.h>
>> -
>> +#include <time.h>
>>  #include <statx_cp.h>
>>  #include <kstat_cp.h>
>> +#include <stat_t64_cp.h>
>>  
>>  int
>> -__fstatat64 (int fd, const char *file, struct stat64 *st, int flag)
>> +__fstatat64_time64 (int fd, const char *file, struct __stat64_t64
>> *st,
>> +		    int flag)
>>  {
>> +  int r;
>> +
>> +#if (__WORDSIZE == 32 \
>> +     && (!defined __SYSCALL_WORDSIZE || __SYSCALL_WORDSIZE == 32))
> 
> I do guess that the above condition is to ensure that 32 bit archs
> (excluding x86_64-x32 - e.g. arm, riscv, etc) use statx?

Yes, this is similar for what is done by SysVIPC fixes.
Maciej W. Rozycki Aug. 2, 2020, 7:46 p.m. UTC | #5
On Thu, 30 Jul 2020, Adhemerval Zanella via Libc-alpha wrote:

> > And one more question what is the correct alignment:
> > 		(statx, fd, file, flag
> > 		 &stx);
> > 
> > or
> > 		(statx, fd, file, flag
> > 		&stx);
> 
> My understanding the latter.

 The opposite AFAIK, and you can always use `indent -gnu' to get the stuff 
right as far as "The GNU Coding Standards" are concerned, except perhaps 
for some really obscure code such as I believe some multi-line macros, and 
also some newer constructs such as ISO C99 compound literals may not work 
out well and have to be fixed up by hand (at least with older versions of 
GNU indent; I haven't checked what the most recent behaves like).

 Of course GNU indent will not handle project-specific extra rules such as 
ours: <https://sourceware.org/glibc/wiki/Style_and_Conventions>.

  Maciej
diff mbox series

Patch

diff --git a/include/sys/stat.h b/include/sys/stat.h
index 199173b007..87d4a5ec4f 100644
--- a/include/sys/stat.h
+++ b/include/sys/stat.h
@@ -3,6 +3,8 @@ 
 
 #ifndef _ISOMAC
 # include <xstatver.h>
+# include <struct___timespec64.h>
+# include <struct_stat_time64.h>
 # include <stdbool.h>
 
 static inline bool
@@ -44,6 +46,36 @@  hidden_proto (__lstat64)
 hidden_proto (__fstatat64)
 # endif
 
+# if __TIMESIZE == 64
+#  define __stat_time64  __stat
+#  define __stat64_time64  __stat64
+#  define __fstat_time64  __fstat
+#  define __fstat64_time64  __fstat64
+#  define __lstat_time64  __lstat
+#  define __lstat64_time64  __lstat64
+#  define __fstatat_time64  __fstatat
+#  define __fstatat64_time64 __fstatat64
+# else
+extern int __stat_time64 (const char *file, struct __stat_t64 *buf);
+libc_hidden_proto (__stat_time64);
+extern int __stat64_time64 (const char *file, struct __stat64_t64 *buf);
+hidden_proto (__stat64_time64);
+extern int __lstat_time64 (const char *file, struct __stat_t64 *buf);
+libc_hidden_proto (__lstat_time64);
+extern int __lstat64_time64 (const char *file, struct __stat64_t64 *buf);
+hidden_proto (__lstat64_time64);
+extern int __fstat_time64 (int fd, struct __stat_t64 *buf);
+libc_hidden_proto (__fstat_time64);
+extern int __fstat64_time64 (int fd, struct __stat64_t64 *buf);
+hidden_proto (__fstat64_time64);
+extern int __fstatat_time64 (int dirfd, const char *pathname,
+			     struct __stat_t64 *buf, int flags);
+libc_hidden_proto (__fstatat_time64);
+extern int __fstatat64_time64 (int dirfd, const char *pathname,
+			       struct __stat64_t64 *buf, int flags);
+hidden_proto (__fstatat64_time64);
+# endif
+
 extern int __chmod (const char *__file, __mode_t __mode);
 libc_hidden_proto (__chmod)
 extern int __fchmod (int __fd, __mode_t __mode);
diff --git a/sysdeps/generic/struct_stat_time64.h b/sysdeps/generic/struct_stat_time64.h
new file mode 100644
index 0000000000..24bb9f75cb
--- /dev/null
+++ b/sysdeps/generic/struct_stat_time64.h
@@ -0,0 +1,7 @@ 
+#ifndef _BITS_STRUCT_STAT_TIME64_H
+#define _BITS_STRUCT_STAT_TIME64_H 1
+
+#define __stat_t64   stat
+#define __stat64_t64 stat64
+
+#endif
diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile
index f189f65daf..95a51ee4f2 100644
--- a/sysdeps/unix/sysv/linux/Makefile
+++ b/sysdeps/unix/sysv/linux/Makefile
@@ -273,7 +273,7 @@  sysdep_routines += xstatconv internal_statvfs internal_statvfs64 \
 		   open_nocancel open64_nocancel \
 		   openat_nocancel openat64_nocancel \
 		   read_nocancel pread64_nocancel \
-		   write_nocancel statx_cp
+		   write_nocancel statx_cp stat_t64_cp
 
 sysdep_headers += bits/fcntl-linux.h
 
diff --git a/sysdeps/unix/sysv/linux/fstat.c b/sysdeps/unix/sysv/linux/fstat.c
index bdbeded956..0981dbaa95 100644
--- a/sysdeps/unix/sysv/linux/fstat.c
+++ b/sysdeps/unix/sysv/linux/fstat.c
@@ -19,13 +19,24 @@ 
 #include <sys/stat.h>
 #include <kernel_stat.h>
 #include <fcntl.h>
+#include <stat_t64_cp.h>
 
 #if !XSTAT_IS_XSTAT64
+int
+__fstat_time64 (int fd, struct __stat_t64 *buf)
+{
+  return __fstatat_time64 (fd, "", buf, AT_EMPTY_PATH);
+}
+# if __TIMESIZE != 64
+libc_hidden_def (__fstat_time64)
+
 int
 __fstat (int fd, struct stat *buf)
 {
-  return __fstatat (fd, "", buf, AT_EMPTY_PATH);
+  struct __stat_t64 st_t64;
+  return __fstat_time64 (fd, &st_t64) ?: __cp_stat_t64_stat (&st_t64, buf);
 }
+# endif
 
 weak_alias (__fstat, fstat)
 #endif
diff --git a/sysdeps/unix/sysv/linux/fstat64.c b/sysdeps/unix/sysv/linux/fstat64.c
index c2ff1ff577..67667e79d8 100644
--- a/sysdeps/unix/sysv/linux/fstat64.c
+++ b/sysdeps/unix/sysv/linux/fstat64.c
@@ -19,16 +19,30 @@ 
 #define __fstat __redirect___fstat
 #define fstat   __redirect_fstat
 #include <sys/stat.h>
-#undef __fstat
-#undef fstat
 #include <fcntl.h>
 #include <kernel_stat.h>
+#include <stat_t64_cp.h>
+
+int
+__fstat64_time64 (int fd, struct __stat64_t64 *buf)
+{
+  return __fstatat64_time64 (fd, "", buf, AT_EMPTY_PATH);
+}
+#if __TIMESIZE != 64
+hidden_def (__fstat64_time64)
 
 int
 __fstat64 (int fd, struct stat64 *buf)
 {
-  return __fstatat64 (fd, "", buf, AT_EMPTY_PATH);
+  struct __stat64_t64 st_t64;
+  return __fstat64_time64 (fd, &st_t64)
+	 ?: __cp_stat64_t64_stat64 (&st_t64, buf);
 }
+#endif
+
+#undef __fstat
+#undef fstat
+
 hidden_def (__fstat64)
 weak_alias (__fstat64, fstat64)
 
diff --git a/sysdeps/unix/sysv/linux/fstatat.c b/sysdeps/unix/sysv/linux/fstatat.c
index 03ddb3f493..f65d3b74a6 100644
--- a/sysdeps/unix/sysv/linux/fstatat.c
+++ b/sysdeps/unix/sysv/linux/fstatat.c
@@ -22,25 +22,28 @@ 
 
 #if !XSTAT_IS_XSTAT64
 # include <kstat_cp.h>
+# include <statx_cp.h>
+# include <stat_t64_cp.h>
 
 int
-__fstatat (int fd, const char *file, struct stat *st, int flag)
+__fstatat_time64 (int fd, const char *file, struct __stat_t64 *st, int flag)
 {
-# if STAT_IS_KERNEL_STAT
-  /* New kABIs which uses generic pre 64-bit time Linux ABI, e.g.
-     csky, nios2  */
-  int r = INLINE_SYSCALL_CALL (fstatat64, fd, file, st, flag);
-  if (r == 0 && (st->__st_ino_pad != 0
-		 || st->__st_size_pad != 0
-		 || st->__st_blocks_pad != 0))
-    return INLINE_SYSCALL_ERROR_RETURN_VALUE (EOVERFLOW);
-  return r;
-# else
-#  ifdef __NR_fstatat64
-  /* Old KABIs with old non-LFS support, e.g. arm, i386, hppa, m68k, mips32,
-     microblaze, s390, sh, powerpc, and sparc.  */
+  struct statx stx;
+  int r = INLINE_SYSCALL_CALL (statx, fd, file, flag, STATX_BASIC_STATS,
+			       &stx);
+  if (r == 0 || errno != ENOSYS)
+    {
+      if (r == 0)
+	__cp_stat_t64_statx (st, &stx);
+      return r;
+    }
+
+# ifdef __NR_fstatat64
+  /* Both new kABI which uses generic pre 64-bit time Linux ABI (e.g. csky
+     and nios) and old kABI with non-LFS support (e.g. arm, i386, hppa, m68k,
+     mips32, microblaze, s390, sh, powerpc, and sparc32).  */
   struct stat64 st64;
-  int r = INLINE_SYSCALL_CALL (fstatat64, fd, file, &st64, flag);
+  r = INLINE_SYSCALL_CALL (fstatat64, fd, file, &st64, flag);
   if (r == 0)
     {
       if (! in_ino_t_range (st64.st_ino)
@@ -48,7 +51,7 @@  __fstatat (int fd, const char *file, struct stat *st, int flag)
 	  || ! in_blkcnt_t_range (st64.st_blocks))
 	return INLINE_SYSCALL_ERROR_RETURN_VALUE (EOVERFLOW);
 
-      /* Clear internal pad and reserved fields.  */
+      /* Clear both pad and reserved fields.  */
       memset (st, 0, sizeof (*st));
 
       st->st_dev = st64.st_dev,
@@ -61,22 +64,29 @@  __fstatat (int fd, const char *file, struct stat *st, int flag)
       st->st_size = st64.st_size;
       st->st_blksize = st64.st_blksize;
       st->st_blocks  = st64.st_blocks;
-      st->st_atim.tv_sec = st64.st_atim.tv_sec;
-      st->st_atim.tv_nsec = st64.st_atim.tv_nsec;
-      st->st_mtim.tv_sec = st64.st_mtim.tv_sec;
-      st->st_mtim.tv_nsec = st64.st_mtim.tv_nsec;
-      st->st_ctim.tv_sec = st64.st_ctim.tv_sec;
-      st->st_ctim.tv_nsec = st64.st_ctim.tv_nsec;
+      st->st_atim = valid_timespec_to_timespec64 (st64.st_atim);
+      st->st_mtim = valid_timespec_to_timespec64 (st64.st_mtim);
+      st->st_ctim = valid_timespec_to_timespec64 (st64.st_ctim);
     }
   return r;
-#  else
+# else
   /* 64-bit kabi outlier, e.g. mips64 and mips64-n32.  */
   struct kernel_stat kst;
-  int r = INTERNAL_SYSCALL_CALL (newfstatat, fd, file, &kst, flag);
-  return r ?: __cp_kstat_stat (&kst, st);
-#  endif /* __nr_fstatat64  */
-# endif /* STAT_IS_KERNEL_STAT  */
+  r = INTERNAL_SYSCALL_CALL (newfstatat, fd, file, &kst, flag);
+  return r ?: __cp_kstat_stat_t64 (&kst, st);
+# endif /* __NR_fstatat64  */
+}
+# if __TIMESIZE != 64
+libc_hidden_def (__fstatat_time64)
+
+int
+__fstatat (int fd, const char *file, struct stat *buf, int flags)
+{
+  struct __stat_t64 st_t64;
+  return __fstatat_time64 (fd, file, &st_t64, flags)
+	 ?: __cp_stat_t64_stat (&st_t64, buf);
 }
+# endif
 
 weak_alias (__fstatat, fstatat)
 #endif
diff --git a/sysdeps/unix/sysv/linux/fstatat64.c b/sysdeps/unix/sysv/linux/fstatat64.c
index 82fab107a5..b3940f0d20 100644
--- a/sysdeps/unix/sysv/linux/fstatat64.c
+++ b/sysdeps/unix/sysv/linux/fstatat64.c
@@ -19,56 +19,102 @@ 
 #define __fstatat __redirect___fstatat
 #define fstatat   __redirect_fstatat
 #include <sys/stat.h>
-#undef __fstatat
-#undef fstatat
 #include <fcntl.h>
-
+#include <string.h>
 #include <kernel_stat.h>
 #include <sysdep.h>
-
+#include <time.h>
 #include <statx_cp.h>
 #include <kstat_cp.h>
+#include <stat_t64_cp.h>
 
 int
-__fstatat64 (int fd, const char *file, struct stat64 *st, int flag)
+__fstatat64_time64 (int fd, const char *file, struct __stat64_t64 *st,
+		    int flag)
 {
+  int r;
+
+#if (__WORDSIZE == 32 \
+     && (!defined __SYSCALL_WORDSIZE || __SYSCALL_WORDSIZE == 32))
+  struct statx tmp;
+  r = INLINE_SYSCALL_CALL (statx, fd, file, AT_NO_AUTOMOUNT | flag,
+			   STATX_BASIC_STATS, &tmp);
+  if (r == 0 || errno != ENOSYS)
+    {
+      if (r == 0)
+	__cp_stat64_t64_statx (st, &tmp);
+      return r;
+    }
+#endif
+
 #if XSTAT_IS_XSTAT64
 # ifdef __NR_newfstatat
   /* 64-bit kABI, e.g. aarch64, ia64, powerpc64*, s390x, riscv64, and
      x86_64.  */
-  return INLINE_SYSCALL_CALL (newfstatat, fd, file, st, flag);
+  r = INLINE_SYSCALL_CALL (newfstatat, fd, file, st, flag);
 # elif defined __NR_fstatat64
 #  if STAT64_IS_KERNEL_STAT64
-  /* 64-bit kABI outlier, e.g. alpha.  */
-  return INLINE_SYSCALL_CALL (fstatat64, fd, file, st, flag);
+  /* 64-bit kABI outlier, e.g. alpha  */
+  r = INLINE_SYSCALL_CALL (fstatat64, fd, file, st, flag);
 #  else
   /* 64-bit kABI outlier, e.g. sparc64.  */
   struct kernel_stat64 kst64;
-  int r = INLINE_SYSCALL_CALL (fstatat64, fd, file, &kst64, flag);
-  return r ?: __cp_stat64_kstat64 (st, &kst64);
-#  endif
-# else
-  /* 32-bit kABI with default 64-bit time_t, e.g. arc, riscv32.  */
-  struct statx tmp;
-  int r = INLINE_SYSCALL_CALL (statx, fd, file, AT_NO_AUTOMOUNT | flag,
-			       STATX_BASIC_STATS, &tmp);
+  r = INLINE_SYSCALL_CALL (fstatat64, fd, file, &kst64, flag);
   if (r == 0)
-    __cp_stat64_statx (st, &tmp);
-  return r;
+    r = __cp_stat64_kstat64 (st, &kst64);
+#  endif
 # endif
 #else
 # ifdef __NR_fstatat64
   /* All kABIs with non-LFS support, e.g. arm, csky, i386, hppa, m68k,
      microblaze, nios2, sh, powerpc32, and sparc32.  */
-  return INLINE_SYSCALL_CALL (fstatat64, fd, file, st, flag);
+  struct stat64 st64;
+  r = INLINE_SYSCALL_CALL (fstatat64, fd, file, &st64, flag);
+  if (r == 0)
+    {
+      /* Clear both pad and reserved fields.  */
+      memset (st, 0, sizeof (*st));
+
+      st->st_dev = st64.st_dev,
+      st->st_ino = st64.st_ino;
+      st->st_mode = st64.st_mode;
+      st->st_nlink = st64.st_nlink;
+      st->st_uid = st64.st_uid;
+      st->st_gid = st64.st_gid;
+      st->st_rdev = st64.st_rdev;
+      st->st_size = st64.st_size;
+      st->st_blksize = st64.st_blksize;
+      st->st_blocks  = st64.st_blocks;
+      st->st_atim = valid_timespec_to_timespec64 (st64.st_atim);
+      st->st_mtim = valid_timespec_to_timespec64 (st64.st_mtim);
+      st->st_ctim = valid_timespec_to_timespec64 (st64.st_ctim);
+    }
 # else
   /* 64-bit kabi outlier, e.g. mips64 and mips64-n32.  */
   struct kernel_stat kst;
-  int r = INTERNAL_SYSCALL_CALL (newfstatat, fd, file, &kst, flag);
-  return r ?: __cp_kstat_stat64 (&kst, st);
+  r = INTERNAL_SYSCALL_CALL (newfstatat, fd, file, &kst, flag);
+  if (r == 0)
+    r =  __cp_kstat_stat64_t64 (&kst, st);
 # endif
 #endif
+
+  return r;
 }
+#if __TIMESIZE != 64
+hidden_def (__fstatat64_time64)
+
+int
+__fstatat64 (int fd, const char *file, struct stat64 *st, int flags)
+{
+  struct __stat64_t64 st_t64;
+  return __fstatat64_time64 (fd, file, &st_t64, flags)
+	 ?: __cp_stat64_t64_stat64 (&st_t64, st);
+}
+#endif
+
+#undef __fstatat
+#undef fstatat
+
 hidden_def (__fstatat64)
 weak_alias (__fstatat64, fstatat64)
 
diff --git a/sysdeps/unix/sysv/linux/lstat.c b/sysdeps/unix/sysv/linux/lstat.c
index b0bdeee9e9..803ad78171 100644
--- a/sysdeps/unix/sysv/linux/lstat.c
+++ b/sysdeps/unix/sysv/linux/lstat.c
@@ -19,13 +19,24 @@ 
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <kernel_stat.h>
+#include <stat_t64_cp.h>
 
 #if !XSTAT_IS_XSTAT64
+int
+__lstat_time64 (const char *file, struct __stat_t64 *buf)
+{
+  return __fstatat_time64 (AT_FDCWD, file, buf, AT_SYMLINK_NOFOLLOW);
+}
+# if __TIMESIZE != 64
+libc_hidden_def (__lstat_time64)
+
 int
 __lstat (const char *file, struct stat *buf)
 {
-  return __fstatat (AT_FDCWD, file, buf, AT_SYMLINK_NOFOLLOW);
+  struct __stat_t64 st_t64;
+  return __lstat_time64 (file, &st_t64) ?: __cp_stat_t64_stat (&st_t64, buf);
 }
+# endif
 
 weak_alias (__lstat, lstat)
 #endif
diff --git a/sysdeps/unix/sysv/linux/lstat64.c b/sysdeps/unix/sysv/linux/lstat64.c
index e5f02e9822..971ab8469d 100644
--- a/sysdeps/unix/sysv/linux/lstat64.c
+++ b/sysdeps/unix/sysv/linux/lstat64.c
@@ -19,19 +19,32 @@ 
 #define __lstat __redirect___lstat
 #define lstat   __redirect_lstat
 #include <sys/stat.h>
-#undef __lstat
-#undef lstat
 #include <fcntl.h>
 #include <kernel_stat.h>
+#include <stat_t64_cp.h>
+
+int
+__lstat64_time64 (const char *file, struct __stat64_t64 *buf)
+{
+  return __fstatat64_time64 (AT_FDCWD, file, buf, AT_SYMLINK_NOFOLLOW);
+}
+#if __TIMESIZE != 64
+hidden_def (__lstat64_time64)
 
 int
 __lstat64 (const char *file, struct stat64 *buf)
 {
-  return __fstatat64 (AT_FDCWD, file, buf, AT_SYMLINK_NOFOLLOW);
+  struct __stat64_t64 st_t64;
+  return __lstat64_time64 (file, &st_t64)
+	 ?: __cp_stat64_t64_stat64 (&st_t64, buf);
 }
+#endif
 hidden_def (__lstat64)
 weak_alias (__lstat64, lstat64)
 
+#undef __lstat
+#undef lstat
+
 #if XSTAT_IS_XSTAT64
 strong_alias (__lstat64, __lstat)
 weak_alias (__lstat64, lstat)
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/kstat_cp.h b/sysdeps/unix/sysv/linux/mips/mips64/kstat_cp.h
index 7f226416f9..553f4226bc 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/kstat_cp.h
+++ b/sysdeps/unix/sysv/linux/mips/mips64/kstat_cp.h
@@ -20,23 +20,21 @@ 
 #include <kernel_stat.h>
 
 static inline int
-__cp_kstat_stat (const struct kernel_stat *kst, struct stat *st)
+__cp_kstat_stat_t64 (const struct kernel_stat *kst, struct __stat_t64 *st)
 {
+  if (! in_ino_t_range (kst->st_ino)
+      || ! in_off_t_range (kst->st_size)
+      || ! in_blkcnt_t_range (kst->st_blocks))
+    return INLINE_SYSCALL_ERROR_RETURN_VALUE (EOVERFLOW);
+
   st->st_dev = kst->st_dev;
-  memset (&st->st_pad1, 0, sizeof (st->st_pad1));
   st->st_ino = kst->st_ino;
-  if (st->st_ino != kst->st_ino)
-    return INLINE_SYSCALL_ERROR_RETURN_VALUE (EOVERFLOW);
   st->st_mode = kst->st_mode;
   st->st_nlink = kst->st_nlink;
   st->st_uid = kst->st_uid;
   st->st_gid = kst->st_gid;
   st->st_rdev = kst->st_rdev;
-  memset (&st->st_pad2, 0, sizeof (st->st_pad2));
   st->st_size = kst->st_size;
-  if (st->st_size != kst->st_size)
-    return INLINE_SYSCALL_ERROR_RETURN_VALUE (EOVERFLOW);
-  st->st_pad3 = 0;
   st->st_atim.tv_sec = kst->st_atime_sec;
   st->st_atim.tv_nsec = kst->st_atime_nsec;
   st->st_mtim.tv_sec = kst->st_mtime_sec;
@@ -45,26 +43,20 @@  __cp_kstat_stat (const struct kernel_stat *kst, struct stat *st)
   st->st_ctim.tv_nsec = kst->st_ctime_nsec;
   st->st_blksize = kst->st_blksize;
   st->st_blocks = kst->st_blocks;
-  if (st->st_blocks != kst->st_blocks)
-    return INLINE_SYSCALL_ERROR_RETURN_VALUE (EOVERFLOW);
-  memset (&st->st_pad5, 0, sizeof (st->st_pad5));
 
   return 0;
 }
 
 static inline int
-__cp_kstat_stat64 (const struct kernel_stat *kst, struct stat64 *st)
+__cp_kstat_stat64_t64 (const struct kernel_stat *kst, struct __stat64_t64 *st)
 {
   st->st_dev = kst->st_dev;
-  memset (&st->st_pad1, 0, sizeof (st->st_pad1));
   st->st_ino = kst->st_ino;
   st->st_mode = kst->st_mode;
   st->st_nlink = kst->st_nlink;
   st->st_uid = kst->st_uid;
   st->st_gid = kst->st_gid;
   st->st_rdev = kst->st_rdev;
-  memset (&st->st_pad2, 0, sizeof (st->st_pad2));
-  st->st_pad3 = 0;
   st->st_size = kst->st_size;
   st->st_blksize = kst->st_blksize;
   st->st_blocks = kst->st_blocks;
@@ -74,7 +66,6 @@  __cp_kstat_stat64 (const struct kernel_stat *kst, struct stat64 *st)
   st->st_mtim.tv_nsec = kst->st_mtime_nsec;
   st->st_ctim.tv_sec = kst->st_ctime_sec;
   st->st_ctim.tv_nsec = kst->st_ctime_nsec;
-  memset (&st->st_pad4, 0, sizeof (st->st_pad4));
 
   return 0;
 }
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/statx_cp.c b/sysdeps/unix/sysv/linux/mips/mips64/statx_cp.c
deleted file mode 100644
index 260cda987e..0000000000
--- a/sysdeps/unix/sysv/linux/mips/mips64/statx_cp.c
+++ /dev/null
@@ -1,3 +0,0 @@ 
-/* Override the generic statx_cp.c which is only needed for new 32-bit arch
-   without stat64 family support.
- */
diff --git a/sysdeps/unix/sysv/linux/stat.c b/sysdeps/unix/sysv/linux/stat.c
index a77502eb95..dcbc22da29 100644
--- a/sysdeps/unix/sysv/linux/stat.c
+++ b/sysdeps/unix/sysv/linux/stat.c
@@ -19,13 +19,24 @@ 
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <kernel_stat.h>
+#include <stat_t64_cp.h>
 
 #if !XSTAT_IS_XSTAT64
+int
+__stat_time64 (const char *file, struct __stat_t64 *buf)
+{
+  return __fstatat_time64 (AT_FDCWD, file, buf, 0);
+}
+# if __TIMESIZE != 64
+libc_hidden_def (__stat_time64);
+
 int
 __stat (const char *file, struct stat *buf)
 {
-  return __fstatat (AT_FDCWD, file, buf, 0);
+  struct __stat_t64 st_t64;
+  return __stat_time64 (file, &st_t64) ?: __cp_stat_t64_stat (&st_t64, buf);
 }
+# endif
 
 weak_alias (__stat, stat)
 #endif
diff --git a/sysdeps/unix/sysv/linux/stat64.c b/sysdeps/unix/sysv/linux/stat64.c
index 2f40037c2c..bd8b17ac49 100644
--- a/sysdeps/unix/sysv/linux/stat64.c
+++ b/sysdeps/unix/sysv/linux/stat64.c
@@ -19,16 +19,30 @@ 
 #define __stat __redirect___stat
 #define stat   __redirect_stat
 #include <sys/stat.h>
-#undef __stat
-#undef stat
 #include <fcntl.h>
 #include <kernel_stat.h>
+#include <stat_t64_cp.h>
+
+int
+__stat64_time64 (const char *file, struct __stat64_t64 *buf)
+{
+  return __fstatat64_time64 (AT_FDCWD, file, buf, 0);
+}
+#if __TIMESIZE != 64
+hidden_def (__stat64_time64)
 
 int
 __stat64 (const char *file, struct stat64 *buf)
 {
-  return __fstatat64 (AT_FDCWD, file, buf, 0);
+  struct __stat64_t64 st_t64;
+  return __stat64_time64 (file, &st_t64)
+	 ?: __cp_stat64_t64_stat64 (&st_t64, buf);
 }
+#endif
+
+#undef __stat
+#undef stat
+
 hidden_def (__stat64)
 weak_alias (__stat64, stat64)
 
diff --git a/sysdeps/unix/sysv/linux/stat_t64_cp.c b/sysdeps/unix/sysv/linux/stat_t64_cp.c
new file mode 100644
index 0000000000..56459b6266
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/stat_t64_cp.c
@@ -0,0 +1,92 @@ 
+/* Struct stat/stat64 to stat/stat64 conversion for Linux.
+   Copyright (C) 2020 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/>.  */
+
+#include <stat_t64_cp.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+
+#if __TIMESIZE != 64
+/* Convert the 64-bit time_t stat ST_T64 to the non-LFS ST stat and returns
+   EOVERFLOW if any of time (access, modification, status) is larger than
+   32-bit time_t.  It is used on non-LFS stat, fstat, lstat, and fstatat to
+   convert from the the 64-bit time_t call.  */
+int
+__cp_stat_t64_stat (const struct __stat_t64 *st_t64, struct stat *st)
+{
+  if (! in_time_t_range (st_t64->st_atim.tv_sec)
+      || ! in_time_t_range (st_t64->st_mtim.tv_sec)
+      || ! in_time_t_range (st_t64->st_ctim.tv_sec))
+    {
+      __set_errno (EOVERFLOW);
+      return -1;
+    }
+
+  /* Clear both pad and reserved fields.  */
+  memset (st, 0, sizeof (*st));
+
+  st->st_dev = st_t64->st_dev,
+  st->st_ino = st_t64->st_ino;
+  st->st_mode = st_t64->st_mode;
+  st->st_nlink = st_t64->st_nlink;
+  st->st_uid = st_t64->st_uid;
+  st->st_gid = st_t64->st_gid;
+  st->st_rdev = st_t64->st_rdev;
+  st->st_size = st_t64->st_size;
+  st->st_blksize = st_t64->st_blksize;
+  st->st_blocks  = st_t64->st_blocks;
+  st->st_atim = valid_timespec64_to_timespec (st_t64->st_atim);
+  st->st_mtim = valid_timespec64_to_timespec (st_t64->st_mtim);
+  st->st_ctim = valid_timespec64_to_timespec (st_t64->st_ctim);
+
+  return 0;
+}
+
+int
+__cp_stat64_t64_stat64 (const struct __stat64_t64 *st64_t64,
+			struct stat64 *st64)
+{
+  if (! in_time_t_range (st64_t64->st_atim.tv_sec)
+      || ! in_time_t_range (st64_t64->st_mtim.tv_sec)
+      || ! in_time_t_range (st64_t64->st_ctim.tv_sec))
+    {
+      __set_errno (EOVERFLOW);
+      return -1;
+    }
+
+  /* Clear both pad and reserved fields.  */
+  memset (st64, 0, sizeof (*st64));
+
+  st64->st_dev = st64_t64->st_dev,
+  st64->st_ino = st64_t64->st_ino;
+  st64->st_mode = st64_t64->st_mode;
+  st64->st_nlink = st64_t64->st_nlink;
+  st64->st_uid = st64_t64->st_uid;
+  st64->st_gid = st64_t64->st_gid;
+  st64->st_rdev = st64_t64->st_rdev;
+  st64->st_size = st64_t64->st_size;
+  st64->st_blksize = st64_t64->st_blksize;
+  st64->st_blocks  = st64_t64->st_blocks;
+  st64->st_atim = valid_timespec64_to_timespec (st64_t64->st_atim);
+  st64->st_mtim = valid_timespec64_to_timespec (st64_t64->st_mtim);
+  st64->st_ctim = valid_timespec64_to_timespec (st64_t64->st_ctim);
+
+  return 0;
+}
+
+#endif
diff --git a/sysdeps/unix/sysv/linux/stat_t64_cp.h b/sysdeps/unix/sysv/linux/stat_t64_cp.h
new file mode 100644
index 0000000000..ad2bcc7a04
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/stat_t64_cp.h
@@ -0,0 +1,28 @@ 
+/* Copy to/from struct stat with and without 64-bit time_t support.
+   Copyright (C) 2020 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/>.  */
+
+#include <sys/stat.h>
+
+#if __TIMESIZE != 64
+extern int __cp_stat_t64_stat (const struct __stat_t64 *st_t64,
+			       struct stat *st)
+  attribute_hidden;
+extern int __cp_stat64_t64_stat64 (const struct __stat64_t64 *st64_t64,
+				   struct stat64 *st64)
+  attribute_hidden;
+#endif
diff --git a/sysdeps/unix/sysv/linux/statx_cp.c b/sysdeps/unix/sysv/linux/statx_cp.c
index cc6e17929e..4b35e86f8d 100644
--- a/sysdeps/unix/sysv/linux/statx_cp.c
+++ b/sysdeps/unix/sysv/linux/statx_cp.c
@@ -47,3 +47,57 @@  __cp_stat64_statx (struct stat64 *to, struct statx *from)
   to->st_blksize = from->stx_blksize;
 }
 #endif
+
+void
+__cp_stat_t64_statx (struct __stat_t64 *to, const struct statx *from)
+{
+  /* Clear both pad and reserved fields.  */
+  memset (to, 0, sizeof (*to));
+
+  to->st_dev = ((from->stx_dev_minor & 0xff) | (from->stx_dev_major << 8)
+		| ((from->stx_dev_minor & ~0xff) << 12));
+  to->st_ino = from->stx_ino;
+  to->st_mode = from->stx_mode;
+  to->st_nlink = from->stx_nlink;
+  to->st_uid = from->stx_uid;
+  to->st_gid = from->stx_gid;
+  to->st_rdev = ((from->stx_rdev_minor & 0xff) | (from->stx_rdev_major << 8)
+		 | ((from->stx_rdev_minor & ~0xff) << 12));
+  to->st_size = from->stx_size;
+  to->st_blksize = from->stx_blksize;
+  to->st_blocks = from->stx_blocks;
+
+  to->st_atime = from->stx_atime.tv_sec;
+  to->st_atim.tv_nsec = from->stx_atime.tv_nsec;
+  to->st_mtime = from->stx_mtime.tv_sec;
+  to->st_mtim.tv_nsec = from->stx_mtime.tv_nsec;
+  to->st_ctime = from->stx_ctime.tv_sec;
+  to->st_ctim.tv_nsec = from->stx_ctime.tv_nsec;
+}
+
+void
+__cp_stat64_t64_statx (struct __stat64_t64 *to, const struct statx *from)
+{
+  /* Clear both pad and reserved fields.  */
+  memset (to, 0, sizeof (*to));
+
+  to->st_dev = ((from->stx_dev_minor & 0xff) | (from->stx_dev_major << 8)
+		| ((from->stx_dev_minor & ~0xff) << 12));
+  to->st_ino = from->stx_ino;
+  to->st_mode = from->stx_mode;
+  to->st_nlink = from->stx_nlink;
+  to->st_uid = from->stx_uid;
+  to->st_gid = from->stx_gid;
+  to->st_rdev = ((from->stx_rdev_minor & 0xff) | (from->stx_rdev_major << 8)
+		 | ((from->stx_rdev_minor & ~0xff) << 12));
+  to->st_size = from->stx_size;
+  to->st_blksize = from->stx_blksize;
+  to->st_blocks = from->stx_blocks;
+
+  to->st_atime = from->stx_atime.tv_sec;
+  to->st_atim.tv_nsec = from->stx_atime.tv_nsec;
+  to->st_mtime = from->stx_mtime.tv_sec;
+  to->st_mtim.tv_nsec = from->stx_mtime.tv_nsec;
+  to->st_ctime = from->stx_ctime.tv_sec;
+  to->st_ctim.tv_nsec = from->stx_ctime.tv_nsec;
+}
diff --git a/sysdeps/unix/sysv/linux/statx_cp.h b/sysdeps/unix/sysv/linux/statx_cp.h
index fdbb807a31..bf3186ffac 100644
--- a/sysdeps/unix/sysv/linux/statx_cp.h
+++ b/sysdeps/unix/sysv/linux/statx_cp.h
@@ -18,3 +18,9 @@ 
 
 extern void __cp_stat64_statx (struct stat64 *to, struct statx *from)
   attribute_hidden;
+extern void __cp_stat_t64_statx (struct __stat_t64 *to,
+				 const struct statx *from)
+  attribute_hidden;
+extern void __cp_stat64_t64_statx (struct __stat64_t64 *to,
+				   const struct statx *from)
+  attribute_hidden;
diff --git a/sysdeps/unix/sysv/linux/struct_stat_time64.h b/sysdeps/unix/sysv/linux/struct_stat_time64.h
new file mode 100644
index 0000000000..b2a51927d0
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/struct_stat_time64.h
@@ -0,0 +1,122 @@ 
+/* Struct stat with 64-bit time support.
+   Copyright (C) 2020 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/>.  */
+
+#ifndef _BITS_STRUCT_STAT_TIME64_H
+#define _BITS_STRUCT_STAT_TIME64_H 1
+
+#if __TIMESIZE == 64
+# define __stat_t64   stat
+# define __stat64_t64 stat64
+#else
+# include <endian.h>
+
+# ifdef __USE_FILE_OFFSET64
+#  define __field64(type, type64, name) type64 name
+# else
+#  define __field64(type, type64, name) type name
+# endif
+
+/* The definition should be equal to the 'struct __timespec64' internal
+   layout.  */
+# if BYTE_ORDER == BIG_ENDIAN
+#  define __fieldts64(name) 					\
+   __time64_t name; __int32_t :32; __int32_t name ## nsec
+# else
+#  define __fieldts64(name)					\
+   __time64_t name; __int32_t name ## nsec; __int32_t :32
+# endif
+
+/* Workaround for the definition from struct_stat.h  */
+# undef st_atime
+# undef st_mtime
+# undef st_ctime
+
+struct __stat_t64
+  {
+    __dev_t st_dev;			/* Device.  */
+    __field64(__ino_t, __ino64_t, st_ino);  /* File serial number.  */
+    __mode_t st_mode;			/* File mode.  */
+    __nlink_t st_nlink;			/* Link count.  */
+    __uid_t st_uid;			/* User ID of the file's owner.	*/
+    __gid_t st_gid;			/* Group ID of the file's group.*/
+    __dev_t st_rdev;			/* Device number, if device.  */
+    __field64(__off_t, __off64_t, st_size);  /* Size of file, in bytes. */
+    __blksize_t st_blksize;		/* Optimal block size for I/O.  */
+    __field64(__blkcnt_t, __blkcnt64_t, st_blocks);  /* 512-byte blocks */
+# ifdef __USE_XOPEN2K8
+    /* Nanosecond resolution timestamps are stored in a format
+       equivalent to 'struct timespec'.  This is the type used
+       whenever possible but the Unix namespace rules do not allow the
+       identifier 'timespec' to appear in the <sys/stat.h> header.
+       Therefore we have to handle the use of this header in strictly
+       standard-compliant sources special.  */
+    struct __timespec64 st_atim;	/* Time of last access.  */
+    struct __timespec64 st_mtim;	/* Time of last modification.  */
+    struct __timespec64 st_ctim;	/* Time of last status change.  */
+#  define st_atime st_atim.tv_sec	/* Backward compatibility.  */
+#  define st_mtime st_mtim.tv_sec
+#  define st_ctime st_ctim.tv_sec
+# else
+    __fieldts64 (st_atime);
+    __fieldts64 (st_mtime);
+    __fieldts64 (st_ctime);
+# endif /* __USE_XOPEN2K8  */
+  };
+
+# undef __field64
+
+#ifdef __USE_LARGEFILE64
+struct __stat64_t64
+  {
+    __dev_t st_dev;			/* Device.  */
+    __ino64_t st_ino;			/* file serial number.	*/
+    __mode_t st_mode;			/* File mode.  */
+    __nlink_t st_nlink;			/* Link count.  */
+    __uid_t st_uid;			/* User ID of the file's owner.	*/
+    __gid_t st_gid;			/* Group ID of the file's group.*/
+    __dev_t st_rdev;			/* Device number, if device.  */
+    __off64_t st_size;			/* Size of file, in bytes.  */
+    __blksize_t st_blksize;		/* Optimal block size for I/O.  */
+    __blkcnt64_t st_blocks;		/* Number 512-byte blocks allocated. */
+# ifdef __USE_XOPEN2K8
+    /* Nanosecond resolution timestamps are stored in a format
+       equivalent to 'struct timespec'.  This is the type used
+       whenever possible but the Unix namespace rules do not allow the
+       identifier 'timespec' to appear in the <sys/stat.h> header.
+       Therefore we have to handle the use of this header in strictly
+       standard-compliant sources special.  */
+    struct __timespec64 st_atim;	/* Time of last access.  */
+    struct __timespec64 st_mtim;	/* Time of last modification.  */
+    struct __timespec64 st_ctim;	/* Time of last status change.  */
+# else
+    __fieldts64 (st_atime);
+    __fieldts64 (st_mtime);
+    __fieldts64 (st_ctime);
+# endif /* __USE_XOPEN2K8  */
+  };
+# endif /* __USE_LARGEFILE64  */
+
+# undef __fieldts64
+
+# define _STATBUF_ST_BLKSIZE
+# define _STATBUF_ST_RDEV
+# define _STATBUF_ST_NSEC
+
+# endif /* __TIMESIZE == 64  */
+
+#endif /* _BITS_STRUCT_STAT_TIME64_H  */