[v5,00/14] port C-SKY to glibc

Message ID 20181123090547.GA11225@vmh-VirtualBox
State New, archived
Headers

Commit Message

毛晗 Nov. 23, 2018, 9:05 a.m. UTC
  On Wed, Nov 21, 2018 at 05:01:32PM +0000, Joseph Myers wrote:
> On Wed, 21 Nov 2018, Mao Han wrote:
> 
> > I'v modified patch 14/14 to a generic version with __NR3264_fstatat
> > conditionals (in the attachment). It is tested in the same environment
> > as patch V5 and got same result, but have't test fstat64 path yet.
> 
> Thanks, this is the sort of thing I'm looking for, but I'm concerned about 
> the use of __NR3264_fstatat.  As I understand, that's an internal 
> implementation detail of the asm-generic unistd.h; it shouldn't be 
> considered something stable that will reliably be there in future kernel 
> versions.  Rather, the stable interface from asm/unistd.h is the 
> __NR_<syscall> macros for each <syscall> provided by the kernel.  So I 
> think the #ifdef conditionals need to be on the actual syscalls used in 
> each case (e.g. you'd condition on __NR_fstat64 when using fstat64 in the 
> existing INLINE_SYSCALL use).  In statx_cp.c you'll need to find some stat 
> syscall to condition on that's present on all existing architectures, as 
> the aim is that no existing glibc architecture should get any code 
> compiled from that file (which you could check using 
> build-many-glibcs.py).

Yes. __NR_<syscall> macros should be used here. I'v change __NR3264_fstat*
to __NR_fstat*.
Current unistd.h alway have __NR_fstatat64 defined to __NR3264_fstatat
for 32-bit platform, so I used __NR3264_fstatat to make conditionals
work. As Arnd said that __NR3264_fstatat and the related macro will be
gone in 4.20 and the patch is not forwarded yet, C-SKY will get some
compile error with current kernel header, I added some conditionals
in unistd.h for test:

+#if defined(__ARCH_WANT_NEW_STAT) || defined(__ARCH_WANT_STAT64)
#define __NR_fstatat64 __NR3264_fstatat
#define __NR_fstat64 __NR3264_fstat
+#endif

Current statx_cp.c will check __NR_fstat64, __NR_fstatat64, __WORDSIZE == 32
to see if it is a 32 bit platforms without fstat64; __NR_fstat and
__NR_newfstatat check is for mips64-*-n32 and x86_64-*-x32 they always use
64 bit system call but got __WORDSIZE == 32.
I'v add an #error in statx_cp.c while using build-many-glibcs.py to do
the build test. Seems no other arch compiled code inside statx_cp.c
 
> Also, it's a bad idea to duplicate the contents of 
> sysdeps/unix/sysv/linux/fxstat64.c in 
> sysdeps/unix/sysv/linux/generic/wordsize-32/fxstat64.c - rather, keep the 
> existing #include, and modify sysdeps/unix/sysv/linux/fxstat64.c 
> appropriately to handle the statx case (which definitely requires using 
> __NR_fstat64 in the #ifdef, since this file is used in cases not using the 
> asm-generic syscall list at all).
>

OK. I've modified sysdeps/unix/sysv/linux/fxstat64.c directly. I was quite
hesitate to change that file as it will affect more arch.

> You're using AT_NO_AUTOMOUNT in these statx calls - does that match what 
> the old stat syscalls do on other architectures?  (I think the aim there 
> should be to match the old stat syscalls, so if those don't automount for 
> stat of an automount point, then using AT_NO_AUTOMOUNT is correct.)

AT_NO_AUTOMOUNT is from ./include/linux/fs.h in the kernel. fstatat64
will call vfs_fstatat and fstat64 will call vfs_stat; they both got
AT_NO_AUTOMOUNT inside in call.
static inline int vfs_stat(const char __user *filename, struct kstat *stat)
{
        return vfs_statx(AT_FDCWD, filename, AT_NO_AUTOMOUNT,
                         stat, STATX_BASIC_STATS);
}
static inline int vfs_fstatat(int dfd, const char __user *filename,
                              struct kstat *stat, int flags)
{
        return vfs_statx(dfd, filename, flags | AT_NO_AUTOMOUNT,
                         stat, STATX_BASIC_STATS);
}


Best Regards,
Mao Han
  

Comments

Joseph Myers Nov. 23, 2018, 5:11 p.m. UTC | #1
On Fri, 23 Nov 2018, Mao Han wrote:

> Current statx_cp.c will check __NR_fstat64, __NR_fstatat64, __WORDSIZE 
> == 32 to see if it is a 32 bit platforms without fstat64; __NR_fstat and 
> __NR_newfstatat check is for mips64-*-n32 and x86_64-*-x32 they always
> use 64 bit system call but got __WORDSIZE == 32.

That might give the right conditions for current glibc platforms, but I 
don't think it's logically correct - putting conditions on __NR_fstat and 
__NR_newfstatat, when none of the files using statx also use those 
syscalls, is hardly a clean approach.

Since the way in which those platforms avoid using the files in question 
is through having their own sysdeps override files that either contain 
just comments or reimplement the functions in question, my suggestion 
would be to have statx_cp.c files in sysdeps/unix/sysv/linux/wordsize-64 
and sysdeps/unix/sysv/linux/mips/mips64, containing just a comment.  Then 
the conditional in the main statx_cp.c file would be

#if !defined(__NR_fstat64) || !defined(__NR_fstatat64)

(note this is || not &&, because if either macro is undefined, the 
relevant source file will use statx).

Note that the ChangeLog entry needs updating to reflect the actual set of 
files modified (it still refers to wordsize-32 for two files being 
modified outside of the wordsize-32 directory).

> +#include "statx_cp.h"

I think these includes should be of <statx_cp.h> not "statx_cp.h" - in 
general, #include <> should be preferred to avoid any problems overriding 
a file in another sysdeps directory if necessary.

You're missing an include of statx_cp.h from statx_cp.c.  It's important 
to include the header from the file defining the corresponding functions, 
so that the compiler checks the prototypes in the two places are 
consistent.

> +__cp_stat_statx (struct stat *to, struct statx *from)
> +{
> +  memset(to, 0, sizeof(struct stat));

Missing space before '(', twice on this line.

> +  memset(to, 0, sizeof(struct stat64));

Likewise.

I also note that when the generic/wordsize-32 files use the existing 
syscalls, they use stat_overflow in the case of success to ensure that if 
any of the fields overflow the 32-bit versions in struct stat, an error is 
properly returned with errno set to EOVERFLOW.  This patch seems to be 
missing anything like that in the case where statx is used - indeed, 
__cp_stat_statx returns void, so preventing it from indicating an error to 
the caller.

It's probably a bad idea to duplicate the logic for such overflow checks - 
rather, the existing stat_overflow code should be used.  Maybe eliminate 
__cp_stat_statx, make the callers cast to (struct stat64 *) and call 
__cp_stat64_statx and then use stat_overflow in the callers?  (As all the 
cases needing this check are in generic/wordsize-32, we know that the stat 
and stat64 layouts are the same, just differing in whether or not high 
parts are counted as padding.  Because statx_cp.c is in a separate 
translation unit and glibc uses C with ABI boundaries, doing the cast in a 
caller like that is safe, whereas defining __cp_stat_statx to do such a 
cast, call and then check with stat_overflow, in the same translation unit 
as the __cp_stat64_statx definition, could run into aliasing issues.)
  

Patch

From d1a3b0fc2cd91f7a969468fd28c7eef27a2359c6 Mon Sep 17 00:00:00 2001
Message-Id: <d1a3b0fc2cd91f7a969468fd28c7eef27a2359c6.1542960800.git.han_mao@c-sky.com>
From: Mao Han <han_mao@c-sky.com>
Date: Fri, 23 Nov 2018 15:46:20 +0800
Subject: [PATCH V2 1/1] Add statx conditionals for wordsize-32 *xstat.c

Linux kernel have remove stat64 family from default syscall set, new
implementations with statx is needed when __ARCH_WANT_STAT64 is not
define. This patch add conditionals for relevant functions, using statx
system call to get information and then copy to the return buf, ref to
include/linux/fs.h from linux kernel.

	* sysdeps/unix/sysv/linux/Makefile: Add statx_cp.c.
	* sysdeps/unix/sysv/linux/generic/wordsize-32/fxstat.c: Add
	conditionals for kernel without stat64.
	* sysdeps/unix/sysv/linux/generic/wordsize-32/fxstat64.c: Likewise.
	* sysdeps/unix/sysv/linux/generic/wordsize-32/fxstatat.c: Likewise.
	* sysdeps/unix/sysv/linux/generic/wordsize-32/fxstatat64.c: Likewise.
	* sysdeps/unix/sysv/linux/generic/wordsize-32/lxstat.c: Likewise.
	* sysdeps/unix/sysv/linux/generic/wordsize-32/lxstat64.c: Likewise.
	* sysdeps/unix/sysv/linux/generic/wordsize-32/xstat.c: Likewise.
	* sysdeps/unix/sysv/linux/generic/wordsize-32/xstat64.c: Likewise.
	* sysdeps/unix/sysv/linux/statx_cp.c: New file.
	* sysdeps/unix/sysv/linux/statx_cp.h: Likewise.
---
 sysdeps/unix/sysv/linux/Makefile                   |  3 +-
 sysdeps/unix/sysv/linux/fxstat64.c                 | 13 +++-
 sysdeps/unix/sysv/linux/fxstatat64.c               | 11 +++
 .../unix/sysv/linux/generic/wordsize-32/fxstat.c   | 11 +++
 .../unix/sysv/linux/generic/wordsize-32/fxstatat.c | 11 +++
 .../unix/sysv/linux/generic/wordsize-32/lxstat.c   | 11 +++
 .../unix/sysv/linux/generic/wordsize-32/lxstat64.c | 19 +++++-
 .../unix/sysv/linux/generic/wordsize-32/xstat.c    | 10 +++
 .../unix/sysv/linux/generic/wordsize-32/xstat64.c  | 14 +++-
 sysdeps/unix/sysv/linux/statx_cp.c                 | 78 ++++++++++++++++++++++
 sysdeps/unix/sysv/linux/statx_cp.h                 | 23 +++++++
 11 files changed, 199 insertions(+), 5 deletions(-)
 create mode 100644 sysdeps/unix/sysv/linux/statx_cp.c
 create mode 100644 sysdeps/unix/sysv/linux/statx_cp.h

diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile
index 72b6b64..2943a10 100644
--- a/sysdeps/unix/sysv/linux/Makefile
+++ b/sysdeps/unix/sysv/linux/Makefile
@@ -177,7 +177,8 @@  sysdep_routines += xstatconv internal_statvfs internal_statvfs64 \
 		   close_nocancel fcntl_nocancel nanosleep_nocancel \
 		   open_nocancel open64_nocancel \
 		   openat_nocancel openat64_nocancel \
-		   pause_nocancel read_nocancel waitpid_nocancel write_nocancel
+		   pause_nocancel read_nocancel waitpid_nocancel \
+		   write_nocancel statx_cp
 
 sysdep_headers += bits/fcntl-linux.h
 
diff --git a/sysdeps/unix/sysv/linux/fxstat64.c b/sysdeps/unix/sysv/linux/fxstat64.c
index 0d05389..e2e51dc 100644
--- a/sysdeps/unix/sysv/linux/fxstat64.c
+++ b/sysdeps/unix/sysv/linux/fxstat64.c
@@ -1,4 +1,4 @@ 
-/* fxstat64 using Linux fstat64 system call.
+/* fxstat64 using Linux fstat64/statx system call.
    Copyright (C) 1997-2018 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
@@ -18,6 +18,7 @@ 
 
 #include <errno.h>
 #include <stddef.h>
+#include <fcntl.h>
 #include <sys/stat.h>
 #include <kernel_stat.h>
 
@@ -26,13 +27,23 @@ 
 
 #include <kernel-features.h>
 
+#include "statx_cp.h"
+
 /* Get information about the file FD in BUF.  */
 
 int
 ___fxstat64 (int vers, int fd, struct stat64 *buf)
 {
   int result;
+#ifdef __NR_fstat64
   result = INLINE_SYSCALL (fstat64, 2, fd, buf);
+#else
+  struct statx tmp;
+  result = INLINE_SYSCALL (statx, 5, fd, "", AT_EMPTY_PATH, STATX_BASIC_STATS,
+                           &tmp);
+  if (result == 0)
+    __cp_stat64_statx (buf, &tmp);
+#endif
 #if defined _HAVE_STAT64___ST_INO && !__ASSUME_ST_INO_64_BIT
   if (__builtin_expect (!result, 1) && buf->__st_ino != (__ino_t) buf->st_ino)
     buf->st_ino = buf->__st_ino;
diff --git a/sysdeps/unix/sysv/linux/fxstatat64.c b/sysdeps/unix/sysv/linux/fxstatat64.c
index baa9a60..95123ed 100644
--- a/sysdeps/unix/sysv/linux/fxstatat64.c
+++ b/sysdeps/unix/sysv/linux/fxstatat64.c
@@ -26,6 +26,8 @@ 
 #include <sysdep.h>
 #include <sys/syscall.h>
 
+#include "statx_cp.h"
+
 /* Get information about the file NAME in BUF.  */
 
 int
@@ -37,7 +39,16 @@  __fxstatat64 (int vers, int fd, const char *file, struct stat64 *st, int flag)
   int result;
   INTERNAL_SYSCALL_DECL (err);
 
+#ifdef __NR_fstatat64
   result = INTERNAL_SYSCALL (fstatat64, err, 4, fd, file, st, flag);
+#else
+  struct statx tmp;
+
+  result = INTERNAL_SYSCALL (statx, err, 5, fd, file, AT_NO_AUTOMOUNT | flag,
+                             STATX_BASIC_STATS, &tmp);
+  if (result == 0)
+    __cp_stat64_statx (st, &tmp);
+#endif
   if (!__builtin_expect (INTERNAL_SYSCALL_ERROR_P (result, err), 1))
     return 0;
   else
diff --git a/sysdeps/unix/sysv/linux/generic/wordsize-32/fxstat.c b/sysdeps/unix/sysv/linux/generic/wordsize-32/fxstat.c
index b5c766d..0c0ff10 100644
--- a/sysdeps/unix/sysv/linux/generic/wordsize-32/fxstat.c
+++ b/sysdeps/unix/sysv/linux/generic/wordsize-32/fxstat.c
@@ -18,6 +18,7 @@ 
 
 #include <errno.h>
 #include <stddef.h>
+#include <fcntl.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <kernel_stat.h>
@@ -27,6 +28,7 @@ 
 
 #if !XSTAT_IS_XSTAT64
 #include "overflow.h"
+#include "statx_cp.h"
 
 /* Get information about the file FD in BUF.  */
 int
@@ -34,8 +36,17 @@  __fxstat (int vers, int fd, struct stat *buf)
 {
   if (vers == _STAT_VER_KERNEL)
     {
+# ifdef __NR_fstat64
       int rc = INLINE_SYSCALL (fstat64, 2, fd, buf);
       return rc ?: stat_overflow (buf);
+# else
+      struct statx tmp;
+      int rc = INLINE_SYSCALL (statx, 5, fd, "", AT_EMPTY_PATH,
+                               STATX_BASIC_STATS, &tmp);
+      if (rc == 0)
+        __cp_stat_statx (buf, &tmp);
+      return rc;
+# endif
     }
 
   errno = EINVAL;
diff --git a/sysdeps/unix/sysv/linux/generic/wordsize-32/fxstatat.c b/sysdeps/unix/sysv/linux/generic/wordsize-32/fxstatat.c
index 0bda8f7..58e3a90 100644
--- a/sysdeps/unix/sysv/linux/generic/wordsize-32/fxstatat.c
+++ b/sysdeps/unix/sysv/linux/generic/wordsize-32/fxstatat.c
@@ -28,6 +28,7 @@ 
 
 #if !XSTAT_IS_XSTAT64
 #include "overflow.h"
+#include "statx_cp.h"
 
 /* Get information about the file NAME in BUF.  */
 int
@@ -35,8 +36,18 @@  __fxstatat (int vers, int fd, const char *file, struct stat *buf, int flag)
 {
   if (vers == _STAT_VER_KERNEL)
     {
+# ifdef __NR_fstatat64
       int rc = INLINE_SYSCALL (fstatat64, 4, fd, file, buf, flag);
       return rc ?: stat_overflow (buf);
+# else
+      struct statx tmp;
+      int rc = INLINE_SYSCALL (statx, 5, fd, file,
+                               AT_NO_AUTOMOUNT | flag,
+                               STATX_BASIC_STATS, &tmp);
+      if (rc == 0)
+        __cp_stat_statx (buf, &tmp);
+      return rc;
+# endif
     }
 
   errno = EINVAL;
diff --git a/sysdeps/unix/sysv/linux/generic/wordsize-32/lxstat.c b/sysdeps/unix/sysv/linux/generic/wordsize-32/lxstat.c
index c278a4d..b680782 100644
--- a/sysdeps/unix/sysv/linux/generic/wordsize-32/lxstat.c
+++ b/sysdeps/unix/sysv/linux/generic/wordsize-32/lxstat.c
@@ -27,6 +27,7 @@ 
 
 #if !XSTAT_IS_XSTAT64
 #include "overflow.h"
+#include "statx_cp.h"
 
 /* Get information about the file NAME in BUF.  */
 int
@@ -34,9 +35,19 @@  __lxstat (int vers, const char *name, struct stat *buf)
 {
   if (vers == _STAT_VER_KERNEL)
     {
+#ifdef __NR_fstatat64
       int rc = INLINE_SYSCALL (fstatat64, 4, AT_FDCWD, name, buf,
                                AT_SYMLINK_NOFOLLOW);
       return rc ?: stat_overflow (buf);
+#else
+      struct statx tmp;
+      int rc = INLINE_SYSCALL (statx, 5, AT_FDCWD, name,
+                               AT_NO_AUTOMOUNT | AT_SYMLINK_NOFOLLOW,
+                               STATX_BASIC_STATS, &tmp);
+      if (rc == 0)
+        __cp_stat_statx (buf, &tmp);
+      return rc;
+#endif
     }
   errno = EINVAL;
   return -1;
diff --git a/sysdeps/unix/sysv/linux/generic/wordsize-32/lxstat64.c b/sysdeps/unix/sysv/linux/generic/wordsize-32/lxstat64.c
index 761dd16..c41c759 100644
--- a/sysdeps/unix/sysv/linux/generic/wordsize-32/lxstat64.c
+++ b/sysdeps/unix/sysv/linux/generic/wordsize-32/lxstat64.c
@@ -32,13 +32,28 @@ 
 #include <sysdep.h>
 #include <sys/syscall.h>
 
+#include "statx_cp.h"
+
 /* Get information about the file NAME in BUF.  */
 int
 __lxstat64 (int vers, const char *name, struct stat64 *buf)
 {
   if (vers == _STAT_VER_KERNEL)
-    return INLINE_SYSCALL (fstatat64, 4, AT_FDCWD, name, buf,
-                           AT_SYMLINK_NOFOLLOW);
+    {
+#ifdef __NR_fstatat64
+      return INLINE_SYSCALL (fstatat64, 4, AT_FDCWD, name, buf,
+                             AT_SYMLINK_NOFOLLOW);
+#else
+      struct statx tmp;
+      int rc = INLINE_SYSCALL (statx, 5, AT_FDCWD, name,
+                               AT_NO_AUTOMOUNT | AT_SYMLINK_NOFOLLOW,
+                               STATX_BASIC_STATS, &tmp);
+      if (rc == 0)
+        __cp_stat64_statx (buf, &tmp);
+      return rc;
+#endif
+    }
+
   errno = EINVAL;
   return -1;
 }
diff --git a/sysdeps/unix/sysv/linux/generic/wordsize-32/xstat.c b/sysdeps/unix/sysv/linux/generic/wordsize-32/xstat.c
index 1fd57ff..b67f4bb 100644
--- a/sysdeps/unix/sysv/linux/generic/wordsize-32/xstat.c
+++ b/sysdeps/unix/sysv/linux/generic/wordsize-32/xstat.c
@@ -27,6 +27,7 @@ 
 
 #if !XSTAT_IS_XSTAT64
 #include "overflow.h"
+#include "statx_cp.h"
 
 /* Get information about the file NAME in BUF.  */
 int
@@ -34,8 +35,17 @@  __xstat (int vers, const char *name, struct stat *buf)
 {
   if (vers == _STAT_VER_KERNEL)
     {
+# ifdef __NR_fstatat64
       int rc = INLINE_SYSCALL (fstatat64, 4, AT_FDCWD, name, buf, 0);
       return rc ?: stat_overflow (buf);
+# else
+      struct statx tmp;
+      int rc = INLINE_SYSCALL (statx, 5, AT_FDCWD, name, AT_NO_AUTOMOUNT,
+                               STATX_BASIC_STATS, &tmp);
+      if (rc == 0)
+        __cp_stat_statx (buf, &tmp);
+      return rc;
+# endif
     }
 
   errno = EINVAL;
diff --git a/sysdeps/unix/sysv/linux/generic/wordsize-32/xstat64.c b/sysdeps/unix/sysv/linux/generic/wordsize-32/xstat64.c
index ae70495..209675e 100644
--- a/sysdeps/unix/sysv/linux/generic/wordsize-32/xstat64.c
+++ b/sysdeps/unix/sysv/linux/generic/wordsize-32/xstat64.c
@@ -32,13 +32,25 @@ 
 #include <sysdep.h>
 #include <sys/syscall.h>
 
+#include "statx_cp.h"
+
 /* Get information about the file NAME in BUF.  */
 int
 __xstat64 (int vers, const char *name, struct stat64 *buf)
 {
   if (vers == _STAT_VER_KERNEL)
+    {
+#ifdef __NR_fstatat64
     return INLINE_SYSCALL (fstatat64, 4, AT_FDCWD, name, buf, 0);
-
+#else
+      struct statx tmp;
+      int rc = INLINE_SYSCALL (statx, 5, AT_FDCWD, name, AT_NO_AUTOMOUNT,
+                               STATX_BASIC_STATS, &tmp);
+      if (rc == 0)
+        __cp_stat64_statx (buf, &tmp);
+      return rc;
+#endif
+    }
   errno = EINVAL;
   return -1;
 }
diff --git a/sysdeps/unix/sysv/linux/statx_cp.c b/sysdeps/unix/sysv/linux/statx_cp.c
new file mode 100644
index 0000000..f31f5da4
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/statx_cp.c
@@ -0,0 +1,78 @@ 
+/* Struct statx to stat/stat64 conversion for Linux.
+   Copyright (C) 2018 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
+   <http://www.gnu.org/licenses/>.  */
+
+#include <errno.h>
+#include <stddef.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <kernel_stat.h>
+
+#include <sysdep.h>
+#include <sys/syscall.h>
+
+#if !defined(__NR_fstat64) && !defined(__NR_fstatat64) && __WORDSIZE == 32 \
+    && !defined(__NR_fstat) && !defined(__NR_newfstatat)
+void
+__cp_stat_statx (struct stat *to, struct statx *from)
+{
+  memset(to, 0, sizeof(struct stat));
+  to->st_dev = ((from->stx_dev_minor & 0xff) | (from->stx_dev_major << 8)
+		| ((from->stx_dev_minor & ~0xff) << 12));
+  to->st_rdev = ((from->stx_rdev_minor & 0xff) | (from->stx_rdev_major << 8)
+		 | ((from->stx_rdev_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_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;
+  to->st_size = from->stx_size;
+  to->st_blocks = from->stx_blocks;
+  to->st_blksize = from->stx_blksize;
+}
+
+void
+__cp_stat64_statx (struct stat64 *to, struct statx *from)
+{
+  memset(to, 0, sizeof(struct stat64));
+  to->st_dev = ((from->stx_dev_minor & 0xff) | (from->stx_dev_major << 8)
+		| ((from->stx_dev_minor & ~0xff) << 12));
+  to->st_rdev = ((from->stx_rdev_minor & 0xff) | (from->stx_rdev_major << 8)
+		 | ((from->stx_rdev_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_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;
+  to->st_size = from->stx_size;
+  to->st_blocks = from->stx_blocks;
+  to->st_blksize = from->stx_blksize;
+}
+#endif
diff --git a/sysdeps/unix/sysv/linux/statx_cp.h b/sysdeps/unix/sysv/linux/statx_cp.h
new file mode 100644
index 0000000..7e411a7
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/statx_cp.h
@@ -0,0 +1,23 @@ 
+/* Struct statx to stat/stat64 conversion for Linux.
+   Copyright (C) 2018 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
+   <http://www.gnu.org/licenses/>.  */
+
+extern void __cp_stat_statx (struct stat *to, struct statx *from)
+  attribute_hidden;
+
+extern void __cp_stat64_statx (struct stat64 *to, struct statx *from)
+  attribute_hidden;
-- 
2.7.4